fix: table agent,promo, form CRUD agent,
This commit is contained in:
parent
a5360837c7
commit
9abbf06fc9
|
|
@ -127,15 +127,44 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
return (
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={onClose}>
|
||||
<DialogContent className="max-w-3xl rounded-2xl p-0 overflow-hidden">
|
||||
<DialogContent className=" rounded-2xl p-0 overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="bg-[#1F6779] text-white px-6 py-4">
|
||||
<DialogTitle className="text-white">Detail Galeri</DialogTitle>
|
||||
<div className="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="px-6 py-3">
|
||||
<div className=" py-3">
|
||||
{/* Images List */}
|
||||
<div>
|
||||
<div className="mx-2">
|
||||
<h2 className="text-2xl font-semibold text-black">
|
||||
{data.title}
|
||||
</h2>
|
||||
|
|
@ -176,7 +205,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
{/* Deskripsi */}
|
||||
|
||||
{/* Tanggal Upload */}
|
||||
<div>
|
||||
<div className="mx-2">
|
||||
<p className="font-medium text-sm text-gray-700">
|
||||
Tanggal Upload
|
||||
</p>
|
||||
|
|
@ -186,7 +215,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
<div>
|
||||
<div className="mx-2">
|
||||
<h4 className="text-sm font-semibold text-gray-700 mb-3">
|
||||
Status Timeline
|
||||
</h4>
|
||||
|
|
@ -270,7 +299,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
</Button>
|
||||
|
||||
<Button
|
||||
className=" w-[180]"
|
||||
className=" w-[150]"
|
||||
variant="destructive"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -284,7 +313,7 @@ export function DialogDetailGaleri({ open, onClose, data }: any) {
|
|||
<Button
|
||||
// variant="ghost"
|
||||
size="sm"
|
||||
className="bg-green-600 hover:bg-green-700 text-white w-[180]"
|
||||
className="bg-green-600 hover:bg-green-700 text-white w-[150]"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleApproveGalery(data.id);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import Cookies from "js-cookie";
|
|||
import { error, loading, success } from "@/config/swal";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import promo from "../landing-page/promo";
|
||||
|
||||
type PromoDetailDialogProps = {
|
||||
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">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white text-xl font-semibold">
|
||||
Detail Promo
|
||||
Detail Promoaa
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
{/* STATUS BADGE */}
|
||||
{promo && (
|
||||
<div className="mt-2 bg-white/20 text-white px-4 py-[3px] rounded-full text-xs inline-flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-lime-300"></span>
|
||||
{promo.status}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2 mt-3">
|
||||
<span
|
||||
className={`text-xs font-medium px-3 py-1 rounded-full ${
|
||||
promo?.status_id === 1
|
||||
? "bg-yellow-100 text-yellow-800"
|
||||
: 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>
|
||||
|
||||
{/* BODY */}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@ import { UploadCloud } from "lucide-react";
|
|||
|
||||
type AgentFormValues = {
|
||||
fullName: string;
|
||||
job_title: string;
|
||||
position: string;
|
||||
phone: string;
|
||||
roles: string[];
|
||||
agent_type: string[];
|
||||
profileImage: File | null;
|
||||
};
|
||||
|
||||
|
|
@ -40,9 +41,10 @@ export default function AddAgentForm() {
|
|||
const form = useForm<AgentFormValues>({
|
||||
defaultValues: {
|
||||
fullName: "",
|
||||
job_title: "",
|
||||
position: "",
|
||||
phone: "",
|
||||
roles: [],
|
||||
agent_type: [],
|
||||
profileImage: null,
|
||||
},
|
||||
});
|
||||
|
|
@ -60,13 +62,13 @@ export default function AddAgentForm() {
|
|||
const onSubmit = async (data: AgentFormValues) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("name", data.fullName);
|
||||
formData.append("job_title", data.position);
|
||||
formData.append("job_title", data.job_title);
|
||||
formData.append("phone", data.phone);
|
||||
|
||||
data.roles.forEach((role) => {
|
||||
formData.append("agent_type[]", role);
|
||||
});
|
||||
// ✅ FIX UTAMA
|
||||
formData.append("agent_type", JSON.stringify(data.agent_type));
|
||||
|
||||
if (file) {
|
||||
formData.append("file", file);
|
||||
|
|
@ -122,7 +124,7 @@ export default function AddAgentForm() {
|
|||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="position"
|
||||
name="job_title"
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
|
|
@ -169,7 +171,7 @@ export default function AddAgentForm() {
|
|||
<FormField
|
||||
key={role}
|
||||
control={form.control}
|
||||
name="roles"
|
||||
name="agent_type"
|
||||
render={({ field }) => {
|
||||
const selected = field.value || [];
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -218,7 +218,16 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
|
|||
<div className="flex gap-6">
|
||||
{AGENT_TYPES.map((item) => (
|
||||
<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>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -230,7 +239,7 @@ export default function DetailAgentForm(props: { isDetail: boolean }) {
|
|||
<Label className="mb-2 block">Foto Profile</Label>
|
||||
<div className="w-24 h-24 rounded-lg overflow-hidden border">
|
||||
<Image
|
||||
src={data.profile_picture_url}
|
||||
src={data?.profile_picture_url}
|
||||
alt="Profile"
|
||||
width={96}
|
||||
height={96}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@ import { X } from "lucide-react";
|
|||
|
||||
type AgentFormValues = {
|
||||
fullName: string;
|
||||
job_title: string;
|
||||
position: string;
|
||||
phone: 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 }) {
|
||||
const router = useRouter();
|
||||
|
|
@ -57,9 +58,9 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
|||
|
||||
form.reset({
|
||||
fullName: agent.name,
|
||||
position: agent.job_title,
|
||||
job_title: agent.job_title,
|
||||
phone: agent.phone,
|
||||
roles: agent.job_title ? [agent.job_title] : [],
|
||||
roles: Array.isArray(agent.agent_type) ? agent.agent_type : [],
|
||||
});
|
||||
|
||||
// ✅ FOTO DARI API
|
||||
|
|
@ -92,14 +93,23 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
|||
const onSubmit = async (data: AgentFormValues) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
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);
|
||||
|
||||
// ✅ MULTI agent_type
|
||||
formData.append("agent_type", JSON.stringify(data.roles));
|
||||
|
||||
if (file) {
|
||||
formData.append("file", file);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
for (const pair of formData.entries()) {
|
||||
console.log(pair[0], pair[1]);
|
||||
}
|
||||
|
||||
await updateAgent(id, formData);
|
||||
|
||||
MySwal.fire({
|
||||
|
|
@ -137,12 +147,12 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
|||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="position"
|
||||
name="job_title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Jabatan</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} readOnly />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
|
|
@ -171,21 +181,27 @@ export default function UpdateAgentForm({ id }: { id: number }) {
|
|||
key={role}
|
||||
control={form.control}
|
||||
name="roles"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center gap-2">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(role)}
|
||||
onCheckedChange={(checked) =>
|
||||
field.onChange(checked ? [role] : [])
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<span className="capitalize">
|
||||
{role.replace("_", " ")}
|
||||
</span>
|
||||
</FormItem>
|
||||
)}
|
||||
render={({ field }) => {
|
||||
const selected = field.value || [];
|
||||
|
||||
return (
|
||||
<FormItem className="flex items-center gap-2">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={selected.includes(role)}
|
||||
onCheckedChange={(checked) => {
|
||||
const updated = checked
|
||||
? [...selected, role] // ➕ tambah
|
||||
: selected.filter((r) => r !== role); // ➖ hapus
|
||||
|
||||
field.onChange(updated);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<span>{role}</span>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ export default function DetailProductForm(props: { isDetail: boolean }) {
|
|||
<Card className="w-full border-none shadow-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl font-bold text-teal-900">
|
||||
Edit Produk
|
||||
Detail Produk
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
|
|
|
|||
|
|
@ -245,9 +245,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">
|
||||
JAECOO{" "}
|
||||
<span className="text-lg font-normal">
|
||||
{/* <span className="text-lg font-normal">
|
||||
{userRoleId != null ? roleLabel[userRoleId] : ""}
|
||||
</span>
|
||||
</span> */}
|
||||
</p>
|
||||
{/* <span className="text-xs text-slate-500">Admin Panel</span> */}
|
||||
</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">
|
||||
{/* Header */}
|
||||
<div className="bg-[#0F6C75] text-white text-lg rounded-t-sm px-6 py-3">
|
||||
Daftar Product
|
||||
Daftar Agen
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
|
|
|
|||
|
|
@ -104,14 +104,14 @@ export default function CostumerServiceTable() {
|
|||
endDate: null,
|
||||
});
|
||||
|
||||
const [userLevelId, setUserLevelId] = useState<string | null>(null);
|
||||
const [userRoleId, setUserRoleId] = useState<string | null>(null);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 🔹 Ambil userlevelId dari cookies
|
||||
useEffect(() => {
|
||||
const ulne = Cookies.get("ulne"); // contoh: "3"
|
||||
setUserLevelId(ulne ?? null);
|
||||
const urie = Cookies.get("urie"); // contoh: "3"
|
||||
setUserRoleId(urie ?? null);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -320,7 +320,7 @@ export default function CostumerServiceTable() {
|
|||
return cellValue;
|
||||
}
|
||||
},
|
||||
[article, page]
|
||||
[article, page],
|
||||
);
|
||||
|
||||
let typingTimer: NodeJS.Timeout;
|
||||
|
|
@ -354,7 +354,7 @@ export default function CostumerServiceTable() {
|
|||
Sales
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
{userLevelId !== "3" && (
|
||||
{userRoleId !== "1" && (
|
||||
<Link href={"/admin/product/create"}>
|
||||
<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" />
|
||||
|
|
|
|||
|
|
@ -522,7 +522,7 @@ export default function ProductTable() {
|
|||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{userRoleId === "1" && item.status_id === 1 && (
|
||||
{/* {userRoleId === "1" && item.status_id === 1 && (
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
|
@ -543,7 +543,7 @@ export default function ProductTable() {
|
|||
Reject
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
)} */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ export default function PromotionTable() {
|
|||
<div className="w-full overflow-x-auto rounded-2xl shadow-sm border border-gray-200">
|
||||
{/* Header */}
|
||||
<div className="bg-[#0F6C75] text-white text-lg rounded-t-sm px-6 py-3">
|
||||
Daftar Product
|
||||
Daftar Promo
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
|
|
|
|||
|
|
@ -104,14 +104,14 @@ export default function ServicesTable() {
|
|||
endDate: null,
|
||||
});
|
||||
|
||||
const [userLevelId, setUserLevelId] = useState<string | null>(null);
|
||||
const [userRoleId, setUserRoleId] = useState<string | null>(null);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 🔹 Ambil userlevelId dari cookies
|
||||
useEffect(() => {
|
||||
const ulne = Cookies.get("ulne"); // contoh: "3"
|
||||
setUserLevelId(ulne ?? null);
|
||||
const urie = Cookies.get("urie"); // contoh: "3"
|
||||
setUserRoleId(urie ?? null);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -320,7 +320,7 @@ export default function ServicesTable() {
|
|||
return cellValue;
|
||||
}
|
||||
},
|
||||
[article, page]
|
||||
[article, page],
|
||||
);
|
||||
|
||||
let typingTimer: NodeJS.Timeout;
|
||||
|
|
@ -354,7 +354,7 @@ export default function ServicesTable() {
|
|||
After Sales
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
{userLevelId !== "3" && (
|
||||
{userRoleId !== "1" && (
|
||||
<Link href={"/admin/product/create"}>
|
||||
<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" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue