diff --git a/app/[locale]/(admin)/admin/settings/tenant/page.tsx b/app/[locale]/(admin)/admin/settings/tenant/page.tsx index 4cd19bc..be19ee7 100644 --- a/app/[locale]/(admin)/admin/settings/tenant/page.tsx +++ b/app/[locale]/(admin)/admin/settings/tenant/page.tsx @@ -3,19 +3,8 @@ import React, { useState } 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 { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { - PlusIcon, - SettingsIcon, - UsersIcon, - WorkflowIcon, -} from "@/components/icons"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { PlusIcon, SettingsIcon, UsersIcon, WorkflowIcon, DotsIcon, DeleteIcon } from "@/components/icons"; import { ApprovalWorkflowForm } from "@/components/form/ApprovalWorkflowForm"; import { UserLevelsForm } from "@/components/form/UserLevelsForm"; import { useWorkflowModal } from "@/components/modals/WorkflowModalProvider"; @@ -50,7 +39,713 @@ import useTableColumns from "./component/columns"; import TablePagination from "@/components/table/table-pagination"; import TenantSettingsPageTable from "./component/table-user-level"; +function TenantSettingsContent() { + const [activeTab, setActiveTab] = useState("workflows"); + const [isUserLevelDialogOpen, setIsUserLevelDialogOpen] = useState(false); + const [workflow, setWorkflow] = useState(null); + const [userLevels, setUserLevels] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [isEditingWorkflow, setIsEditingWorkflow] = useState(false); + const [editingUserLevel, setEditingUserLevel] = useState(null); + const { checkWorkflowStatus } = useWorkflowStatusCheck(); + const { showWorkflowModal } = useWorkflowModal(); + // Load data on component mount + React.useEffect(() => { + loadData(); + }, []); + + const loadData = async () => { + setIsLoading(true); + try { + const [comprehensiveWorkflowRes, userLevelsRes] = await Promise.all([ + getApprovalWorkflowComprehensiveDetails(4), // Using workflow ID 4 as per API example + getUserLevels(), + ]); + + if (!comprehensiveWorkflowRes?.error) { + setWorkflow(comprehensiveWorkflowRes?.data?.data || null); + } else { + setWorkflow(null); + } + + if (!userLevelsRes?.error) { + setUserLevels(userLevelsRes?.data?.data || []); + } + } catch (error) { + console.error("Error loading data:", error); + } finally { + setIsLoading(false); + } + }; + + const handleWorkflowSave = async (data: CreateApprovalWorkflowWithClientSettingsRequest) => { + setIsEditingWorkflow(false); + await loadData(); // Reload data after saving + }; + + const handleUserLevelSave = async (data: UserLevelsCreateRequest) => { + try { + const response = await createUserLevel(data); + + if (response?.error) { + console.error("Error creating user level:", response?.message); + // You can add error handling here (e.g., show error message) + } else { + console.log("User level created successfully:", response); + // You can add success handling here (e.g., show success message) + } + } catch (error) { + console.error("Error creating user level:", error); + } + + setIsUserLevelDialogOpen(false); + setEditingUserLevel(null); + await loadData(); // Reload data after saving + }; + + const handleEditUserLevel = (userLevel: UserLevel) => { + setEditingUserLevel(userLevel); + setIsUserLevelDialogOpen(true); + }; + + const handleDeleteUserLevel = async (userLevel: UserLevel) => { + if (window.confirm(`Are you sure you want to delete "${userLevel.name}"? This action cannot be undone.`)) { + try { + // TODO: Implement delete API call + console.log("Delete user level:", userLevel.id); + await loadData(); // Reload data after deletion + } catch (error) { + console.error("Error deleting user level:", error); + } + } + }; + + const handleBulkUserLevelSave = async (data: UserLevelsCreateRequest[]) => { + setIsUserLevelDialogOpen(false); + await loadData(); // Reload data after saving + }; + + return ( +
+
+
+

Tenant Settings

+

+ Manage approval workflows and user levels for your tenant +

+
+
+ + + +
+
+ + + + + + Approval Workflows + + + + User Levels + + + + {/* Approval Workflows Tab */} + +
+

Approval Workflow Setup

+ {workflow && !isEditingWorkflow && ( + + )} +
+ + {isEditingWorkflow ? ( + + + + Setup Approval Workflow + + + + + ({ + 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)} + /> + + + ) : workflow ? ( + + + + {workflow.workflow.name} +
+ {workflow.workflow.isDefault && ( + + Default + + )} + {workflow.workflow.isActive ? ( + + Active + + ) : ( + + Inactive + + )} +
+
+
+ +

+ {workflow.workflow.description} +

+ +
+
+
{workflow.workflow.totalSteps}
+
Total Steps
+
+ +
+
{workflow.workflow.activeSteps}
+
Active Steps
+
+ +
+
+ {workflow.workflow.requiresApproval ? 'Yes' : 'No'} +
+
Requires Approval
+
+ +
+
+ {workflow.workflow.autoPublish ? 'Yes' : 'No'} +
+
Auto Publish
+
+
+ + {/* Workflow Steps Overview */} + {workflow.steps && workflow.steps.length > 0 && ( +
+

Workflow Steps

+
+ {workflow.steps.map((step: any, index: number) => ( +
+
+
+ {step.stepOrder} +
+
+
{step.stepName}
+
+ {step.conditionType && `Condition: ${step.conditionType}`} + {step.autoApproveAfterHours && ` • Auto-approve after ${step.autoApproveAfterHours}h`} + {step.requiredUserLevelName && ` • Required Level: ${step.requiredUserLevelName}`} +
+
+
+
+ {step.canSkip && ( + + Can Skip + + )} + {step.isParallel && ( + + Parallel + + )} + {step.isActive && ( + + Active + + )} + {step.isFirstStep && ( + + First Step + + )} + {step.isLastStep && ( + + Last Step + + )} +
+
+ ))} +
+
+ )} + + {/* Client Settings */} +
+

Client Settings

+
+
+
Default Workflow
+
{workflow.clientSettings.defaultWorkflowName}
+
+
+
Auto Publish Articles
+
+ {workflow.clientSettings.autoPublishArticles ? 'Yes' : 'No'} +
+
+
+
Requires Approval
+
+ {workflow.clientSettings.requiresApproval ? 'Yes' : 'No'} +
+
+
+
Settings Active
+
+ {workflow.clientSettings.isActive ? 'Yes' : 'No'} +
+
+
+
+ + {/* Statistics */} +
+

Workflow Statistics

+
+
+
Total Articles Processed
+
{workflow.statistics.totalArticlesProcessed}
+
+
+
Pending Articles
+
{workflow.statistics.pendingArticles}
+
+
+
Approved Articles
+
{workflow.statistics.approvedArticles}
+
+
+
Rejected Articles
+
{workflow.statistics.rejectedArticles}
+
+
+
Average Processing Time
+
{workflow.statistics.averageProcessingTime}h
+
+
+
Most Active Step
+
{workflow.statistics.mostActiveStep || 'N/A'}
+
+
+
+ + {/* Workflow Metadata */} +
+

Workflow Information

+
+
+
Client ID
+
{workflow.workflow.clientId}
+
+
+
Created At
+
+ {new Date(workflow.workflow.createdAt).toLocaleString()} +
+
+
+
Updated At
+
+ {new Date(workflow.workflow.updatedAt).toLocaleString()} +
+
+
+
Workflow ID
+
{workflow.workflow.id}
+
+
+
Has Branches
+
+ {workflow.workflow.hasBranches ? 'Yes' : 'No'} +
+
+
+
Max Step Order
+
{workflow.workflow.maxStepOrder}
+
+
+
+
+
+ ) : ( + + +
+ +

No Workflow Configured

+

+ Set up your approval workflow to manage content approval process +

+ +
+
+
+ )} +
+ + {/* User Levels Tab */} + +
+

User Levels

+ + + + + + + + {editingUserLevel ? `Edit User Level: ${editingUserLevel.name}` : "Create New User Level"} + + + { + setIsUserLevelDialogOpen(false); + setEditingUserLevel(null); + }} + /> + + +
+ + {/* User Levels Summary */} + {userLevels.length > 0 && ( +
+
+
{userLevels.length}
+
Total User Levels
+
+ +
+
+ {userLevels.filter(ul => ul.isActive).length} +
+
Active Levels
+
+ +
+
+ {userLevels.filter(ul => ul.isApprovalActive).length} +
+
Approval Active
+
+ +
+
+ {userLevels.filter(ul => ul.parentLevelId).length} +
+
Child Levels
+
+
+ )} + + {/* User Levels Hierarchy */} + {userLevels.length > 0 && ( + + + + + User Levels Hierarchy + + + +
+ {userLevels + .filter(ul => !ul.parentLevelId) // Root levels + .sort((a, b) => a.levelNumber - b.levelNumber) + .map(rootLevel => ( +
+ {/* Root Level */} +
+
+ {rootLevel.levelNumber} +
+
+
{rootLevel.name}
+
+ {rootLevel.aliasName} • {rootLevel.group || 'No group'} +
+
+
+ {rootLevel.isActive && ( + + Active + + )} + {rootLevel.isApprovalActive && ( + + Approval Active + + )} +
+
+ + +
+
+ + {/* Child Levels */} + {userLevels + .filter(ul => ul.parentLevelId === rootLevel.id) + .sort((a, b) => a.levelNumber - b.levelNumber) + .map(childLevel => ( +
+
+ {childLevel.levelNumber} +
+
+
{childLevel.name}
+
+ {childLevel.aliasName} • {childLevel.group || 'No group'} +
+
+
+ {childLevel.isActive && ( + + Active + + )} + {childLevel.isApprovalActive && ( + + Approval + + )} +
+
+ + +
+
+ ))} +
+ ))} +
+
+
+ )} + + {/*
+ {userLevels.length > 0 ? userLevels.map((userLevel) => ( + + + + {userLevel.name} +
+ + Level {userLevel.levelNumber} + + {userLevel.isActive ? ( + + Active + + ) : ( + + Inactive + + )} +
+
+
+ +
+
+ Alias: + + {userLevel.aliasName} + +
+ + {userLevel.group && ( +
+ Group: + {userLevel.group} +
+ )} + +
+ Approval Active: + + {userLevel.isApprovalActive ? 'Yes' : 'No'} + +
+ + {userLevel.parentLevelId && ( +
+ Parent Level: + + { userLevels.length > 0 ? userLevels.find(ul => ul.id === userLevel.parentLevelId)?.name || `Level ${userLevel.parentLevelId}` : `Level ${userLevel.parentLevelId}`} + +
+ )} + + {userLevel.provinceId && ( +
+ Province: + Province {userLevel.provinceId} +
+ )} + +
+ Created: + + {userLevel.createdAt ? new Date(userLevel.createdAt).toLocaleDateString() : 'N/A'} + +
+
+ +
+ + +
+
+
+ )) : ''} +
*/} + + {userLevels.length === 0 && !isLoading && ( + + +
+ +

No User Levels Found

+

+ Create your first user level to define approval hierarchy +

+ +
+
+
+ )} +
+
+
+ ); +} export default function TenantSettingsPage() { return ; diff --git a/app/[locale]/globals.css b/app/[locale]/globals.css index 8ea266a..4ff275f 100644 --- a/app/[locale]/globals.css +++ b/app/[locale]/globals.css @@ -1,6 +1,11 @@ @import "tailwindcss"; @import "tw-animate-css"; +/* SweetAlert2 z-index fix */ +.swal-z-index-9999 { + z-index: 9999 !important; +} + @custom-variant dark (&:is(.dark *)); @theme inline { diff --git a/components/form/ApprovalWorkflowForm.tsx b/components/form/ApprovalWorkflowForm.tsx index 289b5f2..23974fd 100644 --- a/components/form/ApprovalWorkflowForm.tsx +++ b/components/form/ApprovalWorkflowForm.tsx @@ -9,6 +9,7 @@ import { DynamicArray } from "./common/DynamicArray"; import { MultiSelect } from "./common/MultiSelect"; import { CreateApprovalWorkflowWithClientSettingsRequest, + UpdateApprovalWorkflowWithClientSettingsRequest, ApprovalWorkflowStepRequest, ClientApprovalSettingsRequest, UserLevel, @@ -16,6 +17,7 @@ import { UserRole, ArticleCategory, createApprovalWorkflowWithClientSettings, + updateApprovalWorkflowWithClientSettings, getUserLevels, getUsers, getUserRoles, @@ -25,6 +27,7 @@ import Swal from "sweetalert2"; interface ApprovalWorkflowFormProps { initialData?: CreateApprovalWorkflowWithClientSettingsRequest; + workflowId?: number; // For update mode onSave?: (data: CreateApprovalWorkflowWithClientSettingsRequest) => void; onCancel?: () => void; isLoading?: boolean; @@ -32,6 +35,7 @@ interface ApprovalWorkflowFormProps { export const ApprovalWorkflowForm: React.FC = ({ initialData, + workflowId, onSave, onCancel, isLoading = false, @@ -137,8 +141,8 @@ export const ApprovalWorkflowForm: React.FC = ({ if (!formData.description.trim()) { newErrors.description = "Description is required"; - } else if (formData.description.trim().length < 10) { - newErrors.description = "Description must be at least 10 characters"; + } else if (formData.description.trim().length < 3) { + newErrors.description = "Description must be at least 3 characters"; } if (formData.steps.length === 0) { @@ -338,7 +342,10 @@ export const ApprovalWorkflowForm: React.FC = ({ title: "Validation Error", text: "Please fix the errors before submitting", icon: "error", - confirmButtonText: "OK" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } }); return; } @@ -358,31 +365,92 @@ export const ApprovalWorkflowForm: React.FC = ({ skipApprovalFor: [] }; - const submitData = { - ...formData, - clientApprovalSettings: hardcodedClientSettings - }; + if (workflowId) { + // Update mode + const updateData: UpdateApprovalWorkflowWithClientSettingsRequest = { + workflowId, + name: formData.name, + description: formData.description, + isActive: formData.isActive || false, + isDefault: formData.isDefault || false, + steps: formData.steps.map(step => ({ + ...step, + branchName: step.stepName, // branchName should be same as stepName + })), + clientSettings: hardcodedClientSettings + }; - console.log("Submit Data: ", submitData); + console.log("Update Data: ", updateData); - const response = await createApprovalWorkflowWithClientSettings(submitData); - - console.log("Response: ", response); + const response = await updateApprovalWorkflowWithClientSettings(updateData); + + console.log("Update Response: ", response); - if (response?.error) { - Swal.fire({ - title: "Error", - text: response?.message?.messages[0] || "Failed to create approval workflow", - icon: "error", - confirmButtonText: "OK" - }); + if (response?.error) { + Swal.fire({ + title: "Error", + text: response?.message?.messages?.[0] || "Failed to update approval workflow", + icon: "error", + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } + }); + } else { + Swal.fire({ + title: "Success", + text: "Approval workflow updated successfully", + icon: "success", + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } + }).then(() => { + // Call onSave to trigger parent refresh + if (onSave) { + onSave(formData); + } + }); + } } else { - Swal.fire({ - title: "Success", - text: "Approval workflow created successfully", - icon: "success", - confirmButtonText: "OK" - }); + // Create mode + const submitData = { + ...formData, + clientApprovalSettings: hardcodedClientSettings + }; + + console.log("Create Data: ", submitData); + + const response = await createApprovalWorkflowWithClientSettings(submitData); + + console.log("Create Response: ", response); + + if (response?.error) { + Swal.fire({ + title: "Error", + text: response?.message?.messages?.[0] || "Failed to create approval workflow", + icon: "error", + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } + }); + } else { + Swal.fire({ + title: "Success", + text: "Approval workflow created successfully", + icon: "success", + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } + }).then(() => { + // Call onSave to trigger parent refresh + if (onSave) { + onSave(submitData); + } + }); + } } } catch (error) { console.error("Error submitting form:", error); @@ -390,7 +458,10 @@ export const ApprovalWorkflowForm: React.FC = ({ title: "Error", text: "An unexpected error occurred", icon: "error", - confirmButtonText: "OK" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } }); } finally { setIsSubmitting(false); @@ -404,7 +475,10 @@ export const ApprovalWorkflowForm: React.FC = ({ icon: "warning", showCancelButton: true, confirmButtonText: "Yes, reset", - cancelButtonText: "Cancel" + cancelButtonText: "Cancel", + customClass: { + popup: 'swal-z-index-9999' + } }).then((result) => { if (result.isConfirmed) { setFormData({ @@ -617,7 +691,7 @@ export const ApprovalWorkflowForm: React.FC = ({ className="flex items-center gap-2" > - {isSubmitting ? "Saving..." : isLoadingData ? "Loading..." : "Save Workflow"} + {isSubmitting ? "Saving..." : isLoadingData ? "Loading..." : workflowId ? "Update Workflow" : "Save Workflow"} diff --git a/components/form/UserLevelsForm.tsx b/components/form/UserLevelsForm.tsx index 08cf133..458e3bc 100644 --- a/components/form/UserLevelsForm.tsx +++ b/components/form/UserLevelsForm.tsx @@ -4,7 +4,7 @@ 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 } from "@/components/icons"; +import { ChevronDownIcon, ChevronUpIcon, SaveIcon, EyeIcon, RotateCcwIcon, UsersIcon, HierarchyIcon, PlusIcon, TrashIcon } from "@/components/icons"; import { FormField } from "./common/FormField"; import { DynamicArray } from "./common/DynamicArray"; import { @@ -56,6 +56,7 @@ export const UserLevelsForm: React.FC = ({ const [isSubmitting, setIsSubmitting] = useState(false); const [expandedHierarchy, setExpandedHierarchy] = useState(false); const [isLoadingData, setIsLoadingData] = useState(true); + const [activeTab, setActiveTab] = useState(mode === "single" ? "basic" : "bulk"); // Load initial data useEffect(() => { @@ -148,8 +149,28 @@ export const UserLevelsForm: React.FC = ({ }); }; - const handleBulkItemsChange = (items: UserLevelsCreateRequest[]) => { - setBulkFormData(items); + const handleTabChange = (value: string) => { + setActiveTab(value); + // Update mode based on active tab + if (value === "bulk") { + // Mode will be determined by activeTab in handleSubmit + } else { + // Mode will be determined by activeTab in handleSubmit + } + }; + + 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) => { @@ -264,9 +285,13 @@ export const UserLevelsForm: React.FC = ({ const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (mode === "single") { + // Determine current mode based on active tab + const currentMode = activeTab === "bulk" ? "bulk" : "single"; + + if (currentMode === "single") { const validationErrors = validateForm(formData); - console.log(validationErrors); + console.log("Form mode: ", currentMode); + console.log("Error single ", validationErrors); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); @@ -274,7 +299,10 @@ export const UserLevelsForm: React.FC = ({ title: "Validation Error", text: "Please fix the errors before submitting", icon: "error", - confirmButtonText: "OK" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } }); return; } @@ -284,7 +312,10 @@ export const UserLevelsForm: React.FC = ({ title: "Validation Error", text: "Please fix the errors before submitting", icon: "error", - confirmButtonText: "OK" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } }); return; } @@ -294,31 +325,75 @@ export const UserLevelsForm: React.FC = ({ try { if (onSave) { - if (mode === "single") { + if (currentMode === "single") { onSave(formData); } else { // For bulk mode, save each item individually + let hasErrors = false; + let successCount = 0; + for (const item of bulkFormData) { - await createUserLevel(item); + 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 (mode === "single") { + 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" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } }); } else { Swal.fire({ title: "Success", text: "User level created successfully", icon: "success", - confirmButtonText: "OK" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } + }).then(() => { + // Refresh page after successful creation + window.location.reload(); }); } } else { @@ -326,6 +401,8 @@ export const UserLevelsForm: React.FC = ({ 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; @@ -334,14 +411,20 @@ export const UserLevelsForm: React.FC = ({ title: "Success", text: `All ${successCount} user levels created successfully`, icon: "success", - confirmButtonText: "OK" + 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" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } }); } } @@ -352,7 +435,10 @@ export const UserLevelsForm: React.FC = ({ title: "Error", text: "An unexpected error occurred", icon: "error", - confirmButtonText: "OK" + confirmButtonText: "OK", + customClass: { + popup: 'swal-z-index-9999' + } }); } finally { setIsSubmitting(false); @@ -366,7 +452,10 @@ export const UserLevelsForm: React.FC = ({ icon: "warning", showCancelButton: true, confirmButtonText: "Yes, reset", - cancelButtonText: "Cancel" + cancelButtonText: "Cancel", + customClass: { + popup: 'swal-z-index-9999' + } }).then((result) => { if (result.isConfirmed) { if (mode === "single") { @@ -429,7 +518,7 @@ export const UserLevelsForm: React.FC = ({ )} - + Basic Information {/* Hierarchy */} @@ -590,15 +679,68 @@ export const UserLevelsForm: React.FC = ({ - +
+
+

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); + } + )} + +
+ ))} +
+ )} +
@@ -636,7 +778,7 @@ export const UserLevelsForm: React.FC = ({ className="flex items-center gap-2" > - {isSubmitting ? "Saving..." : isLoadingData ? "Loading..." : `Save ${mode === "single" ? "User Level" : "User Levels"}`} + {isSubmitting ? "Saving..." : isLoadingData ? "Loading..." : `Save ${activeTab === "bulk" ? "User Levels" : "User Level"}`} diff --git a/components/icons.tsx b/components/icons.tsx index 969010b..3275315 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -2995,4 +2995,35 @@ export const RotateCcwIcon = ({ size = 24, width, height, ...props }: IconSvgPro +); + +export const EditIcon = ({ + size = 24, + width, + height, + ...props +}: IconSvgProps) => ( + + + + ); \ No newline at end of file diff --git a/service/approval-workflows.ts b/service/approval-workflows.ts index f6337ad..60fb9bc 100644 --- a/service/approval-workflows.ts +++ b/service/approval-workflows.ts @@ -24,6 +24,25 @@ export interface ClientApprovalSettingsRequest { isActive?: boolean; } +export interface UpdateApprovalWorkflowWithClientSettingsRequest { + workflowId: number; + name: string; + description: string; + isActive: boolean; + isDefault: boolean; + steps: ApprovalWorkflowStepRequest[]; + clientSettings: { + approvalExemptCategories: any[]; + approvalExemptRoles: any[]; + approvalExemptUsers: any[]; + autoPublishArticles: boolean; + isActive: boolean; + requireApprovalFor: any[]; + requiresApproval: boolean; + skipApprovalFor: any[]; + }; +} + export interface CreateApprovalWorkflowWithClientSettingsRequest { name: string; description: string; @@ -208,6 +227,11 @@ export async function createApprovalWorkflowWithClientSettings(data: CreateAppro return httpPostInterceptor(url, data); } +export async function updateApprovalWorkflowWithClientSettings(data: any) { + const url = "approval-workflows/with-client-settings"; + return httpPutInterceptor(url, data); +} + export async function getUserLevels() { const url = "user-levels"; return httpGetInterceptor(url); diff --git a/service/auth.ts b/service/auth.ts index d6069ce..e6e15e7 100644 --- a/service/auth.ts +++ b/service/auth.ts @@ -64,11 +64,13 @@ export async function doLogin(data: any) { // } export async function getCsrfToken() { - const pathUrl = "csrf"; + const pathUrl = "csrf-token"; const headers = { "content-type": "application/json", }; return httpGet(pathUrl, headers); + + // const url = 'https://kontenhumas.com/api/csrf'; // try { // const response = await fetch(url, {