Implement admin users and roles management

- Add user CRUD: list, create (via Better Auth), update role/status, remove from port
- Add role CRUD: create, update permissions, delete with system role protection
- Full permissions matrix UI with accordion groups and per-action checkboxes
- Validators, services, API routes, and UI components following existing patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 15:47:11 -04:00
parent a13d7503cc
commit f60159e91a
14 changed files with 1460 additions and 78 deletions

View File

@@ -0,0 +1,35 @@
import { z } from 'zod';
const permissionGroupSchema = z.record(z.string(), z.boolean());
const rolePermissionsSchema = z.object({
clients: permissionGroupSchema,
interests: permissionGroupSchema,
berths: permissionGroupSchema,
documents: permissionGroupSchema,
expenses: permissionGroupSchema,
invoices: permissionGroupSchema,
files: permissionGroupSchema,
email: permissionGroupSchema,
reminders: permissionGroupSchema,
calendar: permissionGroupSchema,
reports: permissionGroupSchema,
document_templates: permissionGroupSchema,
admin: permissionGroupSchema,
});
export const createRoleSchema = z.object({
name: z.string().min(1).max(100),
description: z.string().max(500).optional(),
permissions: rolePermissionsSchema,
});
export type CreateRoleInput = z.infer<typeof createRoleSchema>;
export const updateRoleSchema = z.object({
name: z.string().min(1).max(100).optional(),
description: z.string().max(500).nullable().optional(),
permissions: rolePermissionsSchema.optional(),
});
export type UpdateRoleInput = z.infer<typeof updateRoleSchema>;

View File

@@ -0,0 +1,27 @@
import { z } from 'zod';
export const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(200),
password: z.string().min(12),
displayName: z.string().min(1).max(200),
phone: z.string().optional(),
roleId: z.string().uuid(),
});
export type CreateUserInput = z.infer<typeof createUserSchema>;
export const updateUserSchema = z.object({
displayName: z.string().min(1).max(200).optional(),
phone: z.string().nullable().optional(),
isActive: z.boolean().optional(),
roleId: z.string().uuid().optional(),
});
export type UpdateUserInput = z.infer<typeof updateUserSchema>;
export const resetPasswordSchema = z.object({
newPassword: z.string().min(12),
});
export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;