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:
@@ -4,7 +4,8 @@ import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
import { findUniqueOrThrow } from "../db/helpers.js";
|
||||
import { RESOURCE_BRIEF_SELECT } from "../db/selects.js";
|
||||
import { emitVacationCreated, emitVacationUpdated, emitNotificationCreated, emitTaskAssigned } from "../sse/event-bus.js";
|
||||
import { emitVacationCreated, emitVacationUpdated, emitTaskAssigned } from "../sse/event-bus.js";
|
||||
import { createNotification } from "../lib/create-notification.js";
|
||||
import { createTRPCRouter, adminProcedure, managerProcedure, protectedProcedure } from "../trpc.js";
|
||||
import { sendEmail } from "../lib/email.js";
|
||||
import { anonymizeResource, anonymizeUser, getAnonymizationDirectory } from "../lib/anonymization.js";
|
||||
@@ -55,17 +56,15 @@ async function notifyVacationStatus(
|
||||
: `Your vacation request has been ${statusLabel}.`;
|
||||
|
||||
// In-app notification
|
||||
const notification = await db.notification.create({
|
||||
data: {
|
||||
userId: resource.user.id,
|
||||
type: `VACATION_${newStatus}`,
|
||||
title,
|
||||
body,
|
||||
entityId: vacationId,
|
||||
entityType: "vacation",
|
||||
},
|
||||
await createNotification({
|
||||
db,
|
||||
userId: resource.user.id,
|
||||
type: `VACATION_${newStatus}`,
|
||||
title,
|
||||
body,
|
||||
entityId: vacationId,
|
||||
entityType: "vacation",
|
||||
});
|
||||
emitNotificationCreated(resource.user.id, notification.id);
|
||||
|
||||
// Email (non-blocking)
|
||||
if (resource.user.email) {
|
||||
@@ -233,25 +232,23 @@ export const vacationRouter = createTRPCRouter({
|
||||
|
||||
for (const manager of managers) {
|
||||
if (manager.id === userRecord.id) continue;
|
||||
const task = await ctx.db.notification.create({
|
||||
data: {
|
||||
userId: manager.id,
|
||||
category: "APPROVAL",
|
||||
type: "VACATION_APPROVAL",
|
||||
priority: "NORMAL",
|
||||
title: `Vacation approval: ${resourceName}`,
|
||||
body: `${resourceName} requests ${input.type} from ${startStr} to ${endStr}`,
|
||||
taskStatus: "OPEN",
|
||||
taskAction: buildTaskAction("approve_vacation", vacation.id),
|
||||
entityId: vacation.id,
|
||||
entityType: "vacation",
|
||||
link: "/vacations",
|
||||
senderId: userRecord.id,
|
||||
channel: "in_app",
|
||||
},
|
||||
const taskId = await createNotification({
|
||||
db: ctx.db,
|
||||
userId: manager.id,
|
||||
category: "APPROVAL",
|
||||
type: "VACATION_APPROVAL",
|
||||
priority: "NORMAL",
|
||||
title: `Vacation approval: ${resourceName}`,
|
||||
body: `${resourceName} requests ${input.type} from ${startStr} to ${endStr}`,
|
||||
taskStatus: "OPEN",
|
||||
taskAction: buildTaskAction("approve_vacation", vacation.id),
|
||||
entityId: vacation.id,
|
||||
entityType: "vacation",
|
||||
link: "/vacations",
|
||||
senderId: userRecord.id,
|
||||
channel: "in_app",
|
||||
});
|
||||
emitNotificationCreated(manager.id, task.id);
|
||||
emitTaskAssigned(manager.id, task.id);
|
||||
emitTaskAssigned(manager.id, taskId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user