Compleate create new map.

This commit is contained in:
Paulo Gustavo Veiga 2021-02-04 10:01:35 -08:00
parent a6951b850e
commit 56c843970b
8 changed files with 77 additions and 65 deletions

View File

@ -9,7 +9,7 @@ export type NewUser = {
export type MapInfo = { export type MapInfo = {
id: number; id: number;
starred: boolean; starred: boolean;
name: string; title: string;
labels: string[]; labels: string[];
creator: string; creator: string;
modified: string; modified: string;
@ -18,7 +18,7 @@ export type MapInfo = {
} }
export type BasicMapInfo = { export type BasicMapInfo = {
name: string; title: string;
description?: string; description?: string;
} }
@ -77,15 +77,15 @@ export const parseResponseOnError = (response: any): ErrorInfo => {
} }
interface Client { interface Client {
createMap(rest: { name: string; description?: string | undefined }) createMap(map: BasicMapInfo): Promise<number>;
deleteLabel(label: string): Promise<unknown>; deleteLabel(label: string): Promise<void>;
registerNewUser(user: NewUser): Promise<void>; registerNewUser(user: NewUser): Promise<void>;
resetPassword(email: string): Promise<void>; resetPassword(email: string): Promise<void>;
fetchAllMaps(): Promise<MapInfo[]>; fetchAllMaps(): Promise<MapInfo[]>;
fetchLabels(): Promise<string[]>; fetchLabels(): Promise<string[]>;
deleteMap(id: number): Promise<void>; deleteMap(id: number): Promise<void>;
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>; renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<void>; duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number>;
loadMapInfo(id: number): Promise<BasicMapInfo>; loadMapInfo(id: number): Promise<BasicMapInfo>;
changeStarred(id: number): Promise<void>; changeStarred(id: number): Promise<void>;

View File

@ -1,5 +1,4 @@
import Client, { BasicMapInfo, ErrorInfo, MapInfo, NewUser, parseResponseOnError } from '..'; import Client, { BasicMapInfo, MapInfo, NewUser } from '..';
import axios from "axios";
class MockClient implements Client { class MockClient implements Client {
private maps: MapInfo[] = []; private maps: MapInfo[] = [];
@ -11,14 +10,14 @@ class MockClient implements Client {
function createMapInfo( function createMapInfo(
id: number, id: number,
starred: boolean, starred: boolean,
name: string, title: string,
labels: string[], labels: string[],
creator: string, creator: string,
modified: string, modified: string,
description: string, description: string,
isPublic: boolean isPublic: boolean
): MapInfo { ): MapInfo {
return { id, name, labels, creator, modified, starred, description, isPublic }; return { id, title, labels, creator, modified, starred, description, isPublic };
} }
this.maps = [ this.maps = [
createMapInfo(1, true, "El Mapa", [""], "Paulo", "2008-06-02T00:00:00Z", "", true), createMapInfo(1, true, "El Mapa", [""], "Paulo", "2008-06-02T00:00:00Z", "", true),
@ -39,10 +38,10 @@ class MockClient implements Client {
} }
createMap(rest: { name: string; description?: string | undefined; }) { createMap(map: BasicMapInfo): Promise<number> {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
s
fetchLabels(): Promise<string[]> { fetchLabels(): Promise<string[]> {
console.log("Fetching labels from server") console.log("Fetching labels from server")
return Promise.resolve(this.labels); return Promise.resolve(this.labels);
@ -61,22 +60,22 @@ class MockClient implements Client {
} }
loadMapInfo(id: number): Promise<BasicMapInfo> { loadMapInfo(id: number): Promise<BasicMapInfo> {
return Promise.resolve({ name: 'My Map', description: 'My Description' }); return Promise.resolve({ title: 'My Map', description: 'My Description' });
} }
fetchLabes(id: number): Promise<BasicMapInfo> { fetchLabes(id: number): Promise<BasicMapInfo> {
return Promise.resolve({ name: 'My Map', description: 'My Description' }); return Promise.resolve({ title: 'My Map', description: 'My Description' });
} }
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; const exists = this.maps.find(m => m.title == basicInfo.title) != undefined;
if (!exists) { if (!exists) {
this.maps = this.maps.map(m => { this.maps = this.maps.map(m => {
const result = m; const result = m;
if (m.id == id) { if (m.id == id) {
result.description = basicInfo.description ? basicInfo.description : ''; result.description = basicInfo.description ? basicInfo.description : '';
result.name = basicInfo.name; result.title = basicInfo.title;
} }
return result; return result;
}) })
@ -86,22 +85,22 @@ class MockClient implements Client {
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.title,
fields: fieldErrors fields: fieldErrors
}) })
}; };
} }
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<void> { duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> {
const exists = this.maps.find(m => m.name == basicInfo.name) != undefined; const exists = this.maps.find(m => m.title == basicInfo.title) != undefined;
if (!exists) { if (!exists) {
const newMap: MapInfo = { const newMap: MapInfo = {
id: Math.random() * 1000, id: Math.random() * 1000,
description: String(basicInfo.description), description: String(basicInfo.description),
name: basicInfo.name, title: basicInfo.title,
starred: false, starred: false,
creator: "current user", creator: "current user",
labels: [], labels: [],
@ -109,13 +108,13 @@ class MockClient implements Client {
isPublic: false isPublic: false
}; };
this.maps.push(newMap); this.maps.push(newMap);
return Promise.resolve(); return Promise.resolve(newMap.id);
} else { } 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: 'Maps name must be unique:' + basicInfo.name, msg: 'Maps name must be unique:' + basicInfo.title,
fields: fieldErrors fields: fieldErrors
}) })

View File

@ -1,5 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import { ErrorInfo, MapInfo, NewUser, parseResponseOnError } from '..'; import { ErrorInfo, MapInfo, BasicMapInfo, NewUser, parseResponseOnError } from '..';
import MockClient from '../mock-client/'; import MockClient from '../mock-client/';
//@Remove inheritance once is it completed. //@Remove inheritance once is it completed.
@ -12,22 +12,38 @@ export default class RestClient extends MockClient {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
createMap(model: BasicMapInfo): Promise<number> {
const handler = (success: (mapId:number) => void, reject: (error: ErrorInfo) => void) => {
axios.post(this.baseUrl + `/c/restful/maps?title=${model.title}&description=${model.description ? model.description : ''}`,
null,
{ headers: { 'Content-Type': 'application/json' } }
).then(response => {
const mapId = response.headers.resourceid;
success(mapId);
}).catch(error => {
const response = error.response;
const errorInfo = parseResponseOnError(response);
reject(errorInfo);
});
}
return new Promise(handler);
}
fetchAllMaps(): Promise<MapInfo[]> { fetchAllMaps(): Promise<MapInfo[]> {
const handler = (success: (mapsInfo: MapInfo[]) => void, reject: (error: ErrorInfo) => void) => { const handler = (success: (mapsInfo: MapInfo[]) => void, reject: (error: ErrorInfo) => void) => {
axios.get( axios.get(
this.baseUrl + '/c/restful/maps/', this.baseUrl + '/c/restful/maps/',
{ headers: { 'Content-Type': 'application/json' } {
headers: { 'Content-Type': 'application/json' }
} }
).then(response => { ).then(response => {
console.log("Maps List Response=>")
console.log(response.data)
const data = response.data; const data = response.data;
const maps: MapInfo[] = (data.mindmapsInfo as any[]).map(m => { const maps: MapInfo[] = (data.mindmapsInfo as any[]).map(m => {
return { return {
id: m.id, id: m.id,
starred: Boolean(m.starred), starred: Boolean(m.starred),
name: m.title, title: m.title,
labels: [], labels: [],
creator: m.creator, creator: m.creator,
modified: m.lastModificationTime, modified: m.lastModificationTime,

View File

@ -1,17 +1,17 @@
import React, { useEffect } from "react"; import React from 'react';
import { useIntl } from "react-intl"; import { useIntl } from 'react-intl';
import { useMutation, useQueryClient } from "react-query"; import { useMutation } from 'react-query';
import { useSelector } from "react-redux"; import { useSelector } from 'react-redux';
import { FormControl } from "@material-ui/core"; import { FormControl } from '@material-ui/core';
import Client, { BasicMapInfo, ErrorInfo } from "../../../../client";
import Client, { BasicMapInfo, ErrorInfo } from '../../../../client';
import { activeInstance } from '../../../../reducers/serviceSlice'; import { activeInstance } from '../../../../reducers/serviceSlice';
import Input from "../../../form/input"; import Input from '../../../form/input';
import { DialogProps, fetchMapById, handleOnMutationSuccess } from ".."; import BaseDialog from '../action-dialog';
import BaseDialog from "../action-dialog";
export type CreateModel = { export type CreateModel = {
name: string; title: string;
description?: string; description?: string;
} }
@ -20,23 +20,20 @@ export type CreateProps = {
onClose: () => void onClose: () => void
} }
const defaultModel: CreateModel = { name: '', description: ''}; const defaultModel: CreateModel = { title: '', description: '' };
const CreateDialog = (props: CreateProps) => { const CreateDialog = (props: CreateProps) => {
const client: Client = useSelector(activeInstance); const client: Client = useSelector(activeInstance);
const [model, setModel] = React.useState<CreateModel>(defaultModel); const [model, setModel] = React.useState<CreateModel>(defaultModel);
const [error, setError] = React.useState<ErrorInfo>(); const [error, setError] = React.useState<ErrorInfo>();
const { open } = props; const { open } = props;
const intl = useIntl(); const intl = useIntl();
const queryClient = useQueryClient();
const mutation = useMutation<CreateModel, ErrorInfo, CreateModel>((model: CreateModel) => { const mutation = useMutation<number, ErrorInfo, CreateModel>((model: CreateModel) => {
const { ...rest } = model; return client.createMap(model);
return client.createMap(rest).then(() => model);
}, },
{ {
onSuccess: () => { onSuccess: (mapId: number) => {
handleOnMutationSuccess(props.onClose, queryClient); window.location.href = `/c/maps/${mapId}/edit`;
}, },
onError: (error) => { onError: (error) => {
setError(error); setError(error);
@ -71,8 +68,8 @@ const CreateDialog = (props: CreateProps) => {
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}> submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}>
<FormControl fullWidth={true}> <FormControl fullWidth={true}>
<Input name="name" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }} <Input name="title" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }}
value={model.name} onChange={handleOnChange} error={error} fullWidth={true} /> value={model.title} onChange={handleOnChange} error={error} fullWidth={true} />
<Input name="description" type="text" label={{ id: "action.rename-description-placeholder", defaultMessage: "Description" }} <Input name="description" type="text" label={{ id: "action.rename-description-placeholder", defaultMessage: "Description" }}
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} /> value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />

View File

@ -38,7 +38,7 @@ const DeleteDialog = (props: DialogProps) => {
title={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })} title={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })}
submitButton={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })} > submitButton={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })} >
<Alert severity="warning"> <Alert severity="warning">
<AlertTitle>Delete '{map?.name}'</AlertTitle> <AlertTitle>Delete '{map?.title}'</AlertTitle>
<FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." /> <FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
</Alert> </Alert>
</BaseDialog> </BaseDialog>

View File

@ -12,11 +12,11 @@ import BaseDialog from "../action-dialog";
export type DuplicateModel = { export type DuplicateModel = {
id: number; id: number;
name: string; title: string;
description?: string; description?: string;
} }
const defaultModel: DuplicateModel = { name: '', description: '', id: -1 }; const defaultModel: DuplicateModel = { title: '', description: '', id: -1 };
const DuplicateDialog = (props: DialogProps) => { const DuplicateDialog = (props: DialogProps) => {
const service: Client = useSelector(activeInstance); const service: Client = useSelector(activeInstance);
const [model, setModel] = React.useState<DuplicateModel>(defaultModel); const [model, setModel] = React.useState<DuplicateModel>(defaultModel);
@ -26,9 +26,9 @@ const DuplicateDialog = (props: DialogProps) => {
const intl = useIntl(); const intl = useIntl();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const mutation = useMutation<DuplicateModel, ErrorInfo, DuplicateModel>((model: DuplicateModel) => { const mutation = useMutation<number, ErrorInfo, DuplicateModel>((model: DuplicateModel) => {
const { id, ...rest } = model; const { id, ...rest } = model;
return service.duplicateMap(id, rest).then(() => model); return service.duplicateMap(id, rest);
}, },
{ {
onSuccess: () => { onSuccess: () => {
@ -77,8 +77,8 @@ const DuplicateDialog = (props: DialogProps) => {
submitButton={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}> submitButton={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}>
<FormControl fullWidth={true}> <FormControl fullWidth={true}>
<Input name="name" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }} <Input name="title" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }}
value={model.name} onChange={handleOnChange} error={error} fullWidth={true} /> value={model.title} onChange={handleOnChange} error={error} fullWidth={true} />
<Input name="description" type="text" label={{ id: "action.rename-description-placeholder", defaultMessage: "Description" }} <Input name="description" type="text" label={{ id: "action.rename-description-placeholder", defaultMessage: "Description" }}
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} /> value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />

View File

@ -11,11 +11,11 @@ import BaseDialog from "../action-dialog";
export type RenameModel = { export type RenameModel = {
id: number; id: number;
name: string; title: string;
description?: string; description?: string;
} }
const defaultModel: RenameModel = { name: '', description: '', id: -1 }; const defaultModel: RenameModel = { title: '', description: '', id: -1 };
const RenameDialog = (props: DialogProps) => { const RenameDialog = (props: DialogProps) => {
const service: Client = useSelector(activeInstance); const service: Client = useSelector(activeInstance);
const [model, setModel] = React.useState<RenameModel>(defaultModel); const [model, setModel] = React.useState<RenameModel>(defaultModel);
@ -76,8 +76,8 @@ const RenameDialog = (props: DialogProps) => {
submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}> submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}>
<FormControl fullWidth={true}> <FormControl fullWidth={true}>
<Input name="name" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }} <Input name="title" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }}
value={model.name} onChange={handleOnChange} error={error} fullWidth={true} /> value={model.title} onChange={handleOnChange} error={error} fullWidth={true} />
<Input name="description" type="text" label={{ id: "action.rename-description-placeholder", defaultMessage: "Description" }} <Input name="description" type="text" label={{ id: "action.rename-description-placeholder", defaultMessage: "Description" }}
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} /> value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />

View File

@ -71,7 +71,7 @@ interface HeadCell {
} }
const headCells: HeadCell[] = [ const headCells: HeadCell[] = [
{ id: 'name', numeric: false, label: 'Name' }, { id: 'title', numeric: false, label: 'Name' },
{ id: 'labels', numeric: false }, { id: 'labels', numeric: false },
{ id: 'creator', numeric: false, label: 'Creator', style: { width: '60px' } }, { id: 'creator', numeric: false, label: 'Creator', style: { width: '60px' } },
{ id: 'modified', numeric: true, label: 'Modified', style: { width: '30px' } } { id: 'modified', numeric: true, label: 'Modified', style: { width: '30px' } }
@ -178,7 +178,7 @@ const mapsFilter = (filter: Filter, search: string): ((mapInfo: MapInfo) => bool
// Does it match search filter criteria... // Does it match search filter criteria...
if (search && result) { if (search && result) {
result = mapInfo.name.toLowerCase().indexOf(search.toLowerCase()) != -1; result = mapInfo.title.toLowerCase().indexOf(search.toLowerCase()) != -1;
} }
return result; return result;
@ -434,8 +434,8 @@ export const MapsList = (props: MapsListProps) => {
<TableCell className={classes.bodyCell}> <TableCell className={classes.bodyCell}>
<Tooltip title="Open for edition" placement="bottom-start"> <Tooltip title="Open for edition" placement="bottom-start">
<Link href={`c/maps/${row.id}/edit`} color="textPrimary" underline="always"> <Link href={`/c/maps/${row.id}/edit`} color="textPrimary" underline="always" onClick={(e)=>e.stopPropagation()}>
{row.name} {row.title}
</Link> </Link>
</Tooltip> </Tooltip>
</TableCell> </TableCell>