update:bugs dialog
This commit is contained in:
parent
e1f99bd4c4
commit
9bc0b301cb
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ export default function AddProductForm() {
|
|||
>([{ 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);
|
||||
|
|
@ -40,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,
|
||||
|
|
@ -52,6 +55,22 @@ 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();
|
||||
|
|
@ -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);
|
||||
successSubmit("/admin/product");
|
||||
} catch (err) {
|
||||
|
|
@ -311,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
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ export default function ArticleTable() {
|
|||
setViewBanner(item);
|
||||
setOpenViewDialog(true);
|
||||
};
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
|
||||
const handleOpenApproverHistory = () => {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
images: {
|
||||
domains: ["mikulnews.com", "dev.mikulnews.com", "jaecoocihampelasbdg.com"],
|
||||
domains: [
|
||||
"mikulnews.com",
|
||||
"dev.mikulnews.com",
|
||||
"jaecoocihampelasbdg.com",
|
||||
"jaecookelapagading.com",
|
||||
],
|
||||
},
|
||||
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue