Skip to content

Commit 5203b74

Browse files
authored
Add list adapter recipes for all google integrations (#1266)
1 parent 60b9035 commit 5203b74

File tree

3 files changed

+763
-0
lines changed

3 files changed

+763
-0
lines changed

recipes/calendar-list.tsx

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import {
2+
derive,
3+
h,
4+
ifElse,
5+
JSONSchema,
6+
NAME,
7+
recipe,
8+
str,
9+
UI,
10+
} from "commontools";
11+
12+
// Reuse calendar event schema from gcal.tsx
13+
const CalendarEventSchema = {
14+
type: "object",
15+
properties: {
16+
id: { type: "string" },
17+
summary: { type: "string", default: "" },
18+
description: { type: "string", default: "" },
19+
start: { type: "string" },
20+
end: { type: "string" },
21+
location: { type: "string", default: "" },
22+
eventType: { type: "string", default: "" },
23+
hangoutLink: { type: "string", default: "" },
24+
attendees: {
25+
type: "array",
26+
items: {
27+
type: "object",
28+
properties: {
29+
id: { type: "string" },
30+
email: { type: "string" },
31+
displayName: { type: "string" },
32+
organizer: { type: "boolean" },
33+
self: { type: "boolean" },
34+
resource: { type: "boolean" },
35+
optional: { type: "boolean" },
36+
responseStatus: { type: "string" },
37+
comment: { type: "string" },
38+
additionalGuests: { type: "integer" },
39+
},
40+
required: ["email"],
41+
},
42+
default: [],
43+
},
44+
},
45+
required: [
46+
"id",
47+
"start",
48+
"end",
49+
"summary",
50+
"description",
51+
"location",
52+
"eventType",
53+
"hangoutLink",
54+
"attendees",
55+
],
56+
} as const satisfies JSONSchema;
57+
58+
// Define the list item schema that matches the general pattern
59+
const ListItemSchema = {
60+
type: "object",
61+
properties: {
62+
title: {
63+
type: "string",
64+
description: "The title of the list item",
65+
},
66+
// Include the original event as metadata
67+
event: CalendarEventSchema,
68+
},
69+
required: ["title", "event"],
70+
} as const satisfies JSONSchema;
71+
72+
// Input Schema
73+
const CalendarListInputSchema = {
74+
type: "object",
75+
properties: {
76+
events: {
77+
type: "array",
78+
items: CalendarEventSchema,
79+
default: [],
80+
},
81+
},
82+
required: ["events"],
83+
description:
84+
"Calendar List - Transforms calendar events into a standard list format",
85+
} as const satisfies JSONSchema;
86+
87+
// Output Schema
88+
const CalendarListOutputSchema = {
89+
type: "object",
90+
properties: {
91+
title: {
92+
type: "string",
93+
description: "Title of the list",
94+
},
95+
items: {
96+
type: "array",
97+
items: ListItemSchema,
98+
description: "List items with title field",
99+
},
100+
},
101+
required: ["title", "items"],
102+
} as const satisfies JSONSchema;
103+
104+
// Helper function to format date/time
105+
function formatDateTime(
106+
dateStr: string,
107+
format: string,
108+
includeTime: boolean,
109+
): string {
110+
if (!dateStr) return "";
111+
112+
try {
113+
const date = new Date(dateStr);
114+
115+
if (format === "iso") {
116+
return includeTime
117+
? date.toISOString()
118+
: date.toISOString().split("T")[0];
119+
}
120+
121+
const options: Intl.DateTimeFormatOptions = {
122+
year: format === "long" ? "numeric" : "2-digit",
123+
month: format === "long" ? "long" : "short",
124+
day: "numeric",
125+
};
126+
127+
if (includeTime) {
128+
options.hour = "numeric";
129+
options.minute = "2-digit";
130+
}
131+
132+
return date.toLocaleString("en-US", options);
133+
} catch {
134+
return dateStr;
135+
}
136+
}
137+
138+
export default recipe(
139+
CalendarListInputSchema,
140+
CalendarListOutputSchema,
141+
({ events }) => {
142+
// Transform calendar events into list items with title field
143+
// NOTE(@bf): without derive I get a "Error loading and compiling recipe: Error: Can't read value during recipe creation."
144+
const items = derive(events, (evts) =>
145+
evts.map((event) => {
146+
return {
147+
title: event.summary || event.description,
148+
event, // Include full event as metadata
149+
};
150+
}));
151+
152+
// Count events
153+
const eventCount = derive(events, (events) => events?.length || 0);
154+
155+
// Create list title
156+
const listTitle = derive(
157+
eventCount,
158+
(count) => `Calendar List (${count} events)`,
159+
);
160+
161+
return {
162+
[NAME]: listTitle,
163+
[UI]: (
164+
<os-container>
165+
<h2>Calendar List</h2>
166+
167+
<div>
168+
<p>
169+
Transforms calendar events into a standard list format with a
170+
"title" field for compatibility with other list-based recipes.
171+
</p>
172+
</div>
173+
174+
<div>
175+
<h3>Transformed Items ({eventCount})</h3>
176+
<table>
177+
<thead>
178+
<tr>
179+
<th>Title</th>
180+
<th>Original Summary</th>
181+
<th>Start</th>
182+
<th>End</th>
183+
<th>Location</th>
184+
</tr>
185+
</thead>
186+
<tbody>
187+
{items.map((item) => (
188+
<tr>
189+
<td>{item.title}</td>
190+
<td>{str`${item.event.summary || "Untitled"}`}</td>
191+
<td>{str`${item.event.start}`}</td>
192+
<td>{str`${item.event.end}`}</td>
193+
<td>{str`${item.event.location || "-"}`}</td>
194+
</tr>
195+
))}
196+
</tbody>
197+
</table>
198+
</div>
199+
</os-container>
200+
),
201+
title: listTitle,
202+
items,
203+
};
204+
},
205+
);

0 commit comments

Comments
 (0)