mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-25 23:54:55 +01:00
Refactor
This commit is contained in:
parent
719623ea3a
commit
3dec2cdd77
@ -27,15 +27,10 @@ const queryClient = new QueryClient({
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [messages, setMessages] = useState(undefined);
|
const [messages, setMessages] = useState(undefined);
|
||||||
const [appi18n, setAppi18n] = useState<AppI18n>( new AppI18n('es'));
|
const [appi18n, setAppi18n] = useState<AppI18n>(new AppI18n('es'));
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
|
|
||||||
// @Todo: Why can not be dynamc ?
|
|
||||||
const loadResourceBundle = async (locale: Locale) => {
|
const loadResourceBundle = async (locale: Locale) => {
|
||||||
let result;
|
let result;
|
||||||
console.log("Language:" + locale.code);
|
|
||||||
|
|
||||||
switch (locale.code) {
|
switch (locale.code) {
|
||||||
case 'en':
|
case 'en':
|
||||||
result = require('./compiled-lang/en.json');
|
result = require('./compiled-lang/en.json');
|
||||||
@ -43,17 +38,24 @@ const App = () => {
|
|||||||
case 'es':
|
case 'es':
|
||||||
result = require('./compiled-lang/es.json');
|
result = require('./compiled-lang/es.json');
|
||||||
break;
|
break;
|
||||||
|
case 'de':
|
||||||
|
// result = require('./compiled-lang/de.json');
|
||||||
|
break;
|
||||||
|
case 'fr':
|
||||||
|
// result = require('./compiled-lang/fr.json');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const locale = appi18n.getLocale();
|
const locale = appi18n.getLocale();
|
||||||
const msg = await loadResourceBundle(locale);
|
const msg = await loadResourceBundle(locale);
|
||||||
setMessages(msg);
|
setMessages(msg);
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, [window.location.pathname]);
|
||||||
|
|
||||||
return messages ? (
|
return messages ? (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
import { Button, IconButton, Link, ListItemIcon, Menu, MenuItem, Tooltip } from '@material-ui/core';
|
import { IconButton, Link, ListItemIcon, Menu, MenuItem, Tooltip } from '@material-ui/core';
|
||||||
import { AccountCircle, ExitToAppOutlined, SettingsApplicationsOutlined } from '@material-ui/icons';
|
import { AccountCircle, ExitToAppOutlined, SettingsApplicationsOutlined } from '@material-ui/icons';
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useQuery, useQueryClient } from "react-query";
|
import { useQueryClient } from "react-query";
|
||||||
import Client, { ErrorInfo, AccountInfo } from "../../../classes/client";
|
import Client, { } from "../../../classes/client";
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { activeInstance } from '../../../redux/clientSlice';
|
import { activeInstance, fetchAccount } from '../../../redux/clientSlice';
|
||||||
|
|
||||||
|
|
||||||
const AccountMenu = () => {
|
const AccountMenu = () => {
|
||||||
|
|
||||||
const client: Client = useSelector(activeInstance);
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
@ -24,11 +22,7 @@ const AccountMenu = () => {
|
|||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, AccountInfo>('account', () => {
|
const account = fetchAccount();
|
||||||
return client.fetchAccountInfo();
|
|
||||||
});
|
|
||||||
const account = data;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip title={`${account?.firstName} ${account?.lastName} <${account?.email}>`}>
|
<Tooltip title={`${account?.firstName} ${account?.lastName} <${account?.email}>`}>
|
||||||
|
@ -11,7 +11,7 @@ import PrintOutlinedIcon from '@material-ui/icons/PrintOutlined';
|
|||||||
import ShareOutlinedIcon from '@material-ui/icons/ShareOutlined';
|
import ShareOutlinedIcon from '@material-ui/icons/ShareOutlined';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { LabelOutlined } from '@material-ui/icons';
|
import { LabelOutlined } from '@material-ui/icons';
|
||||||
import { fetchMapById } from '../action-dispatcher';
|
import { fetchMapById } from '../../../redux/clientSlice';
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ 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, fetchMapById } from '../../../../redux/clientSlice';
|
||||||
import { SimpleDialogProps, fetchMapById, handleOnMutationSuccess } from "..";
|
import { SimpleDialogProps, handleOnMutationSuccess } from "..";
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from "../base-dialog";
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@ import { FormControl } from "@material-ui/core";
|
|||||||
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 } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||||
import Input from "../../../form/input";
|
import Input from "../../../form/input";
|
||||||
import { SimpleDialogProps, fetchMapById } from "..";
|
import { SimpleDialogProps } from "..";
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from "../base-dialog";
|
||||||
|
|
||||||
export type DuplicateModel = {
|
export type DuplicateModel = {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { fetchMapById } from "..";
|
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from "../base-dialog";
|
||||||
import { FormControl, FormControlLabel, MenuItem, Radio, RadioGroup, Select } from "@material-ui/core";
|
import { FormControl, FormControlLabel, MenuItem, Radio, RadioGroup, Select } from "@material-ui/core";
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style';
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
|
import { fetchMapById } from "../../../../redux/clientSlice";
|
||||||
|
|
||||||
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';
|
||||||
|
@ -2,7 +2,7 @@ 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 { ErrorInfo, MapInfo } from '../../../classes/client';
|
import { AccountInfo, ErrorInfo, MapInfo } from '../../../classes/client';
|
||||||
import Client from '../../../classes/client';
|
import Client from '../../../classes/client';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { QueryClient, useQuery } from 'react-query';
|
import { QueryClient, useQuery } from 'react-query';
|
||||||
@ -57,29 +57,10 @@ const ActionDispatcher = (props: ActionDialogProps) => {
|
|||||||
{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} />}
|
||||||
</span >
|
</span >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
type MapLoadResult = {
|
|
||||||
isLoading: boolean,
|
|
||||||
error: ErrorInfo | null,
|
|
||||||
map: MapInfo | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchMapById = (id: number): MapLoadResult => {
|
|
||||||
|
|
||||||
const service: Client = useSelector(activeInstance);
|
|
||||||
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
|
||||||
return service.fetchAllMaps();
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = data?.find(m => m.id == id);
|
|
||||||
const map = result ? result : null;
|
|
||||||
return { isLoading: isLoading, error: error, map: map };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
||||||
queryClient.invalidateQueries('maps')
|
queryClient.invalidateQueries('maps')
|
||||||
onClose();
|
onClose();
|
||||||
|
@ -5,9 +5,10 @@ import { Card, List, ListItem, Paper, Typography } from '@material-ui/core';
|
|||||||
|
|
||||||
import { ErrorInfo } from '../../../../classes/client';
|
import { ErrorInfo } from '../../../../classes/client';
|
||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog';
|
||||||
import { fetchMapById, SimpleDialogProps } from '..';
|
import { SimpleDialogProps } from '..';
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { fetchMapById } from '../../../../redux/clientSlice';
|
||||||
|
|
||||||
const InfoDialog = (props: SimpleDialogProps) => {
|
const InfoDialog = (props: SimpleDialogProps) => {
|
||||||
const { mapId, onClose } = props;
|
const { mapId, onClose } = props;
|
||||||
|
@ -6,10 +6,10 @@ import { AppBar, Checkbox, FormControl, FormControlLabel, Tab, TextareaAutosize,
|
|||||||
|
|
||||||
|
|
||||||
import Client, { ErrorInfo } from '../../../../classes/client';
|
import Client, { ErrorInfo } from '../../../../classes/client';
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog';
|
||||||
import { TabContext, TabList, TabPanel } from '@material-ui/lab';
|
import { TabContext, TabList, TabPanel } from '@material-ui/lab';
|
||||||
import { fetchMapById, handleOnMutationSuccess, SimpleDialogProps } from '..';
|
import { handleOnMutationSuccess, SimpleDialogProps } from '..';
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ 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 } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||||
import { SimpleDialogProps, fetchMapById, handleOnMutationSuccess } from "..";
|
import { SimpleDialogProps, handleOnMutationSuccess } from "..";
|
||||||
import Input from "../../../form/input";
|
import Input from "../../../form/input";
|
||||||
import { FormControl } from "@material-ui/core";
|
import { FormControl } from "@material-ui/core";
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from "../base-dialog";
|
||||||
|
@ -11,7 +11,7 @@ import { useStyles } from './style';
|
|||||||
import { AcUnitTwoTone, AddCircleTwoTone, CloudUploadTwoTone, DeleteOutlineTwoTone, LabelTwoTone, PersonOutlineTwoTone, PublicTwoTone, ShareTwoTone, StarTwoTone } from '@material-ui/icons';
|
import { AcUnitTwoTone, AddCircleTwoTone, CloudUploadTwoTone, DeleteOutlineTwoTone, LabelTwoTone, PersonOutlineTwoTone, PublicTwoTone, ShareTwoTone, StarTwoTone } from '@material-ui/icons';
|
||||||
import { Button, Link, ListItemSecondaryAction, ListItemText, Tooltip } from '@material-ui/core';
|
import { Button, Link, ListItemSecondaryAction, ListItemText, Tooltip } from '@material-ui/core';
|
||||||
import { MapsList } from './maps-list';
|
import { MapsList } from './maps-list';
|
||||||
import { FormattedMessage, 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';
|
||||||
@ -20,7 +20,7 @@ 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';
|
||||||
|
|
||||||
const logoIcon = require('../../images/logo-small.svg');
|
const logoIcon = require('../../images/logo-small.svg');
|
||||||
@ -106,6 +106,7 @@ const MapsPage = () => {
|
|||||||
icon: <LabelTwoTone style={{ color: l.color ? l.color : 'inherit' }} />
|
icon: <LabelTwoTone style={{ color: l.color ? l.color : 'inherit' }} />
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<ClientHealthSentinel />
|
<ClientHealthSentinel />
|
||||||
@ -147,7 +148,7 @@ const MapsPage = () => {
|
|||||||
<ActionDispatcher action={activeDialog} onClose={() => setActiveDialog(undefined)} mapsId={[]} />
|
<ActionDispatcher action={activeDialog} onClose={() => setActiveDialog(undefined)} mapsId={[]} />
|
||||||
|
|
||||||
<div className={classes.rightButtonGroup}>
|
<div className={classes.rightButtonGroup}>
|
||||||
<LanguageMenu/>
|
<LanguageMenu />
|
||||||
<HelpMenu />
|
<HelpMenu />
|
||||||
<AccountMenu />
|
<AccountMenu />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import { Button, Divider, Menu, MenuItem, Tooltip } from '@material-ui/core';
|
import { Button, Divider, Menu, MenuItem, Tooltip } from '@material-ui/core';
|
||||||
import { TranslateTwoTone } from '@material-ui/icons';
|
import { TranslateTwoTone } from '@material-ui/icons';
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
import { useMutation, useQueryClient } from "react-query";
|
||||||
import Client, { ErrorInfo, AccountInfo } from "../../../classes/client";
|
import Client from "../../../classes/client";
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { activeInstance } from '../../../redux/clientSlice';
|
import { activeInstance, fetchAccount } from '../../../redux/clientSlice';
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
import AppI18n, { LocaleCode, Locales } from '../../../classes/app-i18n';
|
import { LocaleCode, Locales } from '../../../classes/app-i18n';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const LanguageMenu = () => {
|
const LanguageMenu = () => {
|
||||||
@ -40,11 +39,7 @@ const LanguageMenu = () => {
|
|||||||
mutation.mutate(localeCode);
|
mutation.mutate(localeCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = useQuery<unknown, ErrorInfo, AccountInfo>('account', () => {
|
const accountInfo = fetchAccount();
|
||||||
return client.fetchAccountInfo();
|
|
||||||
});
|
|
||||||
|
|
||||||
const locale = data?.locale;
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip title={intl.formatMessage({ id: 'language.change', defaultMessage: 'Change Language' })}>
|
<Tooltip title={intl.formatMessage({ id: 'language.change', defaultMessage: 'Change Language' })}>
|
||||||
@ -56,7 +51,7 @@ const LanguageMenu = () => {
|
|||||||
onClick={handleMenu}
|
onClick={handleMenu}
|
||||||
startIcon={<TranslateTwoTone />}
|
startIcon={<TranslateTwoTone />}
|
||||||
>
|
>
|
||||||
{locale?.label}
|
{accountInfo?.locale?.label}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu id="appbar-language"
|
<Menu id="appbar-language"
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
import Client from '../classes/client';
|
import { useQuery } from 'react-query';
|
||||||
|
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'
|
||||||
|
|
||||||
|
|
||||||
interface ConfigInfo {
|
interface ConfigInfo {
|
||||||
apiBaseUrl: string
|
apiBaseUrl: string
|
||||||
@ -61,6 +64,34 @@ export const clientSlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
type MapLoadResult = {
|
||||||
|
isLoading: boolean,
|
||||||
|
error: ErrorInfo | null,
|
||||||
|
map: MapInfo | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchMapById = (id: number): MapLoadResult => {
|
||||||
|
|
||||||
|
const client: Client = useSelector(activeInstance);
|
||||||
|
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
||||||
|
return client.fetchAllMaps();
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = data?.find(m => m.id == id);
|
||||||
|
const map = result ? result : null;
|
||||||
|
return { isLoading: isLoading, error: error, map: map };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const fetchAccount = (): AccountInfo | undefined => {
|
||||||
|
const client: Client = useSelector(activeInstance);
|
||||||
|
const { data } = useQuery<unknown, ErrorInfo, AccountInfo>('account', () => {
|
||||||
|
return client.fetchAccountInfo();
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
export const activeInstance = (state: any): Client => {
|
export const activeInstance = (state: any): Client => {
|
||||||
return state.client.instance;
|
return state.client.instance;
|
||||||
}
|
}
|
||||||
@ -72,3 +103,4 @@ export const activeInstanceStatus = (state: any): ClientStatus => {
|
|||||||
export const { sessionExpired } = clientSlice.actions;
|
export const { sessionExpired } = clientSlice.actions;
|
||||||
export default clientSlice.reducer;
|
export default clientSlice.reducer;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user