feat: Sprint 3 — automation, intelligence, skill marketplace
Auto-Staffing Suggestions (A6): - generateAutoSuggestions() ranks top-3 resources on demand creation - Uses existing staffing engine (skill 40%, availability 30%, cost 20%, util 10%) - Creates in-app notification with match scores for managers - Triggered after createDemandRequirement and partial fillDemandRequirement Vacation Conflict Detection (A7): - checkVacationConflicts() warns when >50% chapter absent on same days - Returns warnings array in approve/batchApprove responses (advisory, non-blocking) - Creates VACATION_CONFLICT_WARNING notification for approver Weekly Chargeability Alerts (A10): - checkChargeabilityAlerts() finds resources >15pp below target - Cron endpoint: GET /api/cron/chargeability-alerts - Duplicate-safe by resourceId + month composite key Rate Card Auto-Apply (A11): - lookupRate() finds best matching rate card line (weighted scoring) - Auto-fills demand line rates in estimate create/updateDraft when rates are 0 - Marks auto-filled lines with metadata.autoAppliedRateCard - New lookupDemandLineRate query for on-demand UI lookups Public Holiday Auto-Import (A12): - autoImportPublicHolidays() generates holidays by resource federal state - Cron endpoint: GET /api/cron/public-holidays?year=2027 - Duplicate-safe, uses existing getPublicHolidays() from shared Skill Marketplace MVP (G6): - New page: /analytics/skill-marketplace with 3 sections - Skill Search: filter by name, proficiency, availability, sortable results - Skill Gap Heat Map: supply vs demand per skill, shortage/surplus indicators - Skill Distribution: top-20 horizontal bar chart (reuses SkillDistributionChart) - New getSkillMarketplace query in resource router - Sidebar nav link under Analytics for ADMIN/MANAGER/CONTROLLER Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -8,6 +8,7 @@ import { emitVacationCreated, emitVacationUpdated, emitNotificationCreated, emit
|
||||
import { createTRPCRouter, adminProcedure, managerProcedure, protectedProcedure } from "../trpc.js";
|
||||
import { sendEmail } from "../lib/email.js";
|
||||
import { anonymizeResource, anonymizeUser, getAnonymizationDirectory } from "../lib/anonymization.js";
|
||||
import { checkVacationConflicts, checkBatchVacationConflicts } from "../lib/vacation-conflicts.js";
|
||||
|
||||
/** Types that consume from annual leave balance */
|
||||
const BALANCE_TYPES = [VacationType.ANNUAL, VacationType.OTHER];
|
||||
@@ -277,6 +278,10 @@ export const vacationRouter = createTRPCRouter({
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
// Check for team conflicts before approving (non-blocking)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const conflictResult = await checkVacationConflicts(ctx.db as any, input.id, userRecord?.id);
|
||||
|
||||
const updated = await ctx.db.vacation.update({
|
||||
where: { id: input.id },
|
||||
data: {
|
||||
@@ -307,7 +312,7 @@ export const vacationRouter = createTRPCRouter({
|
||||
void notifyVacationStatus(ctx.db, updated.id, updated.resourceId, VacationStatus.APPROVED);
|
||||
}
|
||||
|
||||
return updated;
|
||||
return { ...updated, warnings: conflictResult.warnings };
|
||||
}),
|
||||
|
||||
/**
|
||||
@@ -373,6 +378,14 @@ export const vacationRouter = createTRPCRouter({
|
||||
select: { id: true, resourceId: true },
|
||||
});
|
||||
|
||||
// Check for team conflicts before approving (non-blocking)
|
||||
const conflictMap = await checkBatchVacationConflicts(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
ctx.db as any,
|
||||
vacations.map((v) => v.id),
|
||||
userRecord?.id,
|
||||
);
|
||||
|
||||
await ctx.db.vacation.updateMany({
|
||||
where: { id: { in: vacations.map((v) => v.id) } },
|
||||
data: {
|
||||
@@ -402,7 +415,13 @@ export const vacationRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
|
||||
return { approved: vacations.length };
|
||||
// Flatten all warnings into a single array
|
||||
const warnings: string[] = [];
|
||||
for (const [, w] of conflictMap) {
|
||||
warnings.push(...w);
|
||||
}
|
||||
|
||||
return { approved: vacations.length, warnings };
|
||||
}),
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user