Complete menu navigation

This commit is contained in:
Paulo Gustavo Veiga 2021-01-31 18:04:50 -08:00
parent db934afed8
commit a1867168ec
20 changed files with 288 additions and 161 deletions

View File

@ -6,7 +6,7 @@
"defaultMessage": "Close" "defaultMessage": "Close"
}, },
"action.delete": { "action.delete": {
"defaultMessage": "Delete" "defaultMessage": "History"
}, },
"action.delete-description": { "action.delete-description": {
"defaultMessage": "Deleted mindmap can not be recovered. Do you want to continue ?." "defaultMessage": "Deleted mindmap can not be recovered. Do you want to continue ?."
@ -26,6 +26,9 @@
"action.info-title": { "action.info-title": {
"defaultMessage": "Info" "defaultMessage": "Info"
}, },
"action.label": {
"defaultMessage": "Add Label"
},
"action.open": { "action.open": {
"defaultMessage": "Open" "defaultMessage": "Open"
}, },
@ -120,6 +123,12 @@
"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!."
}, },
"menu.account": {
"defaultMessage": "Account"
},
"menu.signout": {
"defaultMessage": "Sign Out"
},
"registration.desc": { "registration.desc": {
"defaultMessage": "Signing up is free and just take a moment" "defaultMessage": "Signing up is free and just take a moment"
}, },

View File

