R: Refine feature boundaries and dependencies

This commit is contained in:
Ivan 2025-02-12 20:53:31 +03:00
parent 012fb142ed
commit c75af03bea
134 changed files with 1132 additions and 1069 deletions

View File

@ -7,3 +7,18 @@ export const DELAYS = {
staleMedium: 1 * 60 * 60 * 1000,
staleLong: 24 * 60 * 60 * 1000
};
/** API keys for local cache. */
export const KEYS = {
oss: 'oss',
rsform: 'rsform',
library: 'library',
users: 'users',
cctext: 'cctext',
composite: {
libraryList: ['library', 'list'],
ossItem: ({ itemID }: { itemID?: number }) => [KEYS.oss, 'item', itemID],
rsItem: ({ itemID, version }: { itemID?: number; version?: number }) => [KEYS.rsform, 'item', itemID, version ?? '']
}
};

View File

@ -1,77 +1,17 @@
import { queryOptions } from '@tanstack/react-query';
import { z } from 'zod';
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
import { DELAYS } from '@/backend/configuration';
import { errorMsg, infoMsg } from '@/utils/labels';
import { infoMsg } from '@/utils/labels';
/**
* Represents CurrentUser information.
*/
export interface ICurrentUser {
id: number | null;
username: string;
is_staff: boolean;
editor: number[];
}
/**
* Represents login data, used to authenticate users.
*/
export const schemaUserLogin = z.object({
username: z.string().nonempty(errorMsg.requiredField),
password: z.string().nonempty(errorMsg.requiredField)
});
/**
* Represents login data, used to authenticate users.
*/
export type IUserLoginDTO = z.infer<typeof schemaUserLogin>;
/**
* Represents data needed to update password for current user.
*/
export const schemaChangePassword = z
.object({
old_password: z.string().nonempty(errorMsg.requiredField),
new_password: z.string().nonempty(errorMsg.requiredField),
new_password2: z.string().nonempty(errorMsg.requiredField)
})
.refine(schema => schema.new_password === schema.new_password2, {
path: ['new_password2'],
message: errorMsg.passwordsMismatch
})
.refine(schema => schema.old_password !== schema.new_password, {
path: ['new_password'],
message: errorMsg.passwordsSame
});
/**
* Represents data needed to update password for current user.
*/
export type IChangePasswordDTO = z.infer<typeof schemaChangePassword>;
/**
* Represents password reset request data.
*/
export interface IRequestPasswordDTO {
email: string;
}
/**
* Represents password reset data.
*/
export interface IResetPasswordDTO {
password: string;
token: string;
}
/**
* Represents password token data.
*/
export interface IPasswordTokenDTO {
token: string;
}
import {
IChangePasswordDTO,
ICurrentUser,
IPasswordTokenDTO,
IRequestPasswordDTO,
IResetPasswordDTO,
IUserLoginDTO
} from './types';
/**
* Authentication API.

View File

@ -0,0 +1,71 @@
import { z } from 'zod';
import { errorMsg } from '@/utils/labels';
/**
* Represents CurrentUser information.
*/
export interface ICurrentUser {
id: number | null;
username: string;
is_staff: boolean;
editor: number[];
}
/**
* Represents login data, used to authenticate users.
*/
export const schemaUserLogin = z.object({
username: z.string().nonempty(errorMsg.requiredField),
password: z.string().nonempty(errorMsg.requiredField)
});
/**
* Represents login data, used to authenticate users.
*/
export type IUserLoginDTO = z.infer<typeof schemaUserLogin>;
/**
* Represents data needed to update password for current user.
*/
export const schemaChangePassword = z
.object({
old_password: z.string().nonempty(errorMsg.requiredField),
new_password: z.string().nonempty(errorMsg.requiredField),
new_password2: z.string().nonempty(errorMsg.requiredField)
})
.refine(schema => schema.new_password === schema.new_password2, {
path: ['new_password2'],
message: errorMsg.passwordsMismatch
})
.refine(schema => schema.old_password !== schema.new_password, {
path: ['new_password'],
message: errorMsg.passwordsSame
});
/**
* Represents data needed to update password for current user.
*/
export type IChangePasswordDTO = z.infer<typeof schemaChangePassword>;
/**
* Represents password reset request data.
*/
export interface IRequestPasswordDTO {
email: string;
}
/**
* Represents password reset data.
*/
export interface IResetPasswordDTO {
password: string;
token: string;
}
/**
* Represents password token data.
*/
export interface IPasswordTokenDTO {
token: string;
}

View File

