Compare commits
18 Commits
90be3b5aa7
...
ab9f058b0a
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ab9f058b0a | ||
![]() |
a34af217eb | ||
![]() |
aae57d51f1 | ||
![]() |
75c0a6f848 | ||
![]() |
483fc93b58 | ||
![]() |
1b62c8fbc2 | ||
![]() |
1f73941866 | ||
![]() |
c58a8ec969 | ||
![]() |
ef463897c4 | ||
![]() |
4fe0936b05 | ||
![]() |
e5c595e99e | ||
![]() |
4b450384c4 | ||
![]() |
cdcf1a9c43 | ||
![]() |
d5854366a9 | ||
![]() |
3c92e07d0f | ||
![]() |
6bb02c6462 | ||
![]() |
53a795d3ec | ||
![]() |
1ab4ce2556 |
|
@ -63,6 +63,7 @@ This readme file is used mostly to document project dependencies and conventions
|
||||||
- tailwindcss
|
- tailwindcss
|
||||||
- postcss
|
- postcss
|
||||||
- autoprefixer
|
- autoprefixer
|
||||||
|
- eslint-plugin-import
|
||||||
- eslint-plugin-react-compiler
|
- eslint-plugin-react-compiler
|
||||||
- eslint-plugin-simple-import-sort
|
- eslint-plugin-simple-import-sort
|
||||||
- eslint-plugin-react-hooks
|
- eslint-plugin-react-hooks
|
||||||
|
|
|
@ -4,7 +4,7 @@ import typescriptParser from '@typescript-eslint/parser';
|
||||||
import reactPlugin from 'eslint-plugin-react';
|
import reactPlugin from 'eslint-plugin-react';
|
||||||
import reactCompilerPlugin from 'eslint-plugin-react-compiler';
|
import reactCompilerPlugin from 'eslint-plugin-react-compiler';
|
||||||
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
||||||
|
import importPlugin from 'eslint-plugin-import';
|
||||||
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
|
@ -37,7 +37,8 @@ export default [
|
||||||
'react': reactPlugin,
|
'react': reactPlugin,
|
||||||
'react-compiler': reactCompilerPlugin,
|
'react-compiler': reactCompilerPlugin,
|
||||||
'react-hooks': reactHooksPlugin,
|
'react-hooks': reactHooksPlugin,
|
||||||
'simple-import-sort': simpleImportSort
|
'simple-import-sort': simpleImportSort,
|
||||||
|
'import': importPlugin
|
||||||
},
|
},
|
||||||
settings: { react: { version: 'detect' } },
|
settings: { react: { version: 'detect' } },
|
||||||
rules: {
|
rules: {
|
||||||
|
@ -57,8 +58,33 @@ export default [
|
||||||
|
|
||||||
'react-refresh/only-export-components': ['off', { allowConstantExport: true }],
|
'react-refresh/only-export-components': ['off', { allowConstantExport: true }],
|
||||||
|
|
||||||
'simple-import-sort/imports': 'warn',
|
'simple-import-sort/imports': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
groups: [
|
||||||
|
// Node.js builtins.
|
||||||
|
[
|
||||||
|
'^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)'
|
||||||
|
],
|
||||||
|
// Packages. `react` related packages come first.
|
||||||
|
['^react', '^@?\\w'],
|
||||||
|
// Global app and features
|
||||||
|
['^(@/app|@/features)(/.*|$)'],
|
||||||
|
// Internal packages.
|
||||||
|
['^(@)(/.*|$)'],
|
||||||
|
// Side effect imports.
|
||||||
|
['^\\u0000'],
|
||||||
|
// Parent imports. Put `..` last.
|
||||||
|
['^\\.\\.(?!/?$)', '^\\.\\./?$'],
|
||||||
|
// Other relative imports. Put same-folder imports and `.` last.
|
||||||
|
['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
|
||||||
|
// Style imports.
|
||||||
|
['^.+\\.s?css$']
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
'simple-import-sort/exports': 'error',
|
'simple-import-sort/exports': 'error',
|
||||||
|
'import/no-duplicates': 'warn',
|
||||||
|
|
||||||
...reactHooksPlugin.configs.recommended.rules
|
...reactHooksPlugin.configs.recommended.rules
|
||||||
}
|
}
|
||||||
|
|
957
rsconcept/frontend/package-lock.json
generated
957
rsconcept/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -9,20 +9,21 @@
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"lintFix": "eslint . --report-unused-disable-directives --max-warnings 0 --fix",
|
||||||
"preview": "vite preview --port 3000"
|
"preview": "vite preview --port 3000"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
"@hookform/resolvers": "^3.10.0",
|
"@hookform/resolvers": "^4.1.0",
|
||||||
"@lezer/lr": "^1.4.2",
|
"@lezer/lr": "^1.4.2",
|
||||||
"@tanstack/react-query": "^5.66.0",
|
"@tanstack/react-query": "^5.66.0",
|
||||||
"@tanstack/react-query-devtools": "^5.66.0",
|
"@tanstack/react-query-devtools": "^5.66.0",
|
||||||
"@tanstack/react-table": "^8.20.6",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"@uiw/codemirror-themes": "^4.23.8",
|
"@uiw/codemirror-themes": "^4.23.8",
|
||||||
"@uiw/react-codemirror": "^4.23.8",
|
"@uiw/react-codemirror": "^4.23.8",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.13",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
@ -30,7 +31,7 @@
|
||||||
"react-error-boundary": "^5.0.0",
|
"react-error-boundary": "^5.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-icons": "^5.4.0",
|
"react-icons": "^5.4.0",
|
||||||
"react-intl": "^7.1.5",
|
"react-intl": "^7.1.6",
|
||||||
"react-router": "^7.1.5",
|
"react-router": "^7.1.5",
|
||||||
"react-select": "^5.10.0",
|
"react-select": "^5.10.0",
|
||||||
"react-tabs": "^6.1.0",
|
"react-tabs": "^6.1.0",
|
||||||
|
@ -39,33 +40,34 @@
|
||||||
"react-zoom-pan-pinch": "^3.7.0",
|
"react-zoom-pan-pinch": "^3.7.0",
|
||||||
"reactflow": "^11.11.4",
|
"reactflow": "^11.11.4",
|
||||||
"use-debounce": "^10.0.4",
|
"use-debounce": "^10.0.4",
|
||||||
"zod": "^3.24.1",
|
"zod": "^3.24.2",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lezer/generator": "^1.7.2",
|
"@lezer/generator": "^1.7.2",
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.50.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.13.1",
|
"@types/node": "^22.13.4",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react": "^19.0.8",
|
||||||
"@types/react-dom": "^19.0.3",
|
"@types/react-dom": "^19.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||||
"@typescript-eslint/parser": "^8.0.1",
|
"@typescript-eslint/parser": "^8.0.1",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"babel-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206",
|
"babel-plugin-react-compiler": "^19.0.0-beta-30d8a17-20250209",
|
||||||
"eslint": "^9.19.0",
|
"eslint": "^9.20.1",
|
||||||
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-react": "^7.37.4",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
"eslint-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206",
|
"eslint-plugin-react-compiler": "^19.0.0-beta-30d8a17-20250209",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0",
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"globals": "^15.14.0",
|
"globals": "^15.15.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.2",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"ts-jest": "^29.2.5",
|
"ts-jest": "^29.2.5",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.23.0",
|
"typescript-eslint": "^8.24.0",
|
||||||
"vite": "^6.1.0"
|
"vite": "^6.1.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import { Outlet } from 'react-router';
|
import { Outlet } from 'react-router';
|
||||||
|
|
||||||
import { Loader } from '@/components/Loader';
|
|
||||||
import { ModalLoader } from '@/components/Modal';
|
import { ModalLoader } from '@/components/Modal';
|
||||||
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
|
import { NavigationState } from './Navigation/NavigationContext';
|
||||||
import { Footer } from './Footer';
|
import { Footer } from './Footer';
|
||||||
import { GlobalDialogs } from './GlobalDialogs';
|
import { GlobalDialogs } from './GlobalDialogs';
|
||||||
import ConceptToaster from './GlobalToaster';
|
import { GlobalLoader } from './GlobalLoader';
|
||||||
|
import { ToasterThemed } from './GlobalToaster';
|
||||||
import { GlobalTooltips } from './GlobalTooltips';
|
import { GlobalTooltips } from './GlobalTooltips';
|
||||||
import { Navigation } from './Navigation';
|
import { Navigation } from './Navigation';
|
||||||
import { NavigationState } from './Navigation/NavigationContext';
|
|
||||||
|
|
||||||
function ApplicationLayout() {
|
function ApplicationLayout() {
|
||||||
const mainHeight = useMainHeight();
|
const mainHeight = useMainHeight();
|
||||||
|
@ -24,7 +24,7 @@ function ApplicationLayout() {
|
||||||
return (
|
return (
|
||||||
<NavigationState>
|
<NavigationState>
|
||||||
<div className='min-w-[20rem] antialiased h-full max-w-[120rem] mx-auto'>
|
<div className='min-w-[20rem] antialiased h-full max-w-[120rem] mx-auto'>
|
||||||
<ConceptToaster
|
<ToasterThemed
|
||||||
className='text-[14px] cc-animate-position'
|
className='text-[14px] cc-animate-position'
|
||||||
style={{ marginTop: noNavigationAnimation ? '1.5rem' : '3.5rem' }}
|
style={{ marginTop: noNavigationAnimation ? '1.5rem' : '3.5rem' }}
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
|
@ -47,9 +47,8 @@ function ApplicationLayout() {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<main className='cc-scroll-y' style={{ overflowY: showScroll ? 'scroll' : 'auto', minHeight: mainHeight }}>
|
<main className='cc-scroll-y' style={{ overflowY: showScroll ? 'scroll' : 'auto', minHeight: mainHeight }}>
|
||||||
<Suspense fallback={<Loader />}>
|
<GlobalLoader />
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Suspense>
|
|
||||||
</main>
|
</main>
|
||||||
{!noNavigation && !noFooter ? <Footer /> : null}
|
{!noNavigation && !noFooter ? <Footer /> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,17 +6,17 @@ import { DialogType, useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
const DlgChangeInputSchema = React.lazy(() => import('@/features/oss/dialogs/DlgChangeInputSchema'));
|
const DlgChangeInputSchema = React.lazy(() => import('@/features/oss/dialogs/DlgChangeInputSchema'));
|
||||||
const DlgChangeLocation = React.lazy(() => import('@/features/library/dialogs/DlgChangeLocation'));
|
const DlgChangeLocation = React.lazy(() => import('@/features/library/dialogs/DlgChangeLocation'));
|
||||||
const DlgCloneLibraryItem = React.lazy(() => import('@/features/rsform/dialogs/DlgCloneLibraryItem'));
|
const DlgCloneLibraryItem = React.lazy(() => import('@/features/library/dialogs/DlgCloneLibraryItem'));
|
||||||
const DlgCreateCst = React.lazy(() => import('@/features/rsform/dialogs/DlgCreateCst'));
|
const DlgCreateCst = React.lazy(() => import('@/features/rsform/dialogs/DlgCreateCst'));
|
||||||
const DlgCreateOperation = React.lazy(() => import('@/features/oss/dialogs/DlgCreateOperation'));
|
const DlgCreateOperation = React.lazy(() => import('@/features/oss/dialogs/DlgCreateOperation'));
|
||||||
const DlgCreateVersion = React.lazy(() => import('@/features/rsform/dialogs/DlgCreateVersion'));
|
const DlgCreateVersion = React.lazy(() => import('@/features/library/dialogs/DlgCreateVersion'));
|
||||||
const DlgCstTemplate = React.lazy(() => import('@/features/rsform/dialogs/DlgCstTemplate'));
|
const DlgCstTemplate = React.lazy(() => import('@/features/rsform/dialogs/DlgCstTemplate'));
|
||||||
const DlgDeleteCst = React.lazy(() => import('@/features/rsform/dialogs/DlgDeleteCst'));
|
const DlgDeleteCst = React.lazy(() => import('@/features/rsform/dialogs/DlgDeleteCst'));
|
||||||
const DlgDeleteOperation = React.lazy(() => import('@/features/oss/dialogs/DlgDeleteOperation'));
|
const DlgDeleteOperation = React.lazy(() => import('@/features/oss/dialogs/DlgDeleteOperation'));
|
||||||
const DlgEditEditors = React.lazy(() => import('@/features/library/dialogs/DlgEditEditors'));
|
const DlgEditEditors = React.lazy(() => import('@/features/library/dialogs/DlgEditEditors'));
|
||||||
const DlgEditOperation = React.lazy(() => import('@/features/oss/dialogs/DlgEditOperation'));
|
const DlgEditOperation = React.lazy(() => import('@/features/oss/dialogs/DlgEditOperation'));
|
||||||
const DlgEditReference = React.lazy(() => import('@/features/rsform/dialogs/DlgEditReference'));
|
const DlgEditReference = React.lazy(() => import('@/features/rsform/dialogs/DlgEditReference'));
|
||||||
const DlgEditVersions = React.lazy(() => import('@/features/rsform/dialogs/DlgEditVersions'));
|
const DlgEditVersions = React.lazy(() => import('@/features/library/dialogs/DlgEditVersions'));
|
||||||
const DlgEditWordForms = React.lazy(() => import('@/features/rsform/dialogs/DlgEditWordForms'));
|
const DlgEditWordForms = React.lazy(() => import('@/features/rsform/dialogs/DlgEditWordForms'));
|
||||||
const DlgGraphParams = React.lazy(() => import('@/features/rsform/dialogs/DlgGraphParams'));
|
const DlgGraphParams = React.lazy(() => import('@/features/rsform/dialogs/DlgGraphParams'));
|
||||||
const DlgInlineSynthesis = React.lazy(() => import('@/features/rsform/dialogs/DlgInlineSynthesis'));
|
const DlgInlineSynthesis = React.lazy(() => import('@/features/rsform/dialogs/DlgInlineSynthesis'));
|
||||||
|
|
34
rsconcept/frontend/src/app/GlobalLoader.tsx
Normal file
34
rsconcept/frontend/src/app/GlobalLoader.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { useNavigation } from 'react-router';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { useDebounce } from 'use-debounce';
|
||||||
|
|
||||||
|
import { Loader } from '@/components/Loader';
|
||||||
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
|
// TODO: add animation
|
||||||
|
export function GlobalLoader() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const isLoading = navigation.state === 'loading';
|
||||||
|
const [loadingDebounced] = useDebounce(isLoading, PARAMETER.navigationPopupDelay);
|
||||||
|
|
||||||
|
if (!loadingDebounced) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
|
||||||
|
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'backdrop-blur-[3px] opacity-50')} />
|
||||||
|
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')} />
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'px-10 mb-10',
|
||||||
|
'z-modal absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
|
||||||
|
'border rounded-xl bg-prim-100'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Loader scale={6} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { IntlProvider } from 'react-intl';
|
||||||
import { QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
|
|
||||||
import { queryClient } from '@/backend/queryClient';
|
import { queryClient } from '@/backend/queryClient';
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,7 @@ import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
interface ToasterThemedProps extends Omit<ToastContainerProps, 'theme'> {}
|
interface ToasterThemedProps extends Omit<ToastContainerProps, 'theme'> {}
|
||||||
|
|
||||||
function ToasterThemed(props: ToasterThemedProps) {
|
export function ToasterThemed(props: ToasterThemedProps) {
|
||||||
const darkMode = usePreferencesStore(state => state.darkMode);
|
const darkMode = usePreferencesStore(state => state.darkMode);
|
||||||
return <ToastContainer theme={darkMode ? 'dark' : 'light'} {...props} />;
|
return <ToastContainer theme={darkMode ? 'dark' : 'light'} {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ToasterThemed;
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import InfoConstituenta from '@/features/rsform/components/InfoConstituenta';
|
||||||
|
|
||||||
import { Tooltip } from '@/components/Container';
|
import { Tooltip } from '@/components/Container';
|
||||||
import { Loader } from '@/components/Loader';
|
import { Loader } from '@/components/Loader';
|
||||||
import InfoConstituenta from '@/features/rsform/components/InfoConstituenta';
|
|
||||||
import { useTooltipsStore } from '@/stores/tooltips';
|
import { useTooltipsStore } from '@/stores/tooltips';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { useAppLayoutStore } from '@/stores/appLayout';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
|
|
||||||
import Logo from './Logo';
|
import Logo from './Logo';
|
||||||
import NavigationButton from './NavigationButton';
|
import NavigationButton from './NavigationButton';
|
||||||
import { useConceptNavigation } from './NavigationContext';
|
import { useConceptNavigation } from './NavigationContext';
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { IconLogin, IconUser2 } from '@/components/Icons';
|
import { IconLogin, IconUser2 } from '@/components/Icons';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
import NavigationButton from './NavigationButton';
|
import NavigationButton from './NavigationButton';
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { useAuthSuspense, useLogout } from '@/features/auth';
|
||||||
|
|
||||||
import { Dropdown, DropdownButton } from '@/components/Dropdown';
|
import { Dropdown, DropdownButton } from '@/components/Dropdown';
|
||||||
import {
|
import {
|
||||||
IconAdmin,
|
IconAdmin,
|
||||||
|
@ -14,11 +16,10 @@ import {
|
||||||
IconUser
|
IconUser
|
||||||
} from '@/components/Icons';
|
} from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
|
||||||
import { useLogout } from '@/features/auth/backend/useLogout';
|
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
|
|
||||||
import { useConceptNavigation } from './NavigationContext';
|
import { useConceptNavigation } from './NavigationContext';
|
||||||
|
|
||||||
interface UserDropdownProps {
|
interface UserDropdownProps {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useDropdown } from '@/components/Dropdown';
|
||||||
import { Loader } from '@/components/Loader';
|
import { Loader } from '@/components/Loader';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
|
|
||||||
import { useConceptNavigation } from './NavigationContext';
|
import { useConceptNavigation } from './NavigationContext';
|
||||||
import UserButton from './UserButton';
|
import UserButton from './UserButton';
|
||||||
import UserDropdown from './UserDropdown';
|
import UserDropdown from './UserDropdown';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { createBrowserRouter } from 'react-router';
|
import { createBrowserRouter } from 'react-router';
|
||||||
|
|
||||||
import { Loader } from '@/components/Loader';
|
|
||||||
import { prefetchAuth } from '@/features/auth/backend/useAuth';
|
import { prefetchAuth } from '@/features/auth/backend/useAuth';
|
||||||
import LoginPage from '@/features/auth/pages/LoginPage';
|
import LoginPage from '@/features/auth/pages/LoginPage';
|
||||||
import HomePage from '@/features/home/HomePage';
|
import HomePage from '@/features/home/HomePage';
|
||||||
|
@ -12,6 +11,8 @@ import { prefetchRSForm } from '@/features/rsform/backend/useRSForm';
|
||||||
import { prefetchProfile } from '@/features/users/backend/useProfile';
|
import { prefetchProfile } from '@/features/users/backend/useProfile';
|
||||||
import { prefetchUsers } from '@/features/users/backend/useUsers';
|
import { prefetchUsers } from '@/features/users/backend/useUsers';
|
||||||
|
|
||||||
|
import { Loader } from '@/components/Loader';
|
||||||
|
|
||||||
import ApplicationLayout from './ApplicationLayout';
|
import ApplicationLayout from './ApplicationLayout';
|
||||||
import { ErrorFallback } from './ErrorFallback';
|
import { ErrorFallback } from './ErrorFallback';
|
||||||
import { routes } from './urls';
|
import { routes } from './urls';
|
||||||
|
@ -22,7 +23,7 @@ export const Router = createBrowserRouter([
|
||||||
element: <ApplicationLayout />,
|
element: <ApplicationLayout />,
|
||||||
errorElement: <ErrorFallback />,
|
errorElement: <ErrorFallback />,
|
||||||
loader: prefetchAuth,
|
loader: prefetchAuth,
|
||||||
hydrateFallbackElement: <Loader />,
|
hydrateFallbackElement: fallbackLoader(),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
|
@ -98,3 +99,11 @@ function parseRSFormURL(id: string | undefined, url: string) {
|
||||||
function parseOssURL(id: string | undefined) {
|
function parseOssURL(id: string | undefined) {
|
||||||
return { itemID: id ? Number(id) : undefined };
|
return { itemID: id ? Number(id) : undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fallbackLoader() {
|
||||||
|
return (
|
||||||
|
<div className='flex justify-center items-center h-[100dvh]'>
|
||||||
|
<Loader scale={6} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
/**
|
/**
|
||||||
* Module: generic API for backend REST communications using axios library.
|
* Module: generic API for backend REST communications using axios library.
|
||||||
*/
|
*/
|
||||||
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
|
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
|
||||||
|
import { z, ZodError } from 'zod';
|
||||||
|
|
||||||
import { buildConstants } from '@/utils/buildConstants';
|
import { buildConstants } from '@/utils/buildConstants';
|
||||||
|
import { errorMsg } from '@/utils/labels';
|
||||||
import { extractErrorMessage } from '@/utils/utils';
|
import { extractErrorMessage } from '@/utils/utils';
|
||||||
|
|
||||||
export { AxiosError } from 'axios';
|
export { AxiosError } from 'axios';
|
||||||
|
@ -41,23 +43,32 @@ export interface IAxiosRequest<RequestData, ResponseData> {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
request?: IFrontRequest<RequestData, ResponseData>;
|
request?: IFrontRequest<RequestData, ResponseData>;
|
||||||
options?: AxiosRequestConfig;
|
options?: AxiosRequestConfig;
|
||||||
|
schema?: z.ZodType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAxiosGetRequest {
|
export interface IAxiosGetRequest {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
options?: AxiosRequestConfig;
|
options?: AxiosRequestConfig;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
|
schema?: z.ZodType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================ Transport API calls ================
|
// ================ Transport API calls ================
|
||||||
export function axiosGet<ResponseData>({ endpoint, options }: IAxiosGetRequest) {
|
export function axiosGet<ResponseData>({ endpoint, options, schema }: IAxiosGetRequest) {
|
||||||
return axiosInstance
|
return axiosInstance
|
||||||
.get<ResponseData>(endpoint, options)
|
.get<ResponseData>(endpoint, options)
|
||||||
.then(response => response.data)
|
.then(response => {
|
||||||
|
schema?.parse(response.data);
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
.catch((error: Error | AxiosError) => {
|
.catch((error: Error | AxiosError) => {
|
||||||
|
// Note: Ignore cancellation errors
|
||||||
if (error.name !== 'CanceledError') {
|
if (error.name !== 'CanceledError') {
|
||||||
// Note: Ignore cancellation errors
|
if (error instanceof ZodError) {
|
||||||
toast.error(extractErrorMessage(error));
|
toast.error(errorMsg.invalidResponse);
|
||||||
|
} else {
|
||||||
|
toast.error(extractErrorMessage(error));
|
||||||
|
}
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -67,11 +78,13 @@ export function axiosGet<ResponseData>({ endpoint, options }: IAxiosGetRequest)
|
||||||
export function axiosPost<RequestData, ResponseData = void>({
|
export function axiosPost<RequestData, ResponseData = void>({
|
||||||
endpoint,
|
endpoint,
|
||||||
request,
|
request,
|
||||||
options
|
options,
|
||||||
|
schema
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
return axiosInstance
|
return axiosInstance
|
||||||
.post<ResponseData>(endpoint, request?.data, options)
|
.post<ResponseData>(endpoint, request?.data, options)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
schema?.parse(response.data);
|
||||||
if (request?.successMessage) {
|
if (request?.successMessage) {
|
||||||
if (typeof request.successMessage === 'string') {
|
if (typeof request.successMessage === 'string') {
|
||||||
toast.success(request.successMessage);
|
toast.success(request.successMessage);
|
||||||
|
@ -81,8 +94,12 @@ export function axiosPost<RequestData, ResponseData = void>({
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
})
|
})
|
||||||
.catch((error: Error | AxiosError) => {
|
.catch((error: Error | AxiosError | ZodError) => {
|
||||||
toast.error(extractErrorMessage(error));
|
if (error instanceof ZodError) {
|
||||||
|
toast.error(errorMsg.invalidResponse);
|
||||||
|
} else {
|
||||||
|
toast.error(extractErrorMessage(error));
|
||||||
|
}
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
@ -91,11 +108,13 @@ export function axiosPost<RequestData, ResponseData = void>({
|
||||||
export function axiosDelete<RequestData, ResponseData = void>({
|
export function axiosDelete<RequestData, ResponseData = void>({
|
||||||
endpoint,
|
endpoint,
|
||||||
request,
|
request,
|
||||||
options
|
options,
|
||||||
|
schema
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
return axiosInstance
|
return axiosInstance
|
||||||
.delete<ResponseData>(endpoint, options)
|
.delete<ResponseData>(endpoint, options)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
schema?.parse(response.data);
|
||||||
if (request?.successMessage) {
|
if (request?.successMessage) {
|
||||||
if (typeof request.successMessage === 'string') {
|
if (typeof request.successMessage === 'string') {
|
||||||
toast.success(request.successMessage);
|
toast.success(request.successMessage);
|
||||||
|
@ -105,8 +124,12 @@ export function axiosDelete<RequestData, ResponseData = void>({
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
})
|
})
|
||||||
.catch((error: Error | AxiosError) => {
|
.catch((error: Error | AxiosError | ZodError) => {
|
||||||
toast.error(extractErrorMessage(error));
|
if (error instanceof ZodError) {
|
||||||
|
toast.error(errorMsg.invalidResponse);
|
||||||
|
} else {
|
||||||
|
toast.error(extractErrorMessage(error));
|
||||||
|
}
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
@ -115,11 +138,13 @@ export function axiosDelete<RequestData, ResponseData = void>({
|
||||||
export function axiosPatch<RequestData, ResponseData = void>({
|
export function axiosPatch<RequestData, ResponseData = void>({
|
||||||
endpoint,
|
endpoint,
|
||||||
request,
|
request,
|
||||||
options
|
options,
|
||||||
|
schema
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
return axiosInstance
|
return axiosInstance
|
||||||
.patch<ResponseData>(endpoint, request?.data, options)
|
.patch<ResponseData>(endpoint, request?.data, options)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
schema?.parse(response.data);
|
||||||
if (request?.successMessage) {
|
if (request?.successMessage) {
|
||||||
if (typeof request.successMessage === 'string') {
|
if (typeof request.successMessage === 'string') {
|
||||||
toast.success(request.successMessage);
|
toast.success(request.successMessage);
|
||||||
|
@ -129,8 +154,12 @@ export function axiosPatch<RequestData, ResponseData = void>({
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
})
|
})
|
||||||
.catch((error: Error | AxiosError) => {
|
.catch((error: Error | AxiosError | ZodError) => {
|
||||||
toast.error(extractErrorMessage(error));
|
if (error instanceof ZodError) {
|
||||||
|
toast.error(errorMsg.invalidResponse);
|
||||||
|
} else {
|
||||||
|
toast.error(extractErrorMessage(error));
|
||||||
|
}
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,3 +7,18 @@ export const DELAYS = {
|
||||||
staleMedium: 1 * 60 * 60 * 1000,
|
staleMedium: 1 * 60 * 60 * 1000,
|
||||||
staleLong: 24 * 60 * 60 * 1000
|
staleLong: 24 * 60 * 60 * 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** API keys for local cache. */
|
||||||
|
export const KEYS = {
|
||||||
|
oss: 'oss',
|
||||||
|
rsform: 'rsform',
|
||||||
|
library: 'library',
|
||||||
|
users: 'users',
|
||||||
|
cctext: 'cctext',
|
||||||
|
|
||||||
|
composite: {
|
||||||
|
libraryList: ['library', 'list'],
|
||||||
|
ossItem: ({ itemID }: { itemID?: number }) => [KEYS.oss, 'item', itemID],
|
||||||
|
rsItem: ({ itemID, version }: { itemID?: number; version?: number }) => [KEYS.rsform, 'item', itemID, version ?? '']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { ITooltip, Tooltip as TooltipImpl } from 'react-tooltip';
|
import { ITooltip, Tooltip as TooltipImpl } from 'react-tooltip';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
'use no memo';
|
'use no memo';
|
||||||
|
|
||||||
|
import { useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
ColumnSort,
|
ColumnSort,
|
||||||
createColumnHelper,
|
createColumnHelper,
|
||||||
|
@ -15,9 +16,9 @@ import {
|
||||||
useReactTable,
|
useReactTable,
|
||||||
type VisibilityState
|
type VisibilityState
|
||||||
} from '@tanstack/react-table';
|
} from '@tanstack/react-table';
|
||||||
import { useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
import DefaultNoData from './DefaultNoData';
|
import DefaultNoData from './DefaultNoData';
|
||||||
import PaginationTools from './PaginationTools';
|
import PaginationTools from './PaginationTools';
|
||||||
import TableBody from './TableBody';
|
import TableBody from './TableBody';
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
'use client';
|
'use client';
|
||||||
'use no memo';
|
'use no memo';
|
||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
import { Table } from '@tanstack/react-table';
|
import { Table } from '@tanstack/react-table';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { Cell, flexRender, Row, Table } from '@tanstack/react-table';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
import { IConditionalStyle } from '.';
|
|
||||||
import SelectRow from './SelectRow';
|
import SelectRow from './SelectRow';
|
||||||
|
import { IConditionalStyle } from '.';
|
||||||
|
|
||||||
interface TableBodyProps<TData> {
|
interface TableBodyProps<TData> {
|
||||||
table: Table<TData>;
|
table: Table<TData>;
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
import { AccessPolicy, LibraryItemType, LocationHead } from '@/features/library/models/library';
|
import { LocationHead } from '@/features/library/models/library';
|
||||||
import { CstType, ExpressionStatus } from '@/features/rsform/models/rsform';
|
import { ExpressionStatus } from '@/features/rsform/models/rsform';
|
||||||
import { CstMatchMode, DependencyMode } from '@/features/rsform/stores/cstSearch';
|
import { CstMatchMode, DependencyMode } from '@/features/rsform/stores/cstSearch';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IconAlias,
|
IconAlias,
|
||||||
IconBusiness,
|
IconBusiness,
|
||||||
IconCstAxiom,
|
|
||||||
IconCstBaseSet,
|
|
||||||
IconCstConstSet,
|
|
||||||
IconCstFunction,
|
|
||||||
IconCstPredicate,
|
|
||||||
IconCstStructured,
|
|
||||||
IconCstTerm,
|
|
||||||
IconCstTheorem,
|
|
||||||
IconFilter,
|
IconFilter,
|
||||||
IconFormula,
|
IconFormula,
|
||||||
IconGraphCollapse,
|
IconGraphCollapse,
|
||||||
|
@ -22,12 +14,8 @@ import {
|
||||||
IconHide,
|
IconHide,
|
||||||
IconMoveDown,
|
IconMoveDown,
|
||||||
IconMoveUp,
|
IconMoveUp,
|
||||||
IconOSS,
|
|
||||||
IconPrivate,
|
|
||||||
IconProps,
|
IconProps,
|
||||||
IconProtected,
|
|
||||||
IconPublic,
|
IconPublic,
|
||||||
IconRSForm,
|
|
||||||
IconSettings,
|
IconSettings,
|
||||||
IconShow,
|
IconShow,
|
||||||
IconStatusError,
|
IconStatusError,
|
||||||
|
@ -45,28 +33,6 @@ export interface DomIconProps<RequestData> extends IconProps {
|
||||||
value: RequestData;
|
value: RequestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Icon for library item type. */
|
|
||||||
export function ItemTypeIcon({ value, size = '1.25rem', className }: DomIconProps<LibraryItemType>) {
|
|
||||||
switch (value) {
|
|
||||||
case LibraryItemType.RSFORM:
|
|
||||||
return <IconRSForm size={size} className={className ?? 'text-sec-600'} />;
|
|
||||||
case LibraryItemType.OSS:
|
|
||||||
return <IconOSS size={size} className={className ?? 'text-ok-600'} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Icon for access policy. */
|
|
||||||
export function PolicyIcon({ value, size = '1.25rem', className }: DomIconProps<AccessPolicy>) {
|
|
||||||
switch (value) {
|
|
||||||
case AccessPolicy.PRIVATE:
|
|
||||||
return <IconPrivate size={size} className={className ?? 'text-warn-600'} />;
|
|
||||||
case AccessPolicy.PROTECTED:
|
|
||||||
return <IconProtected size={size} className={className ?? 'text-sec-600'} />;
|
|
||||||
case AccessPolicy.PUBLIC:
|
|
||||||
return <IconPublic size={size} className={className ?? 'text-ok-600'} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Icon for visibility. */
|
/** Icon for visibility. */
|
||||||
export function VisibilityIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
|
export function VisibilityIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -149,28 +115,6 @@ export function StatusIcon({ value, size = '1.25rem', className }: DomIconProps<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Icon for constituenta type. */
|
|
||||||
export function CstTypeIcon({ value, size = '1.25rem', className }: DomIconProps<CstType>) {
|
|
||||||
switch (value) {
|
|
||||||
case CstType.BASE:
|
|
||||||
return <IconCstBaseSet size={size} className={className ?? 'text-ok-600'} />;
|
|
||||||
case CstType.CONSTANT:
|
|
||||||
return <IconCstConstSet size={size} className={className ?? 'text-ok-600'} />;
|
|
||||||
case CstType.STRUCTURED:
|
|
||||||
return <IconCstStructured size={size} className={className ?? 'text-ok-600'} />;
|
|
||||||
case CstType.TERM:
|
|
||||||
return <IconCstTerm size={size} className={className ?? 'text-sec-600'} />;
|
|
||||||
case CstType.AXIOM:
|
|
||||||
return <IconCstAxiom size={size} className={className ?? 'text-warn-600'} />;
|
|
||||||
case CstType.FUNCTION:
|
|
||||||
return <IconCstFunction size={size} className={className ?? 'text-sec-600'} />;
|
|
||||||
case CstType.PREDICATE:
|
|
||||||
return <IconCstPredicate size={size} className={className ?? 'text-warn-600'} />;
|
|
||||||
case CstType.THEOREM:
|
|
||||||
return <IconCstTheorem size={size} className={className ?? 'text-warn-600'} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Icon for relocation direction. */
|
/** Icon for relocation direction. */
|
||||||
export function RelocateUpIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
|
export function RelocateUpIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { ZodError } from 'zod';
|
||||||
|
|
||||||
import { AxiosError, isAxiosError } from '@/backend/apiTransport';
|
import { AxiosError, isAxiosError } from '@/backend/apiTransport';
|
||||||
import { isResponseHtml } from '@/utils/utils';
|
import { isResponseHtml } from '@/utils/utils';
|
||||||
|
|
||||||
import { PrettyJson } from './View';
|
import { PrettyJson } from './View';
|
||||||
|
|
||||||
export type ErrorData = string | Error | AxiosError | undefined | null;
|
export type ErrorData = string | Error | AxiosError | ZodError | undefined | null;
|
||||||
|
|
||||||
interface InfoErrorProps {
|
interface InfoErrorProps {
|
||||||
error: ErrorData;
|
error: ErrorData;
|
||||||
|
@ -16,6 +17,13 @@ function DescribeError({ error }: { error: ErrorData }) {
|
||||||
return <p>Ошибки отсутствуют</p>;
|
return <p>Ошибки отсутствуют</p>;
|
||||||
} else if (typeof error === 'string') {
|
} else if (typeof error === 'string') {
|
||||||
return <p>{error}</p>;
|
return <p>{error}</p>;
|
||||||
|
} else if (error instanceof ZodError) {
|
||||||
|
return (
|
||||||
|
<div className='mt-6'>
|
||||||
|
<p>Ошибка валидации данных</p>
|
||||||
|
<PrettyJson data={JSON.parse(error.toString()) as unknown} />;
|
||||||
|
</div>
|
||||||
|
);
|
||||||
} else if (!isAxiosError(error)) {
|
} else if (!isAxiosError(error)) {
|
||||||
return (
|
return (
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
import { CheckboxChecked, CheckboxNull } from '../Icons';
|
import { CheckboxChecked, CheckboxNull } from '../Icons';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
import { CheckboxProps } from './Checkbox';
|
import { CheckboxProps } from './Checkbox';
|
||||||
|
|
||||||
export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'onChange'> {
|
export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'onChange'> {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import clsx from 'clsx';
|
|
||||||
import { FieldError, GlobalError } from 'react-hook-form';
|
import { FieldError, GlobalError } from 'react-hook-form';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { Button } from '../Control';
|
import { Button } from '../Control';
|
||||||
import { IconUpload } from '../Icons';
|
import { IconUpload } from '../Icons';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
import { Label } from './Label';
|
import { Label } from './Label';
|
||||||
|
|
||||||
interface FileInputProps extends Omit<CProps.Input, 'accept' | 'type'> {
|
interface FileInputProps extends Omit<CProps.Input, 'accept' | 'type'> {
|
||||||
|
|
|
@ -2,9 +2,10 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { Overlay } from '@/components/Container';
|
import { Overlay } from '@/components/Container';
|
||||||
import { IconSearch } from '@/components/Icons';
|
import { IconSearch } from '@/components/Icons';
|
||||||
import { TextInput } from '@/components/Input';
|
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
|
|
||||||
|
import { TextInput } from './TextInput';
|
||||||
|
|
||||||
interface SearchBarProps extends CProps.Styling {
|
interface SearchBarProps extends CProps.Styling {
|
||||||
/** Id of the search bar. */
|
/** Id of the search bar. */
|
||||||
id?: string;
|
id?: string;
|
|
@ -1,5 +1,5 @@
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { globals, PARAMETER } from '@/utils/constants';
|
import { globals, PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { Label } from '../Input/Label';
|
import { Label } from '../Input/Label';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
import { ErrorField } from './ErrorField';
|
import { ErrorField } from './ErrorField';
|
||||||
|
|
||||||
export interface TextAreaProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.TextArea {
|
export interface TextAreaProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.TextArea {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { Label } from '../Input/Label';
|
import { Label } from '../Input/Label';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
import { ErrorField } from './ErrorField';
|
import { ErrorField } from './ErrorField';
|
||||||
|
|
||||||
interface TextInputProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.Input {
|
interface TextInputProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.Input {
|
||||||
|
|
|
@ -3,6 +3,7 @@ export { CheckboxTristate } from './CheckboxTristate';
|
||||||
export { ErrorField } from './ErrorField';
|
export { ErrorField } from './ErrorField';
|
||||||
export { FileInput } from './FileInput';
|
export { FileInput } from './FileInput';
|
||||||
export { Label } from './Label';
|
export { Label } from './Label';
|
||||||
|
export { SearchBar } from './SearchBar';
|
||||||
export { SelectMulti, type SelectMultiProps } from './SelectMulti';
|
export { SelectMulti, type SelectMultiProps } from './SelectMulti';
|
||||||
export { SelectSingle, type SelectSingleProps } from './SelectSingle';
|
export { SelectSingle, type SelectSingleProps } from './SelectSingle';
|
||||||
export { SelectTree } from './SelectTree';
|
export { SelectTree } from './SelectTree';
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { HelpTopic } from '@/features/help/models/helpTopic';
|
import { BadgeHelp, HelpTopic } from '@/features/help';
|
||||||
|
|
||||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
@ -11,7 +12,7 @@ import { prepareTooltip } from '@/utils/utils';
|
||||||
import { Button, MiniButton, SubmitButton } from '../Control';
|
import { Button, MiniButton, SubmitButton } from '../Control';
|
||||||
import { IconClose } from '../Icons';
|
import { IconClose } from '../Icons';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
import { BadgeHelp } from '../shared/BadgeHelp';
|
|
||||||
import { ModalBackdrop } from './ModalBackdrop';
|
import { ModalBackdrop } from './ModalBackdrop';
|
||||||
|
|
||||||
export interface ModalProps extends CProps.Styling {
|
export interface ModalProps extends CProps.Styling {
|
||||||
|
@ -43,6 +44,9 @@ interface ModalFormProps extends ModalProps {
|
||||||
|
|
||||||
/** Callback to be called after submit. */
|
/** Callback to be called after submit. */
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
||||||
|
|
||||||
|
/** Callback to be called when modal is canceled. */
|
||||||
|
onCancel?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,13 +64,19 @@ export function ModalForm({
|
||||||
submitInvalidTooltip,
|
submitInvalidTooltip,
|
||||||
beforeSubmit,
|
beforeSubmit,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
onCancel,
|
||||||
|
|
||||||
helpTopic,
|
helpTopic,
|
||||||
hideHelpWhen,
|
hideHelpWhen,
|
||||||
...restProps
|
...restProps
|
||||||
}: React.PropsWithChildren<ModalFormProps>) {
|
}: React.PropsWithChildren<ModalFormProps>) {
|
||||||
const hideDialog = useDialogsStore(state => state.hideDialog);
|
const hideDialog = useDialogsStore(state => state.hideDialog);
|
||||||
useEscapeKey(hideDialog);
|
|
||||||
|
function handleCancel() {
|
||||||
|
onCancel?.();
|
||||||
|
hideDialog();
|
||||||
|
}
|
||||||
|
useEscapeKey(handleCancel);
|
||||||
|
|
||||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||||
if (beforeSubmit && !beforeSubmit()) {
|
if (beforeSubmit && !beforeSubmit()) {
|
||||||
|
@ -78,7 +88,7 @@ export function ModalForm({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
|
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
|
||||||
<ModalBackdrop onHide={hideDialog} />
|
<ModalBackdrop onHide={handleCancel} />
|
||||||
<form
|
<form
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'cc-animate-modal',
|
'cc-animate-modal',
|
||||||
|
@ -98,7 +108,7 @@ export function ModalForm({
|
||||||
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
|
||||||
icon={<IconClose size='1.25rem' />}
|
icon={<IconClose size='1.25rem' />}
|
||||||
className='float-right mt-2 mr-2'
|
className='float-right mt-2 mr-2'
|
||||||
onClick={hideDialog}
|
onClick={handleCancel}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
|
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}
|
||||||
|
@ -125,7 +135,7 @@ export function ModalForm({
|
||||||
className='min-w-[7rem]'
|
className='min-w-[7rem]'
|
||||||
disabled={!canSubmit}
|
disabled={!canSubmit}
|
||||||
/>
|
/>
|
||||||
<Button text='Отмена' className='min-w-[7rem]' onClick={hideDialog} />
|
<Button text='Отмена' className='min-w-[7rem]' onClick={handleCancel} />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { Loader } from '@/components/Loader';
|
import { Loader } from '@/components/Loader';
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
import { BadgeHelp } from '@/features/help';
|
||||||
|
|
||||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
@ -9,7 +11,7 @@ import { prepareTooltip } from '@/utils/utils';
|
||||||
|
|
||||||
import { Button, MiniButton } from '../Control';
|
import { Button, MiniButton } from '../Control';
|
||||||
import { IconClose } from '../Icons';
|
import { IconClose } from '../Icons';
|
||||||
import { BadgeHelp } from '../shared/BadgeHelp';
|
|
||||||
import { ModalBackdrop } from './ModalBackdrop';
|
import { ModalBackdrop } from './ModalBackdrop';
|
||||||
import { ModalProps } from './ModalForm';
|
import { ModalProps } from './ModalForm';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import clsx from 'clsx';
|
|
||||||
import type { TabProps as TabPropsImpl } from 'react-tabs';
|
import type { TabProps as TabPropsImpl } from 'react-tabs';
|
||||||
import { Tab as TabImpl } from 'react-tabs';
|
import { Tab as TabImpl } from 'react-tabs';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
|
|
|
@ -1,77 +1,17 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
||||||
import { DELAYS } from '@/backend/configuration';
|
import { DELAYS } from '@/backend/configuration';
|
||||||
import { errorMsg, infoMsg } from '@/utils/labels';
|
import { infoMsg } from '@/utils/labels';
|
||||||
|
|
||||||
/**
|
import {
|
||||||
* Represents CurrentUser information.
|
IChangePasswordDTO,
|
||||||
*/
|
ICurrentUser,
|
||||||
export interface ICurrentUser {
|
IPasswordTokenDTO,
|
||||||
id: number | null;
|
IRequestPasswordDTO,
|
||||||
username: string;
|
IResetPasswordDTO,
|
||||||
is_staff: boolean;
|
IUserLoginDTO
|
||||||
editor: number[];
|
} from './types';
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents login data, used to authenticate users.
|
|
||||||
*/
|
|
||||||
export const schemaUserLogin = z.object({
|
|
||||||
username: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
password: z.string().nonempty(errorMsg.requiredField)
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents login data, used to authenticate users.
|
|
||||||
*/
|
|
||||||
export type IUserLoginDTO = z.infer<typeof schemaUserLogin>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents data needed to update password for current user.
|
|
||||||
*/
|
|
||||||
export const schemaChangePassword = z
|
|
||||||
.object({
|
|
||||||
old_password: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
new_password: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
new_password2: z.string().nonempty(errorMsg.requiredField)
|
|
||||||
})
|
|
||||||
.refine(schema => schema.new_password === schema.new_password2, {
|
|
||||||
path: ['new_password2'],
|
|
||||||
message: errorMsg.passwordsMismatch
|
|
||||||
})
|
|
||||||
.refine(schema => schema.old_password !== schema.new_password, {
|
|
||||||
path: ['new_password'],
|
|
||||||
message: errorMsg.passwordsSame
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents data needed to update password for current user.
|
|
||||||
*/
|
|
||||||
export type IChangePasswordDTO = z.infer<typeof schemaChangePassword>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents password reset request data.
|
|
||||||
*/
|
|
||||||
export interface IRequestPasswordDTO {
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents password reset data.
|
|
||||||
*/
|
|
||||||
export interface IResetPasswordDTO {
|
|
||||||
password: string;
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents password token data.
|
|
||||||
*/
|
|
||||||
export interface IPasswordTokenDTO {
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authentication API.
|
* Authentication API.
|
||||||
|
|
71
rsconcept/frontend/src/features/auth/backend/types.ts
Normal file
71
rsconcept/frontend/src/features/auth/backend/types.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents CurrentUser information.
|
||||||
|
*/
|
||||||
|
export interface ICurrentUser {
|
||||||
|
id: number | null;
|
||||||
|
username: string;
|
||||||
|
is_staff: boolean;
|
||||||
|
editor: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents login data, used to authenticate users.
|
||||||
|
*/
|
||||||
|
export const schemaUserLogin = z.object({
|
||||||
|
username: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
password: z.string().nonempty(errorMsg.requiredField)
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents login data, used to authenticate users.
|
||||||
|
*/
|
||||||
|
export type IUserLoginDTO = z.infer<typeof schemaUserLogin>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data needed to update password for current user.
|
||||||
|
*/
|
||||||
|
export const schemaChangePassword = z
|
||||||
|
.object({
|
||||||
|
old_password: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
new_password: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
new_password2: z.string().nonempty(errorMsg.requiredField)
|
||||||
|
})
|
||||||
|
.refine(schema => schema.new_password === schema.new_password2, {
|
||||||
|
path: ['new_password2'],
|
||||||
|
message: errorMsg.passwordsMismatch
|
||||||
|
})
|
||||||
|
.refine(schema => schema.old_password !== schema.new_password, {
|
||||||
|
path: ['new_password'],
|
||||||
|
message: errorMsg.passwordsSame
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data needed to update password for current user.
|
||||||
|
*/
|
||||||
|
export type IChangePasswordDTO = z.infer<typeof schemaChangePassword>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents password reset request data.
|
||||||
|
*/
|
||||||
|
export interface IRequestPasswordDTO {
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents password reset data.
|
||||||
|
*/
|
||||||
|
export interface IResetPasswordDTO {
|
||||||
|
password: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents password token data.
|
||||||
|
*/
|
||||||
|
export interface IPasswordTokenDTO {
|
||||||
|
token: string;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { authApi, IChangePasswordDTO } from './api';
|
import { authApi } from './api';
|
||||||
|
import { IChangePasswordDTO } from './types';
|
||||||
|
|
||||||
export const useChangePassword = () => {
|
export const useChangePassword = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { authApi, IUserLoginDTO } from './api';
|
import { authApi } from './api';
|
||||||
|
import { IUserLoginDTO } from './types';
|
||||||
|
|
||||||
export const useLogin = () => {
|
export const useLogin = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { authApi, IRequestPasswordDTO } from './api';
|
import { authApi } from './api';
|
||||||
|
import { IRequestPasswordDTO } from './types';
|
||||||
|
|
||||||
export const useRequestPasswordReset = () => {
|
export const useRequestPasswordReset = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { authApi, IPasswordTokenDTO, IResetPasswordDTO } from './api';
|
import { authApi } from './api';
|
||||||
|
import { IPasswordTokenDTO, IResetPasswordDTO } from './types';
|
||||||
|
|
||||||
export const useResetPassword = () => {
|
export const useResetPassword = () => {
|
||||||
const validateMutation = useMutation({
|
const validateMutation = useMutation({
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
|
|
||||||
import { TextURL } from '@/components/Control';
|
import { TextURL } from '@/components/Control';
|
||||||
|
|
||||||
import { useAuthSuspense } from '../backend/useAuth';
|
import { useAuthSuspense } from '../backend/useAuth';
|
||||||
import { useLogout } from '../backend/useLogout';
|
import { useLogout } from '../backend/useLogout';
|
||||||
|
|
||||||
function ExpectedAnonymous() {
|
export function ExpectedAnonymous() {
|
||||||
const { user } = useAuthSuspense();
|
const { user } = useAuthSuspense();
|
||||||
const { logout } = useLogout();
|
const { logout } = useLogout();
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
@ -30,5 +31,3 @@ function ExpectedAnonymous() {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ExpectedAnonymous;
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { TextURL } from '@/components/Control';
|
||||||
|
|
||||||
import { useAuthSuspense } from '../backend/useAuth';
|
import { useAuthSuspense } from '../backend/useAuth';
|
||||||
|
|
||||||
function RequireAuth({ children }: React.PropsWithChildren) {
|
export function RequireAuth({ children }: React.PropsWithChildren) {
|
||||||
const { isAnonymous } = useAuthSuspense();
|
const { isAnonymous } = useAuthSuspense();
|
||||||
|
|
||||||
if (isAnonymous) {
|
if (isAnonymous) {
|
||||||
|
@ -19,5 +19,3 @@ function RequireAuth({ children }: React.PropsWithChildren) {
|
||||||
}
|
}
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RequireAuth;
|
|
||||||
|
|
5
rsconcept/frontend/src/features/auth/index.ts
Normal file
5
rsconcept/frontend/src/features/auth/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export { useAuthSuspense } from './backend/useAuth';
|
||||||
|
export { useChangePassword } from './backend/useChangePassword';
|
||||||
|
export { useLogout } from './backend/useLogout';
|
||||||
|
export { ExpectedAnonymous } from './components/ExpectedAnonymous';
|
||||||
|
export { RequireAuth } from './components/RequireAuth';
|
|
@ -1,10 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
|
|
||||||
import { isAxiosError } from '@/backend/apiTransport';
|
import { isAxiosError } from '@/backend/apiTransport';
|
||||||
import { SubmitButton, TextURL } from '@/components/Control';
|
import { SubmitButton, TextURL } from '@/components/Control';
|
||||||
import { ErrorData } from '@/components/InfoError';
|
import { ErrorData } from '@/components/InfoError';
|
||||||
|
@ -12,10 +13,10 @@ import { TextInput } from '@/components/Input';
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { resources } from '@/utils/constants';
|
import { resources } from '@/utils/constants';
|
||||||
|
|
||||||
import { IUserLoginDTO, schemaUserLogin } from '../backend/api';
|
import { IUserLoginDTO, schemaUserLogin } from '../backend/types';
|
||||||
import { useAuthSuspense } from '../backend/useAuth';
|
import { useAuthSuspense } from '../backend/useAuth';
|
||||||
import { useLogin } from '../backend/useLogin';
|
import { useLogin } from '../backend/useLogin';
|
||||||
import ExpectedAnonymous from '../components/ExpectedAnonymous';
|
import { ExpectedAnonymous } from '../components/ExpectedAnonymous';
|
||||||
|
|
||||||
function LoginPage() {
|
function LoginPage() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
@ -26,7 +27,6 @@ function LoginPage() {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
clearErrors,
|
clearErrors,
|
||||||
resetField,
|
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm({
|
} = useForm({
|
||||||
resolver: zodResolver(schemaUserLogin),
|
resolver: zodResolver(schemaUserLogin),
|
||||||
|
@ -38,7 +38,6 @@ function LoginPage() {
|
||||||
|
|
||||||
function onSubmit(data: IUserLoginDTO) {
|
function onSubmit(data: IUserLoginDTO) {
|
||||||
return login(data).then(() => {
|
return login(data).then(() => {
|
||||||
resetField('password');
|
|
||||||
if (router.canBack()) {
|
if (router.canBack()) {
|
||||||
router.back();
|
router.back();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
|
|
||||||
import { isAxiosError } from '@/backend/apiTransport';
|
import { isAxiosError } from '@/backend/apiTransport';
|
||||||
import { SubmitButton } from '@/components/Control';
|
import { SubmitButton } from '@/components/Control';
|
||||||
import { ErrorData, InfoError } from '@/components/InfoError';
|
import { ErrorData, InfoError } from '@/components/InfoError';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { isAxiosError } from '@/backend/apiTransport';
|
import { isAxiosError } from '@/backend/apiTransport';
|
||||||
import { SubmitButton, TextURL } from '@/components/Control';
|
import { SubmitButton, TextURL } from '@/components/Control';
|
||||||
|
|
|
@ -5,9 +5,10 @@ import { TextURL } from '@/components/Control';
|
||||||
import { IconHelp } from '@/components/Icons';
|
import { IconHelp } from '@/components/Icons';
|
||||||
import { Loader } from '@/components/Loader';
|
import { Loader } from '@/components/Loader';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { HelpTopic } from '@/features/help/models/helpTopic';
|
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
|
import { HelpTopic } from '../models/helpTopic';
|
||||||
|
|
||||||
const TopicPage = React.lazy(() => import('@/features/help/pages/ManualsPage/TopicPage'));
|
const TopicPage = React.lazy(() => import('@/features/help/pages/ManualsPage/TopicPage'));
|
||||||
|
|
||||||
interface BadgeHelpProps extends CProps.Styling {
|
interface BadgeHelpProps extends CProps.Styling {
|
|
@ -3,6 +3,7 @@ import clsx from 'clsx';
|
||||||
import { colorBgCstClass } from '@/features/rsform/colors';
|
import { colorBgCstClass } from '@/features/rsform/colors';
|
||||||
import { describeCstClass, labelCstClass } from '@/features/rsform/labels';
|
import { describeCstClass, labelCstClass } from '@/features/rsform/labels';
|
||||||
import { CstClass } from '@/features/rsform/models/rsform';
|
import { CstClass } from '@/features/rsform/models/rsform';
|
||||||
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
interface InfoCstClassProps {
|
interface InfoCstClassProps {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import clsx from 'clsx';
|
||||||
import { colorBgCstStatus } from '@/features/rsform/colors';
|
import { colorBgCstStatus } from '@/features/rsform/colors';
|
||||||
import { describeExpressionStatus, labelExpressionStatus } from '@/features/rsform/labels';
|
import { describeExpressionStatus, labelExpressionStatus } from '@/features/rsform/labels';
|
||||||
import { ExpressionStatus } from '@/features/rsform/models/rsform';
|
import { ExpressionStatus } from '@/features/rsform/models/rsform';
|
||||||
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
interface InfoCstStatusProps {
|
interface InfoCstStatusProps {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { urls } from '@/app';
|
import { urls } from '@/app';
|
||||||
|
|
||||||
import { TextURL } from '@/components/Control';
|
import { TextURL } from '@/components/Control';
|
||||||
|
|
||||||
import { HelpTopic } from '../models/helpTopic';
|
import { HelpTopic } from '../models/helpTopic';
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import { HelpTopic, topicParent } from '../models/helpTopic';
|
import { HelpTopic, topicParent } from '../models/helpTopic';
|
||||||
|
|
||||||
import { TopicItem } from './TopicItem';
|
import { TopicItem } from './TopicItem';
|
||||||
|
|
||||||
interface SubtopicsProps {
|
interface SubtopicsProps {
|
||||||
|
|
2
rsconcept/frontend/src/features/help/index.ts
Normal file
2
rsconcept/frontend/src/features/help/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { BadgeHelp } from './components/BadgeHelp';
|
||||||
|
export { HelpTopic } from './models/helpTopic';
|
|
@ -1,4 +1,5 @@
|
||||||
import { urls } from '@/app';
|
import { urls } from '@/app';
|
||||||
|
|
||||||
import { TextURL } from '@/components/Control';
|
import { TextURL } from '@/components/Control';
|
||||||
import { external_urls } from '@/utils/constants';
|
import { external_urls } from '@/utils/constants';
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
|
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { useMainHeight } from '@/stores/appLayout';
|
import { useMainHeight } from '@/stores/appLayout';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { HelpTopic } from '../../models/helpTopic';
|
import { HelpTopic } from '../../models/helpTopic';
|
||||||
|
|
||||||
import TopicsList from './TopicsList';
|
import TopicsList from './TopicsList';
|
||||||
import ViewTopic from './ViewTopic';
|
import ViewTopic from './ViewTopic';
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,8 @@ import { SelectTree } from '@/components/Input';
|
||||||
import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
|
import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import { describeHelpTopic } from '../../labels';
|
import { describeHelpTopic, labelHelpTopic } from '../../labels';
|
||||||
import { labelHelpTopic } from '../../labels';
|
import { HelpTopic, topicParent } from '../../models/helpTopic';
|
||||||
import { topicParent } from '../../models/helpTopic';
|
|
||||||
import { HelpTopic } from '../../models/helpTopic';
|
|
||||||
|
|
||||||
interface TopicsDropdownProps {
|
interface TopicsDropdownProps {
|
||||||
activeTopic: HelpTopic;
|
activeTopic: HelpTopic;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
|
||||||
import { HelpTopic } from '../../models/helpTopic';
|
import { HelpTopic } from '../../models/helpTopic';
|
||||||
|
|
||||||
import TopicsDropdown from './TopicsDropdown';
|
import TopicsDropdown from './TopicsDropdown';
|
||||||
import TopicsStatic from './TopicsStatic';
|
import TopicsStatic from './TopicsStatic';
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import TopicPage from '@/features/help/pages/ManualsPage/TopicPage';
|
|
||||||
import { useMainHeight } from '@/stores/appLayout';
|
import { useMainHeight } from '@/stores/appLayout';
|
||||||
|
|
||||||
import { HelpTopic } from '../../models/helpTopic';
|
import { HelpTopic } from '../../models/helpTopic';
|
||||||
|
|
||||||
|
import TopicPage from './TopicPage';
|
||||||
|
|
||||||
interface ViewTopicProps {
|
interface ViewTopicProps {
|
||||||
topic: HelpTopic;
|
topic: HelpTopic;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useLayoutEffect } from 'react';
|
||||||
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
|
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
|
||||||
|
|
||||||
import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
|
import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
|
||||||
|
@ -10,7 +10,7 @@ export function Component() {
|
||||||
const hideFooter = useAppLayoutStore(state => state.hideFooter);
|
const hideFooter = useAppLayoutStore(state => state.hideFooter);
|
||||||
const panelHeight = useFitHeight('0px');
|
const panelHeight = useFitHeight('0px');
|
||||||
|
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
hideFooter(true);
|
hideFooter(true);
|
||||||
return () => hideFooter(false);
|
return () => hideFooter(false);
|
||||||
}, [hideFooter]);
|
}, [hideFooter]);
|
||||||
|
|
|
@ -1,27 +1,17 @@
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
import { Loader } from '@/components/Loader';
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
|
||||||
import { PARAMETER } from '@/utils/constants';
|
|
||||||
|
|
||||||
function HomePage() {
|
function HomePage() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { isAnonymous } = useAuthSuspense();
|
const { isAnonymous } = useAuthSuspense();
|
||||||
|
|
||||||
useEffect(() => {
|
if (isAnonymous) {
|
||||||
if (isAnonymous) {
|
router.replace(urls.manuals);
|
||||||
setTimeout(() => {
|
} else {
|
||||||
router.replace(urls.manuals);
|
router.replace(urls.library);
|
||||||
}, PARAMETER.refreshTimeout);
|
}
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
router.replace(urls.library);
|
|
||||||
}, PARAMETER.refreshTimeout);
|
|
||||||
}
|
|
||||||
}, [router, isAnonymous]);
|
|
||||||
|
|
||||||
return <Loader />;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HomePage;
|
export default HomePage;
|
||||||
|
|
|
@ -1,145 +1,42 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
import { z } from 'zod';
|
|
||||||
|
import {
|
||||||
|
IRSFormDTO,
|
||||||
|
IVersionCreatedResponse,
|
||||||
|
schemaRSForm,
|
||||||
|
schemaVersionCreatedResponse
|
||||||
|
} from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
||||||
import { DELAYS } from '@/backend/configuration';
|
import { DELAYS, KEYS } from '@/backend/configuration';
|
||||||
import { ossApi } from '@/features/oss/backend/api';
|
import { infoMsg } from '@/utils/labels';
|
||||||
import { IRSFormDTO, rsformsApi } from '@/features/rsform/backend/api';
|
|
||||||
import { errorMsg, infoMsg } from '@/utils/labels';
|
|
||||||
|
|
||||||
import { AccessPolicy, ILibraryItem, IVersionInfo, LibraryItemID, LibraryItemType, VersionID } from '../models/library';
|
import {
|
||||||
import { validateLocation } from '../models/libraryAPI';
|
AccessPolicy,
|
||||||
|
ICloneLibraryItemDTO,
|
||||||
/**
|
ICreateLibraryItemDTO,
|
||||||
* Represents update data for renaming Location.
|
ILibraryItem,
|
||||||
*/
|
IRenameLocationDTO,
|
||||||
export interface IRenameLocationDTO {
|
IUpdateLibraryItemDTO,
|
||||||
target: string;
|
IVersionCreateDTO,
|
||||||
new_location: string;
|
IVersionInfo,
|
||||||
}
|
IVersionUpdateDTO,
|
||||||
|
schemaLibraryItem,
|
||||||
/**
|
schemaLibraryItemArray,
|
||||||
* Represents data, used for cloning {@link IRSForm}.
|
schemaVersionInfo
|
||||||
*/
|
} from './types';
|
||||||
export const schemaCloneLibraryItem = z.object({
|
|
||||||
id: z.number(),
|
|
||||||
item_type: z.nativeEnum(LibraryItemType),
|
|
||||||
title: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
alias: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
comment: z.string(),
|
|
||||||
visible: z.boolean(),
|
|
||||||
read_only: z.boolean(),
|
|
||||||
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
|
||||||
access_policy: z.nativeEnum(AccessPolicy),
|
|
||||||
|
|
||||||
items: z.array(z.number()).optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents data, used for cloning {@link IRSForm}.
|
|
||||||
*/
|
|
||||||
export type ICloneLibraryItemDTO = z.infer<typeof schemaCloneLibraryItem>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents data, used for creating {@link IRSForm}.
|
|
||||||
*/
|
|
||||||
export const schemaCreateLibraryItem = z
|
|
||||||
.object({
|
|
||||||
item_type: z.nativeEnum(LibraryItemType),
|
|
||||||
title: z.string().optional(),
|
|
||||||
alias: z.string().optional(),
|
|
||||||
comment: z.string(),
|
|
||||||
visible: z.boolean(),
|
|
||||||
read_only: z.boolean(),
|
|
||||||
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
|
||||||
access_policy: z.nativeEnum(AccessPolicy),
|
|
||||||
|
|
||||||
file: z.instanceof(File).optional(),
|
|
||||||
fileName: z.string().optional()
|
|
||||||
})
|
|
||||||
.refine(data => !!data.file || !!data.title, {
|
|
||||||
path: ['title'],
|
|
||||||
message: errorMsg.requiredField
|
|
||||||
})
|
|
||||||
.refine(data => !!data.file || !!data.alias, {
|
|
||||||
path: ['alias'],
|
|
||||||
message: errorMsg.requiredField
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents data, used for creating {@link IRSForm}.
|
|
||||||
*/
|
|
||||||
export type ICreateLibraryItemDTO = z.infer<typeof schemaCreateLibraryItem>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents update data for editing {@link ILibraryItem}.
|
|
||||||
*/
|
|
||||||
export const schemaUpdateLibraryItem = z.object({
|
|
||||||
id: z.number(),
|
|
||||||
item_type: z.nativeEnum(LibraryItemType),
|
|
||||||
title: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
alias: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
comment: z.string(),
|
|
||||||
visible: z.boolean(),
|
|
||||||
read_only: z.boolean()
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents update data for editing {@link ILibraryItem}.
|
|
||||||
*/
|
|
||||||
export type IUpdateLibraryItemDTO = z.infer<typeof schemaUpdateLibraryItem>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create version metadata in persistent storage.
|
|
||||||
*/
|
|
||||||
export const schemaVersionCreate = z.object({
|
|
||||||
version: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
items: z.array(z.number()).optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create version metadata in persistent storage.
|
|
||||||
*/
|
|
||||||
export type IVersionCreateDTO = z.infer<typeof schemaVersionCreate>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents data response when creating {@link IVersionInfo}.
|
|
||||||
*/
|
|
||||||
export interface IVersionCreatedResponse {
|
|
||||||
version: number;
|
|
||||||
schema: IRSFormDTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents version data, intended to update version metadata in persistent storage.
|
|
||||||
*/
|
|
||||||
export const schemaVersionUpdate = z.object({
|
|
||||||
id: z.number(),
|
|
||||||
version: z.string().nonempty(errorMsg.requiredField),
|
|
||||||
description: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents version data, intended to update version metadata in persistent storage.
|
|
||||||
*/
|
|
||||||
export type IVersionUpdateDTO = z.infer<typeof schemaVersionUpdate>;
|
|
||||||
|
|
||||||
export const libraryApi = {
|
export const libraryApi = {
|
||||||
baseKey: 'library',
|
baseKey: KEYS.library,
|
||||||
libraryListKey: ['library', 'list'],
|
libraryListKey: KEYS.composite.libraryList,
|
||||||
|
|
||||||
getItemQueryOptions: ({ itemID, itemType }: { itemID: LibraryItemID; itemType: LibraryItemType }) => {
|
|
||||||
return itemType === LibraryItemType.RSFORM
|
|
||||||
? rsformsApi.getRSFormQueryOptions({ itemID })
|
|
||||||
: ossApi.getOssQueryOptions({ itemID });
|
|
||||||
},
|
|
||||||
getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) =>
|
getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: [...libraryApi.libraryListKey, isAdmin ? 'admin' : 'user'],
|
queryKey: [...libraryApi.libraryListKey, isAdmin ? 'admin' : 'user'],
|
||||||
staleTime: DELAYS.staleMedium,
|
staleTime: DELAYS.staleMedium,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosGet<ILibraryItem[]>({
|
axiosGet<ILibraryItem[]>({
|
||||||
|
schema: schemaLibraryItemArray,
|
||||||
endpoint: isAdmin ? '/api/library/all' : '/api/library/active',
|
endpoint: isAdmin ? '/api/library/all' : '/api/library/active',
|
||||||
options: { signal: meta.signal }
|
options: { signal: meta.signal }
|
||||||
})
|
})
|
||||||
|
@ -150,6 +47,7 @@ export const libraryApi = {
|
||||||
staleTime: DELAYS.staleMedium,
|
staleTime: DELAYS.staleMedium,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosGet<ILibraryItem[]>({
|
axiosGet<ILibraryItem[]>({
|
||||||
|
schema: schemaLibraryItemArray,
|
||||||
endpoint: '/api/library/templates',
|
endpoint: '/api/library/templates',
|
||||||
options: { signal: meta.signal }
|
options: { signal: meta.signal }
|
||||||
})
|
})
|
||||||
|
@ -157,6 +55,7 @@ export const libraryApi = {
|
||||||
|
|
||||||
createItem: (data: ICreateLibraryItemDTO) =>
|
createItem: (data: ICreateLibraryItemDTO) =>
|
||||||
axiosPost<ICreateLibraryItemDTO, ILibraryItem>({
|
axiosPost<ICreateLibraryItemDTO, ILibraryItem>({
|
||||||
|
schema: schemaLibraryItem,
|
||||||
endpoint: !data.file ? '/api/library' : '/api/rsforms/create-detailed',
|
endpoint: !data.file ? '/api/library' : '/api/rsforms/create-detailed',
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -172,13 +71,14 @@ export const libraryApi = {
|
||||||
}),
|
}),
|
||||||
updateItem: (data: IUpdateLibraryItemDTO) =>
|
updateItem: (data: IUpdateLibraryItemDTO) =>
|
||||||
axiosPatch<IUpdateLibraryItemDTO, ILibraryItem>({
|
axiosPatch<IUpdateLibraryItemDTO, ILibraryItem>({
|
||||||
|
schema: schemaLibraryItem,
|
||||||
endpoint: `/api/library/${data.id}`,
|
endpoint: `/api/library/${data.id}`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
successMessage: infoMsg.changesSaved
|
successMessage: infoMsg.changesSaved
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
setOwner: ({ itemID, owner }: { itemID: LibraryItemID; owner: number }) =>
|
setOwner: ({ itemID, owner }: { itemID: number; owner: number }) =>
|
||||||
axiosPatch({
|
axiosPatch({
|
||||||
endpoint: `/api/library/${itemID}/set-owner`,
|
endpoint: `/api/library/${itemID}/set-owner`,
|
||||||
request: {
|
request: {
|
||||||
|
@ -186,7 +86,7 @@ export const libraryApi = {
|
||||||
successMessage: infoMsg.changesSaved
|
successMessage: infoMsg.changesSaved
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
setLocation: ({ itemID, location }: { itemID: LibraryItemID; location: string }) =>
|
setLocation: ({ itemID, location }: { itemID: number; location: string }) =>
|
||||||
axiosPatch({
|
axiosPatch({
|
||||||
endpoint: `/api/library/${itemID}/set-location`,
|
endpoint: `/api/library/${itemID}/set-location`,
|
||||||
request: {
|
request: {
|
||||||
|
@ -194,7 +94,7 @@ export const libraryApi = {
|
||||||
successMessage: infoMsg.moveComplete
|
successMessage: infoMsg.moveComplete
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
setAccessPolicy: ({ itemID, policy }: { itemID: LibraryItemID; policy: AccessPolicy }) =>
|
setAccessPolicy: ({ itemID, policy }: { itemID: number; policy: AccessPolicy }) =>
|
||||||
axiosPatch({
|
axiosPatch({
|
||||||
endpoint: `/api/library/${itemID}/set-access-policy`,
|
endpoint: `/api/library/${itemID}/set-access-policy`,
|
||||||
request: {
|
request: {
|
||||||
|
@ -202,7 +102,7 @@ export const libraryApi = {
|
||||||
successMessage: infoMsg.changesSaved
|
successMessage: infoMsg.changesSaved
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
setEditors: ({ itemID, editors }: { itemID: LibraryItemID; editors: number[] }) =>
|
setEditors: ({ itemID, editors }: { itemID: number; editors: number[] }) =>
|
||||||
axiosPatch({
|
axiosPatch({
|
||||||
endpoint: `/api/library/${itemID}/set-editors`,
|
endpoint: `/api/library/${itemID}/set-editors`,
|
||||||
request: {
|
request: {
|
||||||
|
@ -211,7 +111,7 @@ export const libraryApi = {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
deleteItem: (target: LibraryItemID) =>
|
deleteItem: (target: number) =>
|
||||||
axiosDelete({
|
axiosDelete({
|
||||||
endpoint: `/api/library/${target}`,
|
endpoint: `/api/library/${target}`,
|
||||||
request: {
|
request: {
|
||||||
|
@ -220,6 +120,7 @@ export const libraryApi = {
|
||||||
}),
|
}),
|
||||||
cloneItem: (data: ICloneLibraryItemDTO) =>
|
cloneItem: (data: ICloneLibraryItemDTO) =>
|
||||||
axiosPost<ICloneLibraryItemDTO, IRSFormDTO>({
|
axiosPost<ICloneLibraryItemDTO, IRSFormDTO>({
|
||||||
|
schema: schemaRSForm,
|
||||||
endpoint: `/api/library/${data.id}/clone`,
|
endpoint: `/api/library/${data.id}/clone`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -235,30 +136,33 @@ export const libraryApi = {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionCreateDTO }) =>
|
versionCreate: ({ itemID, data }: { itemID: number; data: IVersionCreateDTO }) =>
|
||||||
axiosPost<IVersionCreateDTO, IVersionCreatedResponse>({
|
axiosPost<IVersionCreateDTO, IVersionCreatedResponse>({
|
||||||
|
schema: schemaVersionCreatedResponse,
|
||||||
endpoint: `/api/library/${itemID}/create-version`,
|
endpoint: `/api/library/${itemID}/create-version`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
successMessage: infoMsg.newVersion(data.version)
|
successMessage: infoMsg.newVersion(data.version)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
versionRestore: ({ versionID }: { versionID: VersionID }) =>
|
versionRestore: ({ versionID }: { versionID: number }) =>
|
||||||
axiosPatch<undefined, IRSFormDTO>({
|
axiosPatch<undefined, IRSFormDTO>({
|
||||||
|
schema: schemaRSForm,
|
||||||
endpoint: `/api/versions/${versionID}/restore`,
|
endpoint: `/api/versions/${versionID}/restore`,
|
||||||
request: {
|
request: {
|
||||||
successMessage: infoMsg.versionRestored
|
successMessage: infoMsg.versionRestored
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
versionUpdate: (data: IVersionUpdateDTO) =>
|
versionUpdate: (data: { itemID: number; version: IVersionUpdateDTO }) =>
|
||||||
axiosPatch<IVersionUpdateDTO, IVersionInfo>({
|
axiosPatch<IVersionUpdateDTO, IVersionInfo>({
|
||||||
endpoint: `/api/versions/${data.id}`,
|
schema: schemaVersionInfo,
|
||||||
|
endpoint: `/api/versions/${data.version.id}`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data.version,
|
||||||
successMessage: infoMsg.changesSaved
|
successMessage: infoMsg.changesSaved
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) =>
|
versionDelete: (data: { itemID: number; versionID: number }) =>
|
||||||
axiosDelete({
|
axiosDelete({
|
||||||
endpoint: `/api/versions/${data.versionID}`,
|
endpoint: `/api/versions/${data.versionID}`,
|
||||||
request: {
|
request: {
|
||||||
|
|
142
rsconcept/frontend/src/features/library/backend/types.ts
Normal file
142
rsconcept/frontend/src/features/library/backend/types.ts
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
|
import { validateLocation } from '../models/libraryAPI';
|
||||||
|
|
||||||
|
/** Represents type of library items. */
|
||||||
|
export enum LibraryItemType {
|
||||||
|
RSFORM = 'rsform',
|
||||||
|
OSS = 'oss'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents Access policy for library items.*/
|
||||||
|
export enum AccessPolicy {
|
||||||
|
PUBLIC = 'public',
|
||||||
|
PROTECTED = 'protected',
|
||||||
|
PRIVATE = 'private'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents library item common data typical for all item types. */
|
||||||
|
export type ILibraryItem = z.infer<typeof schemaLibraryItem>;
|
||||||
|
|
||||||
|
/** Represents {@link ILibraryItem} data loaded for both OSS and RSForm. */
|
||||||
|
export interface ILibraryItemData extends ILibraryItem {
|
||||||
|
editors: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents update data for renaming Location. */
|
||||||
|
export interface IRenameLocationDTO {
|
||||||
|
target: string;
|
||||||
|
new_location: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents library item version information. */
|
||||||
|
export type IVersionInfo = z.infer<typeof schemaVersionInfo>;
|
||||||
|
|
||||||
|
/** Represents data, used for cloning {@link IRSForm}. */
|
||||||
|
export type ICloneLibraryItemDTO = z.infer<typeof schemaCloneLibraryItem>;
|
||||||
|
|
||||||
|
/** Represents data, used for creating {@link IRSForm}. */
|
||||||
|
export type ICreateLibraryItemDTO = z.infer<typeof schemaCreateLibraryItem>;
|
||||||
|
|
||||||
|
/** Represents update data for editing {@link ILibraryItem}. */
|
||||||
|
export type IUpdateLibraryItemDTO = z.infer<typeof schemaUpdateLibraryItem>;
|
||||||
|
|
||||||
|
/** Create version metadata in persistent storage. */
|
||||||
|
export type IVersionCreateDTO = z.infer<typeof schemaVersionCreate>;
|
||||||
|
|
||||||
|
/** Represents version data, intended to update version metadata in persistent storage. */
|
||||||
|
export type IVersionUpdateDTO = z.infer<typeof schemaVersionUpdate>;
|
||||||
|
|
||||||
|
// ======= SCHEMAS =========
|
||||||
|
|
||||||
|
export const schemaLibraryItem = z.object({
|
||||||
|
id: z.coerce.number(),
|
||||||
|
item_type: z.nativeEnum(LibraryItemType),
|
||||||
|
title: z.string(),
|
||||||
|
alias: z.string().nonempty(),
|
||||||
|
comment: z.string(),
|
||||||
|
visible: z.boolean(),
|
||||||
|
read_only: z.boolean(),
|
||||||
|
location: z.string(),
|
||||||
|
access_policy: z.nativeEnum(AccessPolicy),
|
||||||
|
|
||||||
|
time_create: z.string(),
|
||||||
|
time_update: z.string(),
|
||||||
|
owner: z.coerce.number().nullable()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaLibraryItemArray = z.array(schemaLibraryItem);
|
||||||
|
|
||||||
|
export const schemaCloneLibraryItem = schemaLibraryItem
|
||||||
|
.pick({
|
||||||
|
id: true,
|
||||||
|
item_type: true,
|
||||||
|
title: true,
|
||||||
|
alias: true,
|
||||||
|
comment: true,
|
||||||
|
visible: true,
|
||||||
|
read_only: true,
|
||||||
|
location: true,
|
||||||
|
access_policy: true
|
||||||
|
})
|
||||||
|
.extend({
|
||||||
|
title: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
alias: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
||||||
|
|
||||||
|
items: z.array(z.number()).optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaCreateLibraryItem = z
|
||||||
|
.object({
|
||||||
|
item_type: z.nativeEnum(LibraryItemType),
|
||||||
|
title: z.string().optional(),
|
||||||
|
alias: z.string().optional(),
|
||||||
|
comment: z.string(),
|
||||||
|
visible: z.boolean(),
|
||||||
|
read_only: z.boolean(),
|
||||||
|
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
||||||
|
access_policy: z.nativeEnum(AccessPolicy),
|
||||||
|
|
||||||
|
file: z.instanceof(File).optional(),
|
||||||
|
fileName: z.string().optional()
|
||||||
|
})
|
||||||
|
.refine(data => !!data.file || !!data.title, {
|
||||||
|
path: ['title'],
|
||||||
|
message: errorMsg.requiredField
|
||||||
|
})
|
||||||
|
.refine(data => !!data.file || !!data.alias, {
|
||||||
|
path: ['alias'],
|
||||||
|
message: errorMsg.requiredField
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaUpdateLibraryItem = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
item_type: z.nativeEnum(LibraryItemType),
|
||||||
|
title: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
alias: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
comment: z.string(),
|
||||||
|
visible: z.boolean(),
|
||||||
|
read_only: z.boolean()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaVersionInfo = z.object({
|
||||||
|
id: z.coerce.number(),
|
||||||
|
version: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
time_create: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaVersionUpdate = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
version: z.string().nonempty(errorMsg.requiredField),
|
||||||
|
description: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaVersionCreate = z.object({
|
||||||
|
version: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
items: z.array(z.number()).optional()
|
||||||
|
});
|
|
@ -1,7 +1,8 @@
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { ILibraryFilter } from '../models/library';
|
import { ILibraryFilter } from '../models/library';
|
||||||
import { matchLibraryItem, matchLibraryItemLocation } from '../models/libraryAPI';
|
import { matchLibraryItem, matchLibraryItemLocation } from '../models/libraryAPI';
|
||||||
|
|
||||||
import { useLibrary } from './useLibrary';
|
import { useLibrary } from './useLibrary';
|
||||||
|
|
||||||
export function useApplyLibraryFilter(filter: ILibraryFilter) {
|
export function useApplyLibraryFilter(filter: ILibraryFilter) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ICloneLibraryItemDTO, libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
import { ICloneLibraryItemDTO } from './types';
|
||||||
|
|
||||||
export const useCloneItem = () => {
|
export const useCloneItem = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ICreateLibraryItemDTO, libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
import { ICreateLibraryItemDTO } from './types';
|
||||||
|
|
||||||
export const useCreateItem = () => {
|
export const useCreateItem = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ossApi } from '@/features/oss/backend/api';
|
import { KEYS } from '@/backend/configuration';
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { LibraryItemID } from '../models/library';
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
export const useDeleteItem = () => {
|
export const useDeleteItem = () => {
|
||||||
|
@ -17,16 +15,16 @@ export const useDeleteItem = () => {
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() =>
|
() =>
|
||||||
void Promise.allSettled([
|
void Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.resetQueries({ queryKey: rsformsApi.getRSFormQueryOptions({ itemID: variables }).queryKey }),
|
client.resetQueries({ queryKey: KEYS.composite.rsItem({ itemID: variables }) }),
|
||||||
client.resetQueries({ queryKey: ossApi.getOssQueryOptions({ itemID: variables }).queryKey })
|
client.resetQueries({ queryKey: KEYS.composite.ossItem({ itemID: variables }) })
|
||||||
]).catch(console.error),
|
]).catch(console.error),
|
||||||
PARAMETER.navigationDuration
|
PARAMETER.navigationDuration
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
deleteItem: (target: LibraryItemID) => mutation.mutateAsync(target),
|
deleteItem: (target: number) => mutation.mutateAsync(target),
|
||||||
isPending: mutation.isPending
|
isPending: mutation.isPending
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { FolderTree } from '@/features/library/models/FolderTree';
|
import { FolderTree } from '../models/FolderTree';
|
||||||
|
|
||||||
import { LocationHead } from '../models/library';
|
import { LocationHead } from '../models/library';
|
||||||
|
|
||||||
import { useLibrary } from './useLibrary';
|
import { useLibrary } from './useLibrary';
|
||||||
|
|
||||||
export function useFolders() {
|
export function useFolders() {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { queryClient } from '@/backend/queryClient';
|
import { queryClient } from '@/backend/queryClient';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import { useIsMutating } from '@tanstack/react-query';
|
import { useIsMutating } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ossApi } from '@/features/oss/backend/api';
|
import { KEYS } from '@/backend/configuration';
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
|
||||||
|
|
||||||
import { libraryApi } from './api';
|
|
||||||
|
|
||||||
export const useMutatingLibrary = () => {
|
export const useMutatingLibrary = () => {
|
||||||
const countMutations = useIsMutating({ mutationKey: [libraryApi.baseKey] });
|
const countMutations = useIsMutating({ mutationKey: [KEYS.library] });
|
||||||
const countOss = useIsMutating({ mutationKey: [ossApi.baseKey] });
|
const countOss = useIsMutating({ mutationKey: [KEYS.oss] });
|
||||||
const countRSForm = useIsMutating({ mutationKey: [rsformsApi.baseKey] });
|
const countRSForm = useIsMutating({ mutationKey: [KEYS.rsform] });
|
||||||
return countMutations + countOss + countRSForm !== 0;
|
return countMutations + countOss + countRSForm !== 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ossApi } from '@/features/oss/backend/api';
|
import { KEYS } from '@/backend/configuration';
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
|
||||||
|
|
||||||
import { IRenameLocationDTO, libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
import { IRenameLocationDTO } from './types';
|
||||||
|
|
||||||
export const useRenameLocation = () => {
|
export const useRenameLocation = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -12,9 +12,9 @@ export const useRenameLocation = () => {
|
||||||
mutationFn: libraryApi.renameLocation,
|
mutationFn: libraryApi.renameLocation,
|
||||||
onSuccess: () =>
|
onSuccess: () =>
|
||||||
Promise.allSettled([
|
Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [libraryApi.baseKey] }),
|
client.invalidateQueries({ queryKey: [KEYS.library] }),
|
||||||
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }),
|
client.invalidateQueries({ queryKey: [KEYS.rsform] }),
|
||||||
client.invalidateQueries({ queryKey: [ossApi.baseKey] })
|
client.invalidateQueries({ queryKey: [KEYS.oss] })
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
|
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
import { IRSFormDTO } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { AccessPolicy, ILibraryItem, LibraryItemID } from '../models/library';
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
import { AccessPolicy, ILibraryItem } from './types';
|
||||||
|
|
||||||
export const useSetAccessPolicy = () => {
|
export const useSetAccessPolicy = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -12,26 +14,28 @@ export const useSetAccessPolicy = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'set-location'],
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setAccessPolicy,
|
mutationFn: libraryApi.setAccessPolicy,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, access_policy: variables.policy });
|
client.setQueryData(ossKey, { ...ossData, access_policy: variables.policy });
|
||||||
return Promise.allSettled([
|
return Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
...ossData.items
|
...ossData.items
|
||||||
.map(item => {
|
.map(item => {
|
||||||
if (!item.result) {
|
if (!item.result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
|
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
|
||||||
return client.invalidateQueries({ queryKey: itemKey });
|
return client.invalidateQueries({ queryKey: itemKey });
|
||||||
})
|
})
|
||||||
.filter(item => !!item)
|
.filter(item => !!item)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
|
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||||
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, access_policy: variables.policy }));
|
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
|
||||||
|
!prev ? undefined : { ...prev, access_policy: variables.policy }
|
||||||
|
);
|
||||||
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === variables.itemID ? { ...item, access_policy: variables.policy } : item))
|
prev?.map(item => (item.id === variables.itemID ? { ...item, access_policy: variables.policy } : item))
|
||||||
);
|
);
|
||||||
|
@ -39,6 +43,6 @@ export const useSetAccessPolicy = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setAccessPolicy: (data: { itemID: LibraryItemID; policy: AccessPolicy }) => mutation.mutateAsync(data)
|
setAccessPolicy: (data: { itemID: number; policy: AccessPolicy }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ossApi } from '@/features/oss/backend/api';
|
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
import { IRSFormDTO } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
@ -11,8 +13,8 @@ export const useSetEditors = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'set-location'],
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setEditors,
|
mutationFn: libraryApi.setEditors,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, editors: variables.editors });
|
client.setQueryData(ossKey, { ...ossData, editors: variables.editors });
|
||||||
return Promise.allSettled(
|
return Promise.allSettled(
|
||||||
|
@ -21,15 +23,17 @@ export const useSetEditors = () => {
|
||||||
if (!item.result) {
|
if (!item.result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
|
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
|
||||||
return client.invalidateQueries({ queryKey: itemKey });
|
return client.invalidateQueries({ queryKey: itemKey });
|
||||||
})
|
})
|
||||||
.filter(item => !!item)
|
.filter(item => !!item)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
|
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||||
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, editors: variables.editors }));
|
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
|
||||||
|
!prev ? undefined : { ...prev, editors: variables.editors }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
|
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
import { IRSFormDTO } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { ILibraryItem, LibraryItemID } from '../models/library';
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
import { ILibraryItem } from './types';
|
||||||
|
|
||||||
export const useSetLocation = () => {
|
export const useSetLocation = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -12,7 +14,7 @@ export const useSetLocation = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'set-location'],
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setLocation,
|
mutationFn: libraryApi.setLocation,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, location: variables.location });
|
client.setQueryData(ossKey, { ...ossData, location: variables.location });
|
||||||
|
@ -23,15 +25,17 @@ export const useSetLocation = () => {
|
||||||
if (!item.result) {
|
if (!item.result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
|
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
|
||||||
return client.invalidateQueries({ queryKey: itemKey });
|
return client.invalidateQueries({ queryKey: itemKey });
|
||||||
})
|
})
|
||||||
.filter(item => !!item)
|
.filter(item => !!item)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
|
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||||
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, location: variables.location }));
|
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
|
||||||
|
!prev ? undefined : { ...prev, location: variables.location }
|
||||||
|
);
|
||||||
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item))
|
prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item))
|
||||||
);
|
);
|
||||||
|
@ -39,6 +43,6 @@ export const useSetLocation = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setLocation: (data: { itemID: LibraryItemID; location: string }) => mutation.mutateAsync(data)
|
setLocation: (data: { itemID: number; location: string }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
|
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
import { IRSFormDTO } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { ILibraryItem } from '../models/library';
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
import { ILibraryItem } from './types';
|
||||||
|
|
||||||
export const useSetOwner = () => {
|
export const useSetOwner = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -12,7 +14,7 @@ export const useSetOwner = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'set-owner'],
|
mutationKey: [libraryApi.baseKey, 'set-owner'],
|
||||||
mutationFn: libraryApi.setOwner,
|
mutationFn: libraryApi.setOwner,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, owner: variables.owner });
|
client.setQueryData(ossKey, { ...ossData, owner: variables.owner });
|
||||||
|
@ -23,15 +25,17 @@ export const useSetOwner = () => {
|
||||||
if (!item.result) {
|
if (!item.result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
|
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
|
||||||
return client.invalidateQueries({ queryKey: itemKey });
|
return client.invalidateQueries({ queryKey: itemKey });
|
||||||
})
|
})
|
||||||
.filter(item => !!item)
|
.filter(item => !!item)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
|
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||||
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, owner: variables.owner }));
|
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
|
||||||
|
!prev ? undefined : { ...prev, owner: variables.owner }
|
||||||
|
);
|
||||||
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === variables.itemID ? { ...item, owner: variables.owner } : item))
|
prev?.map(item => (item.id === variables.itemID ? { ...item, owner: variables.owner } : item))
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
|
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
|
||||||
import { IRSFormDTO } from '@/features/rsform/backend/api';
|
import { IRSFormDTO } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
import { ILibraryItem, LibraryItemType } from '../models/library';
|
import { KEYS } from '@/backend/configuration';
|
||||||
import { IUpdateLibraryItemDTO, libraryApi } from './api';
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
import { ILibraryItem, IUpdateLibraryItemDTO, LibraryItemType } from './types';
|
||||||
|
|
||||||
export const useUpdateItem = () => {
|
export const useUpdateItem = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -12,7 +14,10 @@ export const useUpdateItem = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'update-item'],
|
mutationKey: [libraryApi.baseKey, 'update-item'],
|
||||||
mutationFn: libraryApi.updateItem,
|
mutationFn: libraryApi.updateItem,
|
||||||
onSuccess: (data: ILibraryItem) => {
|
onSuccess: (data: ILibraryItem) => {
|
||||||
const itemKey = libraryApi.getItemQueryOptions({ itemID: data.id, itemType: data.item_type }).queryKey;
|
const itemKey =
|
||||||
|
data.item_type === LibraryItemType.RSFORM
|
||||||
|
? KEYS.composite.rsItem({ itemID: data.id })
|
||||||
|
: KEYS.composite.ossItem({ itemID: data.id });
|
||||||
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === data.id ? data : item))
|
prev?.map(item => (item.id === data.id ? data : item))
|
||||||
);
|
);
|
||||||
|
@ -23,9 +28,7 @@ export const useUpdateItem = () => {
|
||||||
const schema: IRSFormDTO | undefined = client.getQueryData(itemKey);
|
const schema: IRSFormDTO | undefined = client.getQueryData(itemKey);
|
||||||
if (schema) {
|
if (schema) {
|
||||||
return Promise.allSettled(
|
return Promise.allSettled(
|
||||||
schema.oss.map(item =>
|
schema.oss.map(item => client.invalidateQueries({ queryKey: KEYS.composite.ossItem({ itemID: item.id }) }))
|
||||||
client.invalidateQueries({ queryKey: ossApi.getOssQueryOptions({ itemID: item.id }).queryKey })
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ILibraryItem, LibraryItemID } from '../models/library';
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
import { ILibraryItem } from './types';
|
||||||
|
|
||||||
export function useUpdateTimestamp() {
|
export function useUpdateTimestamp() {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
return {
|
return {
|
||||||
updateTimestamp: (target: LibraryItemID) =>
|
updateTimestamp: (target: number) =>
|
||||||
client.setQueryData(
|
client.setQueryData(
|
||||||
libraryApi.libraryListKey, //
|
libraryApi.libraryListKey, //
|
||||||
(prev: ILibraryItem[] | undefined) =>
|
(prev: ILibraryItem[] | undefined) =>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { LibraryItemID } from '../models/library';
|
import { libraryApi } from './api';
|
||||||
import { IVersionCreateDTO, libraryApi } from './api';
|
import { IVersionCreateDTO } from './types';
|
||||||
import { useUpdateTimestamp } from './useUpdateTimestamp';
|
import { useUpdateTimestamp } from './useUpdateTimestamp';
|
||||||
|
|
||||||
export const useVersionCreate = () => {
|
export const useVersionCreate = () => {
|
||||||
|
@ -13,12 +13,12 @@ export const useVersionCreate = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'create-version'],
|
mutationKey: [libraryApi.baseKey, 'create-version'],
|
||||||
mutationFn: libraryApi.versionCreate,
|
mutationFn: libraryApi.versionCreate,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
client.setQueryData(KEYS.composite.rsItem({ itemID: data.schema.id }), data.schema);
|
||||||
updateTimestamp(data.schema.id);
|
updateTimestamp(data.schema.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
versionCreate: (data: { itemID: LibraryItemID; data: IVersionCreateDTO }) =>
|
versionCreate: (data: { itemID: number; data: IVersionCreateDTO }) =>
|
||||||
mutation.mutateAsync(data).then(response => response.version)
|
mutation.mutateAsync(data).then(response => response.version)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { IRSFormDTO, rsformsApi } from '@/features/rsform/backend/api';
|
import { IRSFormDTO } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { LibraryItemID, VersionID } from '../models/library';
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
export const useVersionDelete = () => {
|
export const useVersionDelete = () => {
|
||||||
|
@ -11,19 +12,17 @@ export const useVersionDelete = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'delete-version'],
|
mutationKey: [libraryApi.baseKey, 'delete-version'],
|
||||||
mutationFn: libraryApi.versionDelete,
|
mutationFn: libraryApi.versionDelete,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
client.setQueryData(
|
client.setQueryData(KEYS.composite.rsItem({ itemID: variables.itemID }), (prev: IRSFormDTO | undefined) =>
|
||||||
rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey,
|
!prev
|
||||||
(prev: IRSFormDTO | undefined) =>
|
? undefined
|
||||||
!prev
|
: {
|
||||||
? undefined
|
...prev,
|
||||||
: {
|
versions: prev.versions.filter(version => version.id !== variables.versionID)
|
||||||
...prev,
|
}
|
||||||
versions: prev.versions.filter(version => version.id !== variables.versionID)
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) => mutation.mutateAsync(data)
|
versionDelete: (data: { itemID: number; versionID: number }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { rsformsApi } from '@/features/rsform/backend/api';
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { VersionID } from '../models/library';
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
export const useVersionRestore = () => {
|
export const useVersionRestore = () => {
|
||||||
|
@ -11,11 +10,11 @@ export const useVersionRestore = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'restore-version'],
|
mutationKey: [libraryApi.baseKey, 'restore-version'],
|
||||||
mutationFn: libraryApi.versionRestore,
|
mutationFn: libraryApi.versionRestore,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
|
||||||
return client.invalidateQueries({ queryKey: [libraryApi.baseKey] });
|
return client.invalidateQueries({ queryKey: [libraryApi.baseKey] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
versionRestore: (data: { versionID: VersionID }) => mutation.mutateAsync(data)
|
versionRestore: (data: { versionID: number }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,32 +1,31 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { IRSFormDTO, rsformsApi } from '@/features/rsform/backend/api';
|
import { IRSFormDTO } from '@/features/rsform/backend/types';
|
||||||
|
|
||||||
import { IVersionUpdateDTO, libraryApi } from './api';
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
import { IVersionUpdateDTO } from './types';
|
||||||
|
|
||||||
export const useVersionUpdate = () => {
|
export const useVersionUpdate = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [libraryApi.baseKey, 'update-version'],
|
mutationKey: [libraryApi.baseKey, 'update-version'],
|
||||||
mutationFn: libraryApi.versionUpdate,
|
mutationFn: libraryApi.versionUpdate,
|
||||||
onSuccess: data => {
|
onSuccess: (data, variables) => {
|
||||||
client.setQueryData(
|
client.setQueryData(KEYS.composite.rsItem({ itemID: variables.itemID }), (prev: IRSFormDTO | undefined) =>
|
||||||
rsformsApi.getRSFormQueryOptions({ itemID: data.item }).queryKey,
|
!prev
|
||||||
(prev: IRSFormDTO | undefined) =>
|
? undefined
|
||||||
!prev
|
: {
|
||||||
? undefined
|
...prev,
|
||||||
: {
|
versions: prev.versions.map(version =>
|
||||||
...prev,
|
version.id === data.id ? { ...version, description: data.description, version: data.version } : version
|
||||||
versions: prev.versions.map(version =>
|
)
|
||||||
version.id === data.id
|
}
|
||||||
? { ...version, description: data.description, version: data.version }
|
|
||||||
: version
|
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
versionUpdate: (data: IVersionUpdateDTO) => mutation.mutateAsync(data)
|
versionUpdate: (data: { itemID: number; version: IVersionUpdateDTO }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { Suspense } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
|
import { InfoUsers, SelectUser, useLabelUser, useRoleStore, UserRole } from '@/features/users';
|
||||||
|
|
||||||
import { Overlay, Tooltip } from '@/components/Container';
|
import { Overlay, Tooltip } from '@/components/Container';
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { useDropdown } from '@/components/Dropdown';
|
import { useDropdown } from '@/components/Dropdown';
|
||||||
|
@ -16,19 +18,28 @@ import {
|
||||||
import { Loader } from '@/components/Loader';
|
import { Loader } from '@/components/Loader';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { ValueIcon } from '@/components/View';
|
import { ValueIcon } from '@/components/View';
|
||||||
import { InfoUsers, SelectUser, useLabelUser, useRoleStore } from '@/features/users';
|
|
||||||
import { UserRole } from '@/features/users/models/user';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { promptText } from '@/utils/labels';
|
import { promptText } from '@/utils/labels';
|
||||||
|
|
||||||
|
import { ILibraryItemData } from '../backend/types';
|
||||||
import { useMutatingLibrary } from '../backend/useMutatingLibrary';
|
import { useMutatingLibrary } from '../backend/useMutatingLibrary';
|
||||||
import { useSetLocation } from '../backend/useSetLocation';
|
import { useSetLocation } from '../backend/useSetLocation';
|
||||||
import { useSetOwner } from '../backend/useSetOwner';
|
import { useSetOwner } from '../backend/useSetOwner';
|
||||||
import { ILibraryItemEditor } from '../models/library';
|
|
||||||
import { useLibrarySearchStore } from '../stores/librarySearch';
|
import { useLibrarySearchStore } from '../stores/librarySearch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents common {@link ILibraryItem} editor controller.
|
||||||
|
*/
|
||||||
|
export interface ILibraryItemEditor {
|
||||||
|
schema: ILibraryItemData;
|
||||||
|
deleteSchema: () => void;
|
||||||
|
|
||||||
|
isMutable: boolean;
|
||||||
|
isAttachedToOSS: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface EditorLibraryItemProps {
|
interface EditorLibraryItemProps {
|
||||||
controller: ILibraryItemEditor;
|
controller: ILibraryItemEditor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,16 @@ import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||||
import { IconOSS } from '@/components/Icons';
|
import { IconOSS } from '@/components/Icons';
|
||||||
import { Label } from '@/components/Input';
|
import { Label } from '@/components/Input';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { ILibraryItemReference } from '@/features/library/models/library';
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
|
import { ILibraryItemReference } from '../models/library';
|
||||||
|
|
||||||
interface MiniSelectorOSSProps extends CProps.Styling {
|
interface MiniSelectorOSSProps extends CProps.Styling {
|
||||||
items: ILibraryItemReference[];
|
items: ILibraryItemReference[];
|
||||||
onSelect: (event: CProps.EventMouse, newValue: ILibraryItemReference) => void;
|
onSelect: (event: CProps.EventMouse, newValue: ILibraryItemReference) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MiniSelectorOSS({ items, onSelect, className, ...restProps }: MiniSelectorOSSProps) {
|
export function MiniSelectorOSS({ items, onSelect, className, ...restProps }: MiniSelectorOSSProps) {
|
||||||
const ossMenu = useDropdown();
|
const ossMenu = useDropdown();
|
||||||
|
|
||||||
function onToggle(event: CProps.EventMouse) {
|
function onToggle(event: CProps.EventMouse) {
|
||||||
|
@ -50,5 +51,3 @@ function MiniSelectorOSS({ items, onSelect, className, ...restProps }: MiniSelec
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MiniSelectorOSS;
|
|
|
@ -1,25 +1,26 @@
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { FlexColumn } from '@/components/Container';
|
import { FlexColumn } from '@/components/Container';
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/DataTable';
|
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/DataTable';
|
||||||
import { Dropdown, useDropdown } from '@/components/Dropdown';
|
import { Dropdown, useDropdown } from '@/components/Dropdown';
|
||||||
import { IconClose, IconFolderTree } from '@/components/Icons';
|
import { IconClose, IconFolderTree } from '@/components/Icons';
|
||||||
|
import { SearchBar } from '@/components/Input';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { SearchBar } from '@/components/shared/SearchBar';
|
|
||||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/features/library/models/library';
|
|
||||||
import { matchLibraryItem } from '@/features/library/models/libraryAPI';
|
|
||||||
import { APP_COLORS } from '@/styling/colors';
|
import { APP_COLORS } from '@/styling/colors';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import SelectLocation from '../../library/components/SelectLocation';
|
import { ILibraryItem, LibraryItemType } from '../backend/types';
|
||||||
|
import { matchLibraryItem } from '../models/libraryAPI';
|
||||||
|
|
||||||
|
import SelectLocation from './SelectLocation';
|
||||||
|
|
||||||
interface PickSchemaProps extends CProps.Styling {
|
interface PickSchemaProps extends CProps.Styling {
|
||||||
id?: string;
|
id?: string;
|
||||||
value: LibraryItemID | null;
|
value: number | null;
|
||||||
onChange: (newValue: LibraryItemID) => void;
|
onChange: (newValue: number) => void;
|
||||||
|
|
||||||
initialFilter?: string;
|
initialFilter?: string;
|
||||||
rows?: number;
|
rows?: number;
|
|
@ -1,13 +1,14 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { PolicyIcon } from '@/components/DomainIcons';
|
import { DomIconProps } from '@/components/DomainIcons';
|
||||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||||
|
import { IconPrivate, IconProtected, IconPublic } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { describeAccessPolicy, labelAccessPolicy } from '@/utils/labels';
|
|
||||||
|
|
||||||
import { AccessPolicy } from '../models/library';
|
import { AccessPolicy } from '../backend/types';
|
||||||
|
import { describeAccessPolicy, labelAccessPolicy } from '../labels';
|
||||||
|
|
||||||
interface SelectAccessPolicyProps extends CProps.Styling {
|
interface SelectAccessPolicyProps extends CProps.Styling {
|
||||||
value: AccessPolicy;
|
value: AccessPolicy;
|
||||||
|
@ -17,7 +18,7 @@ interface SelectAccessPolicyProps extends CProps.Styling {
|
||||||
stretchLeft?: boolean;
|
stretchLeft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectAccessPolicy({ value, disabled, stretchLeft, onChange, ...restProps }: SelectAccessPolicyProps) {
|
export function SelectAccessPolicy({ value, disabled, stretchLeft, onChange, ...restProps }: SelectAccessPolicyProps) {
|
||||||
const menu = useDropdown();
|
const menu = useDropdown();
|
||||||
|
|
||||||
function handleChange(newValue: AccessPolicy) {
|
function handleChange(newValue: AccessPolicy) {
|
||||||
|
@ -52,4 +53,14 @@ function SelectAccessPolicy({ value, disabled, stretchLeft, onChange, ...restPro
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectAccessPolicy;
|
/** Icon for access policy. */
|
||||||
|
function PolicyIcon({ value, size = '1.25rem', className }: DomIconProps<AccessPolicy>) {
|
||||||
|
switch (value) {
|
||||||
|
case AccessPolicy.PRIVATE:
|
||||||
|
return <IconPrivate size={size} className={className ?? 'text-warn-600'} />;
|
||||||
|
case AccessPolicy.PROTECTED:
|
||||||
|
return <IconProtected size={size} className={className ?? 'text-sec-600'} />;
|
||||||
|
case AccessPolicy.PUBLIC:
|
||||||
|
return <IconPublic size={size} className={className ?? 'text-ok-600'} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { SelectorButton } from '@/components/Control';
|
import { SelectorButton } from '@/components/Control';
|
||||||
import { ItemTypeIcon } from '@/components/DomainIcons';
|
import { DomIconProps } from '@/components/DomainIcons';
|
||||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown';
|
||||||
|
import { IconOSS, IconRSForm } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { describeLibraryItemType, labelLibraryItemType } from '@/utils/labels';
|
|
||||||
|
|
||||||
import { LibraryItemType } from '../models/library';
|
import { LibraryItemType } from '../backend/types';
|
||||||
|
import { describeLibraryItemType, labelLibraryItemType } from '../labels';
|
||||||
|
|
||||||
interface SelectItemTypeProps extends CProps.Styling {
|
interface SelectItemTypeProps extends CProps.Styling {
|
||||||
value: LibraryItemType;
|
value: LibraryItemType;
|
||||||
|
@ -16,7 +17,7 @@ interface SelectItemTypeProps extends CProps.Styling {
|
||||||
stretchLeft?: boolean;
|
stretchLeft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectItemType({ value, disabled, stretchLeft, onChange, ...restProps }: SelectItemTypeProps) {
|
export function SelectItemType({ value, disabled, stretchLeft, onChange, ...restProps }: SelectItemTypeProps) {
|
||||||
const menu = useDropdown();
|
const menu = useDropdown();
|
||||||
|
|
||||||
function handleChange(newValue: LibraryItemType) {
|
function handleChange(newValue: LibraryItemType) {
|
||||||
|
@ -53,4 +54,12 @@ function SelectItemType({ value, disabled, stretchLeft, onChange, ...restProps }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectItemType;
|
/** Icon for library item type. */
|
||||||
|
function ItemTypeIcon({ value, size = '1.25rem', className }: DomIconProps<LibraryItemType>) {
|
||||||
|
switch (value) {
|
||||||
|
case LibraryItemType.RSFORM:
|
||||||
|
return <IconRSForm size={size} className={className ?? 'text-sec-600'} />;
|
||||||
|
case LibraryItemType.OSS:
|
||||||
|
return <IconOSS size={size} className={className ?? 'text-ok-600'} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import clsx from 'clsx';
|
||||||
import { SelectSingle } from '@/components/Input';
|
import { SelectSingle } from '@/components/Input';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
|
|
||||||
import { ILibraryItem, LibraryItemID } from '../models/library';
|
import { ILibraryItem } from '../backend/types';
|
||||||
import { matchLibraryItem } from '../models/libraryAPI';
|
import { matchLibraryItem } from '../models/libraryAPI';
|
||||||
|
|
||||||
interface SelectLibraryItemProps extends CProps.Styling {
|
interface SelectLibraryItemProps extends CProps.Styling {
|
||||||
|
@ -17,7 +17,7 @@ interface SelectLibraryItemProps extends CProps.Styling {
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectLibraryItem({
|
export function SelectLibraryItem({
|
||||||
className,
|
className,
|
||||||
items,
|
items,
|
||||||
value,
|
value,
|
||||||
|
@ -31,9 +31,9 @@ function SelectLibraryItem({
|
||||||
label: `${cst.alias}: ${cst.title}`
|
label: `${cst.alias}: ${cst.title}`
|
||||||
})) ?? [];
|
})) ?? [];
|
||||||
|
|
||||||
function filter(option: { value: LibraryItemID | undefined; label: string }, inputValue: string) {
|
function filter(option: { value: string | undefined; label: string }, query: string) {
|
||||||
const item = items?.find(item => item.id === option.value);
|
const item = items?.find(item => item.id === Number(option.value));
|
||||||
return !item ? false : matchLibraryItem(item, inputValue);
|
return !item ? false : matchLibraryItem(item, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -42,12 +42,9 @@ function SelectLibraryItem({
|
||||||
options={options}
|
options={options}
|
||||||
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
||||||
onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
|
onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
|
||||||
// @ts-expect-error: TODO: use type definitions from react-select in filter object
|
|
||||||
filterOption={filter}
|
filterOption={filter}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectLibraryItem;
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened } from '@/components/Icons';
|
import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { FolderNode } from '@/features/library/models/FolderTree';
|
|
||||||
|
|
||||||
import { useFolders } from '../backend/useFolders';
|
import { useFolders } from '../backend/useFolders';
|
||||||
import { labelFolderNode } from '../labels';
|
import { labelFolderNode } from '../labels';
|
||||||
|
import { FolderNode } from '../models/FolderTree';
|
||||||
|
|
||||||
interface SelectLocationProps extends CProps.Styling {
|
interface SelectLocationProps extends CProps.Styling {
|
||||||
value: string;
|
value: string;
|
||||||
|
|
|
@ -17,7 +17,7 @@ interface SelectLocationContextProps extends CProps.Styling {
|
||||||
stretchTop?: boolean;
|
stretchTop?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectLocationContext({
|
export function SelectLocationContext({
|
||||||
value,
|
value,
|
||||||
title = 'Проводник...',
|
title = 'Проводник...',
|
||||||
onChange,
|
onChange,
|
||||||
|
@ -56,5 +56,3 @@ function SelectLocationContext({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectLocationContext;
|
|
||||||
|
|
|
@ -17,7 +17,13 @@ interface SelectLocationHeadProps extends CProps.Styling {
|
||||||
excluded?: LocationHead[];
|
excluded?: LocationHead[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectLocationHead({ value, excluded = [], onChange, className, ...restProps }: SelectLocationHeadProps) {
|
export function SelectLocationHead({
|
||||||
|
value,
|
||||||
|
excluded = [],
|
||||||
|
onChange,
|
||||||
|
className,
|
||||||
|
...restProps
|
||||||
|
}: SelectLocationHeadProps) {
|
||||||
const menu = useDropdown();
|
const menu = useDropdown();
|
||||||
|
|
||||||
function handleChange(newValue: LocationHead) {
|
function handleChange(newValue: LocationHead) {
|
||||||
|
@ -60,5 +66,3 @@ function SelectLocationHead({ value, excluded = [], onChange, className, ...rest
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectLocationHead;
|
|
||||||
|
|
|
@ -4,21 +4,21 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { SelectSingle } from '@/components/Input';
|
import { SelectSingle } from '@/components/Input';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { IVersionInfo, VersionID } from '@/features/library/models/library';
|
|
||||||
|
|
||||||
import { labelVersion } from '../labels';
|
import { labelVersion } from '../../rsform/labels';
|
||||||
|
import { IVersionInfo } from '../backend/types';
|
||||||
|
|
||||||
interface SelectVersionProps extends CProps.Styling {
|
interface SelectVersionProps extends CProps.Styling {
|
||||||
id?: string;
|
id?: string;
|
||||||
items?: IVersionInfo[];
|
items?: IVersionInfo[];
|
||||||
value?: VersionID;
|
value?: number;
|
||||||
onChange: (newValue?: VersionID) => void;
|
onChange: (newValue?: number) => void;
|
||||||
|
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectVersion({ id, className, items, value, onChange, ...restProps }: SelectVersionProps) {
|
export function SelectVersion({ id, className, items, value, onChange, ...restProps }: SelectVersionProps) {
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
value: undefined,
|
value: undefined,
|
||||||
|
@ -46,5 +46,3 @@ function SelectVersion({ id, className, items, value, onChange, ...restProps }:
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectVersion;
|
|
|
@ -1,18 +1,20 @@
|
||||||
|
import { BadgeHelp, HelpTopic } from '@/features/help';
|
||||||
|
import { useRoleStore, UserRole } from '@/features/users';
|
||||||
|
|
||||||
import { Overlay } from '@/components/Container';
|
import { Overlay } from '@/components/Container';
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { VisibilityIcon } from '@/components/DomainIcons';
|
import { VisibilityIcon } from '@/components/DomainIcons';
|
||||||
import { IconImmutable, IconMutable } from '@/components/Icons';
|
import { IconImmutable, IconMutable } from '@/components/Icons';
|
||||||
import { Label } from '@/components/Input';
|
import { Label } from '@/components/Input';
|
||||||
import { BadgeHelp } from '@/components/shared/BadgeHelp';
|
|
||||||
import { HelpTopic } from '@/features/help/models/helpTopic';
|
|
||||||
import { useMutatingLibrary } from '@/features/library/backend/useMutatingLibrary';
|
|
||||||
import { useSetAccessPolicy } from '@/features/library/backend/useSetAccessPolicy';
|
|
||||||
import SelectAccessPolicy from '@/features/library/components/SelectAccessPolicy';
|
|
||||||
import { AccessPolicy, ILibraryItemEditor } from '@/features/library/models/library';
|
|
||||||
import { useRoleStore } from '@/features/users';
|
|
||||||
import { UserRole } from '@/features/users/models/user';
|
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
|
import { AccessPolicy } from '../backend/types';
|
||||||
|
import { useMutatingLibrary } from '../backend/useMutatingLibrary';
|
||||||
|
import { useSetAccessPolicy } from '../backend/useSetAccessPolicy';
|
||||||
|
|
||||||
|
import { ILibraryItemEditor } from './EditorLibraryItem';
|
||||||
|
import { SelectAccessPolicy } from './SelectAccessPolicy';
|
||||||
|
|
||||||
interface ToolbarItemAccessProps {
|
interface ToolbarItemAccessProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
toggleVisible: () => void;
|
toggleVisible: () => void;
|
||||||
|
@ -21,7 +23,13 @@ interface ToolbarItemAccessProps {
|
||||||
controller: ILibraryItemEditor;
|
controller: ILibraryItemEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, controller }: ToolbarItemAccessProps) {
|
export function ToolbarItemAccess({
|
||||||
|
visible,
|
||||||
|
toggleVisible,
|
||||||
|
readOnly,
|
||||||
|
toggleReadOnly,
|
||||||
|
controller
|
||||||
|
}: ToolbarItemAccessProps) {
|
||||||
const role = useRoleStore(state => state.role);
|
const role = useRoleStore(state => state.role);
|
||||||
const isProcessing = useMutatingLibrary();
|
const isProcessing = useMutatingLibrary();
|
||||||
const policy = controller.schema.access_policy;
|
const policy = controller.schema.access_policy;
|
||||||
|
@ -66,5 +74,3 @@ function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, c
|
||||||
</Overlay>
|
</Overlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ToolbarItemAccess;
|
|
|
@ -1,19 +1,20 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { Label, TextArea } from '@/components/Input';
|
import { Label, TextArea } from '@/components/Input';
|
||||||
import { ModalForm } from '@/components/Modal';
|
import { ModalForm } from '@/components/Modal';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { limits } from '@/utils/constants';
|
import { limits } from '@/utils/constants';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
import SelectLocationContext from '../components/SelectLocationContext';
|
import { SelectLocationContext } from '../components/SelectLocationContext';
|
||||||
import SelectLocationHead from '../components/SelectLocationHead';
|
import { SelectLocationHead } from '../components/SelectLocationHead';
|
||||||
import { LocationHead } from '../models/library';
|
import { LocationHead } from '../models/library';
|
||||||
import { combineLocation, validateLocation } from '../models/libraryAPI';
|
import { combineLocation, validateLocation } from '../models/libraryAPI';
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
|
import { useAuthSuspense } from '@/features/auth';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { VisibilityIcon } from '@/components/DomainIcons';
|
import { VisibilityIcon } from '@/components/DomainIcons';
|
||||||
import { Checkbox, Label, TextArea, TextInput } from '@/components/Input';
|
import { Checkbox, Label, TextArea, TextInput } from '@/components/Input';
|
||||||
import { ModalForm } from '@/components/Modal';
|
import { ModalForm } from '@/components/Modal';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
|
||||||
import { ICloneLibraryItemDTO, schemaCloneLibraryItem } from '@/features/library/backend/api';
|
|
||||||
import { useCloneItem } from '@/features/library/backend/useCloneItem';
|
|
||||||
import SelectAccessPolicy from '@/features/library/components/SelectAccessPolicy';
|
|
||||||
import SelectLocationContext from '@/features/library/components/SelectLocationContext';
|
|
||||||
import SelectLocationHead from '@/features/library/components/SelectLocationHead';
|
|
||||||
import { AccessPolicy, ILibraryItem, LocationHead } from '@/features/library/models/library';
|
|
||||||
import { cloneTitle, combineLocation } from '@/features/library/models/libraryAPI';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { ConstituentaID } from '../models/rsform';
|
import { AccessPolicy, ICloneLibraryItemDTO, ILibraryItem, schemaCloneLibraryItem } from '../backend/types';
|
||||||
|
import { useCloneItem } from '../backend/useCloneItem';
|
||||||
|
import { SelectAccessPolicy } from '../components/SelectAccessPolicy';
|
||||||
|
import { SelectLocationContext } from '../components/SelectLocationContext';
|
||||||
|
import { SelectLocationHead } from '../components/SelectLocationHead';
|
||||||
|
import { LocationHead } from '../models/library';
|
||||||
|
import { cloneTitle, combineLocation } from '../models/libraryAPI';
|
||||||
|
|
||||||
export interface DlgCloneLibraryItemProps {
|
export interface DlgCloneLibraryItemProps {
|
||||||
base: ILibraryItem;
|
base: ILibraryItem;
|
||||||
initialLocation: string;
|
initialLocation: string;
|
||||||
selected: ConstituentaID[];
|
selected: number[];
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,36 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { Controller, useForm, useWatch } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Controller, useForm, useWatch } from 'react-hook-form';
|
|
||||||
|
|
||||||
import { Checkbox, TextArea, TextInput } from '@/components/Input';
|
import { Checkbox, TextArea, TextInput } from '@/components/Input';
|
||||||
import { ModalForm } from '@/components/Modal';
|
import { ModalForm } from '@/components/Modal';
|
||||||
import { IVersionCreateDTO, schemaVersionCreate } from '@/features/library/backend/api';
|
|
||||||
import { useVersionCreate } from '@/features/library/backend/useVersionCreate';
|
|
||||||
import { IVersionInfo, LibraryItemID, VersionID } from '@/features/library/models/library';
|
|
||||||
import { nextVersion } from '@/features/library/models/libraryAPI';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
import { ConstituentaID } from '../models/rsform';
|
import { IVersionCreateDTO, IVersionInfo, schemaVersionCreate } from '../backend/types';
|
||||||
|
import { useVersionCreate } from '../backend/useVersionCreate';
|
||||||
|
import { nextVersion } from '../models/libraryAPI';
|
||||||
|
|
||||||
export interface DlgCreateVersionProps {
|
export interface DlgCreateVersionProps {
|
||||||
itemID: LibraryItemID;
|
itemID: number;
|
||||||
versions: IVersionInfo[];
|
versions: IVersionInfo[];
|
||||||
onCreate: (newVersion: VersionID) => void;
|
onCreate: (newVersion: number) => void;
|
||||||
selected: ConstituentaID[];
|
selected: number[];
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgCreateVersion() {
|
function DlgCreateVersion() {
|
||||||
const {
|
const { itemID, versions, selected, totalCount, onCreate } = useDialogsStore(
|
||||||
itemID, //
|
state => state.props as DlgCreateVersionProps
|
||||||
versions,
|
);
|
||||||
selected,
|
|
||||||
totalCount,
|
|
||||||
onCreate
|
|
||||||
} = useDialogsStore(state => state.props as DlgCreateVersionProps);
|
|
||||||
const { versionCreate } = useVersionCreate();
|
const { versionCreate } = useVersionCreate();
|
||||||
|
|
||||||
const { register, handleSubmit, control } = useForm<IVersionCreateDTO>({
|
const { register, handleSubmit, control } = useForm<IVersionCreateDTO>({
|
||||||
resolver: zodResolver(schemaVersionCreate),
|
resolver: zodResolver(schemaVersionCreate),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
version: versions.length > 0 ? nextVersion(versions[0].version) : '1.0.0',
|
version: versions.length > 0 ? nextVersion(versions[versions.length - 1].version) : '1.0.0',
|
||||||
description: '',
|
description: '',
|
||||||
items: undefined
|
items: undefined
|
||||||
}
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
import { SelectUser, TableUsers, useUsers } from '@/features/users';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { IconRemove } from '@/components/Icons';
|
import { IconRemove } from '@/components/Icons';
|
||||||
import { Label } from '@/components/Input';
|
import { Label } from '@/components/Input';
|
||||||
import { ModalForm } from '@/components/Modal';
|
import { ModalForm } from '@/components/Modal';
|
||||||
import { SelectUser, TableUsers, useUsers } from '@/features/users';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { useSetEditors } from '../../backend/useSetEditors';
|
import { useSetEditors } from '../../backend/useSetEditors';
|
||||||
|
|
|
@ -1,28 +1,29 @@
|
||||||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
|
import { useRSFormSuspense } from '@/features/rsform';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { IconReset, IconSave } from '@/components/Icons';
|
import { IconReset, IconSave } from '@/components/Icons';
|
||||||
import { TextArea, TextInput } from '@/components/Input';
|
import { TextArea, TextInput } from '@/components/Input';
|
||||||
import { ModalView } from '@/components/Modal';
|
import { ModalView } from '@/components/Modal';
|
||||||
import { IVersionUpdateDTO, schemaVersionUpdate } from '@/features/library/backend/api';
|
|
||||||
import { useMutatingLibrary } from '@/features/library/backend/useMutatingLibrary';
|
|
||||||
import { useVersionDelete } from '@/features/library/backend/useVersionDelete';
|
|
||||||
import { useVersionUpdate } from '@/features/library/backend/useVersionUpdate';
|
|
||||||
import { LibraryItemID, VersionID } from '@/features/library/models/library';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
import { useRSFormSuspense } from '../../backend/useRSForm';
|
import { IVersionUpdateDTO, schemaVersionUpdate } from '../../backend/types';
|
||||||
|
import { useMutatingLibrary } from '../../backend/useMutatingLibrary';
|
||||||
|
import { useVersionDelete } from '../../backend/useVersionDelete';
|
||||||
|
import { useVersionUpdate } from '../../backend/useVersionUpdate';
|
||||||
|
|
||||||
import TableVersions from './TableVersions';
|
import TableVersions from './TableVersions';
|
||||||
|
|
||||||
export interface DlgEditVersionsProps {
|
export interface DlgEditVersionsProps {
|
||||||
itemID: LibraryItemID;
|
itemID: number;
|
||||||
afterDelete: (targetVersion: VersionID) => void;
|
afterDelete: (targetVersion: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgEditVersions() {
|
function DlgEditVersions() {
|
||||||
|
@ -42,9 +43,9 @@ function DlgEditVersions() {
|
||||||
} = useForm<IVersionUpdateDTO>({
|
} = useForm<IVersionUpdateDTO>({
|
||||||
resolver: zodResolver(schemaVersionUpdate),
|
resolver: zodResolver(schemaVersionUpdate),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: schema.versions[0].id,
|
id: schema.versions[schema.versions.length - 1].id,
|
||||||
version: schema.versions[0].version,
|
version: schema.versions[schema.versions.length - 1].version,
|
||||||
description: schema.versions[0].description
|
description: schema.versions[schema.versions.length - 1].description
|
||||||
},
|
},
|
||||||
context: { schema: schema }
|
context: { schema: schema }
|
||||||
});
|
});
|
||||||
|
@ -56,7 +57,7 @@ function DlgEditVersions() {
|
||||||
[schema, versionID, versionName]
|
[schema, versionID, versionName]
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSelectVersion(targetVersion: VersionID) {
|
function handleSelectVersion(targetVersion: number) {
|
||||||
const ver = schema.versions.find(ver => ver.id === targetVersion);
|
const ver = schema.versions.find(ver => ver.id === targetVersion);
|
||||||
if (!ver) {
|
if (!ver) {
|
||||||
return;
|
return;
|
||||||
|
@ -64,7 +65,7 @@ function DlgEditVersions() {
|
||||||
reset({ ...ver });
|
reset({ ...ver });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeleteVersion(targetVersion: VersionID) {
|
function handleDeleteVersion(targetVersion: number) {
|
||||||
const nextVer = schema.versions.find(ver => ver.id !== targetVersion);
|
const nextVer = schema.versions.find(ver => ver.id !== targetVersion);
|
||||||
void versionDelete({ itemID: itemID, versionID: targetVersion }).then(() => {
|
void versionDelete({ itemID: itemID, versionID: targetVersion }).then(() => {
|
||||||
if (!nextVer) {
|
if (!nextVer) {
|
||||||
|
@ -80,20 +81,20 @@ function DlgEditVersions() {
|
||||||
if (!isDirty || isProcessing || !isValid) {
|
if (!isDirty || isProcessing || !isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void versionUpdate(data).then(() => reset({ ...data }));
|
void versionUpdate({ itemID: itemID, version: data }).then(() => reset({ ...data }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalView header='Редактирование версий' className='flex flex-col w-[40rem] px-6 gap-3 pb-6'>
|
<ModalView header='Редактирование версий' className='flex flex-col w-[40rem] px-6 gap-3 pb-6'>
|
||||||
<TableVersions
|
<TableVersions
|
||||||
processing={isProcessing}
|
processing={isProcessing}
|
||||||
items={schema.versions}
|
items={schema.versions.reverse()}
|
||||||
onDelete={handleDeleteVersion}
|
onDelete={handleDeleteVersion}
|
||||||
onSelect={handleSelectVersion}
|
onSelect={handleSelectVersion}
|
||||||
selected={versionID}
|
selected={versionID}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<form className='flex' onSubmit={event => void handleSubmit(onUpdate)(event)}>
|
<form className='flex items-center ' onSubmit={event => void handleSubmit(onUpdate)(event)}>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='dlg_version'
|
id='dlg_version'
|
||||||
{...register('version')}
|
{...register('version')}
|
||||||
|
@ -102,7 +103,7 @@ function DlgEditVersions() {
|
||||||
className='w-[16rem] mr-3'
|
className='w-[16rem] mr-3'
|
||||||
error={formErrors.version}
|
error={formErrors.version}
|
||||||
/>
|
/>
|
||||||
<div className='cc-icons'>
|
<div className='cc-icons h-fit'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
type='submit'
|
type='submit'
|
||||||
title={isValid ? 'Сохранить изменения' : errorMsg.versionTaken}
|
title={isValid ? 'Сохранить изменения' : errorMsg.versionTaken}
|
|
@ -1,20 +1,21 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/DataTable';
|
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/DataTable';
|
||||||
import { IconRemove } from '@/components/Icons';
|
import { IconRemove } from '@/components/Icons';
|
||||||
import { IVersionInfo, VersionID } from '@/features/library/models/library';
|
|
||||||
import { APP_COLORS } from '@/styling/colors';
|
import { APP_COLORS } from '@/styling/colors';
|
||||||
|
|
||||||
|
import { IVersionInfo } from '../../backend/types';
|
||||||
|
|
||||||
interface TableVersionsProps {
|
interface TableVersionsProps {
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
items: IVersionInfo[];
|
items: IVersionInfo[];
|
||||||
selected?: VersionID;
|
selected?: number;
|
||||||
onDelete: (versionID: VersionID) => void;
|
onDelete: (versionID: number) => void;
|
||||||
onSelect: (versionID: VersionID) => void;
|
onSelect: (versionID: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<IVersionInfo>();
|
const columnHelper = createColumnHelper<IVersionInfo>();
|
||||||
|
@ -22,7 +23,7 @@ const columnHelper = createColumnHelper<IVersionInfo>();
|
||||||
function TableVersions({ processing, items, onDelete, selected, onSelect }: TableVersionsProps) {
|
function TableVersions({ processing, items, onDelete, selected, onSelect }: TableVersionsProps) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
function handleDeleteVersion(event: React.MouseEvent, targetVersion: VersionID) {
|
function handleDeleteVersion(event: React.MouseEvent, targetVersion: number) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
onDelete(targetVersion);
|
onDelete(targetVersion);
|
16
rsconcept/frontend/src/features/library/index.ts
Normal file
16
rsconcept/frontend/src/features/library/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export { AccessPolicy, type ILibraryItem, type IVersionInfo, LibraryItemType } from './backend/types';
|
||||||
|
export { useDeleteItem } from './backend/useDeleteItem';
|
||||||
|
export { useLibrary, useLibrarySuspense } from './backend/useLibrary';
|
||||||
|
export { useMutatingLibrary } from './backend/useMutatingLibrary';
|
||||||
|
export { useTemplatesSuspense } from './backend/useTemplates';
|
||||||
|
export { useUpdateItem } from './backend/useUpdateItem';
|
||||||
|
export { useUpdateTimestamp } from './backend/useUpdateTimestamp';
|
||||||
|
export { useVersionRestore } from './backend/useVersionRestore';
|
||||||
|
export { EditorLibraryItem, type ILibraryItemEditor } from './components/EditorLibraryItem';
|
||||||
|
export { MiniSelectorOSS } from './components/MiniSelectorOSS';
|
||||||
|
export { PickSchema } from './components/PickSchema';
|
||||||
|
export { SelectLibraryItem } from './components/SelectLibraryItem';
|
||||||
|
export { SelectVersion } from './components/SelectVersion';
|
||||||
|
export { ToolbarItemAccess } from './components/ToolbarItemAccess';
|
||||||
|
export { type ILibraryItemReference } from './models/library';
|
||||||
|
export { useLibrarySearchStore } from './stores/librarySearch';
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { AccessPolicy, LibraryItemType } from './backend/types';
|
||||||
import { FolderNode } from './models/FolderTree';
|
import { FolderNode } from './models/FolderTree';
|
||||||
import { LocationHead } from './models/library';
|
import { LocationHead } from './models/library';
|
||||||
import { validateLocation } from './models/libraryAPI';
|
import { validateLocation } from './models/libraryAPI';
|
||||||
|
@ -45,3 +46,52 @@ export function labelFolderNode(node: FolderNode): string {
|
||||||
export function describeFolderNode(node: FolderNode): string {
|
export function describeFolderNode(node: FolderNode): string {
|
||||||
return `${node.filesInside} | ${node.filesTotal}`;
|
return `${node.filesInside} | ${node.filesTotal}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves label for {@link AccessPolicy}.
|
||||||
|
*/
|
||||||
|
export function labelAccessPolicy(policy: AccessPolicy): string {
|
||||||
|
// prettier-ignore
|
||||||
|
switch (policy) {
|
||||||
|
case AccessPolicy.PRIVATE: return 'Личный';
|
||||||
|
case AccessPolicy.PROTECTED: return 'Защищенный';
|
||||||
|
case AccessPolicy.PUBLIC: return 'Открытый';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves description for {@link AccessPolicy}.
|
||||||
|
*/
|
||||||
|
export function describeAccessPolicy(policy: AccessPolicy): string {
|
||||||
|
// prettier-ignore
|
||||||
|
switch (policy) {
|
||||||
|
case AccessPolicy.PRIVATE:
|
||||||
|
return 'Доступ только для владельца';
|
||||||
|
case AccessPolicy.PROTECTED:
|
||||||
|
return 'Доступ для владельца и редакторов';
|
||||||
|
case AccessPolicy.PUBLIC:
|
||||||
|
return 'Открытый доступ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves label for {@link LibraryItemType}.
|
||||||
|
*/
|
||||||
|
export function labelLibraryItemType(itemType: LibraryItemType): string {
|
||||||
|
// prettier-ignore
|
||||||
|
switch (itemType) {
|
||||||
|
case LibraryItemType.RSFORM: return 'КС';
|
||||||
|
case LibraryItemType.OSS: return 'ОСС';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves description for {@link LibraryItemType}.
|
||||||
|
*/
|
||||||
|
export function describeLibraryItemType(itemType: LibraryItemType): string {
|
||||||
|
// prettier-ignore
|
||||||
|
switch (itemType) {
|
||||||
|
case LibraryItemType.RSFORM: return 'Концептуальная схема';
|
||||||
|
case LibraryItemType.OSS: return 'Операционная схема синтеза';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user