@ -14,7 +14,7 @@
"action.delete": [ "action.delete": [
{ {
"type": 0, "type": 0,
"value": "Delete" "value": "History"
} }
], ],
"action.delete-description": [ "action.delete-description": [
@ -53,6 +53,12 @@
"value": "Info" "value": "Info"
} }
], ],
"action.label": [
{
"type": 0,
"value": "Add Label"
}
],
"action.open": [ "action.open": [
{ {
"type": 0, "type": 0,
@ -239,6 +245,18 @@
"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!."
} }
], ],
"menu.account": [
{
"type": 0,
"value": "Account"
}
],
"menu.signout": [
{
"type": 0,
"value": "Sign Out"
}
],
"registration.desc": [ "registration.desc": [
{ {
"type": 0, "type": 0,

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { FormattedMessage, useIntl } from 'react-intl' import { FormattedMessage, useIntl } from 'react-intl'
import { useHistory } from "react-router-dom" import { useHistory } from "react-router-dom"
import { Service, ErrorInfo } from '../../services/Service' import Service, { ErrorInfo } from '../../services'
import Header from '../layout/header' import Header from '../layout/header'
import Footer from '../layout/footer' import Footer from '../layout/footer'

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { ErrorInfo } from "../../../services/Service" import { ErrorInfo } from "../../../services"
import StyledAlert from "./styled"; import StyledAlert from "./styled";

View File

@ -1,7 +1,7 @@
import { TextField } from "@material-ui/core"; import { TextField } from "@material-ui/core";
import React, { ChangeEvent } from "react"; import React, { ChangeEvent } from "react";
import { MessageDescriptor, useIntl } from "react-intl"; import { MessageDescriptor, useIntl } from "react-intl";
import { ErrorInfo } from "../../../services/Service"; import { ErrorInfo } from "../../../services";
type InputProps = { type InputProps = {
name: string; name: string;

View File

@ -36,6 +36,7 @@ const ActionChooser = (props: ActionProps) => {
keepMounted keepMounted
open={Boolean(anchor)} open={Boolean(anchor)}
onClose={handleOnClose(undefined)} onClose={handleOnClose(undefined)}
elevation={1}
> >
<MenuItem onClick={handleOnClose('open')} style={{width:"220px"}}> <MenuItem onClick={handleOnClose('open')} style={{width:"220px"}}>
<ListItemIcon> <ListItemIcon>

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Button, DialogContentText } from "@material-ui/core"; import { Button, DialogContentText } from "@material-ui/core";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { ErrorInfo } from "../../../../services/Service"; import { ErrorInfo } from "../../../../services";
import { StyledDialog, StyledDialogActions, StyledDialogContent, StyledDialogTitle } from "./style"; import { StyledDialog, StyledDialogActions, StyledDialogContent, StyledDialogTitle } from "./style";
import GlobalError from "../../../form/global-error"; import GlobalError from "../../../form/global-error";

View File

@ -3,7 +3,7 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { useMutation, useQueryClient } from "react-query"; import { useMutation, useQueryClient } from "react-query";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { Service } from "../../../../services/Service"; import Service from "../../../../services";
import { activeInstance } from '../../../../reducers/serviceSlice'; import { activeInstance } from '../../../../reducers/serviceSlice';
import { DialogProps, fetchMapById, handleOnMutationSuccess } from ".."; import { DialogProps, fetchMapById, handleOnMutationSuccess } from "..";
import BaseDialog from "../action-dialog"; import BaseDialog from "../action-dialog";
@ -35,7 +35,7 @@ const DeleteDialog = (props: DialogProps) => {
<div> <div>
<BaseDialog <BaseDialog
open={props.open} onClose={handleOnClose} onSubmit={handleOnSubmit} open={props.open} onClose={handleOnClose} onSubmit={handleOnSubmit}
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?.name}'</AlertTitle>

View File

@ -4,7 +4,7 @@ import { useMutation, useQueryClient } 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 { BasicMapInfo, ErrorInfo, Service } from "../../../../services/Service"; import Service, { BasicMapInfo, ErrorInfo } from "../../../../services";
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 { DialogProps, fetchMapById, handleOnMutationSuccess } from "..";
@ -75,7 +75,7 @@ const DuplicateDialog = (props: DialogProps) => {
title={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })} title={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}
description={intl.formatMessage({ id: 'rename.description', defaultMessage: 'Please, fill the new map name and description.' })} description={intl.formatMessage({ id: 'rename.description', defaultMessage: 'Please, fill the new map name and description.' })}
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="name" type="text" label={{ id: "action.rename-name-placeholder", defaultMessage: "Name" }}
value={model.name} onChange={handleOnChange} error={error} fullWidth={true} /> value={model.name} onChange={handleOnChange} error={error} fullWidth={true} />
@ -84,7 +84,7 @@ const DuplicateDialog = (props: DialogProps) => {
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} /> value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />
</FormControl> </FormControl>
</BaseDialog> </BaseDialog>
</div> </div>
); );
} }

View File

@ -2,7 +2,8 @@ import React from 'react';
import RenameDialog from './rename'; import RenameDialog from './rename';
import DeleteDialog from './delete'; import DeleteDialog from './delete';
import { ActionType } from '../action-chooser'; import { ActionType } from '../action-chooser';
import { ErrorInfo, MapInfo, Service } from '../../../services/Service'; import { ErrorInfo, MapInfo } from '../../../services';
import Service from '../../../services';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { QueryClient, useQuery } from 'react-query'; import { QueryClient, useQuery } from 'react-query';
import { activeInstance } from '../../../reducers/serviceSlice'; import { activeInstance } from '../../../reducers/serviceSlice';

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { useQueryClient } from "react-query"; import { useQueryClient } from "react-query";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { Service } from "../../../../services/Service"; import Service from "../../../../services";
import { activeInstance } from '../../../../reducers/serviceSlice'; import { activeInstance } from '../../../../reducers/serviceSlice';
import { DialogProps, fetchMapById } from ".."; import { DialogProps, fetchMapById } from "..";
import BaseDialog from "../action-dialog"; import BaseDialog from "../action-dialog";
@ -26,7 +26,7 @@ const InfoDialog = (props: DialogProps) => {
open={props.open} onClose={handleOnClose} open={props.open} onClose={handleOnClose}
title={intl.formatMessage({ id: "action.info-title", defaultMessage: "Info" })}> title={intl.formatMessage({ id: "action.info-title", defaultMessage: "Info" })}>
<iframe src="http://www.clarin.com" style={{width:'100%',height:'400px'}}/> <iframe src="http://www.clarin.com" style={{ width: '100%', height: '400px' }} />
</BaseDialog> </BaseDialog>
</div> </div>

View File

@ -2,7 +2,7 @@ import React, { useEffect } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { useMutation, useQueryClient } from "react-query"; import { useMutation, useQueryClient } from "react-query";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { BasicMapInfo, ErrorInfo, Service } from "../../../../services/Service"; import Service, { BasicMapInfo, ErrorInfo } from "../../../../services";
import { activeInstance } from '../../../../reducers/serviceSlice'; import { activeInstance } from '../../../../reducers/serviceSlice';
import { DialogProps, fetchMapById, handleOnMutationSuccess } from ".."; import { DialogProps, fetchMapById, handleOnMutationSuccess } from "..";
import Input from "../../../form/input"; import Input from "../../../form/input";

View File

@ -7,31 +7,40 @@ import List from '@material-ui/core/List';
import IconButton from '@material-ui/core/IconButton'; import IconButton from '@material-ui/core/IconButton';
import ListItem from '@material-ui/core/ListItem'; import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemIcon from '@material-ui/core/ListItemIcon';
import { ListItemTextStyled, useStyles } from './style'; import { useStyles } from './style';
import { AccountCircle, AddCircleTwoTone, BlurCircular, CloudUploadTwoTone, DeleteOutlineTwoTone, EmailOutlined, EmojiPeopleOutlined, ExitToAppOutlined, FeedbackOutlined, Help, LabelTwoTone, PolicyOutlined, PublicTwoTone, SettingsApplicationsOutlined, ShareTwoTone, StarRateTwoTone, Translate, TranslateTwoTone } from '@material-ui/icons'; import { AccountCircle, AddCircleTwoTone, BlurCircular, CloudUploadTwoTone, EmailOutlined, EmojiPeopleOutlined, ExitToAppOutlined, FeedbackOutlined, Help, LabelTwoTone, PolicyOutlined, PublicTwoTone, SettingsApplicationsOutlined, ShareTwoTone, StarRateTwoTone, Translate, TranslateTwoTone } from '@material-ui/icons';
import InboxTwoToneIcon from '@material-ui/icons/InboxTwoTone'; import InboxTwoToneIcon from '@material-ui/icons/InboxTwoTone';
import { Button, Link, ListItemSecondaryAction, Menu, MenuItem, Tooltip } from '@material-ui/core'; import { Button, Link, ListItemSecondaryAction, ListItemText, Menu, MenuItem, Tooltip } from '@material-ui/core';
import { MapsList } from './maps-list'; import { MapsList } from './maps-list';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useQueryClient } from 'react-query';
const logoIcon = require('../../images/logo-small.svg') const logoIcon = require('../../images/logo-small.svg')
const poweredByIcon = require('../../images/pwrdby-white.svg') const poweredByIcon = require('../../images/pwrdby-white.svg')
type FilterType = 'public' | 'all' | 'starred' | 'shared' | 'label' | 'owned' export type Filter = GenericFilter | LabelFilter;
interface Filter { interface GenericFilter {
type: FilterType type: 'public' | 'all' | 'starred' | 'shared' | 'label' | 'owned';
} }
interface LabelFinter extends Filter { interface LabelFilter {
type: 'label',
label: string label: string
} }
const MapsPage = (props: any) => { const MapsPage = (props: any) => {
const classes = useStyles(); const classes = useStyles();
const [filter, setFilter] = React.useState<Filter>({ type: 'all' });
const queryClient = useQueryClient();
useEffect(() => { useEffect(() => {
document.title = 'Maps | WiseMapping'; document.title = 'Maps | WiseMapping';
}, []); }, []);
const handleMenuClick = (filter: Filter) => {
setFilter(filter);
};
return ( return (
<div className={classes.root}> <div className={classes.root}>
<AppBar <AppBar
@ -88,53 +97,43 @@ const MapsPage = (props: any) => {
</div> </div>
<List component="nav"> <List component="nav">
<ListItem button > <StyleListItem
<ListItemIcon> icon={<InboxTwoToneIcon color="secondary" />}
<InboxTwoToneIcon color="secondary" /> label={"All"}
</ListItemIcon> filter={{ type: 'all' }}
<ListItemTextStyled primary="All" /> active={filter}
</ListItem> onClick={handleMenuClick}
/>
<ListItem button > <StyleListItem
<ListItemIcon> icon={<BlurCircular color="secondary" />}
<BlurCircular color="secondary" /> label={"Owned"}
</ListItemIcon> filter={{ type: 'owned' }}
<ListItemTextStyled primary="Owned" /> active={filter}
</ListItem> onClick={handleMenuClick}
/>
<ListItem button > <StyleListItem
<ListItemIcon> icon={<StarRateTwoTone color="secondary" />}
<StarRateTwoTone color="secondary" /> label={"Starred"}
</ListItemIcon> filter={{ type: 'starred' }}
<ListItemTextStyled primary="Starred" /> active={filter}
</ListItem> onClick={handleMenuClick}
/>
<ListItem button > <StyleListItem
<ListItemIcon> icon={<ShareTwoTone color="secondary" />}
<ShareTwoTone color="secondary" /> label={"Shared With Me"}
</ListItemIcon> filter={{ type: 'shared' }}
<ListItemTextStyled primary="Shared With Me" /> active={filter}
</ListItem> onClick={handleMenuClick}
/>
<ListItem button > <StyleListItem
<ListItemIcon> icon={<PublicTwoTone color="secondary" />}
<PublicTwoTone color="secondary" /> label={"Public"}
</ListItemIcon> filter={{ type: 'public' }}
<ListItemTextStyled primary="Public" /> active={filter}
</ListItem> onClick={handleMenuClick}
/>
<ListItem button >
<ListItemIcon>
<LabelTwoTone color="secondary" />
</ListItemIcon>
<ListItemTextStyled primary="Some label>" />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteOutlineTwoTone color="secondary" />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List> </List>
<div style={{ position: 'absolute', bottom: '10px', left: '20px' }}> <div style={{ position: 'absolute', bottom: '10px', left: '20px' }}>
<Link href="http://www.wisemapping.org/"> <Link href="http://www.wisemapping.org/">
<img src={poweredByIcon} alt="Powered By WiseMapping" /> <img src={poweredByIcon} alt="Powered By WiseMapping" />
@ -143,12 +142,50 @@ const MapsPage = (props: any) => {
</Drawer> </Drawer>
<main className={classes.content}> <main className={classes.content}>
<div className={classes.toolbar} /> <div className={classes.toolbar} />
<MapsList /> <MapsList filter={filter} />
</main> </main>
</div> </div>
); );
} }
interface ListItemProps {
icon: any,
label: string,
filter: Filter,
active?: Filter
onClick: (filter: Filter) => void;
}
const StyleListItem = (props: ListItemProps) => {
const icon = props.icon;
const label = props.label;
const filter = props.filter;
const activeType = props.active?.type;
const onClick = props.onClick;
const handleOnClick = (event: any, filter: Filter) => {
// Invalidate cache to provide a fresh load ...
event.stopPropagation();
onClick(filter);
}
return (
<ListItem button selected={activeType == filter.type} onClick={e => { handleOnClick(e, filter) }}>
<ListItemIcon>
{icon}
</ListItemIcon>
<ListItemText style={{ color: 'white' }} primary={label} />
{/* <ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteOutlineTwoTone color="secondary" />
</IconButton>
</ListItemSecondaryAction> */}
</ListItem>
);
}
const ProfileToobarButton = () => { const ProfileToobarButton = () => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
@ -163,11 +200,14 @@ const ProfileToobarButton = () => {
return ( return (
<span> <span>
<IconButton <Tooltip title="Paulo Veiga <pveiga@gmail.com>">
aria-haspopup="true" <Button
onClick={handleMenu}> aria-haspopup="true"
<AccountCircle fontSize="large" /> onClick={handleMenu}>
</IconButton > <AccountCircle fontSize="large" />
Paulo Veiga
</Button >
</Tooltip>
<Menu id="appbar-profile" <Menu id="appbar-profile"
anchorEl={anchorEl} anchorEl={anchorEl}
keepMounted keepMounted

View File

@ -1,4 +1,4 @@
import React from 'react' import React, { useEffect } from 'react'
import { useStyles } from './styled'; import { useStyles } from './styled';
import Table from '@material-ui/core/Table'; import Table from '@material-ui/core/Table';
@ -21,12 +21,14 @@ import { CSSProperties } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { activeInstance } from '../../../reducers/serviceSlice'; import { activeInstance } from '../../../reducers/serviceSlice';
import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useMutation, useQuery, useQueryClient } from 'react-query';
import { ErrorInfo, MapInfo, Service } from '../../../services/Service'; import { ErrorInfo, MapInfo } from '../../../services';
import Service from '../../../services';
import ActionChooser, { ActionType } from '../action-chooser'; import ActionChooser, { ActionType } from '../action-chooser';
import ActionDispatcher from '../action-dispatcher'; import ActionDispatcher from '../action-dispatcher';
import { InputBase, Link } from '@material-ui/core'; import { 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 } from '..';
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) { function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
@ -139,21 +141,66 @@ type ActionPanelState = {
mapId: number mapId: number
} }
export const MapsList = () => { interface MapsListProps {
filter: Filter
}
const mapsFilter = (filter: Filter, search: string): ((mapInfo: MapInfo) => boolean) => {
return (mapInfo: MapInfo) => {
// Check for filter condition
let result = false;
switch (filter.type) {
case 'all':
result = true;
break;
case 'public':
result = mapInfo.isPublic;
break;
case 'starred':
result = mapInfo.starred;
break;
default:
result = false;
}
// Does it match search filter criteria...
if (search && result) {
result = mapInfo.name.toLowerCase().indexOf(search.toLowerCase()) != -1;
}
return result;
}
}
export const MapsList = (props: MapsListProps) => {
const classes = useStyles(); const classes = useStyles();
const [order, setOrder] = React.useState<Order>('asc'); const [order, setOrder] = React.useState<Order>('asc');
const [filter, setFilter] = React.useState<Filter>({ type: 'all' });
const [orderBy, setOrderBy] = React.useState<keyof MapInfo>('modified'); const [orderBy, setOrderBy] = React.useState<keyof MapInfo>('modified');
const [selected, setSelected] = React.useState<number[]>([]); const [selected, setSelected] = React.useState<number[]>([]);
const [searchCondition, setSearchCondition] = React.useState<string>('');
const [page, setPage] = React.useState(0); const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(5); const [rowsPerPage, setRowsPerPage] = React.useState(10);
const service: Service = useSelector(activeInstance); const service: Service = useSelector(activeInstance);
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', async () => { useEffect(() => {
console.log("Update maps state.")
setSelected([]);
setSearchCondition('');
setPage(0);
setFilter(props.filter)
queryClient.invalidateQueries('maps');
const result = await service.fetchAllMaps(); }, [props.filter.type]);
return result;
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', async () => {
return await service.fetchAllMaps();
}); });
const mapsInfo: MapInfo[] = data ? data : []; const mapsInfo: MapInfo[] = data ? data.filter(mapsFilter(filter, searchCondition)) : [];
const [activeRowAction, setActiveRowAction] = React.useState<ActionPanelState | undefined>(undefined); const [activeRowAction, setActiveRowAction] = React.useState<ActionPanelState | undefined>(undefined);
@ -236,8 +283,6 @@ export const MapsList = () => {
const handleStarred = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, id: number) => { const handleStarred = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, id: number) => {
event.stopPropagation(); event.stopPropagation();
event.preventDefault();
starredMultation.mutate(id); starredMultation.mutate(id);
} }
@ -253,9 +298,11 @@ export const MapsList = () => {
setActiveRowAction(undefined); setActiveRowAction(undefined);
}; };
const isSelected = (id: number) => selected.indexOf(id) !== -1; const handleOnSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const emptyRows = rowsPerPage - Math.min(rowsPerPage, mapsInfo.length - page * rowsPerPage); setSearchCondition(e.target.value);
}
const isSelected = (id: number) => selected.indexOf(id) !== -1;
return ( return (
<div className={classes.root}> <div className={classes.root}>
<Paper className={classes.paper} elevation={0}> <Paper className={classes.paper} elevation={0}>
@ -276,7 +323,6 @@ export const MapsList = () => {
<div className={classes.toolbarListActions}> <div className={classes.toolbarListActions}>
<TablePagination <TablePagination
style={{ float: 'right', border: "0", paddingBottom: "5px" }} style={{ float: 'right', border: "0", paddingBottom: "5px" }}
rowsPerPageOptions={[50]}
count={mapsInfo.length} count={mapsInfo.length}
rowsPerPage={rowsPerPage} rowsPerPage={rowsPerPage}
page={page} page={page}
@ -295,6 +341,7 @@ export const MapsList = () => {
input: classes.searchInputInput, input: classes.searchInputInput,
}} }}
inputProps={{ 'aria-label': 'search' }} inputProps={{ 'aria-label': 'search' }}
onChange={handleOnSearchChange}
/> />
</div> </div>
</div> </div>

