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;
|
||||
}
|
||||
setError(undefined);
|
||||
getRSFormDetails(String(templateID), {
|
||||
getRSFormDetails(String(templateID), '', {
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setError,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { createContext, useCallback, useContext, useMemo, useState } from 'react
|
|||
|
||||
import { type ErrorData } from '@/components/InfoError';
|
||||
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
||||
import { ILibraryItem } from '@/models/library';
|
||||
import { ILibraryItem, IVersionData } from '@/models/library';
|
||||
import { ILibraryUpdateData } from '@/models/library';
|
||||
import {
|
||||
IConstituentaList,
|
||||
|
@ -20,6 +20,7 @@ import {
|
|||
import {
|
||||
type DataCallback,
|
||||
deleteUnsubscribe,
|
||||
deleteVersion,
|
||||
getTRSFile,
|
||||
patchConstituenta,
|
||||
patchDeleteConstituenta,
|
||||
|
@ -29,7 +30,9 @@ import {
|
|||
patchResetAliases,
|
||||
patchSubstituteConstituenta,
|
||||
patchUploadTRS,
|
||||
patchVersion,
|
||||
postClaimLibraryItem,
|
||||
postCreateVersion,
|
||||
postNewConstituenta,
|
||||
postSubscribe
|
||||
} from '@/utils/backendAPI';
|
||||
|
@ -39,11 +42,13 @@ import { useLibrary } from './LibraryContext';
|
|||
|
||||
interface IRSFormContext {
|
||||
schema?: IRSForm;
|
||||
schemaID: string;
|
||||
|
||||
error: ErrorData;
|
||||
loading: boolean;
|
||||
processing: boolean;
|
||||
|
||||
isArchive: boolean;
|
||||
isOwned: boolean;
|
||||
isClaimable: boolean;
|
||||
isSubscribed: boolean;
|
||||
|
@ -63,6 +68,10 @@ interface IRSFormContext {
|
|||
cstUpdate: (data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => void;
|
||||
cstDelete: (data: IConstituentaList, 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);
|
||||
|
@ -76,13 +85,24 @@ export const useRSForm = () => {
|
|||
|
||||
interface RSFormStateProps {
|
||||
schemaID: string;
|
||||
versionID?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||
export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps) => {
|
||||
const library = useLibrary();
|
||||
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 [toggleTracking, setToggleTracking] = useState(false);
|
||||
|
@ -91,6 +111,8 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
return user?.id === schema?.owner || false;
|
||||
}, [user, schema?.owner]);
|
||||
|
||||
const isArchive = useMemo(() => !!versionID, [versionID]);
|
||||
|
||||
const isClaimable = useMemo(() => {
|
||||
return (user?.id !== schema?.owner && schema?.is_common && !schema?.is_canonical) ?? false;
|
||||
}, [user, schema?.owner, schema?.is_common, schema?.is_canonical]);
|
||||
|
@ -359,16 +381,79 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
[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 (
|
||||
<RSFormContext.Provider
|
||||
value={{
|
||||
schema,
|
||||
schemaID,
|
||||
error,
|
||||
loading,
|
||||
processing,
|
||||
isOwned,
|
||||
isClaimable,
|
||||
isSubscribed,
|
||||
isArchive,
|
||||
update,
|
||||
download,
|
||||
upload,
|
||||
|
@ -381,7 +466,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
cstRename,
|
||||
cstSubstitute,
|
||||
cstDelete,
|
||||
cstMoveTo
|
||||
cstMoveTo,
|
||||
versionCreate,
|
||||
versionUpdate,
|
||||
versionDelete
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { IRSForm, IRSFormData } from '@/models/rsform';
|
|||
import { loadRSFormData } from '@/models/rsformAPI';
|
||||
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 [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<ErrorData>(undefined);
|
||||
|
@ -27,7 +27,7 @@ function useRSFormDetails({ target }: { target?: string }) {
|
|||
if (!target) {
|
||||
return;
|
||||
}
|
||||
getRSFormDetails(target, {
|
||||
getRSFormDetails(target, version ?? '', {
|
||||
showError: true,
|
||||
setLoading: setCustomLoading ?? setLoading,
|
||||
onError: error => {
|
||||
|
@ -40,7 +40,7 @@ function useRSFormDetails({ target }: { target?: string }) {
|
|||
}
|
||||
});
|
||||
},
|
||||
[target]
|
||||
[target, version]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -96,6 +96,11 @@ export interface IVersionInfo {
|
|||
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.
|
||||
*/
|
||||
|
|
|
@ -205,3 +205,11 @@ export interface IRSFormUploadData {
|
|||
file: File;
|
||||
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 InfoError, { ErrorData } from '@/components/InfoError';
|
||||
import Divider from '@/components/ui/Divider';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
|
@ -439,19 +440,31 @@ export const RSEditState = ({
|
|||
) : 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}
|
||||
</RSEditContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// ====== 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) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>Схема с указанным идентификатором отсутствует на портале.</p>
|
||||
<TextURL text='Перейти в Библиотеку' href='/library' />
|
||||
<p>{`Схема с указанным идентификатором ${isArchive ? 'и версией ' : ''}отсутствует`}</p>
|
||||
<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>
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -4,14 +4,17 @@ import { useParams } from 'react-router-dom';
|
|||
|
||||
import { AccessModeState } from '@/context/AccessModeContext';
|
||||
import { RSFormState } from '@/context/RSFormContext';
|
||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||
|
||||
import RSTabs from './RSTabs';
|
||||
|
||||
function RSFormPage() {
|
||||
const params = useParams();
|
||||
const query = useQueryStrings();
|
||||
const version = query.get('v') ?? undefined;
|
||||
return (
|
||||
<AccessModeState>
|
||||
<RSFormState schemaID={params.id ?? ''}>
|
||||
<RSFormState schemaID={params.id ?? ''} versionID={version}>
|
||||
<RSTabs />
|
||||
</RSFormState>
|
||||
</AccessModeState>
|
||||
|
|
|
@ -19,7 +19,8 @@ import {
|
|||
IUserProfile,
|
||||
IUserSignupData,
|
||||
IUserUpdateData,
|
||||
IUserUpdatePassword
|
||||
IUserUpdatePassword,
|
||||
IVersionData
|
||||
} from '@/models/library';
|
||||
import {
|
||||
IConstituentaList,
|
||||
|
@ -32,7 +33,8 @@ import {
|
|||
ICstUpdateData,
|
||||
IRSFormCreateData,
|
||||
IRSFormData,
|
||||
IRSFormUploadData
|
||||
IRSFormUploadData,
|
||||
IVersionCreatedResponse
|
||||
} from '@/models/rsform';
|
||||
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({
|
||||
title: `RSForm details for id=${target}`,
|
||||
endpoint: `/api/rsforms/${target}/details`,
|
||||
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>) {
|
||||
|
@ -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 =============
|
||||
function AxiosGet<ResponseData>({ endpoint, request, title, options }: IAxiosRequest<undefined, ResponseData>) {
|
||||
console.log(`REQUEST: [[${title}]]`);
|
||||
|
|
Loading…
Reference in New Issue
Block a user