Complete history channel

This commit is contained in:
Paulo Gustavo Veiga 2021-02-05 13:15:36 -08:00
parent 87a12c25b1
commit 85d44786c7
16 changed files with 317 additions and 38 deletions

View File

@ -23,15 +23,18 @@
"action.history": { "action.history": {
"defaultMessage": "History" "defaultMessage": "History"
}, },
"action.history-description": {
"defaultMessage": "List of changes introduced in the last 90 days."
},
"action.history-title": {
"defaultMessage": "Version history"
},
"action.import": { "action.import": {
"defaultMessage": "Import" "defaultMessage": "Import"
}, },
"action.info": { "action.info": {
"defaultMessage": "Info" "defaultMessage": "Info"
}, },
"action.info-title": {
"defaultMessage": "Info"
},
"action.label": { "action.label": {
"defaultMessage": "Add Label" "defaultMessage": "Add Label"
}, },
@ -50,6 +53,12 @@
"action.rename": { "action.rename": {
"defaultMessage": "Rename" "defaultMessage": "Rename"
}, },
"action.rename-description-placeholder": {
"defaultMessage": "Description"
},
"action.rename-name-placeholder": {
"defaultMessage": "Name"
},
"action.share": { "action.share": {
"defaultMessage": "Share" "defaultMessage": "Share"
}, },
@ -68,6 +77,12 @@
"duplicate.title": { "duplicate.title": {
"defaultMessage": "Duplicate" "defaultMessage": "Duplicate"
}, },
"expired.description": {
"defaultMessage": "Your current session has expired. Please, sign in and try again."
},
"expired.title": {
"defaultMessage": "Your session has expired"
},
"footer.aboutus": { "footer.aboutus": {
"defaultMessage": "About Us" "defaultMessage": "About Us"
}, },
@ -116,6 +131,9 @@
"login.desc": { "login.desc": {
"defaultMessage": "Log into your account" "defaultMessage": "Log into your account"
}, },
"login.email": {
"defaultMessage": "Email"
},
"login.error": { "login.error": {
"defaultMessage": "The email address or password you entered is not valid." "defaultMessage": "The email address or password you entered is not valid."
}, },
@ -126,6 +144,9 @@
"defaultMessage": "Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL", "defaultMessage": "Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL",
"description": "Missing production database configured" "description": "Missing production database configured"
}, },
"login.password": {
"defaultMessage": "Password"
},
"login.remberme": { "login.remberme": {
"defaultMessage": "Remember me" "defaultMessage": "Remember me"
}, },
@ -141,6 +162,33 @@
"login.userinactive": { "login.userinactive": {
"defaultMessage": "Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!." "defaultMessage": "Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!."
}, },
"map.creator": {
"defaultMessage": "Creator"
},
"map.last-update": {
"defaultMessage": "Last Update"
},
"map.more-actions": {
"defaultMessage": "More Actions"
},
"map.name": {
"defaultMessage": "Name"
},
"maps.empty-result": {
"defaultMessage": "No matching record found with the current filter criteria."
},
"maps.modified": {
"defaultMessage": "Modified"
},
"maps.modified-by": {
"defaultMessage": "Modified By"
},
"maps.revert": {
"defaultMessage": "Revert"
},
"maps.view": {
"defaultMessage": "View"
},
"menu.account": { "menu.account": {
"defaultMessage": "Account" "defaultMessage": "Account"
}, },

View File