View File

@ -17,7 +17,10 @@ export const useStyles = makeStyles((theme: Theme) =>
'& tr:nth-child(odd)': '& tr:nth-child(odd)':
{ {
background: 'rgba(221, 221, 221, 0.35)' background: 'rgba(221, 221, 221, 0.35)'
} },
// '&:hover tr': {
// backgroundColor: 'rgba(150, 150, 150, 0.7)',
// }
}, },
headerCell: { headerCell: {
background: 'white', background: 'white',
@ -26,7 +29,7 @@ export const useStyles = makeStyles((theme: Theme) =>
border: 0 border: 0
}, },
bodyCell: { bodyCell: {
border: 0 border: '0px'
}, },
visuallyHidden: { visuallyHidden: {
border: 0, border: 0,
@ -66,7 +69,7 @@ export const useStyles = makeStyles((theme: Theme) =>
float: 'right' float: 'right'
}, },
searchIcon: { searchIcon: {
padding: '5px 0 0 5px', padding: '6px 0 0 5px',
height: '100%', height: '100%',
position: 'absolute', position: 'absolute',
pointerEvents: 'none', pointerEvents: 'none',

View File

@ -66,20 +66,11 @@ export const useStyles = makeStyles((theme: Theme) =>
toolbar: { toolbar: {
display: 'flex', display: 'flex',
justifyContent: 'flex-end', justifyContent: 'flex-end',
// necessary for content to be below app bar minHeight: '44px'
...theme.mixins.toolbar,
}, },
content: { content: {
flexGrow: 1, flexGrow: 1,
padding: theme.spacing(3), padding: theme.spacing(3),
} }
}), }),
); );
export const ListItemTextStyled = withStyles({
root:
{
color: 'white',
}
})(ListItemText);

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { FormattedMessage, useIntl } from 'react-intl'; import { FormattedMessage, useIntl } from 'react-intl';
import ReCAPTCHA from 'react-google-recaptcha'; import ReCAPTCHA from 'react-google-recaptcha';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { ErrorInfo, Service } from '../../services/Service'; import Service , { ErrorInfo} from '../../services';
import FormContainer from '../layout/form-container'; import FormContainer from '../layout/form-container';
import Header from '../layout/header'; import Header from '../layout/header';

