@rubytech/create-maxy-code 0.1.346 → 0.1.347

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
  3. package/payload/platform/plugins/docs/references/calendar-booking.md +1 -1
  4. package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
  5. package/payload/server/public/assets/{AdminLoginScreens-CpE_Nf5z.js → AdminLoginScreens-GELNKMk6.js} +1 -1
  6. package/payload/server/public/assets/{AdminShell-D3N3Aq04.js → AdminShell-C_KsjYH6.js} +1 -1
  7. package/payload/server/public/assets/{Checkbox-DQGuYIRH.js → Checkbox-iE6iVP7-.js} +1 -1
  8. package/payload/server/public/assets/{OperatorConversations-DLfQEw1r.css → OperatorConversations-C4Cx814-.css} +1 -1
  9. package/payload/server/public/assets/{OperatorConversations-g7aAQCDn.js → OperatorConversations-DAPefQEH.js} +1 -1
  10. package/payload/server/public/assets/{admin-BU0dY3_T.js → admin-Ci2tLhyx.js} +1 -1
  11. package/payload/server/public/assets/{browser-B5ytGJAE.js → browser-9dxtgr6y.js} +1 -1
  12. package/payload/server/public/assets/calendar-BmwMyHcQ.js +1 -0
  13. package/payload/server/public/assets/chat-CnG7zUb7.js +1 -0
  14. package/payload/server/public/assets/{data-BVPOzSZ7.js → data-BpTErRH6.js} +1 -1
  15. package/payload/server/public/assets/{graph-D0fPx1nX.js → graph-DfQblprL.js} +1 -1
  16. package/payload/server/public/assets/{graph-labels-C4dkheQ8.js → graph-labels-Bjyky6oK.js} +1 -1
  17. package/payload/server/public/assets/{operator-DyrF6PQi.js → operator-CHSf3dma.js} +1 -1
  18. package/payload/server/public/assets/page-uweci4TA.js +30 -0
  19. package/payload/server/public/assets/{public-BrIaS6m3.js → public-ZdqJjwT3.js} +1 -1
  20. package/payload/server/public/browser.html +4 -4
  21. package/payload/server/public/calendar.html +4 -4
  22. package/payload/server/public/chat.html +5 -5
  23. package/payload/server/public/data.html +4 -4
  24. package/payload/server/public/graph.html +6 -6
  25. package/payload/server/public/index.html +6 -6
  26. package/payload/server/public/operator.html +7 -7
  27. package/payload/server/public/public.html +5 -5
  28. package/payload/server/server.js +278 -29
  29. package/payload/server/public/assets/calendar-CgVgCBhg.js +0 -1
  30. package/payload/server/public/assets/chat-cxTK--65.js +0 -1
  31. package/payload/server/public/assets/page-BPIaNNW1.js +0 -30
@@ -5,14 +5,14 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Chat — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/chat-cxTK--65.js"></script>
8
+ <script type="module" crossorigin src="/assets/chat-CnG7zUb7.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
10
- <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-g7aAQCDn.js">
11
- <link rel="modulepreload" crossorigin href="/assets/AdminShell-D3N3Aq04.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-DAPefQEH.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/AdminShell-C_KsjYH6.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/admin-types-Dg11L4MQ.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-uTix4PVD.js">
14
- <link rel="modulepreload" crossorigin href="/assets/page-BPIaNNW1.js">
15
- <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-DLfQEw1r.css">
14
+ <link rel="modulepreload" crossorigin href="/assets/page-uweci4TA.js">
15
+ <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-C4Cx814-.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="root"></div>
@@ -5,11 +5,11 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Data — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/data-BVPOzSZ7.js"></script>
8
+ <script type="module" crossorigin src="/assets/data-BpTErRH6.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
10
- <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-g7aAQCDn.js">
11
- <link rel="modulepreload" crossorigin href="/assets/graph-labels-C4dkheQ8.js">
12
- <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-DLfQEw1r.css">
10
+ <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-DAPefQEH.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/graph-labels-Bjyky6oK.js">
12
+ <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-C4Cx814-.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -5,13 +5,13 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Graph — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/graph-D0fPx1nX.js"></script>
8
+ <script type="module" crossorigin src="/assets/graph-DfQblprL.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
10
- <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-g7aAQCDn.js">
11
- <link rel="modulepreload" crossorigin href="/assets/graph-labels-C4dkheQ8.js">
12
- <link rel="modulepreload" crossorigin href="/assets/AdminShell-D3N3Aq04.js">
13
- <link rel="modulepreload" crossorigin href="/assets/Checkbox-DQGuYIRH.js">
14
- <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-DLfQEw1r.css">
10
+ <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-DAPefQEH.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/graph-labels-Bjyky6oK.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/AdminShell-C_KsjYH6.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/Checkbox-iE6iVP7-.js">
14
+ <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-C4Cx814-.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -5,14 +5,14 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/admin-BU0dY3_T.js"></script>
8
+ <script type="module" crossorigin src="/assets/admin-Ci2tLhyx.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
10
- <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-g7aAQCDn.js">
11
- <link rel="modulepreload" crossorigin href="/assets/AdminShell-D3N3Aq04.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-DAPefQEH.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/AdminShell-C_KsjYH6.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/admin-types-Dg11L4MQ.js">
13
- <link rel="modulepreload" crossorigin href="/assets/Checkbox-DQGuYIRH.js">
14
- <link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-CpE_Nf5z.js">
15
- <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-DLfQEw1r.css">
13
+ <link rel="modulepreload" crossorigin href="/assets/Checkbox-iE6iVP7-.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-GELNKMk6.js">
15
+ <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-C4Cx814-.css">
16
16
  <link rel="stylesheet" crossorigin href="/assets/AdminLoginScreens-CWMpccrR.css">
17
17
  <link rel="stylesheet" href="/brand-defaults.css">
18
18
  </head>
@@ -5,16 +5,16 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Operator — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/operator-DyrF6PQi.js"></script>
8
+ <script type="module" crossorigin src="/assets/operator-CHSf3dma.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
10
- <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-g7aAQCDn.js">
11
- <link rel="modulepreload" crossorigin href="/assets/AdminShell-D3N3Aq04.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-DAPefQEH.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/AdminShell-C_KsjYH6.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/admin-types-Dg11L4MQ.js">
13
- <link rel="modulepreload" crossorigin href="/assets/Checkbox-DQGuYIRH.js">
14
- <link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-CpE_Nf5z.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/Checkbox-iE6iVP7-.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-GELNKMk6.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-uTix4PVD.js">
16
- <link rel="modulepreload" crossorigin href="/assets/page-BPIaNNW1.js">
17
- <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-DLfQEw1r.css">
16
+ <link rel="modulepreload" crossorigin href="/assets/page-uweci4TA.js">
17
+ <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-C4Cx814-.css">
18
18
  <link rel="stylesheet" crossorigin href="/assets/AdminLoginScreens-CWMpccrR.css">
