From 978a26e4554ff64119744995209931178b46cda0 Mon Sep 17 00:00:00 2001 From: goodmice Date: Mon, 10 Oct 2022 13:47:42 +0500 Subject: [PATCH 01/28] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=81=D1=82=D0=B8=D0=BB=D1=8C=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D1=81=D1=82=D0=B8=D0=BB=D0=B5?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/components/color_picker.less | 2 +- src/styles/d3.less | 2 +- src/styles/dashboard_nnb.less | 2 +- src/styles/detected_operations.less | 2 +- src/styles/equipment_details.css | 2 +- src/styles/index.css | 110 ++++++++++++------------ src/styles/loader.css | 94 ++++++++++---------- src/styles/message.less | 38 ++++---- src/styles/message_telemetry.css | 26 +++--- src/styles/smbo.css | 2 +- src/styles/wellTreeSelect.css | 18 ++-- src/styles/widgets/base.less | 2 +- 12 files changed, 150 insertions(+), 150 deletions(-) diff --git a/src/styles/components/color_picker.less b/src/styles/components/color_picker.less index aba100d..fd146dd 100644 --- a/src/styles/components/color_picker.less +++ b/src/styles/components/color_picker.less @@ -18,4 +18,4 @@ border: 1px solid black; width: 20px; height: 20px; -} \ No newline at end of file +} diff --git a/src/styles/d3.less b/src/styles/d3.less index 90db40d..58b64d9 100644 --- a/src/styles/d3.less +++ b/src/styles/d3.less @@ -150,4 +150,4 @@ .asb-d3-chart .adaptive-tooltip { font-size: 6px; } -} \ No newline at end of file +} diff --git a/src/styles/dashboard_nnb.less b/src/styles/dashboard_nnb.less index 6291a9b..7b47d6e 100644 --- a/src/styles/dashboard_nnb.less +++ b/src/styles/dashboard_nnb.less @@ -26,4 +26,4 @@ height: 13vh; } } -} \ No newline at end of file +} diff --git a/src/styles/detected_operations.less b/src/styles/detected_operations.less index 1f624d5..8d906d0 100644 --- a/src/styles/detected_operations.less +++ b/src/styles/detected_operations.less @@ -37,4 +37,4 @@ & > .tooltip-label { text-align: center; } -} \ No newline at end of file +} diff --git a/src/styles/equipment_details.css b/src/styles/equipment_details.css index 8ad648f..52e8db4 100755 --- a/src/styles/equipment_details.css +++ b/src/styles/equipment_details.css @@ -1,3 +1,3 @@ span.right-text { float: right; -} \ No newline at end of file +} diff --git a/src/styles/index.css b/src/styles/index.css index bde3eb8..a4c71d3 100755 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,156 +1,156 @@ body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } .d-flex { - display: flex; + display: flex; } .flex-direction-column { - flex-direction: column; + flex-direction: column; } .d-inline { - display: inline; + display: inline; } .d-none { - display: none; + display: none; } .flex-1 { - flex: 1; + flex: 1; } .w-15 { - width: 15% + width: 15% } .w-33 { - width: 33% + width: 33% } .w-50 { - width: 50% + width: 50% } .w-100 { - width: 100% + width: 100% } .m-0 { - margin: 0; + margin: 0; } .mt-8px { - margin-top: 8px; + margin-top: 8px; } .mt-20px { - margin-top: 20px; + margin-top: 20px; } .mb-20px { - margin-bottom: 20px; + margin-bottom: 20px; } .ml-5px { - margin-left: 5px; + margin-left: 5px; } .ml-10px { - margin-left: 10px; + margin-left: 10px; } .ml-30px { - margin-left: 30px; + margin-left: 30px; } .h-100vh { - height: calc(100vh - 64px); + height: calc(100vh - 64px); } .p-10 { - padding: 10px; + padding: 10px; } .vertical-align-center { - vertical-align: center; + vertical-align: center; } .text-align-center { - text-align: center; + text-align: center; } .text-align-r-container { - width: 100%; - text-align: right; + width: 100%; + text-align: right; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; } .linkDocuments { - color: #000; + color: #000; } .linkDocuments:hover { - color: #c32828; + color: #c32828; } .container{ - width: 100%; + width: 100%; } .login-button { - width: 20%; - margin: auto; + width: 20%; + margin: auto; } .register-button { - width: 50%; - margin: auto; + width: 50%; + margin: auto; } .first-column-title { - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: space-between; - position: relative; - padding: 16px 0; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: space-between; + position: relative; + padding: 16px 0; } .noselect { - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Safari */ - -khtml-user-select: none; /* Konqueror HTML */ - -moz-user-select: none; /* Old versions of Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently - supported by Chrome, Edge, Opera and Firefox */ + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Old versions of Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome, Edge, Opera and Firefox */ } .download-link { - height: 32px; - padding: 4px 15px; + height: 32px; + padding: 4px 15px; } .ant-table-cell:has(.color-pale-green) { - background-color: #98fb98; + background-color: #98fb98; } .ant-table-tbody > tr > td.ant-table-cell-row-hover:has( > div.color-pale-green) { - background: #98fb98; + background: #98fb98; } .color-pale-green { - background-color: #98fb98; + background-color: #98fb98; } \ No newline at end of file diff --git a/src/styles/loader.css b/src/styles/loader.css index e8b0844..23ab350 100755 --- a/src/styles/loader.css +++ b/src/styles/loader.css @@ -1,69 +1,69 @@ /* original from https://loading.io/css/ */ .lds-ripple { - display: inline-block; - position: absolute; - width: 80px; - height: 80px; + display: inline-block; + position: absolute; + width: 80px; + height: 80px; } .lds-ripple div { - position: absolute; - border: 4px solid rgb(226, 29, 29); - opacity: 1; - border-radius: 50%; - animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; + position: absolute; + border: 4px solid rgb(226, 29, 29); + opacity: 1; + border-radius: 50%; + animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; } .lds-ripple div:nth-child(2) { - animation-delay: -0.5s; + animation-delay: -0.5s; } @keyframes lds-ripple { - 0% { - top: 36px; - left: 36px; - width: 0; - height: 0; - opacity: 1; - } - 100% { - top: 0px; - left: 0px; - width: 72px; - height: 72px; - opacity: 0; - } + 0% { + top: 36px; + left: 36px; + width: 0; + height: 0; + opacity: 1; + } + 100% { + top: 0px; + left: 0px; + width: 72px; + height: 72px; + opacity: 0; + } } .loader-container{ - display: grid; - grid-template-columns: 1fr 1fr 1fr; + display: grid; + grid-template-columns: 1fr 1fr 1fr; } .loader-content{ - grid-column-start: 1; - grid-column-end: span 3; - grid-row-start: 1; + grid-column-start: 1; + grid-column-end: span 3; + grid-row-start: 1; } .loader-overlay{ - grid-column-start: 1; - grid-column-end: span 3; - grid-row-start: 1; - place-self: center; - align-self: center; - justify-self: center; - z-index: 1; - height: 80px; - width: 80px; - border-radius: 40px; + grid-column-start: 1; + grid-column-end: span 3; + grid-row-start: 1; + place-self: center; + align-self: center; + justify-self: center; + z-index: 1; + height: 80px; + width: 80px; + border-radius: 40px; } .loader-fade{ - grid-column-start: 1; - grid-column-end: span 3; - grid-row-start: 1; - background-color: rgba(255, 255, 255, 0.4); - align-self: stretch; - justify-self: stretch; - z-index: 1; - box-shadow: 0px 0px 6px 5px rgba(255, 254, 254, 0.4); + grid-column-start: 1; + grid-column-end: span 3; + grid-row-start: 1; + background-color: rgba(255, 255, 255, 0.4); + align-self: stretch; + justify-self: stretch; + z-index: 1; + box-shadow: 0px 0px 6px 5px rgba(255, 254, 254, 0.4); } diff --git a/src/styles/message.less b/src/styles/message.less index be78867..8785dbf 100644 --- a/src/styles/message.less +++ b/src/styles/message.less @@ -1,49 +1,49 @@ .event_message > td { - font-size: 14px; - padding: 1px 4px !important; - border-bottom: 1px !important; + font-size: 14px; + padding: 1px 4px !important; + border-bottom: 1px !important; } .event_message .anticon{ - display: none; + display: none; } .event_message:hover a{ - text-decoration: underline; + text-decoration: underline; } .event_message:hover .anticon{ - display: inherit; + display: inherit; } .event_message_off { - color: #505050; - background: #b5b5b5; + color: #505050; + background: #b5b5b5; } .event_message_disabled { - color: #656565; - background: #b5b5b5; + color: #656565; + background: #b5b5b5; } .event_message_1 { - color: white; - background: #ff3737; + color: white; + background: #ff3737; } .event_message_2 { - color: black; - background: gold; + color: black; + background: gold; } .event_message_3 { - color: #c0c0c0; - background: #505060; + color: #c0c0c0; + background: #505060; } td.ant-table-column-sort { - color: black; - background-color: #fafafa; + color: black; + background-color: #fafafa; } .ant-table-tbody > tr > td.ant-table-cell-row-hover { - color: black; + color: black; } diff --git a/src/styles/message_telemetry.css b/src/styles/message_telemetry.css index 5eed9e1..d627755 100755 --- a/src/styles/message_telemetry.css +++ b/src/styles/message_telemetry.css @@ -1,30 +1,30 @@ .event_message { - font-size: 14px; + font-size: 14px; } .event_message_off { - color: #505050; - background: #b5b5b5; + color: #505050; + background: #b5b5b5; } .event_message_disabled { - color: #656565; - background: #b5b5b5; + color: #656565; + background: #b5b5b5; } .event_message_1 { - color: white; - background: #ff3737; + color: white; + background: #ff3737; } .event_message_2 { - color: black; - background: gold; + color: black; + background: gold; } .event_message_3, .event_message_3:hover { - color: #c0c0c0; - background: #505060; - height: 1px; -} \ No newline at end of file + color: #c0c0c0; + background: #505060; + height: 1px; +} diff --git a/src/styles/smbo.css b/src/styles/smbo.css index 5f6e5aa..9a86f6b 100755 --- a/src/styles/smbo.css +++ b/src/styles/smbo.css @@ -120,4 +120,4 @@ font-size: 75px; margin-top: 5px; align-items: center; -} \ No newline at end of file +} diff --git a/src/styles/wellTreeSelect.css b/src/styles/wellTreeSelect.css index c889fd8..f5435d3 100755 --- a/src/styles/wellTreeSelect.css +++ b/src/styles/wellTreeSelect.css @@ -1,19 +1,19 @@ .header-tree-select { - width: 300px + width: 300px } .header-tree-select *{ - color: #fff; - font-size: 1rem; + color: #fff; + font-size: 1rem; } .header-tree-select{ - width: 300px; - border: 1px solid rgba(255, 255, 255, 0.2); - background-color: rgba(0, 0, 0, 0.3); + width: 300px; + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: rgba(0, 0, 0, 0.3); } .header-tree-select:hover{ - border: 1px solid rgba(255, 255, 255, 0.8); - background-color: rgba(0, 0, 0, 0.5); -} \ No newline at end of file + border: 1px solid rgba(255, 255, 255, 0.8); + background-color: rgba(0, 0, 0, 0.5); +} diff --git a/src/styles/widgets/base.less b/src/styles/widgets/base.less index ee611c9..5a1988d 100644 --- a/src/styles/widgets/base.less +++ b/src/styles/widgets/base.less @@ -1,5 +1,5 @@ - @size: 9.9vh; + .number_widget { display: flex; flex-direction: column; From 8c2c1b7913669cee734dd4c2387866ea9d42831c Mon Sep 17 00:00:00 2001 From: goodmice Date: Tue, 11 Oct 2022 17:36:00 +0500 Subject: [PATCH 02/28] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20TelemetryView=20=D0=B8=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=86=D0=B0=20=D0=BF=D1=80=D0=BE=D1=81=D0=BC?= =?UTF-8?q?=D0=BE=D1=82=D1=80=D0=B0=20=D1=82=D0=B5=D0=BB=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/views/TelemetryView.tsx | 20 ++++++++++++------- .../AdminPanel/Telemetry/TelemetryViewer.jsx | 4 ++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/views/TelemetryView.tsx b/src/components/views/TelemetryView.tsx index 702ebd2..8758353 100755 --- a/src/components/views/TelemetryView.tsx +++ b/src/components/views/TelemetryView.tsx @@ -1,8 +1,9 @@ import { Fragment, memo } from 'react' import { Tooltip } from 'antd' -import { TelemetryDto, TelemetryInfoDto } from '@api' import { Grid, GridItem } from '@components/Grid' +import { formatDate } from '@utils' +import { TelemetryDto, TelemetryInfoDto } from '@api' export const lables: Record = { timeZoneId: 'Временная зона', @@ -30,12 +31,17 @@ export const TelemetryView = memo(({ telemetry }) => telemet overlayInnerStyle={{ width: '400px' }} title={ - {(Object.keys(telemetry.info) as Array).map((key, i) => ( - - {lables[key] ?? key}: - {telemetry.info?.[key]} - - ))} + {(Object.keys(telemetry.info) as Array).map((key, i) => { + let value = telemetry.info?.[key] + value = key === 'drillingStartDate' ? formatDate(value) : value + + return ( + + {lables[key] ?? key}: + {value} + + ) + })} } > diff --git a/src/pages/AdminPanel/Telemetry/TelemetryViewer.jsx b/src/pages/AdminPanel/Telemetry/TelemetryViewer.jsx index 41f129d..111d930 100755 --- a/src/pages/AdminPanel/Telemetry/TelemetryViewer.jsx +++ b/src/pages/AdminPanel/Telemetry/TelemetryViewer.jsx @@ -6,7 +6,7 @@ import { Button, Input } from 'antd' import { defaultPagination, makeColumn, - makeDateSorter, + makeDateColumn, makeNumericColumn, makeNumericRender, makeTextColumn, @@ -53,7 +53,7 @@ const TelemetryController = memo(() => { makeNumericColumn('ID', 'id', null, null, makeNumericRender(0)), makeTextColumn('UID', 'remoteUid'), makeTextColumn('Назначена на скважину', 'realWell'), - makeTextColumn('Дата начала бурения', 'drillingStartDate', null, makeDateSorter('drillingStartDate')), + makeDateColumn('Дата начала бурения', 'drillingStartDate'), makeTextColumn('Часовой пояс', 'timeZoneId'), makeTextColumn('Скважина', 'well'), makeTextColumn('Куст', 'cluster'), From ddc6f7840df02f99c4b962e981934434588e6eaa Mon Sep 17 00:00:00 2001 From: goodmice Date: Thu, 13 Oct 2022 13:10:19 +0500 Subject: [PATCH 03/28] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B2=20localStorage=20=D0=B4=D0=BB=D1=8F=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/outlets/UserOutlet.tsx | 17 ++++++++++++++++ src/components/outlets/index.ts | 1 + src/context.ts | 17 ++++++++++++---- src/utils/functions/permissions.tsx | 17 ++++------------ src/utils/functions/storage.ts | 29 +++++++++++++++++---------- 5 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 src/components/outlets/UserOutlet.tsx create mode 100644 src/components/outlets/index.ts diff --git a/src/components/outlets/UserOutlet.tsx b/src/components/outlets/UserOutlet.tsx new file mode 100644 index 0000000..49a2859 --- /dev/null +++ b/src/components/outlets/UserOutlet.tsx @@ -0,0 +1,17 @@ +import { memo, useMemo } from 'react' +import { Outlet } from 'react-router-dom' + +import { UserContext } from '@asb/context' +import { getUser } from '@utils' + +export const UserOutlet = memo(() => { + const user = useMemo(() => getUser() ?? {}, []) + + return ( + + + + ) +}) + +export default UserOutlet diff --git a/src/components/outlets/index.ts b/src/components/outlets/index.ts new file mode 100644 index 0000000..77c9a70 --- /dev/null +++ b/src/components/outlets/index.ts @@ -0,0 +1 @@ +export * from './UserOutlet' diff --git a/src/context.ts b/src/context.ts index 22e62c1..4fddc4f 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,22 +1,31 @@ import { createContext, useContext } from 'react' -import { WellDto } from '@api' +import { UserTokenDto, WellDto } from '@api' /** Контекст текущей скважины */ export const WellContext = createContext<[WellDto, (well: WellDto) => void]>([{}, () => {}]) /** Контекст текущего корневого пути */ -export const RootPathContext = createContext('') +export const RootPathContext = createContext('/') +/** Контекст текущего пользователя */ +export const UserContext = createContext({}) /** - * Получение текущей скважины + * Получить текущую скважину * * @returns Текущая скважина, либо `null` */ export const useWell = () => useContext(WellContext) /** - * Получает текущий корневой путь + * Получить текущий корневой путь * * @returns Текущий корневой путь */ export const useRootPath = () => useContext(RootPathContext) + +/** + * Получить текущего пользователя + * + * @returns Текущий пользователь, либо `null` + */ +export const useUser = () => useContext(UserContext) diff --git a/src/utils/functions/permissions.tsx b/src/utils/functions/permissions.tsx index a8067b0..274a9c5 100644 --- a/src/utils/functions/permissions.tsx +++ b/src/utils/functions/permissions.tsx @@ -1,7 +1,7 @@ import { memo, NamedExoticComponent, ReactElement, ReactNode, useMemo } from 'react' import { Navigate, useParams } from 'react-router-dom' -import { getUserLogin, getUserPermissions, getUserRoles } from '@utils' +import { getUserLogin, getUserPermissions } from '@utils' import { isDev } from '@utils' import AccessDenied from '@pages/AccessDenied' @@ -31,22 +31,13 @@ export const getPermissions = (...values: PermissionRequest[]) => { export const hasPermission = (permission?: Permission | Permission[], userPermissions?: Permission[]): boolean => { if (!Array.isArray(permission) && typeof permission !== 'string') return true - const userPerms = userPermissions ?? getUserPermissions() + const userPerms = userPermissions ?? getUserPermissions() ?? [] if (typeof permission === 'string') permission = [permission] return permission.every((perm) => userPerms.includes(perm)) } -export const isInRole = (roles?: Role[] | Role): boolean => { - if (typeof roles === 'string' && !Array.isArray(roles)) - roles = [roles] - if (!roles?.length) return true - - const user_roles = getUserRoles() - return roles.some((role) => user_roles.includes(role)) -} - -const sectionAvailable = (section: PermissionRecord, userPermission: Permission[] = getUserPermissions()) => { +const sectionAvailable = (section: PermissionRecord, userPermission: Permission[]) => { for (const child of Object.values(section)) { if (!child) continue if (Array.isArray(child)) { @@ -81,7 +72,7 @@ export const isURLAvailable = (path: string, userPermissions?: Permission[]) => } if (!perms) return false - const userPerms: string[] = userPermissions ?? getUserPermissions() + const userPerms: string[] = userPermissions ?? getUserPermissions() ?? [] if (Array.isArray(perms)) { return perms.every((perm) => userPerms.includes(perm)) } diff --git a/src/utils/functions/storage.ts b/src/utils/functions/storage.ts index c4392f7..a95182a 100644 --- a/src/utils/functions/storage.ts +++ b/src/utils/functions/storage.ts @@ -1,7 +1,8 @@ import { OpenAPI, UserTokenDto } from '@api' +import { isDev } from '..' import { wrapValues } from './objects' -import { Role, Permission } from './permissions' +import { Permission } from './permissions' import { normalizeTableColumn, optimizeTableColumn, TableSettings, TableSettingsStore } from './table_settings' export enum StorageNames { @@ -10,6 +11,7 @@ export enum StorageNames { login = 'login', permissions = 'permissions', roles = 'roles', + user = 'user', tableSettings = 'tableSettings', dashboardNNB = 'dashboardNNB', witsInfo = 'witsInfo' @@ -22,11 +24,12 @@ export const getArrayFromLocalStorage = (name: string } export const getJSON = (name: StorageNames): T | null => { - const raw = localStorage.getItem(name) - if (!raw) return null try { - return JSON.parse(raw) - } catch {} + const raw = localStorage.getItem(name) + if (raw) return JSON.parse(raw) + } catch { + if (isDev()) console.warn(`Storage "${name}" parsing exception!`) + } return null } @@ -34,25 +37,28 @@ export const setJSON = (name: StorageNames, data: T | null): boolean => { try { localStorage.setItem(name, JSON.stringify(data)) return true - } catch {} + } catch { + if (isDev()) console.warn(`Storage "${name}" saving exception!`) + } return false } -export const getUserRoles = (): Role[] => getArrayFromLocalStorage(StorageNames.roles) ?? [] -export const getUserPermissions = (): Permission[] => getArrayFromLocalStorage(StorageNames.permissions) ?? [] +export const getUser = (): UserTokenDto | null => getJSON(StorageNames.user) + +export const getUserPermissions = (): Permission[] | null => getUser()?.permissions?.map((perm) => perm.name as string) || null export const getUserId = () => Number(localStorage.getItem(StorageNames.userId)) || null export const getUserLogin = () => localStorage.getItem(StorageNames.login) export const getUserToken = () => localStorage.getItem(StorageNames.token) export const setUser = (user: UserTokenDto) => { OpenAPI.TOKEN = user.token ?? undefined + localStorage.setItem(StorageNames.user, JSON.stringify(user)) + localStorage.setItem(StorageNames.userId, String(user.id)) localStorage.setItem(StorageNames.token, String(user.token)) localStorage.setItem(StorageNames.login, String(user.login)) if (user.permissions) localStorage.setItem(StorageNames.permissions, user.permissions.map((permission) => permission.name).join(',')) - if (user.roleNames) - localStorage.setItem(StorageNames.roles, user.roleNames.join(',')) } export const removeUser = () => { @@ -60,7 +66,8 @@ export const removeUser = () => { localStorage.removeItem(StorageNames.login) localStorage.removeItem(StorageNames.token) localStorage.removeItem(StorageNames.permissions) - localStorage.removeItem(StorageNames.roles) + + localStorage.removeItem(StorageNames.user) } export const getTableSettings = (tableName: string): TableSettings => { From 411b79ee6085392f107353d49ca2c2e7c29553ba Mon Sep 17 00:00:00 2001 From: goodmice Date: Thu, 13 Oct 2022 13:11:07 +0500 Subject: [PATCH 04/28] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=81=D1=82=D0=B8=D0=BB=D0=B8=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D0=BA=D0=B0?= =?UTF-8?q?=D1=80=D0=BA=D0=B0=D1=81=D0=B0=20=D0=B8=20=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=8E=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/App.less | 127 +++++++++++++------------------- src/styles/layout.less | 147 ++++++++++++++++++++++++++++++++++++++ src/styles/user_menu.less | 10 +++ 3 files changed, 205 insertions(+), 79 deletions(-) create mode 100644 src/styles/layout.less create mode 100644 src/styles/user_menu.less diff --git a/src/styles/App.less b/src/styles/App.less index c4399b5..0ddef5d 100755 --- a/src/styles/App.less +++ b/src/styles/App.less @@ -1,142 +1,111 @@ @import './loader.css'; -@header-height: 64px; -@layout-min-height: calc(100vh - @header-height); - #root, .app{ - min-height:100%; + min-height:100%; } .ant-layout{ - flex: 1; + flex: 1; - > .ant-menu { - flex: 0; - } + > .ant-menu { + flex: 0; + } } html { - display: flex; - height: 100%; + display: flex; + height: 100%; } .mt-30px { - margin-top: 30px; + margin-top: 30px; } .ml-30px { - margin-left: 30px; + margin-left: 30px; } .login_page { - position: absolute; - height: 100%; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - padding: 24px; - background-color: #9d9d9d; + position: absolute; + height: 100%; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + padding: 24px; + background-color: #9d9d9d; } .shadow{ - box-shadow: 1px 1px 4px #00000033; -} - -.header { - display: flex; - align-items: center; - justify-content: space-around; - gap: 50px; - height: @header-height; -} - -.header .logo { - padding: 8px 24px; - margin: 0 10px; - margin-bottom: 2px; -} - -.header .title{ - flex-grow: 1; - color: #fff; - padding-left: calc(100vw / 2 - 400px); -} - -.header button{ - color: rgb(255, 255, 255); - background-color: rgba(0, 0, 0, 0.1); - border: 1px solid rgba(0, 0, 0, 0.2); + box-shadow: 1px 1px 4px #00000033; } .small-font { - font-size: 12px; + font-size: 12px; } .yellow-background { - background-color: #FFFF99 !important; + background-color: #FFFF99 !important; } .lightgray-background { - background-color: #AFABAB !important; + background-color: #AFABAB !important; } .lightpurple-background { - background-color: #CCCCFF !important; + background-color: #CCCCFF !important; } .lightorange-background { - background-color: #FFCC99 !important; + background-color: #FFCC99 !important; } .ph-2 { - padding: 2px 10px; + padding: 2px 10px; } .mh-2 { - margin-left: 2px; - margin-right: 2px; + margin-left: 2px; + margin-right: 2px; } .ant-layout-content { - display: flex; - flex-direction: column; - align-items: stretch; -} - -.sheet{ - padding: 5px 24px; - min-height: calc(@layout-min-height - 15px); // 280px; - margin: 0 15px 15px 15px; -} - -.site-layout-background { - background: #fff; + display: flex; + flex-direction: column; + align-items: stretch; } .border_small{ - border: 1px solid rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.05); } .menu-title, .chart-footer { - display: flex; + display: flex; } .submit-button { - border-radius: 5px; - font-weight: bold; - text-align: center; - margin: 0 5px 5px 0; + border-radius: 5px; + font-weight: bold; + text-align: center; + margin: 0 5px 5px 0; } .well_menu { - margin-top: 0; - padding-top: 0; - height: 100%; - border-right: 0; + margin-top: 0; + padding-top: 0; + border-right: 0; } tr.table_row_size { - height: 10px; + height: 10px; +} + +.hide-slider { + scrollbar-width: none; + -ms-overflow-style: none; + + &::-webkit-scrollbar { + display: none; + } } diff --git a/src/styles/layout.less b/src/styles/layout.less new file mode 100644 index 0000000..e4a17da --- /dev/null +++ b/src/styles/layout.less @@ -0,0 +1,147 @@ +@header-height: 64px; +@layout-min-height: calc(100vh - @header-height); + +.no-select { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.header { + display: flex; + align-items: center; + justify-content: space-around; + gap: 50px; + height: @header-height; + + & .logo { + padding: 8px 24px; + margin: 0 10px; + margin-bottom: 2px; + } + + & .title { + flex-grow: 1; + color: #fff; + padding-left: calc(100vw / 2 - 400px); + .no-select; + } + + & button { + color: rgb(255, 255, 255); + background-color: rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.2); + } +} + +@link-color: #FFFC; +@active-bg: #c32828; +@admin-bg: #900; +@admin-active-bg: #413f3d; + +.ant-menu-submenu-popup .ant-menu-sub { + color: @link-color; + + & .ant-menu-item > span > a, & a { + color: @link-color; + } +} + +.page-layout { + height: 100vh; + + & .menu-sider { + overflow-x: hidden; + overflow-y: auto; + height: 100%; + + & .ant-menu-submenu-selected { + &.ant-menu-submenu-vertical { + background-color: @active-bg; + } + + &.ant-menu-submenu-inline { + background-color: inherit; + & .ant-menu-submenu-title { + color: @active-bg; + } + } + } + + & .ant-menu-submenu-title > .ant-menu-title-content > a, + & .ant-menu .ant-menu-item, + & .ant-menu .ant-menu-item > span > a { + color: @link-color; + .no-select; + } + + & .sider-content { + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: start; + height: 100%; + + & .sider-toogle { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: @header-height !important; + color: @link-color; + border: none; + outline: none; + background-color: transparent; + cursor: pointer; + + &:hover { + color: white; + } + } + + & .scrollable { + flex: 1; + overflow-y: scroll; + } + } + + & .ant-menu-dark .ant-menu-inline.ant-menu-sub, & .ant-menu { + background: transparent; + color: @link-color; + } + } + + & .page-content { + overflow-y: auto; + // background: white; + } + + & .sheet{ + padding: 15px; + // min-height: calc(@layout-min-height - 30px); // 280px; + overflow: visible; + margin: 15px; + } + + &-admin { + .header { + background-color: @admin-bg; + } + + .menu-sider { + background-color: @admin-bg; + } + + & .menu-sider .ant-menu-submenu-selected.ant-menu-submenu-inline .ant-menu-submenu-title { + color: white; + & .ant-menu-title-content > a { + color: white; + } + } + } +} + +.site-layout-background { + background: #fff; +} diff --git a/src/styles/user_menu.less b/src/styles/user_menu.less new file mode 100644 index 0000000..6089225 --- /dev/null +++ b/src/styles/user_menu.less @@ -0,0 +1,10 @@ +.user-menu { + + & .profile-links { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 10px; + margin-bottom: 10px; + } +} From 9c2b0ecd265fa6ba6c4db7986bd4042cbaa049f2 Mon Sep 17 00:00:00 2001 From: goodmice Date: Thu, 13 Oct 2022 13:45:32 +0500 Subject: [PATCH 05/28] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D1=81=D1=82=D0=B0=D1=80=D1=8B=D0=B5=20=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/AdminPanel/Telemetry/index.jsx | 25 +++------- .../Analytics/WellCompositeEditor/index.jsx | 42 +++++----------- src/pages/Analytics/index.jsx | 24 +++------ src/pages/Cluster/CompaniesTable.jsx | 6 +-- src/pages/Documents/index.jsx | 49 ++++++------------- src/pages/Reports/index.jsx | 28 +++-------- src/pages/Telemetry/index.jsx | 41 +++++----------- src/pages/WellOperations/index.jsx | 43 ++++------------ 8 files changed, 68 insertions(+), 190 deletions(-) diff --git a/src/pages/AdminPanel/Telemetry/index.jsx b/src/pages/AdminPanel/Telemetry/index.jsx index ec42dec..aae4cb9 100755 --- a/src/pages/AdminPanel/Telemetry/index.jsx +++ b/src/pages/AdminPanel/Telemetry/index.jsx @@ -1,9 +1,7 @@ -import { Layout } from 'antd' import { memo, useMemo } from 'react' import { Navigate, Route, Routes } from 'react-router-dom' import { RootPathContext, useRootPath } from '@asb/context' -import { PrivateMenu } from '@components/Private' import { NoAccessComponent, wrapPrivateComponent } from '@utils' import TelemetryViewer from './TelemetryViewer' @@ -15,23 +13,12 @@ const Telemetry = memo(() => { return ( - - - - - - - - - - } /> - } /> - } /> - } /> - - - - + + } /> + } /> + } /> + } /> + ) }) diff --git a/src/pages/Analytics/WellCompositeEditor/index.jsx b/src/pages/Analytics/WellCompositeEditor/index.jsx index c0cc5b9..2b35e4d 100755 --- a/src/pages/Analytics/WellCompositeEditor/index.jsx +++ b/src/pages/Analytics/WellCompositeEditor/index.jsx @@ -1,9 +1,8 @@ -import { useState, useEffect, memo, useMemo } from 'react' import { Navigate, Route, Routes } from 'react-router-dom' -import { Col, Layout, Row } from 'antd' +import { useState, useEffect, memo } from 'react' +import { Row } from 'antd' -import { useWell, useRootPath } from '@asb/context' -import { PrivateMenu } from '@components/Private' +import { useWell } from '@asb/context' import LoaderPortal from '@components/LoaderPortal' import WellSelector from '@components/selectors/WellSelector' import { invokeWebApiWrapperAsync } from '@components/factory' @@ -13,8 +12,6 @@ import { OperationStatService, WellCompositeService } from '@api' import ClusterWells from '@pages/Cluster/ClusterWells' import WellCompositeSections from './WellCompositeSections' -const { Content } = Layout - const properties = { requirements: ['OperationStat.get', 'WellComposite.get'], title: 'Композитная скважина', @@ -24,12 +21,9 @@ const properties = { const WellCompositeEditor = memo(() => { const [well] = useWell() - const root = useRootPath() - const rootPath = useMemo(() => `${root}/${properties.key}`, [root]) const [statsWells, setStatsWells] = useState([]) const [showLoader, setShowLoader] = useState(false) - const [showTabLoader, setShowTabLoader] = useState(false) const [selectedIdWells, setSelectedIdWells] = useState([]) const [selectedSections, setSelectedSections] = useState([]) @@ -61,7 +55,7 @@ const WellCompositeEditor = memo(() => { const stats = arrayOrDefault(await OperationStatService.getWellsStat(selectedIdWells)) setStatsWells(stats) }, - setShowTabLoader, + setShowLoader, 'Не удалось загрузить статистику по скважинам/секциям', { actionName: 'Получение статистики по скважинам/секциям' } ) @@ -70,29 +64,15 @@ const WellCompositeEditor = memo(() => { return ( - - - - - - - - - + - - - - - } /> - } /> + + } /> + } /> - } /> - } /> - - - - + } /> + } /> + ) }) diff --git a/src/pages/Analytics/index.jsx b/src/pages/Analytics/index.jsx index c5203ed..df5be22 100755 --- a/src/pages/Analytics/index.jsx +++ b/src/pages/Analytics/index.jsx @@ -1,9 +1,7 @@ import { memo, useMemo } from 'react' import { Navigate, Route, Routes } from 'react-router-dom' -import { Layout } from 'antd' import { RootPathContext, useRootPath } from '@asb/context' -import { PrivateMenu } from '@components/Private' import { NoAccessComponent, wrapPrivateComponent } from '@utils' import Statistics from './Statistics' @@ -15,23 +13,13 @@ const Analytics = memo(() => { return ( - - - - - - - - - } /> - } /> + + } /> + } /> - } /> - } /> - - - - + } /> + } /> + ) }) diff --git a/src/pages/Cluster/CompaniesTable.jsx b/src/pages/Cluster/CompaniesTable.jsx index 7520c23..390e2bb 100644 --- a/src/pages/Cluster/CompaniesTable.jsx +++ b/src/pages/Cluster/CompaniesTable.jsx @@ -1,4 +1,4 @@ -import React, { memo, useMemo } from 'react' +import { memo, useMemo } from 'react' import { BankOutlined } from '@ant-design/icons' import { makeTextColumn, Table } from '@components/Table' @@ -9,7 +9,7 @@ const columns = [ makeTextColumn('Тип компании', 'companyTypeCaption'), ] -const CompaniesTable = memo(({companies}) => { +const CompaniesTable = memo(({ companies }) => { const dataCompanies = useMemo(() => companies?.map((company) => ({ key: company.id, logo: company?.logo ? : , @@ -30,4 +30,4 @@ const CompaniesTable = memo(({companies}) => { ) }) -export default CompaniesTable \ No newline at end of file +export default CompaniesTable diff --git a/src/pages/Documents/index.jsx b/src/pages/Documents/index.jsx index d3882fb..1b4c293 100755 --- a/src/pages/Documents/index.jsx +++ b/src/pages/Documents/index.jsx @@ -1,16 +1,11 @@ import { Navigate, Route, Routes } from 'react-router-dom' import { memo, useMemo } from 'react' -import { FolderOutlined } from '@ant-design/icons' -import { Layout } from 'antd' import { RootPathContext, useRootPath } from '@asb/context' -import { PrivateMenu } from '@components/Private' -import { getTabname, wrapPrivateComponent, NoAccessComponent, hasPermission } from '@utils' +import { wrapPrivateComponent, NoAccessComponent, hasPermission } from '@utils' import DocumentsTemplate from './DocumentsTemplate' -const { Content } = Layout - const makeDocCat = (id, key, title, permissions = ['File.get']) => ({ id, key, title, permissions }) export const documentCategories = [ @@ -27,7 +22,6 @@ export const documentCategories = [ ] const MenuDocuments = memo(() => { - const category = getTabname() const root = useRootPath() const rootPath = useMemo(() => `${root}/document`, [root]) @@ -35,34 +29,21 @@ const MenuDocuments = memo(() => { return ( - - {categories.map(category => ( - } - title={category.title} - /> - ))} - - - - - {categories.length > 0 && ( - } /> - )} - } /> + + {categories.length > 0 && ( + } /> + )} + } /> - {categories.map(category => ( - - )} /> - ))} - - - + {categories.map(category => ( + + )} /> + ))} + ) }) diff --git a/src/pages/Reports/index.jsx b/src/pages/Reports/index.jsx index d28275c..d6d7d04 100644 --- a/src/pages/Reports/index.jsx +++ b/src/pages/Reports/index.jsx @@ -1,41 +1,25 @@ import { Navigate, Route, Routes } from 'react-router-dom' import { memo, useMemo } from 'react' -import { FilePdfOutlined } from '@ant-design/icons' -import { Layout } from 'antd' import { RootPathContext, useRootPath } from '@asb/context' -import { PrivateMenu } from '@components/Private' import { NoAccessComponent, wrapPrivateComponent } from '@utils' import DailyReport from './DailyReport' import DiagramReport from './DiagramReport' -const { Content } = Layout - const Reports = memo(() => { const root = useRootPath() const rootPath = useMemo(() => `${root}/reports`, [root]) return ( - - - } /> - - + + } /> + } /> - - - - } /> - } /> - - } /> - } /> - - - - + } /> + } /> + ) }) diff --git a/src/pages/Telemetry/index.jsx b/src/pages/Telemetry/index.jsx index d85f7e8..0420819 100755 --- a/src/pages/Telemetry/index.jsx +++ b/src/pages/Telemetry/index.jsx @@ -1,10 +1,8 @@ import { Navigate, Route, Routes } from 'react-router-dom' import { memo, useMemo } from 'react' -import { Layout } from 'antd' -import { AlertOutlined, FundViewOutlined, DatabaseOutlined } from '@ant-design/icons' +import { FundViewOutlined } from '@ant-design/icons' import { RootPathContext, useRootPath } from '@asb/context' -import { PrivateMenu } from '@components/Private' import { NoAccessComponent, wrapPrivateComponent } from '@utils' import Archive from './Archive' @@ -16,40 +14,23 @@ import OperationTime from './OperationTime' import '@styles/index.css' -const { Content } = Layout - const Telemetry = memo(() => { const root = useRootPath() const rootPath = useMemo(() => `${root}/telemetry`, [root]) return ( - - - } /> - } /> - } /> - - - - + + } /> + } /> - - - - } /> - } /> - - } /> - } /> - } /> - } /> - } /> - } /> - - - - + } /> + } /> + } /> + } /> + } /> + } /> + ) }) diff --git a/src/pages/WellOperations/index.jsx b/src/pages/WellOperations/index.jsx index 2f59865..f34f41d 100755 --- a/src/pages/WellOperations/index.jsx +++ b/src/pages/WellOperations/index.jsx @@ -1,17 +1,8 @@ import { useNavigate, useLocation, Route, Routes, Navigate } from 'react-router-dom' import { memo, useCallback, useMemo } from 'react' -import { Layout } from 'antd' -import { - BarChartOutlined, - BuildOutlined, - ControlOutlined, - LineChartOutlined, - TableOutlined, -} from '@ant-design/icons' import { RootPathContext, useRootPath } from '@asb/context' import { Flex } from '@components/Grid' -import { PrivateMenu } from '@components/Private' import { getTabname, NoAccessComponent, wrapPrivateComponent } from '@utils' import Tvd from './Tvd' @@ -21,8 +12,6 @@ import { DrillProcessFlow } from './DrillProcessFlow' import { WellSectionsStat } from './WellSectionsStat' import { WellOperationsEditorPlan, WellOperationsEditorFact } from './WellOperationsEditor' -const { Content } = Layout - const properties = { requirements: [], title: 'Операции по скважине', @@ -46,31 +35,19 @@ const WellOperations = memo(() => { return( - - } /> - } key={'sections'} title={'Секции'} /> - } /> - } /> - } key={'drillProcessFlow'} title={'РТК'} /> - } key={'params'} title={'Режимы'} /> - - - - - } /> - } /> + + } /> + } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - + } /> + } /> + } /> + } /> + } /> + } /> + ) }) From 60118f9327099a5a4c2fd80c3a678fb34403c78c Mon Sep 17 00:00:00 2001 From: goodmice Date: Thu, 13 Oct 2022 14:00:17 +0500 Subject: [PATCH 06/28] =?UTF-8?q?*=20=D0=9F=D1=83=D0=B1=D0=BB=D0=B8=D1=87?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D1=81=D1=82=D0=B0=D1=80=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D1=8B=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D1=89=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B2=20=D0=BE=D0=B1=D1=89=D1=83=D1=8E=20=D0=B4=D0=B8?= =?UTF-8?q?=D1=80=D0=B5=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D1=8E=20*=20Suspense?= =?UTF-8?q?Fallback=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D1=89=D1=91?= =?UTF-8?q?=D0=BD=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D1=8B=20*=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BB=D0=B5=D0=BD=D0=B8=D0=B2=D0=B0=D1=8F?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20=D0=BE?= =?UTF-8?q?=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D1=8B=D1=85=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 63 +++++++++++-------- .../SuspenseFallback.tsx | 0 src/pages/{ => public}/AccessDenied.jsx | 0 src/pages/{ => public}/Login.jsx | 0 src/pages/{ => public}/Register.jsx | 0 src/utils/functions/permissions.tsx | 2 +- 6 files changed, 37 insertions(+), 28 deletions(-) rename src/{pages => components}/SuspenseFallback.tsx (100%) mode change 100755 => 100644 rename src/pages/{ => public}/AccessDenied.jsx (100%) mode change 100755 => 100644 rename src/pages/{ => public}/Login.jsx (100%) mode change 100755 => 100644 rename src/pages/{ => public}/Register.jsx (100%) mode change 100755 => 100644 diff --git a/src/App.tsx b/src/App.tsx index d883e77..6b410b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,49 +1,58 @@ import { BrowserRouter as Router, Navigate, Route, Routes } from 'react-router-dom' -import { memo } from 'react' -import { ConfigProvider } from 'antd' +import { lazy, memo, Suspense } from 'react' import locale from 'antd/lib/locale/ru_RU' +import { ConfigProvider } from 'antd' import { RootPathContext } from '@asb/context' +import { UserOutlet } from '@components/outlets' +import SuspenseFallback from '@components/SuspenseFallback' import { getUserToken, NoAccessComponent } from '@utils' import { OpenAPI } from '@api' -import AdminPanel from '@pages/AdminPanel' -import Well from '@pages/Well' -import Login from '@pages/Login' -import Cluster from '@pages/Cluster' -import Deposit from '@pages/Deposit' -import Register from '@pages/Register' -import FileDownload from '@pages/FileDownload' -import '@styles/App.less' +const Login = lazy(() => import('@pages/public/Login')) +const Register = lazy(() => import('@pages/public/Register')) +const FileDownload = lazy(() => import('@pages/FileDownload')) + +const AdminPanel = lazy(() => import('@pages/AdminPanel')) +const Deposit = lazy(() => import('@pages/Deposit')) +const Cluster = lazy(() => import('@pages/Cluster')) +const Well = lazy(() => import('@pages/Well')) + import '@styles/include/antd_theme.less' +import '@styles/App.less' //OpenAPI.BASE = 'http://localhost:3000' OpenAPI.TOKEN = async () => getUserToken() ?? '' -OpenAPI.HEADERS = {'Content-Type': 'application/json'} +OpenAPI.HEADERS = { 'Content-Type': 'application/json' } export const App = memo(() => ( - - - } /> - } /> + }> + + + } /> + } /> - {/* Public pages */} - } /> - } /> + {/* Public pages */} + } /> + } /> - {/* Admin pages */} - } /> + {/* User pages */} + }> + {/* Admin pages */} + } /> - {/* User pages */} - } /> - } /> - } /> - } /> - - + {/* Client pages */} + } /> + } /> + } /> + } /> + + + + )) diff --git a/src/pages/SuspenseFallback.tsx b/src/components/SuspenseFallback.tsx old mode 100755 new mode 100644 similarity index 100% rename from src/pages/SuspenseFallback.tsx rename to src/components/SuspenseFallback.tsx diff --git a/src/pages/AccessDenied.jsx b/src/pages/public/AccessDenied.jsx old mode 100755 new mode 100644 similarity index 100% rename from src/pages/AccessDenied.jsx rename to src/pages/public/AccessDenied.jsx diff --git a/src/pages/Login.jsx b/src/pages/public/Login.jsx old mode 100755 new mode 100644 similarity index 100% rename from src/pages/Login.jsx rename to src/pages/public/Login.jsx diff --git a/src/pages/Register.jsx b/src/pages/public/Register.jsx old mode 100755 new mode 100644 similarity index 100% rename from src/pages/Register.jsx rename to src/pages/public/Register.jsx diff --git a/src/utils/functions/permissions.tsx b/src/utils/functions/permissions.tsx index 274a9c5..baa749d 100644 --- a/src/utils/functions/permissions.tsx +++ b/src/utils/functions/permissions.tsx @@ -4,7 +4,7 @@ import { Navigate, useParams } from 'react-router-dom' import { getUserLogin, getUserPermissions } from '@utils' import { isDev } from '@utils' -import AccessDenied from '@pages/AccessDenied' +import AccessDenied from '@pages/public/AccessDenied' export type Role = string export type Permission = string From bd8962df26eaeff5d1ea3119a6a7cef1a42508c0 Mon Sep 17 00:00:00 2001 From: goodmice Date: Thu, 13 Oct 2022 14:31:11 +0500 Subject: [PATCH 07/28] =?UTF-8?q?*=20=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=20LayoutPortal=20*=20=D0=9F?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8C=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20*=20?= =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D0=B0=20?= =?UTF-8?q?=D0=BE=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D1=81=D1=81=D1=8B=D0=BB=D0=BE=D0=BA=20=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=8E=20*=20=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20LayoutPorta?= =?UTF-8?q?l=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=B2=D1=81=D0=B5=20=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=86=D1=8B=20*=20=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D1=91?= =?UTF-8?q?=D0=BD=20=D1=80=D0=B5=D0=B4=D0=B8=D1=80=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?=D1=81=D0=BE=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Layout/AdminLayoutPortal.tsx | 28 ---- src/components/Layout/LayoutPortal.tsx | 31 ---- src/components/Layout/index.ts | 5 - src/components/LayoutPortal.tsx | 110 ++++++++++++++ src/components/PageHeader.tsx | 23 ++- src/components/Private/PrivateContent.tsx | 14 -- .../Private/PrivateDefaultRoute.tsx | 19 --- src/components/Private/PrivateMenu.tsx | 75 ---------- src/components/Private/PrivateMenuItem.tsx | 37 ----- src/components/Private/PrivateRoute.tsx | 30 ---- src/components/Private/PrivateRoutes.tsx | 67 --------- src/components/Private/index.ts | 13 -- src/components/PrivateWellMenu.tsx | 97 +++++++++++++ src/components/UserMenu.tsx | 136 ++++++++++++------ src/components/selectors/WellTreeSelector.tsx | 45 +++--- src/pages/AdminPanel/AdminNavigationMenu.jsx | 46 ++++++ src/pages/AdminPanel/index.jsx | 54 +++---- src/pages/Cluster/index.jsx | 6 +- src/pages/Deposit.jsx | 7 +- src/pages/FileDownload.jsx | 4 +- src/pages/NavigationMenu.jsx | 78 ++++++++++ src/pages/Well.jsx | 66 +++------ 22 files changed, 503 insertions(+), 488 deletions(-) delete mode 100755 src/components/Layout/AdminLayoutPortal.tsx delete mode 100755 src/components/Layout/LayoutPortal.tsx delete mode 100755 src/components/Layout/index.ts create mode 100644 src/components/LayoutPortal.tsx delete mode 100755 src/components/Private/PrivateContent.tsx delete mode 100755 src/components/Private/PrivateDefaultRoute.tsx delete mode 100644 src/components/Private/PrivateMenu.tsx delete mode 100755 src/components/Private/PrivateMenuItem.tsx delete mode 100755 src/components/Private/PrivateRoute.tsx delete mode 100644 src/components/Private/PrivateRoutes.tsx delete mode 100755 src/components/Private/index.ts create mode 100644 src/components/PrivateWellMenu.tsx create mode 100644 src/pages/AdminPanel/AdminNavigationMenu.jsx create mode 100644 src/pages/NavigationMenu.jsx diff --git a/src/components/Layout/AdminLayoutPortal.tsx b/src/components/Layout/AdminLayoutPortal.tsx deleted file mode 100755 index 1d087ba..0000000 --- a/src/components/Layout/AdminLayoutPortal.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { memo, ReactNode } from 'react' -import { Link, useLocation } from 'react-router-dom' -import { Button, Layout, LayoutProps } from 'antd' - -import PageHeader from '@components/PageHeader' - -export type AdminLayoutPortalProps = LayoutProps & { - title?: ReactNode -} - -export const AdminLayoutPortal = memo(({ title, ...props }) => { - const location = useLocation() - - return ( - - - - - - - - - ) -}) - -export default AdminLayoutPortal diff --git a/src/components/Layout/LayoutPortal.tsx b/src/components/Layout/LayoutPortal.tsx deleted file mode 100755 index e754811..0000000 --- a/src/components/Layout/LayoutPortal.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Key, memo, ReactNode } from 'react' -import { Layout, LayoutProps } from 'antd' - -import PageHeader from '@components/PageHeader' -import { WellTreeSelector, WellTreeSelectorProps } from '@components/selectors/WellTreeSelector' -import { wrapPrivateComponent } from '@utils' - -export type LayoutPortalProps = LayoutProps & { - title?: ReactNode - noSheet?: boolean - selector?: WellTreeSelectorProps -} - -const _LayoutPortal = memo(({ title, noSheet, selector, ...props }) => ( - - - - - - {noSheet ? props.children : ( - - )} - - -)) - -export const LayoutPortal = wrapPrivateComponent(_LayoutPortal, { - requirements: ['Deposit.get'], -}) - -export default LayoutPortal diff --git a/src/components/Layout/index.ts b/src/components/Layout/index.ts deleted file mode 100755 index 044f1bc..0000000 --- a/src/components/Layout/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './AdminLayoutPortal' -export * from './LayoutPortal' - -export type { AdminLayoutPortalProps } from './AdminLayoutPortal' -export type { LayoutPortalProps } from './LayoutPortal' diff --git a/src/components/LayoutPortal.tsx b/src/components/LayoutPortal.tsx new file mode 100644 index 0000000..1bcb344 --- /dev/null +++ b/src/components/LayoutPortal.tsx @@ -0,0 +1,110 @@ +import { Button, Layout, LayoutProps, Menu, SiderProps } from 'antd' +import { HTMLProps, Key, memo, ReactNode, useEffect, useMemo, useState } from 'react' +import { ItemType } from 'antd/lib/menu/hooks/useItems' +import { Link } from 'react-router-dom' +import { + ApartmentOutlined, + CodeOutlined, + HomeOutlined, + MenuFoldOutlined, + MenuUnfoldOutlined, + UserOutlined, +} from '@ant-design/icons' + +import PageHeader from '@components/PageHeader' +import { UserMenu, UserMenuProps } from '@components/UserMenu' +import { WellTreeSelector, WellTreeSelectorProps } from '@components/selectors/WellTreeSelector' +import { isURLAvailable, wrapPrivateComponent } from '@utils' + +import '@styles/layout.less' + +const { Content, Sider } = Layout + +export type LayoutPortalProps = HTMLProps & { + title?: ReactNode + noSheet?: boolean + showSelector?: boolean + selectorProps?: WellTreeSelectorProps + sider?: boolean | JSX.Element + siderProps?: SiderProps & { userMenuProps?: UserMenuProps } + isAdmin?: boolean +} + +const makeItem = (title: string, key: Key, icon: JSX.Element, label?: ReactNode, onClick?: () => void) => ({ icon, key, title, label: label ?? title, onClick }) + +const _LayoutPortal = memo(({ isAdmin, title, noSheet, showSelector, selectorProps, sider, siderProps, ...props }) => { + const [menuCollapsed, setMenuCollapsed] = useState(true) + const [wellsTreeOpen, setWellsTreeOpen] = useState(false) + const [userMenuOpen, setUserMenuOpen] = useState(false) + const [currentWell, setCurrentWell] = useState('') + + useEffect(() => { + if (typeof showSelector === 'boolean') + setWellsTreeOpen(showSelector) + }, [showSelector]) + + const menuItems = useMemo(() => [ + !isAdmin && makeItem(currentWell, 'well', , null, () => setWellsTreeOpen((prev) => !prev)), + isAdmin && makeItem('Вернуться на сайт', 'go_back', , Домой), + !isAdmin && isURLAvailable('/admin') && makeItem('Панель администратора', 'admin_panel', , Панель администратора), + makeItem('Профиль', 'profile', , null, () => setUserMenuOpen((prev) => !prev)), + ].filter(Boolean) as ItemType[], [isAdmin, currentWell]) + + return ( + + {(sider || siderProps) && ( + +
+ +
+ {sider} +
+ + setUserMenuOpen(false)} + isAdmin={isAdmin} + {...siderProps?.userMenuProps} + /> +
+
+ )} + + + {isAdmin ? ( + + ) : ( + <> + setWellsTreeOpen(false)} + {...selectorProps} + onChange={(well) => setCurrentWell(well ?? 'Выберите месторождение')} + /> + + + )} + + +
+ + + + ) +}) + +export const LayoutPortal = wrapPrivateComponent(_LayoutPortal, { + requirements: ['Deposit.get'], +}) + +export default LayoutPortal diff --git a/src/components/PageHeader.tsx b/src/components/PageHeader.tsx index 3dc6c02..9c8ab99 100755 --- a/src/components/PageHeader.tsx +++ b/src/components/PageHeader.tsx @@ -4,27 +4,24 @@ import { Link } from 'react-router-dom' import { BasicProps } from 'antd/lib/layout/layout' import { headerHeight } from '@utils' -import { UserMenu } from './UserMenu' import Logo from '@images/Logo' +import '@styles/layout.less' + export type PageHeaderProps = BasicProps & { title?: string - isAdmin?: boolean children?: React.ReactNode } -export const PageHeader: React.FC = memo(({ title = 'Мониторинг', isAdmin, children, ...other }) => ( - - - - - -

{title}

- {children} - -
-
+export const PageHeader: React.FC = memo(({ title = 'Мониторинг', children, ...other }) => ( + + + + +

{title}

+ {children} +
)) export default PageHeader diff --git a/src/components/Private/PrivateContent.tsx b/src/components/Private/PrivateContent.tsx deleted file mode 100755 index 9b9e02b..0000000 --- a/src/components/Private/PrivateContent.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { memo, ReactElement } from 'react' - -import { isURLAvailable } from '@utils' - -export type PrivateContentProps = { - absolutePath: string - children?: ReactElement -} - -export const PrivateContent = memo(({ absolutePath, children = null }) => - isURLAvailable(absolutePath) ? children : null -) - -export default PrivateContent diff --git a/src/components/Private/PrivateDefaultRoute.tsx b/src/components/Private/PrivateDefaultRoute.tsx deleted file mode 100755 index e1cc07e..0000000 --- a/src/components/Private/PrivateDefaultRoute.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { memo } from 'react' -import { Navigate, Route, RouteProps } from 'react-router-dom' - -import { isURLAvailable } from '@utils' - -import { getDefaultRedirectPath } from './PrivateRoutes' - -export type PrivateDefaultRouteProps = RouteProps & { - urls: string[] - elseRedirect?: string -} - -export const PrivateDefaultRoute = memo(({ elseRedirect, urls, ...other }) => ( - isURLAvailable(url)) ?? elseRedirect ?? getDefaultRedirectPath()} /> - )} /> -)) - -export default PrivateDefaultRoute diff --git a/src/components/Private/PrivateMenu.tsx b/src/components/Private/PrivateMenu.tsx deleted file mode 100644 index 8913560..0000000 --- a/src/components/Private/PrivateMenu.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { join } from 'path' -import { Menu, MenuProps } from 'antd' -import { ItemType } from 'antd/lib/menu/hooks/useItems' -import { Link, LinkProps } from 'react-router-dom' -import { Children, isValidElement, memo, ReactNode, RefAttributes, useMemo } from 'react' - -import { useRootPath } from '@asb/context' -import { getTabname, hasPermission, PrivateComponent, PrivateProps } from '@utils' - -export type PrivateMenuProps = MenuProps & { root?: string } - -export type PrivateMenuLinkProps = Partial & Omit & RefAttributes & { - icon?: ReactNode - danger?: boolean - title?: ReactNode - content?: PrivateComponent - path?: string - visible?: boolean - permissions?: string[] -} - -export const PrivateMenuLink = memo(({ content, path = '', title, ...other }) => ( - {title ?? content?.title} -)) - -const PrivateMenuMain = memo(({ selectable, mode, selectedKeys, root, children, ...other }) => { - const rootContext = useRootPath() - const rootPath = useMemo(() => root ?? rootContext ?? '', [root, rootContext]) - - const tab = getTabname() - const keys = useMemo(() => selectedKeys ?? (tab ? [tab] : []), [selectedKeys, tab]) - - const items = useMemo(() => Children.map(children, (child) => { - if (!child || !isValidElement(child)) - return null - const content: PrivateProps | undefined = child.props.content - const visible: boolean | undefined = child.props.visible - - if (visible === false) return null - let key - if (content?.key) - key = content.key - else if (content?.route) - key = content.route - else if (child.key) { - key = child.key?.toString() - key = key.slice(key.lastIndexOf('$') + 1) - } else return null - - const permissions = child.props.permissions ?? content?.requirements - const path = child.props.path ?? join(rootPath, key) - - if (visible || hasPermission(permissions)) - return { - ...child.props, - key, - label: , - } - return null - })?.filter((v) => v) ?? [], [children, rootPath]) - - return ( - - ) -}) - -export const PrivateMenu = Object.assign(PrivateMenuMain, { Link: PrivateMenuLink }) - -export default PrivateMenu diff --git a/src/components/Private/PrivateMenuItem.tsx b/src/components/Private/PrivateMenuItem.tsx deleted file mode 100755 index e549625..0000000 --- a/src/components/Private/PrivateMenuItem.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { join } from 'path' -import { Menu, MenuItemProps } from 'antd' -import { memo, NamedExoticComponent } from 'react' -import { Link, useLocation } from 'react-router-dom' - -import { isURLAvailable } from '@utils' - -export type PrivateMenuItemProps = MenuItemProps & { - root: string - path: string -} - -export type PrivateMenuItemLinkProps = MenuItemProps & { - root?: string - path: string - title: string -} - -export const PrivateMenuItemLink = memo(({ root = '', path, title, ...other }) => { - const location = useLocation() - return ( - - {title} - - ) -}) - -export const PrivateMenuItem: NamedExoticComponent & { - Link: NamedExoticComponent -} = Object.assign(memo(({ root, path, ...other }) => -