diff --git a/packages/webapp/src/components/maps-page/ActionDialog.tsx b/packages/webapp/src/components/maps-page/ActionDialog.tsx index 69b42d82..31328e96 100644 --- a/packages/webapp/src/components/maps-page/ActionDialog.tsx +++ b/packages/webapp/src/components/maps-page/ActionDialog.tsx @@ -1,186 +1,13 @@ import React from 'react'; -import Button from '@material-ui/core/Button'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import { FormattedMessage, useIntl } from 'react-intl'; -import { ErrorInfo, MapInfo, Service } from '../../services/Service'; -import { FormControl, TextField } from '@material-ui/core'; -import { Alert, AlertTitle } from '@material-ui/lab'; -import { useSelector } from 'react-redux'; -import { - activeInstance, -} from '../../reducers/serviceSlice' -import { useMutation, useQuery, useQueryClient } from 'react-query'; +import RenameDialog from './RenameDialog'; +import DeleteDialog from './DeleteDialog'; -type DialogProps = { - open: boolean, - mapId: number, - onClose: () => void -} - export type BasicMapInfo = { name: string; description: string | undefined; } -function DeleteDialog(props: DialogProps) { - const service: Service = useSelector(activeInstance); - const queryClient = useQueryClient(); - const mapId = props.mapId; - - const mutation = useMutation((id: number) => service.deleteMap(id), - { - onSuccess: () => { - queryClient.invalidateQueries() - props.onClose(); - } - } - ); - - const { isLoading, error, data } = useQuery('maps', () => { - return service.fetchAllMaps(); - }); - - let mapInfo: MapInfo | undefined = undefined; - if (data) { - mapInfo = data.find((m) => m.id == mapId); - } - - const handleOnClose = (action: 'accept' | undefined): void => { - if (action == 'accept' && mapInfo) { - mutation.mutate(mapId); - } - }; - - return ( -
- handleOnClose(undefined)} > - - - - Delete '{mapInfo?.name}' - - - - - - - - -
- ); -} -export type RenameModel = { - id: number; - name: string; - description?: string; -} - -function RenameDialog(props: DialogProps) { - - const defaultModel: RenameModel = { name: '', description: '', id: -1 }; - const [model, setModel] = React.useState(defaultModel); - const [errorInfo, setErroInfo] = React.useState(); - const intl = useIntl(); - - // useEffect(() => { - // const mapId: number = props.mapId; - // if (mapId != -1) { - // const mapInfo: MapInfo | undefined = useSelector(activeInstance) - // .find(m => m.id == props.mapId); - - // if (!mapInfo) { - // throw "Please, reflesh the page."; - // } - - // setModel({ ...mapInfo }); - // } - // }, []); - - const handleOnClose = (): void => { - // Clean Up ... - setModel(defaultModel); - setErroInfo(undefined); - - props.onClose(); - }; - - const handleOnSubmit = (event: React.FormEvent): void => { - // Stop form submit ... - event.preventDefault(); - - // Fire rename ... - const mapId: number = props.mapId; - try { - // dispatch(rename({ id: mapId, name: model.name, description: model.description })) - handleOnClose(); - - } catch (errorInfo) { - setErroInfo(errorInfo) - } - }; - - const handleOnChange = (event: React.ChangeEvent): void => { - event.preventDefault(); - - // Update value ... - const name = event.target.name; - const value = event.target.value; - setModel({ ...model, [name as keyof BasicMapInfo]: value }); - } - - return ( -
- handleOnClose()} > -
- - - - - - - - - - {Boolean(errorInfo?.msg) ? : null} - - - - - - - - - - - - - -
-
-
- ); -} - export type DialogType = 'share' | 'delete' | 'info' | 'duplicate' | 'export' | 'rename' | 'publish'; type ActionDialogProps = { diff --git a/packages/webapp/src/components/maps-page/DeleteDialog.tsx b/packages/webapp/src/components/maps-page/DeleteDialog.tsx new file mode 100644 index 00000000..a10087a0 --- /dev/null +++ b/packages/webapp/src/components/maps-page/DeleteDialog.tsx @@ -0,0 +1,58 @@ +import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from "@material-ui/core"; +import { Alert, AlertTitle } from "@material-ui/lab"; +import React from "react"; +import { FormattedMessage } 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"; + + +const DeleteDialog = (props: DialogProps) => { + const service: Service = useSelector(activeInstance); + const queryClient = useQueryClient(); + const mutation = useMutation((id: number) => service.deleteMap(id), + { + onSuccess: () => handleOnMutationSuccess(props.onClose, queryClient) + } + ); + + const mapId = props.mapId; + const handleOnClose = (action: 'accept' | undefined): void => { + if (action == 'accept') { + mutation.mutate(mapId); + } else { + props.onClose(); + } + }; + + // Fetch map model to be rendered ... + const { map } = fetchMapById(mapId); + return ( +
+ handleOnClose(undefined)} > + + + + Delete '{map?.name}' + + + + + + + + +
+ ); + } + + + export default DeleteDialog; \ No newline at end of file diff --git a/packages/webapp/src/components/maps-page/DialogCommon.ts b/packages/webapp/src/components/maps-page/DialogCommon.ts new file mode 100644 index 00000000..16ed5e9c --- /dev/null +++ b/packages/webapp/src/components/maps-page/DialogCommon.ts @@ -0,0 +1,36 @@ +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/RenameDialog.tsx b/packages/webapp/src/components/maps-page/RenameDialog.tsx new file mode 100644 index 00000000..5cf1b0a9 --- /dev/null +++ b/packages/webapp/src/components/maps-page/RenameDialog.tsx @@ -0,0 +1,112 @@ +import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControl, TextField } from "@material-ui/core"; +import { Alert } from "@material-ui/lab"; +import React, { useEffect } from "react"; +import { FormattedMessage, 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 { DialogProps, fetchMapById, handleOnMutationSuccess } from "./DialogCommon"; + +export type RenameModel = { + id: number; + name: string; + description?: string; +} +const defaultModel: RenameModel = { name: '', description: '', id: -1 }; + +const RenameDialog = (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: RenameModel) => { + const { id, ...rest } = model; + return service.renameMap(id, rest).then(() => model); + }, + { + onSuccess: () => { + handleOnMutationSuccess(props.onClose, queryClient); + }, + onError: (error) => { + setError(error); + } + } + ); + + const handleOnClose = (): void => { + setModel(defaultModel); + setError(undefined); + props.onClose(); + }; + + 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 ( +
+ handleOnClose()} > +
+ + + + + + + + + + {Boolean(error?.msg) ? : null} + + + + + + + + + + + + + +
+
+
+ ); +} + +export default RenameDialog; \ No newline at end of file diff --git a/packages/webapp/src/components/maps-page/index.tsx b/packages/webapp/src/components/maps-page/index.tsx index 8a93a65d..ece4fcd7 100644 --- a/packages/webapp/src/components/maps-page/index.tsx +++ b/packages/webapp/src/components/maps-page/index.tsx @@ -236,10 +236,9 @@ const EnhancedTable = () => { type ActiveDialog = { actionType: DialogType; mapId: number - }; - const [activeDialog, setActiveDialog] = React.useState(undefined); + const [activeDialog, setActiveDialog] = React.useState(undefined); const handleRequestSort = (event: React.MouseEvent, property: keyof MapInfo) => { const isAsc = orderBy === property && order === 'asc'; setOrder(isAsc ? 'desc' : 'asc'); diff --git a/packages/webapp/src/services/Service.ts b/packages/webapp/src/services/Service.ts index 573beb86..bf065f2e 100644 --- a/packages/webapp/src/services/Service.ts +++ b/packages/webapp/src/services/Service.ts @@ -76,13 +76,28 @@ class RestService implements Service { } renameMap(id: number, basicInfo: BasicMapInfo): Promise { - const fieldErrors: Map = new Map(); - fieldErrors.set('name', 'name already exists ') - return Promise.reject({ - msg: 'Map already exists ...' + basicInfo.name, - fields: fieldErrors - }); + const exists = this.maps.find(m => m.name == basicInfo.name) != undefined; + if (!exists) { + this.maps = this.maps.map(m => { + const result = m; + if (m.id == id) { + result.description = basicInfo.description ? basicInfo.description : ''; + result.name = basicInfo.name; + } + return result; + }) + return Promise.resolve(); + } else { + const fieldErrors: Map = new Map(); + fieldErrors.set('name', 'name already exists ') + + return Promise.reject({ + msg: 'Map already exists ...' + basicInfo.name, + fields: fieldErrors + + }) + }; } deleteMap(id: number): Promise {