diff --git a/packages/webapp/package.json b/packages/webapp/package.json
index 7833a9c1..721abb41 100644
--- a/packages/webapp/package.json
+++ b/packages/webapp/package.json
@@ -17,9 +17,6 @@
"@babel/preset-env": "^7.12.7",
"@babel/preset-react": "^7.12.7",
"@formatjs/cli": "^2.13.15",
- "@types/react": "^17.0.0",
- "@types/react-dom": "^17.0.0",
- "@types/react-router-dom": "^5.1.6",
"@typescript-eslint/eslint-plugin": "^4.8.1",
"@typescript-eslint/parser": "^4.8.1",
"babel-loader": "^8.2.2",
@@ -49,14 +46,15 @@
"@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",
- "react": "^17.0.1",
- "react-dom": "^17.0.1",
+ "axios": "^0.14.0",
+ "react": "^17.0.0",
+ "react-dom": "^17.0.0",
"react-google-recaptcha": "^2.1.0",
- "react-intl": "^5.10.6",
+ "react-intl": "^3.0.0",
+ "react-query": "^3.5.5",
"react-redux": "^7.2.2",
+ "react-router": "^5.1.8",
"react-router-dom": "^5.2.0",
- "styled-components": "^5.2.1"
+ "styled-components": "^5.1.7"
}
-}
\ No newline at end of file
+}
diff --git a/packages/webapp/src/app.tsx b/packages/webapp/src/app.tsx
index 5f0706ad..57a37420 100644
--- a/packages/webapp/src/app.tsx
+++ b/packages/webapp/src/app.tsx
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
-import { IntlProvider } from 'react-intl'
+import { IntlProvider } from 'react-intl';
+import { Route, Switch, Redirect, BrowserRouter as Router } from 'react-router-dom';
import { GlobalStyle } from './theme/global-style';
import RegistrationSuccessPage from './components/registration-success-page';
@@ -7,14 +8,7 @@ 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 store from "./store"
-
-import {
- Route,
- Switch,
- Redirect,
- BrowserRouter as Router,
-} from 'react-router-dom';
+import store from "./store";
import { ForgotPasswordPage } from './components/forgot-password-page';
import { Provider } from 'react-redux';
diff --git a/packages/webapp/src/components/login-page/index.tsx b/packages/webapp/src/components/login-page/index.tsx
index 9befe644..0a71cf8a 100644
--- a/packages/webapp/src/components/login-page/index.tsx
+++ b/packages/webapp/src/components/login-page/index.tsx
@@ -2,18 +2,18 @@ import React, { useEffect } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { Link } from 'react-router-dom'
-import {PageContent} from '../../theme/global-style';
+import { PageContent } from '../../theme/global-style';
import FormErrorDialog from '../form-error-dialog'
import Header from '../header'
import Footer from '../footer'
import SubmitButton from '../submit-button'
-
+
const ConfigStatusMessage = (props: any) => {
const enabled = props.enabled
- let result = null;
+ let result;
if (enabled === true) {
result = (
@@ -21,7 +21,7 @@ const ConfigStatusMessage = (props: any) => {
);
}
- return result;
+ return result ? result : null;
}
const LoginError = () => {
diff --git a/packages/webapp/src/components/maps-page/ActionDialog.tsx b/packages/webapp/src/components/maps-page/ActionDialog.tsx
index 986a7cfd..69b42d82 100644
--- a/packages/webapp/src/components/maps-page/ActionDialog.tsx
+++ b/packages/webapp/src/components/maps-page/ActionDialog.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react';
+import React from 'react';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
@@ -9,13 +9,11 @@ import { FormattedMessage, useIntl } from 'react-intl';
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 { useSelector } from 'react-redux';
import {
- allMaps,
- remove,
- rename
-} from '../../reducers/mapsListSlice'
-import { Description } from '@material-ui/icons';
+ activeInstance,
+} from '../../reducers/serviceSlice'
+import { useMutation, useQuery, useQueryClient } from 'react-query';
type DialogProps = {
@@ -30,17 +28,32 @@ export type BasicMapInfo = {
}
function DeleteDialog(props: DialogProps) {
- const dispatch = useDispatch()
+ const service: Service = useSelector(activeInstance);
+ const queryClient = useQueryClient();
const mapId = props.mapId;
- const mapInfo: MapInfo | undefined = useSelector(allMaps).
- find(m => m.id == mapId);
+ const mutation = useMutation((id: number) => service.deleteMap(id),
+ {
+ onSuccess: () => {
+ queryClient.invalidateQueries()
+ props.onClose();
+ }
+ }
+ );
+
+ const { isLoading, error, data } = useQuery('maps', () => {
+ return service.fetchAllMaps();
+ });
+
+ let mapInfo: MapInfo | undefined = undefined;
+ if (data) {
+ mapInfo = data.find((m) => m.id == mapId);
+ }
const handleOnClose = (action: 'accept' | undefined): void => {
if (action == 'accept' && mapInfo) {
- dispatch(remove({ id: mapId }))
+ mutation.mutate(mapId);
}
- props.onClose();
};
return (
@@ -78,22 +91,21 @@ function RenameDialog(props: DialogProps) {
const defaultModel: RenameModel = { name: '', description: '', id: -1 };
const [model, setModel] = React.useState(defaultModel);
const [errorInfo, setErroInfo] = React.useState();
- const dispatch = useDispatch()
const intl = useIntl();
- useEffect(() => {
- const mapId: number = props.mapId;
- if (mapId != -1) {
- const mapInfo: MapInfo | undefined = useSelector(allMaps)
- .find(m => m.id == props.mapId);
+ // useEffect(() => {
+ // const mapId: number = props.mapId;
+ // if (mapId != -1) {
+ // const mapInfo: MapInfo | undefined = useSelector(activeInstance)
+ // .find(m => m.id == props.mapId);
- if (!mapInfo) {
- throw "Please, reflesh the page.";
- }
+ // if (!mapInfo) {
+ // throw "Please, reflesh the page.";
+ // }
- setModel({ ...mapInfo });
- }
- }, []);
+ // setModel({ ...mapInfo });
+ // }
+ // }, []);
const handleOnClose = (): void => {
// Clean Up ...
@@ -110,9 +122,9 @@ function RenameDialog(props: DialogProps) {
// Fire rename ...
const mapId: number = props.mapId;
try {
- dispatch(rename({ id: mapId, name: model.name, description: model.description }))
+ // dispatch(rename({ id: mapId, name: model.name, description: model.description }))
handleOnClose();
-
+
} catch (errorInfo) {
setErroInfo(errorInfo)
}
diff --git a/packages/webapp/src/components/maps-page/index.tsx b/packages/webapp/src/components/maps-page/index.tsx
index 02997df9..8a93a65d 100644
--- a/packages/webapp/src/components/maps-page/index.tsx
+++ b/packages/webapp/src/components/maps-page/index.tsx
@@ -24,7 +24,11 @@ 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';
+import { activeInstance } from '../../reducers/serviceSlice';
+import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
+import { ErrorInfo, MapInfo, Service } from '../../services/Service';
+
+
function descendingComparator(a: T, b: T, orderBy: keyof T) {
if (b[orderBy] < a[orderBy]) {
@@ -41,7 +45,7 @@ type Order = 'asc' | 'desc';
function getComparator(
order: Order,
orderBy: Key,
-): (a: { [key in Key]: number | string | boolean | string[] }, b: { [key in Key]: number | string | string[] | boolean }) => number {
+): (a: { [key in Key]: number | string | boolean | string[] | undefined }, b: { [key in Key]: number | string | string[] | boolean }) => number {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
@@ -211,14 +215,22 @@ type ActionPanelState = {
mapId: number
}
-function EnhancedTable() {
+const EnhancedTable = () => {
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('modified');
const [selected, setSelected] = React.useState([]);
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
- const mapsInfo: MapInfo[] = useSelector(allMaps);
+ const service: Service = useSelector(activeInstance);
+
+ const { isLoading, error, data } = useQuery('maps', async () => {
+
+ const result = await service.fetchAllMaps();
+ return result;
+ });
+ const mapsInfo: MapInfo[] = data ? data : [];
+
const [activeRowAction, setActiveRowAction] = React.useState(undefined);
type ActiveDialog = {
@@ -322,7 +334,7 @@ function EnhancedTable() {
rowCount={mapsInfo.length}
/>
- {stableSort(mapsInfo, getComparator(order, orderBy))
+ {isLoading ? () : stableSort(mapsInfo, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row: MapInfo) => {
const isItemSelected = isSelected(row.id);
@@ -391,11 +403,14 @@ function EnhancedTable() {
{/* Action Dialog */}
- setActiveDialog(undefined)} mapId={activeDialog ? activeDialog.mapId : -1}/>
+ setActiveDialog(undefined)} mapId={activeDialog ? activeDialog.mapId : -1} />
);
}
+
+
+const queryClient = new QueryClient();
const MapsPage = () => {
useEffect(() => {
@@ -403,20 +418,21 @@ const MapsPage = () => {
}, []);
return (
-
-
- Header
-
-
- Nav
-
-
-
-
-
+
+
+
+ Header
+
+
+ Nav
+
+
+
+
+
+
);
}
-
export default MapsPage;
diff --git a/packages/webapp/src/index.tsx b/packages/webapp/src/index.tsx
index 54f15a9c..f95ac25c 100644
--- a/packages/webapp/src/index.tsx
+++ b/packages/webapp/src/index.tsx
@@ -1,17 +1,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';
-
async function bootstrapApplication() {
ReactDOM.render(
-
-
- ,
+ ,
document.getElementById('root') as HTMLElement
)
}
bootstrapApplication()
+
\ No newline at end of file
diff --git a/packages/webapp/src/reducers/mapsListSlice.ts b/packages/webapp/src/reducers/mapsListSlice.ts
deleted file mode 100644
index 0d8d877c..00000000
--- a/packages/webapp/src/reducers/mapsListSlice.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-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) {
- const maps: MapInfo[] = state.maps as MapInfo[];
- const payload = action.payload;
- state.maps = maps.filter(map => map.id != payload.id);
- },
- rename(state, action: PayloadAction) {
- 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
\ No newline at end of file
diff --git a/packages/webapp/src/reducers/serviceSlice.ts b/packages/webapp/src/reducers/serviceSlice.ts
new file mode 100644
index 00000000..c0eb9b5b
--- /dev/null
+++ b/packages/webapp/src/reducers/serviceSlice.ts
@@ -0,0 +1,58 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+import axios from 'axios';
+import { ErrorInfo } from 'react';
+import { RestService, Service } from '../services/Service';
+
+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;
+}
+
+
+interface ServiceState {
+ instance: Service
+}
+
+const initialState: ServiceState = {
+ instance: new RestService("", () => { console.log("401 error") })
+};
+
+export const serviceSlice = createSlice({
+ name: "service",
+ initialState: initialState,
+ reducers: {
+ initialize(state, action: PayloadAction) {
+ state.instance = new RestService("", () => { console.log("401 error") });
+ }
+ },
+});
+
+export const activeInstance = (state: any): Service => {
+ return state.service.instance;
+}
+export default serviceSlice.reducer
+
diff --git a/packages/webapp/src/services/Service.ts b/packages/webapp/src/services/Service.ts
index 7a11f1a1..573beb86 100644
--- a/packages/webapp/src/services/Service.ts
+++ b/packages/webapp/src/services/Service.ts
@@ -1,4 +1,3 @@
-import { Description } from '@material-ui/icons'
import axios from 'axios'
export type NewUser = {
@@ -47,11 +46,31 @@ interface Service {
class RestService implements Service {
private baseUrl: string;
private authFailed: () => void
+ private maps: MapInfo[] = [];
constructor(baseUrl: string, authFailed: () => void) {
this.baseUrl = baseUrl;
+
+ // Remove, just for develop ....
+ 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 };
+ }
+ this.maps = [
+ createMapInfo(1, true, "El Mapa", [""], "Paulo", 67, ""),
+ createMapInfo(2, false, "El Mapa2", [""], "Paulo2", 67, ""),
+ createMapInfo(3, false, "El Mapa3", [""], "Paulo3", 67, "")
+ ];
}
+
loadMapInfo(id: number): Promise {
return Promise.resolve({ name: 'My Map', description: 'My Description' });
}
@@ -66,7 +85,8 @@ class RestService implements Service {
});
}
- async deleteMap(id: number): Promise {
+ deleteMap(id: number): Promise {
+ this.maps = this.maps.filter(m => m.id != id);
return Promise.resolve();
}
@@ -87,27 +107,8 @@ class RestService implements Service {
return new Promise(handler);
}
- async fetchAllMaps(): Promise {
-
- 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,"")
- ];
-
- return Promise.resolve(maps);
+ fetchAllMaps(): Promise {
+ return Promise.resolve(this.maps);
}
resetPassword(email: string): Promise {
diff --git a/packages/webapp/src/store.ts b/packages/webapp/src/store.ts
index 65ce9614..a9afbf8d 100644
--- a/packages/webapp/src/store.ts
+++ b/packages/webapp/src/store.ts
@@ -1,12 +1,12 @@
-import { configureStore } from '@reduxjs/toolkit';
-import mapsListReducer from './reducers/mapsListSlice';
+import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
+import serviceReducer from './reducers/serviceSlice';
- // Create Service object...
- const store = configureStore({
- reducer: {
- mapsList: mapsListReducer
- }
- });
+// Create Service object...
+const store = configureStore({
+ reducer: {
+ service: serviceReducer
+ }
+});
- export default store;
\ No newline at end of file
+export default store;
\ No newline at end of file
diff --git a/packages/webapp/src/tsconfig.json b/packages/webapp/src/tsconfig.json
index ad7c4282..5609ad7e 100644
--- a/packages/webapp/src/tsconfig.json
+++ b/packages/webapp/src/tsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
- "noImplicitAny": true,
+ "noImplicitAny": false,
"module": "commonjs",
"target": "es5",
"jsx": "react",
diff --git a/packages/webapp/tsconfig.json b/packages/webapp/tsconfig.json
index e1cb573a..0dd8ee40 100644
--- a/packages/webapp/tsconfig.json
+++ b/packages/webapp/tsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
- "noImplicitAny": true,
+ "noImplicitAny": false,
"module": "commonjs",
"target": "es5",
"jsx": "react",