116 lines
5.1 KiB
TypeScript
116 lines
5.1 KiB
TypeScript
'use client'
|
|
|
|
type Access = 'full' | 'partial' | 'none'
|
|
|
|
interface Feature {
|
|
label: string
|
|
manager: Access
|
|
managerNote?: string
|
|
agent: Access
|
|
agentNote?: string
|
|
}
|
|
|
|
const FEATURES: Feature[] = [
|
|
{ label: 'View fleet & vehicles', manager: 'full', agent: 'full' },
|
|
{ label: 'Add / edit vehicles', manager: 'full', agent: 'none' },
|
|
{ label: 'Publish / unpublish vehicle', manager: 'full', agent: 'none' },
|
|
{ label: 'Upload vehicle photos', manager: 'full', agent: 'none' },
|
|
{ label: 'View reservations', manager: 'full', agent: 'full' },
|
|
{ label: 'Create reservations', manager: 'full', agent: 'full' },
|
|
{ label: 'Cancel reservations', manager: 'full', agent: 'partial', agentNote: 'Own only' },
|
|
{ label: 'Check-in / check-out', manager: 'full', agent: 'full' },
|
|
{ label: 'Damage inspection', manager: 'full', agent: 'full' },
|
|
{ label: 'Customer CRM — view', manager: 'full', agent: 'full' },
|
|
{ label: 'Customer CRM — edit', manager: 'full', agent: 'partial', agentNote: 'Notes only' },
|
|
{ label: 'Approve driver licenses', manager: 'full', agent: 'none' },
|
|
{ label: 'Flag / blacklist customer', manager: 'full', agent: 'none' },
|
|
{ label: 'Offers & promotions', manager: 'full', agent: 'none' },
|
|
{ label: 'Analytics & reports', manager: 'full', agent: 'none' },
|
|
{ label: 'Contract & invoice PDF', manager: 'full', agent: 'full' },
|
|
{ label: 'Billing & subscription', manager: 'none', agent: 'none' },
|
|
{ label: 'Invite / remove staff', manager: 'none', agent: 'none' },
|
|
{ label: 'Brand & site settings', manager: 'none', agent: 'none' },
|
|
{ label: 'Payment settings', manager: 'none', agent: 'none' },
|
|
]
|
|
|
|
function AccessCell({ access, note }: { access: Access; note?: string }) {
|
|
if (access === 'full') {
|
|
return (
|
|
<div className="flex items-center gap-1.5">
|
|
<svg className="w-3.5 h-3.5 text-green-600 dark:text-green-400 flex-shrink-0" fill="none" viewBox="0 0 14 14">
|
|
<path d="M2.5 7L5.5 10L11.5 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
<span className="text-xs text-zinc-700 dark:text-zinc-300">Full</span>
|
|
</div>
|
|
)
|
|
}
|
|
if (access === 'partial') {
|
|
return (
|
|
<div className="flex items-center gap-1.5">
|
|
<div className="w-3.5 h-3.5 flex-shrink-0 flex items-center justify-center">
|
|
<div className="w-2.5 h-0.5 rounded bg-amber-500" />
|
|
</div>
|
|
<span className="text-xs text-amber-700 dark:text-amber-400">{note ?? 'Limited'}</span>
|
|
</div>
|
|
)
|
|
}
|
|
return (
|
|
<div className="flex items-center gap-1.5">
|
|
<div className="w-3.5 h-3.5 flex-shrink-0 flex items-center justify-center">
|
|
<div className="w-2 h-0.5 rounded bg-zinc-300 dark:bg-zinc-600" />
|
|
</div>
|
|
<span className="text-xs text-zinc-400 dark:text-zinc-600">No access</span>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function PermissionsMatrix() {
|
|
return (
|
|
<div>
|
|
<div className="mb-4">
|
|
<h2 className="text-base font-medium text-zinc-900 dark:text-white">Role permissions</h2>
|
|
<p className="text-sm text-zinc-500 dark:text-zinc-400 mt-0.5">
|
|
What each role can do across the dashboard. Owners have full access to everything.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="border border-zinc-200 dark:border-zinc-700 rounded-xl overflow-hidden">
|
|
{/* Header */}
|
|
<div className="grid grid-cols-[1fr_120px_120px] bg-zinc-50 dark:bg-zinc-800/50 border-b border-zinc-200 dark:border-zinc-700">
|
|
<div className="px-4 py-2.5 text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wide">
|
|
Feature
|
|
</div>
|
|
<div className="px-4 py-2.5 text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wide border-l border-zinc-200 dark:border-zinc-700">
|
|
Manager
|
|
</div>
|
|
<div className="px-4 py-2.5 text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wide border-l border-zinc-200 dark:border-zinc-700">
|
|
Agent
|
|
</div>
|
|
</div>
|
|
|
|
{/* Rows */}
|
|
{FEATURES.map((feature, i) => (
|
|
<div
|
|
key={feature.label}
|
|
className={[
|
|
'grid grid-cols-[1fr_120px_120px]',
|
|
i < FEATURES.length - 1 ? 'border-b border-zinc-100 dark:border-zinc-800' : '',
|
|
i % 2 === 0 ? '' : 'bg-zinc-50/50 dark:bg-zinc-800/20',
|
|
].join(' ')}
|
|
>
|
|
<div className="px-4 py-2.5 text-xs text-zinc-600 dark:text-zinc-400">
|
|
{feature.label}
|
|
</div>
|
|
<div className="px-4 py-2.5 border-l border-zinc-100 dark:border-zinc-800">
|
|
<AccessCell access={feature.manager} note={feature.managerNote} />
|
|
</div>
|
|
<div className="px-4 py-2.5 border-l border-zinc-100 dark:border-zinc-800">
|
|
<AccessCell access={feature.agent} note={feature.agentNote} />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|