"use client"; import React, { useState, useEffect } from "react"; 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 { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { ChevronDownIcon, ChevronUpIcon, SaveIcon, EyeIcon, RotateCcwIcon, UsersIcon, HierarchyIcon, PlusIcon, TrashIcon } from "@/components/icons"; import { FormField } from "./common/FormField"; import { DynamicArray } from "./common/DynamicArray"; import { UserLevelsCreateRequest, UserLevel, Province, createUserLevel, getUserLevels, getProvinces, } from "@/service/approval-workflows"; import { MasterModule, getMasterModules, } from "@/service/menu-modules"; import { getUserLevelModuleAccessesByUserLevelId, createUserLevelModuleAccessesBatch, deleteUserLevelModuleAccess, UserLevelModuleAccess, } from "@/service/user-level-module-accesses"; import Swal from "sweetalert2"; interface UserLevelsFormProps { initialData?: UserLevelsCreateRequest; onSave?: (data: UserLevelsCreateRequest) => void; onCancel?: () => void; isLoading?: boolean; mode?: "single" | "bulk"; } export const UserLevelsForm: React.FC = ({ initialData, onSave, onCancel, isLoading = false, mode = "single", }) => { // Form state const [formData, setFormData] = useState({ name: "", aliasName: "", levelNumber: 1, parentLevelId: undefined, provinceId: undefined, group: "", isApprovalActive: true, isActive: true, }); const [bulkFormData, setBulkFormData] = useState([]); const [userLevels, setUserLevels] = useState([]); const [provinces, setProvinces] = useState([]); const [modules, setModules] = useState([]); const [selectedModuleIds, setSelectedModuleIds] = useState([]); const [userLevelModuleAccesses, setUserLevelModuleAccesses] = useState([]); const [errors, setErrors] = useState>({}); const [isSubmitting, setIsSubmitting] = useState(false); const [expandedHierarchy, setExpandedHierarchy] = useState(false); const [isLoadingData, setIsLoadingData] = useState(true); const [activeTab, setActiveTab] = useState(mode === "single" ? "basic" : "bulk"); useEffect(() => { if (initialData) { setFormData(initialData); } }, [initialData]); useEffect(() => { const loadData = async () => { try { const [userLevelsRes, provincesRes, modulesRes] = await Promise.all([ getUserLevels(), getProvinces(), getMasterModules({ limit: 100 }), ]); if (!userLevelsRes?.error) setUserLevels(userLevelsRes?.data?.data || []); if (!provincesRes?.error) setProvinces(provincesRes?.data?.data || []); if (!modulesRes?.error) setModules(modulesRes?.data?.data || []); } catch (error) { console.error("Error loading form data:", error); } finally { setIsLoadingData(false); } }; loadData(); }, []); useEffect(() => { const loadModuleAccesses = async () => { if (initialData && (initialData as any).id) { try { const res = await getUserLevelModuleAccessesByUserLevelId((initialData as any).id); if (!res?.error) { const accesses = res?.data?.data || []; setUserLevelModuleAccesses(accesses); setSelectedModuleIds(accesses.filter((a: UserLevelModuleAccess) => a.canAccess).map((a: UserLevelModuleAccess) => a.moduleId)); } } catch (error) { console.error("Error loading module accesses:", error); } } }; loadModuleAccesses(); }, [initialData]); const validateForm = (data: UserLevelsCreateRequest): Record => { const newErrors: Record = {}; if (!data.name.trim()) { newErrors.name = "Level name is required"; } else if (data.name.trim().length < 3) { newErrors.name = "Level name must be at least 3 characters"; } if (!data.aliasName.trim()) { newErrors.aliasName = "Alias name is required"; } else if (data.aliasName.trim().length < 3) { newErrors.aliasName = "Alias name must be at least 3 characters"; } if (!data.levelNumber || data.levelNumber <= 0) { newErrors.levelNumber = "Level number must be a positive integer"; } // Check for duplicate level numbers // const existingLevel = userLevels.length > 0 ? userLevels.find(level => level.levelNumber === data.levelNumber) : null; // if (existingLevel && (!initialData || existingLevel.id !== (initialData as any).id)) { // newErrors.levelNumber = "Level number already exists"; // } return newErrors; }; const validateBulkForm = (): boolean => { let isValid = true; const newErrors: Record = {}; bulkFormData.forEach((data, index) => { const itemErrors = validateForm(data); Object.keys(itemErrors).forEach(key => { newErrors[`${index}.${key}`] = itemErrors[key]; }); if (Object.keys(itemErrors).length > 0) { isValid = false; } }); setErrors(newErrors); return isValid; }; const handleFieldChange = (field: keyof UserLevelsCreateRequest, value: any) => { setFormData(prev => ({ ...prev, [field]: value })); if (errors[field]) { setErrors(prev => ({ ...prev, [field]: "" })); } }; const handleBulkFieldChange = (index: number, field: keyof UserLevelsCreateRequest, value: any) => { setBulkFormData((prev: UserLevelsCreateRequest[]) => { const newData = [...prev]; newData[index] = { ...newData[index], [field]: value }; return newData; }); }; const handleTabChange = (value: string) => { setActiveTab(value); if (value === "bulk") { } else { } }; const addBulkItem = () => { const newItem: UserLevelsCreateRequest = { name: "", aliasName: "", levelNumber: 1, parentLevelId: undefined, provinceId: undefined, group: "", isApprovalActive: true, isActive: true, }; setBulkFormData(prev => [...prev, newItem]); }; const renderBulkItemForm = (item: UserLevelsCreateRequest, index: number, onUpdate: (item: UserLevelsCreateRequest) => void, onDelete: () => void) => { return (
onUpdate({ ...item, name: value })} error={errors[`${index}.name`]} required /> onUpdate({ ...item, aliasName: value.toUpperCase() })} error={errors[`${index}.aliasName`]} required helpText="Short identifier for system use" /> onUpdate({ ...item, levelNumber: value ? Number(value) : 1 })} error={errors[`${index}.levelNumber`]} required min={1} helpText="Higher number = higher authority" />
0 ? "Select parent level" : "No parent levels available"} value={item.parentLevelId} onChange={(value) => onUpdate({ ...item, parentLevelId: value !== undefined ? Number(value) : undefined })} options={[ { value: 0, label: "No Parent (Root Level)" }, ...(userLevels.length > 0 ? userLevels.map(level => ({ value: level.id, label: `${level.name} (Level ${level.levelNumber})`, })) : []) ]} helpText={userLevels.length === 0 ? "No parent levels found. This will be a root level." : "Select parent level for hierarchy"} disabled={userLevels.length === 0} /> 0 ? "Select province" : "No provinces available"} value={item.provinceId} onChange={(value) => onUpdate({ ...item, provinceId: value ? Number(value) : undefined })} options={provinces.length > 0 ? provinces.map(province => ({ value: province.id, label: province.prov_name, })) : []} helpText={provinces.length === 0 ? "No provinces found. Please ensure provinces are available in the system." : "Geographic scope for this level"} disabled={provinces.length === 0} />
onUpdate({ ...item, group: value })} helpText="Group classification for organization" /> onUpdate({ ...item, isApprovalActive: value })} helpText="Users with this level can participate in approval process" /> onUpdate({ ...item, isActive: value })} helpText="Level is available for assignment" />
); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); // Determine current mode based on active tab const currentMode = activeTab === "bulk" ? "bulk" : "single"; if (currentMode === "single") { const validationErrors = validateForm(formData); console.log("Form mode: ", currentMode); console.log("Error single ", validationErrors); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); Swal.fire({ title: "Validation Error", text: "Please fix the errors before submitting", icon: "error", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); return; } } else { if (!validateBulkForm()) { Swal.fire({ title: "Validation Error", text: "Please fix the errors before submitting", icon: "error", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); return; } } setIsSubmitting(true); try { if (onSave) { if (currentMode === "single") { // Save user level first const userLevelResponse = await createUserLevel(formData); if (userLevelResponse?.error) { Swal.fire({ title: "Error", text: userLevelResponse?.message || "Failed to create user level", icon: "error", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); setIsSubmitting(false); return; } // Get the created user level ID const createdUserLevelId = userLevelResponse?.data?.data?.id || (initialData as any)?.id; if (createdUserLevelId && selectedModuleIds.length > 0) { // Delete existing module accesses if editing if ((initialData as any)?.id) { for (const access of userLevelModuleAccesses) { await deleteUserLevelModuleAccess(access.id); } } // Create new module accesses in batch const moduleAccessResponse = await createUserLevelModuleAccessesBatch({ userLevelId: createdUserLevelId, moduleIds: selectedModuleIds, canAccess: true, }); if (moduleAccessResponse?.error) { console.error("Error saving module accesses:", moduleAccessResponse?.message); // Don't fail the whole operation, just log the error } } onSave(formData); } else { // For bulk mode, save each item individually let hasErrors = false; let successCount = 0; for (const item of bulkFormData) { const response = await createUserLevel(item); if (response?.error) { hasErrors = true; Swal.fire({ title: "Error", text: `Failed to create user level "${item.name}": ${response?.message || "Unknown error"}`, icon: "error", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); } else { successCount++; Swal.fire({ title: "Success", text: `User level "${item.name}" created successfully`, icon: "success", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); } } // Refresh page if at least one item was created successfully if (successCount > 0) { setTimeout(() => { window.location.reload(); }, 1000); // Small delay to let user see the success message } } } else { if (currentMode === "single") { const response = await createUserLevel(formData); console.log("Create Response: ", response); if (response?.error) { Swal.fire({ title: "Error", text: response?.message || "Failed to create user level", icon: "error", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); } else { // Get the created user level ID const createdUserLevelId = response?.data?.data?.id || (initialData as any)?.id; // Save module accesses if any selected if (createdUserLevelId && selectedModuleIds.length > 0) { // Delete existing module accesses if editing if ((initialData as any)?.id) { for (const access of userLevelModuleAccesses) { await deleteUserLevelModuleAccess(access.id); } } // Create new module accesses in batch const moduleAccessResponse = await createUserLevelModuleAccessesBatch({ userLevelId: createdUserLevelId, moduleIds: selectedModuleIds, canAccess: true, }); if (moduleAccessResponse?.error) { console.error("Error saving module accesses:", moduleAccessResponse?.message); // Don't fail the whole operation, just log the error } } Swal.fire({ title: "Success", text: "User level created successfully", icon: "success", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }).then(() => { // Refresh page after successful creation window.location.reload(); }); } } else { // Bulk creation const promises = bulkFormData.map(item => createUserLevel(item)); const responses = await Promise.all(promises); console.log("Create Responses: ", responses); const failedCount = responses.filter((r: any) => r.error).length; const successCount = responses.length - failedCount; if (failedCount === 0) { Swal.fire({ title: "Success", text: `All ${successCount} user levels created successfully`, icon: "success", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); } else { Swal.fire({ title: "Partial Success", text: `${successCount} user levels created successfully, ${failedCount} failed`, icon: "warning", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); } } } } catch (error) { console.error("Error submitting form:", error); Swal.fire({ title: "Error", text: "An unexpected error occurred", icon: "error", confirmButtonText: "OK", customClass: { popup: 'swal-z-index-9999' } }); } finally { setIsSubmitting(false); } }; const handleReset = () => { Swal.fire({ title: "Reset Form", text: "Are you sure you want to reset all form data?", icon: "warning", showCancelButton: true, confirmButtonText: "Yes, reset", cancelButtonText: "Cancel", customClass: { popup: 'swal-z-index-9999' } }).then((result) => { if (result.isConfirmed) { if (mode === "single") { setFormData({ name: "", aliasName: "", levelNumber: 1, parentLevelId: undefined, provinceId: undefined, group: "", isApprovalActive: true, isActive: true, }); } else { setBulkFormData([]); } setErrors({}); } }); }; const renderHierarchyTree = () => { const buildTree = (levels: UserLevel[], parentId?: number): UserLevel[] => { return levels .filter(level => level.parentLevelId === parentId) .map(level => ({ ...level, children: buildTree(levels, level.id) })); }; const tree = buildTree(userLevels); const renderNode = (node: UserLevel & { children?: UserLevel[] }, depth = 0) => (
{node.name} (Level {node.levelNumber}) {node.group && - {node.group}}
{node.children?.map((child: any) => renderNode(child, depth + 1))}
); return (
{tree.map(node => renderNode(node))}
); }; return (
{isLoadingData && (
Loading form data...
)} Basic Information Module Access {/* Hierarchy */} Bulk Operations {/* Basic Information Tab */} User Level Basic Information
handleFieldChange("name", value)} error={errors.name} required /> handleFieldChange("aliasName", value.toUpperCase())} error={errors.aliasName} required helpText="Short identifier for system use" /> handleFieldChange("levelNumber", value ? Number(value) : 1)} error={errors.levelNumber} required min={1} helpText="Higher number = higher authority" />
0 ? "Select parent level" : "No parent levels available"} value={formData.parentLevelId} onChange={(value) => handleFieldChange("parentLevelId", value !== undefined ? Number(value) : undefined)} options={[ { value: 0, label: "No Parent (Root Level)" }, ...(userLevels.length > 0 ? userLevels.map(level => ({ value: level.id, label: `${level.name} (Level ${level.levelNumber})`, })) : []) ]} helpText={userLevels.length === 0 ? "No parent levels found. This will be a root level." : "Select parent level for hierarchy"} disabled={userLevels.length === 0} /> 0 ? "Select province" : "No provinces available"} value={formData.provinceId} onChange={(value) => handleFieldChange("provinceId", value ? Number(value) : undefined)} options={provinces.length > 0 ? provinces.map(province => ({ value: province.id, label: province.prov_name, })) : []} helpText={provinces.length === 0 ? "No provinces found. Please ensure provinces are available in the system." : "Geographic scope for this level"} disabled={provinces.length === 0} />
handleFieldChange("group", value)} helpText="Group classification for organization" />
handleFieldChange("isApprovalActive", value)} helpText="Users with this level can participate in approval process" /> handleFieldChange("isActive", value)} helpText="Level is available for assignment" />
{/* Hierarchy Tab */} {/* Level Hierarchy Visualization {userLevels.length > 0 ? (
{renderHierarchyTree()}
) : (

No user levels found

)}
*/} {/* Module Access Tab */} Module Access Configuration

Select which modules this user level can access. Only selected modules will be accessible to users with this level.

{modules.length > 0 ? (
{modules.map((module) => ( ))}
) : (
No modules available
)}
{/* Bulk Operations Tab */} Bulk User Level Creation

User Levels

{bulkFormData.length === 0 ? (

No user levels added yet. Add multiple levels to create them in bulk.

) : (
{bulkFormData.map((item, index) => (
User Level #{index + 1}
{renderBulkItemForm( item, index, (updatedItem) => { const newItems = [...bulkFormData]; newItems[index] = updatedItem; setBulkFormData(newItems); }, () => { const newItems = bulkFormData.filter((_, i) => i !== index); setBulkFormData(newItems); } )}
))}
)}
{/* Form Actions */}
{onCancel && ( )}
); };