feat(api): complete unified v2 mobile surface

This commit is contained in:
zouantchaw
2026-03-13 17:02:24 +01:00
parent 817a39e305
commit b455455a49
39 changed files with 7726 additions and 506 deletions

View File

@@ -44,6 +44,14 @@ async function main() {
const completedEndsAt = hoursFromNow(-20);
const checkedInAt = hoursFromNow(-27.5);
const checkedOutAt = hoursFromNow(-20.25);
const assignedStartsAt = hoursFromNow(2);
const assignedEndsAt = hoursFromNow(10);
const availableStartsAt = hoursFromNow(30);
const availableEndsAt = hoursFromNow(38);
const cancelledStartsAt = hoursFromNow(20);
const cancelledEndsAt = hoursFromNow(28);
const noShowStartsAt = hoursFromNow(-18);
const noShowEndsAt = hoursFromNow(-10);
const invoiceDueAt = hoursFromNow(72);
await upsertUser(client, fixture.users.businessOwner);
@@ -248,6 +256,16 @@ async function main() {
[fixture.benefits.commuter.id, fixture.tenant.id, fixture.staff.ana.id, fixture.benefits.commuter.title]
);
await client.query(
`
INSERT INTO emergency_contacts (
id, tenant_id, staff_id, full_name, phone, relationship_type, is_primary, metadata
)
VALUES ($1, $2, $3, 'Maria Barista', '+15550007777', 'SIBLING', TRUE, '{"seeded":true}'::jsonb)
`,
[fixture.emergencyContacts.primary.id, fixture.tenant.id, fixture.staff.ana.id]
);
await client.query(
`
INSERT INTO clock_points (
@@ -319,6 +337,34 @@ async function main() {
]
);
await client.query(
`
INSERT INTO orders (
id, tenant_id, business_id, vendor_id, order_number, title, description, status, service_type,
starts_at, ends_at, location_name, location_address, latitude, longitude, notes, created_by_user_id, metadata
)
VALUES (
$1, $2, $3, $4, $5, $6, 'Active order used to populate assigned, available, cancelled, and no-show shift states',
'ACTIVE', 'RESTAURANT', $7, $8, 'Google Cafe', $9, $10, $11, 'Mixed state scenario order', $12,
'{"slice":"active","orderType":"ONE_TIME"}'::jsonb
)
`,
[
fixture.orders.active.id,
fixture.tenant.id,
fixture.business.id,
fixture.vendor.id,
fixture.orders.active.number,
fixture.orders.active.title,
assignedStartsAt,
availableEndsAt,
fixture.clockPoint.address,
fixture.clockPoint.latitude,
fixture.clockPoint.longitude,
fixture.users.operationsManager.id,
]
);
await client.query(
`
INSERT INTO shifts (
@@ -357,6 +403,51 @@ async function main() {
]
);
await client.query(
`
INSERT INTO shifts (
id, tenant_id, order_id, business_id, vendor_id, clock_point_id, shift_code, title, status, starts_at, ends_at, timezone,
location_name, location_address, latitude, longitude, geofence_radius_meters, required_workers, assigned_workers, notes, metadata
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, 'OPEN', $9, $10, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, 1, 0, 'Available shift for staff marketplace', '{"slice":"available"}'::jsonb),
($15, $2, $3, $4, $5, $6, $16, $17, 'ASSIGNED', $18, $19, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, 1, 1, 'Assigned shift waiting for staff confirmation', '{"slice":"assigned"}'::jsonb),
($20, $2, $3, $4, $5, $6, $21, $22, 'CANCELLED', $23, $24, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, 1, 0, 'Cancelled shift history sample', '{"slice":"cancelled"}'::jsonb),
($25, $2, $3, $4, $5, $6, $26, $27, 'COMPLETED', $28, $29, 'America/Los_Angeles', 'Google Cafe', $11, $12, $13, $14, 1, 0, 'No-show historical sample', '{"slice":"no_show"}'::jsonb)
`,
[
fixture.shifts.available.id,
fixture.tenant.id,
fixture.orders.active.id,
fixture.business.id,
fixture.vendor.id,
fixture.clockPoint.id,
fixture.shifts.available.code,
fixture.shifts.available.title,
availableStartsAt,
availableEndsAt,
fixture.clockPoint.address,
fixture.clockPoint.latitude,
fixture.clockPoint.longitude,
fixture.clockPoint.geofenceRadiusMeters,
fixture.shifts.assigned.id,
fixture.shifts.assigned.code,
fixture.shifts.assigned.title,
assignedStartsAt,
assignedEndsAt,
fixture.shifts.cancelled.id,
fixture.shifts.cancelled.code,
fixture.shifts.cancelled.title,
cancelledStartsAt,
cancelledEndsAt,
fixture.shifts.noShow.id,
fixture.shifts.noShow.code,
fixture.shifts.noShow.title,
noShowStartsAt,
noShowEndsAt,
]
);
await client.query(
`
INSERT INTO shift_roles (
@@ -377,6 +468,32 @@ async function main() {
]
);
await client.query(
`
INSERT INTO shift_roles (
id, shift_id, role_id, role_code, role_name, workers_needed, assigned_count, pay_rate_cents, bill_rate_cents, metadata
)
VALUES
($1, $2, $7, $8, $9, 1, 0, 2200, 3500, '{"slice":"available"}'::jsonb),
($3, $4, $7, $8, $9, 1, 1, 2300, 3600, '{"slice":"assigned"}'::jsonb),
($5, $6, $7, $8, $9, 1, 0, 2200, 3500, '{"slice":"cancelled"}'::jsonb),
($10, $11, $7, $8, $9, 1, 0, 2200, 3500, '{"slice":"no_show"}'::jsonb)
`,
[
fixture.shiftRoles.availableBarista.id,
fixture.shifts.available.id,
fixture.shiftRoles.assignedBarista.id,
fixture.shifts.assigned.id,
fixture.shiftRoles.cancelledBarista.id,
fixture.shifts.cancelled.id,
fixture.roles.barista.id,
fixture.roles.barista.code,
fixture.roles.barista.name,
fixture.shiftRoles.noShowBarista.id,
fixture.shifts.noShow.id,
]
);
await client.query(
`
INSERT INTO applications (
@@ -417,6 +534,36 @@ async function main() {
]
);
await client.query(
`
INSERT INTO assignments (
id, tenant_id, business_id, vendor_id, shift_id, shift_role_id, workforce_id, staff_id, status,
assigned_at, accepted_at, checked_in_at, checked_out_at, metadata
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, 'ASSIGNED', NOW(), NULL, NULL, NULL, '{"slice":"assigned"}'::jsonb),
($9, $2, $3, $4, $10, $11, $7, $8, 'CANCELLED', NOW(), NULL, NULL, NULL, '{"slice":"cancelled","cancellationReason":"Client cancelled"}'::jsonb),
($12, $2, $3, $4, $13, $14, $7, $8, 'NO_SHOW', $15, NULL, NULL, NULL, '{"slice":"no_show"}'::jsonb)
`,
[
fixture.assignments.assignedAna.id,
fixture.tenant.id,
fixture.business.id,
fixture.vendor.id,
fixture.shifts.assigned.id,
fixture.shiftRoles.assignedBarista.id,
fixture.workforce.ana.id,
fixture.staff.ana.id,
fixture.assignments.cancelledAna.id,
fixture.shifts.cancelled.id,
fixture.shiftRoles.cancelledBarista.id,
fixture.assignments.noShowAna.id,
fixture.shifts.noShow.id,
fixture.shiftRoles.noShowBarista.id,
noShowStartsAt,
]
);
await client.query(
`
INSERT INTO attendance_events (
@@ -486,50 +633,69 @@ async function main() {
`
INSERT INTO documents (id, tenant_id, document_type, name, required_for_role_code, metadata)
VALUES
($1, $2, 'CERTIFICATION', $3, $6, '{"seeded":true}'::jsonb),
($4, $2, 'ATTIRE', $5, $6, '{"seeded":true}'::jsonb),
($7, $2, 'TAX_FORM', $8, $6, '{"seeded":true}'::jsonb)
($1, $2, 'GOVERNMENT_ID', $3, $10, '{"seeded":true,"description":"State ID or passport","required":true}'::jsonb),
($4, $2, 'CERTIFICATION', $5, $10, '{"seeded":true}'::jsonb),
($6, $2, 'ATTIRE', $7, $10, '{"seeded":true,"description":"Upload a photo of your black shirt","required":true}'::jsonb),
($8, $2, 'TAX_FORM', $9, $10, '{"seeded":true}'::jsonb),
($11, $2, 'TAX_FORM', $12, $10, '{"seeded":true}'::jsonb)
`,
[
fixture.documents.foodSafety.id,
fixture.documents.governmentId.id,
fixture.tenant.id,
fixture.documents.governmentId.name,
fixture.documents.foodSafety.id,
fixture.documents.foodSafety.name,
fixture.documents.attireBlackShirt.id,
fixture.documents.attireBlackShirt.name,
fixture.documents.taxFormI9.id,
fixture.documents.taxFormI9.name,
fixture.roles.barista.code,
fixture.documents.taxFormW9.id,
fixture.documents.taxFormW9.name,
fixture.documents.taxFormW4.id,
fixture.documents.taxFormW4.name,
]
);
await client.query(
`
INSERT INTO staff_documents (id, tenant_id, staff_id, document_id, file_uri, status, expires_at, metadata)
INSERT INTO staff_documents (
id, tenant_id, staff_id, document_id, file_uri, status, expires_at, metadata
)
VALUES
($1, $2, $3, $4, $5, 'VERIFIED', $6, '{"seeded":true}'::jsonb),
($7, $2, $3, $8, $9, 'VERIFIED', NULL, '{"seeded":true}'::jsonb),
($10, $2, $3, $11, $12, 'VERIFIED', NULL, '{"seeded":true}'::jsonb)
($1, $2, $3, $4, $5, 'PENDING', $6, '{"seeded":true,"verificationStatus":"PENDING_REVIEW"}'::jsonb),
($7, $2, $3, $8, $9, 'VERIFIED', $10, '{"seeded":true,"verificationStatus":"APPROVED"}'::jsonb),
($11, $2, $3, $12, $13, 'VERIFIED', NULL, '{"seeded":true,"verificationStatus":"APPROVED"}'::jsonb),
($14, $2, $3, $15, $16, 'VERIFIED', NULL, '{"seeded":true,"formStatus":"SUBMITTED","fields":{"ssnLast4":"1234","filingStatus":"single"}}'::jsonb),
($17, $2, $3, $18, $19, 'PENDING', NULL, '{"seeded":true,"formStatus":"DRAFT","fields":{"section1Complete":true}}'::jsonb)
`,
[
fixture.staffDocuments.foodSafety.id,
fixture.staffDocuments.governmentId.id,
fixture.tenant.id,
fixture.staff.ana.id,
fixture.documents.governmentId.id,
`gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/government-id-front.jpg`,
hoursFromNow(24 * 365),
fixture.staffDocuments.foodSafety.id,
fixture.documents.foodSafety.id,
`gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/food-handler-card.pdf`,
hoursFromNow(24 * 180),
fixture.staffDocuments.attireBlackShirt.id,
fixture.documents.attireBlackShirt.id,
`gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/black-shirt.jpg`,
fixture.staffDocuments.taxFormW9.id,
fixture.documents.taxFormW9.id,
`gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/w9-form.pdf`,
fixture.staffDocuments.taxFormW4.id,
fixture.documents.taxFormW4.id,
`gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/w4-form.pdf`,
fixture.staffDocuments.taxFormI9.id,
fixture.documents.taxFormI9.id,
`gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/i9-form.pdf`,
]
);
await client.query(
`
INSERT INTO certificates (id, tenant_id, staff_id, certificate_type, certificate_number, issued_at, expires_at, status, metadata)
VALUES ($1, $2, $3, 'FOOD_SAFETY', 'FH-ANA-2026', $4, $5, 'VERIFIED', '{"seeded":true}'::jsonb)
INSERT INTO certificates (
id, tenant_id, staff_id, certificate_type, certificate_number, issued_at, expires_at, status, file_uri, metadata
)
VALUES ($1, $2, $3, 'FOOD_SAFETY', 'FH-ANA-2026', $4, $5, 'VERIFIED', $6, $7::jsonb)
`,
[
fixture.certificates.foodSafety.id,
@@ -537,6 +703,13 @@ async function main() {
fixture.staff.ana.id,
hoursFromNow(-24 * 30),
hoursFromNow(24 * 180),
`gs://krow-workforce-dev-v2-private/uploads/${fixture.staff.ana.id}/food-safety-certificate.pdf`,
JSON.stringify({
seeded: true,
name: 'Food Safety Certificate',
issuer: 'ServSafe',
verificationStatus: 'APPROVED',
}),
]
);

View File

@@ -108,6 +108,11 @@ export const V2DemoFixture = {
number: 'ORD-V2-COMP-1002',
title: 'Completed catering shift',
},
active: {
id: 'b6132d7a-45c3-4879-b349-46b2fd518003',
number: 'ORD-V2-ACT-1003',
title: 'Live staffing operations',
},
},
shifts: {
open: {
@@ -120,6 +125,26 @@ export const V2DemoFixture = {
code: 'SHIFT-V2-COMP-1',
title: 'Completed catering shift',
},
available: {
id: '6e7dadad-99e4-45bb-b0da-7bb617954003',
code: 'SHIFT-V2-OPEN-2',
title: 'Available lunch shift',
},
assigned: {
id: '6e7dadad-99e4-45bb-b0da-7bb617954004',
code: 'SHIFT-V2-ASSIGNED-1',
title: 'Assigned espresso shift',
},
cancelled: {
id: '6e7dadad-99e4-45bb-b0da-7bb617954005',
code: 'SHIFT-V2-CANCELLED-1',
title: 'Cancelled hospitality shift',
},
noShow: {
id: '6e7dadad-99e4-45bb-b0da-7bb617954006',
code: 'SHIFT-V2-NOSHOW-1',
title: 'No-show breakfast shift',
},
},
shiftRoles: {
openBarista: {
@@ -128,6 +153,18 @@ export const V2DemoFixture = {
completedBarista: {
id: '4dd35b2b-4aaf-4c28-a91f-7bda05e2b002',
},
availableBarista: {
id: '4dd35b2b-4aaf-4c28-a91f-7bda05e2b003',
},
assignedBarista: {
id: '4dd35b2b-4aaf-4c28-a91f-7bda05e2b004',
},
cancelledBarista: {
id: '4dd35b2b-4aaf-4c28-a91f-7bda05e2b005',
},
noShowBarista: {
id: '4dd35b2b-4aaf-4c28-a91f-7bda05e2b006',
},
},
applications: {
openAna: {
@@ -138,6 +175,15 @@ export const V2DemoFixture = {
completedAna: {
id: 'f1d3f738-a132-4863-b222-4f9cb25aa001',
},
assignedAna: {
id: 'f1d3f738-a132-4863-b222-4f9cb25aa002',
},
cancelledAna: {
id: 'f1d3f738-a132-4863-b222-4f9cb25aa003',
},
noShowAna: {
id: 'f1d3f738-a132-4863-b222-4f9cb25aa004',
},
},
timesheets: {
completedAna: {
@@ -166,6 +212,10 @@ export const V2DemoFixture = {
},
},
documents: {
governmentId: {
id: 'e6fd0183-34d9-4c23-9a9a-bf98da995000',
name: 'Government ID',
},
foodSafety: {
id: 'e6fd0183-34d9-4c23-9a9a-bf98da995001',
name: 'Food Handler Card',
@@ -174,27 +224,42 @@ export const V2DemoFixture = {
id: 'e6fd0183-34d9-4c23-9a9a-bf98da995002',
name: 'Black Shirt',
},
taxFormW9: {
taxFormI9: {
id: 'e6fd0183-34d9-4c23-9a9a-bf98da995003',
name: 'W-9 Tax Form',
name: 'I-9',
},
taxFormW4: {
id: 'e6fd0183-34d9-4c23-9a9a-bf98da995004',
name: 'W-4',
},
},
staffDocuments: {
governmentId: {
id: '4b157236-a4b0-4c44-b199-7d4ea1f95000',
},
foodSafety: {
id: '4b157236-a4b0-4c44-b199-7d4ea1f95001',
},
attireBlackShirt: {
id: '4b157236-a4b0-4c44-b199-7d4ea1f95002',
},
taxFormW9: {
taxFormI9: {
id: '4b157236-a4b0-4c44-b199-7d4ea1f95003',
},
taxFormW4: {
id: '4b157236-a4b0-4c44-b199-7d4ea1f95004',
},
},
certificates: {
foodSafety: {
id: 'df6452dc-4ec7-4d54-876d-26bf8ce5b001',
},
},
emergencyContacts: {
primary: {
id: '8bb1e0c0-59bb-4ce7-8f0f-27674e0b2001',
},
},
accounts: {
businessPrimary: {
id: '5d98e0ba-8e89-4ffb-aafd-df6bbe2fe001',