diff --git a/app/admin/(protected)/notes/page.js b/app/admin/(protected)/notes/page.js new file mode 100644 index 0000000..7a44c22 --- /dev/null +++ b/app/admin/(protected)/notes/page.js @@ -0,0 +1,139 @@ +'use client'; +import { useState, useEffect } from 'react'; +import { Pin, PinOff, Trash2, Plus, Save, Eye, EyeOff, Loader2, Check } from 'lucide-react'; + +const EMPTY = { title: '', content: '', author: 'Редактор', is_pinned: false }; + +export default function AdminNotesPage() { + const [notes, setNotes] = useState([]); + const [loading, setLoading] = useState(true); + const [editing, setEditing] = useState(null); + const [form, setForm] = useState(EMPTY); + const [saving, setSaving] = useState(false); + const [saved, setSaved] = useState(false); + const [err, setErr] = useState(''); + + async function load() { + setLoading(true); + try { + const r = await fetch('/admin/api/notes'); + setNotes(await r.json()); + } catch (e) { setErr(e.message); } + finally { setLoading(false); } + } + + useEffect(() => { load(); }, []); + + function startNew() { setForm(EMPTY); setEditing('new'); setErr(''); setSaved(false); } + function startEdit(n) { setForm({ title: n.title || '', content: n.content, author: n.author, is_pinned: n.is_pinned }); setEditing(n); setErr(''); setSaved(false); } + + async function save() { + if (!form.content.trim()) { setErr('Текст обязателен'); return; } + setSaving(true); setErr(''); + try { + const body = { ...form, title: form.title.trim() || null }; + const method = editing === 'new' ? 'POST' : 'PATCH'; + const url = editing === 'new' ? '/admin/api/notes' : `/admin/api/notes/${editing.id}`; + const r = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); + if (!r.ok) throw new Error(await r.text()); + setSaved(true); + setTimeout(() => { setEditing(null); setSaved(false); }, 800); + await load(); + } catch (e) { setErr(e.message); } + finally { setSaving(false); } + } + + async function toggle(note, field) { + await fetch(`/admin/api/notes/${note.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ [field]: !note[field] }) }); + await load(); + } + + async function del(note) { + if (!confirm(`Удалить: «${(note.title || note.content).slice(0, 50)}»?`)) return; + await fetch(`/admin/api/notes/${note.id}`, { method: 'DELETE' }); + await load(); + } + + const fmt = d => new Date(d).toLocaleDateString('ru-RU', { day: 'numeric', month: 'short', year: 'numeric' }); + + return ( +
+
+
+

Заметки редактора

+

Отображаются на главной странице и на /notes

+
+ +
+ + {editing && ( +
+
+ {editing === 'new' ? 'Новая заметка' : 'Редактировать'} +
+ setForm(f => ({ ...f, title: e.target.value }))} /> +