wulf-pulse/components/admin/roles/permission-picker.tsx
root 9f912aed24 feat: add authentication, user management, and admin features
Added comprehensive authentication and authorization system:

Authentication System:
- Better Auth integration with session management
- Login/logout pages and API routes
- Middleware for route protection
- Auth utilities and client libraries

User Management:
- User list, detail, and invite pages
- User API endpoints (CRUD operations)
- Session management for users
- Profile settings page

Role-Based Access Control:
- Role management pages (list, create, edit)
- Permission system with granular controls
- Role assignment to users
- Role API endpoints

Admin Features:
- Audit log page for tracking system events
- Admin settings page
- Audit service for logging user actions

Additional Features:
- Quotes management pages and components
- SalesBldr API integration
- Email service for notifications

Configuration & Documentation:
- Updated docker-compose.yml
- MCP server configuration (mcp.json)
- CVE-2025-55182 security review documentation
- Standards guide and PRD documents
- Re-enabling authentication documentation

Database Migrations:
- 012: Auth tables (users, sessions, accounts, verifications)
- 013: Role tables (roles, permissions, role_permissions, user_roles)
- 014: Admin settings table

UI Updates:
- Updated dashboard layout
- Enhanced app layout with auth integration
2026-01-31 12:43:14 -05:00

109 lines
3.4 KiB
TypeScript

"use client";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { statement } from "@/lib/permissions";
interface PermissionPickerProps {
value: Record<string, string[]>;
onChange: (permissions: Record<string, string[]>) => void;
disabled?: boolean;
}
const resourceLabels: Record<string, string> = {
tickets: "Tickets",
configItems: "Configuration Items",
admin: "Admin Panel",
users: "User Management",
roles: "Role Management",
auditLog: "Audit Log",
settings: "Settings",
};
const actionLabels: Record<string, string> = {
create: "Create",
read: "Read",
update: "Update",
delete: "Delete",
access: "Access",
invite: "Invite",
ban: "Ban",
};
export function PermissionPicker({ value, onChange, disabled }: PermissionPickerProps) {
const handleToggle = (resource: string, action: string, checked: boolean) => {
const currentActions = value[resource] || [];
let newActions: string[];
if (checked) {
newActions = [...currentActions, action];
} else {
newActions = currentActions.filter((a) => a !== action);
}
onChange({
...value,
[resource]: newActions,
});
};
const handleToggleAll = (resource: string, checked: boolean) => {
const allActions = statement[resource as keyof typeof statement] as readonly string[];
onChange({
...value,
[resource]: checked ? [...allActions] : [],
});
};
return (
<div className="space-y-6">
{Object.entries(statement).map(([resource, actions]) => {
const currentActions = value[resource] || [];
const allChecked = actions.every((a) => currentActions.includes(a));
const someChecked = actions.some((a) => currentActions.includes(a));
return (
<div key={resource} className="space-y-3">
<div className="flex items-center gap-2">
<Checkbox
id={`${resource}-all`}
checked={allChecked}
onCheckedChange={(checked) =>
handleToggleAll(resource, checked as boolean)
}
disabled={disabled}
className={someChecked && !allChecked ? "data-[state=checked]:bg-muted" : ""}
/>
<Label
htmlFor={`${resource}-all`}
className="font-semibold cursor-pointer"
>
{resourceLabels[resource] || resource}
</Label>
</div>
<div className="ml-6 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
{(actions as readonly string[]).map((action) => (
<div key={action} className="flex items-center gap-2">
<Checkbox
id={`${resource}-${action}`}
checked={currentActions.includes(action)}
onCheckedChange={(checked) =>
handleToggle(resource, action, checked as boolean)
}
disabled={disabled}
/>
<Label
htmlFor={`${resource}-${action}`}
className="text-sm cursor-pointer"
>
{actionLabels[action] || action}
</Label>
</div>
))}
</div>
</div>
);
})}
</div>
);
}