@ -1,6 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { authApi, IChangePasswordDTO } from './api';
import { authApi } from './api';
import { IChangePasswordDTO } from './types';
export const useChangePassword = () => {
const client = useQueryClient();

View File

@ -1,6 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { authApi, IUserLoginDTO } from './api';
import { authApi } from './api';
import { IUserLoginDTO } from './types';
export const useLogin = () => {
const client = useQueryClient();

View File

@ -1,6 +1,7 @@
import { useMutation } from '@tanstack/react-query';
import { authApi, IRequestPasswordDTO } from './api';
import { authApi } from './api';
import { IRequestPasswordDTO } from './types';
export const useRequestPasswordReset = () => {
const mutation = useMutation({

View File

@ -1,6 +1,7 @@
import { useMutation } from '@tanstack/react-query';
import { authApi, IPasswordTokenDTO, IResetPasswordDTO } from './api';
import { authApi } from './api';
import { IPasswordTokenDTO, IResetPasswordDTO } from './types';
export const useResetPassword = () => {
const validateMutation = useMutation({

View File

@ -12,7 +12,7 @@ import { TextInput } from '@/components/Input';
import useQueryStrings from '@/hooks/useQueryStrings';
import { resources } from '@/utils/constants';
import { IUserLoginDTO, schemaUserLogin } from '../backend/api';
import { IUserLoginDTO, schemaUserLogin } from '../backend/types';
import { useAuthSuspense } from '../backend/useAuth';
import { useLogin } from '../backend/useLogin';
import { ExpectedAnonymous } from '../components/ExpectedAnonymous';

View File

@ -1,139 +1,25 @@
import { queryOptions } from '@tanstack/react-query';
import { z } from 'zod';
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
import { DELAYS } from '@/backend/configuration';
import { ossApi } from '@/features/oss/backend/api';
import { IRSFormDTO, rsformsApi } from '@/features/rsform/backend/api';
import { errorMsg, infoMsg } from '@/utils/labels';
import { DELAYS, KEYS } from '@/backend/configuration';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { infoMsg } from '@/utils/labels';
import { AccessPolicy, ILibraryItem, IVersionInfo, LibraryItemID, LibraryItemType, VersionID } from '../models/library';
import { validateLocation } from '../models/libraryAPI';
/**
* Represents update data for renaming Location.
*/
export interface IRenameLocationDTO {
target: string;
new_location: string;
}
/**
* Represents data, used for cloning {@link IRSForm}.
*/
export const schemaCloneLibraryItem = z.object({
id: z.number(),
item_type: z.nativeEnum(LibraryItemType),
title: z.string().nonempty(errorMsg.requiredField),
alias: z.string().nonempty(errorMsg.requiredField),
comment: z.string(),
visible: z.boolean(),
read_only: z.boolean(),
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
access_policy: z.nativeEnum(AccessPolicy),
items: z.array(z.number()).optional()
});
/**
* Represents data, used for cloning {@link IRSForm}.
*/
export type ICloneLibraryItemDTO = z.infer<typeof schemaCloneLibraryItem>;
/**
* Represents data, used for creating {@link IRSForm}.
*/
export const schemaCreateLibraryItem = z
.object({
item_type: z.nativeEnum(LibraryItemType),
title: z.string().optional(),
alias: z.string().optional(),
comment: z.string(),
visible: z.boolean(),
read_only: z.boolean(),
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
access_policy: z.nativeEnum(AccessPolicy),
file: z.instanceof(File).optional(),
fileName: z.string().optional()
})
.refine(data => !!data.file || !!data.title, {
path: ['title'],
message: errorMsg.requiredField
})
.refine(data => !!data.file || !!data.alias, {
path: ['alias'],
message: errorMsg.requiredField
});
/**
* Represents data, used for creating {@link IRSForm}.
*/
export type ICreateLibraryItemDTO = z.infer<typeof schemaCreateLibraryItem>;
/**
* Represents update data for editing {@link ILibraryItem}.
*/
export const schemaUpdateLibraryItem = z.object({
id: z.number(),
item_type: z.nativeEnum(LibraryItemType),
title: z.string().nonempty(errorMsg.requiredField),
alias: z.string().nonempty(errorMsg.requiredField),
comment: z.string(),
visible: z.boolean(),
read_only: z.boolean()
});
/**
* Represents update data for editing {@link ILibraryItem}.
*/
export type IUpdateLibraryItemDTO = z.infer<typeof schemaUpdateLibraryItem>;
/**
* Create version metadata in persistent storage.
*/
export const schemaVersionCreate = z.object({
version: z.string(),
description: z.string(),
items: z.array(z.number()).optional()
});
/**
* Create version metadata in persistent storage.
*/
export type IVersionCreateDTO = z.infer<typeof schemaVersionCreate>;
/**
* Represents data response when creating {@link IVersionInfo}.
*/
export interface IVersionCreatedResponse {
version: number;
schema: IRSFormDTO;
}
/**
* Represents version data, intended to update version metadata in persistent storage.
*/
export const schemaVersionUpdate = z.object({
id: z.number(),
version: z.string().nonempty(errorMsg.requiredField),
description: z.string()
});
/**
* Represents version data, intended to update version metadata in persistent storage.
*/
export type IVersionUpdateDTO = z.infer<typeof schemaVersionUpdate>;
import { AccessPolicy, ILibraryItem, IVersionInfo } from '../models/library';
import {
ICloneLibraryItemDTO,
ICreateLibraryItemDTO,
IRenameLocationDTO,
IUpdateLibraryItemDTO,
IVersionCreatedResponse,
IVersionCreateDTO,
IVersionUpdateDTO
} from './types';
export const libraryApi = {
baseKey: 'library',
libraryListKey: ['library', 'list'],
baseKey: KEYS.library,
libraryListKey: KEYS.composite.libraryList,
getItemQueryOptions: ({ itemID, itemType }: { itemID: LibraryItemID; itemType: LibraryItemType }) => {
return itemType === LibraryItemType.RSFORM
? rsformsApi.getRSFormQueryOptions({ itemID })
: ossApi.getOssQueryOptions({ itemID });
},
getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) =>
queryOptions({
queryKey: [...libraryApi.libraryListKey, isAdmin ? 'admin' : 'user'],
@ -178,7 +64,7 @@ export const libraryApi = {
successMessage: infoMsg.changesSaved
}
}),
setOwner: ({ itemID, owner }: { itemID: LibraryItemID; owner: number }) =>
setOwner: ({ itemID, owner }: { itemID: number; owner: number }) =>
axiosPatch({
endpoint: `/api/library/${itemID}/set-owner`,
request: {
@ -186,7 +72,7 @@ export const libraryApi = {
successMessage: infoMsg.changesSaved
}
}),
setLocation: ({ itemID, location }: { itemID: LibraryItemID; location: string }) =>
setLocation: ({ itemID, location }: { itemID: number; location: string }) =>
axiosPatch({
endpoint: `/api/library/${itemID}/set-location`,
request: {
@ -194,7 +80,7 @@ export const libraryApi = {
successMessage: infoMsg.moveComplete
}
}),
setAccessPolicy: ({ itemID, policy }: { itemID: LibraryItemID; policy: AccessPolicy }) =>
setAccessPolicy: ({ itemID, policy }: { itemID: number; policy: AccessPolicy }) =>
axiosPatch({
endpoint: `/api/library/${itemID}/set-access-policy`,
request: {
@ -202,7 +88,7 @@ export const libraryApi = {
successMessage: infoMsg.changesSaved
}
}),
setEditors: ({ itemID, editors }: { itemID: LibraryItemID; editors: number[] }) =>
setEditors: ({ itemID, editors }: { itemID: number; editors: number[] }) =>
axiosPatch({
endpoint: `/api/library/${itemID}/set-editors`,
request: {
@ -211,7 +97,7 @@ export const libraryApi = {
}
}),
deleteItem: (target: LibraryItemID) =>
deleteItem: (target: number) =>
axiosDelete({
endpoint: `/api/library/${target}`,
request: {
@ -235,7 +121,7 @@ export const libraryApi = {
}
}),
versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionCreateDTO }) =>
versionCreate: ({ itemID, data }: { itemID: number; data: IVersionCreateDTO }) =>
axiosPost<IVersionCreateDTO, IVersionCreatedResponse>({
endpoint: `/api/library/${itemID}/create-version`,
request: {
@ -243,7 +129,7 @@ export const libraryApi = {
successMessage: infoMsg.newVersion(data.version)
}
}),
versionRestore: ({ versionID }: { versionID: VersionID }) =>
versionRestore: ({ versionID }: { versionID: number }) =>
axiosPatch<undefined, IRSFormDTO>({
endpoint: `/api/versions/${versionID}/restore`,
request: {
@ -258,7 +144,7 @@ export const libraryApi = {
successMessage: infoMsg.changesSaved
}
}),
versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) =>
versionDelete: (data: { itemID: number; versionID: number }) =>
axiosDelete({
endpoint: `/api/versions/${data.versionID}`,
request: {

View File

@ -0,0 +1,122 @@
import { z } from 'zod';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { errorMsg } from '@/utils/labels';
import { AccessPolicy, LibraryItemType } from '../models/library';
import { validateLocation } from '../models/libraryAPI';
/**
* Represents update data for renaming Location.
*/
export interface IRenameLocationDTO {
target: string;
new_location: string;
}
/**
* Represents data, used for cloning {@link IRSForm}.
*/
export const schemaCloneLibraryItem = z.object({
id: z.number(),
item_type: z.nativeEnum(LibraryItemType),
title: z.string().nonempty(errorMsg.requiredField),
alias: z.string().nonempty(errorMsg.requiredField),
comment: z.string(),
visible: z.boolean(),
read_only: z.boolean(),
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
access_policy: z.nativeEnum(AccessPolicy),
items: z.array(z.number()).optional()
});
/**
* Represents data, used for cloning {@link IRSForm}.
*/
export type ICloneLibraryItemDTO = z.infer<typeof schemaCloneLibraryItem>;
/**
* Represents data, used for creating {@link IRSForm}.
*/
export const schemaCreateLibraryItem = z
.object({
item_type: z.nativeEnum(LibraryItemType),
title: z.string().optional(),
alias: z.string().optional(),
comment: z.string(),
visible: z.boolean(),
read_only: z.boolean(),
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
access_policy: z.nativeEnum(AccessPolicy),
file: z.instanceof(File).optional(),
fileName: z.string().optional()
})
.refine(data => !!data.file || !!data.title, {
path: ['title'],
message: errorMsg.requiredField
})
.refine(data => !!data.file || !!data.alias, {
path: ['alias'],
message: errorMsg.requiredField
});
/**
* Represents data, used for creating {@link IRSForm}.
*/
export type ICreateLibraryItemDTO = z.infer<typeof schemaCreateLibraryItem>;
/**
* Represents update data for editing {@link ILibraryItem}.
*/
export const schemaUpdateLibraryItem = z.object({
id: z.number(),
item_type: z.nativeEnum(LibraryItemType),
title: z.string().nonempty(errorMsg.requiredField),
alias: z.string().nonempty(errorMsg.requiredField),
comment: z.string(),
visible: z.boolean(),
read_only: z.boolean()
});
/**
* Represents update data for editing {@link ILibraryItem}.
*/
export type IUpdateLibraryItemDTO = z.infer<typeof schemaUpdateLibraryItem>;
/**
* Create version metadata in persistent storage.
*/
export const schemaVersionCreate = z.object({
version: z.string(),
description: z.string(),
items: z.array(z.number()).optional()
});
/**
* Create version metadata in persistent storage.
*/
export type IVersionCreateDTO = z.infer<typeof schemaVersionCreate>;
/**
* Represents data response when creating {@link IVersionInfo}.
*/
export interface IVersionCreatedResponse {
version: number;
schema: IRSFormDTO;
}
/**
* Represents version data, intended to update version metadata in persistent storage.
*/
export const schemaVersionUpdate = z.object({
id: z.number(),
version: z.string().nonempty(errorMsg.requiredField),
description: z.string()
});
/**
* Represents version data, intended to update version metadata in persistent storage.
*/
export type IVersionUpdateDTO = z.infer<typeof schemaVersionUpdate>;

View File

@ -1,6 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { ICloneLibraryItemDTO, libraryApi } from './api';
import { libraryApi } from './api';
import { ICloneLibraryItemDTO } from './types';
export const useCloneItem = () => {
const client = useQueryClient();

View File

@ -1,6 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { ICreateLibraryItemDTO, libraryApi } from './api';
import { libraryApi } from './api';
import { ICreateLibraryItemDTO } from './types';
export const useCreateItem = () => {
const client = useQueryClient();

View File

@ -1,10 +1,8 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { PARAMETER } from '@/utils/constants';
import { LibraryItemID } from '../models/library';
import { libraryApi } from './api';
export const useDeleteItem = () => {
@ -17,16 +15,16 @@ export const useDeleteItem = () => {
setTimeout(
() =>
void Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.resetQueries({ queryKey: rsformsApi.getRSFormQueryOptions({ itemID: variables }).queryKey }),
client.resetQueries({ queryKey: ossApi.getOssQueryOptions({ itemID: variables }).queryKey })
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.resetQueries({ queryKey: KEYS.composite.rsItem({ itemID: variables }) }),
client.resetQueries({ queryKey: KEYS.composite.ossItem({ itemID: variables }) })
]).catch(console.error),
PARAMETER.navigationDuration
);
}
});
return {
deleteItem: (target: LibraryItemID) => mutation.mutateAsync(target),
deleteItem: (target: number) => mutation.mutateAsync(target),
isPending: mutation.isPending
};
};

View File

@ -1,13 +1,10 @@
import { useIsMutating } from '@tanstack/react-query';
import { ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from '@/features/rsform/backend/api';
import { libraryApi } from './api';
import { KEYS } from '@/backend/configuration';
export const useMutatingLibrary = () => {
const countMutations = useIsMutating({ mutationKey: [libraryApi.baseKey] });
const countOss = useIsMutating({ mutationKey: [ossApi.baseKey] });
const countRSForm = useIsMutating({ mutationKey: [rsformsApi.baseKey] });
const countMutations = useIsMutating({ mutationKey: [KEYS.library] });
const countOss = useIsMutating({ mutationKey: [KEYS.oss] });
const countRSForm = useIsMutating({ mutationKey: [KEYS.rsform] });
return countMutations + countOss + countRSForm !== 0;
};

View File

@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IRenameLocationDTO, libraryApi } from './api';
import { libraryApi } from './api';
import { IRenameLocationDTO } from './types';
export const useRenameLocation = () => {
const client = useQueryClient();
@ -12,9 +12,9 @@ export const useRenameLocation = () => {
mutationFn: libraryApi.renameLocation,
onSuccess: () =>
Promise.allSettled([
client.invalidateQueries({ queryKey: [libraryApi.baseKey] }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] }),
client.invalidateQueries({ queryKey: [ossApi.baseKey] })
client.invalidateQueries({ queryKey: [KEYS.library] }),
client.invalidateQueries({ queryKey: [KEYS.rsform] }),
client.invalidateQueries({ queryKey: [KEYS.oss] })
])
});
return {

View File

@ -1,9 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { AccessPolicy, ILibraryItem, LibraryItemID } from '../models/library';
import { AccessPolicy, ILibraryItem } from '../models/library';
import { libraryApi } from './api';
export const useSetAccessPolicy = () => {
@ -12,26 +13,28 @@ export const useSetAccessPolicy = () => {
mutationKey: [libraryApi.baseKey, 'set-location'],
mutationFn: libraryApi.setAccessPolicy,
onSuccess: (_, variables) => {
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
if (ossData) {
client.setQueryData(ossKey, { ...ossData, access_policy: variables.policy });
return Promise.allSettled([
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
...ossData.items
.map(item => {
if (!item.result) {
return;
}
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
return client.invalidateQueries({ queryKey: itemKey });
})
.filter(item => !!item)
]);
}
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, access_policy: variables.policy }));
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
!prev ? undefined : { ...prev, access_policy: variables.policy }
);
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === variables.itemID ? { ...item, access_policy: variables.policy } : item))
);
@ -39,6 +42,6 @@ export const useSetAccessPolicy = () => {
});
return {
setAccessPolicy: (data: { itemID: LibraryItemID; policy: AccessPolicy }) => mutation.mutateAsync(data)
setAccessPolicy: (data: { itemID: number; policy: AccessPolicy }) => mutation.mutateAsync(data)
};
};

View File

@ -1,7 +1,8 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { libraryApi } from './api';
@ -11,8 +12,8 @@ export const useSetEditors = () => {
mutationKey: [libraryApi.baseKey, 'set-location'],
mutationFn: libraryApi.setEditors,
onSuccess: (_, variables) => {
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
const ossData = client.getQueryData(ossKey);
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
if (ossData) {
client.setQueryData(ossKey, { ...ossData, editors: variables.editors });
return Promise.allSettled(
@ -21,15 +22,17 @@ export const useSetEditors = () => {
if (!item.result) {
return;
}
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
return client.invalidateQueries({ queryKey: itemKey });
})
.filter(item => !!item)
);
}
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, editors: variables.editors }));
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
!prev ? undefined : { ...prev, editors: variables.editors }
);
}
});

View File

@ -1,9 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { ILibraryItem, LibraryItemID } from '../models/library';
import { ILibraryItem } from '../models/library';
import { libraryApi } from './api';
export const useSetLocation = () => {
@ -12,7 +13,7 @@ export const useSetLocation = () => {
mutationKey: [libraryApi.baseKey, 'set-location'],
mutationFn: libraryApi.setLocation,
onSuccess: (_, variables) => {
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
if (ossData) {
client.setQueryData(ossKey, { ...ossData, location: variables.location });
@ -23,15 +24,17 @@ export const useSetLocation = () => {
if (!item.result) {
return;
}
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
return client.invalidateQueries({ queryKey: itemKey });
})
.filter(item => !!item)
]);
}
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, location: variables.location }));
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
!prev ? undefined : { ...prev, location: variables.location }
);
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item))
);
@ -39,6 +42,6 @@ export const useSetLocation = () => {
});
return {
setLocation: (data: { itemID: LibraryItemID; location: string }) => mutation.mutateAsync(data)
setLocation: (data: { itemID: number; location: string }) => mutation.mutateAsync(data)
};
};

View File

