R: Move notifications into transport layer
This commit is contained in:
parent
421cd98429
commit
54a01b31b3
|
@ -4,7 +4,7 @@ export default defineConfig({
|
||||||
testDir: 'tests',
|
testDir: 'tests',
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
retries: process.env.CI ? 2 : 0,
|
retries: process.env.CI ? 2 : 0,
|
||||||
reporter: 'html',
|
reporter: 'list',
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'Desktop Chrome',
|
name: 'Desktop Chrome',
|
||||||
|
|
|
@ -10,9 +10,9 @@ interface UserButtonProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserButton({ onLogin, onClickUser }: UserButtonProps) {
|
function UserButton({ onLogin, onClickUser }: UserButtonProps) {
|
||||||
const { user } = useAuthSuspense();
|
const { user, isAnonymous } = useAuthSuspense();
|
||||||
const adminMode = usePreferencesStore(state => state.adminMode);
|
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||||
if (!user) {
|
if (isAnonymous) {
|
||||||
return (
|
return (
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
className='cc-fade-in'
|
className='cc-fade-in'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { useLogout } from '@/backend/auth/useLogout';
|
import { useLogout } from '@/backend/auth/useLogout';
|
||||||
import {
|
import {
|
||||||
IconAdmin,
|
IconAdmin,
|
||||||
|
@ -29,7 +29,7 @@ interface UserDropdownProps {
|
||||||
|
|
||||||
function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuthSuspense();
|
||||||
const { logout } = useLogout();
|
const { logout } = useLogout();
|
||||||
|
|
||||||
const darkMode = usePreferencesStore(state => state.darkMode);
|
const darkMode = usePreferencesStore(state => state.darkMode);
|
||||||
|
@ -77,7 +77,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
return (
|
return (
|
||||||
<Dropdown className='mt-[1.5rem] min-w-[18ch] max-w-[12rem]' stretchLeft isOpen={isOpen}>
|
<Dropdown className='mt-[1.5rem] min-w-[18ch] max-w-[12rem]' stretchLeft isOpen={isOpen}>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text={user?.username}
|
text={user.username}
|
||||||
title='Профиль пользователя'
|
title='Профиль пользователя'
|
||||||
icon={<IconUser size='1rem' />}
|
icon={<IconUser size='1rem' />}
|
||||||
onClick={navigateProfile}
|
onClick={navigateProfile}
|
||||||
|
@ -94,7 +94,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
title='Отображение иконок подсказок'
|
title='Отображение иконок подсказок'
|
||||||
onClick={toggleShowHelp}
|
onClick={toggleShowHelp}
|
||||||
/>
|
/>
|
||||||
{user?.is_staff ? (
|
{user.is_staff ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text={adminMode ? 'Админ: Вкл' : 'Админ: Выкл'}
|
text={adminMode ? 'Админ: Вкл' : 'Админ: Выкл'}
|
||||||
icon={adminMode ? <IconAdmin size='1rem' /> : <IconAdminOff size='1rem' />}
|
icon={adminMode ? <IconAdmin size='1rem' /> : <IconAdminOff size='1rem' />}
|
||||||
|
@ -102,7 +102,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
onClick={toggleAdminMode}
|
onClick={toggleAdminMode}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{user?.is_staff ? (
|
{user.is_staff ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='REST API' // prettier: split-line
|
text='REST API' // prettier: split-line
|
||||||
icon={<IconRESTapi size='1rem' />}
|
icon={<IconRESTapi size='1rem' />}
|
||||||
|
@ -110,7 +110,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
onClick={gotoRestApi}
|
onClick={gotoRestApi}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{user?.is_staff ? (
|
{user.is_staff ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='База данных' // prettier: split-line
|
text='База данных' // prettier: split-line
|
||||||
icon={<IconDatabase size='1rem' />}
|
icon={<IconDatabase size='1rem' />}
|
||||||
|
@ -124,7 +124,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
onClick={gotoIcons}
|
onClick={gotoIcons}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{user?.is_staff ? (
|
{user.is_staff ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Структура БД' // prettier: split-line
|
text='Структура БД' // prettier: split-line
|
||||||
icon={<IconDBStructure size='1rem' />}
|
icon={<IconDBStructure size='1rem' />}
|
||||||
|
|
|
@ -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 { AxiosError, AxiosRequestConfig } from 'axios';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { buildConstants } from '@/utils/buildConstants';
|
||||||
import { extractErrorMessage } from '@/utils/utils';
|
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 ================
|
// ================ Data transfer types ================
|
||||||
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
||||||
|
|
||||||
export interface IFrontRequest<RequestData, ResponseData> {
|
export interface IFrontRequest<RequestData, ResponseData> {
|
||||||
data?: RequestData;
|
data?: RequestData;
|
||||||
onSuccess?: DataCallback<ResponseData>;
|
successMessage?: string | ((data: ResponseData) => string);
|
||||||
onError?: (error: ErrorData) => void;
|
|
||||||
setLoading?: (loading: boolean) => void;
|
|
||||||
showError?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FrontPush<DataType> extends IFrontRequest<DataType, undefined> {
|
|
||||||
data: DataType;
|
|
||||||
}
|
|
||||||
export interface FrontPull<DataType> extends IFrontRequest<undefined, DataType> {
|
|
||||||
onSuccess: DataCallback<DataType>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FrontExchange<RequestData, ResponseData> extends IFrontRequest<RequestData, ResponseData> {
|
|
||||||
data: RequestData;
|
|
||||||
onSuccess: DataCallback<ResponseData>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FrontAction extends IFrontRequest<undefined, undefined> {}
|
|
||||||
|
|
||||||
export interface IAxiosRequest<RequestData, ResponseData> {
|
export interface IAxiosRequest<RequestData, ResponseData> {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
request: IFrontRequest<RequestData, ResponseData>;
|
request?: IFrontRequest<RequestData, ResponseData>;
|
||||||
options?: AxiosRequestConfig;
|
options?: AxiosRequestConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IAxiosGetRequest {
|
||||||
|
endpoint: string;
|
||||||
|
options?: AxiosRequestConfig;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}
|
||||||
|
|
||||||
// ================ Transport API calls ================
|
// ================ Transport API calls ================
|
||||||
export function AxiosGet<ResponseData>({ endpoint, request, options }: IAxiosRequest<undefined, ResponseData>) {
|
export function axiosGet<ResponseData>({ endpoint, options }: IAxiosGetRequest) {
|
||||||
request.setLoading?.(true);
|
return axiosInstance
|
||||||
axiosInstance
|
|
||||||
.get<ResponseData>(endpoint, options)
|
.get<ResponseData>(endpoint, options)
|
||||||
.then(response => {
|
.then(response => response.data)
|
||||||
request.setLoading?.(false);
|
|
||||||
request.onSuccess?.(response.data);
|
|
||||||
})
|
|
||||||
.catch((error: Error | AxiosError) => {
|
.catch((error: Error | AxiosError) => {
|
||||||
request.setLoading?.(false);
|
toast.error(extractErrorMessage(error));
|
||||||
if (request.showError) toast.error(extractErrorMessage(error));
|
console.error(error);
|
||||||
request.onError?.(error);
|
throw error;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AxiosPost<RequestData, ResponseData>({
|
export function axiosPost<RequestData, ResponseData = void>({
|
||||||
endpoint,
|
endpoint,
|
||||||
request,
|
request,
|
||||||
options
|
options
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
request.setLoading?.(true);
|
return axiosInstance
|
||||||
axiosInstance
|
.post<ResponseData>(endpoint, request?.data, options)
|
||||||
.post<ResponseData>(endpoint, request.data, options)
|
|
||||||
.then(response => {
|
.then(response => {
|
||||||
request.setLoading?.(false);
|
if (request?.successMessage) {
|
||||||
request.onSuccess?.(response.data);
|
if (typeof request.successMessage === 'string') {
|
||||||
})
|
toast.success(request.successMessage);
|
||||||
.catch((error: Error | AxiosError) => {
|
} else {
|
||||||
request.setLoading?.(false);
|
toast.success(request.successMessage(response.data));
|
||||||
if (request.showError) toast.error(extractErrorMessage(error));
|
}
|
||||||
request.onError?.(error);
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AxiosDelete<RequestData, ResponseData>({
|
|
||||||
endpoint,
|
|
||||||
request,
|
|
||||||
options
|
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
|
||||||
request.setLoading?.(true);
|
|
||||||
axiosInstance
|
|
||||||
.delete<ResponseData>(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<RequestData, ResponseData>({
|
|
||||||
endpoint,
|
|
||||||
request,
|
|
||||||
options
|
|
||||||
}: IAxiosRequest<RequestData, ResponseData>) {
|
|
||||||
request.setLoading?.(true);
|
|
||||||
axiosInstance
|
|
||||||
.patch<ResponseData>(endpoint, request.data, options)
|
|
||||||
.then(response => {
|
|
||||||
request.setLoading?.(false);
|
|
||||||
request.onSuccess?.(response.data);
|
|
||||||
return response.data;
|
return response.data;
|
||||||
})
|
})
|
||||||
.catch((error: Error | AxiosError) => {
|
.catch((error: Error | AxiosError) => {
|
||||||
request.setLoading?.(false);
|
toast.error(extractErrorMessage(error));
|
||||||
if (request.showError) toast.error(extractErrorMessage(error));
|
console.error(error);
|
||||||
request.onError?.(error);
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function axiosDelete<RequestData, ResponseData = void>({
|
||||||
|
endpoint,
|
||||||
|
request,
|
||||||
|
options
|
||||||
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
|
return axiosInstance
|
||||||
|
.delete<ResponseData>(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<RequestData, ResponseData = void>({
|
||||||
|
endpoint,
|
||||||
|
request,
|
||||||
|
options
|
||||||
|
}: IAxiosRequest<RequestData, ResponseData>) {
|
||||||
|
return axiosInstance
|
||||||
|
.patch<ResponseData>(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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { axiosInstance } from '@/backend/axiosInstance';
|
|
||||||
import { DELAYS } from '@/backend/configuration';
|
import { DELAYS } from '@/backend/configuration';
|
||||||
import { ICurrentUser } from '@/models/user';
|
import { ICurrentUser } from '@/models/user';
|
||||||
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
|
import { axiosGet, axiosPost } from '../apiTransport';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents login data, used to authenticate users.
|
* Represents login data, used to authenticate users.
|
||||||
|
@ -53,19 +55,41 @@ export const authApi = {
|
||||||
queryKey: [authApi.baseKey, 'user'],
|
queryKey: [authApi.baseKey, 'user'],
|
||||||
staleTime: DELAYS.staleLong,
|
staleTime: DELAYS.staleLong,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosInstance
|
axiosGet<ICurrentUser>({
|
||||||
.get<ICurrentUser>('/users/api/auth', {
|
endpoint: '/users/api/auth',
|
||||||
signal: meta.signal
|
options: { signal: meta.signal }
|
||||||
})
|
})
|
||||||
.then(response => (response.data.id === null ? null : response.data)),
|
|
||||||
placeholderData: null
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
logout: () => axiosInstance.post('/users/api/logout'),
|
logout: () => axiosPost({ endpoint: '/users/api/logout' }),
|
||||||
login: (data: IUserLoginDTO) => axiosInstance.post('/users/api/login', data),
|
|
||||||
changePassword: (data: IChangePasswordDTO) => axiosInstance.post('/users/api/change-password', data),
|
login: (data: IUserLoginDTO) =>
|
||||||
requestPasswordReset: (data: IRequestPasswordDTO) => axiosInstance.post('/users/api/password-reset', data),
|
axiosPost({
|
||||||
validatePasswordToken: (data: IPasswordTokenDTO) => axiosInstance.post('/users/api/password-reset/validate', data),
|
endpoint: '/users/api/login',
|
||||||
resetPassword: (data: IResetPasswordDTO) => axiosInstance.post('/users/api/password-reset/confirm', data)
|
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 }
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,12 +10,12 @@ export function useAuth() {
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
...authApi.getAuthQueryOptions()
|
...authApi.getAuthQueryOptions()
|
||||||
});
|
});
|
||||||
return { user, isLoading, error };
|
return { user, isLoading, isAnonymous: user?.id === null || user === undefined, error };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAuthSuspense() {
|
export function useAuthSuspense() {
|
||||||
const { data: user } = useSuspenseQuery({
|
const { data: user } = useSuspenseQuery({
|
||||||
...authApi.getAuthQueryOptions()
|
...authApi.getAuthQueryOptions()
|
||||||
});
|
});
|
||||||
return { user };
|
return { user, isAnonymous: user.id === 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;
|
|
||||||
});
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { axiosInstance } from '@/backend/axiosInstance';
|
|
||||||
import { ILexemeData, IWordFormPlain } from '@/models/language';
|
import { ILexemeData, IWordFormPlain } from '@/models/language';
|
||||||
|
|
||||||
|
import { axiosPost } from '../apiTransport';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents API result for text output.
|
* Represents API result for text output.
|
||||||
*/
|
*/
|
||||||
|
@ -12,15 +13,18 @@ export const cctextApi = {
|
||||||
baseKey: 'cctext',
|
baseKey: 'cctext',
|
||||||
|
|
||||||
inflectText: (data: IWordFormPlain) =>
|
inflectText: (data: IWordFormPlain) =>
|
||||||
axiosInstance //
|
axiosPost<IWordFormPlain, ITextResult>({
|
||||||
.post<ITextResult>('/api/cctext/inflect', data)
|
endpoint: '/api/cctext/inflect',
|
||||||
.then(response => response.data),
|
request: { data: data }
|
||||||
|
}),
|
||||||
parseText: (data: { text: string }) =>
|
parseText: (data: { text: string }) =>
|
||||||
axiosInstance //
|
axiosPost<{ text: string }, ITextResult>({
|
||||||
.post<ITextResult>('/api/cctext/parse', data)
|
endpoint: '/api/cctext/parse',
|
||||||
.then(response => response.data),
|
request: { data: data }
|
||||||
|
}),
|
||||||
generateLexeme: (data: { text: string }) =>
|
generateLexeme: (data: { text: string }) =>
|
||||||
axiosInstance //
|
axiosPost<{ text: string }, ILexemeData>({
|
||||||
.post<ILexemeData>('/api/cctext/generate-lexeme', data)
|
endpoint: '/api/cctext/generate-lexeme',
|
||||||
.then(response => response.data)
|
request: { data: data }
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { axiosInstance } from '@/backend/axiosInstance';
|
|
||||||
import { DELAYS } from '@/backend/configuration';
|
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 { ConstituentaID, IRSFormData } from '@/models/rsform';
|
||||||
import { UserID } from '@/models/user';
|
import { UserID } from '@/models/user';
|
||||||
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
|
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '../apiTransport';
|
||||||
import { ossApi } from '../oss/api';
|
import { ossApi } from '../oss/api';
|
||||||
import { rsformsApi } from '../rsform/api';
|
import { rsformsApi } from '../rsform/api';
|
||||||
|
|
||||||
|
@ -59,87 +68,140 @@ export const libraryApi = {
|
||||||
baseKey: 'library',
|
baseKey: 'library',
|
||||||
libraryListKey: ['library', 'list'],
|
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)
|
|
||||||
}),
|
|
||||||
getItemQueryOptions: ({ itemID, itemType }: { itemID: LibraryItemID; itemType: LibraryItemType }) => {
|
getItemQueryOptions: ({ itemID, itemType }: { itemID: LibraryItemID; itemType: LibraryItemType }) => {
|
||||||
return itemType === LibraryItemType.RSFORM
|
return itemType === LibraryItemType.RSFORM
|
||||||
? rsformsApi.getRSFormQueryOptions({ itemID })
|
? rsformsApi.getRSFormQueryOptions({ itemID })
|
||||||
: ossApi.getOssQueryOptions({ itemID });
|
: ossApi.getOssQueryOptions({ itemID });
|
||||||
},
|
},
|
||||||
|
getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: libraryApi.libraryListKey,
|
||||||
|
staleTime: DELAYS.staleMedium,
|
||||||
|
queryFn: meta =>
|
||||||
|
axiosGet<ILibraryItem[]>({
|
||||||
|
endpoint: isAdmin ? '/api/library/all' : '/api/library/active',
|
||||||
|
options: { signal: meta.signal }
|
||||||
|
})
|
||||||
|
}),
|
||||||
getTemplatesQueryOptions: () =>
|
getTemplatesQueryOptions: () =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: [libraryApi.baseKey, 'templates'],
|
queryKey: [libraryApi.baseKey, 'templates'],
|
||||||
staleTime: DELAYS.staleMedium,
|
staleTime: DELAYS.staleMedium,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosInstance
|
axiosGet<ILibraryItem[]>({
|
||||||
.get<ILibraryItem[]>('/api/library/templates', {
|
endpoint: '/api/library/templates',
|
||||||
signal: meta.signal
|
options: { signal: meta.signal }
|
||||||
})
|
})
|
||||||
.then(response => response.data)
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createItem: (data: ILibraryCreateDTO) =>
|
createItem: (data: ILibraryCreateDTO) =>
|
||||||
data.file
|
axiosPost<ILibraryCreateDTO, ILibraryItem>({
|
||||||
? axiosInstance
|
endpoint: !data.file ? '/api/library' : '/api/rsforms/create-detailed',
|
||||||
.post<ILibraryItem>('/api/rsforms/create-detailed', data, {
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.newLibraryItem
|
||||||
|
},
|
||||||
|
options: !data.file
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data'
|
'Content-Type': 'multipart/form-data'
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.then(response => response.data)
|
}),
|
||||||
: axiosInstance //
|
|
||||||
.post<ILibraryItem>('/api/library', data)
|
|
||||||
.then(response => response.data),
|
|
||||||
|
|
||||||
updateItem: (data: ILibraryUpdateDTO) =>
|
updateItem: (data: ILibraryUpdateDTO) =>
|
||||||
axiosInstance //
|
axiosPatch<ILibraryUpdateDTO, ILibraryItem>({
|
||||||
.patch<ILibraryItem>(`/api/library/${data.id}`, data)
|
endpoint: `/api/library/${data.id}`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
setOwner: (data: { itemID: LibraryItemID; owner: UserID }) =>
|
data: data,
|
||||||
axiosInstance //
|
successMessage: information.changesSaved
|
||||||
.patch(`/api/library/${data.itemID}/set-owner`, { user: data.owner }),
|
}
|
||||||
setLocation: (data: { itemID: LibraryItemID; location: string }) =>
|
}),
|
||||||
axiosInstance //
|
setOwner: ({ itemID, owner }: { itemID: LibraryItemID; owner: UserID }) =>
|
||||||
.patch(`/api/library/${data.itemID}/set-location`, { location: data.location }),
|
axiosPatch({
|
||||||
setAccessPolicy: (data: { itemID: LibraryItemID; policy: AccessPolicy }) =>
|
endpoint: `/api/library/${itemID}/set-owner`,
|
||||||
axiosInstance //
|
request: {
|
||||||
.patch(`/api/library/${data.itemID}/set-access-policy`, { access_policy: data.policy }),
|
data: { user: owner },
|
||||||
setEditors: (data: { itemID: LibraryItemID; editors: UserID[] }) =>
|
successMessage: information.changesSaved
|
||||||
axiosInstance //
|
}
|
||||||
.patch(`/api/library/${data.itemID}/set-editors`, { users: data.editors }),
|
}),
|
||||||
|
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) =>
|
deleteItem: (target: LibraryItemID) =>
|
||||||
axiosInstance //
|
axiosDelete({
|
||||||
.delete(`/api/library/${target}`),
|
endpoint: `/api/library/${target}`,
|
||||||
|
request: {
|
||||||
|
successMessage: information.itemDestroyed
|
||||||
|
}
|
||||||
|
}),
|
||||||
cloneItem: (data: IRSFormCloneDTO) =>
|
cloneItem: (data: IRSFormCloneDTO) =>
|
||||||
axiosInstance //
|
axiosPost<IRSFormCloneDTO, IRSFormData>({
|
||||||
.post<IRSFormData>(`/api/library/${data.id}/clone`, data)
|
endpoint: `/api/library/${data.id}/clone`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: newSchema => information.cloneComplete(newSchema.alias)
|
||||||
|
}
|
||||||
|
}),
|
||||||
renameLocation: (data: IRenameLocationDTO) =>
|
renameLocation: (data: IRenameLocationDTO) =>
|
||||||
axiosInstance //
|
axiosPatch({
|
||||||
.patch('/api/library/rename-location', data),
|
endpoint: '/api/library/rename-location',
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.locationRenamed
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
versionCreate: (data: { itemID: LibraryItemID; data: IVersionData }) =>
|
versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionData }) =>
|
||||||
axiosInstance //
|
axiosPost<IVersionData, IVersionCreatedResponse>({
|
||||||
.post<IVersionCreatedResponse>(`/api/library/${data.itemID}/versions`, data.data)
|
endpoint: `/api/library/${itemID}/create-version`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
versionRestore: (data: { itemID: LibraryItemID; versionID: VersionID }) =>
|
data: data,
|
||||||
axiosInstance //
|
successMessage: information.newVersion(data.version)
|
||||||
.patch<IRSFormData>(`/api/versions/${data.versionID}/restore`)
|
}
|
||||||
.then(response => response.data),
|
}),
|
||||||
versionUpdate: (data: { itemID: LibraryItemID; versionID: VersionID; data: IVersionData }) =>
|
versionRestore: ({ versionID }: { versionID: VersionID }) =>
|
||||||
axiosInstance //
|
axiosPatch<undefined, IRSFormData>({
|
||||||
.patch(`/api/versions/${data.versionID}`, data.data),
|
endpoint: `/api/versions/${versionID}/restore`,
|
||||||
|
request: {
|
||||||
|
successMessage: information.versionRestored
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
versionUpdate: ({ versionID, data }: { versionID: VersionID; data: IVersionData }) =>
|
||||||
|
axiosPatch<IVersionData, IVersionInfo>({
|
||||||
|
endpoint: `/api/versions/${versionID}`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.changesSaved
|
||||||
|
}
|
||||||
|
}),
|
||||||
versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) =>
|
versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) =>
|
||||||
axiosInstance //
|
axiosDelete({
|
||||||
.delete(`/api/versions/${data.versionID}`)
|
endpoint: `/api/versions/${data.versionID}`,
|
||||||
|
request: {
|
||||||
|
successMessage: information.versionDestroyed
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ export function useLibrary() {
|
||||||
// NOTE: Using suspense here to avoid duplicated library data requests
|
// NOTE: Using suspense here to avoid duplicated library data requests
|
||||||
const { user } = useAuthSuspense();
|
const { user } = useAuthSuspense();
|
||||||
const { data: items, isLoading } = useQuery({
|
const { data: items, isLoading } = useQuery({
|
||||||
...libraryApi.getLibraryQueryOptions({ isAdmin: user?.is_staff ?? false })
|
...libraryApi.getLibraryQueryOptions({ isAdmin: user.is_staff })
|
||||||
});
|
});
|
||||||
return { items: items ?? [], isLoading };
|
return { items: items ?? [], isLoading };
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,6 @@ export const useSetAccessPolicy = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setAccessPolicy: (
|
setAccessPolicy: (data: { itemID: LibraryItemID; policy: AccessPolicy }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
policy: AccessPolicy;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { rsformsApi } from '@/backend/rsform/api';
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { UserID } from '@/models/user';
|
import { UserID } from '@/models/user';
|
||||||
|
|
||||||
|
import { ossApi } from '../oss/api';
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
export const useSetEditors = () => {
|
export const useSetEditors = () => {
|
||||||
|
@ -12,28 +13,27 @@ export const useSetEditors = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'set-location'],
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setEditors,
|
mutationFn: libraryApi.setEditors,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
|
||||||
prev?.map(item => (item.id === variables.itemID ? { ...item, editors: variables.editors } : item))
|
const ossData = client.getQueryData(ossKey);
|
||||||
);
|
if (ossData) {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey, prev => {
|
client.setQueryData(ossKey, { ...ossData, editors: variables.editors });
|
||||||
if (!prev) {
|
Promise.allSettled([
|
||||||
return undefined;
|
...ossData.items.map(item => {
|
||||||
}
|
if (!item.result) {
|
||||||
return {
|
return;
|
||||||
...prev,
|
}
|
||||||
editors: variables.editors
|
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 {
|
return {
|
||||||
setEditors: (
|
setEditors: (data: { itemID: LibraryItemID; editors: UserID[] }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
editors: UserID[];
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,28 @@ export const useSetLocation = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'set-location'],
|
mutationKey: [libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setLocation,
|
mutationFn: libraryApi.setLocation,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
|
// const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
|
||||||
|
// const 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) =>
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item))
|
prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item))
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,12 +31,6 @@ export const useSetOwner = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setOwner: (
|
setOwner: (data: { itemID: LibraryItemID; owner: UserID }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
owner: UserID;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { rsformsApi } from '@/backend/rsform/api';
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
import { ILibraryItem } from '@/models/library';
|
import { ILibraryItem } from '@/models/library';
|
||||||
|
|
||||||
|
@ -13,7 +12,7 @@ export const useUpdateItem = () => {
|
||||||
mutationFn: libraryApi.updateItem,
|
mutationFn: libraryApi.updateItem,
|
||||||
onSuccess: (data: ILibraryItem) => {
|
onSuccess: (data: ILibraryItem) => {
|
||||||
client
|
client
|
||||||
.cancelQueries({ queryKey: [libraryApi.libraryListKey] })
|
.cancelQueries({ queryKey: libraryApi.libraryListKey })
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === data.id ? data : item))
|
prev?.map(item => (item.id === data.id ? data : item))
|
||||||
|
@ -26,9 +25,6 @@ export const useUpdateItem = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
updateItem: (
|
updateItem: (data: ILibraryUpdateDTO) => mutation.mutate(data)
|
||||||
data: ILibraryUpdateDTO, //
|
|
||||||
onSuccess?: DataCallback<ILibraryItem>
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
import { DataCallback } from '@/backend/apiTransport';
|
||||||
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
import { rsformsApi } from '@/backend/rsform/api';
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
import { IVersionData, LibraryItemID } from '@/models/library';
|
import { IVersionData, LibraryItemID, VersionID } from '@/models/library';
|
||||||
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export const useVersionCreate = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'create-version'],
|
mutationKey: [libraryApi.baseKey, 'create-version'],
|
||||||
mutationFn: libraryApi.versionCreate,
|
mutationFn: libraryApi.versionCreate,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey], data.schema);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
||||||
updateTimestamp(data.schema.id);
|
updateTimestamp(data.schema.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -24,7 +24,7 @@ export const useVersionCreate = () => {
|
||||||
itemID: LibraryItemID; //
|
itemID: LibraryItemID; //
|
||||||
data: IVersionData;
|
data: IVersionData;
|
||||||
},
|
},
|
||||||
onSuccess?: DataCallback<IVersionData>
|
onSuccess?: DataCallback<VersionID>
|
||||||
) => mutation.mutate(data, { onSuccess: () => onSuccess?.(data.data) })
|
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.version) })
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,11 +13,14 @@ export const useVersionDelete = () => {
|
||||||
mutationFn: libraryApi.versionDelete,
|
mutationFn: libraryApi.versionDelete,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
client.setQueryData(
|
client.setQueryData(
|
||||||
[rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey],
|
rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey,
|
||||||
(prev: IRSFormData) => ({
|
(prev: IRSFormData | undefined) =>
|
||||||
...prev,
|
!prev
|
||||||
versions: prev.versions.filter(version => version.id !== variables.versionID)
|
? undefined
|
||||||
})
|
: {
|
||||||
|
...prev,
|
||||||
|
versions: prev.versions.filter(version => version.id !== variables.versionID)
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
import { rsformsApi } from '@/backend/rsform/api';
|
import { rsformsApi } from '@/backend/rsform/api';
|
||||||
import { LibraryItemID, VersionID } from '@/models/library';
|
import { VersionID } from '@/models/library';
|
||||||
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
|
||||||
|
@ -13,17 +13,11 @@ export const useVersionRestore = () => {
|
||||||
mutationKey: [libraryApi.baseKey, 'restore-version'],
|
mutationKey: [libraryApi.baseKey, 'restore-version'],
|
||||||
mutationFn: libraryApi.versionRestore,
|
mutationFn: libraryApi.versionRestore,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
versionRestore: (
|
versionRestore: (data: { versionID: VersionID }, onSuccess?: () => void) => mutation.mutate(data, { onSuccess })
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
versionID: VersionID;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { rsformsApi } from '@/backend/rsform/api';
|
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 { IRSFormData } from '@/models/rsform';
|
||||||
|
|
||||||
import { libraryApi } from './api';
|
import { libraryApi } from './api';
|
||||||
|
@ -11,28 +11,24 @@ export const useVersionUpdate = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [libraryApi.baseKey, 'update-version'],
|
mutationKey: [libraryApi.baseKey, 'update-version'],
|
||||||
mutationFn: libraryApi.versionUpdate,
|
mutationFn: libraryApi.versionUpdate,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: data => {
|
||||||
client.setQueryData(
|
client.setQueryData(
|
||||||
[rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey],
|
rsformsApi.getRSFormQueryOptions({ itemID: data.item }).queryKey,
|
||||||
(prev: IRSFormData) => ({
|
(prev: IRSFormData | undefined) =>
|
||||||
...prev,
|
!prev
|
||||||
versions: prev.versions.map(version =>
|
? undefined
|
||||||
version.id === variables.versionID
|
: {
|
||||||
? { ...version, description: variables.data.description, version: variables.data.version }
|
...prev,
|
||||||
: version
|
versions: prev.versions.map(version =>
|
||||||
)
|
version.id === data.id
|
||||||
})
|
? { ...version, description: data.description, version: data.version }
|
||||||
|
: version
|
||||||
|
)
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
versionUpdate: (
|
versionUpdate: (data: { versionID: VersionID; data: IVersionData }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
versionID: VersionID;
|
|
||||||
data: IVersionData;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
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 { DELAYS } from '@/backend/configuration';
|
||||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
import {
|
import {
|
||||||
|
@ -12,6 +12,7 @@ import {
|
||||||
OperationType
|
OperationType
|
||||||
} from '@/models/oss';
|
} from '@/models/oss';
|
||||||
import { ConstituentaID, IConstituentaReference, ITargetCst } from '@/models/rsform';
|
import { ConstituentaID, IConstituentaReference, ITargetCst } from '@/models/rsform';
|
||||||
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents {@link IOperation} data, used in creation process.
|
* Represents {@link IOperation} data, used in creation process.
|
||||||
|
@ -101,48 +102,90 @@ export const ossApi = {
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
!itemID
|
!itemID
|
||||||
? undefined
|
? undefined
|
||||||
: axiosInstance
|
: axiosGet<IOperationSchemaData>({
|
||||||
.get<IOperationSchemaData>(`/api/oss/${itemID}/details`, {
|
endpoint: `/api/oss/${itemID}/details`,
|
||||||
signal: meta.signal
|
options: { signal: meta.signal }
|
||||||
})
|
})
|
||||||
.then(response => response.data)
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updatePositions: (data: { itemID: LibraryItemID; positions: IOperationPosition[] }) =>
|
updatePositions: ({
|
||||||
axiosInstance //
|
itemID,
|
||||||
.patch(`/api/oss/${data.itemID}/update-positions`, { positions: data.positions }),
|
positions,
|
||||||
operationCreate: (data: { itemID: LibraryItemID; data: IOperationCreateDTO }) =>
|
isSilent
|
||||||
axiosInstance //
|
}: {
|
||||||
.post<IOperationCreatedResponse>(`/api/oss/${data.itemID}/create-operation`, data.data)
|
itemID: LibraryItemID;
|
||||||
.then(response => response.data),
|
positions: IOperationPosition[];
|
||||||
operationDelete: (data: { itemID: LibraryItemID; data: IOperationDeleteDTO }) =>
|
isSilent?: boolean;
|
||||||
axiosInstance //
|
}) =>
|
||||||
.patch<IOperationSchemaData>(`/api/oss/${data.itemID}/delete-operation`, data.data)
|
axiosPatch({
|
||||||
.then(response => response.data),
|
endpoint: `/api/oss/${itemID}/update-positions`,
|
||||||
inputCreate: (data: { itemID: LibraryItemID; data: ITargetOperation }) =>
|
request: {
|
||||||
axiosInstance //
|
data: { positions: positions },
|
||||||
.patch<IInputCreatedResponse>(`/api/oss/${data.itemID}/create-input`, data.data)
|
successMessage: isSilent ? undefined : information.changesSaved
|
||||||
.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 }) =>
|
operationCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationCreateDTO }) =>
|
||||||
axiosInstance //
|
axiosPost<IOperationCreateDTO, IOperationCreatedResponse>({
|
||||||
.post<IOperationSchemaData>(`/api/oss/${data.itemID}/relocate-constituents`, data.data)
|
endpoint: `/api/oss/${itemID}/create-operation`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: response => information.newOperation(response.new_operation.alias)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
operationDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationDeleteDTO }) =>
|
||||||
|
axiosDelete<IOperationDeleteDTO, IOperationSchemaData>({
|
||||||
|
endpoint: `/api/oss/${itemID}/delete-operation`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.operationDestroyed
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
inputCreate: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetOperation }) =>
|
||||||
|
axiosPatch<ITargetOperation, IInputCreatedResponse>({
|
||||||
|
endpoint: `/api/oss/${itemID}/create-input`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.newLibraryItem
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
inputUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: IInputUpdateDTO }) =>
|
||||||
|
axiosPatch<IInputUpdateDTO, IOperationSchemaData>({
|
||||||
|
endpoint: `/api/oss/${itemID}/set-input`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.changesSaved
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
operationUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationUpdateDTO }) =>
|
||||||
|
axiosPatch<IOperationUpdateDTO, IOperationSchemaData>({
|
||||||
|
endpoint: `/api/oss/${itemID}/update-operation`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.changesSaved
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
operationExecute: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetOperation }) =>
|
||||||
|
axiosPost<ITargetOperation, IOperationSchemaData>({
|
||||||
|
endpoint: `/api/oss/${itemID}/execute-operation`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.operationExecuted
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
relocateConstituents: ({ itemID, data }: { itemID: LibraryItemID; data: ICstRelocateDTO }) =>
|
||||||
|
axiosPost<ICstRelocateDTO, IOperationSchemaData>({
|
||||||
|
endpoint: `/api/oss/${itemID}/relocate-constituents`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.changesSaved
|
||||||
|
}
|
||||||
|
}),
|
||||||
getPredecessor: (data: ITargetCst) =>
|
getPredecessor: (data: ITargetCst) =>
|
||||||
axiosInstance //
|
axiosPost<ITargetCst, IConstituentaReference>({
|
||||||
.post<IConstituentaReference>(`/api/oss/get-predecessor`, data)
|
endpoint: '/api/oss/get-predecessor',
|
||||||
.then(response => response.data)
|
request: { data: data }
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,7 @@ export const useInputCreate = () => {
|
||||||
mutationKey: [ossApi.baseKey, 'input-create'],
|
mutationKey: [ossApi.baseKey, 'input-create'],
|
||||||
mutationFn: ossApi.inputCreate,
|
mutationFn: ossApi.inputCreate,
|
||||||
onSuccess: async data => {
|
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] });
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,17 +11,11 @@ export const useInputUpdate = () => {
|
||||||
mutationKey: [ossApi.baseKey, 'input-update'],
|
mutationKey: [ossApi.baseKey, 'input-update'],
|
||||||
mutationFn: ossApi.inputUpdate,
|
mutationFn: ossApi.inputUpdate,
|
||||||
onSuccess: async data => {
|
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] });
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
inputUpdate: (
|
inputUpdate: (data: { itemID: LibraryItemID; data: IInputUpdateDTO }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: IInputUpdateDTO;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const useOperationCreate = () => {
|
||||||
mutationKey: [ossApi.baseKey, 'operation-create'],
|
mutationKey: [ossApi.baseKey, 'operation-create'],
|
||||||
mutationFn: ossApi.operationCreate,
|
mutationFn: ossApi.operationCreate,
|
||||||
onSuccess: data => {
|
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);
|
updateTimestamp(data.oss.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,17 +11,11 @@ export const useOperationDelete = () => {
|
||||||
mutationKey: [ossApi.baseKey, 'operation-delete'],
|
mutationKey: [ossApi.baseKey, 'operation-delete'],
|
||||||
mutationFn: ossApi.operationDelete,
|
mutationFn: ossApi.operationDelete,
|
||||||
onSuccess: async data => {
|
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] });
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
operationDelete: (
|
operationDelete: (data: { itemID: LibraryItemID; data: IOperationDeleteDTO }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: IOperationDeleteDTO;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,17 +11,11 @@ export const useOperationExecute = () => {
|
||||||
mutationKey: [ossApi.baseKey, 'operation-execute'],
|
mutationKey: [ossApi.baseKey, 'operation-execute'],
|
||||||
mutationFn: ossApi.operationExecute,
|
mutationFn: ossApi.operationExecute,
|
||||||
onSuccess: async data => {
|
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] });
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
operationExecute: (
|
operationExecute: (data: { itemID: LibraryItemID; data: ITargetOperation }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: ITargetOperation;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,17 +11,11 @@ export const useOperationUpdate = () => {
|
||||||
mutationKey: [ossApi.baseKey, 'operation-update'],
|
mutationKey: [ossApi.baseKey, 'operation-update'],
|
||||||
mutationFn: ossApi.operationUpdate,
|
mutationFn: ossApi.operationUpdate,
|
||||||
onSuccess: async data => {
|
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] });
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
operationUpdate: (
|
operationUpdate: (data: { itemID: LibraryItemID; data: IOperationUpdateDTO }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: IOperationUpdateDTO;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,17 +11,11 @@ export const useRelocateConstituents = () => {
|
||||||
mutationKey: [ossApi.baseKey, 'relocate-constituents'],
|
mutationKey: [ossApi.baseKey, 'relocate-constituents'],
|
||||||
mutationFn: ossApi.relocateConstituents,
|
mutationFn: ossApi.relocateConstituents,
|
||||||
onSuccess: async data => {
|
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] });
|
await client.invalidateQueries({ queryKey: [libraryApi.libraryListKey] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
relocateConstituents: (
|
relocateConstituents: (data: { itemID: LibraryItemID; data: ICstRelocateDTO }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: ICstRelocateDTO;
|
|
||||||
},
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ export const useUpdatePositions = () => {
|
||||||
data: {
|
data: {
|
||||||
itemID: LibraryItemID; //
|
itemID: LibraryItemID; //
|
||||||
positions: IOperationPosition[];
|
positions: IOperationPosition[];
|
||||||
|
isSilent?: boolean;
|
||||||
},
|
},
|
||||||
onSuccess?: () => void
|
onSuccess?: () => void
|
||||||
) => mutation.mutate(data, { onSuccess })
|
) => mutation.mutate(data, { onSuccess })
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
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 { DELAYS } from '@/backend/configuration';
|
||||||
import { LibraryItemID, VersionID } from '@/models/library';
|
import { LibraryItemID, VersionID } from '@/models/library';
|
||||||
import { ICstSubstitute, ICstSubstitutions } from '@/models/oss';
|
import { ICstSubstitute, ICstSubstitutions } from '@/models/oss';
|
||||||
|
@ -14,6 +14,7 @@ import {
|
||||||
TermForm
|
TermForm
|
||||||
} from '@/models/rsform';
|
} from '@/models/rsform';
|
||||||
import { IExpressionParse } from '@/models/rslang';
|
import { IExpressionParse } from '@/models/rslang';
|
||||||
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents data, used for uploading {@link IRSForm} as file.
|
* Represents data, used for uploading {@link IRSForm} as file.
|
||||||
|
@ -116,76 +117,108 @@ export const rsformsApi = {
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
!itemID
|
!itemID
|
||||||
? undefined
|
? undefined
|
||||||
: axiosInstance
|
: axiosGet<IRSFormData>({
|
||||||
.get<IRSFormData>(
|
endpoint: version ? `/api/library/${itemID}/versions/${version}` : `/api/rsforms/${itemID}/details`,
|
||||||
version ? `/api/library/${itemID}/versions/${version}` : `/api/rsforms/${itemID}/details`,
|
options: { signal: meta.signal }
|
||||||
{
|
})
|
||||||
signal: meta.signal
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(response => response.data)
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
download: ({ itemID, version }: { itemID: LibraryItemID; version?: VersionID }) =>
|
download: ({ itemID, version }: { itemID: LibraryItemID; version?: VersionID }) =>
|
||||||
axiosInstance //
|
axiosGet<Blob>({
|
||||||
.get<Blob>(version ? `/api/versions/${version}/export-file` : `/api/rsforms/${itemID}/export-trs`, {
|
endpoint: version ? `/api/versions/${version}/export-file` : `/api/rsforms/${itemID}/export-trs`,
|
||||||
responseType: 'blob'
|
options: { responseType: 'blob' }
|
||||||
})
|
}),
|
||||||
.then(response => response.data),
|
|
||||||
upload: (data: IRSFormUploadDTO) =>
|
upload: (data: IRSFormUploadDTO) =>
|
||||||
axiosInstance //
|
axiosPatch<IRSFormUploadDTO, IRSFormData>({
|
||||||
.patch<IRSFormData>(`/api/rsforms/${data.itemID}/load-trs`, data, {
|
endpoint: `/api/rsforms/${data.itemID}/load-trs`,
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.uploadSuccess
|
||||||
|
},
|
||||||
|
options: {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data'
|
'Content-Type': 'multipart/form-data'
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.then(response => response.data),
|
}),
|
||||||
|
|
||||||
cstCreate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstCreateDTO }) =>
|
cstCreate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstCreateDTO }) =>
|
||||||
axiosInstance //
|
axiosPost<ICstCreateDTO, ICstCreatedResponse>({
|
||||||
.post<ICstCreatedResponse>(`/api/rsforms/${itemID}/create-cst`, data)
|
endpoint: `/api/rsforms/${itemID}/create-cst`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: response => information.newConstituent(response.new_cst.alias)
|
||||||
|
}
|
||||||
|
}),
|
||||||
cstUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstUpdateDTO }) =>
|
cstUpdate: ({ itemID, data }: { itemID: LibraryItemID; data: ICstUpdateDTO }) =>
|
||||||
axiosInstance //
|
axiosPatch<ICstUpdateDTO, IConstituentaMeta>({
|
||||||
.patch<IConstituentaMeta>(`/api/rsforms/${itemID}/update-cst`, data)
|
endpoint: `/api/rsforms/${itemID}/update-cst`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.changesSaved
|
||||||
|
}
|
||||||
|
}),
|
||||||
cstDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IConstituentaList }) =>
|
cstDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IConstituentaList }) =>
|
||||||
axiosInstance //
|
axiosDelete<IConstituentaList, IRSFormData>({
|
||||||
.patch<IRSFormData>(`/api/rsforms/${itemID}/delete-multiple-cst`, data)
|
endpoint: `/api/rsforms/${itemID}/delete-multiple-cst`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.constituentsDestroyed(data.items.length)
|
||||||
|
}
|
||||||
|
}),
|
||||||
cstRename: ({ itemID, data }: { itemID: LibraryItemID; data: ICstRenameDTO }) =>
|
cstRename: ({ itemID, data }: { itemID: LibraryItemID; data: ICstRenameDTO }) =>
|
||||||
axiosInstance //
|
axiosPatch<ICstRenameDTO, ICstCreatedResponse>({
|
||||||
.patch<ICstCreatedResponse>(`/api/rsforms/${itemID}/rename-cst`, data)
|
endpoint: `/api/rsforms/${itemID}/rename-cst`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.changesSaved
|
||||||
|
}
|
||||||
|
}),
|
||||||
cstSubstitute: ({ itemID, data }: { itemID: LibraryItemID; data: ICstSubstitutions }) =>
|
cstSubstitute: ({ itemID, data }: { itemID: LibraryItemID; data: ICstSubstitutions }) =>
|
||||||
axiosInstance //
|
axiosPatch<ICstSubstitutions, IRSFormData>({
|
||||||
.patch<IRSFormData>(`/api/rsforms/${itemID}/substitute`, data)
|
endpoint: `/api/rsforms/${itemID}/substitute`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.substituteSingle
|
||||||
|
}
|
||||||
|
}),
|
||||||
cstMove: ({ itemID, data }: { itemID: LibraryItemID; data: ICstMoveDTO }) =>
|
cstMove: ({ itemID, data }: { itemID: LibraryItemID; data: ICstMoveDTO }) =>
|
||||||
axiosInstance //
|
axiosPatch<ICstMoveDTO, IRSFormData>({
|
||||||
.patch<IRSFormData>(`/api/rsforms/${itemID}/move-cst`, data)
|
endpoint: `/api/rsforms/${itemID}/move-cst`,
|
||||||
.then(response => response.data),
|
request: { data: data }
|
||||||
|
}),
|
||||||
|
|
||||||
produceStructure: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetCst }) =>
|
produceStructure: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetCst }) =>
|
||||||
axiosInstance //
|
axiosPost<ITargetCst, IProduceStructureResponse>({
|
||||||
.post<IProduceStructureResponse>(`/api/rsforms/${itemID}/produce-structure`, data)
|
endpoint: `/api/rsforms/${itemID}/produce-structure`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: response => information.addedConstituents(response.cst_list.length)
|
||||||
|
}
|
||||||
|
}),
|
||||||
inlineSynthesis: ({ itemID, data }: { itemID: LibraryItemID; data: IInlineSynthesisDTO }) =>
|
inlineSynthesis: ({ itemID, data }: { itemID: LibraryItemID; data: IInlineSynthesisDTO }) =>
|
||||||
axiosInstance //
|
axiosPost<IInlineSynthesisDTO, IRSFormData>({
|
||||||
.post<IRSFormData>(`/api/rsforms/${itemID}/inline-synthesis`, data)
|
endpoint: `/api/rsforms/${itemID}/inline-synthesis`,
|
||||||
.then(response => response.data),
|
request: {
|
||||||
restoreOrder: (itemID: LibraryItemID) =>
|
data: data,
|
||||||
axiosInstance //
|
successMessage: information.inlineSynthesisComplete
|
||||||
.patch<IRSFormData>(`/api/rsforms/${itemID}/restore-order`)
|
}
|
||||||
.then(response => response.data),
|
}),
|
||||||
resetAliases: (itemID: LibraryItemID) =>
|
restoreOrder: ({ itemID }: { itemID: LibraryItemID }) =>
|
||||||
axiosInstance //
|
axiosPatch<undefined, IRSFormData>({
|
||||||
.patch<IRSFormData>(`/api/rsforms/${itemID}/reset-aliases`)
|
endpoint: `/api/rsforms/${itemID}/restore-order`,
|
||||||
.then(response => response.data),
|
request: { successMessage: information.reorderComplete }
|
||||||
|
}),
|
||||||
|
resetAliases: ({ itemID }: { itemID: LibraryItemID }) =>
|
||||||
|
axiosPatch<undefined, IRSFormData>({
|
||||||
|
endpoint: `/api/rsforms/${itemID}/reset-aliases`,
|
||||||
|
request: { successMessage: information.reindexComplete }
|
||||||
|
}),
|
||||||
|
|
||||||
checkConstituenta: ({ itemID, data }: { itemID: LibraryItemID; data: ICheckConstituentaDTO }) =>
|
checkConstituenta: ({ itemID, data }: { itemID: LibraryItemID; data: ICheckConstituentaDTO }) =>
|
||||||
axiosInstance //
|
axiosPost<ICheckConstituentaDTO, IExpressionParse>({
|
||||||
.post<IExpressionParse>(`/api/rsforms/${itemID}/check-constituenta`, data)
|
endpoint: `/api/rsforms/${itemID}/check-constituenta`,
|
||||||
.then(response => response.data)
|
request: { data: data }
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const useCstCreate = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'create-cst'],
|
mutationKey: [rsformsApi.baseKey, 'create-cst'],
|
||||||
mutationFn: rsformsApi.cstCreate,
|
mutationFn: rsformsApi.cstCreate,
|
||||||
onSuccess: data => {
|
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);
|
updateTimestamp(data.schema.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const useCstDelete = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'delete-multiple-cst'],
|
mutationKey: [rsformsApi.baseKey, 'delete-multiple-cst'],
|
||||||
mutationFn: rsformsApi.cstDelete,
|
mutationFn: rsformsApi.cstDelete,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export const useCstMove = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'move-cst'],
|
mutationKey: [rsformsApi.baseKey, 'move-cst'],
|
||||||
mutationFn: rsformsApi.cstMove,
|
mutationFn: rsformsApi.cstMove,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
import { LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { IConstituentaMeta } from '@/models/rsform';
|
|
||||||
|
|
||||||
import { DataCallback } from '../apiTransport';
|
|
||||||
import { ICstRenameDTO, rsformsApi } from './api';
|
import { ICstRenameDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
export const useCstRename = () => {
|
export const useCstRename = () => {
|
||||||
|
@ -14,18 +12,12 @@ export const useCstRename = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'rename-cst'],
|
mutationKey: [rsformsApi.baseKey, 'rename-cst'],
|
||||||
mutationFn: rsformsApi.cstRename,
|
mutationFn: rsformsApi.cstRename,
|
||||||
onSuccess: data => {
|
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);
|
updateTimestamp(data.schema.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
cstRename: (
|
cstRename: (data: { itemID: LibraryItemID; data: ICstRenameDTO }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: ICstRenameDTO;
|
|
||||||
},
|
|
||||||
onSuccess?: DataCallback<IConstituentaMeta>
|
|
||||||
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.new_cst) })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const useCstSubstitute = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'substitute-cst'],
|
mutationKey: [rsformsApi.baseKey, 'substitute-cst'],
|
||||||
mutationFn: rsformsApi.cstSubstitute,
|
mutationFn: rsformsApi.cstSubstitute,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||||
import { LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { IConstituentaMeta } from '@/models/rsform';
|
|
||||||
|
|
||||||
import { ICstUpdateDTO, rsformsApi } from './api';
|
import { ICstUpdateDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
@ -22,12 +20,6 @@ export const useCstUpdate = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
cstUpdate: (
|
cstUpdate: (data: { itemID: LibraryItemID; data: ICstUpdateDTO }) => mutation.mutate(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: ICstUpdateDTO;
|
|
||||||
},
|
|
||||||
onSuccess?: DataCallback<IConstituentaMeta>
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const useInlineSynthesis = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'inline-synthesis'],
|
mutationKey: [rsformsApi.baseKey, 'inline-synthesis'],
|
||||||
mutationFn: rsformsApi.inlineSynthesis,
|
mutationFn: rsformsApi.inlineSynthesis,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const useProduceStructure = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'produce-structure'],
|
mutationKey: [rsformsApi.baseKey, 'produce-structure'],
|
||||||
mutationFn: rsformsApi.produceStructure,
|
mutationFn: rsformsApi.produceStructure,
|
||||||
onSuccess: data => {
|
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);
|
updateTimestamp(data.schema.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { LibraryItemID, VersionID } from '@/models/library';
|
||||||
import { IRSForm, IRSFormData } from '@/models/rsform';
|
|
||||||
import { RSFormLoader } from '@/models/RSFormLoader';
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||||
|
|
||||||
import { rsformsApi } from './api';
|
import { rsformsApi } from './api';
|
||||||
|
@ -22,13 +21,3 @@ export function useRSFormSuspense({ itemID, version }: { itemID: LibraryItemID;
|
||||||
const schema = new RSFormLoader(data!).produceRSForm();
|
const schema = new RSFormLoader(data!).produceRSForm();
|
||||||
return { schema };
|
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))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,15 +12,12 @@ export const useResetAliases = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'reset-aliases'],
|
mutationKey: [rsformsApi.baseKey, 'reset-aliases'],
|
||||||
mutationFn: rsformsApi.resetAliases,
|
mutationFn: rsformsApi.resetAliases,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
// TODO: invalidate OSS?
|
// TODO: invalidate OSS?
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
resetAliases: (
|
resetAliases: (data: { itemID: LibraryItemID }) => mutation.mutate(data)
|
||||||
itemID: LibraryItemID, //
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(itemID, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,14 +12,11 @@ export const useRestoreOrder = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'restore-order'],
|
mutationKey: [rsformsApi.baseKey, 'restore-order'],
|
||||||
mutationFn: rsformsApi.restoreOrder,
|
mutationFn: rsformsApi.restoreOrder,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
restoreOrder: (
|
restoreOrder: (data: { itemID: LibraryItemID }) => mutation.mutate(data)
|
||||||
itemID: LibraryItemID, //
|
|
||||||
onSuccess?: () => void
|
|
||||||
) => mutation.mutate(itemID, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { libraryApi } from '@/backend/library/api';
|
import { libraryApi } from '@/backend/library/api';
|
||||||
import { ILibraryItem } from '@/models/library';
|
import { ILibraryItem } from '@/models/library';
|
||||||
import { IRSFormData } from '@/models/rsform';
|
|
||||||
|
|
||||||
import { IRSFormUploadDTO, rsformsApi } from './api';
|
import { IRSFormUploadDTO, rsformsApi } from './api';
|
||||||
|
|
||||||
|
@ -13,16 +11,13 @@ export const useUploadTRS = () => {
|
||||||
mutationKey: [rsformsApi.baseKey, 'load-trs'],
|
mutationKey: [rsformsApi.baseKey, 'load-trs'],
|
||||||
mutationFn: rsformsApi.upload,
|
mutationFn: rsformsApi.upload,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
client.setQueryData([rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey], data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
client.setQueryData([libraryApi.libraryListKey], (prev: ILibraryItem[] | undefined) =>
|
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === data.id ? data : item))
|
prev?.map(item => (item.id === data.id ? data : item))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
upload: (
|
upload: (data: IRSFormUploadDTO) => mutation.mutate(data)
|
||||||
data: IRSFormUploadDTO, //
|
|
||||||
onSuccess?: DataCallback<IRSFormData>
|
|
||||||
) => mutation.mutate(data, { onSuccess })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { axiosInstance } from '@/backend/axiosInstance';
|
|
||||||
import { DELAYS } from '@/backend/configuration';
|
import { DELAYS } from '@/backend/configuration';
|
||||||
import { IUser, IUserInfo, IUserProfile, IUserSignupData } from '@/models/user';
|
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.
|
* Represents user data, intended to update user profile in persistent storage.
|
||||||
|
@ -16,24 +18,37 @@ export const usersApi = {
|
||||||
queryKey: [usersApi.baseKey, 'list'],
|
queryKey: [usersApi.baseKey, 'list'],
|
||||||
staleTime: DELAYS.staleMedium,
|
staleTime: DELAYS.staleMedium,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosInstance
|
axiosGet<IUserInfo[]>({
|
||||||
.get<IUserInfo[]>('/users/api/active-users', {
|
endpoint: '/users/api/active-users',
|
||||||
signal: meta.signal
|
options: { signal: meta.signal }
|
||||||
})
|
})
|
||||||
.then(response => response.data)
|
|
||||||
}),
|
}),
|
||||||
getProfileQueryOptions: () =>
|
getProfileQueryOptions: () =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: [usersApi.baseKey, 'profile'],
|
queryKey: [usersApi.baseKey, 'profile'],
|
||||||
staleTime: DELAYS.staleShort,
|
staleTime: DELAYS.staleShort,
|
||||||
queryFn: meta =>
|
queryFn: meta =>
|
||||||
axiosInstance
|
axiosGet<IUserProfile>({
|
||||||
.get<IUserProfile>('/users/api/profile', {
|
endpoint: '/users/api/profile',
|
||||||
signal: meta.signal
|
options: { signal: meta.signal }
|
||||||
})
|
})
|
||||||
.then(response => response.data)
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
signup: (data: IUserSignupData) => axiosInstance.post('/users/api/signup', data),
|
signup: (data: IUserSignupData) =>
|
||||||
updateProfile: (data: IUpdateProfileDTO) => axiosInstance.patch('/users/api/profile', data)
|
axiosPost<IUserSignupData, IUserProfile>({
|
||||||
|
endpoint: '/users/api/signup',
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: createdUser => information.newUser(createdUser.username)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
updateProfile: (data: IUpdateProfileDTO) =>
|
||||||
|
axiosPatch<IUpdateProfileDTO, IUserProfile>({
|
||||||
|
endpoint: '/users/api/profile',
|
||||||
|
request: {
|
||||||
|
data: data,
|
||||||
|
successMessage: information.changesSaved
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const useSignup = () => {
|
||||||
signup: (
|
signup: (
|
||||||
data: IUserSignupData, //
|
data: IUserSignupData, //
|
||||||
onSuccess?: DataCallback<IUserProfile>
|
onSuccess?: DataCallback<IUserProfile>
|
||||||
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.data as IUserProfile) }),
|
) => mutation.mutate(data, { onSuccess }),
|
||||||
isPending: mutation.isPending,
|
isPending: mutation.isPending,
|
||||||
error: mutation.error,
|
error: mutation.error,
|
||||||
reset: mutation.reset
|
reset: mutation.reset
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
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 { IUpdateProfileDTO, usersApi } from './api';
|
import { IUpdateProfileDTO, usersApi } from './api';
|
||||||
|
|
||||||
// TODO: reload users / optimistic update
|
// TODO: reload users / optimistic update
|
||||||
|
@ -15,10 +12,7 @@ export const useUpdateProfile = () => {
|
||||||
onSuccess: async () => await client.invalidateQueries({ queryKey: [usersApi.baseKey] })
|
onSuccess: async () => await client.invalidateQueries({ queryKey: [usersApi.baseKey] })
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
updateProfile: (
|
updateProfile: (data: IUpdateProfileDTO) => mutation.mutate(data),
|
||||||
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
|
||||||
|
|
|
@ -31,14 +31,13 @@ function SelectUser({
|
||||||
const getUserLabel = useLabelUser();
|
const getUserLabel = useLabelUser();
|
||||||
|
|
||||||
const items = filter ? users.filter(user => filter(user.id)) : users;
|
const items = filter ? users.filter(user => filter(user.id)) : users;
|
||||||
const options =
|
const options = items.map(user => ({
|
||||||
items?.map(user => ({
|
value: user.id,
|
||||||
value: user.id,
|
label: getUserLabel(user.id)
|
||||||
label: getUserLabel(user.id)
|
}));
|
||||||
})) ?? [];
|
|
||||||
|
|
||||||
function filterLabel(option: { value: UserID | undefined; label: string }, inputValue: string) {
|
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);
|
return !user ? false : matchUser(user, inputValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,23 +79,23 @@ function SelectTree<ItemType>({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...restProps}>
|
<div tabIndex={-1} {...restProps}>
|
||||||
{items.map((item, index) => {
|
{items.map((item, index) => {
|
||||||
const isActive = getParent(item) === item || !folded.includes(getParent(item));
|
const isActive = getParent(item) === item || !folded.includes(getParent(item));
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
tabIndex={-1}
|
|
||||||
key={`${prefix}${index}`}
|
key={`${prefix}${index}`}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'pr-3 pl-6 border-b',
|
'pr-3 pl-6 border-b',
|
||||||
'cc-scroll-row',
|
'cc-scroll-row',
|
||||||
'bg-prim-200 clr-hover cc-animate-color',
|
'bg-prim-200 clr-hover cc-animate-color',
|
||||||
'cursor-pointer',
|
'cursor-pointer',
|
||||||
value === item && 'clr-selected'
|
value === item && 'clr-selected',
|
||||||
|
!isActive && 'pointer-events-none'
|
||||||
)}
|
)}
|
||||||
data-tooltip-id={globals.tooltip}
|
data-tooltip-id={globals.tooltip}
|
||||||
data-tooltip-html={isActive ? getDescription(item) : undefined}
|
data-tooltip-html={getDescription(item)}
|
||||||
onClick={isActive ? event => handleSetValue(event, item) : undefined}
|
onClick={event => handleSetValue(event, item)}
|
||||||
style={{
|
style={{
|
||||||
borderBottomWidth: isActive ? '1px' : '0px',
|
borderBottomWidth: isActive ? '1px' : '0px',
|
||||||
transitionProperty: 'height, opacity, padding',
|
transitionProperty: 'height, opacity, padding',
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
|
|
||||||
import Loader from '../ui/Loader';
|
|
||||||
import TextURL from '../ui/TextURL';
|
import TextURL from '../ui/TextURL';
|
||||||
|
|
||||||
function RequireAuth({ children }: React.PropsWithChildren) {
|
function RequireAuth({ children }: React.PropsWithChildren) {
|
||||||
const { user, isLoading } = useAuth();
|
const { isAnonymous } = useAuthSuspense();
|
||||||
|
|
||||||
if (isLoading) {
|
if (isAnonymous) {
|
||||||
return <Loader key='auth-loader' />;
|
|
||||||
}
|
|
||||||
if (user) {
|
|
||||||
return <>{children}</>;
|
|
||||||
} else {
|
|
||||||
return (
|
return (
|
||||||
<div key='auth-no-user' className='flex flex-col items-center gap-1 mt-2'>
|
<div key='auth-no-user' className='flex flex-col items-center gap-1 mt-2'>
|
||||||
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
||||||
|
@ -23,6 +17,7 @@ function RequireAuth({ children }: React.PropsWithChildren) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RequireAuth;
|
export default RequireAuth;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
|
@ -23,7 +22,6 @@ import { AccessPolicy, ILibraryItem, LocationHead } from '@/models/library';
|
||||||
import { cloneTitle, combineLocation, validateLocation } from '@/models/libraryAPI';
|
import { cloneTitle, combineLocation, validateLocation } from '@/models/libraryAPI';
|
||||||
import { ConstituentaID } from '@/models/rsform';
|
import { ConstituentaID } from '@/models/rsform';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
|
|
||||||
export interface DlgCloneLibraryItemProps {
|
export interface DlgCloneLibraryItemProps {
|
||||||
base: ILibraryItem;
|
base: ILibraryItem;
|
||||||
|
@ -75,10 +73,7 @@ function DlgCloneLibraryItem() {
|
||||||
if (onlySelected) {
|
if (onlySelected) {
|
||||||
data.items = selected;
|
data.items = selected;
|
||||||
}
|
}
|
||||||
cloneItem(data, newSchema => {
|
cloneItem(data, newSchema => router.push(urls.schema(newSchema.id)));
|
||||||
toast.success(information.cloneComplete(newSchema.alias));
|
|
||||||
router.push(urls.schema(newSchema.id));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -189,7 +189,7 @@ function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
|
||||||
<PickConstituenta
|
<PickConstituenta
|
||||||
id='dlg_argument_picker'
|
id='dlg_argument_picker'
|
||||||
value={selectedCst}
|
value={selectedCst}
|
||||||
data={schema?.items}
|
data={schema.items}
|
||||||
onSelectValue={handleSelectConstituenta}
|
onSelectValue={handleSelectConstituenta}
|
||||||
prefixID={prefixes.cst_modal_list}
|
prefixID={prefixes.cst_modal_list}
|
||||||
rows={7}
|
rows={7}
|
||||||
|
|
|
@ -16,16 +16,16 @@ import TableUsers from './TableUsers';
|
||||||
|
|
||||||
export interface DlgEditEditorsProps {
|
export interface DlgEditEditorsProps {
|
||||||
editors: UserID[];
|
editors: UserID[];
|
||||||
setEditors: (newValue: UserID[]) => void;
|
onChangeEditors: (newValue: UserID[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgEditEditors() {
|
function DlgEditEditors() {
|
||||||
const { editors, setEditors } = useDialogsStore(state => state.props as DlgEditEditorsProps);
|
const { editors, onChangeEditors } = useDialogsStore(state => state.props as DlgEditEditorsProps);
|
||||||
const [selected, setSelected] = useState<UserID[]>(editors);
|
const [selected, setSelected] = useState<UserID[]>(editors);
|
||||||
const { users } = useUsers();
|
const { users } = useUsers();
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
setEditors(selected);
|
onChangeEditors(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDeleteEditor(target: UserID) {
|
function onDeleteEditor(target: UserID) {
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
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 { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary';
|
||||||
import { useVersionDelete } from '@/backend/library/useVersionDelete';
|
import { useVersionDelete } from '@/backend/library/useVersionDelete';
|
||||||
import { useVersionUpdate } from '@/backend/library/useVersionUpdate';
|
import { useVersionUpdate } from '@/backend/library/useVersionUpdate';
|
||||||
|
@ -13,19 +10,18 @@ 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 { ILibraryItemVersioned, IVersionData, IVersionInfo, VersionID } from '@/models/library';
|
import { ILibraryItemVersioned, IVersionInfo, VersionID } from '@/models/library';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
|
|
||||||
import TableVersions from './TableVersions';
|
import TableVersions from './TableVersions';
|
||||||
|
|
||||||
export interface DlgEditVersionsProps {
|
export interface DlgEditVersionsProps {
|
||||||
item: ILibraryItemVersioned;
|
item: ILibraryItemVersioned;
|
||||||
|
afterDelete: (targetVersion: VersionID) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgEditVersions() {
|
function DlgEditVersions() {
|
||||||
const { item } = useDialogsStore(state => state.props as DlgEditVersionsProps);
|
const { item, afterDelete } = useDialogsStore(state => state.props as DlgEditVersionsProps);
|
||||||
const router = useConceptNavigation();
|
|
||||||
const processing = useIsProcessingLibrary();
|
const processing = useIsProcessingLibrary();
|
||||||
const { versionDelete } = useVersionDelete();
|
const { versionDelete } = useVersionDelete();
|
||||||
const { versionUpdate } = useVersionUpdate();
|
const { versionUpdate } = useVersionUpdate();
|
||||||
|
@ -37,31 +33,21 @@ function DlgEditVersions() {
|
||||||
const isValid = selected && item.versions.every(ver => ver.id === selected.id || ver.version != version);
|
const isValid = selected && item.versions.every(ver => ver.id === selected.id || ver.version != version);
|
||||||
const isModified = selected && (selected.version != version || selected.description != description);
|
const isModified = selected && (selected.version != version || selected.description != description);
|
||||||
|
|
||||||
function handleDeleteVersion(versionID: VersionID) {
|
function handleDeleteVersion(targetVersion: VersionID) {
|
||||||
versionDelete({ itemID: item.id, versionID: versionID }, () => {
|
versionDelete({ itemID: item.id, versionID: targetVersion }, () => afterDelete(targetVersion));
|
||||||
toast.success(information.versionDestroyed);
|
|
||||||
if (versionID === versionID) {
|
|
||||||
router.push(urls.schema(item.id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUpdate() {
|
function handleUpdate() {
|
||||||
if (!isModified || !selected || processing || !isValid) {
|
if (!isModified || !selected || processing || !isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data: IVersionData = {
|
versionUpdate({
|
||||||
version: version,
|
versionID: selected.id,
|
||||||
description: description
|
data: {
|
||||||
};
|
version: version,
|
||||||
versionUpdate(
|
description: description
|
||||||
{
|
}
|
||||||
itemID: item.id, //
|
});
|
||||||
versionID: selected.id,
|
|
||||||
data: data
|
|
||||||
},
|
|
||||||
() => toast.success(information.changesSaved)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReset() {
|
function handleReset() {
|
||||||
|
|
|
@ -45,7 +45,7 @@ function DlgInlineSynthesis() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onInlineSynthesis({
|
onInlineSynthesis({
|
||||||
source: source.schema?.id,
|
source: source.schema.id,
|
||||||
receiver: receiver.id,
|
receiver: receiver.id,
|
||||||
items: selected,
|
items: selected,
|
||||||
substitutions: substitutions
|
substitutions: substitutions
|
||||||
|
@ -53,7 +53,7 @@ function DlgInlineSynthesis() {
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelected(source.schema ? source.schema?.items.map(cst => cst.id) : []);
|
setSelected(source.schema ? source.schema.items.map(cst => cst.id) : []);
|
||||||
setSubstitutions([]);
|
setSubstitutions([]);
|
||||||
}, [source.schema]);
|
}, [source.schema]);
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,13 @@ function DlgRenameCst() {
|
||||||
const [cstData, updateData] = usePartialUpdate(initial);
|
const [cstData, updateData] = usePartialUpdate(initial);
|
||||||
|
|
||||||
useEffect(() => {
|
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) });
|
updateData({ alias: generateAlias(cstData.cst_type, schema) });
|
||||||
}
|
}
|
||||||
}, [initial, cstData.cst_type, updateData, schema]);
|
}, [initial, cstData.cst_type, updateData, schema]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValidated(
|
setValidated(cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema));
|
||||||
!!schema && cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema)
|
|
||||||
);
|
|
||||||
}, [cstData.cst_type, cstData.alias, initial, schema]);
|
}, [cstData.cst_type, cstData.alias, initial, schema]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { IRSFormUploadDTO } from '@/backend/rsform/api';
|
|
||||||
import { useUploadTRS } from '@/backend/rsform/useUploadTRS';
|
import { useUploadTRS } from '@/backend/rsform/useUploadTRS';
|
||||||
import Checkbox from '@/components/ui/Checkbox';
|
import Checkbox from '@/components/ui/Checkbox';
|
||||||
import FileInput from '@/components/ui/FileInput';
|
import FileInput from '@/components/ui/FileInput';
|
||||||
|
@ -26,13 +24,12 @@ function DlgUploadRSForm() {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data: IRSFormUploadDTO = {
|
upload({
|
||||||
itemID: itemID,
|
itemID: itemID,
|
||||||
load_metadata: loadMetadata,
|
load_metadata: loadMetadata,
|
||||||
file: file,
|
file: file,
|
||||||
fileName: file.name
|
fileName: file.name
|
||||||
};
|
});
|
||||||
upload(data, () => toast.success('Схема загружена из файла'));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
|
|
@ -48,6 +48,7 @@ export type VersionID = number;
|
||||||
*/
|
*/
|
||||||
export interface IVersionInfo {
|
export interface IVersionInfo {
|
||||||
id: VersionID;
|
id: VersionID;
|
||||||
|
item: LibraryItemID;
|
||||||
version: string;
|
version: string;
|
||||||
description: string;
|
description: string;
|
||||||
time_create: string;
|
time_create: string;
|
||||||
|
@ -56,7 +57,7 @@ export interface IVersionInfo {
|
||||||
/**
|
/**
|
||||||
* Represents version data, intended to update version metadata in persistent storage.
|
* Represents version data, intended to update version metadata in persistent storage.
|
||||||
*/
|
*/
|
||||||
export interface IVersionData extends Omit<IVersionInfo, 'id' | 'time_create'> {}
|
export interface IVersionData extends Omit<IVersionInfo, 'id' | 'time_create' | 'item'> {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents library item common data typical for all item types.
|
* Represents library item common data typical for all item types.
|
||||||
|
|
|
@ -25,7 +25,10 @@ export interface IUser {
|
||||||
/**
|
/**
|
||||||
* Represents CurrentUser information.
|
* Represents CurrentUser information.
|
||||||
*/
|
*/
|
||||||
export interface ICurrentUser extends Pick<IUser, 'id' | 'username' | 'is_staff'> {
|
export interface ICurrentUser {
|
||||||
|
id: UserID | null;
|
||||||
|
username: string;
|
||||||
|
is_staff: boolean;
|
||||||
editor: LibraryItemID[];
|
editor: LibraryItemID[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
|
@ -27,7 +26,6 @@ import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library';
|
||||||
import { combineLocation, validateLocation } from '@/models/libraryAPI';
|
import { combineLocation, validateLocation } from '@/models/libraryAPI';
|
||||||
import { useLibrarySearchStore } from '@/stores/librarySearch';
|
import { useLibrarySearchStore } from '@/stores/librarySearch';
|
||||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
|
|
||||||
function FormCreateItem() {
|
function FormCreateItem() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
@ -85,7 +83,6 @@ function FormCreateItem() {
|
||||||
};
|
};
|
||||||
setSearchLocation(location);
|
setSearchLocation(location);
|
||||||
createItem(data, newItem => {
|
createItem(data, newItem => {
|
||||||
toast.success(information.newLibraryItem);
|
|
||||||
if (itemType == LibraryItemType.RSFORM) {
|
if (itemType == LibraryItemType.RSFORM) {
|
||||||
router.push(urls.schema(newItem.id));
|
router.push(urls.schema(newItem.id));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,27 +2,25 @@ import { useEffect } from 'react';
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
function HomePage() {
|
function HomePage() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user, isLoading } = useAuth();
|
const { isAnonymous } = useAuthSuspense();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading) {
|
if (isAnonymous) {
|
||||||
if (!user) {
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
router.replace(urls.manuals);
|
||||||
router.replace(urls.manuals);
|
}, PARAMETER.refreshTimeout);
|
||||||
}, PARAMETER.refreshTimeout);
|
} else {
|
||||||
} else {
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
router.replace(urls.library);
|
||||||
router.replace(urls.library);
|
}, PARAMETER.refreshTimeout);
|
||||||
}, PARAMETER.refreshTimeout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [router, user, isLoading]);
|
}, [router, isAnonymous]);
|
||||||
|
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,10 +40,7 @@ function LibraryPage() {
|
||||||
target: location,
|
target: location,
|
||||||
new_location: newLocation
|
new_location: newLocation
|
||||||
},
|
},
|
||||||
() => {
|
() => setLocation(newLocation)
|
||||||
setLocation(newLocation);
|
|
||||||
toast.success(information.locationRenamed);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ interface ViewSideLocationProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps) {
|
function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps) {
|
||||||
const { user } = useAuth();
|
const { user, isAnonymous } = useAuth();
|
||||||
const { items } = useLibrary();
|
const { items } = useLibrary();
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps
|
||||||
const toggleSubfolders = useLibrarySearchStore(state => state.toggleSubfolders);
|
const toggleSubfolders = useLibrarySearchStore(state => state.toggleSubfolders);
|
||||||
|
|
||||||
const canRename = (() => {
|
const canRename = (() => {
|
||||||
if (location.length <= 3 || !user) {
|
if (location.length <= 3 || isAnonymous || !user) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (user.is_staff) {
|
if (user.is_staff) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ function LoginPage() {
|
||||||
const query = useQueryStrings();
|
const query = useQueryStrings();
|
||||||
const userQuery = query.get('username');
|
const userQuery = query.get('username');
|
||||||
|
|
||||||
const { user } = useAuth();
|
const { isAnonymous } = useAuth();
|
||||||
const { login, isPending, error, reset } = useLogin();
|
const { login, isPending, error, reset } = useLogin();
|
||||||
|
|
||||||
const [username, setUsername] = useState(userQuery || '');
|
const [username, setUsername] = useState(userQuery || '');
|
||||||
|
@ -44,7 +44,7 @@ function LoginPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user) {
|
if (!isAnonymous) {
|
||||||
return <ExpectedAnonymous />;
|
return <ExpectedAnonymous />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { ILibraryUpdateDTO } from '@/backend/library/api';
|
import { ILibraryUpdateDTO } from '@/backend/library/api';
|
||||||
import { useUpdateItem } from '@/backend/library/useUpdateItem';
|
import { useUpdateItem } from '@/backend/library/useUpdateItem';
|
||||||
|
@ -14,7 +13,6 @@ import TextInput from '@/components/ui/TextInput';
|
||||||
import { LibraryItemType } from '@/models/library';
|
import { LibraryItemType } from '@/models/library';
|
||||||
import ToolbarItemAccess from '@/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess';
|
import ToolbarItemAccess from '@/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
|
|
||||||
import { useOssEdit } from '../OssEditContext';
|
import { useOssEdit } from '../OssEditContext';
|
||||||
|
|
||||||
|
@ -29,11 +27,11 @@ function FormOSS({ id }: FormOSSProps) {
|
||||||
const isProcessing = useIsProcessingOss();
|
const isProcessing = useIsProcessingOss();
|
||||||
const schema = controller.schema;
|
const schema = controller.schema;
|
||||||
|
|
||||||
const [title, setTitle] = useState(schema?.title ?? '');
|
const [title, setTitle] = useState(schema.title);
|
||||||
const [alias, setAlias] = useState(schema?.alias ?? '');
|
const [alias, setAlias] = useState(schema.alias);
|
||||||
const [comment, setComment] = useState(schema?.comment ?? '');
|
const [comment, setComment] = useState(schema.comment);
|
||||||
const [visible, setVisible] = useState(schema?.visible ?? false);
|
const [visible, setVisible] = useState(schema.visible);
|
||||||
const [readOnly, setReadOnly] = useState(schema?.read_only ?? false);
|
const [readOnly, setReadOnly] = useState(schema.read_only);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (schema) {
|
if (schema) {
|
||||||
|
@ -46,10 +44,6 @@ function FormOSS({ id }: FormOSSProps) {
|
||||||
}, [schema]);
|
}, [schema]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!schema) {
|
|
||||||
setIsModified(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setIsModified(
|
setIsModified(
|
||||||
schema.title !== title ||
|
schema.title !== title ||
|
||||||
schema.alias !== alias ||
|
schema.alias !== alias ||
|
||||||
|
@ -59,12 +53,11 @@ function FormOSS({ id }: FormOSSProps) {
|
||||||
);
|
);
|
||||||
return () => setIsModified(false);
|
return () => setIsModified(false);
|
||||||
}, [
|
}, [
|
||||||
schema,
|
schema.title,
|
||||||
schema?.title,
|
schema.alias,
|
||||||
schema?.alias,
|
schema.comment,
|
||||||
schema?.comment,
|
schema.visible,
|
||||||
schema?.visible,
|
schema.read_only,
|
||||||
schema?.read_only,
|
|
||||||
title,
|
title,
|
||||||
alias,
|
alias,
|
||||||
comment,
|
comment,
|
||||||
|
@ -89,7 +82,7 @@ function FormOSS({ id }: FormOSSProps) {
|
||||||
visible: visible,
|
visible: visible,
|
||||||
read_only: readOnly
|
read_only: readOnly
|
||||||
};
|
};
|
||||||
update(data, () => toast.success(information.changesSaved));
|
update(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -58,7 +58,7 @@ function NodeContextMenu({
|
||||||
if (operation.operation_type !== OperationType.SYNTHESIS) {
|
if (operation.operation_type !== OperationType.SYNTHESIS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!controller.schema || operation.result) {
|
if (operation.result) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import { useModificationStore } from '@/stores/modification';
|
||||||
import { useOSSGraphStore } from '@/stores/ossGraph';
|
import { useOSSGraphStore } from '@/stores/ossGraph';
|
||||||
import { APP_COLORS } from '@/styling/color';
|
import { APP_COLORS } from '@/styling/color';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { errors, information } from '@/utils/labels';
|
import { errors } from '@/utils/labels';
|
||||||
|
|
||||||
import { useOssEdit } from '../OssEditContext';
|
import { useOssEdit } from '../OssEditContext';
|
||||||
import { OssNodeTypes } from './graph/OssNodeTypes';
|
import { OssNodeTypes } from './graph/OssNodeTypes';
|
||||||
|
@ -81,33 +81,29 @@ function OssFlow() {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!controller.schema) {
|
setNodes(
|
||||||
setNodes([]);
|
controller.schema.items.map(operation => ({
|
||||||
setEdges([]);
|
id: String(operation.id),
|
||||||
} else {
|
data: { label: operation.alias, operation: operation },
|
||||||
setNodes(
|
position: { x: operation.position_x, y: operation.position_y },
|
||||||
controller.schema.items.map(operation => ({
|
type: operation.operation_type.toString()
|
||||||
id: String(operation.id),
|
}))
|
||||||
data: { label: operation.alias, operation: operation },
|
);
|
||||||
position: { x: operation.position_x, y: operation.position_y },
|
setEdges(
|
||||||
type: operation.operation_type.toString()
|
controller.schema.arguments.map((argument, index) => ({
|
||||||
}))
|
id: String(index),
|
||||||
);
|
source: String(argument.argument),
|
||||||
setEdges(
|
target: String(argument.operation),
|
||||||
controller.schema.arguments.map((argument, index) => ({
|
type: edgeStraight ? 'straight' : 'simplebezier',
|
||||||
id: String(index),
|
animated: edgeAnimate,
|
||||||
source: String(argument.argument),
|
targetHandle:
|
||||||
target: String(argument.operation),
|
controller.schema.operationByID.get(argument.argument)!.position_x >
|
||||||
type: edgeStraight ? 'straight' : 'simplebezier',
|
controller.schema.operationByID.get(argument.operation)!.position_x
|
||||||
animated: edgeAnimate,
|
? 'right'
|
||||||
targetHandle:
|
: 'left'
|
||||||
controller.schema.operationByID.get(argument.argument)!.position_x >
|
}))
|
||||||
controller.schema.operationByID.get(argument.operation)!.position_x
|
);
|
||||||
? 'right'
|
|
||||||
: 'left'
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
}, PARAMETER.graphRefreshDelay);
|
}, PARAMETER.graphRefreshDelay);
|
||||||
|
@ -138,15 +134,11 @@ function OssFlow() {
|
||||||
operation.position_y = item.position_y;
|
operation.position_y = item.position_y;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
toast.success(information.changesSaved);
|
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCreateOperation(inputs: OperationID[]) {
|
function handleCreateOperation(inputs: OperationID[]) {
|
||||||
if (!controller.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const positions = getPositions();
|
const positions = getPositions();
|
||||||
const target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
const target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
||||||
controller.promptCreateOperation({
|
controller.promptCreateOperation({
|
||||||
|
@ -181,10 +173,9 @@ function OssFlow() {
|
||||||
toast.error(errors.inputAlreadyExists);
|
toast.error(errors.inputAlreadyExists);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
inputCreate({ itemID: controller.schema.id, data: { target: target, positions: getPositions() } }, new_schema => {
|
inputCreate({ itemID: controller.schema.id, data: { target: target, positions: getPositions() } }, new_schema =>
|
||||||
toast.success(information.newLibraryItem);
|
router.push(urls.schema(new_schema.id))
|
||||||
router.push(urls.schema(new_schema.id));
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEditSchema(target: OperationID) {
|
function handleEditSchema(target: OperationID) {
|
||||||
|
@ -196,13 +187,10 @@ function OssFlow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOperationExecute(target: OperationID) {
|
function handleOperationExecute(target: OperationID) {
|
||||||
operationExecute(
|
operationExecute({
|
||||||
{
|
itemID: controller.schema.id, //
|
||||||
itemID: controller.schema.id, //
|
data: { target: target, positions: getPositions() }
|
||||||
data: { target: target, positions: getPositions() }
|
});
|
||||||
},
|
|
||||||
() => toast.success(information.operationExecuted)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleExecuteSelected() {
|
function handleExecuteSelected() {
|
||||||
|
@ -217,9 +205,6 @@ function OssFlow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSaveImage() {
|
function handleSaveImage() {
|
||||||
if (!controller.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
||||||
if (canvas === null) {
|
if (canvas === null) {
|
||||||
toast.error(errors.imageFailed);
|
toast.error(errors.imageFailed);
|
||||||
|
@ -242,7 +227,7 @@ function OssFlow() {
|
||||||
})
|
})
|
||||||
.then(dataURL => {
|
.then(dataURL => {
|
||||||
const a = document.createElement('a');
|
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.setAttribute('href', dataURL);
|
||||||
a.click();
|
a.click();
|
||||||
})
|
})
|
||||||
|
|
|
@ -53,7 +53,7 @@ function ToolbarOssGraph({
|
||||||
const controller = useOssEdit();
|
const controller = useOssEdit();
|
||||||
const { isModified } = useModificationStore();
|
const { isModified } = useModificationStore();
|
||||||
const isProcessing = useIsProcessingOss();
|
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 showGrid = useOSSGraphStore(state => state.showGrid);
|
||||||
const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate);
|
const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate);
|
||||||
|
@ -66,7 +66,7 @@ function ToolbarOssGraph({
|
||||||
if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) {
|
if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!controller.schema || selectedOperation.result) {
|
if (selectedOperation.result) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { useIsProcessingOss } from '@/backend/oss/useIsProcessingOss';
|
import { useIsProcessingOss } from '@/backend/oss/useIsProcessingOss';
|
||||||
import {
|
import {
|
||||||
IconAdmin,
|
IconAdmin,
|
||||||
|
@ -33,7 +33,7 @@ import { useOssEdit } from './OssEditContext';
|
||||||
function MenuOssTabs() {
|
function MenuOssTabs() {
|
||||||
const controller = useOssEdit();
|
const controller = useOssEdit();
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user, isAnonymous } = useAuthSuspense();
|
||||||
|
|
||||||
const isProcessing = useIsProcessingOss();
|
const isProcessing = useIsProcessingOss();
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ function MenuOssTabs() {
|
||||||
|
|
||||||
<Divider margins='mx-3 my-1' />
|
<Divider margins='mx-3 my-1' />
|
||||||
|
|
||||||
{user ? (
|
{!isAnonymous ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Создать новую схему'
|
text='Создать новую схему'
|
||||||
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
||||||
|
@ -118,7 +118,7 @@ function MenuOssTabs() {
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{user ? (
|
{!isAnonymous ? (
|
||||||
<div ref={editMenu.ref}>
|
<div ref={editMenu.ref}>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
|
@ -143,7 +143,7 @@ function MenuOssTabs() {
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{user ? (
|
{!isAnonymous ? (
|
||||||
<div ref={accessMenu.ref}>
|
<div ref={accessMenu.ref}>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
|
@ -177,7 +177,7 @@ function MenuOssTabs() {
|
||||||
text={labelUserRole(UserRole.EDITOR)}
|
text={labelUserRole(UserRole.EDITOR)}
|
||||||
title={describeUserRole(UserRole.EDITOR)}
|
title={describeUserRole(UserRole.EDITOR)}
|
||||||
icon={<IconEditor size='1rem' className='icon-primary' />}
|
icon={<IconEditor size='1rem' className='icon-primary' />}
|
||||||
disabled={!controller.isOwned && !controller.schema?.editors.includes(user.id)}
|
disabled={!controller.isOwned && (!user.id || !controller.schema.editors.includes(user.id))}
|
||||||
onClick={() => handleChangeRole(UserRole.EDITOR)}
|
onClick={() => handleChangeRole(UserRole.EDITOR)}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
@ -191,13 +191,13 @@ function MenuOssTabs() {
|
||||||
text={labelUserRole(UserRole.ADMIN)}
|
text={labelUserRole(UserRole.ADMIN)}
|
||||||
title={describeUserRole(UserRole.ADMIN)}
|
title={describeUserRole(UserRole.ADMIN)}
|
||||||
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
||||||
disabled={!user?.is_staff}
|
disabled={!user.is_staff}
|
||||||
onClick={() => handleChangeRole(UserRole.ADMIN)}
|
onClick={() => handleChangeRole(UserRole.ADMIN)}
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{!user ? (
|
{isAnonymous ? (
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createContext, useContext, useEffect, useState } from 'react';
|
import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { useDeleteItem } from '@/backend/library/useDeleteItem';
|
import { useDeleteItem } from '@/backend/library/useDeleteItem';
|
||||||
import { useInputUpdate } from '@/backend/oss/useInputUpdate';
|
import { useInputUpdate } from '@/backend/oss/useInputUpdate';
|
||||||
import { useOperationCreate } from '@/backend/oss/useOperationCreate';
|
import { useOperationCreate } from '@/backend/oss/useOperationCreate';
|
||||||
|
@ -22,7 +21,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { useRoleStore } from '@/stores/role';
|
import { useRoleStore } from '@/stores/role';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { information, prompts } from '@/utils/labels';
|
import { prompts } from '@/utils/labels';
|
||||||
|
|
||||||
import { RSTabID } from '../RSFormPage/RSEditContext';
|
import { RSTabID } from '../RSFormPage/RSEditContext';
|
||||||
|
|
||||||
|
@ -79,14 +78,15 @@ interface OssEditStateProps {
|
||||||
|
|
||||||
export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEditStateProps>) => {
|
export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEditStateProps>) => {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
|
||||||
const adminMode = usePreferencesStore(state => state.adminMode);
|
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||||
|
|
||||||
const role = useRoleStore(state => state.role);
|
const role = useRoleStore(state => state.role);
|
||||||
const adjustRole = useRoleStore(state => state.adjustRole);
|
const adjustRole = useRoleStore(state => state.adjustRole);
|
||||||
|
|
||||||
|
const { user } = useAuthSuspense();
|
||||||
const { schema } = useOssSuspense({ itemID: itemID });
|
const { schema } = useOssSuspense({ itemID: itemID });
|
||||||
|
|
||||||
const isOwned = user?.id === schema.owner || false;
|
const isOwned = !!user.id && user.id === schema.owner;
|
||||||
|
|
||||||
const isMutable = role > UserRole.READER && !schema.read_only;
|
const isMutable = role > UserRole.READER && !schema.read_only;
|
||||||
|
|
||||||
|
@ -112,8 +112,8 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
() =>
|
() =>
|
||||||
adjustRole({
|
adjustRole({
|
||||||
isOwner: isOwned,
|
isOwner: isOwned,
|
||||||
isEditor: (user && schema.editors.includes(user?.id)) ?? false,
|
isEditor: !!user.id && schema.editors.includes(user.id),
|
||||||
isStaff: user?.is_staff ?? false,
|
isStaff: user.is_staff,
|
||||||
adminMode: adminMode
|
adminMode: adminMode
|
||||||
}),
|
}),
|
||||||
[schema, adjustRole, isOwned, user, adminMode]
|
[schema, adjustRole, isOwned, user, adminMode]
|
||||||
|
@ -139,13 +139,10 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteSchema() {
|
function deleteSchema() {
|
||||||
if (!schema || !window.confirm(prompts.deleteOSS)) {
|
if (!window.confirm(prompts.deleteOSS)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
deleteItem(schema.id, () => {
|
deleteItem(schema.id, () => router.push(urls.library));
|
||||||
toast.success(information.itemDestroyed);
|
|
||||||
router.push(urls.library);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function promptCreateOperation({ defaultX, defaultY, inputs, positions, callback }: ICreateOperationPrompt) {
|
function promptCreateOperation({ defaultX, defaultY, inputs, positions, callback }: ICreateOperationPrompt) {
|
||||||
|
@ -160,7 +157,6 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
data.item_data.position_x = target.x;
|
data.item_data.position_x = target.x;
|
||||||
data.item_data.position_y = target.y;
|
data.item_data.position_y = target.y;
|
||||||
operationCreate({ itemID: schema.id, data }, operation => {
|
operationCreate({ itemID: schema.id, data }, operation => {
|
||||||
toast.success(information.newOperation(operation.alias));
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
setTimeout(() => callback(operation.id), PARAMETER.refreshTimeout);
|
setTimeout(() => callback(operation.id), PARAMETER.refreshTimeout);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +187,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
target: operation,
|
target: operation,
|
||||||
onSubmit: data => {
|
onSubmit: data => {
|
||||||
data.positions = positions;
|
data.positions = positions;
|
||||||
operationUpdate({ itemID: schema.id, data }, () => toast.success(information.changesSaved));
|
operationUpdate({ itemID: schema.id, data });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -204,18 +200,15 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
showDeleteOperation({
|
showDeleteOperation({
|
||||||
target: operation,
|
target: operation,
|
||||||
onSubmit: (targetID, keepConstituents, deleteSchema) => {
|
onSubmit: (targetID, keepConstituents, deleteSchema) => {
|
||||||
operationDelete(
|
operationDelete({
|
||||||
{
|
itemID: schema.id,
|
||||||
itemID: schema.id,
|
data: {
|
||||||
data: {
|
target: targetID,
|
||||||
target: targetID,
|
positions: positions,
|
||||||
positions: positions,
|
keep_constituents: keepConstituents,
|
||||||
keep_constituents: keepConstituents,
|
delete_schema: deleteSchema
|
||||||
delete_schema: deleteSchema
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
() => toast.success(information.operationDestroyed)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -229,17 +222,14 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
oss: schema,
|
oss: schema,
|
||||||
target: operation,
|
target: operation,
|
||||||
onSubmit: (target, newInput) => {
|
onSubmit: (target, newInput) => {
|
||||||
inputUpdate(
|
inputUpdate({
|
||||||
{
|
itemID: schema.id,
|
||||||
itemID: schema.id,
|
data: {
|
||||||
data: {
|
target: target,
|
||||||
target: target,
|
positions: positions,
|
||||||
positions: positions,
|
input: newInput ?? null
|
||||||
input: newInput ?? null
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
() => toast.success(information.changesSaved)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -256,14 +246,15 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
return operation.position_x === item.position_x && operation.position_y === item.position_y;
|
return operation.position_x === item.position_x && operation.position_y === item.position_y;
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
relocateConstituents({ itemID: schema.id, data }, () => toast.success(information.changesSaved));
|
relocateConstituents({ itemID: schema.id, data });
|
||||||
} else {
|
} else {
|
||||||
updatePositions(
|
updatePositions(
|
||||||
{
|
{
|
||||||
|
isSilent: true,
|
||||||
itemID: schema.id, //
|
itemID: schema.id, //
|
||||||
positions: positions
|
positions: positions
|
||||||
},
|
},
|
||||||
() => relocateConstituents({ itemID: schema.id, data }, () => toast.success(information.changesSaved))
|
() => relocateConstituents({ itemID: schema.id, data })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useCstUpdate } from '@/backend/rsform/useCstUpdate';
|
import { useCstUpdate } from '@/backend/rsform/useCstUpdate';
|
||||||
import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm';
|
import { useIsProcessingRSForm } from '@/backend/rsform/useIsProcessingRSForm';
|
||||||
|
@ -12,7 +11,6 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
import { promptUnsaved } from '@/utils/utils';
|
import { promptUnsaved } from '@/utils/utils';
|
||||||
|
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
@ -69,16 +67,13 @@ function EditorConstituenta() {
|
||||||
showEditTerm({
|
showEditTerm({
|
||||||
target: controller.activeCst,
|
target: controller.activeCst,
|
||||||
onSave: forms =>
|
onSave: forms =>
|
||||||
cstUpdate(
|
cstUpdate({
|
||||||
{
|
itemID: controller.schema.id,
|
||||||
itemID: controller.schema.id,
|
data: {
|
||||||
data: {
|
target: controller.activeCst!.id,
|
||||||
target: controller.activeCst!.id,
|
item_data: { term_forms: forms }
|
||||||
item_data: { term_forms: forms }
|
}
|
||||||
}
|
})
|
||||||
},
|
|
||||||
() => toast.success(information.changesSaved)
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { ICstRenameDTO } from '@/backend/rsform/api';
|
import { ICstRenameDTO } from '@/backend/rsform/api';
|
||||||
import { useCstRename } from '@/backend/rsform/useCstRename';
|
import { useCstRename } from '@/backend/rsform/useCstRename';
|
||||||
|
@ -10,7 +9,7 @@ import Overlay from '@/components/ui/Overlay';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { information, tooltips } from '@/utils/labels';
|
import { tooltips } from '@/utils/labels';
|
||||||
|
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
|
||||||
|
@ -39,10 +38,7 @@ function EditorControls({ constituenta, disabled, onEditTerm }: EditorControlsPr
|
||||||
schema: schema,
|
schema: schema,
|
||||||
initial: initialData,
|
initial: initialData,
|
||||||
allowChangeType: !constituenta.is_inherited,
|
allowChangeType: !constituenta.is_inherited,
|
||||||
onRename: data => {
|
onRename: data => cstRename({ itemID: schema.id, data })
|
||||||
const oldAlias = initialData.alias;
|
|
||||||
cstRename({ itemID: schema.id, data }, () => toast.success(information.renameComplete(oldAlias, data.alias)));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { isBaseSet, isBasicConcept, isFunctional } from '@/models/rsformAPI';
|
||||||
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
|
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { errors, information, labelCstTypification } from '@/utils/labels';
|
import { errors, labelCstTypification } from '@/utils/labels';
|
||||||
|
|
||||||
import EditorRSExpression from '../EditorRSExpression';
|
import EditorRSExpression from '../EditorRSExpression';
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
@ -122,7 +122,7 @@ function FormConstituenta({
|
||||||
convention: activeCst.convention !== convention ? convention : undefined
|
convention: activeCst.convention !== convention ? convention : undefined
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cstUpdate({ itemID: schema.id, data }, () => toast.success(information.changesSaved));
|
cstUpdate({ itemID: schema.id, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTypeGraph(event: CProps.EventMouse) {
|
function handleTypeGraph(event: CProps.EventMouse) {
|
||||||
|
|
|
@ -72,7 +72,7 @@ function ToolbarConstituenta({
|
||||||
position='cc-tab-tools right-1/2 translate-x-1/2 xs:right-4 xs:translate-x-0 md:right-1/2 md:translate-x-1/2'
|
position='cc-tab-tools right-1/2 translate-x-1/2 xs:right-4 xs:translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||||
className='cc-icons cc-animate-position outline-none cc-blur px-1 rounded-b-2xl'
|
className='cc-icons cc-animate-position outline-none cc-blur px-1 rounded-b-2xl'
|
||||||
>
|
>
|
||||||
{controller.schema && controller.schema?.oss.length > 0 ? (
|
{controller.schema.oss.length > 0 ? (
|
||||||
<MiniSelectorOSS
|
<MiniSelectorOSS
|
||||||
items={controller.schema.oss}
|
items={controller.schema.oss}
|
||||||
onSelect={(event, value) => controller.navigateOss(value.id, event.ctrlKey || event.metaKey)}
|
onSelect={(event, value) => controller.navigateOss(value.id, event.ctrlKey || event.metaKey)}
|
||||||
|
@ -131,13 +131,13 @@ function ToolbarConstituenta({
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
||||||
icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
|
icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
|
||||||
disabled={disabled || isModified || (controller.schema && controller.schema?.items.length < 2)}
|
disabled={disabled || isModified || controller.schema.items.length < 2}
|
||||||
onClick={controller.moveUp}
|
onClick={controller.moveUp}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
||||||
icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
|
icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
|
||||||
disabled={disabled || isModified || (controller.schema && controller.schema?.items.length < 2)}
|
disabled={disabled || isModified || controller.schema.items.length < 2}
|
||||||
onClick={controller.moveDown}
|
onClick={controller.moveDown}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
|
@ -34,7 +33,7 @@ import { useLibrarySearchStore } from '@/stores/librarySearch';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { useRoleStore } from '@/stores/role';
|
import { useRoleStore } from '@/stores/role';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { information, prompts } from '@/utils/labels';
|
import { prompts } from '@/utils/labels';
|
||||||
|
|
||||||
interface EditorLibraryItemProps {
|
interface EditorLibraryItemProps {
|
||||||
itemID: LibraryItemID;
|
itemID: LibraryItemID;
|
||||||
|
@ -69,7 +68,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr
|
||||||
if (!window.confirm(prompts.ownerChange)) {
|
if (!window.confirm(prompts.ownerChange)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setOwner({ itemID: itemID, owner: newValue }, () => toast.success(information.changesSaved));
|
setOwner({ itemID: itemID, owner: newValue });
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleOpenLibrary(event: CProps.EventMouse) {
|
function handleOpenLibrary(event: CProps.EventMouse) {
|
||||||
|
@ -86,8 +85,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr
|
||||||
}
|
}
|
||||||
showEditLocation({
|
showEditLocation({
|
||||||
initial: item.location,
|
initial: item.location,
|
||||||
onChangeLocation: newLocation =>
|
onChangeLocation: newLocation => setLocation({ itemID: itemID, location: newLocation })
|
||||||
setLocation({ itemID: itemID, location: newLocation }, () => toast.success(information.moveComplete))
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +95,7 @@ function EditorLibraryItem({ itemID, itemType, controller }: EditorLibraryItemPr
|
||||||
}
|
}
|
||||||
showEditEditors({
|
showEditEditors({
|
||||||
editors: item.editors,
|
editors: item.editors,
|
||||||
setEditors: newEditors =>
|
onChangeEditors: newEditors => setEditors({ itemID: itemID, editors: newEditors })
|
||||||
setEditors({ itemID: itemID, editors: newEditors }, () => toast.success(information.changesSaved))
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,11 @@ function FormRSForm({ id }: FormRSFormProps) {
|
||||||
const { isModified, setIsModified } = useModificationStore();
|
const { isModified, setIsModified } = useModificationStore();
|
||||||
const isProcessing = useIsProcessingRSForm();
|
const isProcessing = useIsProcessingRSForm();
|
||||||
|
|
||||||
const [title, setTitle] = useState(schema?.title ?? '');
|
const [title, setTitle] = useState(schema.title);
|
||||||
const [alias, setAlias] = useState(schema?.alias ?? '');
|
const [alias, setAlias] = useState(schema.alias);
|
||||||
const [comment, setComment] = useState(schema?.comment ?? '');
|
const [comment, setComment] = useState(schema.comment);
|
||||||
const [visible, setVisible] = useState(schema?.visible ?? false);
|
const [visible, setVisible] = useState(schema.visible);
|
||||||
const [readOnly, setReadOnly] = useState(schema?.read_only ?? false);
|
const [readOnly, setReadOnly] = useState(schema.read_only);
|
||||||
|
|
||||||
function handleSelectVersion(version?: VersionID) {
|
function handleSelectVersion(version?: VersionID) {
|
||||||
router.push(urls.schema(schema.id, version));
|
router.push(urls.schema(schema.id, version));
|
||||||
|
@ -54,10 +54,6 @@ function FormRSForm({ id }: FormRSFormProps) {
|
||||||
}, [schema]);
|
}, [schema]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!schema) {
|
|
||||||
setIsModified(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setIsModified(
|
setIsModified(
|
||||||
schema.title !== title ||
|
schema.title !== title ||
|
||||||
schema.alias !== alias ||
|
schema.alias !== alias ||
|
||||||
|
@ -67,12 +63,11 @@ function FormRSForm({ id }: FormRSFormProps) {
|
||||||
);
|
);
|
||||||
return () => setIsModified(false);
|
return () => setIsModified(false);
|
||||||
}, [
|
}, [
|
||||||
schema,
|
schema.title,
|
||||||
schema?.title,
|
schema.alias,
|
||||||
schema?.alias,
|
schema.comment,
|
||||||
schema?.comment,
|
schema.visible,
|
||||||
schema?.visible,
|
schema.read_only,
|
||||||
schema?.read_only,
|
|
||||||
title,
|
title,
|
||||||
alias,
|
alias,
|
||||||
comment,
|
comment,
|
||||||
|
@ -122,7 +117,7 @@ function FormRSForm({ id }: FormRSFormProps) {
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col'>
|
||||||
<ToolbarVersioning blockReload={schema && schema?.oss.length > 0} />
|
<ToolbarVersioning blockReload={schema.oss.length > 0} />
|
||||||
<ToolbarItemAccess
|
<ToolbarItemAccess
|
||||||
visible={visible}
|
visible={visible}
|
||||||
toggleVisible={() => setVisible(prev => !prev)}
|
toggleVisible={() => setVisible(prev => !prev)}
|
||||||
|
@ -134,8 +129,8 @@ function FormRSForm({ id }: FormRSFormProps) {
|
||||||
<SelectVersion
|
<SelectVersion
|
||||||
id='schema_version'
|
id='schema_version'
|
||||||
className='select-none'
|
className='select-none'
|
||||||
value={schema?.version} //
|
value={schema.version} //
|
||||||
items={schema?.versions}
|
items={schema.versions}
|
||||||
onSelectValue={handleSelectVersion}
|
onSelectValue={handleSelectVersion}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary';
|
import { useIsProcessingLibrary } from '@/backend/library/useIsProcessingLibrary';
|
||||||
import { useSetAccessPolicy } from '@/backend/library/useSetAccessPolicy';
|
import { useSetAccessPolicy } from '@/backend/library/useSetAccessPolicy';
|
||||||
import { VisibilityIcon } from '@/components/DomainIcons';
|
import { VisibilityIcon } from '@/components/DomainIcons';
|
||||||
|
@ -14,7 +12,6 @@ import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { UserRole } from '@/models/user';
|
import { UserRole } from '@/models/user';
|
||||||
import { useRoleStore } from '@/stores/role';
|
import { useRoleStore } from '@/stores/role';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
|
|
||||||
interface ToolbarItemAccessProps {
|
interface ToolbarItemAccessProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
@ -31,7 +28,7 @@ function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, c
|
||||||
const { setAccessPolicy } = useSetAccessPolicy();
|
const { setAccessPolicy } = useSetAccessPolicy();
|
||||||
|
|
||||||
function handleSetAccessPolicy(newPolicy: AccessPolicy) {
|
function handleSetAccessPolicy(newPolicy: AccessPolicy) {
|
||||||
setAccessPolicy({ itemID: controller.schema.id, policy: newPolicy }, () => toast.success(information.changesSaved));
|
setAccessPolicy({ itemID: controller.schema.id, policy: newPolicy });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -30,7 +30,7 @@ function ToolbarRSFormCard({ controller, onSubmit }: ToolbarRSFormCardProps) {
|
||||||
const canSave = isModified && !isProcessing;
|
const canSave = isModified && !isProcessing;
|
||||||
|
|
||||||
const ossSelector = (() => {
|
const ossSelector = (() => {
|
||||||
if (!controller.schema || controller.schema?.item_type !== LibraryItemType.RSFORM) {
|
if (controller.schema.item_type !== LibraryItemType.RSFORM) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const schema = controller.schema as IRSForm;
|
const schema = controller.schema as IRSForm;
|
||||||
|
@ -59,10 +59,10 @@ function ToolbarRSFormCard({ controller, onSubmit }: ToolbarRSFormCardProps) {
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={tooltips.shareItem(controller.schema?.access_policy)}
|
titleHtml={tooltips.shareItem(controller.schema.access_policy)}
|
||||||
icon={<IconShare size='1.25rem' className='icon-primary' />}
|
icon={<IconShare size='1.25rem' className='icon-primary' />}
|
||||||
onClick={sharePage}
|
onClick={sharePage}
|
||||||
disabled={controller.schema?.access_policy !== AccessPolicy.PUBLIC}
|
disabled={controller.schema.access_policy !== AccessPolicy.PUBLIC}
|
||||||
/>
|
/>
|
||||||
{controller.isMutable ? (
|
{controller.isMutable ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { toast } from 'react-toastify';
|
'use client';
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
|
||||||
import { urls } from '@/app/urls';
|
|
||||||
import { useVersionCreate } from '@/backend/library/useVersionCreate';
|
import { useVersionCreate } from '@/backend/library/useVersionCreate';
|
||||||
import { useVersionRestore } from '@/backend/library/useVersionRestore';
|
import { useVersionRestore } from '@/backend/library/useVersionRestore';
|
||||||
import { IconNewVersion, IconUpload, IconVersions } from '@/components/Icons';
|
import { IconNewVersion, IconUpload, IconVersions } from '@/components/Icons';
|
||||||
|
@ -12,7 +10,7 @@ import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { information, prompts } from '@/utils/labels';
|
import { prompts } from '@/utils/labels';
|
||||||
import { promptUnsaved } from '@/utils/utils';
|
import { promptUnsaved } from '@/utils/utils';
|
||||||
|
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
@ -23,7 +21,6 @@ interface ToolbarVersioningProps {
|
||||||
|
|
||||||
function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
const router = useConceptNavigation();
|
|
||||||
const { isModified } = useModificationStore();
|
const { isModified } = useModificationStore();
|
||||||
const { versionRestore } = useVersionRestore();
|
const { versionRestore } = useVersionRestore();
|
||||||
const { versionCreate } = useVersionCreate();
|
const { versionCreate } = useVersionCreate();
|
||||||
|
@ -35,10 +32,7 @@ function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
||||||
if (!controller.schema.version || !window.confirm(prompts.restoreArchive)) {
|
if (!controller.schema.version || !window.confirm(prompts.restoreArchive)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
versionRestore({ itemID: controller.schema.id, versionID: controller.schema.version }, () => {
|
versionRestore({ versionID: controller.schema.version }, () => controller.navigateVersion(undefined));
|
||||||
toast.success(information.versionRestored);
|
|
||||||
router.push(urls.schema(controller.schema.id));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCreateVersion() {
|
function handleCreateVersion() {
|
||||||
|
@ -50,15 +44,22 @@ function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
||||||
selected: controller.selected,
|
selected: controller.selected,
|
||||||
totalCount: controller.schema.items.length,
|
totalCount: controller.schema.items.length,
|
||||||
onCreate: data =>
|
onCreate: data =>
|
||||||
versionCreate({ itemID: controller.schema.id, data: data }, () => {
|
versionCreate(
|
||||||
toast.success(information.newVersion(data.version));
|
{
|
||||||
})
|
itemID: controller.schema.id, //
|
||||||
|
data: data
|
||||||
|
},
|
||||||
|
newVersion => controller.navigateVersion(newVersion)
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEditVersions() {
|
function handleEditVersions() {
|
||||||
showEditVersions({
|
showEditVersions({
|
||||||
item: controller.schema
|
item: controller.schema,
|
||||||
|
afterDelete: targetVersion => {
|
||||||
|
if (targetVersion === controller.activeVersion) controller.navigateVersion(undefined);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +86,8 @@ function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
||||||
icon={<IconNewVersion size='1.25rem' className='icon-green' />}
|
icon={<IconNewVersion size='1.25rem' className='icon-green' />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title={controller.schema?.versions.length === 0 ? 'Список версий пуст' : 'Редактировать версии'}
|
title={controller.schema.versions.length === 0 ? 'Список версий пуст' : 'Редактировать версии'}
|
||||||
disabled={!controller.schema || controller.schema?.versions.length === 0}
|
disabled={controller.schema.versions.length === 0}
|
||||||
onClick={handleEditVersions}
|
onClick={handleEditVersions}
|
||||||
icon={<IconVersions size='1.25rem' className='icon-primary' />}
|
icon={<IconVersions size='1.25rem' className='icon-primary' />}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -26,7 +26,7 @@ function EditorRSList() {
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
const isProcessing = useIsProcessingRSForm();
|
const isProcessing = useIsProcessingRSForm();
|
||||||
|
|
||||||
const [filtered, setFiltered] = useState<IConstituenta[]>(controller.schema?.items ?? []);
|
const [filtered, setFiltered] = useState<IConstituenta[]>(controller.schema.items);
|
||||||
const [filterText, setFilterText] = useState('');
|
const [filterText, setFilterText] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -42,17 +42,17 @@ function EditorRSList() {
|
||||||
}, [filtered, setRowSelection, controller.selected]);
|
}, [filtered, setRowSelection, controller.selected]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!controller.schema || controller.schema.items.length === 0) {
|
if (controller.schema.items.length === 0) {
|
||||||
setFiltered([]);
|
setFiltered([]);
|
||||||
} else if (filterText) {
|
} else if (filterText) {
|
||||||
setFiltered(controller.schema.items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL)));
|
setFiltered(controller.schema.items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL)));
|
||||||
} else {
|
} else {
|
||||||
setFiltered(controller.schema.items);
|
setFiltered(controller.schema.items);
|
||||||
}
|
}
|
||||||
}, [filterText, controller.schema?.items, controller.schema]);
|
}, [filterText, controller.schema.items]);
|
||||||
|
|
||||||
function handleDownloadCSV() {
|
function handleDownloadCSV() {
|
||||||
if (!controller.schema || filtered.length === 0) {
|
if (filtered.length === 0) {
|
||||||
toast.error(information.noDataToExport);
|
toast.error(information.noDataToExport);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -65,21 +65,17 @@ function EditorRSList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) {
|
function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) {
|
||||||
if (!controller.schema) {
|
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
|
||||||
controller.deselectAll();
|
const newSelection: ConstituentaID[] = [];
|
||||||
} else {
|
filtered.forEach((cst, index) => {
|
||||||
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
|
if (newRowSelection[String(index)] === true) {
|
||||||
const newSelection: ConstituentaID[] = [];
|
newSelection.push(cst.id);
|
||||||
filtered.forEach((cst, index) => {
|
}
|
||||||
if (newRowSelection[String(index)] === true) {
|
});
|
||||||
newSelection.push(cst.id);
|
controller.setSelected(prev => [
|
||||||
}
|
...prev.filter(cst_id => !filtered.find(cst => cst.id === cst_id)),
|
||||||
});
|
...newSelection
|
||||||
controller.setSelected(prev => [
|
]);
|
||||||
...prev.filter(cst_id => !filtered.find(cst => cst.id === cst_id)),
|
|
||||||
...newSelection
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
|
@ -142,7 +138,7 @@ function EditorRSList() {
|
||||||
{controller.isContentEditable ? (
|
{controller.isContentEditable ? (
|
||||||
<div className='flex items-center border-b'>
|
<div className='flex items-center border-b'>
|
||||||
<div className='px-2'>
|
<div className='px-2'>
|
||||||
Выбор {controller.selected.length} из {controller.schema?.stats?.count_all ?? 0}
|
Выбор {controller.selected.length} из {controller.schema.stats?.count_all}
|
||||||
</div>
|
</div>
|
||||||
<SearchBar
|
<SearchBar
|
||||||
id='constituents_search'
|
id='constituents_search'
|
||||||
|
|
|
@ -33,7 +33,7 @@ function ToolbarRSList() {
|
||||||
position='cc-tab-tools right-4 translate-x-0 md:right-1/2 md:translate-x-1/2'
|
position='cc-tab-tools right-4 translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||||
className='cc-icons cc-animate-position items-start outline-none'
|
className='cc-icons cc-animate-position items-start outline-none'
|
||||||
>
|
>
|
||||||
{controller.schema && controller.schema?.oss.length > 0 ? (
|
{controller.schema.oss.length > 0 ? (
|
||||||
<MiniSelectorOSS
|
<MiniSelectorOSS
|
||||||
items={controller.schema.oss}
|
items={controller.schema.oss}
|
||||||
onSelect={(event, value) => controller.navigateOss(value.id, event.ctrlKey || event.metaKey)}
|
onSelect={(event, value) => controller.navigateOss(value.id, event.ctrlKey || event.metaKey)}
|
||||||
|
@ -51,7 +51,7 @@ function ToolbarRSList() {
|
||||||
disabled={
|
disabled={
|
||||||
isProcessing ||
|
isProcessing ||
|
||||||
controller.selected.length === 0 ||
|
controller.selected.length === 0 ||
|
||||||
(controller.schema && controller.selected.length === controller.schema.items.length)
|
controller.selected.length === controller.schema.items.length
|
||||||
}
|
}
|
||||||
onClick={controller.moveUp}
|
onClick={controller.moveUp}
|
||||||
/>
|
/>
|
||||||
|
@ -61,7 +61,7 @@ function ToolbarRSList() {
|
||||||
disabled={
|
disabled={
|
||||||
isProcessing ||
|
isProcessing ||
|
||||||
controller.selected.length === 0 ||
|
controller.selected.length === 0 ||
|
||||||
(controller.schema && controller.selected.length === controller.schema.items.length)
|
controller.selected.length === controller.schema.items.length
|
||||||
}
|
}
|
||||||
onClick={controller.moveDown}
|
onClick={controller.moveDown}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { SelectorGraphColoring } from '@/utils/selectors';
|
||||||
import SchemasGuide from './SchemasGuide';
|
import SchemasGuide from './SchemasGuide';
|
||||||
|
|
||||||
interface GraphSelectorsProps {
|
interface GraphSelectorsProps {
|
||||||
schema?: IRSForm;
|
schema: IRSForm;
|
||||||
coloring: GraphColoring;
|
coloring: GraphColoring;
|
||||||
onChangeColoring: (newValue: GraphColoring) => void;
|
onChangeColoring: (newValue: GraphColoring) => void;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ function GraphSelectors({ schema, coloring, onChangeColoring }: GraphSelectorsPr
|
||||||
<Overlay position='right-[2.5rem] top-[0.25rem]'>
|
<Overlay position='right-[2.5rem] top-[0.25rem]'>
|
||||||
{coloring === 'status' ? <BadgeHelp topic={HelpTopic.UI_CST_STATUS} className='min-w-[25rem]' /> : null}
|
{coloring === 'status' ? <BadgeHelp topic={HelpTopic.UI_CST_STATUS} className='min-w-[25rem]' /> : null}
|
||||||
{coloring === 'type' ? <BadgeHelp topic={HelpTopic.UI_CST_CLASS} className='min-w-[25rem]' /> : null}
|
{coloring === 'type' ? <BadgeHelp topic={HelpTopic.UI_CST_CLASS} className='min-w-[25rem]' /> : null}
|
||||||
{coloring === 'schemas' && !!schema ? <SchemasGuide schema={schema} /> : null}
|
{coloring === 'schemas' ? <SchemasGuide schema={schema} /> : null}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
noBorder
|
noBorder
|
||||||
|
|
|
@ -72,7 +72,7 @@ function TGFlow() {
|
||||||
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [hoverID, setHoverID] = useState<ConstituentaID | undefined>(undefined);
|
const [hoverID, setHoverID] = useState<ConstituentaID | undefined>(undefined);
|
||||||
const hoverCst = hoverID && controller.schema?.cstByID.get(hoverID);
|
const hoverCst = hoverID && controller.schema.cstByID.get(hoverID);
|
||||||
const [hoverCstDebounced] = useDebounce(hoverCst, PARAMETER.graphPopupDelay);
|
const [hoverCstDebounced] = useDebounce(hoverCst, PARAMETER.graphPopupDelay);
|
||||||
const [hoverLeft, setHoverLeft] = useState(true);
|
const [hoverLeft, setHoverLeft] = useState(true);
|
||||||
|
|
||||||
|
@ -96,9 +96,6 @@ function TGFlow() {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!controller.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newDismissed: ConstituentaID[] = [];
|
const newDismissed: ConstituentaID[] = [];
|
||||||
controller.schema.items.forEach(cst => {
|
controller.schema.items.forEach(cst => {
|
||||||
if (!filteredGraph.nodes.has(cst.id)) {
|
if (!filteredGraph.nodes.has(cst.id)) {
|
||||||
|
@ -161,7 +158,7 @@ function TGFlow() {
|
||||||
}, [controller.schema, filter.noText, focusCst, coloring, flow.viewportInitialized]);
|
}, [controller.schema, filter.noText, focusCst, coloring, flow.viewportInitialized]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!controller.schema || !needReset || !flow.viewportInitialized) {
|
if (!needReset || !flow.viewportInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setNeedReset(false);
|
setNeedReset(false);
|
||||||
|
@ -180,24 +177,18 @@ function TGFlow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCreateCst() {
|
function handleCreateCst() {
|
||||||
if (!controller.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const definition = controller.selected.map(id => controller.schema.cstByID.get(id)!.alias).join(' ');
|
const definition = controller.selected.map(id => controller.schema.cstByID.get(id)!.alias).join(' ');
|
||||||
controller.createCst(controller.selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition);
|
controller.createCst(controller.selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeleteCst() {
|
function handleDeleteCst() {
|
||||||
if (!controller.schema || !controller.canDeleteSelected) {
|
if (!controller.canDeleteSelected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
controller.promptDeleteCst();
|
controller.promptDeleteCst();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSaveImage() {
|
function handleSaveImage() {
|
||||||
if (!controller.schema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
||||||
if (canvas === null) {
|
if (canvas === null) {
|
||||||
toast.error(errors.imageFailed);
|
toast.error(errors.imageFailed);
|
||||||
|
@ -220,7 +211,7 @@ function TGFlow() {
|
||||||
})
|
})
|
||||||
.then(dataURL => {
|
.then(dataURL => {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.setAttribute('download', `${controller.schema?.alias ?? 'graph'}.png`);
|
a.setAttribute('download', `${controller.schema.alias}.png`);
|
||||||
a.setAttribute('href', dataURL);
|
a.setAttribute('href', dataURL);
|
||||||
a.click();
|
a.click();
|
||||||
})
|
})
|
||||||
|
@ -263,7 +254,7 @@ function TGFlow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSetFocus(cstID: ConstituentaID | undefined) {
|
function handleSetFocus(cstID: ConstituentaID | undefined) {
|
||||||
const target = cstID !== undefined ? controller.schema?.cstByID.get(cstID) : cstID;
|
const target = cstID !== undefined ? controller.schema.cstByID.get(cstID) : cstID;
|
||||||
setFocusCst(prev => (prev === target ? undefined : target));
|
setFocusCst(prev => (prev === target ? undefined : target));
|
||||||
if (target) {
|
if (target) {
|
||||||
controller.setSelected([]);
|
controller.setSelected([]);
|
||||||
|
@ -314,9 +305,9 @@ function TGFlow() {
|
||||||
{!focusCst ? (
|
{!focusCst ? (
|
||||||
<ToolbarGraphSelection
|
<ToolbarGraphSelection
|
||||||
graph={controller.schema.graph}
|
graph={controller.schema.graph}
|
||||||
isCore={cstID => isBasicConcept(controller.schema?.cstByID.get(cstID)?.cst_type)}
|
isCore={cstID => isBasicConcept(controller.schema.cstByID.get(cstID)?.cst_type)}
|
||||||
isOwned={
|
isOwned={
|
||||||
controller.schema && controller.schema.inheritance.length > 0
|
controller.schema.inheritance.length > 0
|
||||||
? cstID => !controller.schema.cstByID.get(cstID)?.is_inherited
|
? cstID => !controller.schema.cstByID.get(cstID)?.is_inherited
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
@ -350,7 +341,7 @@ function TGFlow() {
|
||||||
<div className='cc-fade-in' tabIndex={-1} onKeyDown={handleKeyDown}>
|
<div className='cc-fade-in' tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||||
<SelectedCounter
|
<SelectedCounter
|
||||||
hideZero
|
hideZero
|
||||||
totalCount={controller.schema?.stats?.count_all ?? 0}
|
totalCount={controller.schema.stats?.count_all ?? 0}
|
||||||
selectedCount={controller.selected.length}
|
selectedCount={controller.selected.length}
|
||||||
position='top-[4.4rem] sm:top-[4.1rem] left-[0.5rem] sm:left-[0.65rem]'
|
position='top-[4.4rem] sm:top-[4.1rem] left-[0.5rem] sm:left-[0.65rem]'
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -52,17 +52,17 @@ function ToolbarTermGraph({
|
||||||
const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph);
|
const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph);
|
||||||
|
|
||||||
function handleShowTypeGraph() {
|
function handleShowTypeGraph() {
|
||||||
const typeInfo = controller.schema?.items.map(item => ({
|
const typeInfo = controller.schema.items.map(item => ({
|
||||||
alias: item.alias,
|
alias: item.alias,
|
||||||
result: item.parse.typification,
|
result: item.parse.typification,
|
||||||
args: item.parse.args
|
args: item.parse.args
|
||||||
}));
|
}));
|
||||||
showTypeGraph({ items: typeInfo ?? [] });
|
showTypeGraph({ items: typeInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-icons'>
|
<div className='cc-icons'>
|
||||||
{controller.schema && controller.schema?.oss.length > 0 ? (
|
{controller.schema.oss.length > 0 ? (
|
||||||
<MiniSelectorOSS
|
<MiniSelectorOSS
|
||||||
items={controller.schema.oss}
|
items={controller.schema.oss}
|
||||||
onSelect={(event, value) => controller.navigateOss(value.id, event.ctrlKey || event.metaKey)}
|
onSelect={(event, value) => controller.navigateOss(value.id, event.ctrlKey || event.metaKey)}
|
||||||
|
|
|
@ -18,9 +18,9 @@ import { globals, PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
|
||||||
interface ViewHiddenProps {
|
interface ViewHiddenProps {
|
||||||
|
schema: IRSForm;
|
||||||
items: ConstituentaID[];
|
items: ConstituentaID[];
|
||||||
selected: ConstituentaID[];
|
selected: ConstituentaID[];
|
||||||
schema?: IRSForm;
|
|
||||||
coloringScheme: GraphColoring;
|
coloringScheme: GraphColoring;
|
||||||
|
|
||||||
toggleSelection: (cstID: ConstituentaID) => void;
|
toggleSelection: (cstID: ConstituentaID) => void;
|
||||||
|
@ -45,7 +45,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!schema || items.length <= 0) {
|
if (items.length <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { useCstSubstitute } from '@/backend/rsform/useCstSubstitute';
|
import { useCstSubstitute } from '@/backend/rsform/useCstSubstitute';
|
||||||
import { useDownloadRSForm } from '@/backend/rsform/useDownloadRSForm';
|
import { useDownloadRSForm } from '@/backend/rsform/useDownloadRSForm';
|
||||||
import { useInlineSynthesis } from '@/backend/rsform/useInlineSynthesis';
|
import { useInlineSynthesis } from '@/backend/rsform/useInlineSynthesis';
|
||||||
|
@ -50,7 +49,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
import { useRoleStore } from '@/stores/role';
|
import { useRoleStore } from '@/stores/role';
|
||||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||||
import { describeAccessMode, information, labelAccessMode, tooltips } from '@/utils/labels';
|
import { describeAccessMode, labelAccessMode, tooltips } from '@/utils/labels';
|
||||||
import { generatePageQR, promptUnsaved, sharePage } from '@/utils/utils';
|
import { generatePageQR, promptUnsaved, sharePage } from '@/utils/utils';
|
||||||
|
|
||||||
import { OssTabID } from '../OssPage/OssEditContext';
|
import { OssTabID } from '../OssPage/OssEditContext';
|
||||||
|
@ -59,7 +58,7 @@ import { useRSEdit } from './RSEditContext';
|
||||||
function MenuRSTabs() {
|
function MenuRSTabs() {
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user, isAnonymous } = useAuthSuspense();
|
||||||
|
|
||||||
const role = useRoleStore(state => state.role);
|
const role = useRoleStore(state => state.role);
|
||||||
const setRole = useRoleStore(state => state.setRole);
|
const setRole = useRoleStore(state => state.setRole);
|
||||||
|
@ -94,9 +93,9 @@ function MenuRSTabs() {
|
||||||
const location = controller.schema.location;
|
const location = controller.schema.location;
|
||||||
const head = location.substring(0, 2) as LocationHead;
|
const head = location.substring(0, 2) as LocationHead;
|
||||||
if (head === LocationHead.LIBRARY) {
|
if (head === LocationHead.LIBRARY) {
|
||||||
return user?.is_staff ? location : LocationHead.USER;
|
return user.is_staff ? location : LocationHead.USER;
|
||||||
}
|
}
|
||||||
if (controller.schema.owner === user?.id) {
|
if (controller.schema.owner === user.id) {
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
return head === LocationHead.USER ? LocationHead.USER : location;
|
return head === LocationHead.USER ? LocationHead.USER : location;
|
||||||
|
@ -113,13 +112,19 @@ function MenuRSTabs() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fileName = (controller.schema.alias ?? 'Schema') + EXTEOR_TRS_FILE;
|
const fileName = (controller.schema.alias ?? 'Schema') + EXTEOR_TRS_FILE;
|
||||||
download({ itemID: controller.schema.id, version: controller.schema.version }, (data: Blob) => {
|
download(
|
||||||
try {
|
{
|
||||||
fileDownload(data, fileName);
|
itemID: controller.schema.id, //
|
||||||
} catch (error) {
|
version: controller.schema.version
|
||||||
console.error(error);
|
},
|
||||||
|
(data: Blob) => {
|
||||||
|
try {
|
||||||
|
fileDownload(data, fileName);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUpload() {
|
function handleUpload() {
|
||||||
|
@ -152,12 +157,12 @@ function MenuRSTabs() {
|
||||||
|
|
||||||
function handleReindex() {
|
function handleReindex() {
|
||||||
editMenu.hide();
|
editMenu.hide();
|
||||||
resetAliases(controller.schema.id, () => toast.success(information.reindexComplete));
|
resetAliases({ itemID: controller.schema.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRestoreOrder() {
|
function handleRestoreOrder() {
|
||||||
editMenu.hide();
|
editMenu.hide();
|
||||||
restoreOrder(controller.schema.id, () => toast.success(information.reorderComplete));
|
restoreOrder({ itemID: controller.schema.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubstituteCst() {
|
function handleSubstituteCst() {
|
||||||
|
@ -170,12 +175,11 @@ function MenuRSTabs() {
|
||||||
onSubstitute: data =>
|
onSubstitute: data =>
|
||||||
cstSubstitute(
|
cstSubstitute(
|
||||||
{
|
{
|
||||||
itemID: controller.schema.id, //
|
itemID: controller.schema.id,
|
||||||
data
|
data
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
controller.setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)));
|
controller.setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)));
|
||||||
toast.success(information.substituteSingle);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -200,7 +204,6 @@ function MenuRSTabs() {
|
||||||
data: { target: controller.activeCst.id }
|
data: { target: controller.activeCst.id }
|
||||||
},
|
},
|
||||||
cstList => {
|
cstList => {
|
||||||
toast.success(information.addedConstituents(cstList.length));
|
|
||||||
if (cstList.length !== 0) {
|
if (cstList.length !== 0) {
|
||||||
controller.setSelected(cstList);
|
controller.setSelected(cstList);
|
||||||
}
|
}
|
||||||
|
@ -216,11 +219,7 @@ function MenuRSTabs() {
|
||||||
showInlineSynthesis({
|
showInlineSynthesis({
|
||||||
receiver: controller.schema,
|
receiver: controller.schema,
|
||||||
onInlineSynthesis: data => {
|
onInlineSynthesis: data => {
|
||||||
const oldCount = controller.schema.items.length;
|
inlineSynthesis({ itemID: controller.schema.id, data }, () => controller.deselectAll());
|
||||||
inlineSynthesis({ itemID: controller.schema.id, data }, newSchema => {
|
|
||||||
controller.deselectAll();
|
|
||||||
toast.success(information.addedConstituents(newSchema.items.length - oldCount));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -258,7 +257,7 @@ function MenuRSTabs() {
|
||||||
titleHtml={tooltips.shareItem(controller.schema.access_policy)}
|
titleHtml={tooltips.shareItem(controller.schema.access_policy)}
|
||||||
icon={<IconShare size='1rem' className='icon-primary' />}
|
icon={<IconShare size='1rem' className='icon-primary' />}
|
||||||
onClick={handleShare}
|
onClick={handleShare}
|
||||||
disabled={controller.schema?.access_policy !== AccessPolicy.PUBLIC}
|
disabled={controller.schema.access_policy !== AccessPolicy.PUBLIC}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='QR-код'
|
text='QR-код'
|
||||||
|
@ -266,7 +265,7 @@ function MenuRSTabs() {
|
||||||
icon={<IconQR size='1rem' className='icon-primary' />}
|
icon={<IconQR size='1rem' className='icon-primary' />}
|
||||||
onClick={handleShowQR}
|
onClick={handleShowQR}
|
||||||
/>
|
/>
|
||||||
{user ? (
|
{!isAnonymous ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Клонировать'
|
text='Клонировать'
|
||||||
icon={<IconClone size='1rem' className='icon-green' />}
|
icon={<IconClone size='1rem' className='icon-green' />}
|
||||||
|
@ -283,7 +282,7 @@ function MenuRSTabs() {
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Загрузить из Экстеор'
|
text='Загрузить из Экстеор'
|
||||||
icon={<IconUpload size='1rem' className='icon-red' />}
|
icon={<IconUpload size='1rem' className='icon-red' />}
|
||||||
disabled={isProcessing || controller.schema?.oss.length !== 0}
|
disabled={isProcessing || controller.schema.oss.length !== 0}
|
||||||
onClick={handleUpload}
|
onClick={handleUpload}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -298,7 +297,7 @@ function MenuRSTabs() {
|
||||||
|
|
||||||
<Divider margins='mx-3 my-1' />
|
<Divider margins='mx-3 my-1' />
|
||||||
|
|
||||||
{user ? (
|
{!isAnonymous ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Создать новую схему'
|
text='Создать новую схему'
|
||||||
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
icon={<IconNewItem size='1rem' className='icon-primary' />}
|
||||||
|
@ -319,7 +318,7 @@ function MenuRSTabs() {
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
{!controller.isArchive && user ? (
|
{!controller.isArchive && !isAnonymous ? (
|
||||||
<div ref={editMenu.ref}>
|
<div ref={editMenu.ref}>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
|
@ -381,7 +380,7 @@ function MenuRSTabs() {
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{controller.isArchive && user ? (
|
{controller.isArchive && !isAnonymous ? (
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
|
@ -394,7 +393,7 @@ function MenuRSTabs() {
|
||||||
onClick={event => router.push(urls.schema(controller.schema.id), event.ctrlKey || event.metaKey)}
|
onClick={event => router.push(urls.schema(controller.schema.id), event.ctrlKey || event.metaKey)}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{user ? (
|
{!isAnonymous ? (
|
||||||
<div ref={accessMenu.ref}>
|
<div ref={accessMenu.ref}>
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
|
@ -428,7 +427,7 @@ function MenuRSTabs() {
|
||||||
text={labelAccessMode(UserRole.EDITOR)}
|
text={labelAccessMode(UserRole.EDITOR)}
|
||||||
title={describeAccessMode(UserRole.EDITOR)}
|
title={describeAccessMode(UserRole.EDITOR)}
|
||||||
icon={<IconEditor size='1rem' className='icon-primary' />}
|
icon={<IconEditor size='1rem' className='icon-primary' />}
|
||||||
disabled={!controller.isOwned && !controller.schema?.editors.includes(user.id)}
|
disabled={!controller.isOwned && (!user.id || !controller.schema.editors.includes(user.id))}
|
||||||
onClick={() => handleChangeMode(UserRole.EDITOR)}
|
onClick={() => handleChangeMode(UserRole.EDITOR)}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
@ -442,13 +441,13 @@ function MenuRSTabs() {
|
||||||
text={labelAccessMode(UserRole.ADMIN)}
|
text={labelAccessMode(UserRole.ADMIN)}
|
||||||
title={describeAccessMode(UserRole.ADMIN)}
|
title={describeAccessMode(UserRole.ADMIN)}
|
||||||
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
||||||
disabled={!user?.is_staff}
|
disabled={!user.is_staff}
|
||||||
onClick={() => handleChangeMode(UserRole.ADMIN)}
|
onClick={() => handleChangeMode(UserRole.ADMIN)}
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{!user ? (
|
{isAnonymous ? (
|
||||||
<Button
|
<Button
|
||||||
dense
|
dense
|
||||||
noBorder
|
noBorder
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createContext, useContext, useEffect, useState } from 'react';
|
import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { useDeleteItem } from '@/backend/library/useDeleteItem';
|
import { useDeleteItem } from '@/backend/library/useDeleteItem';
|
||||||
import { ICstCreateDTO } from '@/backend/rsform/api';
|
import { ICstCreateDTO } from '@/backend/rsform/api';
|
||||||
import { useCstCreate } from '@/backend/rsform/useCstCreate';
|
import { useCstCreate } from '@/backend/rsform/useCstCreate';
|
||||||
|
@ -21,7 +20,7 @@ import { useModificationStore } from '@/stores/modification';
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { useRoleStore } from '@/stores/role';
|
import { useRoleStore } from '@/stores/role';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { information, prompts } from '@/utils/labels';
|
import { prompts } from '@/utils/labels';
|
||||||
import { promptUnsaved } from '@/utils/utils';
|
import { promptUnsaved } from '@/utils/utils';
|
||||||
|
|
||||||
import { OssTabID } from '../OssPage/OssEditContext';
|
import { OssTabID } from '../OssPage/OssEditContext';
|
||||||
|
@ -37,6 +36,7 @@ export interface IRSEditContext extends ILibraryItemEditor {
|
||||||
schema: IRSForm;
|
schema: IRSForm;
|
||||||
selected: ConstituentaID[];
|
selected: ConstituentaID[];
|
||||||
activeCst?: IConstituenta;
|
activeCst?: IConstituenta;
|
||||||
|
activeVersion?: VersionID;
|
||||||
|
|
||||||
isOwned: boolean;
|
isOwned: boolean;
|
||||||
isArchive: boolean;
|
isArchive: boolean;
|
||||||
|
@ -45,6 +45,7 @@ export interface IRSEditContext extends ILibraryItemEditor {
|
||||||
isAttachedToOSS: boolean;
|
isAttachedToOSS: boolean;
|
||||||
canDeleteSelected: boolean;
|
canDeleteSelected: boolean;
|
||||||
|
|
||||||
|
navigateVersion: (versionID: VersionID | undefined) => void;
|
||||||
navigateRSForm: ({ tab, activeID }: { tab: RSTabID; activeID?: ConstituentaID }) => void;
|
navigateRSForm: ({ tab, activeID }: { tab: RSTabID; activeID?: ConstituentaID }) => void;
|
||||||
navigateCst: (cstID: ConstituentaID) => void;
|
navigateCst: (cstID: ConstituentaID) => void;
|
||||||
navigateOss: (target: LibraryItemID, newTab?: boolean) => void;
|
navigateOss: (target: LibraryItemID, newTab?: boolean) => void;
|
||||||
|
@ -77,21 +78,26 @@ export const useRSEdit = () => {
|
||||||
interface RSEditStateProps {
|
interface RSEditStateProps {
|
||||||
itemID: LibraryItemID;
|
itemID: LibraryItemID;
|
||||||
activeTab: RSTabID;
|
activeTab: RSTabID;
|
||||||
versionID?: VersionID;
|
activeVersion?: VersionID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RSEditState = ({ itemID, versionID, activeTab, children }: React.PropsWithChildren<RSEditStateProps>) => {
|
export const RSEditState = ({
|
||||||
|
itemID,
|
||||||
|
activeVersion,
|
||||||
|
activeTab,
|
||||||
|
children
|
||||||
|
}: React.PropsWithChildren<RSEditStateProps>) => {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
|
||||||
const adminMode = usePreferencesStore(state => state.adminMode);
|
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||||
const role = useRoleStore(state => state.role);
|
const role = useRoleStore(state => state.role);
|
||||||
const adjustRole = useRoleStore(state => state.adjustRole);
|
const adjustRole = useRoleStore(state => state.adjustRole);
|
||||||
|
|
||||||
const { schema } = useRSFormSuspense({ itemID: itemID, version: versionID });
|
const { user } = useAuthSuspense();
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: itemID, version: activeVersion });
|
||||||
const { isModified } = useModificationStore();
|
const { isModified } = useModificationStore();
|
||||||
|
|
||||||
const isOwned = user?.id === schema?.owner || false;
|
const isOwned = !!user.id && user.id === schema.owner;
|
||||||
const isArchive = !!versionID;
|
const isArchive = !!activeVersion;
|
||||||
const isMutable = role > UserRole.READER && !schema.read_only;
|
const isMutable = role > UserRole.READER && !schema.read_only;
|
||||||
const isContentEditable = isMutable && !isArchive;
|
const isContentEditable = isMutable && !isArchive;
|
||||||
const isAttachedToOSS = schema.oss.length > 0;
|
const isAttachedToOSS = schema.oss.length > 0;
|
||||||
|
@ -114,13 +120,17 @@ export const RSEditState = ({ itemID, versionID, activeTab, children }: React.Pr
|
||||||
() =>
|
() =>
|
||||||
adjustRole({
|
adjustRole({
|
||||||
isOwner: isOwned,
|
isOwner: isOwned,
|
||||||
isEditor: (user && schema?.editors.includes(user?.id)) ?? false,
|
isEditor: !!user.id && schema.editors.includes(user.id),
|
||||||
isStaff: user?.is_staff ?? false,
|
isStaff: user.is_staff,
|
||||||
adminMode: adminMode
|
adminMode: adminMode
|
||||||
}),
|
}),
|
||||||
[schema, adjustRole, isOwned, user, adminMode]
|
[schema, adjustRole, isOwned, user, adminMode]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function navigateVersion(versionID: VersionID | undefined) {
|
||||||
|
router.push(urls.schema(schema.id, versionID));
|
||||||
|
}
|
||||||
|
|
||||||
function navigateOss(target: LibraryItemID, newTab?: boolean) {
|
function navigateOss(target: LibraryItemID, newTab?: boolean) {
|
||||||
router.push(urls.oss(target), newTab);
|
router.push(urls.oss(target), newTab);
|
||||||
}
|
}
|
||||||
|
@ -133,7 +143,7 @@ export const RSEditState = ({ itemID, versionID, activeTab, children }: React.Pr
|
||||||
id: schema.id,
|
id: schema.id,
|
||||||
tab: tab,
|
tab: tab,
|
||||||
active: activeID,
|
active: activeID,
|
||||||
version: versionID
|
version: activeVersion
|
||||||
};
|
};
|
||||||
const url = urls.schema_props(data);
|
const url = urls.schema_props(data);
|
||||||
if (activeID) {
|
if (activeID) {
|
||||||
|
@ -162,7 +172,6 @@ export const RSEditState = ({ itemID, versionID, activeTab, children }: React.Pr
|
||||||
}
|
}
|
||||||
const ossID = schema.oss.length > 0 ? schema.oss[0].id : undefined;
|
const ossID = schema.oss.length > 0 ? schema.oss[0].id : undefined;
|
||||||
deleteItem(schema.id, () => {
|
deleteItem(schema.id, () => {
|
||||||
toast.success(information.itemDestroyed);
|
|
||||||
if (ossID) {
|
if (ossID) {
|
||||||
router.push(urls.oss(ossID, OssTabID.GRAPH));
|
router.push(urls.oss(ossID, OssTabID.GRAPH));
|
||||||
} else {
|
} else {
|
||||||
|
@ -174,7 +183,6 @@ export const RSEditState = ({ itemID, versionID, activeTab, children }: React.Pr
|
||||||
function handleCreateCst(data: ICstCreateDTO) {
|
function handleCreateCst(data: ICstCreateDTO) {
|
||||||
data.alias = data.alias || generateAlias(data.cst_type, schema);
|
data.alias = data.alias || generateAlias(data.cst_type, schema);
|
||||||
cstCreate({ itemID: itemID, data }, newCst => {
|
cstCreate({ itemID: itemID, data }, newCst => {
|
||||||
toast.success(information.newConstituent(newCst.alias));
|
|
||||||
setSelected([newCst.id]);
|
setSelected([newCst.id]);
|
||||||
navigateRSForm({ tab: activeTab, activeID: newCst.id });
|
navigateRSForm({ tab: activeTab, activeID: newCst.id });
|
||||||
if (activeTab === RSTabID.CST_LIST) {
|
if (activeTab === RSTabID.CST_LIST) {
|
||||||
|
@ -197,12 +205,10 @@ export const RSEditState = ({ itemID, versionID, activeTab, children }: React.Pr
|
||||||
items: deleted
|
items: deleted
|
||||||
};
|
};
|
||||||
|
|
||||||
const deletedNames = deleted.map(id => schema.cstByID.get(id)!.alias).join(', ');
|
|
||||||
const isEmpty = deleted.length === schema.items.length;
|
const isEmpty = deleted.length === schema.items.length;
|
||||||
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeCst?.id, schema.items, deleted);
|
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeCst?.id, schema.items, deleted);
|
||||||
|
|
||||||
cstDelete({ itemID: itemID, data }, () => {
|
cstDelete({ itemID: itemID, data }, () => {
|
||||||
toast.success(information.constituentsDestroyed(deletedNames));
|
|
||||||
setSelected(nextActive ? [nextActive] : []);
|
setSelected(nextActive ? [nextActive] : []);
|
||||||
if (!nextActive) {
|
if (!nextActive) {
|
||||||
navigateRSForm({ tab: RSTabID.CST_LIST });
|
navigateRSForm({ tab: RSTabID.CST_LIST });
|
||||||
|
@ -314,6 +320,7 @@ export const RSEditState = ({ itemID, versionID, activeTab, children }: React.Pr
|
||||||
schema,
|
schema,
|
||||||
selected,
|
selected,
|
||||||
activeCst,
|
activeCst,
|
||||||
|
activeVersion,
|
||||||
|
|
||||||
isOwned,
|
isOwned,
|
||||||
isArchive,
|
isArchive,
|
||||||
|
@ -322,11 +329,12 @@ export const RSEditState = ({ itemID, versionID, activeTab, children }: React.Pr
|
||||||
isAttachedToOSS,
|
isAttachedToOSS,
|
||||||
canDeleteSelected,
|
canDeleteSelected,
|
||||||
|
|
||||||
|
navigateVersion,
|
||||||
navigateRSForm,
|
navigateRSForm,
|
||||||
navigateCst,
|
navigateCst,
|
||||||
|
navigateOss,
|
||||||
|
|
||||||
deleteSchema,
|
deleteSchema,
|
||||||
navigateOss,
|
|
||||||
|
|
||||||
setSelected,
|
setSelected,
|
||||||
select: (target: ConstituentaID) => setSelected(prev => [...prev, target]),
|
select: (target: ConstituentaID) => setSelected(prev => [...prev, target]),
|
||||||
|
|
|
@ -37,7 +37,7 @@ function RSFormPage() {
|
||||||
<ProcessError error={error as ErrorData} isArchive={!!version} itemID={itemID} />
|
<ProcessError error={error as ErrorData} isArchive={!!version} itemID={itemID} />
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<RSEditState itemID={itemID} versionID={version} activeTab={activeTab}>
|
<RSEditState itemID={itemID} activeVersion={version} activeTab={activeTab}>
|
||||||
<RSTabs />
|
<RSTabs />
|
||||||
</RSEditState>
|
</RSEditState>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { matchConstituenta } from '@/models/rsformAPI';
|
||||||
import { useCstSearchStore } from '@/stores/cstSearch';
|
import { useCstSearchStore } from '@/stores/cstSearch';
|
||||||
|
|
||||||
interface ConstituentsSearchProps {
|
interface ConstituentsSearchProps {
|
||||||
schema?: IRSForm;
|
schema: IRSForm;
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
activeID?: ConstituentaID;
|
activeID?: ConstituentaID;
|
||||||
activeExpression: string;
|
activeExpression: string;
|
||||||
|
@ -31,7 +31,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
|
||||||
const toggleInherited = useCstSearchStore(state => state.toggleInherited);
|
const toggleInherited = useCstSearchStore(state => state.toggleInherited);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!schema || schema.items.length === 0) {
|
if (schema.items.length === 0) {
|
||||||
setFiltered([]);
|
setFiltered([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
|
||||||
setFiltered,
|
setFiltered,
|
||||||
filterSource,
|
filterSource,
|
||||||
activeExpression,
|
activeExpression,
|
||||||
schema?.items,
|
schema.items,
|
||||||
schema,
|
schema,
|
||||||
filterMatch,
|
filterMatch,
|
||||||
activeID,
|
activeID,
|
||||||
|
@ -71,7 +71,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
|
||||||
/>
|
/>
|
||||||
<SelectMatchMode value={filterMatch} onChange={newValue => setMatch(newValue)} dense={dense} />
|
<SelectMatchMode value={filterMatch} onChange={newValue => setMatch(newValue)} dense={dense} />
|
||||||
<SelectGraphFilter value={filterSource} onChange={newValue => setSource(newValue)} dense={dense} />
|
<SelectGraphFilter value={filterSource} onChange={newValue => setSource(newValue)} dense={dense} />
|
||||||
{schema && schema?.stats.count_inherited > 0 ? (
|
{schema.stats.count_inherited > 0 ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
titleHtml={`Наследованные: <b>${includeInherited ? 'отображать' : 'скрывать'}</b>`}
|
titleHtml={`Наследованные: <b>${includeInherited ? 'отображать' : 'скрывать'}</b>`}
|
||||||
|
|
|
@ -29,7 +29,7 @@ function ViewConstituents({ expression, isBottom, isMounted }: ViewConstituentsP
|
||||||
const listHeight = useFitHeight(!isBottom ? '8.2rem' : role !== UserRole.READER ? '42rem' : '35rem', '10rem');
|
const listHeight = useFitHeight(!isBottom ? '8.2rem' : role !== UserRole.READER ? '42rem' : '35rem', '10rem');
|
||||||
const { schema, activeCst, navigateCst } = useRSEdit();
|
const { schema, activeCst, navigateCst } = useRSEdit();
|
||||||
|
|
||||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
|
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema.items);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
|
@ -20,9 +19,7 @@ import TextInput from '@/components/ui/TextInput';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import Tooltip from '@/components/ui/Tooltip';
|
import Tooltip from '@/components/ui/Tooltip';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { IUserSignupData } from '@/models/user';
|
|
||||||
import { globals, patterns } from '@/utils/constants';
|
import { globals, patterns } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
|
|
||||||
function FormSignup() {
|
function FormSignup() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
@ -54,20 +51,20 @@ function FormSignup() {
|
||||||
|
|
||||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!isPending) {
|
if (isPending) {
|
||||||
const data: IUserSignupData = {
|
return;
|
||||||
|
}
|
||||||
|
signup(
|
||||||
|
{
|
||||||
username,
|
username,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
password2,
|
password2,
|
||||||
first_name: firstName,
|
first_name: firstName,
|
||||||
last_name: lastName
|
last_name: lastName
|
||||||
};
|
},
|
||||||
signup(data, createdUser => {
|
createdUser => router.push(urls.login_hint(createdUser.username))
|
||||||
router.push(urls.login_hint(createdUser.username));
|
);
|
||||||
toast.success(information.newUser(createdUser.username));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<form className={clsx('cc-fade-in cc-column', 'mx-auto w-[36rem]', 'px-6 py-3')} onSubmit={handleSubmit}>
|
<form className={clsx('cc-fade-in cc-column', 'mx-auto w-[36rem]', 'px-6 py-3')} onSubmit={handleSubmit}>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
import SubmitButton from '@/components/ui/SubmitButton';
|
import SubmitButton from '@/components/ui/SubmitButton';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import { errors, information } from '@/utils/labels';
|
import { errors } from '@/utils/labels';
|
||||||
|
|
||||||
function EditorPassword() {
|
function EditorPassword() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
@ -38,10 +38,7 @@ function EditorPassword() {
|
||||||
old_password: oldPassword,
|
old_password: oldPassword,
|
||||||
new_password: newPassword
|
new_password: newPassword
|
||||||
};
|
};
|
||||||
changePassword(data, () => {
|
changePassword(data, () => router.push(urls.login));
|
||||||
toast.success(information.changesSaved);
|
|
||||||
router.push(urls.login);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { useBlockNavigation } from '@/app/Navigation/NavigationContext';
|
import { useBlockNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { IUpdateProfileDTO } from '@/backend/users/api';
|
import { IUpdateProfileDTO } from '@/backend/users/api';
|
||||||
|
@ -11,7 +10,6 @@ import { useUpdateProfile } from '@/backend/users/useUpdateProfile';
|
||||||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||||
import SubmitButton from '@/components/ui/SubmitButton';
|
import SubmitButton from '@/components/ui/SubmitButton';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import { information } from '@/utils/labels';
|
|
||||||
|
|
||||||
function EditorProfile() {
|
function EditorProfile() {
|
||||||
const { profile } = useProfileSuspense();
|
const { profile } = useProfileSuspense();
|
||||||
|
@ -41,7 +39,7 @@ function EditorProfile() {
|
||||||
first_name: first_name,
|
first_name: first_name,
|
||||||
last_name: last_name
|
last_name: last_name
|
||||||
};
|
};
|
||||||
updateProfile(data, () => toast.success(information.changesSaved));
|
updateProfile(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -90,7 +90,7 @@ export function prepareTooltip(text: string, hotkey?: string) {
|
||||||
* Generates label for {@link IVersionInfo} of {@link IRSForm}.
|
* Generates label for {@link IVersionInfo} of {@link IRSForm}.
|
||||||
*/
|
*/
|
||||||
export function labelVersion(schema?: IRSForm) {
|
export function labelVersion(schema?: IRSForm) {
|
||||||
const version = schema?.versions.find(ver => ver.id === schema.version);
|
const version = schema?.versions.find(ver => ver.id === schema?.version);
|
||||||
return version ? version.version : 'актуальная';
|
return version ? version.version : 'актуальная';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,6 +957,8 @@ export const information = {
|
||||||
cloneComplete: (alias: string) => `Копия создана: ${alias}`,
|
cloneComplete: (alias: string) => `Копия создана: ${alias}`,
|
||||||
noDataToExport: 'Нет данных для экспорта',
|
noDataToExport: 'Нет данных для экспорта',
|
||||||
substitutionsCorrect: 'Таблица отождествлений прошла проверку',
|
substitutionsCorrect: 'Таблица отождествлений прошла проверку',
|
||||||
|
uploadSuccess: 'Схема загружена из файла',
|
||||||
|
inlineSynthesisComplete: 'Встраивание завершено',
|
||||||
|
|
||||||
newLibraryItem: 'Схема успешно создана',
|
newLibraryItem: 'Схема успешно создана',
|
||||||
addedConstituents: (count: number) => `Добавлены конституенты: ${count}`,
|
addedConstituents: (count: number) => `Добавлены конституенты: ${count}`,
|
||||||
|
@ -964,14 +966,13 @@ export const information = {
|
||||||
newVersion: (version: string) => `Версия создана: ${version}`,
|
newVersion: (version: string) => `Версия создана: ${version}`,
|
||||||
newConstituent: (alias: string) => `Конституента добавлена: ${alias}`,
|
newConstituent: (alias: string) => `Конституента добавлена: ${alias}`,
|
||||||
newOperation: (alias: string) => `Операция добавлена: ${alias}`,
|
newOperation: (alias: string) => `Операция добавлена: ${alias}`,
|
||||||
renameComplete: (oldAlias: string, newAlias: string) => `Переименование: ${oldAlias} -> ${newAlias}`,
|
|
||||||
|
|
||||||
versionDestroyed: 'Версия удалена',
|
versionDestroyed: 'Версия удалена',
|
||||||
itemDestroyed: 'Схема удалена',
|
itemDestroyed: 'Схема удалена',
|
||||||
operationDestroyed: 'Операция удалена',
|
operationDestroyed: 'Операция удалена',
|
||||||
operationExecuted: 'Операция выполнена',
|
operationExecuted: 'Операция выполнена',
|
||||||
allOperationExecuted: 'Все операции выполнены',
|
allOperationExecuted: 'Все операции выполнены',
|
||||||
constituentsDestroyed: (aliases: string) => `Конституенты удалены: ${aliases}`
|
constituentsDestroyed: (count: number) => `Конституенты удалены: ${count}`
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue
Block a user