mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-12-23 12:03:49 +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 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<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';
|
||||
|
||||
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 = {
|
||||
actionType: DialogType;
|
||||
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 isAsc = orderBy === property && order === 'asc';
|
||||
setOrder(isAsc ? 'desc' : 'asc');
|
||||
|
@ -76,13 +76,28 @@ class RestService implements Service {
|
||||
}
|
||||
|
||||
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
|
||||
const fieldErrors: Map<string, string> = new Map<string, string>();
|
||||
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<string, string> = new Map<string, string>();
|
||||
fieldErrors.set('name', 'name already exists ')
|
||||
|
||||
return Promise.reject({
|
||||
msg: 'Map already exists ...' + basicInfo.name,
|
||||
fields: fieldErrors
|
||||
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
deleteMap(id: number): Promise<void> {
|
||||
|
Loading…
Reference in New Issue
Block a user