19
19
  </head>
20
20
  <body>
@@ -5,14 +5,14 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/public-BrIaS6m3.js"></script>
8
+ <script type="module" crossorigin src="/assets/public-ZdqJjwT3.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
10
- <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-g7aAQCDn.js">
11
- <link rel="modulepreload" crossorigin href="/assets/AdminShell-D3N3Aq04.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/OperatorConversations-DAPefQEH.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/AdminShell-C_KsjYH6.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/admin-types-Dg11L4MQ.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-uTix4PVD.js">
14
- <link rel="modulepreload" crossorigin href="/assets/page-BPIaNNW1.js">
15
- <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-DLfQEw1r.css">
14
+ <link rel="modulepreload" crossorigin href="/assets/page-uweci4TA.js">
15
+ <link rel="stylesheet" crossorigin href="/assets/OperatorConversations-C4Cx814-.css">
16
16
  <link rel="stylesheet" href="/brand-defaults.css">
17
17
  </head>
18
18
  <body class="public-surface">
@@ -17580,6 +17580,102 @@ app42.post("/launch", requireAdminSession, async (c) => {
17580
17580
  });
17581
17581
  var browser_default = app42;
17582
17582
 
17583
+ // server/lib/calendar-ics.ts
17584
+ var CRLF = "\r\n";
17585
+ var PRODID = "-//Maxy//Calendar//EN";
17586
+ var MAX_LINE_OCTETS = 75;
17587
+ function escapeText(value) {
17588
+ return value.replace(/\\/g, "\\\\").replace(/;/g, "\\;").replace(/,/g, "\\,").replace(/\r?\n/g, "\\n");
17589
+ }
17590
+ function foldLine(line) {
17591
+ const buf = Buffer.from(line, "utf-8");
17592
+ if (buf.length <= MAX_LINE_OCTETS) return line;
17593
+ const parts = [];
17594
+ let offset = 0;
17595
+ let first = true;
17596
+ while (offset < buf.length) {
17597
+ const maxChunk = first ? MAX_LINE_OCTETS : MAX_LINE_OCTETS - 1;
17598
+ let end = Math.min(offset + maxChunk, buf.length);
17599
+ while (end < buf.length && end > offset && (buf[end] & 192) === 128) end--;
17600
+ const chunk = buf.subarray(offset, end).toString("utf-8");
17601
+ parts.push(first ? chunk : " " + chunk);
17602
+ offset = end;
17603
+ first = false;
17604
+ }
17605
+ return parts.join(CRLF);
17606
+ }
17607
+ function toICSDateTime(iso) {
17608
+ const d = new Date(iso);
17609
+ if (isNaN(d.getTime())) throw new Error(`Invalid date: ${iso}`);
17610
+ const pad = (n) => String(n).padStart(2, "0");
17611
+ return `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}T${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}Z`;
17612
+ }
17613
+ function toICSDate(iso) {
17614
+ const d = new Date(iso);
17615
+ if (isNaN(d.getTime())) throw new Error(`Invalid date: ${iso}`);
17616
+ const pad = (n) => String(n).padStart(2, "0");
17617
+ return `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}`;
17618
+ }
17619
+ function attendeeLine(a) {
17620
+ if (!a.email) return null;
17621
+ const cn = a.name ? `;CN=${escapeText(a.name)}` : "";
17622
+ return `ATTENDEE${cn}:mailto:${a.email}`;
17623
+ }
17624
+ function serializeMeetingICS(m, opts) {
17625
+ const now = opts.now ?? /* @__PURE__ */ new Date();
17626
+ const lines = [];
17627
+ lines.push("BEGIN:VCALENDAR");
17628
+ lines.push("VERSION:2.0");
17629
+ lines.push(`PRODID:${PRODID}`);
17630
+ lines.push("CALSCALE:GREGORIAN");
17631
+ lines.push("METHOD:PUBLISH");
17632
+ lines.push("BEGIN:VEVENT");
17633
+ lines.push(`UID:${m.uid ?? m.id}`);
17634
+ lines.push(`DTSTAMP:${toICSDateTime(now.toISOString())}`);
17635
+ if (m.isAllDay) {
17636
+ lines.push(`DTSTART;VALUE=DATE:${toICSDate(m.startsAt)}`);
17637
+ if (m.endsAt) lines.push(`DTEND;VALUE=DATE:${toICSDate(m.endsAt)}`);
17638
+ } else {
17639
+ lines.push(`DTSTART:${toICSDateTime(m.startsAt)}`);
17640
+ if (m.endsAt) lines.push(`DTEND:${toICSDateTime(m.endsAt)}`);
17641
+ }
17642
+ lines.push(`SUMMARY:${escapeText(m.title ?? "")}`);
17643
+ if (m.location) lines.push(`LOCATION:${escapeText(m.location)}`);
17644
+ if (opts.includeAttendees) {
17645
+ for (const a of m.attendees) {
17646
+ const line = attendeeLine(a);
17647
+ if (line) lines.push(line);
17648
+ }
17649
+ }
17650
+ lines.push("END:VEVENT");
17651
+ lines.push("END:VCALENDAR");
17652
+ return lines.map(foldLine).join(CRLF) + CRLF;
17653
+ }
17654
+ function countVEvents(ics) {
17655
+ const unfolded = ics.replace(/\r?\n[ \t]/g, "");
17656
+ const lines = unfolded.split(/\r?\n/);
17657
+ let count = 0;
17658
+ let inEvent = false;
17659
+ let nested = 0;
17660
+ for (const raw of lines) {
17661
+ const line = raw.trim();
17662
+ if (line === "BEGIN:VEVENT") {
17663
+ inEvent = true;
17664
+ nested = 0;
17665
+ continue;
17666
+ }
17667
+ if (!inEvent) continue;
17668
+ if (line === "END:VEVENT") {
17669
+ inEvent = false;
17670
+ count++;
17671
+ continue;
17672
+ }
17673
+ if (line.startsWith("BEGIN:")) nested++;
17674
+ else if (line.startsWith("END:") && nested > 0) nested--;
17675
+ }
17676
+ return count;
17677
+ }
17678
+
17583
17679
  // server/routes/admin/calendar.ts
17584
17680
  var app43 = new Hono();
17585
17681
  function normalizeAttendees(raw) {
@@ -17592,12 +17688,26 @@ function normalizeAttendees(raw) {
17592
17688
  };
17593
17689
  });
17594
17690
  }
17595
- var EDITABLE_FIELDS = ["title", "startsAt", "endsAt", "location"];
17596
- var MEETING_RETURN = `RETURN elementId(m) AS id, m.title AS title,
17691
+ var EDITABLE_FIELDS = ["title", "startsAt", "endsAt", "location", "privateNote"];
17692
+ var MEETING_COLS = `elementId(m) AS id, m.title AS title,
17597
17693
  toString(m.startsAt) AS startsAt, toString(m.endsAt) AS endsAt,
