update position page design
This commit is contained in:
488
src/api/seed.js
488
src/api/seed.js
@@ -33,10 +33,54 @@ const CERTIFICATIONS = [
|
||||
|
||||
// created_date descending drives list order on Positions and Overview.
|
||||
const JOB_POSTINGS = [
|
||||
{
|
||||
id: 'job_senior_server',
|
||||
title: 'Senior Server – Fine Dining',
|
||||
role_category: 'Server',
|
||||
description:
|
||||
'An advanced service role for seated fine dining events in San Jose. You carry the standard for your section, recover a table before a client notices there was anything to recover, and set the pace for the servers around you.',
|
||||
responsibilities: [
|
||||
'Run a section at fine dining standard without supervision',
|
||||
'Recover service issues before they reach the client',
|
||||
'Set timing and pace for the servers around you',
|
||||
'Handle allergens and special requests accurately',
|
||||
'Support the captain through arrivals, toasts and close',
|
||||
],
|
||||
qualifications: [
|
||||
'4+ years of fine dining or banquet service',
|
||||
'Verified advanced service capability',
|
||||
'Comfortable as the senior server on a floor',
|
||||
'Available evenings and weekends',
|
||||
],
|
||||
nice_to_haves: ['Wine service knowledge', 'Captain experience', 'Bilingual'],
|
||||
min_experience_years: 4,
|
||||
english_required: 'fluent',
|
||||
certifications_required: ['Food Handler Card'],
|
||||
/* What the work actually requires, in verified skill levels. This is what
|
||||
the matching engine reads — see lib/skillGraph.js. Weights say which
|
||||
requirement carries the role; they are normalised, so they need not total
|
||||
100 to produce a meaningful percentage. */
|
||||
skill_requirements: [
|
||||
{ skill_id: 'server', level: 'advanced', weight: 40 },
|
||||
{ skill_id: 'customer_service', level: 'intermediate', weight: 25 },
|
||||
{ skill_id: 'food_safety', level: 'beginner', weight: 20 },
|
||||
{ skill_id: 'leadership', level: 'beginner', weight: 15 },
|
||||
],
|
||||
pay_range_min: 24,
|
||||
pay_range_max: 34,
|
||||
location: 'San Jose, CA',
|
||||
status: 'active',
|
||||
ai_generated: false,
|
||||
created_date: iso('2026-08-08'),
|
||||
},
|
||||
{
|
||||
id: 'job_bartender',
|
||||
title: 'Bartender',
|
||||
role_category: 'Bartender',
|
||||
skill_requirements: [
|
||||
{ skill_id: 'bartending', level: 'intermediate', weight: 60 },
|
||||
{ skill_id: 'customer_service', level: 'beginner', weight: 40 },
|
||||
],
|
||||
description:
|
||||
'We are building a bench of dependable bartenders for high-volume private events across the region. You will own your bar from setup to breakdown, pour with speed and consistency, and keep guests feeling looked after even when the room is three deep.',
|
||||
responsibilities: [
|
||||
@@ -65,6 +109,11 @@ const JOB_POSTINGS = [
|
||||
},
|
||||
{
|
||||
id: 'job_server_fine',
|
||||
skill_requirements: [
|
||||
{ skill_id: 'server', level: 'advanced', weight: 45 },
|
||||
{ skill_id: 'customer_service', level: 'intermediate', weight: 30 },
|
||||
{ skill_id: 'food_safety', level: 'beginner', weight: 25 },
|
||||
],
|
||||
title: 'Event Server – Fine Dining',
|
||||
role_category: 'Server',
|
||||
description:
|
||||
@@ -125,6 +174,11 @@ const JOB_POSTINGS = [
|
||||
},
|
||||
{
|
||||
id: 'job_chef',
|
||||
skill_requirements: [
|
||||
{ skill_id: 'food_safety', level: 'advanced', weight: 40 },
|
||||
{ skill_id: 'leadership', level: 'advanced', weight: 35 },
|
||||
{ skill_id: 'customer_service', level: 'beginner', weight: 25 },
|
||||
],
|
||||
title: 'Executive Chef – Catering',
|
||||
role_category: 'Chef',
|
||||
description:
|
||||
@@ -155,6 +209,11 @@ const JOB_POSTINGS = [
|
||||
},
|
||||
{
|
||||
id: 'job_bartender_corp',
|
||||
skill_requirements: [
|
||||
{ skill_id: 'bartending', level: 'advanced', weight: 50 },
|
||||
{ skill_id: 'customer_service', level: 'intermediate', weight: 30 },
|
||||
{ skill_id: 'leadership', level: 'beginner', weight: 20 },
|
||||
],
|
||||
title: 'Experienced Bartender – Corporate Events',
|
||||
role_category: 'Bartender',
|
||||
description:
|
||||
@@ -215,6 +274,11 @@ const JOB_POSTINGS = [
|
||||
},
|
||||
{
|
||||
id: 'job_banquet',
|
||||
skill_requirements: [
|
||||
{ skill_id: 'server', level: 'advanced', weight: 35 },
|
||||
{ skill_id: 'leadership', level: 'intermediate', weight: 35 },
|
||||
{ skill_id: 'customer_service', level: 'advanced', weight: 30 },
|
||||
],
|
||||
title: 'Banquet Captain – Hotel Events',
|
||||
role_category: 'Server',
|
||||
description:
|
||||
@@ -243,6 +307,12 @@ const JOB_POSTINGS = [
|
||||
ai_generated: true,
|
||||
created_date: iso('2026-07-18'),
|
||||
},
|
||||
|
||||
/* ── The multi-company demand the allocation engine exists for ──────────
|
||||
Three companies wanting the same skill at the same time, with different
|
||||
urgency, start dates and run lengths. Total demand is 90 chefs against a
|
||||
workforce that cannot cover it, which is the situation priority ordering
|
||||
has to resolve — and the reason it is seeded rather than described. */
|
||||
];
|
||||
|
||||
/* ── Applications ──────────────────────────────────────────────────────── */
|
||||
@@ -270,6 +340,53 @@ const unscreened = (over) => ({
|
||||
});
|
||||
|
||||
const JOB_APPLICATIONS = [
|
||||
/* Senior Server – Fine Dining. Two applications with no AI screening on them
|
||||
at all: what the recruiter sees for these is the skill match, computed from
|
||||
verified training rather than from a model's reading of a résumé. */
|
||||
{
|
||||
id: 'app_arun',
|
||||
applicant_name: 'Arun Kumar',
|
||||
email: 'arun.kumar@example.com',
|
||||
phone: '+1-408-555-0210',
|
||||
years_experience: 4,
|
||||
english_level: 'fluent',
|
||||
certifications: ['Food Handler Card'],
|
||||
availability: ['Weekdays', 'Weekends', 'Evenings'],
|
||||
skills: ['Coursed service', 'Guest recovery', 'Allergen handling'],
|
||||
companies_worked: ['Valley Catering Co.'],
|
||||
client_rating: 4.7,
|
||||
professional_summary:
|
||||
'Four years of event service in San Jose, working towards the senior server role on a fine dining floor.',
|
||||
selfie_url: 'https://i.pravatar.cc/240?img=68',
|
||||
job_posting_id: 'job_senior_server',
|
||||
job_title: 'Senior Server – Fine Dining',
|
||||
status: 'applied',
|
||||
ai_score: 0,
|
||||
created_date: iso('2026-08-13'),
|
||||
updated_date: iso('2026-08-13'),
|
||||
},
|
||||
{
|
||||
id: 'app_priya_nair',
|
||||
applicant_name: 'Priya Nair',
|
||||
email: 'priya.nair@example.com',
|
||||
phone: '+1-408-555-0211',
|
||||
years_experience: 2,
|
||||
english_level: 'fluent',
|
||||
certifications: ['Food Handler Card'],
|
||||
availability: ['Weekends', 'Evenings'],
|
||||
skills: ['Table service', 'Guest relations'],
|
||||
companies_worked: ['Bay Event Staffing'],
|
||||
client_rating: 4.5,
|
||||
professional_summary: 'Two years of event service, moving towards fine dining sections.',
|
||||
selfie_url: 'https://i.pravatar.cc/240?img=47',
|
||||
job_posting_id: 'job_senior_server',
|
||||
job_title: 'Senior Server – Fine Dining',
|
||||
status: 'applied',
|
||||
ai_score: 0,
|
||||
created_date: iso('2026-08-12'),
|
||||
updated_date: iso('2026-08-12'),
|
||||
},
|
||||
|
||||
/* Executive Chef – Catering: 2 applicants · 1 screened · 1 hired */
|
||||
{
|
||||
id: 'app_antoine',
|
||||
@@ -736,6 +853,7 @@ const JOB_APPLICATIONS = [
|
||||
phone: '+1-415-555-0122', years_experience: 2, english_level: 'fluent',
|
||||
job_posting_id: 'job_banquet', job_title: 'Banquet Captain – Hotel Events',
|
||||
}),
|
||||
|
||||
];
|
||||
|
||||
/* ── AI interviews ─────────────────────────────────────────────────────── */
|
||||
@@ -906,6 +1024,14 @@ const COURSES = [
|
||||
badge_reward: 'Service Fundamentals',
|
||||
estimated_minutes: 18,
|
||||
proof_skill: 'Coursed Table Service',
|
||||
/* The four skill-graph fields on a record that predates them. Nothing else
|
||||
about these originals changes — the ladder simply now knows where they
|
||||
sit, which is what lets completing one move a worker's level. */
|
||||
skill_id: 'server',
|
||||
target_level: 'advanced',
|
||||
required_level: 'intermediate',
|
||||
completion_criteria: ['Work through the training material', 'Submit evidence for the challenge'],
|
||||
verification_criteria: ['Acknowledges without excuses', 'Offers a concrete remedy', 'Protects the table experience'],
|
||||
challenge: {
|
||||
type: 'roleplay',
|
||||
prompt:
|
||||
@@ -944,6 +1070,11 @@ const COURSES = [
|
||||
badge_reward: 'Responsible Service',
|
||||
estimated_minutes: 22,
|
||||
proof_skill: 'Responsible Service Judgment',
|
||||
skill_id: 'bartending',
|
||||
target_level: 'intermediate',
|
||||
required_level: 'beginner',
|
||||
completion_criteria: ['Work through the training material', 'Submit evidence for the challenge'],
|
||||
verification_criteria: ['Refuses clearly and kindly', 'Offers an alternative', 'Escalates appropriately'],
|
||||
challenge: {
|
||||
type: 'roleplay',
|
||||
prompt: 'A guest who has clearly had enough asks for one more round, and their colleagues are watching. Talk it through.',
|
||||
@@ -976,6 +1107,11 @@ const COURSES = [
|
||||
badge_reward: 'Safety Aware',
|
||||
estimated_minutes: 14,
|
||||
proof_skill: 'Hazard Identification',
|
||||
skill_id: 'food_safety',
|
||||
target_level: 'beginner',
|
||||
required_level: null,
|
||||
completion_criteria: ['Work through the training material', 'Submit evidence for the challenge'],
|
||||
verification_criteria: ['Identifies temperature violations', 'Spots cross-contamination risk', 'Notes obstruction hazards'],
|
||||
challenge: {
|
||||
type: 'photo_identify',
|
||||
prompt: 'Mark every food-safety hazard you can find in this prep station.',
|
||||
@@ -1008,6 +1144,11 @@ const COURSES = [
|
||||
badge_reward: 'Floor Leader',
|
||||
estimated_minutes: 30,
|
||||
proof_skill: 'Team Leadership',
|
||||
skill_id: 'leadership',
|
||||
target_level: 'advanced',
|
||||
required_level: 'intermediate',
|
||||
completion_criteria: ['Work through the training material', 'Submit evidence for the challenge'],
|
||||
verification_criteria: ['Assigns clear ownership', 'Communicates timing', 'Sets a tone people follow'],
|
||||
challenge: {
|
||||
type: 'video',
|
||||
prompt: 'Record the pre-shift briefing you would give a floor team of eight before a 300-guest gala.',
|
||||
@@ -1025,6 +1166,170 @@ const COURSES = [
|
||||
},
|
||||
];
|
||||
|
||||
/* ── Training modules (the skill ladders) ──────────────────────────────── */
|
||||
|
||||
/**
|
||||
* The training behind every level of every skill.
|
||||
*
|
||||
* Written as a table rather than 36 hand-built records: each row is one module,
|
||||
* and the shape it expands to is the same Course entity the four skills above
|
||||
* use, so the challenge page, the Forge library and Owliver's evaluation all
|
||||
* read these without knowing they were generated.
|
||||
*
|
||||
* Columns: skill · level · title · minutes · proof type · description ·
|
||||
* what Owliver checks. `required_level` is the rung below, which is what makes
|
||||
* the ladder a ladder rather than a menu.
|
||||
*/
|
||||
const MODULE_TABLE = [
|
||||
/* Server — the full four-rung ladder. */
|
||||
['server', 'beginner', 'Server Fundamentals', 18, 'roleplay', 'Sequence of service, tray discipline, and the first ninety seconds at a table.', ['Greets and sets expectations', 'Follows the service sequence', 'Handles the tray safely']],
|
||||
['server', 'beginner', 'Workplace Safety', 14, 'photo_identify', 'Spot the hazard before it becomes an incident — spills, traffic, hot pass.', ['Identifies floor hazards', 'Spots obstruction risks', 'Knows when to stop service']],
|
||||
['server', 'beginner', 'Basic Guest Service', 16, 'roleplay', 'Reading a table, timing an approach, and answering what you actually know.', ['Reads the table before approaching', 'Answers accurately', 'Closes the interaction cleanly']],
|
||||
|
||||
['server', 'intermediate', 'Advanced Guest Service', 20, 'roleplay', 'Difficult tables, competing demands, and keeping a section moving anyway.', ['Prioritises across tables', 'Keeps guests informed', 'Recovers timing without panic']],
|
||||
['server', 'intermediate', 'Table Management', 18, 'roleplay', 'Running a section: turns, holds, and the order you touch tables in.', ['Sequences the section', 'Manages holds and turns', 'Communicates with the pass']],
|
||||
['server', 'intermediate', 'Order Accuracy', 15, 'roleplay', 'Modifiers, allergens, and repeating back what you are about to send.', ['Confirms modifiers', 'Flags allergens correctly', 'Verifies before firing']],
|
||||
|
||||
['server', 'advanced', 'Guest Recovery', 22, 'roleplay', 'Turning a failed experience around without giving away the room.', ['Acknowledges without excuses', 'Offers a concrete remedy', 'Protects the table experience']],
|
||||
['server', 'advanced', 'High-Volume Service Operations', 25, 'video', 'Three hundred covers, one pass, and a plan that survives contact.', ['Plans the run', 'Holds the standard at volume', 'Adjusts without dropping tables']],
|
||||
|
||||
['server', 'expert', 'Floor Leadership', 30, 'video', 'Owning the floor: assignments, pace, and the call nobody else will make.', ['Assigns clear ownership', 'Sets and holds the pace', 'Makes the decision on time']],
|
||||
['server', 'expert', 'Service Coaching', 25, 'roleplay', 'Correcting a teammate mid-service without deflating them.', ['Names the behaviour, not the person', 'Gives an actionable correction', 'Confirms understanding']],
|
||||
['server', 'expert', 'Operations Excellence', 28, 'roleplay', 'Reading a BEO, staffing to it, and defending the standard to a client.', ['Reads the event brief accurately', 'Staffs to the requirement', 'Holds the standard with the client']],
|
||||
|
||||
/* Bartending. */
|
||||
['bartending', 'beginner', 'Bar Fundamentals', 16, 'roleplay', 'Station setup, glassware, and pouring to spec under a clock.', ['Sets up a working station', 'Pours to spec', 'Keeps the bar clean during service']],
|
||||
['bartending', 'beginner', 'Responsible Service Basics', 14, 'roleplay', 'Checking age, reading a guest, and knowing where the line is.', ['Verifies age correctly', 'Reads intoxication signs', 'Knows the escalation path']],
|
||||
|
||||
['bartending', 'intermediate', 'Cocktail Specs & Speed', 20, 'video', 'Classic specs from memory, built at event pace without losing accuracy.', ['Builds to spec', 'Maintains pace', 'Recovers from a mistake cleanly']],
|
||||
|
||||
['bartending', 'advanced', 'High-Volume Bar Operations', 24, 'video', 'Running a bar three deep: batching, sequencing, and restock timing.', ['Batches sensibly', 'Sequences the rail', 'Restocks before it hurts']],
|
||||
['bartending', 'advanced', 'Guest Judgment Behind the Bar', 20, 'roleplay', 'The refusal, the regular, and the colleague watching you make the call.', ['Refuses clearly and kindly', 'Protects the guest and the room', 'Documents what happened']],
|
||||
|
||||
['bartending', 'expert', 'Bar Program Leadership', 28, 'roleplay', 'Menu, training, and holding a team of bartenders to one standard.', ['Sets a standard others can follow', 'Trains rather than corrects', 'Owns the program end to end']],
|
||||
['bartending', 'expert', 'Inventory & Cost Control', 24, 'roleplay', 'Par levels, variance, and explaining a number to an operator.', ['Tracks variance accurately', 'Explains the number', 'Proposes a real fix']],
|
||||
|
||||
/* Leadership. */
|
||||
['leadership', 'beginner', 'Shift Communication', 14, 'roleplay', 'Saying the necessary thing, once, so the room hears it.', ['States the message clearly', 'Confirms it landed', 'Keeps it short']],
|
||||
['leadership', 'beginner', 'Giving & Taking Direction', 16, 'roleplay', 'Taking a correction well, and giving one that can be acted on.', ['Receives correction without friction', 'Gives an actionable instruction', 'Follows up']],
|
||||
|
||||
['leadership', 'intermediate', 'Pre-Shift Briefings', 18, 'video', 'Two minutes that decide how the next six hours go.', ['Covers ownership and timing', 'Sets the tone', 'Ends with questions answered']],
|
||||
['leadership', 'intermediate', 'Handling Conflict on the Floor', 20, 'roleplay', 'Two staff, one section, and a room that must not notice.', ['De-escalates in private', 'Resolves rather than defers', 'Protects service']],
|
||||
|
||||
['leadership', 'advanced', 'Section Assignment & Coverage', 22, 'roleplay', 'Matching people to sections, and covering the gap when someone drops.', ['Assigns to strength', 'Plans for absence', 'Rebalances mid-service']],
|
||||
|
||||
['leadership', 'expert', 'Developing Your Team', 26, 'roleplay', 'Turning a good server into the person who runs the floor next year.', ['Identifies real potential', 'Gives progressive responsibility', 'Follows through over time']],
|
||||
['leadership', 'expert', 'Operations Under Pressure', 28, 'video', 'The gala that goes wrong, and the twenty minutes that decide it.', ['Triages correctly', 'Communicates under load', 'Owns the outcome']],
|
||||
|
||||
/* Customer Service. */
|
||||
['customer_service', 'beginner', 'Guest First Impressions', 12, 'roleplay', 'The greeting, the eye contact, and the tone that sets the whole visit.', ['Opens warmly', 'Sets expectations', 'Hands off cleanly']],
|
||||
['customer_service', 'beginner', 'Reading the Room', 14, 'roleplay', 'Knowing when a table wants attention and when it wants to be left alone.', ['Reads guest signals', 'Times the approach', 'Adjusts to the table']],
|
||||
|
||||
['customer_service', 'intermediate', 'Handling Complaints', 18, 'roleplay', 'Hearing the complaint behind the complaint, and fixing that one.', ['Listens before solving', 'Identifies the real issue', 'Resolves within authority']],
|
||||
['customer_service', 'intermediate', 'Service Recovery Basics', 16, 'roleplay', 'What you can offer, what you cannot, and how to escalate fast.', ['Offers a concrete remedy', 'Stays within policy', 'Escalates without stalling']],
|
||||
|
||||
['customer_service', 'advanced', 'VIP & High-Stakes Guests', 20, 'roleplay', 'The head table, the client, and the guest everyone is watching.', ['Handles scrutiny calmly', 'Anticipates needs', 'Protects discretion']],
|
||||
['customer_service', 'advanced', 'Client Relations', 22, 'roleplay', 'The conversation with the person who books the next event.', ['Represents the operation', 'Handles pushback', 'Secures the follow-up']],
|
||||
|
||||
/* Food Safety. */
|
||||
['food_safety', 'beginner', 'Personal Hygiene Standards', 10, 'photo_identify', 'Handwashing, gloves, and the habits an inspector looks for first.', ['Follows hand hygiene', 'Uses gloves correctly', 'Presents to standard']],
|
||||
|
||||
['food_safety', 'intermediate', 'Temperature Control & HACCP', 18, 'photo_identify', 'Danger zone, hold times, and the log that proves you held it.', ['Knows the danger zone', 'Records holds accurately', 'Acts on a violation']],
|
||||
['food_safety', 'intermediate', 'Allergen Management', 16, 'roleplay', 'Stopping the plate, checking the ticket, and saying it out loud.', ['Stops the plate', 'Verifies with the kitchen', 'Communicates to the guest']],
|
||||
|
||||
['food_safety', 'advanced', 'Food Safety Auditing', 22, 'photo_identify', 'Walking a kitchen the way an inspector does, and writing it up.', ['Finds the violations', 'Ranks by severity', 'Writes an actionable report']],
|
||||
['food_safety', 'advanced', 'Incident Response & Reporting', 18, 'roleplay', 'A suspected foodborne incident, in the hour it matters.', ['Isolates the product', 'Documents accurately', 'Notifies the right people']],
|
||||
];
|
||||
|
||||
const SKILL_CATEGORY = {
|
||||
server: 'Service',
|
||||
bartending: 'Bar',
|
||||
leadership: 'Leadership',
|
||||
customer_service: 'Service',
|
||||
food_safety: 'Kitchen',
|
||||
};
|
||||
|
||||
const LEVEL_ORDER = ['beginner', 'intermediate', 'advanced', 'expert'];
|
||||
|
||||
/** Level → the rung below it, which is what a module requires before it opens. */
|
||||
const requiredLevelFor = (level) => LEVEL_ORDER[LEVEL_ORDER.indexOf(level) - 1] || null;
|
||||
|
||||
/** Difficulty is the existing field; level drives it so the two never disagree. */
|
||||
const DIFFICULTY_FOR = { beginner: 'beginner', intermediate: 'intermediate', advanced: 'advanced', expert: 'advanced' };
|
||||
|
||||
const XP_FOR = { beginner: 100, intermediate: 150, advanced: 220, expert: 300 };
|
||||
|
||||
const PROMPT_FOR = {
|
||||
roleplay: (title) => `Work through a ${title.toLowerCase()} scenario with Owliver, start to finish.`,
|
||||
video: (title) => `Record yourself demonstrating ${title.toLowerCase()} as you would on a live shift.`,
|
||||
photo_identify: (title) => `Mark everything a ${title.toLowerCase()} check should catch in this photo.`,
|
||||
};
|
||||
|
||||
const slug = (title) => title.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_|_$/g, '');
|
||||
|
||||
const TRAINING_MODULES = MODULE_TABLE.map(
|
||||
([skill_id, target_level, title, minutes, type, description, criteria], i) => ({
|
||||
id: `mod_${skill_id}_${slug(title)}`,
|
||||
title,
|
||||
description,
|
||||
category: SKILL_CATEGORY[skill_id],
|
||||
difficulty: DIFFICULTY_FOR[target_level],
|
||||
xp: XP_FOR[target_level],
|
||||
estimated_minutes: minutes,
|
||||
proof_skill: title,
|
||||
/* The four fields that make a course a rung on a ladder. */
|
||||
skill_id,
|
||||
target_level,
|
||||
required_level: requiredLevelFor(target_level),
|
||||
completion_criteria: ['Work through the training material', 'Submit evidence for the challenge'],
|
||||
verification_criteria: criteria,
|
||||
challenge: {
|
||||
type,
|
||||
prompt: PROMPT_FOR[type](title),
|
||||
ai_persona: type === 'roleplay' ? 'A guest or teammate in a live service situation.' : undefined,
|
||||
rubric: criteria.map((criterion, n) => ({ criterion, weight: n === 0 ? 40 : 30 })),
|
||||
},
|
||||
unlock_requirements: { min_shifts: 0, min_reliability: 0, required_badges: [] },
|
||||
quiz: [],
|
||||
pass_score: 70,
|
||||
status: 'active',
|
||||
created_date: iso('2026-06-20'),
|
||||
_order: i,
|
||||
})
|
||||
);
|
||||
|
||||
/** Every course the platform holds — the four originals plus the ladders. */
|
||||
const COURSE_LIBRARY = [...COURSES, ...TRAINING_MODULES];
|
||||
|
||||
/** A module id from its skill and title, so profiles read like the ladder does. */
|
||||
const mod = (skill, title) => `mod_${skill}_${slug(title)}`;
|
||||
|
||||
/**
|
||||
* Completed training on a worker profile.
|
||||
*
|
||||
* Titles are looked up rather than repeated, so a renamed module cannot leave a
|
||||
* profile claiming a course that no longer goes by that name.
|
||||
*/
|
||||
const completions = (ids, date, score = 88) =>
|
||||
ids.map((id) => ({
|
||||
course_id: id,
|
||||
title: COURSE_LIBRARY.find((c) => c.id === id)?.title || id,
|
||||
score,
|
||||
completed_date: iso(date),
|
||||
}));
|
||||
|
||||
/* The ladders, as id lists — profiles below compose them. */
|
||||
const SERVER_BEGINNER = ['Server Fundamentals', 'Workplace Safety', 'Basic Guest Service'].map((t) => mod('server', t));
|
||||
const SERVER_INTERMEDIATE = ['Advanced Guest Service', 'Table Management', 'Order Accuracy'].map((t) => mod('server', t));
|
||||
const SERVER_ADVANCED = ['course_service_basics', mod('server', 'Guest Recovery'), mod('server', 'High-Volume Service Operations')];
|
||||
const CS_BEGINNER = ['Guest First Impressions', 'Reading the Room'].map((t) => mod('customer_service', t));
|
||||
const CS_INTERMEDIATE = ['Handling Complaints', 'Service Recovery Basics'].map((t) => mod('customer_service', t));
|
||||
const CS_ADVANCED = ['VIP & High-Stakes Guests', 'Client Relations'].map((t) => mod('customer_service', t));
|
||||
const FOOD_BEGINNER = ['course_kitchen_safety', mod('food_safety', 'Personal Hygiene Standards')];
|
||||
const LEAD_BEGINNER = ['Shift Communication', 'Giving & Taking Direction'].map((t) => mod('leadership', t));
|
||||
const LEAD_INTERMEDIATE = ['Pre-Shift Briefings', 'Handling Conflict on the Floor'].map((t) => mod('leadership', t));
|
||||
const LEAD_ADVANCED = ['course_leadership', mod('leadership', 'Section Assignment & Coverage')];
|
||||
|
||||
const BADGES = [
|
||||
{ id: 'badge_service', name: 'Service Fundamentals', description: 'Verified coursed table service.', level: 'bronze', requirements: 'Complete Fine Dining Service Standards', verification_status: 'verified', created_date: iso('2026-06-10') },
|
||||
{ id: 'badge_responsible', name: 'Responsible Service', description: 'Verified responsible alcohol service judgment.', level: 'silver', requirements: 'Complete Responsible Alcohol Service', expiration_months: 24, verification_status: 'verified', created_date: iso('2026-06-12') },
|
||||
@@ -1096,11 +1401,18 @@ const WORKER_PROFILES = [
|
||||
reliability_score: 95,
|
||||
profile_completion: 100,
|
||||
xp: 1480,
|
||||
completed_courses: [
|
||||
{ course_id: 'course_service_basics', title: 'Fine Dining Service Standards', score: 96, completed_date: iso('2026-06-22') },
|
||||
{ course_id: 'course_kitchen_safety', title: 'Kitchen Safety & Hazard Spotting', score: 92, completed_date: iso('2026-07-01') },
|
||||
{ course_id: 'course_leadership', title: 'Leading a Floor Team', score: 89, completed_date: iso('2026-07-14') },
|
||||
],
|
||||
/* Seven years on the floor, as a completed ladder: Server and Leadership to
|
||||
Advanced, Customer Service to Advanced, Food Safety to Beginner. */
|
||||
completed_courses: completions(
|
||||
[
|
||||
...SERVER_BEGINNER, ...SERVER_INTERMEDIATE, ...SERVER_ADVANCED,
|
||||
...CS_BEGINNER, ...CS_INTERMEDIATE, ...CS_ADVANCED,
|
||||
...FOOD_BEGINNER,
|
||||
...LEAD_BEGINNER, ...LEAD_INTERMEDIATE, ...LEAD_ADVANCED,
|
||||
],
|
||||
'2026-07-14',
|
||||
93
|
||||
),
|
||||
earned_badges: [
|
||||
{ name: 'Service Fundamentals', level: 'bronze', course_id: 'course_service_basics', earned_date: iso('2026-06-22') },
|
||||
{ name: 'Safety Aware', level: 'bronze', course_id: 'course_kitchen_safety', earned_date: iso('2026-07-01') },
|
||||
@@ -1142,9 +1454,14 @@ const WORKER_PROFILES = [
|
||||
reliability_score: 38,
|
||||
profile_completion: 62,
|
||||
xp: 120,
|
||||
completed_courses: [
|
||||
{ course_id: 'course_service_basics', title: 'Fine Dining Service Standards', score: 78, completed_date: iso('2026-07-28') },
|
||||
],
|
||||
/* One year in: the Beginner rung of Server, and one Intermediate module
|
||||
started — so her card reads "Beginner, advancing" rather than a flat
|
||||
label that hides the work in progress. */
|
||||
completed_courses: completions(
|
||||
[...SERVER_BEGINNER, mod('server', 'Advanced Guest Service'), ...CS_BEGINNER],
|
||||
'2026-07-28',
|
||||
79
|
||||
),
|
||||
earned_badges: [
|
||||
{ name: 'Service Fundamentals', level: 'bronze', course_id: 'course_service_basics', earned_date: iso('2026-07-28') },
|
||||
],
|
||||
@@ -1157,6 +1474,146 @@ const WORKER_PROFILES = [
|
||||
status: 'active',
|
||||
created_date: iso('2026-07-22'),
|
||||
},
|
||||
/**
|
||||
* Arun, Priya and Rahul are the same position read three ways.
|
||||
*
|
||||
* All three are measured against Senior Server – Fine Dining, and the only
|
||||
* thing that separates them is verified training: Arun is one module short of
|
||||
* qualifying, Priya is a level behind on the skill that carries the role, and
|
||||
* Rahul has the ground floor but nothing above it. Nothing about their match
|
||||
* is stored — completing training is what moves it.
|
||||
*/
|
||||
{
|
||||
id: 'wp_arun',
|
||||
full_name: 'Arun Kumar',
|
||||
email: 'arun.kumar@example.com',
|
||||
phone: '+1-408-555-0210',
|
||||
address: 'San Jose, CA',
|
||||
selfie_url: 'https://i.pravatar.cc/240?img=68',
|
||||
languages: ['English', 'Tamil'],
|
||||
availability: ['Weekdays', 'Weekends', 'Evenings'],
|
||||
transportation: 'Own vehicle',
|
||||
certifications: ['Food Handler Card'],
|
||||
experience: [{ company: 'Valley Catering Co.', role: 'Event Server', years: 4 }],
|
||||
experience_years: 4,
|
||||
current_position: 'Event Server',
|
||||
desired_position: 'Senior Server',
|
||||
career_goals: 'Run a fine dining section as the senior server on the floor.',
|
||||
skills: ['Coursed service', 'Guest recovery', 'Allergen handling'],
|
||||
industries: ['Hospitality', 'Events'],
|
||||
personality: 'Unflappable. Notices the table nobody else is watching.',
|
||||
strengths: ['Calm at volume', 'Accurate under pressure'],
|
||||
weaknesses: ['Quiet in briefings'],
|
||||
communication_style: 'Measured; confirms detail before acting.',
|
||||
salary_expectations: '$26–$32/hr',
|
||||
leadership_potential: 68,
|
||||
ai_interview_score: 86,
|
||||
krow_score: 82,
|
||||
reliability_score: 91,
|
||||
profile_completion: 94,
|
||||
xp: 1180,
|
||||
/* Server through Intermediate, plus two of the three Advanced modules —
|
||||
Fine Dining Service Standards is the one thing between him and Advanced,
|
||||
and therefore between him and this role. */
|
||||
completed_courses: completions(
|
||||
[
|
||||
...SERVER_BEGINNER, ...SERVER_INTERMEDIATE,
|
||||
mod('server', 'Guest Recovery'), mod('server', 'High-Volume Service Operations'),
|
||||
...CS_BEGINNER, ...CS_INTERMEDIATE,
|
||||
...FOOD_BEGINNER,
|
||||
...LEAD_BEGINNER,
|
||||
],
|
||||
'2026-08-02',
|
||||
89
|
||||
),
|
||||
earned_badges: [],
|
||||
capabilities: [],
|
||||
shifts_completed: 38,
|
||||
attendance_score: 96,
|
||||
performance_score: 88,
|
||||
client_rating: 4.7,
|
||||
supervisor_rating: 4.6,
|
||||
status: 'active',
|
||||
created_date: iso('2026-07-05'),
|
||||
},
|
||||
{
|
||||
id: 'wp_priya',
|
||||
full_name: 'Priya Nair',
|
||||
email: 'priya.nair@example.com',
|
||||
phone: '+1-408-555-0211',
|
||||
address: 'Santa Clara, CA',
|
||||
selfie_url: 'https://i.pravatar.cc/240?img=47',
|
||||
languages: ['English', 'Hindi'],
|
||||
availability: ['Weekends', 'Evenings'],
|
||||
transportation: 'Own vehicle',
|
||||
certifications: ['Food Handler Card'],
|
||||
experience: [{ company: 'Bay Event Staffing', role: 'Event Server', years: 2 }],
|
||||
experience_years: 2,
|
||||
current_position: 'Event Server',
|
||||
desired_position: 'Senior Server',
|
||||
career_goals: 'Move up to fine dining sections.',
|
||||
skills: ['Table service', 'Guest relations'],
|
||||
industries: ['Hospitality'],
|
||||
leadership_potential: 45,
|
||||
ai_interview_score: 74,
|
||||
krow_score: 68,
|
||||
reliability_score: 84,
|
||||
profile_completion: 80,
|
||||
xp: 760,
|
||||
completed_courses: completions(
|
||||
[...SERVER_BEGINNER, ...SERVER_INTERMEDIATE, ...CS_BEGINNER, ...CS_INTERMEDIATE, ...FOOD_BEGINNER],
|
||||
'2026-07-30',
|
||||
85
|
||||
),
|
||||
earned_badges: [],
|
||||
capabilities: [],
|
||||
shifts_completed: 21,
|
||||
attendance_score: 93,
|
||||
performance_score: 81,
|
||||
client_rating: 4.5,
|
||||
supervisor_rating: 4.4,
|
||||
status: 'active',
|
||||
created_date: iso('2026-07-12'),
|
||||
},
|
||||
{
|
||||
id: 'wp_rahul',
|
||||
full_name: 'Rahul Menon',
|
||||
email: 'rahul.menon@example.com',
|
||||
phone: '+1-408-555-0212',
|
||||
address: 'Milpitas, CA',
|
||||
selfie_url: 'https://i.pravatar.cc/240?img=59',
|
||||
languages: ['English'],
|
||||
availability: ['Weekends'],
|
||||
transportation: 'Public transit',
|
||||
certifications: [],
|
||||
experience: [],
|
||||
experience_years: 0,
|
||||
current_position: '',
|
||||
desired_position: 'Event Server',
|
||||
career_goals: 'Get on a floor and learn the work properly.',
|
||||
skills: [],
|
||||
industries: ['Hospitality'],
|
||||
leadership_potential: 20,
|
||||
ai_interview_score: 0,
|
||||
krow_score: 41,
|
||||
reliability_score: 72,
|
||||
profile_completion: 58,
|
||||
xp: 320,
|
||||
completed_courses: completions(
|
||||
[...SERVER_BEGINNER, ...CS_BEGINNER, ...FOOD_BEGINNER, ...LEAD_BEGINNER],
|
||||
'2026-08-05',
|
||||
76
|
||||
),
|
||||
earned_badges: [],
|
||||
capabilities: [],
|
||||
shifts_completed: 4,
|
||||
attendance_score: 100,
|
||||
performance_score: 0,
|
||||
client_rating: 0,
|
||||
supervisor_rating: 0,
|
||||
status: 'active',
|
||||
created_date: iso('2026-07-28'),
|
||||
},
|
||||
{
|
||||
id: 'wp_aravind',
|
||||
full_name: 'Aravind Kumar',
|
||||
@@ -1408,18 +1865,31 @@ export const DEMO_USER = {
|
||||
},
|
||||
};
|
||||
|
||||
/* ── Assignments ───────────────────────────────────────────────────────────
|
||||
Who is on which position, and until when — the record that turns a hire into
|
||||
workforce allocation, and the one that says who is therefore not free for
|
||||
anything else.
|
||||
|
||||
Empty by design. This deployment has no assignment records yet: they are
|
||||
written by the assignment flow, not shipped as seed. The engine reads an
|
||||
empty set correctly — every position simply reports nobody assigned — and
|
||||
the UI omits the demand figures rather than inventing coverage that does
|
||||
not exist. */
|
||||
const ASSIGNMENTS = [];
|
||||
|
||||
export const seedData = {
|
||||
JobPosting: JOB_POSTINGS,
|
||||
JobApplication: JOB_APPLICATIONS,
|
||||
AIInterview: AI_INTERVIEWS,
|
||||
Staff: STAFF,
|
||||
WorkerProfile: WORKER_PROFILES,
|
||||
Course: COURSES,
|
||||
Course: COURSE_LIBRARY,
|
||||
Badge: BADGES,
|
||||
LearningPath: LEARNING_PATHS,
|
||||
Certification: CERTIFICATIONS,
|
||||
RoleCategory: ROLE_CATEGORIES,
|
||||
UserActivity: USER_ACTIVITY,
|
||||
Assignment: ASSIGNMENTS,
|
||||
Evidence: EVIDENCE,
|
||||
User: [DEMO_USER],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user