update:bugs dialog

This commit is contained in:
Anang Yusman 2026-01-28 01:39:40 +08:00
parent e1f99bd4c4
commit 9bc0b301cb
5 changed files with 114 additions and 140 deletions

View File

@ -115,7 +115,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
const res = await commentGalery(id, message || undefined); const res = await commentGalery(id, message || undefined);
if (res?.error) { if (res?.error) {
error(res.message || "Gagal komentar promotion"); error(res.message || "Gagal komentar galeri");
close(); close();
return; return;
} }
@ -161,11 +161,24 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
return ( return (
<> <>
<Dialog open={open} onOpenChange={onClose}> <div
<DialogContent className=" rounded-2xl p-0 overflow-hidden"> className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
{/* Header */} onClick={onClose}
<div className="bg-[#1F6779] text-white px-6 py-4"> >
<DialogTitle className="text-white">Detail Galeri</DialogTitle> <div
className="bg-white rounded-2xl shadow-2xl max-w-xl w-full overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* HEADER */}
<div className="bg-gradient-to-br from-[#1F6779] to-[#0F6C75] text-white px-6 py-5 relative">
<button
onClick={onClose}
className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
>
</button>
<h2 className="text-lg font-semibold">Detail Galeri</h2>
<div className="flex items-center gap-2 mt-3"> <div className="flex items-center gap-2 mt-3">
<span <span
className={`text-xs font-medium px-3 py-1 rounded-full ${ className={`text-xs font-medium px-3 py-1 rounded-full ${
@ -199,7 +212,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
<div className=" py-3"> <div className=" py-3">
{/* Images List */} {/* Images List */}
<div className="mx-2"> <div className="mx-4">
<h2 className="text-2xl font-semibold text-black"> <h2 className="text-2xl font-semibold text-black">
{data.title} {data.title}
</h2> </h2>
@ -240,7 +253,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
{/* Deskripsi */} {/* Deskripsi */}
{/* Tanggal Upload */} {/* Tanggal Upload */}
<div className="mx-2"> <div className="mx-4">
<p className="font-medium text-sm text-gray-700"> <p className="font-medium text-sm text-gray-700">
Tanggal Upload Tanggal Upload
</p> </p>
@ -250,7 +263,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
</div> </div>
{/* Timeline */} {/* Timeline */}
<div className="mx-2"> <div className="mx-4">
<h4 className="text-sm font-semibold text-gray-700 mb-3"> <h4 className="text-sm font-semibold text-gray-700 mb-3">
Status Timeline Status Timeline
</h4> </h4>
@ -374,17 +387,8 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
</div> </div>
)} )}
</div> </div>
</div>
{/* <DialogFooter className="px-6 pb-6"> </div>
<button
onClick={onClose}
className="bg-gray-300 text-gray-700 px-6 py-2 rounded-lg"
>
Tutup
</button>
</DialogFooter> */}
</DialogContent>
</Dialog>
{openApproverHistory && ( {openApproverHistory && (
<div <div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4" className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"

View File

@ -29,6 +29,7 @@ import { error, loading, success } from "@/config/swal";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import promo from "../landing-page/promo"; import promo from "../landing-page/promo";
import Image from "next/image";
type PromoDetailDialogProps = { type PromoDetailDialogProps = {
promoId: number | null; promoId: number | null;
@ -220,16 +221,24 @@ export default function PromoDetailDialog({
return ( return (
<> <>
<Dialog open={open} onOpenChange={onOpenChange}> <div
<DialogContent className="p-0 max-w-lg overflow-hidden"> className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
onClick={() => onOpenChange(false)}
>
<div
className="bg-white rounded-2xl shadow-2xl max-w-xl w-full overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* HEADER */} {/* HEADER */}
<div className="bg-gradient-to-r from-[#0f6c75] to-[#145f66] text-white px-6 py-5 relative"> <div className="bg-gradient-to-br from-[#1F6779] to-[#0F6C75] text-white px-6 py-5 relative">
<DialogHeader> <button
<DialogTitle className="text-white text-xl font-semibold"> onClick={() => onOpenChange(false)}
Detail Promoaa className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
</DialogTitle> >
</DialogHeader>
</button>
<h2 className="text-lg font-semibold">Detail Promo</h2>
<div className="flex items-center gap-2 mt-3"> <div className="flex items-center gap-2 mt-3">
<span <span
className={`text-xs font-medium px-3 py-1 rounded-full ${ className={`text-xs font-medium px-3 py-1 rounded-full ${
@ -279,8 +288,15 @@ export default function PromoDetailDialog({
<div className="space-y-4 text-sm"> <div className="space-y-4 text-sm">
<div> <div>
<p className="text-gray-600">Ukuran File</p> <div className="w-24 h-24 rounded-lg overflow-hidden border">
<p className="font-medium">{promo.fileSize}</p> <Image
src={promo.thumbnail_url}
alt="Profile"
width={96}
height={96}
className="object-cover"
/>
</div>
</div> </div>
<div> <div>
@ -418,104 +434,6 @@ export default function PromoDetailDialog({
)} )}
</div> </div>
)} )}
{openApproverHistory && (
<div
className=" flex items-center justify-center bg-black/50 p-4"
onClick={() => setOpenApproverHistory(false)}
>
<div
className="bg-white rounded-2xl shadow-2xl max-w-3xl w-full overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* HEADER */}
<div className="bg-gradient-to-br from-[#1F6779] to-[#0F6C75] text-white px-6 py-5 relative">
<button
onClick={() => setOpenApproverHistory(false)}
className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
>
</button>
<h2 className="text-lg font-semibold">Approver History</h2>
<div className="flex items-center gap-2 mt-3">
<span className="bg-yellow-100 text-yellow-800 text-xs font-medium px-3 py-1 rounded-full">
Menunggu
</span>
<span className="bg-white text-[#0F6C75] text-xs font-medium px-3 py-1 rounded-full">
Banner
</span>
<span className="bg-white/20 text-white text-xs px-2 py-[2px] rounded-full">
1
</span>
</div>
</div>
{/* BODY */}
<div className="p-6 grid grid-cols-[1fr_auto_1fr] gap-6 items-start">
{/* LEFT TIMELINE */}
<div className="relative space-y-6">
{/* Upload */}
<div className="flex flex-col items-center">
<span className="bg-[#C7DDE4] text-[#0F6C75] text-xs px-4 py-1 rounded-full">
Upload
</span>
<div className="w-px h-6 bg-gray-300" />
</div>
{/* Diterima */}
<div className="relative bg-[#1F6779] text-white rounded-xl p-4">
<h4 className="font-semibold text-sm mb-2">Diterima</h4>
<span className="inline-block bg-[#E3EFF4] text-[#0F6C75] text-xs px-3 py-1 rounded-full">
Direview oleh: approver-jaecoo1
</span>
</div>
<div className="w-px h-6 bg-gray-300 mx-auto" />
{/* Pending */}
<div className="relative bg-[#B36A00] text-white rounded-xl p-4">
<h4 className="font-semibold text-sm mb-2">Pending</h4>
<span className="inline-block bg-[#FFF6CC] text-[#7A4A00] text-xs px-3 py-1 rounded-full">
Direview oleh: approver-jaecoo1
</span>
</div>
</div>
{/* ARROW */}
<div className="flex flex-col gap-20 text-gray-500 font-bold">
<span>&gt;</span>
<span>&gt;</span>
</div>
{/* RIGHT NOTES */}
<div className="space-y-14">
<div>
<div className="bg-[#C7DDE4] text-sm px-4 py-2 rounded-lg">
Catatan:
</div>
</div>
<div>
<div className="bg-[#FFF9C4] text-sm px-4 py-2 rounded-lg">
Catatan:
</div>
</div>
</div>
</div>
{/* FOOTER */}
<div className="border-t bg-[#F2F7FA] text-center py-3">
<button
onClick={() => setOpenApproverHistory(false)}
className="text-[#0F6C75] font-medium hover:underline"
>
Tutup
</button>
</div>
</div>
</div>
)}
{/* FOOTER {/* FOOTER
<div className="bg-[#E3EFF4] text-center py-3"> <div className="bg-[#E3EFF4] text-center py-3">
<button <button
@ -525,8 +443,8 @@ export default function PromoDetailDialog({
Tutup Tutup
</button> </button>
</div> */} </div> */}
</DialogContent> </div>
</Dialog> </div>
{openApproverHistory && ( {openApproverHistory && (
<div <div
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4" className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"

View File

@ -29,7 +29,10 @@ export default function AddProductForm() {
>([{ id: 1, name: "", file: null }]); >([{ id: 1, name: "", file: null }]);
const [selectedColor, setSelectedColor] = useState<string | null>(null); const [selectedColor, setSelectedColor] = useState<string | null>(null);
const [specs, setSpecs] = useState([{ id: 1 }]); const [specs, setSpecs] = useState<
{ id: number; title: string; file: File | null }[]
>([{ id: 1, title: "", file: null }]);
const [file, setFile] = useState<File | null>(null); const [file, setFile] = useState<File | null>(null);
const router = useRouter(); const router = useRouter();
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
@ -40,9 +43,9 @@ export default function AddProductForm() {
if (selected) setFile(selected); if (selected) setFile(selected);
}; };
const handleAddSpec = () => { // const handleAddSpec = () => {
setSpecs((prev) => [...prev, { id: prev.length + 1 }]); // setSpecs((prev) => [...prev, { id: prev.length + 1 }]);
}; // };
const { const {
register, register,
handleSubmit, handleSubmit,
@ -52,6 +55,22 @@ export default function AddProductForm() {
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
}); });
const handleSpecTitleChange = (index: number, value: string) => {
const updated = [...specs];
updated[index].title = value;
setSpecs(updated);
};
const handleSpecFileChange = (
index: number,
e: React.ChangeEvent<HTMLInputElement>,
) => {
const file = e.target.files?.[0] || null;
const updated = [...specs];
updated[index].file = file;
setSpecs(updated);
};
const onSubmit = async (data: z.infer<typeof formSchema>) => { const onSubmit = async (data: z.infer<typeof formSchema>) => {
try { try {
const formData = new FormData(); const formData = new FormData();
@ -80,6 +99,19 @@ export default function AddProductForm() {
} }
}); });
// 🔥 specifications JSON
const specificationsPayload = specs.map((s) => ({
title: s.title,
}));
formData.append("specifications", JSON.stringify(specificationsPayload));
// 🔥 imagespecification_url (files)
specs.forEach((s) => {
if (s.file) {
formData.append("imagespecification_url", s.file);
}
});
await createProduct(formData); await createProduct(formData);
successSubmit("/admin/product"); successSubmit("/admin/product");
} catch (err) { } catch (err) {
@ -311,36 +343,49 @@ export default function AddProductForm() {
Judul Spesifikasi {index + 1} Judul Spesifikasi {index + 1}
</Label> </Label>
<Input <Input
placeholder="Contoh: Mesin Turbo 1.6L, Interior Premium, Safety Features" placeholder="Contoh: Mesin Turbo 1.6L"
className="mt-1" className="mt-1"
value={spec.title}
onChange={(e) => handleSpecTitleChange(index, e.target.value)}
/> />
<Label className="text-sm font-semibold mt-4 block"> <Label className="text-sm font-semibold mt-4 block">
Foto Spesifikasi {index + 1} Foto Spesifikasi {index + 1}
</Label> </Label>
<div className="border-2 border-dashed rounded-lg flex flex-col items-center justify-center py-10 cursor-pointer hover:bg-gray-50 transition mt-1">
<label
htmlFor={`spec-file-${index}`}
className="border-2 border-dashed rounded-lg flex flex-col items-center justify-center py-10 cursor-pointer hover:bg-gray-50 transition mt-1"
>
<Upload className="w-8 h-8 text-gray-400 mb-2" /> <Upload className="w-8 h-8 text-gray-400 mb-2" />
<p className="text-sm text-gray-500 text-center"> <p className="text-sm text-gray-500 text-center">
Klik untuk upload gambar spesifikasi Klik untuk upload gambar spesifikasi
</p> </p>
<p className="text-xs text-gray-400">PNG, JPG (max 5 MB)</p> <p className="text-xs text-gray-400">PNG, JPG (max 5 MB)</p>
<input <input
id={`spec-file-${index}`}
type="file" type="file"
accept="image/png,image/jpeg" accept="image/png,image/jpeg"
className="hidden" className="hidden"
onChange={(e) => handleSpecFileChange(index, e)}
/> />
</div> </label>
{spec.file && (
<p className="text-xs text-teal-700 mt-2">{spec.file.name}</p>
)}
</div> </div>
))} ))}
<button {/* <button
type="button" type="button"
onClick={handleAddSpec} onClick={handleAddSpec}
className="w-full bg-teal-800 hover:bg-teal-900 text-white py-3 rounded-lg mt-6 flex items-center justify-center gap-2" className="w-full bg-teal-800 hover:bg-teal-900 text-white py-3 rounded-lg mt-6 flex items-center justify-center gap-2"
> >
<Plus className="w-4 h-4" /> <Plus className="w-4 h-4" />
Tambahkan Spesifikasi Baru Tambahkan Spesifikasi Baru
</button> </button> */}
</div> </div>
<Button <Button

View File

@ -208,6 +208,7 @@ export default function ArticleTable() {
setViewBanner(item); setViewBanner(item);
setOpenViewDialog(true); setOpenViewDialog(true);
}; };
const [previewImage, setPreviewImage] = useState<string | null>(null); const [previewImage, setPreviewImage] = useState<string | null>(null);
const handleOpenApproverHistory = () => { const handleOpenApproverHistory = () => {

View File

@ -1,8 +1,14 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
images: { images: {
domains: ["mikulnews.com", "dev.mikulnews.com", "jaecoocihampelasbdg.com"], domains: [
"mikulnews.com",
"dev.mikulnews.com",
"jaecoocihampelasbdg.com",
"jaecookelapagading.com",
],
}, },
eslint: { eslint: {
ignoreDuringBuilds: true, ignoreDuringBuilds: true,
}, },