Redux initial refactor

This commit is contained in:
Paulo Gustavo Veiga 2020-12-24 17:39:06 -08:00
parent fed51ad5a9
commit 60fd57b094
12 changed files with 436 additions and 170 deletions

View File

@ -1,4 +1,55 @@
{
"action.cancel-button": {
"defaultMessage": "Cancel"
},
"action.delete": {
"defaultMessage": "Delete"
},
"action.delete-button": {
"defaultMessage": "Delete"
},
"action.delete-description": {
"defaultMessage": "Deleted mindmap can not be recovered. Do you want to continue ?."
},
"action.delete-title": {
"defaultMessage": "Delete"
},
"action.duplicate": {
"defaultMessage": "Duplicate"
},
"action.export": {
"defaultMessage": "Export"
},
"action.open": {
"defaultMessage": "Open"
},
"action.print": {
"defaultMessage": "Print"
},
"action.publish": {
"defaultMessage": "Publish"
},
"action.rename": {
"defaultMessage": "Rename"
},
"action.rename-button": {
"defaultMessage": "Rename"
},
"action.rename-description": {
"defaultMessage": "Please, update the name and description for your mindmap."
},
"action.rename-description-placeholder": {
"defaultMessage": "Description"
},
"action.rename-name-placeholder": {
"defaultMessage": "Name"
},
"action.rename-title": {
"defaultMessage": "Rename"
},
"action.share": {
"defaultMessage": "Info"
},
"common.wait": {
"defaultMessage": "Please wait ..."
},
@ -57,7 +108,7 @@
"defaultMessage": "Email"
},
"login.error": {
"defaultMessage": "The login.email address or login.password you entered is not valid."
"defaultMessage": "The email address or password you entered is not valid."
},
"login.forgotpwd": {
"defaultMessage": "Forgot Password ?"
@ -82,7 +133,7 @@
"defaultMessage": "Welcome"
},
"login.userinactive": {
"defaultMessage": "Sorry, your account has not been activated yet. You'll receive a notification login.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!."
},
"registration.desc": {
"defaultMessage": "Signing up is free and just take a moment"

View File

@ -48,13 +48,15 @@
"@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.57",
"@reduxjs/toolkit": "^1.5.0",
"@types/axios": "^0.14.0",
"@types/react-google-recaptcha": "^2.1.0",
"axios": "^0.21.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-google-recaptcha": "^2.1.0",
"react-intl": "^5.10.6",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"styled-components": "^5.2.1"
}
}
}

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react';
import { Service, RestService } from './services/Service';
import { IntlProvider } from 'react-intl'
import { GlobalStyle } from './theme/global-style';
@ -7,7 +6,8 @@ import RegistrationSuccessPage from './components/registration-success-page';
import ForgotPasswordSuccessPage from './components/forgot-password-success-page';
import RegistationPage from './components/registration-page';
import LoginPage from './components/login-page';
import MapsPage from './components/maps-page';
import MapsPage from './components/maps-page';
import store from "./store"
import {
Route,
@ -17,6 +17,7 @@ import {
} from 'react-router-dom';
import { ForgotPasswordPage } from './components/forgot-password-page';
import { Provider } from 'react-redux';
function loadLocaleData(language: string) {
switch (language) {
@ -25,13 +26,13 @@ function loadLocaleData(language: string) {
default:
return require('./compiled-lang/en.json')
}
}
}
type AppProps = {
baseRestUrl: string;
}
const App = (props: AppProps) => {
const App = () => {
const [messages, setMessages] = useState(undefined);
// Boostrap i18n ...
@ -50,33 +51,32 @@ const App = (props: AppProps) => {
fetchData();
}, []);
// Create Service object...
const service: Service = new RestService(props.baseRestUrl, () => { console.log("401 error") });
return messages ? (
<IntlProvider locale={locale} defaultLocale='en' messages={messages}>
<GlobalStyle />
<Router>
<Switch>
<Route exact path="/">
<Redirect to="/c/login" />
</Route>
<Route path="/c/login" component={LoginPage} />
<Route path="/c/registration">
<RegistationPage service={service} />
</Route>
<Route path="/c/registration-success" component={RegistrationSuccessPage} />
<Route path="/c/forgot-password">
<ForgotPasswordPage service={service} />
</Route>
<Route path="/c/forgot-password-success" component={ForgotPasswordSuccessPage} />
<Route path="/c/maps/">
<MapsPage service={service} />
</Route>
</Switch>
</Router>
</IntlProvider>
<Provider store={store}>
<IntlProvider locale={locale} defaultLocale='en' messages={messages}>
<GlobalStyle />
<Router>
<Switch>
<Route exact path="/">
<Redirect to="/c/login" />
</Route>
<Route path="/c/login" component={LoginPage} />
<Route path="/c/registration">
<RegistationPage />
</Route>
<Route path="/c/registration-success" component={RegistrationSuccessPage} />
<Route path="/c/forgot-password">
<ForgotPasswordPage />
</Route>
<Route path="/c/forgot-password-success" component={ForgotPasswordSuccessPage} />
<Route path="/c/maps/">
<MapsPage />
</Route>
</Switch>
</Router>
</IntlProvider>
</Provider>
) : <div>Loading ... </div>
}

View File

@ -1,4 +1,106 @@
{
"action.cancel-button": [
{
"type": 0,
"value": "Cancel"
}
],
"action.delete": [
{
"type": 0,
"value": "Delete"
}
],
"action.delete-button": [
{
"type": 0,
"value": "Delete"
}
],
"action.delete-description": [
{
"type": 0,
"value": "Deleted mindmap can not be recovered. Do you want to continue ?."
}
],
"action.delete-title": [
{
"type": 0,
"value": "Delete"
}
],
"action.duplicate": [
{
"type": 0,
"value": "Duplicate"
}
],
"action.export": [
{
"type": 0,
"value": "Export"
}
],
"action.open": [
{
"type": 0,
"value": "Open"
}
],
"action.print": [
{
"type": 0,
"value": "Print"
}
],
"action.publish": [
{
"type": 0,
"value": "Publish"
}
],
"action.rename": [
{
"type": 0,
"value": "Rename"
}
],
"action.rename-button": [
{
"type": 0,
"value": "Rename"
}
],
"action.rename-description": [
{
"type": 0,
"value": "Please, update the name and description for your mindmap."
}
],
"action.rename-description-placeholder": [
{
"type": 0,
"value": "Description"
}
],
"action.rename-name-placeholder": [
{
"type": 0,
"value": "Name"
}
],
"action.rename-title": [
{
"type": 0,
"value": "Rename"
}
],
"action.share": [
{
"type": 0,
"value": "Info"
}
],
"common.wait": [
{
"type": 0,
@ -116,7 +218,7 @@
"login.error": [
{
"type": 0,
"value": "The login.email address or login.password you entered is not valid."
"value": "The email address or password you entered is not valid."
}
],
"login.forgotpwd": [
@ -164,7 +266,7 @@
"login.userinactive": [
{
"type": 0,
"value": "Sorry, your account has not been activated yet. You'll receive a notification login.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!."
}
],
"registration.desc": [

View File

@ -13,7 +13,7 @@ type ForgotPasswordProps = {
email: string;
}
const ForgotPassword = (props: ServiceProps) => {
const ForgotPassword = () => {
const [email, setEmail] = useState('');
const [errorMsg, setErrorMsg] = useState('');
const [disableButton, setDisableButton] = useState(false);
@ -26,20 +26,20 @@ const ForgotPassword = (props: ServiceProps) => {
setDisableButton(true);
// Call Service ...
const service = props.service;
service.resetPassword(email)
.then(() => {
history.push("/c/forgot-password-success");
}).catch((error: ErrorInfo) => {
setErrorMsg(error.msg ? error.msg : '');
setDisableButton(false);
});
// const service = props.service;
// service.resetPassword(email)
// .then(() => {
// history.push("/c/forgot-password-success");
// }).catch((error: ErrorInfo) => {
// setErrorMsg(error.msg ? error.msg : '');
// setDisableButton(false);
// });
}
return (
<PageContent>
<h1><FormattedMessage id="forgot.title" defaultMessage="Reset your password"/></h1>
<p><FormattedMessage id="forgot.desc" defaultMessage="We will send you an email to reset your password"/></p>
<h1><FormattedMessage id="forgot.title" defaultMessage="Reset your password" /></h1>
<p><FormattedMessage id="forgot.desc" defaultMessage="We will send you an email to reset your password" /></p>
<form onSubmit={handleOnSubmit}>
<input type="email" name="email" onChange={e => setEmail(e.target.value)} placeholder={intl.formatMessage({ id: "forgot.email", defaultMessage: "Email" })} required={true} autoComplete="email" />
@ -55,7 +55,7 @@ const ForgotPassword = (props: ServiceProps) => {
type ServiceProps = {
service: Service
}
const ForgotPasswordPage = (props: ServiceProps) => {
const ForgotPasswordPage = () => {
useEffect(() => {
document.title = 'Reset Password | WiseMapping';
@ -64,7 +64,7 @@ const ForgotPasswordPage = (props: ServiceProps) => {
return (
<div>
<Header type='only-signin' />
<ForgotPassword service={props.service} />
<ForgotPassword />
<Footer />
</div>
);

View File

@ -6,26 +6,41 @@ 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, ErrorInfo, Service } from '../../services/Service';
import { ErrorInfo, MapInfo, Service } from '../../services/Service';
import { FormControl, TextField } from '@material-ui/core';
import { Alert, AlertTitle } from '@material-ui/lab';
import { useDispatch, useSelector } from 'react-redux';
import {
allMaps,
remove,
rename
} from '../../reducers/mapsListSlice'
import { Description } from '@material-ui/icons';
type DialogProps = {
open: boolean,
mapId: number,
onClose: (reload: boolean) => void,
service: Service
onClose: () => void
}
function DeleteConfirmDialog(props: DialogProps) {
export type BasicMapInfo = {
name: string;
description: string | undefined;
}
function DeleteDialog(props: DialogProps) {
const dispatch = useDispatch()
const mapId = props.mapId;
const mapInfo: MapInfo | undefined = useSelector(allMaps).
find(m => m.id == mapId);
const handleOnClose = (action: 'accept' | undefined): void => {
let result = false;
if (action == 'accept') {
props.service.deleteMap(props.mapId);
result = true;
if (action == 'accept' && mapInfo) {
dispatch(remove({ id: mapId }))
}
props.onClose(result);
props.onClose();
};
return (
@ -35,12 +50,10 @@ function DeleteConfirmDialog(props: DialogProps) {
onClose={() => handleOnClose(undefined)} >
<DialogTitle><FormattedMessage id="action.delete-title" defaultMessage="Delete" /></DialogTitle>
<DialogContent>
<DialogContentText>
<Alert severity="warning">
<AlertTitle>Map to be deleted</AlertTitle>
<FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
</Alert>
</DialogContentText>
<Alert severity="warning">
<AlertTitle>Delete '{mapInfo?.name}'</AlertTitle>
<FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
</Alert>
</DialogContent>
<DialogActions>
<Button onClick={() => handleOnClose('accept')} variant="outlined" color="primary">
@ -54,19 +67,32 @@ function DeleteConfirmDialog(props: DialogProps) {
</div>
);
}
export type RenameModel = {
id: number;
name: string;
description?: string;
}
function RenameDialog(props: DialogProps) {
const defaultModel: BasicMapInfo = { name: '', description: '' };
const [model, setModel] = React.useState<BasicMapInfo>(defaultModel);
const [errorInfo, setErroInfo] = React.useState<ErrorInfo>();
const defaultModel: RenameModel = { name: '', description: '', id: -1 };
const [model, setModel] = React.useState<RenameModel>(defaultModel);
const [errorInfo, setErroInfo] = React.useState<ErrorInfo>();
const dispatch = useDispatch()
const intl = useIntl();
useEffect(() => {
props.service.loadMapInfo(props.mapId)
.then((info: BasicMapInfo) => {
setModel(info);
})
const mapId: number = props.mapId;
if (mapId != -1) {
const mapInfo: MapInfo | undefined = useSelector(allMaps)
.find(m => m.id == props.mapId);
if (!mapInfo) {
throw "Please, reflesh the page.";
}
setModel({ ...mapInfo });
}
}, []);
const handleOnClose = (): void => {
@ -74,21 +100,22 @@ function RenameDialog(props: DialogProps) {
setModel(defaultModel);
setErroInfo(undefined);
props.onClose(false);
props.onClose();
};
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
// Stop form submit ...
event.preventDefault();
// Fire rest call ...
const mapId = props.mapId;
props.service.renameMap(mapId, model).
then(() => {
props.onClose(true);
}).catch((errorInfo: ErrorInfo) => {
setErroInfo(errorInfo)
});
// Fire rename ...
const mapId: number = props.mapId;
try {
dispatch(rename({ id: mapId, name: model.name, description: model.description }))
handleOnClose();
} catch (errorInfo) {
setErroInfo(errorInfo)
}
};
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
@ -118,9 +145,9 @@ function RenameDialog(props: DialogProps) {
{Boolean(errorInfo?.msg) ? <Alert severity="error" variant="filled" hidden={!Boolean(errorInfo?.msg)}>{errorInfo?.msg}</Alert> : null}
<FormControl margin="normal" required fullWidth>
<TextField name="name" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
value={model.name} onChange={handleOnChange}
error={Boolean(errorInfo?.fields?.get('name'))} helperText={errorInfo?.fields?.get('name')}
variant="filled" required={true}/>
value={model.name} onChange={handleOnChange}
error={Boolean(errorInfo?.fields?.get('name'))} helperText={errorInfo?.fields?.get('name')}
variant="filled" required={true} />
</FormControl>
<FormControl margin="normal" required fullWidth>
<TextField name="description" label={intl.formatMessage({ id: "action.rename-description-placeholder", defaultMessage: "Description" })} value={model.description} onChange={handleOnChange} variant="filled" />
@ -129,7 +156,7 @@ function RenameDialog(props: DialogProps) {
<DialogActions>
<Button color="primary" variant="outlined" type="submit">
<FormattedMessage id="action.rename-button" defaultMessage="Rename"/>
<FormattedMessage id="action.rename-button" defaultMessage="Rename" />
</Button>
<Button color="secondary" variant="outlined" autoFocus onClick={handleOnClose}>
@ -145,23 +172,23 @@ function RenameDialog(props: DialogProps) {
export type DialogType = 'share' | 'delete' | 'info' | 'duplicate' | 'export' | 'rename' | 'publish';
type ActionDialogProps = {
action: DialogType | undefined,
action?: DialogType,
mapId: number,
service: Service,
onClose: (reload: boolean) => void
onClose: () => void
}
const ActionDialog = (props: ActionDialogProps) => {
const handleOnClose = (reload: boolean): void => {
props.onClose(reload);
const handleOnClose = (): void => {
props.onClose();
}
const mapId = props.mapId;
const action = props.action;
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} />
<DeleteDialog open={action === 'delete'} onClose={handleOnClose} mapId={mapId} />
<RenameDialog open={action === 'rename'} onClose={handleOnClose} mapId={mapId} />
</span >
);
}

View File

@ -1,5 +1,4 @@
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';
@ -24,6 +23,8 @@ import MoreHorizIcon from '@material-ui/icons/MoreHoriz';
import { CSSProperties } from 'react';
import MapActionMenu, { ActionType } from './MapActionMenu';
import ActionDialog, { DialogType } from './ActionDialog';
import { useSelector } from 'react-redux';
import { allMaps, MapInfo } from '../../reducers/mapsListSlice';
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
if (b[orderBy] < a[orderBy]) {
@ -84,6 +85,7 @@ interface EnhancedTableProps {
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);
};
@ -209,19 +211,16 @@ type ActionPanelState = {
mapId: number
}
function EnhancedTable(props: ServiceProps) {
function EnhancedTable() {
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 mapsInfo: MapInfo[] = useSelector(allMaps);
const [activeRowAction, setActiveRowAction] = React.useState<ActionPanelState | undefined>(undefined);
type ActiveDialog = {
actionType: DialogType;
mapId: number
@ -229,13 +228,6 @@ function EnhancedTable(props: ServiceProps) {
};
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');
@ -244,7 +236,7 @@ function EnhancedTable(props: ServiceProps) {
const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>): void => {
if (event.target.checked) {
const newSelecteds = rows.map((n) => n.id);
const newSelecteds = mapsInfo.map((n) => n.id);
setSelected(newSelecteds);
return;
}
@ -306,7 +298,7 @@ function EnhancedTable(props: ServiceProps) {
const isSelected = (id: number) => selected.indexOf(id) !== -1;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);
const emptyRows = rowsPerPage - Math.min(rowsPerPage, mapsInfo.length - page * rowsPerPage);
return (
<div className={classes.root}>
@ -327,10 +319,10 @@ function EnhancedTable(props: ServiceProps) {
orderBy={orderBy}
onSelectAllClick={handleSelectAllClick}
onRequestSort={handleRequestSort}
rowCount={rows.length}
rowCount={mapsInfo.length}
/>
<TableBody>
{stableSort(rows, getComparator(order, orderBy))
{stableSort(mapsInfo, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row: MapInfo) => {
const isItemSelected = isSelected(row.id);
@ -390,7 +382,7 @@ function EnhancedTable(props: ServiceProps) {
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
count={rows.length}
count={mapsInfo.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
@ -399,16 +391,12 @@ function EnhancedTable(props: ServiceProps) {
</Paper>
{/* Action Dialog */}
<ActionDialog action={activeDialog?.actionType} onClose={(refresh: boolean) => setActiveDialog(undefined)} mapId={activeDialog ? activeDialog.mapId : -1} service={props.service}/>
<ActionDialog action={activeDialog?.actionType} onClose={() => setActiveDialog(undefined)} mapId={activeDialog ? activeDialog.mapId : -1}/>
</div>
);
}
type ServiceProps = {
service: Service
}
const MapsPage = (props: ServiceProps) => {
const MapsPage = () => {
useEffect(() => {
document.title = 'Maps | WiseMapping';
@ -423,7 +411,7 @@ const MapsPage = (props: ServiceProps) => {
<h1> Nav </h1>
</NavArea>
<MapsListArea>
<EnhancedTable service={props.service} />
<EnhancedTable/>
</MapsListArea>
</PageContainer>
);

View File

@ -12,7 +12,7 @@ import SubmitButton from '../submit-button'
import { StyledReCAPTCHA } from './styled';
import { PageContent } from '../../theme/global-style';
const RegistrationForm = (props: ServiceProps) => {
const RegistrationForm = () => {
const [email, setEmail] = useState('');
const [lastname, setLastname] = useState('')
const [firstname, setFirstname] = useState('');
@ -40,15 +40,15 @@ const RegistrationForm = (props: ServiceProps) => {
};
// Call Service ...
const service = props.service;
service.registerNewUser(user)
.then(() => {
history.push("/c/registration-success")
}).catch((error: ErrorInfo) => {
const errorMsg = error.msg ? error.msg : undefined;
setErrorMsg(errorMsg);
setDisableButton(false);
});
// const service = props.service;
// service.registerNewUser(user)
// .then(() => {
// history.push("/c/registration-success")
// }).catch((error: ErrorInfo) => {
// const errorMsg = error.msg ? error.msg : undefined;
// setErrorMsg(errorMsg);
// setDisableButton(false);
// });
}
@ -82,10 +82,7 @@ const RegistrationForm = (props: ServiceProps) => {
);
}
type ServiceProps = {
service: Service
}
const RegistationPage = (props: ServiceProps) => {
const RegistationPage = () => {
useEffect(() => {
document.title = 'Registration | WiseMapping';
@ -94,7 +91,7 @@ const RegistationPage = (props: ServiceProps) => {
return (
<div>
<Header type='only-signin' />
<RegistrationForm service={props.service} />
<RegistrationForm/>
<Footer />
</div>
);

View File

@ -2,43 +2,13 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import { BrowserRouter as Router } from 'react-router-dom';
import axios from 'axios'
import axios from 'axios';
type RutimeConfig = {
apiBaseUrl: string;
}
async function loadRuntimeConfig() {
let result: RutimeConfig | undefined;
await axios.get("runtime-config.json"
).then(response => {
// All was ok, let's sent to success page ...
result = response.data as RutimeConfig;
console.log("Dynamic configuration->" + response.data);
}).catch(e => {
console.log(e)
});
if (!result) {
// Ok, try to create a default configuration relative to the current path ...
console.log("Configuration could not be loaded, falback to default config.")
const location = window.location;
const basePath = location.protocol + "//" + location.host + "/" + location.pathname.split('/')[1]
result = {
apiBaseUrl: basePath
}
}
return result;
}
async function bootstrapApplication() {
const config: RutimeConfig = await loadRuntimeConfig();
ReactDOM.render(
<Router>
<App baseRestUrl={config.apiBaseUrl} />
<App/>
</Router>,
document.getElementById('root') as HTMLElement
)

View File

@ -0,0 +1,113 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import axios from 'axios';
import { RestService, Service } from '../services/Service';
function createMapInfo(
id: number,
starred: boolean,
name: string,
labels: [string],
creator: string,
modified: number,
description: string
): MapInfo {
return { id, name, labels, creator, modified, starred, description };
}
const maps = [
createMapInfo(1, true, "El Mapa", [""], "Paulo", 67, ""),
createMapInfo(2, false, "El Mapa2", [""], "Paulo2", 67, ""),
createMapInfo(3, false, "El Mapa3", [""], "Paulo3", 67, "")
];
export type MapInfo = {
id: number;
starred: boolean;
name: string;
labels: [string];
creator: string;
modified: number;
description: string
}
interface MapsListState {
maps: MapInfo[]
}
type RutimeConfig = {
apiBaseUrl: string;
}
async function loadRuntimeConfig() {
let result: RutimeConfig | undefined;
await axios.get("runtime-config.json"
).then(response => {
// All was ok, let's sent to success page ...
result = response.data as RutimeConfig;
console.log("Dynamic configuration->" + response.data);
}).catch(e => {
console.log(e)
});
if (!result) {
// Ok, try to create a default configuration relative to the current path ...
console.log("Configuration could not be loaded, falback to default config.")
const location = window.location;
const basePath = location.protocol + "//" + location.host + "/" + location.pathname.split('/')[1]
result = {
apiBaseUrl: basePath
}
}
return result;
}
const initialState: MapsListState = { maps: maps };
const service: Service = new RestService("", () => { console.log("401 error") });
type RemovePayload = {
id: number;
}
type RenamePayload = {
id: number;
name: string;
description: string | undefined;
}
export const mapsListSlice = createSlice({
name: 'maps',
initialState: initialState,
reducers: {
remove(state, action: PayloadAction<RemovePayload>) {
const maps: MapInfo[] = state.maps as MapInfo[];
const payload = action.payload;
state.maps = maps.filter(map => map.id != payload.id);
},
rename(state, action: PayloadAction<RenamePayload>) {
let maps: MapInfo[] = state.maps as MapInfo[];
const payload = action.payload;
const mapInfo = maps.find(m => m.id == payload.id);
if (mapInfo) {
mapInfo.name = payload.name;
mapInfo.description = payload.description ? payload.description: "";
// Remove and add the new map.
maps = maps.filter(map => map.id != payload.id);
maps.push(mapInfo);
state.maps = maps;
}
}
},
});
export const allMaps = (state: any): MapInfo[] => state.mapsList.maps;
export const { remove, rename } = mapsListSlice.actions
export default mapsListSlice.reducer

View File

@ -1,3 +1,4 @@
import { Description } from '@material-ui/icons'
import axios from 'axios'
export type NewUser = {
@ -15,11 +16,12 @@ export type MapInfo = {
labels: [string];
creator: string;
modified: number;
description: string;
}
export type BasicMapInfo = {
name: string;
description: string;
description?: string;
}
export type FieldError = {
@ -86,6 +88,7 @@ class RestService implements Service {
}
async fetchAllMaps(): Promise<MapInfo[]> {
function createMapInfo(
id: number,
starred: boolean,
@ -93,14 +96,15 @@ class RestService implements Service {
labels: [string],
creator: string,
modified: number,
description: string
): MapInfo {
return { id, name, labels, creator, modified, starred };
return { id, name, labels, creator, modified, starred, description};
}
const maps = [
createMapInfo(1, true, "El Mapa", [""], "Paulo", 67,),
createMapInfo(2, false, "El Mapa2", [""], "Paulo2", 67),
createMapInfo(3, false, "El Mapa3", [""], "Paulo3", 67)
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);

View File

@ -0,0 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import mapsListReducer from './reducers/mapsListSlice';
// Create Service object...
const store = configureStore({
reducer: {
mapsList: mapsListReducer
}
});
export default store;