This commit is contained in:
Anang Yusman 2025-11-24 00:42:28 +08:00
parent b623304e9d
commit 475987bcd3
2 changed files with 133 additions and 41 deletions

View File

@ -10,83 +10,170 @@ import {
DialogFooter, DialogFooter,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { import {
getGaleryFileData, getGaleryFileData,
updateGalery, updateGalery,
updateUploadGaleryFile, updateUploadGaleryFile,
deleteGaleryFile,
} from "@/service/galery"; } from "@/service/galery";
import { error, success } from "@/config/swal";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
export function DialogUpdateGaleri({ open, onClose, data, onUpdated }: any) { export function DialogUpdateGaleri({ open, onClose, data, onUpdated }: any) {
const [title, setTitle] = useState(data.title || ""); const [title, setTitle] = useState("");
const [description, setDescription] = useState(data.desc || ""); const MySwal = withReactContent(Swal);
const [description, setDescription] = useState("");
const [oldFiles, setOldFiles] = useState<any[]>([]); const [oldFiles, setOldFiles] = useState<any[]>([]);
const [newFiles, setNewFiles] = useState<File[]>([]); const [newFiles, setNewFiles] = useState<File[]>([]);
const [newFilePreviews, setNewFilePreviews] = useState<string[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const fetchOldFiles = async () => { const fetchOldFiles = async (galleryId: any) => {
try { try {
const res = await getGaleryFileData(data.id); const res = await getGaleryFileData(galleryId);
const allImages = res?.data?.data ?? []; const allImages = res?.data?.data ?? [];
const filtered = allImages.filter(
const filteredImages = allImages.filter( (img: any) => img.gallery_id === galleryId
(img: any) => img.gallery_id === data.id
); );
setOldFiles(filtered);
setOldFiles(filteredImages);
} catch (e) { } catch (e) {
console.error("Error fetch files:", e); console.error("Error fetching files:", e);
} }
}; };
useEffect(() => { useEffect(() => {
if (open && data?.id) { if (open && data?.id) {
fetchOldFiles(); setTitle(data.title ?? "");
setTitle(data.title || "");
setDescription(data.description || ""); setDescription(data.description ?? data.desc ?? "");
fetchOldFiles(data.id);
setNewFiles([]);
setNewFilePreviews([]);
} else if (!open) {
} }
}, [open, data]); }, [open, data]);
useEffect(() => {
if (!newFiles || newFiles.length === 0) {
setNewFilePreviews([]);
return;
}
const urls = newFiles.map((f) => URL.createObjectURL(f));
setNewFilePreviews(urls);
return () => {
urls.forEach((u) => URL.revokeObjectURL(u));
};
}, [newFiles]);
const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => { const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const uploaded = Array.from(e.target.files || []) as File[]; const uploaded = Array.from(e.target.files || []) as File[];
if (uploaded.length === 0) return;
setNewFiles((prev) => [...prev, ...uploaded]); setNewFiles((prev) => [...prev, ...uploaded]);
}; };
const removeOldFile = (id: number) => { async function doDelete(id: any) {
setOldFiles((prev) => prev.filter((f) => f.id !== id)); const resDelete = await deleteGaleryFile(id);
if (resDelete?.error) {
error(resDelete.message);
return false;
}
success("Berhasil Hapus");
return;
}
const removeOldFile = (id: any) => {
MySwal.fire({
title: "Hapus Data",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
}; };
const removeNewFile = (index: number) => { const removeNewFile = (index: number) => {
setNewFiles((prev) => prev.filter((_, i) => i !== index)); setNewFiles((prev) => prev.filter((_, i) => i !== index));
setNewFilePreviews((prev) => prev.filter((_, i) => i !== index));
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
try { try {
if (!data?.id) {
alert("Gallery ID tidak tersedia.");
return;
}
setLoading(true); setLoading(true);
const formMain = new FormData(); const formMain = new FormData();
formMain.append("title", title); formMain.append("title", title);
formMain.append("description", description); formMain.append("description", description);
formMain.append("old_files", JSON.stringify(oldFiles.map((f) => f.id))); formMain.append("old_files", JSON.stringify(oldFiles.map((f) => f.id)));
await updateGalery(data.id, formMain); const updateRes = await updateGalery(data.id, formMain);
console.log("updateGalery response:", updateRes);
if (updateRes?.error) {
alert(updateRes.message || "Gagal update galeri");
setLoading(false);
return;
}
const galleryId = data.id;
const failedUploads: any[] = [];
for (const file of newFiles) { for (const file of newFiles) {
const formFile = new FormData(); try {
formFile.append("gallery_id", String(data.id)); const formFile = new FormData();
formFile.append("title", title); formFile.append("gallery_id", String(galleryId));
formFile.append("file", file); formFile.append("title", title);
formFile.append("is_active", "true");
formFile.append("file", file);
await updateUploadGaleryFile(data.id, formFile); const uploadRes = await updateUploadGaleryFile(data.id, formFile);
console.log("uploadRes:", uploadRes);
if (uploadRes?.error) {
failedUploads.push({
name: file.name,
error: uploadRes.message,
});
}
} catch (err: any) {
failedUploads.push({ name: file.name, error: err });
}
} }
setLoading(false); setLoading(false);
if (failedUploads.length > 0) {
let msg = failedUploads.map((f) => `${f.name}: ${f.error}`).join("\n");
alert("Ada file gagal diupload:\n" + msg);
return;
}
onClose(); onClose();
if (onUpdated) onUpdated(); onUpdated && onUpdated();
} catch (err) { } catch (err: any) {
setLoading(false); setLoading(false);
console.error("Error update gallery:", err); console.error("Update error:", err);
alert("Gagal menyimpan perubahan galeri");
if (err?.response?.data) {
alert("Server error: " + JSON.stringify(err.response.data));
} else {
alert("Gagal menyimpan galeri");
}
} }
}; };
@ -97,8 +184,7 @@ export function DialogUpdateGaleri({ open, onClose, data, onUpdated }: any) {
<DialogTitle>Edit Galeri</DialogTitle> <DialogTitle>Edit Galeri</DialogTitle>
</DialogHeader> </DialogHeader>
{/* FORM */} <div className="space-y-4 px-4 pb-4">
<div className="space-y-4">
{/* TITLE */} {/* TITLE */}
<div> <div>
<label className="font-medium text-sm">Judul Galeri *</label> <label className="font-medium text-sm">Judul Galeri *</label>
@ -139,14 +225,13 @@ export function DialogUpdateGaleri({ open, onClose, data, onUpdated }: any) {
</div> </div>
</label> </label>
{/* OLD FILES */}
{oldFiles.length > 0 && ( {oldFiles.length > 0 && (
<div className="flex gap-3 mt-3 flex-wrap"> <div className="flex gap-3 mt-3 flex-wrap">
{oldFiles.map((f) => ( {oldFiles.map((f) => (
<div key={f.id} className="relative w-20 h-20"> <div key={f.id} className="relative w-20 h-20">
<Image <Image
src={f.image_url} src={f.image_url}
alt={f.title} alt={f.title || "image"}
fill fill
className="object-cover rounded-lg" className="object-cover rounded-lg"
/> />
@ -161,16 +246,17 @@ export function DialogUpdateGaleri({ open, onClose, data, onUpdated }: any) {
</div> </div>
)} )}
{/* NEW FILES */} {newFilePreviews.length > 0 && (
{newFiles.length > 0 && (
<div className="flex gap-3 mt-3 flex-wrap"> <div className="flex gap-3 mt-3 flex-wrap">
{newFiles.map((file, idx) => ( {newFilePreviews.map((src, idx) => (
<div key={idx} className="relative w-20 h-20"> <div
<Image key={idx}
src={URL.createObjectURL(file)} className="relative w-20 h-20 overflow-hidden rounded-lg border"
alt={file.name} >
fill <img
className="object-cover rounded-lg" src={src}
alt={`new-${idx}`}
className="object-cover w-full h-full"
/> />
<button <button
onClick={() => removeNewFile(idx)} onClick={() => removeNewFile(idx)}
@ -185,8 +271,7 @@ export function DialogUpdateGaleri({ open, onClose, data, onUpdated }: any) {
</div> </div>
</div> </div>
{/* FOOTER */} <DialogFooter className="mt-6 flex gap-2 px-4 pb-4">
<DialogFooter className="mt-6 flex gap-2">
<button <button
onClick={onClose} onClick={onClose}
className="px-6 py-2 rounded-lg bg-gray-200 text-gray-700" className="px-6 py-2 rounded-lg bg-gray-200 text-gray-700"

View File

@ -63,3 +63,10 @@ export async function deleteGalery(id: string) {
}; };
return await httpDeleteInterceptor(`galleries/${id}`, headers); return await httpDeleteInterceptor(`galleries/${id}`, headers);
} }
export async function deleteGaleryFile(id: any) {
const headers = {
"content-type": "application/json",
};
return await httpDeleteInterceptor(`gallery-files/${id}`, headers);
}