@ -25,7 +25,7 @@ export type MapInfo = {
role: 'owner' | 'editor' | 'viewer' role: 'owner' | 'editor' | 'viewer'
} }
export type HistoryChange = { export type ChangeHistory = {
id: number; id: number;
creator: string; creator: string;
modified: string; modified: string;
@ -62,6 +62,9 @@ interface Client {
registerNewUser(user: NewUser): Promise<void>; registerNewUser(user: NewUser): Promise<void>;
resetPassword(email: string): Promise<void>; resetPassword(email: string): Promise<void>;
fetchHistory(id:number):Promise<ChangeHistory[]>;
revertHistory(id:number,cid:number): Promise<void>
} }

View File

@ -1,4 +1,4 @@
import Client, { BasicMapInfo, Label, MapInfo, NewUser } from '..'; import Client, { BasicMapInfo, ChangeHistory, Label, MapInfo, NewUser } from '..';
class MockClient implements Client { class MockClient implements Client {
private maps: MapInfo[] = []; private maps: MapInfo[] = [];
@ -41,6 +41,9 @@ class MockClient implements Client {
]; ];
} }
revertHistory(id: number, cid: number): Promise<void> {
return Promise.resolve();
}
createMap(map: BasicMapInfo): Promise<number> { createMap(map: BasicMapInfo): Promise<number> {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
@ -91,6 +94,46 @@ class MockClient implements Client {
}) })
}; };
} }
fetchHistory(id: number): Promise<ChangeHistory[]> {
const result = [{
id: 1,
creator: 'Paulo',
modified: '2008-06-02T00:00:00Z'
},
{
id: 2,
creator: 'Paulo',
modified: '2008-06-02T00:00:00Z'
}
,
{
id: 3,
creator: 'Paulo',
modified: '2008-06-02T00:00:00Z'
},
{
id: 4,
creator: 'Paulo',
modified: '2008-06-02T00:00:00Z'
},
{
id: 5,
creator: 'Paulo',
modified: '2008-06-02T00:00:00Z'
},
{
id: 6,
creator: 'Paulo',
modified: '2008-06-02T00:00:00Z'
},
{
id: 7,
creator: 'Paulo',
modified: '2008-06-02T00:00:00Z'
}
]
return Promise.resolve(result);
}
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> { duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> {

View File

@ -1,6 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import Client, { ErrorInfo, MapInfo, BasicMapInfo, NewUser, Label } from '..'; import Client, { ErrorInfo, MapInfo, BasicMapInfo, NewUser, Label, ChangeHistory } from '..';
export default class RestClient implements Client { export default class RestClient implements Client {
private baseUrl: string; private baseUrl: string;
@ -11,6 +11,14 @@ export default class RestClient implements Client {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
this.sessionExpired = sessionExpired; this.sessionExpired = sessionExpired;
} }
revertHistory(id: number, cid: number): Promise<void> {
// '/c/restful/maps/${mindmapId}/history'
throw new Error('Method not implemented.');
}
fetchHistory(id: number): Promise<ChangeHistory[]> {
throw new Error('Method not implemented.');
}
fetchMapInfo(id: number): Promise<BasicMapInfo> { fetchMapInfo(id: number): Promise<BasicMapInfo> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
@ -29,7 +37,7 @@ export default class RestClient implements Client {
case 401: case 401:
case 302: case 302:
this.sessionExpired(); this.sessionExpired();
result = { msg: intl.formatMessage({ id: "expired.title", defaultMessage: "Your current session has expired. Please, sign in and try again." })} result = { msg: intl.formatMessage({ id: "expired.description", defaultMessage: "Your current session has expired. Please, sign in and try again." })}
break; break;
default: default:
if (data) { if (data) {

View File

@ -47,6 +47,18 @@
"value": "History" "value": "History"
} }
], ],
"action.history-description": [
{
"type": 0,
"value": "List of changes introduced in the last 90 days."
}
],
"action.history-title": [
{
"type": 0,
"value": "Version history"
}
],
"action.import": [ "action.import": [
{ {
"type": 0, "type": 0,
@ -59,12 +71,6 @@
"value": "Info" "value": "Info"
} }
], ],
"action.info-title": [
{
"type": 0,
"value": "Info"
}
],
"action.label": [ "action.label": [
{ {
"type": 0, "type": 0,
@ -101,6 +107,18 @@
"value": "Rename" "value": "Rename"
} }
], ],
"action.rename-description-placeholder": [
{
"type": 0,
"value": "Description"
}
],
"action.rename-name-placeholder": [
{
"type": 0,
"value": "Name"
}
],
"action.share": [ "action.share": [
{ {
"type": 0, "type": 0,
@ -137,6 +155,18 @@
"value": "Duplicate" "value": "Duplicate"
} }
], ],
"expired.description": [
{
"type": 0,
"value": "Your current session has expired. Please, sign in and try again."
}
],
"expired.title": [
{
"type": 0,
"value": "Your session has expired"
}
],
"footer.aboutus": [ "footer.aboutus": [
{ {
"type": 0, "type": 0,
@ -233,6 +263,12 @@
"value": "Log into your account" "value": "Log into your account"
} }
], ],
"login.email": [
{
"type": 0,
"value": "Email"
}
],
"login.error": [ "login.error": [
{ {
"type": 0, "type": 0,
@ -251,6 +287,12 @@
"value": "Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL" "value": "Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL"
} }
], ],
"login.password": [
{
"type": 0,
"value": "Password"
}
],
"login.remberme": [ "login.remberme": [
{ {
"type": 0, "type": 0,
@ -281,6 +323,60 @@
"value": "Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!." "value": "Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!."
} }
], ],
"map.creator": [
{
"type": 0,
"value": "Creator"
}
],
"map.last-update": [
{
"type": 0,
"value": "Last Update"
}
],
"map.more-actions": [
{
"type": 0,
"value": "More Actions"
}
],
"map.name": [
{
"type": 0,
"value": "Name"
}
],
"maps.empty-result": [
{
"type": 0,
"value": "No matching record found with the current filter criteria."
}
],
"maps.modified": [
{
"type": 0,
"value": "Modified"
}
],
"maps.modified-by": [
{
"type": 0,
"value": "Modified By"
}
],
"maps.revert": [
{
"type": 0,
"value": "Revert"
}
],
"maps.view": [
{
"type": 0,
"value": "View"
}
],
"menu.account": [ "menu.account": [
{ {
"type": 0, "type": 0,

View File

@ -49,7 +49,7 @@ const ForgotPassword = () => {
<GlobalError error={error} /> <GlobalError error={error} />
<form onSubmit={handleOnSubmit}> <form onSubmit={handleOnSubmit}>
<Input type="email" name="email" label={{ id: "forgot.email", defaultMessage: "Email" }} <Input type="email" name="email" label={intl.formatMessage({ id: "forgot.email", defaultMessage: "Email" })}
autoComplete="email" onChange={e => setEmail(e.target.value)} error={error}/> autoComplete="email" onChange={e => setEmail(e.target.value)} error={error}/>
<SubmitButton value={intl.formatMessage({ id: "forgot.register", defaultMessage: "Send recovery link" })} /> <SubmitButton value={intl.formatMessage({ id: "forgot.register", defaultMessage: "Send recovery link" })} />

View File

@ -7,7 +7,7 @@ type InputProps = {
name: string; name: string;
error?: ErrorInfo; error?: ErrorInfo;
onChange?: (event: ChangeEvent<HTMLInputElement>) => void; onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
label: MessageDescriptor; label: string;
required?: boolean; required?: boolean;
type: string; type: string;
value?: string value?: string
@ -27,7 +27,7 @@ const Input = (props: InputProps) => {
const fullWidth = props.fullWidth != undefined ? props.required : true; const fullWidth = props.fullWidth != undefined ? props.required : true;
return ( return (
<TextField name={name} type={props.type} label={intl.formatMessage(props.label)} <TextField name={name} type={props.type} label={props.label}
value={value} onChange={onChange} value={value} onChange={onChange}
error={Boolean(fieldError)} helperText={fieldError} error={Boolean(fieldError)} helperText={fieldError}
variant="outlined" required={required} fullWidth={fullWidth} margin="dense"/> variant="outlined" required={required} fullWidth={fullWidth} margin="dense"/>

View File

@ -67,8 +67,8 @@ const LoginPage = () => {
<FormControl> <FormControl>
<form action="/c/perform-login" method="POST" > <form action="/c/perform-login" method="POST" >
<Input name="username" type="email" label={{ id: "login.email", defaultMessage: "Email" }} required autoComplete="email" /> <Input name="username" type="email" label={intl.formatMessage({ id: "login.email", defaultMessage: "Email" })} required autoComplete="email" />
<Input name="password" type="password" label={{ id: "login.password", defaultMessage: "Password" }} required autoComplete="current-password" /> <Input name="password" type="password" label={intl.formatMessage({ id: "login.password", defaultMessage: "Password" })} required autoComplete="current-password" />
<div> <div>
<input name="_spring_security_login.remberme" id="staySignIn" type="checkbox" /> <input name="_spring_security_login.remberme" id="staySignIn" type="checkbox" />
<label htmlFor="staySignIn"><FormattedMessage id="login.remberme" defaultMessage="Remember me" /></label> <label htmlFor="staySignIn"><FormattedMessage id="login.remberme" defaultMessage="Remember me" /></label>

View File

@ -68,10 +68,10 @@ 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="title" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }} <Input name="title" type="text" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
value={model.title} 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={intl.formatMessage({ 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} />
</FormControl> </FormControl>
</BaseDialog> </BaseDialog>

View File

@ -77,10 +77,10 @@ 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="title" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }} <Input name="title" type="text" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
value={model.title} 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={intl.formatMessage({ 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} />
</FormControl> </FormControl>
</BaseDialog> </BaseDialog>

View File

@ -0,0 +1,75 @@
import React, { ErrorInfo } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { useSelector } from "react-redux";
import Client, { ChangeHistory } from "../../../../client";
import { activeInstance } from '../../../../redux/clientSlice';
import { DialogProps, fetchMapById, handleOnMutationSuccess } from "..";
import BaseDialog from "../base-dialog";
import { Link, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip } from "@material-ui/core";
import moment from "moment";
const HistoryDialog = (props: DialogProps) => {
const intl = useIntl();
const mapId = props.mapId;
const client: Client = useSelector(activeInstance);
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, ChangeHistory[]>('history', () => {
return client.fetchHistory(mapId);
});
const changeHistory: ChangeHistory[] = data ? data : [];
const handleOnClose = (): void => {
props.onClose();
};
const handleOnClick = (event,vid): void => {
event.preventDefault();
client.revertHistory(mapId,vid)
.then((mapId)=>{
handleOnClose();
})
};
return (
<div>
<BaseDialog
open={props.open} onClose={handleOnClose}
title={intl.formatMessage({ id: "action.history-title", defaultMessage: "Version history" })}
description={intl.formatMessage({ id: "action.history-description", defaultMessage: "List of changes introduced in the last 90 days." })} >
<TableContainer component={Paper} style={{ maxHeight: '200px' }}>
<Table size="small" stickyHeader>
<TableHead>
<TableRow>
<TableCell align="left"><FormattedMessage id="maps.modified-by" defaultMessage="Modified By" /></TableCell>
<TableCell align="left"><FormattedMessage id="maps.modified" defaultMessage="Modified" /></TableCell>
<TableCell align="left"></TableCell>
<TableCell align="left"></TableCell>
</TableRow>
</TableHead>
<TableBody>
{changeHistory.map((row) => (
<TableRow key={row.id}>
<TableCell align="left">{row.creator}</TableCell>
<TableCell align="left">
<Tooltip title={moment(row.modified).format("lll")} placement="bottom-start">
<span>{moment(row.modified).fromNow()}</span>
</Tooltip>
</TableCell>
<TableCell align="left"><Link href={`c/maps/${mapId}/${row.id}/view`} target="history"><FormattedMessage id="maps.view" defaultMessage="View" /></Link></TableCell>
<TableCell align="left"><Link href="#" onClick={(e)=>handleOnClick(e,row.id)}><FormattedMessage id="maps.revert" defaultMessage="Revert" /></Link></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</BaseDialog>
</div>
);
}
export default HistoryDialog;

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import RenameDialog from './rename'; import RenameDialog from './rename-dialog';
import DeleteDialog from './delete-dialog'; import DeleteDialog from './delete-dialog';
import { ActionType } from '../action-chooser'; import { ActionType } from '../action-chooser';
import { ErrorInfo, MapInfo } from '../../../client'; import { ErrorInfo, MapInfo } from '../../../client';
@ -10,6 +10,7 @@ import { activeInstance } from '../../../redux/clientSlice';
import DuplicateDialog from './duplicate-dialog'; import DuplicateDialog from './duplicate-dialog';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import CreateDialog from './create-dialog'; import CreateDialog from './create-dialog';
import HistoryDialog from './history-dialog';
export type BasicMapInfo = { export type BasicMapInfo = {
name: string; name: string;
@ -46,6 +47,7 @@ const ActionDispatcher = (props: ActionDialogProps) => {
<DeleteDialog open={action === 'delete'} onClose={handleOnClose} mapId={mapId} /> <DeleteDialog open={action === 'delete'} onClose={handleOnClose} mapId={mapId} />
<RenameDialog open={action === 'rename'} onClose={handleOnClose} mapId={mapId} /> <RenameDialog open={action === 'rename'} onClose={handleOnClose} mapId={mapId} />
<DuplicateDialog open={action === 'duplicate'} onClose={handleOnClose} mapId={mapId} /> <DuplicateDialog open={action === 'duplicate'} onClose={handleOnClose} mapId={mapId} />
<HistoryDialog open={action === 'history'} onClose={handleOnClose} mapId={mapId} />
</span > </span >
); );
} }

View File

@ -76,10 +76,10 @@ 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="title" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }} <Input name="title" type="text" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
value={model.title} 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={intl.formatMessage({ 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} />
</FormControl> </FormControl>
</BaseDialog> </BaseDialog>

View File

@ -259,7 +259,7 @@ const HandleClientStatus = () => {
<DialogContent> <DialogContent>
<Alert severity="error"> <Alert severity="error">
<AlertTitle><FormattedMessage id="expired.title" defaultMessage="Your current session has expired. Please, sign in and try again." /></AlertTitle> <AlertTitle><FormattedMessage id="expired.description" defaultMessage="Your current session has expired. Please, sign in and try again." /></AlertTitle>
</Alert> </Alert>
</DialogContent> </DialogContent>

View File

@ -28,7 +28,7 @@ import { Button, InputBase, Link } from '@material-ui/core';
import SearchIcon from '@material-ui/icons/Search'; import SearchIcon from '@material-ui/icons/Search';
import moment from 'moment' import moment from 'moment'
import { Filter, LabelFilter } from '..'; import { Filter, LabelFilter } from '..';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage, useIntl } from 'react-intl';
import { DeleteOutlined, LabelTwoTone } from '@material-ui/icons'; import { DeleteOutlined, LabelTwoTone } from '@material-ui/icons';
import Alert from '@material-ui/lab/Alert'; import Alert from '@material-ui/lab/Alert';
@ -71,12 +71,6 @@ interface HeadCell {
style?: CSSProperties; style?: CSSProperties;
} }
const headCells: HeadCell[] = [
{ id: 'title', numeric: false, label: 'Name' },
{ id: 'labels', numeric: false },
{ id: 'creator', numeric: false, label: 'Creator', style: { width: '60px' } },
{ id: 'modified', numeric: true, label: 'Last Update', style: { width: '30px' } }
];
interface EnhancedTableProps { interface EnhancedTableProps {
classes: ReturnType<typeof useStyles>; classes: ReturnType<typeof useStyles>;
@ -89,12 +83,21 @@ interface EnhancedTableProps {
} }
function EnhancedTableHead(props: EnhancedTableProps) { function EnhancedTableHead(props: EnhancedTableProps) {
const intl = useIntl();
const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props; const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props;
const createSortHandler = (property: keyof MapInfo) => (event: React.MouseEvent<unknown>) => { const createSortHandler = (property: keyof MapInfo) => (event: React.MouseEvent<unknown>) => {
onRequestSort(event, property); onRequestSort(event, property);
}; };
const headCells: HeadCell[] = [
{ id: 'title', numeric: false, label: intl.formatMessage({ id: 'map.name', defaultMessage: 'Name' }) },
{ id: 'labels', numeric: false },
{ id: 'creator', numeric: false, label: intl.formatMessage({ id: 'map.creator', defaultMessage: 'Creator' }), style: { width: '70px', whiteSpace: 'nowrap' } },
{ id: 'modified', numeric: true, label: intl.formatMessage({ id: 'map.last-update', defaultMessage: 'Last Update' }), style: { width: '70px', whiteSpace: 'nowrap' } }
];
return ( return (
<TableHead> <TableHead>
<TableRow> <TableRow>
@ -196,6 +199,7 @@ export const MapsList = (props: MapsListProps) => {
const [page, setPage] = React.useState(0); const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10); const [rowsPerPage, setRowsPerPage] = React.useState(10);
const client: Client = useSelector(activeInstance); const client: Client = useSelector(activeInstance);
const intl = useIntl();
useEffect(() => { useEffect(() => {
setSelected([]); setSelected([]);
@ -456,7 +460,7 @@ export const MapsList = (props: MapsListProps) => {
</TableCell> </TableCell>
<TableCell className={classes.bodyCell}> <TableCell className={classes.bodyCell}>
<Tooltip title="Others"> <Tooltip title={intl.formatMessage({ id: 'map.more-actions', defaultMessage: 'More Actions' })}>
<IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}> <IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}>
<MoreHorizIcon color="action" /> <MoreHorizIcon color="action" />
</IconButton> </IconButton>

View File

@ -71,16 +71,16 @@ const RegistrationForm = () => {
<form onSubmit={handleOnSubmit}> <form onSubmit={handleOnSubmit}>
<GlobalError error={error} /> <GlobalError error={error} />
<Input name="email" type="email" onChange={handleOnChange} label={{ id: "registration.email", defaultMessage: "Email" }} <Input name="email" type="email" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.email", defaultMessage: "Email" })}
autoComplete="email" error={error}/> autoComplete="email" error={error}/>
<Input name="firstname" type="text" onChange={handleOnChange} label={{ id: "registration.firstname", defaultMessage: "First Name" }} <Input name="firstname" type="text" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.firstname", defaultMessage: "First Name" })}
autoComplete="given-name" error={error}/> autoComplete="given-name" error={error}/>
<Input name="lastname" type="text" onChange={handleOnChange} label={{ id: "registration.lastname", defaultMessage: "Last Name" }} <Input name="lastname" type="text" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.lastname", defaultMessage: "Last Name" })}
autoComplete="family-name" error={error}/> autoComplete="family-name" error={error}/>
<Input name="password" type="password" onChange={handleOnChange} label={{ id: "registration.password", defaultMessage: "Password" }} <Input name="password" type="password" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.password", defaultMessage: "Password" })}
autoComplete="new-password" error={error}/> autoComplete="new-password" error={error}/>
<div style={{ width: '330px', padding: '5px 0px 5px 20px' }}> <div style={{ width: '330px', padding: '5px 0px 5px 20px' }}>