core/panel/modals/CollectionEditModal.tsx

217 lines
7.0 KiB
TypeScript
Raw Normal View History

2025-06-30 19:19:46 +00:00
import { Component, createEffect, createSignal } from 'solid-js'
import formStyles from '../styles/Form.module.css'
import styles from '../styles/Modal.module.css'
import Button from '../ui/Button'
2025-07-03 09:15:10 +00:00
import HTMLEditor from '../ui/HTMLEditor'
2025-06-30 19:19:46 +00:00
import Modal from '../ui/Modal'
interface Collection {
id: number
slug: string
title: string
desc?: string
pic?: string
amount?: number
published_at?: number
created_at: number
created_by: {
id: number
name: string
email: string
}
}
interface CollectionEditModalProps {
isOpen: boolean
collection: Collection | null // null для создания новой
onClose: () => void
onSave: (collection: Partial<Collection>) => void
}
/**
* Модальное окно для создания и редактирования коллекций
*/
const CollectionEditModal: Component<CollectionEditModalProps> = (props) => {
const [formData, setFormData] = createSignal({
slug: '',
title: '',
desc: '',
pic: ''
})
const [errors, setErrors] = createSignal<Record<string, string>>({})
// Синхронизация с props.collection
createEffect(() => {
if (props.isOpen) {
if (props.collection) {
// Редактирование существующей коллекции
setFormData({
slug: props.collection.slug,
title: props.collection.title,
desc: props.collection.desc || '',
pic: props.collection.pic || ''
})
} else {
// Создание новой коллекции
setFormData({
slug: '',
title: '',
desc: '',
pic: ''
})
}
setErrors({})
}
})
const validateForm = () => {
const newErrors: Record<string, string> = {}
const data = formData()
// Валидация slug
if (!data.slug.trim()) {
newErrors.slug = 'Slug обязателен'
} else if (!/^[a-z0-9-_]+$/.test(data.slug)) {
newErrors.slug = 'Slug может содержать только латинские буквы, цифры, дефисы и подчеркивания'
}
// Валидация названия
if (!data.title.trim()) {
newErrors.title = 'Название обязательно'
}
// Валидация URL картинки (если указан)
if (data.pic.trim() && !/^https?:\/\/.+/.test(data.pic)) {
newErrors.pic = 'Некорректный URL картинки'
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
const updateField = (field: string, value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }))
// Очищаем ошибку для поля при изменении
setErrors((prev) => ({ ...prev, [field]: '' }))
}
const handleSave = () => {
if (!validateForm()) {
return
}
const collectionData = { ...formData() }
props.onSave(collectionData)
}
const isCreating = () => props.collection === null
const modalTitle = () =>
isCreating() ? 'Создание новой коллекции' : `Редактирование коллекции: ${props.collection?.title || ''}`
return (
<Modal isOpen={props.isOpen} onClose={props.onClose} title={modalTitle()} size="medium">
2025-07-02 19:30:21 +00:00
<div class={styles.modalContent}>
2025-06-30 19:19:46 +00:00
<div class={formStyles.form}>
2025-07-02 19:30:21 +00:00
<div class={formStyles.fieldGroup}>
2025-06-30 19:19:46 +00:00
<label class={formStyles.label}>
2025-07-02 19:30:21 +00:00
<span class={formStyles.labelText}>
<span class={formStyles.labelIcon}>📝</span>
Название
<span class={formStyles.required}>*</span>
</span>
2025-06-30 19:19:46 +00:00
</label>
<input
type="text"
2025-07-02 19:30:21 +00:00
class={`${formStyles.input} ${errors().title ? formStyles.error : ''}`}
value={formData().title}
onInput={(e) => updateField('title', e.target.value)}
placeholder="Введите название коллекции"
2025-06-30 19:19:46 +00:00
required
/>
2025-07-02 19:30:21 +00:00
{errors().title && (
<div class={formStyles.fieldError}>
<span class={formStyles.errorIcon}></span>
{errors().title}
</div>
)}
2025-06-30 19:19:46 +00:00
</div>
2025-07-02 19:30:21 +00:00
<div class={formStyles.fieldGroup}>
2025-06-30 19:19:46 +00:00
<label class={formStyles.label}>
2025-07-02 19:30:21 +00:00
<span class={formStyles.labelText}>
<span class={formStyles.labelIcon}>🔗</span>
Slug
<span class={formStyles.required}>*</span>
</span>
2025-06-30 19:19:46 +00:00
</label>
<input
type="text"
2025-07-02 19:30:21 +00:00
class={`${formStyles.input} ${errors().slug ? formStyles.error : ''}`}
value={formData().slug}
onInput={(e) => updateField('slug', e.target.value)}
placeholder="collection-slug"
2025-06-30 19:19:46 +00:00
required
/>
2025-07-02 19:30:21 +00:00
{errors().slug && (
<div class={formStyles.fieldError}>
<span class={formStyles.errorIcon}></span>
{errors().slug}
</div>
)}
2025-06-30 19:19:46 +00:00
</div>
2025-07-02 19:30:21 +00:00
<div class={formStyles.fieldGroup}>
<label class={formStyles.label}>
<span class={formStyles.labelText}>
<span class={formStyles.labelIcon}>📄</span>
Описание
</span>
</label>
2025-07-03 09:15:10 +00:00
<HTMLEditor
2025-06-30 19:19:46 +00:00
value={formData().desc}
2025-07-03 09:15:10 +00:00
onInput={(value) => updateField('desc', value)}
2025-06-30 19:19:46 +00:00
/>
</div>
2025-07-02 19:30:21 +00:00
<div class={formStyles.fieldGroup}>
<label class={formStyles.label}>
<span class={formStyles.labelText}>
<span class={formStyles.labelIcon}>🖼</span>
URL картинки
</span>
</label>
2025-06-30 19:19:46 +00:00
<input
2025-07-02 19:30:21 +00:00
type="url"
class={`${formStyles.input} ${errors().pic ? formStyles.error : ''}`}
2025-06-30 19:19:46 +00:00
value={formData().pic}
onInput={(e) => updateField('pic', e.target.value)}
placeholder="https://example.com/image.jpg"
/>
2025-07-02 19:30:21 +00:00
{errors().pic && (
<div class={formStyles.fieldError}>
<span class={formStyles.errorIcon}></span>
{errors().pic}
</div>
)}
<div class={formStyles.hint}>
<span class={formStyles.hintIcon}>💡</span>
Необязательно. URL изображения для обложки коллекции.
</div>
2025-06-30 19:19:46 +00:00
</div>
2025-07-02 19:30:21 +00:00
<div class={styles.modalActions}>
2025-06-30 19:19:46 +00:00
<Button variant="secondary" onClick={props.onClose}>
Отмена
</Button>
<Button variant="primary" onClick={handleSave}>
{isCreating() ? 'Создать' : 'Сохранить'}
</Button>
</div>
</div>
</div>
</Modal>
)
}
export default CollectionEditModal