From 2beed1c1c69593408ccdf588ce0fa5bf4864e6d4 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Mon, 14 Jul 2025 22:31:20 +0300 Subject: [PATCH] F: Implementing prompt UI pt2 --- rsconcept/frontend/src/app/global-dialogs.tsx | 7 ++ rsconcept/frontend/src/app/router.tsx | 2 + .../ai/backend/use-available-templates.tsx | 10 ++- .../ai/backend/use-prompt-template.tsx | 6 ++ .../ai/dialogs/dlg-create-prompt-template.tsx | 69 +++++++++++++++++++ .../src/features/ai/dialogs/index.tsx | 1 - .../prompt-templates-page/menu-templates.tsx | 13 +++- .../tab-edit-template.tsx | 2 +- .../tab-list-templates.tsx | 15 ++-- .../tab-list-templates/index.tsx | 1 - .../tab-view-variables.tsx | 2 +- .../prompt-templates-page/templates-tabs.tsx | 17 ++++- rsconcept/frontend/src/stores/dialogs.ts | 9 ++- rsconcept/frontend/src/styling/overrides.css | 1 - 14 files changed, 133 insertions(+), 22 deletions(-) create mode 100644 rsconcept/frontend/src/features/ai/dialogs/dlg-create-prompt-template.tsx delete mode 100644 rsconcept/frontend/src/features/ai/dialogs/index.tsx rename rsconcept/frontend/src/features/ai/pages/prompt-templates-page/{tab-list-templates => }/tab-list-templates.tsx (61%) delete mode 100644 rsconcept/frontend/src/features/ai/pages/prompt-templates-page/tab-list-templates/index.tsx diff --git a/rsconcept/frontend/src/app/global-dialogs.tsx b/rsconcept/frontend/src/app/global-dialogs.tsx index ffc79178..b61c4de6 100644 --- a/rsconcept/frontend/src/app/global-dialogs.tsx +++ b/rsconcept/frontend/src/app/global-dialogs.tsx @@ -143,6 +143,11 @@ const DlgImportSchema = React.lazy(() => const DlgAIPromptDialog = React.lazy(() => import('@/features/ai/dialogs/dlg-ai-prompt').then(module => ({ default: module.DlgAIPromptDialog })) ); +const DlgCreatePromptTemplate = React.lazy(() => + import('@/features/ai/dialogs/dlg-create-prompt-template').then(module => ({ + default: module.DlgCreatePromptTemplate + })) +); export const GlobalDialogs = () => { const active = useDialogsStore(state => state.active); @@ -213,5 +218,7 @@ export const GlobalDialogs = () => { return ; case DialogType.AI_PROMPT: return ; + case DialogType.CREATE_PROMPT_TEMPLATE: + return ; } }; diff --git a/rsconcept/frontend/src/app/router.tsx b/rsconcept/frontend/src/app/router.tsx index cf655d5a..f2cef55f 100644 --- a/rsconcept/frontend/src/app/router.tsx +++ b/rsconcept/frontend/src/app/router.tsx @@ -1,5 +1,6 @@ import { createBrowserRouter } from 'react-router'; +import { prefetchAvailableTemplates } from '@/features/ai/backend/use-available-templates'; import { prefetchAuth } from '@/features/auth/backend/use-auth'; import { LoginPage } from '@/features/auth/pages/login-page'; import { HomePage } from '@/features/home/home-page'; @@ -87,6 +88,7 @@ export const Router = createBrowserRouter([ }, { path: routes.prompt_templates, + loader: prefetchAvailableTemplates, lazy: () => import('@/features/ai/pages/prompt-templates-page') }, { diff --git a/rsconcept/frontend/src/features/ai/backend/use-available-templates.tsx b/rsconcept/frontend/src/features/ai/backend/use-available-templates.tsx index 0767f58e..deb37c1b 100644 --- a/rsconcept/frontend/src/features/ai/backend/use-available-templates.tsx +++ b/rsconcept/frontend/src/features/ai/backend/use-available-templates.tsx @@ -1,17 +1,23 @@ import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { queryClient } from '@/backend/query-client'; + import { promptsApi } from './api'; export function useAvailableTemplates() { const { data, isLoading, error } = useQuery({ ...promptsApi.getAvailableTemplatesQueryOptions() }); - return { data, isLoading, error }; + return { items: data, isLoading, error }; } export function useAvailableTemplatesSuspense() { const { data } = useSuspenseQuery({ ...promptsApi.getAvailableTemplatesQueryOptions() }); - return { data }; + return { items: data }; +} + +export function prefetchAvailableTemplates() { + return queryClient.prefetchQuery(promptsApi.getAvailableTemplatesQueryOptions()); } diff --git a/rsconcept/frontend/src/features/ai/backend/use-prompt-template.tsx b/rsconcept/frontend/src/features/ai/backend/use-prompt-template.tsx index e83efbf2..ceb96820 100644 --- a/rsconcept/frontend/src/features/ai/backend/use-prompt-template.tsx +++ b/rsconcept/frontend/src/features/ai/backend/use-prompt-template.tsx @@ -1,5 +1,7 @@ import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { queryClient } from '@/backend/query-client'; + import { promptsApi } from './api'; export function usePromptTemplate(id: number) { @@ -15,3 +17,7 @@ export function usePromptTemplateSuspense(id: number) { }); return { promptTemplate: data }; } + +export function prefetchPromptTemplate({ itemID }: { itemID: number }) { + return queryClient.prefetchQuery(promptsApi.getPromptTemplateQueryOptions(itemID)); +} diff --git a/rsconcept/frontend/src/features/ai/dialogs/dlg-create-prompt-template.tsx b/rsconcept/frontend/src/features/ai/dialogs/dlg-create-prompt-template.tsx new file mode 100644 index 00000000..e85f7c1f --- /dev/null +++ b/rsconcept/frontend/src/features/ai/dialogs/dlg-create-prompt-template.tsx @@ -0,0 +1,69 @@ +import { Controller, useForm, useWatch } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; + +import { useAuthSuspense } from '@/features/auth'; + +import { Checkbox, TextArea, TextInput } from '@/components/input'; +import { ModalForm } from '@/components/modal'; +import { useDialogsStore } from '@/stores/dialogs'; +import { type RO } from '@/utils/meta'; + +import { type ICreatePromptTemplateDTO, type IPromptTemplateDTO, schemaCreatePromptTemplate } from '../backend/types'; +import { useAvailableTemplatesSuspense } from '../backend/use-available-templates'; +import { useCreatePromptTemplate } from '../backend/use-create-prompt-template'; + +export interface DlgCreatePromptTemplateProps { + onCreate: (data: RO) => void; +} + +export function DlgCreatePromptTemplate() { + const { onCreate } = useDialogsStore(state => state.props as DlgCreatePromptTemplateProps); + const { createPromptTemplate } = useCreatePromptTemplate(); + const { items: templates } = useAvailableTemplatesSuspense(); + const { user } = useAuthSuspense(); + + const { handleSubmit, control, register } = useForm({ + resolver: zodResolver(schemaCreatePromptTemplate), + defaultValues: { + label: '', + description: '', + text: '', + is_shared: false + } + }); + const label = useWatch({ control, name: 'label' }); + const isValid = label !== '' && !templates.find(template => template.label === label); + + function onSubmit(data: ICreatePromptTemplateDTO) { + void createPromptTemplate(data).then(onCreate); + } + + return ( + void handleSubmit(onSubmit)(event)} + className='cc-column w-140 max-h-120 py-2 px-6' + > + +