Skip to content

Commit 9868239

Browse files
oskarbrueningclaude
andcommitted
feat: fall back to regular resourceAllocations for timeslots
Timeslot queries now select both inheritedResourceAllocations and resourceAllocations. The converter prefers inherited allocations, but falls back to the regular resourceAllocations when the inherited field is null or an empty array. The existing ACTIVE-only status filter is applied to whichever source is selected. The shared TimeslotResourceAllocationNode gained an optional allocation id (present only on the inherited variant) and resourcePool.id, matching the requested GraphQL selection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ed128e7 commit 9868239

3 files changed

Lines changed: 124 additions & 2 deletions

File tree

src/internal/peek/timeslots/timeslot-converter.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,28 @@ export function fromTimeslotNode(
5555
durationMin: node.minuteLength ?? 0,
5656
date: node.date || "",
5757
startTime: node.start ?? null,
58-
assignedResources: mapAssignedResources(node.inheritedResourceAllocations),
58+
assignedResources: mapAssignedResources(selectResourceAllocations(node)),
5959
};
6060
}
6161

6262
/** Only allocations with this status are considered assigned. */
6363
const ACTIVE_ALLOCATION_STATUS = "ACTIVE";
6464

65+
/**
66+
* Timeslots may carry both inherited (schedule-level) and directly-assigned
67+
* resource allocations. Prefer inherited; fall back to the regular allocations
68+
* only when the inherited field is absent or empty.
69+
*/
70+
function selectResourceAllocations(
71+
node: TimeslotNode,
72+
): TimeslotResourceAllocationNode[] | null | undefined {
73+
const inherited = node.inheritedResourceAllocations;
74+
if (Array.isArray(inherited) && inherited.length > 0) {
75+
return inherited;
76+
}
77+
return node.resourceAllocations;
78+
}
79+
6580
function mapAssignedResources(
6681
allocations: TimeslotResourceAllocationNode[] | null | undefined,
6782
): AssignedResource[] {

src/internal/peek/timeslots/timeslot-queries.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,24 @@ export const TIMESLOTS_QUERY = `
2121
status
2222
date
2323
inheritedResourceAllocations {
24+
id
25+
status
26+
quantity
27+
resourcePool {
28+
id
29+
name
30+
category
31+
capacity
32+
accountUser {
33+
id
34+
}
35+
}
36+
}
37+
resourceAllocations {
2438
quantity
2539
status
2640
resourcePool {
41+
id
2742
name
2843
category
2944
capacity
@@ -61,9 +76,24 @@ export const TIMESLOT_BY_ID_QUERY = `
6176
status
6277
date
6378
inheritedResourceAllocations {
79+
id
80+
status
81+
quantity
82+
resourcePool {
83+
id
84+
name
85+
category
86+
capacity
87+
accountUser {
88+
id
89+
}
90+
}
91+
}
92+
resourceAllocations {
6493
quantity
6594
status
6695
resourcePool {
96+
id
6797
name
6898
category
6999
capacity
@@ -99,11 +129,17 @@ export const UPDATE_TIMESLOT_MUTATION = `
99129
}
100130
`;
101131

102-
/** A single inherited resource allocation on a timeslot node. */
132+
/**
133+
* A single resource allocation on a timeslot node. Shared by both the
134+
* `inheritedResourceAllocations` and `resourceAllocations` selections; the
135+
* allocation-level `id` is only present on the inherited variant.
136+
*/
103137
export interface TimeslotResourceAllocationNode {
138+
id?: string | null;
104139
quantity: number | null;
105140
status: string | null;
106141
resourcePool: {
142+
id: string;
107143
name: string;
108144
category: string;
109145
capacity: number | null;
@@ -125,6 +161,7 @@ export interface TimeslotNode {
125161
date: string | null;
126162
start?: string | null;
127163
inheritedResourceAllocations: TimeslotResourceAllocationNode[] | null;
164+
resourceAllocations: TimeslotResourceAllocationNode[] | null;
128165
}
129166

130167
/** `data` payload of {@link TIMESLOTS_QUERY}. */

test/peek/timeslots/timeslot-converter.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ const fullNode: TimeslotNode = {
2323
quantity: 1,
2424
status: "ACTIVE",
2525
resourcePool: {
26+
id: "rp-1",
2627
name: "Ada",
2728
category: "guide",
2829
capacity: 5,
2930
accountUser: { id: "u1" },
3031
},
3132
},
3233
],
34+
resourceAllocations: null,
3335
};
3436

3537
describe("fromTimeslotNode", () => {
@@ -116,6 +118,74 @@ describe("fromTimeslotNode", () => {
116118
expect(fromTimeslotNode(node, "act-1").assignedResources).toEqual([]);
117119
});
118120

121+
it("falls back to resourceAllocations when inherited is null", () => {
122+
const node = {
123+
...fullNode,
124+
inheritedResourceAllocations: null,
125+
resourceAllocations: [
126+
{
127+
quantity: 2,
128+
status: "ACTIVE",
129+
resourcePool: {
130+
id: "rp-2",
131+
name: "Bob",
132+
category: "guide",
133+
capacity: 3,
134+
accountUser: { id: "u2" },
135+
},
136+
},
137+
],
138+
} as unknown as TimeslotNode;
139+
const { assignedResources } = fromTimeslotNode(node, "act-1");
140+
expect(assignedResources).toEqual([
141+
{ name: "Bob", capacity: 3, category: "guide", quantity: 2, accountUserId: "u2" },
142+
]);
143+
});
144+
145+
it("falls back to resourceAllocations when inherited is an empty array", () => {
146+
const node = {
147+
...fullNode,
148+
inheritedResourceAllocations: [],
149+
resourceAllocations: [
150+
{
151+
quantity: 1,
152+
status: "ACTIVE",
153+
resourcePool: {
154+
id: "rp-2",
155+
name: "Bob",
156+
category: "guide",
157+
capacity: 3,
158+
accountUser: { id: "u2" },
159+
},
160+
},
161+
],
162+
} as unknown as TimeslotNode;
163+
expect(fromTimeslotNode(node, "act-1").assignedResources).toHaveLength(1);
164+
expect(fromTimeslotNode(node, "act-1").assignedResources[0].name).toBe("Bob");
165+
});
166+
167+
it("prefers inherited over regular resourceAllocations when both are present", () => {
168+
const node = {
169+
...fullNode,
170+
resourceAllocations: [
171+
{
172+
quantity: 9,
173+
status: "ACTIVE",
174+
resourcePool: {
175+
id: "rp-2",
176+
name: "Bob",
177+
category: "guide",
178+
capacity: 3,
179+
accountUser: { id: "u2" },
180+
},
181+
},
182+
],
183+
} as unknown as TimeslotNode;
184+
const { assignedResources } = fromTimeslotNode(node, "act-1");
185+
expect(assignedResources).toHaveLength(1);
186+
expect(assignedResources[0].name).toBe("Ada");
187+
});
188+
119189
it("filters out allocations whose status is not ACTIVE", () => {
120190
const node = {
121191
...fullNode,

0 commit comments

Comments
 (0)