feat(gui-v2): statusSeverity SoT map + bidirectional §8.X consistency test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
59
apps/app/src/components-v2/shared/statusSeverity.ts
Normal file
59
apps/app/src/components-v2/shared/statusSeverity.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// apps/app/src/components-v2/shared/statusSeverity.ts
|
||||||
|
// Single source of truth for status → PrimeVue Tag severity.
|
||||||
|
// Seeded VERBATIM from design spec §8 (commit ae0bd2da). Do NOT
|
||||||
|
// reinterpret here — a wrong severity is a spec-amendment process,
|
||||||
|
// not an edit to this file. Enforced bidirectionally by
|
||||||
|
// tests/unit/utils/statusSeverity.consistency.spec.ts (spec §8.X).
|
||||||
|
|
||||||
|
export type TagSeverity = 'success' | 'warn' | 'info' | 'secondary' | 'danger'
|
||||||
|
|
||||||
|
export const STATUS_SEVERITY: Readonly<Record<string, TagSeverity>> = Object.freeze({
|
||||||
|
// success — terminal-good / fully settled
|
||||||
|
approved: 'success',
|
||||||
|
completed: 'success',
|
||||||
|
confirmed: 'success',
|
||||||
|
contracted: 'success',
|
||||||
|
paid_in_full: 'success',
|
||||||
|
|
||||||
|
// warn — organizer action required
|
||||||
|
pending_approval: 'warn',
|
||||||
|
pending: 'warn',
|
||||||
|
applied: 'warn',
|
||||||
|
option: 'warn',
|
||||||
|
offered: 'warn',
|
||||||
|
reverted: 'warn',
|
||||||
|
|
||||||
|
// info — awaiting external party / in-progress, no viewer action
|
||||||
|
invited: 'info',
|
||||||
|
requested: 'info',
|
||||||
|
deposit_paid: 'info',
|
||||||
|
|
||||||
|
// secondary — muted: absent / not-yet-live / archived
|
||||||
|
none: 'secondary',
|
||||||
|
draft: 'secondary',
|
||||||
|
dismissed: 'secondary',
|
||||||
|
|
||||||
|
// danger — terminal-bad
|
||||||
|
rejected: 'danger',
|
||||||
|
cancelled: 'danger',
|
||||||
|
declined: 'danger',
|
||||||
|
no_show: 'danger',
|
||||||
|
})
|
||||||
|
|
||||||
|
const FALLBACK: TagSeverity = 'info'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a status string to a Tag severity. The §8.X consistency
|
||||||
|
* test guarantees every live enum value is mapped, so the fallback
|
||||||
|
* is unreachable in a passing build; it exists only as defence in
|
||||||
|
* depth and emits a dev-only console warning so any gap is loud.
|
||||||
|
*/
|
||||||
|
export function statusSeverity(status: string): TagSeverity {
|
||||||
|
const hit = STATUS_SEVERITY[status]
|
||||||
|
if (hit)
|
||||||
|
return hit
|
||||||
|
if (import.meta.env.DEV)
|
||||||
|
console.warn(`[statusSeverity] unmapped status "${status}" — falling back to "${FALLBACK}". Add a §8 row + extend the §8.X test.`)
|
||||||
|
|
||||||
|
return FALLBACK
|
||||||
|
}
|
||||||
30
apps/app/tests/unit/utils/statusSeverity.consistency.spec.ts
Normal file
30
apps/app/tests/unit/utils/statusSeverity.consistency.spec.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// apps/app/tests/unit/utils/statusSeverity.consistency.spec.ts
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { ShiftAssignmentStatus } from '@/types/shiftAssignment'
|
||||||
|
import { PersonStatus } from '@/types/person'
|
||||||
|
import { MatchStatus } from '@/types/identityMatch'
|
||||||
|
import { ArtistEngagementStatus, PaymentStatus } from '@/types/timetable'
|
||||||
|
import { STATUS_SEVERITY, statusSeverity } from '@/components-v2/shared/statusSeverity'
|
||||||
|
|
||||||
|
const ENUMS = { ShiftAssignmentStatus, ArtistEngagementStatus, PaymentStatus, PersonStatus, MatchStatus }
|
||||||
|
const ALL_VALUES = Object.values(ENUMS).flatMap(e => Object.values(e)) as string[]
|
||||||
|
|
||||||
|
describe('statusSeverity §8.X enforcement', () => {
|
||||||
|
it('completeness: every live enum value resolves to an explicit severity (no dev-fallback)', () => {
|
||||||
|
const missing = ALL_VALUES.filter(v => !(v in STATUS_SEVERITY))
|
||||||
|
|
||||||
|
expect(missing, `unmapped enum values: ${missing.join(', ')}`).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('no phantoms: every map key exists in at least one live enum', () => {
|
||||||
|
const orphans = Object.keys(STATUS_SEVERITY).filter(k => !ALL_VALUES.includes(k))
|
||||||
|
|
||||||
|
expect(orphans, `orphan keys: ${orphans.join(', ')}`).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resolver returns the mapped severity and never throws on a known value', () => {
|
||||||
|
expect(statusSeverity('approved')).toBe('success')
|
||||||
|
expect(statusSeverity('no_show')).toBe('danger')
|
||||||
|
expect(statusSeverity('deposit_paid')).toBe('info')
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user