"use client"; import { useState } from "react"; import { apiFetch, ApiError, toFormData } from "@/lib/api"; import { useList } from "@/lib/useResource"; import { useToast } from "@/components/toast"; import { DataTable, type Column } from "@/components/DataTable"; import { Button, ConfirmDialog, Field, Input, Select, Modal, PageHeader, } from "@/components/ui"; import { EditIcon, PlusIcon, TrashIcon } from "@/components/icons"; import { formatMinutes, fileNameToTitle } from "@/lib/utils"; import { MediaPreview } from "@/components/MediaPreview"; import { ImagePicker } from "@/components/ImagePicker"; import { pickUrl, SOUND_KEYS, IMAGE_KEYS } from "@/lib/media"; interface Track { id: number; title?: string; artist?: string; duration?: number | null; type?: string; playlist_id?: number; image_id?: number | null; } interface Playlist { id: number; name?: string; } export default function MusicTracksPage() { const { data, loading, error, reload } = useList("/music"); const { data: playlists } = useList("/music-playlists"); const toast = useToast(); const [editing, setEditing] = useState(null); const [open, setOpen] = useState(false); const [saving, setSaving] = useState(false); const [title, setTitle] = useState(""); const [artist, setArtist] = useState(""); const [type, setType] = useState("public"); const [duration, setDuration] = useState(""); const [playlistId, setPlaylistId] = useState(""); const [imageId, setImageId] = useState(null); const [imageFile, setImageFile] = useState(null); const [file, setFile] = useState(null); const [deleting, setDeleting] = useState(null); const [removing, setRemoving] = useState(false); function resetForm() { setTitle(""); setArtist(""); setType("public"); setDuration(""); setPlaylistId(""); setImageId(null); setImageFile(null); setFile(null); } function openCreate() { setEditing(null); resetForm(); setOpen(true); } function openEdit(row: Track) { setEditing(row); setTitle(row.title ?? ""); setArtist(row.artist ?? ""); setType(row.type ?? "public"); setDuration(row.duration != null ? String(row.duration) : ""); setPlaylistId(row.playlist_id != null ? String(row.playlist_id) : ""); setImageId(row.image_id ?? null); setImageFile(null); setFile(null); setOpen(true); } async function save(e: React.FormEvent) { e.preventDefault(); setSaving(true); try { if (editing) { // The PUT /music/:id route can carry files, so we POST multipart with // Laravel method spoofing (_method=PUT). const body = toFormData({ _method: "PUT", title, artist, type, duration, image_id: imageId, image: imageFile, file, }); await apiFetch(`/music/${editing.id}`, { method: "POST", body }); toast.success("آهنگ ویرایش شد."); } else { await apiFetch("/music", { method: "POST", body: toFormData({ title, artist, type, duration, playlist_id: playlistId, image_id: imageId, image: imageFile, file, }), }); toast.success("آهنگ افزوده شد."); } setOpen(false); reload(); } catch (err) { toast.error(err instanceof ApiError ? err.message : "خطا در ذخیره‌سازی"); } finally { setSaving(false); } } async function confirmDelete() { if (!deleting) return; setRemoving(true); try { await apiFetch(`/music/${deleting.id}`, { method: "DELETE" }); toast.success("آهنگ حذف شد."); setDeleting(null); reload(); } catch (err) { toast.error(err instanceof ApiError ? err.message : "خطا در حذف"); } finally { setRemoving(false); } } const columns: Column[] = [ { key: "play", header: "پخش", className: "w-20", render: (r) => ( ), }, { key: "thumbnail", header: "تصویر", className: "w-20", render: (r) => ( ), }, { key: "title", header: "عنوان" }, { key: "artist", header: "هنرمند", render: (r) => r.artist ?? "—" }, { key: "duration", header: "مدت زمان", render: (r) => formatMinutes(r.duration), }, { key: "type", header: "نوع", render: (r) => r.type === "private" ? "خصوصی" : r.type === "public" ? "عمومی" : (r.type ?? "—"), }, ]; return (
} onClick={openCreate}> آهنگ جدید } /> } onClick={openCreate}> آهنگ جدید } actions={(row) => ( <> )} /> setOpen(false)} title={editing ? "ویرایش آهنگ" : "آهنگ جدید"} footer={ <> } >
setTitle(e.target.value)} placeholder="مثلاً آرامش شبانه" required autoFocus /> setArtist(e.target.value)} placeholder="نام هنرمند" /> setDuration(e.target.value)} dir="ltr" /> {!editing && ( )} { const f = e.target.files?.[0] ?? null; setFile(f); // Fill the title from the file name if still empty. if (f && !title.trim()) setTitle(fileNameToTitle(f.name)); }} required={!editing} />
setDeleting(null)} />
); }