From 1dda8b6c9f718b80664bf8426144a661c3e2c7af Mon Sep 17 00:00:00 2001 From: Paulo Gustavo Veiga Date: Thu, 14 Jan 2021 01:14:07 -0800 Subject: [PATCH] Complete info dialog. --- packages/webapp/lang/en.json | 8 +- packages/webapp/src/compiled-lang/en.json | 14 ++- .../maps-page/action-chooser/index.tsx | 6 +- .../action-dialog-dispatcher/DialogCommon.ts | 36 -------- .../action-dialog-dispatcher/index.tsx | 34 ------- .../action-dialog/index.tsx | 15 ++-- .../action-dialog/style.ts | 0 .../delete/index.tsx | 5 +- .../action-dispatcher/duplicate/index.tsx | 90 +++++++++++++++++++ .../maps-page/action-dispatcher/index.tsx | 86 ++++++++++++++++++ .../action-dispatcher/info/index.tsx | 35 ++++++++ .../rename/index.tsx | 6 +- .../webapp/src/components/maps-page/index.tsx | 12 +-- .../components/maps-page/nav-panel/index.tsx | 8 ++ .../components/maps-page/nav-panel/style.ts | 7 ++ .../webapp/src/components/maps-page/styled.ts | 5 -- packages/webapp/src/services/Service.ts | 32 ++++++- packages/webapp/src/theme/global-style.ts | 25 ++++-- 18 files changed, 317 insertions(+), 107 deletions(-) delete mode 100644 packages/webapp/src/components/maps-page/action-dialog-dispatcher/DialogCommon.ts delete mode 100644 packages/webapp/src/components/maps-page/action-dialog-dispatcher/index.tsx rename packages/webapp/src/components/maps-page/{action-dialog-dispatcher => action-dispatcher}/action-dialog/index.tsx (75%) rename packages/webapp/src/components/maps-page/{action-dialog-dispatcher => action-dispatcher}/action-dialog/style.ts (100%) rename packages/webapp/src/components/maps-page/{action-dialog-dispatcher => action-dispatcher}/delete/index.tsx (93%) create mode 100644 packages/webapp/src/components/maps-page/action-dispatcher/duplicate/index.tsx create mode 100644 packages/webapp/src/components/maps-page/action-dispatcher/index.tsx create mode 100644 packages/webapp/src/components/maps-page/action-dispatcher/info/index.tsx rename packages/webapp/src/components/maps-page/{action-dialog-dispatcher => action-dispatcher}/rename/index.tsx (96%) create mode 100644 packages/webapp/src/components/maps-page/nav-panel/index.tsx create mode 100644 packages/webapp/src/components/maps-page/nav-panel/style.ts diff --git a/packages/webapp/lang/en.json b/packages/webapp/lang/en.json index 66a22631..de488f3f 100644 --- a/packages/webapp/lang/en.json +++ b/packages/webapp/lang/en.json @@ -8,12 +8,18 @@ "action.delete-description": { "defaultMessage": "Deleted mindmap can not be recovered. Do you want to continue ?." }, + "action.delete-title": { + "defaultMessage": "Delete" + }, "action.duplicate": { "defaultMessage": "Duplicate" }, "action.export": { "defaultMessage": "Export" }, + "action.info": { + "defaultMessage": "Info" + }, "action.open": { "defaultMessage": "Open" }, @@ -27,7 +33,7 @@ "defaultMessage": "Rename" }, "action.share": { - "defaultMessage": "Info" + "defaultMessage": "Share" }, "common.wait": { "defaultMessage": "Please wait ..." diff --git a/packages/webapp/src/compiled-lang/en.json b/packages/webapp/src/compiled-lang/en.json index d9ddd55e..3716dff7 100644 --- a/packages/webapp/src/compiled-lang/en.json +++ b/packages/webapp/src/compiled-lang/en.json @@ -17,6 +17,12 @@ "value": "Deleted mindmap can not be recovered. Do you want to continue ?." } ], + "action.delete-title": [ + { + "type": 0, + "value": "Delete" + } + ], "action.duplicate": [ { "type": 0, @@ -29,6 +35,12 @@ "value": "Export" } ], + "action.info": [ + { + "type": 0, + "value": "Info" + } + ], "action.open": [ { "type": 0, @@ -56,7 +68,7 @@ "action.share": [ { "type": 0, - "value": "Info" + "value": "Share" } ], "common.wait": [ diff --git a/packages/webapp/src/components/maps-page/action-chooser/index.tsx b/packages/webapp/src/components/maps-page/action-chooser/index.tsx index 132e0b70..c26817cc 100644 --- a/packages/webapp/src/components/maps-page/action-chooser/index.tsx +++ b/packages/webapp/src/components/maps-page/action-chooser/index.tsx @@ -14,12 +14,12 @@ import { FormattedMessage } from 'react-intl'; export type ActionType = 'open' | 'share' | 'delete' | 'info' | 'duplicate' | 'export' | 'rename' | 'print' | 'info' | 'publish' | undefined; -interface MapActionProps { +interface ActionProps { onClose: (action: ActionType) => void; anchor: undefined | HTMLElement; } -const ActionChooser = (props: MapActionProps) => { +const ActionChooser = (props: ActionProps) => { const { anchor, onClose } = props; const handleOnClose = (action: ActionType): ((event: React.MouseEvent) => void) => { @@ -68,7 +68,7 @@ const ActionChooser = (props: MapActionProps) => { - + ); } diff --git a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/DialogCommon.ts b/packages/webapp/src/components/maps-page/action-dialog-dispatcher/DialogCommon.ts deleted file mode 100644 index 74031684..00000000 --- a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/DialogCommon.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { QueryClient, useQuery } from "react-query"; -import { useSelector } from "react-redux"; -import { ErrorInfo, MapInfo, Service } from "../../../services/Service"; -import { activeInstance, } from '../../../reducers/serviceSlice'; - -type MapLoadResult = { - isLoading: boolean, - error: ErrorInfo | null, - map: MapInfo | null -} - -export const fetchMapById = (id: number): MapLoadResult => { - - const service: Service = useSelector(activeInstance); - const { isLoading, error, data } = useQuery('maps', () => { - return service.fetchAllMaps(); - }); - - const result = data?.find(m => m.id == id); - const map = result ? result : null; - return { isLoading: isLoading, error: error, map: map }; -} - - -export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => { - queryClient.invalidateQueries('maps') - onClose(); -} - -export type DialogProps = { - open: boolean, - mapId: number, - onClose: () => void -} - - diff --git a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/index.tsx b/packages/webapp/src/components/maps-page/action-dialog-dispatcher/index.tsx deleted file mode 100644 index 2da890d8..00000000 --- a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import RenameDialog from './rename'; -import DeleteDialog from './delete'; -import { ActionType } from '../action-chooser'; - - -export type BasicMapInfo = { - name: string; - description: string | undefined; -} - -type ActionDialogProps = { - action?: ActionType, - mapId: number, - onClose: () => void -} - -const ActionDialogDispatcher = (props: ActionDialogProps) => { - const handleOnClose = (): void => { - props.onClose(); - } - - const mapId = props.mapId; - const action = props.action; - - return ( - - - - - ); -} - -export default ActionDialogDispatcher; diff --git a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/action-dialog/index.tsx b/packages/webapp/src/components/maps-page/action-dispatcher/action-dialog/index.tsx similarity index 75% rename from packages/webapp/src/components/maps-page/action-dialog-dispatcher/action-dialog/index.tsx rename to packages/webapp/src/components/maps-page/action-dispatcher/action-dialog/index.tsx index fa007d36..83afc8e8 100644 --- a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/action-dialog/index.tsx +++ b/packages/webapp/src/components/maps-page/action-dispatcher/action-dialog/index.tsx @@ -7,14 +7,14 @@ import GlobalError from "../../../form/global-error"; export type DialogProps = { onClose: () => void; - onSubmit: (event: React.FormEvent) => void; + onSubmit?: (event: React.FormEvent) => void; open: boolean; children: any; error?: ErrorInfo; title: MessageDescriptor; description?: MessageDescriptor; - submitButton: MessageDescriptor; + submitButton?: MessageDescriptor; } const BaseDialog = (props: DialogProps) => { @@ -44,12 +44,13 @@ const BaseDialog = (props: DialogProps) => { - - {intl.formatMessage(props.title)} - - + {handleOnSubmit ? ( + + {intl.formatMessage(props.title)} + ) : null + } - + {handleOnSubmit ? () : ()}; diff --git a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/action-dialog/style.ts b/packages/webapp/src/components/maps-page/action-dispatcher/action-dialog/style.ts similarity index 100% rename from packages/webapp/src/components/maps-page/action-dialog-dispatcher/action-dialog/style.ts rename to packages/webapp/src/components/maps-page/action-dispatcher/action-dialog/style.ts diff --git a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/delete/index.tsx b/packages/webapp/src/components/maps-page/action-dispatcher/delete/index.tsx similarity index 93% rename from packages/webapp/src/components/maps-page/action-dialog-dispatcher/delete/index.tsx rename to packages/webapp/src/components/maps-page/action-dispatcher/delete/index.tsx index aaca6221..e04ef7ab 100644 --- a/packages/webapp/src/components/maps-page/action-dialog-dispatcher/delete/index.tsx +++ b/packages/webapp/src/components/maps-page/action-dispatcher/delete/index.tsx @@ -1,11 +1,11 @@ import { Alert, AlertTitle } from "@material-ui/lab"; import React from "react"; -import { FormattedMessage } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import { useMutation, useQueryClient } from "react-query"; import { useSelector } from "react-redux"; import { Service } from "../../../../services/Service"; import { activeInstance } from '../../../../reducers/serviceSlice'; -import { DialogProps, fetchMapById, handleOnMutationSuccess } from "../DialogCommon"; +import { DialogProps, fetchMapById, handleOnMutationSuccess } from ".."; import BaseDialog from "../action-dialog"; @@ -35,7 +35,6 @@ const DeleteDialog = (props: DialogProps) => { open={props.open} onClose={handleOnClose} onSubmit={handleOnSubmit} title={{ id: "action.delete-title", defaultMessage: "Delete" }} submitButton={{ id: "action.delete-title", defaultMessage: "Delete" }} > - Delete '{map?.name}' diff --git a/packages/webapp/src/components/maps-page/action-dispatcher/duplicate/index.tsx b/packages/webapp/src/components/maps-page/action-dispatcher/duplicate/index.tsx new file mode 100644 index 00000000..39b68ef6 --- /dev/null +++ b/packages/webapp/src/components/maps-page/action-dispatcher/duplicate/index.tsx @@ -0,0 +1,90 @@ +import React, { useEffect } from "react"; +import { useIntl } from "react-intl"; +import { useMutation, useQueryClient } from "react-query"; +import { useSelector } from "react-redux"; +import { BasicMapInfo, ErrorInfo, Service } from "../../../../services/Service"; +import { activeInstance } from '../../../../reducers/serviceSlice'; +import Input from "../../../form/input"; +import { FormControl } from "@material-ui/core"; +import { DialogProps, fetchMapById, handleOnMutationSuccess } from ".."; +import BaseDialog from "../action-dialog"; + +export type DuplicateModel = { + id: number; + name: string; + description?: string; +} + +const defaultModel: DuplicateModel = { name: '', description: '', id: -1 }; +const DuplicateDialog = (props: DialogProps) => { + const service: Service = useSelector(activeInstance); + const [model, setModel] = React.useState(defaultModel); + const [error, setError] = React.useState(); + const { mapId, open } = props; + + const intl = useIntl(); + const queryClient = useQueryClient(); + + const mutation = useMutation((model: DuplicateModel) => { + const { id, ...rest } = model; + return service.duplicateMap(id, rest).then(() => model); + }, + { + onSuccess: () => { + handleOnMutationSuccess(props.onClose, queryClient); + }, + onError: (error) => { + setError(error); + } + } + ); + + const handleOnClose = (): void => { + props.onClose(); + setModel(defaultModel); + setError(undefined); + }; + + const handleOnSubmit = (event: React.FormEvent): void => { + event.preventDefault(); + mutation.mutate(model); + }; + + const handleOnChange = (event: React.ChangeEvent): void => { + event.preventDefault(); + + const name = event.target.name; + const value = event.target.value; + setModel({ ...model, [name as keyof BasicMapInfo]: value }); + } + + const { map } = fetchMapById(mapId); + useEffect(() => { + if (open && map) { + setModel(map); + } else { + setModel(defaultModel); + setError(undefined); + } + }, [mapId]) + + return ( +
+ + + + + + + + +
+ ); +} + +export default DuplicateDialog; \ No newline at end of file diff --git a/packages/webapp/src/components/maps-page/action-dispatcher/index.tsx b/packages/webapp/src/components/maps-page/action-dispatcher/index.tsx new file mode 100644 index 00000000..3be2cb44 --- /dev/null +++ b/packages/webapp/src/components/maps-page/action-dispatcher/index.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import RenameDialog from './rename'; +import DeleteDialog from './delete'; +import { ActionType } from '../action-chooser'; +import { ErrorInfo, MapInfo, Service } from '../../../services/Service'; +import { useSelector } from 'react-redux'; +import { QueryClient, useQuery } from 'react-query'; +import { activeInstance } from '../../../reducers/serviceSlice'; +import DuplicateDialog from './duplicate'; +import { useHistory } from 'react-router-dom'; +import InfoDialog from './info'; + + +export type BasicMapInfo = { + name: string; + description: string | undefined; +} + +type ActionDialogProps = { + action?: ActionType, + mapId: number, + onClose: () => void +} + +const ActionDispatcher = (props: ActionDialogProps) => { + const history = useHistory(); + const mapId = props.mapId; + const action = props.action; + + const handleOnClose = (): void => { + props.onClose(); + } + + switch (action) { + case 'open': + history.push(`/c/maps/${mapId}/edit`); + break; + case 'print': + history.push(`/c/maps/${mapId}/print`); + break; + } + + return ( + + + + + + + ); +} + + +type MapLoadResult = { + isLoading: boolean, + error: ErrorInfo | null, + map: MapInfo | null +} + +export const fetchMapById = (id: number): MapLoadResult => { + + const service: Service = useSelector(activeInstance); + const { isLoading, error, data } = useQuery('maps', () => { + return service.fetchAllMaps(); + }); + + const result = data?.find(m => m.id == id); + const map = result ? result : null; + return { isLoading: isLoading, error: error, map: map }; +} + + +export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => { + queryClient.invalidateQueries('maps') + onClose(); +} + +export type DialogProps = { + open: boolean, + mapId: number, + onClose: () => void +} + +export default ActionDispatcher; + + diff --git a/packages/webapp/src/components/maps-page/action-dispatcher/info/index.tsx b/packages/webapp/src/components/maps-page/action-dispatcher/info/index.tsx new file mode 100644 index 00000000..401c6735 --- /dev/null +++ b/packages/webapp/src/components/maps-page/action-dispatcher/info/index.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { useQueryClient } from "react-query"; +import { useSelector } from "react-redux"; +import { Service } from "../../../../services/Service"; +import { activeInstance } from '../../../../reducers/serviceSlice'; +import { DialogProps, fetchMapById } from ".."; +import BaseDialog from "../action-dialog"; + + +const InfoDialog = (props: DialogProps) => { + const service: Service = useSelector(activeInstance); + const queryClient = useQueryClient(); + + + const mapId = props.mapId; + const handleOnClose = (): void => { + props.onClose(); + }; + + const { map } = fetchMapById(mapId); + return ( +
+ + +