fix: table agent,promo, form CRUD agent,
This commit is contained in:
parent
52a5d8f9eb
commit
274c41bf88
|
|
@ -126,193 +126,221 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<Dialog open={open} onOpenChange={onClose}>
|
||||||
<Dialog open={open} onOpenChange={onClose}>
|
<DialogContent className=" rounded-2xl p-0 overflow-hidden">
|
||||||
<DialogContent className=" rounded-2xl p-0 overflow-hidden">
|
{/* Header */}
|
||||||
{/* Header */}
|
<div className="bg-[#1F6779] text-white px-6 py-4">
|
||||||
<div className="bg-[#1F6779] text-white px-6 py-4">
|
<DialogTitle className="text-white">Detail Galeri</DialogTitle>
|
||||||
<DialogTitle className="text-white">Detail Galeri</DialogTitle>
|
<div className="flex items-center gap-2 mt-3">
|
||||||
|
<span
|
||||||
|
className={`text-xs font-medium px-3 py-1 rounded-full ${
|
||||||
|
data?.status_id === 1
|
||||||
|
? "bg-yellow-100 text-yellow-800"
|
||||||
|
: data?.status_id === 2
|
||||||
|
? "bg-green-100 text-green-800"
|
||||||
|
: data?.status_id === 3
|
||||||
|
? "bg-red-100 text-red-800"
|
||||||
|
: "bg-gray-100 text-gray-800"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{data?.status_id === 1
|
||||||
|
? "Menunggu"
|
||||||
|
: data?.status_id === 2
|
||||||
|
? "Disetujui"
|
||||||
|
: data?.status_id === 3
|
||||||
|
? "Ditolak"
|
||||||
|
: "Tidak Diketahui"}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="bg-white text-[#0F6C75] text-xs font-medium px-3 py-1 rounded-full">
|
||||||
|
Galeri
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* <span className="bg-white/20 text-white text-xs px-2 py-[2px] rounded-full">
|
||||||
|
{promo.position}
|
||||||
|
</span> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className=" py-3">
|
||||||
|
{/* Images List */}
|
||||||
|
<div className="mx-2">
|
||||||
|
<h2 className="text-2xl font-semibold text-black">
|
||||||
|
{data.title}
|
||||||
|
</h2>
|
||||||
|
<div className="my-3">
|
||||||
|
<p className="font-medium text-sm text-black">Deskripsi</p>
|
||||||
|
<p className="text-gray-800">{data.description}</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
{images.length === 0 && (
|
||||||
|
<p className="text-gray-500 col-span-3 text-center">
|
||||||
|
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 className=" py-3">
|
{/* Title */}
|
||||||
{/* Images List */}
|
|
||||||
<div className="mx-2">
|
{/* Deskripsi */}
|
||||||
<h2 className="text-2xl font-semibold text-black">
|
|
||||||
{data.title}
|
{/* Tanggal Upload */}
|
||||||
</h2>
|
<div className="mx-2">
|
||||||
<div className="my-3">
|
<p className="font-medium text-sm text-gray-700">
|
||||||
<p className="font-medium text-sm text-black">Deskripsi</p>
|
Tanggal Upload
|
||||||
<p className="text-gray-800">{data.description}</p>
|
</p>
|
||||||
</div>
|
<p className="text-gray-600">
|
||||||
<div className="grid grid-cols-3 gap-4">
|
{new Date(data.created_at).toLocaleDateString("id-ID")}
|
||||||
{images.length === 0 && (
|
</p>
|
||||||
<p className="text-gray-500 col-span-3 text-center">
|
</div>
|
||||||
Tidak ada gambar.
|
|
||||||
|
{/* Timeline */}
|
||||||
|
<div className="mx-2">
|
||||||
|
<h4 className="text-sm font-semibold text-gray-700 mb-3">
|
||||||
|
Status Timeline
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center">
|
||||||
|
<Check className="w-4 h-4 text-green-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-gray-800">
|
||||||
|
Diupload oleh Operator
|
||||||
</p>
|
</p>
|
||||||
)}
|
<p className="text-sm text-gray-500">
|
||||||
|
{convertDateFormat(data?.created_at)} WIB
|
||||||
{images.map((img) => (
|
</p>
|
||||||
<div
|
</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>
|
|
||||||
|
|
||||||
{/* Title */}
|
<div className="flex gap-3">
|
||||||
|
<div
|
||||||
{/* Deskripsi */}
|
className={`w-6 h-6 rounded-full flex items-center justify-center ${
|
||||||
|
data?.status_id === 1
|
||||||
{/* Tanggal Upload */}
|
? "bg-yellow-100"
|
||||||
<div className="mx-2">
|
: data?.status_id === 2
|
||||||
<p className="font-medium text-sm text-gray-700">
|
? "bg-green-100"
|
||||||
Tanggal Upload
|
: "bg-red-100"
|
||||||
</p>
|
}`}
|
||||||
<p className="text-gray-600">
|
>
|
||||||
{new Date(data.created_at).toLocaleDateString("id-ID")}
|
{data?.status_id === 1 ? (
|
||||||
</p>
|
<Clock className="w-4 h-4 text-yellow-700" />
|
||||||
</div>
|
) : data?.status_id === 2 ? (
|
||||||
|
|
||||||
{/* Timeline */}
|
|
||||||
<div className="mx-2">
|
|
||||||
<h4 className="text-sm font-semibold text-gray-700 mb-3">
|
|
||||||
Status Timeline
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<div className="w-6 h-6 rounded-full bg-green-100 flex items-center justify-center">
|
|
||||||
<Check className="w-4 h-4 text-green-600" />
|
<Check className="w-4 h-4 text-green-600" />
|
||||||
</div>
|
) : (
|
||||||
<div>
|
<X className="w-4 h-4 text-red-700" />
|
||||||
<p className="font-medium text-gray-800">
|
)}
|
||||||
Diupload oleh Operator
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-500">
|
|
||||||
{convertDateFormat(data?.created_at)} WIB
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-3">
|
<div>
|
||||||
<div
|
<p className="font-medium text-gray-800">
|
||||||
className={`w-6 h-6 rounded-full flex items-center justify-center ${
|
{data?.status_id === 1
|
||||||
data?.status_id === 1
|
? "Menunggu disetujui oleh Approver"
|
||||||
? "bg-yellow-100"
|
: data?.status_id === 2
|
||||||
: data?.status_id === 2
|
? "Disetujui oleh Approver"
|
||||||
? "bg-green-100"
|
: "Ditolak oleh Approver"}
|
||||||
: "bg-red-100"
|
</p>
|
||||||
}`}
|
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
{convertDateFormat(data?.updated_at)} WIB
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border rounded-lg px-3 py-3">
|
||||||
|
<p>Comment : </p>
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<button
|
||||||
|
onClick={handleOpenApproverHistory}
|
||||||
|
className="text-sm text-blue-600 hover:underline mt-2"
|
||||||
>
|
>
|
||||||
{data?.status_id === 1 ? (
|
View Approver History
|
||||||
<Clock className="w-4 h-4 text-yellow-700" />
|
</button>
|
||||||
) : data?.status_id === 2 ? (
|
<p>Jaecoo - Approver | 10/11/2026</p>
|
||||||
<Check className="w-4 h-4 text-green-600" />
|
|
||||||
) : (
|
|
||||||
<X className="w-4 h-4 text-red-700" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="font-medium text-gray-800">
|
|
||||||
{data?.status_id === 1
|
|
||||||
? "Menunggu disetujui oleh Approver"
|
|
||||||
: data?.status_id === 2
|
|
||||||
? "Disetujui oleh Approver"
|
|
||||||
: "Ditolak oleh Approver"}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p className="text-sm text-gray-500">
|
|
||||||
{convertDateFormat(data?.updated_at)} WIB
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border rounded-lg px-3 py-3">
|
|
||||||
<p>Comment : </p>
|
|
||||||
<div className="flex flex-row justify-between">
|
|
||||||
<button
|
|
||||||
onClick={handleOpenApproverHistory}
|
|
||||||
className="text-sm text-blue-600 hover:underline mt-2"
|
|
||||||
>
|
|
||||||
View Approver History
|
|
||||||
</button>
|
|
||||||
<p>Jaecoo - Approver | 10/11/2026</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{userRoleId !== "2" && data && (
|
</div>
|
||||||
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
|
{userRoleId !== "2" && data && (
|
||||||
{data.status_id === 1 ? (
|
<div className="flex justify-between items-center gap-3 px-6 py-4 border-t bg-[#F2F7FA]">
|
||||||
<>
|
{data.status_id === 1 ? (
|
||||||
<Button
|
<>
|
||||||
variant="secondary"
|
|
||||||
className="bg-blue-200 hover:bg-blue-400"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setOpenCommentModal(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Beri Tanggapan
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className=" w-[150]"
|
|
||||||
variant="destructive"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleRejectGalery(data.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reject
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{userRoleId === "1" && (
|
|
||||||
<Button
|
|
||||||
// variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="bg-green-600 hover:bg-green-700 text-white w-[150]"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleApproveGalery(data.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CheckCheck className="w-4 h-4 mr-1" />
|
|
||||||
Approve
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="mx-auto"
|
className="bg-blue-200 hover:bg-blue-400"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setOpenViewDialog(false);
|
setOpenCommentModal(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Tutup
|
Beri Tanggapan
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <DialogFooter className="px-6 pb-6">
|
<Button
|
||||||
|
className=" w-[150]"
|
||||||
|
variant="destructive"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleRejectGalery(data.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reject
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{userRoleId === "1" && (
|
||||||
|
<Button
|
||||||
|
// variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="bg-green-600 hover:bg-green-700 text-white w-[150]"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleApproveGalery(data.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CheckCheck className="w-4 h-4 mr-1" />
|
||||||
|
Approve
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
className="mx-auto"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setOpenViewDialog(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Tutup
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <DialogFooter className="px-6 pb-6">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="bg-gray-300 text-gray-700 px-6 py-2 rounded-lg"
|
className="bg-gray-300 text-gray-700 px-6 py-2 rounded-lg"
|
||||||
|
|
@ -320,9 +348,8 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
||||||
Tutup
|
Tutup
|
||||||
</button>
|
</button>
|
||||||
</DialogFooter> */}
|
</DialogFooter> */}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
|
||||||
{openApproverHistory && (
|
{openApproverHistory && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
|
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import Cookies from "js-cookie";
|
||||||
import { error, loading, success } from "@/config/swal";
|
import { error, loading, success } from "@/config/swal";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
import promo from "../landing-page/promo";
|
||||||
|
|
||||||
type PromoDetailDialogProps = {
|
type PromoDetailDialogProps = {
|
||||||
promoId: number | null;
|
promoId: number | null;
|
||||||
|
|
@ -187,17 +188,39 @@ export default function PromoDetailDialog({
|
||||||
<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">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="text-white text-xl font-semibold">
|
<DialogTitle className="text-white text-xl font-semibold">
|
||||||
Detail Promo
|
Detail Promoaa
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
{/* STATUS BADGE */}
|
<div className="flex items-center gap-2 mt-3">
|
||||||
{promo && (
|
<span
|
||||||
<div className="mt-2 bg-white/20 text-white px-4 py-[3px] rounded-full text-xs inline-flex items-center gap-2">
|
className={`text-xs font-medium px-3 py-1 rounded-full ${
|
||||||
<span className="w-2 h-2 rounded-full bg-lime-300"></span>
|
promo?.status_id === 1
|
||||||
{promo.status}
|
? "bg-yellow-100 text-yellow-800"
|
||||||
</div>
|
: promo?.status_id === 2
|
||||||
)}
|
? "bg-green-100 text-green-800"
|
||||||
|
: promo?.status_id === 3
|
||||||
|
? "bg-red-100 text-red-800"
|
||||||
|
: "bg-gray-100 text-gray-800"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{promo?.status_id === 1
|
||||||
|
? "Menunggu"
|
||||||
|
: promo?.status_id === 2
|
||||||
|
? "Disetujui"
|
||||||
|
: promo?.status_id === 3
|
||||||
|
? "Ditolak"
|
||||||
|
: "Tidak Diketahui"}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="bg-white text-[#0F6C75] text-xs font-medium px-3 py-1 rounded-full">
|
||||||
|
Promo
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* <span className="bg-white/20 text-white text-xs px-2 py-[2px] rounded-full">
|
||||||
|
{promo.position}
|
||||||
|
</span> */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* BODY */}
|
{/* BODY */}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,10 @@ import { UploadCloud } from "lucide-react";
|
||||||
|
|
||||||
type AgentFormValues = {
|
type AgentFormValues = {
|
||||||
fullName: string;
|
fullName: string;
|
||||||
|
job_title: string;
|
||||||
position: string;
|
position: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
roles: string[];
|
agent_type: string[];
|
||||||
profileImage: File | null;
|
profileImage: File | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -40,9 +41,10 @@ export default function AddAgentForm() {
|
||||||
const form = useForm<AgentFormValues>({
|
const form = useForm<AgentFormValues>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
fullName: "",
|
fullName: "",
|
||||||
|
job_title: "",
|
||||||
position: "",
|
position: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
roles: [],
|
agent_type: [],
|
||||||
profileImage: null,
|
profileImage: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -60,13 +62,13 @@ export default function AddAgentForm() {
|
||||||
const onSubmit = async (data: AgentFormValues) => {
|
const onSubmit = async (data: AgentFormValues) => {
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
formData.append("name", data.fullName);
|
formData.append("name", data.fullName);
|
||||||
formData.append("job_title", data.position);
|
formData.append("job_title", data.job_title);
|
||||||
formData.append("phone", data.phone);
|
formData.append("phone", data.phone);
|
||||||
|
|
||||||
data.roles.forEach((role) => {
|
// ✅ FIX UTAMA
|
||||||
formData.append("agent_type[]", role);
|
formData.append("agent_type", JSON.stringify(data.agent_type));
|
||||||
});
|
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
|
|
@ -122,7 +124,7 @@ export default function AddAgentForm() {
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="position"
|
name="job_title"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
|
|
@ -169,7 +171,7 @@ export default function AddAgentForm() {
|
||||||
<FormField
|
<FormField
|
||||||
key={role}
|
key={role}
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="roles"
|
name="agent_type"
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
const selected = field.value || [];
|
const selected = field.value || [];
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,16 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
|
||||||
<div className="flex gap-6">
|
<div className="flex gap-6">
|
||||||
{AGENT_TYPES.map((item) => (
|
{AGENT_TYPES.map((item) => (
|
||||||
<div key={item.key} className="flex items-center gap-2">
|
<div key={item.key} className="flex items-center gap-2">
|
||||||
<Checkbox checked={data.job_title === item.key} disabled />
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
Array.isArray(data.agent_type) &&
|
||||||
|
data.agent_type
|
||||||
|
.map((v: string) => v.toLowerCase())
|
||||||
|
.includes(item.label.toLowerCase())
|
||||||
|
}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
|
||||||
<span>{item.label}</span>
|
<span>{item.label}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -230,7 +239,7 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
|
||||||
<Label className="mb-2 block">Foto Profile</Label>
|
<Label className="mb-2 block">Foto Profile</Label>
|
||||||
<div className="w-24 h-24 rounded-lg overflow-hidden border">
|
<div className="w-24 h-24 rounded-lg overflow-hidden border">
|
||||||
<Image
|
<Image
|
||||||
src={data.profile_picture_url}
|
src={data?.profile_picture_url}
|
||||||
alt="Profile"
|
alt="Profile"
|
||||||
width={96}
|
width={96}
|
||||||
height={96}
|
height={96}
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,13 @@ import { X } from "lucide-react";
|
||||||
|
|
||||||
type AgentFormValues = {
|
type AgentFormValues = {
|
||||||
fullName: string;
|
fullName: string;
|
||||||
|
job_title: string;
|
||||||
position: string;
|
position: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
roles: string[];
|
roles: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const AGENT_TYPES = ["after-Sales", "sales", "spv", "branch_manager"];
|
const AGENT_TYPES = ["After Sales", "Sales", "Spv", "Branch Manager"];
|
||||||
|
|
||||||
export default function UpdateAgentForm({ id }: { id: number }) {
|
export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -57,9 +58,9 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
|
|
||||||
form.reset({
|
form.reset({
|
||||||
fullName: agent.name,
|
fullName: agent.name,
|
||||||
position: agent.job_title,
|
job_title: agent.job_title,
|
||||||
phone: agent.phone,
|
phone: agent.phone,
|
||||||
roles: agent.job_title ? [agent.job_title] : [],
|
roles: Array.isArray(agent.agent_type) ? agent.agent_type : [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ FOTO DARI API
|
// ✅ FOTO DARI API
|
||||||
|
|
@ -92,14 +93,23 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
const onSubmit = async (data: AgentFormValues) => {
|
const onSubmit = async (data: AgentFormValues) => {
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
formData.append("name", data.fullName);
|
formData.append("name", data.fullName);
|
||||||
formData.append("job_title", data.roles[0]); // single role
|
formData.append("job_title", data.job_title);
|
||||||
formData.append("phone", data.phone);
|
formData.append("phone", data.phone);
|
||||||
|
|
||||||
|
// ✅ MULTI agent_type
|
||||||
|
formData.append("agent_type", JSON.stringify(data.roles));
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
for (const pair of formData.entries()) {
|
||||||
|
console.log(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
|
||||||
await updateAgent(id, formData);
|
await updateAgent(id, formData);
|
||||||
|
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
|
|
@ -137,12 +147,12 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="position"
|
name="job_title"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Jabatan</FormLabel>
|
<FormLabel>Jabatan</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} readOnly />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|
@ -171,21 +181,27 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
||||||
key={role}
|
key={role}
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="roles"
|
name="roles"
|
||||||
render={({ field }) => (
|
render={({ field }) => {
|
||||||
<FormItem className="flex items-center gap-2">
|
const selected = field.value || [];
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
return (
|
||||||
checked={field.value?.includes(role)}
|
<FormItem className="flex items-center gap-2">
|
||||||
onCheckedChange={(checked) =>
|
<FormControl>
|
||||||
field.onChange(checked ? [role] : [])
|
<Checkbox
|
||||||
}
|
checked={selected.includes(role)}
|
||||||
/>
|
onCheckedChange={(checked) => {
|
||||||
</FormControl>
|
const updated = checked
|
||||||
<span className="capitalize">
|
? [...selected, role] // ➕ tambah
|
||||||
{role.replace("_", " ")}
|
: selected.filter((r) => r !== role); // ➖ hapus
|
||||||
</span>
|
|
||||||
</FormItem>
|
field.onChange(updated);
|
||||||
)}
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<span>{role}</span>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
|
||||||
<Card className="w-full border-none shadow-md">
|
<Card className="w-full border-none shadow-md">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-xl font-bold text-teal-900">
|
<CardTitle className="text-xl font-bold text-teal-900">
|
||||||
Edit Produk
|
Detail Produk
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,15 @@ export type OptionProps = {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Option = ({ Icon, title, selected, setSelected, open, notifs, active }: OptionProps) => {
|
const Option = ({
|
||||||
|
Icon,
|
||||||
|
title,
|
||||||
|
selected,
|
||||||
|
setSelected,
|
||||||
|
open,
|
||||||
|
notifs,
|
||||||
|
active,
|
||||||
|
}: OptionProps) => {
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
const isActive = active ?? selected === title;
|
const isActive = active ?? selected === title;
|
||||||
|
|
||||||
|
|
@ -23,7 +31,7 @@ const Option = ({ Icon, title, selected, setSelected, open, notifs, active }: Op
|
||||||
onMouseLeave={() => setHovered(false)}
|
onMouseLeave={() => setHovered(false)}
|
||||||
className={`relative flex h-12 w-full px-3 items-center rounded-xl transition-all duration-200 cursor-pointer group ${
|
className={`relative flex h-12 w-full px-3 items-center rounded-xl transition-all duration-200 cursor-pointer group ${
|
||||||
isActive
|
isActive
|
||||||
? "bg-gradient-to-r from-emerald-500 to-green-500 text-white shadow-lg shadow-emerald-500/25"
|
? "bg-gradient-to-r from-[#1F6779] to-[#1F6779] text-white shadow-lg shadow-emerald-500/25"
|
||||||
: "text-slate-600 hover:bg-gradient-to-r hover:from-slate-100 hover:to-slate-200/50 hover:text-slate-800"
|
: "text-slate-600 hover:bg-gradient-to-r hover:from-slate-100 hover:to-slate-200/50 hover:text-slate-800"
|
||||||
}`}
|
}`}
|
||||||
whileHover={{ scale: 1.02 }}
|
whileHover={{ scale: 1.02 }}
|
||||||
|
|
@ -46,11 +54,13 @@ const Option = ({ Icon, title, selected, setSelected, open, notifs, active }: Op
|
||||||
open ? "w-12" : "w-full"
|
open ? "w-12" : "w-full"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className={`text-lg transition-all duration-200 ${
|
<div
|
||||||
isActive
|
className={`text-lg transition-all duration-200 ${
|
||||||
? "text-white"
|
isActive
|
||||||
: "text-slate-500 group-hover:text-slate-700"
|
? "text-white"
|
||||||
}`}>
|
: "text-slate-500 group-hover:text-slate-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<Icon />
|
<Icon />
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -93,9 +103,7 @@ const Option = ({ Icon, title, selected, setSelected, open, notifs, active }: Op
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
transition={{ delay: 0.3, type: "spring" }}
|
transition={{ delay: 0.3, type: "spring" }}
|
||||||
className={`absolute right-3 top-1/2 -translate-y-1/2 size-5 rounded-full text-xs font-semibold flex items-center justify-center ${
|
className={`absolute right-3 top-1/2 -translate-y-1/2 size-5 rounded-full text-xs font-semibold flex items-center justify-center ${
|
||||||
isActive
|
isActive ? "bg-white text-emerald-500" : "bg-red-500 text-white"
|
||||||
? "bg-white text-emerald-500"
|
|
||||||
: "bg-red-500 text-white"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{notifs}
|
{notifs}
|
||||||
|
|
|
||||||
|
|
@ -242,9 +242,9 @@ const SidebarContent = ({
|
||||||
>
|
>
|
||||||
<p className="text-lg font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-white dark:text-white">
|
<p className="text-lg font-bold bg-gradient-to-r from-slate-800 to-slate-600 bg-clip-text text-white dark:text-white">
|
||||||
JAECOO{" "}
|
JAECOO{" "}
|
||||||
<span className="text-lg font-normal">
|
{/* <span className="text-lg font-normal">
|
||||||
{userRoleId != null ? roleLabel[userRoleId] : ""}
|
{userRoleId != null ? roleLabel[userRoleId] : ""}
|
||||||
</span>
|
</span> */}
|
||||||
</p>
|
</p>
|
||||||
{/* <span className="text-xs text-slate-500">Admin Panel</span> */}
|
{/* <span className="text-xs text-slate-500">Admin Panel</span> */}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ export default function AgentTable() {
|
||||||
<div className="w-full overflow-x-auto rounded-2xl shadow-sm border border-gray-200">
|
<div className="w-full overflow-x-auto rounded-2xl shadow-sm border border-gray-200">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="bg-[#0F6C75] text-white text-lg rounded-t-sm px-6 py-3">
|
<div className="bg-[#0F6C75] text-white text-lg rounded-t-sm px-6 py-3">
|
||||||
Daftar Product
|
Daftar Agen
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
|
|
|
||||||
|
|
@ -104,14 +104,14 @@ export default function CostumerServiceTable() {
|
||||||
endDate: null,
|
endDate: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [userLevelId, setUserLevelId] = useState<string | null>(null);
|
const [userRoleId, setUserRoleId] = useState<string | null>(null);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// 🔹 Ambil userlevelId dari cookies
|
// 🔹 Ambil userlevelId dari cookies
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ulne = Cookies.get("ulne"); // contoh: "3"
|
const urie = Cookies.get("urie"); // contoh: "3"
|
||||||
setUserLevelId(ulne ?? null);
|
setUserRoleId(urie ?? null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -354,7 +354,7 @@ export default function CostumerServiceTable() {
|
||||||
Sales
|
Sales
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
{userLevelId !== "3" && (
|
{userRoleId !== "1" && (
|
||||||
<Link href={"/admin/product/create"}>
|
<Link href={"/admin/product/create"}>
|
||||||
<Button className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2">
|
<Button className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2">
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
|
|
|
||||||
|
|
@ -383,7 +383,7 @@ export default function PromotionTable() {
|
||||||
<div className="w-full overflow-x-auto rounded-2xl shadow-sm border border-gray-200">
|
<div className="w-full overflow-x-auto rounded-2xl shadow-sm border border-gray-200">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="bg-[#0F6C75] text-white text-lg rounded-t-sm px-6 py-3">
|
<div className="bg-[#0F6C75] text-white text-lg rounded-t-sm px-6 py-3">
|
||||||
Daftar Product
|
Daftar Promo
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
|
|
|
||||||
|
|
@ -104,14 +104,14 @@ export default function ServicesTable() {
|
||||||
endDate: null,
|
endDate: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [userLevelId, setUserLevelId] = useState<string | null>(null);
|
const [userRoleId, setUserRoleId] = useState<string | null>(null);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// 🔹 Ambil userlevelId dari cookies
|
// 🔹 Ambil userlevelId dari cookies
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ulne = Cookies.get("ulne"); // contoh: "3"
|
const urie = Cookies.get("urie"); // contoh: "3"
|
||||||
setUserLevelId(ulne ?? null);
|
setUserRoleId(urie ?? null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -354,7 +354,7 @@ export default function ServicesTable() {
|
||||||
After Sales
|
After Sales
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
{userLevelId !== "3" && (
|
{userRoleId !== "1" && (
|
||||||
<Link href={"/admin/product/create"}>
|
<Link href={"/admin/product/create"}>
|
||||||
<Button className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2">
|
<Button className="bg-[#1F6779] text-white w-full lg:w-fit hover:bg-[#1a9bb5] flex items-center gap-2">
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue