mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-09-07 21:15:42 +00:00
backoffice
This commit is contained in:
83
libs/ui/src/lib/data/WaitingFor.tsx
Normal file
83
libs/ui/src/lib/data/WaitingFor.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Text, Tooltip } from '@mantine/core';
|
||||
import { statusToneVars } from '@ema-platform/shared';
|
||||
|
||||
export interface WaitingForProps {
|
||||
/** When the clock started — normally the moment the applicant submitted. */
|
||||
since: string | null | undefined;
|
||||
/**
|
||||
* Days after which the wait reads as overdue. Amber at half of it, red past
|
||||
* it. Defaults to 14, which is the shortest SLA any configured licence type
|
||||
* currently uses; pass the real one where a queue knows it.
|
||||
*/
|
||||
slaDays?: number;
|
||||
/** Set once the item is decided — a closed item is not waiting for anyone. */
|
||||
done?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fraction of the target elapsed before the wait reads as at-risk. Matches the
|
||||
* licence queue's own `WARNING_RATIO`, so a queue using this component and one
|
||||
* using `computeSla` turn amber at the same point rather than disagreeing.
|
||||
*/
|
||||
const WARNING_RATIO = 0.7;
|
||||
|
||||
/** Whole days between `since` and now, floored. Negative clock skew reads as 0. */
|
||||
function daysSince(since: string): number {
|
||||
const ms = Date.now() - new Date(since).getTime();
|
||||
return Math.max(0, Math.floor(ms / 86_400_000));
|
||||
}
|
||||
|
||||
/**
|
||||
* How long an item has been sitting in a queue.
|
||||
*
|
||||
* No review queue showed this. An officer opening a list of thirty
|
||||
* registrations could see what each one *was*, but not which had been waiting
|
||||
* three days and which had been waiting three weeks — so the queue was worked
|
||||
* top-down by whatever the sort happened to be rather than by urgency.
|
||||
*
|
||||
* Colour comes from the platform's status tones rather than its own scale, so
|
||||
* "overdue" here is the same red as "rejected" everywhere else.
|
||||
*/
|
||||
export function WaitingFor({ since, slaDays = 14, done }: WaitingForProps) {
|
||||
if (!since) {
|
||||
return (
|
||||
<Text size="sm" c="dimmed">
|
||||
—
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const days = daysSince(since);
|
||||
|
||||
// A decided item keeps its elapsed time visible — useful when reviewing how
|
||||
// long something took — but never coloured, because nothing is pending.
|
||||
const tone = done
|
||||
? 'neutral'
|
||||
: days >= slaDays
|
||||
? 'danger'
|
||||
: days >= slaDays * WARNING_RATIO
|
||||
? 'pending'
|
||||
: 'neutral';
|
||||
|
||||
const label = days === 0 ? 'today' : `${days}d`;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
label={
|
||||
done
|
||||
? `Took ${days} day${days === 1 ? '' : 's'}`
|
||||
: `Waiting ${days} day${days === 1 ? '' : 's'} · ${slaDays}-day target`
|
||||
}
|
||||
withArrow
|
||||
>
|
||||
<Text
|
||||
size="sm"
|
||||
fw={tone === 'neutral' ? 400 : 600}
|
||||
c={tone === 'neutral' ? 'dimmed' : undefined}
|
||||
style={tone === 'neutral' ? undefined : { color: statusToneVars(tone).color }}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user