@ -1,7 +1,8 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { ILibraryItem } from '../models/library';
import { libraryApi } from './api';
@ -12,7 +13,7 @@ export const useSetOwner = () => {
mutationKey: [libraryApi.baseKey, 'set-owner'],
mutationFn: libraryApi.setOwner,
onSuccess: (_, variables) => {
const ossKey = ossApi.getOssQueryOptions({ itemID: variables.itemID }).queryKey;
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
if (ossData) {
client.setQueryData(ossKey, { ...ossData, owner: variables.owner });
@ -23,15 +24,17 @@ export const useSetOwner = () => {
if (!item.result) {
return;
}
const itemKey = rsformsApi.getRSFormQueryOptions({ itemID: item.result }).queryKey;
const itemKey = KEYS.composite.rsItem({ itemID: item.result });
return client.invalidateQueries({ queryKey: itemKey });
})
.filter(item => !!item)
]);
}
const rsKey = rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey;
client.setQueryData(rsKey, prev => (!prev ? undefined : { ...prev, owner: variables.owner }));
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
!prev ? undefined : { ...prev, owner: variables.owner }
);
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === variables.itemID ? { ...item, owner: variables.owner } : item))
);

View File

@ -1,10 +1,12 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { IOperationSchemaDTO, ossApi } from '@/features/oss/backend/api';
import { IRSFormDTO } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IOperationSchemaDTO } from '@/features/oss/backend/types';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { ILibraryItem, LibraryItemType } from '../models/library';
import { IUpdateLibraryItemDTO, libraryApi } from './api';
import { libraryApi } from './api';
import { IUpdateLibraryItemDTO } from './types';
export const useUpdateItem = () => {
const client = useQueryClient();
@ -12,7 +14,10 @@ export const useUpdateItem = () => {
mutationKey: [libraryApi.baseKey, 'update-item'],
mutationFn: libraryApi.updateItem,
onSuccess: (data: ILibraryItem) => {
const itemKey = libraryApi.getItemQueryOptions({ itemID: data.id, itemType: data.item_type }).queryKey;
const itemKey =
data.item_type === LibraryItemType.RSFORM
? KEYS.composite.rsItem({ itemID: data.id })
: KEYS.composite.ossItem({ itemID: data.id });
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === data.id ? data : item))
);
@ -23,9 +28,7 @@ export const useUpdateItem = () => {
const schema: IRSFormDTO | undefined = client.getQueryData(itemKey);
if (schema) {
return Promise.allSettled(
schema.oss.map(item =>
client.invalidateQueries({ queryKey: ossApi.getOssQueryOptions({ itemID: item.id }).queryKey })
)
schema.oss.map(item => client.invalidateQueries({ queryKey: KEYS.composite.ossItem({ itemID: item.id }) }))
);
}
}

View File

@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { LibraryItemID } from '../models/library';
import { IVersionCreateDTO, libraryApi } from './api';
import { libraryApi } from './api';
import { IVersionCreateDTO } from './types';
import { useUpdateTimestamp } from './useUpdateTimestamp';
export const useVersionCreate = () => {
@ -13,12 +13,12 @@ export const useVersionCreate = () => {
mutationKey: [libraryApi.baseKey, 'create-version'],
mutationFn: libraryApi.versionCreate,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
client.setQueryData(KEYS.composite.rsItem({ itemID: data.schema.id }), data.schema);
updateTimestamp(data.schema.id);
}
});
return {
versionCreate: (data: { itemID: LibraryItemID; data: IVersionCreateDTO }) =>
versionCreate: (data: { itemID: number; data: IVersionCreateDTO }) =>
mutation.mutateAsync(data).then(response => response.version)
};
};

View File

@ -1,8 +1,8 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { IRSFormDTO, rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { LibraryItemID, VersionID } from '../models/library';
import { libraryApi } from './api';
export const useVersionDelete = () => {
@ -11,19 +11,17 @@ export const useVersionDelete = () => {
mutationKey: [libraryApi.baseKey, 'delete-version'],
mutationFn: libraryApi.versionDelete,
onSuccess: (_, variables) => {
client.setQueryData(
rsformsApi.getRSFormQueryOptions({ itemID: variables.itemID }).queryKey,
(prev: IRSFormDTO | undefined) =>
!prev
? undefined
: {
...prev,
versions: prev.versions.filter(version => version.id !== variables.versionID)
}
client.setQueryData(KEYS.composite.rsItem({ itemID: variables.itemID }), (prev: IRSFormDTO | undefined) =>
!prev
? undefined
: {
...prev,
versions: prev.versions.filter(version => version.id !== variables.versionID)
}
);
}
});
return {
versionDelete: (data: { itemID: LibraryItemID; versionID: VersionID }) => mutation.mutateAsync(data)
versionDelete: (data: { itemID: number; versionID: number }) => mutation.mutateAsync(data)
};
};

View File

@ -1,8 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { VersionID } from '../models/library';
import { libraryApi } from './api';
export const useVersionRestore = () => {
@ -11,11 +10,11 @@ export const useVersionRestore = () => {
mutationKey: [libraryApi.baseKey, 'restore-version'],
mutationFn: libraryApi.versionRestore,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
return client.invalidateQueries({ queryKey: [libraryApi.baseKey] });
}
});
return {
versionRestore: (data: { versionID: VersionID }) => mutation.mutateAsync(data)
versionRestore: (data: { versionID: number }) => mutation.mutateAsync(data)
};
};

View File

