feat(scheduling): prioritize request worklists
This commit is contained in:
111
webui/tests/scheduling-view-model.test.ts
Normal file
111
webui/tests/scheduling-view-model.test.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import type { SchedulingRequest } from "../src/api/scheduling.ts";
|
||||
import {
|
||||
groupSchedulingRequests,
|
||||
schedulingSortPhase,
|
||||
type SchedulingActor
|
||||
} from "../src/features/scheduling/schedulingViewModel.ts";
|
||||
|
||||
const now = new Date("2026-07-20T10:00:00Z");
|
||||
const actor: SchedulingActor = {
|
||||
accountId: "account-1",
|
||||
membershipId: "membership-1",
|
||||
email: "person@example.test"
|
||||
};
|
||||
|
||||
function request(
|
||||
id: string,
|
||||
options: {
|
||||
organizer?: string;
|
||||
participantStatus?: string;
|
||||
status?: SchedulingRequest["status"];
|
||||
start?: string;
|
||||
end?: string;
|
||||
} = {}
|
||||
): SchedulingRequest {
|
||||
return {
|
||||
id,
|
||||
tenant_id: "tenant-1",
|
||||
title: id,
|
||||
timezone: "UTC",
|
||||
status: options.status ?? "collecting",
|
||||
organizer_user_id: options.organizer ?? "organizer-elsewhere",
|
||||
allow_external_participants: true,
|
||||
allow_participant_updates: true,
|
||||
result_visibility: "after_close",
|
||||
calendar_integration_enabled: false,
|
||||
calendar_freebusy_enabled: false,
|
||||
calendar_hold_enabled: false,
|
||||
create_calendar_event_on_decision: false,
|
||||
created_at: "2026-07-01T00:00:00Z",
|
||||
updated_at: "2026-07-01T00:00:00Z",
|
||||
metadata: {},
|
||||
slots: [{
|
||||
id: `slot-${id}`,
|
||||
label: id,
|
||||
start_at: options.start ?? "2026-07-21T09:00:00Z",
|
||||
end_at: options.end ?? "2026-07-21T10:00:00Z",
|
||||
timezone: "UTC",
|
||||
position: 0,
|
||||
freebusy_conflicts: [],
|
||||
metadata: {}
|
||||
}],
|
||||
participants: options.participantStatus ? [{
|
||||
id: `participant-${id}`,
|
||||
respondent_id: "membership-1",
|
||||
email: "person@example.test",
|
||||
participant_type: "internal",
|
||||
required: true,
|
||||
status: options.participantStatus,
|
||||
metadata: {}
|
||||
}] : []
|
||||
};
|
||||
}
|
||||
|
||||
test("groups owned, invited, and administrator-only requests without hiding any", () => {
|
||||
const groups = groupSchedulingRequests([
|
||||
request("managed"),
|
||||
request("mine", { organizer: "account-1" }),
|
||||
request("invited", { participantStatus: "invited" })
|
||||
], actor, now);
|
||||
|
||||
assert.deepEqual(groups.owned.map((item) => item.id), ["mine"]);
|
||||
assert.deepEqual(groups.invited.map((item) => item.id), ["invited"]);
|
||||
assert.deepEqual(groups.other.map((item) => item.id), ["managed"]);
|
||||
});
|
||||
|
||||
test("keeps an organizer's active request in the open phase even if they also responded", () => {
|
||||
const owned = request("mine-and-invited", {
|
||||
organizer: "account-1",
|
||||
participantStatus: "responded"
|
||||
});
|
||||
|
||||
assert.equal(schedulingSortPhase(owned, actor, now), "unanswered");
|
||||
});
|
||||
|
||||
test("orders unanswered by nearest slot before answered, closed, determined, and past", () => {
|
||||
const groups = groupSchedulingRequests([
|
||||
request("past", {
|
||||
participantStatus: "invited",
|
||||
start: "2026-07-18T09:00:00Z",
|
||||
end: "2026-07-18T10:00:00Z"
|
||||
}),
|
||||
request("determined", { participantStatus: "invited", status: "decided" }),
|
||||
request("closed", { participantStatus: "invited", status: "closed" }),
|
||||
request("answered", { participantStatus: "responded" }),
|
||||
request("later", { participantStatus: "invited", start: "2026-07-23T09:00:00Z" }),
|
||||
request("nearer", { participantStatus: "invited", start: "2026-07-21T09:00:00Z" })
|
||||
], actor, now);
|
||||
|
||||
assert.deepEqual(groups.invited.map((item) => item.id), [
|
||||
"nearer",
|
||||
"later",
|
||||
"answered",
|
||||
"closed",
|
||||
"determined",
|
||||
"past"
|
||||
]);
|
||||
assert.equal(schedulingSortPhase(groups.invited[0], actor, now), "unanswered");
|
||||
assert.equal(schedulingSortPhase(groups.invited.at(-1)!, actor, now), "past");
|
||||
});
|
||||
Reference in New Issue
Block a user