F: Implement react-query pt3
This commit is contained in:
parent
76aee5bea7
commit
6543d88cbe
|
@ -5,7 +5,7 @@ import ConceptToaster from '@/app/ConceptToaster';
|
||||||
import Footer from '@/app/Footer';
|
import Footer from '@/app/Footer';
|
||||||
import Navigation from '@/app/Navigation';
|
import Navigation from '@/app/Navigation';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import { NavigationState } from '@/context/NavigationContext';
|
import { NavigationState } from '@/app/Navigation/NavigationContext';
|
||||||
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import DlgChangeInputSchema from '@/dialogs/DlgChangeInputSchema';
|
import DlgChangeInputSchema from '@/dialogs/DlgChangeInputSchema';
|
||||||
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
import DlgChangeLocation from '@/pages/OssPage/DlgChangeLocation';
|
||||||
import DlgCloneLibraryItem from '@/dialogs/DlgCloneLibraryItem';
|
import DlgCloneLibraryItem from '@/dialogs/DlgCloneLibraryItem';
|
||||||
import DlgCreateCst from '@/dialogs/DlgCreateCst';
|
import DlgCreateCst from '@/dialogs/DlgCreateCst';
|
||||||
import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
|
import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
|
||||||
|
|
|
@ -2,24 +2,22 @@
|
||||||
|
|
||||||
import { createContext, useCallback, useContext, useState } from 'react';
|
import { createContext, useCallback, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
import { useOss, useOssInvalidate, useOssUpdate } from '@/backend/oss/useOSS';
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { ErrorData } from '@/components/info/InfoError';
|
||||||
import useOssDetails from '@/hooks/useOssDetails';
|
|
||||||
import { LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
|
import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
|
||||||
import { contextOutsideScope } from '@/utils/labels';
|
import { contextOutsideScope } from '@/utils/labels';
|
||||||
|
|
||||||
interface IGlobalOssContext {
|
interface IGlobalOssContext {
|
||||||
schema: IOperationSchema | undefined;
|
schema: IOperationSchema | undefined;
|
||||||
setID: (id: string | undefined) => void;
|
setID: (id: LibraryItemID | undefined) => void;
|
||||||
setData: (data: IOperationSchemaData) => void;
|
setData: (data: IOperationSchemaData) => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
loadingError: ErrorData;
|
loadingError: ErrorData;
|
||||||
isValid: boolean;
|
|
||||||
|
|
||||||
invalidate: () => void;
|
invalidate: () => Promise<void>;
|
||||||
invalidateItem: (target: LibraryItemID) => void;
|
invalidateItem: (target: LibraryItemID) => void;
|
||||||
partialUpdate: (data: Partial<IOperationSchema>) => void;
|
partialUpdate: (data: Partial<IOperationSchema>) => void;
|
||||||
reload: (callback?: () => void) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlobalOssContext = createContext<IGlobalOssContext | null>(null);
|
const GlobalOssContext = createContext<IGlobalOssContext | null>(null);
|
||||||
|
@ -32,58 +30,18 @@ export const useGlobalOss = (): IGlobalOssContext => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GlobalOssState = ({ children }: React.PropsWithChildren) => {
|
export const GlobalOssState = ({ children }: React.PropsWithChildren) => {
|
||||||
const [isValid, setIsValid] = useState(true);
|
const [ossID, setID] = useState<LibraryItemID | undefined>(undefined);
|
||||||
const [ossID, setIdInternal] = useState<string | undefined>(undefined);
|
const { schema: schema, error: loadingError, isLoading: loading } = useOss({ itemID: ossID });
|
||||||
const {
|
const { update, partialUpdate } = useOssUpdate({ itemID: ossID });
|
||||||
schema: schema, // prettier: split lines
|
const { invalidate } = useOssInvalidate({ itemID: ossID });
|
||||||
error: loadingError,
|
|
||||||
setSchema: setDataInternal,
|
|
||||||
loading: loading,
|
|
||||||
reload: reloadInternal,
|
|
||||||
partialUpdate
|
|
||||||
} = useOssDetails({ target: ossID });
|
|
||||||
|
|
||||||
const reload = useCallback(
|
|
||||||
(callback?: () => void) => {
|
|
||||||
reloadInternal(undefined, () => {
|
|
||||||
setIsValid(true);
|
|
||||||
callback?.();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[reloadInternal]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setData = useCallback(
|
|
||||||
(data: IOperationSchemaData) => {
|
|
||||||
setDataInternal(data);
|
|
||||||
setIsValid(true);
|
|
||||||
},
|
|
||||||
[setDataInternal]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setID = useCallback(
|
|
||||||
(id: string | undefined) => {
|
|
||||||
setIdInternal(prev => {
|
|
||||||
if (prev === id && !isValid) {
|
|
||||||
reload();
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[setIdInternal, isValid, reload]
|
|
||||||
);
|
|
||||||
|
|
||||||
const invalidate = useCallback(() => {
|
|
||||||
setIsValid(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const invalidateItem = useCallback(
|
const invalidateItem = useCallback(
|
||||||
(target: LibraryItemID) => {
|
(target: LibraryItemID) => {
|
||||||
if (schema?.schemas.includes(target)) {
|
if (schema?.schemas.includes(target)) {
|
||||||
setIsValid(false);
|
invalidate().catch(console.error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[schema]
|
[invalidate, schema]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -91,12 +49,10 @@ export const GlobalOssState = ({ children }: React.PropsWithChildren) => {
|
||||||
value={{
|
value={{
|
||||||
schema,
|
schema,
|
||||||
setID,
|
setID,
|
||||||
setData,
|
setData: update,
|
||||||
loading,
|
loading,
|
||||||
loadingError,
|
loadingError,
|
||||||
reload,
|
|
||||||
partialUpdate,
|
partialUpdate,
|
||||||
isValid,
|
|
||||||
invalidateItem,
|
invalidateItem,
|
||||||
invalidate
|
invalidate
|
||||||
}}
|
}}
|
|
@ -6,8 +6,7 @@ import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
|
|
||||||
import { queryClient } from '@/backend/queryClient';
|
import { queryClient } from '@/backend/queryClient';
|
||||||
import { GlobalOssState } from '@/context/GlobalOssContext';
|
import { GlobalOssState } from '@/app/GlobalOssContext';
|
||||||
import { LibraryState } from '@/context/LibraryContext';
|
|
||||||
|
|
||||||
import ErrorFallback from './ErrorFallback';
|
import ErrorFallback from './ErrorFallback';
|
||||||
|
|
||||||
|
@ -32,14 +31,12 @@ function GlobalProviders({ children }: React.PropsWithChildren) {
|
||||||
>
|
>
|
||||||
<IntlProvider locale='ru' defaultLocale='ru'>
|
<IntlProvider locale='ru' defaultLocale='ru'>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<LibraryState>
|
|
||||||
<GlobalOssState>
|
<GlobalOssState>
|
||||||
|
|
||||||
<ReactQueryDevtools initialIsOpen={false} />
|
<ReactQueryDevtools initialIsOpen={false} />
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
</GlobalOssState>
|
</GlobalOssState>
|
||||||
</LibraryState>
|
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
</ErrorBoundary>);
|
</ErrorBoundary>);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { useAppLayoutStore } from '@/stores/appLayout';
|
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
@ -27,7 +27,7 @@ function Navigation() {
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'z-navigation', // prettier: split lines
|
'z-navigation', //
|
||||||
'sticky top-0 left-0 right-0',
|
'sticky top-0 left-0 right-0',
|
||||||
'select-none',
|
'select-none',
|
||||||
'bg-prim-100'
|
'bg-prim-100'
|
||||||
|
@ -36,7 +36,7 @@ function Navigation() {
|
||||||
<ToggleNavigation />
|
<ToggleNavigation />
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'pl-2 pr-[1.5rem] sm:pr-[0.9rem] h-[3rem]', // prettier: split lines
|
'pl-2 pr-[1.5rem] sm:pr-[0.9rem] h-[3rem]', //
|
||||||
'flex',
|
'flex',
|
||||||
'cc-shadow-border'
|
'cc-shadow-border'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -29,7 +29,7 @@ function NavigationButton({
|
||||||
data-tooltip-hidden={hideTitle}
|
data-tooltip-hidden={hideTitle}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mr-1 h-full', // prettier: split lines
|
'mr-1 h-full', //
|
||||||
'flex items-center gap-1',
|
'flex items-center gap-1',
|
||||||
'clr-btn-nav cc-animate-color duration-500',
|
'clr-btn-nav cc-animate-color duration-500',
|
||||||
'rounded-xl',
|
'rounded-xl',
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import Dropdown from '@/components/ui/Dropdown';
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
import DropdownButton from '@/components/ui/DropdownButton';
|
import DropdownButton from '@/components/ui/DropdownButton';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { toast } from 'react-toastify';
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { ErrorData } from '@/components/info/InfoError';
|
||||||
import { extractErrorMessage } from '@/utils/utils';
|
import { extractErrorMessage } from '@/utils/utils';
|
||||||
|
|
||||||
import { axiosInstance } from './apiConfiguration';
|
import { axiosInstance } from './axiosInstance';
|
||||||
|
|
||||||
// ================ Data transfer types ================
|
// ================ Data transfer types ================
|
||||||
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { ICurrentUser, IUser } from '@/models/user';
|
import { axiosInstance } from '@/backend/axiosInstance';
|
||||||
|
import { DELAYS } from '@/backend/configuration';
|
||||||
import { axiosInstance } from '../apiConfiguration';
|
import { ICurrentUser } from '@/models/user';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents login data, used to authenticate users.
|
* Represents login data, used to authenticate users.
|
||||||
|
@ -23,7 +23,9 @@ export interface IChangePasswordDTO {
|
||||||
/**
|
/**
|
||||||
* Represents password reset request data.
|
* Represents password reset request data.
|
||||||
*/
|
*/
|
||||||
export interface IRequestPasswordDTO extends Pick<IUser, 'email'> {}
|
export interface IRequestPasswordDTO {
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents password reset data.
|
* Represents password reset data.
|
||||||
|
@ -36,7 +38,9 @@ export interface IResetPasswordDTO {
|
||||||
/**
|
/**
|
||||||
* Represents password token data.
|
* Represents password token data.
|
||||||
*/
|
*/
|
||||||
export interface IPasswordTokenDTO extends Pick<IResetPasswordDTO, 'token'> {}
|
export interface IPasswordTokenDTO {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authentication API.
|
* Authentication API.
|
||||||
|
@ -47,14 +51,14 @@ export const authApi = {
|
||||||
getAuthQueryOptions: () => {
|
getAuthQueryOptions: () => {
|
||||||
return queryOptions({
|
return queryOptions({
|
||||||
queryKey: [authApi.baseKey, 'user'],
|
queryKey: [authApi.baseKey, 'user'],
|
||||||
|
staleTime: DELAYS.staleLong,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get<ICurrentUser>('/users/api/auth', {
|
.get<ICurrentUser>('/users/api/auth', {
|
||||||
signal: meta.signal
|
signal: meta.signal
|
||||||
})
|
})
|
||||||
.then(response => (response.data.id === null ? null : response.data)),
|
.then(response => (response.data.id === null ? null : response.data)),
|
||||||
placeholderData: null,
|
placeholderData: null
|
||||||
staleTime: 24 * 60 * 60 * 1000
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,17 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { authApi, IChangePasswordDTO } from './api';
|
import { authApi, IChangePasswordDTO } from './api';
|
||||||
|
|
||||||
export const useChangePassword = () => {
|
export const useChangePassword = () => {
|
||||||
const queryClient = useQueryClient();
|
const client = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: ['change-password'],
|
mutationKey: ['change-password'],
|
||||||
mutationFn: authApi.changePassword,
|
mutationFn: authApi.changePassword,
|
||||||
onSettled: () => queryClient.invalidateQueries({ queryKey: [authApi.baseKey] })
|
onSettled: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] })
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
changePassword: (data: IChangePasswordDTO, onSuccess?: () => void) => mutation.mutate(data, { onSuccess }),
|
changePassword: (
|
||||||
|
data: IChangePasswordDTO, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess }),
|
||||||
isPending: mutation.isPending,
|
isPending: mutation.isPending,
|
||||||
error: mutation.error,
|
error: mutation.error,
|
||||||
reset: mutation.reset
|
reset: mutation.reset
|
||||||
|
|
|
@ -3,15 +3,18 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { authApi } from './api';
|
import { authApi } from './api';
|
||||||
|
|
||||||
export const useLogin = () => {
|
export const useLogin = () => {
|
||||||
const queryClient = useQueryClient();
|
const client = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: ['login'],
|
mutationKey: ['login'],
|
||||||
mutationFn: authApi.login,
|
mutationFn: authApi.login,
|
||||||
onSettled: () => queryClient.invalidateQueries({ queryKey: [authApi.baseKey] })
|
onSettled: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] })
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
login: (username: string, password: string, onSuccess?: () => void) =>
|
login: (
|
||||||
mutation.mutate({ username, password }, { onSuccess }),
|
username: string, //
|
||||||
|
password: string,
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate({ username, password }, { onSuccess }),
|
||||||
isPending: mutation.isPending,
|
isPending: mutation.isPending,
|
||||||
error: mutation.error,
|
error: mutation.error,
|
||||||
reset: mutation.reset
|
reset: mutation.reset
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { authApi } from './api';
|
import { authApi } from './api';
|
||||||
|
|
||||||
export const useLogout = () => {
|
export const useLogout = () => {
|
||||||
const queryClient = useQueryClient();
|
const client = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: ['logout'],
|
mutationKey: ['logout'],
|
||||||
mutationFn: authApi.logout,
|
mutationFn: authApi.logout,
|
||||||
onSettled: () => queryClient.invalidateQueries({ queryKey: [authApi.baseKey] })
|
onSettled: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] })
|
||||||
});
|
});
|
||||||
return { logout: (onSuccess?: () => void) => mutation.mutate(undefined, { onSuccess }) };
|
return { logout: (onSuccess?: () => void) => mutation.mutate(undefined, { onSuccess }) };
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,10 @@ export const useRequestPasswordReset = () => {
|
||||||
mutationFn: authApi.requestPasswordReset
|
mutationFn: authApi.requestPasswordReset
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
requestPasswordReset: (data: IRequestPasswordDTO, onSuccess?: () => void) => mutation.mutate(data, { onSuccess }),
|
requestPasswordReset: (
|
||||||
|
data: IRequestPasswordDTO, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess }),
|
||||||
isPending: mutation.isPending,
|
isPending: mutation.isPending,
|
||||||
error: mutation.error,
|
error: mutation.error,
|
||||||
reset: mutation.reset
|
reset: mutation.reset
|
||||||
|
|
|
@ -3,20 +3,26 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { authApi, IPasswordTokenDTO, IResetPasswordDTO } from './api';
|
import { authApi, IPasswordTokenDTO, IResetPasswordDTO } from './api';
|
||||||
|
|
||||||
export const useResetPassword = () => {
|
export const useResetPassword = () => {
|
||||||
const queryClient = useQueryClient();
|
const client = useQueryClient();
|
||||||
const validateMutation = useMutation({
|
const validateMutation = useMutation({
|
||||||
mutationKey: ['reset-password'],
|
mutationKey: ['reset-password'],
|
||||||
mutationFn: authApi.validatePasswordToken,
|
mutationFn: authApi.validatePasswordToken,
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [authApi.baseKey] })
|
onSuccess: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] })
|
||||||
});
|
});
|
||||||
const resetMutation = useMutation({
|
const resetMutation = useMutation({
|
||||||
mutationKey: ['reset-password'],
|
mutationKey: ['reset-password'],
|
||||||
mutationFn: authApi.resetPassword,
|
mutationFn: authApi.resetPassword,
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [authApi.baseKey] })
|
onSuccess: async () => await client.invalidateQueries({ queryKey: [authApi.baseKey] })
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
validateToken: (data: IPasswordTokenDTO, onSuccess?: () => void) => validateMutation.mutate(data, { onSuccess }),
|
validateToken: (
|
||||||
resetPassword: (data: IResetPasswordDTO, onSuccess?: () => void) => resetMutation.mutate(data, { onSuccess }),
|
data: IPasswordTokenDTO, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => validateMutation.mutate(data, { onSuccess }),
|
||||||
|
resetPassword: (
|
||||||
|
data: IResetPasswordDTO, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => resetMutation.mutate(data, { onSuccess }),
|
||||||
isPending: resetMutation.isPending,
|
isPending: resetMutation.isPending,
|
||||||
error: resetMutation.error,
|
error: resetMutation.error,
|
||||||
reset: resetMutation.reset
|
reset: resetMutation.reset
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
/**
|
|
||||||
* Endpoints: cctext.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ILexemeData, ITextRequest, ITextResult, IWordFormPlain } from '@/models/language';
|
|
||||||
|
|
||||||
import { AxiosPost, FrontExchange } from './apiTransport';
|
|
||||||
|
|
||||||
export function postInflectText(request: FrontExchange<IWordFormPlain, ITextResult>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/cctext/inflect`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postParseText(request: FrontExchange<ITextRequest, ITextResult>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/cctext/parse`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postGenerateLexeme(request: FrontExchange<ITextRequest, ILexemeData>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/cctext/generate-lexeme`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
26
rsconcept/frontend/src/backend/cctext/api.ts
Normal file
26
rsconcept/frontend/src/backend/cctext/api.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { axiosInstance } from '@/backend/axiosInstance';
|
||||||
|
import { ILexemeData, IWordFormPlain } from '@/models/language';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents API result for text output.
|
||||||
|
*/
|
||||||
|
export interface ITextResult {
|
||||||
|
result: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cctextApi = {
|
||||||
|
baseKey: 'cctext',
|
||||||
|
|
||||||
|
inflectText: (data: IWordFormPlain) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<ITextResult>('/api/cctext/inflect', data)
|
||||||
|
.then(response => response.data),
|
||||||
|
parseText: (data: { text: string }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<ITextResult>('/api/cctext/parse', data)
|
||||||
|
.then(response => response.data),
|
||||||
|
generateLexeme: (data: { text: string }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<ILexemeData>('/api/cctext/generate-lexeme', data)
|
||||||
|
.then(response => response.data)
|
||||||
|
};
|
19
rsconcept/frontend/src/backend/cctext/useGenerateLexeme.tsx
Normal file
19
rsconcept/frontend/src/backend/cctext/useGenerateLexeme.tsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { ILexemeData } from '@/models/language';
|
||||||
|
|
||||||
|
import { DataCallback } from '../apiTransport';
|
||||||
|
import { cctextApi } from './api';
|
||||||
|
|
||||||
|
export const useGenerateLexeme = () => {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [cctextApi.baseKey, 'generate-lexeme'],
|
||||||
|
mutationFn: cctextApi.generateLexeme
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
generateLexeme: (
|
||||||
|
data: { text: string }, //
|
||||||
|
onSuccess?: DataCallback<ILexemeData>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
19
rsconcept/frontend/src/backend/cctext/useInflectText.tsx
Normal file
19
rsconcept/frontend/src/backend/cctext/useInflectText.tsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { IWordFormPlain } from '@/models/language';
|
||||||
|
|
||||||
|
import { DataCallback } from '../apiTransport';
|
||||||
|
import { cctextApi, ITextResult } from './api';
|
||||||
|
|
||||||
|
export const useInflectText = () => {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [cctextApi.baseKey, 'inflect-text'],
|
||||||
|
mutationFn: cctextApi.inflectText
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
inflectText: (
|
||||||
|
data: IWordFormPlain, //
|
||||||
|
onSuccess?: DataCallback<ITextResult>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { useIsMutating } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { cctextApi } from './api';
|
||||||
|
|
||||||
|
export const useIsProcessingCctext = () => {
|
||||||
|
const countMutations = useIsMutating({ mutationKey: [cctextApi.baseKey] });
|
||||||
|
return countMutations !== 0;
|
||||||
|
};
|
17
rsconcept/frontend/src/backend/cctext/useParseText.tsx
Normal file
17
rsconcept/frontend/src/backend/cctext/useParseText.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '../apiTransport';
|
||||||
|
import { cctextApi, ITextResult } from './api';
|
||||||
|
|
||||||
|
export const useParseText = () => {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [cctextApi.baseKey, 'parse-text'],
|
||||||
|
mutationFn: cctextApi.parseText
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
parseText: (
|
||||||
|
data: { text: string }, //
|
||||||
|
onSuccess?: DataCallback<ITextResult>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
9
rsconcept/frontend/src/backend/configuration.ts
Normal file
9
rsconcept/frontend/src/backend/configuration.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/** Timing constants for API requests. */
|
||||||
|
export const DELAYS = {
|
||||||
|
garbageCollection: 1 * 60 * 60 * 1000,
|
||||||
|
staleDefault: 5 * 60 * 1000,
|
||||||
|
|
||||||
|
staleShort: 5 * 60 * 1000,
|
||||||
|
staleMedium: 1 * 60 * 60 * 1000,
|
||||||
|
staleLong: 24 * 60 * 60 * 1000
|
||||||
|
};
|
|
@ -1,117 +0,0 @@
|
||||||
/**
|
|
||||||
* Endpoints: library.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
ILibraryCreateData,
|
|
||||||
ILibraryItem,
|
|
||||||
ILibraryUpdateData,
|
|
||||||
IRenameLocationData,
|
|
||||||
ITargetAccessPolicy,
|
|
||||||
ITargetLocation,
|
|
||||||
IVersionCreateData
|
|
||||||
} from '@/models/library';
|
|
||||||
import { IRSFormCloneData, IRSFormData, IVersionCreatedResponse } from '@/models/rsform';
|
|
||||||
import { ITargetUser, ITargetUsers } from '@/models/user';
|
|
||||||
|
|
||||||
import {
|
|
||||||
AxiosDelete,
|
|
||||||
AxiosGet,
|
|
||||||
AxiosPatch,
|
|
||||||
AxiosPost,
|
|
||||||
FrontAction,
|
|
||||||
FrontExchange,
|
|
||||||
FrontPull,
|
|
||||||
FrontPush
|
|
||||||
} from './apiTransport';
|
|
||||||
|
|
||||||
export function getLibrary(request: FrontPull<ILibraryItem[]>) {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: '/api/library/active',
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAdminLibrary(request: FrontPull<ILibraryItem[]>) {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: '/api/library/all',
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTemplates(request: FrontPull<ILibraryItem[]>) {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: '/api/library/templates',
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postCreateLibraryItem(request: FrontExchange<ILibraryCreateData, ILibraryItem>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: '/api/library',
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postCloneLibraryItem(target: string, request: FrontExchange<IRSFormCloneData, IRSFormData>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/library/${target}/clone`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchLibraryItem(target: string, request: FrontExchange<ILibraryUpdateData, ILibraryItem>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/library/${target}`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteLibraryItem(target: string, request: FrontAction) {
|
|
||||||
AxiosDelete({
|
|
||||||
endpoint: `/api/library/${target}`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchSetOwner(target: string, request: FrontPush<ITargetUser>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/library/${target}/set-owner`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchSetAccessPolicy(target: string, request: FrontPush<ITargetAccessPolicy>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/library/${target}/set-access-policy`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchSetLocation(target: string, request: FrontPush<ITargetLocation>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/library/${target}/set-location`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchRenameLocation(request: FrontPush<IRenameLocationData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/library/rename-location`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchSetEditors(target: string, request: FrontPush<ITargetUsers>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/library/${target}/set-editors`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postCreateVersion(target: string, request: FrontExchange<IVersionCreateData, IVersionCreatedResponse>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/library/${target}/create-version`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
137
rsconcept/frontend/src/backend/library/api.ts
Normal file
137
rsconcept/frontend/src/backend/library/api.ts
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { axiosInstance } from '@/backend/axiosInstance';
|
||||||
|
import { DELAYS } from '@/backend/configuration';
|
||||||
|
import { AccessPolicy, ILibraryItem, IVersionData, LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
import { ConstituentaID, IRSFormData } from '@/models/rsform';
|
||||||
|
import { UserID } from '@/models/user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents update data for renaming Location.
|
||||||
|
*/
|
||||||
|
export interface IRenameLocationDTO {
|
||||||
|
target: string;
|
||||||
|
new_location: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data, used for cloning {@link IRSForm}.
|
||||||
|
*/
|
||||||
|
export interface IRSFormCloneDTO extends Omit<ILibraryItem, 'time_create' | 'time_update' | 'owner'> {
|
||||||
|
items?: ConstituentaID[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data, used for creating {@link IRSForm}.
|
||||||
|
*/
|
||||||
|
export interface ILibraryCreateDTO extends Omit<ILibraryItem, 'time_create' | 'time_update' | 'id' | 'owner'> {
|
||||||
|
file?: File;
|
||||||
|
fileName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents update data for editing {@link ILibraryItem}.
|
||||||
|
*/
|
||||||
|
export interface ILibraryUpdateDTO
|
||||||
|
extends Omit<ILibraryItem, 'time_create' | 'time_update' | 'access_policy' | 'location' | 'owner'> {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create version metadata in persistent storage.
|
||||||
|
*/
|
||||||
|
export interface IVersionCreateDTO {
|
||||||
|
version: string;
|
||||||
|
description: string;
|
||||||
|
items?: ConstituentaID[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data response when creating {@link IVersionInfo}.
|
||||||
|
*/
|
||||||
|
export interface IVersionCreatedResponse {
|
||||||
|
version: number;
|
||||||
|
schema: IRSFormData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const libraryApi = {
|
||||||
|
baseKey: 'library',
|
||||||
|
libraryListKey: ['library', 'list'],
|
||||||
|
|
||||||
|
getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: libraryApi.libraryListKey,
|
||||||
|
staleTime: DELAYS.staleMedium,
|
||||||
|
queryFn: meta =>
|
||||||
|
axiosInstance
|
||||||
|
.get<ILibraryItem[]>(isAdmin ? '/api/library/all' : '/api/library/active', {
|
||||||
|
signal: meta.signal
|
||||||
|
})
|
||||||
|
.then(response => response.data)
|
||||||
|
}),
|
||||||
|
getTemplatesQueryOptions: () =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: [libraryApi.baseKey, 'templates'],
|
||||||
|
staleTime: DELAYS.staleMedium,
|
||||||
|
queryFn: meta =>
|
||||||
|
axiosInstance
|
||||||
|
.get<ILibraryItem[]>('/api/library/templates', {
|
||||||
|
signal: meta.signal
|
||||||
|
})
|
||||||
|
.then(response => response.data)
|
||||||
|
}),
|
||||||
|
|
||||||
|
createItem: (data: ILibraryCreateDTO) =>
|
||||||
|
data.file
|
||||||
|
? axiosInstance
|
||||||
|
.post<ILibraryItem>('/api/rsforms/create-detailed', data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.data)
|
||||||
|
: axiosInstance //
|
||||||
|
.post<ILibraryItem>('/api/library', data)
|
||||||
|
.then(response => response.data),
|
||||||
|
|
||||||
|
updateItem: (data: ILibraryUpdateDTO) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<ILibraryItem>(`/api/library/${data.id}`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
setOwner: (data: { itemID: LibraryItemID; owner: UserID }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch(`/api/library/${data.itemID}/set-owner`, { user: data.owner }),
|
||||||
|
setLocation: (data: { itemID: LibraryItemID; location: string }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch(`/api/library/${data.itemID}/set-location`, { location: data.location }),
|
||||||
|
setAccessPolicy: (data: { itemID: LibraryItemID; policy: AccessPolicy }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch(`/api/library/${data.itemID}/set-access-policy`, { access_policy: data.policy }),
|
||||||
|
setEditors: (data: { itemID: LibraryItemID; editors: UserID[] }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch(`/api/library/${data.itemID}/set-editors`, { users: data.editors }),
|
||||||
|
|
||||||
|
deleteItem: (target: LibraryItemID) =>
|
||||||
|
axiosInstance //
|
||||||
|
.delete(`/api/library/${target}`),
|
||||||
|
cloneItem: (data: IRSFormCloneDTO) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IRSFormData>(`/api/library/${data.id}/clone`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
renameLocation: (data: IRenameLocationDTO) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch('/api/library/rename-location', data),
|
||||||
|
|
||||||
|
versionCreate: (data: { itemID: LibraryItemID; data: IVersionData }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IVersionCreatedResponse>(`/api/library/${data.itemID}/versions`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
versionRestore: (data: { itemID: LibraryItemID; versionID: VersionID }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IRSFormData>(`/api/versions/${data.versionID}/restore`)
|
||||||
|
.then(response => response.data),
|
||||||
|
versionUpdate: (data: { itemID: LibraryItemID; versionID: VersionID; data: IVersionData }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch(`/api/versions/${data.versionID}`, data.data),
|
||||||
|
versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.delete(`/api/versions/${data.versionID}`)
|
||||||
|
};
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { useAuth } from '@/backend/auth/useAuth';
|
||||||
|
import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI';
|
||||||
|
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||||
|
|
||||||
|
import { useLibrary } from './useLibrary';
|
||||||
|
|
||||||
|
export function useApplyLibraryFilter(filter: ILibraryFilter) {
|
||||||
|
const { items } = useLibrary();
|
||||||
|
const { user } = useAuth();
|
||||||
|
|
||||||
|
let result = items;
|
||||||
|
if (!filter.folderMode && filter.head) {
|
||||||
|
result = result.filter(item => item.location.startsWith(filter.head!));
|
||||||
|
}
|
||||||
|
if (filter.folderMode && filter.location) {
|
||||||
|
if (filter.subfolders) {
|
||||||
|
result = result.filter(
|
||||||
|
item => item.location == filter.location || item.location.startsWith(filter.location! + '/')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result = result.filter(item => item.location == filter.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter.type) {
|
||||||
|
result = result.filter(item => item.item_type === filter.type);
|
||||||
|
}
|
||||||
|
if (filter.isVisible !== undefined) {
|
||||||
|
result = result.filter(item => filter.isVisible === item.visible);
|
||||||
|
}
|
||||||
|
if (filter.isOwned !== undefined) {
|
||||||
|
result = result.filter(item => filter.isOwned === (item.owner === user?.id));
|
||||||
|
}
|
||||||
|
if (filter.isEditor !== undefined) {
|
||||||
|
result = result.filter(item => filter.isEditor == user?.editor.includes(item.id));
|
||||||
|
}
|
||||||
|
if (filter.filterUser !== undefined) {
|
||||||
|
result = result.filter(item => filter.filterUser === item.owner);
|
||||||
|
}
|
||||||
|
if (!filter.folderMode && filter.path) {
|
||||||
|
result = result.filter(item => matchLibraryItemLocation(item, filter.path!));
|
||||||
|
}
|
||||||
|
if (filter.query) {
|
||||||
|
result = result.filter(item => matchLibraryItem(item, filter.query!));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { filtered: result };
|
||||||
|
}
|
21
rsconcept/frontend/src/backend/library/useCloneItem.tsx
Normal file
21
rsconcept/frontend/src/backend/library/useCloneItem.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { IRSFormData } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { IRSFormCloneDTO, libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useCloneItem = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'clone-item'],
|
||||||
|
mutationFn: libraryApi.cloneItem,
|
||||||
|
onSuccess: async () => await client.invalidateQueries({ queryKey: [libraryApi.baseKey] })
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cloneItem: (
|
||||||
|
data: IRSFormCloneDTO, //
|
||||||
|
onSuccess?: DataCallback<IRSFormData>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
24
rsconcept/frontend/src/backend/library/useCreateItem.tsx
Normal file
24
rsconcept/frontend/src/backend/library/useCreateItem.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { ILibraryItem } from '@/models/library';
|
||||||
|
|
||||||
|
import { ILibraryCreateDTO, libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useCreateItem = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'create-item'],
|
||||||
|
mutationFn: libraryApi.createItem,
|
||||||
|
onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] })
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
createItem: (
|
||||||
|
data: ILibraryCreateDTO, //
|
||||||
|
onSuccess?: DataCallback<ILibraryItem>
|
||||||
|
) => mutation.mutate(data, { onSuccess }),
|
||||||
|
isPending: mutation.isPending,
|
||||||
|
error: mutation.error,
|
||||||
|
reset: mutation.reset
|
||||||
|
};
|
||||||
|
};
|
26
rsconcept/frontend/src/backend/library/useDeleteItem.tsx
Normal file
26
rsconcept/frontend/src/backend/library/useDeleteItem.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useDeleteItem = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'delete-item'],
|
||||||
|
mutationFn: libraryApi.deleteItem,
|
||||||
|
onSuccess: async (_, variables) => {
|
||||||
|
await client.cancelQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.filter(item => item.id !== variables)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
deleteItem: (
|
||||||
|
target: LibraryItemID, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(target, { onSuccess }),
|
||||||
|
isPending: mutation.isPending
|
||||||
|
};
|
||||||
|
};
|
17
rsconcept/frontend/src/backend/library/useFolders.tsx
Normal file
17
rsconcept/frontend/src/backend/library/useFolders.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { FolderTree } from '@/models/FolderTree';
|
||||||
|
import { LocationHead } from '@/models/library';
|
||||||
|
|
||||||
|
import { useLibrary } from './useLibrary';
|
||||||
|
|
||||||
|
export function useFolders() {
|
||||||
|
const { items } = useLibrary();
|
||||||
|
|
||||||
|
const result = new FolderTree();
|
||||||
|
result.addPath(LocationHead.USER, 0);
|
||||||
|
result.addPath(LocationHead.COMMON, 0);
|
||||||
|
result.addPath(LocationHead.LIBRARY, 0);
|
||||||
|
result.addPath(LocationHead.PROJECTS, 0);
|
||||||
|
items.forEach(item => result.addPath(item.location));
|
||||||
|
|
||||||
|
return { folders: result };
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { useIsMutating } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useIsProcessingLibrary = () => {
|
||||||
|
const countMutations = useIsMutating({ mutationKey: [libraryApi.baseKey] });
|
||||||
|
return countMutations !== 0;
|
||||||
|
};
|
21
rsconcept/frontend/src/backend/library/useLibrary.tsx
Normal file
21
rsconcept/frontend/src/backend/library/useLibrary.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export function useLibrarySuspense() {
|
||||||
|
const { user } = useAuthSuspense();
|
||||||
|
const { data: items } = useSuspenseQuery({
|
||||||
|
...libraryApi.getLibraryQueryOptions({ isAdmin: user?.is_staff ?? false })
|
||||||
|
});
|
||||||
|
return { items };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLibrary() {
|
||||||
|
// NOTE: Using suspense here to avoid duplicated library data requests
|
||||||
|
const { user } = useAuthSuspense();
|
||||||
|
const { data: items, isLoading } = useQuery({
|
||||||
|
...libraryApi.getLibraryQueryOptions({ isAdmin: user?.is_staff ?? false })
|
||||||
|
});
|
||||||
|
return { items: items ?? [], isLoading };
|
||||||
|
}
|
18
rsconcept/frontend/src/backend/library/useRenameLocation.tsx
Normal file
18
rsconcept/frontend/src/backend/library/useRenameLocation.tsx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { IRenameLocationDTO, libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useRenameLocation = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'rename-location'],
|
||||||
|
mutationFn: libraryApi.renameLocation,
|
||||||
|
onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] })
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
renameLocation: (
|
||||||
|
data: IRenameLocationDTO, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { AccessPolicy, ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useSetAccessPolicy = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
|
mutationFn: libraryApi.setAccessPolicy,
|
||||||
|
onSuccess: (_, variables) => {
|
||||||
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.map(item => (item.id === variables.itemID ? { ...item, access_policy: variables.policy } : item))
|
||||||
|
);
|
||||||
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => {
|
||||||
|
if (!prev) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (prev.item_type === LibraryItemType.OSS) {
|
||||||
|
client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }).catch(console.error);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
access_policy: variables.policy
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
setAccessPolicy: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
policy: AccessPolicy;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
39
rsconcept/frontend/src/backend/library/useSetEditors.tsx
Normal file
39
rsconcept/frontend/src/backend/library/useSetEditors.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
|
import { UserID } from '@/models/user';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useSetEditors = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
|
mutationFn: libraryApi.setEditors,
|
||||||
|
onSuccess: (_, variables) => {
|
||||||
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.map(item => (item.id === variables.itemID ? { ...item, editors: variables.editors } : item))
|
||||||
|
);
|
||||||
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => {
|
||||||
|
if (!prev) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
editors: variables.editors
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
setEditors: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
editors: UserID[];
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
41
rsconcept/frontend/src/backend/library/useSetLocation.tsx
Normal file
41
rsconcept/frontend/src/backend/library/useSetLocation.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useSetLocation = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
|
mutationFn: libraryApi.setLocation,
|
||||||
|
onSuccess: (_, variables) => {
|
||||||
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item))
|
||||||
|
);
|
||||||
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => {
|
||||||
|
if (!prev) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (prev.item_type === LibraryItemType.OSS) {
|
||||||
|
client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }).catch(console.error);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
location: variables.location
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
setLocation: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
location: string;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
42
rsconcept/frontend/src/backend/library/useSetOwner.tsx
Normal file
42
rsconcept/frontend/src/backend/library/useSetOwner.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
|
import { UserID } from '@/models/user';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useSetOwner = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'set-owner'],
|
||||||
|
mutationFn: libraryApi.setOwner,
|
||||||
|
onSuccess: (_, variables) => {
|
||||||
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.map(item => (item.id === variables.itemID ? { ...item, owner: variables.owner } : item))
|
||||||
|
);
|
||||||
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => {
|
||||||
|
if (!prev) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (prev.item_type === LibraryItemType.OSS) {
|
||||||
|
client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }).catch(console.error);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
owner: variables.owner
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
setOwner: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
owner: UserID;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
17
rsconcept/frontend/src/backend/library/useTemplates.tsx
Normal file
17
rsconcept/frontend/src/backend/library/useTemplates.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export function useTemplatesSuspense() {
|
||||||
|
const { data: templates } = useSuspenseQuery({
|
||||||
|
...libraryApi.getTemplatesQueryOptions()
|
||||||
|
});
|
||||||
|
return { templates };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTemplates() {
|
||||||
|
const { data: templates } = useQuery({
|
||||||
|
...libraryApi.getTemplatesQueryOptions()
|
||||||
|
});
|
||||||
|
return { templates: templates ?? [] };
|
||||||
|
}
|
34
rsconcept/frontend/src/backend/library/useUpdateItem.tsx
Normal file
34
rsconcept/frontend/src/backend/library/useUpdateItem.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { ILibraryItem } from '@/models/library';
|
||||||
|
|
||||||
|
import { ILibraryUpdateDTO, libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useUpdateItem = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'update-item'],
|
||||||
|
mutationFn: libraryApi.updateItem,
|
||||||
|
onSuccess: (data: ILibraryItem) => {
|
||||||
|
client
|
||||||
|
.cancelQueries({ queryKey: [libraryApi.libraryListKey] })
|
||||||
|
.then(async () => {
|
||||||
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.map(item => (item.id === data.id ? data : item))
|
||||||
|
);
|
||||||
|
await client.invalidateQueries({
|
||||||
|
queryKey: [rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey]
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
updateItem: (
|
||||||
|
data: ILibraryUpdateDTO, //
|
||||||
|
onSuccess?: DataCallback<ILibraryItem>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export function useUpdateTimestamp() {
|
||||||
|
const client = useQueryClient();
|
||||||
|
return {
|
||||||
|
updateTimestamp: (target: LibraryItemID) =>
|
||||||
|
client.setQueryData(
|
||||||
|
libraryApi.libraryListKey, //
|
||||||
|
(prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.map(item => (item.id === target ? { ...item, time_update: Date() } : item))
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
30
rsconcept/frontend/src/backend/library/useVersionCreate.tsx
Normal file
30
rsconcept/frontend/src/backend/library/useVersionCreate.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { IVersionData, LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useVersionCreate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'create-version'],
|
||||||
|
mutationFn: libraryApi.versionCreate,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema);
|
||||||
|
updateTimestamp(data.schema.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
versionCreate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: IVersionData;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<IVersionData>
|
||||||
|
) => mutation.mutate(data, { onSuccess: () => onSuccess?.(data.data) })
|
||||||
|
};
|
||||||
|
};
|
33
rsconcept/frontend/src/backend/library/useVersionDelete.tsx
Normal file
33
rsconcept/frontend/src/backend/library/useVersionDelete.tsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
import { IRSFormData } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useVersionDelete = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'delete-version'],
|
||||||
|
mutationFn: libraryApi.versionDelete,
|
||||||
|
onSuccess: (_, variables) => {
|
||||||
|
client.setQueryData(
|
||||||
|
[rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey],
|
||||||
|
(prev: IRSFormData) => ({
|
||||||
|
...prev,
|
||||||
|
versions: prev.versions.filter(version => version.id !== variables.versionID)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
versionDelete: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
versionID: VersionID;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
29
rsconcept/frontend/src/backend/library/useVersionRestore.tsx
Normal file
29
rsconcept/frontend/src/backend/library/useVersionRestore.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useVersionRestore = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'restore-version'],
|
||||||
|
mutationFn: libraryApi.versionRestore,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
versionRestore: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
versionID: VersionID;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
38
rsconcept/frontend/src/backend/library/useVersionUpdate.tsx
Normal file
38
rsconcept/frontend/src/backend/library/useVersionUpdate.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
|
import { IVersionData, LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
import { IRSFormData } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
export const useVersionUpdate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [libraryApi.baseKey, 'update-version'],
|
||||||
|
mutationFn: libraryApi.versionUpdate,
|
||||||
|
onSuccess: (_, variables) => {
|
||||||
|
client.setQueryData(
|
||||||
|
[rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey],
|
||||||
|
(prev: IRSFormData) => ({
|
||||||
|
...prev,
|
||||||
|
versions: prev.versions.map(version =>
|
||||||
|
version.id === variables.versionID
|
||||||
|
? { ...version, description: variables.data.description, version: variables.data.version }
|
||||||
|
: version
|
||||||
|
)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
versionUpdate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
versionID: VersionID;
|
||||||
|
data: IVersionData;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,92 +0,0 @@
|
||||||
/**
|
|
||||||
* Endpoints: oss.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
ICstRelocateData,
|
|
||||||
IInputCreatedResponse,
|
|
||||||
IOperationCreateData,
|
|
||||||
IOperationCreatedResponse,
|
|
||||||
IOperationDeleteData,
|
|
||||||
IOperationSchemaData,
|
|
||||||
IOperationSetInputData,
|
|
||||||
IOperationUpdateData,
|
|
||||||
IPositionsData,
|
|
||||||
ITargetOperation
|
|
||||||
} from '@/models/oss';
|
|
||||||
import { IConstituentaReference, ITargetCst } from '@/models/rsform';
|
|
||||||
|
|
||||||
import { AxiosGet, AxiosPatch, AxiosPost, FrontExchange, FrontPull, FrontPush } from './apiTransport';
|
|
||||||
|
|
||||||
export function getOssDetails(target: string, request: FrontPull<IOperationSchemaData>) {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: `/api/oss/${target}/details`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchUpdatePositions(oss: string, request: FrontPush<IPositionsData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/oss/${oss}/update-positions`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postCreateOperation(
|
|
||||||
oss: string,
|
|
||||||
request: FrontExchange<IOperationCreateData, IOperationCreatedResponse>
|
|
||||||
) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/oss/${oss}/create-operation`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchDeleteOperation(oss: string, request: FrontExchange<IOperationDeleteData, IOperationSchemaData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/oss/${oss}/delete-operation`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchCreateInput(oss: string, request: FrontExchange<ITargetOperation, IInputCreatedResponse>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/oss/${oss}/create-input`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchSetInput(oss: string, request: FrontExchange<IOperationSetInputData, IOperationSchemaData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/oss/${oss}/set-input`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchUpdateOperation(oss: string, request: FrontExchange<IOperationUpdateData, IOperationSchemaData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/oss/${oss}/update-operation`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postExecuteOperation(oss: string, request: FrontExchange<ITargetOperation, IOperationSchemaData>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/oss/${oss}/execute-operation`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postRelocateConstituents(request: FrontPush<ICstRelocateData>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/oss/relocate-constituents`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postFindPredecessor(request: FrontExchange<ITargetCst, IConstituentaReference>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/oss/get-predecessor`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
148
rsconcept/frontend/src/backend/oss/api.ts
Normal file
148
rsconcept/frontend/src/backend/oss/api.ts
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { axiosInstance } from '@/backend/axiosInstance';
|
||||||
|
import { DELAYS } from '@/backend/configuration';
|
||||||
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
|
import {
|
||||||
|
ICstSubstitute,
|
||||||
|
IOperationData,
|
||||||
|
IOperationPosition,
|
||||||
|
IOperationSchemaData,
|
||||||
|
OperationID,
|
||||||
|
OperationType
|
||||||
|
} from '@/models/oss';
|
||||||
|
import { ConstituentaID, IConstituentaReference, ITargetCst } from '@/models/rsform';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} data, used in creation process.
|
||||||
|
*/
|
||||||
|
export interface IOperationCreateDTO {
|
||||||
|
positions: IOperationPosition[];
|
||||||
|
item_data: {
|
||||||
|
alias: string;
|
||||||
|
operation_type: OperationType;
|
||||||
|
title: string;
|
||||||
|
comment: string;
|
||||||
|
position_x: number;
|
||||||
|
position_y: number;
|
||||||
|
result: LibraryItemID | null;
|
||||||
|
};
|
||||||
|
arguments: OperationID[] | undefined;
|
||||||
|
create_schema: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data response when creating {@link IOperation}.
|
||||||
|
*/
|
||||||
|
export interface IOperationCreatedResponse {
|
||||||
|
new_operation: IOperationData;
|
||||||
|
oss: IOperationSchemaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents target {@link IOperation}.
|
||||||
|
*/
|
||||||
|
export interface ITargetOperation {
|
||||||
|
positions: IOperationPosition[];
|
||||||
|
target: OperationID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} data, used in destruction process.
|
||||||
|
*/
|
||||||
|
export interface IOperationDeleteDTO extends ITargetOperation {
|
||||||
|
keep_constituents: boolean;
|
||||||
|
delete_schema: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data response when creating {@link IRSForm} for Input {@link IOperation}.
|
||||||
|
*/
|
||||||
|
export interface IInputCreatedResponse {
|
||||||
|
new_schema: ILibraryItem;
|
||||||
|
oss: IOperationSchemaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} data, used in setInput process.
|
||||||
|
*/
|
||||||
|
export interface IInputUpdateDTO extends ITargetOperation {
|
||||||
|
input: LibraryItemID | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} data, used in update process.
|
||||||
|
*/
|
||||||
|
export interface IOperationUpdateDTO extends ITargetOperation {
|
||||||
|
item_data: {
|
||||||
|
alias: string;
|
||||||
|
title: string;
|
||||||
|
comment: string;
|
||||||
|
};
|
||||||
|
arguments: OperationID[] | undefined;
|
||||||
|
substitutions: ICstSubstitute[] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s.
|
||||||
|
*/
|
||||||
|
export interface ICstRelocateDTO {
|
||||||
|
destination: LibraryItemID;
|
||||||
|
items: ConstituentaID[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ossApi = {
|
||||||
|
baseKey: 'library',
|
||||||
|
|
||||||
|
getOssQueryOptions: ({ itemID }: { itemID?: LibraryItemID }) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryKey: [ossApi.baseKey, 'item', itemID],
|
||||||
|
staleTime: DELAYS.staleShort,
|
||||||
|
queryFn: meta =>
|
||||||
|
!itemID
|
||||||
|
? undefined
|
||||||
|
: axiosInstance
|
||||||
|
.get<IOperationSchemaData>(`/api/oss/${itemID}/details`, {
|
||||||
|
signal: meta.signal
|
||||||
|
})
|
||||||
|
.then(response => response.data)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePositions: (data: { itemID: LibraryItemID; positions: IOperationPosition[] }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch(`/api/oss/${data.itemID}/update-positions`, { positions: data.positions }),
|
||||||
|
operationCreate: (data: { itemID: LibraryItemID; data: IOperationCreateDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IOperationCreatedResponse>(`/api/oss/${data.itemID}/create-operation`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
operationDelete: (data: { itemID: LibraryItemID; data: IOperationDeleteDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IOperationSchemaData>(`/api/oss/${data.itemID}/delete-operation`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
inputCreate: (data: { itemID: LibraryItemID; data: ITargetOperation }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IInputCreatedResponse>(`/api/oss/${data.itemID}/create-input`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
inputUpdate: (data: { itemID: LibraryItemID; data: IInputUpdateDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IOperationSchemaData>(`/api/oss/${data.itemID}/set-input`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
operationUpdate: (data: { itemID: LibraryItemID; data: IOperationUpdateDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IOperationSchemaData>(`/api/oss/${data.itemID}/update-operation`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
operationExecute: (data: { itemID: LibraryItemID; data: ITargetOperation }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IOperationSchemaData>(`/api/oss/${data.itemID}/execute-operation`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
|
||||||
|
relocateConstituents: (data: { itemID: LibraryItemID; data: ICstRelocateDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IOperationSchemaData>(`/api/oss/${data.itemID}/relocate-constituents`, data.data)
|
||||||
|
.then(response => response.data),
|
||||||
|
getPredecessor: (data: ITargetCst) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IConstituentaReference>(`/api/oss/get-predecessor`, data)
|
||||||
|
.then(response => response.data)
|
||||||
|
};
|
19
rsconcept/frontend/src/backend/oss/useFindPredecessor.tsx
Normal file
19
rsconcept/frontend/src/backend/oss/useFindPredecessor.tsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { IConstituentaReference, ITargetCst } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { ossApi } from './api';
|
||||||
|
|
||||||
|
export const useFindPredecessor = () => {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'find-predecessor'],
|
||||||
|
mutationFn: ossApi.getPredecessor
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
findPredecessor: (
|
||||||
|
data: ITargetCst, //
|
||||||
|
onSuccess?: DataCallback<IConstituentaReference>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
28
rsconcept/frontend/src/backend/oss/useInputCreate.tsx
Normal file
28
rsconcept/frontend/src/backend/oss/useInputCreate.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { DataCallback } from '../apiTransport';
|
||||||
|
import { ITargetOperation, ossApi } from './api';
|
||||||
|
|
||||||
|
export const useInputCreate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'input-create'],
|
||||||
|
mutationFn: ossApi.inputCreate,
|
||||||
|
onSuccess: async data => {
|
||||||
|
client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey], data.oss);
|
||||||
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
inputCreate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ITargetOperation;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<ILibraryItem>
|
||||||
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.new_schema) })
|
||||||
|
};
|
||||||
|
};
|
27
rsconcept/frontend/src/backend/oss/useInputUpdate.tsx
Normal file
27
rsconcept/frontend/src/backend/oss/useInputUpdate.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { IInputUpdateDTO, ossApi } from './api';
|
||||||
|
|
||||||
|
export const useInputUpdate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'input-update'],
|
||||||
|
mutationFn: ossApi.inputUpdate,
|
||||||
|
onSuccess: async data => {
|
||||||
|
client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
inputUpdate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: IInputUpdateDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
11
rsconcept/frontend/src/backend/oss/useIsProcessingOss.tsx
Normal file
11
rsconcept/frontend/src/backend/oss/useIsProcessingOss.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { useIsMutating } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
|
||||||
|
import { ossApi } from './api';
|
||||||
|
|
||||||
|
export const useIsProcessingOss = () => {
|
||||||
|
const countLibrary = useIsMutating({ mutationKey: [libraryApi.baseKey] });
|
||||||
|
const countOss = useIsMutating({ mutationKey: [ossApi.baseKey] });
|
||||||
|
return countLibrary + countOss !== 0;
|
||||||
|
};
|
47
rsconcept/frontend/src/backend/oss/useOSS.tsx
Normal file
47
rsconcept/frontend/src/backend/oss/useOSS.tsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IOperationSchema, IOperationSchemaData } from '@/models/oss';
|
||||||
|
import { OssLoader } from '@/models/OssLoader';
|
||||||
|
|
||||||
|
import { useLibrary, useLibrarySuspense } from '@/backend/library/useLibrary';
|
||||||
|
import { ossApi } from './api';
|
||||||
|
|
||||||
|
export function useOss({ itemID }: { itemID?: LibraryItemID }) {
|
||||||
|
const { items: libraryItems, isLoading: libraryLoading } = useLibrary();
|
||||||
|
const { data, isLoading, error } = useQuery({
|
||||||
|
...ossApi.getOssQueryOptions({ itemID })
|
||||||
|
});
|
||||||
|
|
||||||
|
const schema = data && !libraryLoading ? new OssLoader(data, libraryItems).produceOSS() : undefined;
|
||||||
|
return { schema: schema, isLoading: isLoading || libraryLoading, error: error };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useOssSuspense({ itemID }: { itemID?: LibraryItemID }) {
|
||||||
|
const { items: libraryItems } = useLibrarySuspense();
|
||||||
|
const { data } = useSuspenseQuery({
|
||||||
|
...ossApi.getOssQueryOptions({ itemID })
|
||||||
|
});
|
||||||
|
const schema = data ? new OssLoader(data, libraryItems).produceOSS() : undefined;
|
||||||
|
return { schema };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useOssUpdate({ itemID }: { itemID?: LibraryItemID }) {
|
||||||
|
const { items: libraryItems } = useLibrary();
|
||||||
|
const client = useQueryClient();
|
||||||
|
const queryKey = [ossApi.getOssQueryOptions({ itemID }).queryKey];
|
||||||
|
return {
|
||||||
|
update: (data: IOperationSchemaData) =>
|
||||||
|
client.setQueryData(queryKey, new OssLoader(data, libraryItems).produceOSS()),
|
||||||
|
partialUpdate: (data: Partial<IOperationSchema>) =>
|
||||||
|
client.setQueryData(queryKey, (prev: IOperationSchema) => (prev ? { ...prev, ...data } : prev))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useOssInvalidate({ itemID }: { itemID?: LibraryItemID }) {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const queryKey = [ossApi.getOssQueryOptions({ itemID }).queryKey];
|
||||||
|
return {
|
||||||
|
invalidate: () => client.invalidateQueries({ queryKey })
|
||||||
|
};
|
||||||
|
}
|
30
rsconcept/frontend/src/backend/oss/useOperationCreate.tsx
Normal file
30
rsconcept/frontend/src/backend/oss/useOperationCreate.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IOperationData } from '@/models/oss';
|
||||||
|
|
||||||
|
import { IOperationCreateDTO, ossApi } from './api';
|
||||||
|
|
||||||
|
export const useOperationCreate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'operation-create'],
|
||||||
|
mutationFn: ossApi.operationCreate,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey], data.oss);
|
||||||
|
updateTimestamp(data.oss.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
operationCreate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: IOperationCreateDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<IOperationData>
|
||||||
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.new_operation) })
|
||||||
|
};
|
||||||
|
};
|
27
rsconcept/frontend/src/backend/oss/useOperationDelete.tsx
Normal file
27
rsconcept/frontend/src/backend/oss/useOperationDelete.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { IOperationDeleteDTO, ossApi } from './api';
|
||||||
|
|
||||||
|
export const useOperationDelete = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'operation-delete'],
|
||||||
|
mutationFn: ossApi.operationDelete,
|
||||||
|
onSuccess: async data => {
|
||||||
|
client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
operationDelete: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: IOperationDeleteDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
27
rsconcept/frontend/src/backend/oss/useOperationExecute.tsx
Normal file
27
rsconcept/frontend/src/backend/oss/useOperationExecute.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { ITargetOperation, ossApi } from './api';
|
||||||
|
|
||||||
|
export const useOperationExecute = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'operation-execute'],
|
||||||
|
mutationFn: ossApi.operationExecute,
|
||||||
|
onSuccess: async data => {
|
||||||
|
client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
operationExecute: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ITargetOperation;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
27
rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx
Normal file
27
rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { IOperationUpdateDTO, ossApi } from './api';
|
||||||
|
|
||||||
|
export const useOperationUpdate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'operation-update'],
|
||||||
|
mutationFn: ossApi.operationUpdate,
|
||||||
|
onSuccess: async data => {
|
||||||
|
client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
operationUpdate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: IOperationUpdateDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { ICstRelocateDTO, ossApi } from './api';
|
||||||
|
|
||||||
|
export const useRelocateConstituents = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'relocate-constituents'],
|
||||||
|
mutationFn: ossApi.relocateConstituents,
|
||||||
|
onSuccess: async data => {
|
||||||
|
client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
relocateConstituents: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ICstRelocateDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
25
rsconcept/frontend/src/backend/oss/useUpdatePositions.tsx
Normal file
25
rsconcept/frontend/src/backend/oss/useUpdatePositions.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IOperationPosition } from '@/models/oss';
|
||||||
|
|
||||||
|
import { ossApi } from './api';
|
||||||
|
|
||||||
|
export const useUpdatePositions = () => {
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [ossApi.baseKey, 'update-positions'],
|
||||||
|
mutationFn: ossApi.updatePositions,
|
||||||
|
onSuccess: (_, variables) => updateTimestamp(variables.itemID)
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cstDelete: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
positions: IOperationPosition[];
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,6 +1,8 @@
|
||||||
import { QueryClient } from '@tanstack/react-query';
|
import { QueryClient } from '@tanstack/react-query';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
import { DELAYS } from './configuration';
|
||||||
|
|
||||||
declare module '@tanstack/react-query' {
|
declare module '@tanstack/react-query' {
|
||||||
interface Register {
|
interface Register {
|
||||||
defaultError: AxiosError;
|
defaultError: AxiosError;
|
||||||
|
@ -10,8 +12,8 @@ declare module '@tanstack/react-query' {
|
||||||
export const queryClient = new QueryClient({
|
export const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
staleTime: 5 * 60 * 1000,
|
staleTime: DELAYS.staleDefault,
|
||||||
gcTime: 24 * 60 * 60 * 1000,
|
gcTime: DELAYS.garbageCollection,
|
||||||
retry: 3,
|
retry: 3,
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
|
|
191
rsconcept/frontend/src/backend/rsform/api.ts
Normal file
191
rsconcept/frontend/src/backend/rsform/api.ts
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { axiosInstance } from '@/backend/axiosInstance';
|
||||||
|
import { DELAYS } from '@/backend/configuration';
|
||||||
|
import { LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
import { ICstSubstitute, ICstSubstitutions } from '@/models/oss';
|
||||||
|
import {
|
||||||
|
ConstituentaID,
|
||||||
|
CstType,
|
||||||
|
IConstituentaList,
|
||||||
|
IConstituentaMeta,
|
||||||
|
IRSFormData,
|
||||||
|
ITargetCst,
|
||||||
|
TermForm
|
||||||
|
} from '@/models/rsform';
|
||||||
|
import { IExpressionParse } from '@/models/rslang';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data, used for uploading {@link IRSForm} as file.
|
||||||
|
*/
|
||||||
|
export interface IRSFormUploadDTO {
|
||||||
|
itemID: LibraryItemID;
|
||||||
|
load_metadata: boolean;
|
||||||
|
file: File;
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IConstituenta} data, used in creation process.
|
||||||
|
*/
|
||||||
|
export interface ICstCreateDTO {
|
||||||
|
alias: string;
|
||||||
|
cst_type: CstType;
|
||||||
|
definition_raw: string;
|
||||||
|
term_raw: string;
|
||||||
|
convention: string;
|
||||||
|
definition_formal: string;
|
||||||
|
term_forms: TermForm[];
|
||||||
|
|
||||||
|
insert_after: ConstituentaID | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data response when creating {@link IConstituenta}.
|
||||||
|
*/
|
||||||
|
export interface ICstCreatedResponse {
|
||||||
|
new_cst: IConstituentaMeta;
|
||||||
|
schema: IRSFormData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
|
||||||
|
*/
|
||||||
|
export interface ICstUpdateDTO {
|
||||||
|
target: ConstituentaID;
|
||||||
|
item_data: {
|
||||||
|
convention?: string;
|
||||||
|
definition_formal?: string;
|
||||||
|
definition_raw?: string;
|
||||||
|
term_raw?: string;
|
||||||
|
term_forms?: TermForm[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data, used in renaming {@link IConstituenta}.
|
||||||
|
*/
|
||||||
|
export interface ICstRenameDTO {
|
||||||
|
alias: string;
|
||||||
|
cst_type: CstType;
|
||||||
|
target: ConstituentaID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data, used in ordering a list of {@link IConstituenta}.
|
||||||
|
*/
|
||||||
|
export interface ICstMoveDTO {
|
||||||
|
items: ConstituentaID[];
|
||||||
|
move_to: number; // Note: 0-base index
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data response when creating producing structure of {@link IConstituenta}.
|
||||||
|
*/
|
||||||
|
export interface IProduceStructureResponse {
|
||||||
|
cst_list: ConstituentaID[];
|
||||||
|
schema: IRSFormData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents input data for inline synthesis.
|
||||||
|
*/
|
||||||
|
export interface IInlineSynthesisDTO {
|
||||||
|
receiver: LibraryItemID;
|
||||||
|
source: LibraryItemID;
|
||||||
|
items: ConstituentaID[];
|
||||||
|
substitutions: ICstSubstitute[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IConstituenta} data, used for checking expression.
|
||||||
|
*/
|
||||||
|
export interface ICheckConstituentaDTO {
|
||||||
|
alias: string;
|
||||||
|
cst_type: CstType;
|
||||||
|
definition_formal: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rsformsApi = {
|
||||||
|
baseKey: 'library',
|
||||||
|
|
||||||
|
getRSFormQueryOptions: ({ itemID, version }: { itemID?: LibraryItemID; version?: VersionID }) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryKey: [rsformsApi.baseKey, 'item', itemID, version ?? ''],
|
||||||
|
staleTime: DELAYS.staleShort,
|
||||||
|
queryFn: meta =>
|
||||||
|
!itemID
|
||||||
|
? undefined
|
||||||
|
: axiosInstance
|
||||||
|
.get<IRSFormData>(
|
||||||
|
version ? `/api/library/${itemID}/versions/${version}` : `/api/rsforms/${itemID}/details`,
|
||||||
|
{
|
||||||
|
signal: meta.signal
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(response => response.data)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
download: ({ itemID, version }: { itemID: LibraryItemID; version?: VersionID }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.get<Blob>(version ? `/api/versions/${version}/export-file` : `/api/rsforms/${itemID}/export-trs`, {
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
.then(response => response.data),
|
||||||
|
upload: (data: IRSFormUploadDTO) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IRSFormData>(`/api/rsforms/${data.itemID}/load-trs`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.data),
|
||||||
|
|
||||||
|
cstCreate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstCreateDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<ICstCreatedResponse>(`/api/rsforms/${itemID}/create-cst`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
cstUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstUpdateDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IConstituentaMeta>(`/api/rsforms/${itemID}/update-cst`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
cstDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IConstituentaList }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IRSFormData>(`/api/rsforms/${itemID}/delete-multiple-cst`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
cstRename: ({ itemID, data }: { itemID: LibraryItemID; data: ICstRenameDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<ICstCreatedResponse>(`/api/rsforms/${itemID}/rename-cst`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
cstSubstitute: ({ itemID, data }: { itemID: LibraryItemID; data: ICstSubstitutions }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IRSFormData>(`/api/rsforms/${itemID}/substitute`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
cstMove: ({ itemID, data }: { itemID: LibraryItemID; data: ICstMoveDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IRSFormData>(`/api/rsforms/${itemID}/move-cst`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
|
||||||
|
produceStructure: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetCst }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IProduceStructureResponse>(`/api/rsforms/${itemID}/produce-structure`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
inlineSynthesis: ({ itemID, data }: { itemID: LibraryItemID; data: IInlineSynthesisDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IRSFormData>(`/api/rsforms/${itemID}/inline-synthesis`, data)
|
||||||
|
.then(response => response.data),
|
||||||
|
restoreOrder: (itemID: LibraryItemID) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IRSFormData>(`/api/rsforms/${itemID}/restore-order`)
|
||||||
|
.then(response => response.data),
|
||||||
|
resetAliases: (itemID: LibraryItemID) =>
|
||||||
|
axiosInstance //
|
||||||
|
.patch<IRSFormData>(`/api/rsforms/${itemID}/reset-aliases`)
|
||||||
|
.then(response => response.data),
|
||||||
|
|
||||||
|
checkConstituenta: ({ itemID, data }: { itemID: LibraryItemID; data: ICheckConstituentaDTO }) =>
|
||||||
|
axiosInstance //
|
||||||
|
.post<IExpressionParse>(`/api/rsforms/${itemID}/check-constituenta`, data)
|
||||||
|
.then(response => response.data)
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IExpressionParse } from '@/models/rslang';
|
||||||
|
|
||||||
|
import { DataCallback } from '../apiTransport';
|
||||||
|
import { ICheckConstituentaDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useCheckConstituenta = () => {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'check-constituenta'],
|
||||||
|
mutationFn: rsformsApi.checkConstituenta
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
checkConstituenta: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ICheckConstituentaDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<IExpressionParse>
|
||||||
|
) => mutation.mutate(data, { onSuccess }),
|
||||||
|
isPending: mutation.isPending,
|
||||||
|
error: mutation.error,
|
||||||
|
reset: mutation.reset
|
||||||
|
};
|
||||||
|
};
|
31
rsconcept/frontend/src/backend/rsform/useCstCreate.tsx
Normal file
31
rsconcept/frontend/src/backend/rsform/useCstCreate.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IConstituentaMeta } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { ICstCreateDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useCstCreate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'create-cst'],
|
||||||
|
mutationFn: rsformsApi.cstCreate,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema);
|
||||||
|
updateTimestamp(data.schema.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cstCreate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ICstCreateDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<IConstituentaMeta>
|
||||||
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.new_cst) })
|
||||||
|
};
|
||||||
|
};
|
30
rsconcept/frontend/src/backend/rsform/useCstDelete.tsx
Normal file
30
rsconcept/frontend/src/backend/rsform/useCstDelete.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IConstituentaList } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useCstDelete = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'delete-multiple-cst'],
|
||||||
|
mutationFn: rsformsApi.cstDelete,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cstDelete: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: IConstituentaList;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
29
rsconcept/frontend/src/backend/rsform/useCstMove.tsx
Normal file
29
rsconcept/frontend/src/backend/rsform/useCstMove.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { ICstMoveDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useCstMove = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'move-cst'],
|
||||||
|
mutationFn: rsformsApi.cstMove,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cstMove: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ICstMoveDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
31
rsconcept/frontend/src/backend/rsform/useCstRename.tsx
Normal file
31
rsconcept/frontend/src/backend/rsform/useCstRename.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IConstituentaMeta } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { DataCallback } from '../apiTransport';
|
||||||
|
import { ICstRenameDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useCstRename = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'rename-cst'],
|
||||||
|
mutationFn: rsformsApi.cstRename,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema);
|
||||||
|
updateTimestamp(data.schema.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cstRename: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ICstRenameDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<IConstituentaMeta>
|
||||||
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.new_cst) })
|
||||||
|
};
|
||||||
|
};
|
30
rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx
Normal file
30
rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { ICstSubstitutions } from '@/models/oss';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useCstSubstitute = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'substitute-cst'],
|
||||||
|
mutationFn: rsformsApi.cstSubstitute,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cstSubstitute: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ICstSubstitutions;
|
||||||
|
},
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
33
rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx
Normal file
33
rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IConstituentaMeta } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { ICstUpdateDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useCstUpdate = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'update-cst'],
|
||||||
|
mutationFn: rsformsApi.cstUpdate,
|
||||||
|
onSuccess: async (_, variables) => {
|
||||||
|
updateTimestamp(variables.itemID);
|
||||||
|
await client.invalidateQueries({
|
||||||
|
queryKey: [rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey]
|
||||||
|
});
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cstUpdate: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ICstUpdateDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<IConstituentaMeta>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
21
rsconcept/frontend/src/backend/rsform/useDownloadRSForm.tsx
Normal file
21
rsconcept/frontend/src/backend/rsform/useDownloadRSForm.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useDownloadRSForm = () => {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'download'],
|
||||||
|
mutationFn: rsformsApi.download
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
download: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
version?: VersionID;
|
||||||
|
},
|
||||||
|
onSuccess?: (data: Blob) => void
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
31
rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx
Normal file
31
rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IRSFormData } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { DataCallback } from '../apiTransport';
|
||||||
|
import { IInlineSynthesisDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useInlineSynthesis = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'inline-synthesis'],
|
||||||
|
mutationFn: rsformsApi.inlineSynthesis,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
inlineSynthesis: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: IInlineSynthesisDTO;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<IRSFormData>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { useIsMutating } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useIsProcessingRSForm = () => {
|
||||||
|
const countLibrary = useIsMutating({ mutationKey: [libraryApi.baseKey] });
|
||||||
|
const countRsform = useIsMutating({ mutationKey: [rsformsApi.baseKey] });
|
||||||
|
return countLibrary + countRsform !== 0;
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { ConstituentaID, ITargetCst } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useProduceStructure = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'produce-structure'],
|
||||||
|
mutationFn: rsformsApi.produceStructure,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema);
|
||||||
|
updateTimestamp(data.schema.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
produceStructure: (
|
||||||
|
data: {
|
||||||
|
itemID: LibraryItemID; //
|
||||||
|
data: ITargetCst;
|
||||||
|
},
|
||||||
|
onSuccess?: DataCallback<ConstituentaID[]>
|
||||||
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.cst_list) })
|
||||||
|
};
|
||||||
|
};
|
34
rsconcept/frontend/src/backend/rsform/useRSForm.tsx
Normal file
34
rsconcept/frontend/src/backend/rsform/useRSForm.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
import { IRSForm, IRSFormData } from '@/models/rsform';
|
||||||
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export function useRSForm({ itemID, version }: { itemID?: LibraryItemID; version?: VersionID }) {
|
||||||
|
const { data, isLoading, error } = useQuery({
|
||||||
|
...rsformsApi.getRSFormQueryOptions({ itemID, version })
|
||||||
|
});
|
||||||
|
|
||||||
|
const schema = data ? new RSFormLoader(data).produceRSForm() : undefined;
|
||||||
|
return { schema, isLoading, error };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRSFormSuspense({ itemID, version }: { itemID?: LibraryItemID; version?: VersionID }) {
|
||||||
|
const { data } = useSuspenseQuery({
|
||||||
|
...rsformsApi.getRSFormQueryOptions({ itemID, version })
|
||||||
|
});
|
||||||
|
const schema = data ? new RSFormLoader(data).produceRSForm() : undefined;
|
||||||
|
return { schema };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRSFormUpdate({ itemID }: { itemID: LibraryItemID }) {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const queryKey = [rsformsApi.getRSFormQueryOptions({ itemID }).queryKey];
|
||||||
|
return {
|
||||||
|
update: (data: IRSFormData) => client.setQueryData(queryKey, data),
|
||||||
|
partialUpdate: (data: Partial<IRSForm>) =>
|
||||||
|
client.setQueryData(queryKey, (prev: IRSForm) => (prev ? { ...prev, ...data } : prev))
|
||||||
|
};
|
||||||
|
}
|
23
rsconcept/frontend/src/backend/rsform/useRSForms.tsx
Normal file
23
rsconcept/frontend/src/backend/rsform/useRSForms.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { useQueries } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||||
|
|
||||||
|
import { DELAYS } from '../configuration';
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export function useRSForms(itemIDs: LibraryItemID[]) {
|
||||||
|
const results = useQueries({
|
||||||
|
queries: itemIDs.map(itemID => ({
|
||||||
|
...rsformsApi.getRSFormQueryOptions({ itemID }),
|
||||||
|
enabled: itemIDs.length > 0,
|
||||||
|
staleTime: DELAYS.staleShort
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
const schemas = results
|
||||||
|
.map(result => result.data)
|
||||||
|
.filter(data => data !== undefined)
|
||||||
|
.map(data => new RSFormLoader(data).produceRSForm());
|
||||||
|
return schemas;
|
||||||
|
}
|
26
rsconcept/frontend/src/backend/rsform/useResetAliases.tsx
Normal file
26
rsconcept/frontend/src/backend/rsform/useResetAliases.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useResetAliases = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'reset-aliases'],
|
||||||
|
mutationFn: rsformsApi.resetAliases,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
// TODO: invalidate OSS?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
resetAliases: (
|
||||||
|
itemID: LibraryItemID, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(itemID, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
25
rsconcept/frontend/src/backend/rsform/useRestoreOrder.tsx
Normal file
25
rsconcept/frontend/src/backend/rsform/useRestoreOrder.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
|
||||||
|
import { rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useRestoreOrder = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'restore-order'],
|
||||||
|
mutationFn: rsformsApi.restoreOrder,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
updateTimestamp(data.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
restoreOrder: (
|
||||||
|
itemID: LibraryItemID, //
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => mutation.mutate(itemID, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
28
rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx
Normal file
28
rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
|
import { libraryApi } from '@/backend/library/api';
|
||||||
|
import { ILibraryItem } from '@/models/library';
|
||||||
|
import { IRSFormData } from '@/models/rsform';
|
||||||
|
|
||||||
|
import { IRSFormUploadDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
export const useUploadTRS = () => {
|
||||||
|
const client = useQueryClient();
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationKey: [rsformsApi.baseKey, 'load-trs'],
|
||||||
|
mutationFn: rsformsApi.upload,
|
||||||
|
onSuccess: data => {
|
||||||
|
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
||||||
|
client.setQueryData([libraryApi.libraryListKey], (prev: ILibraryItem[] | undefined) =>
|
||||||
|
prev?.map(item => (item.id === data.id ? data : item))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
upload: (
|
||||||
|
data: IRSFormUploadDTO, //
|
||||||
|
onSuccess?: DataCallback<IRSFormData>
|
||||||
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,158 +0,0 @@
|
||||||
/**
|
|
||||||
* Endpoints: rsforms.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ILibraryCreateData, ILibraryItem } from '@/models/library';
|
|
||||||
import { ICstSubstituteData } from '@/models/oss';
|
|
||||||
import {
|
|
||||||
ICheckConstituentaData,
|
|
||||||
IConstituentaList,
|
|
||||||
IConstituentaMeta,
|
|
||||||
ICstCreateData,
|
|
||||||
ICstCreatedResponse,
|
|
||||||
ICstMovetoData,
|
|
||||||
ICstRenameData,
|
|
||||||
ICstUpdateData,
|
|
||||||
IInlineSynthesisData,
|
|
||||||
IProduceStructureResponse,
|
|
||||||
IRSFormData,
|
|
||||||
IRSFormUploadData,
|
|
||||||
ITargetCst
|
|
||||||
} from '@/models/rsform';
|
|
||||||
import { IExpressionParse } from '@/models/rslang';
|
|
||||||
|
|
||||||
import { AxiosGet, AxiosPatch, AxiosPost, FrontExchange, FrontPull } from './apiTransport';
|
|
||||||
|
|
||||||
export function postRSFormFromFile(request: FrontExchange<ILibraryCreateData, ILibraryItem>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: '/api/rsforms/create-detailed',
|
|
||||||
request: request,
|
|
||||||
options: {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRSFormDetails(target: string, version: string, request: FrontPull<IRSFormData>) {
|
|
||||||
if (!version) {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: `/api/rsforms/${target}/details`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: `/api/library/${target}/versions/${version}`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTRSFile(target: string, version: string, request: FrontPull<Blob>) {
|
|
||||||
if (!version) {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: `/api/rsforms/${target}/export-trs`,
|
|
||||||
request: request,
|
|
||||||
options: { responseType: 'blob' }
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
AxiosGet({
|
|
||||||
endpoint: `/api/versions/${version}/export-file`,
|
|
||||||
request: request,
|
|
||||||
options: { responseType: 'blob' }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postCreateConstituenta(schema: string, request: FrontExchange<ICstCreateData, ICstCreatedResponse>) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/rsforms/${schema}/create-cst`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchUpdateConstituenta(schema: string, request: FrontExchange<ICstUpdateData, IConstituentaMeta>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${schema}/update-cst`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchDeleteConstituenta(schema: string, request: FrontExchange<IConstituentaList, IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${schema}/delete-multiple-cst`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchRenameConstituenta(schema: string, request: FrontExchange<ICstRenameData, ICstCreatedResponse>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${schema}/rename-cst`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchProduceStructure(schema: string, request: FrontExchange<ITargetCst, IProduceStructureResponse>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${schema}/produce-structure`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchSubstituteConstituents(schema: string, request: FrontExchange<ICstSubstituteData, IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${schema}/substitute`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchMoveConstituenta(schema: string, request: FrontExchange<ICstMovetoData, IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${schema}/move-cst`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postCheckConstituenta(
|
|
||||||
schema: string,
|
|
||||||
request: FrontExchange<ICheckConstituentaData, IExpressionParse>
|
|
||||||
) {
|
|
||||||
AxiosPost({
|
|
||||||
endpoint: `/api/rsforms/${schema}/check-constituenta`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchResetAliases(target: string, request: FrontPull<IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${target}/reset-aliases`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchRestoreOrder(target: string, request: FrontPull<IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${target}/restore-order`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchUploadTRS(target: string, request: FrontExchange<IRSFormUploadData, IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/${target}/load-trs`,
|
|
||||||
request: request,
|
|
||||||
options: {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchInlineSynthesis(request: FrontExchange<IInlineSynthesisData, IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/rsforms/inline-synthesis`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { axiosInstance } from '@/backend/axiosInstance';
|
||||||
|
import { DELAYS } from '@/backend/configuration';
|
||||||
import { IUser, IUserInfo, IUserProfile, IUserSignupData } from '@/models/user';
|
import { IUser, IUserInfo, IUserProfile, IUserSignupData } from '@/models/user';
|
||||||
|
|
||||||
import { axiosInstance } from '../apiConfiguration';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents user data, intended to update user profile in persistent storage.
|
* Represents user data, intended to update user profile in persistent storage.
|
||||||
*/
|
*/
|
||||||
|
@ -14,17 +14,18 @@ export const usersApi = {
|
||||||
getUsersQueryOptions: () =>
|
getUsersQueryOptions: () =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: [usersApi.baseKey, 'list'],
|
queryKey: [usersApi.baseKey, 'list'],
|
||||||
|
staleTime: DELAYS.staleMedium,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get<IUserInfo[]>('/users/api/active-users', {
|
.get<IUserInfo[]>('/users/api/active-users', {
|
||||||
signal: meta.signal
|
signal: meta.signal
|
||||||
})
|
})
|
||||||
.then(response => response.data),
|
.then(response => response.data)
|
||||||
placeholderData: []
|
|
||||||
}),
|
}),
|
||||||
getProfileQueryOptions: () =>
|
getProfileQueryOptions: () =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: [usersApi.baseKey, 'profile'],
|
queryKey: [usersApi.baseKey, 'profile'],
|
||||||
|
staleTime: DELAYS.staleShort,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get<IUserProfile>('/users/api/profile', {
|
.get<IUserProfile>('/users/api/profile', {
|
||||||
|
@ -36,5 +37,3 @@ export const usersApi = {
|
||||||
signup: (data: IUserSignupData) => axiosInstance.post('/users/api/signup', data),
|
signup: (data: IUserSignupData) => axiosInstance.post('/users/api/signup', data),
|
||||||
updateProfile: (data: IUpdateProfileDTO) => axiosInstance.patch('/users/api/profile', data)
|
updateProfile: (data: IUpdateProfileDTO) => axiosInstance.patch('/users/api/profile', data)
|
||||||
};
|
};
|
||||||
|
|
||||||
//DataCallback<IUserProfile>
|
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
import { usersApi } from '@/backend/users/api';
|
import { usersApi } from '@/backend/users/api';
|
||||||
import { IUserProfile, IUserSignupData } from '@/models/user';
|
import { IUserProfile, IUserSignupData } from '@/models/user';
|
||||||
|
|
||||||
import { DataCallback } from '../apiTransport';
|
|
||||||
|
|
||||||
export const useSignup = () => {
|
export const useSignup = () => {
|
||||||
const queryClient = useQueryClient();
|
const client = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: ['signup'],
|
mutationKey: ['signup'],
|
||||||
mutationFn: usersApi.signup,
|
mutationFn: usersApi.signup,
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [usersApi.baseKey] })
|
onSuccess: async () => await client.invalidateQueries({ queryKey: [usersApi.baseKey] })
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
signup: (data: IUserSignupData, onSuccess?: DataCallback<IUserProfile>) =>
|
signup: (
|
||||||
mutation.mutate(data, { onSuccess: response => onSuccess?.(response.data as IUserProfile) }),
|
data: IUserSignupData, //
|
||||||
|
onSuccess?: DataCallback<IUserProfile>
|
||||||
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.data as IUserProfile) }),
|
||||||
isPending: mutation.isPending,
|
isPending: mutation.isPending,
|
||||||
error: mutation.error,
|
error: mutation.error,
|
||||||
reset: mutation.reset
|
reset: mutation.reset
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
import { IUserProfile } from '@/models/user';
|
import { IUserProfile } from '@/models/user';
|
||||||
|
|
||||||
import { IUpdateProfileDTO, usersApi } from './api';
|
import { IUpdateProfileDTO, usersApi } from './api';
|
||||||
|
@ -7,15 +8,17 @@ import { IUpdateProfileDTO, usersApi } from './api';
|
||||||
// TODO: reload users / optimistic update
|
// TODO: reload users / optimistic update
|
||||||
|
|
||||||
export const useUpdateProfile = () => {
|
export const useUpdateProfile = () => {
|
||||||
const queryClient = useQueryClient();
|
const client = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: ['update-profile'],
|
mutationKey: ['update-profile'],
|
||||||
mutationFn: usersApi.updateProfile,
|
mutationFn: usersApi.updateProfile,
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [usersApi.baseKey] })
|
onSuccess: async () => await client.invalidateQueries({ queryKey: [usersApi.baseKey] })
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
updateProfile: (data: IUpdateProfileDTO, onSuccess?: (newUser: IUserProfile) => void) =>
|
updateProfile: (
|
||||||
mutation.mutate(data, { onSuccess: response => onSuccess?.(response.data as IUserProfile) }),
|
data: IUpdateProfileDTO, //
|
||||||
|
onSuccess?: DataCallback<IUserProfile>
|
||||||
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.data as IUserProfile) }),
|
||||||
isPending: mutation.isPending,
|
isPending: mutation.isPending,
|
||||||
error: mutation.error,
|
error: mutation.error,
|
||||||
reset: mutation.reset
|
reset: mutation.reset
|
||||||
|
|
|
@ -4,8 +4,7 @@ import { usersApi } from './api';
|
||||||
|
|
||||||
export function useUsersSuspense() {
|
export function useUsersSuspense() {
|
||||||
const { data: users } = useSuspenseQuery({
|
const { data: users } = useSuspenseQuery({
|
||||||
...usersApi.getUsersQueryOptions(),
|
...usersApi.getUsersQueryOptions()
|
||||||
initialData: []
|
|
||||||
});
|
});
|
||||||
return { users };
|
return { users };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/**
|
|
||||||
* Endpoints: versions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { IVersionData } from '@/models/library';
|
|
||||||
import { IRSFormData } from '@/models/rsform';
|
|
||||||
|
|
||||||
import { AxiosDelete, AxiosPatch, FrontAction, FrontPull, FrontPush } from './apiTransport';
|
|
||||||
|
|
||||||
export function patchVersion(target: string, request: FrontPush<IVersionData>) {
|
|
||||||
// title: `Version id=${target}`,
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/versions/${target}`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchRestoreVersion(target: string, request: FrontPull<IRSFormData>) {
|
|
||||||
AxiosPatch({
|
|
||||||
endpoint: `/api/versions/${target}/restore`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteVersion(target: string, request: FrontAction) {
|
|
||||||
AxiosDelete({
|
|
||||||
endpoint: `/api/versions/${target}`,
|
|
||||||
request: request
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -47,7 +47,7 @@ interface RSInputProps
|
||||||
const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
id, // prettier: split lines
|
id, //
|
||||||
label,
|
label,
|
||||||
disabled,
|
disabled,
|
||||||
noTooltip,
|
noTooltip,
|
||||||
|
|
|
@ -16,7 +16,7 @@ function BadgeGrammeme({ grammeme }: BadgeGrammemeProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'min-w-[3rem]', // prettier: split lines
|
'min-w-[3rem]', //
|
||||||
'px-1',
|
'px-1',
|
||||||
'border rounded-md',
|
'border rounded-md',
|
||||||
'text-sm font-medium text-center whitespace-nowrap'
|
'text-sm font-medium text-center whitespace-nowrap'
|
||||||
|
|
|
@ -19,7 +19,7 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||||
<p key={`${prefixes.cst_status_list}${index}`}>
|
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||||
<span
|
<span
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'inline-block', // prettier: split lines
|
'inline-block', //
|
||||||
'min-w-[7rem]',
|
'min-w-[7rem]',
|
||||||
'px-1',
|
'px-1',
|
||||||
'border',
|
'border',
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { isResponseHtml } from '@/utils/utils';
|
||||||
|
|
||||||
import PrettyJson from '../ui/PrettyJSON';
|
import PrettyJson from '../ui/PrettyJSON';
|
||||||
|
|
||||||
export type ErrorData = string | Error | AxiosError | undefined;
|
export type ErrorData = string | Error | AxiosError | undefined | null;
|
||||||
|
|
||||||
interface InfoErrorProps {
|
interface InfoErrorProps {
|
||||||
error: ErrorData;
|
error: ErrorData;
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
|
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
|
||||||
import SearchBar from '@/components/ui/SearchBar';
|
import SearchBar from '@/components/ui/SearchBar';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
import { matchLibraryItem } from '@/models/libraryAPI';
|
import { matchLibraryItem } from '@/models/libraryAPI';
|
||||||
|
@ -45,8 +44,6 @@ function PickSchema({
|
||||||
...restProps
|
...restProps
|
||||||
}: PickSchemaProps) {
|
}: PickSchemaProps) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { folders } = useLibrary();
|
|
||||||
|
|
||||||
const [filterText, setFilterText] = useState(initialFilter);
|
const [filterText, setFilterText] = useState(initialFilter);
|
||||||
const [filterLocation, setFilterLocation] = useState('');
|
const [filterLocation, setFilterLocation] = useState('');
|
||||||
const [filtered, setFiltered] = useState<ILibraryItem[]>([]);
|
const [filtered, setFiltered] = useState<ILibraryItem[]>([]);
|
||||||
|
@ -128,7 +125,6 @@ function PickSchema({
|
||||||
/>
|
/>
|
||||||
<Dropdown isOpen={locationMenu.isOpen} stretchLeft className='w-[20rem] h-[12.5rem] z-modalTooltip mt-0'>
|
<Dropdown isOpen={locationMenu.isOpen} stretchLeft className='w-[20rem] h-[12.5rem] z-modalTooltip mt-0'>
|
||||||
<SelectLocation
|
<SelectLocation
|
||||||
folderTree={folders}
|
|
||||||
value={filterLocation}
|
value={filterLocation}
|
||||||
prefix={prefixes.folders_list}
|
prefix={prefixes.folders_list}
|
||||||
dense
|
dense
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { FolderNode, FolderTree } from '@/models/FolderTree';
|
import { useFolders } from '@/backend/library/useFolders';
|
||||||
|
import { FolderNode } from '@/models/FolderTree';
|
||||||
import { labelFolderNode } from '@/utils/labels';
|
import { labelFolderNode } from '@/utils/labels';
|
||||||
|
|
||||||
import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened } from '../Icons';
|
import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened } from '../Icons';
|
||||||
|
@ -12,15 +13,15 @@ import MiniButton from '../ui/MiniButton';
|
||||||
|
|
||||||
interface SelectLocationProps extends CProps.Styling {
|
interface SelectLocationProps extends CProps.Styling {
|
||||||
value: string;
|
value: string;
|
||||||
folderTree: FolderTree;
|
|
||||||
prefix: string;
|
prefix: string;
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
onClick: (event: CProps.EventMouse, target: FolderNode) => void;
|
onClick: (event: CProps.EventMouse, target: FolderNode) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectLocation({ value, folderTree, dense, prefix, onClick, className, style }: SelectLocationProps) {
|
function SelectLocation({ value, dense, prefix, onClick, className, style }: SelectLocationProps) {
|
||||||
const activeNode = folderTree.at(value);
|
const { folders } = useFolders();
|
||||||
const items = folderTree.getTree();
|
const activeNode = folders.at(value);
|
||||||
|
const items = folders.getTree();
|
||||||
const [folded, setFolded] = useState<FolderNode[]>(items);
|
const [folded, setFolded] = useState<FolderNode[]>(items);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import clsx from 'clsx';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { FolderTree } from '@/models/FolderTree';
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import { IconFolderTree } from '../Icons';
|
import { IconFolderTree } from '../Icons';
|
||||||
|
@ -16,7 +15,6 @@ import SelectLocation from './SelectLocation';
|
||||||
interface SelectLocationContextProps extends CProps.Styling {
|
interface SelectLocationContextProps extends CProps.Styling {
|
||||||
value: string;
|
value: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
folderTree: FolderTree;
|
|
||||||
stretchTop?: boolean;
|
stretchTop?: boolean;
|
||||||
|
|
||||||
onChange: (newValue: string) => void;
|
onChange: (newValue: string) => void;
|
||||||
|
@ -25,7 +23,6 @@ interface SelectLocationContextProps extends CProps.Styling {
|
||||||
function SelectLocationContext({
|
function SelectLocationContext({
|
||||||
value,
|
value,
|
||||||
title = 'Проводник...',
|
title = 'Проводник...',
|
||||||
folderTree,
|
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
style
|
style
|
||||||
|
@ -56,7 +53,6 @@ function SelectLocationContext({
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
<SelectLocation
|
<SelectLocation
|
||||||
folderTree={folderTree}
|
|
||||||
value={value}
|
value={value}
|
||||||
prefix={prefixes.folders_list}
|
prefix={prefixes.folders_list}
|
||||||
dense
|
dense
|
||||||
|
|
|
@ -48,7 +48,7 @@ function Checkbox({
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex items-center gap-2', // prettier: split lines
|
'flex items-center gap-2', //
|
||||||
'outline-none',
|
'outline-none',
|
||||||
'focus-frame',
|
'focus-frame',
|
||||||
cursor,
|
cursor,
|
||||||
|
@ -64,7 +64,7 @@ function Checkbox({
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'max-w-[1rem] min-w-[1rem] h-4', // prettier: split lines
|
'max-w-[1rem] min-w-[1rem] h-4', //
|
||||||
'pt-[0.1rem] pl-[0.1rem]',
|
'pt-[0.1rem] pl-[0.1rem]',
|
||||||
'border rounded-sm',
|
'border rounded-sm',
|
||||||
'cc-animate-color',
|
'cc-animate-color',
|
||||||
|
|
|
@ -49,7 +49,7 @@ function CheckboxTristate({
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex items-center gap-2', // prettier: split lines
|
'flex items-center gap-2', //
|
||||||
'outline-none',
|
'outline-none',
|
||||||
'focus-frame',
|
'focus-frame',
|
||||||
cursor,
|
cursor,
|
||||||
|
@ -65,7 +65,7 @@ function CheckboxTristate({
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'w-4 h-4', // prettier: split lines
|
'w-4 h-4', //
|
||||||
'pt-[0.1rem] pl-[0.1rem]',
|
'pt-[0.1rem] pl-[0.1rem]',
|
||||||
'border rounded-sm',
|
'border rounded-sm',
|
||||||
'cc-animate-color',
|
'cc-animate-color',
|
||||||
|
|
|
@ -6,7 +6,8 @@ interface PrettyJsonProps {
|
||||||
* Displays JSON data in a formatted string.
|
* Displays JSON data in a formatted string.
|
||||||
*/
|
*/
|
||||||
function PrettyJson({ data }: PrettyJsonProps) {
|
function PrettyJson({ data }: PrettyJsonProps) {
|
||||||
return <pre>{JSON.stringify(data, null, 2)}</pre>;
|
const text = JSON.stringify(data, null, 2);
|
||||||
|
return <pre>{text === '{}' ? '' : text}</pre>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrettyJson;
|
export default PrettyJson;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuth } from '@/backend/auth/useAuth';
|
||||||
import { useLogout } from '@/backend/auth/useLogout';
|
import { useLogout } from '@/backend/auth/useLogout';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
|
|
||||||
import TextURL from '../ui/TextURL';
|
import TextURL from '../ui/TextURL';
|
||||||
|
|
||||||
|
|
|
@ -1,322 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
|
||||||
import {
|
|
||||||
deleteLibraryItem,
|
|
||||||
getAdminLibrary,
|
|
||||||
getLibrary,
|
|
||||||
getTemplates,
|
|
||||||
patchRenameLocation,
|
|
||||||
postCloneLibraryItem,
|
|
||||||
postCreateLibraryItem
|
|
||||||
} from '@/backend/library';
|
|
||||||
import { getRSFormDetails, postRSFormFromFile } from '@/backend/rsforms';
|
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
|
||||||
import { FolderTree } from '@/models/FolderTree';
|
|
||||||
import { ILibraryItem, IRenameLocationData, LibraryItemID, LocationHead } from '@/models/library';
|
|
||||||
import { ILibraryCreateData } from '@/models/library';
|
|
||||||
import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI';
|
|
||||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
|
||||||
import { IRSForm, IRSFormCloneData, IRSFormData } from '@/models/rsform';
|
|
||||||
import { RSFormLoader } from '@/models/RSFormLoader';
|
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
|
||||||
import { contextOutsideScope } from '@/utils/labels';
|
|
||||||
|
|
||||||
interface ILibraryContext {
|
|
||||||
items: ILibraryItem[];
|
|
||||||
templates: ILibraryItem[];
|
|
||||||
folders: FolderTree;
|
|
||||||
|
|
||||||
loading: boolean;
|
|
||||||
loadingError: ErrorData;
|
|
||||||
setLoadingError: (error: ErrorData) => void;
|
|
||||||
|
|
||||||
processing: boolean;
|
|
||||||
processingError: ErrorData;
|
|
||||||
setProcessingError: (error: ErrorData) => void;
|
|
||||||
|
|
||||||
reloadItems: (callback?: () => void) => void;
|
|
||||||
|
|
||||||
applyFilter: (params: ILibraryFilter) => ILibraryItem[];
|
|
||||||
retrieveTemplate: (templateID: LibraryItemID, callback: (schema: IRSForm) => void) => void;
|
|
||||||
createItem: (data: ILibraryCreateData, callback?: DataCallback<ILibraryItem>) => void;
|
|
||||||
cloneItem: (target: LibraryItemID, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => void;
|
|
||||||
destroyItem: (target: LibraryItemID, callback?: () => void) => void;
|
|
||||||
renameLocation: (data: IRenameLocationData, callback?: () => void) => void;
|
|
||||||
|
|
||||||
localUpdateItem: (data: Partial<ILibraryItem>) => void;
|
|
||||||
localUpdateTimestamp: (target: LibraryItemID) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const LibraryContext = createContext<ILibraryContext | null>(null);
|
|
||||||
export const useLibrary = (): ILibraryContext => {
|
|
||||||
const context = useContext(LibraryContext);
|
|
||||||
if (context === null) {
|
|
||||||
throw new Error(contextOutsideScope('useLibrary', 'LibraryState'));
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LibraryState = ({ children }: React.PropsWithChildren) => {
|
|
||||||
const { user, isLoading: userLoading } = useAuth();
|
|
||||||
const adminMode = usePreferencesStore(state => state.adminMode);
|
|
||||||
|
|
||||||
const [items, setItems] = useState<ILibraryItem[]>([]);
|
|
||||||
const [templates, setTemplates] = useState<ILibraryItem[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [processing, setProcessing] = useState(false);
|
|
||||||
const [loadingError, setLoadingError] = useState<ErrorData>(undefined);
|
|
||||||
const [processingError, setProcessingError] = useState<ErrorData>(undefined);
|
|
||||||
const [cachedTemplates, setCachedTemplates] = useState<IRSForm[]>([]);
|
|
||||||
|
|
||||||
const folders = useMemo(() => {
|
|
||||||
const result = new FolderTree();
|
|
||||||
result.addPath(LocationHead.USER, 0);
|
|
||||||
result.addPath(LocationHead.COMMON, 0);
|
|
||||||
result.addPath(LocationHead.LIBRARY, 0);
|
|
||||||
result.addPath(LocationHead.PROJECTS, 0);
|
|
||||||
items.forEach(item => result.addPath(item.location));
|
|
||||||
return result;
|
|
||||||
}, [items]);
|
|
||||||
|
|
||||||
const applyFilter = useCallback(
|
|
||||||
(filter: ILibraryFilter) => {
|
|
||||||
let result = items;
|
|
||||||
if (!filter.folderMode && filter.head) {
|
|
||||||
result = result.filter(item => item.location.startsWith(filter.head!));
|
|
||||||
}
|
|
||||||
if (filter.folderMode && filter.location) {
|
|
||||||
if (filter.subfolders) {
|
|
||||||
result = result.filter(
|
|
||||||
item => item.location == filter.location || item.location.startsWith(filter.location! + '/')
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
result = result.filter(item => item.location == filter.location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (filter.type) {
|
|
||||||
result = result.filter(item => item.item_type === filter.type);
|
|
||||||
}
|
|
||||||
if (filter.isVisible !== undefined) {
|
|
||||||
result = result.filter(item => filter.isVisible === item.visible);
|
|
||||||
}
|
|
||||||
if (filter.isOwned !== undefined) {
|
|
||||||
result = result.filter(item => filter.isOwned === (item.owner === user?.id));
|
|
||||||
}
|
|
||||||
if (filter.isEditor !== undefined) {
|
|
||||||
result = result.filter(item => filter.isEditor == user?.editor.includes(item.id));
|
|
||||||
}
|
|
||||||
if (filter.filterUser !== undefined) {
|
|
||||||
result = result.filter(item => filter.filterUser === item.owner);
|
|
||||||
}
|
|
||||||
if (!filter.folderMode && filter.path) {
|
|
||||||
result = result.filter(item => matchLibraryItemLocation(item, filter.path!));
|
|
||||||
}
|
|
||||||
if (filter.query) {
|
|
||||||
result = result.filter(item => matchLibraryItem(item, filter.query!));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
[items, user]
|
|
||||||
);
|
|
||||||
|
|
||||||
const retrieveTemplate = useCallback(
|
|
||||||
(templateID: LibraryItemID, callback: (schema: IRSForm) => void) => {
|
|
||||||
const cached = cachedTemplates.find(schema => schema.id == templateID);
|
|
||||||
if (cached) {
|
|
||||||
callback(cached);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
getRSFormDetails(String(templateID), '', {
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: data => {
|
|
||||||
const schema = new RSFormLoader(data).produceRSForm();
|
|
||||||
setCachedTemplates(prev => [...prev, schema]);
|
|
||||||
callback(schema);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[cachedTemplates]
|
|
||||||
);
|
|
||||||
|
|
||||||
const reloadItems = useCallback(
|
|
||||||
(callback?: () => void) => {
|
|
||||||
setItems([]);
|
|
||||||
setLoadingError(undefined);
|
|
||||||
if (user?.is_staff && adminMode) {
|
|
||||||
getAdminLibrary({
|
|
||||||
setLoading: setLoading,
|
|
||||||
showError: true,
|
|
||||||
onError: setLoadingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
setItems(newData);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
getLibrary({
|
|
||||||
setLoading: setLoading,
|
|
||||||
showError: true,
|
|
||||||
onError: setLoadingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
setItems(newData);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[user, adminMode]
|
|
||||||
);
|
|
||||||
|
|
||||||
const reloadTemplates = useCallback(() => {
|
|
||||||
setTemplates([]);
|
|
||||||
getTemplates({
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
showError: true,
|
|
||||||
onSuccess: newData => setTemplates(newData)
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!userLoading) {
|
|
||||||
reloadItems();
|
|
||||||
}
|
|
||||||
}, [reloadItems, userLoading]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
reloadTemplates();
|
|
||||||
}, [reloadTemplates]);
|
|
||||||
|
|
||||||
const localUpdateItem = useCallback(
|
|
||||||
(data: Partial<ILibraryItem>) => {
|
|
||||||
setItems(prev => prev.map(item => (item.id === data.id ? { ...item, ...data } : item)));
|
|
||||||
},
|
|
||||||
[setItems]
|
|
||||||
);
|
|
||||||
|
|
||||||
const localUpdateTimestamp = useCallback(
|
|
||||||
(target: LibraryItemID) => {
|
|
||||||
setItems(prev => prev.map(item => (item.id === target ? { ...item, time_update: Date() } : item)));
|
|
||||||
},
|
|
||||||
[setItems]
|
|
||||||
);
|
|
||||||
|
|
||||||
const createItem = useCallback(
|
|
||||||
(data: ILibraryCreateData, callback?: DataCallback<ILibraryItem>) => {
|
|
||||||
const onSuccess = (newSchema: ILibraryItem) =>
|
|
||||||
reloadItems(() => {
|
|
||||||
callback?.(newSchema);
|
|
||||||
});
|
|
||||||
setProcessingError(undefined);
|
|
||||||
if (data.file) {
|
|
||||||
postRSFormFromFile({
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: onSuccess
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
postCreateLibraryItem({
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: onSuccess
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[reloadItems]
|
|
||||||
);
|
|
||||||
|
|
||||||
const destroyItem = useCallback(
|
|
||||||
(target: LibraryItemID, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
deleteLibraryItem(String(target), {
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () =>
|
|
||||||
reloadItems(() => {
|
|
||||||
callback?.();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[reloadItems]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cloneItem = useCallback(
|
|
||||||
(target: LibraryItemID, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => {
|
|
||||||
if (!user) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
postCloneLibraryItem(String(target), {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newSchema =>
|
|
||||||
reloadItems(() => {
|
|
||||||
callback?.(newSchema);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[reloadItems, user]
|
|
||||||
);
|
|
||||||
|
|
||||||
const renameLocation = useCallback(
|
|
||||||
(data: IRenameLocationData, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchRenameLocation({
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () =>
|
|
||||||
reloadItems(() => {
|
|
||||||
callback?.();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[reloadItems]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LibraryContext
|
|
||||||
value={{
|
|
||||||
items,
|
|
||||||
folders,
|
|
||||||
templates,
|
|
||||||
|
|
||||||
loading,
|
|
||||||
loadingError,
|
|
||||||
setLoadingError,
|
|
||||||
reloadItems,
|
|
||||||
|
|
||||||
processing,
|
|
||||||
processingError,
|
|
||||||
setProcessingError,
|
|
||||||
|
|
||||||
applyFilter,
|
|
||||||
createItem,
|
|
||||||
cloneItem,
|
|
||||||
destroyItem,
|
|
||||||
renameLocation,
|
|
||||||
|
|
||||||
retrieveTemplate,
|
|
||||||
localUpdateItem,
|
|
||||||
localUpdateTimestamp
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</LibraryContext>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,390 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
|
||||||
import {
|
|
||||||
patchLibraryItem,
|
|
||||||
patchSetAccessPolicy,
|
|
||||||
patchSetEditors,
|
|
||||||
patchSetLocation,
|
|
||||||
patchSetOwner
|
|
||||||
} from '@/backend/library';
|
|
||||||
import {
|
|
||||||
patchCreateInput,
|
|
||||||
patchDeleteOperation,
|
|
||||||
patchSetInput,
|
|
||||||
patchUpdateOperation,
|
|
||||||
patchUpdatePositions,
|
|
||||||
postCreateOperation,
|
|
||||||
postExecuteOperation,
|
|
||||||
postRelocateConstituents
|
|
||||||
} from '@/backend/oss';
|
|
||||||
import { type ErrorData } from '@/components/info/InfoError';
|
|
||||||
import { AccessPolicy, ILibraryItem } from '@/models/library';
|
|
||||||
import { ILibraryUpdateData } from '@/models/library';
|
|
||||||
import {
|
|
||||||
ICstRelocateData,
|
|
||||||
IOperationCreateData,
|
|
||||||
IOperationData,
|
|
||||||
IOperationDeleteData,
|
|
||||||
IOperationSchema,
|
|
||||||
IOperationSchemaData,
|
|
||||||
IOperationSetInputData,
|
|
||||||
IOperationUpdateData,
|
|
||||||
IPositionsData,
|
|
||||||
ITargetOperation
|
|
||||||
} from '@/models/oss';
|
|
||||||
import { UserID } from '@/models/user';
|
|
||||||
import { contextOutsideScope } from '@/utils/labels';
|
|
||||||
|
|
||||||
import { useGlobalOss } from './GlobalOssContext';
|
|
||||||
import { useLibrary } from './LibraryContext';
|
|
||||||
|
|
||||||
interface IOssContext {
|
|
||||||
schema?: IOperationSchema;
|
|
||||||
itemID: string;
|
|
||||||
|
|
||||||
loading: boolean;
|
|
||||||
loadingError: ErrorData;
|
|
||||||
processing: boolean;
|
|
||||||
processingError: ErrorData;
|
|
||||||
|
|
||||||
isOwned: boolean;
|
|
||||||
|
|
||||||
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => void;
|
|
||||||
|
|
||||||
setOwner: (newOwner: UserID, callback?: () => void) => void;
|
|
||||||
setAccessPolicy: (newPolicy: AccessPolicy, callback?: () => void) => void;
|
|
||||||
setLocation: (newLocation: string, callback?: () => void) => void;
|
|
||||||
setEditors: (newEditors: UserID[], callback?: () => void) => void;
|
|
||||||
|
|
||||||
savePositions: (data: IPositionsData, callback?: () => void) => void;
|
|
||||||
createOperation: (data: IOperationCreateData, callback?: DataCallback<IOperationData>) => void;
|
|
||||||
deleteOperation: (data: IOperationDeleteData, callback?: () => void) => void;
|
|
||||||
createInput: (data: ITargetOperation, callback?: DataCallback<ILibraryItem>) => void;
|
|
||||||
setInput: (data: IOperationSetInputData, callback?: () => void) => void;
|
|
||||||
updateOperation: (data: IOperationUpdateData, callback?: () => void) => void;
|
|
||||||
executeOperation: (data: ITargetOperation, callback?: () => void) => void;
|
|
||||||
relocateConstituents: (data: ICstRelocateData, callback?: () => void) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const OssContext = createContext<IOssContext | null>(null);
|
|
||||||
export const useOSS = () => {
|
|
||||||
const context = useContext(OssContext);
|
|
||||||
if (context === null) {
|
|
||||||
throw new Error(contextOutsideScope('useOSS', 'OssState'));
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface OssStateProps {
|
|
||||||
itemID: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateProps>) => {
|
|
||||||
const library = useLibrary();
|
|
||||||
const ossData = useGlobalOss();
|
|
||||||
const { user } = useAuth();
|
|
||||||
const [processing, setProcessing] = useState(false);
|
|
||||||
const [processingError, setProcessingError] = useState<ErrorData>(undefined);
|
|
||||||
|
|
||||||
const isOwned = useMemo(() => {
|
|
||||||
return user?.id === ossData.schema?.owner || false;
|
|
||||||
}, [user, ossData.schema?.owner]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
ossData.setID(itemID);
|
|
||||||
}, [itemID, ossData]);
|
|
||||||
|
|
||||||
const update = useCallback(
|
|
||||||
(data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchLibraryItem(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
const fullData: IOperationSchemaData = Object.assign(ossData.schema!, newData);
|
|
||||||
ossData.setData(fullData);
|
|
||||||
library.localUpdateItem(newData);
|
|
||||||
callback?.(newData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, library, ossData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setOwner = useCallback(
|
|
||||||
(newOwner: UserID, callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetOwner(itemID, {
|
|
||||||
data: { user: newOwner },
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
ossData.partialUpdate({ owner: newOwner });
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, ossData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setAccessPolicy = useCallback(
|
|
||||||
(newPolicy: AccessPolicy, callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetAccessPolicy(itemID, {
|
|
||||||
data: {
|
|
||||||
access_policy: newPolicy
|
|
||||||
},
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
ossData.partialUpdate({ access_policy: newPolicy });
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, ossData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setLocation = useCallback(
|
|
||||||
(newLocation: string, callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetLocation(itemID, {
|
|
||||||
data: {
|
|
||||||
location: newLocation
|
|
||||||
},
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
ossData.partialUpdate({ location: newLocation });
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, ossData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setEditors = useCallback(
|
|
||||||
(newEditors: UserID[], callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetEditors(itemID, {
|
|
||||||
data: {
|
|
||||||
users: newEditors
|
|
||||||
},
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
ossData.partialUpdate({ editors: newEditors });
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, ossData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const savePositions = useCallback(
|
|
||||||
(data: IPositionsData, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchUpdatePositions(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
library.localUpdateTimestamp(Number(itemID));
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const createOperation = useCallback(
|
|
||||||
(data: IOperationCreateData, callback?: DataCallback<IOperationData>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
postCreateOperation(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
ossData.setData(newData.oss);
|
|
||||||
library.localUpdateTimestamp(newData.oss.id);
|
|
||||||
callback?.(newData.new_operation);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, library, ossData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const deleteOperation = useCallback(
|
|
||||||
(data: IOperationDeleteData, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchDeleteOperation(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
ossData.setData(newData);
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, library, ossData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const createInput = useCallback(
|
|
||||||
(data: ITargetOperation, callback?: DataCallback<ILibraryItem>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchCreateInput(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
ossData.setData(newData.oss);
|
|
||||||
library.reloadItems(() => {
|
|
||||||
callback?.(newData.new_schema);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, library, ossData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setInput = useCallback(
|
|
||||||
(data: IOperationSetInputData, callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetInput(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
ossData.setData(newData);
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, ossData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateOperation = useCallback(
|
|
||||||
(data: IOperationUpdateData, callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchUpdateOperation(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
ossData.setData(newData);
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, library, ossData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const executeOperation = useCallback(
|
|
||||||
(data: ITargetOperation, callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
postExecuteOperation(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
ossData.setData(newData);
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, library, ossData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const relocateConstituents = useCallback(
|
|
||||||
(data: ICstRelocateData, callback?: () => void) => {
|
|
||||||
if (!ossData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
postRelocateConstituents({
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
ossData.reload();
|
|
||||||
library.reloadItems(callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[library, ossData]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<OssContext
|
|
||||||
value={{
|
|
||||||
schema: ossData.schema,
|
|
||||||
itemID,
|
|
||||||
loading: ossData.loading,
|
|
||||||
loadingError: ossData.loadingError,
|
|
||||||
processing,
|
|
||||||
processingError,
|
|
||||||
isOwned,
|
|
||||||
update,
|
|
||||||
|
|
||||||
setOwner,
|
|
||||||
setEditors,
|
|
||||||
setAccessPolicy,
|
|
||||||
setLocation,
|
|
||||||
|
|
||||||
savePositions,
|
|
||||||
createOperation,
|
|
||||||
deleteOperation,
|
|
||||||
createInput,
|
|
||||||
setInput,
|
|
||||||
updateOperation,
|
|
||||||
executeOperation,
|
|
||||||
relocateConstituents
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</OssContext>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,592 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
|
||||||
import {
|
|
||||||
patchLibraryItem,
|
|
||||||
patchSetAccessPolicy,
|
|
||||||
patchSetEditors,
|
|
||||||
patchSetLocation,
|
|
||||||
patchSetOwner,
|
|
||||||
postCreateVersion
|
|
||||||
} from '@/backend/library';
|
|
||||||
import { postFindPredecessor } from '@/backend/oss';
|
|
||||||
import {
|
|
||||||
getTRSFile,
|
|
||||||
patchDeleteConstituenta,
|
|
||||||
patchInlineSynthesis,
|
|
||||||
patchMoveConstituenta,
|
|
||||||
patchProduceStructure,
|
|
||||||
patchRenameConstituenta,
|
|
||||||
patchResetAliases,
|
|
||||||
patchRestoreOrder,
|
|
||||||
patchSubstituteConstituents,
|
|
||||||
patchUpdateConstituenta,
|
|
||||||
patchUploadTRS,
|
|
||||||
postCreateConstituenta
|
|
||||||
} from '@/backend/rsforms';
|
|
||||||
import { deleteVersion, patchRestoreVersion, patchVersion } from '@/backend/versions';
|
|
||||||
import { type ErrorData } from '@/components/info/InfoError';
|
|
||||||
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
|
||||||
import { AccessPolicy, ILibraryItem, IVersionData, VersionID } from '@/models/library';
|
|
||||||
import { ILibraryUpdateData } from '@/models/library';
|
|
||||||
import { ICstSubstituteData } from '@/models/oss';
|
|
||||||
import {
|
|
||||||
ConstituentaID,
|
|
||||||
IConstituentaList,
|
|
||||||
IConstituentaMeta,
|
|
||||||
IConstituentaReference,
|
|
||||||
ICstCreateData,
|
|
||||||
ICstMovetoData,
|
|
||||||
ICstRenameData,
|
|
||||||
ICstUpdateData,
|
|
||||||
IInlineSynthesisData,
|
|
||||||
IRSForm,
|
|
||||||
IRSFormData,
|
|
||||||
IRSFormUploadData,
|
|
||||||
ITargetCst
|
|
||||||
} from '@/models/rsform';
|
|
||||||
import { UserID } from '@/models/user';
|
|
||||||
import { contextOutsideScope } from '@/utils/labels';
|
|
||||||
|
|
||||||
import { useGlobalOss } from './GlobalOssContext';
|
|
||||||
import { useLibrary } from './LibraryContext';
|
|
||||||
|
|
||||||
interface IRSFormContext {
|
|
||||||
schema?: IRSForm;
|
|
||||||
itemID: string;
|
|
||||||
versionID?: string;
|
|
||||||
|
|
||||||
loading: boolean;
|
|
||||||
errorLoading: ErrorData;
|
|
||||||
processing: boolean;
|
|
||||||
processingError: ErrorData;
|
|
||||||
|
|
||||||
isArchive: boolean;
|
|
||||||
isOwned: boolean;
|
|
||||||
|
|
||||||
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => void;
|
|
||||||
download: (callback: DataCallback<Blob>) => void;
|
|
||||||
upload: (data: IRSFormUploadData, callback: () => void) => void;
|
|
||||||
|
|
||||||
setOwner: (newOwner: UserID, callback?: () => void) => void;
|
|
||||||
setAccessPolicy: (newPolicy: AccessPolicy, callback?: () => void) => void;
|
|
||||||
setLocation: (newLocation: string, callback?: () => void) => void;
|
|
||||||
setEditors: (newEditors: UserID[], callback?: () => void) => void;
|
|
||||||
|
|
||||||
resetAliases: (callback: () => void) => void;
|
|
||||||
restoreOrder: (callback: () => void) => void;
|
|
||||||
produceStructure: (data: ITargetCst, callback?: DataCallback<ConstituentaID[]>) => void;
|
|
||||||
inlineSynthesis: (data: IInlineSynthesisData, callback?: DataCallback<IRSFormData>) => void;
|
|
||||||
|
|
||||||
cstCreate: (data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => void;
|
|
||||||
cstRename: (data: ICstRenameData, callback?: DataCallback<IConstituentaMeta>) => void;
|
|
||||||
cstSubstitute: (data: ICstSubstituteData, callback?: () => void) => void;
|
|
||||||
cstUpdate: (data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => void;
|
|
||||||
cstDelete: (data: IConstituentaList, callback?: () => void) => void;
|
|
||||||
cstMoveTo: (data: ICstMovetoData, callback?: () => void) => void;
|
|
||||||
findPredecessor: (data: ITargetCst, callback: (reference: IConstituentaReference) => void) => void;
|
|
||||||
|
|
||||||
versionCreate: (data: IVersionData, callback?: (version: VersionID) => void) => void;
|
|
||||||
versionUpdate: (target: VersionID, data: IVersionData, callback?: () => void) => void;
|
|
||||||
versionDelete: (target: VersionID, callback?: () => void) => void;
|
|
||||||
versionRestore: (target: string, callback?: () => void) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RSFormContext = createContext<IRSFormContext | null>(null);
|
|
||||||
export const useRSForm = () => {
|
|
||||||
const context = useContext(RSFormContext);
|
|
||||||
if (context === null) {
|
|
||||||
throw new Error(contextOutsideScope('useRSForm', 'RSFormState'));
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface RSFormStateProps {
|
|
||||||
itemID: string;
|
|
||||||
versionID?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChildren<RSFormStateProps>) => {
|
|
||||||
const library = useLibrary();
|
|
||||||
const oss = useGlobalOss();
|
|
||||||
const { user } = useAuth();
|
|
||||||
const rsData = useRSFormDetails({ target: itemID, version: versionID });
|
|
||||||
const [processing, setProcessing] = useState(false);
|
|
||||||
const [processingError, setProcessingError] = useState<ErrorData>(undefined);
|
|
||||||
|
|
||||||
const isOwned = useMemo(() => {
|
|
||||||
return user?.id === rsData.schema?.owner || false;
|
|
||||||
}, [user, rsData.schema?.owner]);
|
|
||||||
|
|
||||||
const isArchive = useMemo(() => !!versionID, [versionID]);
|
|
||||||
|
|
||||||
const update = useCallback(
|
|
||||||
(data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => {
|
|
||||||
if (!rsData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchLibraryItem(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(Object.assign(rsData.schema!, newData));
|
|
||||||
library.localUpdateItem(newData);
|
|
||||||
oss.invalidateItem(newData.id);
|
|
||||||
callback?.(newData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const upload = useCallback(
|
|
||||||
(data: IRSFormUploadData, callback?: () => void) => {
|
|
||||||
if (!rsData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchUploadTRS(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateItem(newData);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setOwner = useCallback(
|
|
||||||
(newOwner: UserID, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetOwner(itemID, {
|
|
||||||
data: {
|
|
||||||
user: newOwner
|
|
||||||
},
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
rsData.partialUpdate({ owner: newOwner });
|
|
||||||
library.localUpdateItem({ id: Number(itemID), owner: newOwner });
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setAccessPolicy = useCallback(
|
|
||||||
(newPolicy: AccessPolicy, callback?: () => void) => {
|
|
||||||
if (!rsData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetAccessPolicy(itemID, {
|
|
||||||
data: {
|
|
||||||
access_policy: newPolicy
|
|
||||||
},
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
rsData.partialUpdate({ access_policy: newPolicy });
|
|
||||||
library.localUpdateItem({ id: Number(itemID), access_policy: newPolicy });
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setLocation = useCallback(
|
|
||||||
(newLocation: string, callback?: () => void) => {
|
|
||||||
if (!rsData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetLocation(itemID, {
|
|
||||||
data: {
|
|
||||||
location: newLocation
|
|
||||||
},
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
rsData.partialUpdate({ location: newLocation });
|
|
||||||
library.localUpdateItem({ id: Number(itemID), location: newLocation });
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const setEditors = useCallback(
|
|
||||||
(newEditors: UserID[], callback?: () => void) => {
|
|
||||||
if (!rsData.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSetEditors(itemID, {
|
|
||||||
data: {
|
|
||||||
users: newEditors
|
|
||||||
},
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
rsData.partialUpdate({ editors: newEditors });
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const resetAliases = useCallback(
|
|
||||||
(callback?: () => void) => {
|
|
||||||
if (!rsData.schema || !user) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchResetAliases(itemID, {
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateTimestamp(newData.id);
|
|
||||||
oss.invalidateItem(newData.id);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, user, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const restoreOrder = useCallback(
|
|
||||||
(callback?: () => void) => {
|
|
||||||
if (!rsData.schema || !user) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchRestoreOrder(itemID, {
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateTimestamp(newData.id);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, user, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const produceStructure = useCallback(
|
|
||||||
(data: ITargetCst, callback?: DataCallback<ConstituentaID[]>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchProduceStructure(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData.schema);
|
|
||||||
library.localUpdateTimestamp(newData.schema.id);
|
|
||||||
oss.invalidateItem(newData.schema.id);
|
|
||||||
callback?.(newData.cst_list);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[rsData, itemID, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const download = useCallback(
|
|
||||||
(callback: DataCallback<Blob>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
getTRSFile(itemID, String(rsData.schema?.version ?? ''), {
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: callback
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cstCreate = useCallback(
|
|
||||||
(data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
postCreateConstituenta(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData.schema);
|
|
||||||
library.localUpdateTimestamp(newData.schema.id);
|
|
||||||
oss.invalidateItem(newData.schema.id);
|
|
||||||
callback?.(newData.new_cst);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cstDelete = useCallback(
|
|
||||||
(data: IConstituentaList, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchDeleteConstituenta(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateTimestamp(newData.id);
|
|
||||||
oss.invalidateItem(newData.id);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cstUpdate = useCallback(
|
|
||||||
(data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchUpdateConstituenta(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData =>
|
|
||||||
rsData.reload(setProcessing, () => {
|
|
||||||
library.localUpdateTimestamp(Number(itemID));
|
|
||||||
oss.invalidateItem(Number(itemID));
|
|
||||||
callback?.(newData);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cstRename = useCallback(
|
|
||||||
(data: ICstRenameData, callback?: DataCallback<IConstituentaMeta>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchRenameConstituenta(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData.schema);
|
|
||||||
library.localUpdateTimestamp(newData.schema.id);
|
|
||||||
oss.invalidateItem(newData.schema.id);
|
|
||||||
callback?.(newData.new_cst);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[rsData, itemID, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cstSubstitute = useCallback(
|
|
||||||
(data: ICstSubstituteData, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchSubstituteConstituents(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateTimestamp(newData.id);
|
|
||||||
oss.invalidateItem(newData.id);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[rsData, itemID, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cstMoveTo = useCallback(
|
|
||||||
(data: ICstMovetoData, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchMoveConstituenta(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateTimestamp(Number(itemID));
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const versionCreate = useCallback(
|
|
||||||
(data: IVersionData, callback?: (version: number) => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
postCreateVersion(itemID, {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData.schema);
|
|
||||||
library.localUpdateTimestamp(Number(itemID));
|
|
||||||
callback?.(newData.version);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[itemID, rsData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const findPredecessor = useCallback((data: ITargetCst, callback: (reference: IConstituentaReference) => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
postFindPredecessor({
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: callback
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const versionUpdate = useCallback(
|
|
||||||
(target: number, data: IVersionData, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchVersion(String(target), {
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
const newVersions = rsData.schema!.versions.map(prev => {
|
|
||||||
if (prev.id === target) {
|
|
||||||
prev.description = data.description;
|
|
||||||
prev.version = data.version;
|
|
||||||
return prev;
|
|
||||||
} else {
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
rsData.partialUpdate({ versions: newVersions });
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[rsData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const versionDelete = useCallback(
|
|
||||||
(target: number, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
deleteVersion(String(target), {
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: () => {
|
|
||||||
const newVersions = rsData.schema!.versions.filter(prev => prev.id !== target);
|
|
||||||
rsData.partialUpdate({ versions: newVersions });
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[rsData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const versionRestore = useCallback(
|
|
||||||
(target: string, callback?: () => void) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchRestoreVersion(target, {
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateItem(newData);
|
|
||||||
callback?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[rsData, library]
|
|
||||||
);
|
|
||||||
|
|
||||||
const inlineSynthesis = useCallback(
|
|
||||||
(data: IInlineSynthesisData, callback?: DataCallback<IRSFormData>) => {
|
|
||||||
setProcessingError(undefined);
|
|
||||||
patchInlineSynthesis({
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setProcessing,
|
|
||||||
onError: setProcessingError,
|
|
||||||
onSuccess: newData => {
|
|
||||||
rsData.setSchema(newData);
|
|
||||||
library.localUpdateTimestamp(newData.id);
|
|
||||||
oss.invalidateItem(newData.id);
|
|
||||||
callback?.(newData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[rsData, library, oss]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RSFormContext
|
|
||||||
value={{
|
|
||||||
schema: rsData.schema,
|
|
||||||
itemID,
|
|
||||||
versionID,
|
|
||||||
loading: rsData.loading,
|
|
||||||
errorLoading: rsData.error,
|
|
||||||
processing,
|
|
||||||
processingError,
|
|
||||||
isOwned,
|
|
||||||
isArchive,
|
|
||||||
update,
|
|
||||||
download,
|
|
||||||
upload,
|
|
||||||
restoreOrder,
|
|
||||||
resetAliases,
|
|
||||||
produceStructure,
|
|
||||||
inlineSynthesis,
|
|
||||||
setOwner,
|
|
||||||
setEditors,
|
|
||||||
setAccessPolicy,
|
|
||||||
setLocation,
|
|
||||||
|
|
||||||
cstUpdate,
|
|
||||||
cstCreate,
|
|
||||||
cstRename,
|
|
||||||
cstSubstitute,
|
|
||||||
cstDelete,
|
|
||||||
cstMoveTo,
|
|
||||||
findPredecessor,
|
|
||||||
|
|
||||||
versionCreate,
|
|
||||||
versionUpdate,
|
|
||||||
versionDelete,
|
|
||||||
versionRestore
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</RSFormContext>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -3,12 +3,12 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { useLibrary } from '@/backend/library/useLibrary';
|
||||||
import { IconReset } from '@/components/Icons';
|
import { IconReset } from '@/components/Icons';
|
||||||
import PickSchema from '@/components/select/PickSchema';
|
import PickSchema from '@/components/select/PickSchema';
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import Modal from '@/components/ui/Modal';
|
import Modal from '@/components/ui/Modal';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
|
||||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
import { IOperation, IOperationSchema, OperationID } from '@/models/oss';
|
import { IOperation, IOperationSchema, OperationID } from '@/models/oss';
|
||||||
import { sortItemsForOSS } from '@/models/ossAPI';
|
import { sortItemsForOSS } from '@/models/ossAPI';
|
||||||
|
@ -23,8 +23,8 @@ export interface DlgChangeInputSchemaProps {
|
||||||
function DlgChangeInputSchema() {
|
function DlgChangeInputSchema() {
|
||||||
const { oss, target, onSubmit } = useDialogsStore(state => state.props as DlgChangeInputSchemaProps);
|
const { oss, target, onSubmit } = useDialogsStore(state => state.props as DlgChangeInputSchemaProps);
|
||||||
const [selected, setSelected] = useState<LibraryItemID | undefined>(target.result ?? undefined);
|
const [selected, setSelected] = useState<LibraryItemID | undefined>(target.result ?? undefined);
|
||||||
const library = useLibrary();
|
const { items } = useLibrary();
|
||||||
const sortedItems = sortItemsForOSS(oss, library.items);
|
const sortedItems = sortItemsForOSS(oss, items);
|
||||||
const isValid = target.result !== selected;
|
const isValid = target.result !== selected;
|
||||||
|
|
||||||
function baseFilter(item: ILibraryItem) {
|
function baseFilter(item: ILibraryItem) {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuth } from '@/backend/auth/useAuth';
|
||||||
|
import { IRSFormCloneDTO } from '@/backend/library/api';
|
||||||
|
import { useCloneItem } from '@/backend/library/useCloneItem';
|
||||||
import { VisibilityIcon } from '@/components/DomainIcons';
|
import { VisibilityIcon } from '@/components/DomainIcons';
|
||||||
import SelectAccessPolicy from '@/components/select/SelectAccessPolicy';
|
import SelectAccessPolicy from '@/components/select/SelectAccessPolicy';
|
||||||
import SelectLocationContext from '@/components/select/SelectLocationContext';
|
import SelectLocationContext from '@/components/select/SelectLocationContext';
|
||||||
|
@ -16,11 +18,10 @@ import MiniButton from '@/components/ui/MiniButton';
|
||||||
import Modal from '@/components/ui/Modal';
|
import Modal from '@/components/ui/Modal';
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
|
||||||
import { AccessPolicy, ILibraryItem, LocationHead } from '@/models/library';
|
import { AccessPolicy, ILibraryItem, LocationHead } from '@/models/library';
|
||||||
import { cloneTitle, combineLocation, validateLocation } from '@/models/libraryAPI';
|
import { cloneTitle, combineLocation, validateLocation } from '@/models/libraryAPI';
|
||||||
import { ConstituentaID, IRSFormCloneData } from '@/models/rsform';
|
import { ConstituentaID } from '@/models/rsform';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { information } from '@/utils/labels';
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ function DlgCloneLibraryItem() {
|
||||||
const [body, setBody] = useState(initialLocation.substring(3));
|
const [body, setBody] = useState(initialLocation.substring(3));
|
||||||
const location = combineLocation(head, body);
|
const location = combineLocation(head, body);
|
||||||
|
|
||||||
const { cloneItem, folders } = useLibrary();
|
const { cloneItem } = useCloneItem();
|
||||||
|
|
||||||
const canSubmit = title !== '' && alias !== '' && validateLocation(location);
|
const canSubmit = title !== '' && alias !== '' && validateLocation(location);
|
||||||
|
|
||||||
|
@ -60,7 +61,8 @@ function DlgCloneLibraryItem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
const data: IRSFormCloneData = {
|
const data: IRSFormCloneDTO = {
|
||||||
|
id: base.id,
|
||||||
item_type: base.item_type,
|
item_type: base.item_type,
|
||||||
title: title,
|
title: title,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
|
@ -73,7 +75,7 @@ function DlgCloneLibraryItem() {
|
||||||
if (onlySelected) {
|
if (onlySelected) {
|
||||||
data.items = selected;
|
data.items = selected;
|
||||||
}
|
}
|
||||||
cloneItem(base.id, data, newSchema => {
|
cloneItem(data, newSchema => {
|
||||||
toast.success(information.cloneComplete(newSchema.alias));
|
toast.success(information.cloneComplete(newSchema.alias));
|
||||||
router.push(urls.schema(newSchema.id));
|
router.push(urls.schema(newSchema.id));
|
||||||
});
|
});
|
||||||
|
@ -128,7 +130,7 @@ function DlgCloneLibraryItem() {
|
||||||
excluded={!user?.is_staff ? [LocationHead.LIBRARY] : []}
|
excluded={!user?.is_staff ? [LocationHead.LIBRARY] : []}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SelectLocationContext folderTree={folders} value={location} onChange={handleSelectLocation} />
|
<SelectLocationContext value={location} onChange={handleSelectLocation} />
|
||||||
<TextArea
|
<TextArea
|
||||||
id='dlg_cst_body'
|
id='dlg_cst_body'
|
||||||
label='Путь'
|
label='Путь'
|
||||||
|
|
|
@ -2,18 +2,19 @@
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { ICstCreateDTO } from '@/backend/rsform/api';
|
||||||
import Modal from '@/components/ui/Modal';
|
import Modal from '@/components/ui/Modal';
|
||||||
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
||||||
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
import { CstType, IRSForm } from '@/models/rsform';
|
||||||
import { generateAlias } from '@/models/rsformAPI';
|
import { generateAlias } from '@/models/rsformAPI';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import FormCreateCst from './FormCreateCst';
|
import FormCreateCst from './FormCreateCst';
|
||||||
|
|
||||||
export interface DlgCreateCstProps {
|
export interface DlgCreateCstProps {
|
||||||
initial?: ICstCreateData;
|
initial?: ICstCreateDTO;
|
||||||
schema: IRSForm;
|
schema: IRSForm;
|
||||||
onCreate: (data: ICstCreateData) => void;
|
onCreate: (data: ICstCreateDTO) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgCreateCst() {
|
function DlgCreateCst() {
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { ICstCreateDTO } from '@/backend/rsform/api';
|
||||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||||
import RSInput from '@/components/RSInput';
|
import RSInput from '@/components/RSInput';
|
||||||
import SelectSingle from '@/components/ui/SelectSingle';
|
import SelectSingle from '@/components/ui/SelectSingle';
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
import { CstType, IRSForm } from '@/models/rsform';
|
||||||
import { generateAlias, isBaseSet, isBasicConcept, isFunctional, validateNewAlias } from '@/models/rsformAPI';
|
import { generateAlias, isBaseSet, isBasicConcept, isFunctional, validateNewAlias } from '@/models/rsformAPI';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { labelCstType } from '@/utils/labels';
|
import { labelCstType } from '@/utils/labels';
|
||||||
|
@ -17,9 +18,9 @@ import { SelectorCstType } from '@/utils/selectors';
|
||||||
|
|
||||||
interface FormCreateCstProps {
|
interface FormCreateCstProps {
|
||||||
schema: IRSForm;
|
schema: IRSForm;
|
||||||
state: ICstCreateData;
|
state: ICstCreateDTO;
|
||||||
|
|
||||||
partialUpdate: React.Dispatch<Partial<ICstCreateData>>;
|
partialUpdate: React.Dispatch<Partial<ICstCreateDTO>>;
|
||||||
setValidated?: React.Dispatch<React.SetStateAction<boolean>>;
|
setValidated?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user