17598
17694
  m.location AS location, coalesce(m.isAllDay, false) AS isAllDay,
17599
- 'meeting' AS kind, m.uid AS uid,
17600
- [(p:Person)-[:ATTENDED]->(m) | {name: coalesce(p.displayName, p.email), email: p.email}] AS attendees`;
17695
+ 'meeting' AS kind, m.uid AS uid, m.privateNote AS privateNote,
17696
+ [(att:Person)-[:ATTENDED]->(m) | {name: coalesce(att.displayName, att.email), email: att.email}] AS attendees`;
17697
+ var MEETING_RETURN = `RETURN ${MEETING_COLS}`;
17698
+ function mapMeeting(r) {
17699
+ return {
17700
+ meetingId: r.get("id"),
17701
+ title: r.get("title"),
17702
+ startsAt: r.get("startsAt"),
17703
+ endsAt: r.get("endsAt"),
17704
+ location: r.get("location"),
17705
+ isAllDay: r.get("isAllDay"),
17706
+ kind: r.get("kind"),
17707
+ attendees: normalizeAttendees(r.get("attendees")),
17708
+ privateNote: r.get("privateNote")
17709
+ };
17710
+ }
17601
17711
  app43.get("/meetings", requireAdminSession, async (c) => {
17602
17712
  const cacheKey = c.var.cacheKey;
17603
17713
  const accountId = getAccountIdForSession(cacheKey);
@@ -17617,7 +17727,7 @@ app43.get("/meetings", requireAdminSession, async (c) => {
17617
17727
  RETURN elementId(m) AS id, m.title AS title,
17618
17728
  toString(m.startsAt) AS startsAt, toString(m.endsAt) AS endsAt,
17619
17729
  m.location AS location, coalesce(m.isAllDay, false) AS isAllDay,
17620
- 'meeting' AS kind,
17730
+ 'meeting' AS kind, m.privateNote AS privateNote,
17621
17731
  [(p:Person)-[:ATTENDED]->(m) | {name: coalesce(p.displayName, p.email), email: p.email}] AS attendees
17622
17732
  UNION
17623
17733
  MATCH (e:Event {accountId: $accountId})
@@ -17628,7 +17738,7 @@ app43.get("/meetings", requireAdminSession, async (c) => {
17628
17738
  toString(datetime(e.startDate)) AS startsAt,
17629
17739
  CASE WHEN e.endDate IS NULL THEN null ELSE toString(datetime(e.endDate)) END AS endsAt,
17630
17740
  e.location AS location, false AS isAllDay,
17631
- 'event' AS kind,
17741
+ 'event' AS kind, null AS privateNote,
17632
17742
  [] AS attendees
17633
17743
  UNION
17634
17744
  MATCH (t:Task {accountId: $accountId})
@@ -17638,20 +17748,11 @@ app43.get("/meetings", requireAdminSession, async (c) => {
17638
17748
  RETURN elementId(t) AS id, t.name AS title,
17639
17749
  toString(datetime(t.dueDate)) AS startsAt, null AS endsAt,
17640
17750
  null AS location, true AS isAllDay,
17641
- 'task' AS kind,
17751
+ 'task' AS kind, null AS privateNote,
17642
17752
  [] AS attendees`,
17643
17753
  { accountId, from, to }
17644
17754
  );
17645
- const meetings = res.records.map((r) => ({
17646
- meetingId: r.get("id"),
17647
- title: r.get("title"),
17648
- startsAt: r.get("startsAt"),
17649
- endsAt: r.get("endsAt"),
17650
- location: r.get("location"),
17651
- isAllDay: r.get("isAllDay"),
17652
- kind: r.get("kind"),
17653
- attendees: normalizeAttendees(r.get("attendees"))
17654
- })).sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime());
17755
+ const meetings = res.records.map(mapMeeting).sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime());
17655
17756
  const counts = { meeting: 0, event: 0, task: 0 };
17656
17757
  for (const m of meetings) counts[m.kind]++;
17657
17758
  console.log(
@@ -17666,6 +17767,68 @@ app43.get("/meetings", requireAdminSession, async (c) => {
17666
17767
  await session.close();
17667
17768
  }
17668
17769
  });
17770
+ function icsFilename(title) {
17771
+ const slug = (title ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
17772
+ return `${slug || "meeting"}.ics`;
17773
+ }
17774
+ app43.get("/meetings/:id/ics", requireAdminSession, async (c) => {
17775
+ const cacheKey = c.var.cacheKey;
17776
+ const accountId = getAccountIdForSession(cacheKey);
17777
+ if (!accountId) {
17778
+ console.error('[calendar] op=ics auth-rejected reason="no account for session"');
17779
+ return c.json({ error: "Account not found for session" }, 401);
17780
+ }
17781
+ const id = c.req.param("id");
17782
+ const includeAttendees = c.req.query("attendees") === "1";
17783
+ console.log(`[calendar] op=ics-request id=${id} attendees=${includeAttendees}`);
17784
+ const session = getSession();
17785
+ try {
17786
+ const res = await session.run(
17787
+ `MATCH (m:Meeting {accountId: $accountId})
17788
+ WHERE elementId(m) = $id
17789
+ ${MEETING_RETURN}`,
17790
+ { accountId, id }
17791
+ );
17792
+ if (res.records.length === 0) {
17793
+ console.warn(`[calendar] op=ics-404 id=${id} reason="no meeting for account"`);
17794
+ return c.json({ error: "Meeting not found for this account" }, 404);
17795
+ }
17796
+ const r = res.records[0];
17797
+ const mapped = mapMeeting(r);
17798
+ const uid = r.get("uid") ?? null;
17799
+ const corr = uid ?? mapped.meetingId;
17800
+ const input = {
17801
+ uid,
17802
+ id: mapped.meetingId,
17803
+ title: mapped.title,
17804
+ startsAt: mapped.startsAt,
17805
+ endsAt: mapped.endsAt,
17806
+ location: mapped.location,
17807
+ isAllDay: mapped.isAllDay,
17808
+ attendees: mapped.attendees
17809
+ };
17810
+ console.log(`[calendar] op=ics-resolved id=${corr} allDay=${input.isAllDay} attendees=${input.attendees.length}`);
17811
+ const ics = serializeMeetingICS(input, { includeAttendees });
17812
+ const bytes = Buffer.byteLength(ics, "utf-8");
17813
+ const vevents = countVEvents(ics);
17814
+ console.log(`[calendar] op=ics-serialized id=${corr} bytes=${bytes} vevents=${vevents}`);
17815
+ if (vevents !== 1 || bytes === 0) {
17816
+ console.error(`[calendar] op=ics-malformed id=${corr} vevents=${vevents} bytes=${bytes}`);
17817
+ return c.json({ error: "Failed to produce a valid calendar file" }, 500);
17818
+ }
17819
+ console.log(`[calendar] op=ics-export id=${corr} bytes=${bytes} attendees=${includeAttendees}`);
17820
+ return c.body(ics, 200, {
17821
+ "content-type": "text/calendar; charset=utf-8",
17822
+ "content-disposition": `attachment; filename="${icsFilename(mapped.title)}"`
17823
+ });
17824
+ } catch (err) {
17825
+ const message = err instanceof Error ? err.message : String(err);
17826
+ console.error(`[calendar] op=ics-fail id=${id} err="${message}"`);
17827
+ return c.json({ error: `ICS export failed: ${message}` }, 503);
17828
+ } finally {
17829
+ await session.close();
17830
+ }
17831
+ });
17669
17832
  app43.patch("/meetings/:id", requireAdminSession, async (c) => {
17670
17833
  const cacheKey = c.var.cacheKey;
17671
17834
  const accountId = getAccountIdForSession(cacheKey);
@@ -17677,7 +17840,7 @@ app43.patch("/meetings/:id", requireAdminSession, async (c) => {
17677
17840
  const body = await safeJson(c) ?? {};
17678
17841
  const fields = EDITABLE_FIELDS.filter((f) => f in body);
17679
17842
  if (fields.length === 0) {
17680
- return c.json({ error: "No editable field supplied (title, startsAt, endsAt, location)" }, 400);
17843
+ return c.json({ error: "No editable field supplied (title, startsAt, endsAt, location, privateNote)" }, 400);
17681
17844
  }
17682
17845
  const setClauses = fields.map(
17683
17846
  (f) => f === "startsAt" || f === "endsAt" ? `m.${f} = datetime($${f})` : `m.${f} = $${f}`
@@ -17701,17 +17864,7 @@ app43.patch("/meetings/:id", requireAdminSession, async (c) => {
17701
17864
  const r = res.records[0];
17702
17865
  const uid = r.get("uid");
17703
17866
  console.log(`[calendar-edit] account=${accountId.slice(0, 8)} uid=${uid ?? "absent"} fields=${fields.join(",")}`);
17704
- const meeting = {
17705
- meetingId: r.get("id"),
17706
- title: r.get("title"),
17707
- startsAt: r.get("startsAt"),
17708
- endsAt: r.get("endsAt"),
17709
- location: r.get("location"),
17710
- isAllDay: r.get("isAllDay"),
17711
- kind: "meeting",
17712
- attendees: normalizeAttendees(r.get("attendees"))
17713
- };
17714
- return c.json({ meeting });
17867
+ return c.json({ meeting: mapMeeting(r) });
17715
17868
  } catch (err) {
17716
17869
  const message = err instanceof Error ? err.message : String(err);
17717
17870
  console.error(`[calendar-edit] op=fail err="${message}"`);
@@ -17720,6 +17873,102 @@ app43.patch("/meetings/:id", requireAdminSession, async (c) => {
17720
17873
  await session.close();
17721
17874
  }
17722
17875
  });
17876
+ app43.post("/meetings/:id/attendees", requireAdminSession, async (c) => {
17877
+ const cacheKey = c.var.cacheKey;
17878
+ const accountId = getAccountIdForSession(cacheKey);
17879
+ if (!accountId) {
17880
+ console.error('[calendar] op=attendee-add auth-rejected reason="no account for session"');
17881
+ return c.json({ error: "Account not found for session" }, 401);
17882
+ }
17883
+ const id = c.req.param("id");
17884
+ const body = await safeJson(c) ?? {};
17885
+ const email = typeof body.email === "string" && body.email.trim() ? body.email.trim() : null;
17886
+ const name = typeof body.name === "string" && body.name.trim() ? body.name.trim() : null;
17887
+ if (!email && !name) {
17888
+ return c.json({ error: "email or name required" }, 400);
17889
+ }
17890
+ const cypher = email ? `MATCH (m:Meeting {accountId: $accountId})
17891
+ WHERE elementId(m) = $id
17892
+ OPTIONAL MATCH (existing:Person {accountId: $accountId, email: $email})
17893
+ WITH m, existing IS NULL AS personCreated
17894
+ MERGE (p:Person {accountId: $accountId, email: $email})
17895
+ ON CREATE SET p.displayName = $name
17896
+ MERGE (p)-[:ATTENDED]->(m)
17897
+ WITH m, personCreated
17898
+ RETURN ${MEETING_COLS}, personCreated AS personCreated` : `MATCH (m:Meeting {accountId: $accountId})
17899
+ WHERE elementId(m) = $id
17900
+ CREATE (p:Person {accountId: $accountId, displayName: $name})
17901
+ MERGE (p)-[:ATTENDED]->(m)
17902
+ WITH m, true AS personCreated
17903
+ RETURN ${MEETING_COLS}, personCreated AS personCreated`;
17904
+ const session = getSession();
17905
+ try {
17906
+ const res = await session.run(cypher, { accountId, id, email, name });
17907
+ if (res.records.length === 0) {
17908
+ console.warn(`[calendar-attendee] op=add account=${accountId.slice(0, 8)} uid=absent email=${email ?? "none"} found=0`);
17909
+ return c.json({ error: "Meeting not found for this account" }, 404);
17910
+ }
17911
+ const r = res.records[0];
17912
+ const uid = r.get("uid");
17913
+ const personCreated = r.get("personCreated");
17914
+ console.log(
17915
+ `[calendar-attendee] op=add account=${accountId.slice(0, 8)} uid=${uid ?? "absent"} email=${email ?? "none"} personCreated=${personCreated}`
17916
+ );
17917
+ return c.json({ meeting: mapMeeting(r) });
17918
+ } catch (err) {
17919
+ const message = err instanceof Error ? err.message : String(err);
17920
+ console.error(`[calendar-attendee] op=add-fail err="${message}"`);
17921
+ return c.json({ error: `Attendee add failed: ${message}` }, 503);
17922
+ } finally {
17923
+ await session.close();
17924
+ }
17925
+ });
17926
+ app43.delete("/meetings/:id/attendees", requireAdminSession, async (c) => {
17927
+ const cacheKey = c.var.cacheKey;
17928
+ const accountId = getAccountIdForSession(cacheKey);
17929
+ if (!accountId) {
17930
+ console.error('[calendar] op=attendee-remove auth-rejected reason="no account for session"');
17931
+ return c.json({ error: "Account not found for session" }, 401);
17932
+ }
17933
+ const id = c.req.param("id");
17934
+ const body = await safeJson(c) ?? {};
17935
+ const email = typeof body.email === "string" && body.email.trim() ? body.email.trim() : null;
17936
+ const name = typeof body.name === "string" && body.name.trim() ? body.name.trim() : null;
17937
+ if (!email && !name) {
17938
+ return c.json({ error: "email or name required" }, 400);
17939
+ }
17940
+ const keyPredicate = email ? "att.email = $email" : "att.displayName = $name";
17941
+ const cypher = `MATCH (m:Meeting {accountId: $accountId})
17942
+ WHERE elementId(m) = $id
17943
+ OPTIONAL MATCH (att:Person {accountId: $accountId})-[r:ATTENDED]->(m)
17944
+ WHERE ${keyPredicate}
17945
+ DELETE r
17946
+ WITH m, count(att) AS removed, collect(att) AS survivors
17947
+ RETURN ${MEETING_COLS}, removed AS removed,
17948
+ all(s IN survivors WHERE s IS NOT NULL) AS personSurvived`;
17949
+ const session = getSession();
17950
+ try {
17951
+ const res = await session.run(cypher, { accountId, id, email, name });
17952
+ if (res.records.length === 0) {
17953
+ console.warn(`[calendar-attendee] op=remove account=${accountId.slice(0, 8)} uid=absent key=${email ?? name} found=0`);
17954
+ return c.json({ error: "Meeting not found for this account" }, 404);
17955
+ }
17956
+ const r = res.records[0];
17957
+ const uid = r.get("uid");
17958
+ const removed = Number(r.get("removed"));
17959
+ const personSurvived = r.get("personSurvived");
17960
+ console.log(
17961
+ `[calendar-attendee] op=remove account=${accountId.slice(0, 8)} uid=${uid ?? "absent"} key=${email ?? name} removed=${removed} personSurvived=${personSurvived}`
17962
+ );
17963
+ return c.json({ meeting: mapMeeting(r), removed });
17964
+ } catch (err) {
17965
+ const message = err instanceof Error ? err.message : String(err);
17966
+ console.error(`[calendar-attendee] op=remove-fail err="${message}"`);
17967
+ return c.json({ error: `Attendee remove failed: ${message}` }, 503);
17968
+ } finally {
17969
+ await session.close();
17970
+ }
17971
+ });
17723
17972
  app43.delete("/meetings/:id", requireAdminSession, async (c) => {
17724
17973
  const cacheKey = c.var.cacheKey;
17725
17974
  const accountId = getAccountIdForSession(cacheKey);
@@ -1 +0,0 @@
1
- import{o as e}from"./chunk-CAM3fms7.js";import{A as t,B as n,C as r,I as i,O as a,R as o,T as s,U as c,V as l,v as u,w as d}from"./OperatorConversations-g7aAQCDn.js";import{d as f,g as p,t as m}from"./AdminShell-D3N3Aq04.js";var h=o(`calendar`,[[`path`,{d:`M8 2v4`,key:`1cmpym`}],[`path`,{d:`M16 2v4`,key:`4m81vk`}],[`rect`,{width:`18`,height:`18`,x:`3`,y:`4`,rx:`2`,key:`1hopcy`}],[`path`,{d:`M3 10h18`,key:`8toen8`}]]),g=o(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]),_=o(`clock`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 6v6l4 2`,key:`mmk7yg`}]]),v=o(`map-pin`,[[`path`,{d:`M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0`,key:`1r0f0z`}],[`circle`,{cx:`12`,cy:`10`,r:`3`,key:`ilqhr7`}]]),y=l(),b=e(c(),1);function x(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate())}function S(e,t){let n=new Date(e);return n.setDate(n.getDate()+t),n}function C(e){let t=x(e),n=t.getDay();return S(t,n===0?-6:1-n)}function w(e){let t=C(e);return Array.from({length:7},(e,n)=>S(t,n))}function T(e){let t=C(new Date(e.getFullYear(),e.getMonth(),1));return Array.from({length:42},(e,n)=>S(t,n))}function E(){return Array.from({length:24},(e,t)=>t)}function D(e){let t=e.getFullYear();return Array.from({length:12},(e,n)=>new Date(t,n,1))}function O(e,t){switch(e){case`day`:{let e=x(t);return{from:e,to:S(e,1)}}case`week`:{let e=C(t);return{from:e,to:S(e,7)}}case`month`:{let e=C(new Date(t.getFullYear(),t.getMonth(),1));return{from:e,to:S(e,42)}}case`year`:return{from:new Date(t.getFullYear(),0,1),to:new Date(t.getFullYear()+1,0,1)}}}function k(e,t,n){switch(e){case`day`:return S(t,n);case`week`:return S(t,n*7);case`month`:return new Date(t.getFullYear(),t.getMonth()+n,1);case`year`:return new Date(t.getFullYear()+n,t.getMonth(),1)}}function A(e,t){return e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()&&e.getDate()===t.getDate()}function j(e,t){return{role:`button`,tabIndex:0,onClick:()=>t(e),onKeyDown:n=>{(n.key===`Enter`||n.key===` `)&&(n.preventDefault(),t(e))}}}var M=r(),N=[`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`,`Sun`],P=3;function F(e){return e.toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`})}function I({anchor:e,meetings:t,onSelect:n}){let r=T(e),i=e.getMonth(),a=new Date;return(0,M.jsxs)(`div`,{className:`cal-month`,children:[(0,M.jsx)(`div`,{className:`cal-weekday-row`,children:N.map(e=>(0,M.jsx)(`div`,{className:`cal-weekday`,children:e},e))}),(0,M.jsx)(`div`,{className:`cal-month-grid`,children:r.map(e=>{let r=t.filter(t=>A(new Date(t.startsAt),e)).sort((e,t)=>e.startsAt.localeCompare(t.startsAt)),o=e.getMonth()!==i,s=A(e,a);return(0,M.jsxs)(`div`,{className:`cal-cell${o?` cal-cell-out`:``}${s?` cal-cell-today`:``}`,children:[(0,M.jsx)(`div`,{className:`cal-cell-num`,children:e.getDate()}),r.slice(0,P).map(e=>(0,M.jsxs)(`div`,{className:`cal-chip`,title:e.title??`Untitled`,...j(e,n),children:[!e.isAllDay&&(0,M.jsx)(`span`,{className:`cal-chip-time`,children:F(new Date(e.startsAt))}),e.title??`Untitled`]},e.meetingId)),r.length>P&&(0,M.jsxs)(`div`,{className:`cal-more`,children:[`+`,r.length-P,` more`]})]},e.toISOString())})})]})}var L=1440,R=1.8;function z(e){return e.getHours()*60+e.getMinutes()}function B(e){return e.toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`})}function V({day:e,meetings:t,hourHeight:n,onSelect:r}){let i=n*24,a=t.filter(t=>!t.isAllDay&&A(new Date(t.startsAt),e));return(0,M.jsx)(`div`,{className:`cal-day-col`,children:(0,M.jsxs)(`div`,{className:`cal-hours`,style:{height:`${i}px`},children:[Array.from({length:24},(e,t)=>(0,M.jsx)(`div`,{className:`cal-hour-line`,style:{top:`${t*n}px`}},t)),a.map(t=>{let n=new Date(t.startsAt),i=t.endsAt?new Date(t.endsAt):new Date(n.getTime()+30*6e4),a=z(n),o=A(i,e)?z(i):L,s=a/L*100,c=Math.max((o-a)/L*100,R);return(0,M.jsxs)(`div`,{className:`cal-event`,style:{top:`${s}%`,height:`${c}%`},title:t.title??`Untitled`,...j(t,r),children:[(0,M.jsx)(`span`,{className:`cal-event-time`,children:B(n)}),(0,M.jsx)(`span`,{className:`cal-event-title`,children:t.title??`Untitled`})]},t.meetingId)})]})})}var H=44;function U(e){let t=new Date;return t.setHours(e,0,0,0),t.toLocaleTimeString([],{hour:`numeric`})}function W({anchor:e,meetings:t,onSelect:n}){let r=w(e),i=new Date;return(0,M.jsxs)(`div`,{className:`cal-timed`,children:[(0,M.jsxs)(`div`,{className:`cal-day-headrow`,children:[(0,M.jsx)(`div`,{className:`cal-timed-gutter`}),r.map(e=>(0,M.jsxs)(`div`,{className:`cal-day-head${A(e,i)?` cal-day-head-today`:``}`,children:[(0,M.jsx)(`span`,{className:`cal-day-head-name`,children:e.toLocaleDateString([],{weekday:`short`})}),(0,M.jsx)(`span`,{className:`cal-day-head-num`,children:e.getDate()})]},e.toISOString()))]}),(0,M.jsxs)(`div`,{className:`cal-allday-band`,children:[(0,M.jsx)(`div`,{className:`cal-timed-gutter`,children:`All-day`}),r.map(e=>(0,M.jsx)(`div`,{className:`cal-allday-cell`,children:t.filter(t=>t.isAllDay&&A(new Date(t.startsAt),e)).map(e=>(0,M.jsx)(`div`,{className:`cal-allday-chip`,title:e.title??`Untitled`,...j(e,n),children:e.title??`Untitled`},e.meetingId))},e.toISOString()))]}),(0,M.jsxs)(`div`,{className:`cal-timed-cols`,children:[(0,M.jsx)(`div`,{className:`cal-timed-gutter`,children:E().map(e=>(0,M.jsx)(`div`,{className:`cal-hour-label`,style:{height:`${H}px`},children:e===0?``:U(e)},e))}),r.map(e=>(0,M.jsx)(V,{day:e,meetings:t,hourHeight:H,onSelect:n},e.toISOString()))]})]})}var G=52;function K(e){let t=new Date;return t.setHours(e,0,0,0),t.toLocaleTimeString([],{hour:`numeric`})}function q({anchor:e,meetings:t,onSelect:n}){let r=t.filter(t=>t.isAllDay&&A(new Date(t.startsAt),e));return(0,M.jsxs)(`div`,{className:`cal-timed cal-timed-single`,children:[(0,M.jsxs)(`div`,{className:`cal-allday-band`,children:[(0,M.jsx)(`div`,{className:`cal-timed-gutter`,children:`All-day`}),(0,M.jsx)(`div`,{className:`cal-allday-cell`,children:r.map(e=>(0,M.jsx)(`div`,{className:`cal-allday-chip`,title:e.title??`Untitled`,...j(e,n),children:e.title??`Untitled`},e.meetingId))})]}),(0,M.jsxs)(`div`,{className:`cal-timed-cols`,children:[(0,M.jsx)(`div`,{className:`cal-timed-gutter`,children:E().map(e=>(0,M.jsx)(`div`,{className:`cal-hour-label`,style:{height:`${G}px`},children:e===0?``:K(e)},e))}),(0,M.jsx)(V,{day:e,meetings:t,hourHeight:G,onSelect:n})]})]})}var J=[`M`,`T`,`W`,`T`,`F`,`S`,`S`];function Y(e){return`${e.getFullYear()}-${e.getMonth()}-${e.getDate()}`}function ee({anchor:e,meetings:t,onPickMonth:n}){let r=new Date,i=new Set(t.map(e=>Y(new Date(e.startsAt))));return(0,M.jsx)(`div`,{className:`cal-year`,children:(0,M.jsx)(`div`,{className:`cal-year-grid`,children:D(e).map(e=>{let t=T(e),a=e.getMonth();return(0,M.jsxs)(`button`,{type:`button`,className:`cal-mini`,onClick:()=>n(e),title:e.toLocaleDateString([],{month:`long`,year:`numeric`}),children:[(0,M.jsx)(`div`,{className:`cal-mini-title`,children:e.toLocaleDateString([],{month:`long`})}),(0,M.jsx)(`div`,{className:`cal-mini-weekrow`,children:J.map((e,t)=>(0,M.jsx)(`span`,{className:`cal-mini-weekday`,children:e},t))}),(0,M.jsx)(`div`,{className:`cal-mini-grid`,children:t.map(e=>{let t=e.getMonth()!==a,n=A(e,r),o=!t&&i.has(Y(e));return(0,M.jsxs)(`span`,{className:`cal-mini-day${t?` cal-mini-day-out`:``}${n?` cal-mini-day-today`:``}`,children:[e.getDate(),o&&(0,M.jsx)(`span`,{className:`cal-mini-dot`})]},e.toISOString())})})]},a)})})})}var te={meeting:`Meeting`,event:`Event`,task:`Task`};function X(e){let t=new Date(e),n=e=>String(e).padStart(2,`0`);return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())}T${n(t.getHours())}:${n(t.getMinutes())}`}function Z(e){return new Date(e).toISOString()}function Q(e){return Math.floor(new Date(e).getTime()/6e4)}function ne(e){let t=new Date(e.startsAt);if(e.isAllDay)return t.toLocaleDateString([],{weekday:`long`,day:`numeric`,month:`long`,year:`numeric`});let n=t.toLocaleDateString([],{weekday:`long`,day:`numeric`,month:`long`,year:`numeric`}),r=t.toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`});return e.endsAt?`${n} · ${r} – ${new Date(e.endsAt).toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`})}`:`${n} · ${r}`}function re({meeting:e,adminFetch:n,onClose:r,onSaved:i,onDeleted:o}){let c=e.kind===`meeting`,[l,u]=(0,b.useState)(!1),[p,m]=(0,b.useState)(!1),[g,y]=(0,b.useState)(!1),[x,S]=(0,b.useState)(null),[C,w]=(0,b.useState)(e.title??``),[T,E]=(0,b.useState)(X(e.startsAt)),[D,O]=(0,b.useState)(e.endsAt?X(e.endsAt):``),[k,A]=(0,b.useState)(e.location??``);(0,b.useEffect)(()=>{let e=e=>{e.key===`Escape`&&!g&&r()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[g,r]);let j=(0,b.useCallback)(()=>{let t={},n=C.trim();n!==(e.title??``)&&(t.title=n);let r=Z(T);Q(r)!==Q(e.startsAt)&&(t.startsAt=r);let i=D?Z(D):null;(i===null?e.endsAt!==null:e.endsAt===null||Q(i)!==Q(e.endsAt))&&(t.endsAt=i);let a=k.trim()||null;return a!==(e.location??null)&&(t.location=a),t},[C,T,D,k,e]),N=(0,b.useCallback)(async()=>{let t=j();if(Object.keys(t).length===0){u(!1);return}y(!0),S(null);try{let r=await n(`/api/admin/calendar/meetings/${encodeURIComponent(e.meetingId)}`,{method:`PATCH`,headers:{"content-type":`application/json`},body:JSON.stringify(t)});if(!r.ok)throw Error(`HTTP ${r.status}`);i((await r.json()).meeting)}catch(e){S(e instanceof Error?e.message:`Save failed`)}finally{y(!1)}},[n,j,e.meetingId,i]),P=(0,b.useCallback)(async()=>{y(!0),S(null);try{let t=await n(`/api/admin/calendar/meetings/${encodeURIComponent(e.meetingId)}`,{method:`DELETE`});if(!t.ok)throw Error(`HTTP ${t.status}`);o(e.meetingId)}catch(e){S(e instanceof Error?e.message:`Delete failed`),y(!1)}},[n,e.meetingId,o]),F=te[e.kind??`meeting`];return(0,M.jsx)(`div`,{className:`cal-modal-overlay`,onClick:()=>{g||r()},children:(0,M.jsxs)(`div`,{className:`cal-modal`,role:`dialog`,"aria-modal":`true`,"aria-label":l?`Edit meeting`:`Meeting detail`,onClick:e=>e.stopPropagation(),children:[(0,M.jsxs)(`div`,{className:`cal-modal-header`,children:[(0,M.jsx)(`span`,{className:`cal-modal-kind`,children:F}),(0,M.jsx)(`button`,{type:`button`,className:`cal-modal-close`,onClick:r,"aria-label":`Close`,autoFocus:!0,disabled:g,children:(0,M.jsx)(d,{size:16})})]}),!l&&(0,M.jsxs)(`div`,{className:`cal-modal-body`,children:[(0,M.jsx)(`div`,{className:`cal-modal-title`,children:e.title??`Untitled`}),(0,M.jsxs)(`div`,{className:`cal-modal-row`,children:[(0,M.jsx)(_,{size:14}),(0,M.jsx)(`span`,{children:ne(e)})]}),e.location&&(0,M.jsxs)(`div`,{className:`cal-modal-row`,children:[(0,M.jsx)(v,{size:14}),(0,M.jsx)(`span`,{children:e.location})]}),e.attendees.length>0&&(0,M.jsxs)(`div`,{className:`cal-modal-row cal-modal-row-top`,children:[(0,M.jsx)(f,{size:14}),(0,M.jsx)(`span`,{children:e.attendees.map(e=>e.name||e.email||`Unknown`).join(`, `)})]}),(0,M.jsxs)(`div`,{className:`cal-modal-row cal-modal-meta`,children:[(0,M.jsx)(h,{size:14}),(0,M.jsx)(`span`,{children:F})]})]}),l&&(0,M.jsxs)(`div`,{className:`cal-modal-body`,children:[(0,M.jsxs)(`label`,{className:`cal-modal-field`,children:[(0,M.jsx)(`span`,{children:`Title`}),(0,M.jsx)(`input`,{type:`text`,value:C,onChange:e=>w(e.target.value),disabled:g})]}),(0,M.jsxs)(`label`,{className:`cal-modal-field`,children:[(0,M.jsx)(`span`,{children:`Start`}),(0,M.jsx)(`input`,{type:`datetime-local`,value:T,onChange:e=>E(e.target.value),disabled:g})]}),(0,M.jsxs)(`label`,{className:`cal-modal-field`,children:[(0,M.jsx)(`span`,{children:`End`}),(0,M.jsx)(`input`,{type:`datetime-local`,value:D,onChange:e=>O(e.target.value),disabled:g})]}),(0,M.jsxs)(`label`,{className:`cal-modal-field`,children:[(0,M.jsx)(`span`,{children:`Location`}),(0,M.jsx)(`input`,{type:`text`,value:k,onChange:e=>A(e.target.value),disabled:g})]})]}),x&&(0,M.jsx)(`div`,{className:`cal-modal-error`,children:x}),c&&(0,M.jsxs)(`div`,{className:`cal-modal-actions`,children:[!l&&!p&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`button`,{type:`button`,className:`cal-modal-btn`,onClick:()=>u(!0),children:[(0,M.jsx)(a,{size:14}),` Edit`]}),(0,M.jsxs)(`button`,{type:`button`,className:`cal-modal-btn cal-modal-btn-danger`,onClick:()=>m(!0),children:[(0,M.jsx)(s,{size:14}),` Delete`]})]}),l&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`button`,{type:`button`,className:`cal-modal-btn cal-modal-btn-primary`,onClick:N,disabled:g,children:[g?(0,M.jsx)(t,{size:14,className:`spin`}):null,` Save`]}),(0,M.jsx)(`button`,{type:`button`,className:`cal-modal-btn`,onClick:()=>{u(!1),S(null)},disabled:g,children:`Cancel`})]}),p&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsx)(`span`,{className:`cal-modal-confirm-text`,children:`Delete this meeting?`}),(0,M.jsxs)(`button`,{type:`button`,className:`cal-modal-btn cal-modal-btn-danger`,onClick:P,disabled:g,children:[g?(0,M.jsx)(t,{size:14,className:`spin`}):null,` Delete`]}),(0,M.jsx)(`button`,{type:`button`,className:`cal-modal-btn`,onClick:()=>m(!1),disabled:g,children:`Cancel`})]})]})]})})}var $=`maxy-admin-session-key`,ie=[`day`,`week`,`month`,`year`];function ae(e,t){switch(e){case`day`:return t.toLocaleDateString([],{weekday:`long`,day:`numeric`,month:`long`,year:`numeric`});case`week`:{let{from:e,to:n}=O(`week`,t),r=new Date(n.getTime()-1);return`${e.toLocaleDateString([],{day:`numeric`,month:`short`})} – ${r.toLocaleDateString([],{day:`numeric`,month:`short`,year:`numeric`})}`}case`month`:return t.toLocaleDateString([],{month:`long`,year:`numeric`});case`year`:return String(t.getFullYear())}}function oe(){let[e,n]=(0,b.useState)(null),[r,i]=(0,b.useState)(!1),[a,o]=(0,b.useState)(void 0),[s,c]=(0,b.useState)(null),[l,d]=(0,b.useState)(void 0),[f,h]=(0,b.useState)(null),[g,_]=(0,b.useState)(null);(0,b.useEffect)(()=>{let e=!1,t=null;try{t=sessionStorage.getItem($)}catch{}if(!t){i(!0);return}return fetch(`/api/admin/session?session_key=${encodeURIComponent(t)}`).then(async r=>{if(!e){if(r.status===401){try{sessionStorage.removeItem($)}catch{}window.location.href=`/`;return}if(r.ok)try{let e=await r.json();typeof e.businessName==`string`&&o(e.businessName),e.sessionId!==void 0&&c(e.sessionId??null),_(e.role??null),d(e.userName===void 0?null:e.userName),h(e.avatar??null)}catch{}n(t),i(!0)}}).catch(()=>{e||(n(t),i(!0))}),()=>{e=!0}},[]);let v=(0,b.useCallback)(()=>{try{sessionStorage.removeItem($)}catch{}window.location.href=`/`},[]),[y,x]=(0,b.useState)(!1),S=(0,b.useCallback)(async()=>{x(!0);try{let e=await p();if(e){try{sessionStorage.removeItem($)}catch{}window.location.href=`/`}return e}finally{x(!1)}},[]);return r?e?(0,M.jsx)(u,{cacheKey:e,surface:`calendar`,onSessionExpired:({code:e,path:t})=>{console.warn(`[admin-auth] outcome=session-expired-redirect code=${e} path=${t} surface=calendar`);try{sessionStorage.removeItem($)}catch{}window.location.href=`/`},children:(0,M.jsx)(m,{cacheKey:e,businessName:a,sessionId:s,onLogout:v,onDisconnect:S,disconnecting:y,userName:l,userAvatar:f,role:g,children:(0,M.jsx)(se,{cacheKey:e})})}):(0,M.jsx)(`div`,{className:`cal-page`,children:(0,M.jsxs)(`div`,{className:`cal-empty`,children:[(0,M.jsx)(`p`,{children:`You are not signed in.`}),(0,M.jsxs)(`p`,{children:[`Open the `,(0,M.jsx)(`a`,{href:`/`,className:`cal-link`,children:`main admin page`}),` and log in, then return here.`]})]})}):(0,M.jsx)(`div`,{className:`cal-page`,children:(0,M.jsxs)(`div`,{className:`cal-loading`,children:[(0,M.jsx)(t,{size:18,className:`spin`}),` Loading…`]})})}function se({cacheKey:e}){let{adminFetch:r,sessionRefetchNonce:a}=n({initialCacheKey:e,surface:`calendar`}),[o,s]=(0,b.useState)(`month`),[c,l]=(0,b.useState)(()=>new Date),[u,d]=(0,b.useState)([]),[f,p]=(0,b.useState)(!1),[m,_]=(0,b.useState)(null),[v,y]=(0,b.useState)(null);(0,b.useEffect)(()=>{let e=!1,{from:t,to:n}=O(o,c);return p(!0),_(null),r(`/api/admin/calendar/meetings?from=${encodeURIComponent(t.toISOString())}&to=${encodeURIComponent(n.toISOString())}`).then(async t=>{if(e)return;if(!t.ok)throw Error(`HTTP ${t.status}`);let n=await t.json();d(Array.isArray(n.meetings)?n.meetings:[])}).catch(t=>{e||(_(t instanceof Error?t.message:`Failed to load`),d([]))}).finally(()=>{e||p(!1)}),()=>{e=!0}},[o,c,a,r]);let x=(0,b.useCallback)(()=>l(new Date),[]),S=(0,b.useCallback)(()=>l(e=>k(o,e,-1)),[o]),C=(0,b.useCallback)(()=>l(e=>k(o,e,1)),[o]),w=(0,b.useCallback)(e=>{l(e),s(`month`)},[]);(0,b.useEffect)(()=>{y(null)},[o,c]);let T=(0,b.useCallback)(e=>{d(t=>t.map(t=>t.meetingId===e.meetingId?e:t)),y(e)},[]),E=(0,b.useCallback)(e=>{d(t=>t.filter(t=>t.meetingId!==e)),y(null)},[]);return(0,M.jsxs)(`div`,{className:`cal-page`,children:[(0,M.jsxs)(`header`,{className:`cal-header`,children:[(0,M.jsxs)(`div`,{className:`cal-title`,children:[(0,M.jsx)(h,{size:20}),` Calendar`]}),(0,M.jsxs)(`div`,{className:`cal-nav`,children:[(0,M.jsx)(`button`,{type:`button`,className:`cal-today-btn`,onClick:x,children:`Today`}),(0,M.jsx)(`button`,{type:`button`,className:`cal-nav-btn`,onClick:S,"aria-label":`Previous`,children:(0,M.jsx)(i,{size:18})}),(0,M.jsx)(`button`,{type:`button`,className:`cal-nav-btn`,onClick:C,"aria-label":`Next`,children:(0,M.jsx)(g,{size:18})}),(0,M.jsx)(`span`,{className:`cal-range-label`,children:ae(o,c)}),f&&(0,M.jsx)(t,{size:16,className:`spin`})]}),(0,M.jsx)(`div`,{className:`cal-views`,children:ie.map(e=>(0,M.jsx)(`button`,{type:`button`,className:`cal-view-btn${e===o?` cal-view-btn-active`:``}`,onClick:()=>s(e),children:e.charAt(0).toUpperCase()+e.slice(1)},e))})]}),(0,M.jsxs)(`div`,{className:`cal-body`,children:[m&&(0,M.jsxs)(`div`,{className:`cal-error`,children:[`Could not load the calendar: `,m]}),o===`day`&&(0,M.jsx)(q,{anchor:c,meetings:u,onSelect:y}),o===`week`&&(0,M.jsx)(W,{anchor:c,meetings:u,onSelect:y}),o===`month`&&(0,M.jsx)(I,{anchor:c,meetings:u,onSelect:y}),o===`year`&&(0,M.jsx)(ee,{anchor:c,meetings:u,onPickMonth:w})]}),v&&(0,M.jsx)(re,{meeting:v,adminFetch:r,onClose:()=>y(null),onSaved:T,onDeleted:E})]})}(0,y.createRoot)(document.getElementById(`root`)).render((0,M.jsx)(oe,{}));
@@ -1 +0,0 @@
1
- import{C as e,V as t}from"./OperatorConversations-g7aAQCDn.js";import"./admin-types-Dg11L4MQ.js";import"./AdminShell-D3N3Aq04.js";import{n}from"./page-BPIaNNW1.js";var r=t(),i=e();(0,r.createRoot)(document.getElementById(`root`)).render((0,i.jsx)(n,{}));