View File

@ -1,7 +1,7 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import axios from 'axios'; import axios from 'axios';
import { ErrorInfo } from 'react'; import Service from '../services';
import { RestService, Service } from '../services/Service'; import MockService from '../services/mock-service';
type RutimeConfig = { type RutimeConfig = {
apiBaseUrl: string; apiBaseUrl: string;
@ -38,7 +38,7 @@ interface ServiceState {
} }
const initialState: ServiceState = { const initialState: ServiceState = {
instance: new RestService("", () => { console.log("401 error") }) instance: new MockService("", () => { console.log("401 error") })
}; };
export const serviceSlice = createSlice({ export const serviceSlice = createSlice({
@ -46,7 +46,7 @@ export const serviceSlice = createSlice({
initialState: initialState, initialState: initialState,
reducers: { reducers: {
initialize(state, action: PayloadAction<void[]>) { initialize(state, action: PayloadAction<void[]>) {
state.instance = new RestService("", () => { console.log("401 error") }); state.instance = new MockService("", () => { console.log("401 error") });
} }
}, },
}); });

View File

@ -0,0 +1,48 @@
export type NewUser = {
email: string;
firstname: string;
lastname: string;
password: string;
recaptcha: string | null;
}
export type MapInfo = {
id: number;
starred: boolean;
name: string;
labels: string[];
creator: string;
modified: number;
description: string;
isPublic: boolean;
}
export type BasicMapInfo = {
name: string;
description?: string;
}
export type FieldError = {
id: string,
msg: string
}
export type ErrorInfo = {
msg?: string;
fields?: Map<String, String>;
}
interface Service {
registerNewUser(user: NewUser): Promise<void>;
resetPassword(email: string): Promise<void>;
fetchAllMaps(): Promise<MapInfo[]>;
deleteMap(id: number): Promise<void>;
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
loadMapInfo(id: number): Promise<BasicMapInfo>;
changeStarred(id: number): Promise<void>;
}
export default Service;

View File

@ -1,49 +1,6 @@
import axios from 'axios' import { BasicMapInfo, ErrorInfo, MapInfo, NewUser } from "..";
import Service from "..";
export type NewUser = { import axios from "axios";
email: string;
firstname: string;
lastname: string;
password: string;
recaptcha: string | null;
}
export type MapInfo = {
id: number;
starred: boolean;
name: string;
labels: string[];
creator: string;
modified: number;
description: string;
}
export type BasicMapInfo = {
name: string;
description?: string;
}
export type FieldError = {
id: string,
msg: string
}
export type ErrorInfo = {
msg?: string;
fields?: Map<String, String>;
}
interface Service {
registerNewUser(user: NewUser): Promise<void>;
resetPassword(email: string): Promise<void>;
fetchAllMaps(): Promise<MapInfo[]>;
deleteMap(id: number): Promise<void>;
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
loadMapInfo(id: number): Promise<BasicMapInfo>;
changeStarred(id: number): Promise<void>;
}
class MockService implements Service { class MockService implements Service {
private baseUrl: string; private baseUrl: string;
@ -61,14 +18,24 @@ class MockService implements Service {
labels: string[], labels: string[],
creator: string, creator: string,
modified: number, modified: number,
description: string description: string,
isPublic: boolean
): MapInfo { ): MapInfo {
return { id, name, labels, creator, modified, starred, description }; return { id, name, labels, creator, modified, starred, description, isPublic };
} }
this.maps = [ this.maps = [
createMapInfo(1, true, "El Mapa", [""], "Paulo", 67, ""), createMapInfo(1, true, "El Mapa", [""], "Paulo", 67, "", true),
createMapInfo(2, false, "El Mapa2", [""], "Paulo2", 67, ""), createMapInfo(2, false, "El Mapa2", [""], "Paulo2", 67, "", false),
createMapInfo(3, false, "El Mapa3", [""], "Paulo3", 67, "") createMapInfo(3, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(4, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(5, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(6, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(7, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(8, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(9, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(10, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(11, false, "El Mapa3", [""], "Paulo3", 67, "", false),
createMapInfo(12, false, "El Mapa3", [""], "Paulo3", 67, "", false)
]; ];
} }
@ -112,7 +79,7 @@ class MockService implements Service {
}) })
}; };
} }
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<void> { duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
const exists = this.maps.find(m => m.name == basicInfo.name) != undefined; const exists = this.maps.find(m => m.name == basicInfo.name) != undefined;
@ -125,7 +92,8 @@ class MockService implements Service {
starred: false, starred: false,
creator: "current user", creator: "current user",
labels: [], labels: [],
modified: -1 modified: -1,
isPublic: false
}; };
this.maps.push(newMap); this.maps.push(newMap);
return Promise.resolve(); return Promise.resolve();
@ -164,6 +132,7 @@ class MockService implements Service {
} }
fetchAllMaps(): Promise<MapInfo[]> { fetchAllMaps(): Promise<MapInfo[]> {
console.log("Fetch maps from server")
return Promise.resolve(this.maps); return Promise.resolve(this.maps);
} }
@ -229,6 +198,6 @@ class MockService implements Service {
return result; return result;
} }
} }
export { Service, MockService as RestService }
export default MockService;