"use client"; import { useState } from "react"; import type { RoleWithResourceCount } from "@capakraken/shared"; import { trpc } from "~/lib/trpc/client.js"; import { AnimatedModal } from "~/components/ui/AnimatedModal.js"; import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; const PRESET_COLORS = [ "#6366f1", "#8b5cf6", "#ec4899", "#ef4444", "#f97316", "#eab308", "#22c55e", "#14b8a6", "#06b6d4", "#3b82f6", ]; interface RoleModalProps { role?: RoleWithResourceCount | null; onClose: () => void; onSuccess: () => void; } export function RoleModal({ role, onClose, onSuccess }: RoleModalProps) { const isEditing = Boolean(role); const [name, setName] = useState(role?.name ?? ""); const [description, setDescription] = useState(role?.description ?? ""); const [color, setColor] = useState(role?.color ?? PRESET_COLORS[0]!); const [serverError, setServerError] = useState(null); const utils = trpc.useUtils(); const createMutation = trpc.role.create.useMutation({ onSuccess: async () => { await utils.role.list.invalidate(); onSuccess(); }, onError: (err) => setServerError(err.message), }); const updateMutation = trpc.role.update.useMutation({ onSuccess: async () => { await utils.role.list.invalidate(); onSuccess(); }, onError: (err) => setServerError(err.message), }); const isPending = createMutation.isPending || updateMutation.isPending; function handleSubmit(e: React.FormEvent) { e.preventDefault(); setServerError(null); if (!name.trim()) { setServerError("Name is required."); return; } const payload = { name: name.trim(), description: description.trim() || undefined, color: color || undefined, }; if (isEditing && role) { updateMutation.mutate({ id: role.id, data: payload }); } else { createMutation.mutate(payload); } } const inputClass = "app-input"; const labelClass = "app-label"; return (

{isEditing ? "Edit Role" : "New Role"}

{ setName(e.target.value); setServerError(null); }} placeholder="e.g. 3D Artist" className={inputClass} maxLength={100} required />
setDescription(e.target.value)} placeholder="Optional description" className={inputClass} maxLength={500} />

Pick a color that stays readable in timelines and chips.

{PRESET_COLORS.map((c) => (
setColor(e.target.value)} className="mt-3 h-8 w-10 cursor-pointer rounded border border-gray-300 bg-transparent dark:border-gray-600" title="Custom color" />
{serverError && (
{serverError}
)}
); }