fix:bugs dialog
This commit is contained in:
parent
ae03db15be
commit
83eae371a6
|
|
@ -115,7 +115,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
const res = await commentGalery(id, message || undefined);
|
||||
|
||||
if (res?.error) {
|
||||
error(res.message || "Gagal komentar promotion");
|
||||
error(res.message || "Gagal komentar galeri");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
|
@ -161,11 +161,24 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={onClose}>
|
||||
<DialogContent className=" rounded-2xl p-0 overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="bg-[#1F6779] text-white px-6 py-4">
|
||||
<DialogTitle className="text-white">Detail Galeri</DialogTitle>
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
|
||||
onClick={onClose}
|
||||
>
|
||||
<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">
|
||||
<span
|
||||
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">
|
||||
{/* Images List */}
|
||||
<div className="mx-2">
|
||||
<div className="mx-4">
|
||||
<h2 className="text-2xl font-semibold text-black">
|
||||
{data.title}
|
||||
</h2>
|
||||
|
|
@ -240,7 +253,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
{/* Deskripsi */}
|
||||
|
||||
{/* Tanggal Upload */}
|
||||
<div className="mx-2">
|
||||
<div className="mx-4">
|
||||
<p className="font-medium text-sm text-gray-700">
|
||||
Tanggal Upload
|
||||
</p>
|
||||
|
|
@ -250,7 +263,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
<div className="mx-2">
|
||||
<div className="mx-4">
|
||||
<h4 className="text-sm font-semibold text-gray-700 mb-3">
|
||||
Status Timeline
|
||||
</h4>
|
||||
|
|
@ -374,17 +387,8 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* <DialogFooter className="px-6 pb-6">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="bg-gray-300 text-gray-700 px-6 py-2 rounded-lg"
|
||||
>
|
||||
Tutup
|
||||
</button>
|
||||
</DialogFooter> */}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
{openApproverHistory && (
|
||||
<div
|
||||
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { error, loading, success } from "@/config/swal";
|
|||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import promo from "../landing-page/promo";
|
||||
import Image from "next/image";
|
||||
|
||||
type PromoDetailDialogProps = {
|
||||
promoId: number | null;
|
||||
|
|
@ -220,16 +221,24 @@ export default function PromoDetailDialog({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="p-0 max-w-lg overflow-hidden">
|
||||
<div
|
||||
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 */}
|
||||
<div className="bg-gradient-to-r from-[#0f6c75] to-[#145f66] text-white px-6 py-5 relative">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white text-xl font-semibold">
|
||||
Detail Promoaa
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="bg-gradient-to-br from-[#1F6779] to-[#0F6C75] text-white px-6 py-5 relative">
|
||||
<button
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="absolute top-4 right-4 text-white/80 hover:text-white text-xl"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
<h2 className="text-lg font-semibold">Detail Promo</h2>
|
||||
<div className="flex items-center gap-2 mt-3">
|
||||
<span
|
||||
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>
|
||||
<p className="text-gray-600">Ukuran File</p>
|
||||
<p className="font-medium">{promo.fileSize}</p>
|
||||
<div className="w-24 h-24 rounded-lg overflow-hidden border">
|
||||
<Image
|
||||
src={promo.thumbnail_url}
|
||||
alt="Profile"
|
||||
width={96}
|
||||
height={96}
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
@ -418,104 +434,6 @@ export default function PromoDetailDialog({
|
|||
)}
|
||||
</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>></span>
|
||||
<span>></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
|
||||
<div className="bg-[#E3EFF4] text-center py-3">
|
||||
<button
|
||||
|
|
@ -525,8 +443,8 @@ export default function PromoDetailDialog({
|
|||
Tutup
|
||||
</button>
|
||||
</div> */}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
{openApproverHistory && (
|
||||
<div
|
||||
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
|
||||
|
|
|
|||
|
|
@ -24,9 +24,15 @@ const formSchema = z.object({
|
|||
});
|
||||
|
||||
export default function AddProductForm() {
|
||||
const [colors, setColors] = useState([{ id: 1 }]);
|
||||
const [colors, setColors] = useState<
|
||||
{ id: number; name: string; file: File | null }[]
|
||||
>([{ id: 1, name: "", file: 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 router = useRouter();
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
|
@ -37,9 +43,9 @@ export default function AddProductForm() {
|
|||
if (selected) setFile(selected);
|
||||
};
|
||||
|
||||
const handleAddSpec = () => {
|
||||
setSpecs((prev) => [...prev, { id: prev.length + 1 }]);
|
||||
};
|
||||
// const handleAddSpec = () => {
|
||||
// setSpecs((prev) => [...prev, { id: prev.length + 1 }]);
|
||||
// };
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
|
|
@ -49,29 +55,67 @@ export default function AddProductForm() {
|
|||
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>) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("title", data.name);
|
||||
formData.append("variant", data.variant);
|
||||
formData.append("is_active", "1");
|
||||
formData.append("price", data.price.toString());
|
||||
// if (data.banner && data.banner.length > 0) {
|
||||
// formData.append("thumbnail_path", data.banner[0]);
|
||||
// }
|
||||
formData.append("status", "1");
|
||||
formData.append("is_active", "1");
|
||||
|
||||
// banner
|
||||
if (file) {
|
||||
formData.append("file", file);
|
||||
}
|
||||
const colorsArray = colors.map((c) => selectedColor || "");
|
||||
formData.append("colors", JSON.stringify(colorsArray));
|
||||
|
||||
const res = await createProduct(formData);
|
||||
// 🔥 colors JSON (object)
|
||||
const colorsPayload = colors.map((c) => ({
|
||||
name: c.name,
|
||||
}));
|
||||
formData.append("colors", JSON.stringify(colorsPayload));
|
||||
|
||||
console.log("API Success:", res);
|
||||
// 🔥 color images
|
||||
colors.forEach((c) => {
|
||||
if (c.file) {
|
||||
formData.append("color_images", c.file);
|
||||
}
|
||||
});
|
||||
|
||||
// 🔥 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);
|
||||
successSubmit("/admin/product");
|
||||
} catch (error) {
|
||||
console.error("Submit Error:", error);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert("Gagal mengirim produk");
|
||||
}
|
||||
};
|
||||
|
|
@ -96,7 +140,20 @@ export default function AddProductForm() {
|
|||
};
|
||||
|
||||
const handleAddColor = () => {
|
||||
setColors((prev) => [...prev, { id: prev.length + 1 }]);
|
||||
setColors((prev) => [
|
||||
...prev,
|
||||
{ id: prev.length + 1, name: "", file: null },
|
||||
]);
|
||||
};
|
||||
|
||||
const handleColorFileChange = (
|
||||
index: number,
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
const file = e.target.files?.[0] || null;
|
||||
const updated = [...colors];
|
||||
updated[index].file = file;
|
||||
setColors(updated);
|
||||
};
|
||||
|
||||
const formatRupiah = (value: string) => {
|
||||
|
|
@ -192,7 +249,14 @@ export default function AddProductForm() {
|
|||
<Label className="text-sm font-semibold">
|
||||
Pilih Warna {index + 1}
|
||||
</Label>
|
||||
<Input placeholder="Contoh: Silver or #E2E2E2" />
|
||||
<Input
|
||||
value={color.name}
|
||||
onChange={(e) => {
|
||||
const updated = [...colors];
|
||||
updated[index].name = e.target.value;
|
||||
setColors(updated);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Pilihan Warna */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
|
|
@ -214,7 +278,12 @@ export default function AddProductForm() {
|
|||
<button
|
||||
key={colorCode}
|
||||
type="button"
|
||||
onClick={() => setSelectedColor(colorCode)}
|
||||
onClick={() => {
|
||||
const updated = [...colors];
|
||||
updated[index].name = colorCode; // 🔥 INI KUNCINYA
|
||||
setColors(updated);
|
||||
setSelectedColor(colorCode);
|
||||
}}
|
||||
className={`w-8 h-8 rounded-full border-2 transition ${
|
||||
selectedColor === colorCode
|
||||
? "border-teal-700 scale-110"
|
||||
|
|
@ -227,19 +296,29 @@ export default function AddProductForm() {
|
|||
|
||||
{/* Upload Foto Warna */}
|
||||
<div>
|
||||
<Label>Foto Warna {index + 1}</Label>
|
||||
<div className="border-2 border-dashed rounded-lg flex flex-col items-center justify-center py-6 cursor-pointer hover:bg-gray-50 transition">
|
||||
<label
|
||||
htmlFor={`color-file-${index}`}
|
||||
className="border-2 border-dashed rounded-lg flex flex-col items-center justify-center py-6 cursor-pointer hover:bg-gray-50 transition"
|
||||
>
|
||||
<Upload className="w-6 h-6 text-gray-400 mb-2" />
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Klik untuk upload foto mobil warna ini
|
||||
</p>
|
||||
<p className="text-xs text-gray-400">PNG, JPG (max 5 MB)</p>
|
||||
|
||||
<input
|
||||
id={`color-file-${index}`}
|
||||
type="file"
|
||||
accept="image/png,image/jpeg"
|
||||
className="hidden"
|
||||
onChange={(e) => handleColorFileChange(index, e)}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
{color.file && (
|
||||
<p className="text-xs text-teal-700 mt-2">
|
||||
{color.file.name}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -264,36 +343,49 @@ export default function AddProductForm() {
|
|||
Judul Spesifikasi {index + 1}
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="Contoh: Mesin Turbo 1.6L, Interior Premium, Safety Features"
|
||||
placeholder="Contoh: Mesin Turbo 1.6L"
|
||||
className="mt-1"
|
||||
value={spec.title}
|
||||
onChange={(e) => handleSpecTitleChange(index, e.target.value)}
|
||||
/>
|
||||
|
||||
<Label className="text-sm font-semibold mt-4 block">
|
||||
Foto Spesifikasi {index + 1}
|
||||
</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" />
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Klik untuk upload gambar spesifikasi
|
||||
</p>
|
||||
<p className="text-xs text-gray-400">PNG, JPG (max 5 MB)</p>
|
||||
|
||||
<input
|
||||
id={`spec-file-${index}`}
|
||||
type="file"
|
||||
accept="image/png,image/jpeg"
|
||||
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>
|
||||
))}
|
||||
|
||||
<button
|
||||
{/* <button
|
||||
type="button"
|
||||
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"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Tambahkan Spesifikasi Baru
|
||||
</button>
|
||||
</button> */}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
|
|
|
|||
Loading…
Reference in New Issue