mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-26 08:04:56 +01:00
formatting all the files
This commit is contained in:
parent
acf2da9272
commit
692f98f2bb
@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import { StyledCanvas } from './styled';
|
import { StyledCanvas } from './styled'
|
||||||
|
|
||||||
const Canvas = (): React.ReactElement => (
|
const Canvas = (): React.ReactElement => <StyledCanvas>canvas</StyledCanvas>
|
||||||
<StyledCanvas>canvas</StyledCanvas>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Canvas;
|
export default Canvas
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components'
|
||||||
|
|
||||||
export const StyledCanvas = styled.div`
|
export const StyledCanvas = styled.div`
|
||||||
height: 100%
|
height: 100%
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
`;
|
`
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import { StyledFooter } from './styled';
|
import { StyledFooter } from './styled'
|
||||||
|
|
||||||
const Footer = (): React.ReactElement => (
|
const Footer = (): React.ReactElement => <StyledFooter>footer</StyledFooter>
|
||||||
<StyledFooter>footer</StyledFooter>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Footer;
|
export default Footer
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components'
|
||||||
import { times } from '../../size';
|
import { times } from '../../size'
|
||||||
|
|
||||||
export const StyledFooter = styled.div`
|
export const StyledFooter = styled.div`
|
||||||
height: ${times(10)};
|
height: ${times(10)};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
`;
|
`
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import Footer from '../footer';
|
import Footer from '../footer'
|
||||||
import TopBar from '../top-bar';
|
import TopBar from '../top-bar'
|
||||||
import Canvas from '../canvas';
|
import Canvas from '../canvas'
|
||||||
import { StyledFrame } from './styled';
|
import { StyledFrame } from './styled'
|
||||||
|
|
||||||
const Frame = (): React.ReactElement => (
|
const Frame = (): React.ReactElement => (
|
||||||
<StyledFrame>
|
<StyledFrame>
|
||||||
@ -10,6 +10,6 @@ const Frame = (): React.ReactElement => (
|
|||||||
<Canvas />
|
<Canvas />
|
||||||
<Footer />
|
<Footer />
|
||||||
</StyledFrame>
|
</StyledFrame>
|
||||||
);
|
)
|
||||||
|
|
||||||
export default Frame;
|
export default Frame
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components'
|
||||||
|
|
||||||
export const StyledFrame = styled.div`
|
export const StyledFrame = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`;
|
`
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import { StyledTopBar } from './styled'
|
import { StyledTopBar } from './styled'
|
||||||
|
|
||||||
const TopBar = ():React.ReactElement => (
|
const TopBar = (): React.ReactElement => <StyledTopBar>top bar</StyledTopBar>
|
||||||
<StyledTopBar>top bar</StyledTopBar>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default TopBar;
|
export default TopBar
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components'
|
||||||
import { times } from '../../size';
|
import { times } from '../../size'
|
||||||
|
|
||||||
export const StyledTopBar = styled.div`
|
export const StyledTopBar = styled.div`
|
||||||
height: ${times(10)};
|
height: ${times(10)};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
`;
|
`
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import Editor from './components/frame';
|
import Editor from './components/frame'
|
||||||
|
|
||||||
export default Editor;
|
export default Editor
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
|
const unit = 4 // pixels
|
||||||
|
|
||||||
const unit = 4; // pixels
|
export const XS = '4px'
|
||||||
|
export const S = '8px'
|
||||||
|
export const M = '16px'
|
||||||
|
export const L = '24px'
|
||||||
|
export const XL = '24px'
|
||||||
|
|
||||||
export const XS = '4px';
|
export const times = (n: number): string => `${unit * n}px`
|
||||||
export const S = '8px';
|
|
||||||
export const M = '16px';
|
|
||||||
export const L = '24px';
|
|
||||||
export const XL = '24px';
|
|
||||||
|
|
||||||
export const times = (n: number):string => `${unit * n}px`;
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import MapsPage from "../pageObject/MapsPage";
|
import MapsPage from '../pageObject/MapsPage'
|
||||||
|
|
||||||
context("Maps Page", () => {
|
context('Maps Page', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit("http://localhost:3000/c/maps");
|
cy.visit('http://localhost:3000/c/maps')
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should load the maps page", () => {
|
it('should load the maps page', () => {
|
||||||
MapsPage.isLoaded();
|
MapsPage.isLoaded()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should open the create dialog", () => {
|
it('should open the create dialog', () => {
|
||||||
MapsPage.create();
|
MapsPage.create()
|
||||||
MapsPage.isCreateDialogVisible();
|
MapsPage.isCreateDialogVisible()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
export default class MapsPage {
|
export default class MapsPage {
|
||||||
static isLoaded() {
|
static isLoaded() {
|
||||||
return cy.findByTestId("create");
|
return cy.findByTestId('create')
|
||||||
}
|
}
|
||||||
|
|
||||||
static create() {
|
static create() {
|
||||||
return cy.findByTestId("create").click();
|
return cy.findByTestId('create').click()
|
||||||
}
|
}
|
||||||
|
|
||||||
static isCreateDialogVisible() {
|
static isCreateDialogVisible() {
|
||||||
//TODO move to findByText when the double create dialog issue is solved
|
//TODO move to findByText when the double create dialog issue is solved
|
||||||
return cy.findAllByText("Create a new mindmap");
|
return cy.findAllByText('Create a new mindmap')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
packages/webapp/src/@types/index.d.ts
vendored
4
packages/webapp/src/@types/index.d.ts
vendored
@ -1,2 +1,2 @@
|
|||||||
declare module '*.png';
|
declare module '*.png'
|
||||||
declare module '*.svg';
|
declare module '*.svg'
|
||||||
|
12
packages/webapp/src/@types/typings.d.ts
vendored
12
packages/webapp/src/@types/typings.d.ts
vendored
@ -1,9 +1,9 @@
|
|||||||
declare module '*.jpeg';
|
declare module '*.jpeg'
|
||||||
declare module '*.jpg';
|
declare module '*.jpg'
|
||||||
declare module '*.jpeg';
|
declare module '*.jpeg'
|
||||||
declare module '*.png';
|
declare module '*.png'
|
||||||
declare module '*.svg';
|
declare module '*.svg'
|
||||||
declare module '*.json';
|
declare module '*.json'
|
||||||
|
|
||||||
import { Dayjs } from 'dayjs'
|
import { Dayjs } from 'dayjs'
|
||||||
type DateType = string | number | Date | Dayjs
|
type DateType = string | number | Date | Dayjs
|
||||||
|
@ -1,38 +1,42 @@
|
|||||||
import React, { ReactElement } from 'react';
|
import React, { ReactElement } from 'react'
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl'
|
||||||
import { Route, Switch, Redirect, BrowserRouter as Router } from 'react-router-dom';
|
import { Route, Switch, Redirect, BrowserRouter as Router } from 'react-router-dom'
|
||||||
|
|
||||||
import RegistrationSuccessPage from './components/registration-success-page';
|
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 store from "./redux/store";
|
import store from './redux/store'
|
||||||
import { ForgotPasswordPage } from './components/forgot-password-page';
|
import { ForgotPasswordPage } from './components/forgot-password-page'
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux'
|
||||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
import { QueryClient, QueryClientProvider } from 'react-query'
|
||||||
import { theme } from './theme'
|
import { theme } from './theme'
|
||||||
import AppI18n, { Locales } from './classes/app-i18n';
|
import AppI18n, { Locales } from './classes/app-i18n'
|
||||||
import MapsPage from './components/maps-page';
|
import MapsPage from './components/maps-page'
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
import { ThemeProvider } from '@material-ui/core/styles';
|
import { ThemeProvider } from '@material-ui/core/styles'
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
refetchIntervalInBackground: false,
|
refetchIntervalInBackground: false,
|
||||||
staleTime: 5 * 1000 * 60 // 10 minutes
|
staleTime: 5 * 1000 * 60, // 10 minutes
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const App = ():ReactElement => {
|
const App = (): ReactElement => {
|
||||||
const appi18n = new AppI18n();
|
const appi18n = new AppI18n()
|
||||||
const locale = appi18n.getBrowserLocale();
|
const locale = appi18n.getBrowserLocale()
|
||||||
|
|
||||||
return locale.message ? (
|
return locale.message ? (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<IntlProvider locale={locale.code} defaultLocale={Locales.EN.code} messages={locale.message as Record<string, string> }>
|
<IntlProvider
|
||||||
|
locale={locale.code}
|
||||||
|
defaultLocale={Locales.EN.code}
|
||||||
|
messages={locale.message as Record<string, string>}
|
||||||
|
>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<Router>
|
<Router>
|
||||||
@ -44,11 +48,17 @@ const App = ():ReactElement => {
|
|||||||
<Route path="/c/registration">
|
<Route path="/c/registration">
|
||||||
<RegistationPage />
|
<RegistationPage />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/c/registration-success" component={RegistrationSuccessPage} />
|
<Route
|
||||||
|
path="/c/registration-success"
|
||||||
|
component={RegistrationSuccessPage}
|
||||||
|
/>
|
||||||
<Route path="/c/forgot-password">
|
<Route path="/c/forgot-password">
|
||||||
<ForgotPasswordPage />
|
<ForgotPasswordPage />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/c/forgot-password-success" component={ForgotPasswordSuccessPage} />
|
<Route
|
||||||
|
path="/c/forgot-password-success"
|
||||||
|
component={ForgotPasswordSuccessPage}
|
||||||
|
/>
|
||||||
<Route path="/c/maps/">
|
<Route path="/c/maps/">
|
||||||
<MapsPage />
|
<MapsPage />
|
||||||
</Route>
|
</Route>
|
||||||
@ -58,8 +68,9 @@ const App = ():ReactElement => {
|
|||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
) : (
|
||||||
) : (<div>Loading ... </div>)
|
<div>Loading ... </div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App
|
||||||
|
@ -1,48 +1,46 @@
|
|||||||
import { fetchAccount } from './../../redux/clientSlice';
|
import { fetchAccount } from './../../redux/clientSlice'
|
||||||
import 'dayjs/locale/fr';
|
import 'dayjs/locale/fr'
|
||||||
import 'dayjs/locale/en';
|
import 'dayjs/locale/en'
|
||||||
import 'dayjs/locale/es';
|
import 'dayjs/locale/es'
|
||||||
|
|
||||||
export class Locale {
|
export class Locale {
|
||||||
code: LocaleCode;
|
code: LocaleCode
|
||||||
label: string;
|
label: string
|
||||||
message: Record<string, string> ;
|
message: Record<string, string>
|
||||||
|
|
||||||
constructor(code: LocaleCode, label: string, message: unknown) {
|
constructor(code: LocaleCode, label: string, message: unknown) {
|
||||||
this.code = code;
|
this.code = code
|
||||||
this.label = label;
|
this.label = label
|
||||||
this.message = message as Record<string, string>;
|
this.message = message as Record<string, string>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class AppI18n {
|
export default class AppI18n {
|
||||||
public getUserLocale(): Locale {
|
public getUserLocale(): Locale {
|
||||||
const account = fetchAccount();
|
const account = fetchAccount()
|
||||||
return account ? account.locale : this.getBrowserLocale();
|
return account ? account.locale : this.getBrowserLocale()
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBrowserLocale(): Locale {
|
public getBrowserLocale(): Locale {
|
||||||
let localeCode = (navigator.languages && navigator.languages[0])
|
let localeCode = (navigator.languages && navigator.languages[0]) || navigator.language
|
||||||
|| navigator.language;
|
|
||||||
|
|
||||||
// Just remove the variant ...
|
// Just remove the variant ...
|
||||||
localeCode = localeCode.split('-')[0];
|
localeCode = localeCode.split('-')[0]
|
||||||
|
|
||||||
let result = Locales.EN;
|
let result = Locales.EN
|
||||||
try {
|
try {
|
||||||
result = localeFromStr(localeCode)
|
result = localeFromStr(localeCode)
|
||||||
} catch {
|
} catch {
|
||||||
console.warn(`Unsupported languange code ${localeCode}`);
|
console.warn(`Unsupported languange code ${localeCode}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LocaleCode = 'en' | 'es' | 'fr' | 'de';
|
export type LocaleCode = 'en' | 'es' | 'fr' | 'de'
|
||||||
|
|
||||||
export const Locales =
|
export const Locales = {
|
||||||
{
|
|
||||||
EN: new Locale('en', 'English', require('./../../compiled-lang/en.json')),
|
EN: new Locale('en', 'English', require('./../../compiled-lang/en.json')),
|
||||||
ES: new Locale('es', 'Español', require('./../../compiled-lang/es.json')),
|
ES: new Locale('es', 'Español', require('./../../compiled-lang/es.json')),
|
||||||
DE: new Locale('fr', 'Français', require('./../../compiled-lang/fr.json')),
|
DE: new Locale('fr', 'Français', require('./../../compiled-lang/fr.json')),
|
||||||
@ -50,15 +48,13 @@ export const Locales =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const localeFromStr = (code: string): Locale => {
|
export const localeFromStr = (code: string): Locale => {
|
||||||
const locales: Locale[] = Object
|
const locales: Locale[] = Object.values(Locales)
|
||||||
.values(Locales);
|
|
||||||
|
|
||||||
const result = locales
|
const result = locales.find((l) => l.code == code)
|
||||||
.find((l) => l.code == code);
|
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw `Language code could not be found in list of default supported: + ${code}`
|
throw `Language code could not be found in list of default supported: + ${code}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { activeInstanceStatus, ClientStatus } from '../../../redux/clientSlice';
|
import { activeInstanceStatus, ClientStatus } from '../../../redux/clientSlice'
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl'
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import Alert from '@material-ui/lab/Alert';
|
import Alert from '@material-ui/lab/Alert'
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import AlertTitle from '@material-ui/lab/AlertTitle';
|
import AlertTitle from '@material-ui/lab/AlertTitle'
|
||||||
|
|
||||||
const ClientHealthSentinel = (): React.ReactElement => {
|
const ClientHealthSentinel = (): React.ReactElement => {
|
||||||
const status: ClientStatus = useSelector(activeInstanceStatus);
|
const status: ClientStatus = useSelector(activeInstanceStatus)
|
||||||
|
|
||||||
const handleOnClose = () => {
|
const handleOnClose = () => {
|
||||||
window.location.href = '/c/login';
|
window.location.href = '/c/login'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -23,30 +23,33 @@ const ClientHealthSentinel = (): React.ReactElement => {
|
|||||||
open={status.state != 'healthy'}
|
open={status.state != 'healthy'}
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
maxWidth="sm"
|
maxWidth="sm"
|
||||||
fullWidth={true}>
|
fullWidth={true}
|
||||||
|
>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<FormattedMessage id="expired.title" defaultMessage="Your session has expired" />
|
<FormattedMessage
|
||||||
|
id="expired.title"
|
||||||
|
defaultMessage="Your session has expired"
|
||||||
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Alert severity="error">
|
<Alert severity="error">
|
||||||
<AlertTitle><FormattedMessage id="expired.description" defaultMessage="Your current session has expired. Please, sign in and try again." /></AlertTitle>
|
<AlertTitle>
|
||||||
|
<FormattedMessage
|
||||||
|
id="expired.description"
|
||||||
|
defaultMessage="Your current session has expired. Please, sign in and try again."
|
||||||
|
/>
|
||||||
|
</AlertTitle>
|
||||||
</Alert>
|
</Alert>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button type="button" color="primary" size="medium" onClick={handleOnClose}>
|
||||||
type="button"
|
|
||||||
color="primary"
|
|
||||||
size="medium"
|
|
||||||
onClick={handleOnClose} >
|
|
||||||
<FormattedMessage id="action.close-button" defaultMessage="Close" />
|
<FormattedMessage id="action.close-button" defaultMessage="Close" />
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
};
|
}
|
||||||
export default ClientHealthSentinel;
|
export default ClientHealthSentinel
|
||||||
|
@ -1,109 +1,108 @@
|
|||||||
import { Locale, LocaleCode } from "../app-i18n"
|
import { Locale, LocaleCode } from '../app-i18n'
|
||||||
|
|
||||||
export type NewUser = {
|
export type NewUser = {
|
||||||
email: string;
|
email: string
|
||||||
firstname: string;
|
firstname: string
|
||||||
lastname: string;
|
lastname: string
|
||||||
password: string;
|
password: string
|
||||||
recaptcha: string | null;
|
recaptcha: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ImportMapInfo = {
|
export type ImportMapInfo = {
|
||||||
title: string;
|
title: string
|
||||||
description?: string;
|
description?: string
|
||||||
contentType?: string;
|
contentType?: string
|
||||||
content?: ArrayBuffer | null | string;
|
content?: ArrayBuffer | null | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Label = {
|
export type Label = {
|
||||||
id: number;
|
id: number
|
||||||
title: string;
|
title: string
|
||||||
color: string;
|
color: string
|
||||||
iconName: string;
|
iconName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Role = 'owner' | 'editor' | 'viewer';
|
export type Role = 'owner' | 'editor' | 'viewer'
|
||||||
|
|
||||||
export type MapInfo = {
|
export type MapInfo = {
|
||||||
id: number;
|
id: number
|
||||||
starred: boolean;
|
starred: boolean
|
||||||
title: string;
|
title: string
|
||||||
labels: number[];
|
labels: number[]
|
||||||
createdBy: string;
|
createdBy: string
|
||||||
creationTime: string;
|
creationTime: string
|
||||||
lastModificationBy: string;
|
lastModificationBy: string
|
||||||
lastModificationTime: string;
|
lastModificationTime: string
|
||||||
description: string;
|
description: string
|
||||||
isPublic: boolean;
|
isPublic: boolean
|
||||||
role: Role;
|
role: Role
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChangeHistory = {
|
export type ChangeHistory = {
|
||||||
id: number;
|
id: number
|
||||||
lastModificationBy: string;
|
lastModificationBy: string
|
||||||
lastModificationTime: string;
|
lastModificationTime: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BasicMapInfo = {
|
export type BasicMapInfo = {
|
||||||
title: string;
|
title: string
|
||||||
description?: string;
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FieldError = {
|
export type FieldError = {
|
||||||
id: string,
|
id: string
|
||||||
msg: string
|
msg: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ErrorInfo = {
|
export type ErrorInfo = {
|
||||||
msg?: string;
|
msg?: string
|
||||||
fields?: Map<string, string>;
|
fields?: Map<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AccountInfo = {
|
export type AccountInfo = {
|
||||||
firstname: string;
|
firstname: string
|
||||||
lastname: string;
|
lastname: string
|
||||||
email: string;
|
email: string
|
||||||
locale: Locale;
|
locale: Locale
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Permission = {
|
export type Permission = {
|
||||||
name?: string;
|
name?: string
|
||||||
email: string;
|
email: string
|
||||||
role: Role;
|
role: Role
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Client {
|
interface Client {
|
||||||
deleteAccount(): Promise<void>
|
deleteAccount(): Promise<void>
|
||||||
importMap(model: ImportMapInfo): Promise<number>
|
importMap(model: ImportMapInfo): Promise<number>
|
||||||
createMap(map: BasicMapInfo): Promise<number>;
|
createMap(map: BasicMapInfo): Promise<number>
|
||||||
deleteMaps(ids: number[]): Promise<void>;
|
deleteMaps(ids: number[]): Promise<void>
|
||||||
deleteMap(id: number): Promise<void>;
|
deleteMap(id: number): Promise<void>
|
||||||
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
|
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>
|
||||||
fetchAllMaps(): Promise<MapInfo[]>;
|
fetchAllMaps(): Promise<MapInfo[]>
|
||||||
|
|
||||||
fetchMapPermissions(id: number): Promise<Permission[]>;
|
fetchMapPermissions(id: number): Promise<Permission[]>
|
||||||
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void>;
|
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void>
|
||||||
deleteMapPermission(id: number, email: string): Promise<void>;
|
deleteMapPermission(id: number, email: string): Promise<void>
|
||||||
|
|
||||||
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number>;
|
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number>
|
||||||
|
|
||||||
updateAccountLanguage(locale: LocaleCode): Promise<void>;
|
updateAccountLanguage(locale: LocaleCode): Promise<void>
|
||||||
updateAccountPassword(pasword: string): Promise<void>;
|
updateAccountPassword(pasword: string): Promise<void>
|
||||||
updateAccountInfo(firstname: string, lastname: string): Promise<void>;
|
updateAccountInfo(firstname: string, lastname: string): Promise<void>
|
||||||
|
|
||||||
updateStarred(id: number, starred: boolean): Promise<void>;
|
updateStarred(id: number, starred: boolean): Promise<void>
|
||||||
updateMapToPublic(id: number, starred: boolean): Promise<void>;
|
updateMapToPublic(id: number, starred: boolean): Promise<void>
|
||||||
|
|
||||||
fetchLabels(): Promise<Label[]>;
|
fetchLabels(): Promise<Label[]>
|
||||||
deleteLabel(id: number): Promise<void>;
|
deleteLabel(id: number): Promise<void>
|
||||||
fetchAccountInfo(): Promise<AccountInfo>;
|
fetchAccountInfo(): Promise<AccountInfo>
|
||||||
|
|
||||||
registerNewUser(user: NewUser): Promise<void>;
|
registerNewUser(user: NewUser): Promise<void>
|
||||||
resetPassword(email: string): Promise<void>;
|
resetPassword(email: string): Promise<void>
|
||||||
|
|
||||||
fetchHistory(id: number): Promise<ChangeHistory[]>;
|
fetchHistory(id: number): Promise<ChangeHistory[]>
|
||||||
revertHistory(id: number, cid: number): Promise<void>
|
revertHistory(id: number, cid: number): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Client
|
||||||
export default Client;
|
|
||||||
|
@ -1,13 +1,21 @@
|
|||||||
import Client, { AccountInfo, BasicMapInfo, ChangeHistory, ImportMapInfo, Label, MapInfo, NewUser, Permission } from '..';
|
import Client, {
|
||||||
import { LocaleCode, localeFromStr } from '../../app-i18n';
|
AccountInfo,
|
||||||
|
BasicMapInfo,
|
||||||
|
ChangeHistory,
|
||||||
|
ImportMapInfo,
|
||||||
|
Label,
|
||||||
|
MapInfo,
|
||||||
|
NewUser,
|
||||||
|
Permission,
|
||||||
|
} from '..'
|
||||||
|
import { LocaleCode, localeFromStr } from '../../app-i18n'
|
||||||
|
|
||||||
class MockClient implements Client {
|
class MockClient implements Client {
|
||||||
private maps: MapInfo[] = [];
|
private maps: MapInfo[] = []
|
||||||
private labels: Label[] = [];
|
private labels: Label[] = []
|
||||||
private permissionsByMap: Map<number, Permission[]> = new Map();
|
private permissionsByMap: Map<number, Permission[]> = new Map()
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
// Remove, just for develop ....
|
// Remove, just for develop ....
|
||||||
function createMapInfo(
|
function createMapInfo(
|
||||||
id: number,
|
id: number,
|
||||||
@ -22,251 +30,298 @@ class MockClient implements Client {
|
|||||||
isPublic: boolean,
|
isPublic: boolean,
|
||||||
role: 'owner' | 'viewer' | 'editor'
|
role: 'owner' | 'viewer' | 'editor'
|
||||||
): MapInfo {
|
): MapInfo {
|
||||||
return { id, title, labels, createdBy: creator, creationTime, lastModificationBy: modifiedByUser, lastModificationTime: modifiedTime, starred, description, isPublic, role };
|
return {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
labels,
|
||||||
|
createdBy: creator,
|
||||||
|
creationTime,
|
||||||
|
lastModificationBy: modifiedByUser,
|
||||||
|
lastModificationTime: modifiedTime,
|
||||||
|
starred,
|
||||||
|
description,
|
||||||
|
isPublic,
|
||||||
|
role,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maps = [
|
this.maps = [
|
||||||
createMapInfo(1, true, "El Mapa", [], "Paulo", "2008-06-02T00:00:00Z", "Berna", "2008-06-02T00:00:00Z", "", true, 'owner'),
|
createMapInfo(
|
||||||
createMapInfo(11, false, "El Mapa3", [1, 2, 3], "Paulo3", "2008-06-02T00:00:00Z", "Berna", "2008-06-02T00:00:00Z", "", false, 'editor'),
|
1,
|
||||||
createMapInfo(12, false, "El Mapa3", [1, 2, 3], "Paulo3", "2008-06-02T00:00:00Z", "Berna", "2008-06-02T00:00:00Z", "", false, 'editor')
|
true,
|
||||||
];
|
'El Mapa',
|
||||||
|
[],
|
||||||
|
'Paulo',
|
||||||
|
'2008-06-02T00:00:00Z',
|
||||||
|
'Berna',
|
||||||
|
'2008-06-02T00:00:00Z',
|
||||||
|
'',
|
||||||
|
true,
|
||||||
|
'owner'
|
||||||
|
),
|
||||||
|
createMapInfo(
|
||||||
|
11,
|
||||||
|
false,
|
||||||
|
'El Mapa3',
|
||||||
|
[1, 2, 3],
|
||||||
|
'Paulo3',
|
||||||
|
'2008-06-02T00:00:00Z',
|
||||||
|
'Berna',
|
||||||
|
'2008-06-02T00:00:00Z',
|
||||||
|
'',
|
||||||
|
false,
|
||||||
|
'editor'
|
||||||
|
),
|
||||||
|
createMapInfo(
|
||||||
|
12,
|
||||||
|
false,
|
||||||
|
'El Mapa3',
|
||||||
|
[1, 2, 3],
|
||||||
|
'Paulo3',
|
||||||
|
'2008-06-02T00:00:00Z',
|
||||||
|
'Berna',
|
||||||
|
'2008-06-02T00:00:00Z',
|
||||||
|
'',
|
||||||
|
false,
|
||||||
|
'editor'
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
this.labels = [
|
this.labels = [
|
||||||
{ id: 1, title: "Red Label", iconName: "", color: 'red' },
|
{ id: 1, title: 'Red Label', iconName: '', color: 'red' },
|
||||||
{ id: 2, title: "Blue Label", iconName: "", color: 'blue' }
|
{ id: 2, title: 'Blue Label', iconName: '', color: 'blue' },
|
||||||
];
|
]
|
||||||
}
|
}
|
||||||
deleteMapPermission(id: number, email: string): Promise<void> {
|
deleteMapPermission(id: number, email: string): Promise<void> {
|
||||||
let perm = this.permissionsByMap.get(id) || [];
|
let perm = this.permissionsByMap.get(id) || []
|
||||||
perm = perm.filter(p=>p.email!=email)
|
perm = perm.filter((p) => p.email != email)
|
||||||
this.permissionsByMap.set(id, perm);
|
this.permissionsByMap.set(id, perm)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
|
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
|
||||||
let perm = this.permissionsByMap.get(id) || [];
|
let perm = this.permissionsByMap.get(id) || []
|
||||||
perm = perm.concat(permissions);
|
perm = perm.concat(permissions)
|
||||||
this.permissionsByMap.set(id, perm);
|
this.permissionsByMap.set(id, perm)
|
||||||
|
|
||||||
console.log(`Message ${message}`)
|
console.log(`Message ${message}`)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchMapPermissions(id: number): Promise<Permission[]> {
|
fetchMapPermissions(id: number): Promise<Permission[]> {
|
||||||
let perm = this.permissionsByMap.get(id);
|
let perm = this.permissionsByMap.get(id)
|
||||||
if (!perm) {
|
if (!perm) {
|
||||||
perm = [{
|
perm = [
|
||||||
|
{
|
||||||
name: 'Cosme Editor',
|
name: 'Cosme Editor',
|
||||||
email: 'pepe@example.com',
|
email: 'pepe@example.com',
|
||||||
role: 'editor'
|
role: 'editor',
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: 'Cosme Owner',
|
name: 'Cosme Owner',
|
||||||
email: 'pepe2@example.com',
|
email: 'pepe2@example.com',
|
||||||
role: 'owner'
|
role: 'owner',
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
name: 'Cosme Viewer',
|
name: 'Cosme Viewer',
|
||||||
email: 'pepe3@example.com',
|
email: 'pepe3@example.com',
|
||||||
role: 'viewer'
|
role: 'viewer',
|
||||||
}];
|
},
|
||||||
this.permissionsByMap.set(id, perm);
|
]
|
||||||
|
this.permissionsByMap.set(id, perm)
|
||||||
}
|
}
|
||||||
return Promise.resolve(perm);
|
return Promise.resolve(perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAccount(): Promise<void> {
|
deleteAccount(): Promise<void> {
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAccountInfo(firstname: string, lastname: string): Promise<void> {
|
updateAccountInfo(firstname: string, lastname: string): Promise<void> {
|
||||||
console.log("firstname:" + firstname, +lastname)
|
console.log('firstname:' + firstname, +lastname)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAccountPassword(pasword: string): Promise<void> {
|
updateAccountPassword(pasword: string): Promise<void> {
|
||||||
console.log("password:" + pasword)
|
console.log('password:' + pasword)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAccountLanguage(locale: LocaleCode): Promise<void> {
|
updateAccountLanguage(locale: LocaleCode): Promise<void> {
|
||||||
localStorage.setItem('locale', locale);
|
localStorage.setItem('locale', locale)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
importMap(model: ImportMapInfo): Promise<number> {
|
importMap(model: ImportMapInfo): Promise<number> {
|
||||||
console.log("model:" + model);
|
console.log('model:' + model)
|
||||||
return Promise.resolve(10);
|
return Promise.resolve(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAccountInfo(): Promise<AccountInfo> {
|
fetchAccountInfo(): Promise<AccountInfo> {
|
||||||
console.log('Fetch account info ...')
|
console.log('Fetch account info ...')
|
||||||
const locale: LocaleCode | null = localStorage.getItem('locale') as LocaleCode;
|
const locale: LocaleCode | null = localStorage.getItem('locale') as LocaleCode
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
firstname: 'Costme',
|
firstname: 'Costme',
|
||||||
lastname: 'Fulanito',
|
lastname: 'Fulanito',
|
||||||
email: 'test@example.com',
|
email: 'test@example.com',
|
||||||
locale: localeFromStr(locale)
|
locale: localeFromStr(locale),
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMaps(ids: number[]): Promise<void> {
|
deleteMaps(ids: number[]): Promise<void> {
|
||||||
ids.forEach(id => this.deleteMap(id));
|
ids.forEach((id) => this.deleteMap(id))
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
revertHistory(id: number, cid: number): Promise<void> {
|
revertHistory(id: number, cid: number): Promise<void> {
|
||||||
console.log("model:" + id + cid);
|
console.log('model:' + id + cid)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
createMap(map: BasicMapInfo): Promise<number> {
|
createMap(map: BasicMapInfo): Promise<number> {
|
||||||
throw new Error("Method not implemented." + map);
|
throw new Error('Method not implemented.' + map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchLabels(): Promise<Label[]> {
|
fetchLabels(): Promise<Label[]> {
|
||||||
console.log("Fetching labels from server")
|
console.log('Fetching labels from server')
|
||||||
return Promise.resolve(this.labels);
|
return Promise.resolve(this.labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMapToPublic(id: number, isPublic: boolean): Promise<void> {
|
updateMapToPublic(id: number, isPublic: boolean): Promise<void> {
|
||||||
const mapInfo = this.maps.find(m => m.id == id);
|
const mapInfo = this.maps.find((m) => m.id == id)
|
||||||
if (mapInfo) {
|
if (mapInfo) {
|
||||||
mapInfo.isPublic = isPublic;
|
mapInfo.isPublic = isPublic
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStarred(id: number, starred: boolean): Promise<void> {
|
updateStarred(id: number, starred: boolean): Promise<void> {
|
||||||
const mapInfo = this.maps.find(m => m.id == id);
|
const mapInfo = this.maps.find((m) => m.id == id)
|
||||||
if (!mapInfo) {
|
if (!mapInfo) {
|
||||||
console.log(`Could not find the map iwth id ${id}`);
|
console.log(`Could not find the map iwth id ${id}`)
|
||||||
return Promise.reject();
|
return Promise.reject()
|
||||||
}
|
}
|
||||||
mapInfo.starred = starred;
|
mapInfo.starred = starred
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
|
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
|
||||||
|
const exists = this.maps.find((m) => m.title == basicInfo.title) != undefined
|
||||||
const exists = this.maps.find(m => m.title == basicInfo.title) != undefined;
|
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
this.maps = this.maps.map(m => {
|
this.maps = this.maps.map((m) => {
|
||||||
const result = m;
|
const result = m
|
||||||
if (m.id == id) {
|
if (m.id == id) {
|
||||||
result.description = basicInfo.description ? basicInfo.description : '';
|
result.description = basicInfo.description ? basicInfo.description : ''
|
||||||
result.title = basicInfo.title;
|
result.title = basicInfo.title
|
||||||
}
|
}
|
||||||
return result;
|
return result
|
||||||
})
|
})
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
} else {
|
} else {
|
||||||
const fieldErrors: Map<string, string> = new Map<string, string>();
|
const fieldErrors: Map<string, string> = new Map<string, string>()
|
||||||
fieldErrors.set('name', 'name already exists ')
|
fieldErrors.set('name', 'name already exists ')
|
||||||
|
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
msg: 'Map already exists ...' + basicInfo.title,
|
msg: 'Map already exists ...' + basicInfo.title,
|
||||||
fields: fieldErrors
|
fields: fieldErrors,
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchHistory(id: number): Promise<ChangeHistory[]> {
|
fetchHistory(id: number): Promise<ChangeHistory[]> {
|
||||||
console.log(`Fetching history for ${id}`)
|
console.log(`Fetching history for ${id}`)
|
||||||
const result = [{
|
const result = [
|
||||||
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
lastModificationBy: 'Paulo',
|
lastModificationBy: 'Paulo',
|
||||||
lastModificationTime: '2008-06-02T00:00:00Z'
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
lastModificationBy: 'Paulo',
|
lastModificationBy: 'Paulo',
|
||||||
lastModificationTime: '2008-06-02T00:00:00Z'
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
}
|
},
|
||||||
,
|
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
lastModificationBy: 'Paulo',
|
lastModificationBy: 'Paulo',
|
||||||
lastModificationTime: '2008-06-02T00:00:00Z'
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
lastModificationBy: 'Paulo',
|
lastModificationBy: 'Paulo',
|
||||||
lastModificationTime: '2008-06-02T00:00:00Z'
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
lastModificationBy: 'Paulo',
|
lastModificationBy: 'Paulo',
|
||||||
lastModificationTime: '2008-06-02T00:00:00Z'
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
lastModificationBy: 'Paulo',
|
lastModificationBy: 'Paulo',
|
||||||
lastModificationTime: '2008-06-02T00:00:00Z'
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 7,
|
id: 7,
|
||||||
lastModificationBy: 'Paulo',
|
lastModificationBy: 'Paulo',
|
||||||
lastModificationTime: '2008-06-02T00:00:00Z'
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> {
|
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> {
|
||||||
|
const exists = this.maps.find((m) => m.title == basicInfo.title) != undefined
|
||||||
const exists = this.maps.find(m => m.title == basicInfo.title) != undefined;
|
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
|
|
||||||
const newMap: MapInfo = {
|
const newMap: MapInfo = {
|
||||||
id: Math.random() * 1000,
|
id: Math.random() * 1000,
|
||||||
description: String(basicInfo.description),
|
description: String(basicInfo.description),
|
||||||
title: basicInfo.title,
|
title: basicInfo.title,
|
||||||
starred: false,
|
starred: false,
|
||||||
createdBy: "current user",
|
createdBy: 'current user',
|
||||||
labels: [],
|
labels: [],
|
||||||
lastModificationTime: "2008-06-02T00:00:00Z",
|
lastModificationTime: '2008-06-02T00:00:00Z',
|
||||||
lastModificationBy: "Berna",
|
lastModificationBy: 'Berna',
|
||||||
creationTime: "2008-06-02T00:00:00Z",
|
creationTime: '2008-06-02T00:00:00Z',
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
role: 'owner'
|
role: 'owner',
|
||||||
};
|
}
|
||||||
this.maps.push(newMap);
|
this.maps.push(newMap)
|
||||||
return Promise.resolve(newMap.id);
|
return Promise.resolve(newMap.id)
|
||||||
} else {
|
} else {
|
||||||
const fieldErrors: Map<string, string> = new Map<string, string>();
|
const fieldErrors: Map<string, string> = new Map<string, string>()
|
||||||
fieldErrors.set('name', 'name already exists ')
|
fieldErrors.set('name', 'name already exists ')
|
||||||
|
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
msg: 'Maps name must be unique:' + basicInfo.title,
|
msg: 'Maps name must be unique:' + basicInfo.title,
|
||||||
fields: fieldErrors
|
fields: fieldErrors,
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteLabel(id: number): Promise<void> {
|
deleteLabel(id: number): Promise<void> {
|
||||||
this.labels = this.labels.filter(l => l.id != id);
|
this.labels = this.labels.filter((l) => l.id != id)
|
||||||
console.log("Label delete:" + this.labels);
|
console.log('Label delete:' + this.labels)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMap(id: number): Promise<void> {
|
deleteMap(id: number): Promise<void> {
|
||||||
this.maps = this.maps.filter(m => m.id != id);
|
this.maps = this.maps.filter((m) => m.id != id)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
registerNewUser(user: NewUser): Promise<void> {
|
registerNewUser(user: NewUser): Promise<void> {
|
||||||
console.log("user:" + user)
|
console.log('user:' + user)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAllMaps(): Promise<MapInfo[]> {
|
fetchAllMaps(): Promise<MapInfo[]> {
|
||||||
console.log("Fetching maps from server")
|
console.log('Fetching maps from server')
|
||||||
return Promise.resolve(this.maps);
|
return Promise.resolve(this.maps)
|
||||||
}
|
}
|
||||||
|
|
||||||
resetPassword(email: string): Promise<void> {
|
resetPassword(email: string): Promise<void> {
|
||||||
console.log("email:" + email)
|
console.log('email:' + email)
|
||||||
return Promise.resolve();
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MockClient;
|
export default MockClient
|
||||||
|
@ -1,273 +1,327 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios'
|
||||||
import Client, { ErrorInfo, MapInfo, BasicMapInfo, NewUser, Label, ChangeHistory, AccountInfo, ImportMapInfo, Permission } from '..';
|
import Client, {
|
||||||
import { LocaleCode, localeFromStr, Locales } from '../../app-i18n';
|
ErrorInfo,
|
||||||
|
MapInfo,
|
||||||
|
BasicMapInfo,
|
||||||
|
NewUser,
|
||||||
|
Label,
|
||||||
|
ChangeHistory,
|
||||||
|
AccountInfo,
|
||||||
|
ImportMapInfo,
|
||||||
|
Permission,
|
||||||
|
} from '..'
|
||||||
|
import { LocaleCode, localeFromStr, Locales } from '../../app-i18n'
|
||||||
|
|
||||||
export default class RestClient implements Client {
|
export default class RestClient implements Client {
|
||||||
private baseUrl: string;
|
private baseUrl: string
|
||||||
private sessionExpired: () => void
|
private sessionExpired: () => void
|
||||||
|
|
||||||
constructor(baseUrl: string, sessionExpired: () => void) {
|
constructor(baseUrl: string, sessionExpired: () => void) {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl
|
||||||
this.sessionExpired = sessionExpired;
|
this.sessionExpired = sessionExpired
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
deleteMapPermission(id: number, email: string): Promise<void> {
|
deleteMapPermission(id: number, email: string): Promise<void> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.')
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
|
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(`${this.baseUrl}/c/restful/maps/${id}/collabs/`,
|
axios
|
||||||
|
.put(
|
||||||
|
`${this.baseUrl}/c/restful/maps/${id}/collabs/`,
|
||||||
{
|
{
|
||||||
messasge: message,
|
messasge: message,
|
||||||
collaborations: permissions
|
collaborations: permissions,
|
||||||
},
|
},
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
{ headers: { 'Content-Type': 'application/json' } }
|
||||||
).then(() => {
|
)
|
||||||
|
.then(() => {
|
||||||
// All was ok, let's sent to success page ...;
|
// All was ok, let's sent to success page ...;
|
||||||
success();
|
success()
|
||||||
}).catch(error => {
|
})
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchMapPermissions(id: number): Promise<Permission[]> {
|
fetchMapPermissions(id: number): Promise<Permission[]> {
|
||||||
const handler = (success: (labels: Permission[]) => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (
|
||||||
axios.get(
|
success: (labels: Permission[]) => void,
|
||||||
this.baseUrl + `/c/restful/maps/${id}/collabs`,
|
reject: (error: ErrorInfo) => void
|
||||||
{
|
) => {
|
||||||
headers: { 'Content-Type': 'text/plain' }
|
axios
|
||||||
}
|
.get(this.baseUrl + `/c/restful/maps/${id}/collabs`, {
|
||||||
).then(response => {
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
const data = response.data;
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const data = response.data
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const perms: Permission[] = (data.collaborations as any[]).map(p => {
|
const perms: Permission[] = (data.collaborations as any[]).map((p) => {
|
||||||
return {
|
return {
|
||||||
id: p.id,
|
id: p.id,
|
||||||
email: p.email,
|
email: p.email,
|
||||||
name: p.name,
|
name: p.name,
|
||||||
role: p.role
|
role: p.role,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
success(perms);
|
success(perms)
|
||||||
}).catch(error => {
|
})
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAccount(): Promise<void> {
|
deleteAccount(): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.delete(this.baseUrl + `/c/restful/account`,
|
axios
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
.delete(this.baseUrl + `/c/restful/account`, {
|
||||||
).then(() => {
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
success();
|
})
|
||||||
}).catch(error => {
|
.then(() => {
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
success()
|
||||||
reject(errorInfo);
|
})
|
||||||
});
|
.catch((error) => {
|
||||||
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAccountInfo(firstname: string, lastname: string): Promise<void> {
|
updateAccountInfo(firstname: string, lastname: string): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(`${this.baseUrl}/c/restful/account/firstname`,
|
axios
|
||||||
firstname,
|
.put(`${this.baseUrl}/c/restful/account/firstname`, firstname, {
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
).then(() => {
|
})
|
||||||
return axios.put(`${this.baseUrl}/c/restful/account/lastname`,
|
.then(() => {
|
||||||
lastname,
|
return axios.put(`${this.baseUrl}/c/restful/account/lastname`, lastname, {
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
)
|
})
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
// All was ok, let's sent to success page ...;
|
// All was ok, let's sent to success page ...;
|
||||||
success();
|
success()
|
||||||
}).catch(error => {
|
})
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAccountPassword(pasword: string): Promise<void> {
|
updateAccountPassword(pasword: string): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(`${this.baseUrl}/c/restful/account/password`,
|
axios
|
||||||
pasword,
|
.put(`${this.baseUrl}/c/restful/account/password`, pasword, {
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
).then(() => {
|
})
|
||||||
success();
|
.then(() => {
|
||||||
}).catch(error => {
|
success()
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
})
|
||||||
reject(errorInfo);
|
.catch((error) => {
|
||||||
});
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAccountLanguage(locale: LocaleCode): Promise<void> {
|
updateAccountLanguage(locale: LocaleCode): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(`${this.baseUrl}/c/restful/account/locale`,
|
axios
|
||||||
locale,
|
.put(`${this.baseUrl}/c/restful/account/locale`, locale, {
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
// All was ok, let's sent to success page ...;
|
// All was ok, let's sent to success page ...;
|
||||||
success();
|
success()
|
||||||
}).catch(error => {
|
})
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
importMap(model: ImportMapInfo): Promise<number> {
|
importMap(model: ImportMapInfo): Promise<number> {
|
||||||
const handler = (success: (mapId: number) => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: (mapId: number) => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.post(this.baseUrl + `/c/restful/maps?title=${model.title}&description=${model.description ? model.description : ''}`,
|
axios
|
||||||
|
.post(
|
||||||
|
this.baseUrl +
|
||||||
|
`/c/restful/maps?title=${model.title}&description=${
|
||||||
|
model.description ? model.description : ''
|
||||||
|
}`,
|
||||||
model.content,
|
model.content,
|
||||||
{ headers: { 'Content-Type': model.contentType } }
|
{ headers: { 'Content-Type': model.contentType } }
|
||||||
).then(response => {
|
)
|
||||||
const mapId = response.headers.resourceid;
|
.then((response) => {
|
||||||
success(mapId);
|
const mapId = response.headers.resourceid
|
||||||
}).catch(error => {
|
success(mapId)
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
})
|
||||||
reject(errorInfo);
|
.catch((error) => {
|
||||||
});
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAccountInfo(): Promise<AccountInfo> {
|
fetchAccountInfo(): Promise<AccountInfo> {
|
||||||
const handler = (success: (account: AccountInfo) => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (
|
||||||
axios.get(
|
success: (account: AccountInfo) => void,
|
||||||
this.baseUrl + '/c/restful/account',
|
reject: (error: ErrorInfo) => void
|
||||||
{
|
) => {
|
||||||
headers: { 'Content-Type': 'application/json' }
|
axios
|
||||||
}
|
.get(this.baseUrl + '/c/restful/account', {
|
||||||
).then(response => {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
const account = response.data;
|
})
|
||||||
const locale: LocaleCode | null = account.locale;
|
.then((response) => {
|
||||||
|
const account = response.data
|
||||||
|
const locale: LocaleCode | null = account.locale
|
||||||
success({
|
success({
|
||||||
lastname: account.lastname ? account.lastname : '',
|
lastname: account.lastname ? account.lastname : '',
|
||||||
firstname: account.firstname ? account.firstname : '',
|
firstname: account.firstname ? account.firstname : '',
|
||||||
email: account.email,
|
email: account.email,
|
||||||
locale: locale ? localeFromStr(locale) : Locales.EN
|
locale: locale ? localeFromStr(locale) : Locales.EN,
|
||||||
});
|
})
|
||||||
}).catch(error => {
|
})
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMaps(ids: number[]): Promise<void> {
|
deleteMaps(ids: number[]): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.delete(this.baseUrl + `/c/restful/maps/batch?ids=${ids.join()}`,
|
axios
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
.delete(this.baseUrl + `/c/restful/maps/batch?ids=${ids.join()}`, {
|
||||||
).then(() => {
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
success();
|
})
|
||||||
}).catch(error => {
|
.then(() => {
|
||||||
const response = error.response;
|
success()
|
||||||
const errorInfo = this.parseResponseOnError(response);
|
})
|
||||||
reject(errorInfo);
|
.catch((error) => {
|
||||||
});
|
const response = error.response
|
||||||
|
const errorInfo = this.parseResponseOnError(response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMapToPublic(id: number, isPublic: boolean): Promise<void> {
|
updateMapToPublic(id: number, isPublic: boolean): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(`${this.baseUrl}/c/restful/maps/${id}/publish`,
|
axios
|
||||||
isPublic,
|
.put(`${this.baseUrl}/c/restful/maps/${id}/publish`, isPublic, {
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
// All was ok, let's sent to success page ...;
|
// All was ok, let's sent to success page ...;
|
||||||
success();
|
success()
|
||||||
}).catch(error => {
|
})
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
revertHistory(id: number, hid: number): Promise<void> {
|
revertHistory(id: number, hid: number): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.post(this.baseUrl + `/maps/${id}/history/${hid}`,
|
axios
|
||||||
null,
|
.post(this.baseUrl + `/maps/${id}/history/${hid}`, null, {
|
||||||
{ headers: { 'Content-Type': 'text/pain' } }
|
headers: { 'Content-Type': 'text/pain' },
|
||||||
).then(() => {
|
})
|
||||||
success();
|
.then(() => {
|
||||||
}).catch(error => {
|
success()
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
})
|
||||||
reject(errorInfo);
|
.catch((error) => {
|
||||||
});
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchHistory(id: number): Promise<ChangeHistory[]> {
|
fetchHistory(id: number): Promise<ChangeHistory[]> {
|
||||||
throw new Error(`Method not implemented. ${id}`);
|
throw new Error(`Method not implemented. ${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
|
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(`${this.baseUrl}/c/restful/maps/${id}/title`,
|
axios
|
||||||
basicInfo.title,
|
.put(`${this.baseUrl}/c/restful/maps/${id}/title`, basicInfo.title, {
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
).then(() => {
|
})
|
||||||
return axios.put(`${this.baseUrl}/c/restful/maps/${id}/description`,
|
.then(() => {
|
||||||
|
return axios.put(
|
||||||
|
`${this.baseUrl}/c/restful/maps/${id}/description`,
|
||||||
basicInfo.description,
|
basicInfo.description,
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
{ headers: { 'Content-Type': 'text/plain' } }
|
||||||
)
|
)
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
// All was ok, let's sent to success page ...;
|
// All was ok, let's sent to success page ...;
|
||||||
success();
|
success()
|
||||||
}).catch(error => {
|
})
|
||||||
const response = error.response;
|
.catch((error) => {
|
||||||
const errorInfo = this.parseResponseOnError(response);
|
const response = error.response
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
createMap(model: BasicMapInfo): Promise<number> {
|
createMap(model: BasicMapInfo): Promise<number> {
|
||||||
const handler = (success: (mapId: number) => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: (mapId: number) => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.post(this.baseUrl + `/c/restful/maps?title=${model.title}&description=${model.description ? model.description : ''}`,
|
axios
|
||||||
|
.post(
|
||||||
|
this.baseUrl +
|
||||||
|
`/c/restful/maps?title=${model.title}&description=${
|
||||||
|
model.description ? model.description : ''
|
||||||
|
}`,
|
||||||
null,
|
null,
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
{ headers: { 'Content-Type': 'application/json' } }
|
||||||
).then(response => {
|
)
|
||||||
const mapId = response.headers.resourceid;
|
.then((response) => {
|
||||||
success(mapId);
|
const mapId = response.headers.resourceid
|
||||||
}).catch(error => {
|
success(mapId)
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
})
|
||||||
reject(errorInfo);
|
.catch((error) => {
|
||||||
});
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAllMaps(): Promise<MapInfo[]> {
|
fetchAllMaps(): Promise<MapInfo[]> {
|
||||||
const handler = (success: (mapsInfo: MapInfo[]) => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (
|
||||||
axios.get(
|
success: (mapsInfo: MapInfo[]) => void,
|
||||||
this.baseUrl + '/c/restful/maps/',
|
reject: (error: ErrorInfo) => void
|
||||||
{
|
) => {
|
||||||
headers: { 'Content-Type': 'application/json' }
|
axios
|
||||||
}
|
.get(this.baseUrl + '/c/restful/maps/', {
|
||||||
).then(response => {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
const data = response.data;
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const data = response.data
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const maps: MapInfo[] = (data.mindmapsInfo as any[]).map(m => {
|
const maps: MapInfo[] = (data.mindmapsInfo as any[]).map((m) => {
|
||||||
return {
|
return {
|
||||||
id: m.id,
|
id: m.id,
|
||||||
starred: Boolean(m.starred),
|
starred: Boolean(m.starred),
|
||||||
@ -279,191 +333,205 @@ export default class RestClient implements Client {
|
|||||||
lastModificationTime: m.lastModificationTime,
|
lastModificationTime: m.lastModificationTime,
|
||||||
description: m.description,
|
description: m.description,
|
||||||
isPublic: m['public'],
|
isPublic: m['public'],
|
||||||
role: m.role
|
role: m.role,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
success(maps);
|
success(maps)
|
||||||
}).catch(error => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
console.log("Maps List Error=>")
|
console.log('Maps List Error=>')
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
|
||||||
const response = error.response;
|
const response = error.response
|
||||||
const errorInfo = this.parseResponseOnError(response);
|
const errorInfo = this.parseResponseOnError(response)
|
||||||
reject(errorInfo);
|
reject(errorInfo)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerNewUser(user: NewUser): Promise<void> {
|
registerNewUser(user: NewUser): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.post(this.baseUrl + '/service/users/',
|
axios
|
||||||
JSON.stringify(user),
|
.post(this.baseUrl + '/service/users/', JSON.stringify(user), {
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
headers: { 'Content-Type': 'application/json' },
|
||||||
).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
// All was ok, let's sent to success page ...;
|
// All was ok, let's sent to success page ...;
|
||||||
success();
|
success()
|
||||||
}).catch(error => {
|
})
|
||||||
console.log(error);
|
.catch((error) => {
|
||||||
const response = error.response;
|
console.log(error)
|
||||||
const errorInfo = this.parseResponseOnError(response);
|
const response = error.response
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMap(id: number): Promise<void> {
|
deleteMap(id: number): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.delete(this.baseUrl + `/c/restful/maps/${id}`,
|
axios
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
.delete(this.baseUrl + `/c/restful/maps/${id}`, {
|
||||||
).then(() => {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
success();
|
})
|
||||||
}).catch(error => {
|
.then(() => {
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
success()
|
||||||
reject(errorInfo);
|
})
|
||||||
});
|
.catch((error) => {
|
||||||
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
resetPassword(email: string): Promise<void> {
|
resetPassword(email: string): Promise<void> {
|
||||||
|
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(`${this.baseUrl}/service/users/resetPassword?email=${email}`,
|
axios
|
||||||
null,
|
.put(`${this.baseUrl}/service/users/resetPassword?email=${email}`, null, {
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
headers: { 'Content-Type': 'application/json' },
|
||||||
).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
// All was ok, let's sent to success page ...;
|
// All was ok, let's sent to success page ...;
|
||||||
success();
|
success()
|
||||||
}).catch(error => {
|
})
|
||||||
const response = error.response;
|
.catch((error) => {
|
||||||
const errorInfo = this.parseResponseOnError(response);
|
const response = error.response
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> {
|
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> {
|
||||||
|
|
||||||
const handler = (success: (mapId: number) => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: (mapId: number) => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.post(this.baseUrl + `/c/restful/maps/${id}`,
|
axios
|
||||||
JSON.stringify(basicInfo),
|
.post(this.baseUrl + `/c/restful/maps/${id}`, JSON.stringify(basicInfo), {
|
||||||
{ headers: { 'Content-Type': 'application/json' } }
|
headers: { 'Content-Type': 'application/json' },
|
||||||
).then(response => {
|
})
|
||||||
const mapId = response.headers.resourceid;
|
.then((response) => {
|
||||||
success(mapId);
|
const mapId = response.headers.resourceid
|
||||||
}).catch(error => {
|
success(mapId)
|
||||||
const response = error.response;
|
})
|
||||||
const errorInfo = this.parseResponseOnError(response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const response = error.response
|
||||||
});
|
const errorInfo = this.parseResponseOnError(response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStarred(id: number, starred: boolean): Promise<void> {
|
updateStarred(id: number, starred: boolean): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.put(this.baseUrl + `/c/restful/maps/${id}/starred`,
|
axios
|
||||||
starred,
|
.put(this.baseUrl + `/c/restful/maps/${id}/starred`, starred, {
|
||||||
{ headers: { 'Content-Type': 'text/plain' } }
|
headers: { 'Content-Type': 'text/plain' },
|
||||||
).then(() => {
|
})
|
||||||
success();
|
.then(() => {
|
||||||
}).catch(error => {
|
success()
|
||||||
const response = error.response;
|
})
|
||||||
const errorInfo = this.parseResponseOnError(response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const response = error.response
|
||||||
});
|
const errorInfo = this.parseResponseOnError(response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchLabels(): Promise<Label[]> {
|
fetchLabels(): Promise<Label[]> {
|
||||||
const handler = (success: (labels: Label[]) => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (
|
||||||
axios.get(
|
success: (labels: Label[]) => void,
|
||||||
this.baseUrl + '/c/restful/labels/',
|
reject: (error: ErrorInfo) => void
|
||||||
{
|
) => {
|
||||||
headers: { 'Content-Type': 'application/json' }
|
axios
|
||||||
}
|
.get(this.baseUrl + '/c/restful/labels/', {
|
||||||
).then(response => {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
const data = response.data;
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const data = response.data
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const maps: Label[] = (data.labels as any[]).map(l => {
|
const maps: Label[] = (data.labels as any[]).map((l) => {
|
||||||
return {
|
return {
|
||||||
id: l.id,
|
id: l.id,
|
||||||
color: l.color,
|
color: l.color,
|
||||||
title: l.title,
|
title: l.title,
|
||||||
iconName: l.iconName
|
iconName: l.iconName,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
success(maps);
|
success(maps)
|
||||||
}).catch(error => {
|
})
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
.catch((error) => {
|
||||||
reject(errorInfo);
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
});
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteLabel(id: number): Promise<void> {
|
deleteLabel(id: number): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.delete(this.baseUrl + `/c/restful/label/${id}`).then(() => {
|
axios
|
||||||
success();
|
.delete(this.baseUrl + `/c/restful/label/${id}`)
|
||||||
}).catch(error => {
|
.then(() => {
|
||||||
const errorInfo = this.parseResponseOnError(error.response);
|
success()
|
||||||
reject(errorInfo);
|
})
|
||||||
});
|
.catch((error) => {
|
||||||
|
const errorInfo = this.parseResponseOnError(error.response)
|
||||||
|
reject(errorInfo)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return new Promise(handler);
|
return new Promise(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
private parseResponseOnError = (response: any): ErrorInfo => {
|
private parseResponseOnError = (response: any): ErrorInfo => {
|
||||||
let result: ErrorInfo | undefined;
|
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
|
||||||
console.log(data);
|
console.log(data)
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 401:
|
case 401:
|
||||||
case 302:
|
case 302:
|
||||||
this.sessionExpired();
|
this.sessionExpired()
|
||||||
result = { msg: "Your current session has expired. Please, sign in and try again." };
|
result = {
|
||||||
break;
|
msg: 'Your current session has expired. Please, sign in and try again.',
|
||||||
|
}
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
if (data) {
|
if (data) {
|
||||||
// Set global errors ...
|
// Set global errors ...
|
||||||
result = {};
|
result = {}
|
||||||
const globalErrors = data.globalErrors;
|
const globalErrors = data.globalErrors
|
||||||
if (globalErrors && globalErrors.length > 0) {
|
if (globalErrors && globalErrors.length > 0) {
|
||||||
result.msg = globalErrors[0];
|
result.msg = globalErrors[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set field errors ...
|
// Set field errors ...
|
||||||
if (data.fieldErrors && Object.keys(data.fieldErrors).length > 0) {
|
if (data.fieldErrors && Object.keys(data.fieldErrors).length > 0) {
|
||||||
result.fields = data.fieldErrors;
|
result.fields = data.fieldErrors
|
||||||
if (!result.msg) {
|
if (!result.msg) {
|
||||||
const key = Object.keys(data.fieldErrors)[0];
|
const key = Object.keys(data.fieldErrors)[0]
|
||||||
result.msg = data.fieldErrors[key];
|
result.msg = data.fieldErrors[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
result = { msg: response.statusText };
|
result = { msg: response.statusText }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network related problem ...
|
// Network related problem ...
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = { msg: 'Unexpected error. Please, try latter' };
|
result = { msg: 'Unexpected error. Please, try latter' }
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import Client, { ErrorInfo } from '../../classes/client'
|
|||||||
|
|
||||||
import Header from '../layout/header'
|
import Header from '../layout/header'
|
||||||
import Footer from '../layout/footer'
|
import Footer from '../layout/footer'
|
||||||
import FormContainer from '../layout/form-container';
|
import FormContainer from '../layout/form-container'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { useMutation } from 'react-query'
|
import { useMutation } from 'react-query'
|
||||||
import { activeInstance } from '../../redux/clientSlice'
|
import { activeInstance } from '../../redux/clientSlice'
|
||||||
@ -16,25 +16,25 @@ import SubmitButton from '../form/submit-button'
|
|||||||
import Typography from '@material-ui/core/Typography'
|
import Typography from '@material-ui/core/Typography'
|
||||||
|
|
||||||
const ForgotPassword = () => {
|
const ForgotPassword = () => {
|
||||||
const [email, setEmail] = useState<string>('');
|
const [email, setEmail] = useState<string>('')
|
||||||
const [error, setError] = useState<ErrorInfo>();
|
const [error, setError] = useState<ErrorInfo>()
|
||||||
const history = useHistory();
|
const history = useHistory()
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const service: Client = useSelector(activeInstance);
|
const service: Client = useSelector(activeInstance)
|
||||||
const mutation = useMutation<void, ErrorInfo, string>(
|
const mutation = useMutation<void, ErrorInfo, string>(
|
||||||
(email: string) => service.resetPassword(email),
|
(email: string) => service.resetPassword(email),
|
||||||
{
|
{
|
||||||
onSuccess: () => history.push("/c/forgot-password-success"),
|
onSuccess: () => history.push('/c/forgot-password-success'),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
mutation.mutate(email);
|
mutation.mutate(email)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -44,36 +44,47 @@ const ForgotPassword = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
<FormattedMessage id="forgot.desc" defaultMessage="We will send you an email to reset your password" />
|
<FormattedMessage
|
||||||
|
id="forgot.desc"
|
||||||
|
defaultMessage="We will send you an email to reset your password"
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<GlobalError error={error} />
|
<GlobalError error={error} />
|
||||||
|
|
||||||
<form onSubmit={handleOnSubmit}>
|
<form onSubmit={handleOnSubmit}>
|
||||||
<Input type="email" name="email" label={intl.formatMessage({ id: "forgot.email", defaultMessage: "Email" })}
|
<Input
|
||||||
autoComplete="email" onChange={e => setEmail(e.target.value)} error={error} />
|
type="email"
|
||||||
|
name="email"
|
||||||
|
label={intl.formatMessage({ id: 'forgot.email', defaultMessage: 'Email' })}
|
||||||
|
autoComplete="email"
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
|
||||||
<SubmitButton value={intl.formatMessage({ id: "forgot.register", defaultMessage: "Send recovery link" })} />
|
<SubmitButton
|
||||||
|
value={intl.formatMessage({
|
||||||
|
id: 'forgot.register',
|
||||||
|
defaultMessage: 'Send recovery link',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ForgotPasswordPage = ():React.ReactElement => {
|
const ForgotPasswordPage = (): React.ReactElement => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Reset Password | WiseMapping';
|
document.title = 'Reset Password | WiseMapping'
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type='only-signin' />
|
<Header type="only-signin" />
|
||||||
<ForgotPassword />
|
<ForgotPassword />
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ForgotPasswordPage }
|
export { ForgotPasswordPage }
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,40 +1,49 @@
|
|||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { FormattedMessage } from 'react-intl'
|
import { FormattedMessage } from 'react-intl'
|
||||||
import FormContainer from '../layout/form-container';
|
import FormContainer from '../layout/form-container'
|
||||||
import Header from '../layout/header'
|
import Header from '../layout/header'
|
||||||
import Footer from '../layout/footer'
|
import Footer from '../layout/footer'
|
||||||
import { Link as RouterLink } from 'react-router-dom'
|
import { Link as RouterLink } from 'react-router-dom'
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
|
|
||||||
|
|
||||||
const ForgotPasswordSuccessPage = (): React.ReactElement => {
|
const ForgotPasswordSuccessPage = (): React.ReactElement => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Reset Password | WiseMapping';
|
document.title = 'Reset Password | WiseMapping'
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type='none' />
|
<Header type="none" />
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
<FormattedMessage id="forgot.success.title" defaultMessage="Your temporal password has been sent" />
|
<FormattedMessage
|
||||||
|
id="forgot.success.title"
|
||||||
|
defaultMessage="Your temporal password has been sent"
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography paragraph>
|
<Typography paragraph>
|
||||||
<FormattedMessage id="forgot.success.desc" defaultMessage="We've sent you an email that will allow you to reset your password. Please check your email now." />
|
<FormattedMessage
|
||||||
|
id="forgot.success.desc"
|
||||||
|
defaultMessage="We've sent you an email that will allow you to reset your password. Please check your email now."
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Button color="primary" size="medium" variant="contained" component={RouterLink} to="/c/login" disableElevation={true}>
|
<Button
|
||||||
|
color="primary"
|
||||||
|
size="medium"
|
||||||
|
variant="contained"
|
||||||
|
component={RouterLink}
|
||||||
|
to="/c/login"
|
||||||
|
disableElevation={true}
|
||||||
|
>
|
||||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ForgotPasswordSuccessPage
|
export default ForgotPasswordSuccessPage
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
import React from "react";
|
import React from 'react'
|
||||||
import { ErrorInfo } from "../../../classes/client"
|
import { ErrorInfo } from '../../../classes/client'
|
||||||
import StyledAlert from "./styled";
|
import StyledAlert from './styled'
|
||||||
|
|
||||||
|
|
||||||
type GlobalErrorProps = {
|
type GlobalErrorProps = {
|
||||||
error?: ErrorInfo;
|
error?: ErrorInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlobalError = (props: GlobalErrorProps): React.ReactElement | null => {
|
const GlobalError = (props: GlobalErrorProps): React.ReactElement | null => {
|
||||||
|
const error = props.error
|
||||||
|
const hasError = Boolean(error?.msg)
|
||||||
|
const errorMsg = error?.msg
|
||||||
|
|
||||||
const error = props.error;
|
return hasError ? (
|
||||||
const hasError = Boolean(error?.msg);
|
<StyledAlert severity="error" variant="filled" hidden={!hasError}>
|
||||||
const errorMsg = error?.msg;
|
{' '}
|
||||||
|
{errorMsg}
|
||||||
|
</StyledAlert>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
return (hasError ?
|
export default GlobalError
|
||||||
<StyledAlert severity="error" variant="filled" hidden={!hasError}> {errorMsg}</StyledAlert> : null);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GlobalError;
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import withStyles from "@material-ui/core/styles/withStyles";
|
import withStyles from '@material-ui/core/styles/withStyles'
|
||||||
import Alert from "@material-ui/lab/Alert";
|
import Alert from '@material-ui/lab/Alert'
|
||||||
|
|
||||||
export const StyledAlert = withStyles({
|
export const StyledAlert = withStyles({
|
||||||
root:
|
root: {
|
||||||
{
|
|
||||||
padding: '10px 15px',
|
padding: '10px 15px',
|
||||||
margin: '5px 0px '
|
margin: '5px 0px ',
|
||||||
}
|
},
|
||||||
})(Alert);
|
})(Alert)
|
||||||
|
|
||||||
export default StyledAlert;
|
export default StyledAlert
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from '@material-ui/core/TextField'
|
||||||
import React, { ChangeEvent } from "react";
|
import React, { ChangeEvent } from 'react'
|
||||||
import { ErrorInfo } from "../../../classes/client";
|
import { ErrorInfo } from '../../../classes/client'
|
||||||
|
|
||||||
type InputProps = {
|
type InputProps = {
|
||||||
name: string;
|
name: string
|
||||||
error?: ErrorInfo;
|
error?: ErrorInfo
|
||||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
|
||||||
label: string;
|
label: string
|
||||||
required?: boolean;
|
required?: boolean
|
||||||
type: string;
|
type: string
|
||||||
value?: string
|
value?: string
|
||||||
autoComplete?: string;
|
autoComplete?: string
|
||||||
fullWidth?: boolean
|
fullWidth?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
@ -25,17 +25,25 @@ const Input = ({
|
|||||||
label,
|
label,
|
||||||
autoComplete,
|
autoComplete,
|
||||||
fullWidth = true,
|
fullWidth = true,
|
||||||
disabled = false
|
disabled = false,
|
||||||
|
|
||||||
}: InputProps): React.ReactElement => {
|
}: InputProps): React.ReactElement => {
|
||||||
|
const fieldError = error?.fields?.[name]
|
||||||
const fieldError = error?.fields?.[name];
|
|
||||||
return (
|
return (
|
||||||
<TextField name={name} type={type} label={label}
|
<TextField
|
||||||
value={value} onChange={onChange}
|
name={name}
|
||||||
error={Boolean(fieldError)} helperText={fieldError}
|
type={type}
|
||||||
variant="outlined" required={required} fullWidth={fullWidth} margin="dense" disabled={disabled} autoComplete={autoComplete} />
|
label={label}
|
||||||
|
value={value}
|
||||||
);
|
onChange={onChange}
|
||||||
|
error={Boolean(fieldError)}
|
||||||
|
helperText={fieldError}
|
||||||
|
variant="outlined"
|
||||||
|
required={required}
|
||||||
|
fullWidth={fullWidth}
|
||||||
|
margin="dense"
|
||||||
|
disabled={disabled}
|
||||||
|
autoComplete={autoComplete}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
export default Input;
|
export default Input
|
||||||
|
@ -1,27 +1,39 @@
|
|||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useIntl } from 'react-intl'
|
import { useIntl } from 'react-intl'
|
||||||
|
|
||||||
type SubmitButton = {
|
type SubmitButton = {
|
||||||
value: string;
|
value: string
|
||||||
disabled?: boolean;
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
const SubmitButton = (props: SubmitButton): React.ReactElement => {
|
const SubmitButton = (props: SubmitButton): React.ReactElement => {
|
||||||
const [disabled] = useState(props.disabled ? true : false);
|
const [disabled] = useState(props.disabled ? true : false)
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
let valueTxt = props.value;
|
let valueTxt = props.value
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
valueTxt = intl.formatMessage({ id: "common.wait", defaultMessage: "Please wait ..." });
|
valueTxt = intl.formatMessage({ id: 'common.wait', defaultMessage: 'Please wait ...' })
|
||||||
}
|
}
|
||||||
const [value] = useState(valueTxt);
|
const [value] = useState(valueTxt)
|
||||||
return (
|
return (
|
||||||
<Button color="primary" size="medium" variant="contained" type="submit"
|
<Button
|
||||||
disableElevation={true} disabled={disabled}
|
color="primary"
|
||||||
style={{ width: '350px', height: '53px', padding: '0px 20px', margin: '7px 0px', fontSize: '18px' }} >
|
size="medium"
|
||||||
|
variant="contained"
|
||||||
|
type="submit"
|
||||||
|
disableElevation={true}
|
||||||
|
disabled={disabled}
|
||||||
|
style={{
|
||||||
|
width: '350px',
|
||||||
|
height: '53px',
|
||||||
|
padding: '0px 20px',
|
||||||
|
margin: '7px 0px',
|
||||||
|
fontSize: '18px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{value}
|
{value}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SubmitButton;
|
export default SubmitButton
|
||||||
|
@ -14,20 +14,60 @@ const Footer = (): React.ReactElement => {
|
|||||||
<img src={poweredByIcon} alt="Powered By WiseMapping" />
|
<img src={poweredByIcon} alt="Powered By WiseMapping" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div >
|
<div>
|
||||||
<h4><FormattedMessage id="footer.faqandhelp" defaultMessage="Help & FAQ" /></h4>
|
<h4>
|
||||||
<div><a href="https://www.wisemapping.com/faq.html"> <FormattedMessage id="footer.faq" defaultMessage="F.A.Q." /> </a></div >
|
<FormattedMessage id="footer.faqandhelp" defaultMessage="Help & FAQ" />
|
||||||
<div><a href="https://www.wisemapping.com/termsofuse.html"> <FormattedMessage id="footer.termsandconditions" defaultMessage="Term And Conditions" /> </a></div>
|
</h4>
|
||||||
<div><a href="mailto:team@wisemapping.com"> <FormattedMessage id="footer.contactus" defaultMessage="Contact Us" /> </a></div>
|
<div>
|
||||||
|
<a href="https://www.wisemapping.com/faq.html">
|
||||||
|
{' '}
|
||||||
|
<FormattedMessage id="footer.faq" defaultMessage="F.A.Q." />{' '}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="https://www.wisemapping.com/termsofuse.html">
|
||||||
|
{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id="footer.termsandconditions"
|
||||||
|
defaultMessage="Term And Conditions"
|
||||||
|
/>{' '}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="mailto:team@wisemapping.com">
|
||||||
|
{' '}
|
||||||
|
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />{' '}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>
|
||||||
|
<FormattedMessage id="footer.others" defaultMessage="Others" />
|
||||||
|
</h4>
|
||||||
|
<div>
|
||||||
|
<a href="https://www.wisemapping.com/aboutus.html">
|
||||||
|
{' '}
|
||||||
|
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="mailto:feedback@wisemapping.com">
|
||||||
|
{' '}
|
||||||
|
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />{' '}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="http://www.wisemapping.org/">
|
||||||
|
{' '}
|
||||||
|
<FormattedMessage
|
||||||
|
id="footer.opensource"
|
||||||
|
defaultMessage="Open Source"
|
||||||
|
/>{' '}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div >
|
|
||||||
<h4><FormattedMessage id="footer.others" defaultMessage="Others" /></h4>
|
|
||||||
<div><a href="https://www.wisemapping.com/aboutus.html"> <FormattedMessage id="footer.aboutus" defaultMessage="About Us" /></a></div >
|
|
||||||
<div><a href="mailto:feedback@wisemapping.com" > <FormattedMessage id="footer.feedback" defaultMessage="Feedback" /> </a></div>
|
|
||||||
<div><a href="http://www.wisemapping.org/"> <FormattedMessage id="footer.opensource" defaultMessage="Open Source" /> </a></div>
|
|
||||||
</div>
|
</div>
|
||||||
</StyledFooter>
|
</StyledFooter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Footer;
|
export default Footer
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components'
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|
||||||
export const StyledFooter = styled.footer`
|
export const StyledFooter = styled.footer`
|
||||||
height: 250px;
|
height: 250px;
|
||||||
margin-top: 80px;
|
margin-top: 80px;
|
||||||
padding: 60px 40px 10px 50px;
|
padding: 60px 40px 10px 50px;
|
||||||
background-color: #f9a826;
|
background-color: #f9a826;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 200px 1fr 1fr 3fr;
|
grid-template-columns: 200px 1fr 1fr 3fr;
|
||||||
|
|
||||||
& a {
|
& a {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: white;
|
color: white;
|
||||||
word-wrap: nowrap;
|
word-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
& h4 {
|
& h4 {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: white;
|
color: white;
|
||||||
word-wrap: nowrap;
|
word-wrap: nowrap;
|
||||||
font-weight: 500px;
|
font-weight: 500px;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&>svg {
|
& > svg {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
& div:nth-child(2) {
|
& div:nth-child(2) {
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
& div:nth-child(3) {
|
& div:nth-child(3) {
|
||||||
grid-column: 3;
|
grid-column: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
& div:nth-child(4) {
|
& div:nth-child(4) {
|
||||||
grid-column: 4;
|
grid-column: 4;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}`
|
}
|
||||||
|
`
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import Container from "@material-ui/core/Container";
|
import Container from '@material-ui/core/Container'
|
||||||
import withStyles from "@material-ui/core/styles/withStyles";
|
import withStyles from '@material-ui/core/styles/withStyles'
|
||||||
|
|
||||||
const FormContainer = withStyles({
|
const FormContainer = withStyles({
|
||||||
root: {
|
root: {
|
||||||
padding: '20px 10px 0px 20px',
|
padding: '20px 10px 0px 20px',
|
||||||
maxWidth: '380px',
|
maxWidth: '380px',
|
||||||
textAlign: 'center'
|
textAlign: 'center',
|
||||||
}
|
},
|
||||||
})(Container)
|
})(Container)
|
||||||
|
|
||||||
export default FormContainer;
|
export default FormContainer
|
||||||
|
@ -1,39 +1,60 @@
|
|||||||
import { StyledNav, StyledDiv, Logo } from './styled';
|
import { StyledNav, StyledDiv, Logo } from './styled'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FormattedMessage } from 'react-intl'
|
import { FormattedMessage } from 'react-intl'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
|
|
||||||
import logo from '../../../images/logo-small.svg';
|
import logo from '../../../images/logo-small.svg'
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
type: 'only-signup' | 'only-signin' | 'none';
|
type: 'only-signup' | 'only-signin' | 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Header = ({ type }: HeaderProps): React.ReactElement => {
|
export const Header = ({ type }: HeaderProps): React.ReactElement => {
|
||||||
|
let signUpButton
|
||||||
let signUpButton;
|
let text
|
||||||
let text;
|
let signInButton
|
||||||
let signInButton;
|
|
||||||
if (type === 'only-signup') {
|
if (type === 'only-signup') {
|
||||||
text = <span className="header-area-content-span"><span><FormattedMessage id="header.donthaveaccount" defaultMessage="Don't have an account ?" /></span></span>;
|
text = (
|
||||||
signUpButton = <SignUpButton className="header-area-right2" />;
|
<span className="header-area-content-span">
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id="header.donthaveaccount"
|
||||||
|
defaultMessage="Don't have an account ?"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
signUpButton = <SignUpButton className="header-area-right2" />
|
||||||
} else if (type === 'only-signin') {
|
} else if (type === 'only-signin') {
|
||||||
text = <span className="header-area-content-span"><span><FormattedMessage id="header.haveaccount" defaultMessage="Already have an account?" /></span></span>;
|
text = (
|
||||||
signUpButton = <SignInButton className="header-area-right2" />;
|
<span className="header-area-content-span">
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id="header.haveaccount"
|
||||||
|
defaultMessage="Already have an account?"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
signUpButton = <SignInButton className="header-area-right2" />
|
||||||
} else if (type === 'none') {
|
} else if (type === 'none') {
|
||||||
text = '';
|
text = ''
|
||||||
signUpButton = '';
|
signUpButton = ''
|
||||||
} else {
|
} else {
|
||||||
signUpButton = <SignUpButton className="header-area-right2" />
|
signUpButton = <SignUpButton className="header-area-right2" />
|
||||||
signInButton = <SignInButton className="header-area-right2" />;
|
signInButton = <SignInButton className="header-area-right2" />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledNav>
|
<StyledNav>
|
||||||
<StyledDiv>
|
<StyledDiv>
|
||||||
<Logo><Link to="/c/login" className="header-logo"><img src={String(logo)} alt="logo" /></Link></Logo>
|
<Logo>
|
||||||
|
<Link to="/c/login" className="header-logo">
|
||||||
|
<img src={String(logo)} alt="logo" />
|
||||||
|
</Link>
|
||||||
|
</Logo>
|
||||||
{text}
|
{text}
|
||||||
{signUpButton}
|
{signUpButton}
|
||||||
{signInButton}
|
{signInButton}
|
||||||
@ -42,23 +63,34 @@ export const Header = ({ type }: HeaderProps): React.ReactElement => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
className?: string;
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SignInButton = (props: ButtonProps): React.ReactElement => {
|
export const SignInButton = (props: ButtonProps): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<span className={`${props.className}`}>
|
<span className={`${props.className}`}>
|
||||||
<Button color="primary" size="medium" variant="outlined" component={Link} to="/c/login"><FormattedMessage id="login.signin" defaultMessage="Sign In" /></Button>
|
<Button color="primary" size="medium" variant="outlined" component={Link} to="/c/login">
|
||||||
</span>);
|
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SignUpButton = (props: ButtonProps): React.ReactElement => {
|
const SignUpButton = (props: ButtonProps): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<span className={`${props.className}`}>
|
<span className={`${props.className}`}>
|
||||||
<Button color="primary" size="medium" variant="outlined" component={Link} to="/c/registration"><FormattedMessage id="login.signup" defaultMessage="Sign Up" /></Button>
|
<Button
|
||||||
</span>);
|
color="primary"
|
||||||
|
size="medium"
|
||||||
|
variant="outlined"
|
||||||
|
component={Link}
|
||||||
|
to="/c/registration"
|
||||||
|
>
|
||||||
|
<FormattedMessage id="login.signup" defaultMessage="Sign Up" />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Header;
|
export default Header
|
||||||
|
@ -1,77 +1,77 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components'
|
||||||
|
|
||||||
export const StyledNav = styled.nav`
|
export const StyledNav = styled.nav`
|
||||||
height: 90px;
|
height: 90px;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: -16px;
|
top: -16px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
-webkit-backface-visibility: hidden;
|
-webkit-backface-visibility: hidden;
|
||||||
|
|
||||||
&::before,
|
&::before,
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
top: 58px;
|
top: 58px;
|
||||||
box-shadow: 0 4px 10px 0 rgba(202, 34, 34, 0.05), 0 5px 30px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 4px 10px 0 rgba(202, 34, 34, 0.05), 0 5px 30px 0 rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
background: linear-gradient(white, rgba(255, 255, 255, 0.3));
|
background: linear-gradient(white, rgba(255, 255, 255, 0.3));
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Review ....*/
|
/* Review ....*/
|
||||||
.header-middle {
|
.header-middle {
|
||||||
grid-column-start: 2;
|
grid-column-start: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-area-right1 {
|
.header-area-right1 {
|
||||||
grid-column-start: 3;
|
grid-column-start: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-area-right2 {
|
.header-area-right2 {
|
||||||
grid-column-start: 4;
|
grid-column-start: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-area-right1 span,
|
.header-area-right1 span,
|
||||||
.header-area-right2 span {
|
.header-area-right2 span {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
.header-area-content-span {
|
.header-area-content-span {
|
||||||
grid-column-start: 2;
|
grid-column-start: 2;
|
||||||
grid-column-end: 4;
|
grid-column-end: 4;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
|
|
||||||
export const StyledDiv = styled.nav`
|
export const StyledDiv = styled.nav`
|
||||||
background: white;
|
background: white;
|
||||||
height: 74px;
|
height: 74px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
margin-top: -16px;
|
margin-top: -16px;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
grid-template-columns: 150px 1fr 130px 160px 50px;
|
grid-template-columns: 150px 1fr 130px 160px 50px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const Logo = styled.span`
|
export const Logo = styled.span`
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
|
||||||
.header-logo a {
|
.header-logo a {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
`
|
`
|
@ -1,62 +1,78 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react'
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom'
|
||||||
import Header from '../layout/header';
|
import Header from '../layout/header'
|
||||||
import Footer from '../layout/footer';
|
import Footer from '../layout/footer'
|
||||||
import SubmitButton from '../form/submit-button';
|
import SubmitButton from '../form/submit-button'
|
||||||
import Input from '../form/input';
|
import Input from '../form/input'
|
||||||
import GlobalError from '../form/global-error';
|
import GlobalError from '../form/global-error'
|
||||||
import FormContainer from '../layout/form-container';
|
import FormContainer from '../layout/form-container'
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography'
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
import Link from '@material-ui/core/Link';
|
import Link from '@material-ui/core/Link'
|
||||||
|
|
||||||
type ConfigStatusProps = {
|
type ConfigStatusProps = {
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConfigStatusMessage = ({ enabled = false }: ConfigStatusProps): React.ReactElement => {
|
const ConfigStatusMessage = ({ enabled = false }: ConfigStatusProps): React.ReactElement => {
|
||||||
let result;
|
let result
|
||||||
if (enabled === true) {
|
if (enabled === true) {
|
||||||
result = (<div className="db-warn-msg">
|
result = (
|
||||||
|
<div className="db-warn-msg">
|
||||||
<p>
|
<p>
|
||||||
<FormattedMessage id="login.hsqldbcofig" defaultMessage="Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL" description="Missing production database configured" /><a href="https://wisemapping.atlassian.net/wiki/display/WS/Database+Configuration"> here</a>
|
<FormattedMessage
|
||||||
|
id="login.hsqldbcofig"
|
||||||
|
defaultMessage="Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL"
|
||||||
|
description="Missing production database configured"
|
||||||
|
/>
|
||||||
|
<a href="https://wisemapping.atlassian.net/wiki/display/WS/Database+Configuration">
|
||||||
|
{' '}
|
||||||
|
here
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>);
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return result || null;
|
return result || null
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoginError = () => {
|
const LoginError = () => {
|
||||||
// @Todo: This must be reviewed to be based on navigation state.
|
// @Todo: This must be reviewed to be based on navigation state.
|
||||||
// Login error example: http://localhost:8080/c/login?login.error=2
|
// Login error example: http://localhost:8080/c/login?login.error=2
|
||||||
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 | string = null;
|
let msg: null | string = null
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
switch (errorCode) {
|
switch (errorCode) {
|
||||||
case "3":
|
case '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({
|
||||||
break;
|
id: 'login.userinactive',
|
||||||
|
defaultMessage:
|
||||||
|
"Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!.",
|
||||||
|
})
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
msg = intl.formatMessage({ id: "login.error", defaultMessage: "The email address or password you entered is not valid." });
|
msg = intl.formatMessage({
|
||||||
|
id: 'login.error',
|
||||||
|
defaultMessage: 'The email address or password you entered is not valid.',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (msg ? <GlobalError error={{ msg: msg }} /> : null);
|
return msg ? <GlobalError error={{ msg: msg }} /> : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const LoginPage = (): React.ReactElement => {
|
const LoginPage = (): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Login | WiseMapping';
|
document.title = 'Login | WiseMapping'
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type='only-signup' />
|
<Header type="only-signup" />
|
||||||
|
|
||||||
<FormContainer maxWidth="xs">
|
<FormContainer maxWidth="xs">
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
@ -70,26 +86,54 @@ const LoginPage = (): React.ReactElement => {
|
|||||||
<LoginError />
|
<LoginError />
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<form action="/c/perform-login" method="POST" >
|
<form action="/c/perform-login" method="POST">
|
||||||
<Input name="username" type="email" label={intl.formatMessage({ id: "login.email", defaultMessage: "Email" })} required autoComplete="email" />
|
<Input
|
||||||
<Input name="password" type="password" label={intl.formatMessage({ id: "login.password", defaultMessage: "Password" })} required autoComplete="current-password" />
|
name="username"
|
||||||
|
type="email"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'login.email',
|
||||||
|
defaultMessage: 'Email',
|
||||||
|
})}
|
||||||
|
required
|
||||||
|
autoComplete="email"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'login.password',
|
||||||
|
defaultMessage: 'Password',
|
||||||
|
})}
|
||||||
|
required
|
||||||
|
autoComplete="current-password"
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<input name="remember-me" id="remember-me" type="checkbox" />
|
<input name="remember-me" id="remember-me" type="checkbox" />
|
||||||
<label htmlFor="remember-me"><FormattedMessage id="login.remberme" defaultMessage="Remember me" /></label>
|
<label htmlFor="remember-me">
|
||||||
|
<FormattedMessage
|
||||||
|
id="login.remberme"
|
||||||
|
defaultMessage="Remember me"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<SubmitButton value={intl.formatMessage({ id: "login.signin", defaultMessage: "Sign In" })} />
|
<SubmitButton
|
||||||
|
value={intl.formatMessage({
|
||||||
|
id: 'login.signin',
|
||||||
|
defaultMessage: 'Sign In',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<Link component={RouterLink} to="/c/forgot-password"><FormattedMessage id="login.forgotpwd" defaultMessage="Forgot Password ?" /></Link>
|
<Link component={RouterLink} to="/c/forgot-password">
|
||||||
|
<FormattedMessage id="login.forgotpwd" defaultMessage="Forgot Password ?" />
|
||||||
|
</Link>
|
||||||
<ConfigStatusMessage />
|
<ConfigStatusMessage />
|
||||||
|
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage
|
||||||
|
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } 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 Client, { ErrorInfo } from "../../../../classes/client";
|
import Client, { ErrorInfo } from '../../../../classes/client'
|
||||||
import Input from "../../../form/input";
|
import Input from '../../../form/input'
|
||||||
import BaseDialog from "../../action-dispatcher/base-dialog";
|
import BaseDialog from '../../action-dispatcher/base-dialog'
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import { activeInstance, fetchAccount } from "../../../../redux/clientSlice";
|
import { activeInstance, fetchAccount } from '../../../../redux/clientSlice'
|
||||||
|
|
||||||
import Alert from "@material-ui/lab/Alert";
|
|
||||||
import FormControl from "@material-ui/core/FormControl";
|
|
||||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
|
||||||
import FormGroup from "@material-ui/core/FormGroup";
|
|
||||||
import Switch from "@material-ui/core/Switch";
|
|
||||||
|
|
||||||
|
import Alert from '@material-ui/lab/Alert'
|
||||||
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
|
import FormControlLabel from '@material-ui/core/FormControlLabel'
|
||||||
|
import FormGroup from '@material-ui/core/FormGroup'
|
||||||
|
import Switch from '@material-ui/core/Switch'
|
||||||
|
|
||||||
type AccountInfoDialogProps = {
|
type AccountInfoDialogProps = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountInfoModel = {
|
type AccountInfoModel = {
|
||||||
email: string,
|
email: string
|
||||||
firstname: string,
|
firstname: string
|
||||||
lastname: string
|
lastname: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: AccountInfoModel = { firstname: '', lastname: '', email: '' };
|
const defaultModel: AccountInfoModel = { firstname: '', lastname: '', email: '' }
|
||||||
const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps):React.ReactElement => {
|
const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
const [remove, setRemove] = React.useState<boolean>(false);
|
const [remove, setRemove] = React.useState<boolean>(false)
|
||||||
|
|
||||||
const [model, setModel] = React.useState<AccountInfoModel>(defaultModel);
|
const [model, setModel] = React.useState<AccountInfoModel>(defaultModel)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const mutationChangeName = useMutation<void, ErrorInfo, AccountInfoModel>((model: AccountInfoModel) => {
|
const mutationChangeName = useMutation<void, ErrorInfo, AccountInfoModel>(
|
||||||
return client.updateAccountInfo(model.firstname, model.lastname);
|
(model: AccountInfoModel) => {
|
||||||
|
return client.updateAccountInfo(model.firstname, model.lastname)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@ -43,13 +43,14 @@ const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps):React.ReactEleme
|
|||||||
onClose()
|
onClose()
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const mutationRemove = useMutation<void, ErrorInfo, void>(() => {
|
const mutationRemove = useMutation<void, ErrorInfo, void>(
|
||||||
return client.deleteAccount();
|
() => {
|
||||||
|
return client.deleteAccount()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@ -57,80 +58,121 @@ const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps):React.ReactEleme
|
|||||||
onClose()
|
onClose()
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const account = fetchAccount();
|
const account = fetchAccount()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (account) {
|
if (account) {
|
||||||
setModel({
|
setModel({
|
||||||
email: account?.email,
|
email: account?.email,
|
||||||
lastname: account?.lastname,
|
lastname: account?.lastname,
|
||||||
firstname: account?.firstname
|
firstname: account?.firstname,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}, [account?.email])
|
}, [account?.email])
|
||||||
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
if (remove) {
|
if (remove) {
|
||||||
mutationRemove.mutate();
|
mutationRemove.mutate()
|
||||||
} else {
|
} else {
|
||||||
mutationChangeName.mutate(model);
|
mutationChangeName.mutate(model)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name
|
||||||
const value = event.target.value;
|
const value = event.target.value
|
||||||
setModel({ ...model, [name as keyof AccountInfoModel]: value });
|
setModel({ ...model, [name as keyof AccountInfoModel]: value })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOnRemoveChange = (event) => {
|
const handleOnRemoveChange = (event) => {
|
||||||
setRemove(event.target.checked);
|
setRemove(event.target.checked)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
|
<BaseDialog
|
||||||
|
onClose={handleOnClose}
|
||||||
|
onSubmit={handleOnSubmit}
|
||||||
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'accountinfo.title', defaultMessage: 'Account info' })}
|
title={intl.formatMessage({ id: 'accountinfo.title', defaultMessage: 'Account info' })}
|
||||||
submitButton={intl.formatMessage({ id: 'accountinfo.button', defaultMessage: 'Accept' })}>
|
submitButton={intl.formatMessage({
|
||||||
|
id: 'accountinfo.button',
|
||||||
|
defaultMessage: 'Accept',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input name="email" type="text" disabled={true} label={intl.formatMessage({ id: "accountinfo.email", defaultMessage: "Email" })}
|
<Input
|
||||||
value={model.email} onChange={handleOnChange} error={error} fullWidth={true} />
|
name="email"
|
||||||
|
type="text"
|
||||||
|
disabled={true}
|
||||||
|
label={intl.formatMessage({ id: 'accountinfo.email', defaultMessage: 'Email' })}
|
||||||
|
value={model.email}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
error={error}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="firstname" type="text" label={intl.formatMessage({ id: "accountinfo.firstname", defaultMessage: "First Name" })}
|
<Input
|
||||||
value={model.firstname} onChange={handleOnChange} required={true} fullWidth={true} />
|
name="firstname"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'accountinfo.firstname',
|
||||||
|
defaultMessage: 'First Name',
|
||||||
|
})}
|
||||||
|
value={model.firstname}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
required={true}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="lastname" type="text" label={intl.formatMessage({ id: "accountinfo.lastname", defaultMessage: "Last Name" })}
|
<Input
|
||||||
value={model.lastname} onChange={handleOnChange} required={true} fullWidth={true} />
|
name="lastname"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'accountinfo.lastname',
|
||||||
|
defaultMessage: 'Last Name',
|
||||||
|
})}
|
||||||
|
value={model.lastname}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
required={true}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
{remove &&
|
{remove && (
|
||||||
<Alert severity="error">
|
<Alert severity="error">
|
||||||
<FormattedMessage id="account.delete-warning" defaultMessage="Keep in mind that you will not be able retrieve any mindmap you have added. All your information will be deleted and it can not be restored." />
|
<FormattedMessage
|
||||||
|
id="account.delete-warning"
|
||||||
|
defaultMessage="Keep in mind that you will not be able retrieve any mindmap you have added. All your information will be deleted and it can not be restored."
|
||||||
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
)}
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={<Switch checked={remove} onChange={(handleOnRemoveChange)} name="remove" color="primary" />}
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={remove}
|
||||||
|
onChange={handleOnRemoveChange}
|
||||||
|
name="remove"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
}
|
||||||
label="Delete Account"
|
label="Delete Account"
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
export default AccountInfoDialog;
|
export default AccountInfoDialog
|
||||||
|
|
||||||
|
@ -1,84 +1,116 @@
|
|||||||
import FormControl from "@material-ui/core/FormControl";
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from 'react-intl'
|
||||||
import { useMutation } from "react-query";
|
import { useMutation } from 'react-query'
|
||||||
import Client, { ErrorInfo } from "../../../../classes/client";
|
import Client, { ErrorInfo } from '../../../../classes/client'
|
||||||
import Input from "../../../form/input";
|
import Input from '../../../form/input'
|
||||||
import BaseDialog from "../../action-dispatcher/base-dialog";
|
import BaseDialog from '../../action-dispatcher/base-dialog'
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import { activeInstance } from "../../../../redux/clientSlice";
|
import { activeInstance } from '../../../../redux/clientSlice'
|
||||||
|
|
||||||
|
|
||||||
type ChangePasswordDialogProps = {
|
type ChangePasswordDialogProps = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChangePasswordModel = {
|
type ChangePasswordModel = {
|
||||||
password: string,
|
password: string
|
||||||
retryPassword: string
|
retryPassword: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: ChangePasswordModel = { password: '', retryPassword: '' };
|
const defaultModel: ChangePasswordModel = { password: '', retryPassword: '' }
|
||||||
const ChangePasswordDialog = ({ onClose }: ChangePasswordDialogProps):React.ReactElement => {
|
const ChangePasswordDialog = ({ onClose }: ChangePasswordDialogProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const [model, setModel] = React.useState<ChangePasswordModel>(defaultModel);
|
const [model, setModel] = React.useState<ChangePasswordModel>(defaultModel)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const mutation = useMutation<void, ErrorInfo, ChangePasswordModel>((model: ChangePasswordModel) => {
|
const mutation = useMutation<void, ErrorInfo, ChangePasswordModel>(
|
||||||
return client.updateAccountPassword(model.password);
|
(model: ChangePasswordModel) => {
|
||||||
|
return client.updateAccountPassword(model.password)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
onClose()
|
onClose()
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
|
|
||||||
// Check password are equal ...
|
// Check password are equal ...
|
||||||
if (model.password != model.retryPassword) {
|
if (model.password != model.retryPassword) {
|
||||||
setError({ msg: intl.formatMessage({ id: 'changepwd.password-match', defaultMessage: 'Password do not match. Please, try again.' }) });
|
setError({
|
||||||
return;
|
msg: intl.formatMessage({
|
||||||
|
id: 'changepwd.password-match',
|
||||||
|
defaultMessage: 'Password do not match. Please, try again.',
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation.mutate(model);
|
mutation.mutate(model)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name
|
||||||
const value = event.target.value;
|
const value = event.target.value
|
||||||
setModel({ ...model, [name as keyof ChangePasswordModel]: value });
|
setModel({ ...model, [name as keyof ChangePasswordModel]: value })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
|
<BaseDialog
|
||||||
|
onClose={handleOnClose}
|
||||||
|
onSubmit={handleOnSubmit}
|
||||||
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'changepwd.title', defaultMessage: 'Change Password' })}
|
title={intl.formatMessage({ id: 'changepwd.title', defaultMessage: 'Change Password' })}
|
||||||
description={intl.formatMessage({ id: 'changepwd.description', defaultMessage: 'Please, provide the new password for your account.' })}
|
description={intl.formatMessage({
|
||||||
submitButton={intl.formatMessage({ id: 'changepwd.button', defaultMessage: 'Change' })}>
|
id: 'changepwd.description',
|
||||||
|
defaultMessage: 'Please, provide the new password for your account.',
|
||||||
|
})}
|
||||||
|
submitButton={intl.formatMessage({ id: 'changepwd.button', defaultMessage: 'Change' })}
|
||||||
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input name="password" type="password" label={intl.formatMessage({ id: "changepwd.password", defaultMessage: "Password" })}
|
<Input
|
||||||
value={model.password} onChange={handleOnChange} error={error} fullWidth={true} autoComplete="new-password" />
|
name="password"
|
||||||
|
type="password"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'changepwd.password',
|
||||||
|
defaultMessage: 'Password',
|
||||||
|
})}
|
||||||
|
value={model.password}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
error={error}
|
||||||
|
fullWidth={true}
|
||||||
|
autoComplete="new-password"
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="retryPassword" type="password" label={intl.formatMessage({ id: "changepwd.confirm-password", defaultMessage: "Confirm Password" })}
|
<Input
|
||||||
value={model.retryPassword} onChange={handleOnChange} required={true} fullWidth={true} autoComplete="new-password" />
|
name="retryPassword"
|
||||||
|
type="password"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'changepwd.confirm-password',
|
||||||
|
defaultMessage: 'Confirm Password',
|
||||||
|
})}
|
||||||
|
value={model.retryPassword}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
required={true}
|
||||||
|
fullWidth={true}
|
||||||
|
autoComplete="new-password"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
export default ChangePasswordDialog;
|
export default ChangePasswordDialog
|
||||||
|
|
||||||
|
@ -1,44 +1,46 @@
|
|||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||||
import Menu from "@material-ui/core/Menu";
|
import Menu from '@material-ui/core/Menu'
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
import SettingsApplicationsOutlined from "@material-ui/icons/SettingsApplicationsOutlined";
|
import SettingsApplicationsOutlined from '@material-ui/icons/SettingsApplicationsOutlined'
|
||||||
import AccountCircle from "@material-ui/icons/AccountCircle";
|
import AccountCircle from '@material-ui/icons/AccountCircle'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from 'react-intl'
|
||||||
import { fetchAccount } from '../../../redux/clientSlice';
|
import { fetchAccount } from '../../../redux/clientSlice'
|
||||||
import AccountInfoDialog from './account-info-dialog';
|
import AccountInfoDialog from './account-info-dialog'
|
||||||
import ChangePasswordDialog from './change-password-dialog';
|
import ChangePasswordDialog from './change-password-dialog'
|
||||||
import LockOpenOutlined from "@material-ui/icons/LockOpenOutlined";
|
import LockOpenOutlined from '@material-ui/icons/LockOpenOutlined'
|
||||||
import Link from "@material-ui/core/Link";
|
import Link from '@material-ui/core/Link'
|
||||||
import ExitToAppOutlined from "@material-ui/icons/ExitToAppOutlined";
|
import ExitToAppOutlined from '@material-ui/icons/ExitToAppOutlined'
|
||||||
|
|
||||||
type ActionType = 'change-password' | 'account-info' | undefined;
|
type ActionType = 'change-password' | 'account-info' | undefined
|
||||||
const AccountMenu = (): React.ReactElement => {
|
const AccountMenu = (): React.ReactElement => {
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl)
|
||||||
const [action, setAction] = React.useState<ActionType>(undefined);
|
const [action, setAction] = React.useState<ActionType>(undefined)
|
||||||
|
|
||||||
|
|
||||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null)
|
||||||
};
|
}
|
||||||
|
|
||||||
const account = fetchAccount();
|
const account = fetchAccount()
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip arrow={true} title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}>
|
<Tooltip
|
||||||
<IconButton
|
arrow={true}
|
||||||
onClick={handleMenu}>
|
title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}
|
||||||
|
>
|
||||||
|
<IconButton onClick={handleMenu}>
|
||||||
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
||||||
</IconButton >
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu id="appbar-profile"
|
<Menu
|
||||||
|
id="appbar-profile"
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
keepMounted
|
keepMounted
|
||||||
open={open}
|
open={open}
|
||||||
@ -53,14 +55,22 @@ const AccountMenu = (): React.ReactElement => {
|
|||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={() => { handleClose(), setAction('account-info') }}>
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
handleClose(), setAction('account-info')
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<SettingsApplicationsOutlined fontSize="small" />
|
<SettingsApplicationsOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="menu.account" defaultMessage="Account" />
|
<FormattedMessage id="menu.account" defaultMessage="Account" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onClick={() => { handleClose(), setAction('change-password') }}>
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
handleClose(), setAction('change-password')
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<LockOpenOutlined fontSize="small" />
|
<LockOpenOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
@ -76,15 +86,12 @@ const AccountMenu = (): React.ReactElement => {
|
|||||||
</Link>
|
</Link>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
{action == 'change-password' &&
|
{action == 'change-password' && (
|
||||||
<ChangePasswordDialog onClose={() => setAction(undefined)} />
|
<ChangePasswordDialog onClose={() => setAction(undefined)} />
|
||||||
}
|
)}
|
||||||
{action == 'account-info' &&
|
{action == 'account-info' && <AccountInfoDialog onClose={() => setAction(undefined)} />}
|
||||||
<AccountInfoDialog onClose={() => setAction(undefined)} />
|
</span>
|
||||||
}
|
)
|
||||||
</span>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default AccountMenu
|
||||||
|
|
||||||
export default AccountMenu;
|
|
||||||
|
@ -1,40 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import DescriptionOutlinedIcon from '@material-ui/icons/DescriptionOutlined';
|
import DescriptionOutlinedIcon from '@material-ui/icons/DescriptionOutlined'
|
||||||
import FileCopyOutlinedIcon from '@material-ui/icons/FileCopyOutlined';
|
import FileCopyOutlinedIcon from '@material-ui/icons/FileCopyOutlined'
|
||||||
import DeleteOutlinedIcon from '@material-ui/icons/DeleteOutlined';
|
import DeleteOutlinedIcon from '@material-ui/icons/DeleteOutlined'
|
||||||
import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined';
|
import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined'
|
||||||
import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
|
import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'
|
||||||
import EditOutlinedIcon from '@material-ui/icons/EditOutlined';
|
import EditOutlinedIcon from '@material-ui/icons/EditOutlined'
|
||||||
import PublicOutlinedIcon from '@material-ui/icons/PublicOutlined';
|
import PublicOutlinedIcon from '@material-ui/icons/PublicOutlined'
|
||||||
import PrintOutlinedIcon from '@material-ui/icons/PrintOutlined';
|
import PrintOutlinedIcon from '@material-ui/icons/PrintOutlined'
|
||||||
import ShareOutlinedIcon from '@material-ui/icons/ShareOutlined';
|
import ShareOutlinedIcon from '@material-ui/icons/ShareOutlined'
|
||||||
import LabelOutlined from '@material-ui/icons/LabelOutlined';
|
import LabelOutlined from '@material-ui/icons/LabelOutlined'
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl'
|
||||||
import { fetchMapById } from '../../../redux/clientSlice';
|
import { fetchMapById } from '../../../redux/clientSlice'
|
||||||
import Menu from '@material-ui/core/Menu';
|
import Menu from '@material-ui/core/Menu'
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from '@material-ui/core/Divider'
|
||||||
export type ActionType = 'open' | 'share' | 'import' | 'delete' | 'info' | 'create' | 'duplicate' | 'export' | 'label' | 'rename' | 'print' | 'info' | 'publish' | 'history' | undefined;
|
export type ActionType =
|
||||||
|
| 'open'
|
||||||
|
| 'share'
|
||||||
|
| 'import'
|
||||||
|
| 'delete'
|
||||||
|
| 'info'
|
||||||
|
| 'create'
|
||||||
|
| 'duplicate'
|
||||||
|
| 'export'
|
||||||
|
| 'label'
|
||||||
|
| 'rename'
|
||||||
|
| 'print'
|
||||||
|
| 'info'
|
||||||
|
| 'publish'
|
||||||
|
| 'history'
|
||||||
|
| undefined
|
||||||
|
|
||||||
interface ActionProps {
|
interface ActionProps {
|
||||||
onClose: (action: ActionType) => void;
|
onClose: (action: ActionType) => void
|
||||||
anchor?: HTMLElement;
|
anchor?: HTMLElement
|
||||||
mapId?: number
|
mapId?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActionChooser = (props: ActionProps): React.ReactElement => {
|
const ActionChooser = (props: ActionProps): React.ReactElement => {
|
||||||
const { anchor, onClose, mapId } = props;
|
const { anchor, onClose, mapId } = props
|
||||||
|
|
||||||
const handleOnClose = (action: ActionType): ((event: React.MouseEvent<HTMLLIElement>) => void) => {
|
const handleOnClose = (
|
||||||
|
action: ActionType
|
||||||
|
): ((event: React.MouseEvent<HTMLLIElement>) => void) => {
|
||||||
return (event): void => {
|
return (event): void => {
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
onClose(action);
|
onClose(action)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const role = mapId ? fetchMapById(mapId)?.map?.role : undefined;
|
const role = mapId ? fetchMapById(mapId)?.map?.role : undefined
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={anchor}
|
anchorEl={anchor}
|
||||||
@ -43,7 +60,7 @@ const ActionChooser = (props: ActionProps): React.ReactElement => {
|
|||||||
onClose={handleOnClose(undefined)}
|
onClose={handleOnClose(undefined)}
|
||||||
elevation={1}
|
elevation={1}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={handleOnClose('open')} style={{ width: "220px" }}>
|
<MenuItem onClick={handleOnClose('open')} style={{ width: '220px' }}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DescriptionOutlinedIcon />
|
<DescriptionOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
@ -59,14 +76,14 @@ const ActionChooser = (props: ActionProps): React.ReactElement => {
|
|||||||
<FormattedMessage id="action.duplicate" defaultMessage="Duplicate" />
|
<FormattedMessage id="action.duplicate" defaultMessage="Duplicate" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{role == 'owner' &&
|
{role == 'owner' && (
|
||||||
<MenuItem onClick={handleOnClose('rename')}>
|
<MenuItem onClick={handleOnClose('rename')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<EditOutlinedIcon />
|
<EditOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.rename" defaultMessage="Rename" />
|
<FormattedMessage id="action.rename" defaultMessage="Rename" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('label')}>
|
<MenuItem onClick={handleOnClose('label')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
@ -97,23 +114,23 @@ const ActionChooser = (props: ActionProps): React.ReactElement => {
|
|||||||
<FormattedMessage id="action.print" defaultMessage="Print" />
|
<FormattedMessage id="action.print" defaultMessage="Print" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{role == 'owner' &&
|
{role == 'owner' && (
|
||||||
<MenuItem onClick={handleOnClose('publish')}>
|
<MenuItem onClick={handleOnClose('publish')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<PublicOutlinedIcon />
|
<PublicOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.publish" defaultMessage="Publish" />
|
<FormattedMessage id="action.publish" defaultMessage="Publish" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{role == 'owner' &&
|
{role == 'owner' && (
|
||||||
<MenuItem onClick={handleOnClose('share')}>
|
<MenuItem onClick={handleOnClose('share')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<ShareOutlinedIcon />
|
<ShareOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.share" defaultMessage="Share" />
|
<FormattedMessage id="action.share" defaultMessage="Share" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('info')}>
|
<MenuItem onClick={handleOnClose('info')}>
|
||||||
@ -123,15 +140,16 @@ const ActionChooser = (props: ActionProps): React.ReactElement => {
|
|||||||
<FormattedMessage id="action.info" defaultMessage="Info" />
|
<FormattedMessage id="action.info" defaultMessage="Info" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{role != 'viewer' &&
|
{role != 'viewer' && (
|
||||||
<MenuItem onClick={handleOnClose('history')}>
|
<MenuItem onClick={handleOnClose('history')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DeleteOutlinedIcon />
|
<DeleteOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.history" defaultMessage="History" />
|
<FormattedMessage id="action.history" defaultMessage="History" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
}
|
)}
|
||||||
</Menu>);
|
</Menu>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ActionChooser;
|
export default ActionChooser
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
import withStyles from "@material-ui/core/styles/withStyles";
|
import withStyles from '@material-ui/core/styles/withStyles'
|
||||||
|
|
||||||
export const StyledMenuItem = withStyles({
|
export const StyledMenuItem = withStyles({
|
||||||
root: {
|
root: {
|
||||||
width: '300px',
|
width: '300px',
|
||||||
}
|
},
|
||||||
})(MenuItem);
|
})(MenuItem)
|
||||||
|
|
||||||
|
@ -1,38 +1,40 @@
|
|||||||
import React from "react";
|
import React from 'react'
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from 'react-intl'
|
||||||
import { ErrorInfo } from "../../../../classes/client";
|
import { ErrorInfo } from '../../../../classes/client'
|
||||||
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'
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from '@material-ui/core/Button'
|
||||||
import { PaperProps } from "@material-ui/core/Paper";
|
import { PaperProps } from '@material-ui/core/Paper'
|
||||||
|
|
||||||
export type DialogProps = {
|
export type DialogProps = {
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void;
|
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
children: unknown;
|
children: unknown
|
||||||
error?: ErrorInfo;
|
error?: ErrorInfo
|
||||||
|
|
||||||
title: string;
|
title: string
|
||||||
description?: string;
|
description?: string
|
||||||
|
|
||||||
submitButton?: string;
|
submitButton?: string
|
||||||
actionUrl?: string;
|
actionUrl?: string
|
||||||
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
|
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false
|
||||||
PaperProps?: Partial<PaperProps>;
|
PaperProps?: Partial<PaperProps>
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseDialog = (props: DialogProps): React.ReactElement => {
|
const BaseDialog = (props: DialogProps): React.ReactElement => {
|
||||||
const { onClose, onSubmit, maxWidth = 'sm', PaperProps } = props;
|
const { onClose, onSubmit, maxWidth = 'sm', PaperProps } = props
|
||||||
|
|
||||||
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
if (onSubmit) {
|
if (onSubmit) {
|
||||||
onSubmit(e);
|
onSubmit(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const description = props.description ? (<DialogContentText>{props.description}</DialogContentText>) : null;
|
const description = props.description ? (
|
||||||
|
<DialogContentText>{props.description}</DialogContentText>
|
||||||
|
) : null
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<StyledDialog
|
<StyledDialog
|
||||||
@ -40,11 +42,10 @@ const BaseDialog = (props: DialogProps): React.ReactElement => {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
maxWidth={maxWidth}
|
maxWidth={maxWidth}
|
||||||
PaperProps={PaperProps}
|
PaperProps={PaperProps}
|
||||||
fullWidth={true}>
|
fullWidth={true}
|
||||||
|
>
|
||||||
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
||||||
<StyledDialogTitle>
|
<StyledDialogTitle>{props.title}</StyledDialogTitle>
|
||||||
{props.title}
|
|
||||||
</StyledDialogTitle>
|
|
||||||
|
|
||||||
<StyledDialogContent>
|
<StyledDialogContent>
|
||||||
{description}
|
{description}
|
||||||
@ -53,30 +54,32 @@ const BaseDialog = (props: DialogProps): React.ReactElement => {
|
|||||||
</StyledDialogContent>
|
</StyledDialogContent>
|
||||||
|
|
||||||
<StyledDialogActions>
|
<StyledDialogActions>
|
||||||
<Button
|
<Button type="button" color="primary" size="medium" onClick={onClose}>
|
||||||
type="button"
|
{onSubmit ? (
|
||||||
color="primary"
|
<FormattedMessage
|
||||||
size="medium"
|
id="action.cancel-button"
|
||||||
onClick={onClose} >
|
defaultMessage="Cancel"
|
||||||
{onSubmit ? (<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />) :
|
/>
|
||||||
(<FormattedMessage id="action.close-button" defaultMessage="Close" />)
|
) : (
|
||||||
}
|
<FormattedMessage id="action.close-button" defaultMessage="Close" />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
{onSubmit &&
|
{onSubmit && (
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
type="submit"
|
type="submit"
|
||||||
disableElevation={true}>
|
disableElevation={true}
|
||||||
|
>
|
||||||
{props.submitButton}
|
{props.submitButton}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
)}
|
||||||
</StyledDialogActions>
|
</StyledDialogActions>
|
||||||
</form>
|
</form>
|
||||||
</StyledDialog>
|
</StyledDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BaseDialog;
|
export default BaseDialog
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
import Dialog from "@material-ui/core/Dialog";
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import DialogActions from "@material-ui/core/DialogActions";
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import DialogContent from "@material-ui/core/DialogContent";
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import withStyles from "@material-ui/core/styles/withStyles";
|
import withStyles from '@material-ui/core/styles/withStyles'
|
||||||
|
|
||||||
export const StyledDialogContent = withStyles({
|
export const StyledDialogContent = withStyles({
|
||||||
root: {
|
root: {
|
||||||
padding: '0px 39px'
|
padding: '0px 39px',
|
||||||
}
|
},
|
||||||
})(DialogContent);
|
})(DialogContent)
|
||||||
|
|
||||||
export const StyledDialogTitle = withStyles({
|
export const StyledDialogTitle = withStyles({
|
||||||
root: {
|
root: {
|
||||||
padding: '39px 39px 10px 39px'
|
padding: '39px 39px 10px 39px',
|
||||||
}
|
},
|
||||||
})(DialogTitle);
|
})(DialogTitle)
|
||||||
|
|
||||||
export const StyledDialogActions = withStyles({
|
export const StyledDialogActions = withStyles({
|
||||||
root: {
|
root: {
|
||||||
padding: '39px 39px 39px 39px'
|
padding: '39px 39px 39px 39px',
|
||||||
}
|
},
|
||||||
})(DialogActions);
|
})(DialogActions)
|
||||||
|
|
||||||
export const StyledDialog = withStyles({
|
export const StyledDialog = withStyles({
|
||||||
root: {
|
root: {
|
||||||
borderRadius: '9px'
|
borderRadius: '9px',
|
||||||
}
|
},
|
||||||
})(Dialog)
|
})(Dialog)
|
||||||
|
@ -1,79 +1,109 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl'
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query'
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
|
|
||||||
import Client, { BasicMapInfo, ErrorInfo } from '../../../../classes/client';
|
import Client, { BasicMapInfo, ErrorInfo } from '../../../../classes/client'
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice'
|
||||||
import Input from '../../../form/input';
|
import Input from '../../../form/input'
|
||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog'
|
||||||
|
|
||||||
export type CreateModel = {
|
export type CreateModel = {
|
||||||
title: string;
|
title: string
|
||||||
description?: string;
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateProps = {
|
export type CreateProps = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: CreateModel = { title: '', description: '' };
|
const defaultModel: CreateModel = { title: '', description: '' }
|
||||||
const CreateDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
const CreateDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const [model, setModel] = React.useState<CreateModel>(defaultModel);
|
const [model, setModel] = React.useState<CreateModel>(defaultModel)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const mutation = useMutation<number, ErrorInfo, CreateModel>((model: CreateModel) => {
|
const mutation = useMutation<number, ErrorInfo, CreateModel>(
|
||||||
return client.createMap(model);
|
(model: CreateModel) => {
|
||||||
|
return client.createMap(model)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (mapId: number) => {
|
onSuccess: (mapId: number) => {
|
||||||
window.location.href = `/c/maps/${mapId}/edit`;
|
window.location.href = `/c/maps/${mapId}/edit`
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
mutation.mutate(model);
|
mutation.mutate(model)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name
|
||||||
const value = event.target.value;
|
const value = event.target.value
|
||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
setModel({ ...model, [name as keyof BasicMapInfo]: value })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
|
<BaseDialog
|
||||||
title={intl.formatMessage({ id: 'create.title', defaultMessage: 'Create a new mindmap' })}
|
onClose={handleOnClose}
|
||||||
description={intl.formatMessage({ id: 'create.description', defaultMessage: 'Please, fill the new map name and description.' })}
|
onSubmit={handleOnSubmit}
|
||||||
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}>
|
error={error}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'create.title',
|
||||||
|
defaultMessage: 'Create a new mindmap',
|
||||||
|
})}
|
||||||
|
description={intl.formatMessage({
|
||||||
|
id: 'create.description',
|
||||||
|
defaultMessage: 'Please, fill the new map name and description.',
|
||||||
|
})}
|
||||||
|
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}
|
||||||
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input name="title" type="text" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
|
<Input
|
||||||
value={model.title} onChange={handleOnChange} error={error} fullWidth={true} />
|
name="title"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-name-placeholder',
|
||||||
|
defaultMessage: 'Name',
|
||||||
|
})}
|
||||||
|
value={model.title}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
error={error}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="description" type="text" label={intl.formatMessage({ id: "action.rename-description-placeholder", defaultMessage: "Description" })}
|
<Input
|
||||||
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />
|
name="description"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-description-placeholder',
|
||||||
|
defaultMessage: 'Description',
|
||||||
|
})}
|
||||||
|
value={model.description}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
required={false}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CreateDialog;
|
export default CreateDialog
|
||||||
|
@ -1,59 +1,60 @@
|
|||||||
import React from "react";
|
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 Client, { ErrorInfo } from "../../../../classes/client";
|
import Client, { ErrorInfo } from '../../../../classes/client'
|
||||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice'
|
||||||
import { SimpleDialogProps, handleOnMutationSuccess } from "..";
|
import { SimpleDialogProps, handleOnMutationSuccess } from '..'
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from '../base-dialog'
|
||||||
import Alert from "@material-ui/lab/Alert";
|
import Alert from '@material-ui/lab/Alert'
|
||||||
import AlertTitle from "@material-ui/lab/AlertTitle";
|
import AlertTitle from '@material-ui/lab/AlertTitle'
|
||||||
|
|
||||||
|
|
||||||
const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
|
|
||||||
const mutation = useMutation((id: number) => client.deleteMap(id),
|
const mutation = useMutation((id: number) => client.deleteMap(id), {
|
||||||
{
|
|
||||||
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
|
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
|
||||||
onError: (error: ErrorInfo) => {
|
onError: (error: ErrorInfo) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
}
|
},
|
||||||
}
|
})
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (): void => {
|
const handleOnSubmit = (): void => {
|
||||||
mutation.mutate(mapId);
|
mutation.mutate(mapId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch map model to be rendered ...
|
// Fetch map model to be rendered ...
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId)
|
||||||
const alertTitle = `Delete ${map?.title}`;
|
const alertTitle = `Delete ${map?.title}`
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
error={error}
|
error={error}
|
||||||
onClose={handleOnClose} onSubmit={handleOnSubmit}
|
onClose={handleOnClose}
|
||||||
title={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })}
|
onSubmit={handleOnSubmit}
|
||||||
submitButton={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',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Alert severity="warning">
|
<Alert severity="warning">
|
||||||
<AlertTitle>{alertTitle}</AlertTitle>
|
<AlertTitle>{alertTitle}</AlertTitle>
|
||||||
<FormattedMessage id="action.delete-description"
|
<FormattedMessage
|
||||||
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
|
id="action.delete-description"
|
||||||
|
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||||
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default DeleteDialog
|
||||||
export default DeleteDialog;
|
|
||||||
|
@ -1,55 +1,68 @@
|
|||||||
import React from "react";
|
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 Client from "../../../../classes/client";
|
import Client from '../../../../classes/client'
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice'
|
||||||
import { handleOnMutationSuccess } from "..";
|
import { handleOnMutationSuccess } from '..'
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from '../base-dialog'
|
||||||
import Alert from "@material-ui/lab/Alert";
|
import Alert from '@material-ui/lab/Alert'
|
||||||
import AlertTitle from "@material-ui/lab/AlertTitle";
|
import AlertTitle from '@material-ui/lab/AlertTitle'
|
||||||
|
|
||||||
export type DeleteMultiselectDialogProps = {
|
export type DeleteMultiselectDialogProps = {
|
||||||
mapsId: number[],
|
mapsId: number[]
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const DeleteMultiselectDialog = ({ onClose, mapsId }: DeleteMultiselectDialogProps): React.ReactElement => {
|
const DeleteMultiselectDialog = ({
|
||||||
const intl = useIntl();
|
onClose,
|
||||||
const client: Client = useSelector(activeInstance);
|
mapsId,
|
||||||
const queryClient = useQueryClient();
|
}: DeleteMultiselectDialogProps): React.ReactElement => {
|
||||||
|
const intl = useIntl()
|
||||||
|
const client: Client = useSelector(activeInstance)
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids),
|
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids), {
|
||||||
{
|
|
||||||
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
|
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(`Unexpected error ${error}`);
|
console.error(`Unexpected error ${error}`)
|
||||||
}
|
},
|
||||||
}
|
})
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (): void => {
|
const handleOnSubmit = (): void => {
|
||||||
mutation.mutate(mapsId);
|
mutation.mutate(mapsId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose} onSubmit={handleOnSubmit}
|
onClose={handleOnClose}
|
||||||
title={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })}
|
onSubmit={handleOnSubmit}
|
||||||
submitButton={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',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Alert severity="warning">
|
<Alert severity="warning">
|
||||||
<AlertTitle><FormattedMessage id="deletem.title" defaultMessage="All selected maps will be deleted" /></AlertTitle>
|
<AlertTitle>
|
||||||
<FormattedMessage id="action.delete-description" defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?." />
|
<FormattedMessage
|
||||||
|
id="deletem.title"
|
||||||
|
defaultMessage="All selected maps will be deleted"
|
||||||
|
/>
|
||||||
|
</AlertTitle>
|
||||||
|
<FormattedMessage
|
||||||
|
id="action.delete-description"
|
||||||
|
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||||
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default DeleteMultiselectDialog
|
||||||
export default DeleteMultiselectDialog;
|
|
||||||
|
@ -1,87 +1,117 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from 'react'
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from 'react-intl'
|
||||||
import { useMutation } from "react-query";
|
import { useMutation } from 'react-query'
|
||||||
import FormControl from "@material-ui/core/FormControl";
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
import Client, { BasicMapInfo, ErrorInfo } from "../../../../classes/client";
|
import Client, { BasicMapInfo, ErrorInfo } from '../../../../classes/client'
|
||||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice'
|
||||||
import Input from "../../../form/input";
|
import Input from '../../../form/input'
|
||||||
import { SimpleDialogProps } from "..";
|
import { SimpleDialogProps } from '..'
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from '../base-dialog'
|
||||||
|
|
||||||
export type DuplicateModel = {
|
export type DuplicateModel = {
|
||||||
id: number;
|
id: number
|
||||||
title: string;
|
title: string
|
||||||
description?: string;
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: DuplicateModel = { title: '', description: '', id: -1 };
|
const defaultModel: DuplicateModel = { title: '', description: '', id: -1 }
|
||||||
const DuplicateDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const DuplicateDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const service: Client = useSelector(activeInstance);
|
const service: Client = useSelector(activeInstance)
|
||||||
const [model, setModel] = React.useState<DuplicateModel>(defaultModel);
|
const [model, setModel] = React.useState<DuplicateModel>(defaultModel)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const mutation = useMutation<number, ErrorInfo, DuplicateModel>((model: DuplicateModel) => {
|
const mutation = useMutation<number, ErrorInfo, DuplicateModel>(
|
||||||
const { id, ...rest } = model;
|
(model: DuplicateModel) => {
|
||||||
return service.duplicateMap(id, rest);
|
const { id, ...rest } = model
|
||||||
|
return service.duplicateMap(id, rest)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (mapId) => {
|
onSuccess: (mapId) => {
|
||||||
window.location.href = `/c/maps/${mapId}/edit`;
|
window.location.href = `/c/maps/${mapId}/edit`
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
|
||||||
event.preventDefault();
|
|
||||||
mutation.mutate(model);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const name = event.target.name;
|
|
||||||
const value = event.target.value;
|
|
||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { map } = fetchMapById(mapId);
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
|
event.preventDefault()
|
||||||
|
mutation.mutate(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
const name = event.target.name
|
||||||
|
const value = event.target.value
|
||||||
|
setModel({ ...model, [name as keyof BasicMapInfo]: value })
|
||||||
|
}
|
||||||
|
|
||||||
|
const { map } = fetchMapById(mapId)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && map) {
|
if (open && map) {
|
||||||
setModel(map);
|
setModel(map)
|
||||||
} else {
|
} else {
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
}
|
}
|
||||||
}, [mapId])
|
}, [mapId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
|
<BaseDialog
|
||||||
|
onClose={handleOnClose}
|
||||||
|
onSubmit={handleOnSubmit}
|
||||||
|
error={error}
|
||||||
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({
|
||||||
submitButton={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}>
|
id: 'rename.description',
|
||||||
|
defaultMessage: 'Please, fill the new map name and description.',
|
||||||
|
})}
|
||||||
|
submitButton={intl.formatMessage({
|
||||||
|
id: 'duplicate.title',
|
||||||
|
defaultMessage: 'Duplicate',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input name="title" type="text" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
|
<Input
|
||||||
value={model.title} onChange={handleOnChange} error={error} fullWidth={true} />
|
name="title"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-name-placeholder',
|
||||||
|
defaultMessage: 'Name',
|
||||||
|
})}
|
||||||
|
value={model.title}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
error={error}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="description" type="text" label={intl.formatMessage({ id: "action.rename-description-placeholder", defaultMessage: "Description" })}
|
<Input
|
||||||
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />
|
name="description"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-description-placeholder',
|
||||||
|
defaultMessage: 'Description',
|
||||||
|
})}
|
||||||
|
value={model.description}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
required={false}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DuplicateDialog;
|
export default DuplicateDialog
|
||||||
|
@ -1,97 +1,114 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from 'react'
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from '../base-dialog'
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style'
|
||||||
import Alert from "@material-ui/lab/Alert";
|
import Alert from '@material-ui/lab/Alert'
|
||||||
import { fetchMapById } from "../../../../redux/clientSlice";
|
import { fetchMapById } from '../../../../redux/clientSlice'
|
||||||
import FormControl from "@material-ui/core/FormControl";
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
import RadioGroup from "@material-ui/core/RadioGroup";
|
import RadioGroup from '@material-ui/core/RadioGroup'
|
||||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
import FormControlLabel from '@material-ui/core/FormControlLabel'
|
||||||
import Radio from "@material-ui/core/Radio";
|
import Radio from '@material-ui/core/Radio'
|
||||||
import Select from "@material-ui/core/Select";
|
import Select from '@material-ui/core/Select'
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
|
|
||||||
type ExportFormat = 'pdf' | 'svg' | 'jpg' | 'png' | 'txt' | 'mm' | 'wxml' | 'xls' | 'txt';
|
type ExportFormat = 'pdf' | 'svg' | 'jpg' | 'png' | 'txt' | 'mm' | 'wxml' | 'xls' | 'txt'
|
||||||
type ExportGroup = 'image' | 'document' | 'mindmap-tool';
|
type ExportGroup = 'image' | 'document' | 'mindmap-tool'
|
||||||
|
|
||||||
type ExportDialogProps = {
|
type ExportDialogProps = {
|
||||||
mapId: number,
|
mapId: number
|
||||||
enableImgExport: boolean,
|
enableImgExport: boolean
|
||||||
svgXml?: string,
|
svgXml?: string
|
||||||
onClose: () => void,
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExportDialog = ({ mapId, onClose, enableImgExport, svgXml }: ExportDialogProps): React.ReactElement => {
|
const ExportDialog = ({
|
||||||
const intl = useIntl();
|
mapId,
|
||||||
const [submit, setSubmit] = React.useState<boolean>(false);
|
onClose,
|
||||||
|
enableImgExport,
|
||||||
|
svgXml,
|
||||||
|
}: ExportDialogProps): React.ReactElement => {
|
||||||
|
const intl = useIntl()
|
||||||
|
const [submit, setSubmit] = React.useState<boolean>(false)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const [formExportRef, setExportFormRef] = React.useState<any>();
|
const [formExportRef, setExportFormRef] = React.useState<any>()
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const [formTransformtRef, setTransformFormRef] = React.useState<any>();
|
const [formTransformtRef, setTransformFormRef] = React.useState<any>()
|
||||||
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(enableImgExport ? 'image' : 'document');
|
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
||||||
const [exportFormat, setExportFormat] = React.useState<ExportFormat>(enableImgExport ? 'svg' : 'xls');
|
enableImgExport ? 'image' : 'document'
|
||||||
const classes = useStyles();
|
)
|
||||||
|
const [exportFormat, setExportFormat] = React.useState<ExportFormat>(
|
||||||
|
enableImgExport ? 'svg' : 'xls'
|
||||||
|
)
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
const handleOnExportFormatChange = (event) => {
|
const handleOnExportFormatChange = (event) => {
|
||||||
setExportFormat(event.target.value);
|
setExportFormat(event.target.value)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnGroupChange = (event) => {
|
const handleOnGroupChange = (event) => {
|
||||||
const value: ExportGroup = event.target.value;
|
const value: ExportGroup = event.target.value
|
||||||
setExportGroup(value);
|
setExportGroup(value)
|
||||||
|
|
||||||
let defaultFormat: ExportFormat;
|
let defaultFormat: ExportFormat
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'document':
|
case 'document':
|
||||||
defaultFormat = 'pdf';
|
defaultFormat = 'pdf'
|
||||||
break;
|
break
|
||||||
case 'image':
|
case 'image':
|
||||||
defaultFormat = 'svg';
|
defaultFormat = 'svg'
|
||||||
break;
|
break
|
||||||
case 'mindmap-tool':
|
case 'mindmap-tool':
|
||||||
defaultFormat = 'wxml';
|
defaultFormat = 'wxml'
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
setExportFormat(defaultFormat);
|
setExportFormat(defaultFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (): void => {
|
const handleOnSubmit = (): void => {
|
||||||
setSubmit(true);
|
setSubmit(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (submit) {
|
if (submit) {
|
||||||
// Depending on the type of export. It will require differt POST.
|
// Depending on the type of export. It will require differt POST.
|
||||||
if (exportFormat == 'pdf' || exportFormat == "svg" || exportFormat == "jpg" || exportFormat == "png") {
|
if (
|
||||||
formTransformtRef?.submit();
|
exportFormat == 'pdf' ||
|
||||||
|
exportFormat == 'svg' ||
|
||||||
|
exportFormat == 'jpg' ||
|
||||||
|
exportFormat == 'png'
|
||||||
|
) {
|
||||||
|
formTransformtRef?.submit()
|
||||||
} else {
|
} else {
|
||||||
|
formExportRef?.submit()
|
||||||
formExportRef?.submit();
|
|
||||||
}
|
}
|
||||||
onClose();
|
onClose()
|
||||||
}
|
}
|
||||||
}, [submit]);
|
}, [submit])
|
||||||
|
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
onSubmit={handleOnSubmit}
|
onSubmit={handleOnSubmit}
|
||||||
title={intl.formatMessage({ id: "export.title", defaultMessage: "Export" })}
|
title={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||||
description={"Export this map in the format that you want and start using it in your presentations or sharing by email"}
|
description={
|
||||||
submitButton={intl.formatMessage({ id: "export.title", defaultMessage: "Export" })} >
|
'Export this map in the format that you want and start using it in your presentations or sharing by email'
|
||||||
|
}
|
||||||
|
submitButton={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||||
|
>
|
||||||
<Alert severity="info">
|
<Alert severity="info">
|
||||||
<FormattedMessage id="export.warning" defaultMessage="Exporting to Image (SVG,PNG,JPEG,PDF) is only available in the editor toolbar." />
|
<FormattedMessage
|
||||||
|
id="export.warning"
|
||||||
|
defaultMessage="Exporting to Image (SVG,PNG,JPEG,PDF) is only available in the editor toolbar."
|
||||||
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<FormControl component="fieldset" >
|
<FormControl component="fieldset">
|
||||||
<RadioGroup name="export" value={exportGroup} onChange={handleOnGroupChange}>
|
<RadioGroup name="export" value={exportGroup} onChange={handleOnGroupChange}>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
@ -99,21 +116,35 @@ const ExportDialog = ({ mapId, onClose, enableImgExport, svgXml }: ExportDialogP
|
|||||||
value="image"
|
value="image"
|
||||||
disabled={!enableImgExport}
|
disabled={!enableImgExport}
|
||||||
control={<Radio color="primary" />}
|
control={<Radio color="primary" />}
|
||||||
label={intl.formatMessage({ id: "export.image", defaultMessage: "Image: Get a graphic representation of your map including all colors and shapes." })}
|
label={intl.formatMessage({
|
||||||
|
id: 'export.image',
|
||||||
|
defaultMessage:
|
||||||
|
'Image: Get a graphic representation of your map including all colors and shapes.',
|
||||||
|
})}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
style={{ fontSize: '9px' }} />
|
style={{ fontSize: '9px' }}
|
||||||
{(exportGroup == 'image') &&
|
/>
|
||||||
(<Select
|
{exportGroup == 'image' && (
|
||||||
|
<Select
|
||||||
onSelect={handleOnExportFormatChange}
|
onSelect={handleOnExportFormatChange}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={exportFormat}
|
value={exportFormat}
|
||||||
className={classes.label}>
|
className={classes.label}
|
||||||
<MenuItem value="svg" className={classes.menu}>Scalable Vector Graphics (SVG)</MenuItem>
|
>
|
||||||
<MenuItem value="pdf" className={classes.select} >Portable Document Format (PDF)</MenuItem>
|
<MenuItem value="svg" className={classes.menu}>
|
||||||
<MenuItem value="png" className={classes.menu}>Portable Network Graphics (PNG)</MenuItem>
|
Scalable Vector Graphics (SVG)
|
||||||
<MenuItem value="jpg" className={classes.menu}>JPEG Image (JPEG)</MenuItem>
|
</MenuItem>
|
||||||
</Select>)
|
<MenuItem value="pdf" className={classes.select}>
|
||||||
}
|
Portable Document Format (PDF)
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value="png" className={classes.menu}>
|
||||||
|
Portable Network Graphics (PNG)
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value="jpg" className={classes.menu}>
|
||||||
|
JPEG Image (JPEG)
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@ -121,18 +152,28 @@ const ExportDialog = ({ mapId, onClose, enableImgExport, svgXml }: ExportDialogP
|
|||||||
className={classes.label}
|
className={classes.label}
|
||||||
value="document"
|
value="document"
|
||||||
control={<Radio color="primary" />}
|
control={<Radio color="primary" />}
|
||||||
label={intl.formatMessage({ id: "export.document-label", defaultMessage: "Document: Export your mindmap in a self-contained document ready to share" })}
|
label={intl.formatMessage({
|
||||||
color="secondary" />
|
id: 'export.document-label',
|
||||||
{exportGroup == 'document' &&
|
defaultMessage:
|
||||||
(<Select onChange={handleOnExportFormatChange}
|
'Document: Export your mindmap in a self-contained document ready to share',
|
||||||
|
})}
|
||||||
|
color="secondary"
|
||||||
|
/>
|
||||||
|
{exportGroup == 'document' && (
|
||||||
|
<Select
|
||||||
|
onChange={handleOnExportFormatChange}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={exportFormat}
|
value={exportFormat}
|
||||||
className={classes.select}
|
className={classes.select}
|
||||||
>
|
>
|
||||||
<MenuItem className={classes.select} value="xls">Microsoft Excel (XLS)</MenuItem>
|
<MenuItem className={classes.select} value="xls">
|
||||||
<MenuItem className={classes.select} value="txt">Plain Text File (TXT)</MenuItem>
|
Microsoft Excel (XLS)
|
||||||
</Select>)
|
</MenuItem>
|
||||||
}
|
<MenuItem className={classes.select} value="txt">
|
||||||
|
Plain Text File (TXT)
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@ -140,39 +181,57 @@ const ExportDialog = ({ mapId, onClose, enableImgExport, svgXml }: ExportDialogP
|
|||||||
className={classes.label}
|
className={classes.label}
|
||||||
value="mindmap-tool"
|
value="mindmap-tool"
|
||||||
control={<Radio color="primary" />}
|
control={<Radio color="primary" />}
|
||||||
label={intl.formatMessage({ id: "export.document", defaultMessage: "Mindmap Tools: Export your mindmap in thirdparty mindmap tool formats" })}
|
label={intl.formatMessage({
|
||||||
color="secondary" />
|
id: 'export.document',
|
||||||
{exportGroup == 'mindmap-tool' &&
|
defaultMessage:
|
||||||
(<Select
|
'Mindmap Tools: Export your mindmap in thirdparty mindmap tool formats',
|
||||||
|
})}
|
||||||
|
color="secondary"
|
||||||
|
/>
|
||||||
|
{exportGroup == 'mindmap-tool' && (
|
||||||
|
<Select
|
||||||
onChange={handleOnExportFormatChange}
|
onChange={handleOnExportFormatChange}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
className={classes.select}
|
className={classes.select}
|
||||||
value={exportFormat}
|
value={exportFormat}
|
||||||
>
|
>
|
||||||
<MenuItem className={classes.select} value="wxml">WiseMapping (WXML)</MenuItem>
|
<MenuItem className={classes.select} value="wxml">
|
||||||
<MenuItem className={classes.select} value="mm">Freemind 1.0.1 (MM)</MenuItem>
|
WiseMapping (WXML)
|
||||||
<MenuItem className={classes.select} value="mmap">MindManager (MMAP)</MenuItem>
|
</MenuItem>
|
||||||
</Select>)
|
<MenuItem className={classes.select} value="mm">
|
||||||
}
|
Freemind 1.0.1 (MM)
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem className={classes.select} value="mmap">
|
||||||
|
MindManager (MMAP)
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
||||||
{/* Hidden form for the purpose of summit */}
|
{/* Hidden form for the purpose of summit */}
|
||||||
<form action={`/c/restful/maps/${mapId}.${exportFormat}`} ref={setExportFormRef} method="GET">
|
<form
|
||||||
|
action={`/c/restful/maps/${mapId}.${exportFormat}`}
|
||||||
|
ref={setExportFormRef}
|
||||||
|
method="GET"
|
||||||
|
>
|
||||||
<input name="download" type="hidden" value={exportFormat} />
|
<input name="download" type="hidden" value={exportFormat} />
|
||||||
<input name="filename" type="hidden" value={map?.title} />
|
<input name="filename" type="hidden" value={map?.title} />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form action={`/c/restful/transform.${exportFormat}`} ref={setTransformFormRef} method="POST">
|
<form
|
||||||
|
action={`/c/restful/transform.${exportFormat}`}
|
||||||
|
ref={setTransformFormRef}
|
||||||
|
method="POST"
|
||||||
|
>
|
||||||
<input name="download" type="hidden" value={exportFormat} />
|
<input name="download" type="hidden" value={exportFormat} />
|
||||||
<input name="filename" type="hidden" value={map?.title} />
|
<input name="filename" type="hidden" value={map?.title} />
|
||||||
<input name="svgXml" id="svgXml" value={svgXml} type="hidden" />
|
<input name="svgXml" id="svgXml" value={svgXml} type="hidden" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default ExportDialog
|
||||||
export default ExportDialog;
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import createStyles from "@material-ui/core/styles/createStyles";
|
import createStyles from '@material-ui/core/styles/createStyles'
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
import makeStyles from '@material-ui/core/styles/makeStyles'
|
||||||
|
|
||||||
export const useStyles = makeStyles(() =>
|
export const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@ -8,13 +8,13 @@ export const useStyles = makeStyles(() =>
|
|||||||
borderRadius: '9px',
|
borderRadius: '9px',
|
||||||
width: '300px',
|
width: '300px',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
margin: '0px 40px'
|
margin: '0px 40px',
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
fontSize: '14px'
|
fontSize: '14px',
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
margin: '5px 0px' }
|
margin: '5px 0px',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
|
@ -1,90 +1,133 @@
|
|||||||
import React, { ErrorInfo } from "react";
|
import React, { ErrorInfo } from 'react'
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
import { useQuery } from "react-query";
|
import { useQuery } from 'react-query'
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from 'react-redux'
|
||||||
import Client, { ChangeHistory } from "../../../../classes/client";
|
import Client, { ChangeHistory } from '../../../../classes/client'
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice'
|
||||||
import { SimpleDialogProps } from "..";
|
import { SimpleDialogProps } from '..'
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from '../base-dialog'
|
||||||
import dayjs from "dayjs";
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
import TableContainer from "@material-ui/core/TableContainer";
|
|
||||||
import Table from "@material-ui/core/Table";
|
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
|
||||||
import TableHead from "@material-ui/core/TableHead";
|
|
||||||
import TableBody from "@material-ui/core/TableBody";
|
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
|
||||||
import Link from "@material-ui/core/Link";
|
|
||||||
import Paper from "@material-ui/core/Paper";
|
|
||||||
|
|
||||||
|
import TableContainer from '@material-ui/core/TableContainer'
|
||||||
|
import Table from '@material-ui/core/Table'
|
||||||
|
import TableRow from '@material-ui/core/TableRow'
|
||||||
|
import TableCell from '@material-ui/core/TableCell'
|
||||||
|
import TableHead from '@material-ui/core/TableHead'
|
||||||
|
import TableBody from '@material-ui/core/TableBody'
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
|
import Link from '@material-ui/core/Link'
|
||||||
|
import Paper from '@material-ui/core/Paper'
|
||||||
|
|
||||||
const HistoryDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const HistoryDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const { data } = useQuery<unknown, ErrorInfo, ChangeHistory[]>('history', () => {
|
const { data } = useQuery<unknown, ErrorInfo, ChangeHistory[]>('history', () => {
|
||||||
return client.fetchHistory(mapId);
|
return client.fetchHistory(mapId)
|
||||||
});
|
})
|
||||||
const changeHistory: ChangeHistory[] = data ? data : [];
|
const changeHistory: ChangeHistory[] = data ? data : []
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnClick = (event, vid): void => {
|
const handleOnClick = (event, vid): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
client.revertHistory(mapId, vid)
|
client.revertHistory(mapId, vid).then(() => {
|
||||||
.then(() => {
|
handleOnClose()
|
||||||
handleOnClose();
|
|
||||||
})
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
title={intl.formatMessage({ id: "action.history-title", defaultMessage: "Version history" })}
|
title={intl.formatMessage({
|
||||||
description={intl.formatMessage({ id: "action.history-description", defaultMessage: "List of changes introduced in the last 90 days." })} >
|
id: 'action.history-title',
|
||||||
|
defaultMessage: 'Version history',
|
||||||
|
})}
|
||||||
|
description={intl.formatMessage({
|
||||||
|
id: 'action.history-description',
|
||||||
|
defaultMessage: 'List of changes introduced in the last 90 days.',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<TableContainer component={Paper} style={{ maxHeight: '200px' }} variant="outlined">
|
<TableContainer component={Paper} style={{ maxHeight: '200px' }} variant="outlined">
|
||||||
<Table size="small" stickyHeader>
|
<Table size="small" stickyHeader>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell align="left"><FormattedMessage id="maps.modified-by" defaultMessage="Modified By" /></TableCell>
|
<TableCell align="left">
|
||||||
<TableCell align="left"><FormattedMessage id="maps.modified" defaultMessage="Modified" /></TableCell>
|
<FormattedMessage
|
||||||
|
id="maps.modified-by"
|
||||||
|
defaultMessage="Modified By"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="left">
|
||||||
|
<FormattedMessage
|
||||||
|
id="maps.modified"
|
||||||
|
defaultMessage="Modified"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
<TableCell align="left"></TableCell>
|
<TableCell align="left"></TableCell>
|
||||||
<TableCell align="left"></TableCell>
|
<TableCell align="left"></TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
|
|
||||||
{changeHistory.length == 0 ? (
|
{changeHistory.length == 0 ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={4}><FormattedMessage id='history.no-changes' defaultMessage='There is no changes available' />
|
<TableCell colSpan={4}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="history.no-changes"
|
||||||
|
defaultMessage="There is no changes available"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
) :
|
) : (
|
||||||
changeHistory.map((row) => (
|
changeHistory.map((row) => (
|
||||||
<TableRow key={row.id}>
|
<TableRow key={row.id}>
|
||||||
<TableCell align="left">{row.lastModificationBy}</TableCell>
|
<TableCell align="left">{row.lastModificationBy}</TableCell>
|
||||||
<TableCell align="left">
|
<TableCell align="left">
|
||||||
<Tooltip title={dayjs(row.lastModificationTime).format("lll")} placement="bottom-start">
|
<Tooltip
|
||||||
<span>{dayjs(row.lastModificationTime).fromNow()}</span>
|
title={dayjs(row.lastModificationTime).format(
|
||||||
|
'lll'
|
||||||
|
)}
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{dayjs(row.lastModificationTime).fromNow()}
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="left"><Link href={`c/maps/${mapId}/${row.id}/view`} target="history"><FormattedMessage id="maps.view" defaultMessage="View" /></Link></TableCell>
|
<TableCell align="left">
|
||||||
<TableCell align="left"><Link href="#" onClick={(e) => handleOnClick(e, row.id)}><FormattedMessage id="maps.revert" defaultMessage="Revert" /></Link></TableCell>
|
<Link
|
||||||
|
href={`c/maps/${mapId}/${row.id}/view`}
|
||||||
|
target="history"
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="maps.view"
|
||||||
|
defaultMessage="View"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="left">
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => handleOnClick(e, row.id)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="maps.revert"
|
||||||
|
defaultMessage="Revert"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|
||||||
|
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HistoryDialog;
|
export default HistoryDialog
|
||||||
|
@ -1,101 +1,114 @@
|
|||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
import React from 'react';
|
import React from 'react'
|
||||||
|
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query'
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import Client, { ErrorInfo } from '../../../../classes/client';
|
import Client, { ErrorInfo } from '../../../../classes/client'
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice'
|
||||||
import Input from '../../../form/input';
|
import Input from '../../../form/input'
|
||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog'
|
||||||
|
|
||||||
export type ImportModel = {
|
export type ImportModel = {
|
||||||
title: string;
|
title: string
|
||||||
description?: string;
|
description?: string
|
||||||
contentType?: string;
|
contentType?: string
|
||||||
content?: ArrayBuffer | null | string;
|
content?: ArrayBuffer | null | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateProps = {
|
export type CreateProps = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: ImportModel = { title: '' };
|
const defaultModel: ImportModel = { title: '' }
|
||||||
const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const [model, setModel] = React.useState<ImportModel>(defaultModel);
|
const [model, setModel] = React.useState<ImportModel>(defaultModel)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const mutation = useMutation<number, ErrorInfo, ImportModel>((model: ImportModel) => {
|
const mutation = useMutation<number, ErrorInfo, ImportModel>(
|
||||||
return client.importMap(model);
|
(model: ImportModel) => {
|
||||||
|
return client.importMap(model)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (mapId: number) => {
|
onSuccess: (mapId: number) => {
|
||||||
window.location.href = `/c/maps/${mapId}/edit`;
|
window.location.href = `/c/maps/${mapId}/edit`
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
mutation.mutate(model);
|
mutation.mutate(model)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name
|
||||||
const value = event.target.value;
|
const value = event.target.value
|
||||||
setModel({ ...model, [name as keyof ImportModel]: value });
|
setModel({ ...model, [name as keyof ImportModel]: value })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOnFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleOnFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const files = event?.target?.files;
|
const files = event?.target?.files
|
||||||
const reader = new FileReader();
|
const reader = new FileReader()
|
||||||
|
|
||||||
if (files) {
|
if (files) {
|
||||||
const file = files[0];
|
const file = files[0]
|
||||||
// Closure to capture the file information.
|
// Closure to capture the file information.
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
const fileContent = event?.target?.result;
|
const fileContent = event?.target?.result
|
||||||
model.content = fileContent;
|
model.content = fileContent
|
||||||
|
|
||||||
// Suggest file name ...
|
// Suggest file name ...
|
||||||
const fileName = file.name;
|
const fileName = file.name
|
||||||
if (fileName) {
|
if (fileName) {
|
||||||
const title = fileName.split('.')[0]
|
const title = fileName.split('.')[0]
|
||||||
if (!model.title || 0 === model.title.length) {
|
if (!model.title || 0 === model.title.length) {
|
||||||
model.title = title;
|
model.title = title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
model.contentType = file.name.lastIndexOf(".wxml") != -1 ? "application/xml" : "application/freemind";
|
model.contentType =
|
||||||
setModel({ ...model });
|
file.name.lastIndexOf('.wxml') != -1
|
||||||
};
|
? 'application/xml'
|
||||||
|
: 'application/freemind'
|
||||||
|
setModel({ ...model })
|
||||||
|
}
|
||||||
|
|
||||||
// Read in the image file as a data URL.
|
// Read in the image file as a data URL.
|
||||||
reader.readAsText(file);
|
reader.readAsText(file)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
|
<BaseDialog
|
||||||
title={intl.formatMessage({ id: 'import.title', defaultMessage: 'Import existing mindmap' })}
|
onClose={handleOnClose}
|
||||||
description={intl.formatMessage({ id: 'import.description', defaultMessage: 'You can import FreeMind 1.0.1 and WiseMapping maps to your list of maps. Select the file you want to import.' })}
|
onSubmit={handleOnSubmit}
|
||||||
submitButton={intl.formatMessage({ id: 'import.button', defaultMessage: 'Create' })}>
|
error={error}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'import.title',
|
||||||
|
defaultMessage: 'Import existing mindmap',
|
||||||
|
})}
|
||||||
|
description={intl.formatMessage({
|
||||||
|
id: 'import.description',
|
||||||
|
defaultMessage:
|
||||||
|
'You can import FreeMind 1.0.1 and WiseMapping maps to your list of maps. Select the file you want to import.',
|
||||||
|
})}
|
||||||
|
submitButton={intl.formatMessage({ id: 'import.button', defaultMessage: 'Create' })}
|
||||||
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<input
|
<input
|
||||||
accept=".wxml,.mm"
|
accept=".wxml,.mm"
|
||||||
@ -106,21 +119,49 @@ const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
|||||||
onChange={handleOnFileChange}
|
onChange={handleOnFileChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input name="title" type="text" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
|
<Input
|
||||||
value={model.title} onChange={handleOnChange} error={error} fullWidth={true} />
|
name="title"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-name-placeholder',
|
||||||
|
defaultMessage: 'Name',
|
||||||
|
})}
|
||||||
|
value={model.title}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
error={error}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="description" type="text" label={intl.formatMessage({ id: "action.rename-description-placeholder", defaultMessage: "Description" })}
|
<Input
|
||||||
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />
|
name="description"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-description-placeholder',
|
||||||
|
defaultMessage: 'Description',
|
||||||
|
})}
|
||||||
|
value={model.description}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
required={false}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<label htmlFor="contained-button-file">
|
<label htmlFor="contained-button-file">
|
||||||
<Button variant="outlined" color="primary" component="span" style={{ margin: '10px 5px', width: '100%' }}>
|
<Button
|
||||||
<FormattedMessage id="maps.choose-file" defaultMessage="Choose a file" />
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
component="span"
|
||||||
|
style={{ margin: '10px 5px', width: '100%' }}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="maps.choose-file"
|
||||||
|
defaultMessage="Choose a file"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</label>
|
</label>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImportDialog;
|
export default ImportDialog
|
||||||
|
@ -1,72 +1,76 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import RenameDialog from './rename-dialog';
|
import RenameDialog from './rename-dialog'
|
||||||
import DeleteDialog from './delete-dialog';
|
import DeleteDialog from './delete-dialog'
|
||||||
import { ActionType } from '../action-chooser';
|
import { ActionType } from '../action-chooser'
|
||||||
import { QueryClient } from 'react-query';
|
import { QueryClient } from 'react-query'
|
||||||
import DuplicateDialog from './duplicate-dialog';
|
import DuplicateDialog from './duplicate-dialog'
|
||||||
import CreateDialog from './create-dialog';
|
import CreateDialog from './create-dialog'
|
||||||
import HistoryDialog from './history-dialog';
|
import HistoryDialog from './history-dialog'
|
||||||
import ImportDialog from './import-dialog';
|
import ImportDialog from './import-dialog'
|
||||||
import PublishDialog from './publish-dialog';
|
import PublishDialog from './publish-dialog'
|
||||||
import InfoDialog from './info-dialog';
|
import InfoDialog from './info-dialog'
|
||||||
import DeleteMultiselectDialog from './delete-multiselect-dialog';
|
import DeleteMultiselectDialog from './delete-multiselect-dialog'
|
||||||
import ExportDialog from './export-dialog';
|
import ExportDialog from './export-dialog'
|
||||||
import ShareDialog from './share-dialog';
|
import ShareDialog from './share-dialog'
|
||||||
|
|
||||||
export type BasicMapInfo = {
|
export type BasicMapInfo = {
|
||||||
name: string;
|
name: string
|
||||||
description: string | undefined;
|
description: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionDialogProps = {
|
type ActionDialogProps = {
|
||||||
action?: ActionType,
|
action?: ActionType
|
||||||
mapsId: number[],
|
mapsId: number[]
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActionDispatcher = ({ mapsId, action, onClose }: ActionDialogProps): React.ReactElement => {
|
const ActionDispatcher = ({ mapsId, action, onClose }: ActionDialogProps): React.ReactElement => {
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'open':
|
case 'open':
|
||||||
window.location.href = `/c/maps/${mapsId}/edit`;
|
window.location.href = `/c/maps/${mapsId}/edit`
|
||||||
break;
|
break
|
||||||
case 'print':
|
case 'print':
|
||||||
window.open(`/c/maps/${mapsId}/print`, 'print');
|
window.open(`/c/maps/${mapsId}/print`, 'print')
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
|
|
||||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||||
{(action === 'delete' && mapsId.length == 1) && <DeleteDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
{action === 'delete' && mapsId.length == 1 && (
|
||||||
{(action === 'delete' && mapsId.length > 1) && <DeleteMultiselectDialog onClose={handleOnClose} mapsId={mapsId} />}
|
<DeleteDialog onClose={handleOnClose} mapId={mapsId[0]} />
|
||||||
|
)}
|
||||||
|
{action === 'delete' && mapsId.length > 1 && (
|
||||||
|
<DeleteMultiselectDialog onClose={handleOnClose} mapsId={mapsId} />
|
||||||
|
)}
|
||||||
{action === 'rename' && <RenameDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
{action === 'rename' && <RenameDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
{action === 'duplicate' && <DuplicateDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
{action === 'duplicate' && (
|
||||||
|
<DuplicateDialog onClose={handleOnClose} mapId={mapsId[0]} />
|
||||||
|
)}
|
||||||
{action === 'history' && <HistoryDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
{action === 'history' && <HistoryDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
{action === 'import' && <ImportDialog onClose={handleOnClose} />}
|
{action === 'import' && <ImportDialog onClose={handleOnClose} />}
|
||||||
{action === 'publish' && <PublishDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
{action === 'publish' && <PublishDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
{action === 'info' && <InfoDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
{action === 'info' && <InfoDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||||
{action === 'export' && <ExportDialog onClose={handleOnClose} mapId={mapsId[0]} enableImgExport={false} />}
|
{action === 'export' && (
|
||||||
|
<ExportDialog onClose={handleOnClose} mapId={mapsId[0]} enableImgExport={false} />
|
||||||
|
)}
|
||||||
{action === 'share' && <ShareDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
{action === 'share' && <ShareDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
</span >
|
</span>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
||||||
queryClient.invalidateQueries('maps')
|
queryClient.invalidateQueries('maps')
|
||||||
onClose();
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SimpleDialogProps = {
|
export type SimpleDialogProps = {
|
||||||
mapId: number,
|
mapId: number
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ActionDispatcher;
|
export default ActionDispatcher
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,102 +1,146 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
|
|
||||||
import { ErrorInfo } from '../../../../classes/client';
|
import { ErrorInfo } from '../../../../classes/client'
|
||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog'
|
||||||
import { SimpleDialogProps } from '..';
|
import { SimpleDialogProps } from '..'
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style'
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs'
|
||||||
import { fetchMapById } from '../../../../redux/clientSlice';
|
import { fetchMapById } from '../../../../redux/clientSlice'
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper'
|
||||||
import Card from '@material-ui/core/Card';
|
import Card from '@material-ui/core/Card'
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem'
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography'
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List'
|
||||||
|
|
||||||
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog onClose={handleOnClose} error={error}
|
<BaseDialog
|
||||||
|
onClose={handleOnClose}
|
||||||
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'info.title', defaultMessage: 'Info' })}
|
title={intl.formatMessage({ id: 'info.title', defaultMessage: 'Info' })}
|
||||||
description={intl.formatMessage({ id: 'info.description-msg', defaultMessage: 'By publishing the map you make it visible to everyone on the Internet.' })}
|
description={intl.formatMessage({
|
||||||
submitButton={intl.formatMessage({ id: 'info.button', defaultMessage: 'Accept' })}>
|
id: 'info.description-msg',
|
||||||
|
defaultMessage:
|
||||||
|
'By publishing the map you make it visible to everyone on the Internet.',
|
||||||
|
})}
|
||||||
|
submitButton={intl.formatMessage({ id: 'info.button', defaultMessage: 'Accept' })}
|
||||||
|
>
|
||||||
<Paper style={{ maxHeight: 200, overflowY: 'scroll' }} variant="outlined" elevation={0}>
|
<Paper style={{ maxHeight: 200, overflowY: 'scroll' }} variant="outlined" elevation={0}>
|
||||||
<Card variant="outlined">
|
<Card variant="outlined">
|
||||||
<List dense={true}>
|
<List dense={true}>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||||
<FormattedMessage id="info.basic-info" defaultMessage="Basic Info" />
|
<FormattedMessage
|
||||||
|
id="info.basic-info"
|
||||||
|
defaultMessage="Basic Info"
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
color="textPrimary"
|
||||||
|
className={classes.textDesc}
|
||||||
|
>
|
||||||
<FormattedMessage id="info.name" defaultMessage="Name" />:
|
<FormattedMessage id="info.name" defaultMessage="Name" />:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">{map?.title}</Typography>
|
||||||
{map?.title}
|
|
||||||
</Typography>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
<Typography
|
||||||
<FormattedMessage id="info.description" defaultMessage="Description" />:
|
variant="caption"
|
||||||
</Typography>
|
color="textPrimary"
|
||||||
<Typography variant="body2">
|
className={classes.textDesc}
|
||||||
{map?.description}
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="info.description"
|
||||||
|
defaultMessage="Description"
|
||||||
|
/>
|
||||||
|
:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="body2">{map?.description}</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
color="textPrimary"
|
||||||
|
className={classes.textDesc}
|
||||||
|
>
|
||||||
<FormattedMessage id="info.creator" defaultMessage="Creator" />:
|
<FormattedMessage id="info.creator" defaultMessage="Creator" />:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="body2">{map?.createdBy}</Typography>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
color="textPrimary"
|
||||||
|
className={classes.textDesc}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="info.creation-time"
|
||||||
|
defaultMessage="Creation Date"
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{map?.createdBy}
|
{dayjs(map?.creationTime).format('lll')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
<Typography
|
||||||
<FormattedMessage id="info.creation-time" defaultMessage="Creation Date" />:
|
variant="caption"
|
||||||
</Typography>
|
color="textPrimary"
|
||||||
<Typography variant="body2">
|
className={classes.textDesc}
|
||||||
{dayjs(map?.creationTime).format("lll")}
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="info.modified-tny"
|
||||||
|
defaultMessage="Last Modified By"
|
||||||
|
/>
|
||||||
|
:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="body2">{map?.lastModificationBy}</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
<Typography
|
||||||
<FormattedMessage id="info.modified-tny" defaultMessage="Last Modified By" />:
|
variant="caption"
|
||||||
|
color="textPrimary"
|
||||||
|
className={classes.textDesc}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="info.modified-time"
|
||||||
|
defaultMessage="Last Modified Date"
|
||||||
|
/>
|
||||||
|
:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{map?.lastModificationBy}
|
{dayjs(map?.lastModificationTime).format('lll')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
<Typography
|
||||||
<FormattedMessage id="info.modified-time" defaultMessage="Last Modified Date" />:
|
variant="caption"
|
||||||
</Typography>
|
color="textPrimary"
|
||||||
<Typography variant="body2">
|
className={classes.textDesc}
|
||||||
{dayjs(map?.lastModificationTime).format("lll")}
|
>
|
||||||
</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
|
||||||
<FormattedMessage id="info.starred" defaultMessage="Starred" />:
|
<FormattedMessage id="info.starred" defaultMessage="Starred" />:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
@ -106,7 +150,7 @@ const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement =
|
|||||||
</List>
|
</List>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card variant="outlined" style={{ marginTop: "10px" }}>
|
<Card variant="outlined" style={{ marginTop: '10px' }}>
|
||||||
<List dense={true}>
|
<List dense={true}>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||||
@ -115,18 +159,23 @@ const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement =
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
<Typography
|
||||||
<FormattedMessage id="info.public-visibility" defaultMessage="Publicly Visible" />:
|
variant="caption"
|
||||||
</Typography>
|
color="textPrimary"
|
||||||
<Typography variant="body2">
|
className={classes.textDesc}
|
||||||
{Boolean(map?.isPublic).toString()}
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="info.public-visibility"
|
||||||
|
defaultMessage="Publicly Visible"
|
||||||
|
/>
|
||||||
|
:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="body2">{Boolean(map?.isPublic).toString()}</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
</Card>
|
</Card>
|
||||||
</Paper>
|
</Paper>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InfoDialog;
|
export default InfoDialog
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import createStyles from "@material-ui/core/styles/createStyles";
|
import createStyles from '@material-ui/core/styles/createStyles'
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
import makeStyles from '@material-ui/core/styles/makeStyles'
|
||||||
|
|
||||||
export const useStyles = makeStyles(() =>
|
export const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
textarea: {
|
textarea: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '15px 15px',
|
padding: '15px 15px',
|
||||||
marging: '0px 10px'
|
marging: '0px 10px',
|
||||||
},
|
},
|
||||||
textDesc: {
|
textDesc: {
|
||||||
width: '150px'
|
width: '150px',
|
||||||
}
|
},
|
||||||
}),
|
})
|
||||||
);
|
)
|
||||||
|
@ -1,76 +1,86 @@
|
|||||||
import React from 'react';
|
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 Client, { ErrorInfo } from '../../../../classes/client';
|
import Client, { ErrorInfo } from '../../../../classes/client'
|
||||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice'
|
||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog'
|
||||||
import { handleOnMutationSuccess, SimpleDialogProps } from '..';
|
import { handleOnMutationSuccess, SimpleDialogProps } from '..'
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style'
|
||||||
|
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
|
||||||
import Checkbox from '@material-ui/core/Checkbox';
|
|
||||||
import TabContext from '@material-ui/lab/TabContext';
|
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
|
||||||
import TabList from '@material-ui/lab/TabList';
|
|
||||||
import Tab from '@material-ui/core/Tab';
|
|
||||||
import TabPanel from '@material-ui/lab/TabPanel';
|
|
||||||
import Typography from '@material-ui/core/Typography';
|
|
||||||
import TextareaAutosize from '@material-ui/core/TextareaAutosize';
|
|
||||||
|
|
||||||
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
|
import FormControlLabel from '@material-ui/core/FormControlLabel'
|
||||||
|
import Checkbox from '@material-ui/core/Checkbox'
|
||||||
|
import TabContext from '@material-ui/lab/TabContext'
|
||||||
|
import AppBar from '@material-ui/core/AppBar'
|
||||||
|
import TabList from '@material-ui/lab/TabList'
|
||||||
|
import Tab from '@material-ui/core/Tab'
|
||||||
|
import TabPanel from '@material-ui/lab/TabPanel'
|
||||||
|
import Typography from '@material-ui/core/Typography'
|
||||||
|
import TextareaAutosize from '@material-ui/core/TextareaAutosize'
|
||||||
|
|
||||||
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId)
|
||||||
|
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
const [activeTab, setActiveTab] = React.useState('1');
|
const [activeTab, setActiveTab] = React.useState('1')
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
const mutation = useMutation<void, ErrorInfo, boolean>((model: boolean) => {
|
const mutation = useMutation<void, ErrorInfo, boolean>(
|
||||||
return client.updateMapToPublic(mapId, model);
|
(model: boolean) => {
|
||||||
|
return client.updateMapToPublic(mapId, model)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setModel(model);
|
setModel(model)
|
||||||
handleOnMutationSuccess(onClose, queryClient);
|
handleOnMutationSuccess(onClose, queryClient)
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
mutation.mutate(model);
|
mutation.mutate(model)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
setModel(checked);
|
setModel(checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTabChange = (event, newValue) => {
|
const handleTabChange = (event, newValue) => {
|
||||||
setActiveTab(newValue);
|
setActiveTab(newValue)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
|
<BaseDialog
|
||||||
|
onClose={handleOnClose}
|
||||||
|
onSubmit={handleOnSubmit}
|
||||||
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'publish.title', defaultMessage: 'Publish' })}
|
title={intl.formatMessage({ id: 'publish.title', defaultMessage: 'Publish' })}
|
||||||
description={intl.formatMessage({ id: 'publish.description', defaultMessage: 'By publishing the map you make it visible to everyone on the Internet.' })}
|
description={intl.formatMessage({
|
||||||
submitButton={intl.formatMessage({ id: 'publish.button', defaultMessage: 'Accept' })}>
|
id: 'publish.description',
|
||||||
|
defaultMessage:
|
||||||
|
'By publishing the map you make it visible to everyone on the Internet.',
|
||||||
|
})}
|
||||||
|
submitButton={intl.formatMessage({
|
||||||
|
id: 'publish.button',
|
||||||
|
defaultMessage: 'Accept',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
@ -81,7 +91,10 @@ const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElemen
|
|||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={intl.formatMessage({ id: 'publish.checkbox', defaultMessage: 'Enable public sharing' })}
|
label={intl.formatMessage({
|
||||||
|
id: 'publish.checkbox',
|
||||||
|
defaultMessage: 'Enable public sharing',
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
@ -89,29 +102,57 @@ const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElemen
|
|||||||
<TabContext value={activeTab}>
|
<TabContext value={activeTab}>
|
||||||
<AppBar position="static">
|
<AppBar position="static">
|
||||||
<TabList onChange={handleTabChange}>
|
<TabList onChange={handleTabChange}>
|
||||||
<Tab label={intl.formatMessage({ id: 'publish.embedded', defaultMessage: 'Embedded' })} value="1" />
|
<Tab
|
||||||
<Tab label={intl.formatMessage({ id: 'publish.public-url', defaultMessage: 'Public URL' })} value="2" />
|
label={intl.formatMessage({
|
||||||
|
id: 'publish.embedded',
|
||||||
|
defaultMessage: 'Embedded',
|
||||||
|
})}
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'publish.public-url',
|
||||||
|
defaultMessage: 'Public URL',
|
||||||
|
})}
|
||||||
|
value="2"
|
||||||
|
/>
|
||||||
</TabList>
|
</TabList>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<TabPanel value="1">
|
<TabPanel value="1">
|
||||||
<Typography variant="subtitle2">
|
<Typography variant="subtitle2">
|
||||||
<FormattedMessage id="publish.embedded-msg" defaultMessage="Copy this snippet of code to embed in your blog or page:" />
|
<FormattedMessage
|
||||||
|
id="publish.embedded-msg"
|
||||||
|
defaultMessage="Copy this snippet of code to embed in your blog or page:"
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextareaAutosize className={classes.textarea} readOnly={true} spellCheck={false} rowsMax={6} defaultValue={`<iframe style="width:600px;height:400px;border:1px solid black" src="https://app.wisemapping.com/c/maps/${mapId}/embed?zoom=1.0"></iframe>`} />
|
<TextareaAutosize
|
||||||
|
className={classes.textarea}
|
||||||
|
readOnly={true}
|
||||||
|
spellCheck={false}
|
||||||
|
rowsMax={6}
|
||||||
|
defaultValue={`<iframe style="width:600px;height:400px;border:1px solid black" src="https://app.wisemapping.com/c/maps/${mapId}/embed?zoom=1.0"></iframe>`}
|
||||||
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="2">
|
<TabPanel value="2">
|
||||||
<Typography variant="subtitle2">
|
<Typography variant="subtitle2">
|
||||||
<FormattedMessage id="publish.public-url-msg" defaultMessage="Copy and paste the link below to share your map with colleagues:" />
|
<FormattedMessage
|
||||||
|
id="publish.public-url-msg"
|
||||||
|
defaultMessage="Copy and paste the link below to share your map with colleagues:"
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextareaAutosize className={classes.textarea} readOnly={true} spellCheck={false} rowsMax={1} defaultValue={`https://app.wisemapping.com/c/maps/${mapId}/public`} />
|
<TextareaAutosize
|
||||||
|
className={classes.textarea}
|
||||||
|
readOnly={true}
|
||||||
|
spellCheck={false}
|
||||||
|
rowsMax={1}
|
||||||
|
defaultValue={`https://app.wisemapping.com/c/maps/${mapId}/public`}
|
||||||
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabContext>
|
</TabContext>
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default PublishDialog
|
||||||
|
|
||||||
export default PublishDialog;
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import createStyles from "@material-ui/core/styles/createStyles";
|
import createStyles from '@material-ui/core/styles/createStyles'
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
import makeStyles from '@material-ui/core/styles/makeStyles'
|
||||||
|
|
||||||
export const useStyles = makeStyles(() =>
|
export const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
textarea: {
|
textarea: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '15px 15px',
|
padding: '15px 15px',
|
||||||
marging: '0px 10px'
|
marging: '0px 10px',
|
||||||
}
|
},
|
||||||
}),
|
})
|
||||||
);
|
)
|
||||||
|
@ -1,89 +1,116 @@
|
|||||||
import React, { useEffect } from "react";
|
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 Client, { BasicMapInfo, ErrorInfo } from "../../../../classes/client";
|
import Client, { BasicMapInfo, ErrorInfo } from '../../../../classes/client'
|
||||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice'
|
||||||
import { SimpleDialogProps, handleOnMutationSuccess } from "..";
|
import { SimpleDialogProps, handleOnMutationSuccess } from '..'
|
||||||
import Input from "../../../form/input";
|
import Input from '../../../form/input'
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from '../base-dialog'
|
||||||
import FormControl from "@material-ui/core/FormControl";
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
|
|
||||||
export type RenameModel = {
|
export type RenameModel = {
|
||||||
id: number;
|
id: number
|
||||||
title: string;
|
title: string
|
||||||
description?: string;
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: RenameModel = { title: '', description: '', id: -1 };
|
const defaultModel: RenameModel = { title: '', description: '', id: -1 }
|
||||||
const RenameDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const RenameDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const service: Client = useSelector(activeInstance);
|
const service: Client = useSelector(activeInstance)
|
||||||
const [model, setModel] = React.useState<RenameModel>(defaultModel);
|
const [model, setModel] = React.useState<RenameModel>(defaultModel)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
const mutation = useMutation<RenameModel, ErrorInfo, RenameModel>((model: RenameModel) => {
|
const mutation = useMutation<RenameModel, ErrorInfo, RenameModel>(
|
||||||
const { id, ...rest } = model;
|
(model: RenameModel) => {
|
||||||
return service.renameMap(id, rest).then(() => model);
|
const { id, ...rest } = model
|
||||||
|
return service.renameMap(id, rest).then(() => model)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
handleOnMutationSuccess(onClose, queryClient);
|
handleOnMutationSuccess(onClose, queryClient)
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
|
||||||
event.preventDefault();
|
|
||||||
mutation.mutate(model);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const name = event.target.name;
|
|
||||||
const value = event.target.value;
|
|
||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { map } = fetchMapById(mapId);
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
|
event.preventDefault()
|
||||||
|
mutation.mutate(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
const name = event.target.name
|
||||||
|
const value = event.target.value
|
||||||
|
setModel({ ...model, [name as keyof BasicMapInfo]: value })
|
||||||
|
}
|
||||||
|
|
||||||
|
const { map } = fetchMapById(mapId)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && map) {
|
if (open && map) {
|
||||||
setModel(map);
|
setModel(map)
|
||||||
} else {
|
} else {
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
setError(undefined);
|
setError(undefined)
|
||||||
}
|
}
|
||||||
}, [mapId])
|
}, [mapId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog onClose={handleOnClose} onSubmit={handleOnSubmit} error={error}
|
<BaseDialog
|
||||||
|
onClose={handleOnClose}
|
||||||
|
onSubmit={handleOnSubmit}
|
||||||
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
title={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||||
description={intl.formatMessage({ id: 'rename.description', defaultMessage: 'Please, fill the new map name and description.' })}
|
description={intl.formatMessage({
|
||||||
submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}>
|
id: 'rename.description',
|
||||||
|
defaultMessage: 'Please, fill the new map name and description.',
|
||||||
|
})}
|
||||||
|
submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||||
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input name="title" type="text" label={intl.formatMessage({ id: "action.rename-name-placeholder", defaultMessage: "Name" })}
|
<Input
|
||||||
value={model.title} onChange={handleOnChange} error={error} fullWidth={true} />
|
name="title"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-name-placeholder',
|
||||||
|
defaultMessage: 'Name',
|
||||||
|
})}
|
||||||
|
value={model.title}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
error={error}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="description" type="text" label={intl.formatMessage({ id: "action.rename-description-placeholder", defaultMessage: "Description" })}
|
<Input
|
||||||
value={model.description} onChange={handleOnChange} required={false} fullWidth={true} />
|
name="description"
|
||||||
|
type="text"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'action.rename-description-placeholder',
|
||||||
|
defaultMessage: 'Description',
|
||||||
|
})}
|
||||||
|
value={model.description}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
required={false}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RenameDialog;
|
export default RenameDialog
|
||||||
|
@ -1,119 +1,132 @@
|
|||||||
import React from "react";
|
import React from 'react'
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
import { useMutation, useQuery, useQueryClient } from 'react-query'
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from 'react-redux'
|
||||||
import Client, { ErrorInfo, Permission } from "../../../../classes/client";
|
import Client, { ErrorInfo, Permission } from '../../../../classes/client'
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice'
|
||||||
import { SimpleDialogProps } from "..";
|
import { SimpleDialogProps } from '..'
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from '../base-dialog'
|
||||||
import List from "@material-ui/core/List";
|
import List from '@material-ui/core/List'
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItem from '@material-ui/core/ListItem'
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from '@material-ui/core/ListItemText'
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
|
|
||||||
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
|
|
||||||
import DeleteIcon from '@material-ui/icons/Delete';
|
|
||||||
import Paper from "@material-ui/core/Paper";
|
|
||||||
import Select from "@material-ui/core/Select";
|
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
|
||||||
import Button from "@material-ui/core/Button";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
|
||||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
|
||||||
import Checkbox from "@material-ui/core/Checkbox";
|
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import { useStyles } from "./style";
|
|
||||||
import RoleIcon from "../../role-icon";
|
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
|
||||||
|
|
||||||
|
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'
|
||||||
|
import DeleteIcon from '@material-ui/icons/Delete'
|
||||||
|
import Paper from '@material-ui/core/Paper'
|
||||||
|
import Select from '@material-ui/core/Select'
|
||||||
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
|
import Button from '@material-ui/core/Button'
|
||||||
|
import TextField from '@material-ui/core/TextField'
|
||||||
|
import FormControlLabel from '@material-ui/core/FormControlLabel'
|
||||||
|
import Checkbox from '@material-ui/core/Checkbox'
|
||||||
|
import Typography from '@material-ui/core/Typography'
|
||||||
|
import { useStyles } from './style'
|
||||||
|
import RoleIcon from '../../role-icon'
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
|
|
||||||
type ShareModel = {
|
type ShareModel = {
|
||||||
emails: string,
|
emails: string
|
||||||
role: 'editor' | 'viewer',
|
role: 'editor' | 'viewer'
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: ShareModel = { emails: '', role: 'editor', message: '' };
|
const defaultModel: ShareModel = { emails: '', role: 'editor', message: '' }
|
||||||
const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
const [showMessage, setShowMessage] = React.useState<boolean>(false);
|
const [showMessage, setShowMessage] = React.useState<boolean>(false)
|
||||||
const [model, setModel] = React.useState<ShareModel>(defaultModel);
|
const [model, setModel] = React.useState<ShareModel>(defaultModel)
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>()
|
||||||
|
|
||||||
const deleteMutation = useMutation(
|
const deleteMutation = useMutation(
|
||||||
(email: string) => {
|
(email: string) => {
|
||||||
return client.deleteMapPermission(mapId, email);
|
return client.deleteMapPermission(mapId, email)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
queryClient.invalidateQueries(`perm-${mapId}`)
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
},
|
},
|
||||||
onError: (error: ErrorInfo) => {
|
onError: (error: ErrorInfo) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const addMutation = useMutation(
|
const addMutation = useMutation(
|
||||||
(model: ShareModel) => {
|
(model: ShareModel) => {
|
||||||
const emails = model.emails.split("'");
|
const emails = model.emails.split("'")
|
||||||
const permissions = emails.map((email) => { return { email: email, role: model.role } });
|
const permissions = emails.map((email) => {
|
||||||
return client.addMapPermissions(mapId, model.message, permissions);
|
return { email: email, role: model.role }
|
||||||
|
})
|
||||||
|
return client.addMapPermissions(mapId, model.message, permissions)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
queryClient.invalidateQueries(`perm-${mapId}`)
|
||||||
setModel(defaultModel);
|
setModel(defaultModel)
|
||||||
},
|
},
|
||||||
onError: (error: ErrorInfo) => {
|
onError: (error: ErrorInfo) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name
|
||||||
const value = event.target.value;
|
const value = event.target.value
|
||||||
setModel({ ...model, [name as keyof ShareModel]: value });
|
setModel({ ...model, [name as keyof ShareModel]: value })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOnAddClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
const handleOnAddClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
addMutation.mutate(model);
|
addMutation.mutate(model)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOnDeleteClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, email: string): void => {
|
const handleOnDeleteClick = (
|
||||||
event.stopPropagation();
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||||
deleteMutation.mutate(email);
|
email: string
|
||||||
};
|
): void => {
|
||||||
|
event.stopPropagation()
|
||||||
|
deleteMutation.mutate(email)
|
||||||
|
}
|
||||||
|
|
||||||
const { isLoading, data: permissions = [] } = useQuery<unknown, ErrorInfo, Permission[]>(`perm-${mapId}`, () => {
|
const { isLoading, data: permissions = [] } = useQuery<unknown, ErrorInfo, Permission[]>(
|
||||||
return client.fetchMapPermissions(mapId);
|
`perm-${mapId}`,
|
||||||
});
|
() => {
|
||||||
|
return client.fetchMapPermissions(mapId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const formatName = (perm: Permission): string => {
|
const formatName = (perm: Permission): string => {
|
||||||
return perm.name ? `${perm.name}<${perm.email}>` : perm.email;
|
return perm.name ? `${perm.name}<${perm.email}>` : perm.email
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
title={intl.formatMessage({ id: "share.delete-title", defaultMessage: "Share with people" })}
|
title={intl.formatMessage({
|
||||||
description={intl.formatMessage({ id: "share.delete-description", defaultMessage: "Invite people to collaborate with you in the creation of your midnmap. They will be notified by email. " })}
|
id: 'share.delete-title',
|
||||||
|
defaultMessage: 'Share with people',
|
||||||
|
})}
|
||||||
|
description={intl.formatMessage({
|
||||||
|
id: 'share.delete-description',
|
||||||
|
defaultMessage:
|
||||||
|
'Invite people to collaborate with you in the creation of your midnmap. They will be notified by email. ',
|
||||||
|
})}
|
||||||
PaperProps={{ classes: { root: classes.paper } }}
|
PaperProps={{ classes: { root: classes.paper } }}
|
||||||
error={error}
|
error={error}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div className={classes.actionContainer}>
|
<div className={classes.actionContainer}>
|
||||||
<TextField
|
<TextField
|
||||||
id="emails"
|
id="emails"
|
||||||
@ -136,16 +149,29 @@ const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
name="role"
|
name="role"
|
||||||
style={{ margin: '0px 10px' }}
|
style={{ margin: '0px 10px' }}
|
||||||
>
|
>
|
||||||
<MenuItem value='editor'><FormattedMessage id="share.can-edit" defaultMessage="Can edit" /></MenuItem>
|
<MenuItem value="editor">
|
||||||
<MenuItem value='viewer'><FormattedMessage id="share.can-view" defaultMessage="Can view" /></MenuItem>
|
<FormattedMessage id="share.can-edit" defaultMessage="Can edit" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value="viewer">
|
||||||
|
<FormattedMessage id="share.can-view" defaultMessage="Can view" />
|
||||||
|
</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="start"
|
value="start"
|
||||||
onChange={(event, value) => { setShowMessage(value) }}
|
onChange={(event, value) => {
|
||||||
style={{ fontSize: "5px" }}
|
setShowMessage(value)
|
||||||
|
}}
|
||||||
|
style={{ fontSize: '5px' }}
|
||||||
control={<Checkbox color="primary" />}
|
control={<Checkbox color="primary" />}
|
||||||
label={<Typography variant="subtitle2"><FormattedMessage id="share.add-message" defaultMessage="Add message" /></Typography>}
|
label={
|
||||||
|
<Typography variant="subtitle2">
|
||||||
|
<FormattedMessage
|
||||||
|
id="share.add-message"
|
||||||
|
defaultMessage="Add message"
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
labelPlacement="end"
|
labelPlacement="end"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -154,11 +180,12 @@ const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
type="button"
|
type="button"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
onClick={handleOnAddClick}>
|
onClick={handleOnAddClick}
|
||||||
|
>
|
||||||
<FormattedMessage id="share.add-button" defaultMessage="Add" />
|
<FormattedMessage id="share.add-button" defaultMessage="Add" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{showMessage &&
|
{showMessage && (
|
||||||
<TextField
|
<TextField
|
||||||
multiline
|
multiline
|
||||||
rows={3}
|
rows={3}
|
||||||
@ -168,37 +195,61 @@ const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
name="message"
|
name="message"
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
value={model.message}
|
value={model.message}
|
||||||
label={intl.formatMessage({ id: 'share.message', defaultMessage: 'Message' })}
|
label={intl.formatMessage({
|
||||||
|
id: 'share.message',
|
||||||
|
defaultMessage: 'Message',
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isLoading &&
|
{!isLoading && (
|
||||||
<Paper elevation={1} className={classes.listPaper} variant="outlined">
|
<Paper elevation={1} className={classes.listPaper} variant="outlined">
|
||||||
<List>
|
<List>
|
||||||
{permissions && permissions.map((permission) => {
|
{permissions &&
|
||||||
|
permissions.map((permission) => {
|
||||||
return (
|
return (
|
||||||
<ListItem key={permission.email} role={undefined} dense button>
|
<ListItem
|
||||||
<ListItemText id={permission.email} primary={formatName(permission)} />
|
key={permission.email}
|
||||||
|
role={undefined}
|
||||||
|
dense
|
||||||
|
button
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
id={permission.email}
|
||||||
|
primary={formatName(permission)}
|
||||||
|
/>
|
||||||
|
|
||||||
<RoleIcon role={permission.role} />
|
<RoleIcon role={permission.role} />
|
||||||
< ListItemSecondaryAction >
|
<ListItemSecondaryAction>
|
||||||
<Tooltip title={<FormattedMessage id="share.delete" defaultMessage="Delete collaborator" />}>
|
<Tooltip
|
||||||
<IconButton edge="end" disabled={permission.role == 'owner'} onClick={e => handleOnDeleteClick(e, permission.email)}>
|
title={
|
||||||
|
<FormattedMessage
|
||||||
|
id="share.delete"
|
||||||
|
defaultMessage="Delete collaborator"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
edge="end"
|
||||||
|
disabled={permission.role == 'owner'}
|
||||||
|
onClick={(e) =>
|
||||||
|
handleOnDeleteClick(e, permission.email)
|
||||||
|
}
|
||||||
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</List>
|
</List>
|
||||||
</Paper>
|
</Paper>
|
||||||
}
|
)}
|
||||||
|
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div >
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ShareDialog;
|
export default ShareDialog
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import createStyles from "@material-ui/core/styles/createStyles";
|
import createStyles from '@material-ui/core/styles/createStyles'
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
import makeStyles from '@material-ui/core/styles/makeStyles'
|
||||||
|
|
||||||
export const useStyles = makeStyles(() =>
|
export const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@ -7,21 +7,20 @@ export const useStyles = makeStyles(() =>
|
|||||||
padding: '10px 0px',
|
padding: '10px 0px',
|
||||||
border: '1px solid rgba(0, 0, 0, 0.12)',
|
border: '1px solid rgba(0, 0, 0, 0.12)',
|
||||||
borderRadius: '8px 8px 0px 0px',
|
borderRadius: '8px 8px 0px 0px',
|
||||||
textAlign: "center"
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
textArea:
|
textArea: {
|
||||||
{
|
|
||||||
width: '730px',
|
width: '730px',
|
||||||
margin: '5px 0px',
|
margin: '5px 0px',
|
||||||
padding: '10px'
|
padding: '10px',
|
||||||
},
|
},
|
||||||
listPaper: {
|
listPaper: {
|
||||||
maxHeight: 200,
|
maxHeight: 200,
|
||||||
overflowY: 'scroll',
|
overflowY: 'scroll',
|
||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
width: "850px",
|
width: '850px',
|
||||||
minWidth: "850px"
|
minWidth: '850px',
|
||||||
}
|
},
|
||||||
}),
|
})
|
||||||
);
|
)
|
||||||
|
@ -1,42 +1,43 @@
|
|||||||
import React from "react";
|
import React from 'react'
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
|
|
||||||
import Help from "@material-ui/icons/Help";
|
import Help from '@material-ui/icons/Help'
|
||||||
import PolicyOutlined from "@material-ui/icons/PolicyOutlined";
|
import PolicyOutlined from '@material-ui/icons/PolicyOutlined'
|
||||||
import FeedbackOutlined from "@material-ui/icons/FeedbackOutlined";
|
import FeedbackOutlined from '@material-ui/icons/FeedbackOutlined'
|
||||||
import EmojiPeopleOutlined from "@material-ui/icons/EmailOutlined";
|
import EmojiPeopleOutlined from '@material-ui/icons/EmailOutlined'
|
||||||
import EmailOutlined from "@material-ui/icons/EmailOutlined";
|
import EmailOutlined from '@material-ui/icons/EmailOutlined'
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
import Menu from "@material-ui/core/Menu";
|
import Menu from '@material-ui/core/Menu'
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
import Link from "@material-ui/core/Link";
|
import Link from '@material-ui/core/Link'
|
||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
|
|
||||||
const HelpMenu = (): React.ReactElement => {
|
const HelpMenu = (): React.ReactElement => {
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl)
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip arrow={true} title={intl.formatMessage({ id: 'help.support', defaultMessage: 'Support' })}>
|
<Tooltip
|
||||||
|
arrow={true}
|
||||||
<IconButton
|
title={intl.formatMessage({ id: 'help.support', defaultMessage: 'Support' })}
|
||||||
aria-haspopup="true"
|
>
|
||||||
onClick={handleMenu}>
|
<IconButton aria-haspopup="true" onClick={handleMenu}>
|
||||||
<Help />
|
<Help />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu id="appbar-profile"
|
<Menu
|
||||||
|
id="appbar-profile"
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
keepMounted
|
keepMounted
|
||||||
open={open}
|
open={open}
|
||||||
@ -49,14 +50,21 @@ const HelpMenu = (): React.ReactElement => {
|
|||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<MenuItem onClick={handleClose}>
|
<MenuItem onClick={handleClose}>
|
||||||
<Link color="textSecondary" href="https://www.wisemapping.com/termsofuse.html" target="help">
|
<Link
|
||||||
|
color="textSecondary"
|
||||||
|
href="https://www.wisemapping.com/termsofuse.html"
|
||||||
|
target="help"
|
||||||
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<PolicyOutlined fontSize="small" />
|
<PolicyOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="footer.termsandconditions" defaultMessage="Term And Conditions" />
|
<FormattedMessage
|
||||||
|
id="footer.termsandconditions"
|
||||||
|
defaultMessage="Term And Conditions"
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
@ -79,7 +87,11 @@ const HelpMenu = (): React.ReactElement => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onClick={handleClose}>
|
<MenuItem onClick={handleClose}>
|
||||||
<Link color="textSecondary" href="https://www.wisemapping.com/aboutus.html" target="help">
|
<Link
|
||||||
|
color="textSecondary"
|
||||||
|
href="https://www.wisemapping.com/aboutus.html"
|
||||||
|
target="help"
|
||||||
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<EmojiPeopleOutlined fontSize="small" />
|
<EmojiPeopleOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
@ -87,7 +99,8 @@ const HelpMenu = (): React.ReactElement => {
|
|||||||
</Link>
|
</Link>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</span>);
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HelpMenu;
|
export default HelpMenu
|
||||||
|
@ -1,137 +1,142 @@
|
|||||||
import React, { ErrorInfo, ReactElement, useEffect } from 'react';
|
import React, { ErrorInfo, ReactElement, useEffect } from 'react'
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx'
|
||||||
import Drawer from '@material-ui/core/Drawer';
|
import Drawer from '@material-ui/core/Drawer'
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from '@material-ui/core/AppBar'
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar'
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List'
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style'
|
||||||
import { MapsList } from './maps-list';
|
import { MapsList } from './maps-list'
|
||||||
import { FormattedMessage, IntlProvider, useIntl } from 'react-intl';
|
import { FormattedMessage, IntlProvider, useIntl } from 'react-intl'
|
||||||
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
import { useQuery, useMutation, useQueryClient } from 'react-query'
|
||||||
import { activeInstance } from '../../redux/clientSlice';
|
import { activeInstance } from '../../redux/clientSlice'
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import Client, { Label } from '../../classes/client';
|
import Client, { Label } from '../../classes/client'
|
||||||
import ActionDispatcher from './action-dispatcher';
|
import ActionDispatcher from './action-dispatcher'
|
||||||
import { ActionType } from './action-chooser';
|
import { ActionType } from './action-chooser'
|
||||||
import AccountMenu from './account-menu';
|
import AccountMenu from './account-menu'
|
||||||
import ClientHealthSentinel from '../../classes/client/client-health-sentinel';
|
import ClientHealthSentinel from '../../classes/client/client-health-sentinel'
|
||||||
import HelpMenu from './help-menu';
|
import HelpMenu from './help-menu'
|
||||||
import LanguageMenu from './language-menu';
|
import LanguageMenu from './language-menu'
|
||||||
import AppI18n, { Locales } from '../../classes/app-i18n';
|
import AppI18n, { Locales } from '../../classes/app-i18n'
|
||||||
|
|
||||||
|
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||||
|
import ListItem from '@material-ui/core/ListItem'
|
||||||
|
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
import AddCircleTwoTone from '@material-ui/icons/AddCircleTwoTone'
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import CloudUploadTwoTone from '@material-ui/icons/CloudUploadTwoTone'
|
||||||
|
import DeleteOutlineTwoTone from '@material-ui/icons/DeleteOutlineTwoTone'
|
||||||
|
import LabelTwoTone from '@material-ui/icons/LabelTwoTone'
|
||||||
|
import PersonOutlineTwoTone from '@material-ui/icons/PersonOutlineTwoTone'
|
||||||
|
import PublicTwoTone from '@material-ui/icons/PublicTwoTone'
|
||||||
|
import ScatterPlotTwoTone from '@material-ui/icons/ScatterPlotTwoTone'
|
||||||
|
import ShareTwoTone from '@material-ui/icons/ShareTwoTone'
|
||||||
|
import StarTwoTone from '@material-ui/icons/StarTwoTone'
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
|
import Button from '@material-ui/core/Button'
|
||||||
|
import Link from '@material-ui/core/Link'
|
||||||
|
import ListItemText from '@material-ui/core/ListItemText'
|
||||||
|
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'
|
||||||
|
|
||||||
import AddCircleTwoTone from '@material-ui/icons/AddCircleTwoTone';
|
import logoIcon from '../../images/logo-small.svg'
|
||||||
import CloudUploadTwoTone from '@material-ui/icons/CloudUploadTwoTone';
|
import poweredByIcon from '../../images/pwrdby-white.svg'
|
||||||
import DeleteOutlineTwoTone from '@material-ui/icons/DeleteOutlineTwoTone';
|
|
||||||
import LabelTwoTone from '@material-ui/icons/LabelTwoTone';
|
|
||||||
import PersonOutlineTwoTone from '@material-ui/icons/PersonOutlineTwoTone';
|
|
||||||
import PublicTwoTone from '@material-ui/icons/PublicTwoTone';
|
|
||||||
import ScatterPlotTwoTone from '@material-ui/icons/ScatterPlotTwoTone';
|
|
||||||
import ShareTwoTone from '@material-ui/icons/ShareTwoTone';
|
|
||||||
import StarTwoTone from '@material-ui/icons/StarTwoTone';
|
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
|
||||||
import Button from '@material-ui/core/Button';
|
|
||||||
import Link from '@material-ui/core/Link';
|
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
|
||||||
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
|
|
||||||
|
|
||||||
import logoIcon from '../../images/logo-small.svg';
|
export type Filter = GenericFilter | LabelFilter
|
||||||
import poweredByIcon from '../../images/pwrdby-white.svg';
|
|
||||||
|
|
||||||
export type Filter = GenericFilter | LabelFilter;
|
|
||||||
|
|
||||||
export interface GenericFilter {
|
export interface GenericFilter {
|
||||||
type: 'public' | 'all' | 'starred' | 'shared' | 'label' | 'owned';
|
type: 'public' | 'all' | 'starred' | 'shared' | 'label' | 'owned'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LabelFilter {
|
export interface LabelFilter {
|
||||||
type: 'label',
|
type: 'label'
|
||||||
label: Label
|
label: Label
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolbarButtonInfo {
|
interface ToolbarButtonInfo {
|
||||||
filter: GenericFilter | LabelFilter,
|
filter: GenericFilter | LabelFilter
|
||||||
label: string
|
label: string
|
||||||
icon: React.ReactElement;
|
icon: React.ReactElement
|
||||||
}
|
}
|
||||||
|
|
||||||
const MapsPage = (): ReactElement => {
|
const MapsPage = (): ReactElement => {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
const [filter, setFilter] = React.useState<Filter>({ type: 'all' });
|
const [filter, setFilter] = React.useState<Filter>({ type: 'all' })
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
const [activeDialog, setActiveDialog] = React.useState<ActionType | undefined>(undefined);
|
const [activeDialog, setActiveDialog] = React.useState<ActionType | undefined>(undefined)
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Maps | WiseMapping';
|
document.title = 'Maps | WiseMapping'
|
||||||
}, []);
|
}, [])
|
||||||
|
|
||||||
const mutation = useMutation(
|
const mutation = useMutation((id: number) => client.deleteLabel(id), {
|
||||||
(id: number) => client.deleteLabel(id),
|
|
||||||
{
|
|
||||||
onSuccess: () => queryClient.invalidateQueries('labels'),
|
onSuccess: () => queryClient.invalidateQueries('labels'),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(`Unexpected error ${error}`);
|
console.error(`Unexpected error ${error}`)
|
||||||
}
|
},
|
||||||
}
|
})
|
||||||
);
|
|
||||||
|
|
||||||
const handleMenuClick = (filter: Filter) => {
|
const handleMenuClick = (filter: Filter) => {
|
||||||
queryClient.invalidateQueries('maps');
|
queryClient.invalidateQueries('maps')
|
||||||
setFilter(filter);
|
setFilter(filter)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLabelDelete = (id: number) => {
|
const handleLabelDelete = (id: number) => {
|
||||||
mutation.mutate(id);
|
mutation.mutate(id)
|
||||||
};
|
}
|
||||||
|
|
||||||
const { data } = useQuery<unknown, ErrorInfo, Label[]>('labels', () => {
|
const { data } = useQuery<unknown, ErrorInfo, Label[]>('labels', () => {
|
||||||
return client.fetchLabels();
|
return client.fetchLabels()
|
||||||
});
|
})
|
||||||
|
|
||||||
const labels: Label[] = data ? data : [];
|
const labels: Label[] = data ? data : []
|
||||||
const filterButtons: ToolbarButtonInfo[] = [{
|
const filterButtons: ToolbarButtonInfo[] = [
|
||||||
|
{
|
||||||
filter: { type: 'all' },
|
filter: { type: 'all' },
|
||||||
label: 'All',
|
label: 'All',
|
||||||
icon: <ScatterPlotTwoTone color="secondary" />
|
icon: <ScatterPlotTwoTone color="secondary" />,
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
filter: { type: 'owned' },
|
filter: { type: 'owned' },
|
||||||
label: 'Owned',
|
label: 'Owned',
|
||||||
icon: <PersonOutlineTwoTone color="secondary" />
|
icon: <PersonOutlineTwoTone color="secondary" />,
|
||||||
|
},
|
||||||
}, {
|
{
|
||||||
filter: { type: 'starred' },
|
filter: { type: 'starred' },
|
||||||
label: 'Starred',
|
label: 'Starred',
|
||||||
icon: <StarTwoTone color="secondary" />
|
icon: <StarTwoTone color="secondary" />,
|
||||||
|
},
|
||||||
}, {
|
{
|
||||||
filter: { type: 'shared' },
|
filter: { type: 'shared' },
|
||||||
label: 'Shared with me',
|
label: 'Shared with me',
|
||||||
icon: <ShareTwoTone color="secondary" />
|
icon: <ShareTwoTone color="secondary" />,
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
filter: { type: 'public' },
|
filter: { type: 'public' },
|
||||||
label: 'Public',
|
label: 'Public',
|
||||||
icon: <PublicTwoTone color="secondary" />
|
icon: <PublicTwoTone color="secondary" />,
|
||||||
}];
|
},
|
||||||
|
]
|
||||||
|
|
||||||
labels.forEach(l => filterButtons.push({
|
labels.forEach((l) =>
|
||||||
|
filterButtons.push({
|
||||||
filter: { type: 'label', label: l },
|
filter: { type: 'label', label: l },
|
||||||
label: l.title,
|
label: l.title,
|
||||||
icon: <LabelTwoTone style={{ color: l.color ? l.color : 'inherit' }} />
|
icon: <LabelTwoTone style={{ color: l.color ? l.color : 'inherit' }} />,
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
// Configure using user settings ...
|
// Configure using user settings ...
|
||||||
const appi18n = new AppI18n();
|
const appi18n = new AppI18n()
|
||||||
const userLocale = appi18n.getUserLocale();
|
const userLocale = appi18n.getUserLocale()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={userLocale.code} defaultLocale={Locales.EN.code} messages={userLocale.message} >
|
<IntlProvider
|
||||||
|
locale={userLocale.code}
|
||||||
|
defaultLocale={Locales.EN.code}
|
||||||
|
messages={userLocale.message}
|
||||||
|
>
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<ClientHealthSentinel />
|
<ClientHealthSentinel />
|
||||||
<AppBar
|
<AppBar
|
||||||
@ -139,12 +144,19 @@ const MapsPage = (): ReactElement => {
|
|||||||
className={clsx(classes.appBar, {
|
className={clsx(classes.appBar, {
|
||||||
[classes.appBarShift]: open,
|
[classes.appBarShift]: open,
|
||||||
})}
|
})}
|
||||||
variant='outlined'
|
variant="outlined"
|
||||||
elevation={0}>
|
elevation={0}
|
||||||
|
>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Tooltip arrow={true} title={intl.formatMessage({ id: 'maps.create-tooltip', defaultMessage: 'Create a New Map' })}>
|
<Tooltip
|
||||||
<Button color="primary"
|
arrow={true}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'maps.create-tooltip',
|
||||||
|
defaultMessage: 'Create a New Map',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
data-testid="create"
|
data-testid="create"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -152,12 +164,19 @@ const MapsPage = (): ReactElement => {
|
|||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
startIcon={<AddCircleTwoTone />}
|
startIcon={<AddCircleTwoTone />}
|
||||||
className={classes.newMapButton}
|
className={classes.newMapButton}
|
||||||
onClick={() => setActiveDialog('create')}>
|
onClick={() => setActiveDialog('create')}
|
||||||
|
>
|
||||||
<FormattedMessage id="action.new" defaultMessage="New Map" />
|
<FormattedMessage id="action.new" defaultMessage="New Map" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip arrow={true} title={intl.formatMessage({ id: 'maps.import-desc', defaultMessage: 'Import from other tools' })}>
|
<Tooltip
|
||||||
|
arrow={true}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'maps.import-desc',
|
||||||
|
defaultMessage: 'Import from other tools',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
@ -166,11 +185,16 @@ const MapsPage = (): ReactElement => {
|
|||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
startIcon={<CloudUploadTwoTone />}
|
startIcon={<CloudUploadTwoTone />}
|
||||||
className={classes.importButton}
|
className={classes.importButton}
|
||||||
onClick={() => setActiveDialog('import')}>
|
onClick={() => setActiveDialog('import')}
|
||||||
|
>
|
||||||
<FormattedMessage id="action.import" defaultMessage="Import" />
|
<FormattedMessage id="action.import" defaultMessage="Import" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<ActionDispatcher action={activeDialog} onClose={() => setActiveDialog(undefined)} mapsId={[]} />
|
<ActionDispatcher
|
||||||
|
action={activeDialog}
|
||||||
|
onClose={() => setActiveDialog(undefined)}
|
||||||
|
mapsId={[]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className={classes.rightButtonGroup}>
|
<div className={classes.rightButtonGroup}>
|
||||||
<LanguageMenu />
|
<LanguageMenu />
|
||||||
@ -182,34 +206,40 @@ const MapsPage = (): ReactElement => {
|
|||||||
<Drawer
|
<Drawer
|
||||||
variant="permanent"
|
variant="permanent"
|
||||||
className={clsx(classes.drawer, {
|
className={clsx(classes.drawer, {
|
||||||
[classes.drawerOpen]: open
|
[classes.drawerOpen]: open,
|
||||||
})}
|
})}
|
||||||
classes={{
|
classes={{
|
||||||
paper: clsx({
|
paper: clsx({
|
||||||
[classes.drawerOpen]: open
|
[classes.drawerOpen]: open,
|
||||||
}),
|
}),
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<div style={{ padding: "20px 0 20px 15px" }} key="logo">
|
<div style={{ padding: '20px 0 20px 15px' }} key="logo">
|
||||||
<img src={logoIcon} alt="logo" />
|
<img src={logoIcon} alt="logo" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<List component="nav">
|
<List component="nav">
|
||||||
{filterButtons.map(buttonInfo => {
|
{filterButtons.map((buttonInfo) => {
|
||||||
return (<StyleListItem
|
return (
|
||||||
|
<StyleListItem
|
||||||
icon={buttonInfo.icon}
|
icon={buttonInfo.icon}
|
||||||
label={buttonInfo.label}
|
label={buttonInfo.label}
|
||||||
filter={buttonInfo.filter}
|
filter={buttonInfo.filter}
|
||||||
active={filter}
|
active={filter}
|
||||||
onClick={handleMenuClick}
|
onClick={handleMenuClick}
|
||||||
onDelete={handleLabelDelete}
|
onDelete={handleLabelDelete}
|
||||||
key={`${buttonInfo.filter.type}:${(buttonInfo.filter as LabelFilter).label}`}
|
key={`${buttonInfo.filter.type}:${
|
||||||
/>)
|
(buttonInfo.filter as LabelFilter).label
|
||||||
}
|
}`}
|
||||||
)}
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<div style={{ position: 'absolute', bottom: '10px', left: '20px' }} key="power-by">
|
<div
|
||||||
|
style={{ position: 'absolute', bottom: '10px', left: '20px' }}
|
||||||
|
key="power-by"
|
||||||
|
>
|
||||||
<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" />
|
||||||
</Link>
|
</Link>
|
||||||
@ -221,60 +251,64 @@ const MapsPage = (): ReactElement => {
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ListItemProps {
|
interface ListItemProps {
|
||||||
icon: React.ReactElement,
|
icon: React.ReactElement
|
||||||
label: string,
|
label: string
|
||||||
filter: Filter,
|
filter: Filter
|
||||||
active?: Filter
|
active?: Filter
|
||||||
onClick: (filter: Filter) => void;
|
onClick: (filter: Filter) => void
|
||||||
onDelete?: (id: number) => void;
|
onDelete?: (id: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyleListItem = (props: ListItemProps) => {
|
const StyleListItem = (props: ListItemProps) => {
|
||||||
const icon = props.icon;
|
const icon = props.icon
|
||||||
const label = props.label;
|
const label = props.label
|
||||||
const filter = props.filter;
|
const filter = props.filter
|
||||||
const activeFilter = props.active;
|
const activeFilter = props.active
|
||||||
const onClick = props.onClick;
|
const onClick = props.onClick
|
||||||
const onDeleteLabel = props.onDelete;
|
const onDeleteLabel = props.onDelete
|
||||||
const isSelected = activeFilter
|
const isSelected =
|
||||||
&& (activeFilter.type == filter.type)
|
activeFilter &&
|
||||||
&& (activeFilter.type != 'label' || ((activeFilter as LabelFilter).label == (filter as LabelFilter).label));
|
activeFilter.type == filter.type &&
|
||||||
|
(activeFilter.type != 'label' ||
|
||||||
|
(activeFilter as LabelFilter).label == (filter as LabelFilter).label)
|
||||||
|
|
||||||
const handleOnClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>, filter: Filter) => {
|
const handleOnClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>, filter: Filter) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
onClick(filter);
|
onClick(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOnDelete = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, filter: Filter) => {
|
const handleOnDelete = (
|
||||||
event.stopPropagation();
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||||
|
filter: Filter
|
||||||
|
) => {
|
||||||
|
event.stopPropagation()
|
||||||
if (!onDeleteLabel) {
|
if (!onDeleteLabel) {
|
||||||
throw "Illegal state exeption";
|
throw 'Illegal state exeption'
|
||||||
}
|
}
|
||||||
onDeleteLabel((filter as LabelFilter).label.id);
|
onDeleteLabel((filter as LabelFilter).label.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem button
|
<ListItem button selected={isSelected} onClick={(e) => handleOnClick(e, filter)}>
|
||||||
selected={isSelected}
|
<ListItemIcon>{icon}</ListItemIcon>
|
||||||
onClick={e => handleOnClick(e, filter)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
{icon}
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText style={{ color: 'white' }} primary={label} />
|
<ListItemText style={{ color: 'white' }} primary={label} />
|
||||||
{filter.type == 'label' &&
|
{filter.type == 'label' && (
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<IconButton edge="end" aria-label="delete" onClick={e => handleOnDelete(e, filter)}>
|
<IconButton
|
||||||
|
edge="end"
|
||||||
|
aria-label="delete"
|
||||||
|
onClick={(e) => handleOnDelete(e, filter)}
|
||||||
|
>
|
||||||
<DeleteOutlineTwoTone color="secondary" />
|
<DeleteOutlineTwoTone color="secondary" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
}
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MapsPage;
|
export default MapsPage
|
||||||
|
@ -1,61 +1,64 @@
|
|||||||
import TranslateTwoTone from '@material-ui/icons/TranslateTwoTone';
|
import TranslateTwoTone from '@material-ui/icons/TranslateTwoTone'
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { useMutation, useQueryClient } from "react-query";
|
import { useMutation, useQueryClient } from 'react-query'
|
||||||
import Client from "../../../classes/client";
|
import Client from '../../../classes/client'
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import { activeInstance, fetchAccount } from '../../../redux/clientSlice';
|
import { activeInstance, fetchAccount } from '../../../redux/clientSlice'
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
import { LocaleCode, Locales } from '../../../classes/app-i18n';
|
import { LocaleCode, Locales } from '../../../classes/app-i18n'
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import Menu from '@material-ui/core/Menu';
|
import Menu from '@material-ui/core/Menu'
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
import MenuItem from '@material-ui/core/MenuItem'
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from '@material-ui/core/Divider'
|
||||||
|
|
||||||
|
|
||||||
const LanguageMenu = (): React.ReactElement => {
|
const LanguageMenu = (): React.ReactElement => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
|
||||||
const [openHelpDialog, setHelpDialogOpen] = React.useState<boolean>(false);
|
const [openHelpDialog, setHelpDialogOpen] = React.useState<boolean>(false)
|
||||||
|
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl)
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const mutation = useMutation((locale: LocaleCode) => client.updateAccountLanguage(locale),
|
const mutation = useMutation((locale: LocaleCode) => client.updateAccountLanguage(locale), {
|
||||||
{
|
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries('account')
|
queryClient.invalidateQueries('account')
|
||||||
handleClose();
|
handleClose()
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(`Unexpected error ${error}`)
|
console.error(`Unexpected error ${error}`)
|
||||||
}
|
},
|
||||||
}
|
})
|
||||||
);
|
|
||||||
|
|
||||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget)
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnClick = (event: React.MouseEvent<HTMLElement>) => {
|
|
||||||
const localeCode = event.target['id'];
|
|
||||||
mutation.mutate(localeCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountInfo = fetchAccount();
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
const localeCode = event.target['id']
|
||||||
|
mutation.mutate(localeCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountInfo = fetchAccount()
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip arrow={true} title={intl.formatMessage({ id: 'language.change', defaultMessage: 'Change Language' })}>
|
<Tooltip
|
||||||
|
arrow={true}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'language.change',
|
||||||
|
defaultMessage: 'Change Language',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@ -68,7 +71,8 @@ const LanguageMenu = (): React.ReactElement => {
|
|||||||
{accountInfo?.locale?.label}
|
{accountInfo?.locale?.label}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu id="appbar-language"
|
<Menu
|
||||||
|
id="appbar-language"
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
keepMounted
|
keepMounted
|
||||||
open={open}
|
open={open}
|
||||||
@ -100,38 +104,40 @@ const LanguageMenu = (): React.ReactElement => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<MenuItem onClick={() => { handleClose(); setHelpDialogOpen(true) }} >
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
handleClose()
|
||||||
|
setHelpDialogOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<FormattedMessage id="language.help" defaultMessage="Help to Translate" />
|
<FormattedMessage id="language.help" defaultMessage="Help to Translate" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
{openHelpDialog &&
|
{openHelpDialog && <HelpUsToTranslateDialog onClose={() => setHelpDialogOpen(false)} />}
|
||||||
<HelpUsToTranslateDialog onClose={() => setHelpDialogOpen(false)} />
|
</span>
|
||||||
}
|
)
|
||||||
</span>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HelpUsToTranslateDialogProp = {
|
type HelpUsToTranslateDialogProp = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
const HelpUsToTranslateDialog = ({ onClose }: HelpUsToTranslateDialogProp) => {
|
const HelpUsToTranslateDialog = ({ onClose }: HelpUsToTranslateDialogProp) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog open={true} onClose={onClose}>
|
||||||
open={true}
|
|
||||||
onClose={onClose}
|
|
||||||
>
|
|
||||||
<DialogTitle>Help us to support more languages !</DialogTitle>
|
<DialogTitle>Help us to support more languages !</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
We need your help !. If you are interested, send us an email to team@wisemapping.com.
|
We need your help !. If you are interested, send us an email to
|
||||||
|
team@wisemapping.com.
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button autoFocus onClick={onClose}>Close</Button>
|
<Button autoFocus onClick={onClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default LanguageMenu
|
||||||
export default LanguageMenu;
|
|
||||||
|
@ -1,127 +1,160 @@
|
|||||||
import React, { useEffect, CSSProperties } from 'react';
|
import React, { useEffect, CSSProperties } from 'react'
|
||||||
|
|
||||||
import { useStyles } from './styled';
|
import { useStyles } from './styled'
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import { activeInstance, fetchAccount } from '../../../redux/clientSlice';
|
import { activeInstance, fetchAccount } from '../../../redux/clientSlice'
|
||||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
import { useMutation, useQuery, useQueryClient } from 'react-query'
|
||||||
import Client, { ErrorInfo, MapInfo } from '../../../classes/client';
|
import Client, { ErrorInfo, MapInfo } from '../../../classes/client'
|
||||||
import ActionChooser, { ActionType } from '../action-chooser';
|
import ActionChooser, { ActionType } from '../action-chooser'
|
||||||
import ActionDispatcher from '../action-dispatcher';
|
import ActionDispatcher from '../action-dispatcher'
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs'
|
||||||
import { Filter, LabelFilter } from '..';
|
import { Filter, LabelFilter } from '..'
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl'
|
||||||
|
|
||||||
import Table from '@material-ui/core/Table';
|
import Table from '@material-ui/core/Table'
|
||||||
import TableBody from '@material-ui/core/TableBody';
|
import TableBody from '@material-ui/core/TableBody'
|
||||||
import TableCell from '@material-ui/core/TableCell';
|
import TableCell from '@material-ui/core/TableCell'
|
||||||
import TableContainer from '@material-ui/core/TableContainer';
|
import TableContainer from '@material-ui/core/TableContainer'
|
||||||
import TableHead from '@material-ui/core/TableHead';
|
import TableHead from '@material-ui/core/TableHead'
|
||||||
import TablePagination from '@material-ui/core/TablePagination';
|
import TablePagination from '@material-ui/core/TablePagination'
|
||||||
import TableRow from '@material-ui/core/TableRow';
|
import TableRow from '@material-ui/core/TableRow'
|
||||||
import TableSortLabel from '@material-ui/core/TableSortLabel';
|
import TableSortLabel from '@material-ui/core/TableSortLabel'
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar'
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper'
|
||||||
import Checkbox from '@material-ui/core/Checkbox';
|
import Checkbox from '@material-ui/core/Checkbox'
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import InputBase from '@material-ui/core/InputBase';
|
import InputBase from '@material-ui/core/InputBase'
|
||||||
import Link from '@material-ui/core/Link';
|
import Link from '@material-ui/core/Link'
|
||||||
|
|
||||||
import LabelTwoTone from '@material-ui/icons/LabelTwoTone';
|
import LabelTwoTone from '@material-ui/icons/LabelTwoTone'
|
||||||
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
|
import DeleteOutlined from '@material-ui/icons/DeleteOutlined'
|
||||||
import MoreHorizIcon from '@material-ui/icons/MoreHoriz';
|
import MoreHorizIcon from '@material-ui/icons/MoreHoriz'
|
||||||
import StarRateRoundedIcon from '@material-ui/icons/StarRateRounded';
|
import StarRateRoundedIcon from '@material-ui/icons/StarRateRounded'
|
||||||
import SearchIcon from '@material-ui/icons/Search';
|
import SearchIcon from '@material-ui/icons/Search'
|
||||||
|
|
||||||
// Load fromNow pluggin
|
// Load fromNow pluggin
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
|
|
||||||
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
|
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
|
||||||
if (b[orderBy] < a[orderBy]) {
|
if (b[orderBy] < a[orderBy]) {
|
||||||
return -1;
|
return -1
|
||||||
}
|
}
|
||||||
if (b[orderBy] > a[orderBy]) {
|
if (b[orderBy] > a[orderBy]) {
|
||||||
return 1;
|
return 1
|
||||||
}
|
}
|
||||||
return 0;
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type Order = 'asc' | 'desc';
|
type Order = 'asc' | 'desc'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function getComparator<Key extends keyof any>(
|
function getComparator<Key extends keyof any>(
|
||||||
order: Order,
|
order: Order,
|
||||||
orderBy: Key,
|
orderBy: Key
|
||||||
): (a: { [key in Key]: number | string | boolean | number[] | undefined }, b: { [key in Key]: number | string | number[] | boolean }) => number {
|
): (
|
||||||
|
a: { [key in Key]: number | string | boolean | number[] | undefined },
|
||||||
|
b: { [key in Key]: number | string | number[] | boolean }
|
||||||
|
) => number {
|
||||||
return order === 'desc'
|
return order === 'desc'
|
||||||
? (a, b) => descendingComparator(a, b, orderBy)
|
? (a, b) => descendingComparator(a, b, orderBy)
|
||||||
: (a, b) => -descendingComparator(a, b, orderBy);
|
: (a, b) => -descendingComparator(a, b, orderBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
function stableSort<T>(array: T[], comparator: (a: T, b: T) => number) {
|
function stableSort<T>(array: T[], comparator: (a: T, b: T) => number) {
|
||||||
const stabilizedThis = array.map((el, index) => [el, index] as [T, number]);
|
const stabilizedThis = array.map((el, index) => [el, index] as [T, number])
|
||||||
stabilizedThis.sort((a, b) => {
|
stabilizedThis.sort((a, b) => {
|
||||||
const order = comparator(a[0], b[0]);
|
const order = comparator(a[0], b[0])
|
||||||
if (order !== 0) return order;
|
if (order !== 0) return order
|
||||||
return a[1] - b[1];
|
return a[1] - b[1]
|
||||||
});
|
})
|
||||||
return stabilizedThis.map((el) => el[0]);
|
return stabilizedThis.map((el) => el[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HeadCell {
|
interface HeadCell {
|
||||||
id: keyof MapInfo;
|
id: keyof MapInfo
|
||||||
label?: string;
|
label?: string
|
||||||
numeric: boolean;
|
numeric: boolean
|
||||||
style?: CSSProperties;
|
style?: CSSProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface EnhancedTableProps {
|
interface EnhancedTableProps {
|
||||||
classes: ReturnType<typeof useStyles>;
|
classes: ReturnType<typeof useStyles>
|
||||||
numSelected: number;
|
numSelected: number
|
||||||
onRequestSort: (event: React.MouseEvent<unknown>, property: keyof MapInfo) => void;
|
onRequestSort: (event: React.MouseEvent<unknown>, property: keyof MapInfo) => void
|
||||||
onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
order: Order;
|
order: Order
|
||||||
orderBy: string;
|
orderBy: string
|
||||||
rowCount: number;
|
rowCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function EnhancedTableHead(props: EnhancedTableProps) {
|
function EnhancedTableHead(props: EnhancedTableProps) {
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props;
|
const {
|
||||||
|
classes,
|
||||||
|
onSelectAllClick,
|
||||||
|
order,
|
||||||
|
orderBy,
|
||||||
|
numSelected,
|
||||||
|
rowCount,
|
||||||
|
onRequestSort,
|
||||||
|
} = props
|
||||||
|
|
||||||
const createSortHandler = (property: keyof MapInfo) => (event: React.MouseEvent<unknown>) => {
|
const createSortHandler = (property: keyof MapInfo) => (event: React.MouseEvent<unknown>) => {
|
||||||
onRequestSort(event, property);
|
onRequestSort(event, property)
|
||||||
};
|
}
|
||||||
|
|
||||||
const headCells: HeadCell[] = [
|
const headCells: HeadCell[] = [
|
||||||
{ id: 'title', numeric: false, label: intl.formatMessage({ id: 'map.name', defaultMessage: 'Name' }) },
|
{
|
||||||
|
id: 'title',
|
||||||
|
numeric: false,
|
||||||
|
label: intl.formatMessage({ id: 'map.name', defaultMessage: 'Name' }),
|
||||||
|
},
|
||||||
{ id: 'labels', numeric: false },
|
{ id: 'labels', numeric: false },
|
||||||
{ id: 'createdBy', numeric: false, label: intl.formatMessage({ id: 'map.creator', defaultMessage: 'Creator' }), style: { width: '70px', whiteSpace: 'nowrap' } },
|
{
|
||||||
{ id: 'lastModificationTime', numeric: true, label: intl.formatMessage({ id: 'map.last-update', defaultMessage: 'Last Update' }), style: { width: '70px', whiteSpace: 'nowrap' } }
|
id: 'createdBy',
|
||||||
];
|
numeric: false,
|
||||||
|
label: intl.formatMessage({ id: 'map.creator', defaultMessage: 'Creator' }),
|
||||||
|
style: { width: '70px', whiteSpace: 'nowrap' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'lastModificationTime',
|
||||||
|
numeric: true,
|
||||||
|
label: intl.formatMessage({ id: 'map.last-update', defaultMessage: 'Last Update' }),
|
||||||
|
style: { width: '70px', whiteSpace: 'nowrap' },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell padding='checkbox' key='select' style={{ width: '20px' }} className={classes.headerCell}>
|
<TableCell
|
||||||
|
padding="checkbox"
|
||||||
|
key="select"
|
||||||
|
style={{ width: '20px' }}
|
||||||
|
className={classes.headerCell}
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
indeterminate={numSelected > 0 && numSelected < rowCount}
|
indeterminate={numSelected > 0 && numSelected < rowCount}
|
||||||
checked={rowCount > 0 && numSelected === rowCount}
|
checked={rowCount > 0 && numSelected === rowCount}
|
||||||
onChange={onSelectAllClick}
|
onChange={onSelectAllClick}
|
||||||
size='small'
|
size="small"
|
||||||
inputProps={{ 'aria-label': 'select all desserts' }}
|
inputProps={{ 'aria-label': 'select all desserts' }}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell padding='checkbox' key='starred' className={classes.headerCell}></TableCell>
|
<TableCell
|
||||||
|
padding="checkbox"
|
||||||
|
key="starred"
|
||||||
|
className={classes.headerCell}
|
||||||
|
></TableCell>
|
||||||
|
|
||||||
{headCells.map((headCell) => {
|
{headCells.map((headCell) => {
|
||||||
return (<TableCell
|
return (
|
||||||
|
<TableCell
|
||||||
key={headCell.id}
|
key={headCell.id}
|
||||||
sortDirection={orderBy === headCell.id ? order : false}
|
sortDirection={orderBy === headCell.id ? order : false}
|
||||||
style={headCell.style}
|
style={headCell.style}
|
||||||
@ -130,28 +163,34 @@ function EnhancedTableHead(props: EnhancedTableProps) {
|
|||||||
<TableSortLabel
|
<TableSortLabel
|
||||||
active={orderBy === headCell.id}
|
active={orderBy === headCell.id}
|
||||||
direction={orderBy === headCell.id ? order : 'asc'}
|
direction={orderBy === headCell.id ? order : 'asc'}
|
||||||
onClick={createSortHandler(headCell.id)}>
|
onClick={createSortHandler(headCell.id)}
|
||||||
|
>
|
||||||
{headCell.label}
|
{headCell.label}
|
||||||
|
|
||||||
{orderBy === headCell.id && (
|
{orderBy === headCell.id && (
|
||||||
<span className={classes.visuallyHidden}>
|
<span className={classes.visuallyHidden}>
|
||||||
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
{order === 'desc'
|
||||||
|
? 'sorted descending'
|
||||||
|
: 'sorted ascending'}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</TableSortLabel>
|
</TableSortLabel>
|
||||||
</TableCell>)
|
</TableCell>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<TableCell padding='checkbox' key='action' className={classes.headerCell}></TableCell>
|
<TableCell
|
||||||
|
padding="checkbox"
|
||||||
|
key="action"
|
||||||
|
className={classes.headerCell}
|
||||||
|
></TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionPanelState = {
|
type ActionPanelState = {
|
||||||
el: HTMLElement | undefined,
|
el: HTMLElement | undefined
|
||||||
mapId: number
|
mapId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,191 +200,193 @@ interface MapsListProps {
|
|||||||
|
|
||||||
const mapsFilter = (filter: Filter, search: string): ((mapInfo: MapInfo) => boolean) => {
|
const mapsFilter = (filter: Filter, search: string): ((mapInfo: MapInfo) => boolean) => {
|
||||||
return (mapInfo: MapInfo) => {
|
return (mapInfo: MapInfo) => {
|
||||||
|
|
||||||
// Check for filter condition
|
// Check for filter condition
|
||||||
let result = false;
|
let result = false
|
||||||
switch (filter.type) {
|
switch (filter.type) {
|
||||||
case 'all':
|
case 'all':
|
||||||
result = true;
|
result = true
|
||||||
break;
|
break
|
||||||
case 'starred':
|
case 'starred':
|
||||||
result = mapInfo.starred;
|
result = mapInfo.starred
|
||||||
break;
|
break
|
||||||
case 'owned':
|
case 'owned':
|
||||||
result = mapInfo.role == 'owner';
|
result = mapInfo.role == 'owner'
|
||||||
break;
|
break
|
||||||
case 'shared':
|
case 'shared':
|
||||||
result = mapInfo.role != 'owner';
|
result = mapInfo.role != 'owner'
|
||||||
break;
|
break
|
||||||
case 'label':
|
case 'label':
|
||||||
result = !mapInfo.labels || mapInfo.labels.includes((filter as LabelFilter).label.id)
|
result =
|
||||||
break;
|
!mapInfo.labels || mapInfo.labels.includes((filter as LabelFilter).label.id)
|
||||||
|
break
|
||||||
case 'public':
|
case 'public':
|
||||||
result = mapInfo.isPublic;
|
result = mapInfo.isPublic
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
result = false;
|
result = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does it match search filter criteria...
|
// Does it match search filter criteria...
|
||||||
if (search && result) {
|
if (search && result) {
|
||||||
result = mapInfo.title.toLowerCase().indexOf(search.toLowerCase()) != -1;
|
result = mapInfo.title.toLowerCase().indexOf(search.toLowerCase()) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MapsList = (props: MapsListProps): React.ReactElement => {
|
export const MapsList = (props: MapsListProps): React.ReactElement => {
|
||||||
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 [filter, setFilter] = React.useState<Filter>({ type: 'all' })
|
||||||
|
|
||||||
const [orderBy, setOrderBy] = React.useState<keyof MapInfo>('lastModificationTime');
|
const [orderBy, setOrderBy] = React.useState<keyof MapInfo>('lastModificationTime')
|
||||||
const [selected, setSelected] = React.useState<number[]>([]);
|
const [selected, setSelected] = React.useState<number[]>([])
|
||||||
const [searchCondition, setSearchCondition] = React.useState<string>('');
|
const [searchCondition, setSearchCondition] = React.useState<string>('')
|
||||||
|
|
||||||
const [page, setPage] = React.useState(0);
|
const [page, setPage] = React.useState(0)
|
||||||
const [rowsPerPage, setRowsPerPage] = React.useState(10);
|
const [rowsPerPage, setRowsPerPage] = React.useState(10)
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
// Configure locale ...
|
// Configure locale ...
|
||||||
const account = fetchAccount();
|
const account = fetchAccount()
|
||||||
if (account) {
|
if (account) {
|
||||||
dayjs.locale(account.locale.code);
|
dayjs.locale(account.locale.code)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelected([]);
|
setSelected([])
|
||||||
setPage(0);
|
setPage(0)
|
||||||
setFilter(props.filter)
|
setFilter(props.filter)
|
||||||
}, [props.filter.type, (props.filter as LabelFilter).label]);
|
}, [props.filter.type, (props.filter as LabelFilter).label])
|
||||||
|
|
||||||
|
|
||||||
const { isLoading, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
const { isLoading, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
||||||
return client.fetchAllMaps();
|
return client.fetchAllMaps()
|
||||||
});
|
})
|
||||||
const mapsInfo: MapInfo[] = data ? data.filter(mapsFilter(filter, searchCondition)) : [];
|
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
|
||||||
|
)
|
||||||
type ActiveDialog = {
|
type ActiveDialog = {
|
||||||
actionType: ActionType;
|
actionType: ActionType
|
||||||
mapsId: number[];
|
mapsId: number[]
|
||||||
};
|
}
|
||||||
|
|
||||||
const [activeDialog, setActiveDialog] = React.useState<ActiveDialog | undefined>(undefined);
|
const [activeDialog, setActiveDialog] = React.useState<ActiveDialog | undefined>(undefined)
|
||||||
const handleRequestSort = (event: React.MouseEvent<unknown>, property: keyof MapInfo) => {
|
const handleRequestSort = (event: React.MouseEvent<unknown>, property: keyof MapInfo) => {
|
||||||
const isAsc = orderBy === property && order === 'asc';
|
const isAsc = orderBy === property && order === 'asc'
|
||||||
setOrder(isAsc ? 'desc' : 'asc');
|
setOrder(isAsc ? 'desc' : 'asc')
|
||||||
setOrderBy(property);
|
setOrderBy(property)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
if (event.target.checked) {
|
if (event.target.checked) {
|
||||||
const newSelecteds = mapsInfo.map((n) => n.id);
|
const newSelecteds = mapsInfo.map((n) => n.id)
|
||||||
setSelected(newSelecteds);
|
setSelected(newSelecteds)
|
||||||
return;
|
return
|
||||||
|
}
|
||||||
|
setSelected([])
|
||||||
}
|
}
|
||||||
setSelected([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRowClick = (event: React.MouseEvent<unknown>, id: number): void => {
|
const handleRowClick = (event: React.MouseEvent<unknown>, id: number): void => {
|
||||||
const selectedIndex = selected.indexOf(id);
|
const selectedIndex = selected.indexOf(id)
|
||||||
let newSelected: number[] = [];
|
let newSelected: number[] = []
|
||||||
|
|
||||||
if (selectedIndex === -1) {
|
if (selectedIndex === -1) {
|
||||||
newSelected = newSelected.concat(selected, id);
|
newSelected = newSelected.concat(selected, id)
|
||||||
} else if (selectedIndex === 0) {
|
} else if (selectedIndex === 0) {
|
||||||
newSelected = newSelected.concat(selected.slice(1));
|
newSelected = newSelected.concat(selected.slice(1))
|
||||||
} else if (selectedIndex === selected.length - 1) {
|
} else if (selectedIndex === selected.length - 1) {
|
||||||
newSelected = newSelected.concat(selected.slice(0, -1));
|
newSelected = newSelected.concat(selected.slice(0, -1))
|
||||||
} else if (selectedIndex > 0) {
|
} else if (selectedIndex > 0) {
|
||||||
newSelected = newSelected.concat(
|
newSelected = newSelected.concat(
|
||||||
selected.slice(0, selectedIndex),
|
selected.slice(0, selectedIndex),
|
||||||
selected.slice(selectedIndex + 1),
|
selected.slice(selectedIndex + 1)
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelected(newSelected);
|
setSelected(newSelected)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleChangePage = (event: unknown, newPage: number) => {
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
setPage(newPage);
|
setPage(newPage)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setRowsPerPage(parseInt(event.target.value, 10));
|
setRowsPerPage(parseInt(event.target.value, 10))
|
||||||
setPage(0);
|
setPage(0)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleActionClick = (mapId: number): ((event) => void) => {
|
const handleActionClick = (mapId: number): ((event) => void) => {
|
||||||
return (event): void => {
|
return (event): void => {
|
||||||
setActiveRowAction(
|
setActiveRowAction({
|
||||||
{
|
|
||||||
mapId: mapId,
|
mapId: mapId,
|
||||||
el: event.currentTarget
|
el: event.currentTarget,
|
||||||
|
})
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
|
||||||
event.stopPropagation();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const starredMultation = useMutation<void, ErrorInfo, number>(
|
||||||
const starredMultation = useMutation<void, ErrorInfo, number>((id: number) => {
|
(id: number) => {
|
||||||
const map = mapsInfo.find(m => m.id == id);
|
const map = mapsInfo.find((m) => m.id == id)
|
||||||
return client.updateStarred(id, !map?.starred);
|
return client.updateStarred(id, !map?.starred)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries('maps');
|
queryClient.invalidateQueries('maps')
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
// setError(error);
|
// setError(error);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleStarred = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, id: number) => {
|
const handleStarred = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, id: number) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
starredMultation.mutate(id);
|
starredMultation.mutate(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleActionMenuClose = (action: ActionType): void => {
|
const handleActionMenuClose = (action: ActionType): void => {
|
||||||
if (action) {
|
if (action) {
|
||||||
const mapId = activeRowAction?.mapId;
|
const mapId = activeRowAction?.mapId
|
||||||
|
|
||||||
setActiveDialog({
|
setActiveDialog({
|
||||||
actionType: action as ActionType,
|
actionType: action as ActionType,
|
||||||
mapsId: [mapId] as number[]
|
mapsId: [mapId] as number[],
|
||||||
});
|
})
|
||||||
|
}
|
||||||
|
setActiveRowAction(undefined)
|
||||||
}
|
}
|
||||||
setActiveRowAction(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleOnSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearchCondition(e.target.value);
|
setSearchCondition(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteClick = () => {
|
const handleDeleteClick = () => {
|
||||||
setActiveDialog({
|
setActiveDialog({
|
||||||
actionType: 'delete',
|
actionType: 'delete',
|
||||||
mapsId: selected
|
mapsId: selected,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSelected = (id: number) => selected.indexOf(id) !== -1;
|
const isSelected = (id: number) => selected.indexOf(id) !== -1
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<ActionChooser anchor={activeRowAction?.el} onClose={handleActionMenuClose} mapId={activeRowAction?.mapId} />
|
<ActionChooser
|
||||||
|
anchor={activeRowAction?.el}
|
||||||
|
onClose={handleActionMenuClose}
|
||||||
|
mapId={activeRowAction?.mapId}
|
||||||
|
/>
|
||||||
|
|
||||||
<Paper className={classes.paper} elevation={0}>
|
<Paper className={classes.paper} elevation={0}>
|
||||||
<Toolbar className={classes.toolbar} variant="dense">
|
<Toolbar className={classes.toolbar} variant="dense">
|
||||||
|
|
||||||
<div className={classes.toolbarActions}>
|
<div className={classes.toolbarActions}>
|
||||||
{selected.length > 0 &&
|
{selected.length > 0 && (
|
||||||
<Tooltip arrow={true} title="Delete selected">
|
<Tooltip arrow={true} title="Delete selected">
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
@ -354,32 +395,36 @@ export const MapsList = (props: MapsListProps): React.ReactElement => {
|
|||||||
type="button"
|
type="button"
|
||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
onClick={handleDeleteClick}
|
onClick={handleDeleteClick}
|
||||||
startIcon={<DeleteOutlined
|
startIcon={<DeleteOutlined />}
|
||||||
/>}>
|
>
|
||||||
<FormattedMessage id="action.delete" defaultMessage="Delete" />
|
<FormattedMessage id="action.delete" defaultMessage="Delete" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{selected.length > 0 &&
|
{selected.length > 0 && (
|
||||||
<Tooltip arrow={true} title="Add label to selected">
|
<Tooltip arrow={true} title="Add label to selected">
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
type="button"
|
type="button"
|
||||||
style={{ marginLeft: "10px" }}
|
style={{ marginLeft: '10px' }}
|
||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
startIcon={<LabelTwoTone />}>
|
startIcon={<LabelTwoTone />}
|
||||||
<FormattedMessage id="action.label" defaultMessage="Add Label" />
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="action.label"
|
||||||
|
defaultMessage="Add Label"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={classes.toolbarListActions}>
|
<div className={classes.toolbarListActions}>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
style={{ float: 'right', border: "0", paddingBottom: "5px" }}
|
style={{ float: 'right', border: '0', paddingBottom: '5px' }}
|
||||||
count={mapsInfo.length}
|
count={mapsInfo.length}
|
||||||
rowsPerPageOptions={[]}
|
rowsPerPageOptions={[]}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
@ -403,15 +448,10 @@ export const MapsList = (props: MapsListProps): React.ReactElement => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table
|
<Table className={classes.table} size="small" stickyHeader>
|
||||||
className={classes.table}
|
|
||||||
size="small"
|
|
||||||
stickyHeader
|
|
||||||
>
|
|
||||||
<EnhancedTableHead
|
<EnhancedTableHead
|
||||||
classes={classes}
|
classes={classes}
|
||||||
numSelected={selected.length}
|
numSelected={selected.length}
|
||||||
@ -424,14 +464,24 @@ export const MapsList = (props: MapsListProps): React.ReactElement => {
|
|||||||
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<TableRow><TableCell colSpan={6}>Loading ...</TableCell></TableRow>) :
|
<TableRow>
|
||||||
(mapsInfo.length == 0 ?
|
<TableCell colSpan={6}>Loading ...</TableCell>
|
||||||
(<TableRow><TableCell colSpan={6} style={{ textAlign: 'center' }}><FormattedMessage id="maps.empty-result" defaultMessage="No matching record found with the current filter criteria." /></TableCell></TableRow>) :
|
</TableRow>
|
||||||
|
) : mapsInfo.length == 0 ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={6} style={{ textAlign: 'center' }}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="maps.empty-result"
|
||||||
|
defaultMessage="No matching record found with the current filter criteria."
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : (
|
||||||
stableSort(mapsInfo, getComparator(order, orderBy))
|
stableSort(mapsInfo, getComparator(order, orderBy))
|
||||||
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||||
.map((row: MapInfo) => {
|
.map((row: MapInfo) => {
|
||||||
const isItemSelected = isSelected(row.id);
|
const isItemSelected = isSelected(row.id)
|
||||||
const labelId = row.id;
|
const labelId = row.id
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
@ -442,30 +492,57 @@ export const MapsList = (props: MapsListProps): React.ReactElement => {
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
key={row.id}
|
key={row.id}
|
||||||
selected={isItemSelected}
|
selected={isItemSelected}
|
||||||
style={{ border: "0" }}
|
style={{ border: '0' }}
|
||||||
>
|
>
|
||||||
<TableCell
|
<TableCell
|
||||||
padding="checkbox"
|
padding="checkbox"
|
||||||
className={classes.bodyCell}>
|
className={classes.bodyCell}
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isItemSelected}
|
checked={isItemSelected}
|
||||||
inputProps={{ 'aria-labelledby': String(labelId) }}
|
inputProps={{
|
||||||
size="small" />
|
'aria-labelledby': String(labelId),
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell
|
<TableCell
|
||||||
padding="checkbox"
|
padding="checkbox"
|
||||||
className={classes.bodyCell}>
|
className={classes.bodyCell}
|
||||||
|
>
|
||||||
<Tooltip arrow={true} title="Starred">
|
<Tooltip arrow={true} title="Starred">
|
||||||
<IconButton aria-label="Starred" size="small" onClick={(e) => handleStarred(e, row.id)}>
|
<IconButton
|
||||||
<StarRateRoundedIcon color="action" style={{ color: row.starred ? 'yellow' : 'gray' }} />
|
aria-label="Starred"
|
||||||
|
size="small"
|
||||||
|
onClick={(e) =>
|
||||||
|
handleStarred(e, row.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StarRateRoundedIcon
|
||||||
|
color="action"
|
||||||
|
style={{
|
||||||
|
color: row.starred
|
||||||
|
? 'yellow'
|
||||||
|
: 'gray',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell className={classes.bodyCell}>
|
<TableCell className={classes.bodyCell}>
|
||||||
<Tooltip arrow={true} title="Open for edition" placement="bottom-start">
|
<Tooltip
|
||||||
<Link href={`/c/maps/${row.id}/edit`} color="textPrimary" underline="always" onClick={(e) => e.stopPropagation()}>
|
arrow={true}
|
||||||
|
title="Open for edition"
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href={`/c/maps/${row.id}/edit`}
|
||||||
|
color="textPrimary"
|
||||||
|
underline="always"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
{row.title}
|
{row.title}
|
||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -480,31 +557,54 @@ export const MapsList = (props: MapsListProps): React.ReactElement => {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell className={classes.bodyCell}>
|
<TableCell className={classes.bodyCell}>
|
||||||
<Tooltip arrow={true} title={
|
<Tooltip
|
||||||
`Modified by ${row.lastModificationBy} on ${dayjs(row.lastModificationTime).format("lll")}`
|
arrow={true}
|
||||||
} placement="bottom-start">
|
title={`Modified by ${
|
||||||
<span>{dayjs(row.lastModificationTime).fromNow()}</span>
|
row.lastModificationBy
|
||||||
|
} on ${dayjs(
|
||||||
|
row.lastModificationTime
|
||||||
|
).format('lll')}`}
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{dayjs(
|
||||||
|
row.lastModificationTime
|
||||||
|
).fromNow()}
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell className={classes.bodyCell}>
|
<TableCell className={classes.bodyCell}>
|
||||||
<Tooltip arrow={true} title={intl.formatMessage({ id: 'map.more-actions', defaultMessage: 'More Actions' })}>
|
<Tooltip
|
||||||
<IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}>
|
arrow={true}
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'map.more-actions',
|
||||||
|
defaultMessage: 'More Actions',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
aria-label="Others"
|
||||||
|
size="small"
|
||||||
|
onClick={handleActionClick(row.id)}
|
||||||
|
>
|
||||||
<MoreHorizIcon color="action" />
|
<MoreHorizIcon color="action" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
)
|
||||||
}))}
|
})
|
||||||
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<ActionDispatcher action={activeDialog?.actionType} onClose={() => setActiveDialog(undefined)} mapsId={activeDialog ? activeDialog.mapsId : []} />
|
<ActionDispatcher
|
||||||
</div >
|
action={activeDialog?.actionType}
|
||||||
);
|
onClose={() => setActiveDialog(undefined)}
|
||||||
|
mapsId={activeDialog ? activeDialog.mapsId : []}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { fade } from "@material-ui/core/styles";
|
import { fade } from '@material-ui/core/styles'
|
||||||
import { Theme } from "@material-ui/core/styles/createMuiTheme";
|
import { Theme } from '@material-ui/core/styles/createMuiTheme'
|
||||||
import createStyles from "@material-ui/core/styles/createStyles";
|
import createStyles from '@material-ui/core/styles/createStyles'
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
import makeStyles from '@material-ui/core/styles/makeStyles'
|
||||||
|
|
||||||
export const useStyles = makeStyles((theme: Theme) =>
|
export const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@ -15,11 +15,10 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
table: {
|
table: {
|
||||||
minWidth: 750,
|
minWidth: 750,
|
||||||
'& tr:nth-child(even)': {
|
'& tr:nth-child(even)': {
|
||||||
background: 'white'
|
background: 'white',
|
||||||
},
|
},
|
||||||
'& tr:nth-child(odd)':
|
'& tr:nth-child(odd)': {
|
||||||
{
|
background: 'rgba(221, 221, 221, 0.35)',
|
||||||
background: 'rgba(221, 221, 221, 0.35)'
|
|
||||||
},
|
},
|
||||||
// '&:hover tr': {
|
// '&:hover tr': {
|
||||||
// backgroundColor: 'rgba(150, 150, 150, 0.7)',
|
// backgroundColor: 'rgba(150, 150, 150, 0.7)',
|
||||||
@ -29,10 +28,10 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
background: 'white',
|
background: 'white',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: 'rgba(0, 0, 0, 0.44)',
|
color: 'rgba(0, 0, 0, 0.44)',
|
||||||
border: 0
|
border: 0,
|
||||||
},
|
},
|
||||||
bodyCell: {
|
bodyCell: {
|
||||||
border: '0px'
|
border: '0px',
|
||||||
},
|
},
|
||||||
visuallyHidden: {
|
visuallyHidden: {
|
||||||
border: 0,
|
border: 0,
|
||||||
@ -49,13 +48,13 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
borderBottom: '1px solid #cccccc',
|
borderBottom: '1px solid #cccccc',
|
||||||
padding: '0',
|
padding: '0',
|
||||||
marging: '0'
|
marging: '0',
|
||||||
},
|
},
|
||||||
toolbarActions: {
|
toolbarActions: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
toolbarListActions: {
|
toolbarListActions: {
|
||||||
flexGrow: 1
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
borderRadius: 9,
|
borderRadius: 9,
|
||||||
@ -69,7 +68,7 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
},
|
},
|
||||||
float: 'right'
|
float: 'right',
|
||||||
},
|
},
|
||||||
searchIcon: {
|
searchIcon: {
|
||||||
padding: '6px 0 0 5px',
|
padding: '6px 0 0 5px',
|
||||||
@ -81,8 +80,9 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
},
|
},
|
||||||
searchInputRoot: {
|
searchInputRoot: {
|
||||||
color: 'inherit',
|
color: 'inherit',
|
||||||
}, toolbalLeft: {
|
},
|
||||||
float: 'right'
|
toolbalLeft: {
|
||||||
|
float: 'right',
|
||||||
},
|
},
|
||||||
searchInputInput: {
|
searchInputInput: {
|
||||||
// padding: theme.spacing(1, 1, 1, 0),
|
// padding: theme.spacing(1, 1, 1, 0),
|
||||||
@ -98,6 +98,6 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
width: '20ch',
|
width: '20ch',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
|
@ -1,47 +1,48 @@
|
|||||||
import React from "react";
|
import React from 'react'
|
||||||
|
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
import Tooltip from '@material-ui/core/Tooltip'
|
||||||
import PersonSharpIcon from '@material-ui/icons/PersonSharp';
|
import PersonSharpIcon from '@material-ui/icons/PersonSharp'
|
||||||
import EditSharpIcon from '@material-ui/icons/EditSharp';
|
import EditSharpIcon from '@material-ui/icons/EditSharp'
|
||||||
import VisibilitySharpIcon from '@material-ui/icons/VisibilitySharp';
|
import VisibilitySharpIcon from '@material-ui/icons/VisibilitySharp'
|
||||||
|
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from 'react-intl'
|
||||||
import { Role } from "../../../classes/client";
|
import { Role } from '../../../classes/client'
|
||||||
|
|
||||||
type RoleIconProps = {
|
type RoleIconProps = {
|
||||||
role: Role;
|
role: Role
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoleIcon = ({ role }: RoleIconProps): React.ReactElement => {
|
|
||||||
|
|
||||||
|
const RoleIcon = ({ role }: RoleIconProps): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{role == 'owner' &&
|
{role == 'owner' && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={<FormattedMessage id="role.owner" defaultMessage="Onwer" />}
|
title={<FormattedMessage id="role.owner" defaultMessage="Onwer" />}
|
||||||
arrow={true}
|
arrow={true}
|
||||||
>
|
>
|
||||||
<PersonSharpIcon />
|
<PersonSharpIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{ role == 'editor' &&
|
{role == 'editor' && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={<FormattedMessage id="role.editor" defaultMessage="Editor" />}
|
title={<FormattedMessage id="role.editor" defaultMessage="Editor" />}
|
||||||
arrow={true}>
|
arrow={true}
|
||||||
|
>
|
||||||
<EditSharpIcon />
|
<EditSharpIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{role == 'viewer' &&
|
{role == 'viewer' && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={<FormattedMessage id="role.viewer" defaultMessage="Viewer" />}
|
title={<FormattedMessage id="role.viewer" defaultMessage="Viewer" />}
|
||||||
arrow={true}>
|
arrow={true}
|
||||||
|
>
|
||||||
<VisibilitySharpIcon />
|
<VisibilitySharpIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
)}
|
||||||
</span>)
|
</span>
|
||||||
};
|
)
|
||||||
|
}
|
||||||
export default RoleIcon;
|
|
||||||
|
|
||||||
|
export default RoleIcon
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Theme } from "@material-ui/core/styles/createMuiTheme";
|
import { Theme } from '@material-ui/core/styles/createMuiTheme'
|
||||||
import createStyles from "@material-ui/core/styles/createStyles";
|
import createStyles from '@material-ui/core/styles/createStyles'
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
import makeStyles from '@material-ui/core/styles/makeStyles'
|
||||||
|
|
||||||
const drawerWidth = 300;
|
const drawerWidth = 300
|
||||||
|
|
||||||
export const useStyles = makeStyles((theme: Theme) =>
|
export const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@ -16,7 +16,6 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
duration: theme.transitions.duration.leavingScreen,
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
}),
|
}),
|
||||||
background: '#ffffff',
|
background: '#ffffff',
|
||||||
|
|
||||||
},
|
},
|
||||||
appBarShift: {
|
appBarShift: {
|
||||||
marginLeft: drawerWidth,
|
marginLeft: drawerWidth,
|
||||||
@ -36,7 +35,7 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
flexGrow: 10,
|
flexGrow: 10,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
minWidth: '200px'
|
minWidth: '200px',
|
||||||
},
|
},
|
||||||
drawer: {
|
drawer: {
|
||||||
width: drawerWidth,
|
width: drawerWidth,
|
||||||
@ -54,11 +53,11 @@ export const useStyles = makeStyles((theme: Theme) =>
|
|||||||
toolbar: {
|
toolbar: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
minHeight: '44px'
|
minHeight: '44px',
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3),
|
||||||
}
|
},
|
||||||
}),
|
})
|
||||||
);
|
)
|
||||||
|
@ -1,61 +1,60 @@
|
|||||||
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 ReCAPTCHA from 'react-google-recaptcha';
|
import ReCAPTCHA from 'react-google-recaptcha'
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom'
|
||||||
import Client, { ErrorInfo } from '../../classes/client';
|
import Client, { ErrorInfo } from '../../classes/client'
|
||||||
import FormContainer from '../layout/form-container';
|
import FormContainer from '../layout/form-container'
|
||||||
|
|
||||||
import Header from '../layout/header';
|
import Header from '../layout/header'
|
||||||
import Footer from '../layout/footer';
|
import Footer from '../layout/footer'
|
||||||
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux'
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query'
|
||||||
import { activeInstance } from '../../redux/clientSlice';
|
import { activeInstance } from '../../redux/clientSlice'
|
||||||
import Input from '../form/input';
|
import Input from '../form/input'
|
||||||
import GlobalError from '../form/global-error';
|
import GlobalError from '../form/global-error'
|
||||||
import SubmitButton from '../form/submit-button';
|
import SubmitButton from '../form/submit-button'
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography'
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
import FormControl from '@material-ui/core/FormControl'
|
||||||
|
|
||||||
export type Model = {
|
export type Model = {
|
||||||
email: string;
|
email: string
|
||||||
lastname: string;
|
lastname: string
|
||||||
firstname: string;
|
firstname: string
|
||||||
password: string;
|
password: string
|
||||||
recaptcha: string;
|
recaptcha: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModel: Model = { email: '', lastname: '', firstname: '', password: '', recaptcha: '' };
|
const defaultModel: Model = { email: '', lastname: '', firstname: '', password: '', recaptcha: '' }
|
||||||
const RegistrationForm = () => {
|
const RegistrationForm = () => {
|
||||||
const [model, setModel] = useState<Model>(defaultModel);
|
const [model, setModel] = useState<Model>(defaultModel)
|
||||||
const [error, setError] = useState<ErrorInfo>();
|
const [error, setError] = useState<ErrorInfo>()
|
||||||
const history = useHistory();
|
const history = useHistory()
|
||||||
const intl = useIntl();
|
const intl = useIntl()
|
||||||
|
|
||||||
const Client: Client = useSelector(activeInstance);
|
const Client: Client = useSelector(activeInstance)
|
||||||
const mutation = useMutation<void, ErrorInfo, Model>(
|
const mutation = useMutation<void, ErrorInfo, Model>(
|
||||||
(model: Model) => Client.registerNewUser({ ...model }),
|
(model: Model) => Client.registerNewUser({ ...model }),
|
||||||
{
|
{
|
||||||
onSuccess: () => history.push("/c/registration-success"),
|
onSuccess: () => history.push('/c/registration-success'),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
mutation.mutate(model);
|
mutation.mutate(model)
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const name = event.target.name;
|
|
||||||
const value = event.target.value;
|
|
||||||
setModel({ ...model, [name as keyof Model]: value });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
const name = event.target.name
|
||||||
|
const value = event.target.value
|
||||||
|
setModel({ ...model, [name as keyof Model]: value })
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
@ -64,59 +63,105 @@ const RegistrationForm = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography paragraph>
|
<Typography paragraph>
|
||||||
<FormattedMessage id="registration.desc" defaultMessage="Signing up is free and just take a moment " />
|
<FormattedMessage
|
||||||
|
id="registration.desc"
|
||||||
|
defaultMessage="Signing up is free and just take a moment "
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
|
|
||||||
<form onSubmit={handleOnSubmit}>
|
<form onSubmit={handleOnSubmit}>
|
||||||
<GlobalError error={error} />
|
<GlobalError error={error} />
|
||||||
|
|
||||||
<Input name="email" type="email" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.email", defaultMessage: "Email" })}
|
<Input
|
||||||
autoComplete="email" error={error} />
|
name="email"
|
||||||
|
type="email"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'registration.email',
|
||||||
|
defaultMessage: 'Email',
|
||||||
|
})}
|
||||||
|
autoComplete="email"
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="firstname" type="text" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.firstname", defaultMessage: "First Name" })}
|
<Input
|
||||||
autoComplete="given-name" error={error} />
|
name="firstname"
|
||||||
|
type="text"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'registration.firstname',
|
||||||
|
defaultMessage: 'First Name',
|
||||||
|
})}
|
||||||
|
autoComplete="given-name"
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="lastname" type="text" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.lastname", defaultMessage: "Last Name" })}
|
<Input
|
||||||
autoComplete="family-name" error={error} />
|
name="lastname"
|
||||||
|
type="text"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'registration.lastname',
|
||||||
|
defaultMessage: 'Last Name',
|
||||||
|
})}
|
||||||
|
autoComplete="family-name"
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input name="password" type="password" onChange={handleOnChange} label={intl.formatMessage({ id: "registration.password", defaultMessage: "Password" })}
|
<Input
|
||||||
autoComplete="new-password" error={error} />
|
name="password"
|
||||||
|
type="password"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'registration.password',
|
||||||
|
defaultMessage: 'Password',
|
||||||
|
})}
|
||||||
|
autoComplete="new-password"
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
|
||||||
<div style={{ width: '330px', padding: '5px 0px 5px 20px' }}>
|
<div style={{ width: '330px', padding: '5px 0px 5px 20px' }}>
|
||||||
<ReCAPTCHA
|
<ReCAPTCHA
|
||||||
sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
|
sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
|
||||||
onChange={(value: string) => { model.recaptcha = value; setModel(model) }} />
|
onChange={(value: string) => {
|
||||||
|
model.recaptcha = value
|
||||||
|
setModel(model)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ fontSize: "12px", padding: "10px 0px" }}>
|
<div style={{ fontSize: '12px', padding: '10px 0px' }}>
|
||||||
<FormattedMessage id="registration.termandconditions" defaultMessage="Terms of Client: Please check the WiseMapping Account information you've entered above, and review the Terms of Client here. By clicking on 'Register' below you are agreeing to the Terms of Client above and the Privacy Policy" />
|
<FormattedMessage
|
||||||
|
id="registration.termandconditions"
|
||||||
|
defaultMessage="Terms of Client: Please check the WiseMapping Account information you've entered above, and review the Terms of Client here. By clicking on 'Register' below you are agreeing to the Terms of Client above and the Privacy Policy"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SubmitButton value={intl.formatMessage({ id: "registration.register", defaultMessage: "Register" })} />
|
<SubmitButton
|
||||||
|
value={intl.formatMessage({
|
||||||
|
id: 'registration.register',
|
||||||
|
defaultMessage: 'Register',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegistationPage = (): React.ReactElement => {
|
const RegistationPage = (): React.ReactElement => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Registration | WiseMapping';
|
document.title = 'Registration | WiseMapping'
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type='only-signin' />
|
<Header type="only-signin" />
|
||||||
<RegistrationForm />
|
<RegistrationForm />
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RegistationPage;
|
export default RegistationPage
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,41 +1,49 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react'
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl'
|
||||||
import FormContainer from '../layout/form-container';
|
import FormContainer from '../layout/form-container'
|
||||||
import Header from '../layout/header';
|
import Header from '../layout/header'
|
||||||
import Footer from '../layout/footer';
|
import Footer from '../layout/footer'
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom'
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
|
|
||||||
|
|
||||||
const RegistrationSuccessPage = (): React.ReactElement => {
|
const RegistrationSuccessPage = (): React.ReactElement => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Reset Password | WiseMapping';
|
document.title = 'Reset Password | WiseMapping'
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type='none' />
|
<Header type="none" />
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
<FormattedMessage id="resetpassword.success.title" defaultMessage="Your account has been created successfully" />
|
<FormattedMessage
|
||||||
|
id="resetpassword.success.title"
|
||||||
|
defaultMessage="Your account has been created successfully"
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography paragraph>
|
<Typography paragraph>
|
||||||
<FormattedMessage id="registration.success.desc" defaultMessage="Click 'Sign In' button below and start creating mind maps." />
|
<FormattedMessage
|
||||||
|
id="registration.success.desc"
|
||||||
|
defaultMessage="Click 'Sign In' button below and start creating mind maps."
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
<Button
|
||||||
<Button color="primary" size="medium" variant="contained" component={RouterLink} to="/c/login" disableElevation={true}>
|
color="primary"
|
||||||
|
size="medium"
|
||||||
|
variant="contained"
|
||||||
|
component={RouterLink}
|
||||||
|
to="/c/login"
|
||||||
|
disableElevation={true}
|
||||||
|
>
|
||||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RegistrationSuccessPage;
|
export default RegistrationSuccessPage
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom'
|
||||||
import App from './app';
|
import App from './app'
|
||||||
|
|
||||||
async function bootstrapApplication() {
|
async function bootstrapApplication() {
|
||||||
ReactDOM.render(
|
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement)
|
||||||
<App />,
|
|
||||||
document.getElementById('root') as HTMLElement
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrapApplication()
|
bootstrapApplication()
|
||||||
|
|
@ -1,105 +1,101 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit'
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query'
|
||||||
import Client, { AccountInfo, ErrorInfo, MapInfo } from '../classes/client';
|
import Client, { AccountInfo, ErrorInfo, MapInfo } from '../classes/client'
|
||||||
import MockClient from '../classes/client/mock-client';
|
import MockClient from '../classes/client/mock-client'
|
||||||
import RestClient from '../classes/client/rest-client';
|
import RestClient from '../classes/client/rest-client'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
|
||||||
interface ConfigInfo {
|
interface ConfigInfo {
|
||||||
apiBaseUrl: string
|
apiBaseUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
class RutimeConfig {
|
class RutimeConfig {
|
||||||
private config: ConfigInfo;
|
private config: ConfigInfo
|
||||||
load() {
|
load() {
|
||||||
|
|
||||||
// Config can be inserted in the html page to define the global properties ...
|
// Config can be inserted in the html page to define the global properties ...
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
this.config = (window as any).serverconfig;
|
this.config = (window as any).serverconfig
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
buildClient(): Client {
|
buildClient(): Client {
|
||||||
let result: Client;
|
let result: Client
|
||||||
if (this.config) {
|
if (this.config) {
|
||||||
result = new RestClient(this.config.apiBaseUrl, () => { sessionExpired() });
|
result = new RestClient(this.config.apiBaseUrl, () => {
|
||||||
console.log("Service using rest client. " + JSON.stringify(this.config))
|
sessionExpired()
|
||||||
|
})
|
||||||
|
console.log('Service using rest client. ' + JSON.stringify(this.config))
|
||||||
} else {
|
} else {
|
||||||
console.log("Warning:Service using mockservice client")
|
console.log('Warning:Service using mockservice client')
|
||||||
result = new MockClient();
|
result = new MockClient()
|
||||||
}
|
}
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClientStatus {
|
export interface ClientStatus {
|
||||||
state: 'healthy' | 'session-expired';
|
state: 'healthy' | 'session-expired'
|
||||||
msg?: string
|
msg?: string
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClientState {
|
export interface ClientState {
|
||||||
instance: Client;
|
instance: Client
|
||||||
status: ClientStatus;
|
status: ClientStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: ClientState = {
|
const initialState: ClientState = {
|
||||||
instance: new RutimeConfig().load().buildClient(),
|
instance: new RutimeConfig().load().buildClient(),
|
||||||
status: { state: 'healthy' }
|
status: { state: 'healthy' },
|
||||||
};
|
}
|
||||||
|
|
||||||
export const clientSlice = createSlice({
|
export const clientSlice = createSlice({
|
||||||
name: "client",
|
name: 'client',
|
||||||
initialState: initialState,
|
initialState: initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
sessionExpired(state) {
|
sessionExpired(state) {
|
||||||
state.status = { state: 'session-expired', msg: 'Sessions has expired. You need to login again' }
|
state.status = {
|
||||||
|
state: 'session-expired',
|
||||||
|
msg: 'Sessions has expired. You need to login again',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
})
|
||||||
|
|
||||||
type MapLoadResult = {
|
type MapLoadResult = {
|
||||||
isLoading: boolean,
|
isLoading: boolean
|
||||||
error: ErrorInfo | null,
|
error: ErrorInfo | null
|
||||||
map: MapInfo | null
|
map: MapInfo | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchMapById = (id: number): MapLoadResult => {
|
export const fetchMapById = (id: number): MapLoadResult => {
|
||||||
|
const client: Client = useSelector(activeInstance)
|
||||||
const client: Client = useSelector(activeInstance);
|
|
||||||
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
||||||
return client.fetchAllMaps();
|
return client.fetchAllMaps()
|
||||||
});
|
})
|
||||||
|
|
||||||
const result = data?.find(m => m.id == id);
|
const result = data?.find((m) => m.id == id)
|
||||||
const map = result || null;
|
const map = result || null
|
||||||
return { isLoading: isLoading, error: error, map: map };
|
return { isLoading: isLoading, error: error, map: map }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const fetchAccount = (): AccountInfo | undefined => {
|
export const fetchAccount = (): AccountInfo | undefined => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance)
|
||||||
const { data } = useQuery<unknown, ErrorInfo, AccountInfo>('account', () => {
|
const { data } = useQuery<unknown, ErrorInfo, AccountInfo>('account', () => {
|
||||||
return client.fetchAccountInfo();
|
return client.fetchAccountInfo()
|
||||||
});
|
})
|
||||||
return data;
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const activeInstance = (state: any): Client => {
|
export const activeInstance = (state: any): Client => {
|
||||||
return state.client.instance;
|
return state.client.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const activeInstanceStatus = (state: any): ClientStatus => {
|
export const activeInstanceStatus = (state: any): ClientStatus => {
|
||||||
return state.client.status;
|
return state.client.status
|
||||||
}
|
}
|
||||||
|
|
||||||
export const { sessionExpired } = clientSlice.actions;
|
export const { sessionExpired } = clientSlice.actions
|
||||||
export default clientSlice.reducer;
|
export default clientSlice.reducer
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
import { configureStore } from '@reduxjs/toolkit';
|
import clientReducer from './clientSlice'
|
||||||
import clientReducer from './clientSlice';
|
|
||||||
|
|
||||||
// Create Service object...
|
// Create Service object...
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
client: clientReducer
|
client: clientReducer,
|
||||||
}
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
export default store;
|
export default store
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import createMuiTheme from "@material-ui/core/styles/createMuiTheme";
|
import createMuiTheme from '@material-ui/core/styles/createMuiTheme'
|
||||||
|
|
||||||
const theme = createMuiTheme({
|
const theme = createMuiTheme({
|
||||||
overrides: {
|
overrides: {
|
||||||
@ -19,13 +19,13 @@ const theme = createMuiTheme({
|
|||||||
},
|
},
|
||||||
'&:hover:not($disabled):not($focused):not($error) $notchedOutline': {
|
'&:hover:not($disabled):not($focused):not($error) $notchedOutline': {
|
||||||
borderColor: '#f9a826',
|
borderColor: '#f9a826',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiInputLabel: {
|
MuiInputLabel: {
|
||||||
root: {
|
root: {
|
||||||
color: '#f9a826'
|
color: '#f9a826',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
MuiButton: {
|
MuiButton: {
|
||||||
root: {
|
root: {
|
||||||
@ -35,29 +35,26 @@ const theme = createMuiTheme({
|
|||||||
textTransform: 'none',
|
textTransform: 'none',
|
||||||
borderRadius: '9px',
|
borderRadius: '9px',
|
||||||
padding: '6px 54px 6px 54px',
|
padding: '6px 54px 6px 54px',
|
||||||
width: '136px'
|
width: '136px',
|
||||||
},
|
},
|
||||||
containedPrimary: {
|
containedPrimary: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'rgba(249, 168, 38, 0.91)'
|
backgroundColor: 'rgba(249, 168, 38, 0.91)',
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
textPrimary: {},
|
||||||
},
|
},
|
||||||
textPrimary: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
fontFamily: [
|
fontFamily: ['Montserrat'].join(','),
|
||||||
'Montserrat'
|
|
||||||
].join(','),
|
|
||||||
h4: {
|
h4: {
|
||||||
color: '#ffa800',
|
color: '#ffa800',
|
||||||
fontWeight: 600
|
fontWeight: 600,
|
||||||
},
|
},
|
||||||
h6: {
|
h6: {
|
||||||
fontSize: '25px',
|
fontSize: '25px',
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
palette: {
|
palette: {
|
||||||
@ -73,9 +70,7 @@ const theme = createMuiTheme({
|
|||||||
dark: '#FFFFFF',
|
dark: '#FFFFFF',
|
||||||
contrastText: '#FFFFFF',
|
contrastText: '#FFFFFF',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
}
|
export { theme }
|
||||||
});
|
|
||||||
|
|
||||||
export {theme };
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user