@ -1,8 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { IRSFormDTO, rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IRSFormDTO } from '@/features/rsform/backend/types';
import { IVersionUpdateDTO, libraryApi } from './api';
import { libraryApi } from './api';
import { IVersionUpdateDTO } from './types';
export const useVersionUpdate = () => {
const client = useQueryClient();
@ -10,19 +12,15 @@ export const useVersionUpdate = () => {
mutationKey: [libraryApi.baseKey, 'update-version'],
mutationFn: libraryApi.versionUpdate,
onSuccess: data => {
client.setQueryData(
rsformsApi.getRSFormQueryOptions({ itemID: data.item }).queryKey,
(prev: IRSFormDTO | undefined) =>
!prev
? undefined
: {
...prev,
versions: prev.versions.map(version =>
version.id === data.id
? { ...version, description: data.description, version: data.version }
: version
)
}
client.setQueryData(KEYS.composite.rsItem({ itemID: data.item }), (prev: IRSFormDTO | undefined) =>
!prev
? undefined
: {
...prev,
versions: prev.versions.map(version =>
version.id === data.id ? { ...version, description: data.description, version: data.version } : version
)
}
);
}
});

View File

@ -12,7 +12,7 @@ import { ModalForm } from '@/components/Modal';
import { useAuthSuspense } from '@/features/auth';
import { useDialogsStore } from '@/stores/dialogs';
import { ICloneLibraryItemDTO, schemaCloneLibraryItem } from '../backend/api';
import { ICloneLibraryItemDTO, schemaCloneLibraryItem } from '../backend/types';
import { useCloneItem } from '../backend/useCloneItem';
import { SelectAccessPolicy } from '../components/SelectAccessPolicy';
import { SelectLocationContext } from '../components/SelectLocationContext';

View File

@ -9,7 +9,7 @@ import { ModalForm } from '@/components/Modal';
import { useDialogsStore } from '@/stores/dialogs';
import { errorMsg } from '@/utils/labels';
import { IVersionCreateDTO, schemaVersionCreate } from '../backend/api';
import { IVersionCreateDTO, schemaVersionCreate } from '../backend/types';
import { useVersionCreate } from '../backend/useVersionCreate';
import { IVersionInfo } from '../models/library';
import { nextVersion } from '../models/libraryAPI';

View File

@ -9,11 +9,11 @@ import { MiniButton } from '@/components/Control';
import { IconReset, IconSave } from '@/components/Icons';
import { TextArea, TextInput } from '@/components/Input';
import { ModalView } from '@/components/Modal';
import { useRSFormSuspense } from '@/features/rsform/backend/useRSForm';
import { useRSFormSuspense } from '@/features/rsform';
import { useDialogsStore } from '@/stores/dialogs';
import { errorMsg } from '@/utils/labels';
import { IVersionUpdateDTO, schemaVersionUpdate } from '../../backend/api';
import { IVersionUpdateDTO, schemaVersionUpdate } from '../../backend/types';
import { useMutatingLibrary } from '../../backend/useMutatingLibrary';
import { useVersionDelete } from '../../backend/useVersionDelete';
import { useVersionUpdate } from '../../backend/useVersionUpdate';

View File

@ -1,4 +1,3 @@
export { libraryApi } from './backend/api';
export { useDeleteItem } from './backend/useDeleteItem';
export { useLibrary, useLibrarySuspense } from './backend/useLibrary';
export { useMutatingLibrary } from './backend/useMutatingLibrary';

View File

@ -31,22 +31,12 @@ export enum LocationHead {
export const BASIC_SCHEMAS = '/L/Базовые';
/**
* Represents {@link LibraryItem} identifier type.
*/
export type LibraryItemID = number;
/**
* Represents {@link Version} identifier type.
*/
export type VersionID = number;
/**
* Represents library item version information.
*/
export interface IVersionInfo {
id: VersionID;
item: LibraryItemID;
id: number;
item: number;
version: string;
description: string;
time_create: string;
@ -56,7 +46,7 @@ export interface IVersionInfo {
* Represents library item common data typical for all item types.
*/
export interface ILibraryItem {
id: LibraryItemID;
id: number;
item_type: LibraryItemType;
title: string;
alias: string;
@ -86,7 +76,7 @@ export interface ILibraryItemReference extends Pick<ILibraryItem, 'id' | 'alias'
* Represents {@link ILibraryItem} extended data with versions.
*/
export interface ILibraryItemVersioned extends ILibraryItemData {
version?: VersionID;
version?: number;
versions: IVersionInfo[];
}

View File

@ -15,7 +15,7 @@ import { Label, TextArea, TextInput } from '@/components/Input';
import { useAuthSuspense } from '@/features/auth';
import { EXTEOR_TRS_FILE } from '@/utils/constants';
import { ICreateLibraryItemDTO, schemaCreateLibraryItem } from '../../backend/api';
import { ICreateLibraryItemDTO, schemaCreateLibraryItem } from '../../backend/types';
import { useCreateItem } from '../../backend/useCreateItem';
import { SelectAccessPolicy } from '../../components/SelectAccessPolicy';
import { SelectItemType } from '../../components/SelectItemType';

View File

@ -3,16 +3,10 @@
*/
import { ILibraryItem } from '@/features/library/models/library';
import {
IOperation,
IOperationSchema,
IOperationSchemaStats,
OperationID,
OperationType
} from '@/features/oss/models/oss';
import { Graph } from '@/models/Graph';
import { IOperationSchemaDTO } from './api';
import { IOperation, IOperationSchema, IOperationSchemaStats, OperationType } from '../models/oss';
import { IOperationSchemaDTO } from './types';
/**
* Loads data into an {@link IOperationSchema} based on {@link IOperationSchemaDTO}.
@ -21,7 +15,7 @@ import { IOperationSchemaDTO } from './api';
export class OssLoader {
private oss: IOperationSchemaDTO;
private graph: Graph = new Graph();
private operationByID = new Map<OperationID, IOperation>();
private operationByID = new Map<number, IOperation>();
private schemaIDs: number[] = [];
private items: ILibraryItem[];
@ -72,7 +66,7 @@ export class OssLoader {
});
}
private inferConsolidation(operationID: OperationID): boolean {
private inferConsolidation(operationID: number): boolean {
const inputs = this.graph.expandInputs([operationID]);
if (inputs.length === 0) {
return false;

View File

@ -1,157 +1,29 @@
import { queryOptions } from '@tanstack/react-query';
import { z } from 'zod';
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
import { DELAYS } from '@/backend/configuration';
import { ILibraryItem, ILibraryItemData } from '@/features/library/models/library';
import { IArgument, ICstSubstituteEx, IOperation, OperationID, OperationType } from '@/features/oss/models/oss';
import { schemaCstSubstitute } from '@/features/rsform/backend/api';
import { DELAYS, KEYS } from '@/backend/configuration';
import { IConstituentaReference, ITargetCst } from '@/features/rsform/models/rsform';
import { infoMsg } from '@/utils/labels';
/**
* Represents {@link IOperation} data from server.
*/
export interface IOperationDTO extends Omit<IOperation, 'substitutions' | 'arguments'> {}
/**
* Represents backend data for {@link IOperationSchema}.
*/
export interface IOperationSchemaDTO extends ILibraryItemData {
items: IOperationDTO[];
arguments: IArgument[];
substitutions: ICstSubstituteEx[];
}
/**
* Represents {@link IOperation} position.
*/
export const schemaOperationPosition = z.object({
id: z.number(),
position_x: z.number(),
position_y: z.number()
});
/**
* Represents {@link IOperation} position.
*/
export type IOperationPosition = z.infer<typeof schemaOperationPosition>;
/**
* Represents {@link IOperation} data, used in creation process.
*/
export const schemaOperationCreate = z.object({
positions: z.array(schemaOperationPosition),
item_data: z.object({
alias: z.string().nonempty(),
operation_type: z.nativeEnum(OperationType),
title: z.string(),
comment: z.string(),
position_x: z.number(),
position_y: z.number(),
result: z.number().nullable()
}),
arguments: z.array(z.number()),
create_schema: z.boolean()
});
/**
* Represents {@link IOperation} data, used in creation process.
*/
export type IOperationCreateDTO = z.infer<typeof schemaOperationCreate>;
/**
* Represents data response when creating {@link IOperation}.
*/
export interface IOperationCreatedResponse {
new_operation: IOperationDTO;
oss: IOperationSchemaDTO;
}
/**
* Represents target {@link IOperation}.
*/
export interface ITargetOperation {
positions: IOperationPosition[];
target: OperationID;
}
/**
* Represents {@link IOperation} data, used in destruction process.
*/
export const schemaOperationDelete = z.object({
target: z.number(),
positions: z.array(schemaOperationPosition),
keep_constituents: z.boolean(),
delete_schema: z.boolean()
});
/**
* Represents {@link IOperation} data, used in destruction process.
*/
export type IOperationDeleteDTO = z.infer<typeof schemaOperationDelete>;
/**
* Represents data response when creating {@link IRSForm} for Input {@link IOperation}.
*/
export interface IInputCreatedResponse {
new_schema: ILibraryItem;
oss: IOperationSchemaDTO;
}
/**
* Represents {@link IOperation} data, used in setInput process.
*/
export const schemaInputUpdate = z.object({
target: z.number(),
positions: z.array(schemaOperationPosition),
input: z.number().nullable()
});
/**
* Represents {@link IOperation} data, used in setInput process.
*/
export type IInputUpdateDTO = z.infer<typeof schemaInputUpdate>;
/**
* Represents {@link IOperation} data, used in update process.
*/
export const schemaOperationUpdate = z.object({
target: z.number(),
positions: z.array(schemaOperationPosition),
item_data: z.object({
alias: z.string().nonempty(),
title: z.string(),
comment: z.string()
}),
arguments: z.array(z.number()),
substitutions: z.array(schemaCstSubstitute)
});
/**
* Represents {@link IOperation} data, used in update process.
*/
export type IOperationUpdateDTO = z.infer<typeof schemaOperationUpdate>;
/**
* Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s.
*/
export const schemaCstRelocate = z.object({
destination: z.number(),
items: z.array(z.number()).refine(data => data.length > 0)
});
/**
* Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s.
*/
export type ICstRelocateDTO = z.infer<typeof schemaCstRelocate>;
import {
ICstRelocateDTO,
IInputCreatedResponse,
IInputUpdateDTO,
IOperationCreatedResponse,
IOperationCreateDTO,
IOperationDeleteDTO,
IOperationPosition,
IOperationSchemaDTO,
IOperationUpdateDTO,
ITargetOperation
} from './types';
export const ossApi = {
baseKey: 'oss',
baseKey: KEYS.oss,
getOssQueryOptions: ({ itemID }: { itemID?: number }) => {
return queryOptions({
queryKey: [ossApi.baseKey, 'item', itemID],
queryKey: KEYS.composite.ossItem({ itemID }),
staleTime: DELAYS.staleShort,
queryFn: meta =>
!itemID

View File

@ -0,0 +1,143 @@
import { z } from 'zod';
import { ILibraryItem, ILibraryItemData } from '@/features/library/models/library';
import { schemaCstSubstitute } from '@/features/rsform/backend/types';
import { IArgument, ICstSubstituteEx, IOperation, OperationType } from '../models/oss';
/**
* Represents {@link IOperation} data from server.
*/
export interface IOperationDTO extends Omit<IOperation, 'substitutions' | 'arguments'> {}
/**
* Represents backend data for {@link IOperationSchema}.
*/
export interface IOperationSchemaDTO extends ILibraryItemData {
items: IOperationDTO[];
arguments: IArgument[];
substitutions: ICstSubstituteEx[];
}
/**
* Represents {@link IOperation} position.
*/
export const schemaOperationPosition = z.object({
id: z.number(),
position_x: z.number(),
position_y: z.number()
});
/**
* Represents {@link IOperation} position.
*/
export type IOperationPosition = z.infer<typeof schemaOperationPosition>;
/**
* Represents {@link IOperation} data, used in creation process.
*/
export const schemaOperationCreate = z.object({
positions: z.array(schemaOperationPosition),
item_data: z.object({
alias: z.string().nonempty(),
operation_type: z.nativeEnum(OperationType),
title: z.string(),
comment: z.string(),
position_x: z.number(),
position_y: z.number(),
result: z.number().nullable()
}),
arguments: z.array(z.number()),
create_schema: z.boolean()
});
/**
* Represents {@link IOperation} data, used in creation process.
*/
export type IOperationCreateDTO = z.infer<typeof schemaOperationCreate>;
/**
* Represents data response when creating {@link IOperation}.
*/
export interface IOperationCreatedResponse {
new_operation: IOperationDTO;
oss: IOperationSchemaDTO;
}
/**
* Represents target {@link IOperation}.
*/
export interface ITargetOperation {
positions: IOperationPosition[];
target: number;
}
/**
* Represents {@link IOperation} data, used in destruction process.
*/
export const schemaOperationDelete = z.object({
target: z.number(),
positions: z.array(schemaOperationPosition),
keep_constituents: z.boolean(),
delete_schema: z.boolean()
});
/**
* Represents {@link IOperation} data, used in destruction process.
*/
export type IOperationDeleteDTO = z.infer<typeof schemaOperationDelete>;
/**
* Represents data response when creating {@link IRSForm} for Input {@link IOperation}.
*/
export interface IInputCreatedResponse {
new_schema: ILibraryItem;
oss: IOperationSchemaDTO;
}
/**
* Represents {@link IOperation} data, used in setInput process.
*/
export const schemaInputUpdate = z.object({
target: z.number(),
positions: z.array(schemaOperationPosition),
input: z.number().nullable()
});
/**
* Represents {@link IOperation} data, used in setInput process.
*/
export type IInputUpdateDTO = z.infer<typeof schemaInputUpdate>;
/**
* Represents {@link IOperation} data, used in update process.
*/
export const schemaOperationUpdate = z.object({
target: z.number(),
positions: z.array(schemaOperationPosition),
item_data: z.object({
alias: z.string().nonempty(),
title: z.string(),
comment: z.string()
}),
arguments: z.array(z.number()),
substitutions: z.array(schemaCstSubstitute)
});
/**
* Represents {@link IOperation} data, used in update process.
*/
export type IOperationUpdateDTO = z.infer<typeof schemaOperationUpdate>;
/**
* Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s.
*/
export const schemaCstRelocate = z.object({
destination: z.number(),
items: z.array(z.number()).refine(data => data.length > 0)
});
/**
* Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s.
*/
export type ICstRelocateDTO = z.infer<typeof schemaCstRelocate>;

View File

@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { ITargetOperation, ossApi } from './api';
import { ossApi } from './api';
import { ITargetOperation } from './types';
export const useInputCreate = () => {
const client = useQueryClient();
@ -13,8 +13,8 @@ export const useInputCreate = () => {
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
return Promise.allSettled([
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
});

View File

@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IInputUpdateDTO, ossApi } from './api';
import { ossApi } from './api';
import { IInputUpdateDTO } from './types';
export const useInputUpdate = () => {
const client = useQueryClient();
@ -13,8 +13,8 @@ export const useInputUpdate = () => {
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
return Promise.allSettled([
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
});

View File

@ -1,11 +1,9 @@
import { useIsMutating } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { ossApi } from './api';
import { KEYS } from '@/backend/configuration';
export const useMutatingOss = () => {
const countLibrary = useIsMutating({ mutationKey: [libraryApi.baseKey] });
const countOss = useIsMutating({ mutationKey: [ossApi.baseKey] });
const countLibrary = useIsMutating({ mutationKey: [KEYS.library] });
const countOss = useIsMutating({ mutationKey: [KEYS.oss] });
return countLibrary + countOss !== 0;
};

View File

@ -1,8 +1,8 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { queryClient } from '@/backend/queryClient';
import { useLibrary, useLibrarySuspense } from '@/features/library/backend/useLibrary';
import { queryClient } from '../../../backend/queryClient';
import { ossApi } from './api';
import { OssLoader } from './OssLoader';

View File

@ -2,7 +2,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library';
import { IOperationCreateDTO, ossApi } from './api';
import { ossApi } from './api';
import { IOperationCreateDTO } from './types';
export const useOperationCreate = () => {
const client = useQueryClient();

View File

@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { IOperationDeleteDTO, ossApi } from './api';
import { ossApi } from './api';
import { IOperationDeleteDTO } from './types';
export const useOperationDelete = () => {
const client = useQueryClient();
@ -13,8 +13,8 @@ export const useOperationDelete = () => {
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
return Promise.allSettled([
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
});

View File

@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { ITargetOperation, ossApi } from './api';
import { ossApi } from './api';
import { ITargetOperation } from './types';
export const useOperationExecute = () => {
const client = useQueryClient();
@ -13,8 +13,8 @@ export const useOperationExecute = () => {
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
return Promise.allSettled([
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
});

View File

@ -1,10 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { KEYS } from '@/backend/configuration';
import { ILibraryItem } from '@/features/library/models/library';
import { rsformsApi } from '@/features/rsform/backend/api';
import { IOperationUpdateDTO, ossApi } from './api';
import { ossApi } from './api';
import { IOperationUpdateDTO } from './types';
export const useOperationUpdate = () => {
const client = useQueryClient();
@ -12,12 +12,12 @@ export const useOperationUpdate = () => {
mutationKey: [ossApi.baseKey, 'operation-update'],
mutationFn: ossApi.operationUpdate,
onSuccess: (data, variables) => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
client.setQueryData(KEYS.composite.ossItem({ itemID: data.id }), data);
const schemaID = data.items.find(item => item.id === variables.data.target)?.result;
if (!schemaID) {
return;
}
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
client.setQueryData(KEYS.composite.libraryList, (prev: ILibraryItem[] | undefined) =>
!prev
? undefined
: prev.map(item =>
@ -25,7 +25,7 @@ export const useOperationUpdate = () => {
)
);
return client.invalidateQueries({
queryKey: rsformsApi.getRSFormQueryOptions({ itemID: schemaID }).queryKey
queryKey: KEYS.composite.rsItem({ itemID: schemaID })
});
}
});

View File

@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { rsformsApi } from '@/features/rsform/backend/api';
import { KEYS } from '@/backend/configuration';
import { ICstRelocateDTO, ossApi } from './api';
import { ossApi } from './api';
import { ICstRelocateDTO } from './types';
export const useRelocateConstituents = () => {
const client = useQueryClient();
@ -13,8 +13,8 @@ export const useRelocateConstituents = () => {
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
return Promise.allSettled([
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
});

View File

@ -2,7 +2,8 @@ import { useMutation } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library';
import { IOperationPosition, ossApi } from './api';
import { ossApi } from './api';
import { IOperationPosition } from './types';
export const useUpdatePositions = () => {
const { updateTimestamp } = useUpdateTimestamp();

View File

@ -11,7 +11,7 @@ import { ModalForm } from '@/components/Modal';
import { ILibraryItem, LibraryItemType, PickSchema, useLibrary } from '@/features/library';
import { useDialogsStore } from '@/stores/dialogs';
import { IInputUpdateDTO, IOperationPosition, schemaInputUpdate } from '../backend/api';
import { IInputUpdateDTO, IOperationPosition, schemaInputUpdate } from '../backend/types';
import { useInputUpdate } from '../backend/useInputUpdate';
import { IOperation, IOperationSchema } from '../models/oss';
import { sortItemsForOSS } from '../models/ossAPI';

View File

@ -10,7 +10,7 @@ import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
import { HelpTopic } from '@/features/help';
import { useDialogsStore } from '@/stores/dialogs';
import { IOperationCreateDTO, IOperationPosition, schemaOperationCreate } from '../../backend/api';
import { IOperationCreateDTO, IOperationPosition, schemaOperationCreate } from '../../backend/types';
import { useOperationCreate } from '../../backend/useOperationCreate';
import { describeOperationType, labelOperationType } from '../../labels';
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';

View File

@ -8,7 +8,7 @@ import { Checkbox, Label, TextArea, TextInput } from '@/components/Input';
import { ILibraryItem, LibraryItemType, PickSchema, useLibrary } from '@/features/library';
import { useDialogsStore } from '@/stores/dialogs';
import { IOperationCreateDTO } from '../../backend/api';
import { IOperationCreateDTO } from '../../backend/types';
import { sortItemsForOSS } from '../../models/ossAPI';
import { DlgCreateOperationProps } from './DlgCreateOperation';

View File

@ -4,7 +4,7 @@ import { FlexColumn } from '@/components/Container';
import { Label, TextArea, TextInput } from '@/components/Input';
import { useDialogsStore } from '@/stores/dialogs';
import { IOperationCreateDTO } from '../../backend/api';
import { IOperationCreateDTO } from '../../backend/types';
import { PickMultiOperation } from '../../components/PickMultiOperation';
import { DlgCreateOperationProps } from './DlgCreateOperation';

View File

@ -7,11 +7,11 @@ import { Controller, useForm } from 'react-hook-form';
import { Checkbox, TextInput } from '@/components/Input';
import { ModalForm } from '@/components/Modal';
import { HelpTopic } from '@/features/help';
import { IOperation, IOperationSchema } from '@/features/oss/models/oss';
import { useDialogsStore } from '@/stores/dialogs';
import { IOperationDeleteDTO, IOperationPosition, schemaOperationDelete } from '../backend/api';
import { IOperationDeleteDTO, IOperationPosition, schemaOperationDelete } from '../backend/types';
import { useOperationDelete } from '../backend/useOperationDelete';
import { IOperation, IOperationSchema } from '../models/oss';
export interface DlgDeleteOperationProps {
oss: IOperationSchema;

View File

@ -11,7 +11,7 @@ import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
import { HelpTopic } from '@/features/help';
import { useDialogsStore } from '@/stores/dialogs';
import { IOperationPosition, IOperationUpdateDTO, schemaOperationUpdate } from '../../backend/api';
import { IOperationPosition, IOperationUpdateDTO, schemaOperationUpdate } from '../../backend/types';
import { useOperationUpdate } from '../../backend/useOperationUpdate';
import { IOperation, IOperationSchema, OperationType } from '../../models/oss';
import TabArguments from './TabArguments';

View File

@ -5,7 +5,7 @@ import { FlexColumn } from '@/components/Container';
import { Label } from '@/components/Input';
import { useDialogsStore } from '@/stores/dialogs';
import { IOperationUpdateDTO } from '../../backend/api';
import { IOperationUpdateDTO } from '../../backend/types';
import { PickMultiOperation } from '../../components/PickMultiOperation';
import { DlgEditOperationProps } from './DlgEditOperation';

View File

@ -2,7 +2,7 @@ import { useFormContext } from 'react-hook-form';
import { TextArea, TextInput } from '@/components/Input';
import { IOperationUpdateDTO } from '../../backend/api';
import { IOperationUpdateDTO } from '../../backend/types';
function TabOperation() {
const {

View File

@ -3,12 +3,11 @@
import { Controller, useFormContext, useWatch } from 'react-hook-form';
import { TextArea } from '@/components/Input';
import { useRSForms } from '@/features/rsform/backend/useRSForms';
import PickSubstitutions from '@/features/rsform/components/PickSubstitutions';
import { PickSubstitutions, useRSForms } from '@/features/rsform';
import { useDialogsStore } from '@/stores/dialogs';
import { APP_COLORS } from '@/styling/colors';
import { IOperationUpdateDTO } from '../../backend/api';
import { IOperationUpdateDTO } from '../../backend/types';
import { SubstitutionValidator } from '../../models/ossAPI';
import { DlgEditOperationProps } from './DlgEditOperation';

View File

@ -11,11 +11,10 @@ import { Loader } from '@/components/Loader';
import { ModalForm } from '@/components/Modal';
import { HelpTopic } from '@/features/help';
import { ILibraryItem, SelectLibraryItem, useLibrary } from '@/features/library';
import { useRSForm } from '@/features/rsform/backend/useRSForm';
import PickMultiConstituenta from '@/features/rsform/components/PickMultiConstituenta';
import { PickMultiConstituenta, useRSForm } from '@/features/rsform';
import { useDialogsStore } from '@/stores/dialogs';
import { ICstRelocateDTO, IOperationPosition, schemaCstRelocate } from '../backend/api';
import { ICstRelocateDTO, IOperationPosition, schemaCstRelocate } from '../backend/types';
import { useRelocateConstituents } from '../backend/useRelocateConstituents';
import { useUpdatePositions } from '../backend/useUpdatePositions';
import { IOperation, IOperationSchema } from '../models/oss';

View File

@ -0,0 +1 @@
export { useFindPredecessor } from './backend/useFindPredecessor';

View File

@ -1,11 +1,9 @@
/**
* Module: Schema of Synthesis Operations.
*/
import { ILibraryItem, ILibraryItemData } from '@/features/library/models/library';
import { ICstSubstitute } from '@/features/rsform/backend/api';
import { IConstituenta } from '@/features/rsform/models/rsform';
import { Graph } from '../../../models/Graph';
import { ILibraryItemData } from '@/features/library/models/library';
import { ICstSubstitute } from '@/features/rsform';
import { Graph } from '@/models/Graph';
/**
* Represents {@link IOperation} identifier type.
@ -51,17 +49,6 @@ export interface IArgument {
argument: OperationID;
}
/**
* Represents substitution for multi synthesis table.
*/
export interface IMultiSubstitution {
original_source: ILibraryItem;
original: IConstituenta;
substitution: IConstituenta;
substitution_source: ILibraryItem;
is_suggestion: boolean;
}
/**
* Represents {@link ICstSubstitute} extended data.
*/

View File

@ -3,8 +3,8 @@
*/
import { ILibraryItem } from '@/features/library/models/library';
import { ICstSubstitute } from '@/features/rsform/backend/api';
import { ConstituentaID, CstClass, CstType, IConstituenta, IRSForm } from '@/features/rsform/models/rsform';
import { CstClass, CstType, IConstituenta, IRSForm } from '@/features/rsform';
import { ICstSubstitute } from '@/features/rsform/backend/types';
import { AliasMapping, ParsingStatus } from '@/features/rsform/models/rslang';
import {
applyAliasMapping,
@ -17,7 +17,7 @@ import { infoMsg } from '@/utils/labels';
import { TextMatcher } from '@/utils/utils';
import { Graph } from '../../../models/Graph';
import { IOperationPosition } from '../backend/api';
import { IOperationPosition } from '../backend/types';
import { describeSubstitutionError } from '../labels';
import { IOperation, IOperationSchema, OperationID, OperationType, SubstitutionErrorType } from './oss';
import { Position2D } from './ossLayout';
@ -67,13 +67,13 @@ export class SubstitutionValidator {
private schemas: IRSForm[];
private substitutions: ICstSubstitute[];
private constituents = new Set<ConstituentaID>();
private originals = new Set<ConstituentaID>();
private constituents = new Set<number>();
private originals = new Set<number>();
private mapping: CrossMapping = new Map();
private cstByID = new Map<ConstituentaID, IConstituenta>();
private cstByID = new Map<number, IConstituenta>();
private schemaByID = new Map<number, IRSForm>();
private schemaByCst = new Map<ConstituentaID, IRSForm>();
private schemaByCst = new Map<number, IRSForm>();
constructor(schemas: IRSForm[], substitutions: ICstSubstitute[]) {
this.schemas = schemas;
@ -125,9 +125,9 @@ export class SubstitutionValidator {
}
private calculateSuggestions(): void {
const candidates = new Map<ConstituentaID, string>();
const minors = new Set<ConstituentaID>();
const schemaByCst = new Map<ConstituentaID, IRSForm>();
const candidates = new Map<number, string>();
const minors = new Set<number>();
const schemaByCst = new Map<number, IRSForm>();
for (const schema of this.schemas) {
for (const cst of schema.items) {
if (this.originals.has(cst.id)) {
@ -456,7 +456,7 @@ export function getRelocateCandidates(
return addedCst;
}
const unreachableBases: ConstituentaID[] = [];
const unreachableBases: number[] = [];
for (const cst of schema.items.filter(item => item.is_inherited)) {
if (cst.parent_schema == destinationSchema) {
continue;

View File

@ -4,7 +4,7 @@ import clsx from 'clsx';
import { FlexColumn } from '@/components/Container';
import { EditorLibraryItem } from '@/features/library';
import ToolbarRSFormCard from '@/features/rsform/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard';
import { ToolbarRSFormCard } from '@/features/rsform';
import { useModificationStore } from '@/stores/modification';
import { globals } from '@/utils/constants';

View File

@ -10,7 +10,7 @@ import { SubmitButton } from '@/components/Control';
import { IconSave } from '@/components/Icons';
import { TextArea, TextInput } from '@/components/Input';
import { LibraryItemType, ToolbarItemAccess, useUpdateItem } from '@/features/library';
import { IUpdateLibraryItemDTO, schemaUpdateLibraryItem } from '@/features/library/backend/api';
import { IUpdateLibraryItemDTO, schemaUpdateLibraryItem } from '@/features/library/backend/types';
import { useModificationStore } from '@/stores/modification';
import { globals } from '@/utils/constants';

View File

@ -12,12 +12,12 @@ import {
IconNewRSForm,
IconRSForm
} from '@/components/Icons';
import { useMutatingOss } from '@/features/oss/backend/useMutatingOss';
import { IOperation, OperationID, OperationType } from '@/features/oss/models/oss';
import useClickedOutside from '@/hooks/useClickedOutside';
import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/utils';
import { useMutatingOss } from '../../../backend/useMutatingOss';
import { IOperation, OperationType } from '../../../models/oss';
import { useOssEdit } from '../OssEditContext';
export interface ContextMenuData {
@ -29,12 +29,12 @@ export interface ContextMenuData {
interface NodeContextMenuProps extends ContextMenuData {
isOpen: boolean;
onHide: () => void;
onDelete: (target: OperationID) => void;
onCreateInput: (target: OperationID) => void;
onEditSchema: (target: OperationID) => void;
onEditOperation: (target: OperationID) => void;
onExecuteOperation: (target: OperationID) => void;
onRelocateConstituents: (target: OperationID) => void;
onDelete: (target: number) => void;
onCreateInput: (target: number) => void;
onEditSchema: (target: number) => void;
onEditOperation: (target: number) => void;
onExecuteOperation: (target: number) => void;
onRelocateConstituents: (target: number) => void;
}
function NodeContextMenu({

View File

@ -19,11 +19,11 @@ import {
IconShare
} from '@/components/Icons';
import { useAuthSuspense } from '@/features/auth';
import { useMutatingOss } from '@/features/oss/backend/useMutatingOss';
import { useRoleStore, UserRole } from '@/features/users';
import { describeAccessMode as describeUserRole, labelAccessMode as labelUserRole } from '@/utils/labels';
import { sharePage } from '@/utils/utils';
import { useMutatingOss } from '../../backend/useMutatingOss';
import { useOssEdit } from './OssEditContext';
function MenuOssTabs() {

View File

@ -11,7 +11,7 @@ import { useDialogsStore } from '@/stores/dialogs';
import { usePreferencesStore } from '@/stores/preferences';
import { promptText } from '@/utils/labels';
import { IOperationPosition } from '../../backend/api';
import { IOperationPosition } from '../../backend/types';
import { useOssSuspense } from '../../backend/useOSS';
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';

View File

@ -17,7 +17,7 @@ import OssTabs from './OssTabs';
const paramsSchema = z.object({
id: z.coerce.number(),
tab: z.preprocess(v => (v ? Number(v) : undefined), z.nativeEnum(OssTabID).default(OssTabID.CARD))
tab: z.preprocess(v => (v ? Number(v) : undefined), z.nativeEnum(OssTabID).default(OssTabID.GRAPH))
});
export function OssPage() {

View File

@ -4,11 +4,11 @@
import { Graph } from '@/models/Graph';
import { ConstituentaID, CstType, IConstituenta, IRSForm, IRSFormStats } from '../models/rsform';
import { CstType, IConstituenta, IRSForm, IRSFormStats } from '../models/rsform';
import { inferClass, inferStatus, inferTemplate, isBaseSet, isFunctional } from '../models/rsformAPI';
import { ParsingStatus, ValueClass } from '../models/rslang';
import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from '../models/rslangAPI';
import { IRSFormDTO } from './api';
import { IRSFormDTO } from './types';
/**
* Loads data into an {@link IRSForm} based on {@link IRSFormDTO}.
@ -21,7 +21,7 @@ export class RSFormLoader {
private schema: IRSFormDTO;
private graph: Graph = new Graph();
private cstByAlias = new Map<string, IConstituenta>();
private cstByID = new Map<ConstituentaID, IConstituenta>();
private cstByID = new Map<number, IConstituenta>();
constructor(input: IRSFormDTO) {
this.schema = input;
@ -61,7 +61,7 @@ export class RSFormLoader {
}
private inferCstAttributes() {
const schemaByCst = new Map<ConstituentaID, number>();
const schemaByCst = new Map<number, number>();
const parents: number[] = [];
this.schema.inheritance.forEach(item => {
if (item.child_source === this.schema.id) {
@ -120,7 +120,7 @@ export class RSFormLoader {
return isSimpleExpression(expression);
}
private inferParent(target: IConstituenta): ConstituentaID | undefined {
private inferParent(target: IConstituenta): number | undefined {
const sources = this.extractSources(target);
if (sources.size !== 1 || sources.has(target.id)) {
return undefined;
@ -133,8 +133,8 @@ export class RSFormLoader {
return parent_id;
}
private extractSources(target: IConstituenta): Set<ConstituentaID> {
const sources = new Set<ConstituentaID>();
private extractSources(target: IConstituenta): Set<number> {
const sources = new Set<number>();
if (!isFunctional(target.cst_type)) {
const node = this.graph.at(target.id)!;
node.inputs.forEach(id => {

View File

@ -1,181 +1,31 @@
import { queryOptions } from '@tanstack/react-query';
import { z } from 'zod';
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
import { DELAYS } from '@/backend/configuration';
import { ILibraryItemReference, ILibraryItemVersioned } from '@/features/library/models/library';
import { errorMsg, infoMsg } from '@/utils/labels';
import { DELAYS, KEYS } from '@/backend/configuration';
import { infoMsg } from '@/utils/labels';
import { IConstituentaList, IConstituentaMeta, ITargetCst } from '../models/rsform';
import { IExpressionParse } from '../models/rslang';
import {
ConstituentaID,
CstType,
IConstituentaList,
IConstituentaMeta,
IInheritanceInfo,
ITargetCst,
TermForm
} from '../models/rsform';
import { IArgumentInfo, IExpressionParse, ParsingStatus, ValueClass } from '../models/rslang';
/**
* Represents {@link IConstituenta} data from server.
*/
export interface IConstituentaDTO extends IConstituentaMeta {
parse: {
status: ParsingStatus;
valueClass: ValueClass;
typification: string;
syntaxTree: string;
args: IArgumentInfo[];
};
}
/**
* Represents data for {@link IRSForm} provided by backend.
*/
export interface IRSFormDTO extends ILibraryItemVersioned {
items: IConstituentaDTO[];
inheritance: IInheritanceInfo[];
oss: ILibraryItemReference[];
}
/**
* Represents data, used for uploading {@link IRSForm} as file.
*/
export interface IRSFormUploadDTO {
itemID: number;
load_metadata: boolean;
file: File;
fileName: string;
}
/**
* Represents {@link IConstituenta} data, used in creation process.
*/
export interface ICstCreateDTO {
alias: string;
cst_type: CstType;
definition_raw: string;
term_raw: string;
convention: string;
definition_formal: string;
term_forms: TermForm[];
insert_after: ConstituentaID | null;
}
/**
* Represents data response when creating {@link IConstituenta}.
*/
export interface ICstCreatedResponse {
new_cst: IConstituentaMeta;
schema: IRSFormDTO;
}
/**
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
*/
export const schemaCstUpdate = z.object({
target: z.number(),
item_data: z.object({
convention: z.string().optional(),
definition_formal: z.string().optional(),
definition_raw: z.string().optional(),
term_raw: z.string().optional(),
term_forms: z.array(z.object({ text: z.string(), tags: z.string() })).optional()
})
});
/**
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
*/
export type ICstUpdateDTO = z.infer<typeof schemaCstUpdate>;
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export const schemaCstRename = z.object({
target: z.number(),
alias: z.string(),
cst_type: z.nativeEnum(CstType)
});
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export type ICstRenameDTO = z.infer<typeof schemaCstRename>;
/**
* Represents data, used in ordering a list of {@link IConstituenta}.
*/
export interface ICstMoveDTO {
items: ConstituentaID[];
move_to: number; // Note: 0-base index
}
/**
* Represents data response when creating producing structure of {@link IConstituenta}.
*/
export interface IProduceStructureResponse {
cst_list: ConstituentaID[];
schema: IRSFormDTO;
}
/**
* Represents data, used in merging single {@link IConstituenta}.
*/
export const schemaCstSubstitute = z.object({
original: z.number(),
substitution: z.number()
});
/**
* Represents data, used in merging single {@link IConstituenta}.
*/
export type ICstSubstitute = z.infer<typeof schemaCstSubstitute>;
/**
* Represents input data for inline synthesis.
*/
export const schemaInlineSynthesis = z.object({
receiver: z.number(),
source: z.number().nullable(),
items: z.array(z.number()),
substitutions: z.array(schemaCstSubstitute)
});
/**
* Represents input data for inline synthesis.
*/
export type IInlineSynthesisDTO = z.infer<typeof schemaInlineSynthesis>;
/**
* Represents {@link IConstituenta} data, used for checking expression.
*/
export interface ICheckConstituentaDTO {
alias: string;
cst_type: CstType;
definition_formal: string;
}
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export const schemaCstSubstitutions = z.object({
substitutions: z.array(schemaCstSubstitute).min(1, { message: errorMsg.emptySubstitutions })
});
/**
* Represents data, used in merging multiple {@link IConstituenta}.
*/
export type ICstSubstitutionsDTO = z.infer<typeof schemaCstSubstitutions>;
ICheckConstituentaDTO,
ICstCreatedResponse,
ICstCreateDTO,
ICstMoveDTO,
ICstRenameDTO,
ICstSubstitutionsDTO,
ICstUpdateDTO,
IInlineSynthesisDTO,
IProduceStructureResponse,
IRSFormDTO,
IRSFormUploadDTO
} from './types';
export const rsformsApi = {
baseKey: 'rsform',
baseKey: KEYS.rsform,
getRSFormQueryOptions: ({ itemID, version }: { itemID?: number; version?: number }) => {
return queryOptions({
queryKey: [rsformsApi.baseKey, 'item', itemID, version ?? ''],
queryKey: KEYS.composite.rsItem({ itemID, version }),
staleTime: DELAYS.staleShort,
queryFn: meta =>
!itemID

View File

@ -1,4 +1,5 @@
import { axiosPost } from '@/backend/apiTransport';
import { KEYS } from '@/backend/configuration';
/**
* Represents API result for text output.
@ -23,7 +24,7 @@ export interface ILexemeResponse {
}
export const cctextApi = {
baseKey: 'cctext',
baseKey: KEYS.cctext,
inflectText: (data: IWordFormDTO) =>
axiosPost<IWordFormDTO, ITextResult>({

View File

@ -1,8 +1,8 @@
import { useIsMutating } from '@tanstack/react-query';
import { cctextApi } from './api';
import { KEYS } from '@/backend/configuration';
export const useIsProcessingCctext = () => {
const countMutations = useIsMutating({ mutationKey: [cctextApi.baseKey] });
const countMutations = useIsMutating({ mutationKey: [KEYS.cctext] });
return countMutations !== 0;
};

View File

@ -0,0 +1,160 @@
import { z } from 'zod';
import { ILibraryItemReference, ILibraryItemVersioned } from '@/features/library/models/library';
import { errorMsg } from '@/utils/labels';
import { CstType, IConstituentaMeta, IInheritanceInfo, TermForm } from '../models/rsform';
import { IArgumentInfo, ParsingStatus, ValueClass } from '../models/rslang';
/**
* Represents {@link IConstituenta} data from server.
*/
export interface IConstituentaDTO extends IConstituentaMeta {
parse: {
status: ParsingStatus;
valueClass: ValueClass;
typification: string;
syntaxTree: string;
args: IArgumentInfo[];
};
}
/**
* Represents data for {@link IRSForm} provided by backend.
*/
export interface IRSFormDTO extends ILibraryItemVersioned {
items: IConstituentaDTO[];
inheritance: IInheritanceInfo[];
oss: ILibraryItemReference[];
}
/**
* Represents data, used for uploading {@link IRSForm} as file.
*/
export interface IRSFormUploadDTO {
itemID: number;
load_metadata: boolean;
file: File;
fileName: string;
}
/**
* Represents {@link IConstituenta} data, used in creation process.
*/
export interface ICstCreateDTO {
alias: string;
cst_type: CstType;
definition_raw: string;
term_raw: string;
convention: string;
definition_formal: string;
term_forms: TermForm[];
insert_after: number | null;
}
/**
* Represents data response when creating {@link IConstituenta}.
*/
export interface ICstCreatedResponse {
new_cst: IConstituentaMeta;
schema: IRSFormDTO;
}
/**
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
*/
export const schemaCstUpdate = z.object({
target: z.number(),
item_data: z.object({
convention: z.string().optional(),
definition_formal: z.string().optional(),
definition_raw: z.string().optional(),
term_raw: z.string().optional(),
term_forms: z.array(z.object({ text: z.string(), tags: z.string() })).optional()
})
});
/**
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
*/
export type ICstUpdateDTO = z.infer<typeof schemaCstUpdate>;
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export const schemaCstRename = z.object({
target: z.number(),
alias: z.string(),
cst_type: z.nativeEnum(CstType)
});
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export type ICstRenameDTO = z.infer<typeof schemaCstRename>;
/**
* Represents data, used in ordering a list of {@link IConstituenta}.
*/
export interface ICstMoveDTO {
items: number[];
move_to: number; // Note: 0-base index
}
/**
* Represents data response when creating producing structure of {@link IConstituenta}.
*/
export interface IProduceStructureResponse {
cst_list: number[];
schema: IRSFormDTO;
}
/**
* Represents data, used in merging single {@link IConstituenta}.
*/
export const schemaCstSubstitute = z.object({
original: z.number(),
substitution: z.number()
});
/**
* Represents data, used in merging single {@link IConstituenta}.
*/
export type ICstSubstitute = z.infer<typeof schemaCstSubstitute>;
/**
* Represents input data for inline synthesis.
*/
export const schemaInlineSynthesis = z.object({
receiver: z.number(),
source: z.number().nullable(),
items: z.array(z.number()),
substitutions: z.array(schemaCstSubstitute)
});
/**
* Represents input data for inline synthesis.
*/
export type IInlineSynthesisDTO = z.infer<typeof schemaInlineSynthesis>;
/**
* Represents {@link IConstituenta} data, used for checking expression.
*/
export interface ICheckConstituentaDTO {
alias: string;
cst_type: CstType;
definition_formal: string;
}
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export const schemaCstSubstitutions = z.object({
substitutions: z.array(schemaCstSubstitute).min(1, { message: errorMsg.emptySubstitutions })
});
/**
* Represents data, used in merging multiple {@link IConstituenta}.
*/
export type ICstSubstitutionsDTO = z.infer<typeof schemaCstSubstitutions>;

View File

@ -1,6 +1,7 @@
import { useMutation } from '@tanstack/react-query';
import { ICheckConstituentaDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { ICheckConstituentaDTO } from './types';
export const useCheckConstituenta = () => {
const mutation = useMutation({

View File

@ -1,9 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { ICstCreateDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { ICstCreateDTO } from './types';
export const useCstCreate = () => {
const client = useQueryClient();
@ -16,7 +17,7 @@ export const useCstCreate = () => {
updateTimestamp(data.schema.id);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id

View File

@ -1,7 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { IConstituentaList } from '../models/rsform';
import { rsformsApi } from './api';
@ -17,7 +17,7 @@ export const useCstDelete = () => {
updateTimestamp(data.id);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id

View File

@ -2,7 +2,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library';
import { ICstMoveDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { ICstMoveDTO } from './types';
export const useCstMove = () => {
const client = useQueryClient();

View File

@ -1,9 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { ICstRenameDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { ICstRenameDTO } from './types';
export const useCstRename = () => {
const client = useQueryClient();
@ -16,7 +17,7 @@ export const useCstRename = () => {
updateTimestamp(data.schema.id);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id

View File

@ -1,9 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { ICstSubstitutionsDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { ICstSubstitutionsDTO } from './types';
export const useCstSubstitute = () => {
const client = useQueryClient();
@ -16,7 +17,7 @@ export const useCstSubstitute = () => {
updateTimestamp(data.id);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id

View File

@ -1,9 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { ICstUpdateDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { ICstUpdateDTO } from './types';
export const useCstUpdate = () => {
const client = useQueryClient();
@ -15,7 +16,7 @@ export const useCstUpdate = () => {
updateTimestamp(variables.itemID);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
]);
}

View File

@ -1,9 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { IInlineSynthesisDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { IInlineSynthesisDTO } from './types';
export const useInlineSynthesis = () => {
const client = useQueryClient();
@ -16,7 +17,7 @@ export const useInlineSynthesis = () => {
updateTimestamp(data.id);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id

View File

@ -1,11 +1,9 @@
import { useIsMutating } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { rsformsApi } from './api';
import { KEYS } from '@/backend/configuration';
export const useMutatingRSForm = () => {
const countLibrary = useIsMutating({ mutationKey: [libraryApi.baseKey] });
const countRsform = useIsMutating({ mutationKey: [rsformsApi.baseKey] });
const countLibrary = useIsMutating({ mutationKey: [KEYS.library] });
const countRsform = useIsMutating({ mutationKey: [KEYS.rsform] });
return countLibrary + countRsform !== 0;
};

View File

@ -1,7 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { ITargetCst } from '../models/rsform';
import { rsformsApi } from './api';
@ -17,7 +17,7 @@ export const useProduceStructure = () => {
updateTimestamp(data.schema.id);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id

View File

@ -1,6 +1,7 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { queryClient } from '../../../backend/queryClient';
import { queryClient } from '@/backend/queryClient';
import { rsformsApi } from './api';
import { RSFormLoader } from './RSFormLoader';

View File

@ -1,7 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { useUpdateTimestamp } from '@/features/library';
import { ossApi } from '@/features/oss/backend/api';
import { rsformsApi } from './api';
@ -16,7 +16,7 @@ export const useResetAliases = () => {
updateTimestamp(data.id);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id

View File

@ -1,10 +1,10 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { libraryApi } from '@/features/library';
import { KEYS } from '@/backend/configuration';
import { ILibraryItem } from '@/features/library/models/library';
import { ossApi } from '@/features/oss/backend/api';
import { IRSFormUploadDTO, rsformsApi } from './api';
import { rsformsApi } from './api';
import { IRSFormUploadDTO } from './types';
export const useUploadTRS = () => {
const client = useQueryClient();
@ -12,13 +12,13 @@ export const useUploadTRS = () => {
mutationKey: [rsformsApi.baseKey, 'load-trs'],
mutationFn: rsformsApi.upload,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
client.setQueryData(KEYS.composite.libraryList, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === data.id ? data : item))
);
return Promise.allSettled([
client.invalidateQueries({ queryKey: [ossApi.baseKey] }),
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({
queryKey: [rsformsApi.baseKey],
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id

View File

@ -31,7 +31,7 @@ interface PickConstituentaProps extends CProps.Styling {
const columnHelper = createColumnHelper<IConstituenta>();
function PickConstituenta({
export function PickConstituenta({
id,
items,
value,
@ -102,5 +102,3 @@ function PickConstituenta({
</div>
);
}
export default PickConstituenta;

View File

@ -10,7 +10,7 @@ import { NoData } from '@/components/View';
import { Graph } from '@/models/Graph';
import { describeConstituenta } from '../labels';
import { ConstituentaID, IConstituenta, IRSForm } from '../models/rsform';
import { IConstituenta, IRSForm } from '../models/rsform';
import { isBasicConcept, matchConstituenta } from '../models/rsformAPI';
import { CstMatchMode } from '../stores/cstSearch';
import BadgeConstituenta from './BadgeConstituenta';
@ -18,8 +18,8 @@ import ToolbarGraphSelection from './ToolbarGraphSelection';
interface PickMultiConstituentaProps extends CProps.Styling {
id?: string;
value: ConstituentaID[];
onChange: (newValue: ConstituentaID[]) => void;
value: number[];
onChange: (newValue: number[]) => void;
schema: IRSForm;
items: IConstituenta[];
@ -30,7 +30,7 @@ interface PickMultiConstituentaProps extends CProps.Styling {
const columnHelper = createColumnHelper<IConstituenta>();
function PickMultiConstituenta({
export function PickMultiConstituenta({
id,
schema,
items,
@ -72,7 +72,7 @@ function PickMultiConstituenta({
onChange([]);
} else {
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
const newSelection: ConstituentaID[] = [];
const newSelection: number[] = [];
filtered.forEach((cst, index) => {
if (newRowSelection[String(index)] === true) {
newSelection.push(cst.id);
@ -141,5 +141,3 @@ function PickMultiConstituenta({
</div>
);
}
export default PickMultiConstituenta;

View File

@ -10,15 +10,22 @@ import { IconAccept, IconPageLeft, IconPageRight, IconRemove, IconReplace } from
import { CProps } from '@/components/props';
import { NoData } from '@/components/View';
import { ILibraryItem, SelectLibraryItem } from '@/features/library';
import { IMultiSubstitution } from '@/features/oss/models/oss';
import { APP_COLORS } from '@/styling/colors';
import { errorMsg } from '@/utils/labels';
import { ICstSubstitute } from '../backend/api';
import { ConstituentaID, IConstituenta, IRSForm } from '../models/rsform';
import { ICstSubstitute } from '../backend/types';
import { IConstituenta, IRSForm } from '../models/rsform';
import BadgeConstituenta from './BadgeConstituenta';
import SelectConstituenta from './SelectConstituenta';
interface IMultiSubstitution {
original_source: ILibraryItem;
original: IConstituenta;
substitution: IConstituenta;
substitution_source: ILibraryItem;
is_suggestion: boolean;
}
interface PickSubstitutionsProps extends CProps.Styling {
value: ICstSubstitute[];
onChange: (newValue: ICstSubstitute[]) => void;
@ -34,7 +41,7 @@ interface PickSubstitutionsProps extends CProps.Styling {
const columnHelper = createColumnHelper<IMultiSubstitution>();
function PickSubstitutions({
export function PickSubstitutions({
value,
onChange,
suggestions,
@ -81,7 +88,7 @@ function PickSubstitutions({
}))
];
function getSchemaByCst(id: ConstituentaID): IRSForm | undefined {
function getSchemaByCst(id: number): IRSForm | undefined {
for (const schema of schemas) {
const cst = schema.cstByID.get(id);
if (cst) {
@ -91,7 +98,7 @@ function PickSubstitutions({
return undefined;
}
function getConstituenta(id: ConstituentaID): IConstituenta | undefined {
function getConstituenta(id: number): IConstituenta | undefined {
for (const schema of schemas) {
const cst = schema.cstByID.get(id);
if (cst) {
@ -291,5 +298,3 @@ function PickSubstitutions({
</div>
);
}
export default PickSubstitutions;

View File

@ -12,7 +12,7 @@ import { Label } from '@/components/Input';
import { usePreferencesStore } from '@/stores/preferences';
import { APP_COLORS } from '@/styling/colors';
import { ConstituentaID, IRSForm } from '../../models/rsform';
import { IRSForm } from '../../models/rsform';
import { generateAlias, getCstTypePrefix, guessCstType } from '../../models/rsformAPI';
import { extractGlobals } from '../../models/rslangAPI';
import { ccBracketMatching } from './bracketMatching';
@ -41,7 +41,7 @@ interface RSInputProps
onChange?: (newValue: string) => void;
onAnalyze?: () => void;
schema?: IRSForm;
onOpenEdit?: (cstID: ConstituentaID) => void;
onOpenEdit?: (cstID: number) => void;
}
const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(

View File

@ -1,11 +1,10 @@
import { Extension } from '@codemirror/state';
import { EditorView } from '@uiw/react-codemirror';
import { findAliasAt } from '@/utils/codemirror';
import { IRSForm } from '../../models/rsform';
import { findAliasAt } from './utils';
import { ConstituentaID, IRSForm } from '../../models/rsform';
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => {
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: number) => void) => {
return EditorView.domEventHandlers({
click: (event: MouseEvent, view: EditorView) => {
if (!event.ctrlKey && !event.metaKey) {
@ -34,6 +33,6 @@ const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID)
});
};
export function rsNavigation(schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void): Extension {
export function rsNavigation(schema: IRSForm, onOpenEdit: (cstID: number) => void): Extension {
return [navigationProducer(schema, onOpenEdit)];
}

View File

@ -2,11 +2,10 @@ import { Extension } from '@codemirror/state';
import { hoverTooltip, TooltipView } from '@codemirror/view';
import clsx from 'clsx';
import { findAliasAt } from '@/utils/codemirror';
import { labelCstTypification } from '../../labels';
import { IConstituenta, IRSForm } from '../../models/rsform';
import { isBasicConcept } from '../../models/rsformAPI';
import { findAliasAt } from './utils';
const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
return hoverTooltip((view, pos) => {

View File

@ -0,0 +1,25 @@
import { syntaxTree } from '@codemirror/language';
import { EditorState } from '@uiw/react-codemirror';
import { findEnvelopingNodes } from '@/utils/codemirror';
import { GlobalTokens } from './rslang';
/**
* Retrieves globalID from position in Editor.
*/
export function findAliasAt(pos: number, state: EditorState) {
const { from: lineStart, to: lineEnd, text } = state.doc.lineAt(pos);
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(state), GlobalTokens);
let alias = '';
let start = 0;
let end = 0;
nodes.forEach(node => {
if (node.to <= lineEnd && node.from >= lineStart) {
alias = text.slice(node.from - lineStart, node.to - lineStart);
start = node.from;
end = node.to;
}
});
return { alias, start, end };
}

View File

@ -15,7 +15,7 @@ import { APP_COLORS } from '@/styling/colors';
import { CodeMirrorWrapper } from '@/utils/codemirror';
import { ReferenceType } from '../../models/language';
import { ConstituentaID, IRSForm } from '../../models/rsform';
import { IRSForm } from '../../models/rsform';
import { refsNavigation } from './clickNavigation';
import { NaturalLanguage, ReferenceTokens } from './parse';
import { RefEntity } from './parse/parser.terms';
@ -67,7 +67,7 @@ interface RefsInputInputProps
onChange?: (newValue: string) => void;
schema: IRSForm;
onOpenEdit?: (cstID: ConstituentaID) => void;
onOpenEdit?: (cstID: number) => void;
label?: string;
disabled?: boolean;

View File

@ -1,11 +1,10 @@
import { Extension } from '@codemirror/state';
import { EditorView } from '@uiw/react-codemirror';
import { findReferenceAt } from '@/utils/codemirror';
import { IRSForm } from '../../models/rsform';
import { findReferenceAt } from './utils';
import { ConstituentaID, IRSForm } from '../../models/rsform';
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => {
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: number) => void) => {
return EditorView.domEventHandlers({
click: (event: MouseEvent, view: EditorView) => {
if (!event.ctrlKey && !event.metaKey) {
@ -34,6 +33,6 @@ const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID)
});
};
export function refsNavigation(schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void): Extension {
export function refsNavigation(schema: IRSForm, onOpenEdit: (cstID: number) => void): Extension {
return [navigationProducer(schema, onOpenEdit)];
}

View File

@ -4,7 +4,7 @@ import { hoverTooltip, TooltipView } from '@codemirror/view';
import clsx from 'clsx';
import { APP_COLORS } from '@/styling/colors';
import { findContainedNodes, findReferenceAt } from '@/utils/codemirror';
import { findContainedNodes } from '@/utils/codemirror';
import { colorFgGrammeme } from '../../colors';
import { labelGrammeme } from '../../labels';
@ -13,6 +13,7 @@ import { IEntityReference, ISyntacticReference } from '../../models/language';
import { parseGrammemes } from '../../models/languageAPI';
import { IConstituenta, IRSForm } from '../../models/rsform';
import { RefEntity } from './parse/parser.terms';
import { findReferenceAt } from './utils';
export const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
return hoverTooltip((view, pos) => {

View File

@ -0,0 +1,26 @@
import { syntaxTree } from '@codemirror/language';
import { EditorState } from '@uiw/react-codemirror';
import { findEnvelopingNodes } from '@/utils/codemirror';
import { parseEntityReference, parseSyntacticReference } from '../../models/languageAPI';
import { ReferenceTokens } from './parse';
import { RefEntity } from './parse/parser.terms';
/**
* Retrieves reference from position in Editor.
*/
export function findReferenceAt(pos: number, state: EditorState) {
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(state), ReferenceTokens);
if (nodes.length !== 1) {
return undefined;
}
const start = nodes[0].from;
const end = nodes[0].to;
const text = state.doc.sliceString(start, end);
if (nodes[0].type.id === RefEntity) {
return { ref: parseEntityReference(text), start, end };
} else {
return { ref: parseSyntacticReference(text), start, end };
}
}

View File

@ -7,7 +7,7 @@ import { CProps } from '@/components/props';
import { describeConstituentaTerm } from '../labels';
import { describeConstituenta } from '../labels';
import { ConstituentaID, IConstituenta } from '../models/rsform';
import { IConstituenta } from '../models/rsform';
import { matchConstituenta } from '../models/rsformAPI';
import { CstMatchMode } from '../stores/cstSearch';
@ -34,7 +34,7 @@ function SelectConstituenta({
label: `${cst.alias}${cst.is_inherited ? '*' : ''}: ${describeConstituenta(cst)}`
})) ?? [];
function filter(option: { value: ConstituentaID | undefined; label: string }, inputValue: string) {
function filter(option: { value: number | undefined; label: string }, inputValue: string) {
const cst = items?.find(item => item.id === option.value);
return !cst ? false : matchConstituenta(cst, inputValue, CstMatchMode.ALL);
}

View File

@ -4,23 +4,28 @@ import { Overlay } from '@/components/Container';
import { MiniButton } from '@/components/Control';
import { IconDestroy, IconSave, IconShare } from '@/components/Icons';
import { BadgeHelp, HelpTopic } from '@/features/help';
import { ILibraryItemEditor, MiniSelectorOSS, useMutatingLibrary } from '@/features/library';
import { AccessPolicy, LibraryItemType } from '@/features/library';
import {
AccessPolicy,
ILibraryItemEditor,
LibraryItemType,
MiniSelectorOSS,
useMutatingLibrary
} from '@/features/library';
import { useRoleStore, UserRole } from '@/features/users';
import { useModificationStore } from '@/stores/modification';
import { PARAMETER } from '@/utils/constants';
import { tooltipText } from '@/utils/labels';
import { prepareTooltip, sharePage } from '@/utils/utils';
import { IRSForm } from '../../../models/rsform';
import { IRSEditContext } from '../RSEditContext';
import { IRSForm } from '../models/rsform';
import { IRSEditContext } from '../pages/RSFormPage/RSEditContext';
interface ToolbarRSFormCardProps {
onSubmit: () => void;
controller: ILibraryItemEditor;
}
function ToolbarRSFormCard({ controller, onSubmit }: ToolbarRSFormCardProps) {
export function ToolbarRSFormCard({ controller, onSubmit }: ToolbarRSFormCardProps) {
const role = useRoleStore(state => state.role);
const { isModified } = useModificationStore();
const isProcessing = useMutatingLibrary();
@ -73,5 +78,3 @@ function ToolbarRSFormCard({ controller, onSubmit }: ToolbarRSFormCardProps) {
</Overlay>
);
}
export default ToolbarRSFormCard;

View File

@ -6,7 +6,7 @@ import { ModalForm } from '@/components/Modal';
import usePartialUpdate from '@/hooks/usePartialUpdate';
import { useDialogsStore } from '@/stores/dialogs';
import { ICstCreateDTO } from '../../backend/api';
import { ICstCreateDTO } from '../../backend/types';
import { CstType, IRSForm } from '../../models/rsform';
import { generateAlias } from '../../models/rsformAPI';
import FormCreateCst from './FormCreateCst';

View File

@ -7,7 +7,7 @@ import { TextArea, TextInput } from '@/components/Input';
import { BadgeHelp, HelpTopic } from '@/features/help';
import { PARAMETER } from '@/utils/constants';
import { ICstCreateDTO } from '../../backend/api';
import { ICstCreateDTO } from '../../backend/types';
import RSInput from '../../components/RSInput';
import { SelectCstType } from '../../components/SelectCstType';
import { CstType, IRSForm } from '../../models/rsform';

View File

@ -11,7 +11,7 @@ import usePartialUpdate from '@/hooks/usePartialUpdate';
import { useDialogsStore } from '@/stores/dialogs';
import { promptText } from '@/utils/labels';
import { ICstCreateDTO } from '../../backend/api';
import { ICstCreateDTO } from '../../backend/types';
import { useRSForm } from '../../backend/useRSForm';
import { CstType, IRSForm } from '../../models/rsform';
import { generateAlias, validateNewAlias } from '../../models/rsformAPI';

View File

@ -10,7 +10,7 @@ import { IconAccept, IconRemove, IconReset } from '@/components/Icons';
import { NoData } from '@/components/View';
import { APP_COLORS } from '@/styling/colors';
import PickConstituenta from '../../components/PickConstituenta';
import { PickConstituenta } from '../../components/PickConstituenta';
import RSInput from '../../components/RSInput';
import { IConstituenta, IRSForm } from '../../models/rsform';
import { IArgumentValue } from '../../models/rslang';

View File

@ -4,8 +4,8 @@ import { Dispatch, useEffect, useState } from 'react';
import { SelectSingle, TextArea } from '@/components/Input';
import { useTemplatesSuspense } from '@/features/library';
import PickConstituenta from '@/features/rsform/components/PickConstituenta';
import { PickConstituenta } from '../../components/PickConstituenta';
import RSInput from '../../components/RSInput';
import { CATEGORY_CST_TYPE, IConstituenta, IRSForm } from '../../models/rsform';
import { applyFilterCategory } from '../../models/rsformAPI';

View File

@ -9,13 +9,13 @@ import { useDialogsStore } from '@/stores/dialogs';
import { prefixes } from '@/utils/constants';
import { useCstDelete } from '../../backend/useCstDelete';
import { ConstituentaID, IRSForm } from '../../models/rsform';
import { IRSForm } from '../../models/rsform';
import ListConstituents from './ListConstituents';
export interface DlgDeleteCstProps {
schema: IRSForm;
selected: ConstituentaID[];
afterDelete: (initialSchema: IRSForm, deleted: ConstituentaID[]) => void;
selected: number[];
afterDelete: (initialSchema: IRSForm, deleted: number[]) => void;
}
function DlgDeleteCst() {
@ -23,7 +23,7 @@ function DlgDeleteCst() {
const { cstDelete } = useCstDelete();
const [expandOut, setExpandOut] = useState(false);
const expansion: ConstituentaID[] = schema.graph.expandAllOutputs(selected);
const expansion: number[] = schema.graph.expandAllOutputs(selected);
const hasInherited = selected.some(
id => schema.inheritance.find(item => item.parent === id),
[selected, schema.inheritance]

View File

@ -1,10 +1,10 @@
import clsx from 'clsx';
import { labelConstituenta } from '../../labels';
import { ConstituentaID, IRSForm } from '../../models/rsform';
import { IRSForm } from '../../models/rsform';
interface ListConstituentsProps {
list: ConstituentaID[];
list: number[];
schema: IRSForm;
prefix: string;
title?: string;

Some files were not shown because too many files have changed in this diff Show More