refactor: implement permission pair structure to group view and edit controls within settings

This commit is contained in:
sazzadulalambd
2026-05-06 02:44:45 +06:00
parent a8634789f0
commit 3444a048fe

View File

@@ -1,12 +1,20 @@
'use client';
import { useState } from 'react';
import { Shield, Plus, Search, X, Edit, Trash2, Copy, Check, ChevronDown, ChevronRight, BookOpen, FileSearch, ClipboardList, Settings, BarChart3, Bike, Users, Briefcase, Truck, Store, BatteryCharging, Building2, Wrench, DollarSign, TrendingUp, UserCog } from 'lucide-react';
import { Shield, Plus, Search, X, Edit, Trash2, Copy, Check, ChevronDown, ChevronRight, BookOpen, FileSearch, Settings, BarChart3, Bike, Users, Briefcase, Truck, Store, BatteryCharging, Building2, Wrench, DollarSign, TrendingUp, UserCog } from 'lucide-react';
interface Permission {
key: string;
label: string;
enabled: boolean;
divider?: boolean;
pair?: boolean;
}
interface PermissionPair {
label: string;
view: Permission;
edit: Permission;
}
interface PermissionGroup {
@@ -15,6 +23,7 @@ interface PermissionGroup {
description: string;
icon: React.ComponentType<{ className?: string }>;
permissions: Permission[];
permissionPairs?: PermissionPair[];
}
interface Role {
@@ -38,36 +47,47 @@ const buildDefaultGroups = (): PermissionGroup[] => [
{ key: 'kyc.doc_upload', label: 'Document Upload', enabled: false },
{ key: 'kyc.doc_approve', label: 'Document Approve', enabled: false },
{ key: 'kyc.doc_reject', label: 'Document Reject', enabled: false },
{ key: 'kyc.make_valid_user', label: 'Make a Biker | Investor | Shop | Merchant', enabled: false },
]
},
{
id: 'plans',
title: 'Plan Selection + EV Condition',
description: 'Manage investment plans, swap station plans, and rider request plans',
icon: ClipboardList,
permissions: [
{ key: 'plans.investment.view', label: 'View Investment Plan', enabled: false },
{ key: 'plans.investment.edit', label: 'Edit Investment Plan', enabled: false },
{ key: 'plans.swap_station.view', label: 'View Swap Station Plan', enabled: false },
{ key: 'plans.swap_station.edit', label: 'Edit Swap Station Plan', enabled: false },
{ key: 'plans.rider_request.view', label: 'View Rider Request Plan', enabled: false },
{ key: 'plans.rider_request.edit', label: 'Edit Rider Request Plan', enabled: false },
{ key: 'kyc.make_valid_user', label: 'Make a Biker | Make an Investor | Make a Shop | Make a Merchant', enabled: false },
]
},
{
id: 'settings',
title: 'Settings',
description: 'Manage system settings including KYC documents and plans',
description: 'Manage KYC documents, plans, company policies, and system configuration',
icon: Settings,
permissions: [
{ key: 'settings.kyc_documents.view', label: 'View KYC Documents', enabled: false },
{ key: 'settings.kyc_documents.edit', label: 'Edit KYC Documents', enabled: false },
{ key: 'settings.plan_selection.view', label: 'View Plan Selection with Condition', enabled: false },
{ key: 'settings.plan_selection.edit', label: 'Edit Plan Selection with Condition', enabled: false },
{ key: 'settings.company_policy.view', label: 'View Company Policy', enabled: false },
{ key: 'settings.company_policy.edit', label: 'Edit Company Policy', enabled: false },
]
permissionPairs: [
{
label: 'KYC Documents',
view: { key: 'settings.kyc_documents.view', label: 'View', enabled: false },
edit: { key: 'settings.kyc_documents.edit', label: 'Edit', enabled: false },
},
{
label: 'Plan Selection with Condition',
view: { key: 'settings.plan_selection.view', label: 'View', enabled: false },
edit: { key: 'settings.plan_selection.edit', label: 'Edit', enabled: false },
},
{
label: 'Investment Plan',
view: { key: 'settings.investment_plan.view', label: 'View', enabled: false },
edit: { key: 'settings.investment_plan.edit', label: 'Edit', enabled: false },
},
{
label: 'Swap Station Plan',
view: { key: 'settings.swap_station_plan.view', label: 'View', enabled: false },
edit: { key: 'settings.swap_station_plan.edit', label: 'Edit', enabled: false },
},
{
label: 'Rider Request Plan',
view: { key: 'settings.rider_request_plan.view', label: 'View', enabled: false },
edit: { key: 'settings.rider_request_plan.edit', label: 'Edit', enabled: false },
},
{
label: 'Company Policy',
view: { key: 'settings.company_policy.view', label: 'View', enabled: false },
edit: { key: 'settings.company_policy.edit', label: 'Edit', enabled: false },
},
],
permissions: [],
},
{
id: 'dashboard',
@@ -397,17 +417,59 @@ export default function RolesPage() {
setRoles(roles.map(r => r.id === selectedRole.id ? { ...selectedRole, permissionGroups: updated } : r));
};
const toggleGroupAll = (groupIndex: number, enabled: boolean) => {
const togglePermissionPair = (groupIndex: number, pairIndex: number, type: 'view' | 'edit') => {
if (!selectedRole) return;
const updated = [...selectedRole.permissionGroups];
updated[groupIndex].permissions = updated[groupIndex].permissions.map(p => ({ ...p, enabled }));
const pair = updated[groupIndex].permissionPairs![pairIndex];
pair[type].enabled = !pair[type].enabled;
setSelectedRole({ ...selectedRole, permissionGroups: updated });
setRoles(roles.map(r => r.id === selectedRole.id ? { ...selectedRole, permissionGroups: updated } : r));
};
const isGroupAllEnabled = (group: PermissionGroup) => group.permissions.every(p => p.enabled);
const getEnabledCount = (group: PermissionGroup) => group.permissions.filter(p => p.enabled).length;
const getTotalEnabled = (role: Role) => role.permissionGroups.reduce((a, g) => a + g.permissions.filter(p => p.enabled).length, 0);
const toggleGroupAll = (groupIndex: number, enabled: boolean) => {
if (!selectedRole) return;
const updated = [...selectedRole.permissionGroups];
const group = updated[groupIndex];
if (group.permissionPairs) {
group.permissionPairs = group.permissionPairs.map(p => ({
...p,
view: { ...p.view, enabled },
edit: { ...p.edit, enabled },
}));
} else {
group.permissions = group.permissions.map(p => ({ ...p, enabled }));
}
setSelectedRole({ ...selectedRole, permissionGroups: updated });
setRoles(roles.map(r => r.id === selectedRole.id ? { ...selectedRole, permissionGroups: updated } : r));
};
const isGroupAllEnabled = (group: PermissionGroup) => {
if (group.permissionPairs) {
return group.permissionPairs.every(p => p.view.enabled && p.edit.enabled);
}
return group.permissions.every(p => p.enabled);
};
const getEnabledCount = (group: PermissionGroup) => {
if (group.permissionPairs) {
return group.permissionPairs.filter(p => p.view.enabled && p.edit.enabled).length;
}
return group.permissions.filter(p => p.enabled).length;
};
const getTotalCount = (group: PermissionGroup) => {
if (group.permissionPairs) {
return group.permissionPairs.length;
}
return group.permissions.length;
};
const getTotalEnabled = (role: Role) => role.permissionGroups.reduce((a, g) => {
if (g.permissionPairs) {
return a + g.permissionPairs.filter(p => p.view.enabled && p.edit.enabled).length;
}
return a + g.permissions.filter(p => p.enabled).length;
}, 0);
const toggleGroup = (groupId: string) => {
setExpandedGroups(prev => ({ ...prev, [groupId]: !prev[groupId] }));
@@ -522,7 +584,7 @@ export default function RolesPage() {
<group.icon className="w-5 h-5 text-slate-600" />
<div>
<p className="text-sm font-medium text-slate-700">{group.title}</p>
<p className="text-xs text-slate-400">{getEnabledCount(group)}/{group.permissions.length} enabled</p>
<p className="text-xs text-slate-400">{getEnabledCount(group)}/{getTotalCount(group)} enabled</p>
</div>
</div>
<div className="flex items-center gap-2">
@@ -547,25 +609,66 @@ export default function RolesPage() {
<div className="px-3 py-2 bg-slate-50">
<p className="text-xs text-slate-500">{group.description}</p>
</div>
<div className="divide-y divide-slate-100">
{group.permissions.map((perm, pi) => (
<div key={perm.key} className="flex items-center justify-between px-3 py-2.5">
<div className="flex items-center gap-2">
{perm.enabled ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-slate-300" />
)}
<span className="text-sm text-slate-700">{perm.label}</span>
<span className="text-xs text-slate-400 font-mono">{perm.key}</span>
{group.permissionPairs ? (
<div className="divide-y divide-slate-200">
{group.permissionPairs.map((pair, pairIdx) => (
<div key={pair.label}>
<div className="flex items-center justify-between px-3 py-2.5">
<span className="text-sm font-medium text-slate-700">{pair.label}</span>
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
{pair.view.enabled ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-slate-300" />
)}
<span className="text-xs text-slate-600">View</span>
<Toggle
checked={pair.view.enabled}
onChange={() => togglePermissionPair(gi, pairIdx, 'view')}
/>
</div>
<div className="flex items-center gap-2">
{pair.edit.enabled ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-slate-300" />
)}
<span className="text-xs text-slate-600">Edit</span>
<Toggle
checked={pair.edit.enabled}
onChange={() => togglePermissionPair(gi, pairIdx, 'edit')}
/>
</div>
</div>
</div>
{/* {pairIdx < (group.permissionPairs?.length || 0) - 1 && (
<hr className="my-2 border-slate-200" />
)} */}
</div>
<Toggle
checked={perm.enabled}
onChange={() => togglePermission(gi, pi)}
/>
</div>
))}
</div>
))}
</div>
) : (
<div className="divide-y divide-slate-100">
{group.permissions.map((perm, pi) => (
<div key={perm.key} className="flex items-center justify-between px-3 py-2.5">
<div className="flex items-center gap-2">
{perm.enabled ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<X className="w-4 h-4 text-slate-300" />
)}
<span className="text-sm text-slate-700">{perm.label}</span>
<span className="text-xs text-slate-400 font-mono">{perm.key}</span>
</div>
<Toggle
checked={perm.enabled}
onChange={() => togglePermission(gi, pi)}
/>
</div>
))}
</div>
)}
</div>
)}
</div>