diff --git a/client/src/App.tsx b/client/src/App.tsx index c3a82f7..e6d2e8f 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -5,7 +5,7 @@ import { Toaster } from "@/components/ui/toaster"; import { ToastAction } from "@/components/ui/toast"; import { TooltipProvider } from "@/components/ui/tooltip"; import { useAuthProvider, AuthContext } from "@/hooks/useAuth"; -import { ThemeProvider } from "@/hooks/use-theme"; +import { ThemeProvider } from "@/themes/ThemeProvider"; import { OfflineSyncProvider } from "@/contexts/OfflineSyncContext"; import { useEffect, lazy, Suspense, useCallback, type ComponentType, type LazyExoticComponent } from "react"; import { initializePushNotifications } from "@/lib/push-notifications"; @@ -243,8 +243,8 @@ function App() { return ( - - + + @@ -257,8 +257,8 @@ function App() { - - + + ); } diff --git a/client/src/components/folders/FormFolderTree.tsx b/client/src/components/folders/FormFolderTree.tsx index 6f55721..8842054 100644 --- a/client/src/components/folders/FormFolderTree.tsx +++ b/client/src/components/folders/FormFolderTree.tsx @@ -295,8 +295,7 @@ function FolderNodeContent({ isOver && 'ring-2 ring-primary bg-primary/10' )} style={{ paddingLeft: `${item.level * 16 + 8}px` }} - onClick={() => onSelectFolder(item.id)} - onDoubleClick={() => onToggleExpand(item.id)} + onClick={() => onToggleExpand(item.id)} > + + {!skin.isBuiltIn && skin.isEditable && ( + + )} + + + ); + })} + + + + + + {effectiveSkin && !isSelectedBuiltIn && ( + + + Редактор скина «{effectiveSkin.name}» + Изменяйте цвета, радиусы и тени. Сохранённые изменения применятся ко всей организации. + + +
+
+ + setForm((prev) => ({ + name: e.target.value, + description: prev?.description ?? effectiveSkin.description ?? '', + tokens: prev?.tokens ?? { + light: cloneTokens(effectiveSkin.tokens.light), + dark: cloneTokens(effectiveSkin.tokens.dark), + }, + }))} + /> +
+
+ + setForm((prev) => ({ + name: prev?.name ?? effectiveSkin.name, + description: e.target.value, + tokens: prev?.tokens ?? { + light: cloneTokens(effectiveSkin.tokens.light), + dark: cloneTokens(effectiveSkin.tokens.dark), + }, + }))} + /> +
+
+ + setPreviewTheme(v as Theme)}> + + Светлая тема + Тёмная тема + + + + +
+
+
Primary
+
Secondary
+
Danger
+
+
+
Card
+
Muted
+
Accent
+
+
+ +
+

Цвета

+
+ {EDITABLE_COLOR_TOKENS.map((token) => { + const value = getTokenValue(form?.tokens ?? effectiveSkin.tokens, previewTheme, token.key) as string; + return ( +
+ +
+ { + const next = setTokenValue(form?.tokens ?? effectiveSkin.tokens, previewTheme, token.key, e.target.value); + setForm((prev) => ({ + name: prev?.name ?? effectiveSkin.name, + description: prev?.description ?? effectiveSkin.description ?? '', + tokens: next, + })); + }} + className="h-8 w-8 rounded border p-0.5" + /> + { + const next = setTokenValue(form?.tokens ?? effectiveSkin.tokens, previewTheme, token.key, e.target.value); + setForm((prev) => ({ + name: prev?.name ?? effectiveSkin.name, + description: prev?.description ?? effectiveSkin.description ?? '', + tokens: next, + })); + }} + className="h-8 text-xs" + /> +
+
+ ); + })} +
+
+ +
+

Радиусы

