This commit is contained in:
Sabda Yagra 2025-10-11 17:07:14 +07:00
parent e21ff675a9
commit e66e77e796
36 changed files with 1724 additions and 890 deletions

View File

@ -9,7 +9,7 @@ import Link from "next/link";
const ReactTableAudioPage = () => {
return (
<div className="min-h-screen bg-gray-50">
{/* <SiteBreadcrumb /> */}
<SiteBreadcrumb />
<div className="p-6">
<div className="max-w-7xl mx-auto">
<Card className="shadow-sm border-0">

View File

@ -107,7 +107,6 @@ const TableImage = () => {
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState("");
const roleId = getCookiesDecrypt("urie");
const columns = useTableColumns();
const table = useReactTable({

View File

@ -9,7 +9,7 @@ import Link from "next/link";
const ReactTableImagePage = () => {
return (
<div className="min-h-screen bg-gray-50">
{/* <SiteBreadcrumb /> */}
<SiteBreadcrumb />
<div className="p-6">
<div className="max-w-7xl mx-auto">
<Card className="shadow-sm border-0">

View File

@ -259,7 +259,7 @@ const useTableColumns = () => {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link href={`/admin/content/document/detail/${row.original.id}`}>
<Link href={`/admin/content/text/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
@ -268,7 +268,7 @@ const useTableColumns = () => {
{(Number(row.original.uploadedById) === Number(userId) ||
isMabesApprover) && (
<Link
href={`/admin/content/document/update/${row.original.id}`}
href={`/admin/content/text/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />

View File

@ -26,7 +26,7 @@ const ReactTableDocumentPage = () => {
</div>
</div>
<div className="flex-none">
<Link href={"/admin/content/document/create"}>
<Link href={"/admin/content/text/create"}>
<Button color="primary" className="text-white shadow-sm hover:shadow-md transition-shadow">
<UploadIcon size={18} className="mr-2" />
Create Document

View File

@ -254,7 +254,7 @@ const useTableColumns = () => {
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/admin/content/audio-visual/detail/${row.original.id}`}
href={`/admin/content/video/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group rounded-none">
<Eye className="w-4 h-4 me-1.5" />
@ -272,7 +272,7 @@ const useTableColumns = () => {
{/* {(Number(row.original.uploadedById) === Number(userId) ||
isMabesApprover) && ( */}
<Link
href={`/admin/content/audio-visual/update/${row.original.id}`}
href={`/admin/content/video/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />

View File

@ -1,9 +1,10 @@
import FormVideoDetail from "@/components/form/content/audio-visual/video-detail-form";
import SiteBreadcrumb from "@/components/site-breadcrumb";
const VideoDetailPage = async () => {
return (
<div>
{/* <SiteBreadcrumb /> */}
<SiteBreadcrumb />
<div className="space-y-4">
<FormVideoDetail />
</div>

View File

@ -9,7 +9,7 @@ import Link from "next/link";
const ReactTableAudioVisualPage = () => {
return (
<div className="min-h-screen bg-gray-50">
{/* <SiteBreadcrumb /> */}
<SiteBreadcrumb />
<div className="p-6">
<div className="max-w-7xl mx-auto">
<Card className="shadow-sm border-0">
@ -26,7 +26,7 @@ const ReactTableAudioVisualPage = () => {
</div>
</div>
<div className="flex-none">
<Link href={"/admin/content/audio-visual/create"}>
<Link href={"/admin/content/video/create"}>
<Button color="primary" className="text-white shadow-sm hover:shadow-md transition-shadow">
<UploadIcon size={18} className="mr-2" />
Create Audio-Visual

View File

@ -296,7 +296,7 @@ const useTableColumns = (onEdit?: (data: any) => void) => {
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setOpen(false); // ⬅️ tutup dropdown manual
setOpen(false);
onEdit?.(row.original);
}}
className="p-2 border-b text-default-700 rounded-none cursor-pointer"
@ -307,7 +307,7 @@ const useTableColumns = (onEdit?: (data: any) => void) => {
<DropdownMenuItem
onClick={() => {
setOpen(false); // ⬅️ pastikan dropdown tertutup sebelum alert
setOpen(false);
handleDeleteMedia(row.original.id);
}}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-white rounded-none"
@ -338,7 +338,7 @@ const useTableColumns = (onEdit?: (data: any) => void) => {
</p>
<p>
<span className="font-medium">Parent Level:</span>{" "}
{detailData.parentLevelName || "-"}
{detailData.parentLevelId || "-"}
</p>
<p>
<span className="font-medium">Created At:</span>{" "}

View File

@ -57,6 +57,7 @@ import useTableColumns from "./columns";
import TenantUpdateForm from "@/components/form/tenant/tenant-update-form";
import { errorAutoClose, successAutoClose } from "@/lib/swal";
import { close, loading } from "@/config/swal";
import DetailTenant from "@/components/form/tenant/tenant-detail-update-form";
function TenantSettingsContentTable() {
const [activeTab, setActiveTab] = useState("workflows");
@ -217,7 +218,14 @@ function TenantSettingsContentTable() {
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger
value="tenant"
className="flex items-center gap-2 border rounded-lg"
>
<WorkflowIcon className="h-4 w-4" />
Detail Tenant
</TabsTrigger>
<TabsTrigger
value="workflows"
className="flex items-center gap-2 border rounded-lg"
@ -235,6 +243,9 @@ function TenantSettingsContentTable() {
</TabsList>
{/* Approval Workflows Tab */}
<TabsContent value="tenant" className="space-y-6 border rounded-lg">
<DetailTenant id={10} />
</TabsContent>
<TabsContent value="workflows" className="space-y-6 border rounded-lg">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-semibold ml-2 mt-4">
@ -718,90 +729,92 @@ function TenantSettingsContentTable() {
<CardHeader className="cursor-pointer hover:bg-gray-50 transition-colors">
<CardTitle className="flex items-center justify-between">
<div className="flex items-center gap-2">
<UsersIcon className="h-5 w-5" />
User Levels Hierarchy
<UsersIcon className="h-5 w-5" />
User Levels Hierarchy
</div>
{isHierarchyExpanded ? (
<ChevronUpIcon className="h-4 w-4" />
) : (
<ChevronDownIcon className="h-4 w-4" />
)}
</CardTitle>
</CardHeader>
</CardTitle>
</CardHeader>
</CollapsibleTrigger>
<CollapsibleContent>
<CardContent>
<div className="space-y-3">
{userLevels
.filter((ul) => !ul.parentLevelId) // Root levels
.sort((a, b) => a.levelNumber - b.levelNumber)
.map((rootLevel) => (
<div key={rootLevel.id} className="space-y-2">
{/* Root Level */}
<div className="flex items-center gap-3 p-3 bg-blue-50 rounded-lg border-l-4 border-blue-500">
<div className="w-8 h-8 bg-blue-100 text-blue-600 rounded-full flex items-center justify-center text-sm font-medium">
{rootLevel.levelNumber}
</div>
<div className="flex-1">
<div className="font-medium">{rootLevel.name}</div>
<div className="text-sm text-gray-500">
{rootLevel.aliasName} {" "}
{rootLevel.group || "No group"}
</div>
</div>
<div className="flex items-center gap-2">
{rootLevel.isActive && (
<span className="px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full">
Active
</span>
)}
{rootLevel.isApprovalActive && (
<span className="px-2 py-1 text-xs bg-purple-100 text-purple-800 rounded-full">
Approval Active
</span>
)}
</div>
</div>
{/* Child Levels */}
{userLevels
.filter((ul) => ul.parentLevelId === rootLevel.id)
.sort((a, b) => a.levelNumber - b.levelNumber)
.map((childLevel) => (
<div
key={childLevel.id}
className="ml-8 flex items-center gap-3 p-3 bg-gray-50 rounded-lg border-l-4 border-gray-300"
>
<div className="w-6 h-6 bg-gray-100 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">
{childLevel.levelNumber}
<CardContent>
<div className="space-y-3">
{userLevels
.filter((ul) => !ul.parentLevelId) // Root levels
.sort((a, b) => a.levelNumber - b.levelNumber)
.map((rootLevel) => (
<div key={rootLevel.id} className="space-y-2">
{/* Root Level */}
<div className="flex items-center gap-3 p-3 bg-blue-50 rounded-lg border-l-4 border-blue-500">
<div className="w-8 h-8 bg-blue-100 text-blue-600 rounded-full flex items-center justify-center text-sm font-medium">
{rootLevel.levelNumber}
</div>
<div className="flex-1">
<div className="font-medium text-sm">
{childLevel.name}
<div className="font-medium">
{rootLevel.name}
</div>
<div className="text-xs text-gray-500">
{childLevel.aliasName} {" "}
{childLevel.group || "No group"}
<div className="text-sm text-gray-500">
{rootLevel.aliasName} {" "}
{rootLevel.group || "No group"}
</div>
</div>
<div className="flex items-center gap-1">
{childLevel.isActive && (
<span className="px-1 py-0.5 text-xs bg-green-100 text-green-800 rounded-full">
<div className="flex items-center gap-2">
{rootLevel.isActive && (
<span className="px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full">
Active
</span>
)}
{childLevel.isApprovalActive && (
<span className="px-1 py-0.5 text-xs bg-purple-100 text-purple-800 rounded-full">
Approval
{rootLevel.isApprovalActive && (
<span className="px-2 py-1 text-xs bg-purple-100 text-purple-800 rounded-full">
Approval Active
</span>
)}
</div>
</div>
))}
</div>
))}
</div>
</CardContent>
{/* Child Levels */}
{userLevels
.filter((ul) => ul.parentLevelId === rootLevel.id)
.sort((a, b) => a.levelNumber - b.levelNumber)
.map((childLevel) => (
<div
key={childLevel.id}
className="ml-8 flex items-center gap-3 p-3 bg-gray-50 rounded-lg border-l-4 border-gray-300"
>
<div className="w-6 h-6 bg-gray-100 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">
{childLevel.levelNumber}
</div>
<div className="flex-1">
<div className="font-medium text-sm">
{childLevel.name}
</div>
<div className="text-xs text-gray-500">
{childLevel.aliasName} {" "}
{childLevel.group || "No group"}
</div>
</div>
<div className="flex items-center gap-1">
{childLevel.isActive && (
<span className="px-1 py-0.5 text-xs bg-green-100 text-green-800 rounded-full">
Active
</span>
)}
{childLevel.isApprovalActive && (
<span className="px-1 py-0.5 text-xs bg-purple-100 text-purple-800 rounded-full">
Approval
</span>
)}
</div>
</div>
))}
</div>
))}
</div>
</CardContent>
</CollapsibleContent>
</Collapsible>
</Card>

View File

@ -8,7 +8,7 @@ export default function Home() {
return (
<div className="relative min-h-screen font-[family-name:var(--font-geist-sans)]">
<div className="relative z-10 bg-white w-full mx-auto">
<Navbar />
{/* <Navbar /> */}
<div className="flex-1">
<Header />
</div>

File diff suppressed because it is too large Load Diff

View File

@ -152,10 +152,8 @@ export default function FormVideo() {
const options: Option[] = [
{ id: "all", label: "SEMUA" },
{ id: "5", label: "UMUM" },
{ id: "6", label: "JOURNALIS" },
{ id: "7", label: "POLRI" },
{ id: "8", label: "KSP" },
{ id: "4", label: "UMUM" },
{ id: "5", label: "JOURNALIS" },
];
const MAX_FILE_SIZE = 100 * 1024 * 1024;
@ -654,7 +652,7 @@ export default function FormVideo() {
.replace(/[^a-z0-9-]/g, ""),
tags: finalTags,
title: finalTitle,
typeId: 1, // Image content type
typeId: 2,
};
// Use new Articles API
@ -749,7 +747,7 @@ export default function FormVideo() {
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/admin/content/audio-visual");
router.push("/admin/content/video");
});
Cookies.remove("idCreate");
@ -1000,8 +998,8 @@ export default function FormVideo() {
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col lg:flex-row gap-10">
<Card className="w-full lg:w-8/12">
<div className="flex flex-col lg:flex-row gap-10 border">
<Card className="w-full lg:w-8/12 m-2">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">Form Video</p>
<div className="gap-5 mb-5">
@ -1453,7 +1451,7 @@ export default function FormVideo() {
</div>
</Card>
<div className="w-full lg:w-4/12">
<div className="w-full lg:w-4/12 m-2">
<Card className="h-fit">
<div className="px-3 py-3">
<div className="space-y-2">
@ -1594,7 +1592,8 @@ export default function FormVideo() {
<Checkbox
id={option.id}
checked={isChecked}
onCheckedChange={handleChange}
onCheckedChange={handleChange}
className="border"
/>
<Label htmlFor={option.id}>{option.label}</Label>
</div>

View File

@ -134,7 +134,7 @@ export default function FormAudio() {
polres: false,
});
let fileTypeId = "3";
let fileTypeId = "4";
let progressInfo: any = [];
let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]);
@ -149,12 +149,10 @@ export default function FormAudio() {
type FileWithPreview = File & { preview: string };
const userId = Cookies.get("userId");
const options: Option[] = [
const options: Option[] = [
{ id: "all", label: "SEMUA" },
{ id: "5", label: "UMUM" },
{ id: "6", label: "JOURNALIS" },
{ id: "7", label: "POLRI" },
{ id: "8", label: "KSP" },
{ id: "4", label: "UMUM" },
{ id: "5", label: "JOURNALIS" },
];
const audioRefs = useRef<HTMLAudioElement[]>([]);
@ -651,7 +649,7 @@ export default function FormAudio() {
.replace(/[^a-z0-9-]/g, ""),
tags: finalTags,
title: finalTitle,
typeId: 1, // Image content type
typeId: 4,
};
// Use new Articles API

View File

@ -132,7 +132,7 @@ export default function FormTeks() {
polres: false,
});
const userId = Cookies.get("userId");
let fileTypeId = "2";
let fileTypeId = "3";
let progressInfo: any = [];
let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]);

View File

@ -602,7 +602,6 @@ export default function FormImageDetail() {
<div className="flex items-center">
<div className="py-3 w-full space-y-2">
<Label>Category</Label>
<Select
disabled
value={String(detail?.categoryId || detail?.category?.id)}
@ -779,24 +778,7 @@ export default function FormImageDetail() {
/>
<Label htmlFor="6">JOURNALIS</Label>
</div>
<div className="flex gap-2 items-center">
<Checkbox
id="7"
checked={selectedPublishers.includes(7)}
onChange={() => handleCheckboxChange(7)}
className="border"
/>
<Label htmlFor="7">POLRI</Label>
</div>
<div className="flex gap-2 items-center">
<Checkbox
id="8"
checked={selectedPublishers.includes(8)}
onChange={() => handleCheckboxChange(8)}
className="border"
/>
<Label htmlFor="8">KSP</Label>
</div>
</div>
</div>

View File

@ -0,0 +1,305 @@
"use client";
import React, { useState, useEffect } from "react";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import {
Card,
CardHeader,
CardTitle,
CardContent,
CardFooter,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Checkbox } from "@/components/ui/checkbox";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { errorAutoClose, loading, successAutoClose } from "@/lib/swal";
import { close } from "@/config/swal";
import Image from "next/image";
// ✅ Zod Schema Validasi
const companySchema = z.object({
companyName: z.string().trim().min(1, "Nama perusahaan wajib diisi"),
address: z.string().trim().min(1, "Alamat perusahaan wajib diisi"),
phone: z
.string()
.regex(/^[0-9+\-\s]+$/, "Nomor telepon tidak valid")
.min(6, "Nomor telepon minimal 6 karakter"),
website: z
.string()
.url("Masukkan URL website yang valid")
.optional()
.or(z.literal("")),
description: z.string().trim().optional(),
isActive: z.boolean().default(true),
logo: z.any().optional(),
});
type CompanySchema = z.infer<typeof companySchema>;
interface TenantCompanyUpdateFormProps {
id: number;
initialData?: any;
onSuccess?: () => void;
onCancel?: () => void;
}
export default function TenantCompanyUpdateForm({
id,
initialData,
onSuccess,
onCancel,
}: TenantCompanyUpdateFormProps) {
const MySwal = withReactContent(Swal);
const [previewLogo, setPreviewLogo] = useState<string | null>(null);
const [loadingData, setLoadingData] = useState(false);
const {
control,
handleSubmit,
setValue,
formState: { errors },
watch,
} = useForm<CompanySchema>({
resolver: zodResolver(companySchema),
defaultValues: {
companyName: "",
address: "",
phone: "",
website: "",
description: "",
isActive: true,
},
});
// ✅ Load data awal dari server
useEffect(() => {
async function fetchCompanyData() {
setLoadingData(true);
try {
// TODO: ganti dengan service API kamu (misalnya getTenantCompanyDetail)
const response = initialData; // simulasi
if (response) {
setValue("companyName", response.companyName || "");
setValue("address", response.address || "");
setValue("phone", response.phone || "");
setValue("website", response.website || "");
setValue("description", response.description || "");
setValue("isActive", response.isActive ?? true);
setPreviewLogo(response.logoUrl || null);
}
} catch (err) {
console.error("❌ Gagal memuat data perusahaan:", err);
} finally {
setLoadingData(false);
}
}
fetchCompanyData();
}, [initialData, setValue]);
// ✅ Fungsi Upload Logo
const handleLogoChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
if (!file.type.startsWith("image/")) {
MySwal.fire({
icon: "error",
title: "Format tidak valid",
text: "File harus berupa gambar (jpg, png, webp, dll)",
});
return;
}
setValue("logo", file);
setPreviewLogo(URL.createObjectURL(file));
};
// ✅ Submit Form
const onSubmit = async (data: CompanySchema) => {
try {
loading();
const formData = new FormData();
formData.append("companyName", data.companyName);
formData.append("address", data.address);
formData.append("phone", data.phone);
formData.append("website", data.website || "");
formData.append("description", data.description || "");
formData.append("isActive", data.isActive ? "true" : "false");
if (data.logo) formData.append("logo", data.logo);
console.log("📦 Payload:", Object.fromEntries(formData.entries()));
// TODO: Ganti dengan service API kamu → misalnya updateTenantCompany(formData)
// const response = await updateTenantCompany(id, formData);
await new Promise((resolve) => setTimeout(resolve, 1000)); // simulate
close();
successAutoClose("Data perusahaan berhasil diperbarui.");
setTimeout(() => {
if (onSuccess) onSuccess();
}, 2000);
} catch (err) {
close();
console.error("❌ Gagal update perusahaan:", err);
errorAutoClose("Terjadi kesalahan saat memperbarui data perusahaan.");
}
};
if (loadingData) {
return (
<Card className="p-6 w-full lg:w-2/3">
<div className="flex items-center justify-center py-8">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-4"></div>
<p>Memuat data perusahaan...</p>
</div>
</div>
</Card>
);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Card className="p-6 w-full lg:w-2/3">
<CardHeader>
<CardTitle className="text-lg font-semibold">
Update Informasi Perusahaan
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{/* Logo Upload */}
<div className="space-y-2">
<Label>Logo Perusahaan</Label>
<div className="flex items-center gap-4">
<Input
type="file"
accept="image/*"
onChange={handleLogoChange}
className="max-w-xs"
/>
{previewLogo && (
<div className="relative w-20 h-20 rounded-lg overflow-hidden border">
<Image
src={previewLogo}
alt="Company Logo"
fill
className="object-cover"
/>
</div>
)}
</div>
</div>
{/* Nama Perusahaan */}
<div>
<Label>Nama Perusahaan</Label>
<Controller
control={control}
name="companyName"
render={({ field }) => (
<Input {...field} placeholder="Masukkan nama perusahaan" />
)}
/>
{errors.companyName && (
<p className="text-red-500 text-sm">
{errors.companyName.message}
</p>
)}
</div>
{/* Alamat */}
<div>
<Label>Alamat</Label>
<Controller
control={control}
name="address"
render={({ field }) => (
<Textarea {...field} placeholder="Masukkan alamat lengkap" />
)}
/>
{errors.address && (
<p className="text-red-500 text-sm">{errors.address.message}</p>
)}
</div>
{/* Nomor Telepon */}
<div>
<Label>Nomor Telepon</Label>
<Controller
control={control}
name="phone"
render={({ field }) => (
<Input {...field} placeholder="Masukkan nomor telepon" />
)}
/>
{errors.phone && (
<p className="text-red-500 text-sm">{errors.phone.message}</p>
)}
</div>
{/* Website */}
<div>
<Label>Website Resmi</Label>
<Controller
control={control}
name="website"
render={({ field }) => (
<Input {...field} placeholder="https://example.com" />
)}
/>
{errors.website && (
<p className="text-red-500 text-sm">{errors.website.message}</p>
)}
</div>
{/* Deskripsi Perusahaan */}
<div>
<Label>Biodata / Deskripsi Perusahaan</Label>
<Controller
control={control}
name="description"
render={({ field }) => (
<Textarea
{...field}
placeholder="Ceritakan tentang perusahaan Anda..."
/>
)}
/>
</div>
{/* Status Aktif */}
<div className="flex items-center space-x-2">
<Controller
control={control}
name="isActive"
render={({ field }) => (
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
)}
/>
<Label>Aktif</Label>
</div>
</CardContent>
<CardFooter className="flex justify-end gap-3">
<Button type="submit">Update</Button>
<Button type="button" variant="outline" onClick={() => onCancel?.()}>
Cancel
</Button>
</CardFooter>
</Card>
</form>
);
}

View File

@ -13,8 +13,18 @@ import withReactContent from "sweetalert2-react-content";
import { errorAutoClose, loading, successAutoClose } from "@/lib/swal";
import { close } from "@/config/swal";
import { getUserLevelDetail, updateUserLevel } from "@/service/tenant";
import { UserLevelsCreateRequest, getUserLevels, getProvinces } from "@/service/approval-workflows";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import {
UserLevelsCreateRequest,
getUserLevels,
getProvinces,
} from "@/service/approval-workflows";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
const tenantSchema = z.object({
@ -48,8 +58,12 @@ export default function TenantUpdateForm({
}: TenantUpdateFormProps) {
const MySwal = withReactContent(Swal);
const [loadingData, setLoadingData] = useState(false);
const [userLevels, setUserLevels] = useState<{id: number; name: string}[]>([]);
const [provinces, setProvinces] = useState<{id: number; name: string}[]>([]);
const [userLevels, setUserLevels] = useState<{ id: number; name: string }[]>(
[]
);
const [provinces, setProvinces] = useState<{ id: number; prov_name: string }[]>(
[]
);
const {
control,
@ -59,6 +73,7 @@ export default function TenantUpdateForm({
} = useForm<TenantSchema>({
resolver: zodResolver(tenantSchema),
defaultValues: {
aliasName: "",
levelNumber: 1,
name: "",
@ -74,14 +89,15 @@ export default function TenantUpdateForm({
async function loadData() {
setLoadingData(true);
try {
const [detailResponse, userLevelsResponse, provincesResponse] = await Promise.all([
getUserLevelDetail(id),
getUserLevels(),
getProvinces(),
]);
const [detailResponse, userLevelsResponse, provincesResponse] =
await Promise.all([
getUserLevelDetail(id),
getUserLevels(),
getProvinces(),
]);
if (!detailResponse.error && detailResponse.data) {
const detail = detailResponse.data;
const detail = detailResponse.data.data;
setValue("aliasName", detail.aliasName ?? "");
setValue("levelNumber", detail.levelNumber ?? 1);
setValue("name", detail.name ?? "");
@ -90,6 +106,7 @@ export default function TenantUpdateForm({
setValue("group", detail.group ?? "");
setValue("isApprovalActive", detail.isApprovalActive ?? true);
setValue("isActive", detail.isActive ?? true);
console.log("OOOO", detailResponse.data);
} else {
console.error("Gagal mengambil detail:", detailResponse.message);
}
@ -114,7 +131,8 @@ export default function TenantUpdateForm({
try {
loading();
const payload: UserLevelsCreateRequest = {
const payload = {
id: id,
aliasName: data.aliasName,
levelNumber: data.levelNumber,
name: data.name,
@ -228,7 +246,14 @@ export default function TenantUpdateForm({
control={control}
name="parentLevelId"
render={({ field }) => (
<Select onValueChange={(value) => field.onChange(value && value !== "no-data" ? Number(value) : undefined)} value={field.value?.toString() || ""}>
<Select
onValueChange={(value) =>
field.onChange(
value && value !== "no-data" ? Number(value) : undefined
)
}
value={field.value?.toString() || ""}
>
<SelectTrigger>
<SelectValue placeholder="Pilih parent level" />
</SelectTrigger>
@ -249,7 +274,9 @@ export default function TenantUpdateForm({
)}
/>
{errors.parentLevelId && (
<p className="text-red-500 text-sm">{errors.parentLevelId.message}</p>
<p className="text-red-500 text-sm">
{errors.parentLevelId.message}
</p>
)}
</div>
@ -260,15 +287,25 @@ export default function TenantUpdateForm({
control={control}
name="provinceId"
render={({ field }) => (
<Select onValueChange={(value) => field.onChange(value && value !== "no-data" ? Number(value) : undefined)} value={field.value?.toString() || ""}>
<Select
onValueChange={(value) =>
field.onChange(
value && value !== "no-data" ? Number(value) : undefined
)
}
value={field.value?.toString() || ""}
>
<SelectTrigger>
<SelectValue placeholder="Pilih provinsi" />
</SelectTrigger>
<SelectContent>
{provinces.length > 0 ? (
provinces.map((province) => (
<SelectItem key={province.id} value={province.id.toString()}>
{province.name}
<SelectItem
key={province.id}
value={province.id.toString()}
>
{province.prov_name}
</SelectItem>
))
) : (
@ -281,7 +318,9 @@ export default function TenantUpdateForm({
)}
/>
{errors.provinceId && (
<p className="text-red-500 text-sm">{errors.provinceId.message}</p>
<p className="text-red-500 text-sm">
{errors.provinceId.message}
</p>
)}
</div>
@ -314,7 +353,9 @@ export default function TenantUpdateForm({
/>
<Label>Approval Active</Label>
{errors.isApprovalActive && (
<p className="text-red-500 text-sm">{errors.isApprovalActive.message}</p>
<p className="text-red-500 text-sm">
{errors.isApprovalActive.message}
</p>
)}
</div>

View File

@ -17,7 +17,7 @@ import { useAutoWorkflowCheck } from "@/hooks/useWorkflowStatusCheck";
const DashCodeHeader = () => {
// Auto-check workflow status when header mounts
useAutoWorkflowCheck();
// useAutoWorkflowCheck();
return (
<>

View File

@ -44,13 +44,10 @@ export const useAuth = (): AuthContextType => {
error: null,
});
// Check if user is authenticated on mount
useEffect(() => {
const checkAuth = async () => {
try {
setState((prev) => ({ ...prev, loading: true }));
// Add logic to check if user is authenticated
// This could check for valid tokens, etc.
} catch (error) {
setState((prev) => ({
...prev,

View File

@ -84,9 +84,9 @@ export function getMenuList(pathname: string, t: any): Group[] {
children: [],
},
{
href: "/admin/content/teks",
href: "/admin/content/text",
label: t("text"),
active: pathname.includes("/content/teks"),
active: pathname.includes("/content/text"),
icon: "heroicons:document",
children: [],
},