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,6 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { AnimatedModal } from "~/components/ui/AnimatedModal.js";
|
||||
import { ConfirmDialog } from "~/components/ui/ConfirmDialog.js";
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
import { trpc } from "~/lib/trpc/client.js";
|
||||
|
||||
@@ -71,6 +73,7 @@ const emptyRule: EditingRule = {
|
||||
|
||||
export function CalculationRulesClient() {
|
||||
const [editing, setEditing] = useState<EditingRule | null>(null);
|
||||
const [confirmDeleteRule, setConfirmDeleteRule] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const utils = trpc.useUtils();
|
||||
@@ -219,7 +222,7 @@ export function CalculationRulesClient() {
|
||||
<td className="px-4 py-3 text-right text-sm">
|
||||
<button onClick={() => openEdit(rule)} className="mr-2 text-blue-600 hover:underline dark:text-blue-400">Edit</button>
|
||||
<button
|
||||
onClick={() => { if (confirm("Delete this rule?")) deleteMut.mutate({ id: rule.id }); }}
|
||||
onClick={() => setConfirmDeleteRule(rule.id)}
|
||||
className="text-red-600 hover:underline dark:text-red-400"
|
||||
>
|
||||
Delete
|
||||
@@ -240,9 +243,9 @@ export function CalculationRulesClient() {
|
||||
</div>
|
||||
|
||||
{/* ── Edit/Create Modal ── */}
|
||||
{editing && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div className="w-full max-w-lg rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800">
|
||||
<AnimatedModal open={editing !== null} onClose={() => setEditing(null)} maxWidth="max-w-lg">
|
||||
{editing && (<>
|
||||
<div className="p-6">
|
||||
<h2 className="mb-4 text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||
{editing.id ? "Edit Rule" : "New Rule"}
|
||||
</h2>
|
||||
@@ -363,8 +366,22 @@ export function CalculationRulesClient() {
|
||||
{createMut.isPending || updateMut.isPending ? "Saving..." : "Save"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>)}
|
||||
</AnimatedModal>
|
||||
|
||||
{confirmDeleteRule && (
|
||||
<ConfirmDialog
|
||||
title="Delete rule"
|
||||
message="Are you sure you want to delete this calculation rule?"
|
||||
confirmLabel="Delete"
|
||||
variant="danger"
|
||||
onConfirm={() => {
|
||||
deleteMut.mutate({ id: confirmDeleteRule });
|
||||
setConfirmDeleteRule(null);
|
||||
}}
|
||||
onCancel={() => setConfirmDeleteRule(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user