Diverse scenario engine: 15 weighted staffing situations replace crisis-every-refresh
Simulation now uses weighted random selection across 4 priority tiers: - Urgent (walkoff, quarantine, no-show), High (new client, cert expiry, expansion), Medium (recurring, seasonal, medical leave, cross-train), Low (future, exploratory) - Color-coded scenario banners on ALL contracts, not just urgent - Each scenario carries context (note) + recommended action Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+52
-17
@@ -1061,19 +1061,47 @@ const CITIES: Record<string, string[]> = {
|
||||
WI: ["Milwaukee","Madison"], MI: ["Detroit","Grand Rapids"],
|
||||
};
|
||||
const CLIENTS = ["Midwest Logistics","Precision Mfg","Amazon DSP","CleanSpace","AutoParts Direct","Great Lakes Steel","Heartland Foods","Summit Packaging","Cardinal Health","TechFlow Assembly","River City Plastics","Prairie Wind Energy"];
|
||||
const PRIORITIES = ["urgent","high","medium","medium","medium","low"];
|
||||
const STARTS = ["5:00 AM","6:00 AM","6:30 AM","7:00 AM","7:30 AM","8:00 AM"];
|
||||
const NOTES = [
|
||||
"Warehouse expansion — need experienced workers",
|
||||
"Peak season surge — client called last night",
|
||||
"2nd shift, CNC preferred",
|
||||
"Chemical plant — hazmat cert MANDATORY",
|
||||
"ISO audit next week — need detail-oriented workers",
|
||||
"Structural welding — experienced only",
|
||||
"Regular fill — ongoing contract",
|
||||
"Client doubled their order",
|
||||
"Night shift coverage needed",
|
||||
"Replacing 2 no-shows from yesterday",
|
||||
|
||||
// Diverse scenarios — each tells a different story about WHY this contract exists
|
||||
const SCENARIOS = [
|
||||
// URGENT — real emergencies that need immediate action
|
||||
{ priority: "urgent", weight: 8, note: "Worker walked off the job at 3 PM yesterday — client needs replacement by morning",
|
||||
situation: "walkoff", action: "Replacement needed ASAP — previous worker quit mid-shift" },
|
||||
{ priority: "urgent", weight: 5, note: "Client emailed at 11 PM — their regular crew has COVID exposure, entire team quarantined",
|
||||
situation: "quarantine", action: "Full crew replacement — health emergency at job site" },
|
||||
{ priority: "urgent", weight: 5, note: "2 no-shows this morning — client is short-staffed on the floor right now",
|
||||
situation: "noshow", action: "Immediate backfill — client waiting on the phone" },
|
||||
|
||||
// HIGH — important but not crisis
|
||||
{ priority: "high", weight: 10, note: "New contract starting Monday — client wants to meet workers this week",
|
||||
situation: "new_client", action: "New client onboarding — first impression matters" },
|
||||
{ priority: "high", weight: 8, note: "Client expanding to 2nd shift — need additional crew by next week",
|
||||
situation: "expansion", action: "Growth opportunity — client adding a shift" },
|
||||
{ priority: "high", weight: 6, note: "Worker's OSHA certification expires Friday — need certified replacement lined up",
|
||||
situation: "cert_expiry", action: "Cert compliance — current worker can't continue without renewal" },
|
||||
{ priority: "high", weight: 5, note: "Client requested specific workers back from last month's project",
|
||||
situation: "client_request", action: "Client relationship — they asked for specific people" },
|
||||
|
||||
// MEDIUM — standard day-to-day operations
|
||||
{ priority: "medium", weight: 15, note: "Ongoing weekly fill — same client, same role, reliable pipeline",
|
||||
situation: "recurring", action: "Recurring contract — steady work" },
|
||||
{ priority: "medium", weight: 12, note: "Seasonal uptick — warehouse volume increasing ahead of holidays",
|
||||
situation: "seasonal", action: "Seasonal planning — volume ramping up" },
|
||||
{ priority: "medium", weight: 10, note: "Backfill for worker on approved medical leave — returns in 3 weeks",
|
||||
situation: "medical_leave", action: "Temporary coverage — worker returning soon" },
|
||||
{ priority: "medium", weight: 8, note: "Client testing new role — wants to try 2 workers for a week before committing",
|
||||
situation: "trial", action: "Trial placement — client evaluating the role" },
|
||||
{ priority: "medium", weight: 6, note: "Cross-training opportunity — client wants workers who can learn a new skill",
|
||||
situation: "cross_train", action: "Development opportunity — workers can learn new skills" },
|
||||
|
||||
// LOW — planning ahead
|
||||
{ priority: "low", weight: 10, note: "Future fill — project starts in 2 weeks, gathering candidates now",
|
||||
situation: "future", action: "Pipeline building — no rush, quality over speed" },
|
||||
{ priority: "low", weight: 8, note: "Client exploring staffing options — not committed yet, just want to see who's available",
|
||||
situation: "exploratory", action: "Exploratory — client shopping, impress them with quality" },
|
||||
{ priority: "low", weight: 5, note: "Internal transfer — moving a worker from one site to another, need replacement at original",
|
||||
situation: "transfer", action: "Planned transition — smooth handoff between sites" },
|
||||
];
|
||||
|
||||
function pick<T>(arr: T[]): T { return arr[Math.floor(Math.random() * arr.length)]; }
|
||||
@@ -1095,10 +1123,16 @@ async function runWeekSimulation() {
|
||||
const state = pick(STATES);
|
||||
const city = pick(CITIES[state] || [state]);
|
||||
const role = pick(ROLES);
|
||||
const priority = pick(PRIORITIES) as string;
|
||||
const headcount = priority === "urgent" ? 4 + Math.floor(Math.random() * 5) :
|
||||
priority === "high" ? 3 + Math.floor(Math.random() * 3) :
|
||||
2 + Math.floor(Math.random() * 3);
|
||||
// Weighted scenario selection
|
||||
const totalWeight = SCENARIOS.reduce((s, sc) => s + sc.weight, 0);
|
||||
let r = Math.random() * totalWeight;
|
||||
let scenario = SCENARIOS[0];
|
||||
for (const sc of SCENARIOS) { r -= sc.weight; if (r <= 0) { scenario = sc; break; } }
|
||||
const priority = scenario.priority;
|
||||
const headcount = priority === "urgent" ? 3 + Math.floor(Math.random() * 4) :
|
||||
priority === "high" ? 2 + Math.floor(Math.random() * 3) :
|
||||
priority === "medium" ? 2 + Math.floor(Math.random() * 3) :
|
||||
1 + Math.floor(Math.random() * 2);
|
||||
const minRel = priority === "urgent" ? 0.6 : priority === "high" ? 0.75 : 0.8;
|
||||
const cid = `W${d+1}-${String(c+1).padStart(3,"0")}`;
|
||||
|
||||
@@ -1132,7 +1166,8 @@ async function runWeekSimulation() {
|
||||
contracts.push({
|
||||
id: cid, client: pick(CLIENTS), role, state, city,
|
||||
headcount, filled: Math.min(filled, headcount), priority,
|
||||
start: pick(STARTS), notes: pick(NOTES), matches,
|
||||
start: pick(STARTS), notes: scenario.note, situation: scenario.situation,
|
||||
action: scenario.action, matches,
|
||||
staffer, handoff_to: d < 4 ? handoffTo : null,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user