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,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { ConfirmDialog } from "~/components/ui/ConfirmDialog.js";
|
||||
import { formatCents } from "~/lib/format.js";
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
import { trpc } from "~/lib/trpc/client.js";
|
||||
@@ -101,6 +102,8 @@ export function RateCardsClient() {
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const [editingCard, setEditingCard] = useState<EditingCard | null>(null);
|
||||
const [editingLine, setEditingLine] = useState<EditingLine | null>(null);
|
||||
const [confirmDeleteLine, setConfirmDeleteLine] = useState<string | null>(null);
|
||||
const [confirmDeactivate, setConfirmDeactivate] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const utils = trpc.useUtils();
|
||||
@@ -260,7 +263,6 @@ export function RateCardsClient() {
|
||||
}
|
||||
|
||||
async function handleDeleteLine(lineId: string) {
|
||||
if (!confirm("Delete this rate line?")) return;
|
||||
try {
|
||||
await deleteLineMut.mutateAsync({ lineId });
|
||||
invalidateAll();
|
||||
@@ -270,7 +272,6 @@ export function RateCardsClient() {
|
||||
}
|
||||
|
||||
async function handleDeactivate(id: string) {
|
||||
if (!confirm("Deactivate this rate card?")) return;
|
||||
try {
|
||||
await deactivateMut.mutateAsync({ id });
|
||||
invalidateAll();
|
||||
@@ -445,7 +446,7 @@ export function RateCardsClient() {
|
||||
{detail.isActive ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDeactivate(detail.id)}
|
||||
onClick={() => setConfirmDeactivate(detail.id)}
|
||||
className="px-3 py-1.5 text-sm text-gray-500 hover:text-red-600 font-medium"
|
||||
>
|
||||
Deactivate
|
||||
@@ -528,7 +529,7 @@ export function RateCardsClient() {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDeleteLine(line.id)}
|
||||
onClick={() => setConfirmDeleteLine(line.id)}
|
||||
className="text-xs text-red-500 hover:text-red-700 font-medium"
|
||||
>
|
||||
Delete
|
||||
@@ -780,6 +781,34 @@ export function RateCardsClient() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{confirmDeleteLine && (
|
||||
<ConfirmDialog
|
||||
title="Delete rate line"
|
||||
message="Are you sure you want to delete this rate line?"
|
||||
confirmLabel="Delete"
|
||||
variant="danger"
|
||||
onConfirm={() => {
|
||||
void handleDeleteLine(confirmDeleteLine);
|
||||
setConfirmDeleteLine(null);
|
||||
}}
|
||||
onCancel={() => setConfirmDeleteLine(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{confirmDeactivate && (
|
||||
<ConfirmDialog
|
||||
title="Deactivate rate card"
|
||||
message="Are you sure you want to deactivate this rate card?"
|
||||
confirmLabel="Deactivate"
|
||||
variant="danger"
|
||||
onConfirm={() => {
|
||||
void handleDeactivate(confirmDeactivate);
|
||||
setConfirmDeactivate(null);
|
||||
}}
|
||||
onCancel={() => setConfirmDeactivate(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user