+
+ {EDITABLE_RADIUS_TOKENS.map((token) => { + const value = getTokenValue(form?.tokens ?? effectiveSkin.tokens, previewTheme, token.key) as string; + return ( +
+ + { + const next = setTokenValue(form?.tokens ?? effectiveSkin.tokens, previewTheme, token.key, e.target.value); + setForm((prev) => ({ + name: prev?.name ?? effectiveSkin.name, + description: prev?.description ?? effectiveSkin.description ?? '', + tokens: next, + })); + }} + className="h-8 text-xs" + /> +
+ ); + })} +
+
+ +
+ +
+
+
+ )} + + ); +} + +const DEFAULT_SKIN_ID = 'iistwin'; diff --git a/client/src/pages/Forms.tsx b/client/src/pages/Forms.tsx index aab8ea5..3e0737c 100644 --- a/client/src/pages/Forms.tsx +++ b/client/src/pages/Forms.tsx @@ -12,10 +12,9 @@ import { Plus, Search, Folder, X, FileText } from 'lucide-react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { FolderTreeWithItems } from '@/components/folders/FolderTreeWithItems'; -import { Checkbox } from '@/components/ui/checkbox'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; -import { insertFormSchema, updateFormSchema } from '@shared/schema'; +import { insertFormSchema } from '@shared/schema'; import { z } from 'zod'; import { useToast } from '@/hooks/use-toast'; import { queryClient, apiRequest } from '@/lib/queryClient'; @@ -48,7 +47,6 @@ const clientFormSchema = insertFormSchema.omit({ }); type FormCreateData = z.infer; -type FormUpdateData = z.infer; interface FormData { id: number; @@ -71,8 +69,6 @@ function parseFormDraggableId(id: string | number): number | null { const Forms = () => { const [createModalOpen, setCreateModalOpen] = useState(false); - const [editModalOpen, setEditModalOpen] = useState(false); - const [editingForm, setEditingForm] = useState(null); const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); const [deletingForm, setDeletingForm] = useState(null); const [tasksCount, setTasksCount] = useState<{ total: number; active: number; completed: number }>({ total: 0, active: 0, completed: 0 }); @@ -106,15 +102,6 @@ const Forms = () => { }, }); - const editForm = useForm({ - resolver: zodResolver(updateFormSchema), - defaultValues: { - name: '', - description: '', - isActive: true, - }, - }); - const { data: formsData, isLoading } = useQuery<{ success: boolean; forms: FormData[] }>({ queryKey: ['/api/forms'], queryFn: async () => { @@ -155,22 +142,6 @@ const Forms = () => { }, }); - const updateFormMutation = useMutation({ - mutationFn: async ({ id, data }: { id: number; data: FormUpdateData }) => { - return apiRequest('PUT', `/api/forms/${id}`, data); - }, - onSuccess: () => { - toast({ title: 'Форма обновлена', description: 'Форма успешно обновлена' }); - setEditModalOpen(false); - setEditingForm(null); - editForm.reset(); - queryClient.invalidateQueries({ queryKey: ['/api/forms'] }); - }, - onError: (error: any) => { - toast({ title: 'Ошибка', description: error.message || 'Не удалось обновить форму', variant: 'destructive' }); - }, - }); - const deleteFormMutation = useMutation({ mutationFn: async (formId: number) => { return apiRequest('DELETE', `/api/forms/${formId}`); @@ -187,18 +158,9 @@ const Forms = () => { }); const onSubmit = (data: FormCreateData) => createFormMutation.mutate(data); - const onEditSubmit = (data: FormUpdateData) => { - if (editingForm) updateFormMutation.mutate({ id: editingForm.id, data }); - }; const handleEditForm = (formData: FormData) => { - setEditingForm(formData); - editForm.reset({ - name: formData.name, - description: formData.description || '', - isActive: formData.isActive, - }); - setEditModalOpen(true); + setLocation(`/forms/${formData.id}/edit`); }; const handleDeleteForm = async (formData: FormData) => { @@ -516,7 +478,7 @@ const Forms = () => { - {/* Create/Edit Form Dialogs */} + {/* Create Form Dialog */} @@ -554,54 +516,6 @@ const Forms = () => { - - - - Редактировать форму - Изменение настроек формы iistwin - -
- - ( - - Название формы * - - - - - - )} /> - ( - - Описание - -