PermissionsManagement.tsx
Golden-Hour-backend-layer-2/pages/PermissionsManagement.tsx
import React, { useState } from 'react';
interface ModulePermission {
module: string;
read: boolean;
write: boolean;
delete: boolean;
export: boolean;
}
interface Role {
id: string;
name: string;
count: number;
permissions: ModulePermission[];
}
const PermissionsManagement: React.FC = () => {
const [selectedRoleId, setSelectedRoleId] = useState('1');
const [activeTab, setActiveTab] = useState<'Roles' | 'Users'>('Roles');
const [roles, setRoles] = useState<Role[]>([
{
id: '1', name: 'Super Admin', count: 2,
permissions: [
{ module: 'Dashboard', read: true, write: true, delete: true, export: true },
{ module: 'Content', read: true, write: true, delete: true, export: true },
{ module: 'Orders', read: true, write: true, delete: true, export: true },
{ module: 'Invoices', read: true, write: true, delete: true, export: true },
{ module: 'Staff', read: true, write: true, delete: true, export: true },
]
},
{
id: '2', name: 'Manager', count: 5,
permissions: [
{ module: 'Dashboard', read: true, write: true, delete: false, export: true },
{ module: 'Content', read: true, write: true, delete: false, export: true },
{ module: 'Orders', read: true, write: true, delete: false, export: true },
{ module: 'Invoices', read: true, write: false, delete: false, export: true },
{ module: 'Staff', read: true, write: true, delete: false, export: false },
]
},
{
id: '3', name: 'Clerk / Staff', count: 18,
permissions: [
{ module: 'Dashboard', read: true, write: false, delete: false, export: false },
{ module: 'Content', read: true, write: true, delete: false, export: false },
{ module: 'Orders', read: true, write: false, delete: false, export: false },
{ module: 'Invoices', read: false, write: false, delete: false, export: false },
{ module: 'Staff', read: false, write: false, delete: false, export: false },
]
},
]);
const selectedRole = roles.find(r => r.id === selectedRoleId);
const togglePermission = (moduleIndex: number, action: keyof Omit<ModulePermission, 'module'>) => {
const updatedRoles = [...roles];
const role = updatedRoles.find(r => r.id === selectedRoleId);
if (role) {
role.permissions[moduleIndex][action] = !role.permissions[moduleIndex][action];
setRoles(updatedRoles);
}
};
return (
<div className="fade-in-section space-y-16 pb-24">
{/* Editorial Header */}
<div className="flex flex-col md:flex-row md:items-end justify-between gap-10 border-b border-[#c5a059]/10 pb-12 px-2">
<div className="flex items-baseline space-x-6">
<span className="font-serif italic text-6xl text-slate-200">Protocol</span>
<div>
<h2 className="font-serif italic text-6xl text-slate-900 tracking-tight">Security</h2>
<p className="text-[10px] font-black uppercase tracking-[0.6em] text-[#c5a059] mt-2">Authority & Access Control</p>
</div>
</div>
<div className="flex bg-[#fdfcf9] p-1 border border-[#c5a059]/10 rounded-full">
<button
onClick={() => setActiveTab('Roles')}
className={`px-10 py-3 rounded-full text-[9px] font-black uppercase tracking-[0.4em] transition-all ${activeTab === 'Roles' ? 'bg-black text-white shadow-xl' : 'text-slate-300 hover:text-slate-600'}`}
>
Ledger
</button>
<button
onClick={() => setActiveTab('Users')}
className={`px-10 py-3 rounded-full text-[9px] font-black uppercase tracking-[0.4em] transition-all ${activeTab === 'Users' ? 'bg-black text-white shadow-xl' : 'text-slate-300 hover:text-slate-600'}`}
>
Overrides
</button>
</div>
</div>
{activeTab === 'Roles' ? (
<div className="flex flex-col xl:flex-row gap-12">
{/* Roles Selection - Gallery Style */}
<div className="xl:w-1/3 space-y-8">
<h3 className="font-serif italic text-3xl text-slate-900 px-2">Access Tiers</h3>
<div className="grid grid-cols-1 gap-4">
{roles.map((role, idx) => (
<button
key={role.id}
onClick={() => setSelectedRoleId(role.id)}
className={`relative group flex items-center p-10 rounded-[2.5rem] transition-all duration-700 text-left border overflow-hidden ${
selectedRoleId === role.id
? 'bg-white border-[#c5a059]/20 gallery-shadow translate-x-4'
: 'bg-[#fdfcf9]/50 border-transparent hover:bg-white hover:border-[#c5a059]/10'
}`}
>
<div className="mr-8">
<span className="font-serif italic text-4xl text-slate-200 group-hover:text-[#c5a059] transition-colors">
{String(idx + 1).padStart(2, '0')}
</span>
</div>
<div>
<h4 className={`text-xl font-serif italic ${selectedRoleId === role.id ? 'text-slate-900' : 'text-slate-400'}`}>
{role.name}
</h4>
<p className="text-[9px] font-black uppercase tracking-[0.3em] text-[#c5a059]/50 mt-2">
{role.count} Active Profiles
</p>
</div>
{selectedRoleId === role.id && (
<div className="absolute right-10 top-1/2 -translate-y-1/2">
<i className="fa-solid fa-arrow-right-long text-[#c5a059] opacity-40"></i>
</div>
)}
</button>
))}
</div>
<button className="w-full py-8 border-2 border-dashed border-[#c5a059]/10 rounded-[2.5rem] text-[10px] font-black uppercase tracking-[0.4em] text-slate-300 hover:text-[#c5a059] hover:border-[#c5a059]/30 transition-all flex flex-col items-center group">
<i className="fa-solid fa-plus mb-3 opacity-20 group-hover:opacity-100 transition-opacity"></i>
Register New Tier
</button>
</div>
{/* Permissions Matrix - Detailed Plate Style */}
<div className="xl:w-2/3 bg-white gallery-shadow rounded-[4rem] border border-[#c5a059]/5 overflow-hidden flex flex-col">
<div className="p-16 border-b border-[#c5a059]/5 bg-[#fdfcf9]/30 flex flex-col md:flex-row md:items-center justify-between gap-8">
<div>
<span className="text-[9px] font-black uppercase tracking-[0.6em] text-[#c5a059] block mb-4">Authority Configuration</span>
<h3 className="font-serif italic text-5xl text-slate-900">{selectedRole?.name}</h3>
</div>
<button className="bg-black text-white px-12 py-5 rounded-full text-[10px] font-black uppercase tracking-[0.4em] hover:bg-[#c5a059] transition-all duration-700 shadow-2xl">
Commit Changes
</button>
</div>
<div className="divide-y divide-[#c5a059]/5">
{selectedRole?.permissions.map((perm, idx) => (
<div key={idx} className="p-16 hover:bg-[#fdfcf9] transition-all group/perm">
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-12">
<div className="flex items-start lg:w-1/3">
<div className="w-16 h-16 rounded-2xl border border-[#c5a059]/10 flex items-center justify-center text-[#c5a059] bg-white gallery-shadow mr-8 transform group-hover/perm:rotate-3 transition-transform duration-500">
<i className={`fa-solid ${
perm.module === 'Dashboard' ? 'fa-chart-pie' :
perm.module === 'Content' ? 'fa-pen-to-square' :
perm.module === 'Orders' ? 'fa-cart-shopping' :
perm.module === 'Invoices' ? 'fa-file-invoice' : 'fa-users-gear'
} text-xl`}></i>
</div>
<div>
<h5 className="font-serif italic text-3xl text-slate-900">{perm.module}</h5>
<p className="text-[8px] font-black uppercase tracking-[0.3em] text-slate-300 mt-2">Section Module Control</p>
</div>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-8 flex-1">
{(['read', 'write', 'delete', 'export'] as const).map(action => (
<div key={action} className="flex flex-col items-center">
<span className="text-[8px] font-black uppercase tracking-[0.5em] text-slate-300 mb-6">{action}</span>
<button
onClick={() => togglePermission(idx, action)}
className={`w-12 h-6 rounded-full relative transition-all duration-500 border ${
perm[action]
? 'bg-black border-black'
: 'bg-transparent border-[#c5a059]/20'
}`}
>
<div className={`absolute top-1/2 -translate-y-1/2 w-3.5 h-3.5 rounded-full transition-all duration-500 ${
perm[action]
? 'right-1.5 bg-[#c5a059]'
: 'left-1.5 bg-slate-200'
}`}></div>
</button>
</div>
))}
</div>
</div>
</div>
))}
</div>
<div className="p-16 bg-[#fdfcf9]/50 border-t border-[#c5a059]/10">
<div className="max-w-xl flex items-start space-x-8">
<div className="text-3xl text-[#c5a059] opacity-30 italic font-serif leading-none">!</div>
<div>
<h6 className="text-[9px] font-black uppercase tracking-[0.4em] text-slate-900 mb-3">Security Mandate</h6>
<p className="text-[12px] font-serif italic text-slate-500 leading-relaxed">
Note that administrative adjustments to the <span className="text-[#c5a059]">{selectedRole?.name}</span> ledger will propagate across all {selectedRole?.count} verified accounts within this tier.
</p>
</div>
</div>
</div>
</div>
</div>
) : (
<div className="flex flex-col items-center justify-center min-h-[500px] text-center px-4">
<div className="w-48 h-48 rounded-full border border-[#c5a059]/10 bg-white gallery-shadow flex items-center justify-center mb-12 relative">
<div className="absolute inset-0 border border-[#c5a059]/5 rounded-full animate-ping opacity-20"></div>
<i className="fa-solid fa-user-lock text-4xl text-[#c5a059] opacity-20"></i>
</div>
<h3 className="font-serif italic text-5xl text-slate-900 mb-6">User Overrides</h3>
<p className="text-xl font-serif italic text-slate-400 max-w-2xl leading-relaxed mb-16">
Refine individual access protocols without compromising general tier constraints.
A precise method for temporary executive delegation.
</p>
<div className="w-full max-w-2xl relative group">
<i className="fa-solid fa-magnifying-glass absolute left-10 top-1/2 -translate-y-1/2 text-slate-200 group-focus-within:text-[#c5a059] transition-all"></i>
<input
type="text"
placeholder="Query Identity archives..."
className="w-full pl-24 pr-12 py-8 bg-white border border-[#c5a059]/10 rounded-[2rem] text-[11px] font-bold uppercase tracking-[0.3em] outline-none gallery-shadow focus:border-[#c5a059] transition-all placeholder:text-slate-100"
/>
</div>
<div className="mt-20 flex space-x-12 grayscale opacity-20">
{[1,2,3,4,5].map(i => (
<div key={i} className="w-16 h-16 rounded-full bg-slate-100 border border-slate-200"></div>
))}
</div>
</div>
)}
</div>
);
};
export default PermissionsManagement;
관련 글
App.tsx
App.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/App.tsx).
글 읽기 →Header.tsx
Header.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/components/Header.tsx).
글 읽기 →Sidebar.tsx
Sidebar.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/components/Sidebar.tsx).
글 읽기 →index.tsx
index.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/index.tsx).
글 읽기 →ContentManagement.tsx
ContentManagement.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/pages/ContentManagement.tsx).
글 읽기 →CustomerManagement.tsx
CustomerManagement.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/pages/CustomerManagement.tsx).
글 읽기 →