mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-25 15:47:55 +01:00
Complete refactor to react query
This commit is contained in:
parent
fbb2b11175
commit
503bc125c7
@ -1,186 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from '@material-ui/core/Button';
|
import RenameDialog from './RenameDialog';
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import DeleteDialog from './DeleteDialog';
|
||||||
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';
|
|
||||||
|
|
||||||
|
|
||||||
type DialogProps = {
|
|
||||||
open: boolean,
|
|
||||||
mapId: number,
|
|
||||||
onClose: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export type BasicMapInfo = {
|
export type BasicMapInfo = {
|
||||||
name: string;
|
name: string;
|
||||||
description: string | undefined;
|
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<unknown, ErrorInfo, MapInfo[]>('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 (
|
|
||||||
<div>
|
|
||||||
<Dialog
|
|
||||||
open={props.open}
|
|
||||||
onClose={() => handleOnClose(undefined)} >
|
|
||||||
<DialogTitle><FormattedMessage id="action.delete-title" defaultMessage="Delete" /></DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<Alert severity="warning">
|
|
||||||
<AlertTitle>Delete '{mapInfo?.name}'</AlertTitle>
|
|
||||||
<FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
|
|
||||||
</Alert>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => handleOnClose('accept')} variant="outlined" color="primary">
|
|
||||||
<FormattedMessage id="action.delete-button" defaultMessage="Delete" />
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => handleOnClose(undefined)} variant="outlined" color="secondary" autoFocus>
|
|
||||||
<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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<RenameModel>(defaultModel);
|
|
||||||
const [errorInfo, setErroInfo] = React.useState<ErrorInfo>();
|
|
||||||
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<HTMLFormElement>): 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<HTMLInputElement>): void => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// Update value ...
|
|
||||||
const name = event.target.name;
|
|
||||||
const value = event.target.value;
|
|
||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Dialog
|
|
||||||
open={props.open}
|
|
||||||
onClose={() => handleOnClose()} >
|
|
||||||
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
|
||||||
<DialogTitle>
|
|
||||||
<FormattedMessage id="action.rename-title" defaultMessage="Rename" />
|
|
||||||
</DialogTitle>
|
|
||||||
|
|
||||||
<DialogContent>
|
|
||||||
<DialogContentText>
|
|
||||||
<FormattedMessage id="action.rename-description" defaultMessage="Please, update the name and description for your mindmap." />
|
|
||||||
</DialogContentText>
|
|
||||||
|
|
||||||
{Boolean(errorInfo?.msg) ? <Alert severity="error" variant="filled" hidden={!Boolean(errorInfo?.msg)}>{errorInfo?.msg}</Alert> : null}
|
|
||||||
<FormControl margin="normal" required fullWidth>
|
|
||||||
<TextField name="name" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
|
|
||||||
value={model.name} onChange={handleOnChange}
|
|
||||||
error={Boolean(errorInfo?.fields?.get('name'))} helperText={errorInfo?.fields?.get('name')}
|
|
||||||
variant="filled" required={true} />
|
|
||||||
</FormControl>
|
|
||||||
<FormControl margin="normal" required fullWidth>
|
|
||||||
<TextField name="description" label={intl.formatMessage({ id: "action.rename-description-placeholder", defaultMessage: "Description" })} value={model.description} onChange={handleOnChange} variant="filled" />
|
|
||||||
</FormControl>
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
<DialogActions>
|
|
||||||
<Button color="primary" variant="outlined" type="submit">
|
|
||||||
<FormattedMessage id="action.rename-button" defaultMessage="Rename" />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button color="secondary" variant="outlined" autoFocus onClick={handleOnClose}>
|
|
||||||
<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</form>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DialogType = 'share' | 'delete' | 'info' | 'duplicate' | 'export' | 'rename' | 'publish';
|
export type DialogType = 'share' | 'delete' | 'info' | 'duplicate' | 'export' | 'rename' | 'publish';
|
||||||
|
|
||||||
type ActionDialogProps = {
|
type ActionDialogProps = {
|
||||||
|
58
packages/webapp/src/components/maps-page/DeleteDialog.tsx
Normal file
58
packages/webapp/src/components/maps-page/DeleteDialog.tsx
Normal file
@ -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 (
|
||||||
|
<div>
|
||||||
|
<Dialog
|
||||||
|
open={props.open}
|
||||||
|
onClose={() => handleOnClose(undefined)} >
|
||||||
|
<DialogTitle><FormattedMessage id="action.delete-title" defaultMessage="Delete" /></DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Alert severity="warning">
|
||||||
|
<AlertTitle>Delete '{map?.name}'</AlertTitle>
|
||||||
|
<FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
|
||||||
|
</Alert>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => handleOnClose('accept')} variant="outlined" color="primary">
|
||||||
|
<FormattedMessage id="action.delete-button" defaultMessage="Delete" />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => handleOnClose(undefined)} variant="outlined" color="secondary" autoFocus>
|
||||||
|
<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default DeleteDialog;
|
36
packages/webapp/src/components/maps-page/DialogCommon.ts
Normal file
36
packages/webapp/src/components/maps-page/DialogCommon.ts
Normal file
@ -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<unknown, ErrorInfo, MapInfo[]>('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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
112
packages/webapp/src/components/maps-page/RenameDialog.tsx
Normal file
112
packages/webapp/src/components/maps-page/RenameDialog.tsx
Normal file
@ -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<RenameModel>(defaultModel);
|
||||||
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
const { mapId, open } = props;
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const mutation = useMutation<RenameModel, ErrorInfo, RenameModel>((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<HTMLFormElement>): void => {
|
||||||
|
event.preventDefault();
|
||||||
|
mutation.mutate(model);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): 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 (
|
||||||
|
<div>
|
||||||
|
<Dialog
|
||||||
|
open={props.open}
|
||||||
|
onClose={() => handleOnClose()} >
|
||||||
|
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
||||||
|
<DialogTitle>
|
||||||
|
<FormattedMessage id="action.rename-title" defaultMessage="Rename" />
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage id="action.rename-description" defaultMessage="Please, update the name and description for your mindmap." />
|
||||||
|
</DialogContentText>
|
||||||
|
|
||||||
|
{Boolean(error?.msg) ? <Alert severity="error" variant="filled" hidden={!Boolean(error?.msg)}>{error?.msg}</Alert> : null}
|
||||||
|
<FormControl margin="normal" required fullWidth>
|
||||||
|
<TextField name="name" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
|
||||||
|
value={model.name} onChange={handleOnChange}
|
||||||
|
error={Boolean(error?.fields?.get('name'))} helperText={error?.fields?.get('name')}
|
||||||
|
variant="filled" required={true} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl margin="normal" required fullWidth>
|
||||||
|
<TextField name="description" label={intl.formatMessage({ id: "action.rename-description-placeholder", defaultMessage: "Description" })} value={model.description} onChange={handleOnChange} variant="filled" />
|
||||||
|
</FormControl>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<Button color="primary" variant="outlined" type="submit">
|
||||||
|
<FormattedMessage id="action.rename-button" defaultMessage="Rename" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button color="secondary" variant="outlined" autoFocus onClick={handleOnClose}>
|
||||||
|
<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RenameDialog;
|
@ -236,10 +236,9 @@ const EnhancedTable = () => {
|
|||||||
type ActiveDialog = {
|
type ActiveDialog = {
|
||||||
actionType: DialogType;
|
actionType: DialogType;
|
||||||
mapId: number
|
mapId: number
|
||||||
|
|
||||||
};
|
};
|
||||||
const [activeDialog, setActiveDialog] = React.useState<ActiveDialog | undefined>(undefined);
|
|
||||||
|
|
||||||
|
const [activeDialog, setActiveDialog] = React.useState<ActiveDialog | undefined>(undefined);
|
||||||
const handleRequestSort = (event: React.MouseEvent<unknown>, property: keyof MapInfo) => {
|
const handleRequestSort = (event: React.MouseEvent<unknown>, property: keyof MapInfo) => {
|
||||||
const isAsc = orderBy === property && order === 'asc';
|
const isAsc = orderBy === property && order === 'asc';
|
||||||
setOrder(isAsc ? 'desc' : 'asc');
|
setOrder(isAsc ? 'desc' : 'asc');
|
||||||
|
@ -76,13 +76,28 @@ class RestService implements Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
|
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
|
||||||
|
|
||||||
|
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<string, string> = new Map<string, string>();
|
const fieldErrors: Map<string, string> = new Map<string, string>();
|
||||||
fieldErrors.set('name', 'name already exists ')
|
fieldErrors.set('name', 'name already exists ')
|
||||||
|
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
msg: 'Map already exists ...' + basicInfo.name,
|
msg: 'Map already exists ...' + basicInfo.name,
|
||||||
fields: fieldErrors
|
fields: fieldErrors
|
||||||
});
|
|
||||||
|
})
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMap(id: number): Promise<void> {
|
deleteMap(id: number): Promise<void> {
|
||||||
|
Loading…
Reference in New Issue
Block a user