fix: ui and checkbox in all content
This commit is contained in:
parent
c793844840
commit
02a253ccdc
|
|
@ -412,7 +412,7 @@ const useTableColumns = (onEdit?: (data: any) => void) => {
|
||||||
) : detailData ? (
|
) : detailData ? (
|
||||||
<Tabs defaultValue="basic" className="w-full mt-4">
|
<Tabs defaultValue="basic" className="w-full mt-4">
|
||||||
<TabsList className="grid w-full grid-cols-3">
|
<TabsList className="grid w-full grid-cols-3">
|
||||||
<TabsTrigger value="basic">Basic Information</TabsTrigger>
|
<TabsTrigger className="border border-black" value="basic">Basic Information</TabsTrigger>
|
||||||
<TabsTrigger value="menus">Menu Access</TabsTrigger>
|
<TabsTrigger value="menus">Menu Access</TabsTrigger>
|
||||||
<TabsTrigger value="actions">Action Access</TabsTrigger>
|
<TabsTrigger value="actions">Action Access</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
@ -467,7 +467,7 @@ const useTableColumns = (onEdit?: (data: any) => void) => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm font-medium text-gray-600">Created At:</span>
|
<span className="text-sm font-medium text-gray-600">Created At:</span>
|
||||||
<p className="text-base text-sm">
|
<p className="text-sm">
|
||||||
{detailData.createdAt
|
{detailData.createdAt
|
||||||
? new Date(detailData.createdAt).toLocaleString("id-ID")
|
? new Date(detailData.createdAt).toLocaleString("id-ID")
|
||||||
: "-"}
|
: "-"}
|
||||||
|
|
@ -475,7 +475,7 @@ const useTableColumns = (onEdit?: (data: any) => void) => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm font-medium text-gray-600">Updated At:</span>
|
<span className="text-sm font-medium text-gray-600">Updated At:</span>
|
||||||
<p className="text-base text-sm">
|
<p className="text-sm">
|
||||||
{detailData.updatedAt
|
{detailData.updatedAt
|
||||||
? new Date(detailData.updatedAt).toLocaleString("id-ID")
|
? new Date(detailData.updatedAt).toLocaleString("id-ID")
|
||||||
: "-"}
|
: "-"}
|
||||||
|
|
@ -595,7 +595,7 @@ const useTableColumns = (onEdit?: (data: any) => void) => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex justify-end mt-5">
|
<div className="flex justify-end mt-5">
|
||||||
<Button onClick={() => setIsDialogOpen(false)}>Tutup</Button>
|
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>Tutup</Button>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,10 @@ import { close, loading } from "@/config/swal";
|
||||||
import DetailTenant from "@/components/form/tenant/tenant-detail-update-form";
|
import DetailTenant from "@/components/form/tenant/tenant-detail-update-form";
|
||||||
|
|
||||||
function TenantSettingsContentTable() {
|
function TenantSettingsContentTable() {
|
||||||
const [activeTab, setActiveTab] = useLocalStorage('tenant-settings-active-tab', 'profile');
|
const [activeTab, setActiveTab] = useLocalStorage(
|
||||||
|
"tenant-settings-active-tab",
|
||||||
|
"profile",
|
||||||
|
);
|
||||||
const [isUserLevelDialogOpen, setIsUserLevelDialogOpen] = useState(false);
|
const [isUserLevelDialogOpen, setIsUserLevelDialogOpen] = useState(false);
|
||||||
const [workflow, setWorkflow] =
|
const [workflow, setWorkflow] =
|
||||||
useState<ComprehensiveWorkflowResponse | null>(null);
|
useState<ComprehensiveWorkflowResponse | null>(null);
|
||||||
|
|
@ -120,7 +123,7 @@ function TenantSettingsContentTable() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWorkflowSave = async (
|
const handleWorkflowSave = async (
|
||||||
data: CreateApprovalWorkflowWithClientSettingsRequest
|
data: CreateApprovalWorkflowWithClientSettingsRequest,
|
||||||
) => {
|
) => {
|
||||||
setIsEditingWorkflow(false);
|
setIsEditingWorkflow(false);
|
||||||
await loadData();
|
await loadData();
|
||||||
|
|
@ -160,7 +163,7 @@ function TenantSettingsContentTable() {
|
||||||
|
|
||||||
const columns = React.useMemo(
|
const columns = React.useMemo(
|
||||||
() => useTableColumns((data) => handleEditUserLevel(data)),
|
() => useTableColumns((data) => handleEditUserLevel(data)),
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
const [showData, setShowData] = React.useState("10");
|
const [showData, setShowData] = React.useState("10");
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
|
|
@ -252,7 +255,7 @@ function TenantSettingsContentTable() {
|
||||||
Approval Workflow Setup
|
Approval Workflow Setup
|
||||||
</h2>
|
</h2>
|
||||||
{workflow && !isEditingWorkflow && (
|
{workflow && !isEditingWorkflow && (
|
||||||
<Button
|
<Button variant="outline"
|
||||||
onClick={() => setIsEditingWorkflow(true)}
|
onClick={() => setIsEditingWorkflow(true)}
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
>
|
>
|
||||||
|
|
@ -667,7 +670,10 @@ function TenantSettingsContentTable() {
|
||||||
onOpenChange={setIsUserLevelDialogOpen}
|
onOpenChange={setIsUserLevelDialogOpen}
|
||||||
>
|
>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button className="flex items-center gap-2">
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="flex items-center gap-2 hover:bg-muted focus-visible:bg-muted"
|
||||||
|
>
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
Create User Level
|
Create User Level
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -830,7 +836,7 @@ function TenantSettingsContentTable() {
|
||||||
? null
|
? null
|
||||||
: flexRender(
|
: flexRender(
|
||||||
header.column.columnDef.header,
|
header.column.columnDef.header,
|
||||||
header.getContext()
|
header.getContext(),
|
||||||
)}
|
)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
))}
|
))}
|
||||||
|
|
@ -849,7 +855,7 @@ function TenantSettingsContentTable() {
|
||||||
<TableCell key={cell.id}>
|
<TableCell key={cell.id}>
|
||||||
{flexRender(
|
{flexRender(
|
||||||
cell.column.columnDef.cell,
|
cell.column.columnDef.cell,
|
||||||
cell.getContext()
|
cell.getContext(),
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,21 @@ import React, { useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
import {
|
||||||
import { PlusIcon, SettingsIcon, UsersIcon, WorkflowIcon, DotsIcon, DeleteIcon } from "@/components/icons";
|
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 { ApprovalWorkflowForm } from "@/components/form/ApprovalWorkflowForm";
|
||||||
import { UserLevelsForm } from "@/components/form/UserLevelsForm";
|
import { UserLevelsForm } from "@/components/form/UserLevelsForm";
|
||||||
import { useWorkflowModal } from "@/components/modals/WorkflowModalProvider";
|
import { useWorkflowModal } from "@/components/modals/WorkflowModalProvider";
|
||||||
|
|
@ -42,11 +55,14 @@ import TenantSettingsPageTable from "./component/tenant-settings-content-table";
|
||||||
function TenantSettingsContent() {
|
function TenantSettingsContent() {
|
||||||
const [activeTab, setActiveTab] = useState("workflows");
|
const [activeTab, setActiveTab] = useState("workflows");
|
||||||
const [isUserLevelDialogOpen, setIsUserLevelDialogOpen] = useState(false);
|
const [isUserLevelDialogOpen, setIsUserLevelDialogOpen] = useState(false);
|
||||||
const [workflow, setWorkflow] = useState<ComprehensiveWorkflowResponse | null>(null);
|
const [workflow, setWorkflow] =
|
||||||
|
useState<ComprehensiveWorkflowResponse | null>(null);
|
||||||
const [userLevels, setUserLevels] = useState<UserLevel[]>([]);
|
const [userLevels, setUserLevels] = useState<UserLevel[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isEditingWorkflow, setIsEditingWorkflow] = useState(false);
|
const [isEditingWorkflow, setIsEditingWorkflow] = useState(false);
|
||||||
const [editingUserLevel, setEditingUserLevel] = useState<UserLevel | null>(null);
|
const [editingUserLevel, setEditingUserLevel] = useState<UserLevel | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
const { checkWorkflowStatus } = useWorkflowStatusCheck();
|
const { checkWorkflowStatus } = useWorkflowStatusCheck();
|
||||||
const { showWorkflowModal } = useWorkflowModal();
|
const { showWorkflowModal } = useWorkflowModal();
|
||||||
|
|
||||||
|
|
@ -59,7 +75,7 @@ function TenantSettingsContent() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const [comprehensiveWorkflowRes, userLevelsRes] = await Promise.all([
|
const [comprehensiveWorkflowRes, userLevelsRes] = await Promise.all([
|
||||||
getApprovalWorkflowComprehensiveDetails(),
|
getApprovalWorkflowComprehensiveDetails(),
|
||||||
getUserLevels(),
|
getUserLevels(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -68,7 +84,7 @@ function TenantSettingsContent() {
|
||||||
} else {
|
} else {
|
||||||
setWorkflow(null);
|
setWorkflow(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userLevelsRes?.error) {
|
if (!userLevelsRes?.error) {
|
||||||
setUserLevels(userLevelsRes?.data?.data || []);
|
setUserLevels(userLevelsRes?.data?.data || []);
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +95,9 @@ function TenantSettingsContent() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWorkflowSave = async (data: CreateApprovalWorkflowWithClientSettingsRequest) => {
|
const handleWorkflowSave = async (
|
||||||
|
data: CreateApprovalWorkflowWithClientSettingsRequest,
|
||||||
|
) => {
|
||||||
setIsEditingWorkflow(false);
|
setIsEditingWorkflow(false);
|
||||||
await loadData(); // Reload data after saving
|
await loadData(); // Reload data after saving
|
||||||
};
|
};
|
||||||
|
|
@ -87,7 +105,7 @@ function TenantSettingsContent() {
|
||||||
const handleUserLevelSave = async (data: UserLevelsCreateRequest) => {
|
const handleUserLevelSave = async (data: UserLevelsCreateRequest) => {
|
||||||
try {
|
try {
|
||||||
const response = await createUserLevel(data);
|
const response = await createUserLevel(data);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
console.error("Error creating user level:", response?.message);
|
console.error("Error creating user level:", response?.message);
|
||||||
// You can add error handling here (e.g., show error message)
|
// You can add error handling here (e.g., show error message)
|
||||||
|
|
@ -98,7 +116,7 @@ function TenantSettingsContent() {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating user level:", error);
|
console.error("Error creating user level:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsUserLevelDialogOpen(false);
|
setIsUserLevelDialogOpen(false);
|
||||||
setEditingUserLevel(null);
|
setEditingUserLevel(null);
|
||||||
await loadData(); // Reload data after saving
|
await loadData(); // Reload data after saving
|
||||||
|
|
@ -110,7 +128,11 @@ function TenantSettingsContent() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteUserLevel = async (userLevel: UserLevel) => {
|
const handleDeleteUserLevel = async (userLevel: UserLevel) => {
|
||||||
if (window.confirm(`Are you sure you want to delete "${userLevel.name}"? This action cannot be undone.`)) {
|
if (
|
||||||
|
window.confirm(
|
||||||
|
`Are you sure you want to delete "${userLevel.name}"? This action cannot be undone.`,
|
||||||
|
)
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
// TODO: Implement delete API call
|
// TODO: Implement delete API call
|
||||||
console.log("Delete user level:", userLevel.id);
|
console.log("Delete user level:", userLevel.id);
|
||||||
|
|
@ -137,15 +159,11 @@ function TenantSettingsContent() {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<SettingsIcon className="h-6 w-6 text-gray-500" />
|
<SettingsIcon className="h-6 w-6 text-gray-500" />
|
||||||
<Button
|
<Button variant="outline" size="sm" onClick={checkWorkflowStatus}>
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={checkWorkflowStatus}
|
|
||||||
>
|
|
||||||
Check Workflow Status
|
Check Workflow Status
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => showWorkflowModal({ hasWorkflowSetup: false })}
|
onClick={() => showWorkflowModal({ hasWorkflowSetup: false })}
|
||||||
className="bg-red-50 text-red-600 border-red-200 hover:bg-red-100"
|
className="bg-red-50 text-red-600 border-red-200 hover:bg-red-100"
|
||||||
|
|
@ -172,7 +190,8 @@ function TenantSettingsContent() {
|
||||||
<div className="flex items-center justify-between">
|
<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 && (
|
{workflow && !isEditingWorkflow && (
|
||||||
<Button
|
<Button
|
||||||
|
variant="outline"
|
||||||
onClick={() => setIsEditingWorkflow(true)}
|
onClick={() => setIsEditingWorkflow(true)}
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
>
|
>
|
||||||
|
|
@ -197,34 +216,47 @@ function TenantSettingsContent() {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<ApprovalWorkflowForm
|
<ApprovalWorkflowForm
|
||||||
initialData={workflow ? {
|
initialData={
|
||||||
name: workflow.workflow.name,
|
workflow
|
||||||
description: workflow.workflow.description,
|
? {
|
||||||
isDefault: workflow.workflow.isDefault,
|
name: workflow.workflow.name,
|
||||||
isActive: workflow.workflow.isActive,
|
description: workflow.workflow.description,
|
||||||
requiresApproval: workflow.workflow.requiresApproval,
|
isDefault: workflow.workflow.isDefault,
|
||||||
autoPublish: workflow.workflow.autoPublish,
|
isActive: workflow.workflow.isActive,
|
||||||
steps: workflow.steps?.map(step => ({
|
requiresApproval: workflow.workflow.requiresApproval,
|
||||||
stepOrder: step.stepOrder,
|
autoPublish: workflow.workflow.autoPublish,
|
||||||
stepName: step.stepName,
|
steps:
|
||||||
requiredUserLevelId: step.requiredUserLevelId,
|
workflow.steps?.map((step) => ({
|
||||||
canSkip: step.canSkip,
|
stepOrder: step.stepOrder,
|
||||||
autoApproveAfterHours: step.autoApproveAfterHours,
|
stepName: step.stepName,
|
||||||
isActive: step.isActive,
|
requiredUserLevelId: step.requiredUserLevelId,
|
||||||
conditionType: step.conditionType,
|
canSkip: step.canSkip,
|
||||||
conditionValue: step.conditionValue,
|
autoApproveAfterHours: step.autoApproveAfterHours,
|
||||||
})) || [],
|
isActive: step.isActive,
|
||||||
clientApprovalSettings: {
|
conditionType: step.conditionType,
|
||||||
approvalExemptCategories: workflow.clientSettings.exemptCategoriesDetails || [],
|
conditionValue: step.conditionValue,
|
||||||
approvalExemptRoles: workflow.clientSettings.exemptRolesDetails || [],
|
})) || [],
|
||||||
approvalExemptUsers: workflow.clientSettings.exemptUsersDetails || [],
|
clientApprovalSettings: {
|
||||||
autoPublishArticles: workflow.clientSettings.autoPublishArticles,
|
approvalExemptCategories:
|
||||||
isActive: workflow.clientSettings.isActive,
|
workflow.clientSettings.exemptCategoriesDetails ||
|
||||||
requireApprovalFor: workflow.clientSettings.requireApprovalFor || [],
|
[],
|
||||||
requiresApproval: workflow.clientSettings.requiresApproval,
|
approvalExemptRoles:
|
||||||
skipApprovalFor: workflow.clientSettings.skipApprovalFor || []
|
workflow.clientSettings.exemptRolesDetails || [],
|
||||||
}
|
approvalExemptUsers:
|
||||||
} : undefined}
|
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}
|
workflowId={workflow?.workflow.id}
|
||||||
onSave={handleWorkflowSave}
|
onSave={handleWorkflowSave}
|
||||||
onCancel={() => setIsEditingWorkflow(false)}
|
onCancel={() => setIsEditingWorkflow(false)}
|
||||||
|
|
@ -258,28 +290,38 @@ function TenantSettingsContent() {
|
||||||
<p className="text-gray-600 text-sm mb-4">
|
<p className="text-gray-600 text-sm mb-4">
|
||||||
{workflow.workflow.description}
|
{workflow.workflow.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
<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-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">
|
||||||
<div className="text-sm text-gray-600">Total Steps</div>
|
{workflow.workflow.totalSteps}
|
||||||
</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>
|
||||||
<div className="text-sm text-gray-600">Requires Approval</div>
|
<div className="text-sm text-gray-600">Total Steps</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
<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'}`}>
|
<div className="text-2xl font-bold text-green-600">
|
||||||
{workflow.workflow.autoPublish ? 'Yes' : 'No'}
|
{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>
|
||||||
<div className="text-sm text-gray-600">Auto Publish</div>
|
<div className="text-sm text-gray-600">Auto Publish</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -291,7 +333,10 @@ function TenantSettingsContent() {
|
||||||
<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">
|
<div className="space-y-2">
|
||||||
{workflow.steps.map((step: any, index: number) => (
|
{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="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">
|
<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}
|
{step.stepOrder}
|
||||||
|
|
@ -299,9 +344,12 @@ function TenantSettingsContent() {
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium">{step.stepName}</div>
|
<div className="font-medium">{step.stepName}</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
{step.conditionType && `Condition: ${step.conditionType}`}
|
{step.conditionType &&
|
||||||
{step.autoApproveAfterHours && ` • Auto-approve after ${step.autoApproveAfterHours}h`}
|
`Condition: ${step.conditionType}`}
|
||||||
{step.requiredUserLevelName && ` • Required Level: ${step.requiredUserLevelName}`}
|
{step.autoApproveAfterHours &&
|
||||||
|
` • Auto-approve after ${step.autoApproveAfterHours}h`}
|
||||||
|
{step.requiredUserLevelName &&
|
||||||
|
` • Required Level: ${step.requiredUserLevelName}`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -343,25 +391,45 @@ function TenantSettingsContent() {
|
||||||
<h4 className="text-lg font-medium mb-3">Client Settings</h4>
|
<h4 className="text-lg font-medium mb-3">Client Settings</h4>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Default Workflow</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-sm text-gray-600">{workflow.clientSettings.defaultWorkflowName}</div>
|
Default Workflow
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="text-sm text-gray-600">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Auto Publish Articles</div>
|
{workflow.clientSettings.defaultWorkflowName}
|
||||||
<div className={`text-sm font-medium ${workflow.clientSettings.autoPublishArticles ? 'text-green-600' : 'text-red-600'}`}>
|
|
||||||
{workflow.clientSettings.autoPublishArticles ? 'Yes' : 'No'}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Requires Approval</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className={`text-sm font-medium ${workflow.clientSettings.requiresApproval ? 'text-green-600' : 'text-red-600'}`}>
|
Auto Publish Articles
|
||||||
{workflow.clientSettings.requiresApproval ? 'Yes' : 'No'}
|
</div>
|
||||||
|
<div
|
||||||
|
className={`text-sm font-medium ${workflow.clientSettings.autoPublishArticles ? "text-green-600" : "text-red-600"}`}
|
||||||
|
>
|
||||||
|
{workflow.clientSettings.autoPublishArticles
|
||||||
|
? "Yes"
|
||||||
|
: "No"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Settings Active</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className={`text-sm font-medium ${workflow.clientSettings.isActive ? 'text-green-600' : 'text-red-600'}`}>
|
Requires Approval
|
||||||
{workflow.clientSettings.isActive ? 'Yes' : 'No'}
|
</div>
|
||||||
|
<div
|
||||||
|
className={`text-sm font-medium ${workflow.clientSettings.requiresApproval ? "text-green-600" : "text-red-600"}`}
|
||||||
|
>
|
||||||
|
{workflow.clientSettings.requiresApproval
|
||||||
|
? "Yes"
|
||||||
|
: "No"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Settings Active
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`text-sm font-medium ${workflow.clientSettings.isActive ? "text-green-600" : "text-red-600"}`}
|
||||||
|
>
|
||||||
|
{workflow.clientSettings.isActive ? "Yes" : "No"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -369,68 +437,116 @@ function TenantSettingsContent() {
|
||||||
|
|
||||||
{/* Statistics */}
|
{/* Statistics */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h4 className="text-lg font-medium mb-3">Workflow Statistics</h4>
|
<h4 className="text-lg font-medium mb-3">
|
||||||
|
Workflow Statistics
|
||||||
|
</h4>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Total Articles Processed</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-2xl font-bold text-blue-600">{workflow.statistics.totalArticlesProcessed}</div>
|
Total Articles Processed
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-blue-600">
|
||||||
|
{workflow.statistics.totalArticlesProcessed}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Pending Articles</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-2xl font-bold text-yellow-600">{workflow.statistics.pendingArticles}</div>
|
Pending Articles
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-yellow-600">
|
||||||
|
{workflow.statistics.pendingArticles}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Approved Articles</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-2xl font-bold text-green-600">{workflow.statistics.approvedArticles}</div>
|
Approved Articles
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-green-600">
|
||||||
|
{workflow.statistics.approvedArticles}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Rejected Articles</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-2xl font-bold text-red-600">{workflow.statistics.rejectedArticles}</div>
|
Rejected Articles
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-red-600">
|
||||||
|
{workflow.statistics.rejectedArticles}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Average Processing Time</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-2xl font-bold text-purple-600">{workflow.statistics.averageProcessingTime}h</div>
|
Average Processing Time
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-purple-600">
|
||||||
|
{workflow.statistics.averageProcessingTime}h
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Most Active Step</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-sm text-gray-600">{workflow.statistics.mostActiveStep || 'N/A'}</div>
|
Most Active Step
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
{workflow.statistics.mostActiveStep || "N/A"}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Workflow Metadata */}
|
{/* Workflow Metadata */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h4 className="text-lg font-medium mb-3">Workflow Information</h4>
|
<h4 className="text-lg font-medium mb-3">
|
||||||
|
Workflow Information
|
||||||
|
</h4>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Client ID</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-sm text-gray-600 font-mono">{workflow.workflow.clientId}</div>
|
Client ID
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-600 font-mono">
|
||||||
|
{workflow.workflow.clientId}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Created At</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Created At
|
||||||
|
</div>
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
{new Date(workflow.workflow.createdAt).toLocaleString()}
|
{new Date(workflow.workflow.createdAt).toLocaleString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Updated At</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Updated At
|
||||||
|
</div>
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
{new Date(workflow.workflow.updatedAt).toLocaleString()}
|
{new Date(workflow.workflow.updatedAt).toLocaleString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Workflow ID</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-sm text-gray-600 font-mono">{workflow.workflow.id}</div>
|
Workflow ID
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="text-sm text-gray-600 font-mono">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Has Branches</div>
|
{workflow.workflow.id}
|
||||||
<div className={`text-sm font-medium ${workflow.workflow.hasBranches ? 'text-green-600' : 'text-gray-600'}`}>
|
|
||||||
{workflow.workflow.hasBranches ? 'Yes' : 'No'}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-50 rounded-lg">
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
<div className="text-sm font-medium text-gray-700 mb-1">Max Step Order</div>
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
<div className="text-sm text-gray-600">{workflow.workflow.maxStepOrder}</div>
|
Has Branches
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`text-sm font-medium ${workflow.workflow.hasBranches ? "text-green-600" : "text-gray-600"}`}
|
||||||
|
>
|
||||||
|
{workflow.workflow.hasBranches ? "Yes" : "No"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-3 bg-gray-50 rounded-lg">
|
||||||
|
<div className="text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Max Step Order
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
{workflow.workflow.maxStepOrder}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -441,9 +557,12 @@ function TenantSettingsContent() {
|
||||||
<CardContent className="flex items-center justify-center py-12">
|
<CardContent className="flex items-center justify-center py-12">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<WorkflowIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
<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 Configured</h3>
|
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||||
|
No Workflow Configured
|
||||||
|
</h3>
|
||||||
<p className="text-gray-500 mb-4">
|
<p className="text-gray-500 mb-4">
|
||||||
Set up your approval workflow to manage content approval process
|
Set up your approval workflow to manage content approval
|
||||||
|
process
|
||||||
</p>
|
</p>
|
||||||
<Button onClick={() => setIsEditingWorkflow(true)}>
|
<Button onClick={() => setIsEditingWorkflow(true)}>
|
||||||
<PlusIcon className="h-4 w-4 mr-2" />
|
<PlusIcon className="h-4 w-4 mr-2" />
|
||||||
|
|
@ -459,7 +578,10 @@ function TenantSettingsContent() {
|
||||||
<TabsContent value="user-levels" className="space-y-6">
|
<TabsContent value="user-levels" className="space-y-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-2xl font-semibold">User Levels</h2>
|
<h2 className="text-2xl font-semibold">User Levels</h2>
|
||||||
<Dialog open={isUserLevelDialogOpen} onOpenChange={setIsUserLevelDialogOpen}>
|
<Dialog
|
||||||
|
open={isUserLevelDialogOpen}
|
||||||
|
onOpenChange={setIsUserLevelDialogOpen}
|
||||||
|
>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button className="flex items-center gap-2">
|
<Button className="flex items-center gap-2">
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
|
@ -469,22 +591,28 @@ function TenantSettingsContent() {
|
||||||
<DialogContent className="md:max-w-6xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="md:max-w-6xl max-h-[90vh] overflow-y-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{editingUserLevel ? `Edit User Level: ${editingUserLevel.name}` : "Create New User Level"}
|
{editingUserLevel
|
||||||
|
? `Edit User Level: ${editingUserLevel.name}`
|
||||||
|
: "Create New User Level"}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<UserLevelsForm
|
<UserLevelsForm
|
||||||
mode="single"
|
mode="single"
|
||||||
initialData={editingUserLevel ? {
|
initialData={
|
||||||
// id: editingUserLevel.id,
|
editingUserLevel
|
||||||
name: editingUserLevel.name,
|
? {
|
||||||
aliasName: editingUserLevel.aliasName,
|
// id: editingUserLevel.id,
|
||||||
levelNumber: editingUserLevel.levelNumber,
|
name: editingUserLevel.name,
|
||||||
parentLevelId: editingUserLevel.parentLevelId || 0,
|
aliasName: editingUserLevel.aliasName,
|
||||||
provinceId: editingUserLevel.provinceId,
|
levelNumber: editingUserLevel.levelNumber,
|
||||||
group: editingUserLevel.group || "",
|
parentLevelId: editingUserLevel.parentLevelId || 0,
|
||||||
isApprovalActive: editingUserLevel.isApprovalActive,
|
provinceId: editingUserLevel.provinceId,
|
||||||
isActive: editingUserLevel.isActive,
|
group: editingUserLevel.group || "",
|
||||||
} : undefined}
|
isApprovalActive: editingUserLevel.isApprovalActive,
|
||||||
|
isActive: editingUserLevel.isActive,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
onSave={handleUserLevelSave}
|
onSave={handleUserLevelSave}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setIsUserLevelDialogOpen(false);
|
setIsUserLevelDialogOpen(false);
|
||||||
|
|
@ -499,27 +627,29 @@ function TenantSettingsContent() {
|
||||||
{userLevels.length > 0 && (
|
{userLevels.length > 0 && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
<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-center p-4 bg-gray-50 rounded-lg">
|
||||||
<div className="text-2xl font-bold text-blue-600">{userLevels.length}</div>
|
<div className="text-2xl font-bold text-blue-600">
|
||||||
|
{userLevels.length}
|
||||||
|
</div>
|
||||||
<div className="text-sm text-gray-600">Total User Levels</div>
|
<div className="text-sm text-gray-600">Total User Levels</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
||||||
<div className="text-2xl font-bold text-green-600">
|
<div className="text-2xl font-bold text-green-600">
|
||||||
{userLevels.filter(ul => ul.isActive).length}
|
{userLevels.filter((ul) => ul.isActive).length}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">Active Levels</div>
|
<div className="text-sm text-gray-600">Active Levels</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
||||||
<div className="text-2xl font-bold text-purple-600">
|
<div className="text-2xl font-bold text-purple-600">
|
||||||
{userLevels.filter(ul => ul.isApprovalActive).length}
|
{userLevels.filter((ul) => ul.isApprovalActive).length}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">Approval Active</div>
|
<div className="text-sm text-gray-600">Approval Active</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
||||||
<div className="text-2xl font-bold text-orange-600">
|
<div className="text-2xl font-bold text-orange-600">
|
||||||
{userLevels.filter(ul => ul.parentLevelId).length}
|
{userLevels.filter((ul) => ul.parentLevelId).length}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">Child Levels</div>
|
<div className="text-sm text-gray-600">Child Levels</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -538,9 +668,9 @@ function TenantSettingsContent() {
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{userLevels
|
{userLevels
|
||||||
.filter(ul => !ul.parentLevelId) // Root levels
|
.filter((ul) => !ul.parentLevelId)
|
||||||
.sort((a, b) => a.levelNumber - b.levelNumber)
|
.sort((a, b) => a.levelNumber - b.levelNumber)
|
||||||
.map(rootLevel => (
|
.map((rootLevel) => (
|
||||||
<div key={rootLevel.id} className="space-y-2">
|
<div key={rootLevel.id} className="space-y-2">
|
||||||
{/* Root Level */}
|
{/* Root Level */}
|
||||||
<div className="flex items-center gap-3 p-3 bg-blue-50 rounded-lg border-l-4 border-blue-500">
|
<div className="flex items-center gap-3 p-3 bg-blue-50 rounded-lg border-l-4 border-blue-500">
|
||||||
|
|
@ -550,7 +680,8 @@ function TenantSettingsContent() {
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="font-medium">{rootLevel.name}</div>
|
<div className="font-medium">{rootLevel.name}</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
{rootLevel.aliasName} • {rootLevel.group || 'No group'}
|
{rootLevel.aliasName} •{" "}
|
||||||
|
{rootLevel.group || "No group"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -586,20 +717,26 @@ function TenantSettingsContent() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Child Levels */}
|
{/* Child Levels */}
|
||||||
{userLevels
|
{userLevels
|
||||||
.filter(ul => ul.parentLevelId === rootLevel.id)
|
.filter((ul) => ul.parentLevelId === rootLevel.id)
|
||||||
.sort((a, b) => a.levelNumber - b.levelNumber)
|
.sort((a, b) => a.levelNumber - b.levelNumber)
|
||||||
.map(childLevel => (
|
.map((childLevel) => (
|
||||||
<div key={childLevel.id} className="ml-8 flex items-center gap-3 p-3 bg-gray-50 rounded-lg border-l-4 border-gray-300">
|
<div
|
||||||
|
key={childLevel.id}
|
||||||
|
className="ml-8 flex items-center gap-3 p-3 bg-gray-50 rounded-lg border-l-4 border-gray-300"
|
||||||
|
>
|
||||||
<div className="w-6 h-6 bg-gray-100 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">
|
<div className="w-6 h-6 bg-gray-100 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">
|
||||||
{childLevel.levelNumber}
|
{childLevel.levelNumber}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="font-medium text-sm">{childLevel.name}</div>
|
<div className="font-medium text-sm">
|
||||||
|
{childLevel.name}
|
||||||
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
{childLevel.aliasName} • {childLevel.group || 'No group'}
|
{childLevel.aliasName} •{" "}
|
||||||
|
{childLevel.group || "No group"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
|
@ -618,7 +755,9 @@ function TenantSettingsContent() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleEditUserLevel(childLevel)}
|
onClick={() =>
|
||||||
|
handleEditUserLevel(childLevel)
|
||||||
|
}
|
||||||
className="h-7 w-7 p-0 border-gray-300 hover:border-blue-500 hover:bg-blue-50"
|
className="h-7 w-7 p-0 border-gray-300 hover:border-blue-500 hover:bg-blue-50"
|
||||||
title="Edit User Level"
|
title="Edit User Level"
|
||||||
>
|
>
|
||||||
|
|
@ -627,7 +766,9 @@ function TenantSettingsContent() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleDeleteUserLevel(childLevel)}
|
onClick={() =>
|
||||||
|
handleDeleteUserLevel(childLevel)
|
||||||
|
}
|
||||||
className="h-7 w-7 p-0 border-gray-300 hover:border-red-500 hover:bg-red-50"
|
className="h-7 w-7 p-0 border-gray-300 hover:border-red-500 hover:bg-red-50"
|
||||||
title="Delete User Level"
|
title="Delete User Level"
|
||||||
>
|
>
|
||||||
|
|
@ -730,7 +871,9 @@ function TenantSettingsContent() {
|
||||||
<CardContent className="flex items-center justify-center py-12">
|
<CardContent className="flex items-center justify-center py-12">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<UsersIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
<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">
|
<p className="text-gray-500 mb-4">
|
||||||
Create your first user level to define approval hierarchy
|
Create your first user level to define approval hierarchy
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,10 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { PlusIcon, EditIcon, DeleteIcon } from "@/components/icons";
|
import { PlusIcon, DeleteIcon } from "@/components/icons";
|
||||||
import { errorAutoClose, successAutoClose } from "@/lib/swal";
|
import { errorAutoClose, successAutoClose } from "@/lib/swal";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { EditIcon } from "lucide-react";
|
||||||
|
|
||||||
export default function EditTenantPage() {
|
export default function EditTenantPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -610,7 +611,7 @@ export default function EditTenantPage() {
|
||||||
Approval Workflow Setup
|
Approval Workflow Setup
|
||||||
</h2>
|
</h2>
|
||||||
{workflow && !isEditingWorkflow && (
|
{workflow && !isEditingWorkflow && (
|
||||||
<Button
|
<Button variant="outline"
|
||||||
onClick={() => setIsEditingWorkflow(true)}
|
onClick={() => setIsEditingWorkflow(true)}
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1486,7 +1486,7 @@ export const UserLevelsForm: React.FC<UserLevelsFormProps> = ({
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button type="submit" disabled={isSubmitting}>
|
<Button variant="outline" type="submit" disabled={isSubmitting}>
|
||||||
{isSubmitting ? "Saving..." : "Save User Level"}
|
{isSubmitting ? "Saving..." : "Save User Level"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,28 @@ import React from "react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
|
||||||
interface FormFieldProps {
|
interface FormFieldProps {
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
type?: "text" | "email" | "password" | "number" | "tel" | "url" | "textarea" | "select" | "checkbox";
|
type?:
|
||||||
|
| "text"
|
||||||
|
| "email"
|
||||||
|
| "password"
|
||||||
|
| "number"
|
||||||
|
| "tel"
|
||||||
|
| "url"
|
||||||
|
| "textarea"
|
||||||
|
| "select"
|
||||||
|
| "checkbox";
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
value: any;
|
value: any;
|
||||||
onChange: (value: any) => void;
|
onChange: (value: any) => void;
|
||||||
|
|
@ -68,7 +83,9 @@ export const FormField: React.FC<FormFieldProps> = ({
|
||||||
onValueChange={(val) => onChange(val)}
|
onValueChange={(val) => onChange(val)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<SelectTrigger className={`${hasError ? "border-red-500" : ""} ${className}`}>
|
<SelectTrigger
|
||||||
|
className={`${hasError ? "border-red-500" : ""} ${className}`}
|
||||||
|
>
|
||||||
<SelectValue placeholder={placeholder || `Select ${label}`} />
|
<SelectValue placeholder={placeholder || `Select ${label}`} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|
@ -83,20 +100,45 @@ export const FormField: React.FC<FormFieldProps> = ({
|
||||||
|
|
||||||
case "checkbox":
|
case "checkbox":
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center gap-3">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={fieldId}
|
checked={value}
|
||||||
checked={!!value}
|
onCheckedChange={(v) => onChange(!!v)}
|
||||||
onCheckedChange={(checked) => onChange(checked)}
|
className="
|
||||||
disabled={disabled}
|
h-4 w-4
|
||||||
className={hasError ? "border-red-500" : ""}
|
rounded-full
|
||||||
|
border border-black
|
||||||
|
text-white
|
||||||
|
data-[state=checked]:bg-black
|
||||||
|
focus-visible:ring-2 focus-visible:ring-black
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<Label htmlFor={fieldId} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
||||||
|
<Label
|
||||||
|
htmlFor={fieldId}
|
||||||
|
className="text-sm font-medium leading-none cursor-pointer"
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// case "checkbox":
|
||||||
|
// return (
|
||||||
|
// <div className="flex items-center space-x-2">
|
||||||
|
// <Checkbox
|
||||||
|
// id={fieldId}
|
||||||
|
// checked={!!value}
|
||||||
|
// onCheckedChange={(checked) => onChange(checked)}
|
||||||
|
// disabled={disabled}
|
||||||
|
// className={hasError ? "border-red-500" : ""}
|
||||||
|
// />
|
||||||
|
// <Label htmlFor={fieldId} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||||
|
// {label}
|
||||||
|
// </Label>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
|
||||||
case "number":
|
case "number":
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -104,7 +146,9 @@ export const FormField: React.FC<FormFieldProps> = ({
|
||||||
type="number"
|
type="number"
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={value || ""}
|
value={value || ""}
|
||||||
onChange={(e) => onChange(e.target.value ? Number(e.target.value) : "")}
|
onChange={(e) =>
|
||||||
|
onChange(e.target.value ? Number(e.target.value) : "")
|
||||||
|
}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={`${hasError ? "border-red-500" : ""} ${className}`}
|
className={`${hasError ? "border-red-500" : ""} ${className}`}
|
||||||
min={min}
|
min={min}
|
||||||
|
|
@ -136,16 +180,12 @@ export const FormField: React.FC<FormFieldProps> = ({
|
||||||
{required && <span className="text-red-500 ml-1">*</span>}
|
{required && <span className="text-red-500 ml-1">*</span>}
|
||||||
</Label>
|
</Label>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{renderField()}
|
{renderField()}
|
||||||
|
|
||||||
{helpText && (
|
{helpText && <p className="text-xs text-gray-500">{helpText}</p>}
|
||||||
<p className="text-xs text-gray-500">{helpText}</p>
|
|
||||||
)}
|
{hasError && <p className="text-red-500 text-xs">{error}</p>}
|
||||||
|
|
||||||
{hasError && (
|
|
||||||
<p className="text-red-500 text-xs">{error}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ export default function Navbar() {
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/auth">
|
<Link href="/auth">
|
||||||
<Button className="bg-red-700 text-white cursor-pointer hover:bg-white hover:text-red-700">{t("login")}</Button>
|
<Button className="bg-red-700 text-white cursor-pointer hover:bg-white hover:border hover:border-red-700 hover:text-red-700">{t("login")}</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ export default function DashboardContainer() {
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ delay: 0.7 }}
|
transition={{ delay: 0.7 }}
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-center mb-6">
|
{/* <div className="flex justify-between items-center mb-6">
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-lg font-semibold text-gray-900">
|
||||||
Recent Content
|
Recent Content
|
||||||
</h3>
|
</h3>
|
||||||
|
|
@ -277,7 +277,7 @@ export default function DashboardContainer() {
|
||||||
Create Content
|
Create Content
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<div className="space-y-4 max-h-96 overflow-y-auto scrollbar-thin">
|
<div className="space-y-4 max-h-96 overflow-y-auto scrollbar-thin">
|
||||||
{article?.map((list: any) => (
|
{article?.map((list: any) => (
|
||||||
|
|
|
||||||
|
|
@ -143,14 +143,14 @@ const columns: ColumnDef<any>[] = [
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-slate-200 focus:text-blue-500 rounded-none">
|
||||||
<Link
|
<Link
|
||||||
href={`/admin/management-user/detail/${row.original.id}`}
|
href={`/admin/management-user/detail/${row.original.id}`}
|
||||||
>
|
>
|
||||||
Detail
|
Detail
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-slate-200 focus:text-blue-500 rounded-none">
|
||||||
<Link
|
<Link
|
||||||
href={`/admin/management-user/edit/${row.original.id}`}
|
href={`/admin/management-user/edit/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -159,7 +159,7 @@ const columns: ColumnDef<any>[] = [
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
color="red"
|
color="red"
|
||||||
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none"
|
className="p-2 border-b text-red-500 group focus:bg-red-500 focus:text-white rounded-none cursor-pointer"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue