fix: errors build

This commit is contained in:
Sabda Yagra 2026-01-19 11:03:45 +07:00
parent ebed666eba
commit 292c2496b3
8 changed files with 4205 additions and 961 deletions

View File

@ -2,7 +2,13 @@
import React, { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { PlusIcon, ModuleIcon, EditIcon, DeleteIcon } from "@/components/icons";
import {
MasterModule,
@ -50,11 +56,11 @@ export default function ModulesSettingsPage() {
if (module) {
setEditingModule(module);
setFormData({
name: module.name,
description: module.description,
pathUrl: module.pathUrl,
actionType: module.actionType,
statusId: module.statusId,
name: module.name ?? "",
description: module.description ?? "",
pathUrl: module.pathUrl ?? "",
actionType: module.actionType ?? "",
statusId: module.statusId ?? 1,
});
} else {
setEditingModule(null);
@ -80,8 +86,8 @@ export default function ModulesSettingsPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
} else {
Swal.fire({
@ -90,8 +96,8 @@ export default function ModulesSettingsPage() {
icon: "success",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
await loadData();
setIsDialogOpen(false);
@ -105,8 +111,8 @@ export default function ModulesSettingsPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
} else {
Swal.fire({
@ -115,8 +121,8 @@ export default function ModulesSettingsPage() {
icon: "success",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
await loadData();
setIsDialogOpen(false);
@ -130,8 +136,8 @@ export default function ModulesSettingsPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
}
};
@ -145,8 +151,8 @@ export default function ModulesSettingsPage() {
confirmButtonText: "Yes, delete it",
cancelButtonText: "Cancel",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
if (result.isConfirmed) {
@ -159,8 +165,8 @@ export default function ModulesSettingsPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
} else {
Swal.fire({
@ -169,8 +175,8 @@ export default function ModulesSettingsPage() {
icon: "success",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
await loadData();
}
@ -182,8 +188,8 @@ export default function ModulesSettingsPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
}
}
@ -195,14 +201,19 @@ export default function ModulesSettingsPage() {
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-gray-900">Modules Settings</h1>
<h1 className="text-3xl font-bold text-gray-900">
Modules Settings
</h1>
<p className="text-gray-600 mt-2">
Manage system modules and their configurations
</p>
</div>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="flex items-center gap-2" onClick={() => handleOpenDialog()}>
<Button
className="flex items-center gap-2"
onClick={() => handleOpenDialog()}
>
<PlusIcon className="h-4 w-4" />
Create Module
</Button>
@ -210,7 +221,9 @@ export default function ModulesSettingsPage() {
<DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>
{editingModule ? `Edit Module: ${editingModule.name}` : "Create New Module"}
{editingModule
? `Edit Module: ${editingModule.name}`
: "Create New Module"}
</DialogTitle>
</DialogHeader>
<div className="space-y-4">
@ -220,7 +233,9 @@ export default function ModulesSettingsPage() {
type="text"
placeholder="e.g., View Articles, Create Content"
value={formData.name}
onChange={(value) => setFormData({ ...formData, name: value })}
onChange={(value) =>
setFormData({ ...formData, name: value })
}
required
/>
<FormField
@ -229,7 +244,9 @@ export default function ModulesSettingsPage() {
type="text"
placeholder="Brief description of the module"
value={formData.description}
onChange={(value) => setFormData({ ...formData, description: value })}
onChange={(value) =>
setFormData({ ...formData, description: value })
}
required
/>
<FormField
@ -238,7 +255,9 @@ export default function ModulesSettingsPage() {
type="text"
placeholder="e.g., /api/articles, /api/content"
value={formData.pathUrl}
onChange={(value) => setFormData({ ...formData, pathUrl: value })}
onChange={(value) =>
setFormData({ ...formData, pathUrl: value })
}
required
/>
<FormField
@ -247,7 +266,9 @@ export default function ModulesSettingsPage() {
type="select"
placeholder="Select action type"
value={formData.actionType}
onChange={(value) => setFormData({ ...formData, actionType: value })}
onChange={(value) =>
setFormData({ ...formData, actionType: value })
}
options={[
{ value: "view", label: "View" },
{ value: "create", label: "Create" },
@ -264,7 +285,9 @@ export default function ModulesSettingsPage() {
type="number"
placeholder="1"
value={formData.statusId}
onChange={(value) => setFormData({ ...formData, statusId: Number(value) || 1 })}
onChange={(value) =>
setFormData({ ...formData, statusId: Number(value) || 1 })
}
required
/>
<div className="flex items-center justify-end gap-2 pt-4 border-t">
@ -291,7 +314,10 @@ export default function ModulesSettingsPage() {
) : modules.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{modules.map((module) => (
<Card key={module.id} className="hover:shadow-lg transition-shadow">
<Card
key={module.id}
className="hover:shadow-lg transition-shadow"
>
<CardHeader>
<CardTitle className="flex items-center justify-between">
<span className="truncate">{module.name}</span>
@ -308,7 +334,9 @@ export default function ModulesSettingsPage() {
</CardHeader>
<CardContent>
<div className="space-y-2 mb-4">
<div className="text-sm text-gray-600">{module.description}</div>
<div className="text-sm text-gray-600">
{module.description}
</div>
<div className="text-xs text-gray-500 font-mono bg-gray-50 p-2 rounded">
{module.pathUrl}
</div>
@ -347,7 +375,9 @@ export default function ModulesSettingsPage() {
<CardContent className="flex items-center justify-center py-12">
<div className="text-center">
<ModuleIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">No Modules Found</h3>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No Modules Found
</h3>
<p className="text-gray-500 mb-4">
Create your first module to define system capabilities
</p>
@ -363,4 +393,3 @@ export default function ModulesSettingsPage() {
</>
);
}

View File

@ -881,7 +881,7 @@ function TenantSettingsContentTable() {
<UserLevelsForm
mode="single"
initialData={{
id: editingUserLevel.id,
// id: editingUserLevel.id,
name: editingUserLevel.name,
aliasName: editingUserLevel.aliasName,
levelNumber: editingUserLevel.levelNumber,

View File

@ -475,7 +475,7 @@ function TenantSettingsContent() {
<UserLevelsForm
mode="single"
initialData={editingUserLevel ? {
id: editingUserLevel.id,
// id: editingUserLevel.id,
name: editingUserLevel.name,
aliasName: editingUserLevel.aliasName,
levelNumber: editingUserLevel.levelNumber,

View File

@ -4,7 +4,13 @@ import { useRouter, useParams } from "next/navigation";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ChevronLeftIcon, SaveIcon, SettingsIcon, UsersIcon, WorkflowIcon } from "@/components/icons";
import {
ChevronLeftIcon,
SaveIcon,
SettingsIcon,
UsersIcon,
WorkflowIcon,
} from "@/components/icons";
import {
Tenant,
TenantUpdateRequest,
@ -47,7 +53,13 @@ import {
} from "@tanstack/react-table";
import TablePagination from "@/components/table/table-pagination";
import useTableColumns from "@/app/[locale]/(admin)/admin/settings/tenant/component/columns";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { PlusIcon, EditIcon, DeleteIcon } from "@/components/icons";
import { errorAutoClose, successAutoClose } from "@/lib/swal";
import { close, loading } from "@/config/swal";
@ -56,22 +68,27 @@ export default function EditTenantPage() {
const router = useRouter();
const params = useParams();
const tenantId = params?.id as string;
const [totalData, setTotalData] = useState<number>(0);
const [totalPage, setTotalPage] = useState<number>(1);
const [tenant, setTenant] = useState<Tenant | null>(null);
const [parentTenants, setParentTenants] = useState<Tenant[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isSaving, setIsSaving] = useState(false);
const [activeTab, setActiveTab] = useState("profile");
// Workflow state
const [workflow, setWorkflow] = useState<ComprehensiveWorkflowResponse | null>(null);
const [workflow, setWorkflow] =
useState<ComprehensiveWorkflowResponse | null>(null);
const [isEditingWorkflow, setIsEditingWorkflow] = useState(false);
// User Levels state
const [userLevels, setUserLevels] = useState<UserLevel[]>([]);
const [isUserLevelDialogOpen, setIsUserLevelDialogOpen] = useState(false);
const [editingUserLevel, setEditingUserLevel] = useState<UserLevel | null>(null);
const [editingUserLevel, setEditingUserLevel] = useState<UserLevel | null>(
null,
);
// Tenant form data
const [formData, setFormData] = useState<TenantUpdateRequest>({
name: "",
@ -96,8 +113,8 @@ export default function EditTenantPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
}).then(() => {
router.push("/admin/dashboard");
});
@ -132,8 +149,8 @@ export default function EditTenantPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
}).then(() => {
router.push("/admin/tenants");
});
@ -154,7 +171,8 @@ export default function EditTenantPage() {
address: tenantData.address || "",
phoneNumber: tenantData.phoneNumber || "",
website: tenantData.website || "",
isActive: tenantData.isActive !== undefined ? tenantData.isActive : true,
isActive:
tenantData.isActive !== undefined ? tenantData.isActive : true,
logoUrl: tenantData.logoUrl || undefined,
logoImagePath: tenantData.logoImagePath || undefined,
});
@ -173,8 +191,8 @@ export default function EditTenantPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
}).then(() => {
router.push("/admin/tenants");
});
@ -195,6 +213,15 @@ export default function EditTenantPage() {
} else {
setWorkflow(null);
}
if (!userLevelsRes?.error) {
const data = userLevelsRes?.data?.data || [];
setUserLevels(data);
setTotalData(data.length);
const pageSize = pagination.pageSize;
setTotalPage(Math.max(1, Math.ceil(data.length / pageSize)));
}
if (!userLevelsRes?.error) {
setUserLevels(userLevelsRes?.data?.data || []);
@ -230,8 +257,8 @@ export default function EditTenantPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
} else {
Swal.fire({
@ -240,8 +267,8 @@ export default function EditTenantPage() {
icon: "success",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
await loadData();
}
@ -253,15 +280,17 @@ export default function EditTenantPage() {
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
} finally {
setIsSaving(false);
}
};
const handleWorkflowSave = async (data: CreateApprovalWorkflowWithClientSettingsRequest) => {
const handleWorkflowSave = async (
data: CreateApprovalWorkflowWithClientSettingsRequest,
) => {
setIsEditingWorkflow(false);
await loadWorkflowAndUserLevels();
};
@ -304,8 +333,8 @@ export default function EditTenantPage() {
confirmButtonText: "Yes, delete it",
cancelButtonText: "Cancel",
customClass: {
popup: 'swal-z-index-9999'
}
popup: "swal-z-index-9999",
},
});
if (result.isConfirmed) {
@ -321,7 +350,7 @@ export default function EditTenantPage() {
const columns = React.useMemo(
() => useTableColumns((data) => handleEditUserLevel(data)),
[]
[],
);
const [pagination, setPagination] = React.useState<PaginationState>({
@ -379,7 +408,9 @@ export default function EditTenantPage() {
Back
</Button>
<div>
<h1 className="text-3xl font-bold text-gray-900">Edit Tenant: {tenant.name}</h1>
<h1 className="text-3xl font-bold text-gray-900">
Edit Tenant: {tenant.name}
</h1>
<p className="text-gray-600 mt-2">
Manage tenant information, workflows, and user levels
</p>
@ -397,7 +428,10 @@ export default function EditTenantPage() {
<WorkflowIcon className="h-4 w-4" />
Approval Workflows
</TabsTrigger>
<TabsTrigger value="user-levels" className="flex items-center gap-2">
<TabsTrigger
value="user-levels"
className="flex items-center gap-2"
>
<UsersIcon className="h-4 w-4" />
User Levels
</TabsTrigger>
@ -416,7 +450,9 @@ export default function EditTenantPage() {
type="text"
placeholder="e.g., Company ABC, Organization XYZ"
value={formData.name}
onChange={(value) => setFormData({ ...formData, name: value })}
onChange={(value) =>
setFormData({ ...formData, name: value })
}
required
/>
<FormField
@ -425,7 +461,12 @@ export default function EditTenantPage() {
type="textarea"
placeholder="Brief description of the tenant"
value={formData.description || ""}
onChange={(value) => setFormData({ ...formData, description: value || undefined })}
onChange={(value) =>
setFormData({
...formData,
description: value || undefined,
})
}
/>
<FormField
label="Client Type"
@ -433,7 +474,9 @@ export default function EditTenantPage() {
type="select"
placeholder="Select client type"
value={formData.clientType}
onChange={(value) => setFormData({ ...formData, clientType: value as any })}
onChange={(value) =>
setFormData({ ...formData, clientType: value as any })
}
options={[
{ value: "standalone", label: "Standalone" },
{ value: "parent_client", label: "Parent Client" },
@ -448,7 +491,12 @@ export default function EditTenantPage() {
type="select"
placeholder="Select parent tenant"
value={formData.parentClientId || "none"}
onChange={(value) => setFormData({ ...formData, parentClientId: value === "none" ? undefined : value })}
onChange={(value) =>
setFormData({
...formData,
parentClientId: value === "none" ? undefined : value,
})
}
options={[
{ value: "none", label: "No Parent Tenant" },
...parentTenants
@ -468,7 +516,12 @@ export default function EditTenantPage() {
type="number"
placeholder="e.g., 100"
value={formData.maxUsers?.toString() || ""}
onChange={(value) => setFormData({ ...formData, maxUsers: value ? Number(value) : undefined })}
onChange={(value) =>
setFormData({
...formData,
maxUsers: value ? Number(value) : undefined,
})
}
helpText="Maximum number of users allowed"
/>
<FormField
@ -477,7 +530,12 @@ export default function EditTenantPage() {
type="number"
placeholder="e.g., 10000"
value={formData.maxStorage?.toString() || ""}
onChange={(value) => setFormData({ ...formData, maxStorage: value ? Number(value) : undefined })}
onChange={(value) =>
setFormData({
...formData,
maxStorage: value ? Number(value) : undefined,
})
}
helpText="Maximum storage in MB"
/>
</div>
@ -487,7 +545,9 @@ export default function EditTenantPage() {
type="textarea"
placeholder="Tenant address"
value={formData.address || ""}
onChange={(value) => setFormData({ ...formData, address: value || undefined })}
onChange={(value) =>
setFormData({ ...formData, address: value || undefined })
}
/>
<div className="grid grid-cols-2 gap-4">
<FormField
@ -496,7 +556,12 @@ export default function EditTenantPage() {
type="tel"
placeholder="e.g., +62 123 456 7890"
value={formData.phoneNumber || ""}
onChange={(value) => setFormData({ ...formData, phoneNumber: value || undefined })}
onChange={(value) =>
setFormData({
...formData,
phoneNumber: value || undefined,
})
}
/>
<FormField
label="Website"
@ -504,7 +569,9 @@ export default function EditTenantPage() {
type="url"
placeholder="e.g., https://example.com"
value={formData.website || ""}
onChange={(value) => setFormData({ ...formData, website: value || undefined })}
onChange={(value) =>
setFormData({ ...formData, website: value || undefined })
}
/>
</div>
<FormField
@ -513,7 +580,9 @@ export default function EditTenantPage() {
type="select"
placeholder="Select status"
value={formData.isActive ? "active" : "inactive"}
onChange={(value) => setFormData({ ...formData, isActive: value === "active" })}
onChange={(value) =>
setFormData({ ...formData, isActive: value === "active" })
}
options={[
{ value: "active", label: "Active" },
{ value: "inactive", label: "Inactive" },
@ -537,7 +606,9 @@ export default function EditTenantPage() {
{/* Approval Workflows Tab */}
<TabsContent value="workflows" className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-semibold">Approval Workflow Setup</h2>
<h2 className="text-2xl font-semibold">
Approval Workflow Setup
</h2>
{workflow && !isEditingWorkflow && (
<Button
onClick={() => setIsEditingWorkflow(true)}
@ -571,7 +642,8 @@ export default function EditTenantPage() {
description: workflow.workflow.description,
isDefault: workflow.workflow.isDefault,
isActive: workflow.workflow.isActive,
requiresApproval: workflow.workflow.requiresApproval,
requiresApproval:
workflow.workflow.requiresApproval,
autoPublish: workflow.workflow.autoPublish,
steps:
workflow.steps?.map((step) => ({
@ -579,23 +651,28 @@ export default function EditTenantPage() {
stepName: step.stepName,
requiredUserLevelId: step.requiredUserLevelId,
canSkip: step.canSkip,
autoApproveAfterHours: step.autoApproveAfterHours,
autoApproveAfterHours:
step.autoApproveAfterHours,
isActive: step.isActive,
conditionType: step.conditionType,
conditionValue: step.conditionValue,
})) || [],
clientApprovalSettings: {
approvalExemptCategories:
workflow.clientSettings.exemptCategoriesDetails || [],
workflow.clientSettings
.exemptCategoriesDetails || [],
approvalExemptRoles:
workflow.clientSettings.exemptRolesDetails || [],
workflow.clientSettings.exemptRolesDetails ||
[],
approvalExemptUsers:
workflow.clientSettings.exemptUsersDetails || [],
workflow.clientSettings.exemptUsersDetails ||
[],
autoPublishArticles:
workflow.clientSettings.autoPublishArticles,
isActive: workflow.clientSettings.isActive,
requireApprovalFor:
workflow.clientSettings.requireApprovalFor || [],
workflow.clientSettings.requireApprovalFor ||
[],
requiresApproval:
workflow.clientSettings.requiresApproval,
skipApprovalFor:
@ -639,38 +716,55 @@ export default function EditTenantPage() {
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className="text-2xl font-bold text-blue-600">{workflow.workflow.totalSteps}</div>
<div className="text-2xl font-bold text-blue-600">
{workflow.workflow.totalSteps}
</div>
<div className="text-sm text-gray-600">Total Steps</div>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className="text-2xl font-bold text-green-600">{workflow.workflow.activeSteps}</div>
<div className="text-2xl font-bold text-green-600">
{workflow.workflow.activeSteps}
</div>
<div className="text-sm text-gray-600">Active Steps</div>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className={`text-2xl font-bold ${workflow.workflow.requiresApproval ? 'text-green-600' : 'text-red-600'}`}>
{workflow.workflow.requiresApproval ? 'Yes' : 'No'}
<div
className={`text-2xl font-bold ${workflow.workflow.requiresApproval ? "text-green-600" : "text-red-600"}`}
>
{workflow.workflow.requiresApproval ? "Yes" : "No"}
</div>
<div className="text-sm text-gray-600">
Requires Approval
</div>
<div className="text-sm text-gray-600">Requires Approval</div>
</div>
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className={`text-2xl font-bold ${workflow.workflow.autoPublish ? 'text-green-600' : 'text-red-600'}`}>
{workflow.workflow.autoPublish ? 'Yes' : 'No'}
<div
className={`text-2xl font-bold ${workflow.workflow.autoPublish ? "text-green-600" : "text-red-600"}`}
>
{workflow.workflow.autoPublish ? "Yes" : "No"}
</div>
<div className="text-sm text-gray-600">Auto Publish</div>
</div>
</div>
{workflow.steps && workflow.steps.length > 0 && (
<div>
<h4 className="text-lg font-medium mb-3">Workflow Steps</h4>
<h4 className="text-lg font-medium mb-3">
Workflow Steps
</h4>
<div className="space-y-2">
{workflow.steps.map((step: any, index: number) => (
<div key={index} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div
key={index}
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-blue-100 text-blue-600 rounded-full flex items-center justify-center text-sm font-medium">
{step.stepOrder}
</div>
<div>
<div className="font-medium">{step.stepName}</div>
<div className="font-medium">
{step.stepName}
</div>
<div className="text-sm text-gray-600">
Required Level: {step.requiredUserLevelName}
</div>
@ -697,7 +791,9 @@ export default function EditTenantPage() {
<CardContent className="flex items-center justify-center py-12">
<div className="text-center">
<WorkflowIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">No Workflow Found</h3>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No Workflow Found
</h3>
<p className="text-gray-500 mb-4">
No approval workflow has been set up for this tenant yet.
</p>
@ -715,12 +811,18 @@ export default function EditTenantPage() {
<TabsContent value="user-levels" className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-semibold">User Levels Management</h2>
<Dialog open={isUserLevelDialogOpen} onOpenChange={setIsUserLevelDialogOpen}>
<Dialog
open={isUserLevelDialogOpen}
onOpenChange={setIsUserLevelDialogOpen}
>
<DialogTrigger asChild>
<Button className="flex items-center gap-2" onClick={() => {
setEditingUserLevel(null);
setIsUserLevelDialogOpen(true);
}}>
<Button
className="flex items-center gap-2"
onClick={() => {
setEditingUserLevel(null);
setIsUserLevelDialogOpen(true);
}}
>
<PlusIcon className="h-4 w-4" />
Create User Level
</Button>
@ -728,22 +830,28 @@ export default function EditTenantPage() {
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>
{editingUserLevel ? `Edit User Level: ${editingUserLevel.name}` : "Create New User Level"}
{editingUserLevel
? `Edit User Level: ${editingUserLevel.name}`
: "Create New User Level"}
</DialogTitle>
</DialogHeader>
<UserLevelsForm
mode="single"
initialData={editingUserLevel ? {
id: editingUserLevel.id,
name: editingUserLevel.name,
aliasName: editingUserLevel.aliasName,
levelNumber: editingUserLevel.levelNumber,
parentLevelId: editingUserLevel.parentLevelId || 0,
provinceId: editingUserLevel.provinceId,
group: editingUserLevel.group || "",
isApprovalActive: editingUserLevel.isApprovalActive,
isActive: editingUserLevel.isActive,
} : undefined}
initialData={
editingUserLevel
? {
// id: editingUserLevel.id,
name: editingUserLevel.name,
aliasName: editingUserLevel.aliasName,
levelNumber: editingUserLevel.levelNumber,
parentLevelId: editingUserLevel.parentLevelId || 0,
provinceId: editingUserLevel.provinceId,
group: editingUserLevel.group || "",
isApprovalActive: editingUserLevel.isApprovalActive,
isActive: editingUserLevel.isActive,
}
: undefined
}
onSave={handleUserLevelSave}
onCancel={() => {
setIsUserLevelDialogOpen(false);
@ -760,18 +868,20 @@ export default function EditTenantPage() {
<Table>
<TableHeader>
<TableRow>
{table.getHeaderGroups().map((headerGroup) =>
headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))
)}
{table
.getHeaderGroups()
.map((headerGroup) =>
headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
)),
)}
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
@ -783,7 +893,7 @@ export default function EditTenantPage() {
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
cell.getContext(),
)}
</TableCell>
))}
@ -792,7 +902,9 @@ export default function EditTenantPage() {
<Button
variant="outline"
size="sm"
onClick={() => handleEditUserLevel(row.original)}
onClick={() =>
handleEditUserLevel(row.original)
}
>
<EditIcon className="h-4 w-4 mr-2" />
Edit
@ -801,7 +913,9 @@ export default function EditTenantPage() {
variant="outline"
size="sm"
className="text-red-600 hover:text-red-700 hover:bg-red-50"
onClick={() => handleDeleteUserLevel(row.original)}
onClick={() =>
handleDeleteUserLevel(row.original)
}
>
<DeleteIcon className="h-4 w-4 mr-2" />
Delete
@ -823,7 +937,11 @@ export default function EditTenantPage() {
</TableBody>
</Table>
<div className="p-4">
<TablePagination table={table} />
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</div>
</CardContent>
</Card>
@ -832,14 +950,18 @@ export default function EditTenantPage() {
<CardContent className="flex items-center justify-center py-12">
<div className="text-center">
<UsersIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">No User Levels Found</h3>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No User Levels Found
</h3>
<p className="text-gray-500 mb-4">
Create your first user level to define user hierarchy
</p>
<Button onClick={() => {
setEditingUserLevel(null);
setIsUserLevelDialogOpen(true);
}}>
<Button
onClick={() => {
setEditingUserLevel(null);
setIsUserLevelDialogOpen(true);
}}
>
<PlusIcon className="h-4 w-4 mr-2" />
Create User Level
</Button>

View File

@ -1,7 +1,142 @@
@import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
/* ================================
GLOBAL CSS VARIABLE (LIGHT MODE)
================================ */
:root {
--radius: 0.625rem;
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 215.3 19.3% 34.5%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--success: 154 52% 55%;
--warning: 16 93% 70%;
--info: 185 96% 51%;
--sidebar: 0 0% 100%;
--sidebar-foreground: 215 20% 65%;
}
/* ================================
DARK MODE VARIABLES
================================ */
.dark {
--background: 222.2 47.4% 11.2%;
--foreground: 210 40% 98%;
--card: 215 27.9% 16.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 215.3 25% 26.7%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
--sidebar: 215 27.9% 16.9%;
--sidebar-foreground: 214.3 31.8% 91.4%;
}
/* ================================
BASE LAYER
================================ */
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
/* ================================
GLOBAL UTILITIES
================================ */
@layer utilities {
/* SweetAlert z-index fix */
.swal-z-index-9999 {
z-index: 9999 !important;
}
/* Scrollbar hide */
.no-scrollbar::-webkit-scrollbar {
width: 0px;
}
.no-scrollbar::-webkit-scrollbar-thumb {
background-color: transparent;
}
/* Input group helpers */
.input-group :not(:first-child) input {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.input-group.merged :not(:first-child) input {
border-left-width: 0 !important;
padding-left: 0 !important;
}
.input-group :not(:last-child) input {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.input-group.merged :not(:last-child) input {
border-right-width: 0 !important;
padding-right: 0 !important;
}
}
/* @import "tailwindcss";
@import "tw-animate-css";
/* SweetAlert2 z-index fix */
.swal-z-index-9999 {
z-index: 9999 !important;
}
@ -297,4 +432,4 @@
.no-scrollbar::-webkit-scrollbar-thumb {
background-color: transparent;
}
}
} */

4596
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,7 @@
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"cookie": "^1.0.2",
"cookies-next": "^6.1.1",
"crypto-js": "^4.2.0",
"date-fns": "^3.6.0",
"dayjs": "^1.11.11",
@ -142,7 +143,6 @@
"devDependencies": {
"@dnd-kit/utilities": "^3.2.2",
"@next/bundle-analyzer": "^15.0.3",
"@tailwindcss/postcss": "^4",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
@ -160,6 +160,7 @@
"@types/react-geocode": "^0.2.4",
"@types/rtl-detect": "^1.0.3",
"@types/sizzle": "^2.3.10",
"autoprefixer": "^10.4.19",
"cross-env": "^7.0.3",
"d3-shape": "^3.2.0",
"eslint": "^8",
@ -167,7 +168,7 @@
"jest": "^30.0.4",
"jest-environment-jsdom": "^30.0.4",
"postcss": "^8",
"tailwindcss": "^4",
"tailwindcss": "^3.4.14",
"tw-animate-css": "^1.3.5",
"typescript": "^5"
},

View File

@ -1,7 +1,18 @@
// /** @type {import('postcss-load-config').Config} */
// const config = {
// plugins: {
// '@tailwindcss/postcss': {},
// },
// };
// export default config;
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
'@tailwindcss/postcss': {},
tailwindcss: {},
autoprefixer: {},
},
};