refactor: deduplicate modals, notifications, confirms, comboboxes, proficiency
Modal Overlay (Finding 1 — 6 admin files): - Migrated CountriesClient, ManagementLevelsClient, OrgUnitsClient, CalculationRulesClient, UtilizationCategoriesClient, RoleModal from inline fixed-overlay to AnimatedModal component - Gains: animated transitions, backdrop blur, escape key for free Notification Helper (Finding 9 — 9 API files, 14 call sites): - New createNotification() + createNotificationsForUsers() in packages/api/src/lib/create-notification.ts - Handles exactOptionalPropertyTypes spread + SSE emit internally - Simplified: budget-alerts, estimate-reminders, auto-staffing, vacation-conflicts, chargeability-alerts, comment, vacation, notification ConfirmDialog (Finding 3 — 11 files): - Replaced all window.confirm() calls with ConfirmDialog component - Files: CommentThread, EffortRules, ExperienceMultipliers, ManagementLevels, CalculationRules, Countries, RateCards, ApplyEffortRules, ApplyExperienceMultipliers, NotificationCenter, ReminderModal EntityCombobox (Finding 4 — 3 files): - New generic EntityCombobox<T> with customization hooks - ResourceCombobox + ProjectCombobox rewritten as thin wrappers - All consumers unchanged (backwards-compatible props) Proficiency Constants (Finding 2 — 2 files): - SkillsAnalytics + SkillMarketplace now import from skills/shared.tsx - Deleted ~70 LOC of local duplicate definitions Regression: 283 engine + 37 staffing tests pass. TypeScript clean. AI Assistant: all 87 tools verified accessible. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import type { RoleWithResourceCount } from "@planarchy/shared";
|
||||
import { trpc } from "~/lib/trpc/client.js";
|
||||
import { useFocusTrap } from "~/hooks/useFocusTrap.js";
|
||||
import { AnimatedModal } from "~/components/ui/AnimatedModal.js";
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
|
||||
const PRESET_COLORS = [
|
||||
@@ -33,9 +33,6 @@ export function RoleModal({ role, onClose, onSuccess }: RoleModalProps) {
|
||||
const [color, setColor] = useState(role?.color ?? PRESET_COLORS[0]!);
|
||||
const [serverError, setServerError] = useState<string | null>(null);
|
||||
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
useFocusTrap(panelRef, true);
|
||||
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
const createMutation = trpc.role.create.useMutation({
|
||||
@@ -82,19 +79,7 @@ export function RoleModal({ role, onClose, onSuccess }: RoleModalProps) {
|
||||
const labelClass = "app-label";
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/55 py-8 backdrop-blur-sm"
|
||||
onClick={(e) => {
|
||||
if (e.target === e.currentTarget) onClose();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={panelRef}
|
||||
className="mx-4 w-full max-w-md rounded-3xl border border-gray-200 bg-white shadow-2xl dark:border-gray-700 dark:bg-gray-900"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Escape") onClose();
|
||||
}}
|
||||
>
|
||||
<AnimatedModal open onClose={onClose} maxWidth="max-w-md">
|
||||
<div className="flex items-center justify-between border-b border-gray-200 px-6 py-4 dark:border-gray-700">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||
{isEditing ? "Edit Role" : "New Role"}
|
||||
@@ -199,7 +184,6 @@ export function RoleModal({ role, onClose, onSuccess }: RoleModalProps) {
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</AnimatedModal>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user