Skip to content

Commit

Permalink
Merge pull request #336 from virtualidentityag/develop
Browse files Browse the repository at this point in the history
Merge develop
  • Loading branch information
web-mi authored Jan 15, 2024
2 parents 19776d5 + 84cdc3e commit 3fe61cb
Show file tree
Hide file tree
Showing 50 changed files with 1,830 additions and 90 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
.env.development.local
.env.test.local
.env.production.local
/.vite/

npm-debug.log*
yarn-debug.log*
Expand Down
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
<meta name="theme-color" content="#ffffff" />

<title>Online-Beratung Admin</title>
<script>
const global = globalThis;
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"homepage": "http://tenant1.onlineberatung.local/admin",
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@draft-js-plugins/buttons": "^4.3.3",
"@draft-js-plugins/editor": "^4.1.4",
"@draft-js-plugins/static-toolbar": "^4.1.4",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mui/icons-material": "^5.8.4",
Expand All @@ -13,9 +16,12 @@
"axios": "^0.25.0",
"classnames": "^2.3.2",
"clsx": "^1.1.1",
"draft-js": ">=0.10.0",
"draft-js-export-html": "^1.4.1",
"hi-base32": "0.5.1",
"i18next": "^21.6.11",
"i18next-browser-languagedetector": "^6.1.3",
"immutable": "^3.8.1",
"lodash.merge": "^4.6.2",
"lodash.mergewith": "^4.6.2",
"lodash.set": "^4.3.2",
Expand Down
2 changes: 2 additions & 0 deletions src/api/agency/updateAgencyData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ export const updateAgencyData = async (agencyModel: AgencyData, formInput: Agenc
external: false,
demographics: formInput.demographics,
counsellingRelations: formInput.counsellingRelations,
dataProtection: formInput.dataProtection,
};

