feat: add playlist
This commit is contained in:
@@ -20,6 +20,14 @@ import { MediaPreview } from "@/components/MediaPreview";
|
|||||||
import { ImagePicker } from "@/components/ImagePicker";
|
import { ImagePicker } from "@/components/ImagePicker";
|
||||||
import { pickUrl, SOUND_KEYS, IMAGE_KEYS } from "@/lib/media";
|
import { pickUrl, SOUND_KEYS, IMAGE_KEYS } from "@/lib/media";
|
||||||
|
|
||||||
|
interface Playlist {
|
||||||
|
id: number;
|
||||||
|
name?: string;
|
||||||
|
// Some endpoints nest the playlist's sub-categories; key casing varies.
|
||||||
|
subcategories?: Playlist[];
|
||||||
|
subCategories?: Playlist[];
|
||||||
|
}
|
||||||
|
|
||||||
interface Track {
|
interface Track {
|
||||||
id: number;
|
id: number;
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -28,11 +36,12 @@ interface Track {
|
|||||||
type?: string;
|
type?: string;
|
||||||
playlist_id?: number;
|
playlist_id?: number;
|
||||||
image_id?: number | null;
|
image_id?: number | null;
|
||||||
}
|
// A track belongs to many playlists; the /music row embeds them as an array.
|
||||||
|
playlists?: Playlist[];
|
||||||
interface Playlist {
|
// Sub-categories may also be embedded directly on the track (key casing varies).
|
||||||
id: number;
|
subcategories?: Playlist[];
|
||||||
name?: string;
|
subCategories?: Playlist[];
|
||||||
|
sub_categories?: Playlist[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MusicTracksPage() {
|
export default function MusicTracksPage() {
|
||||||
@@ -56,6 +65,26 @@ export default function MusicTracksPage() {
|
|||||||
const [deleting, setDeleting] = useState<Track | null>(null);
|
const [deleting, setDeleting] = useState<Track | null>(null);
|
||||||
const [removing, setRemoving] = useState(false);
|
const [removing, setRemoving] = useState(false);
|
||||||
|
|
||||||
|
// A track belongs to many playlists — list their names.
|
||||||
|
function playlistName(r: Track): string {
|
||||||
|
const names = (r.playlists ?? []).map((p) => p.name).filter(Boolean);
|
||||||
|
return names.length ? (names as string[]).join("، ") : "—";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub-categories may sit on the track directly or come through its playlists;
|
||||||
|
// key casing varies between endpoints, so gather from every shape.
|
||||||
|
function subCategoryNames(r: Track): string {
|
||||||
|
const subs: Playlist[] = [
|
||||||
|
...(r.subcategories ?? r.subCategories ?? r.sub_categories ?? []),
|
||||||
|
...(r.playlists ?? []).flatMap(
|
||||||
|
(p) => p.subcategories ?? p.subCategories ?? [],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const names = subs.map((s) => s.name).filter(Boolean) as string[];
|
||||||
|
const unique = Array.from(new Set(names));
|
||||||
|
return unique.length ? unique.join("، ") : "—";
|
||||||
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
setTitle("");
|
setTitle("");
|
||||||
setArtist("");
|
setArtist("");
|
||||||
@@ -163,6 +192,16 @@ export default function MusicTracksPage() {
|
|||||||
},
|
},
|
||||||
{ key: "title", header: "عنوان" },
|
{ key: "title", header: "عنوان" },
|
||||||
{ key: "artist", header: "هنرمند", render: (r) => r.artist ?? "—" },
|
{ key: "artist", header: "هنرمند", render: (r) => r.artist ?? "—" },
|
||||||
|
{
|
||||||
|
key: "playlist",
|
||||||
|
header: "پلیلیست",
|
||||||
|
render: (r) => playlistName(r),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "subcategory",
|
||||||
|
header: "زیردسته",
|
||||||
|
render: (r) => subCategoryNames(r),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "duration",
|
key: "duration",
|
||||||
header: "مدت زمان",
|
header: "مدت زمان",
|
||||||
|
|||||||
Reference in New Issue
Block a user