mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Add version frontend API
This commit is contained in:
parent
ce945711e2
commit
7d4b87aa7d
|
@ -95,7 +95,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
getRSFormDetails(String(templateID), {
|
getRSFormDetails(String(templateID), '', {
|
||||||
showError: true,
|
showError: true,
|
||||||
setLoading: setProcessing,
|
setLoading: setProcessing,
|
||||||
onError: setError,
|
onError: setError,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { createContext, useCallback, useContext, useMemo, useState } from 'react
|
||||||
|
|
||||||
import { type ErrorData } from '@/components/InfoError';
|
import { type ErrorData } from '@/components/InfoError';
|
||||||
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
||||||
import { ILibraryItem } from '@/models/library';
|
import { ILibraryItem, IVersionData } from '@/models/library';
|
||||||
import { ILibraryUpdateData } from '@/models/library';
|
import { ILibraryUpdateData } from '@/models/library';
|
||||||
import {
|
import {
|
||||||
IConstituentaList,
|
IConstituentaList,
|
||||||
|
@ -20,6 +20,7 @@ import {
|
||||||
import {
|
import {
|
||||||
type DataCallback,
|
type DataCallback,
|
||||||
deleteUnsubscribe,
|
deleteUnsubscribe,
|
||||||
|
deleteVersion,
|
||||||
getTRSFile,
|
getTRSFile,
|
||||||
patchConstituenta,
|
patchConstituenta,
|
||||||
patchDeleteConstituenta,
|
patchDeleteConstituenta,
|
||||||
|
@ -29,7 +30,9 @@ import {
|
||||||
patchResetAliases,
|
patchResetAliases,
|
||||||
patchSubstituteConstituenta,
|
patchSubstituteConstituenta,
|
||||||
patchUploadTRS,
|
patchUploadTRS,
|
||||||
|
patchVersion,
|
||||||
postClaimLibraryItem,
|
postClaimLibraryItem,
|
||||||
|
postCreateVersion,
|
||||||
postNewConstituenta,
|
postNewConstituenta,
|
||||||
postSubscribe
|
postSubscribe
|
||||||
} from '@/utils/backendAPI';
|
} from '@/utils/backendAPI';
|
||||||
|
@ -39,11 +42,13 @@ import { useLibrary } from './LibraryContext';
|
||||||
|
|
||||||
interface IRSFormContext {
|
interface IRSFormContext {
|
||||||
schema?: IRSForm;
|
schema?: IRSForm;
|
||||||
|
schemaID: string;
|
||||||
|
|
||||||
error: ErrorData;
|
error: ErrorData;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
|
|
||||||
|
isArchive: boolean;
|
||||||
isOwned: boolean;
|
isOwned: boolean;
|
||||||
isClaimable: boolean;
|
isClaimable: boolean;
|
||||||
isSubscribed: boolean;
|
isSubscribed: boolean;
|
||||||
|
@ -63,6 +68,10 @@ interface IRSFormContext {
|
||||||
cstUpdate: (data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => void;
|
cstUpdate: (data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => void;
|
||||||
cstDelete: (data: IConstituentaList, callback?: () => void) => void;
|
cstDelete: (data: IConstituentaList, callback?: () => void) => void;
|
||||||
cstMoveTo: (data: ICstMovetoData, callback?: () => void) => void;
|
cstMoveTo: (data: ICstMovetoData, callback?: () => void) => void;
|
||||||
|
|
||||||
|
versionCreate: (data: IVersionData, callback?: () => void) => void;
|
||||||
|
versionUpdate: (target: number, data: IVersionData, callback?: () => void) => void;
|
||||||
|
versionDelete: (target: number, callback?: () => void) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RSFormContext = createContext<IRSFormContext | null>(null);
|
const RSFormContext = createContext<IRSFormContext | null>(null);
|
||||||
|
@ -76,13 +85,24 @@ export const useRSForm = () => {
|
||||||
|
|
||||||
interface RSFormStateProps {
|
interface RSFormStateProps {
|
||||||
schemaID: string;
|
schemaID: string;
|
||||||
|
versionID?: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps) => {
|
||||||
const library = useLibrary();
|
const library = useLibrary();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { schema, reload, error, setError, setSchema, loading } = useRSFormDetails({ target: schemaID });
|
const {
|
||||||
|
schema, // prettier: split lines
|
||||||
|
reload,
|
||||||
|
error,
|
||||||
|
setError,
|
||||||
|
setSchema,
|
||||||
|
loading
|
||||||
|
} = useRSFormDetails({
|
||||||
|
target: schemaID,
|
||||||
|
version: versionID
|
||||||
|
});
|
||||||
const [processing, setProcessing] = useState(false);
|
const [processing, setProcessing] = useState(false);
|
||||||
|
|
||||||
const [toggleTracking, setToggleTracking] = useState(false);
|
const [toggleTracking, setToggleTracking] = useState(false);
|
||||||
|
@ -91,6 +111,8 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
return user?.id === schema?.owner || false;
|
return user?.id === schema?.owner || false;
|
||||||
}, [user, schema?.owner]);
|
}, [user, schema?.owner]);
|
||||||
|
|
||||||
|
const isArchive = useMemo(() => !!versionID, [versionID]);
|
||||||
|
|
||||||
const isClaimable = useMemo(() => {
|
const isClaimable = useMemo(() => {
|
||||||
return (user?.id !== schema?.owner && schema?.is_common && !schema?.is_canonical) ?? false;
|
return (user?.id !== schema?.owner && schema?.is_common && !schema?.is_canonical) ?? false;
|
||||||
}, [user, schema?.owner, schema?.is_common, schema?.is_canonical]);
|
}, [user, schema?.owner, schema?.is_common, schema?.is_canonical]);
|
||||||
|
@ -359,16 +381,79 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
[schemaID, setError, library, setSchema]
|
[schemaID, setError, library, setSchema]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const versionCreate = useCallback(
|
||||||
|
(data: IVersionData, callback?: () => void) => {
|
||||||
|
setError(undefined);
|
||||||
|
postCreateVersion(schemaID, {
|
||||||
|
data: data,
|
||||||
|
showError: true,
|
||||||
|
setLoading: setProcessing,
|
||||||
|
onError: setError,
|
||||||
|
onSuccess: newData => {
|
||||||
|
setSchema(newData.schema);
|
||||||
|
library.localUpdateTimestamp(Number(schemaID));
|
||||||
|
if (callback) callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[schemaID, setError, library, setSchema]
|
||||||
|
);
|
||||||
|
|
||||||
|
const versionUpdate = useCallback(
|
||||||
|
(target: number, data: IVersionData, callback?: () => void) => {
|
||||||
|
setError(undefined);
|
||||||
|
patchVersion(String(target), {
|
||||||
|
data: data,
|
||||||
|
showError: true,
|
||||||
|
setLoading: setProcessing,
|
||||||
|
onError: setError,
|
||||||
|
onSuccess: () => {
|
||||||
|
schema!.versions = schema!.versions.map(prev => {
|
||||||
|
if (prev.id === target) {
|
||||||
|
prev.description = data.description;
|
||||||
|
prev.version = data.version;
|
||||||
|
return prev;
|
||||||
|
} else {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setSchema(schema);
|
||||||
|
if (callback) callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setError, schema, setSchema]
|
||||||
|
);
|
||||||
|
|
||||||
|
const versionDelete = useCallback(
|
||||||
|
(target: number, callback?: () => void) => {
|
||||||
|
setError(undefined);
|
||||||
|
deleteVersion(String(target), {
|
||||||
|
showError: true,
|
||||||
|
setLoading: setProcessing,
|
||||||
|
onError: setError,
|
||||||
|
onSuccess: () => {
|
||||||
|
schema!.versions = schema!.versions.filter(prev => prev.id !== target);
|
||||||
|
setSchema(schema);
|
||||||
|
if (callback) callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setError, schema, setSchema]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RSFormContext.Provider
|
<RSFormContext.Provider
|
||||||
value={{
|
value={{
|
||||||
schema,
|
schema,
|
||||||
|
schemaID,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
processing,
|
processing,
|
||||||
isOwned,
|
isOwned,
|
||||||
isClaimable,
|
isClaimable,
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
|
isArchive,
|
||||||
update,
|
update,
|
||||||
download,
|
download,
|
||||||
upload,
|
upload,
|
||||||
|
@ -381,7 +466,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
cstRename,
|
cstRename,
|
||||||
cstSubstitute,
|
cstSubstitute,
|
||||||
cstDelete,
|
cstDelete,
|
||||||
cstMoveTo
|
cstMoveTo,
|
||||||
|
versionCreate,
|
||||||
|
versionUpdate,
|
||||||
|
versionDelete
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { IRSForm, IRSFormData } from '@/models/rsform';
|
||||||
import { loadRSFormData } from '@/models/rsformAPI';
|
import { loadRSFormData } from '@/models/rsformAPI';
|
||||||
import { getRSFormDetails } from '@/utils/backendAPI';
|
import { getRSFormDetails } from '@/utils/backendAPI';
|
||||||
|
|
||||||
function useRSFormDetails({ target }: { target?: string }) {
|
function useRSFormDetails({ target, version }: { target?: string; version?: string }) {
|
||||||
const [schema, setInnerSchema] = useState<IRSForm | undefined>(undefined);
|
const [schema, setInnerSchema] = useState<IRSForm | undefined>(undefined);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<ErrorData>(undefined);
|
const [error, setError] = useState<ErrorData>(undefined);
|
||||||
|
@ -27,7 +27,7 @@ function useRSFormDetails({ target }: { target?: string }) {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getRSFormDetails(target, {
|
getRSFormDetails(target, version ?? '', {
|
||||||
showError: true,
|
showError: true,
|
||||||
setLoading: setCustomLoading ?? setLoading,
|
setLoading: setCustomLoading ?? setLoading,
|
||||||
onError: error => {
|
onError: error => {
|
||||||
|
@ -40,7 +40,7 @@ function useRSFormDetails({ target }: { target?: string }) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[target]
|
[target, version]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -96,6 +96,11 @@ export interface IVersionInfo {
|
||||||
time_create: string;
|
time_create: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents user data, intended to create or update version metadata in persistent storage.
|
||||||
|
*/
|
||||||
|
export interface IVersionData extends Omit<IVersionInfo, 'id' | 'time_create'> {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents library item common data typical for all item types.
|
* Represents library item common data typical for all item types.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -205,3 +205,11 @@ export interface IRSFormUploadData {
|
||||||
file: File;
|
file: File;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents data response when creating {@link IVersionInfo}.
|
||||||
|
*/
|
||||||
|
export interface IVersionCreatedResponse {
|
||||||
|
version: number;
|
||||||
|
schema: IRSFormData;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useSt
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import InfoError, { ErrorData } from '@/components/InfoError';
|
import InfoError, { ErrorData } from '@/components/InfoError';
|
||||||
|
import Divider from '@/components/ui/Divider';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
|
@ -439,19 +440,31 @@ export const RSEditState = ({
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{model.loading ? <Loader /> : null}
|
{model.loading ? <Loader /> : null}
|
||||||
{model.error ? <ProcessError error={model.error} /> : null}
|
{model.error ? <ProcessError error={model.error} isArchive={model.isArchive} schemaID={model.schemaID} /> : null}
|
||||||
{model.schema && !model.loading ? children : null}
|
{model.schema && !model.loading ? children : null}
|
||||||
</RSEditContext.Provider>
|
</RSEditContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ====== Internals =========
|
// ====== Internals =========
|
||||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
function ProcessError({
|
||||||
|
error,
|
||||||
|
isArchive,
|
||||||
|
schemaID
|
||||||
|
}: {
|
||||||
|
error: ErrorData;
|
||||||
|
isArchive: boolean;
|
||||||
|
schemaID: string;
|
||||||
|
}): React.ReactElement {
|
||||||
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
||||||
return (
|
return (
|
||||||
<div className='p-2 text-center'>
|
<div className='p-2 text-center'>
|
||||||
<p>Схема с указанным идентификатором отсутствует на портале.</p>
|
<p>{`Схема с указанным идентификатором ${isArchive ? 'и версией ' : ''}отсутствует`}</p>
|
||||||
<TextURL text='Перейти в Библиотеку' href='/library' />
|
<div className='flex justify-center'>
|
||||||
|
<TextURL text='Библиотека' href='/library' />
|
||||||
|
{isArchive ? <Divider vertical margins='mx-3' /> : null}
|
||||||
|
{isArchive ? <TextURL text='Актуальная версия' href={`/rsforms/${schemaID}`} /> : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,14 +4,17 @@ import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { AccessModeState } from '@/context/AccessModeContext';
|
import { AccessModeState } from '@/context/AccessModeContext';
|
||||||
import { RSFormState } from '@/context/RSFormContext';
|
import { RSFormState } from '@/context/RSFormContext';
|
||||||
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
|
|
||||||
import RSTabs from './RSTabs';
|
import RSTabs from './RSTabs';
|
||||||
|
|
||||||
function RSFormPage() {
|
function RSFormPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const query = useQueryStrings();
|
||||||
|
const version = query.get('v') ?? undefined;
|
||||||
return (
|
return (
|
||||||
<AccessModeState>
|
<AccessModeState>
|
||||||
<RSFormState schemaID={params.id ?? ''}>
|
<RSFormState schemaID={params.id ?? ''} versionID={version}>
|
||||||
<RSTabs />
|
<RSTabs />
|
||||||
</RSFormState>
|
</RSFormState>
|
||||||
</AccessModeState>
|
</AccessModeState>
|
||||||
|
|
|
@ -19,7 +19,8 @@ import {
|
||||||
IUserProfile,
|
IUserProfile,
|
||||||
IUserSignupData,
|
IUserSignupData,
|
||||||
IUserUpdateData,
|
IUserUpdateData,
|
||||||
IUserUpdatePassword
|
IUserUpdatePassword,
|
||||||
|
IVersionData
|
||||||
} from '@/models/library';
|
} from '@/models/library';
|
||||||
import {
|
import {
|
||||||
IConstituentaList,
|
IConstituentaList,
|
||||||
|
@ -32,7 +33,8 @@ import {
|
||||||
ICstUpdateData,
|
ICstUpdateData,
|
||||||
IRSFormCreateData,
|
IRSFormCreateData,
|
||||||
IRSFormData,
|
IRSFormData,
|
||||||
IRSFormUploadData
|
IRSFormUploadData,
|
||||||
|
IVersionCreatedResponse
|
||||||
} from '@/models/rsform';
|
} from '@/models/rsform';
|
||||||
import { IExpressionParse, IRSExpression } from '@/models/rslang';
|
import { IExpressionParse, IRSExpression } from '@/models/rslang';
|
||||||
|
|
||||||
|
@ -215,12 +217,20 @@ export function postCloneLibraryItem(target: string, request: FrontExchange<IRSF
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRSFormDetails(target: string, request: FrontPull<IRSFormData>) {
|
export function getRSFormDetails(target: string, version: string, request: FrontPull<IRSFormData>) {
|
||||||
|
if (!version) {
|
||||||
AxiosGet({
|
AxiosGet({
|
||||||
title: `RSForm details for id=${target}`,
|
title: `RSForm details for id=${target}`,
|
||||||
endpoint: `/api/rsforms/${target}/details`,
|
endpoint: `/api/rsforms/${target}/details`,
|
||||||
request: request
|
request: request
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
AxiosGet({
|
||||||
|
title: `RSForm details for id=${target}`,
|
||||||
|
endpoint: `/api/rsforms/${target}/versions/{version}`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function patchLibraryItem(target: string, request: FrontExchange<ILibraryUpdateData, ILibraryItem>) {
|
export function patchLibraryItem(target: string, request: FrontExchange<ILibraryUpdateData, ILibraryItem>) {
|
||||||
|
@ -383,6 +393,30 @@ export function postGenerateLexeme(request: FrontExchange<ITextRequest, ILexemeD
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function postCreateVersion(target: string, request: FrontExchange<IVersionData, IVersionCreatedResponse>) {
|
||||||
|
AxiosPost({
|
||||||
|
title: `Create version for RSForm id=${target}`,
|
||||||
|
endpoint: `/api/rsforms/${target}/versions/create`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function patchVersion(target: string, request: FrontPush<IVersionData>) {
|
||||||
|
AxiosPatch({
|
||||||
|
title: `Version id=${target}`,
|
||||||
|
endpoint: `/api/versions/${target}`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteVersion(target: string, request: FrontAction) {
|
||||||
|
AxiosDelete({
|
||||||
|
title: `Version id=${target}`,
|
||||||
|
endpoint: `/api/versions/${target}`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ============ Helper functions =============
|
// ============ Helper functions =============
|
||||||
function AxiosGet<ResponseData>({ endpoint, request, title, options }: IAxiosRequest<undefined, ResponseData>) {
|
function AxiosGet<ResponseData>({ endpoint, request, title, options }: IAxiosRequest<undefined, ResponseData>) {
|
||||||
console.log(`REQUEST: [[${title}]]`);
|
console.log(`REQUEST: [[${title}]]`);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user