mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-14 12:50:37 +03:00
F: Implement backend hooks for frontend
This commit is contained in:
parent
df72b1f527
commit
9a47ad5609
|
@ -1,2 +1,2 @@
|
|||
''' Serializers for persistent data manipulation (AI Prompts). '''
|
||||
from .data_access import PromptTemplateSerializer
|
||||
from .data_access import PromptTemplateListSerializer, PromptTemplateSerializer
|
||||
|
|
|
@ -37,3 +37,12 @@ class PromptTemplateSerializer(serializers.ModelSerializer):
|
|||
if validated_data['is_shared'] and not (user.is_superuser or user.is_staff):
|
||||
raise serializers.ValidationError(msg.promptSharedPermissionDenied())
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
|
||||
class PromptTemplateListSerializer(serializers.ModelSerializer):
|
||||
'''Serializer for listing PromptTemplates without the 'text' field.'''
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = PromptTemplate
|
||||
fields = ['id', 'owner', 'is_shared', 'label', 'description']
|
||||
read_only_fields = ['id', 'owner']
|
||||
|
|
|
@ -98,6 +98,8 @@ class TestPromptTemplateViewSet(EndpointTester):
|
|||
labels = [item['label'] for item in response.data]
|
||||
self.assertIn('Mine', labels)
|
||||
self.assertIn('Shared', labels)
|
||||
for item in response.data:
|
||||
self.assertNotIn('text', item)
|
||||
|
||||
|
||||
@decl_endpoint('/api/prompts/{item}/', method='patch')
|
||||
|
|
|
@ -7,7 +7,7 @@ from rest_framework.decorators import action
|
|||
from rest_framework.response import Response
|
||||
|
||||
from ..models import PromptTemplate
|
||||
from ..serializers import PromptTemplateSerializer
|
||||
from ..serializers import PromptTemplateListSerializer, PromptTemplateSerializer
|
||||
|
||||
|
||||
class IsOwnerOrAdmin(permissions.BasePermission):
|
||||
|
@ -48,5 +48,5 @@ class PromptTemplateViewSet(viewsets.ModelViewSet):
|
|||
owned = PromptTemplate.objects.filter(owner=user)
|
||||
shared = PromptTemplate.objects.filter(is_shared=True)
|
||||
templates = (owned | shared).distinct()
|
||||
serializer = self.get_serializer(templates, many=True)
|
||||
serializer = PromptTemplateListSerializer(templates, many=True)
|
||||
return Response(serializer.data)
|
||||
|
|
|
@ -16,6 +16,7 @@ export const KEYS = {
|
|||
library: 'library',
|
||||
users: 'users',
|
||||
cctext: 'cctext',
|
||||
prompts: 'prompts',
|
||||
global_mutation: 'global_mutation',
|
||||
|
||||
composite: {
|
||||
|
|
70
rsconcept/frontend/src/features/ai/backend/api.ts
Normal file
70
rsconcept/frontend/src/features/ai/backend/api.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { queryOptions } from '@tanstack/react-query';
|
||||
|
||||
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/api-transport';
|
||||
import { DELAYS, KEYS } from '@/backend/configuration';
|
||||
import { infoMsg } from '@/utils/labels';
|
||||
|
||||
import {
|
||||
type ICreatePromptTemplateDTO,
|
||||
type IPromptTemplateDTO,
|
||||
type IPromptTemplateListDTO,
|
||||
type IUpdatePromptTemplateDTO,
|
||||
schemaPromptTemplate,
|
||||
schemaPromptTemplateList
|
||||
} from './types';
|
||||
|
||||
export const promptsApi = {
|
||||
baseKey: KEYS.prompts,
|
||||
|
||||
getAvailableTemplatesQueryOptions: () =>
|
||||
queryOptions({
|
||||
queryKey: [KEYS.prompts, 'available'] as const,
|
||||
staleTime: DELAYS.staleShort,
|
||||
queryFn: meta =>
|
||||
axiosGet<IPromptTemplateListDTO>({
|
||||
schema: schemaPromptTemplateList,
|
||||
endpoint: '/api/prompts/available/',
|
||||
options: { signal: meta.signal }
|
||||
})
|
||||
}),
|
||||
|
||||
getPromptTemplateQueryOptions: (id: number) =>
|
||||
queryOptions({
|
||||
queryKey: [KEYS.prompts, id],
|
||||
staleTime: DELAYS.staleShort,
|
||||
queryFn: meta =>
|
||||
axiosGet<IPromptTemplateDTO>({
|
||||
schema: schemaPromptTemplate,
|
||||
endpoint: `/api/prompts/${id}/`,
|
||||
options: { signal: meta.signal }
|
||||
})
|
||||
}),
|
||||
|
||||
createPromptTemplate: (data: ICreatePromptTemplateDTO) =>
|
||||
axiosPost<ICreatePromptTemplateDTO, IPromptTemplateDTO>({
|
||||
schema: schemaPromptTemplate,
|
||||
endpoint: '/api/prompts/',
|
||||
request: {
|
||||
data: data,
|
||||
successMessage: infoMsg.changesSaved
|
||||
}
|
||||
}),
|
||||
|
||||
updatePromptTemplate: (id: number, data: IUpdatePromptTemplateDTO) =>
|
||||
axiosPatch<IUpdatePromptTemplateDTO, IPromptTemplateDTO>({
|
||||
schema: schemaPromptTemplate,
|
||||
endpoint: `/api/prompts/${id}/`,
|
||||
request: {
|
||||
data: data,
|
||||
successMessage: infoMsg.changesSaved
|
||||
}
|
||||
}),
|
||||
|
||||
deletePromptTemplate: (id: number) =>
|
||||
axiosDelete({
|
||||
endpoint: `/api/prompts/${id}/`,
|
||||
request: {
|
||||
successMessage: infoMsg.changesSaved
|
||||
}
|
||||
})
|
||||
} as const;
|
|
@ -1,11 +1,54 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
/** Represents AI prompt. */
|
||||
export interface IPromptTemplate {
|
||||
id: number;
|
||||
owner: number | null;
|
||||
is_shared: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
text: string;
|
||||
}
|
||||
export type IPromptTemplate = IPromptTemplateDTO;
|
||||
|
||||
export type IPromptTemplateInfo = z.infer<typeof schemaPromptTemplateInfo>;
|
||||
|
||||
/** Full prompt template as returned by backend. */
|
||||
export type IPromptTemplateDTO = z.infer<typeof schemaPromptTemplate>;
|
||||
|
||||
/** List item for available prompt templates (no text field). */
|
||||
export type IPromptTemplateListDTO = z.infer<typeof schemaPromptTemplateList>;
|
||||
|
||||
/** Data for creating a prompt template. */
|
||||
export type ICreatePromptTemplateDTO = z.infer<typeof schemaCreatePromptTemplate>;
|
||||
|
||||
/** Data for updating a prompt template. */
|
||||
export type IUpdatePromptTemplateDTO = z.infer<typeof schemaUpdatePromptTemplate>;
|
||||
|
||||
// ========= SCHEMAS ========
|
||||
|
||||
export const schemaPromptTemplate = z.strictObject({
|
||||
id: z.number(),
|
||||
owner: z.number().nullable(),
|
||||
label: z.string(),
|
||||
description: z.string(),
|
||||
text: z.string(),
|
||||
is_shared: z.boolean()
|
||||
});
|
||||
|
||||
export const schemaCreatePromptTemplate = schemaPromptTemplate.pick({
|
||||
label: true,
|
||||
description: true,
|
||||
text: true,
|
||||
is_shared: true
|
||||
});
|
||||
|
||||
export const schemaUpdatePromptTemplate = schemaPromptTemplate.pick({
|
||||
owner: true,
|
||||
label: true,
|
||||
description: true,
|
||||
text: true,
|
||||
is_shared: true
|
||||
});
|
||||
|
||||
export const schemaPromptTemplateInfo = schemaPromptTemplate.pick({
|
||||
id: true,
|
||||
owner: true,
|
||||
label: true,
|
||||
description: true,
|
||||
is_shared: true
|
||||
});
|
||||
|
||||
export const schemaPromptTemplateList = schemaPromptTemplateInfo.array();
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||
|
||||
import { promptsApi } from './api';
|
||||
|
||||
export function useAvailableTemplates() {
|
||||
const { data, isLoading, error } = useQuery({
|
||||
...promptsApi.getAvailableTemplatesQueryOptions()
|
||||
});
|
||||
return { data, isLoading, error };
|
||||
}
|
||||
|
||||
export function useAvailableTemplatesSuspense() {
|
||||
const { data } = useSuspenseQuery({
|
||||
...promptsApi.getAvailableTemplatesQueryOptions()
|
||||
});
|
||||
return { data };
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { promptsApi } from './api';
|
||||
|
||||
export function useCreatePromptTemplate() {
|
||||
const client = useQueryClient();
|
||||
const mutation = useMutation({
|
||||
mutationKey: [promptsApi.baseKey, 'create'],
|
||||
mutationFn: promptsApi.createPromptTemplate,
|
||||
onSuccess: () => {
|
||||
void client.invalidateQueries({ queryKey: [promptsApi.baseKey] });
|
||||
}
|
||||
});
|
||||
return {
|
||||
createPromptTemplate: mutation.mutateAsync,
|
||||
isPending: mutation.isPending,
|
||||
error: mutation.error,
|
||||
reset: mutation.reset
|
||||
};
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { promptsApi } from './api';
|
||||
|
||||
export function useDeletePromptTemplate() {
|
||||
const client = useQueryClient();
|
||||
const mutation = useMutation({
|
||||
mutationKey: [promptsApi.baseKey, 'delete'],
|
||||
mutationFn: promptsApi.deletePromptTemplate,
|
||||
onSuccess: (_data, id) => {
|
||||
void client.invalidateQueries({ queryKey: [promptsApi.baseKey] });
|
||||
void client.invalidateQueries({ queryKey: [promptsApi.baseKey, id] });
|
||||
}
|
||||
});
|
||||
return {
|
||||
deletePromptTemplate: mutation.mutateAsync,
|
||||
isPending: mutation.isPending,
|
||||
error: mutation.error,
|
||||
reset: mutation.reset
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||
|
||||
import { promptsApi } from './api';
|
||||
|
||||
export function usePromptTemplate(id: number) {
|
||||
const { data, isLoading, error } = useQuery({
|
||||
...promptsApi.getPromptTemplateQueryOptions(id)
|
||||
});
|
||||
return { data, isLoading, error };
|
||||
}
|
||||
|
||||
export function usePromptTemplateSuspense(id: number) {
|
||||
const { data } = useSuspenseQuery({
|
||||
...promptsApi.getPromptTemplateQueryOptions(id)
|
||||
});
|
||||
return { data };
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { promptsApi } from './api';
|
||||
import { type IUpdatePromptTemplateDTO } from './types';
|
||||
|
||||
export function useUpdatePromptTemplate() {
|
||||
const client = useQueryClient();
|
||||
const mutation = useMutation({
|
||||
mutationKey: [promptsApi.baseKey, 'update'],
|
||||
mutationFn: ({ id, data }: { id: number; data: IUpdatePromptTemplateDTO }) =>
|
||||
promptsApi.updatePromptTemplate(id, data),
|
||||
onSuccess: (_, variables) => {
|
||||
void client.invalidateQueries({ queryKey: [promptsApi.baseKey, variables.id] });
|
||||
}
|
||||
});
|
||||
return {
|
||||
updatePromptTemplate: mutation.mutateAsync,
|
||||
isPending: mutation.isPending,
|
||||
error: mutation.error,
|
||||
reset: mutation.reset
|
||||
};
|
||||
}
|
|
@ -89,9 +89,7 @@ export interface ICheckConstituentaDTO {
|
|||
/** Represents data, used in merging multiple {@link IConstituenta}. */
|
||||
export type ISubstitutionsDTO = z.infer<typeof schemaSubstitutions>;
|
||||
|
||||
/**
|
||||
* Represents Constituenta list.
|
||||
*/
|
||||
/** Represents Constituenta list. */
|
||||
export interface IConstituentaList {
|
||||
items: number[];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user