дизайн меню
This commit is contained in:
@@ -14,12 +14,14 @@ interface CategoryFieldsSelectorProps {
|
|||||||
selectedValues: AttributeValue[]
|
selectedValues: AttributeValue[]
|
||||||
onValuesChange: (values: AttributeValue[]) => void
|
onValuesChange: (values: AttributeValue[]) => void
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
compact?: boolean // Новый проп для компактного отображения
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CategoryFieldsSelector: React.FC<CategoryFieldsSelectorProps> = ({
|
export const CategoryFieldsSelector: React.FC<CategoryFieldsSelectorProps> = ({
|
||||||
selectedValues,
|
selectedValues,
|
||||||
onValuesChange,
|
onValuesChange,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
compact = false, // По умолчанию обычный режим
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
@@ -56,7 +58,10 @@ export const CategoryFieldsSelector: React.FC<CategoryFieldsSelectorProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={attribute.id} className="space-y-2">
|
<div key={attribute.id} className="space-y-2">
|
||||||
<Label htmlFor={attribute.id} className="text-sm font-medium">
|
<Label
|
||||||
|
htmlFor={attribute.id}
|
||||||
|
className={`text-sm font-medium ${compact ? 'text-xs' : ''}`}
|
||||||
|
>
|
||||||
{attribute.name}
|
{attribute.name}
|
||||||
{attribute.is_required && (
|
{attribute.is_required && (
|
||||||
<span className="ml-1 text-red-500">*</span>
|
<span className="ml-1 text-red-500">*</span>
|
||||||
@@ -68,20 +73,48 @@ export const CategoryFieldsSelector: React.FC<CategoryFieldsSelectorProps> = ({
|
|||||||
id={attribute.id}
|
id={attribute.id}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={e => updateAttributeValue(attribute.id, e.target.value)}
|
onChange={e => updateAttributeValue(attribute.id, e.target.value)}
|
||||||
placeholder={`Введите ${attribute.name.toLowerCase()}`}
|
placeholder={
|
||||||
|
compact ? attribute.name : `Введите ${attribute.name.toLowerCase()}`
|
||||||
|
}
|
||||||
|
className={compact ? 'h-8 text-sm' : ''}
|
||||||
{...fieldProps}
|
{...fieldProps}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{attribute.is_required && !value && (
|
{attribute.is_required && !value && (
|
||||||
<p className="flex items-center gap-1 text-xs text-red-500">
|
<p
|
||||||
|
className={`flex items-center gap-1 text-red-500 ${compact ? 'text-xs' : 'text-xs'}`}
|
||||||
|
>
|
||||||
<AlertCircle className="h-3 w-3" />
|
<AlertCircle className="h-3 w-3" />
|
||||||
Это поле обязательно для заполнения
|
{compact ? 'Обязательно' : 'Это поле обязательно для заполнения'}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const renderAttributeSection = (
|
||||||
|
attributesList: Attribute[],
|
||||||
|
title: string,
|
||||||
|
titleColor: string = ''
|
||||||
|
) => {
|
||||||
|
if (attributesList.length === 0) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={compact ? 'space-y-3' : 'space-y-4'}>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label
|
||||||
|
className={`text-sm font-semibold ${titleColor} ${compact ? 'text-xs' : ''}`}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<div className={compact ? 'grid grid-cols-2 gap-3' : 'space-y-4'}>
|
||||||
|
{attributesList.map(renderAttribute)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -93,34 +126,16 @@ export const CategoryFieldsSelector: React.FC<CategoryFieldsSelectorProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className={compact ? 'space-y-4' : 'space-y-6'}>
|
||||||
{/* Обязательные атрибуты */}
|
{/* Обязательные атрибуты */}
|
||||||
{requiredAttributes.length > 0 && (
|
{renderAttributeSection(
|
||||||
<div className="space-y-4">
|
requiredAttributes,
|
||||||
<div className="flex items-center gap-2">
|
'Обязательные атрибуты',
|
||||||
<Label className="text-sm font-semibold text-red-600">
|
'text-red-600'
|
||||||
Обязательные атрибуты
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{requiredAttributes.map(renderAttribute)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Необязательные атрибуты */}
|
{/* Необязательные атрибуты */}
|
||||||
{optionalAttributes.length > 0 && (
|
{renderAttributeSection(optionalAttributes, 'Дополнительные атрибуты')}
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Label className="text-sm font-semibold">
|
|
||||||
Дополнительные атрибуты
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{optionalAttributes.map(renderAttribute)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{attributes.length === 0 && (
|
{attributes.length === 0 && (
|
||||||
<div className="py-4 text-center">
|
<div className="py-4 text-center">
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import {
|
|||||||
TemplateFilters,
|
TemplateFilters,
|
||||||
} from '@/component/TemplateManager/TemplateFilterSidebar'
|
} from '@/component/TemplateManager/TemplateFilterSidebar'
|
||||||
import { Button } from '@/component/ui/button'
|
import { Button } from '@/component/ui/button'
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/component/ui/card'
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
@@ -32,7 +32,18 @@ import { useTemplateContext } from '@/entitiy/template/model/TemplateContext'
|
|||||||
import { Template, TemplateAttributeDetail } from '@/type/template'
|
import { Template, TemplateAttributeDetail } from '@/type/template'
|
||||||
import TemplateCard from '@/widget/template/ui/TemplateCard'
|
import TemplateCard from '@/widget/template/ui/TemplateCard'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { CheckSquare, Plus, Square, Trash2 } from 'lucide-react'
|
import {
|
||||||
|
CheckSquare,
|
||||||
|
Copy,
|
||||||
|
Edit3,
|
||||||
|
FileText,
|
||||||
|
Plus,
|
||||||
|
Settings2,
|
||||||
|
Square,
|
||||||
|
Tag,
|
||||||
|
Trash2,
|
||||||
|
Type,
|
||||||
|
} from 'lucide-react'
|
||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
|
||||||
interface AttributeValue {
|
interface AttributeValue {
|
||||||
@@ -65,6 +76,172 @@ const fromCategoryAttributeValues = (
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Компактный селектор атрибутов
|
||||||
|
interface CompactCategoryFieldsSelectorProps {
|
||||||
|
selectedValues: CategoryAttributeValue[]
|
||||||
|
onValuesChange: (values: CategoryAttributeValue[]) => void
|
||||||
|
isLoading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompactCategoryFieldsSelector: React.FC<
|
||||||
|
CompactCategoryFieldsSelectorProps
|
||||||
|
> = ({ selectedValues, onValuesChange, isLoading = false }) => {
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="py-4 text-center text-sm text-muted-foreground">
|
||||||
|
Загрузка атрибутов...
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<CategoryFieldsSelector
|
||||||
|
selectedValues={selectedValues}
|
||||||
|
onValuesChange={onValuesChange}
|
||||||
|
compact={true} // Передаем флаг для компактного отображения
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Общий компонент формы шаблона
|
||||||
|
interface TemplateFormProps {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
icon: React.ReactNode
|
||||||
|
iconColor: string
|
||||||
|
name: string
|
||||||
|
onNameChange: (value: string) => void
|
||||||
|
templateDescription: string
|
||||||
|
onDescriptionChange: (value: string) => void
|
||||||
|
attributeValues: AttributeValue[]
|
||||||
|
onAttributeValuesChange: (values: AttributeValue[]) => void
|
||||||
|
attributesLoading: boolean
|
||||||
|
onSubmit: () => void
|
||||||
|
onCancel: () => void
|
||||||
|
submitText: string
|
||||||
|
submitIcon: React.ReactNode
|
||||||
|
isSubmitDisabled: boolean
|
||||||
|
nameLabel?: string
|
||||||
|
descriptionLabel?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const TemplateForm: React.FC<TemplateFormProps> = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon,
|
||||||
|
iconColor,
|
||||||
|
name,
|
||||||
|
onNameChange,
|
||||||
|
templateDescription,
|
||||||
|
onDescriptionChange,
|
||||||
|
attributeValues,
|
||||||
|
onAttributeValuesChange,
|
||||||
|
attributesLoading,
|
||||||
|
onSubmit,
|
||||||
|
onCancel,
|
||||||
|
submitText,
|
||||||
|
submitIcon,
|
||||||
|
isSubmitDisabled,
|
||||||
|
nameLabel = 'Название шаблона',
|
||||||
|
descriptionLabel = 'Описание',
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DialogHeader className="pb-4">
|
||||||
|
<DialogTitle className="flex items-center gap-2 text-lg">
|
||||||
|
{icon}
|
||||||
|
{title}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>{description}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="max-h-[50vh] space-y-4 overflow-y-auto pr-2">
|
||||||
|
{/* Основные настройки */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Settings2 className={`h-4 w-4 ${iconColor}`} />
|
||||||
|
<CardTitle className="text-sm">Основная информация</CardTitle>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label className="flex items-center gap-2 text-sm font-medium">
|
||||||
|
<Type className="h-3 w-3" />
|
||||||
|
{nameLabel}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
placeholder={`Введите ${nameLabel.toLowerCase()}`}
|
||||||
|
value={name}
|
||||||
|
onChange={e => onNameChange(e.target.value)}
|
||||||
|
className="h-9"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label className="flex items-center gap-2 text-sm font-medium">
|
||||||
|
<FileText className="h-3 w-3" />
|
||||||
|
{descriptionLabel}
|
||||||
|
</Label>
|
||||||
|
<Textarea
|
||||||
|
placeholder={`${descriptionLabel}...`}
|
||||||
|
value={templateDescription}
|
||||||
|
onChange={e => onDescriptionChange(e.target.value)}
|
||||||
|
className="min-h-[80px] resize-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Атрибуты */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Tag className="h-4 w-4 text-purple-600" />
|
||||||
|
<CardTitle className="text-sm">Атрибуты шаблона</CardTitle>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<CompactCategoryFieldsSelector
|
||||||
|
selectedValues={toCategoryAttributeValues(attributeValues)}
|
||||||
|
onValuesChange={values =>
|
||||||
|
onAttributeValuesChange(fromCategoryAttributeValues(values))
|
||||||
|
}
|
||||||
|
isLoading={attributesLoading}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator className="my-4" />
|
||||||
|
<div className="flex items-center justify-between pt-2">
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{!name.trim() && (
|
||||||
|
<span className="text-amber-600">
|
||||||
|
Заполните {nameLabel.toLowerCase()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button variant="outline">Отмена</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button
|
||||||
|
onClick={onSubmit}
|
||||||
|
disabled={isSubmitDisabled}
|
||||||
|
className="min-w-[130px]"
|
||||||
|
>
|
||||||
|
{submitIcon}
|
||||||
|
{submitText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const TemplatesOverviewPage = () => {
|
export const TemplatesOverviewPage = () => {
|
||||||
const {
|
const {
|
||||||
templates,
|
templates,
|
||||||
@@ -360,6 +537,7 @@ export const TemplatesOverviewPage = () => {
|
|||||||
Выбрать
|
Выбрать
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{/* Диалог создания шаблона */}
|
||||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button size="sm">
|
<Button size="sm">
|
||||||
@@ -367,67 +545,25 @@ export const TemplatesOverviewPage = () => {
|
|||||||
Создать
|
Создать
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-2xl">
|
<DialogContent className="max-h-[90vh] max-w-2xl overflow-hidden">
|
||||||
<DialogHeader>
|
<TemplateForm
|
||||||
<DialogTitle>Создать новый шаблон</DialogTitle>
|
title="Создать новый шаблон"
|
||||||
<DialogDescription>
|
description="Заполните основную информацию о шаблоне протокола"
|
||||||
Заполните основную информацию о шаблоне
|
icon={<Plus className="h-5 w-5 text-blue-600" />}
|
||||||
</DialogDescription>
|
iconColor="text-blue-600"
|
||||||
</DialogHeader>
|
name={newName}
|
||||||
<div className="grid gap-4 py-4">
|
onNameChange={setNewName}
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
templateDescription={newDescription}
|
||||||
<Label htmlFor="template-name" className="text-right">
|
onDescriptionChange={setNewDescription}
|
||||||
Название
|
attributeValues={newAttributeValues}
|
||||||
</Label>
|
onAttributeValuesChange={setNewAttributeValues}
|
||||||
<Input
|
attributesLoading={attributesLoading}
|
||||||
id="template-name"
|
onSubmit={handleCreate}
|
||||||
className="col-span-3"
|
onCancel={() => setIsDialogOpen(false)}
|
||||||
value={newName}
|
submitText="Создать"
|
||||||
onChange={e => setNewName(e.target.value)}
|
submitIcon={<Plus className="mr-2 h-4 w-4" />}
|
||||||
placeholder="Введите название шаблона"
|
isSubmitDisabled={!newName.trim()}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
|
||||||
<Label
|
|
||||||
htmlFor="template-description"
|
|
||||||
className="text-right"
|
|
||||||
>
|
|
||||||
Описание
|
|
||||||
</Label>
|
|
||||||
<Textarea
|
|
||||||
id="template-description"
|
|
||||||
className="col-span-3"
|
|
||||||
value={newDescription}
|
|
||||||
onChange={e => setNewDescription(e.target.value)}
|
|
||||||
placeholder="Введите описание"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-4 gap-4">
|
|
||||||
<Label className="self-start pt-2 text-right">
|
|
||||||
Атрибуты
|
|
||||||
</Label>
|
|
||||||
<div className="col-span-3">
|
|
||||||
{!attributesLoading && (
|
|
||||||
<CategoryFieldsSelector
|
|
||||||
selectedValues={toCategoryAttributeValues(
|
|
||||||
newAttributeValues
|
|
||||||
)}
|
|
||||||
onValuesChange={values =>
|
|
||||||
setNewAttributeValues(
|
|
||||||
fromCategoryAttributeValues(values)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="outline">Отмена</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button onClick={handleCreate}>Создать</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
@@ -436,72 +572,27 @@ export const TemplatesOverviewPage = () => {
|
|||||||
open={isCopyDialogOpen}
|
open={isCopyDialogOpen}
|
||||||
onOpenChange={setIsCopyDialogOpen}
|
onOpenChange={setIsCopyDialogOpen}
|
||||||
>
|
>
|
||||||
<DialogContent className="max-w-md">
|
<DialogContent className="max-h-[90vh] max-w-2xl overflow-hidden">
|
||||||
<DialogHeader>
|
<TemplateForm
|
||||||
<DialogTitle>Копировать шаблон</DialogTitle>
|
title="Копировать шаблон"
|
||||||
<DialogDescription>
|
description="Укажите название и описание для копии шаблона"
|
||||||
Укажите название и описание для копии шаблона
|
icon={<Copy className="h-5 w-5 text-green-600" />}
|
||||||
</DialogDescription>
|
iconColor="text-green-600"
|
||||||
</DialogHeader>
|
name={copyName}
|
||||||
<div className="grid gap-4 py-4">
|
onNameChange={setCopyName}
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
templateDescription={copyDescription}
|
||||||
<Label htmlFor="copy-name" className="text-right">
|
onDescriptionChange={setCopyDescription}
|
||||||
Название
|
attributeValues={copyAttributeValues}
|
||||||
</Label>
|
onAttributeValuesChange={setCopyAttributeValues}
|
||||||
<Input
|
attributesLoading={attributesLoading}
|
||||||
id="copy-name"
|
onSubmit={handleCopyConfirm}
|
||||||
className="col-span-3"
|
onCancel={() => setIsCopyDialogOpen(false)}
|
||||||
value={copyName}
|
submitText="Копировать"
|
||||||
onChange={e => setCopyName(e.target.value)}
|
submitIcon={<Copy className="mr-2 h-4 w-4" />}
|
||||||
placeholder="Введите название"
|
isSubmitDisabled={!copyName.trim()}
|
||||||
/>
|
nameLabel="Название копии"
|
||||||
</div>
|
descriptionLabel="Описание копии"
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
/>
|
||||||
<Label
|
|
||||||
htmlFor="copy-description"
|
|
||||||
className="text-right"
|
|
||||||
>
|
|
||||||
Описание
|
|
||||||
</Label>
|
|
||||||
<Textarea
|
|
||||||
id="copy-description"
|
|
||||||
className="col-span-3"
|
|
||||||
value={copyDescription}
|
|
||||||
onChange={e => setCopyDescription(e.target.value)}
|
|
||||||
placeholder="Введите описание"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-4 gap-4">
|
|
||||||
<Label className="self-start pt-2 text-right">
|
|
||||||
Атрибуты
|
|
||||||
</Label>
|
|
||||||
<div className="col-span-3">
|
|
||||||
{!attributesLoading && (
|
|
||||||
<CategoryFieldsSelector
|
|
||||||
selectedValues={toCategoryAttributeValues(
|
|
||||||
copyAttributeValues
|
|
||||||
)}
|
|
||||||
onValuesChange={values =>
|
|
||||||
setCopyAttributeValues(
|
|
||||||
fromCategoryAttributeValues(values)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="outline">Отмена</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button
|
|
||||||
onClick={handleCopyConfirm}
|
|
||||||
disabled={!copyName.trim()}
|
|
||||||
>
|
|
||||||
Копировать
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
@@ -510,72 +601,25 @@ export const TemplatesOverviewPage = () => {
|
|||||||
open={isEditDialogOpen}
|
open={isEditDialogOpen}
|
||||||
onOpenChange={setIsEditDialogOpen}
|
onOpenChange={setIsEditDialogOpen}
|
||||||
>
|
>
|
||||||
<DialogContent className="max-w-md">
|
<DialogContent className="max-h-[90vh] max-w-2xl overflow-hidden">
|
||||||
<DialogHeader>
|
<TemplateForm
|
||||||
<DialogTitle>Редактировать шаблон</DialogTitle>
|
title="Редактировать шаблон"
|
||||||
<DialogDescription>
|
description="Измените основную информацию о шаблоне"
|
||||||
Измените название и описание шаблона
|
icon={<Edit3 className="h-5 w-5 text-orange-600" />}
|
||||||
</DialogDescription>
|
iconColor="text-orange-600"
|
||||||
</DialogHeader>
|
name={editName}
|
||||||
<div className="grid gap-4 py-4">
|
onNameChange={setEditName}
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
templateDescription={editDescription}
|
||||||
<Label htmlFor="edit-name" className="text-right">
|
onDescriptionChange={setEditDescription}
|
||||||
Название
|
attributeValues={editAttributeValues}
|
||||||
</Label>
|
onAttributeValuesChange={setEditAttributeValues}
|
||||||
<Input
|
attributesLoading={attributesLoading}
|
||||||
id="edit-name"
|
onSubmit={handleEditConfirm}
|
||||||
className="col-span-3"
|
onCancel={() => setIsEditDialogOpen(false)}
|
||||||
value={editName}
|
submitText="Сохранить"
|
||||||
onChange={e => setEditName(e.target.value)}
|
submitIcon={<Edit3 className="mr-2 h-4 w-4" />}
|
||||||
placeholder="Введите название"
|
isSubmitDisabled={!editName.trim()}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
|
||||||
<Label
|
|
||||||
htmlFor="edit-description"
|
|
||||||
className="text-right"
|
|
||||||
>
|
|
||||||
Описание
|
|
||||||
</Label>
|
|
||||||
<Textarea
|
|
||||||
id="edit-description"
|
|
||||||
className="col-span-3"
|
|
||||||
value={editDescription}
|
|
||||||
onChange={e => setEditDescription(e.target.value)}
|
|
||||||
placeholder="Введите описание"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-4 gap-4">
|
|
||||||
<Label className="self-start pt-2 text-right">
|
|
||||||
Атрибуты
|
|
||||||
</Label>
|
|
||||||
<div className="col-span-3">
|
|
||||||
{!attributesLoading && (
|
|
||||||
<CategoryFieldsSelector
|
|
||||||
selectedValues={toCategoryAttributeValues(
|
|
||||||
editAttributeValues
|
|
||||||
)}
|
|
||||||
onValuesChange={values =>
|
|
||||||
setEditAttributeValues(
|
|
||||||
fromCategoryAttributeValues(values)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="outline">Отмена</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button
|
|
||||||
onClick={handleEditConfirm}
|
|
||||||
disabled={!editName.trim()}
|
|
||||||
>
|
|
||||||
Сохранить
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user