mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2025-02-16 21:08:15 +01:00
Initial version of maps list. WIP
This commit is contained in:
parent
3a049d203d
commit
5b2bc9390b
@ -45,6 +45,8 @@
|
|||||||
"webpack-dev-server": "^3.11.0"
|
"webpack-dev-server": "^3.11.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material-ui/core": "^4.11.2",
|
||||||
|
"@material-ui/icons": "^4.11.2",
|
||||||
"@types/react-google-recaptcha": "^2.1.0",
|
"@types/react-google-recaptcha": "^2.1.0",
|
||||||
"axios": "^0.21.0",
|
"axios": "^0.21.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
|
@ -7,6 +7,7 @@ import RegistrationSuccessPage from './components/registration-success-page';
|
|||||||
import ForgotPasswordSuccessPage from './components/forgot-password-success-page';
|
import ForgotPasswordSuccessPage from './components/forgot-password-success-page';
|
||||||
import RegistationPage from './components/registration-page';
|
import RegistationPage from './components/registration-page';
|
||||||
import LoginPage from './components/login-page';
|
import LoginPage from './components/login-page';
|
||||||
|
import MapsPage from './components/maps-page';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Route,
|
Route,
|
||||||
@ -70,6 +71,9 @@ const App = (props: AppProps) => {
|
|||||||
<ForgotPasswordPage service={service} />
|
<ForgotPasswordPage service={service} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/c/forgot-password-success" component={ForgotPasswordSuccessPage} />
|
<Route path="/c/forgot-password-success" component={ForgotPasswordSuccessPage} />
|
||||||
|
<Route path="/c/maps/">
|
||||||
|
<MapsPage service={service} />
|
||||||
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
|
@ -17,8 +17,8 @@ type ForgotPasswordProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ForgotPassword = (props: ServiceProps) => {
|
const ForgotPassword = (props: ServiceProps) => {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState('');
|
||||||
const [errorMsg, setErrorMsg] = useState("");
|
const [errorMsg, setErrorMsg] = useState('');
|
||||||
|
|
||||||
const [disableButton, setDisableButton] = useState(false);
|
const [disableButton, setDisableButton] = useState(false);
|
||||||
|
|
||||||
@ -33,8 +33,8 @@ const ForgotPassword = (props: ServiceProps) => {
|
|||||||
props.service.resetPassword(
|
props.service.resetPassword(
|
||||||
email,
|
email,
|
||||||
() => history.push("/c/forgot-password-success"),
|
() => history.push("/c/forgot-password-success"),
|
||||||
(msg) => {
|
(errorInfo) => {
|
||||||
setErrorMsg(msg);
|
setErrorMsg(errorInfo.msg ? errorInfo.msg : '');
|
||||||
setDisableButton(false);
|
setDisableButton(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||||||
import { StyleDiv } from './styled'
|
import { StyleDiv } from './styled'
|
||||||
|
|
||||||
type FormErrorDialogProps = {
|
type FormErrorDialogProps = {
|
||||||
message: string | null;
|
message?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FormErrorDialog = (props: FormErrorDialogProps) => {
|
const FormErrorDialog = (props: FormErrorDialogProps) => {
|
||||||
|
@ -30,7 +30,7 @@ const LoginError = () => {
|
|||||||
const errorCode = new URLSearchParams(window.location.search).get('login_error');
|
const errorCode = new URLSearchParams(window.location.search).get('login_error');
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
let msg = null;
|
let msg;
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
if (errorCode === "3") {
|
if (errorCode === "3") {
|
||||||
msg = intl.formatMessage({ id: "login.userinactive", defaultMessage: "Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!." });
|
msg = intl.formatMessage({ id: "login.userinactive", defaultMessage: "Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!." });
|
||||||
|
157
packages/webapp/src/components/maps-page/ActionDialog.tsx
Normal file
157
packages/webapp/src/components/maps-page/ActionDialog.tsx
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import React, { useEffect } 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 { BasicMapInfo, Service } from '../../services/Service';
|
||||||
|
import { FormControl, TextField } from '@material-ui/core';
|
||||||
|
|
||||||
|
type DialogProps = {
|
||||||
|
open: boolean,
|
||||||
|
mapId: number,
|
||||||
|
onClose: (reload: boolean) => void,
|
||||||
|
service: Service
|
||||||
|
}
|
||||||
|
|
||||||
|
function DeleteConfirmDialog(props: DialogProps) {
|
||||||
|
|
||||||
|
const handleOnClose = (action: 'accept' | undefined): void => {
|
||||||
|
let result = false;
|
||||||
|
if (action == 'accept') {
|
||||||
|
props.service.deleteMap(props.mapId);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
props.onClose(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog
|
||||||
|
open={props.open}
|
||||||
|
onClose={() => handleOnClose(undefined)} >
|
||||||
|
<DialogTitle><FormattedMessage id="action.delete-title" defaultMessage="Delete" /></DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
<FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => handleOnClose('accept')} color="primary">
|
||||||
|
<FormattedMessage id="action.delete-button" defaultMessage="Delete" />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => handleOnClose(undefined)} color="secondary" autoFocus>
|
||||||
|
<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RenameDialog(props: DialogProps) {
|
||||||
|
const [model, setModel] = React.useState<BasicMapInfo>({ name: '', description: '' });
|
||||||
|
const intl = useIntl();
|
||||||
|
const [nameErrorMsg, setNameErrorMsg] = React.useState<string|undefined>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.service.loadMapInfo(props.mapId)
|
||||||
|
.then((info: BasicMapInfo) => {
|
||||||
|
setModel(info);
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleOnClose = (): void => {
|
||||||
|
props.onClose(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
|
// Stop form submit ...
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Fire rest call ...
|
||||||
|
const mapId = props.mapId;
|
||||||
|
props.service.remameMap(mapId, model).
|
||||||
|
then(e => {
|
||||||
|
// Close the dialog ...
|
||||||
|
props.onClose(true);
|
||||||
|
}).catch(e => {
|
||||||
|
setNameErrorMsg("Error ....")
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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="Rename Desc" />
|
||||||
|
</DialogContentText>
|
||||||
|
|
||||||
|
<FormControl margin="normal" required fullWidth>
|
||||||
|
<TextField name="name" label={intl.formatMessage({ id: "action.name-lable", defaultMessage: "Name" })} value={model.name} variant="filled" onChange={handleOnChange} required={true} error={Boolean(nameErrorMsg)} helperText={nameErrorMsg} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl margin="normal" required fullWidth>
|
||||||
|
<TextField name="description" label={intl.formatMessage({ id: "action.description-lable", defaultMessage: "Description" })} value={model.description} onChange={handleOnChange} variant="filled" />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<Button color="primary" fullWidth type="submit">
|
||||||
|
<FormattedMessage id="action.rename-button" defaultMessage="Rename" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button color="secondary" autoFocus fullWidth 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 = {
|
||||||
|
action: DialogType | undefined,
|
||||||
|
mapId: number,
|
||||||
|
service: Service,
|
||||||
|
onClose: (reload: boolean) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionDialog = (props: ActionDialogProps) => {
|
||||||
|
|
||||||
|
const handleOnClose = (reload: boolean): void => {
|
||||||
|
props.onClose(reload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<DeleteConfirmDialog open={props.action === 'delete'} service={props.service} onClose={handleOnClose} mapId={props.mapId} />
|
||||||
|
<RenameDialog open={props.action === 'rename'} service={props.service} onClose={handleOnClose} mapId={props.mapId} />
|
||||||
|
|
||||||
|
</span >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionDialog;
|
76
packages/webapp/src/components/maps-page/MapActionMenu.tsx
Normal file
76
packages/webapp/src/components/maps-page/MapActionMenu.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { Divider, Menu, MenuItem } from '@material-ui/core';
|
||||||
|
import React from 'react';
|
||||||
|
import DescriptionOutlinedIcon from '@material-ui/icons/DescriptionOutlined';
|
||||||
|
import FileCopyOutlinedIcon from '@material-ui/icons/FileCopyOutlined';
|
||||||
|
import DeleteOutlinedIcon from '@material-ui/icons/DeleteOutlined';
|
||||||
|
import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined';
|
||||||
|
import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
|
||||||
|
import EditOutlinedIcon from '@material-ui/icons/EditOutlined';
|
||||||
|
import PublicOutlinedIcon from '@material-ui/icons/PublicOutlined';
|
||||||
|
import PrintOutlinedIcon from '@material-ui/icons/PrintOutlined';
|
||||||
|
import ShareOutlinedIcon from '@material-ui/icons/ShareOutlined';
|
||||||
|
import { StyledMenuItem } from './styled';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
export type ActionType = 'open' | 'share' | 'delete' | 'info' | 'duplicate' | 'export' | 'rename' | 'print' | 'info' | 'publish' | undefined;
|
||||||
|
|
||||||
|
interface MapActionProps {
|
||||||
|
onClose: (action: ActionType) => void;
|
||||||
|
anchor: undefined | HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MapActionDialog = (props: MapActionProps) => {
|
||||||
|
const { anchor, onClose } = props;
|
||||||
|
|
||||||
|
const handleOnClose = (action: ActionType): ((event: React.MouseEvent<HTMLLIElement>) => void) => {
|
||||||
|
return (event): void => {
|
||||||
|
event.stopPropagation();
|
||||||
|
onClose(action);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchor}
|
||||||
|
keepMounted
|
||||||
|
open={Boolean(anchor)}
|
||||||
|
onClose={handleOnClose(undefined)}
|
||||||
|
>
|
||||||
|
<StyledMenuItem onClick={handleOnClose('open')}>
|
||||||
|
<DescriptionOutlinedIcon /><FormattedMessage id="action.open" defaultMessage="Open" />
|
||||||
|
</StyledMenuItem>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<MenuItem onClick={handleOnClose('duplicate')}>
|
||||||
|
<FileCopyOutlinedIcon /><FormattedMessage id="action.duplicate" defaultMessage="Duplicate" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleOnClose('rename')}>
|
||||||
|
<EditOutlinedIcon /> <FormattedMessage id="action.rename" defaultMessage="Rename" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleOnClose('delete')}>
|
||||||
|
<DeleteOutlinedIcon /><FormattedMessage id="action.delete" defaultMessage="Delete" />
|
||||||
|
</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<MenuItem onClick={handleOnClose('export')}>
|
||||||
|
<CloudDownloadOutlinedIcon /><FormattedMessage id="action.export" defaultMessage="Export" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleOnClose('print')}>
|
||||||
|
<PrintOutlinedIcon /><FormattedMessage id="action.print" defaultMessage="Print" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleOnClose('publish')}>
|
||||||
|
<PublicOutlinedIcon /><FormattedMessage id="action.publish" defaultMessage="Publish" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleOnClose('share')}>
|
||||||
|
<ShareOutlinedIcon /><FormattedMessage id="action.share" defaultMessage="Share" />
|
||||||
|
</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<MenuItem onClick={handleOnClose('info')}>
|
||||||
|
<InfoOutlinedIcon /><FormattedMessage id="action.share" defaultMessage="Info" />
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MapActionDialog;
|
434
packages/webapp/src/components/maps-page/index.tsx
Normal file
434
packages/webapp/src/components/maps-page/index.tsx
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { MapInfo, Service } from '../../services/Service'
|
||||||
|
import { PageContainer, MapsListArea, NavArea, HeaderArea, StyledTableCell } from './styled';
|
||||||
|
|
||||||
|
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
|
||||||
|
import Table from '@material-ui/core/Table';
|
||||||
|
import TableBody from '@material-ui/core/TableBody';
|
||||||
|
import TableCell from '@material-ui/core/TableCell';
|
||||||
|
import TableContainer from '@material-ui/core/TableContainer';
|
||||||
|
import TableHead from '@material-ui/core/TableHead';
|
||||||
|
import TablePagination from '@material-ui/core/TablePagination';
|
||||||
|
import TableRow from '@material-ui/core/TableRow';
|
||||||
|
import TableSortLabel from '@material-ui/core/TableSortLabel';
|
||||||
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
import Checkbox from '@material-ui/core/Checkbox';
|
||||||
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
import DeleteIcon from '@material-ui/icons/Delete';
|
||||||
|
import FilterListIcon from '@material-ui/icons/FilterList';
|
||||||
|
import StarRateRoundedIcon from '@material-ui/icons/StarRateRounded';
|
||||||
|
import MoreHorizIcon from '@material-ui/icons/MoreHoriz';
|
||||||
|
import { CSSProperties } from 'react';
|
||||||
|
import MapActionMenu, { ActionType } from './MapActionMenu';
|
||||||
|
import ActionDialog, { DialogType } from './ActionDialog';
|
||||||
|
|
||||||
|
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
|
||||||
|
if (b[orderBy] < a[orderBy]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b[orderBy] > a[orderBy]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Order = 'asc' | 'desc';
|
||||||
|
|
||||||
|
function getComparator<Key extends keyof any>(
|
||||||
|
order: Order,
|
||||||
|
orderBy: Key,
|
||||||
|
): (a: { [key in Key]: number | string | boolean | string[] }, b: { [key in Key]: number | string | string[] | boolean }) => number {
|
||||||
|
return order === 'desc'
|
||||||
|
? (a, b) => descendingComparator(a, b, orderBy)
|
||||||
|
: (a, b) => -descendingComparator(a, b, orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stableSort<T>(array: T[], comparator: (a: T, b: T) => number) {
|
||||||
|
const stabilizedThis = array.map((el, index) => [el, index] as [T, number]);
|
||||||
|
stabilizedThis.sort((a, b) => {
|
||||||
|
const order = comparator(a[0], b[0]);
|
||||||
|
if (order !== 0) return order;
|
||||||
|
return a[1] - b[1];
|
||||||
|
});
|
||||||
|
return stabilizedThis.map((el) => el[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HeadCell {
|
||||||
|
disablePadding: boolean;
|
||||||
|
id: keyof MapInfo;
|
||||||
|
label: string;
|
||||||
|
numeric: boolean;
|
||||||
|
style: CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const headCells: HeadCell[] = [
|
||||||
|
{ id: 'starred', numeric: false, disablePadding: false, label: '', style: { width: '20px', padding: '0px' } },
|
||||||
|
{ id: 'name', numeric: false, disablePadding: true, label: 'Name', style: {} },
|
||||||
|
{ id: 'labels', numeric: false, disablePadding: true, label: 'Labels', style: {} },
|
||||||
|
{ id: 'creator', numeric: false, disablePadding: false, label: 'Creator', style: {} },
|
||||||
|
{ id: 'modified', numeric: true, disablePadding: false, label: 'Modified', style: { width: '50px' } }
|
||||||
|
];
|
||||||
|
|
||||||
|
interface EnhancedTableProps {
|
||||||
|
classes: ReturnType<typeof useStyles>;
|
||||||
|
numSelected: number;
|
||||||
|
onRequestSort: (event: React.MouseEvent<unknown>, property: keyof MapInfo) => void;
|
||||||
|
onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
order: Order;
|
||||||
|
orderBy: string;
|
||||||
|
rowCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EnhancedTableHead(props: EnhancedTableProps) {
|
||||||
|
const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props;
|
||||||
|
const createSortHandler = (property: keyof MapInfo) => (event: React.MouseEvent<unknown>) => {
|
||||||
|
onRequestSort(event, property);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHead>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableCell padding='checkbox' key='select' style={{ width: '20px' }}>
|
||||||
|
<Checkbox
|
||||||
|
indeterminate={numSelected > 0 && numSelected < rowCount}
|
||||||
|
checked={rowCount > 0 && numSelected === rowCount}
|
||||||
|
onChange={onSelectAllClick}
|
||||||
|
size='small'
|
||||||
|
inputProps={{ 'aria-label': 'select all desserts' }}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
{headCells.map((headCell) => (
|
||||||
|
<TableCell
|
||||||
|
key={headCell.id}
|
||||||
|
sortDirection={orderBy === headCell.id ? order : false}
|
||||||
|
style={headCell.style}
|
||||||
|
>
|
||||||
|
<TableSortLabel
|
||||||
|
active={orderBy === headCell.id}
|
||||||
|
direction={orderBy === headCell.id ? order : 'asc'}
|
||||||
|
onClick={createSortHandler(headCell.id)}>
|
||||||
|
{headCell.label}
|
||||||
|
{orderBy === headCell.id ? (
|
||||||
|
<span className={classes.visuallyHidden}>
|
||||||
|
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</TableSortLabel>
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
<TableCell style={{ width: '20px', padding: '0px' }} key='actions'>
|
||||||
|
<TableSortLabel>""</TableSortLabel>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useToolbarStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
flex: '1 1 100%',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
interface EnhancedTableToolbarProps {
|
||||||
|
numSelected: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EnhancedTableToolbar = (props: EnhancedTableToolbarProps) => {
|
||||||
|
const classes = useToolbarStyles();
|
||||||
|
const { numSelected } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Toolbar>
|
||||||
|
{numSelected > 0 ? (
|
||||||
|
<Typography className={classes.title} color="inherit" variant="subtitle1" component="div">
|
||||||
|
{numSelected} selected
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography className={classes.title} variant="h6" id="tableTitle" component="div">
|
||||||
|
Nutrition
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{numSelected > 0 ? (
|
||||||
|
<Tooltip title="Delete">
|
||||||
|
<IconButton aria-label="delete">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Tooltip title="Filter list">
|
||||||
|
<IconButton aria-label="filter list">
|
||||||
|
<FilterListIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Toolbar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
width: '100%',
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
minWidth: 750,
|
||||||
|
border: 0,
|
||||||
|
},
|
||||||
|
visuallyHidden: {
|
||||||
|
border: 0,
|
||||||
|
clip: 'rect(0 0 0 0)',
|
||||||
|
height: 1,
|
||||||
|
margin: -1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
padding: 0,
|
||||||
|
position: 'absolute',
|
||||||
|
top: 20,
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
type ActionPanelState = {
|
||||||
|
el: HTMLElement | undefined,
|
||||||
|
mapId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
function EnhancedTable(props: ServiceProps) {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const [order, setOrder] = React.useState<Order>('asc');
|
||||||
|
const [orderBy, setOrderBy] = React.useState<keyof MapInfo>('modified');
|
||||||
|
const [selected, setSelected] = React.useState<number[]>([]);
|
||||||
|
const [page, setPage] = React.useState(0);
|
||||||
|
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
||||||
|
const [rows, setRows] = React.useState<MapInfo[]>([]);
|
||||||
|
|
||||||
|
|
||||||
|
const [activeRowAction, setActiveRowAction] = React.useState<ActionPanelState | undefined>(undefined);
|
||||||
|
|
||||||
|
type ActiveDialog = {
|
||||||
|
actionType: DialogType;
|
||||||
|
mapId: number
|
||||||
|
|
||||||
|
};
|
||||||
|
const [activeDialog, setActiveDialog] = React.useState<ActiveDialog | undefined>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const mapsInfo = await props.service.fetchAllMaps();
|
||||||
|
setRows(mapsInfo);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleRequestSort = (event: React.MouseEvent<unknown>, property: keyof MapInfo) => {
|
||||||
|
const isAsc = orderBy === property && order === 'asc';
|
||||||
|
setOrder(isAsc ? 'desc' : 'asc');
|
||||||
|
setOrderBy(property);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
if (event.target.checked) {
|
||||||
|
const newSelecteds = rows.map((n) => n.id);
|
||||||
|
setSelected(newSelecteds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelected([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowClick = (event: React.MouseEvent<unknown>, id: number): void => {
|
||||||
|
const selectedIndex = selected.indexOf(id);
|
||||||
|
let newSelected: number[] = [];
|
||||||
|
|
||||||
|
if (selectedIndex === -1) {
|
||||||
|
newSelected = newSelected.concat(selected, id);
|
||||||
|
} else if (selectedIndex === 0) {
|
||||||
|
newSelected = newSelected.concat(selected.slice(1));
|
||||||
|
} else if (selectedIndex === selected.length - 1) {
|
||||||
|
newSelected = newSelected.concat(selected.slice(0, -1));
|
||||||
|
} else if (selectedIndex > 0) {
|
||||||
|
newSelected = newSelected.concat(
|
||||||
|
selected.slice(0, selectedIndex),
|
||||||
|
selected.slice(selectedIndex + 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelected(newSelected);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setRowsPerPage(parseInt(event.target.value, 10));
|
||||||
|
setPage(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleActionClick = (mapId: number): ((event: any) => void) => {
|
||||||
|
return (event: any): void => {
|
||||||
|
setActiveRowAction(
|
||||||
|
{
|
||||||
|
mapId: mapId,
|
||||||
|
el: event.currentTarget
|
||||||
|
}
|
||||||
|
);
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleActionMenuClose = (action: ActionType): void => {
|
||||||
|
if (action) {
|
||||||
|
const mapId = activeRowAction?.mapId;
|
||||||
|
|
||||||
|
setActiveRowAction(undefined);
|
||||||
|
setActiveDialog({
|
||||||
|
actionType: action as DialogType,
|
||||||
|
mapId: mapId as number
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isSelected = (id: number) => selected.indexOf(id) !== -1;
|
||||||
|
|
||||||
|
const emptyRows = rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<EnhancedTableToolbar numSelected={selected.length} />
|
||||||
|
<TableContainer>
|
||||||
|
<Table
|
||||||
|
className={classes.table}
|
||||||
|
aria-labelledby="tableTitle"
|
||||||
|
size={'small'}
|
||||||
|
aria-label="sticky table"
|
||||||
|
stickyHeader
|
||||||
|
>
|
||||||
|
<EnhancedTableHead
|
||||||
|
classes={classes}
|
||||||
|
numSelected={selected.length}
|
||||||
|
order={order}
|
||||||
|
orderBy={orderBy}
|
||||||
|
onSelectAllClick={handleSelectAllClick}
|
||||||
|
onRequestSort={handleRequestSort}
|
||||||
|
rowCount={rows.length}
|
||||||
|
/>
|
||||||
|
<TableBody>
|
||||||
|
{stableSort(rows, 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}
|
||||||
|
>
|
||||||
|
|
||||||
|
<StyledTableCell padding="checkbox">
|
||||||
|
<Checkbox
|
||||||
|
checked={isItemSelected}
|
||||||
|
inputProps={{ 'aria-labelledby': String(labelId) }}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</StyledTableCell>
|
||||||
|
|
||||||
|
<StyledTableCell>
|
||||||
|
<Tooltip title="Starred">
|
||||||
|
<IconButton aria-label="Starred" size="small" onClick={(e) => { alert("") }}>
|
||||||
|
<StarRateRoundedIcon color="action" style={{ color: row.starred ? 'yellow' : 'gray' }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</StyledTableCell>
|
||||||
|
|
||||||
|
<StyledTableCell>{row.name}</StyledTableCell>
|
||||||
|
<StyledTableCell>{row.labels}</StyledTableCell>
|
||||||
|
<StyledTableCell>{row.creator}</StyledTableCell>
|
||||||
|
<StyledTableCell>{row.modified}</StyledTableCell>
|
||||||
|
|
||||||
|
<StyledTableCell>
|
||||||
|
<Tooltip title="Others">
|
||||||
|
<IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}>
|
||||||
|
<MoreHorizIcon color="action" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<MapActionMenu anchor={activeRowAction?.el} onClose={handleActionMenuClose} />
|
||||||
|
</StyledTableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{emptyRows > 0 && (
|
||||||
|
<TableRow style={{ height: 33 * emptyRows }}>
|
||||||
|
<TableCell colSpan={6} />
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
<TablePagination
|
||||||
|
rowsPerPageOptions={[5, 10, 25]}
|
||||||
|
component="div"
|
||||||
|
count={rows.length}
|
||||||
|
rowsPerPage={rowsPerPage}
|
||||||
|
page={page}
|
||||||
|
onChangePage={handleChangePage}
|
||||||
|
onChangeRowsPerPage={handleChangeRowsPerPage}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Action Dialog */}
|
||||||
|
<ActionDialog action={activeDialog?.actionType} onClose={(refresh: boolean) => setActiveDialog(undefined)} mapId={activeDialog ? activeDialog.mapId : -1} service={props.service}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceProps = {
|
||||||
|
service: Service
|
||||||
|
}
|
||||||
|
|
||||||
|
const MapsPage = (props: ServiceProps) => {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = 'Maps | WiseMapping';
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<HeaderArea>
|
||||||
|
<h2>Header</h2>
|
||||||
|
</HeaderArea>
|
||||||
|
<NavArea>
|
||||||
|
<h1> Nav </h1>
|
||||||
|
</NavArea>
|
||||||
|
<MapsListArea>
|
||||||
|
<EnhancedTable service={props.service} />
|
||||||
|
</MapsListArea>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MapsPage;
|
||||||
|
|
||||||
|
|
46
packages/webapp/src/components/maps-page/styled.ts
Normal file
46
packages/webapp/src/components/maps-page/styled.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { MenuItem, TableCell } from '@material-ui/core';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const PageContainer = styled.div`
|
||||||
|
display: grid;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
grid-template-areas: "nav header header"
|
||||||
|
"nav main ads"
|
||||||
|
"nav main ads";
|
||||||
|
grid-template-columns: 240px 1fr 240px;
|
||||||
|
grid-template-rows: 60px 1fr 30px;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const MapsListArea = styled.div`
|
||||||
|
grid-area: main;
|
||||||
|
background-color: #ffff64;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const NavArea = styled.div`
|
||||||
|
grid-area: nav;
|
||||||
|
background-color: red;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const HeaderArea = styled.div`
|
||||||
|
grid-area: header;
|
||||||
|
background-color: blue;
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
export const StyledTableCell = withStyles({
|
||||||
|
root: {
|
||||||
|
color: 'black',
|
||||||
|
padding: '0px',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}
|
||||||
|
})(TableCell);
|
||||||
|
|
||||||
|
export const StyledMenuItem = withStyles({
|
||||||
|
root: {
|
||||||
|
width: '300px',
|
||||||
|
padding: '10px 20px',
|
||||||
|
marging: '0px 20px'
|
||||||
|
}
|
||||||
|
})(MenuItem)
|
@ -13,12 +13,12 @@ import { StyledReCAPTCHA } from './styled';
|
|||||||
import { PageContent } from '../../theme/global-style';
|
import { PageContent } from '../../theme/global-style';
|
||||||
|
|
||||||
const RegistrationForm = (props: ServiceProps) => {
|
const RegistrationForm = (props: ServiceProps) => {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState('');
|
||||||
const [lastname, setLastname] = useState("")
|
const [lastname, setLastname] = useState('')
|
||||||
const [firstname, setFirstname] = useState("");
|
const [firstname, setFirstname] = useState('');
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState('');
|
||||||
const [recaptchaToken, setRecaptchaToken] = useState<string | null>("");
|
const [recaptchaToken, setRecaptchaToken] = useState<string | null>('');
|
||||||
const [errorMsg, setErrorMsg] = useState("");
|
const [errorMsg, setErrorMsg] = useState<string>();
|
||||||
|
|
||||||
const [disableButton, setDisableButton] = useState(false);
|
const [disableButton, setDisableButton] = useState(false);
|
||||||
|
|
||||||
@ -42,7 +42,10 @@ const RegistrationForm = (props: ServiceProps) => {
|
|||||||
props.service.registerNewUser(
|
props.service.registerNewUser(
|
||||||
user,
|
user,
|
||||||
() => history.push("/c/registration-success"),
|
() => history.push("/c/registration-success"),
|
||||||
(msg) => { setErrorMsg(msg); setDisableButton(false); }
|
(errorInfo) => {
|
||||||
|
const errorMsg = errorInfo.msg ? errorInfo.msg : '';
|
||||||
|
setErrorMsg(errorMsg); setDisableButton(false);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
type NewUser = {
|
export type NewUser = {
|
||||||
email: string;
|
email: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
@ -8,10 +8,38 @@ type NewUser = {
|
|||||||
recaptcha: string | null;
|
recaptcha: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MapInfo = {
|
||||||
|
id: number;
|
||||||
|
starred: boolean;
|
||||||
|
name: string;
|
||||||
|
labels: [string];
|
||||||
|
creator: string;
|
||||||
|
modified: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BasicMapInfo = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FieldError = {
|
||||||
|
id: string,
|
||||||
|
msg: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ErrorInfo = {
|
||||||
|
msg?: string;
|
||||||
|
fields?: FieldError[];
|
||||||
|
}
|
||||||
|
|
||||||
interface Service {
|
interface Service {
|
||||||
registerNewUser(user: NewUser, onSuccess: () => void, onError: (msg: string) => void): void;
|
registerNewUser(user: NewUser, onSuccess: () => void, onError: (errorInfo: ErrorInfo) => void):void;
|
||||||
resetPassword(email: string, onSuccess: () => void, onError: (msg: string) => void): void;
|
resetPassword(email: string, onSuccess: () => void, onError: (errorInfo: ErrorInfo) => void): void;
|
||||||
|
fetchAllMaps(): Promise<MapInfo[]>;
|
||||||
|
|
||||||
|
deleteMap(id: number): Promise<void>;
|
||||||
|
remameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
|
||||||
|
loadMapInfo(id: number): Promise<BasicMapInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RestService implements Service {
|
class RestService implements Service {
|
||||||
@ -22,7 +50,19 @@ class RestService implements Service {
|
|||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerNewUser(user: NewUser, onSuccess: () => void, onError: (msg: string) => void) {
|
loadMapInfo(id: number): Promise<BasicMapInfo> {
|
||||||
|
return Promise.resolve({ name: 'My Map', description: 'My Descition' });
|
||||||
|
}
|
||||||
|
|
||||||
|
async remameMap(id: number, basicInfo: BasicMapInfo) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteMap(id: number): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerNewUser(user: NewUser, onSuccess: () => void, onError: (errorInfo: ErrorInfo) => void) {
|
||||||
|
|
||||||
await axios.post(this.baseUrl + '/service/users',
|
await axios.post(this.baseUrl + '/service/users',
|
||||||
JSON.stringify(user),
|
JSON.stringify(user),
|
||||||
@ -37,7 +77,29 @@ class RestService implements Service {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetPassword(email: string, onSuccess: () => void, onError: (msg: string) => void) {
|
async fetchAllMaps(): Promise<MapInfo[]> {
|
||||||
|
function createMapInfo(
|
||||||
|
id: number,
|
||||||
|
starred: boolean,
|
||||||
|
name: string,
|
||||||
|
labels: [string],
|
||||||
|
creator: string,
|
||||||
|
modified: number,
|
||||||
|
): MapInfo {
|
||||||
|
return { id, name, labels, creator, modified, starred };
|
||||||
|
}
|
||||||
|
|
||||||
|
const maps = [
|
||||||
|
createMapInfo(1, true, "El Mapa", [""], "Paulo", 67,),
|
||||||
|
createMapInfo(2, false, "El Mapa2", [""], "Paulo2", 67),
|
||||||
|
createMapInfo(3, false, "El Mapa3", [""], "Paulo3", 67)
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
return Promise.resolve(maps);
|
||||||
|
}
|
||||||
|
|
||||||
|
async resetPassword(email: string, onSuccess: () => void, onError: (errorInfo: ErrorInfo) => void) {
|
||||||
await axios.put(this.baseUrl + '/service/users/resetPassword?email=' + email,
|
await axios.put(this.baseUrl + '/service/users/resetPassword?email=' + email,
|
||||||
null,
|
null,
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
{ headers: { 'Content-Type': 'application/json' } }
|
||||||
@ -46,13 +108,14 @@ class RestService implements Service {
|
|||||||
onSuccess();
|
onSuccess();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
const response = error.response;
|
const response = error.response;
|
||||||
const errorMsg = this.parseResponseOnError(response);
|
const errorInfo: ErrorInfo = this.parseResponseOnError(response);
|
||||||
onError(errorMsg);
|
onError(errorInfo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseResponseOnError = (response: any) => {
|
private parseResponseOnError = (response: any): ErrorInfo => {
|
||||||
let msg;
|
|
||||||
|
let result: ErrorInfo | undefined;
|
||||||
if (response) {
|
if (response) {
|
||||||
const status: number = response.status;
|
const status: number = response.status;
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
@ -64,30 +127,35 @@ class RestService implements Service {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (data) {
|
if (data) {
|
||||||
let errors: string[] = [];
|
// Set global errorrs ...
|
||||||
if (data.globalErrors) {
|
if (data.globalErrors) {
|
||||||
errors = data.globalErrors;
|
let msg;
|
||||||
} else if (data.fieldErrors) {
|
let errors = data.globalErrors;
|
||||||
errors = Object.values(data.fieldErrors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
msg = errors[0];
|
msg = errors[0];
|
||||||
}
|
}
|
||||||
|
result = { msg: errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set field errors ...
|
||||||
|
if (data.fieldErrors) {
|
||||||
|
// @Todo: Fix this ...
|
||||||
|
result = { msg: data.fieldErrors };
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
msg = response.statusText;
|
result = { msg: response.statusText };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network related problem ...
|
// Network related problem ...
|
||||||
if (!msg) {
|
if (!result) {
|
||||||
msg = 'Unexpected error. Please, try latter';
|
result = { msg: 'Unexpected error. Please, try latter' };
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
export { Service, RestService, NewUser }
|
export { Service, RestService }
|
@ -38,11 +38,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CleanWebpackPlugin(),
|
new CleanWebpackPlugin(),
|
||||||
// @FixMe: Fatten the dir ...
|
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
patterns: [{
|
patterns: [{
|
||||||
from: 'public/*',
|
from: 'public/*',
|
||||||
to: '[name].[ext]'
|
to: '[name].[ext]',
|
||||||
|
globOptions: {
|
||||||
|
ignore: [
|
||||||
|
'**/index.html'
|
||||||
|
]
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user