Add create dialog

This commit is contained in:
Paulo Gustavo Veiga 2021-02-02 10:54:37 -08:00
parent 67fec75d91
commit 481c1fe619
10 changed files with 199 additions and 68 deletions

View File

@ -56,6 +56,15 @@
"common.wait": {
"defaultMessage": "Please wait ..."
},
"create.button": {
"defaultMessage": "Create"
},
"create.description": {
"defaultMessage": "Please, fill the new map name and description."
},
"create.title": {
"defaultMessage": "Create a new mindmap"
},
"duplicate.title": {
"defaultMessage": "Duplicate"
},

View File

@ -33,6 +33,7 @@ export type ErrorInfo = {
}
interface Client {
createMap(rest: { name: string; description?: string | undefined })
deleteLabel(label: string): Promise<unknown>;
registerNewUser(user: NewUser): Promise<void>;
resetPassword(email: string): Promise<void>;

View File

@ -43,7 +43,10 @@ class MockClient implements Client {
}
createMap(rest: { name: string; description?: string | undefined; }) {
throw new Error("Method not implemented.");
}
s
fetchLabels(): Promise<string[]> {
console.log("Fetching labels from server")
return Promise.resolve(this.labels);

View File

@ -113,6 +113,24 @@
"value": "Please wait ..."
}
],
"create.button": [
{
"type": 0,
"value": "Create"
}
],
"create.description": [
{
"type": 0,
"value": "Please, fill the new map name and description."
}
],
"create.title": [
{
"type": 0,
"value": "Create a new mindmap"
}
],
"duplicate.title": [
{
"type": 0,

View File

@ -12,7 +12,7 @@ import ShareOutlinedIcon from '@material-ui/icons/ShareOutlined';
import { FormattedMessage } from 'react-intl';
import { LabelOutlined } from '@material-ui/icons';
export type ActionType = 'open' | 'share' | 'delete' | 'info' | 'duplicate' | 'export' | 'label' | 'rename' | 'print' | 'info' | 'publish' | 'history' | undefined;
export type ActionType = 'open' | 'share' | 'delete' | 'info' | 'create'| 'duplicate' | 'export' | 'label' | 'rename' | 'print' | 'info' | 'publish' | 'history' | undefined;
interface ActionProps {
onClose: (action: ActionType) => void;

View File

@ -68,7 +68,7 @@ const BaseDialog = (props: DialogProps) => {
variant="contained"
type="submit"
disableElevation={true}>
{props.title}
{props.submitButton}
</Button>) : null
}
</StyledDialogActions>

View File

@ -0,0 +1,85 @@
import React, { useEffect } from "react";
import { useIntl } from "react-intl";
import { useMutation, useQueryClient } from "react-query";
import { useSelector } from "react-redux";
import { FormControl } from "@material-ui/core";
import Client, { BasicMapInfo, ErrorInfo } from "../../../../client";
import { activeInstance } from '../../../../reducers/serviceSlice';
import Input from "../../../form/input";
import { DialogProps, fetchMapById, handleOnMutationSuccess } from "..";
import BaseDialog from "../action-dialog";
export type CreateModel = {
name: string;
description?: string;
}
export type CreateProps = {
open: boolean,
onClose: () => void
}
const defaultModel: CreateModel = { name: '', description: ''};
const CreateDialog = (props: CreateProps) => {
const client: Client = useSelector(activeInstance);
const [model, setModel] = React.useState<CreateModel>(defaultModel);
const [error, setError] = React.useState<ErrorInfo>();
const { open } = props;
const intl = useIntl();
const queryClient = useQueryClient();
const mutation = useMutation<CreateModel, ErrorInfo, CreateModel>((model: CreateModel) => {
const { ...rest } = model;
return client.createMap(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<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 });
}
return (
<div>
<BaseDialog open={open} onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
title={intl.formatMessage({ id: 'create.title', defaultMessage: 'Create a new mindmap' })}
description={intl.formatMessage({ id: 'create.description', defaultMessage: 'Please, fill the new map name and description.' })}
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}>
<FormControl fullWidth={true}>
<Input name="name" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }}
value={model.name} onChange={handleOnChange} error={error} fullWidth={true} />
<Input name="description" type="text" label={{ id: "action.rename-description-placeholder", defaultMessage: "Description" }}
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />
</FormControl>
</BaseDialog>
</div>
);
}
export default CreateDialog;

View File

