fix
This commit is contained in:
parent
8cd20ddd8f
commit
b623304e9d
|
|
@ -14,7 +14,6 @@ export default function AgentPage() {
|
||||||
|
|
||||||
const handleSubmitBanner = (data: any) => {
|
const handleSubmitBanner = (data: any) => {
|
||||||
console.log("Banner Data:", data);
|
console.log("Banner Data:", data);
|
||||||
// TODO: kirim data ke API di sini
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ export default function GaleryPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="dark:bg-[#18181b] rounded-xl p-3">
|
<div className="dark:bg-[#18181b] rounded-xl p-3">
|
||||||
{/* Tombol buka dialog */}
|
|
||||||
<Button
|
<Button
|
||||||
className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2"
|
className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2"
|
||||||
onClick={() => setOpenDialog(true)}
|
onClick={() => setOpenDialog(true)}
|
||||||
|
|
@ -34,13 +33,11 @@ export default function GaleryPage() {
|
||||||
Tambah Galeri Baru
|
Tambah Galeri Baru
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Komponen Galeri */}
|
|
||||||
<Galery />
|
<Galery />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dialog Tambah Galeri */}
|
|
||||||
<GaleriDialog
|
<GaleriDialog
|
||||||
open={openDialog}
|
open={openDialog}
|
||||||
onClose={() => setOpenDialog(false)}
|
onClose={() => setOpenDialog(false)}
|
||||||
|
|
|
||||||
|
|
@ -32,23 +32,13 @@ export default function AgentDetailDialog({
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="p-0 max-w-lg overflow-hidden rounded-2xl">
|
<DialogContent className="p-0 max-w-lg overflow-hidden rounded-2xl">
|
||||||
{/* HEADER */}
|
|
||||||
<div className="bg-gradient-to-r from-[#0f6c75] to-[#145f66] text-white px-6 py-5 relative">
|
<div className="bg-gradient-to-r from-[#0f6c75] to-[#145f66] text-white px-6 py-5 relative">
|
||||||
{/* CLOSE BUTTON */}
|
|
||||||
{/* <button
|
|
||||||
onClick={() => onOpenChange(false)}
|
|
||||||
className="absolute top-4 right-5 text-white/80 hover:text-white text-xl"
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</button> */}
|
|
||||||
|
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="text-white text-xl font-semibold">
|
<DialogTitle className="text-white text-xl font-semibold">
|
||||||
Detail Agen
|
Detail Agen
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
{/* STATUS BADGE */}
|
|
||||||
<div className="mt-2 bg-white/20 text-white px-3 py-1 rounded-full text-xs inline-flex items-center gap-2">
|
<div className="mt-2 bg-white/20 text-white px-3 py-1 rounded-full text-xs inline-flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={`w-2 h-2 rounded-full ${
|
className={`w-2 h-2 rounded-full ${
|
||||||
|
|
@ -59,9 +49,7 @@ export default function AgentDetailDialog({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* BODY */}
|
|
||||||
<div className="p-6 text-center">
|
<div className="p-6 text-center">
|
||||||
{/* FOTO PROFIL */}
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<Image
|
<Image
|
||||||
src={data.imageUrl}
|
src={data.imageUrl}
|
||||||
|
|
@ -72,16 +60,12 @@ export default function AgentDetailDialog({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* NAMA */}
|
|
||||||
<h2 className="text-xl font-semibold mt-5">{data.name}</h2>
|
<h2 className="text-xl font-semibold mt-5">{data.name}</h2>
|
||||||
|
|
||||||
{/* JABATAN */}
|
|
||||||
<p className="text-gray-600">{data.position}</p>
|
<p className="text-gray-600">{data.position}</p>
|
||||||
|
|
||||||
{/* NOMOR TELEPON */}
|
|
||||||
<p className="text-gray-700 mt-2 font-medium">{data.phone}</p>
|
<p className="text-gray-700 mt-2 font-medium">{data.phone}</p>
|
||||||
|
|
||||||
{/* JENIS AGEN */}
|
|
||||||
<div className="text-left mt-5">
|
<div className="text-left mt-5">
|
||||||
<p className="font-semibold mb-2">Jenis Agen</p>
|
<p className="font-semibold mb-2">Jenis Agen</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|
@ -7,8 +9,37 @@ import {
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { CheckCircle } from "lucide-react";
|
import { CheckCircle } from "lucide-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { getGaleryFileData } from "@/service/galery";
|
||||||
|
|
||||||
export function DialogDetailGaleri({ open, onClose, data }: any) {
|
export function DialogDetailGaleri({ open, onClose, data }: any) {
|
||||||
|
const [images, setImages] = useState<any[]>([]);
|
||||||
|
|
||||||
|
const fetchImages = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getGaleryFileData(data.id);
|
||||||
|
const allImages = res?.data?.data ?? [];
|
||||||
|
|
||||||
|
const filteredImages = allImages.filter(
|
||||||
|
(img: any) => img.gallery_id === data.id
|
||||||
|
);
|
||||||
|
|
||||||
|
setImages(filteredImages);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error fetch files:", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && data?.id) {
|
||||||
|
fetchImages();
|
||||||
|
}
|
||||||
|
}, [open, data]);
|
||||||
|
|
||||||
|
const openFile = (url: string) => {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onClose}>
|
<Dialog open={open} onOpenChange={onClose}>
|
||||||
<DialogContent className="max-w-3xl rounded-2xl p-0 overflow-hidden">
|
<DialogContent className="max-w-3xl rounded-2xl p-0 overflow-hidden">
|
||||||
|
|
@ -18,15 +49,35 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="px-6 py-6 space-y-6">
|
<div className="px-6 py-6 space-y-6">
|
||||||
{/* Images */}
|
{/* Images List */}
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div>
|
||||||
<div className="relative h-28 w-full">
|
<p className="font-medium mb-2">Daftar Gambar</p>
|
||||||
<Image
|
|
||||||
src={data.image_url}
|
<div className="grid grid-cols-3 gap-4">
|
||||||
alt="Galery Image"
|
{images.length === 0 && (
|
||||||
fill
|
<p className="text-gray-500 col-span-3 text-center">
|
||||||
className="object-cover rounded-lg"
|
Tidak ada gambar.
|
||||||
/>
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{images.map((img) => (
|
||||||
|
<div
|
||||||
|
key={img.id}
|
||||||
|
className="relative h-32 w-full cursor-pointer group"
|
||||||
|
onClick={() => openFile(img.image_url)}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={img.image_url}
|
||||||
|
alt={img.title}
|
||||||
|
fill
|
||||||
|
className="object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 text-white flex items-center justify-center text-sm transition">
|
||||||
|
Lihat File
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -38,7 +89,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
||||||
{/* Deskripsi */}
|
{/* Deskripsi */}
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-sm text-gray-700">Deskripsi</p>
|
<p className="font-medium text-sm text-gray-700">Deskripsi</p>
|
||||||
<p className="text-gray-600">{data.desc}</p>
|
<p className="text-gray-600">{data.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tanggal Upload */}
|
{/* Tanggal Upload */}
|
||||||
|
|
@ -81,7 +132,6 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
|
||||||
<DialogFooter className="px-6 pb-6">
|
<DialogFooter className="px-6 pb-6">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|
|
||||||
|
|
@ -9,54 +9,89 @@ import {
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Upload } from "lucide-react";
|
import { Upload, X } from "lucide-react";
|
||||||
import { useState, useRef } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import { createGalery } from "@/service/galery";
|
import { createGalery, uploadGaleryFile } from "@/service/galery";
|
||||||
|
|
||||||
export function GaleriDialog({ open, onClose, onSubmit }: any) {
|
export function GaleriDialog({ open, onClose, onSubmit }: any) {
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [desc, setDesc] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [file, setFile] = useState<File | null>(null);
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
|
const [previews, setPreviews] = useState<string[]>([]);
|
||||||
const fileRef = useRef<HTMLInputElement>(null);
|
const fileRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
useEffect(() => {
|
||||||
if (!file) {
|
if (!files || files.length === 0) {
|
||||||
alert("File wajib diupload!");
|
setPreviews([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const form = new FormData();
|
const objectUrls = files.map((file) => URL.createObjectURL(file));
|
||||||
form.append("gallery_id", "1"); // nilai default (bisa dinamis)
|
setPreviews(objectUrls);
|
||||||
form.append("title", title);
|
|
||||||
form.append("desc", desc);
|
|
||||||
form.append("file", file);
|
|
||||||
|
|
||||||
try {
|
return () => {
|
||||||
const res = await createGalery(form);
|
objectUrls.forEach((url) => URL.revokeObjectURL(url));
|
||||||
|
};
|
||||||
|
}, [files]);
|
||||||
|
|
||||||
console.log("Upload Success:", res?.data);
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const files = e.target.files;
|
||||||
onSubmit(); // tutup dialog
|
if (!files) return;
|
||||||
} catch (error: any) {
|
setFiles((prev: File[]) => [...prev, ...Array.from(files)]);
|
||||||
console.error("Upload failed:", error);
|
|
||||||
alert("Gagal mengupload file");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileChange = (e: any) => {
|
const removeFile = (index: number) => {
|
||||||
const selected = e.target.files[0];
|
setFiles((prev) => prev.filter((_, i) => i !== index));
|
||||||
if (selected) setFile(selected);
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
if (!title) return alert("Judul wajib diisi!");
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("title", title);
|
||||||
|
formData.append("description", description);
|
||||||
|
|
||||||
|
const res = await createGalery(formData);
|
||||||
|
|
||||||
|
const galleryId = res?.data?.data?.id;
|
||||||
|
if (!galleryId) {
|
||||||
|
alert("Galeri gagal dibuat");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Galeri Created:", galleryId);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const fileForm = new FormData();
|
||||||
|
fileForm.append("gallery_id", galleryId);
|
||||||
|
fileForm.append("title", title);
|
||||||
|
fileForm.append("file", file);
|
||||||
|
|
||||||
|
const uploadRes = await uploadGaleryFile(galleryId, fileForm);
|
||||||
|
console.log("File Uploaded:", uploadRes?.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit();
|
||||||
|
|
||||||
|
setTitle("");
|
||||||
|
setDescription("");
|
||||||
|
setFiles([]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Submit failed:", error);
|
||||||
|
alert("Gagal mengupload galeri");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onClose}>
|
<Dialog open={open} onOpenChange={onClose}>
|
||||||
<DialogContent className="max-w-2xl rounded-2xl p-0 overflow-hidden">
|
<DialogContent className="max-w-2xl rounded-2xl p-0 overflow-hidden">
|
||||||
{/* Header */}
|
{/* HEADER */}
|
||||||
<div className="bg-[#1F6779] text-white px-6 py-4 flex justify-between items-center">
|
<div className="bg-[#1F6779] text-white px-6 py-4 flex justify-between items-center">
|
||||||
<DialogTitle className="text-white">Tambah Galeri</DialogTitle>
|
<DialogTitle className="text-white">Tambah Galeri</DialogTitle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* BODY */}
|
||||||
<div className="px-6 py-6 space-y-6">
|
<div className="px-6 py-6 space-y-6">
|
||||||
{/* Judul */}
|
{/* Judul */}
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -79,15 +114,15 @@ export function GaleriDialog({ open, onClose, onSubmit }: any) {
|
||||||
<Input
|
<Input
|
||||||
placeholder="Masukkan deskripsi galeri"
|
placeholder="Masukkan deskripsi galeri"
|
||||||
className="mt-1 h-12"
|
className="mt-1 h-12"
|
||||||
value={desc}
|
value={description}
|
||||||
onChange={(e) => setDesc(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Upload */}
|
{/* Upload File */}
|
||||||
<div>
|
<div>
|
||||||
<label className="font-medium text-sm">
|
<label className="font-medium text-sm">
|
||||||
Upload File <span className="text-red-500">*</span>
|
Upload File (opsional)
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
@ -107,18 +142,39 @@ export function GaleriDialog({ open, onClose, onSubmit }: any) {
|
||||||
className="hidden"
|
className="hidden"
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
|
multiple
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{file && (
|
{/* Preview Gambar */}
|
||||||
<p className="text-xs mt-2 text-green-700">
|
{previews.length > 0 && (
|
||||||
File dipilih: {file.name}
|
<div className="mt-4 flex flex-wrap gap-4">
|
||||||
</p>
|
{previews.map((src, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="relative w-24 h-24 rounded-lg overflow-hidden border border-gray-300"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
alt={`preview-${i}`}
|
||||||
|
className="object-cover w-full h-full"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => removeFile(i)}
|
||||||
|
className="absolute top-1 right-1 bg-red-600 rounded-full p-1 text-white hover:bg-red-700 transition"
|
||||||
|
title="Hapus gambar"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* FOOTER */}
|
||||||
<DialogFooter className="flex justify-between px-6 pb-6 gap-4">
|
<DialogFooter className="flex justify-between px-6 pb-6 gap-4">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -10,90 +10,119 @@ import {
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import { updateGalery } from "@/service/galery";
|
import {
|
||||||
|
getGaleryFileData,
|
||||||
|
updateGalery,
|
||||||
|
updateUploadGaleryFile,
|
||||||
|
} from "@/service/galery";
|
||||||
|
|
||||||
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(data.title || "");
|
||||||
const [desc, setDesc] = useState(data.desc);
|
const [description, setDescription] = useState(data.desc || "");
|
||||||
const [files, setFiles] = useState(data.files || []);
|
const [oldFiles, setOldFiles] = useState<any[]>([]);
|
||||||
const [newFiles, setNewFiles] = useState<File[]>([]);
|
const [newFiles, setNewFiles] = useState<File[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const fetchOldFiles = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getGaleryFileData(data.id);
|
||||||
|
const allImages = res?.data?.data ?? [];
|
||||||
|
|
||||||
|
const filteredImages = allImages.filter(
|
||||||
|
(img: any) => img.gallery_id === data.id
|
||||||
|
);
|
||||||
|
|
||||||
|
setOldFiles(filteredImages);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error fetch files:", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && data?.id) {
|
||||||
|
fetchOldFiles();
|
||||||
|
setTitle(data.title || "");
|
||||||
|
setDescription(data.description || "");
|
||||||
|
}
|
||||||
|
}, [open, data]);
|
||||||
|
|
||||||
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[];
|
||||||
setNewFiles((prev) => [...prev, ...uploaded]);
|
setNewFiles((prev) => [...prev, ...uploaded]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeOldFile = (id: number) => {
|
const removeOldFile = (id: number) => {
|
||||||
setFiles(files.filter((f: any) => f.id !== id));
|
setOldFiles((prev) => prev.filter((f) => f.id !== id));
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeNewFile = (index: number) => {
|
const removeNewFile = (index: number) => {
|
||||||
setNewFiles(newFiles.filter((_, i) => i !== index));
|
setNewFiles((prev) => prev.filter((_, i) => i !== index));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const form = new FormData();
|
const formMain = new FormData();
|
||||||
form.append("title", title);
|
formMain.append("title", title);
|
||||||
form.append("desc", desc);
|
formMain.append("description", description);
|
||||||
|
|
||||||
// file lama yang masih dipakai
|
formMain.append("old_files", JSON.stringify(oldFiles.map((f) => f.id)));
|
||||||
form.append("old_files", JSON.stringify(files.map((f: any) => f.id)));
|
|
||||||
|
|
||||||
// file baru
|
await updateGalery(data.id, formMain);
|
||||||
newFiles.forEach((file) => {
|
|
||||||
form.append("files", file);
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = await updateGalery(data.id, form);
|
for (const file of newFiles) {
|
||||||
|
const formFile = new FormData();
|
||||||
|
formFile.append("gallery_id", String(data.id));
|
||||||
|
formFile.append("title", title);
|
||||||
|
formFile.append("file", file);
|
||||||
|
|
||||||
|
await updateUploadGaleryFile(data.id, formFile);
|
||||||
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
onClose();
|
onClose();
|
||||||
if (onUpdated) onUpdated(); // refresh list
|
if (onUpdated) onUpdated();
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
console.error("Error update:", error);
|
console.error("Error update gallery:", err);
|
||||||
|
alert("Gagal menyimpan perubahan galeri");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onClose}>
|
<Dialog open={open} onOpenChange={onClose}>
|
||||||
<DialogContent className="max-w-xl rounded-2xl">
|
<DialogContent className="max-w-xl rounded-2xl">
|
||||||
<DialogHeader className="mb-4">
|
<DialogHeader>
|
||||||
<DialogTitle>Edit Banner</DialogTitle>
|
<DialogTitle>Edit Galeri</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
{/* Form */}
|
{/* FORM */}
|
||||||
<div className="space-y-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>
|
||||||
<input
|
<input
|
||||||
className="w-full border rounded-lg px-3 py-2 mt-1"
|
className="w-full border rounded-lg px-3 py-2 mt-1"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Desc */}
|
{/* DESCRIPTION */}
|
||||||
<div>
|
<div>
|
||||||
<label className="font-medium text-sm">Deskripsi Galeri *</label>
|
<label className="font-medium text-sm">Deskripsi Galeri *</label>
|
||||||
<textarea
|
<textarea
|
||||||
className="w-full border rounded-lg px-3 py-2 mt-1"
|
className="w-full border rounded-lg px-3 py-2 mt-1"
|
||||||
rows={3}
|
rows={3}
|
||||||
value={desc}
|
value={description}
|
||||||
onChange={(e) => setDesc(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Upload */}
|
{/* UPLOAD FILE */}
|
||||||
<div>
|
<div>
|
||||||
<label className="font-medium text-sm">Upload File *</label>
|
<label className="font-medium text-sm">Upload File</label>
|
||||||
|
|
||||||
<label className="mt-2 flex flex-col items-center justify-center border border-dashed rounded-xl py-6 cursor-pointer">
|
<label className="mt-2 flex flex-col items-center justify-center border border-dashed rounded-xl py-6 cursor-pointer">
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -110,48 +139,54 @@ export function DialogUpdateGaleri({ open, onClose, data, onUpdated }: any) {
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{/* EXISTING FILES */}
|
{/* OLD FILES */}
|
||||||
<div className="flex gap-3 mt-3">
|
{oldFiles.length > 0 && (
|
||||||
<div className="relative w-20 h-20">
|
<div className="flex gap-3 mt-3 flex-wrap">
|
||||||
<Image
|
{oldFiles.map((f) => (
|
||||||
src={data.image_url}
|
<div key={f.id} className="relative w-20 h-20">
|
||||||
alt=""
|
<Image
|
||||||
fill
|
src={f.image_url}
|
||||||
className="object-cover rounded-lg"
|
alt={f.title}
|
||||||
/>
|
fill
|
||||||
<button
|
className="object-cover rounded-lg"
|
||||||
onClick={() => removeOldFile(data.id)}
|
/>
|
||||||
className="absolute -top-2 -right-2 bg-red-600 text-white rounded-full p-1"
|
<button
|
||||||
>
|
onClick={() => removeOldFile(f.id)}
|
||||||
<X size={12} />
|
className="absolute -top-2 -right-2 bg-red-600 text-white rounded-full p-1"
|
||||||
</button>
|
>
|
||||||
|
<X size={12} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
{/* NEW FILES */}
|
{/* NEW FILES */}
|
||||||
<div className="flex gap-3 mt-3">
|
{newFiles.length > 0 && (
|
||||||
{newFiles.map((file, idx) => (
|
<div className="flex gap-3 mt-3 flex-wrap">
|
||||||
<div key={idx} className="relative w-20 h-20">
|
{newFiles.map((file, idx) => (
|
||||||
<Image
|
<div key={idx} className="relative w-20 h-20">
|
||||||
src={URL.createObjectURL(file)}
|
<Image
|
||||||
alt=""
|
src={URL.createObjectURL(file)}
|
||||||
fill
|
alt={file.name}
|
||||||
className="object-cover rounded-lg"
|
fill
|
||||||
/>
|
className="object-cover rounded-lg"
|
||||||
<button
|
/>
|
||||||
onClick={() => removeNewFile(idx)}
|
<button
|
||||||
className="absolute -top-2 -right-2 bg-red-600 text-white rounded-full p-1"
|
onClick={() => removeNewFile(idx)}
|
||||||
>
|
className="absolute -top-2 -right-2 bg-red-600 text-white rounded-full p-1"
|
||||||
<X size={12} />
|
>
|
||||||
</button>
|
<X size={12} />
|
||||||
</div>
|
</button>
|
||||||
))}
|
</div>
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* FOOTER */}
|
||||||
<DialogFooter className="mt-6">
|
<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"
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import { createAgent } from "@/service/agent";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { UploadCloud } from "lucide-react";
|
||||||
|
|
||||||
type AgentFormValues = {
|
type AgentFormValues = {
|
||||||
fullName: string;
|
fullName: string;
|
||||||
|
|
@ -29,8 +31,10 @@ type AgentFormValues = {
|
||||||
const agentTypes = ["After Sales", "Sales", "Spv", "Branch Manager"];
|
const agentTypes = ["After Sales", "Sales", "Spv", "Branch Manager"];
|
||||||
|
|
||||||
export default function AddAgentForm() {
|
export default function AddAgentForm() {
|
||||||
const [previewImg, setPreviewImg] = useState<string | null>(null);
|
// const [previewImg, setPreviewImg] = useState<string | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const [previewImg, setPreview] = useState<string | null>(null);
|
||||||
|
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const form = useForm<AgentFormValues>({
|
const form = useForm<AgentFormValues>({
|
||||||
|
|
@ -43,29 +47,37 @@ export default function AddAgentForm() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleImageChange = (file?: File) => {
|
const handleFileChange = (e: any) => {
|
||||||
if (!file) return;
|
const selected = e.target.files[0];
|
||||||
form.setValue("profileImage", file);
|
if (selected) setFile(selected);
|
||||||
|
};
|
||||||
|
|
||||||
const preview = URL.createObjectURL(file);
|
const handleRemoveFile = () => {
|
||||||
setPreviewImg(preview);
|
setFile(null);
|
||||||
|
setPreview(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async (data: AgentFormValues) => {
|
const onSubmit = async (data: AgentFormValues) => {
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const formData = new FormData();
|
||||||
name: data.fullName,
|
formData.append("name", data.fullName);
|
||||||
job_title: data.position,
|
formData.append("job_title", data.position);
|
||||||
phone: data.phone,
|
formData.append("phone", data.phone);
|
||||||
agent_type: data.roles, // langsung array
|
|
||||||
profile_picture_path: data.profileImage ? data.profileImage.name : "",
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = await createAgent(payload);
|
data.roles.forEach((role) => {
|
||||||
|
formData.append("agent_type[]", role);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
formData.append("file", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await createAgent(formData);
|
||||||
console.log("SUCCESS:", res);
|
console.log("SUCCESS:", res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("ERROR CREATE AGENT:", err);
|
console.error("ERROR CREATE AGENT:", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
successSubmit("/admin/agent");
|
successSubmit("/admin/agent");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -90,9 +102,7 @@ export default function AddAgentForm() {
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
{/* GRID INPUT */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
|
||||||
{/* Nama */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="fullName"
|
name="fullName"
|
||||||
|
|
@ -110,7 +120,6 @@ export default function AddAgentForm() {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Jabatan */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="position"
|
name="position"
|
||||||
|
|
@ -150,7 +159,6 @@ export default function AddAgentForm() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CHECKBOX ROLE */}
|
|
||||||
<div>
|
<div>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Pilih Jenis Agen <span className="text-red-500">*</span>
|
Pilih Jenis Agen <span className="text-red-500">*</span>
|
||||||
|
|
@ -189,52 +197,32 @@ export default function AddAgentForm() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* UPLOAD FOTO */}
|
<div>
|
||||||
<div className="space-y-3">
|
<Label className="text-gray-700">
|
||||||
<FormLabel>
|
Upload Foto Profil <span className="text-red-500">*</span>
|
||||||
Upload Foto Profile <span className="text-red-500">*</span>
|
</Label>
|
||||||
</FormLabel>
|
<label
|
||||||
|
htmlFor="uploadFile"
|
||||||
<div className="relative border-2 border-dashed rounded-xl bg-slate-50 dark:bg-neutral-800 p-6 flex flex-col items-center cursor-pointer">
|
className="mt-1 border-2 border-dashed border-gray-300 rounded-xl p-6 flex flex-col items-center justify-center text-gray-500 cursor-pointer hover:border-[#1F6779]/50 transition"
|
||||||
{previewImg ? (
|
>
|
||||||
<img
|
<UploadCloud className="w-10 h-10 text-[#1F6779] mb-2" />
|
||||||
src={previewImg}
|
<p className="text-sm font-medium">
|
||||||
className="w-32 h-32 rounded-full object-cover mb-4"
|
Klik untuk upload atau drag & drop
|
||||||
/>
|
</p>
|
||||||
) : (
|
<p className="text-xs text-gray-400 mt-1">PNG, JPG (max 2 MB)</p>
|
||||||
<div className="flex flex-col items-center">
|
|
||||||
<div className="bg-teal-200 p-4 rounded-full mb-3">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
strokeWidth={1.8}
|
|
||||||
stroke="currentColor"
|
|
||||||
className="w-8 h-8 text-teal-700"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
d="M12 16.5v-9m0 0L9 10m3-2.5l3 2.5m1.5 9H6a1.5 1.5 0 01-1.5-1.5v-12A1.5 1.5 0 016 3h12a1.5 1.5 0 011.5 1.5v12a1.5 1.5 0 01-1.5 1.5z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<p className="text-gray-600 dark:text-gray-300">
|
|
||||||
Klik untuk upload atau drag & drop
|
|
||||||
</p>
|
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
|
||||||
PNG, JPG (max 2 MB)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
id="uploadFile"
|
||||||
type="file"
|
type="file"
|
||||||
className="absolute inset-0 opacity-0 cursor-pointer"
|
accept="image/png, image/jpeg"
|
||||||
accept="image/*"
|
className="hidden"
|
||||||
onChange={(e) => handleImageChange(e.target.files?.[0])}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
{file && (
|
||||||
|
<p className="mt-2 text-xs text-[#1F6779] font-medium">
|
||||||
|
{file.name}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* BUTTON */}
|
{/* BUTTON */}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { getAgentById, updateAgent } from "@/service/agent";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
type AgentFormValues = {
|
type AgentFormValues = {
|
||||||
fullName: string;
|
fullName: string;
|
||||||
|
|
@ -32,6 +33,8 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [previewImg, setPreviewImg] = useState<string | null>(null);
|
const [previewImg, setPreviewImg] = useState<string | null>(null);
|
||||||
const [agentData, setAgentData] = useState<any>(null);
|
const [agentData, setAgentData] = useState<any>(null);
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const [preview, setPreview] = useState<string | null>(null);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
@ -46,9 +49,6 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// ================================
|
|
||||||
// 🔥 FETCH API & SET DEFAULT VALUES
|
|
||||||
// ================================
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -61,7 +61,6 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
|
|
||||||
setAgentData(res.data);
|
setAgentData(res.data);
|
||||||
|
|
||||||
// set form default values
|
|
||||||
form.reset({
|
form.reset({
|
||||||
fullName: res?.data?.data?.name,
|
fullName: res?.data?.data?.name,
|
||||||
position: res?.data?.data?.job_title,
|
position: res?.data?.data?.job_title,
|
||||||
|
|
@ -71,7 +70,7 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("name", res?.data?.data?.name);
|
console.log("name", res?.data?.data?.name);
|
||||||
setPreviewImg(res.data.profile_picture_path || null);
|
setPreviewImg(res.data.data.profile_picture_path || null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("ERROR FETCH DATA AGENT:", err);
|
console.error("ERROR FETCH DATA AGENT:", err);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -82,27 +81,37 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [id, form]);
|
}, [id, form]);
|
||||||
|
|
||||||
const handleImageChange = (file?: File) => {
|
const handleFileChange = (e: any) => {
|
||||||
if (!file) return;
|
const selected = e.target.files[0];
|
||||||
form.setValue("profileImage", file);
|
if (selected) {
|
||||||
|
setFile(selected);
|
||||||
|
setPreview(URL.createObjectURL(selected));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const preview = URL.createObjectURL(file);
|
const handleRemoveFile = () => {
|
||||||
setPreviewImg(preview);
|
setFile(null);
|
||||||
|
setPreview(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async (data: AgentFormValues) => {
|
const onSubmit = async (data: AgentFormValues) => {
|
||||||
const payload = {
|
|
||||||
name: data.fullName,
|
|
||||||
job_title: data.position,
|
|
||||||
phone: data.phone,
|
|
||||||
agent_type: data.roles,
|
|
||||||
profile_picture_path: data.profileImage
|
|
||||||
? data.profileImage.name
|
|
||||||
: agentData.profile_picture_path,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateAgent(id, payload);
|
const formData = new FormData();
|
||||||
|
|
||||||
|
formData.append("name", data.fullName);
|
||||||
|
formData.append("job_title", data.position);
|
||||||
|
formData.append("phone", data.phone);
|
||||||
|
|
||||||
|
data.roles.forEach((role) => {
|
||||||
|
formData.append("agent_type", role);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
formData.append("file", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateAgent(id, formData);
|
||||||
|
|
||||||
successSubmit("/admin/agent");
|
successSubmit("/admin/agent");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("ERROR UPDATE AGENT:", err);
|
console.error("ERROR UPDATE AGENT:", err);
|
||||||
|
|
@ -130,9 +139,7 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
{/* INPUT GRID */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
|
||||||
{/* Nama */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="fullName"
|
name="fullName"
|
||||||
|
|
@ -217,11 +224,21 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
<FormLabel>Foto Agen</FormLabel>
|
<FormLabel>Foto Agen</FormLabel>
|
||||||
|
|
||||||
<div className="flex items-center gap-5">
|
<div className="flex items-center gap-5">
|
||||||
{previewImg && (
|
{preview && (
|
||||||
<img
|
<div className="mt-3 relative w-28 h-28 rounded-md overflow-hidden border">
|
||||||
src={previewImg}
|
<img
|
||||||
className="w-24 h-24 rounded-lg object-cover shadow"
|
src={preview}
|
||||||
/>
|
alt="Preview"
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleRemoveFile}
|
||||||
|
className="absolute top-1 right-1 bg-red-600 text-white p-1 rounded-full"
|
||||||
|
>
|
||||||
|
<X className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -237,7 +254,7 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
type="file"
|
type="file"
|
||||||
className="hidden"
|
className="hidden"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
onChange={(e) => handleImageChange(e.target.files?.[0])}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export function BannerDialog({
|
||||||
confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
router.refresh(); // ⬅️ refresh halaman
|
router.refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -69,7 +69,7 @@ export function BannerDialog({
|
||||||
|
|
||||||
if (onSubmit) {
|
if (onSubmit) {
|
||||||
await onSubmit(formData);
|
await onSubmit(formData);
|
||||||
successSubmit(); // swal muncul disini
|
successSubmit();
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export function EditBannerDialog({
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (value: boolean) => void;
|
onOpenChange: (value: boolean) => void;
|
||||||
bannerData: any;
|
bannerData: any;
|
||||||
onSubmit?: (data: FormData, id: number) => void; // ← fix di sini
|
onSubmit?: (data: FormData, id: number) => void;
|
||||||
}) {
|
}) {
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [file, setFile] = useState<File | null>(null);
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
|
@ -49,7 +49,6 @@ export function EditBannerDialog({
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
// Set data awal ketika bannerData berubah
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (bannerData) {
|
if (bannerData) {
|
||||||
setTitle(bannerData.title || "");
|
setTitle(bannerData.title || "");
|
||||||
|
|
@ -60,7 +59,10 @@ export function EditBannerDialog({
|
||||||
|
|
||||||
const handleFileChange = (e: any) => {
|
const handleFileChange = (e: any) => {
|
||||||
const selected = e.target.files[0];
|
const selected = e.target.files[0];
|
||||||
if (selected) setFile(selected);
|
if (selected) {
|
||||||
|
setFile(selected);
|
||||||
|
setPreview(URL.createObjectURL(selected));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveFile = () => {
|
const handleRemoveFile = () => {
|
||||||
|
|
@ -85,16 +87,19 @@ export function EditBannerDialog({
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("title", title);
|
formData.append("title", title);
|
||||||
formData.append("position", selectedOrder.toString());
|
formData.append("position", selectedOrder.toString());
|
||||||
formData.append("description", "hardcode edit description");
|
formData.append("description", "dummy description");
|
||||||
|
|
||||||
if (file) formData.append("file", file);
|
if (file) {
|
||||||
|
formData.append("file", file);
|
||||||
if (onSubmit) {
|
|
||||||
await onSubmit(formData, bannerData.id); // ← kirim ID di sini
|
|
||||||
successSubmit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpenChange(false);
|
try {
|
||||||
|
await onSubmit?.(formData, bannerData.id);
|
||||||
|
successSubmit();
|
||||||
|
onOpenChange(false);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error submit:", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const orderOptions = [
|
const orderOptions = [
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { useRouter } from "next/navigation";
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
name: z.string().min(1, "Nama produk wajib diisi"),
|
name: z.string().min(1, "Nama produk wajib diisi"),
|
||||||
variant: z.string().min(1, "Tipe varian wajib diisi"),
|
variant: z.string().min(1, "Tipe varian wajib diisi"),
|
||||||
price: z.coerce.number().min(1, "Harga produk wajib diisi"), // ⬅️ PRICE NUMBER
|
price: z.coerce.number().min(1, "Harga produk wajib diisi"),
|
||||||
banner: z.instanceof(FileList).optional(),
|
banner: z.instanceof(FileList).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -99,7 +99,6 @@ export default function AddProductForm() {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
||||||
{/* 3 Input Field */}
|
|
||||||
<div className="grid md:grid-cols-3 gap-4">
|
<div className="grid md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<Label>Nama Produk *</Label>
|
<Label>Nama Produk *</Label>
|
||||||
|
|
@ -138,7 +137,6 @@ export default function AddProductForm() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Upload Banner */}
|
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-gray-700">
|
<Label className="text-gray-700">
|
||||||
Upload Banner <span className="text-red-500">*</span>
|
Upload Banner <span className="text-red-500">*</span>
|
||||||
|
|
@ -230,7 +228,6 @@ export default function AddProductForm() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Tambah Warna Baru */}
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAddColor}
|
onClick={handleAddColor}
|
||||||
|
|
@ -247,7 +244,6 @@ export default function AddProductForm() {
|
||||||
|
|
||||||
{specs.map((spec, index) => (
|
{specs.map((spec, index) => (
|
||||||
<div key={spec.id} className="mt-4">
|
<div key={spec.id} className="mt-4">
|
||||||
{/* Judul Spesifikasi */}
|
|
||||||
<Label className="text-sm font-semibold">
|
<Label className="text-sm font-semibold">
|
||||||
Judul Spesifikasi {index + 1}
|
Judul Spesifikasi {index + 1}
|
||||||
</Label>
|
</Label>
|
||||||
|
|
@ -256,7 +252,6 @@ export default function AddProductForm() {
|
||||||
className="mt-1"
|
className="mt-1"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Foto Spesifikasi */}
|
|
||||||
<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>
|
||||||
|
|
@ -275,7 +270,6 @@ export default function AddProductForm() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Tambah spesifikasi baru */}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAddSpec}
|
onClick={handleAddSpec}
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,6 @@ export default function UpdateProductForm() {
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// UPLOAD SYSTEM
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false);
|
const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false);
|
||||||
const [uploadTarget, setUploadTarget] = useState<{
|
const [uploadTarget, setUploadTarget] = useState<{
|
||||||
type: "spec" | "color";
|
type: "spec" | "color";
|
||||||
|
|
@ -136,7 +132,6 @@ export default function UpdateProductForm() {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent className="space-y-8">
|
<CardContent className="space-y-8">
|
||||||
{/* === 3 Input Field === */}
|
|
||||||
<div className="grid md:grid-cols-3 gap-4">
|
<div className="grid md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<Label>Nama Produk *</Label>
|
<Label>Nama Produk *</Label>
|
||||||
|
|
@ -154,13 +149,11 @@ export default function UpdateProductForm() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* === WARNA PRODUK === */}
|
|
||||||
<div>
|
<div>
|
||||||
<Label className="font-semibold">Warna Produk *</Label>
|
<Label className="font-semibold">Warna Produk *</Label>
|
||||||
|
|
||||||
{colors.map((item, index) => (
|
{colors.map((item, index) => (
|
||||||
<div key={item.id} className="mt-6 border-b pb-6">
|
<div key={item.id} className="mt-6 border-b pb-6">
|
||||||
{/* Color Name */}
|
|
||||||
<Label>Pilih Warna {index + 1}</Label>
|
<Label>Pilih Warna {index + 1}</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Contoh: Silver / #E2E2E2"
|
placeholder="Contoh: Silver / #E2E2E2"
|
||||||
|
|
@ -168,7 +161,6 @@ export default function UpdateProductForm() {
|
||||||
defaultValue={item.name}
|
defaultValue={item.name}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Palette */}
|
|
||||||
<div className="flex items-center gap-2 mt-3">
|
<div className="flex items-center gap-2 mt-3">
|
||||||
<div className="w-10 h-10 rounded-full flex items-center justify-center bg-teal-900">
|
<div className="w-10 h-10 rounded-full flex items-center justify-center bg-teal-900">
|
||||||
<Settings className="w-5 h-5 text-white" />
|
<Settings className="w-5 h-5 text-white" />
|
||||||
|
|
@ -195,7 +187,6 @@ export default function UpdateProductForm() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Foto Produk Warna */}
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Label className="font-semibold">
|
<Label className="font-semibold">
|
||||||
Foto Produk Warna {index + 1}
|
Foto Produk Warna {index + 1}
|
||||||
|
|
@ -224,7 +215,6 @@ export default function UpdateProductForm() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Add Color */}
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAddColor}
|
onClick={handleAddColor}
|
||||||
|
|
@ -234,7 +224,6 @@ export default function UpdateProductForm() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* === SPESIFIKASI === */}
|
|
||||||
<div className="mt-10">
|
<div className="mt-10">
|
||||||
<Label className="text-lg font-semibold text-teal-900">
|
<Label className="text-lg font-semibold text-teal-900">
|
||||||
Spesifikasi Produk <span className="text-red-500">*</span>
|
Spesifikasi Produk <span className="text-red-500">*</span>
|
||||||
|
|
@ -297,7 +286,6 @@ export default function UpdateProductForm() {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* ===== Dialog Upload File ===== */}
|
|
||||||
<Dialog open={isUploadDialogOpen} onOpenChange={setIsUploadDialogOpen}>
|
<Dialog open={isUploadDialogOpen} onOpenChange={setIsUploadDialogOpen}>
|
||||||
<DialogContent className="max-w-md">
|
<DialogContent className="max-w-md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,14 @@ export default function AddPromoForm() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [file, setFile] = useState<File | null>(null); // ⬅️ TAMBAH FILE STATE
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const selected = e.target.files?.[0];
|
const selected = e.target.files?.[0];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
setFile(selected); // ⬅️ SET FILE
|
setFile(selected);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,12 +36,11 @@ export default function AddPromoForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ⬅️ GUNAKAN FORMDATA
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
formData.append("title", title);
|
formData.append("title", title);
|
||||||
formData.append("description", title); // sementara sama dulu
|
formData.append("description", title);
|
||||||
formData.append("file", file); // ⬅️ FILE TERKIRIM
|
formData.append("file", file);
|
||||||
|
|
||||||
await createPromotion(formData);
|
await createPromotion(formData);
|
||||||
|
|
||||||
|
|
@ -74,7 +73,6 @@ export default function AddPromoForm() {
|
||||||
|
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<form className="space-y-6" onSubmit={onSubmit}>
|
<form className="space-y-6" onSubmit={onSubmit}>
|
||||||
{/* Judul Promo */}
|
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-sm font-semibold">
|
<Label className="text-sm font-semibold">
|
||||||
Judul Promo <span className="text-red-500">*</span>
|
Judul Promo <span className="text-red-500">*</span>
|
||||||
|
|
@ -87,7 +85,6 @@ export default function AddPromoForm() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Upload File */}
|
|
||||||
<div>
|
<div>
|
||||||
<Label className="text-sm font-semibold">Upload File *</Label>
|
<Label className="text-sm font-semibold">Upload File *</Label>
|
||||||
|
|
||||||
|
|
@ -98,7 +95,6 @@ export default function AddPromoForm() {
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 mt-1">PNG, JPG (max 2 MB)</p>
|
<p className="text-xs text-gray-500 mt-1">PNG, JPG (max 2 MB)</p>
|
||||||
|
|
||||||
{/* ⬅️ Tambahkan onChange disini */}
|
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/png,image/jpeg"
|
accept="image/png,image/jpeg"
|
||||||
|
|
@ -107,7 +103,6 @@ export default function AddPromoForm() {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{/* Nama file muncul setelah dipilih */}
|
|
||||||
{file && (
|
{file && (
|
||||||
<p className="text-xs text-teal-800 mt-2 font-medium">
|
<p className="text-xs text-teal-800 mt-2 font-medium">
|
||||||
{file.name}
|
{file.name}
|
||||||
|
|
@ -115,7 +110,6 @@ export default function AddPromoForm() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tombol Submit */}
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-teal-800 hover:bg-teal-900 text-white px-8 py-6 text-lg mt-4"
|
className="bg-teal-800 hover:bg-teal-900 text-white px-8 py-6 text-lg mt-4"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,12 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Eye, Pencil, Trash2, Calendar, MapPin } from "lucide-react";
|
import { Eye, Pencil, Trash2, Calendar, MapPin } from "lucide-react";
|
||||||
import { deleteGalery, getGaleryById, getGaleryData } from "@/service/galery";
|
import {
|
||||||
|
deleteGalery,
|
||||||
|
getGaleryById,
|
||||||
|
getGaleryData,
|
||||||
|
getGaleryFileData,
|
||||||
|
} from "@/service/galery";
|
||||||
import { DialogDetailGaleri } from "../dialog/galery-detail-dialog";
|
import { DialogDetailGaleri } from "../dialog/galery-detail-dialog";
|
||||||
import { DialogUpdateGaleri } from "../dialog/galery-update-dialog";
|
import { DialogUpdateGaleri } from "../dialog/galery-update-dialog";
|
||||||
import { error, success } from "@/config/swal";
|
import { error, success } from "@/config/swal";
|
||||||
|
|
@ -12,7 +17,8 @@ import Swal from "sweetalert2";
|
||||||
|
|
||||||
export default function Galery() {
|
export default function Galery() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const [data, setData] = useState([]);
|
const [data, setData] = useState<any[]>([]);
|
||||||
|
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [showData, setShowData] = useState("10");
|
const [showData, setShowData] = useState("10");
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
@ -30,10 +36,35 @@ export default function Galery() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await getGaleryData(req);
|
const res = await getGaleryData(req);
|
||||||
|
const list = res?.data?.data ?? [];
|
||||||
|
|
||||||
// Pastikan respons API sesuai bentuknya
|
// Ambil gambar yang sesuai gallery_id dan gabungkan ke data galeri
|
||||||
// Misal: { data: [...], total: number }
|
const merged = await Promise.all(
|
||||||
setData(res?.data?.data);
|
list.map(async (item: any) => {
|
||||||
|
try {
|
||||||
|
const filesRes = await getGaleryFileData(item.id);
|
||||||
|
const images = filesRes?.data?.data ?? [];
|
||||||
|
|
||||||
|
// Filter file dengan gallery_id sama dengan item.id
|
||||||
|
const filteredImages = images?.filter(
|
||||||
|
(img: any) => img.gallery_id === item.id
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ambil gambar pertama sebagai thumbnail
|
||||||
|
const coverImage =
|
||||||
|
filteredImages.length > 0 ? filteredImages[0].image_url : null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
image_url: coverImage,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return { ...item, image_url: null };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
setData(merged);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetch galeri:", error);
|
console.error("Error fetch galeri:", error);
|
||||||
}
|
}
|
||||||
|
|
@ -64,14 +95,12 @@ export default function Galery() {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
// loading();
|
|
||||||
const resDelete = await deleteGalery(id);
|
const resDelete = await deleteGalery(id);
|
||||||
|
|
||||||
if (resDelete?.error) {
|
if (resDelete?.error) {
|
||||||
error(resDelete.message);
|
error(resDelete.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
close();
|
|
||||||
success("Berhasil Hapus");
|
success("Berhasil Hapus");
|
||||||
fetchData();
|
fetchData();
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +122,6 @@ export default function Galery() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
{/* Card Wrapper */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||||
{data?.map((item: any) => (
|
{data?.map((item: any) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -102,12 +130,18 @@ export default function Galery() {
|
||||||
>
|
>
|
||||||
{/* Image */}
|
{/* Image */}
|
||||||
<div className="relative w-full h-48">
|
<div className="relative w-full h-48">
|
||||||
<Image
|
{item.image_url ? (
|
||||||
src={item.image_url}
|
<Image
|
||||||
alt={item.title}
|
src={item.image_url}
|
||||||
fill
|
alt={item.title}
|
||||||
className="object-cover"
|
fill
|
||||||
/>
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-center h-full bg-gray-100 text-gray-400">
|
||||||
|
No Image
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Status Badge */}
|
{/* Status Badge */}
|
||||||
<span
|
<span
|
||||||
|
|
@ -127,14 +161,13 @@ export default function Galery() {
|
||||||
<h2 className="text-[#1F6779] text-lg font-semibold">
|
<h2 className="text-[#1F6779] text-lg font-semibold">
|
||||||
{item.title}
|
{item.title}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 text-sm">{item.desc ?? "-"}</p>
|
<p className="text-gray-600 text-sm">{item.description ?? "-"}</p>
|
||||||
|
|
||||||
{/* Date */}
|
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-700">
|
<div className="flex items-center gap-2 text-sm text-gray-700">
|
||||||
<Calendar className="h-4 w-4" />{" "}
|
<Calendar className="h-4 w-4" />{" "}
|
||||||
{new Date(item.created_at).toLocaleDateString("id-ID")}
|
{new Date(item.created_at).toLocaleDateString("id-ID")}
|
||||||
</div>
|
</div>
|
||||||
{/* Location (jika tidak ada, tampilkan strip) */}
|
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-700">
|
<div className="flex items-center gap-2 text-sm text-gray-700">
|
||||||
<MapPin className="h-4 w-4" /> {item.location ?? "-"}
|
<MapPin className="h-4 w-4" /> {item.location ?? "-"}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -188,6 +221,7 @@ export default function Galery() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showDetail && detailData && (
|
{showDetail && detailData && (
|
||||||
<DialogDetailGaleri
|
<DialogDetailGaleri
|
||||||
open={showDetail}
|
open={showDetail}
|
||||||
|
|
@ -195,12 +229,13 @@ export default function Galery() {
|
||||||
data={detailData}
|
data={detailData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showEdit && editData && (
|
{showEdit && editData && (
|
||||||
<DialogUpdateGaleri
|
<DialogUpdateGaleri
|
||||||
open={showEdit}
|
open={showEdit}
|
||||||
onClose={() => setShowEdit(false)}
|
onClose={() => setShowEdit(false)}
|
||||||
data={editData}
|
data={editData}
|
||||||
onUpdated={fetchData} // refresh setelah update
|
onUpdated={fetchData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,17 @@ export async function getAgentById(id: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createAgent(data: any) {
|
export async function createAgent(data: any) {
|
||||||
const pathUrl = `/sales-agents`;
|
const headers = {
|
||||||
return await httpPostInterceptor(pathUrl, data);
|
"content-type": "multipart/form-data",
|
||||||
|
};
|
||||||
|
return await httpPostInterceptor(`/sales-agents`, data, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateAgent(id: number, data: any) {
|
export async function updateAgent(id: number, data: any) {
|
||||||
const pathUrl = `/sales-agents/${id}`;
|
const headers = {
|
||||||
return await httpPutInterceptor(pathUrl, data);
|
"content-type": "multipart/form-data",
|
||||||
|
};
|
||||||
|
return await httpPutInterceptor(`/sales-agents/${id}`, data, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteAgent(id: string) {
|
export async function deleteAgent(id: string) {
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,17 @@ export async function createBanner(data: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateBanner(data: any, id: any) {
|
export async function updateBanner(data: any, id: any) {
|
||||||
const pathUrl = `/banners/${id}`;
|
const headers = {
|
||||||
return await httpPutInterceptor(pathUrl, data);
|
"content-type": "multipart/form-data",
|
||||||
|
};
|
||||||
|
return await httpPutInterceptor(`/banners/${id}`, data, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export async function updateBanner(data: any, id: any) {
|
||||||
|
// const pathUrl = `/banners/${id}`;
|
||||||
|
// return await httpPutInterceptor(pathUrl, data);
|
||||||
|
// }
|
||||||
|
|
||||||
export async function deleteBanner(id: string) {
|
export async function deleteBanner(id: string) {
|
||||||
const headers = {
|
const headers = {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@ import {
|
||||||
import { httpGet } from "./http-config/http-base-services";
|
import { httpGet } from "./http-config/http-base-services";
|
||||||
|
|
||||||
export async function getGaleryData(props: PaginationRequest) {
|
export async function getGaleryData(props: PaginationRequest) {
|
||||||
|
const { page, limit, search } = props;
|
||||||
|
return await httpGetInterceptor(`/galleries`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGaleryFileData(props: PaginationRequest) {
|
||||||
const { page, limit, search } = props;
|
const { page, limit, search } = props;
|
||||||
return await httpGetInterceptor(`/gallery-files`);
|
return await httpGetInterceptor(`/gallery-files`);
|
||||||
}
|
}
|
||||||
|
|
@ -16,31 +21,45 @@ export async function getGaleryById(id: any) {
|
||||||
const headers = {
|
const headers = {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
};
|
};
|
||||||
return await httpGet(`/gallery-files/${id}`, headers);
|
return await httpGet(`/galleries/${id}`, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGaleryFileName(filename: any) {
|
||||||
|
const headers = {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
return await httpGet(`/gallery-files/viewer/${filename}`, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createGalery(data: any) {
|
export async function createGalery(data: any) {
|
||||||
const headers = {
|
const pathUrl = `/galleries`;
|
||||||
"content-type": "multipart/form-data",
|
return await httpPostInterceptor(pathUrl, data);
|
||||||
};
|
|
||||||
return await httpPostInterceptor(`/gallery-files`, data, headers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateGalery(id: any, data: any) {
|
export async function updateGalery(id: any, data: any) {
|
||||||
|
const headers = {
|
||||||
|
"content-type": "multipart/form-data",
|
||||||
|
};
|
||||||
|
return await httpPutInterceptor(`/galleries/${id}`, data, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUploadGaleryFile(id: any, data: any) {
|
||||||
const headers = {
|
const headers = {
|
||||||
"content-type": "multipart/form-data",
|
"content-type": "multipart/form-data",
|
||||||
};
|
};
|
||||||
return await httpPutInterceptor(`/gallery-files/${id}`, data, headers);
|
return await httpPutInterceptor(`/gallery-files/${id}`, data, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// export async function updateGalery(data: any, id: any) {
|
export async function uploadGaleryFile(id: any, data: any) {
|
||||||
// const pathUrl = `/gallery-files/${id}`;
|
const headers = {
|
||||||
// return await httpPutInterceptor(pathUrl, data);
|
"content-type": "multipart/form-data",
|
||||||
// }
|
};
|
||||||
|
return await httpPostInterceptor(`/gallery-files/`, data, headers);
|
||||||
|
}
|
||||||
|
|
||||||
export async function deleteGalery(id: string) {
|
export async function deleteGalery(id: string) {
|
||||||
const headers = {
|
const headers = {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
};
|
};
|
||||||
return await httpDeleteInterceptor(`gallery-files/${id}`, headers);
|
return await httpDeleteInterceptor(`galleries/${id}`, headers);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue