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:
2026-03-22 21:50:39 +01:00
parent c7b76e086d
commit ac845d72b7
29 changed files with 737 additions and 607 deletions
+14 -19
View File
@@ -8,7 +8,7 @@ import {
import type { SpainScheduleRule } from "@planarchy/shared";
import { isChargeabilityActualBooking, listAssignmentBookings } from "@planarchy/application";
import { VacationStatus } from "@planarchy/db";
import { emitNotificationCreated } from "../sse/event-bus.js";
import { createNotificationsForUsers } from "./create-notification.js";
/**
* Minimal DB client type for chargeability alerts.
@@ -237,24 +237,19 @@ export async function checkChargeabilityAlerts(
if (existing) continue;
for (const manager of managers) {
const notification = await (db as DbClient).notification.create({
data: {
userId: manager.id,
type: "CHARGEABILITY_ALERT",
category: "NOTIFICATION",
priority: "HIGH",
title: `Low chargeability: ${resource.displayName}`,
body: `${resource.displayName} is at ${chg}% chargeability this month (target: ${target}%, gap: ${gap}pp).`,
entityId,
entityType: "chargeability_alert",
link: "/chargeability",
channel: "in_app",
},
});
emitNotificationCreated(manager.id, notification.id);
}
await createNotificationsForUsers({
db: db as DbClient,
userIds: managers.map((m) => m.id),
type: "CHARGEABILITY_ALERT",
category: "NOTIFICATION",
priority: "HIGH",
title: `Low chargeability: ${resource.displayName}`,
body: `${resource.displayName} is at ${chg}% chargeability this month (target: ${target}%, gap: ${gap}pp).`,
entityId,
entityType: "chargeability_alert",
link: "/chargeability",
channel: "in_app",
});
alertCount++;
}