kontenhumas-fe/app/[locale]/(admin)/admin/tenants/[id]/edit/page.tsx

978 lines
36 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
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 {
Tenant,
TenantUpdateRequest,
getTenantById,
updateTenant,
} from "@/service/tenant";
import { getTenantList } from "@/service/tenant";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import Swal from "sweetalert2";
import { FormField } from "@/components/form/common/FormField";
import { getCookiesDecrypt } from "@/lib/utils";
import { ApprovalWorkflowForm } from "@/components/form/ApprovalWorkflowForm";
import { UserLevelsForm } from "@/components/form/UserLevelsForm";
import {
CreateApprovalWorkflowWithClientSettingsRequest,
UserLevelsCreateRequest,
UserLevel,
getUserLevels,
getApprovalWorkflowComprehensiveDetails,
ComprehensiveWorkflowResponse,
createUserLevel,
} from "@/service/approval-workflows";
import TenantCompanyUpdateForm from "@/components/form/tenant/tenant-detail-update-form";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
useReactTable,
} 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 { PlusIcon, EditIcon, DeleteIcon } from "@/components/icons";
import { errorAutoClose, successAutoClose } from "@/lib/swal";
import { close, loading } from "@/config/swal";
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 [isEditingWorkflow, setIsEditingWorkflow] = useState(false);
// User Levels state
const [userLevels, setUserLevels] = useState<UserLevel[]>([]);
const [isUserLevelDialogOpen, setIsUserLevelDialogOpen] = useState(false);
const [editingUserLevel, setEditingUserLevel] = useState<UserLevel | null>(
null,
);
// Tenant form data
const [formData, setFormData] = useState<TenantUpdateRequest>({
name: "",
description: "",
clientType: "standalone",
parentClientId: undefined,
maxUsers: undefined,
maxStorage: undefined,
address: "",
phoneNumber: "",
website: "",
isActive: true,
});
useEffect(() => {
// Check if user has roleId = 1
const roleId = getCookiesDecrypt("urie");
if (Number(roleId) !== 1) {
Swal.fire({
title: "Access Denied",
text: "You don't have permission to access this page",
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: "swal-z-index-9999",
},
}).then(() => {
router.push("/admin/dashboard");
});
return;
}
if (tenantId) {
loadData();
}
}, [tenantId, router]);
// Load workflow and user levels when switching to those tabs
useEffect(() => {
if (tenant && (activeTab === "workflows" || activeTab === "user-levels")) {
loadWorkflowAndUserLevels();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTab]);
const loadData = async () => {
setIsLoading(true);
try {
const [tenantRes, parentRes] = await Promise.all([
getTenantById(tenantId),
getTenantList({ clientType: "parent_client", limit: 100 }),
]);
if (tenantRes?.error || !tenantRes?.data?.data) {
Swal.fire({
title: "Error",
text: tenantRes?.message || "Failed to load tenant data",
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: "swal-z-index-9999",
},
}).then(() => {
router.push("/admin/tenants");
});
return;
}
const tenantData = tenantRes.data.data;
setTenant(tenantData);
// Set form data with all available fields
setFormData({
name: tenantData.name || "",
description: tenantData.description || "",
clientType: tenantData.clientType || "standalone",
parentClientId: tenantData.parentClientId || undefined,
maxUsers: tenantData.maxUsers || undefined,
maxStorage: tenantData.maxStorage || undefined,
address: tenantData.address || "",
phoneNumber: tenantData.phoneNumber || "",
website: tenantData.website || "",
isActive:
tenantData.isActive !== undefined ? tenantData.isActive : true,
logoUrl: tenantData.logoUrl || undefined,
logoImagePath: tenantData.logoImagePath || undefined,
});
if (!parentRes?.error) {
setParentTenants(parentRes?.data?.data || []);
}
// Load workflow and user levels if on those tabs
// Note: This will be loaded when tab changes via useEffect
} catch (error) {
console.error("Error loading tenant:", error);
Swal.fire({
title: "Error",
text: "An unexpected error occurred while loading tenant data",
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: "swal-z-index-9999",
},
}).then(() => {
router.push("/admin/tenants");
});
} finally {
setIsLoading(false);
}
};
const loadWorkflowAndUserLevels = async () => {
try {
const [comprehensiveWorkflowRes, userLevelsRes] = await Promise.all([
getApprovalWorkflowComprehensiveDetails(),
getUserLevels(),
]);
if (!comprehensiveWorkflowRes?.error) {
setWorkflow(comprehensiveWorkflowRes?.data?.data || null);
} 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 || []);
}
} catch (error) {
console.error("Error loading workflow and user levels:", error);
}
};
const handleSaveTenantInfo = async () => {
if (!tenantId) return;
setIsSaving(true);
try {
const updateData: TenantUpdateRequest = {
name: formData.name,
description: formData.description || undefined,
clientType: formData.clientType,
parentClientId: formData.parentClientId || undefined,
maxUsers: formData.maxUsers,
maxStorage: formData.maxStorage,
address: formData.address || undefined,
phoneNumber: formData.phoneNumber || undefined,
website: formData.website || undefined,
isActive: formData.isActive,
};
const res = await updateTenant(tenantId, updateData);
if (res?.error) {
Swal.fire({
title: "Error",
text: res?.message || "Failed to update tenant",
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: "swal-z-index-9999",
},
});
} else {
Swal.fire({
title: "Success",
text: "Tenant updated successfully",
icon: "success",
confirmButtonText: "OK",
customClass: {
popup: "swal-z-index-9999",
},
});
await loadData();
}
} catch (error) {
console.error("Error saving tenant:", error);
Swal.fire({
title: "Error",
text: "An unexpected error occurred",
icon: "error",
confirmButtonText: "OK",
customClass: {
popup: "swal-z-index-9999",
},
});
} finally {
setIsSaving(false);
}
};
const handleWorkflowSave = async (
data: CreateApprovalWorkflowWithClientSettingsRequest,
) => {
setIsEditingWorkflow(false);
await loadWorkflowAndUserLevels();
};
const handleUserLevelSave = async (data: UserLevelsCreateRequest) => {
try {
loading();
const response = await createUserLevel(data);
close();
if (response?.error) {
errorAutoClose(response.message || "Failed to create user level.");
return;
}
successAutoClose("User level created successfully.");
setIsUserLevelDialogOpen(false);
setEditingUserLevel(null);
setTimeout(async () => {
await loadWorkflowAndUserLevels();
}, 1000);
} catch (error) {
close();
errorAutoClose("An error occurred while creating user level.");
console.error("Error creating user level:", error);
}
};
const handleEditUserLevel = (userLevel: UserLevel) => {
setEditingUserLevel(userLevel);
setIsUserLevelDialogOpen(true);
};
const handleDeleteUserLevel = async (userLevel: UserLevel) => {
const result = await Swal.fire({
title: "Delete User Level?",
text: `Are you sure you want to delete "${userLevel.name}"? This action cannot be undone.`,
icon: "warning",
showCancelButton: true,
confirmButtonText: "Yes, delete it",
cancelButtonText: "Cancel",
customClass: {
popup: "swal-z-index-9999",
},
});
if (result.isConfirmed) {
try {
// TODO: Implement delete API call
console.log("Delete user level:", userLevel.id);
await loadWorkflowAndUserLevels();
} catch (error) {
console.error("Error deleting user level:", error);
}
}
};
const columns = React.useMemo(
() => useTableColumns((data) => handleEditUserLevel(data)),
[],
);
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const table = useReactTable({
data: userLevels,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onPaginationChange: setPagination,
state: {
pagination,
},
});
const roleId = getCookiesDecrypt("urie");
if (Number(roleId) !== 1) {
return null;
}
if (isLoading) {
return (
<>
<SiteBreadcrumb />
<div className="container mx-auto p-6">
<div className="text-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading tenant data...</p>
</div>
</div>
</>
);
}
if (!tenant) {
return null;
}
return (
<>
<SiteBreadcrumb />
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<Button
variant="outline"
onClick={() => router.push("/admin/tenants")}
>
<ChevronLeftIcon className="h-4 w-4 mr-2" />
Back
</Button>
<div>
<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>
</div>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="profile" className="flex items-center gap-2">
<SettingsIcon className="h-4 w-4" />
Tenant Information
</TabsTrigger>
<TabsTrigger value="workflows" className="flex items-center gap-2">
<WorkflowIcon className="h-4 w-4" />
Approval Workflows
</TabsTrigger>
<TabsTrigger
value="user-levels"
className="flex items-center gap-2"
>
<UsersIcon className="h-4 w-4" />
User Levels
</TabsTrigger>
</TabsList>
{/* Tenant Information Tab */}
<TabsContent value="profile" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Tenant Information</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<FormField
label="Tenant Name"
name="name"
type="text"
placeholder="e.g., Company ABC, Organization XYZ"
value={formData.name}
onChange={(value) =>
setFormData({ ...formData, name: value })
}
required
/>
<FormField
label="Description"
name="description"
type="textarea"
placeholder="Brief description of the tenant"
value={formData.description || ""}
onChange={(value) =>
setFormData({
...formData,
description: value || undefined,
})
}
/>
<FormField
label="Client Type"
name="clientType"
type="select"
placeholder="Select client type"
value={formData.clientType}
onChange={(value) =>
setFormData({ ...formData, clientType: value as any })
}
options={[
{ value: "standalone", label: "Standalone" },
{ value: "parent_client", label: "Parent Client" },
{ value: "sub_client", label: "Sub Client" },
]}
required
/>
{formData.clientType === "sub_client" && (
<FormField
label="Parent Tenant"
name="parentClientId"
type="select"
placeholder="Select parent tenant"
value={formData.parentClientId || "none"}
onChange={(value) =>
setFormData({
...formData,
parentClientId: value === "none" ? undefined : value,
})
}
options={[
{ value: "none", label: "No Parent Tenant" },
...parentTenants
.filter((t) => t.id !== tenantId)
.map((t) => ({
value: t.id,
label: t.name,
})),
]}
required
/>
)}
<div className="grid grid-cols-2 gap-4">
<FormField
label="Max Users"
name="maxUsers"
type="number"
placeholder="e.g., 100"
value={formData.maxUsers?.toString() || ""}
onChange={(value) =>
setFormData({
...formData,
maxUsers: value ? Number(value) : undefined,
})
}
helpText="Maximum number of users allowed"
/>
<FormField
label="Max Storage (MB)"
name="maxStorage"
type="number"
placeholder="e.g., 10000"
value={formData.maxStorage?.toString() || ""}
onChange={(value) =>
setFormData({
...formData,
maxStorage: value ? Number(value) : undefined,
})
}
helpText="Maximum storage in MB"
/>
</div>
<FormField
label="Address"
name="address"
type="textarea"
placeholder="Tenant address"
value={formData.address || ""}
onChange={(value) =>
setFormData({ ...formData, address: value || undefined })
}
/>
<div className="grid grid-cols-2 gap-4">
<FormField
label="Phone Number"
name="phoneNumber"
type="tel"
placeholder="e.g., +62 123 456 7890"
value={formData.phoneNumber || ""}
onChange={(value) =>
setFormData({
...formData,
phoneNumber: value || undefined,
})
}
/>
<FormField
label="Website"
name="website"
type="url"
placeholder="e.g., https://example.com"
value={formData.website || ""}
onChange={(value) =>
setFormData({ ...formData, website: value || undefined })
}
/>
</div>
<FormField
label="Status"
name="isActive"
type="select"
placeholder="Select status"
value={formData.isActive ? "active" : "inactive"}
onChange={(value) =>
setFormData({ ...formData, isActive: value === "active" })
}
options={[
{ value: "active", label: "Active" },
{ value: "inactive", label: "Inactive" },
]}
required
/>
<div className="flex justify-end pt-4 border-t">
<Button
onClick={handleSaveTenantInfo}
disabled={isSaving}
className="flex items-center gap-2"
>
<SaveIcon className="h-4 w-4" />
{isSaving ? "Saving..." : "Save Tenant Information"}
</Button>
</div>
</CardContent>
</Card>
</TabsContent>
{/* 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>
{workflow && !isEditingWorkflow && (
<Button
onClick={() => setIsEditingWorkflow(true)}
className="flex items-center gap-2"
>
<SettingsIcon className="h-4 w-4" />
Edit Workflow
</Button>
)}
</div>
{isEditingWorkflow ? (
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
<span>Setup Approval Workflow</span>
<Button
variant="outline"
onClick={() => setIsEditingWorkflow(false)}
>
Cancel
</Button>
</CardTitle>
</CardHeader>
<CardContent>
<ApprovalWorkflowForm
initialData={
workflow
? {
name: workflow.workflow.name,
description: workflow.workflow.description,
isDefault: workflow.workflow.isDefault,
isActive: workflow.workflow.isActive,
requiresApproval:
workflow.workflow.requiresApproval,
autoPublish: workflow.workflow.autoPublish,
steps:
workflow.steps?.map((step) => ({
stepOrder: step.stepOrder,
stepName: step.stepName,
requiredUserLevelId: step.requiredUserLevelId,
canSkip: step.canSkip,
autoApproveAfterHours:
step.autoApproveAfterHours,
isActive: step.isActive,
conditionType: step.conditionType,
conditionValue: step.conditionValue,
})) || [],
clientApprovalSettings: {
approvalExemptCategories:
workflow.clientSettings
.exemptCategoriesDetails || [],
approvalExemptRoles:
workflow.clientSettings.exemptRolesDetails ||
[],
approvalExemptUsers:
workflow.clientSettings.exemptUsersDetails ||
[],
autoPublishArticles:
workflow.clientSettings.autoPublishArticles,
isActive: workflow.clientSettings.isActive,
requireApprovalFor:
workflow.clientSettings.requireApprovalFor ||
[],
requiresApproval:
workflow.clientSettings.requiresApproval,
skipApprovalFor:
workflow.clientSettings.skipApprovalFor || [],
},
}
: undefined
}
workflowId={workflow?.workflow.id}
onSave={handleWorkflowSave}
onCancel={() => setIsEditingWorkflow(false)}
/>
</CardContent>
</Card>
) : workflow ? (
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
<span>{workflow.workflow.name}</span>
<div className="flex items-center gap-2">
{workflow.workflow.isDefault && (
<span className="px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded-full">
Default
</span>
)}
{workflow.workflow.isActive ? (
<span className="px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full">
Active
</span>
) : (
<span className="px-2 py-1 text-xs bg-gray-100 text-gray-800 rounded-full">
Inactive
</span>
)}
</div>
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-gray-600 text-sm mb-4">
{workflow.workflow.description}
</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-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-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>
<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>
<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>
<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 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="text-sm text-gray-600">
Required Level: {step.requiredUserLevelName}
</div>
</div>
</div>
{step.isActive ? (
<span className="px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full">
Active
</span>
) : (
<span className="px-2 py-1 text-xs bg-gray-100 text-gray-800 rounded-full">
Inactive
</span>
)}
</div>
))}
</div>
</div>
)}
</CardContent>
</Card>
) : (
<Card>
<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>
<p className="text-gray-500 mb-4">
No approval workflow has been set up for this tenant yet.
</p>
<Button onClick={() => setIsEditingWorkflow(true)}>
<PlusIcon className="h-4 w-4 mr-2" />
Create Workflow
</Button>
</div>
</CardContent>
</Card>
)}
</TabsContent>
{/* User Levels Tab */}
<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}
>
<DialogTrigger asChild>
<Button
className="flex items-center gap-2"
onClick={() => {
setEditingUserLevel(null);
setIsUserLevelDialogOpen(true);
}}
>
<PlusIcon className="h-4 w-4" />
Create User Level
</Button>
</DialogTrigger>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>
{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
}
onSave={handleUserLevelSave}
onCancel={() => {
setIsUserLevelDialogOpen(false);
setEditingUserLevel(null);
}}
/>
</DialogContent>
</Dialog>
</div>
{userLevels.length > 0 ? (
<Card>
<CardContent className="p-0">
<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>
)),
)}
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow key={row.id}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
<TableCell className="text-right">
<div className="flex items-center justify-end gap-2">
<Button
variant="outline"
size="sm"
onClick={() =>
handleEditUserLevel(row.original)
}
>
<EditIcon className="h-4 w-4 mr-2" />
Edit
</Button>
<Button
variant="outline"
size="sm"
className="text-red-600 hover:text-red-700 hover:bg-red-50"
onClick={() =>
handleDeleteUserLevel(row.original)
}
>
<DeleteIcon className="h-4 w-4 mr-2" />
Delete
</Button>
</div>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length + 1}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<div className="p-4">
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</div>
</CardContent>
</Card>
) : (
<Card>
<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>
<p className="text-gray-500 mb-4">
Create your first user level to define user hierarchy
</p>
<Button
onClick={() => {
setEditingUserLevel(null);
setIsUserLevelDialogOpen(true);
}}
>
<PlusIcon className="h-4 w-4 mr-2" />
Create User Level
</Button>
</div>
</CardContent>
</Card>
)}
</TabsContent>
</Tabs>
</div>
</>
);
}