@ -10,6 +10,7 @@ import { activeInstance } from '../../../reducers/serviceSlice';
import DuplicateDialog from './duplicate';
import { useHistory } from 'react-router-dom';
import InfoDialog from './info';
import CreateDialog from './create';
export type BasicMapInfo = {
@ -43,6 +44,7 @@ const ActionDispatcher = (props: ActionDialogProps) => {
return (
<span>
<CreateDialog open={action === 'create'} onClose={handleOnClose}/>
<DeleteDialog open={action === 'delete'} onClose={handleOnClose} mapId={mapId} />
<RenameDialog open={action === 'rename'} onClose={handleOnClose} mapId={mapId} />
<DuplicateDialog open={action === 'duplicate'} onClose={handleOnClose} mapId={mapId} />

View File

@ -16,11 +16,12 @@ import { useQuery, useMutation, useQueryClient } from 'react-query';
import { activeInstance } from '../../reducers/serviceSlice';
import { useSelector } from 'react-redux';
import Client from '../../client';
import ActionDispatcher from './action-dispatcher';
import { ActionType } from './action-chooser';
const logoIcon = require('../../images/logo-small.svg');
const poweredByIcon = require('../../images/pwrdby-white.svg');
export type Filter = GenericFilter | LabelFilter;
export interface GenericFilter {
@ -42,6 +43,7 @@ const MapsPage = () => {
const [filter, setFilter] = React.useState<Filter>({ type: 'all' });
const client: Client = useSelector(activeInstance);
const queryClient = useQueryClient();
const [activeDialog, setActiveDialog] = React.useState<ActionType | undefined>(undefined);
useEffect(() => {
@ -103,8 +105,14 @@ const MapsPage = () => {
<Toolbar>
<Tooltip title="Create a New Map">
<Button color="primary" size="medium" variant="contained" type="button"
disableElevation={true} startIcon={<AddCircleTwoTone />} className={classes.newMapButton}>
<Button color="primary"
size="medium"
variant="contained"
type="button"
disableElevation={true}
startIcon={<AddCircleTwoTone />}
className={classes.newMapButton}
onClick={e => setActiveDialog('create')}>
<FormattedMessage id="action.new" defaultMessage="New Map" />
</Button>
</Tooltip>
@ -115,6 +123,7 @@ const MapsPage = () => {
<FormattedMessage id="action.import" defaultMessage="Import" />
</Button>
</Tooltip>
<ActionDispatcher action={activeDialog} onClose={() => setActiveDialog(undefined)} mapId={-1} />
<div className={classes.rightButtonGroup}>
<HelpToobarButton />

View File

@ -392,75 +392,79 @@ export const MapsList = (props: MapsListProps) => {
/>
<TableBody>
{isLoading ? (<TableRow></TableRow>) : stableSort(mapsInfo, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row: MapInfo) => {
const isItemSelected = isSelected(row.id);
const labelId = row.id;
{isLoading ? (
<TableRow><TableCell rowSpan={6}>Loading ...</TableCell></TableRow>) :
(mapsInfo.length == 0 ?
(<TableRow><TableCell rowSpan={6}>No matching records found</TableCell></TableRow>) :
stableSort(mapsInfo, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row: MapInfo) => {
const isItemSelected = isSelected(row.id);
const labelId = row.id;
return (
<TableRow
hover
onClick={(event: any) => handleRowClick(event, row.id)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.id}
selected={isItemSelected}
style={{ border: "0" }}
>
<TableCell
padding="checkbox"
className={classes.bodyCell}>
<Checkbox
checked={isItemSelected}
inputProps={{ 'aria-labelledby': String(labelId) }}
size="small" />
</TableCell>
return (
<TableRow
hover
onClick={(event: any) => handleRowClick(event, row.id)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.id}
selected={isItemSelected}
style={{ border: "0" }}
>
<TableCell
padding="checkbox"
className={classes.bodyCell}>
<Checkbox
checked={isItemSelected}
inputProps={{ 'aria-labelledby': String(labelId) }}
size="small" />
</TableCell>
<TableCell
padding="checkbox"
className={classes.bodyCell}>
<Tooltip title="Starred">
<IconButton aria-label="Starred" size="small" onClick={(e) => handleStarred(e, row.id)}>
<StarRateRoundedIcon color="action" style={{ color: row.starred ? 'yellow' : 'gray' }} />
</IconButton>
</Tooltip>
</TableCell>
<TableCell
padding="checkbox"
className={classes.bodyCell}>
<Tooltip title="Starred">
<IconButton aria-label="Starred" size="small" onClick={(e) => handleStarred(e, row.id)}>
<StarRateRoundedIcon color="action" style={{ color: row.starred ? 'yellow' : 'gray' }} />
</IconButton>
</Tooltip>
</TableCell>
<TableCell className={classes.bodyCell}>
<Tooltip title="Open for edition" placement="bottom-start">
<Link href={`c/maps/${row.id}/edit`} color="textPrimary" underline="always">
{row.name}
</Link>
</Tooltip>
</TableCell>
<TableCell className={classes.bodyCell}>
<Tooltip title="Open for edition" placement="bottom-start">
<Link href={`c/maps/${row.id}/edit`} color="textPrimary" underline="always">
{row.name}
</Link>
</Tooltip>
</TableCell>
<TableCell className={classes.bodyCell}>
{row.labels}
</TableCell>
<TableCell className={classes.bodyCell}>
{row.labels}
</TableCell>
<TableCell className={classes.bodyCell}>
{row.creator}
</TableCell>
<TableCell className={classes.bodyCell}>
{row.creator}
</TableCell>
<TableCell className={classes.bodyCell}>
<Tooltip title={moment(row.modified).format("lll")} placement="bottom-start">
<span>{moment(row.modified).fromNow()}</span>
</Tooltip>
</TableCell>
<TableCell className={classes.bodyCell}>
<Tooltip title={moment(row.modified).format("lll")} placement="bottom-start">
<span>{moment(row.modified).fromNow()}</span>
</Tooltip>
</TableCell>
<TableCell className={classes.bodyCell}>
<Tooltip title="Others">
<IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}>
<MoreHorizIcon color="action" />
</IconButton>
</Tooltip>
<ActionChooser anchor={activeRowAction?.el} onClose={handleActionMenuClose} />
</TableCell>
</TableRow>
);
})}
<TableCell className={classes.bodyCell}>
<Tooltip title="Others">
<IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}>
<MoreHorizIcon color="action" />
</IconButton>
</Tooltip>
<ActionChooser anchor={activeRowAction?.el} onClose={handleActionMenuClose} />
</TableCell>
</TableRow>
);
}))}
</TableBody>
</Table>
</TableContainer>