return fetchData({
url: `${agencyEndpointBase}/${agencyModel.id}`,
method: FETCH_METHODS.PUT,
Expand Down
44 changes: 25 additions & 19 deletions src/components/CardEditable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ interface CardEditableProps {
| ((data: { form: FormInstance<any>; editing: boolean }) => React.ReactElement | React.ReactElement[]);
onSave: <T>(formData: T, options?: { onError?: () => void }) => void;
formProp?: FormInstance;
onAddMode?: boolean;
editMode?: boolean;
hideSaveButton?: boolean;
hideCancelButton?: boolean;
tooltip?: string;
allowUnsavedChanges?: boolean;
editButton?: React.ReactChild;
Expand All @@ -41,7 +43,9 @@ export const CardEditable = ({
cancelKey = 'card.edit.cancel',
saveKey = 'card.edit.save',
children,
onAddMode,
editMode,
hideSaveButton,
hideCancelButton,
onSave,
formProp,
tooltip,
Expand All @@ -50,7 +54,7 @@ export const CardEditable = ({
}: CardEditableProps) => {
const [form] = Form.useForm(formProp);
const { t } = useTranslation();
const [editing, setEditing] = useState(onAddMode);
const [editing, setEditing] = useState(editMode);
const [hasChanges, setHasChanges] = useState(false);
const [showUnsavedChangesModal, setShowUnsavedChangesModal] = useState(false);

Expand All @@ -66,8 +70,8 @@ export const CardEditable = ({

const onFormSubmit = useCallback(
(formData) => {
onSave(formData, { onError: () => setEditing(true) });
setEditing(false);
onSave(formData, { onError: () => setEditing(editMode) });
setEditing(editMode);
setHasChanges(false);
},
[onSave],
Expand All @@ -83,7 +87,7 @@ export const CardEditable = ({
isLoading={isLoading}
cardTitleClassName={styles.cardTitleClassName}
cardTitleChildren={
!onAddMode &&
!editMode &&
!editing && (
<button className={styles.editCard} type="button" onClick={() => setEditing(true)}>
{editButton}
Expand All @@ -106,20 +110,22 @@ export const CardEditable = ({
{typeof children === 'function' ? children({ form, editing }) : children}
</Form>

{editing && !onAddMode && (
{editing && (!hideSaveButton || !hideCancelButton) && (
<div className={styles.footerActions}>
<Button
item={cancelEditButton}
buttonHandle={() => {
if (allowUnsavedChanges && hasChanges) {
setShowUnsavedChangesModal(true);
} else {
form.resetFields();
setEditing(false);
}
}}
/>
<Button item={saveEditButton} buttonHandle={() => form.submit()} />
{!hideCancelButton && (
<Button
item={cancelEditButton}
buttonHandle={() => {
if (allowUnsavedChanges && hasChanges) {
setShowUnsavedChangesModal(true);
} else {
form.resetFields();
setEditing(false);
}
}}
/>
)}
{!hideSaveButton && <Button item={saveEditButton} buttonHandle={() => form.submit()} />}
</div>
)}
{allowUnsavedChanges && showUnsavedChangesModal && (
Expand Down
19 changes: 9 additions & 10 deletions src/components/FormRichTextEditorField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Form } from 'antd';
import DisabledContext from 'antd/es/config-provider/DisabledContext';
import classNames from 'classnames';
import { useCallback, useContext, useState } from 'react';
import { useContext, useState } from 'react';
import { useTranslation } from 'react-i18next';
import RichTextEditor from '../RichText/RichTextEditor';
import styles from './styles.module.scss';
Expand All @@ -12,15 +12,17 @@ interface FormRichTextEditorFieldProps {
required?: boolean;
placeholderKey?: string;
name?: string | string[];
placeholders?: { [key: string]: string };
}

interface FormRichTextEditorProps {
onFocus?: () => void;
onBlur?: () => void;
onChange?: (value: boolean) => void;
onChange?: (value: string) => void;
value?: boolean;
placeholderKey: string;
className?: string;
placeholders?: { [key: string]: string };
}

const FormRichTextEditor = ({
Expand All @@ -30,26 +32,21 @@ const FormRichTextEditor = ({
value,
className,
placeholderKey,
placeholders,
}: FormRichTextEditorProps) => {
const { t } = useTranslation();
const contextDisabled = useContext(DisabledContext);

const onChangeLocal = useCallback((ev) => {
const html = ev.toString('html');
// Rich editor when we click even if it's empty it creates the content
// '<p><br></p>' so when that happens we still want to validate as empty
onChange(html === '<p><br></p>' ? '' : html);
}, []);

return (
<RichTextEditor
className={classNames(className)}
onChange={onChangeLocal}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
value={value || ''}
disabled={contextDisabled}
placeholder={placeholderKey ? t(placeholderKey) : undefined}
placeholders={placeholders}
/>
);
};
Expand All @@ -59,6 +56,7 @@ export const FormRichTextEditorField = ({
labelKey,
required,
placeholderKey,
placeholders,
}: FormRichTextEditorFieldProps) => {
const { t } = useTranslation();
const [focused, setFocused] = useState(false);
Expand All @@ -79,6 +77,7 @@ export const FormRichTextEditorField = ({
onBlur={() => setFocused(false)}
placeholderKey={placeholderKey}
className={styles.input}
placeholders={placeholders}
/>
</Form.Item>
);
Expand Down
9 changes: 5 additions & 4 deletions src/components/Page/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ChevronLeft } from '@mui/icons-material';
import { Spin } from 'antd';
import Title from 'antd/es/typography/Title';
import classNames from 'classnames';
import React, { forwardRef, LegacyRef, useMemo } from 'react';
import React, { cloneElement, forwardRef, LegacyRef, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { NavLink } from 'react-router-dom';
import styles from './styles.module.scss';
Expand All @@ -26,7 +26,7 @@ interface PageBackProps {
titleKey?: string;
path: string;
children?: React.ReactChild | React.ReactChild[];
tabs?: Array<{ to: string; titleKey: string }>;
tabs?: Array<{ to: string; titleKey: string; icon?: JSX.Element }>;
}

export const Page = ({ children, stickyHeader, isLoading }: PageProps) => {
Expand All @@ -42,16 +42,17 @@ export const Page = ({ children, stickyHeader, isLoading }: PageProps) => {
);
};

const PageTabs = ({ tabs }: { tabs: Array<{ to: string; titleKey }> }) => {
const PageTabs = ({ tabs }: { tabs: Array<{ to: string; titleKey; icon?: JSX.Element }> }) => {
const { t } = useTranslation();

return (
<div className={styles.tabsContainer}>
{tabs
?.filter((tab) => tab && tab.to)
.map((tab) => (
.map(({ icon, ...tab }) => (
<NavLink className={styles.tab} to={tab.to} key={tab.titleKey}>
{t(tab.titleKey)}
{icon && cloneElement(icon, { className: styles.tabIcon })}
</NavLink>
))}
</div>
Expand Down
5 changes: 5 additions & 0 deletions src/components/Page/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
border-bottom: 4px solid transparent;
font-size: 16px;
line-height: 24px;
display: inline-flex;
}

.tabIcon {
margin-left: 4px;
}

.titleContainer {
Expand Down
65 changes: 65 additions & 0 deletions src/components/RichText/BlockStyleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable react/no-children-prop */
import clsx from 'clsx';
import { EditorState, RichUtils } from 'draft-js';
import React, { MouseEvent, ReactNode, useCallback, useMemo } from 'react';
import { DraftJsStyleButtonProps } from '@draft-js-plugins/buttons';
import { Button } from 'antd';
import { ButtonType } from 'antd/lib/button/button';

interface ButtonProps extends DraftJsStyleButtonProps {
type: ButtonType;
}

interface CreateBlockStyleButtonProps extends Omit<DraftJsStyleButtonProps, 'buttonProps'> {
blockType: string;
children: ReactNode;
editorState: EditorState;
buttonProps?: ButtonProps;
}

const BlockStyleButton = ({
blockType,
children,
theme,
buttonProps,
setEditorState,
editorState,
}: CreateBlockStyleButtonProps) => {
const toggleStyle = useCallback(
(event: MouseEvent): void => {
event.preventDefault();
setEditorState(RichUtils.toggleBlockType(editorState, blockType));
},
[editorState, setEditorState],
);

const preventBubblingUp = useCallback((event: MouseEvent): void => {
event.preventDefault();
}, []);

const blockTypeIsActive = useMemo((): boolean => {
// if the button is rendered before the editor
if (!editorState) {
return false;
}

const type = editorState.getCurrentContent().getBlockForKey(editorState.getSelection().getStartKey()).getType();
return type === blockType;
}, [editorState]);

const className = blockTypeIsActive ? clsx(theme.button, theme.active) : theme.button;

return (
<Button
children={children}
className={className}
onMouseDown={preventBubblingUp}
onClick={toggleStyle}
size="small"
role="button"
aria-label={`create ${blockType}`}
{...buttonProps}
/>
);
};
export default BlockStyleButton;
59 changes: 59 additions & 0 deletions src/components/RichText/InlineStyleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* eslint-disable react/no-children-prop */
import clsx from 'clsx';
import { EditorState, RichUtils } from 'draft-js';
import React, { MouseEvent, ReactNode, useCallback, useMemo } from 'react';
import { DraftJsStyleButtonProps } from '@draft-js-plugins/buttons';
import { Button } from 'antd';
import { ButtonType } from 'antd/lib/button/button';

interface ButtonProps extends DraftJsStyleButtonProps {
type: ButtonType;
}

interface CreateInlineStyleButtonProps extends Omit<DraftJsStyleButtonProps, 'buttonProps'> {
inlineStyle: string;
children: ReactNode;
editorState: EditorState;
buttonProps?: ButtonProps;
}

const InlineStyleButton = ({
inlineStyle,
children,
theme,
buttonProps,
setEditorState,
editorState,
}: CreateInlineStyleButtonProps) => {
const toggleStyle = useCallback(
(event: MouseEvent): void => {
event.preventDefault();
setEditorState(RichUtils.toggleInlineStyle(editorState, inlineStyle));
},
[editorState, setEditorState],
);

const preventBubblingUp = useCallback((event: MouseEvent): void => {
event.preventDefault();
}, []);

const styleIsActive = useMemo((): boolean => {
return editorState && editorState.getCurrentInlineStyle().has(inlineStyle);
}, [editorState]);

const className = styleIsActive ? clsx(theme.button, theme.active) : theme.button;

return (
<Button
children={children}
className={className}
onMouseDown={preventBubblingUp}
onClick={toggleStyle}
size="small"
role="button"
aria-label={`${inlineStyle} text`}
{...buttonProps}
/>
);
};
export default InlineStyleButton;
Loading

0 comments on commit 3fe61cb

Please sign in to comment.