'use client' import CreateEditor from '../../_com/createEditor'; import stateDashboardMusik from '../../_state/desa/musik'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; import { Box, Button, Card, Group, Image, Paper, Stack, Text, TextInput, Title, Loader, ActionIcon, NumberInput } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { useShallowEffect } from '@mantine/hooks'; import { IconArrowBack, IconPhoto, IconUpload, IconX, IconMusic } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; export default function CreateMusik() { const musikState = useProxy(stateDashboardMusik); const [previewCover, setPreviewCover] = useState(null); const [coverFile, setCoverFile] = useState(null); const [previewAudio, setPreviewAudio] = useState(null); const [audioFile, setAudioFile] = useState(null); const [isExtractingDuration, setIsExtractingDuration] = useState(false); const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); // Fungsi untuk mendapatkan durasi dari file audio const getAudioDuration = (file: File): Promise => { return new Promise((resolve) => { const audio = new Audio(); const url = URL.createObjectURL(file); audio.addEventListener('loadedmetadata', () => { const duration = audio.duration; const minutes = Math.floor(duration / 60); const seconds = Math.floor(duration % 60); const formatted = `${minutes}:${seconds.toString().padStart(2, '0')}`; URL.revokeObjectURL(url); resolve(formatted); }); audio.addEventListener('error', () => { URL.revokeObjectURL(url); resolve('0:00'); }); audio.src = url; }); }; const isFormValid = () => { return ( musikState.musik.create.form.judul?.trim() !== '' && musikState.musik.create.form.artis?.trim() !== '' && musikState.musik.create.form.durasi?.trim() !== '' && audioFile !== null && coverFile !== null ); }; useShallowEffect(() => { return () => { musikState.musik.create.resetForm(); }; }, []); const resetForm = () => { musikState.musik.create.form = { judul: '', artis: '', deskripsi: '', durasi: '', audioFileId: '', coverImageId: '', genre: '', tahunRilis: undefined, }; setPreviewCover(null); setCoverFile(null); setPreviewAudio(null); setAudioFile(null); }; const handleSubmit = async () => { if (!musikState.musik.create.form.judul?.trim()) { toast.error('Judul wajib diisi'); return; } if (!musikState.musik.create.form.artis?.trim()) { toast.error('Artis wajib diisi'); return; } if (!musikState.musik.create.form.durasi?.trim()) { toast.error('Durasi wajib diisi'); return; } if (!coverFile) { toast.error('Cover image wajib dipilih'); return; } if (!audioFile) { toast.error('File audio wajib dipilih'); return; } try { setIsSubmitting(true); // Upload cover image console.log('Uploading cover image:', coverFile.name); const coverRes = await ApiFetch.api.fileStorage.create.post({ file: coverFile, name: coverFile.name, }); console.log('Cover upload response:', coverRes); const coverUploaded = coverRes.data?.data; if (!coverUploaded?.id) { console.error('Cover upload failed:', coverRes); toast.error('Gagal mengunggah cover, silakan coba lagi'); return; } musikState.musik.create.form.coverImageId = coverUploaded.id; // Upload audio file console.log('Uploading audio file:', audioFile.name); const audioRes = await ApiFetch.api.fileStorage.create.post({ file: audioFile, name: audioFile.name, }); console.log('Audio upload response:', audioRes); const audioUploaded = audioRes.data?.data; if (!audioUploaded?.id) { console.error('Audio upload failed:', audioRes); toast.error('Gagal mengunggah audio, silakan coba lagi'); return; } musikState.musik.create.form.audioFileId = audioUploaded.id; // Create musik entry console.log('Creating musik entry with form:', musikState.musik.create.form); await musikState.musik.create.create(); resetForm(); router.push('/admin/musik'); } catch (error) { console.error('Error creating musik:', { error, message: error instanceof Error ? error.message : 'Unknown error', stack: error instanceof Error ? error.stack : undefined, }); toast.error('Terjadi kesalahan saat membuat musik'); } finally { setIsSubmitting(false); } }; return ( {/* Header dengan tombol kembali */} Tambah Musik (musikState.musik.create.form.judul = e.target.value)} required /> (musikState.musik.create.form.artis = e.target.value)} required /> Deskripsi { musikState.musik.create.form.deskripsi = htmlContent; }} /> (musikState.musik.create.form.durasi = e.target.value)} required style={{ flex: 1 }} /> (musikState.musik.create.form.genre = e.target.value)} style={{ flex: 1 }} /> (musikState.musik.create.form.tahunRilis = val as number | undefined)} min={1900} max={new Date().getFullYear() + 1} /> {/* Cover Image */} Cover Image { const selectedFile = files[0]; if (selectedFile) { setCoverFile(selectedFile); setPreviewCover(URL.createObjectURL(selectedFile)); } }} onReject={() => toast.error('File tidak valid, gunakan format gambar')} maxSize={5 * 1024 ** 2} accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }} radius="md" p="xl" > Seret gambar atau klik untuk memilih file (maks 5MB) {previewCover && ( Preview Cover { setPreviewCover(null); setCoverFile(null); }} style={{ boxShadow: '0 2px 6px rgba(0,0,0,0.15)', }} > )} {/* Audio File */} File Audio { const selectedFile = files[0]; if (selectedFile) { setAudioFile(selectedFile); setPreviewAudio(selectedFile.name); // Extract durasi otomatis dari audio setIsExtractingDuration(true); try { const duration = await getAudioDuration(selectedFile); musikState.musik.create.form.durasi = duration; toast.success(`Durasi audio terdeteksi: ${duration}`); } catch (error) { console.error('Error extracting audio duration:', error); toast.error('Gagal mendeteksi durasi audio, silakan isi manual'); } finally { setIsExtractingDuration(false); } } }} onReject={() => toast.error('File tidak valid, gunakan format audio (MP3, WAV, OGG)')} maxSize={50 * 1024 ** 2} accept={{ 'audio/*': ['.mp3', '.wav', '.ogg', '.m4a'] }} radius="md" p="xl" > Seret file audio atau klik untuk memilih file (maks 50MB) {previewAudio && ( {previewAudio} {isExtractingDuration && ( Mendeteksi durasi... )} { setPreviewAudio(null); setAudioFile(null); }} > )} ); }