feat(api): add M5 coverage controls and frontend spec

This commit is contained in:
zouantchaw
2026-03-18 08:18:50 +01:00
parent 008dd7efb1
commit 32f6cd55c8
14 changed files with 894 additions and 8 deletions

View File

@@ -37,6 +37,7 @@ async function apiCall(path, {
idempotencyKey,
body,
expectedStatus = 200,
allowFailure = false,
} = {}) {
const headers = {};
if (token) headers.Authorization = `Bearer ${token}`;
@@ -49,6 +50,12 @@ async function apiCall(path, {
body: body === undefined ? undefined : JSON.stringify(body),
});
const payload = await readJson(response);
if (allowFailure) {
return {
statusCode: response.status,
body: payload,
};
}
if (response.status !== expectedStatus) {
throw new Error(`${method} ${path} expected ${expectedStatus}, got ${response.status}: ${JSON.stringify(payload)}`);
}
@@ -381,6 +388,29 @@ async function main() {
assert.ok(Array.isArray(teamMembers.items));
logStep('client.team-members.ok', { count: teamMembers.items.length });
const createdShiftManager = await apiCall('/client/shift-managers', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('create-shift-manager'),
body: {
hubId: fixture.clockPoint.id,
email: `smoke.manager.${Date.now()}@krowd.com`,
firstName: 'Smoke',
lastName: 'Manager',
phone: '+15550009999',
},
});
assert.ok(createdShiftManager.businessMembershipId);
assert.equal(createdShiftManager.membershipStatus, 'INVITED');
assert.ok(createdShiftManager.managerAssignmentId);
logStep('client.shift-manager.create.ok', createdShiftManager);
const teamMembersAfterCreate = await apiCall('/client/team-members', {
token: ownerSession.sessionToken,
});
assert.ok(teamMembersAfterCreate.items.some((item) => item.businessMembershipId === createdShiftManager.businessMembershipId));
logStep('client.team-members.after-create.ok', { count: teamMembersAfterCreate.items.length });
const viewedOrders = await apiCall(`/client/orders/view?${reportWindow}`, {
token: ownerSession.sessionToken,
});
@@ -754,7 +784,9 @@ async function main() {
});
const openShift = openShifts.items.find((shift) => shift.shiftId === fixture.shifts.available.id)
|| openShifts.items[0];
const blockedApplyCandidate = openShifts.items.find((shift) => shift.shiftId !== openShift.shiftId);
assert.ok(openShift);
assert.ok(blockedApplyCandidate);
logStep('staff.shifts.open.ok', { count: openShifts.items.length });
const pendingShifts = await apiCall('/staff/shifts/pending', {
@@ -858,6 +890,13 @@ async function main() {
assert.ok(Array.isArray(benefits.items));
logStep('staff.profile.benefits.ok', { count: benefits.items.length });
const benefitHistory = await apiCall('/staff/profile/benefits/history?limit=10', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(benefitHistory.items));
assert.ok(benefitHistory.items.length >= 1);
logStep('staff.profile.benefits.history.ok', { count: benefitHistory.items.length });
const timeCard = await apiCall(`/staff/profile/time-card?month=${new Date().getUTCMonth() + 1}&year=${new Date().getUTCFullYear()}`, {
token: staffAuth.idToken,
});
@@ -1168,6 +1207,59 @@ async function main() {
});
logStep('staff.shifts.request-swap.ok', requestedSwap);
const blockedReview = await apiCall('/client/coverage/reviews', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('coverage-block'),
body: {
staffId: fixture.staff.ana.id,
assignmentId: fixture.assignments.completedAna.id,
rating: 2,
markAsBlocked: true,
markAsFavorite: false,
issueFlags: ['LATE_CLOCK_IN'],
feedback: 'Smoke blocked staff test',
},
});
assert.equal(blockedReview.markAsBlocked, true);
logStep('client.coverage.block.ok', blockedReview);
const blockedStaff = await apiCall('/client/coverage/blocked-staff', {
token: ownerSession.sessionToken,
});
assert.ok(blockedStaff.items.some((item) => item.staffId === fixture.staff.ana.id));
logStep('client.coverage.blocked-staff.ok', { count: blockedStaff.items.length });
const blockedApplyAttempt = await apiCall(`/staff/shifts/${blockedApplyCandidate.shiftId}/apply`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-shift-apply-blocked'),
body: {
roleId: blockedApplyCandidate.roleId,
},
allowFailure: true,
});
assert.equal(blockedApplyAttempt.statusCode, 409);
assert.equal(blockedApplyAttempt.body?.code, 'STAFF_BLOCKED');
logStep('staff.shifts.apply-blocked.ok', blockedApplyAttempt.body);
const unblockedReview = await apiCall('/client/coverage/reviews', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('coverage-unblock'),
body: {
staffId: fixture.staff.ana.id,
assignmentId: fixture.assignments.completedAna.id,
rating: 5,
markAsBlocked: false,
markAsFavorite: true,
issueFlags: [],
feedback: 'Smoke unblock cleanup',
},
});
assert.equal(unblockedReview.markAsBlocked, false);
logStep('client.coverage.unblock.ok', unblockedReview);
const uploadedProfilePhoto = await uploadFile('/staff/profile/photo', staffAuth.idToken, {
filename: 'profile-photo.jpg',
contentType: 'image/jpeg',