diff --git a/rsconcept/frontend/playwright.config.ts b/rsconcept/frontend/playwright.config.ts index 8bd8ad25..bd160f93 100644 --- a/rsconcept/frontend/playwright.config.ts +++ b/rsconcept/frontend/playwright.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ testDir: 'tests', forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, - reporter: 'html', + reporter: 'list', projects: [ { name: 'Desktop Chrome', diff --git a/rsconcept/frontend/src/app/Navigation/UserButton.tsx b/rsconcept/frontend/src/app/Navigation/UserButton.tsx index ff13baf0..bd00f10e 100644 --- a/rsconcept/frontend/src/app/Navigation/UserButton.tsx +++ b/rsconcept/frontend/src/app/Navigation/UserButton.tsx @@ -10,9 +10,9 @@ interface UserButtonProps { } function UserButton({ onLogin, onClickUser }: UserButtonProps) { - const { user } = useAuthSuspense(); + const { user, isAnonymous } = useAuthSuspense(); const adminMode = usePreferencesStore(state => state.adminMode); - if (!user) { + if (isAnonymous) { return ( state.darkMode); @@ -77,7 +77,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) { return ( } onClick={navigateProfile} @@ -94,7 +94,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) { title='Отображение иконок подсказок' onClick={toggleShowHelp} /> - {user?.is_staff ? ( + {user.is_staff ? ( : } @@ -102,7 +102,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) { onClick={toggleAdminMode} /> ) : null} - {user?.is_staff ? ( + {user.is_staff ? ( } @@ -110,7 +110,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) { onClick={gotoRestApi} /> ) : null} - {user?.is_staff ? ( + {user.is_staff ? ( } @@ -124,7 +124,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) { onClick={gotoIcons} /> ) : null} - {user?.is_staff ? ( + {user.is_staff ? ( } diff --git a/rsconcept/frontend/src/backend/apiTransport.ts b/rsconcept/frontend/src/backend/apiTransport.ts index 6916e3c3..12b8b89a 100644 --- a/rsconcept/frontend/src/backend/apiTransport.ts +++ b/rsconcept/frontend/src/backend/apiTransport.ts @@ -1,115 +1,132 @@ /** - * Module: generic API for backend REST communications. + * Module: generic API for backend REST communications using axios library. */ +import axios from 'axios'; import { AxiosError, AxiosRequestConfig } from 'axios'; import { toast } from 'react-toastify'; -import { ErrorData } from '@/components/info/InfoError'; +import { buildConstants } from '@/utils/buildConstants'; import { extractErrorMessage } from '@/utils/utils'; -import { axiosInstance } from './axiosInstance'; +const defaultOptions = { + xsrfCookieName: 'csrftoken', + xsrfHeaderName: 'x-csrftoken', + baseURL: `${buildConstants.backend}`, + withCredentials: true +}; + +const axiosInstance = axios.create(defaultOptions); +axiosInstance.interceptors.request.use(config => { + const token = document.cookie + .split('; ') + .find(row => row.startsWith('csrftoken=')) + ?.split('=')[1]; + if (token) { + config.headers['x-csrftoken'] = token; + } + return config; +}); // ================ Data transfer types ================ export type DataCallback = (data: ResponseData) => void; export interface IFrontRequest { data?: RequestData; - onSuccess?: DataCallback; - onError?: (error: ErrorData) => void; - setLoading?: (loading: boolean) => void; - showError?: boolean; + successMessage?: string | ((data: ResponseData) => string); } -export interface FrontPush extends IFrontRequest { - data: DataType; -} -export interface FrontPull extends IFrontRequest { - onSuccess: DataCallback; -} - -export interface FrontExchange extends IFrontRequest { - data: RequestData; - onSuccess: DataCallback; -} - -export interface FrontAction extends IFrontRequest {} - export interface IAxiosRequest { endpoint: string; - request: IFrontRequest; + request?: IFrontRequest; options?: AxiosRequestConfig; } +export interface IAxiosGetRequest { + endpoint: string; + options?: AxiosRequestConfig; + signal?: AbortSignal; +} + // ================ Transport API calls ================ -export function AxiosGet({ endpoint, request, options }: IAxiosRequest) { - request.setLoading?.(true); - axiosInstance +export function axiosGet({ endpoint, options }: IAxiosGetRequest) { + return axiosInstance .get(endpoint, options) - .then(response => { - request.setLoading?.(false); - request.onSuccess?.(response.data); - }) + .then(response => response.data) .catch((error: Error | AxiosError) => { - request.setLoading?.(false); - if (request.showError) toast.error(extractErrorMessage(error)); - request.onError?.(error); + toast.error(extractErrorMessage(error)); + console.error(error); + throw error; }); } -export function AxiosPost({ +export function axiosPost({ endpoint, request, options }: IAxiosRequest) { - request.setLoading?.(true); - axiosInstance - .post(endpoint, request.data, options) + return axiosInstance + .post(endpoint, request?.data, options) .then(response => { - request.setLoading?.(false); - request.onSuccess?.(response.data); - }) - .catch((error: Error | AxiosError) => { - request.setLoading?.(false); - if (request.showError) toast.error(extractErrorMessage(error)); - request.onError?.(error); - }); -} - -export function AxiosDelete({ - endpoint, - request, - options -}: IAxiosRequest) { - request.setLoading?.(true); - axiosInstance - .delete(endpoint, options) - .then(response => { - request.setLoading?.(false); - request.onSuccess?.(response.data); - }) - .catch((error: Error | AxiosError) => { - request.setLoading?.(false); - if (request.showError) toast.error(extractErrorMessage(error)); - request.onError?.(error); - }); -} - -export function AxiosPatch({ - endpoint, - request, - options -}: IAxiosRequest) { - request.setLoading?.(true); - axiosInstance - .patch(endpoint, request.data, options) - .then(response => { - request.setLoading?.(false); - request.onSuccess?.(response.data); + if (request?.successMessage) { + if (typeof request.successMessage === 'string') { + toast.success(request.successMessage); + } else { + toast.success(request.successMessage(response.data)); + } + } return response.data; }) .catch((error: Error | AxiosError) => { - request.setLoading?.(false); - if (request.showError) toast.error(extractErrorMessage(error)); - request.onError?.(error); + toast.error(extractErrorMessage(error)); + console.error(error); + throw error; + }); +} + +export function axiosDelete({ + endpoint, + request, + options +}: IAxiosRequest) { + return axiosInstance + .delete(endpoint, options) + .then(response => { + if (request?.successMessage) { + if (typeof request.successMessage === 'string') { + toast.success(request.successMessage); + } else { + toast.success(request.successMessage(response.data)); + } + } + return response.data; + }) + .catch((error: Error | AxiosError) => { + toast.error(extractErrorMessage(error)); + console.error(error); + throw error; + }); +} + +export function axiosPatch({ + endpoint, + request, + options +}: IAxiosRequest) { + return axiosInstance + .patch(endpoint, request?.data, options) + .then(response => { + if (request?.successMessage) { + if (typeof request.successMessage === 'string') { + toast.success(request.successMessage); + } else { + toast.success(request.successMessage(response.data)); + } + } + return response.data; + }) + .catch((error: Error | AxiosError) => { + toast.error(extractErrorMessage(error)); + console.error(error); + throw error; }); } diff --git a/rsconcept/frontend/src/backend/auth/api.ts b/rsconcept/frontend/src/backend/auth/api.ts index 340d4e8a..c492cae6 100644 --- a/rsconcept/frontend/src/backend/auth/api.ts +++ b/rsconcept/frontend/src/backend/auth/api.ts @@ -1,8 +1,10 @@ import { queryOptions } from '@tanstack/react-query'; -import { axiosInstance } from '@/backend/axiosInstance'; import { DELAYS } from '@/backend/configuration'; import { ICurrentUser } from '@/models/user'; +import { information } from '@/utils/labels'; + +import { axiosGet, axiosPost } from '../apiTransport'; /** * Represents login data, used to authenticate users. @@ -53,19 +55,41 @@ export const authApi = { queryKey: [authApi.baseKey, 'user'], staleTime: DELAYS.staleLong, queryFn: meta => - axiosInstance - .get('/users/api/auth', { - signal: meta.signal - }) - .then(response => (response.data.id === null ? null : response.data)), - placeholderData: null + axiosGet({ + endpoint: '/users/api/auth', + options: { signal: meta.signal } + }) }); }, - logout: () => axiosInstance.post('/users/api/logout'), - login: (data: IUserLoginDTO) => axiosInstance.post('/users/api/login', data), - changePassword: (data: IChangePasswordDTO) => axiosInstance.post('/users/api/change-password', data), - requestPasswordReset: (data: IRequestPasswordDTO) => axiosInstance.post('/users/api/password-reset', data), - validatePasswordToken: (data: IPasswordTokenDTO) => axiosInstance.post('/users/api/password-reset/validate', data), - resetPassword: (data: IResetPasswordDTO) => axiosInstance.post('/users/api/password-reset/confirm', data) + logout: () => axiosPost({ endpoint: '/users/api/logout' }), + + login: (data: IUserLoginDTO) => + axiosPost({ + endpoint: '/users/api/login', + request: { data: data } + }), + changePassword: (data: IChangePasswordDTO) => + axiosPost({ + endpoint: '/users/api/change-password', + request: { + data: data, + successMessage: information.changesSaved + } + }), + requestPasswordReset: (data: IRequestPasswordDTO) => + axiosPost({ + endpoint: '/users/api/password-reset', + request: { data: data } + }), + validatePasswordToken: (data: IPasswordTokenDTO) => + axiosPost({ + endpoint: '/users/api/password-reset/validate', + request: { data: data } + }), + resetPassword: (data: IResetPasswordDTO) => + axiosPost({ + endpoint: '/users/api/password-reset/confirm', + request: { data: data } + }) }; diff --git a/rsconcept/frontend/src/backend/auth/useAuth.tsx b/rsconcept/frontend/src/backend/auth/useAuth.tsx index 14f86f2e..d04d215b 100644 --- a/rsconcept/frontend/src/backend/auth/useAuth.tsx +++ b/rsconcept/frontend/src/backend/auth/useAuth.tsx @@ -10,12 +10,12 @@ export function useAuth() { } = useQuery({ ...authApi.getAuthQueryOptions() }); - return { user, isLoading, error }; + return { user, isLoading, isAnonymous: user?.id === null || user === undefined, error }; } export function useAuthSuspense() { const { data: user } = useSuspenseQuery({ ...authApi.getAuthQueryOptions() }); - return { user }; + return { user, isAnonymous: user.id === null }; } diff --git a/rsconcept/frontend/src/backend/axiosInstance.ts b/rsconcept/frontend/src/backend/axiosInstance.ts deleted file mode 100644 index 5bde1a68..00000000 --- a/rsconcept/frontend/src/backend/axiosInstance.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Module: communication setup. - */ -import axios from 'axios'; - -import { buildConstants } from '@/utils/buildConstants'; - -const defaultOptions = { - xsrfCookieName: 'csrftoken', - xsrfHeaderName: 'x-csrftoken', - baseURL: `${buildConstants.backend}`, - withCredentials: true -}; - -export const axiosInstance = axios.create(defaultOptions); -axiosInstance.interceptors.request.use(config => { - const token = document.cookie - .split('; ') - .find(row => row.startsWith('csrftoken=')) - ?.split('=')[1]; - if (token) { - config.headers['x-csrftoken'] = token; - } - return config; -}); diff --git a/rsconcept/frontend/src/backend/cctext/api.ts b/rsconcept/frontend/src/backend/cctext/api.ts index b968ed91..b2434d09 100644 --- a/rsconcept/frontend/src/backend/cctext/api.ts +++ b/rsconcept/frontend/src/backend/cctext/api.ts @@ -1,6 +1,7 @@ -import { axiosInstance } from '@/backend/axiosInstance'; import { ILexemeData, IWordFormPlain } from '@/models/language'; +import { axiosPost } from '../apiTransport'; + /** * Represents API result for text output. */ @@ -12,15 +13,18 @@ export const cctextApi = { baseKey: 'cctext', inflectText: (data: IWordFormPlain) => - axiosInstance // - .post('/api/cctext/inflect', data) - .then(response => response.data), + axiosPost({ + endpoint: '/api/cctext/inflect', + request: { data: data } + }), parseText: (data: { text: string }) => - axiosInstance // - .post('/api/cctext/parse', data) - .then(response => response.data), + axiosPost<{ text: string }, ITextResult>({ + endpoint: '/api/cctext/parse', + request: { data: data } + }), generateLexeme: (data: { text: string }) => - axiosInstance // - .post('/api/cctext/generate-lexeme', data) - .then(response => response.data) + axiosPost<{ text: string }, ILexemeData>({ + endpoint: '/api/cctext/generate-lexeme', + request: { data: data } + }) }; diff --git a/rsconcept/frontend/src/backend/library/api.ts b/rsconcept/frontend/src/backend/library/api.ts index 79f5b8d2..7ec2d725 100644 --- a/rsconcept/frontend/src/backend/library/api.ts +++ b/rsconcept/frontend/src/backend/library/api.ts @@ -1,11 +1,20 @@ import { queryOptions } from '@tanstack/react-query'; -import { axiosInstance } from '@/backend/axiosInstance'; import { DELAYS } from '@/backend/configuration'; -import { AccessPolicy, ILibraryItem, IVersionData, LibraryItemID, LibraryItemType, VersionID } from '@/models/library'; +import { + AccessPolicy, + ILibraryItem, + IVersionData, + IVersionInfo, + LibraryItemID, + LibraryItemType, + VersionID +} from '@/models/library'; import { ConstituentaID, IRSFormData } from '@/models/rsform'; import { UserID } from '@/models/user'; +import { information } from '@/utils/labels'; +import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '../apiTransport'; import { ossApi } from '../oss/api'; import { rsformsApi } from '../rsform/api'; @@ -59,87 +68,140 @@ export const libraryApi = { baseKey: 'library', libraryListKey: ['library', 'list'], - getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) => - queryOptions({ - queryKey: libraryApi.libraryListKey, - staleTime: DELAYS.staleMedium, - queryFn: meta => - axiosInstance - .get(isAdmin ? '/api/library/all' : '/api/library/active', { - signal: meta.signal - }) - .then(response => response.data) - }), getItemQueryOptions: ({ itemID, itemType }: { itemID: LibraryItemID; itemType: LibraryItemType }) => { return itemType === LibraryItemType.RSFORM ? rsformsApi.getRSFormQueryOptions({ itemID }) : ossApi.getOssQueryOptions({ itemID }); }, + getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) => + queryOptions({ + queryKey: libraryApi.libraryListKey, + staleTime: DELAYS.staleMedium, + queryFn: meta => + axiosGet({ + endpoint: isAdmin ? '/api/library/all' : '/api/library/active', + options: { signal: meta.signal } + }) + }), getTemplatesQueryOptions: () => queryOptions({ queryKey: [libraryApi.baseKey, 'templates'], staleTime: DELAYS.staleMedium, queryFn: meta => - axiosInstance - .get('/api/library/templates', { - signal: meta.signal - }) - .then(response => response.data) + axiosGet({ + endpoint: '/api/library/templates', + options: { signal: meta.signal } + }) }), createItem: (data: ILibraryCreateDTO) => - data.file - ? axiosInstance - .post('/api/rsforms/create-detailed', data, { + axiosPost({ + endpoint: !data.file ? '/api/library' : '/api/rsforms/create-detailed', + request: { + data: data, + successMessage: information.newLibraryItem + }, + options: !data.file + ? undefined + : { headers: { 'Content-Type': 'multipart/form-data' } - }) - .then(response => response.data) - : axiosInstance // - .post('/api/library', data) - .then(response => response.data), - + } + }), updateItem: (data: ILibraryUpdateDTO) => - axiosInstance // - .patch(`/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 }), + axiosPatch({ + endpoint: `/api/library/${data.id}`, + request: { + data: data, + successMessage: information.changesSaved + } + }), + setOwner: ({ itemID, owner }: { itemID: LibraryItemID; owner: UserID }) => + axiosPatch({ + endpoint: `/api/library/${itemID}/set-owner`, + request: { + data: { user: owner }, + successMessage: information.changesSaved + } + }), + setLocation: ({ itemID, location }: { itemID: LibraryItemID; location: string }) => + axiosPatch({ + endpoint: `/api/library/${itemID}/set-location`, + request: { + data: { location: location }, + successMessage: information.moveComplete + } + }), + setAccessPolicy: ({ itemID, policy }: { itemID: LibraryItemID; policy: AccessPolicy }) => + axiosPatch({ + endpoint: `/api/library/${itemID}/set-access-policy`, + request: { + data: { access_policy: policy }, + successMessage: information.changesSaved + } + }), + setEditors: ({ itemID, editors }: { itemID: LibraryItemID; editors: UserID[] }) => + axiosPatch({ + endpoint: `/api/library/${itemID}/set-editors`, + request: { + data: { users: editors }, + successMessage: information.changesSaved + } + }), deleteItem: (target: LibraryItemID) => - axiosInstance // - .delete(`/api/library/${target}`), + axiosDelete({ + endpoint: `/api/library/${target}`, + request: { + successMessage: information.itemDestroyed + } + }), cloneItem: (data: IRSFormCloneDTO) => - axiosInstance // - .post(`/api/library/${data.id}/clone`, data) - .then(response => response.data), + axiosPost({ + endpoint: `/api/library/${data.id}/clone`, + request: { + data: data, + successMessage: newSchema => information.cloneComplete(newSchema.alias) + } + }), renameLocation: (data: IRenameLocationDTO) => - axiosInstance // - .patch('/api/library/rename-location', data), + axiosPatch({ + endpoint: '/api/library/rename-location', + request: { + data: data, + successMessage: information.locationRenamed + } + }), - versionCreate: (data: { itemID: LibraryItemID; data: IVersionData }) => - axiosInstance // - .post(`/api/library/${data.itemID}/versions`, data.data) - .then(response => response.data), - versionRestore: (data: { itemID: LibraryItemID; versionID: VersionID }) => - axiosInstance // - .patch(`/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), + versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionData }) => + axiosPost({ + endpoint: `/api/library/${itemID}/create-version`, + request: { + data: data, + successMessage: information.newVersion(data.version) + } + }), + versionRestore: ({ versionID }: { versionID: VersionID }) => + axiosPatch({ + endpoint: `/api/versions/${versionID}/restore`, + request: { + successMessage: information.versionRestored + } + }), + versionUpdate: ({ versionID, data }: { versionID: VersionID; data: IVersionData }) => + axiosPatch({ + endpoint: `/api/versions/${versionID}`, + request: { + data: data, + successMessage: information.changesSaved + } + }), versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) => - axiosInstance // - .delete(`/api/versions/${data.versionID}`) + axiosDelete({ + endpoint: `/api/versions/${data.versionID}`, + request: { + successMessage: information.versionDestroyed + } + }) }; diff --git a/rsconcept/frontend/src/backend/library/useLibrary.tsx b/rsconcept/frontend/src/backend/library/useLibrary.tsx index e06aa2e9..f7d3c703 100644 --- a/rsconcept/frontend/src/backend/library/useLibrary.tsx +++ b/rsconcept/frontend/src/backend/library/useLibrary.tsx @@ -16,7 +16,7 @@ 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 }) + ...libraryApi.getLibraryQueryOptions({ isAdmin: user.is_staff }) }); return { items: items ?? [], isLoading }; } diff --git a/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx b/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx index 6d26c1a0..b492d401 100644 --- a/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx +++ b/rsconcept/frontend/src/backend/library/useSetAccessPolicy.tsx @@ -30,12 +30,6 @@ export const useSetAccessPolicy = () => { }); return { - setAccessPolicy: ( - data: { - itemID: LibraryItemID; // - policy: AccessPolicy; - }, - onSuccess?: () => void - ) => mutation.mutate(data, { onSuccess }) + setAccessPolicy: (data: { itemID: LibraryItemID; policy: AccessPolicy }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/library/useSetEditors.tsx b/rsconcept/frontend/src/backend/library/useSetEditors.tsx index 89b066f1..5eaca0f0 100644 --- a/rsconcept/frontend/src/backend/library/useSetEditors.tsx +++ b/rsconcept/frontend/src/backend/library/useSetEditors.tsx @@ -1,9 +1,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { rsformsApi } from '@/backend/rsform/api'; -import { ILibraryItem, LibraryItemID } from '@/models/library'; +import { LibraryItemID } from '@/models/library'; import { UserID } from '@/models/user'; +import { ossApi } from '../oss/api'; import { libraryApi } from './api'; export const useSetEditors = () => { @@ -12,28 +13,27 @@ export const useSetEditors = () => { 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 - }; - }); + const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey; + const ossData = client.getQueryData(ossKey); + if (ossData) { + client.setQueryData(ossKey, { ...ossData, editors: variables.editors }); + Promise.allSettled([ + ...ossData.items.map(item => { + if (!item.result) { + return; + } + const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; + return client.invalidateQueries({ queryKey: itemKey }); + }) + ]).catch(console.error); + } else { + const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; + client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, editors: variables.editors })); + } } }); return { - setEditors: ( - data: { - itemID: LibraryItemID; // - editors: UserID[]; - }, - onSuccess?: () => void - ) => mutation.mutate(data, { onSuccess }) + setEditors: (data: { itemID: LibraryItemID; editors: UserID[] }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/library/useSetLocation.tsx b/rsconcept/frontend/src/backend/library/useSetLocation.tsx index ae143d49..fd370b0a 100644 --- a/rsconcept/frontend/src/backend/library/useSetLocation.tsx +++ b/rsconcept/frontend/src/backend/library/useSetLocation.tsx @@ -11,6 +11,28 @@ export const useSetLocation = () => { mutationKey: [libraryApi.baseKey, 'set-location'], mutationFn: libraryApi.setLocation, onSuccess: (_, variables) => { + // const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey; + // const ossData = client.getQueryData(ossKey); + // if (ossData) { + // client.setQueryData(ossKey, { ...ossData, editors: variables.editors }); + // Promise.allSettled([ + // client.invalidateQueries({ queryKey: libraryApi.libraryListKey }), + // ...ossData.items.map(item => { + // if (!item.result) { + // return; + // } + // const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey; + // return client.invalidateQueries({ queryKey: itemKey }); + // }) + // ]).catch(console.error); + // } else { + // const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey; + // client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, editors: variables.editors })); + // client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => + // prev?.map(item => (item.id === variables.itemID ? { ...item, editors: variables.editors } : item)) + // ); + // } + client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item)) ); diff --git a/rsconcept/frontend/src/backend/library/useSetOwner.tsx b/rsconcept/frontend/src/backend/library/useSetOwner.tsx index 96dbcc89..24cc6213 100644 --- a/rsconcept/frontend/src/backend/library/useSetOwner.tsx +++ b/rsconcept/frontend/src/backend/library/useSetOwner.tsx @@ -31,12 +31,6 @@ export const useSetOwner = () => { }); return { - setOwner: ( - data: { - itemID: LibraryItemID; // - owner: UserID; - }, - onSuccess?: () => void - ) => mutation.mutate(data, { onSuccess }) + setOwner: (data: { itemID: LibraryItemID; owner: UserID }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/library/useUpdateItem.tsx b/rsconcept/frontend/src/backend/library/useUpdateItem.tsx index 53e0d80b..78fc4e65 100644 --- a/rsconcept/frontend/src/backend/library/useUpdateItem.tsx +++ b/rsconcept/frontend/src/backend/library/useUpdateItem.tsx @@ -1,6 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { DataCallback } from '@/backend/apiTransport'; import { rsformsApi } from '@/backend/rsform/api'; import { ILibraryItem } from '@/models/library'; @@ -13,7 +12,7 @@ export const useUpdateItem = () => { mutationFn: libraryApi.updateItem, onSuccess: (data: ILibraryItem) => { client - .cancelQueries({ queryKey: [libraryApi.libraryListKey] }) + .cancelQueries({ queryKey: libraryApi.libraryListKey }) .then(async () => { client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) => prev?.map(item => (item.id === data.id ? data : item)) @@ -26,9 +25,6 @@ export const useUpdateItem = () => { } }); return { - updateItem: ( - data: ILibraryUpdateDTO, // - onSuccess?: DataCallback - ) => mutation.mutate(data, { onSuccess }) + updateItem: (data: ILibraryUpdateDTO) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/library/useVersionCreate.tsx b/rsconcept/frontend/src/backend/library/useVersionCreate.tsx index 94bfee8a..6aad2c5f 100644 --- a/rsconcept/frontend/src/backend/library/useVersionCreate.tsx +++ b/rsconcept/frontend/src/backend/library/useVersionCreate.tsx @@ -3,7 +3,7 @@ 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 { IVersionData, LibraryItemID, VersionID } from '@/models/library'; import { libraryApi } from './api'; @@ -14,7 +14,7 @@ export const useVersionCreate = () => { mutationKey: [libraryApi.baseKey, 'create-version'], mutationFn: libraryApi.versionCreate, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema); updateTimestamp(data.schema.id); } }); @@ -24,7 +24,7 @@ export const useVersionCreate = () => { itemID: LibraryItemID; // data: IVersionData; }, - onSuccess?: DataCallback - ) => mutation.mutate(data, { onSuccess: () => onSuccess?.(data.data) }) + onSuccess?: DataCallback + ) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.version) }) }; }; diff --git a/rsconcept/frontend/src/backend/library/useVersionDelete.tsx b/rsconcept/frontend/src/backend/library/useVersionDelete.tsx index e13de4f0..ec3765a8 100644 --- a/rsconcept/frontend/src/backend/library/useVersionDelete.tsx +++ b/rsconcept/frontend/src/backend/library/useVersionDelete.tsx @@ -13,11 +13,14 @@ export const useVersionDelete = () => { 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) - }) + rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, + (prev: IRSFormData | undefined) => + !prev + ? undefined + : { + ...prev, + versions: prev.versions.filter(version => version.id !== variables.versionID) + } ); } }); diff --git a/rsconcept/frontend/src/backend/library/useVersionRestore.tsx b/rsconcept/frontend/src/backend/library/useVersionRestore.tsx index e5cf8aeb..4aa4f887 100644 --- a/rsconcept/frontend/src/backend/library/useVersionRestore.tsx +++ b/rsconcept/frontend/src/backend/library/useVersionRestore.tsx @@ -2,7 +2,7 @@ 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 { VersionID } from '@/models/library'; import { libraryApi } from './api'; @@ -13,17 +13,11 @@ export const useVersionRestore = () => { mutationKey: [libraryApi.baseKey, 'restore-version'], mutationFn: libraryApi.versionRestore, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], 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 }) + versionRestore: (data: { versionID: VersionID }, onSuccess?: () => void) => mutation.mutate(data, { onSuccess }) }; }; diff --git a/rsconcept/frontend/src/backend/library/useVersionUpdate.tsx b/rsconcept/frontend/src/backend/library/useVersionUpdate.tsx index c6d8d8ec..7507f023 100644 --- a/rsconcept/frontend/src/backend/library/useVersionUpdate.tsx +++ b/rsconcept/frontend/src/backend/library/useVersionUpdate.tsx @@ -1,7 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { rsformsApi } from '@/backend/rsform/api'; -import { IVersionData, LibraryItemID, VersionID } from '@/models/library'; +import { IVersionData, VersionID } from '@/models/library'; import { IRSFormData } from '@/models/rsform'; import { libraryApi } from './api'; @@ -11,28 +11,24 @@ export const useVersionUpdate = () => { const mutation = useMutation({ mutationKey: [libraryApi.baseKey, 'update-version'], mutationFn: libraryApi.versionUpdate, - onSuccess: (_, variables) => { + onSuccess: data => { 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 - ) - }) + rsformsApi.getRSFormQueryOptions({ itemID: data.item }).queryKey, + (prev: IRSFormData | undefined) => + !prev + ? undefined + : { + ...prev, + versions: prev.versions.map(version => + version.id === data.id + ? { ...version, description: data.description, version: data.version } + : version + ) + } ); } }); return { - versionUpdate: ( - data: { - itemID: LibraryItemID; // - versionID: VersionID; - data: IVersionData; - }, - onSuccess?: () => void - ) => mutation.mutate(data, { onSuccess }) + versionUpdate: (data: { versionID: VersionID; data: IVersionData }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/oss/api.ts b/rsconcept/frontend/src/backend/oss/api.ts index 7e2820fb..aed0bcaa 100644 --- a/rsconcept/frontend/src/backend/oss/api.ts +++ b/rsconcept/frontend/src/backend/oss/api.ts @@ -1,6 +1,6 @@ import { queryOptions } from '@tanstack/react-query'; -import { axiosInstance } from '@/backend/axiosInstance'; +import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport'; import { DELAYS } from '@/backend/configuration'; import { ILibraryItem, LibraryItemID } from '@/models/library'; import { @@ -12,6 +12,7 @@ import { OperationType } from '@/models/oss'; import { ConstituentaID, IConstituentaReference, ITargetCst } from '@/models/rsform'; +import { information } from '@/utils/labels'; /** * Represents {@link IOperation} data, used in creation process. @@ -101,48 +102,90 @@ export const ossApi = { queryFn: meta => !itemID ? undefined - : axiosInstance - .get(`/api/oss/${itemID}/details`, { - signal: meta.signal - }) - .then(response => response.data) + : axiosGet({ + endpoint: `/api/oss/${itemID}/details`, + options: { signal: meta.signal } + }) }); }, - 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(`/api/oss/${data.itemID}/create-operation`, data.data) - .then(response => response.data), - operationDelete: (data: { itemID: LibraryItemID; data: IOperationDeleteDTO }) => - axiosInstance // - .patch(`/api/oss/${data.itemID}/delete-operation`, data.data) - .then(response => response.data), - inputCreate: (data: { itemID: LibraryItemID; data: ITargetOperation }) => - axiosInstance // - .patch(`/api/oss/${data.itemID}/create-input`, data.data) - .then(response => response.data), - inputUpdate: (data: { itemID: LibraryItemID; data: IInputUpdateDTO }) => - axiosInstance // - .patch(`/api/oss/${data.itemID}/set-input`, data.data) - .then(response => response.data), - operationUpdate: (data: { itemID: LibraryItemID; data: IOperationUpdateDTO }) => - axiosInstance // - .patch(`/api/oss/${data.itemID}/update-operation`, data.data) - .then(response => response.data), - operationExecute: (data: { itemID: LibraryItemID; data: ITargetOperation }) => - axiosInstance // - .post(`/api/oss/${data.itemID}/execute-operation`, data.data) - .then(response => response.data), + updatePositions: ({ + itemID, + positions, + isSilent + }: { + itemID: LibraryItemID; + positions: IOperationPosition[]; + isSilent?: boolean; + }) => + axiosPatch({ + endpoint: `/api/oss/${itemID}/update-positions`, + request: { + data: { positions: positions }, + successMessage: isSilent ? undefined : information.changesSaved + } + }), - relocateConstituents: (data: { itemID: LibraryItemID; data: ICstRelocateDTO }) => - axiosInstance // - .post(`/api/oss/${data.itemID}/relocate-constituents`, data.data) - .then(response => response.data), + operationCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationCreateDTO }) => + axiosPost({ + endpoint: `/api/oss/${itemID}/create-operation`, + request: { + data: data, + successMessage: response => information.newOperation(response.new_operation.alias) + } + }), + operationDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationDeleteDTO }) => + axiosDelete({ + endpoint: `/api/oss/${itemID}/delete-operation`, + request: { + data: data, + successMessage: information.operationDestroyed + } + }), + inputCreate: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetOperation }) => + axiosPatch({ + endpoint: `/api/oss/${itemID}/create-input`, + request: { + data: data, + successMessage: information.newLibraryItem + } + }), + inputUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: IInputUpdateDTO }) => + axiosPatch({ + endpoint: `/api/oss/${itemID}/set-input`, + request: { + data: data, + successMessage: information.changesSaved + } + }), + operationUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationUpdateDTO }) => + axiosPatch({ + endpoint: `/api/oss/${itemID}/update-operation`, + request: { + data: data, + successMessage: information.changesSaved + } + }), + operationExecute: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetOperation }) => + axiosPost({ + endpoint: `/api/oss/${itemID}/execute-operation`, + request: { + data: data, + successMessage: information.operationExecuted + } + }), + + relocateConstituents: ({ itemID, data }: { itemID: LibraryItemID; data: ICstRelocateDTO }) => + axiosPost({ + endpoint: `/api/oss/${itemID}/relocate-constituents`, + request: { + data: data, + successMessage: information.changesSaved + } + }), getPredecessor: (data: ITargetCst) => - axiosInstance // - .post(`/api/oss/get-predecessor`, data) - .then(response => response.data) + axiosPost({ + endpoint: '/api/oss/get-predecessor', + request: { data: data } + }) }; diff --git a/rsconcept/frontend/src/backend/oss/useInputCreate.tsx b/rsconcept/frontend/src/backend/oss/useInputCreate.tsx index b92c0c50..7fa23111 100644 --- a/rsconcept/frontend/src/backend/oss/useInputCreate.tsx +++ b/rsconcept/frontend/src/backend/oss/useInputCreate.tsx @@ -12,7 +12,7 @@ export const useInputCreate = () => { mutationKey: [ossApi.baseKey, 'input-create'], mutationFn: ossApi.inputCreate, onSuccess: async data => { - client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey], data.oss); + client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss); await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] }); } }); diff --git a/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx b/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx index 03e59114..11aef44f 100644 --- a/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx +++ b/rsconcept/frontend/src/backend/oss/useInputUpdate.tsx @@ -11,17 +11,11 @@ export const useInputUpdate = () => { mutationKey: [ossApi.baseKey, 'input-update'], mutationFn: ossApi.inputUpdate, onSuccess: async data => { - client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], 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 }) + inputUpdate: (data: { itemID: LibraryItemID; data: IInputUpdateDTO }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/oss/useOperationCreate.tsx b/rsconcept/frontend/src/backend/oss/useOperationCreate.tsx index 84bf6f9b..e1187712 100644 --- a/rsconcept/frontend/src/backend/oss/useOperationCreate.tsx +++ b/rsconcept/frontend/src/backend/oss/useOperationCreate.tsx @@ -14,7 +14,7 @@ export const useOperationCreate = () => { mutationKey: [ossApi.baseKey, 'operation-create'], mutationFn: ossApi.operationCreate, onSuccess: data => { - client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey], data.oss); + client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss); updateTimestamp(data.oss.id); } }); diff --git a/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx b/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx index 9ec316b4..eecd07a5 100644 --- a/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx +++ b/rsconcept/frontend/src/backend/oss/useOperationDelete.tsx @@ -11,17 +11,11 @@ export const useOperationDelete = () => { mutationKey: [ossApi.baseKey, 'operation-delete'], mutationFn: ossApi.operationDelete, onSuccess: async data => { - client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], 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 }) + operationDelete: (data: { itemID: LibraryItemID; data: IOperationDeleteDTO }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx b/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx index a618d21f..c8d030f1 100644 --- a/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx +++ b/rsconcept/frontend/src/backend/oss/useOperationExecute.tsx @@ -11,17 +11,11 @@ export const useOperationExecute = () => { mutationKey: [ossApi.baseKey, 'operation-execute'], mutationFn: ossApi.operationExecute, onSuccess: async data => { - client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], 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 }) + operationExecute: (data: { itemID: LibraryItemID; data: ITargetOperation }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx b/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx index 340b19ef..0f982040 100644 --- a/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx +++ b/rsconcept/frontend/src/backend/oss/useOperationUpdate.tsx @@ -11,17 +11,11 @@ export const useOperationUpdate = () => { mutationKey: [ossApi.baseKey, 'operation-update'], mutationFn: ossApi.operationUpdate, onSuccess: async data => { - client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], 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 }) + operationUpdate: (data: { itemID: LibraryItemID; data: IOperationUpdateDTO }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx b/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx index 374ccbb3..dd05fc3f 100644 --- a/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx +++ b/rsconcept/frontend/src/backend/oss/useRelocateConstituents.tsx @@ -11,17 +11,11 @@ export const useRelocateConstituents = () => { mutationKey: [ossApi.baseKey, 'relocate-constituents'], mutationFn: ossApi.relocateConstituents, onSuccess: async data => { - client.setQueryData([ossApi.getOssQueryOptions({ itemID: data.id }).queryKey], 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 }) + relocateConstituents: (data: { itemID: LibraryItemID; data: ICstRelocateDTO }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/oss/useUpdatePositions.tsx b/rsconcept/frontend/src/backend/oss/useUpdatePositions.tsx index 22cdbe4d..0ba54cda 100644 --- a/rsconcept/frontend/src/backend/oss/useUpdatePositions.tsx +++ b/rsconcept/frontend/src/backend/oss/useUpdatePositions.tsx @@ -18,6 +18,7 @@ export const useUpdatePositions = () => { data: { itemID: LibraryItemID; // positions: IOperationPosition[]; + isSilent?: boolean; }, onSuccess?: () => void ) => mutation.mutate(data, { onSuccess }) diff --git a/rsconcept/frontend/src/backend/rsform/api.ts b/rsconcept/frontend/src/backend/rsform/api.ts index 35f1993b..5b4d9d89 100644 --- a/rsconcept/frontend/src/backend/rsform/api.ts +++ b/rsconcept/frontend/src/backend/rsform/api.ts @@ -1,6 +1,6 @@ import { queryOptions } from '@tanstack/react-query'; -import { axiosInstance } from '@/backend/axiosInstance'; +import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport'; import { DELAYS } from '@/backend/configuration'; import { LibraryItemID, VersionID } from '@/models/library'; import { ICstSubstitute, ICstSubstitutions } from '@/models/oss'; @@ -14,6 +14,7 @@ import { TermForm } from '@/models/rsform'; import { IExpressionParse } from '@/models/rslang'; +import { information } from '@/utils/labels'; /** * Represents data, used for uploading {@link IRSForm} as file. @@ -116,76 +117,108 @@ export const rsformsApi = { queryFn: meta => !itemID ? undefined - : axiosInstance - .get( - version ? `/api/library/${itemID}/versions/${version}` : `/api/rsforms/${itemID}/details`, - { - signal: meta.signal - } - ) - .then(response => response.data) + : axiosGet({ + endpoint: version ? `/api/library/${itemID}/versions/${version}` : `/api/rsforms/${itemID}/details`, + options: { signal: meta.signal } + }) }); }, download: ({ itemID, version }: { itemID: LibraryItemID; version?: VersionID }) => - axiosInstance // - .get(version ? `/api/versions/${version}/export-file` : `/api/rsforms/${itemID}/export-trs`, { - responseType: 'blob' - }) - .then(response => response.data), + axiosGet({ + endpoint: version ? `/api/versions/${version}/export-file` : `/api/rsforms/${itemID}/export-trs`, + options: { responseType: 'blob' } + }), upload: (data: IRSFormUploadDTO) => - axiosInstance // - .patch(`/api/rsforms/${data.itemID}/load-trs`, data, { + axiosPatch({ + endpoint: `/api/rsforms/${data.itemID}/load-trs`, + request: { + data: data, + successMessage: information.uploadSuccess + }, + options: { headers: { 'Content-Type': 'multipart/form-data' } - }) - .then(response => response.data), + } + }), cstCreate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstCreateDTO }) => - axiosInstance // - .post(`/api/rsforms/${itemID}/create-cst`, data) - .then(response => response.data), + axiosPost({ + endpoint: `/api/rsforms/${itemID}/create-cst`, + request: { + data: data, + successMessage: response => information.newConstituent(response.new_cst.alias) + } + }), cstUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstUpdateDTO }) => - axiosInstance // - .patch(`/api/rsforms/${itemID}/update-cst`, data) - .then(response => response.data), + axiosPatch({ + endpoint: `/api/rsforms/${itemID}/update-cst`, + request: { + data: data, + successMessage: information.changesSaved + } + }), cstDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IConstituentaList }) => - axiosInstance // - .patch(`/api/rsforms/${itemID}/delete-multiple-cst`, data) - .then(response => response.data), + axiosDelete({ + endpoint: `/api/rsforms/${itemID}/delete-multiple-cst`, + request: { + data: data, + successMessage: information.constituentsDestroyed(data.items.length) + } + }), cstRename: ({ itemID, data }: { itemID: LibraryItemID; data: ICstRenameDTO }) => - axiosInstance // - .patch(`/api/rsforms/${itemID}/rename-cst`, data) - .then(response => response.data), + axiosPatch({ + endpoint: `/api/rsforms/${itemID}/rename-cst`, + request: { + data: data, + successMessage: information.changesSaved + } + }), cstSubstitute: ({ itemID, data }: { itemID: LibraryItemID; data: ICstSubstitutions }) => - axiosInstance // - .patch(`/api/rsforms/${itemID}/substitute`, data) - .then(response => response.data), + axiosPatch({ + endpoint: `/api/rsforms/${itemID}/substitute`, + request: { + data: data, + successMessage: information.substituteSingle + } + }), cstMove: ({ itemID, data }: { itemID: LibraryItemID; data: ICstMoveDTO }) => - axiosInstance // - .patch(`/api/rsforms/${itemID}/move-cst`, data) - .then(response => response.data), + axiosPatch({ + endpoint: `/api/rsforms/${itemID}/move-cst`, + request: { data: data } + }), produceStructure: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetCst }) => - axiosInstance // - .post(`/api/rsforms/${itemID}/produce-structure`, data) - .then(response => response.data), + axiosPost({ + endpoint: `/api/rsforms/${itemID}/produce-structure`, + request: { + data: data, + successMessage: response => information.addedConstituents(response.cst_list.length) + } + }), inlineSynthesis: ({ itemID, data }: { itemID: LibraryItemID; data: IInlineSynthesisDTO }) => - axiosInstance // - .post(`/api/rsforms/${itemID}/inline-synthesis`, data) - .then(response => response.data), - restoreOrder: (itemID: LibraryItemID) => - axiosInstance // - .patch(`/api/rsforms/${itemID}/restore-order`) - .then(response => response.data), - resetAliases: (itemID: LibraryItemID) => - axiosInstance // - .patch(`/api/rsforms/${itemID}/reset-aliases`) - .then(response => response.data), + axiosPost({ + endpoint: `/api/rsforms/${itemID}/inline-synthesis`, + request: { + data: data, + successMessage: information.inlineSynthesisComplete + } + }), + restoreOrder: ({ itemID }: { itemID: LibraryItemID }) => + axiosPatch({ + endpoint: `/api/rsforms/${itemID}/restore-order`, + request: { successMessage: information.reorderComplete } + }), + resetAliases: ({ itemID }: { itemID: LibraryItemID }) => + axiosPatch({ + endpoint: `/api/rsforms/${itemID}/reset-aliases`, + request: { successMessage: information.reindexComplete } + }), checkConstituenta: ({ itemID, data }: { itemID: LibraryItemID; data: ICheckConstituentaDTO }) => - axiosInstance // - .post(`/api/rsforms/${itemID}/check-constituenta`, data) - .then(response => response.data) + axiosPost({ + endpoint: `/api/rsforms/${itemID}/check-constituenta`, + request: { data: data } + }) }; diff --git a/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx b/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx index 1f069803..03bbef6b 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstCreate.tsx @@ -14,7 +14,7 @@ export const useCstCreate = () => { mutationKey: [rsformsApi.baseKey, 'create-cst'], mutationFn: rsformsApi.cstCreate, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema); updateTimestamp(data.schema.id); // TODO: invalidate OSS? } diff --git a/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx b/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx index 3871015a..ed7f8a72 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstDelete.tsx @@ -13,7 +13,7 @@ export const useCstDelete = () => { mutationKey: [rsformsApi.baseKey, 'delete-multiple-cst'], mutationFn: rsformsApi.cstDelete, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); // TODO: invalidate OSS? } diff --git a/rsconcept/frontend/src/backend/rsform/useCstMove.tsx b/rsconcept/frontend/src/backend/rsform/useCstMove.tsx index 3f0cb9f8..6a85715b 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstMove.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstMove.tsx @@ -12,7 +12,7 @@ export const useCstMove = () => { mutationKey: [rsformsApi.baseKey, 'move-cst'], mutationFn: rsformsApi.cstMove, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); // TODO: invalidate OSS? } diff --git a/rsconcept/frontend/src/backend/rsform/useCstRename.tsx b/rsconcept/frontend/src/backend/rsform/useCstRename.tsx index 0d6e627f..e5f6edb8 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstRename.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstRename.tsx @@ -2,9 +2,7 @@ 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 = () => { @@ -14,18 +12,12 @@ export const useCstRename = () => { mutationKey: [rsformsApi.baseKey, 'rename-cst'], mutationFn: rsformsApi.cstRename, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema); + 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 - ) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.new_cst) }) + cstRename: (data: { itemID: LibraryItemID; data: ICstRenameDTO }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx b/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx index fe23e90d..997ae772 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstSubstitute.tsx @@ -13,7 +13,7 @@ export const useCstSubstitute = () => { mutationKey: [rsformsApi.baseKey, 'substitute-cst'], mutationFn: rsformsApi.cstSubstitute, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); // TODO: invalidate OSS? } diff --git a/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx b/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx index 17aa8bd6..96d21b8d 100644 --- a/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCstUpdate.tsx @@ -1,9 +1,7 @@ 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'; @@ -22,12 +20,6 @@ export const useCstUpdate = () => { } }); return { - cstUpdate: ( - data: { - itemID: LibraryItemID; // - data: ICstUpdateDTO; - }, - onSuccess?: DataCallback - ) => mutation.mutate(data, { onSuccess }) + cstUpdate: (data: { itemID: LibraryItemID; data: ICstUpdateDTO }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx b/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx index 913341ee..6f5ae2a0 100644 --- a/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx +++ b/rsconcept/frontend/src/backend/rsform/useInlineSynthesis.tsx @@ -14,7 +14,7 @@ export const useInlineSynthesis = () => { mutationKey: [rsformsApi.baseKey, 'inline-synthesis'], mutationFn: rsformsApi.inlineSynthesis, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); // TODO: invalidate OSS? } diff --git a/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx b/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx index 6df5802c..72de9a6a 100644 --- a/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx +++ b/rsconcept/frontend/src/backend/rsform/useProduceStructure.tsx @@ -14,7 +14,7 @@ export const useProduceStructure = () => { mutationKey: [rsformsApi.baseKey, 'produce-structure'], mutationFn: rsformsApi.produceStructure, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema); updateTimestamp(data.schema.id); // TODO: invalidate OSS? } diff --git a/rsconcept/frontend/src/backend/rsform/useRSForm.tsx b/rsconcept/frontend/src/backend/rsform/useRSForm.tsx index 86334a5f..027793b9 100644 --- a/rsconcept/frontend/src/backend/rsform/useRSForm.tsx +++ b/rsconcept/frontend/src/backend/rsform/useRSForm.tsx @@ -1,7 +1,6 @@ -import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'; +import { useQuery, 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'; @@ -22,13 +21,3 @@ export function useRSFormSuspense({ itemID, version }: { itemID: LibraryItemID; const schema = new RSFormLoader(data!).produceRSForm(); 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) => - client.setQueryData(queryKey, (prev: IRSForm) => (prev ? { ...prev, ...data } : prev)) - }; -} diff --git a/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx b/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx index 16801841..2315e86a 100644 --- a/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx +++ b/rsconcept/frontend/src/backend/rsform/useResetAliases.tsx @@ -12,15 +12,12 @@ export const useResetAliases = () => { mutationKey: [rsformsApi.baseKey, 'reset-aliases'], mutationFn: rsformsApi.resetAliases, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], 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 }) + resetAliases: (data: { itemID: LibraryItemID }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/rsform/useRestoreOrder.tsx b/rsconcept/frontend/src/backend/rsform/useRestoreOrder.tsx index 64dde162..cdf58ae6 100644 --- a/rsconcept/frontend/src/backend/rsform/useRestoreOrder.tsx +++ b/rsconcept/frontend/src/backend/rsform/useRestoreOrder.tsx @@ -12,14 +12,11 @@ export const useRestoreOrder = () => { mutationKey: [rsformsApi.baseKey, 'restore-order'], mutationFn: rsformsApi.restoreOrder, onSuccess: data => { - client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data); + client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data); updateTimestamp(data.id); } }); return { - restoreOrder: ( - itemID: LibraryItemID, // - onSuccess?: () => void - ) => mutation.mutate(itemID, { onSuccess }) + restoreOrder: (data: { itemID: LibraryItemID }) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx b/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx index 587f3bdd..2a49118b 100644 --- a/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx +++ b/rsconcept/frontend/src/backend/rsform/useUploadTRS.tsx @@ -1,9 +1,7 @@ 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'; @@ -13,16 +11,13 @@ export const useUploadTRS = () => { 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) => + 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 - ) => mutation.mutate(data, { onSuccess }) + upload: (data: IRSFormUploadDTO) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/backend/users/api.ts b/rsconcept/frontend/src/backend/users/api.ts index c1aeb469..b405f1fe 100644 --- a/rsconcept/frontend/src/backend/users/api.ts +++ b/rsconcept/frontend/src/backend/users/api.ts @@ -1,8 +1,10 @@ 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 { information } from '@/utils/labels'; + +import { axiosGet, axiosPatch, axiosPost } from '../apiTransport'; /** * Represents user data, intended to update user profile in persistent storage. @@ -16,24 +18,37 @@ export const usersApi = { queryKey: [usersApi.baseKey, 'list'], staleTime: DELAYS.staleMedium, queryFn: meta => - axiosInstance - .get('/users/api/active-users', { - signal: meta.signal - }) - .then(response => response.data) + axiosGet({ + endpoint: '/users/api/active-users', + options: { signal: meta.signal } + }) }), getProfileQueryOptions: () => queryOptions({ queryKey: [usersApi.baseKey, 'profile'], staleTime: DELAYS.staleShort, queryFn: meta => - axiosInstance - .get('/users/api/profile', { - signal: meta.signal - }) - .then(response => response.data) + axiosGet({ + endpoint: '/users/api/profile', + options: { signal: meta.signal } + }) }), - signup: (data: IUserSignupData) => axiosInstance.post('/users/api/signup', data), - updateProfile: (data: IUpdateProfileDTO) => axiosInstance.patch('/users/api/profile', data) + signup: (data: IUserSignupData) => + axiosPost({ + endpoint: '/users/api/signup', + request: { + data: data, + successMessage: createdUser => information.newUser(createdUser.username) + } + }), + + updateProfile: (data: IUpdateProfileDTO) => + axiosPatch({ + endpoint: '/users/api/profile', + request: { + data: data, + successMessage: information.changesSaved + } + }) }; diff --git a/rsconcept/frontend/src/backend/users/useSignup.tsx b/rsconcept/frontend/src/backend/users/useSignup.tsx index 5065d13b..1dba7864 100644 --- a/rsconcept/frontend/src/backend/users/useSignup.tsx +++ b/rsconcept/frontend/src/backend/users/useSignup.tsx @@ -15,7 +15,7 @@ export const useSignup = () => { signup: ( data: IUserSignupData, // onSuccess?: DataCallback - ) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.data as IUserProfile) }), + ) => mutation.mutate(data, { onSuccess }), isPending: mutation.isPending, error: mutation.error, reset: mutation.reset diff --git a/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx b/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx index 507c1fd6..86c24498 100644 --- a/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx +++ b/rsconcept/frontend/src/backend/users/useUpdateProfile.tsx @@ -1,8 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { DataCallback } from '@/backend/apiTransport'; -import { IUserProfile } from '@/models/user'; - import { IUpdateProfileDTO, usersApi } from './api'; // TODO: reload users / optimistic update @@ -15,10 +12,7 @@ export const useUpdateProfile = () => { onSuccess: async () => await client.invalidateQueries({ queryKey: [usersApi.baseKey] }) }); return { - updateProfile: ( - data: IUpdateProfileDTO, // - onSuccess?: DataCallback - ) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.data as IUserProfile) }), + updateProfile: (data: IUpdateProfileDTO) => mutation.mutate(data), isPending: mutation.isPending, error: mutation.error, reset: mutation.reset diff --git a/rsconcept/frontend/src/components/select/SelectUser.tsx b/rsconcept/frontend/src/components/select/SelectUser.tsx index dfffb17b..fee19845 100644 --- a/rsconcept/frontend/src/components/select/SelectUser.tsx +++ b/rsconcept/frontend/src/components/select/SelectUser.tsx @@ -31,14 +31,13 @@ function SelectUser({ const getUserLabel = useLabelUser(); const items = filter ? users.filter(user => filter(user.id)) : users; - const options = - items?.map(user => ({ - value: user.id, - label: getUserLabel(user.id) - })) ?? []; + const options = items.map(user => ({ + value: user.id, + label: getUserLabel(user.id) + })); function filterLabel(option: { value: UserID | undefined; label: string }, inputValue: string) { - const user = items?.find(item => item.id === option.value); + const user = items.find(item => item.id === option.value); return !user ? false : matchUser(user, inputValue); } diff --git a/rsconcept/frontend/src/components/ui/SelectTree.tsx b/rsconcept/frontend/src/components/ui/SelectTree.tsx index 286cd4da..7e594299 100644 --- a/rsconcept/frontend/src/components/ui/SelectTree.tsx +++ b/rsconcept/frontend/src/components/ui/SelectTree.tsx @@ -79,23 +79,23 @@ function SelectTree({ } return ( -
+
{items.map((item, index) => { const isActive = getParent(item) === item || !folded.includes(getParent(item)); return (
handleSetValue(event, item) : undefined} + data-tooltip-html={getDescription(item)} + onClick={event => handleSetValue(event, item)} style={{ borderBottomWidth: isActive ? '1px' : '0px', transitionProperty: 'height, opacity, padding', diff --git a/rsconcept/frontend/src/components/wrap/RequireAuth.tsx b/rsconcept/frontend/src/components/wrap/RequireAuth.tsx index 280ef4c2..fafa8546 100644 --- a/rsconcept/frontend/src/components/wrap/RequireAuth.tsx +++ b/rsconcept/frontend/src/components/wrap/RequireAuth.tsx @@ -1,19 +1,13 @@ 'use client'; -import { useAuth } from '@/backend/auth/useAuth'; +import { useAuthSuspense } from '@/backend/auth/useAuth'; -import Loader from '../ui/Loader'; import TextURL from '../ui/TextURL'; function RequireAuth({ children }: React.PropsWithChildren) { - const { user, isLoading } = useAuth(); + const { isAnonymous } = useAuthSuspense(); - if (isLoading) { - return ; - } - if (user) { - return <>{children}; - } else { + if (isAnonymous) { return (

Пожалуйста войдите в систему

@@ -23,6 +17,7 @@ function RequireAuth({ children }: React.PropsWithChildren) {
); } + return <>{children}; } export default RequireAuth; diff --git a/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx b/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx index d5633be0..6649596b 100644 --- a/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx @@ -2,7 +2,6 @@ import clsx from 'clsx'; import { useState } from 'react'; -import { toast } from 'react-toastify'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; @@ -23,7 +22,6 @@ import { AccessPolicy, ILibraryItem, LocationHead } from '@/models/library'; import { cloneTitle, combineLocation, validateLocation } from '@/models/libraryAPI'; import { ConstituentaID } from '@/models/rsform'; import { useDialogsStore } from '@/stores/dialogs'; -import { information } from '@/utils/labels'; export interface DlgCloneLibraryItemProps { base: ILibraryItem; @@ -75,10 +73,7 @@ function DlgCloneLibraryItem() { if (onlySelected) { data.items = selected; } - cloneItem(data, newSchema => { - toast.success(information.cloneComplete(newSchema.alias)); - router.push(urls.schema(newSchema.id)); - }); + cloneItem(data, newSchema => router.push(urls.schema(newSchema.id))); } return ( diff --git a/rsconcept/frontend/src/dialogs/DlgCstTemplate/TabArguments.tsx b/rsconcept/frontend/src/dialogs/DlgCstTemplate/TabArguments.tsx index 95e8f32d..d499d69b 100644 --- a/rsconcept/frontend/src/dialogs/DlgCstTemplate/TabArguments.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCstTemplate/TabArguments.tsx @@ -189,7 +189,7 @@ function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) { void; + onChangeEditors: (newValue: UserID[]) => void; } function DlgEditEditors() { - const { editors, setEditors } = useDialogsStore(state => state.props as DlgEditEditorsProps); + const { editors, onChangeEditors } = useDialogsStore(state => state.props as DlgEditEditorsProps); const [selected, setSelected] = useState(editors); const { users } = useUsers(); function handleSubmit() { - setEditors(selected); + onChangeEditors(selected); } function onDeleteEditor(target: UserID) { diff --git a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx index 660fa82f..93ba8303 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx @@ -1,10 +1,7 @@ 'use client'; import { useEffect, useState } from 'react'; -import { toast } from 'react-toastify'; -import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; -import { urls } from '@/app/urls'; import { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary'; import { useVersionDelete } from '@/backend/library/useVersionDelete'; import { useVersionUpdate } from '@/backend/library/useVersionUpdate'; @@ -13,19 +10,18 @@ import MiniButton from '@/components/ui/MiniButton'; import Modal from '@/components/ui/Modal'; import TextArea from '@/components/ui/TextArea'; import TextInput from '@/components/ui/TextInput'; -import { ILibraryItemVersioned, IVersionData, IVersionInfo, VersionID } from '@/models/library'; +import { ILibraryItemVersioned, IVersionInfo, VersionID } from '@/models/library'; import { useDialogsStore } from '@/stores/dialogs'; -import { information } from '@/utils/labels'; import TableVersions from './TableVersions'; export interface DlgEditVersionsProps { item: ILibraryItemVersioned; + afterDelete: (targetVersion: VersionID) => void; } function DlgEditVersions() { - const { item } = useDialogsStore(state => state.props as DlgEditVersionsProps); - const router = useConceptNavigation(); + const { item, afterDelete } = useDialogsStore(state => state.props as DlgEditVersionsProps); const processing = useIsProcessingLibrary(); const { versionDelete } = useVersionDelete(); const { versionUpdate } = useVersionUpdate(); @@ -37,31 +33,21 @@ function DlgEditVersions() { const isValid = selected && item.versions.every(ver => ver.id === selected.id || ver.version != version); const isModified = selected && (selected.version != version || selected.description != description); - function handleDeleteVersion(versionID: VersionID) { - versionDelete({ itemID: item.id, versionID: versionID }, () => { - toast.success(information.versionDestroyed); - if (versionID === versionID) { - router.push(urls.schema(item.id)); - } - }); + function handleDeleteVersion(targetVersion: VersionID) { + versionDelete({ itemID: item.id, versionID: targetVersion }, () => afterDelete(targetVersion)); } function handleUpdate() { if (!isModified || !selected || processing || !isValid) { return; } - const data: IVersionData = { - version: version, - description: description - }; - versionUpdate( - { - itemID: item.id, // - versionID: selected.id, - data: data - }, - () => toast.success(information.changesSaved) - ); + versionUpdate({ + versionID: selected.id, + data: { + version: version, + description: description + } + }); } function handleReset() { diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx index 2bf6eefd..80f38f68 100644 --- a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx @@ -45,7 +45,7 @@ function DlgInlineSynthesis() { return; } onInlineSynthesis({ - source: source.schema?.id, + source: source.schema.id, receiver: receiver.id, items: selected, substitutions: substitutions @@ -53,7 +53,7 @@ function DlgInlineSynthesis() { } useEffect(() => { - setSelected(source.schema ? source.schema?.items.map(cst => cst.id) : []); + setSelected(source.schema ? source.schema.items.map(cst => cst.id) : []); setSubstitutions([]); }, [source.schema]); diff --git a/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx b/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx index c587f098..b996c6d9 100644 --- a/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx +++ b/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx @@ -28,15 +28,13 @@ function DlgRenameCst() { const [cstData, updateData] = usePartialUpdate(initial); useEffect(() => { - if (schema && initial && cstData.cst_type !== initial.cst_type) { + if (initial && cstData.cst_type !== initial.cst_type) { updateData({ alias: generateAlias(cstData.cst_type, schema) }); } }, [initial, cstData.cst_type, updateData, schema]); useEffect(() => { - setValidated( - !!schema && cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema) - ); + setValidated(cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema)); }, [cstData.cst_type, cstData.alias, initial, schema]); return ( diff --git a/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx b/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx index be37a576..ee4db5c9 100644 --- a/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx +++ b/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx @@ -1,9 +1,7 @@ 'use client'; import { useState } from 'react'; -import { toast } from 'react-toastify'; -import { IRSFormUploadDTO } from '@/backend/rsform/api'; import { useUploadTRS } from '@/backend/rsform/useUploadTRS'; import Checkbox from '@/components/ui/Checkbox'; import FileInput from '@/components/ui/FileInput'; @@ -26,13 +24,12 @@ function DlgUploadRSForm() { if (!file) { return; } - const data: IRSFormUploadDTO = { + upload({ itemID: itemID, load_metadata: loadMetadata, file: file, fileName: file.name - }; - upload(data, () => toast.success('Схема загружена из файла')); + }); }; const handleFile = (event: React.ChangeEvent) => { diff --git a/rsconcept/frontend/src/models/library.ts b/rsconcept/frontend/src/models/library.ts index ed2d6067..b08adf1a 100644 --- a/rsconcept/frontend/src/models/library.ts +++ b/rsconcept/frontend/src/models/library.ts @@ -48,6 +48,7 @@ export type VersionID = number; */ export interface IVersionInfo { id: VersionID; + item: LibraryItemID; version: string; description: string; time_create: string; @@ -56,7 +57,7 @@ export interface IVersionInfo { /** * Represents version data, intended to update version metadata in persistent storage. */ -export interface IVersionData extends Omit {} +export interface IVersionData extends Omit {} /** * Represents library item common data typical for all item types. diff --git a/rsconcept/frontend/src/models/user.ts b/rsconcept/frontend/src/models/user.ts index 98f37466..0def18c4 100644 --- a/rsconcept/frontend/src/models/user.ts +++ b/rsconcept/frontend/src/models/user.ts @@ -25,7 +25,10 @@ export interface IUser { /** * Represents CurrentUser information. */ -export interface ICurrentUser extends Pick { +export interface ICurrentUser { + id: UserID | null; + username: string; + is_staff: boolean; editor: LibraryItemID[]; } diff --git a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx index 92f2fcaf..f6c4e880 100644 --- a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx +++ b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx @@ -2,7 +2,6 @@ import clsx from 'clsx'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { toast } from 'react-toastify'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; @@ -27,7 +26,6 @@ import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library'; import { combineLocation, validateLocation } from '@/models/libraryAPI'; import { useLibrarySearchStore } from '@/stores/librarySearch'; import { EXTEOR_TRS_FILE } from '@/utils/constants'; -import { information } from '@/utils/labels'; function FormCreateItem() { const router = useConceptNavigation(); @@ -85,7 +83,6 @@ function FormCreateItem() { }; setSearchLocation(location); createItem(data, newItem => { - toast.success(information.newLibraryItem); if (itemType == LibraryItemType.RSFORM) { router.push(urls.schema(newItem.id)); } else { diff --git a/rsconcept/frontend/src/pages/HomePage.tsx b/rsconcept/frontend/src/pages/HomePage.tsx index 3b257506..34415485 100644 --- a/rsconcept/frontend/src/pages/HomePage.tsx +++ b/rsconcept/frontend/src/pages/HomePage.tsx @@ -2,27 +2,25 @@ import { useEffect } from 'react'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; -import { useAuth } from '@/backend/auth/useAuth'; +import { useAuthSuspense } from '@/backend/auth/useAuth'; import Loader from '@/components/ui/Loader'; import { PARAMETER } from '@/utils/constants'; function HomePage() { const router = useConceptNavigation(); - const { user, isLoading } = useAuth(); + const { isAnonymous } = useAuthSuspense(); useEffect(() => { - if (!isLoading) { - if (!user) { - setTimeout(() => { - router.replace(urls.manuals); - }, PARAMETER.refreshTimeout); - } else { - setTimeout(() => { - router.replace(urls.library); - }, PARAMETER.refreshTimeout); - } + if (isAnonymous) { + setTimeout(() => { + router.replace(urls.manuals); + }, PARAMETER.refreshTimeout); + } else { + setTimeout(() => { + router.replace(urls.library); + }, PARAMETER.refreshTimeout); } - }, [router, user, isLoading]); + }, [router, isAnonymous]); return ; } diff --git a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx index f8191f02..051fd2ce 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx @@ -40,10 +40,7 @@ function LibraryPage() { target: location, new_location: newLocation }, - () => { - setLocation(newLocation); - toast.success(information.locationRenamed); - } + () => setLocation(newLocation) ); } diff --git a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx index d28e8011..fc001449 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx @@ -23,7 +23,7 @@ interface ViewSideLocationProps { } function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps) { - const { user } = useAuth(); + const { user, isAnonymous } = useAuth(); const { items } = useLibrary(); const windowSize = useWindowSize(); @@ -34,7 +34,7 @@ function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps const toggleSubfolders = useLibrarySearchStore(state => state.toggleSubfolders); const canRename = (() => { - if (location.length <= 3 || !user) { + if (location.length <= 3 || isAnonymous || !user) { return false; } if (user.is_staff) { diff --git a/rsconcept/frontend/src/pages/LoginPage.tsx b/rsconcept/frontend/src/pages/LoginPage.tsx index 0472e2ac..a2e6c82f 100644 --- a/rsconcept/frontend/src/pages/LoginPage.tsx +++ b/rsconcept/frontend/src/pages/LoginPage.tsx @@ -21,7 +21,7 @@ function LoginPage() { const query = useQueryStrings(); const userQuery = query.get('username'); - const { user } = useAuth(); + const { isAnonymous } = useAuth(); const { login, isPending, error, reset } = useLogin(); const [username, setUsername] = useState(userQuery || ''); @@ -44,7 +44,7 @@ function LoginPage() { } } - if (user) { + if (!isAnonymous) { return ; } return ( diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx index 88fb9b3a..2e9d7494 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx @@ -2,7 +2,6 @@ import clsx from 'clsx'; import { useEffect, useState } from 'react'; -import { toast } from 'react-toastify'; import { ILibraryUpdateDTO } from '@/backend/library/api'; import { useUpdateItem } from '@/backend/library/useUpdateItem'; @@ -14,7 +13,6 @@ import TextInput from '@/components/ui/TextInput'; import { LibraryItemType } from '@/models/library'; import ToolbarItemAccess from '@/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess'; import { useModificationStore } from '@/stores/modification'; -import { information } from '@/utils/labels'; import { useOssEdit } from '../OssEditContext'; @@ -29,11 +27,11 @@ function FormOSS({ id }: FormOSSProps) { const isProcessing = useIsProcessingOss(); const schema = controller.schema; - const [title, setTitle] = useState(schema?.title ?? ''); - const [alias, setAlias] = useState(schema?.alias ?? ''); - const [comment, setComment] = useState(schema?.comment ?? ''); - const [visible, setVisible] = useState(schema?.visible ?? false); - const [readOnly, setReadOnly] = useState(schema?.read_only ?? false); + const [title, setTitle] = useState(schema.title); + const [alias, setAlias] = useState(schema.alias); + const [comment, setComment] = useState(schema.comment); + const [visible, setVisible] = useState(schema.visible); + const [readOnly, setReadOnly] = useState(schema.read_only); useEffect(() => { if (schema) { @@ -46,10 +44,6 @@ function FormOSS({ id }: FormOSSProps) { }, [schema]); useEffect(() => { - if (!schema) { - setIsModified(false); - return; - } setIsModified( schema.title !== title || schema.alias !== alias || @@ -59,12 +53,11 @@ function FormOSS({ id }: FormOSSProps) { ); return () => setIsModified(false); }, [ - schema, - schema?.title, - schema?.alias, - schema?.comment, - schema?.visible, - schema?.read_only, + schema.title, + schema.alias, + schema.comment, + schema.visible, + schema.read_only, title, alias, comment, @@ -89,7 +82,7 @@ function FormOSS({ id }: FormOSSProps) { visible: visible, read_only: readOnly }; - update(data, () => toast.success(information.changesSaved)); + update(data); }; return ( diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx index adf7c535..008ac3c9 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx @@ -58,7 +58,7 @@ function NodeContextMenu({ if (operation.operation_type !== OperationType.SYNTHESIS) { return false; } - if (!controller.schema || operation.result) { + if (operation.result) { return false; } diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx index a4f077f7..da1c8ca4 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx @@ -32,7 +32,7 @@ import { useModificationStore } from '@/stores/modification'; import { useOSSGraphStore } from '@/stores/ossGraph'; import { APP_COLORS } from '@/styling/color'; import { PARAMETER } from '@/utils/constants'; -import { errors, information } from '@/utils/labels'; +import { errors } from '@/utils/labels'; import { useOssEdit } from '../OssEditContext'; import { OssNodeTypes } from './graph/OssNodeTypes'; @@ -81,33 +81,29 @@ function OssFlow() { }); useEffect(() => { - if (!controller.schema) { - setNodes([]); - setEdges([]); - } else { - setNodes( - controller.schema.items.map(operation => ({ - id: String(operation.id), - data: { label: operation.alias, operation: operation }, - position: { x: operation.position_x, y: operation.position_y }, - type: operation.operation_type.toString() - })) - ); - setEdges( - controller.schema.arguments.map((argument, index) => ({ - id: String(index), - source: String(argument.argument), - target: String(argument.operation), - type: edgeStraight ? 'straight' : 'simplebezier', - animated: edgeAnimate, - targetHandle: - controller.schema.operationByID.get(argument.argument)!.position_x > - controller.schema.operationByID.get(argument.operation)!.position_x - ? 'right' - : 'left' - })) - ); - } + setNodes( + controller.schema.items.map(operation => ({ + id: String(operation.id), + data: { label: operation.alias, operation: operation }, + position: { x: operation.position_x, y: operation.position_y }, + type: operation.operation_type.toString() + })) + ); + setEdges( + controller.schema.arguments.map((argument, index) => ({ + id: String(index), + source: String(argument.argument), + target: String(argument.operation), + type: edgeStraight ? 'straight' : 'simplebezier', + animated: edgeAnimate, + targetHandle: + controller.schema.operationByID.get(argument.argument)!.position_x > + controller.schema.operationByID.get(argument.operation)!.position_x + ? 'right' + : 'left' + })) + ); + setTimeout(() => { setIsModified(false); }, PARAMETER.graphRefreshDelay); @@ -138,15 +134,11 @@ function OssFlow() { operation.position_y = item.position_y; } }); - toast.success(information.changesSaved); setIsModified(false); }); } function handleCreateOperation(inputs: OperationID[]) { - if (!controller.schema) { - return; - } const positions = getPositions(); const target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 }); controller.promptCreateOperation({ @@ -181,10 +173,9 @@ function OssFlow() { toast.error(errors.inputAlreadyExists); return; } - inputCreate({ itemID: controller.schema.id, data: { target: target, positions: getPositions() } }, new_schema => { - toast.success(information.newLibraryItem); - router.push(urls.schema(new_schema.id)); - }); + inputCreate({ itemID: controller.schema.id, data: { target: target, positions: getPositions() } }, new_schema => + router.push(urls.schema(new_schema.id)) + ); } function handleEditSchema(target: OperationID) { @@ -196,13 +187,10 @@ function OssFlow() { } function handleOperationExecute(target: OperationID) { - operationExecute( - { - itemID: controller.schema.id, // - data: { target: target, positions: getPositions() } - }, - () => toast.success(information.operationExecuted) - ); + operationExecute({ + itemID: controller.schema.id, // + data: { target: target, positions: getPositions() } + }); } function handleExecuteSelected() { @@ -217,9 +205,6 @@ function OssFlow() { } function handleSaveImage() { - if (!controller.schema) { - return; - } const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport'); if (canvas === null) { toast.error(errors.imageFailed); @@ -242,7 +227,7 @@ function OssFlow() { }) .then(dataURL => { const a = document.createElement('a'); - a.setAttribute('download', `${controller.schema?.alias ?? 'oss'}.png`); + a.setAttribute('download', `${controller.schema.alias}.png`); a.setAttribute('href', dataURL); a.click(); }) diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx index bad9d138..f2c32ebf 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx @@ -53,7 +53,7 @@ function ToolbarOssGraph({ const controller = useOssEdit(); const { isModified } = useModificationStore(); const isProcessing = useIsProcessingOss(); - const selectedOperation = controller.schema?.operationByID.get(controller.selected[0]); + const selectedOperation = controller.schema.operationByID.get(controller.selected[0]); const showGrid = useOSSGraphStore(state => state.showGrid); const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate); @@ -66,7 +66,7 @@ function ToolbarOssGraph({ if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) { return false; } - if (!controller.schema || selectedOperation.result) { + if (selectedOperation.result) { return false; } diff --git a/rsconcept/frontend/src/pages/OssPage/MenuOssTabs.tsx b/rsconcept/frontend/src/pages/OssPage/MenuOssTabs.tsx index addc8b3c..ae22544c 100644 --- a/rsconcept/frontend/src/pages/OssPage/MenuOssTabs.tsx +++ b/rsconcept/frontend/src/pages/OssPage/MenuOssTabs.tsx @@ -2,7 +2,7 @@ import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; -import { useAuth } from '@/backend/auth/useAuth'; +import { useAuthSuspense } from '@/backend/auth/useAuth'; import { useIsProcessingOss } from '@/backend/oss/useIsProcessingOss'; import { IconAdmin, @@ -33,7 +33,7 @@ import { useOssEdit } from './OssEditContext'; function MenuOssTabs() { const controller = useOssEdit(); const router = useConceptNavigation(); - const { user } = useAuth(); + const { user, isAnonymous } = useAuthSuspense(); const isProcessing = useIsProcessingOss(); @@ -103,7 +103,7 @@ function MenuOssTabs() { - {user ? ( + {!isAnonymous ? ( } @@ -118,7 +118,7 @@ function MenuOssTabs() {
- {user ? ( + {!isAnonymous ? (
) : null} - {!user ? ( + {isAnonymous ? (
) : null} - {controller.isArchive && user ? ( + {controller.isArchive && !isAnonymous ? (
) : null} - {!user ? ( + {isAnonymous ? (