@burdenoff/microfe-vibecontrols 2026.625.3 → 2026.626.2

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 (59) hide show
  1. package/dist/components/agents/AgentForm.js +129 -127
  2. package/dist/components/agents/AgentForm.js.map +1 -1
  3. package/dist/components/shared/InstallTabs.js +19 -7
  4. package/dist/components/shared/InstallTabs.js.map +1 -1
  5. package/dist/pages/ActionsPage.js +209 -204
  6. package/dist/pages/ActionsPage.js.map +1 -1
  7. package/dist/pages/AgentDetailsPage.js +678 -648
  8. package/dist/pages/AgentDetailsPage.js.map +1 -1
  9. package/dist/pages/AgentGraphPage.js +112 -108
  10. package/dist/pages/AgentGraphPage.js.map +1 -1
  11. package/dist/pages/AgentsPage.js +159 -155
  12. package/dist/pages/AgentsPage.js.map +1 -1
  13. package/dist/pages/AnalyticsPage.js +140 -136
  14. package/dist/pages/AnalyticsPage.js.map +1 -1
  15. package/dist/pages/CatalogDetailPage.js +249 -244
  16. package/dist/pages/CatalogDetailPage.js.map +1 -1
  17. package/dist/pages/CatalogPage.js +73 -68
  18. package/dist/pages/CatalogPage.js.map +1 -1
  19. package/dist/pages/ComponentDetailPage.js +195 -164
  20. package/dist/pages/ComponentDetailPage.js.map +1 -1
  21. package/dist/pages/ComponentsPage.js +80 -75
  22. package/dist/pages/ComponentsPage.js.map +1 -1
  23. package/dist/pages/EnvTopologyPage.js +100 -95
  24. package/dist/pages/EnvTopologyPage.js.map +1 -1
  25. package/dist/pages/HealthPage.js +331 -204
  26. package/dist/pages/HealthPage.js.map +1 -1
  27. package/dist/pages/LogsPage.js +54 -49
  28. package/dist/pages/LogsPage.js.map +1 -1
  29. package/dist/pages/OverviewPage.js +159 -129
  30. package/dist/pages/OverviewPage.js.map +1 -1
  31. package/dist/pages/PluginsCatalogPage.js +72 -78
  32. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  33. package/dist/pages/SessionDetailsPage.js +517 -512
  34. package/dist/pages/SessionDetailsPage.js.map +1 -1
  35. package/dist/pages/SessionsPage.js +1084 -1079
  36. package/dist/pages/SessionsPage.js.map +1 -1
  37. package/dist/pages/SettingsPage.js +107 -103
  38. package/dist/pages/SettingsPage.js.map +1 -1
  39. package/dist/pages/TargetDetailsPage.js +131 -123
  40. package/dist/pages/TargetDetailsPage.js.map +1 -1
  41. package/dist/pages/TargetsPage.js +198 -194
  42. package/dist/pages/TargetsPage.js.map +1 -1
  43. package/dist/pages/TemplatesPage.js +77 -71
  44. package/dist/pages/TemplatesPage.js.map +1 -1
  45. package/dist/pages/VibeAuditPage.js +83 -78
  46. package/dist/pages/VibeAuditPage.js.map +1 -1
  47. package/dist/pages/VibeCalendarPage.js +109 -104
  48. package/dist/pages/VibeCalendarPage.js.map +1 -1
  49. package/dist/pages/VibeDeckPage.js +222 -217
  50. package/dist/pages/VibeDeckPage.js.map +1 -1
  51. package/dist/pages/VibeDetailsPage.js +532 -501
  52. package/dist/pages/VibeDetailsPage.js.map +1 -1
  53. package/dist/pages/VibesPage.js +137 -133
  54. package/dist/pages/VibesPage.js.map +1 -1
  55. package/dist/pages/WebhooksPage.js +329 -324
  56. package/dist/pages/WebhooksPage.js.map +1 -1
  57. package/dist/pages/ai/AIPage.js +149 -144
  58. package/dist/pages/ai/AIPage.js.map +1 -1
  59. package/package.json +2 -2
@@ -2,38 +2,39 @@ import { useCreateVibeCalendarTaskMutation as e, useExecuteScheduledJobNowMutati
2
2
  import { useVibeControls as r, useVibeNavigate as ee } from "../providers/VibeControlsProvider.js";
3
3
  import { LoadingState as te } from "../components/shared/LoadingState.js";
4
4
  import { VIBE_PERMISSIONS as i } from "../constants/permissions.js";
5
- import { useTenantContext as ne } from "../hooks/useTenantContext.js";
6
- import { LogsDrawer as re } from "../components/vibecalendar/LogsDrawer.js";
7
- import { VibeCalendarHeader as ie } from "../components/vibecalendar/VibeCalendarHeader.js";
8
- import { VibeCalMonthView as ae } from "../components/vibecalendar/VibeCalMonthView.js";
9
- import { VibeCalWeekView as oe } from "../components/vibecalendar/VibeCalWeekView.js";
10
- import { VibeCalDayView as a } from "../components/vibecalendar/VibeCalDayView.js";
5
+ import { useTenantContext as a } from "../hooks/useTenantContext.js";
6
+ import { LogsDrawer as ne } from "../components/vibecalendar/LogsDrawer.js";
7
+ import { VibeCalendarHeader as re } from "../components/vibecalendar/VibeCalendarHeader.js";
8
+ import { VibeCalMonthView as ie } from "../components/vibecalendar/VibeCalMonthView.js";
9
+ import { VibeCalWeekView as ae } from "../components/vibecalendar/VibeCalWeekView.js";
10
+ import { VibeCalDayView as oe } from "../components/vibecalendar/VibeCalDayView.js";
11
11
  import { VibeCalAgendaView as se } from "../components/vibecalendar/VibeCalAgendaView.js";
12
12
  import { ScheduleTaskModal as ce } from "../components/vibecalendar/ScheduleTaskModal.js";
13
13
  import "../components/vibecalendar/index.js";
14
14
  import { expandJobsToOccurrences as le } from "../components/vibecalendar/expandOccurrences.js";
15
15
  import { useCallback as o, useEffect as s, useMemo as c, useState as l } from "react";
16
16
  import { useI18n as ue } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
17
- import { AlertCircle as u, CalendarClock as de, X as fe } from "lucide-react";
18
- import { useParams as pe, useSearchParams as me } from "react-router-dom";
19
- import { graphqlFetch as he } from "@burdenoff/fe-libs/shared/graphql";
20
- import { usePermissions as ge } from "@burdenoff/fe-libs/shared/providers/shell";
21
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
17
+ import { AlertCircle as u, CalendarClock as d, X as de } from "lucide-react";
18
+ import { useParams as fe, useSearchParams as pe } from "react-router-dom";
19
+ import { graphqlFetch as me } from "@burdenoff/fe-libs/shared/graphql";
20
+ import { usePermissions as he } from "@burdenoff/fe-libs/shared/providers/shell";
21
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
22
+ import { PagePurpose as ge } from "@burdenoff/fe-libs/ui";
22
23
  import { AccessDenied as _e } from "@burdenoff/fe-libs/shared/components";
23
- import { MotionPage as p } from "@burdenoff/fe-libs/shared/motion";
24
+ import { MotionPage as m } from "@burdenoff/fe-libs/shared/motion";
24
25
  //#region src/pages/VibeCalendarPage.tsx
25
26
  var ve = "\n query GetWorkspaceScheduledJobsForCalendar($workspaceId: ID!) {\n scheduledJobs(workspaceId: $workspaceId, pagination: { first: 500 }) {\n jobs {\n id\n name\n description\n status\n scheduleType\n cronExpression\n rrule\n interval\n scheduledAt\n nextExecutionAt\n lastExecutionAt\n actionId\n timezone\n startDate\n endDate\n }\n total\n }\n }\n";
26
- function m() {
27
- let m = ee(), { workspaceId: h } = r(), { t: g } = ue(), { viewType: ye } = pe(), [_, v] = me(), y = _.get("actionId") ?? void 0, { hasPermission: b, isLoading: be, permissions: x } = ge(), xe = b(i.SCHEDULE_READ), S = b(i.LOG_READ), C = b(i.CALENDAR_AGENT_TASK_CREATE), w = b(i.CALENDAR_AGENT_TASK_EXECUTE), T = b(i.SCHEDULE_EXECUTE), E = T, D = ne(), [O, Se] = l(/* @__PURE__ */ new Date()), [k, Ce] = l(ye || "month"), [A, j] = l(!1);
27
+ function h() {
28
+ let h = ee(), { workspaceId: g } = r(), { t: _ } = ue(), { viewType: ye } = fe(), [v, y] = pe(), b = v.get("actionId") ?? void 0, { hasPermission: x, isLoading: be, permissions: S } = he(), xe = x(i.SCHEDULE_READ), C = x(i.LOG_READ), w = x(i.CALENDAR_AGENT_TASK_CREATE), T = x(i.CALENDAR_AGENT_TASK_EXECUTE), E = x(i.SCHEDULE_EXECUTE), D = E, O = a(), [k, Se] = l(/* @__PURE__ */ new Date()), [A, Ce] = l(ye || "month"), [we, j] = l(!1);
28
29
  s(() => {
29
- y && j(!0);
30
- }, [y]);
31
- let [we, M] = l(!1), [N, Te] = l(null), [Ee, De] = l(null), [P, F] = l(null), I = o((e, t) => {
32
- let n = g(e);
30
+ b && j(!0);
31
+ }, [b]);
32
+ let [Te, M] = l(!1), [N, Ee] = l(null), [De, Oe] = l(null), [P, F] = l(null), I = o((e, t) => {
33
+ let n = _(e);
33
34
  return n === e ? t : n;
34
- }, [g]), L = c(() => {
35
- let e = new Date(O), t = new Date(O);
36
- switch (k) {
35
+ }, [_]), L = c(() => {
36
+ let e = new Date(k), t = new Date(k);
37
+ switch (A) {
37
38
  case "month":
38
39
  e.setDate(1), e.setHours(0, 0, 0, 0), t.setMonth(t.getMonth() + 1, 0), t.setHours(23, 59, 59, 999);
39
40
  break;
@@ -51,16 +52,16 @@ function m() {
51
52
  startDate: e.toISOString(),
52
53
  endDate: t.toISOString()
53
54
  };
54
- }, [O, k]), [R, z] = l([]), [B, V] = l(void 0), [Oe, H] = l(!1), [ke, U] = l(!1), W = o(async () => {
55
- if (h) {
55
+ }, [k, A]), [R, z] = l([]), [B, V] = l(void 0), [ke, H] = l(!1), [Ae, U] = l(!1), W = o(async () => {
56
+ if (g) {
56
57
  U(!0);
57
58
  try {
58
- let e = new URLSearchParams(typeof window > "u" ? "" : window.location.search), t = e.get("org") ?? void 0, n = e.get("tenant") ?? void 0, r = await he({
59
+ let e = new URLSearchParams(typeof window > "u" ? "" : window.location.search), t = e.get("org") ?? void 0, n = e.get("tenant") ?? void 0, r = await me({
59
60
  gateway: "workspace",
60
61
  query: ve,
61
62
  operationName: "GetWorkspaceScheduledJobsForCalendar",
62
- variables: { workspaceId: h },
63
- workspaceId: h,
63
+ variables: { workspaceId: g },
64
+ workspaceId: g,
64
65
  organizationId: t ?? !0,
65
66
  tenantId: n ?? !0
66
67
  });
@@ -72,14 +73,14 @@ function m() {
72
73
  U(!1);
73
74
  }
74
75
  }
75
- }, [h]);
76
+ }, [g]);
76
77
  s(() => {
77
- if (!h) {
78
+ if (!g) {
78
79
  z([]), V(void 0), H(!1);
79
80
  return;
80
81
  }
81
82
  W();
82
- }, [h, W]);
83
+ }, [g, W]);
83
84
  let { occurrences: G, sourceById: K } = c(() => {
84
85
  if (!R.length) return {
85
86
  occurrences: [],
@@ -95,16 +96,16 @@ function m() {
95
96
  R,
96
97
  L.startDate,
97
98
  L.endDate
98
- ]), [q, { loading: Ae }] = e(), [J] = n(), [Y] = t(), je = o((e) => {
99
+ ]), [q, { loading: je }] = e(), [J] = n(), [Y] = t(), Me = o((e) => {
99
100
  Ce(e);
100
101
  }, []), X = o((e) => K.get(e), [K]), Z = o(async (e) => {
101
102
  F(null);
102
103
  let t = X(e);
103
- if (t === "scheduledJob" && !T) {
104
+ if (t === "scheduledJob" && !E) {
104
105
  F(I("calendar.scheduleExecuteDenied", "You do not have permission to run scheduled jobs (schedule:execute)."));
105
106
  return;
106
107
  }
107
- if (t !== "scheduledJob" && !w) {
108
+ if (t !== "scheduledJob" && !T) {
108
109
  F(I("calendar.calendarTaskExecuteDenied", "You do not have permission to run calendar tasks (calendar-agent-task:execute)."));
109
110
  return;
110
111
  }
@@ -113,12 +114,12 @@ function m() {
113
114
  let r = t === "scheduledJob" ? await Y({
114
115
  variables: {
115
116
  id: e,
116
- canReadLogs: S
117
+ canReadLogs: C
117
118
  },
118
- context: D
119
+ context: O
119
120
  }) : await J({
120
121
  variables: { id: e },
121
- context: D
122
+ context: O
122
123
  });
123
124
  r.error && (console.error("Trigger mutation returned an Apollo error:", r.error), F(r.error.message || n));
124
125
  } catch (e) {
@@ -128,21 +129,21 @@ function m() {
128
129
  J,
129
130
  Y,
130
131
  X,
131
- S,
132
- w,
132
+ C,
133
133
  T,
134
- D,
134
+ E,
135
+ O,
135
136
  I
136
137
  ]), Q = o((e) => {
137
- Te(e), De(null), M(!0);
138
+ Ee(e), Oe(null), M(!0);
138
139
  }, []), $ = o((e) => {
139
- let t = encodeURIComponent(e), n = _.toString(), r = n ? `?${n}` : "";
140
- m(`${X(e) === "scheduledJob" ? `/calendar/scheduled-job/${t}` : `/calendar/task/${t}`}${r}`);
140
+ let t = encodeURIComponent(e), n = v.toString(), r = n ? `?${n}` : "";
141
+ h(`${X(e) === "scheduledJob" ? `/calendar/scheduled-job/${t}` : `/calendar/task/${t}`}${r}`);
141
142
  }, [
142
- m,
143
+ h,
143
144
  X,
144
- _
145
- ]), Me = o(async (e) => {
145
+ v
146
+ ]), Ne = o(async (e) => {
146
147
  try {
147
148
  if (await q({ variables: { input: {
148
149
  calendarId: e.calendarId || "default",
@@ -155,9 +156,9 @@ function m() {
155
156
  interval: e.interval,
156
157
  intervalUnit: e.intervalUnit,
157
158
  timezone: e.timezone
158
- } } }), await W(), j(!1), _.has("actionId")) {
159
- let e = new URLSearchParams(_);
160
- e.delete("actionId"), v(e, { replace: !0 });
159
+ } } }), await W(), j(!1), v.has("actionId")) {
160
+ let e = new URLSearchParams(v);
161
+ e.delete("actionId"), y(e, { replace: !0 });
161
162
  }
162
163
  } catch (e) {
163
164
  console.error("Failed to create task:", e);
@@ -165,155 +166,159 @@ function m() {
165
166
  }, [
166
167
  q,
167
168
  W,
168
- _,
169
- v
169
+ v,
170
+ y
170
171
  ]);
171
- if (be) return /* @__PURE__ */ d(p, {
172
+ if (be) return /* @__PURE__ */ f(m, {
172
173
  className: "flex flex-col h-full p-4 gap-4",
173
- children: /* @__PURE__ */ d(te, { message: I("calendar.loadingPermissions", "Loading permissions...") })
174
+ children: /* @__PURE__ */ f(te, { message: I("calendar.loadingPermissions", "Loading permissions...") })
174
175
  });
175
- if (x && x.permissionStrings?.length > 0 && !xe) return /* @__PURE__ */ d(p, {
176
+ if (S && S.permissionStrings?.length > 0 && !xe) return /* @__PURE__ */ f(m, {
176
177
  className: "flex flex-col h-full p-4 gap-4",
177
- children: /* @__PURE__ */ d(_e, {
178
+ children: /* @__PURE__ */ f(_e, {
178
179
  title: I("common.accessDeniedTitle", "Access Denied"),
179
180
  description: I("calendar.accessDeniedDescription", "You don't have permission to view the Calendar.")
180
181
  })
181
182
  });
182
- let Ne = Oe && !B && R.length === 0;
183
- return /* @__PURE__ */ f(p, {
183
+ let Pe = ke && !B && R.length === 0;
184
+ return /* @__PURE__ */ p(m, {
184
185
  className: "flex flex-col h-full p-4 gap-4",
185
186
  children: [
186
- B && R.length === 0 && /* @__PURE__ */ f("div", {
187
+ B && R.length === 0 && /* @__PURE__ */ p("div", {
187
188
  className: "flex items-center gap-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2",
188
189
  children: [
189
- /* @__PURE__ */ d(u, {
190
+ /* @__PURE__ */ f(u, {
190
191
  size: 16,
191
192
  className: "text-status-error-text flex-shrink-0"
192
193
  }),
193
- /* @__PURE__ */ d("p", {
194
+ /* @__PURE__ */ f("p", {
194
195
  className: "flex-1 text-sm text-status-error-text",
195
196
  children: I("calendar.couldNotLoadEvents", "Could not load events. We'll keep trying — you can retry below.")
196
197
  }),
197
- /* @__PURE__ */ d("button", {
198
+ /* @__PURE__ */ f("button", {
198
199
  type: "button",
199
200
  onClick: () => W(),
200
- disabled: ke,
201
+ disabled: Ae,
201
202
  className: "rounded px-3 py-1 text-xs text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
202
203
  children: I("calendar.retry", "Retry")
203
204
  })
204
205
  ]
205
206
  }),
206
- P && /* @__PURE__ */ f("div", {
207
+ P && /* @__PURE__ */ p("div", {
207
208
  role: "alert",
208
209
  className: "flex items-start gap-2 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
209
210
  children: [
210
- /* @__PURE__ */ d(u, {
211
+ /* @__PURE__ */ f(u, {
211
212
  size: 16,
212
213
  className: "mt-0.5 flex-shrink-0"
213
214
  }),
214
- /* @__PURE__ */ d("p", {
215
+ /* @__PURE__ */ f("p", {
215
216
  className: "flex-1 break-words",
216
217
  children: P
217
218
  }),
218
- /* @__PURE__ */ d("button", {
219
+ /* @__PURE__ */ f("button", {
219
220
  type: "button",
220
221
  onClick: () => F(null),
221
222
  className: "p-0.5 rounded hover:bg-bg-sunken transition-colors",
222
223
  "aria-label": I("common.dismiss", "Dismiss"),
223
- children: /* @__PURE__ */ d(fe, { size: 14 })
224
+ children: /* @__PURE__ */ f(de, { size: 14 })
224
225
  })
225
226
  ]
226
227
  }),
227
- /* @__PURE__ */ f("div", {
228
+ /* @__PURE__ */ p("div", {
228
229
  className: "flex items-center gap-3 mb-2",
229
- children: [/* @__PURE__ */ d(de, {
230
+ children: [/* @__PURE__ */ f(d, {
230
231
  size: 24,
231
232
  className: "text-text-primary"
232
- }), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("h1", {
233
+ }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
233
234
  className: "text-xl font-bold text-text-primary",
234
235
  children: I("nav.vibeCalendar", "Calendar")
235
- }), /* @__PURE__ */ d("p", {
236
+ }), /* @__PURE__ */ f("p", {
236
237
  className: "text-sm text-text-secondary",
237
238
  children: I("calendar.description", "Schedule and manage tasks across your agents")
238
239
  })] })]
239
240
  }),
240
- Ne && /* @__PURE__ */ f("div", {
241
+ /* @__PURE__ */ f(ge, {
242
+ icon: /* @__PURE__ */ f(d, { className: "size-4" }),
243
+ children: I("calendar.purpose", "The calendar is where you schedule work to run on its own — recurring or one-off tasks that fire actions on your agents at set times (nightly builds, periodic checks, cleanups). Plan it here once and VibeControls runs it for you, so routine jobs happen on time without anyone remembering to kick them off.")
244
+ }),
245
+ Pe && /* @__PURE__ */ p("div", {
241
246
  className: "flex flex-col sm:flex-row sm:items-center gap-3 rounded-lg border border-border-default bg-bg-surface px-4 py-3",
242
- children: [/* @__PURE__ */ f("div", {
247
+ children: [/* @__PURE__ */ p("div", {
243
248
  className: "flex-1",
244
- children: [/* @__PURE__ */ d("p", {
249
+ children: [/* @__PURE__ */ f("p", {
245
250
  className: "text-sm font-medium text-text-primary",
246
251
  children: I("calendar.empty.title", "No scheduled tasks yet")
247
- }), /* @__PURE__ */ d("p", {
252
+ }), /* @__PURE__ */ f("p", {
248
253
  className: "text-xs text-text-secondary mt-0.5",
249
254
  children: I("calendar.empty.description", "Schedule your first task to start automating actions across your agents.")
250
255
  })]
251
- }), C && /* @__PURE__ */ d("button", {
256
+ }), w && /* @__PURE__ */ f("button", {
252
257
  type: "button",
253
258
  onClick: () => j(!0),
254
259
  className: "px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors",
255
260
  children: I("calendar.empty.cta", "Schedule your first task")
256
261
  })]
257
262
  }),
258
- /* @__PURE__ */ d("div", {
263
+ /* @__PURE__ */ f("div", {
259
264
  "data-tour": "calendar-header",
260
- children: /* @__PURE__ */ d(ie, {
261
- currentDate: O,
262
- viewMode: k,
265
+ children: /* @__PURE__ */ f(re, {
266
+ currentDate: k,
267
+ viewMode: A,
263
268
  onDateChange: Se,
264
- onViewModeChange: je,
265
- onCreateTask: C ? () => j(!0) : void 0
269
+ onViewModeChange: Me,
270
+ onCreateTask: w ? () => j(!0) : void 0
266
271
  })
267
272
  }),
268
- /* @__PURE__ */ f("div", {
273
+ /* @__PURE__ */ p("div", {
269
274
  "data-tour": "calendar-view-container",
270
275
  className: "flex-1 overflow-hidden",
271
276
  children: [
272
- k === "month" && /* @__PURE__ */ d(ae, {
273
- currentDate: O,
277
+ A === "month" && /* @__PURE__ */ f(ie, {
278
+ currentDate: k,
274
279
  occurrences: G,
275
- onTriggerNow: E ? Z : void 0,
280
+ onTriggerNow: D ? Z : void 0,
276
281
  onViewLogs: Q,
277
282
  onViewDetail: $
278
283
  }),
279
- k === "week" && /* @__PURE__ */ d(oe, {
280
- currentDate: O,
284
+ A === "week" && /* @__PURE__ */ f(ae, {
285
+ currentDate: k,
281
286
  occurrences: G,
282
- onTriggerNow: E ? Z : void 0,
287
+ onTriggerNow: D ? Z : void 0,
283
288
  onViewLogs: Q,
284
289
  onViewDetail: $
285
290
  }),
286
- k === "day" && /* @__PURE__ */ d(a, {
287
- currentDate: O,
291
+ A === "day" && /* @__PURE__ */ f(oe, {
292
+ currentDate: k,
288
293
  occurrences: G,
289
- onTriggerNow: E ? Z : void 0,
294
+ onTriggerNow: D ? Z : void 0,
290
295
  onViewLogs: Q,
291
296
  onViewDetail: $
292
297
  }),
293
- k === "agenda" && /* @__PURE__ */ d(se, {
298
+ A === "agenda" && /* @__PURE__ */ f(se, {
294
299
  occurrences: G,
295
- onTriggerNow: E ? Z : void 0,
300
+ onTriggerNow: D ? Z : void 0,
296
301
  onViewLogs: Q,
297
302
  onViewDetail: $
298
303
  })
299
304
  ]
300
305
  }),
301
- /* @__PURE__ */ d(ce, {
302
- isOpen: A,
306
+ /* @__PURE__ */ f(ce, {
307
+ isOpen: we,
303
308
  onClose: () => {
304
- if (j(!1), _.has("actionId")) {
305
- let e = new URLSearchParams(_);
306
- e.delete("actionId"), v(e, { replace: !0 });
309
+ if (j(!1), v.has("actionId")) {
310
+ let e = new URLSearchParams(v);
311
+ e.delete("actionId"), y(e, { replace: !0 });
307
312
  }
308
313
  },
309
- onSubmit: Me,
310
- loading: Ae,
311
- prefillActionId: y
314
+ onSubmit: Ne,
315
+ loading: je,
316
+ prefillActionId: b
312
317
  }),
313
- /* @__PURE__ */ d(re, {
314
- isOpen: we,
318
+ /* @__PURE__ */ f(ne, {
319
+ isOpen: Te,
315
320
  onClose: () => M(!1),
316
- execution: Ee,
321
+ execution: De,
317
322
  taskTitle: N?.title || "",
318
323
  agentId: N?.agentId || ""
319
324
  })
@@ -321,6 +326,6 @@ function m() {
321
326
  });
322
327
  }
323
328
  //#endregion
324
- export { m as VibeCalendarPage };
329
+ export { h as VibeCalendarPage };
325
330
 
326
331
  //# sourceMappingURL=VibeCalendarPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VibeCalendarPage.js","names":[],"sources":["../../src/pages/VibeCalendarPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport { CalendarClock, AlertCircle, X } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { useTenantContext } from '@/hooks/useTenantContext';\nimport {\n VibeCalendarHeader,\n VibeCalMonthView,\n VibeCalWeekView,\n VibeCalDayView,\n VibeCalAgendaView,\n ScheduleTaskModal,\n LogsDrawer,\n} from '../components/vibecalendar';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n CalendarViewMode,\n VibeCalendarTask,\n VibeCalendarExecution,\n ScheduleTaskInput,\n} from '../components/vibecalendar';\nimport {\n expandJobsToOccurrences,\n type SchedulerJobLike,\n} from '../components/vibecalendar/expandOccurrences';\nimport {\n useCreateVibeCalendarTaskMutation,\n useExecuteScheduledJobNowMutation,\n useTriggerVibeCalendarTaskNowMutation,\n} from '@/generated/wspace-operations';\n\n/**\n * The /scheduler page writes ScheduledJobs through the WSPACE gateway —\n * `scheduledJobs` is only published by `wspace-scheduler-svc` and is\n * workspace-scoped (the `$workspaceId: ID!` argument is required). The\n * Calendar renders those jobs alongside its own VibeCalendarTasks. We use\n * `graphqlFetch` rather than Apollo to keep the Calendar's local-state\n * timeline decoupled from the global Apollo cache that powers other\n * VibeControls pages.\n */\nconst WORKSPACE_SCHEDULED_JOBS_QUERY = /* GraphQL */ `\n query GetWorkspaceScheduledJobsForCalendar($workspaceId: ID!) {\n scheduledJobs(workspaceId: $workspaceId, pagination: { first: 500 }) {\n jobs {\n id\n name\n description\n status\n scheduleType\n cronExpression\n rrule\n interval\n scheduledAt\n nextExecutionAt\n lastExecutionAt\n actionId\n timezone\n startDate\n endDate\n }\n total\n }\n }\n`;\n\ninterface WorkspaceScheduledJobsResponse {\n scheduledJobs: {\n jobs: SchedulerJobLike[];\n total: number;\n };\n}\n\n/**\n * Calendar Page\n *\n * Main page for scheduling and managing tasks on VibeControls agents.\n * Supports month and agenda views with task creation, on-demand triggering,\n * and log viewing via the LogsDrawer.\n */\nexport function VibeCalendarPage() {\n const navigate = useVibeNavigate();\n const { workspaceId } = useVibeControls();\n const { t } = useI18n();\n const { viewType } = useParams<{ viewType?: string }>();\n const [searchParams, setSearchParams] = useSearchParams();\n // When navigated here from the Actions page via \"Add to calendar\",\n // the URL carries ?actionId=... — open the modal in Action-linked mode.\n const prefillActionId = searchParams.get('actionId') ?? undefined;\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n // The unified-source migration moved the calendar's only list\n // path onto `scheduledJobs(workspaceId)` (the scheduler subgraph) —\n // there's no parallel `vibeCalendarTasks` query firing here\n // anymore. The page-level access gate is therefore\n // `schedule:read` specifically; admitting calendar-task-only\n // readers would let them past the UI only to hit an empty\n // calendar / scheduler RBAC denial on the actual fetch.\n // calendar-agent-task permissions are still consumed downstream\n // (CREATE for the schedule button, EXECUTE for the per-tile ▶\n // branch) but they don't gate the page mount.\n const canList = hasPermission(VIBE_PERMISSIONS.SCHEDULE_READ);\n // Required by the @include gate on `output`/`errorMessage` in\n // both run-now mutations — without this Apollo rejects the\n // operation before it leaves the browser.\n const canReadExecutionLogs = hasPermission(VIBE_PERMISSIONS.LOG_READ);\n const canCreate = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_CREATE);\n // The agenda exclusively renders ScheduledJob tiles in the\n // current unified-source path, so the prop-level gate on\n // `onTriggerNow` is `schedule:execute` — admitting\n // `calendar-agent-task:execute`-only users would show ▶ on every\n // tile just to bounce off a friendly denial banner. Both\n // permissions stay tracked because handleTriggerNow's per-source\n // pre-check uses them defensively in case a future mixed-source\n // render lands here.\n const canExecuteCalendarTask = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_EXECUTE);\n const canExecuteScheduledJob = hasPermission(VIBE_PERMISSIONS.SCHEDULE_EXECUTE);\n const canExecute = canExecuteScheduledJob;\n\n // URL overrides reach Apollo via per-request headers so tile-▶\n // mutations stay in the same tenant the support deep-link list\n // loaded against.\n const tenantContext = useTenantContext();\n\n const [currentDate, setCurrentDate] = useState(new Date());\n const [viewMode, setViewMode] = useState<CalendarViewMode>(\n (viewType as CalendarViewMode) || 'month'\n );\n const [showCreateModal, setShowCreateModal] = useState(false);\n\n // Auto-open the modal when arriving with an actionId query param.\n useEffect(() => {\n if (prefillActionId) {\n setShowCreateModal(true);\n }\n }, [prefillActionId]);\n const [showLogsDrawer, setShowLogsDrawer] = useState(false);\n const [selectedTask, setSelectedTask] = useState<VibeCalendarTask | null>(null);\n const [selectedExecution, setSelectedExecution] = useState<VibeCalendarExecution | null>(null);\n const [triggerError, setTriggerError] = useState<string | null>(null);\n\n // Memoized so downstream useCallback deps stay referentially\n // stable across renders.\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n // Calculate date range for current view\n const dateRange = useMemo(() => {\n const start = new Date(currentDate);\n const end = new Date(currentDate);\n\n switch (viewMode) {\n case 'month':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'week':\n start.setDate(start.getDate() - start.getDay());\n start.setHours(0, 0, 0, 0);\n end.setDate(start.getDate() + 6);\n end.setHours(23, 59, 59, 999);\n break;\n case 'day':\n start.setHours(0, 0, 0, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'agenda':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n }\n\n return { startDate: start.toISOString(), endDate: end.toISOString() };\n }, [currentDate, viewMode]);\n\n // Unified calendar view: fetch every ScheduledJob the workspace owns\n // (where /scheduler writes). The calendar renders jobs regardless of how\n // they were created, so all schedule-to-Action tasks show up here as a\n // single source of truth.\n //\n // `scheduledJobs` is published by `wspace-scheduler-svc` and is workspace-\n // scoped — `workspaceId` is required as a query variable. `graphqlFetch`\n // auto-resolves the workspace/org/tenant context from the shell storage\n // and sets the matching headers; URL params (`?org=`, `?tenant=`)\n // override that resolution for the support-tooling deep-link flow.\n const [jobs, setJobs] = useState<SchedulerJobLike[]>([]);\n const [rangeError, setRangeError] = useState<Error | undefined>(undefined);\n const [jobsLoaded, setJobsLoaded] = useState(false);\n const [jobsLoading, setJobsLoading] = useState(false);\n const refetch = useCallback(async () => {\n // Guard: scheduledJobs requires workspaceId — if it isn't resolved yet,\n // skip rather than firing a request that's guaranteed to fail validation.\n // The effect below re-invokes refetch once workspaceId becomes available.\n // (The state-reset on workspace transitions lives in the effect itself,\n // not here, because the effect early-returns when workspaceId is falsy\n // and refetch never gets called in that path.)\n if (!workspaceId) return;\n setJobsLoading(true);\n try {\n // graphqlFetch reads workspace/org/tenant context from storage and sets\n // the proper x-workspace-id / x-org-id / x-tenant-id headers itself —\n // passing `true` opts in to that auto-resolution. URL params override\n // storage for the org/tenant context (used by the support tooling\n // when impersonating a different tenant via deep-link).\n const urlParams = new URLSearchParams(\n typeof window === 'undefined' ? '' : window.location.search\n );\n const orgFromUrl = urlParams.get('org') ?? undefined;\n const tenantFromUrl = urlParams.get('tenant') ?? undefined;\n\n const res = await graphqlFetch<WorkspaceScheduledJobsResponse>({\n gateway: 'workspace',\n query: WORKSPACE_SCHEDULED_JOBS_QUERY,\n operationName: 'GetWorkspaceScheduledJobsForCalendar',\n variables: { workspaceId },\n // Pass the resolved workspaceId STRING (not `true`) so the\n // `x-workspace-id` header matches the GraphQL variable even when\n // ActiveContextStorage is stale / not hydrated yet. Otherwise the\n // gateway can authorize/federate against the previous workspace's\n // context while the resolver receives the new one.\n workspaceId,\n organizationId: orgFromUrl ?? true,\n tenantId: tenantFromUrl ?? true,\n });\n\n if (res.errors && res.errors.length) {\n // Wrap GraphQL/network errors in a generic, UI-safe Error so we\n // never leak raw resolver / federation messages into the banner.\n throw new Error(res.errors[0]?.message ?? 'Unable to load scheduled tasks');\n }\n setJobs(res.data?.scheduledJobs?.jobs ?? []);\n setRangeError(undefined);\n setJobsLoaded(true);\n } catch (err) {\n console.error('[VibeCalendarPage] Failed to load scheduled jobs:', err);\n setRangeError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setJobsLoading(false);\n }\n }, [workspaceId]);\n\n useEffect(() => {\n if (!workspaceId) {\n // Reset on workspace transitions so the empty-state CTA doesn't\n // flash stale data from the previous workspace while the new one\n // is still resolving. Lives here, not in refetch, because refetch\n // is only called from this effect — when workspaceId is falsy,\n // refetch never runs.\n setJobs([]);\n setRangeError(undefined);\n setJobsLoaded(false);\n return;\n }\n void refetch();\n }, [workspaceId, refetch]);\n\n // `sourceById` built in the same pass so handleTriggerNow does\n // an O(1) Map lookup per click — month/agenda views can mint\n // hundreds of occurrences from a single recurring job.\n const { occurrences, sourceById } = useMemo(() => {\n if (!jobs.length) {\n return {\n occurrences: [],\n sourceById: new Map<string, VibeCalendarTask['source']>(),\n };\n }\n const occs = expandJobsToOccurrences(\n jobs,\n new Date(dateRange.startDate),\n new Date(dateRange.endDate)\n );\n const map = new Map<string, VibeCalendarTask['source']>();\n for (const o of occs) {\n // Always populate with an explicit discriminator. Future\n // shapes (e.g. an externalTrigger source) would otherwise\n // collapse into the default branch silently — `undefined`\n // here means \"id no longer in the visible window\", nothing\n // more.\n map.set(o.task.id, o.task.source ?? 'vibeCalendarTask');\n }\n return { occurrences: occs, sourceById: map };\n }, [jobs, dateRange.startDate, dateRange.endDate]);\n\n // Agents are no longer collected in the modal — the scheduler resolves\n // them from the referenced Action at execution time.\n\n // Mutations\n const [createTaskMutation, { loading: createLoading }] = useCreateVibeCalendarTaskMutation();\n const [triggerNowMutation] = useTriggerVibeCalendarTaskNowMutation();\n // Scheduled-job tiles route to the scheduler subgraph rather than\n // the calendar one (different id space, different RBAC resource).\n const [executeScheduledJobNowMutation] = useExecuteScheduledJobNowMutation();\n\n const handleViewModeChange = useCallback((mode: CalendarViewMode) => {\n setViewMode(mode);\n }, []);\n\n // `undefined` return signals \"no longer in the visible window\" —\n // callers should treat that as the safe default `'vibeCalendarTask'`.\n const sourceForTask = useCallback(\n (taskId: string): VibeCalendarTask['source'] => {\n return sourceById.get(taskId);\n },\n [sourceById]\n );\n\n const handleTriggerNow = useCallback(\n async (taskId: string) => {\n // Clear any prior failure banner before re-attempting — same\n // pattern as ScheduledJobDetailPage.handleExecuteNow.\n setTriggerError(null);\n // Branch on the discriminator set by `jobToVibeCalendarTask`.\n // The view passes `onTriggerNow` to every tile when EITHER\n // permission is held (combined `canExecute` gate above), so we\n // pre-check the per-source permission here and surface a\n // friendly inline banner if missing rather than firing the\n // mutation just to bounce off a server-side RBAC denial.\n const source = sourceForTask(taskId);\n if (source === 'scheduledJob' && !canExecuteScheduledJob) {\n setTriggerError(\n tr(\n 'calendar.scheduleExecuteDenied',\n 'You do not have permission to run scheduled jobs (schedule:execute).'\n )\n );\n return;\n }\n if (source !== 'scheduledJob' && !canExecuteCalendarTask) {\n setTriggerError(\n tr(\n 'calendar.calendarTaskExecuteDenied',\n 'You do not have permission to run calendar tasks (calendar-agent-task:execute).'\n )\n );\n return;\n }\n // VibeControlsProvider sets `errorPolicy: 'all'` so RBAC /\n // GraphQL errors resolve into `result.error` (ApolloError)\n // instead of throwing. Inspect both paths consistently —\n // denials would otherwise be silently dropped. Same pattern\n // ActionsPage already uses for quota-exhaust handling.\n const fallbackMsg = tr(\n 'calendar.triggerFailedFallback',\n 'Failed to trigger task. Try again or contact support.'\n );\n try {\n const result =\n source === 'scheduledJob'\n ? await executeScheduledJobNowMutation({\n variables: { id: taskId, canReadLogs: canReadExecutionLogs },\n context: tenantContext,\n })\n : await triggerNowMutation({\n variables: { id: taskId },\n context: tenantContext,\n });\n if (result.error) {\n console.error('Trigger mutation returned an Apollo error:', result.error);\n setTriggerError(result.error.message || fallbackMsg);\n }\n } catch (err) {\n console.error('Failed to trigger task:', err);\n const msg = err instanceof Error && err.message ? err.message : fallbackMsg;\n setTriggerError(msg);\n }\n },\n [\n triggerNowMutation,\n executeScheduledJobNowMutation,\n sourceForTask,\n canReadExecutionLogs,\n canExecuteCalendarTask,\n canExecuteScheduledJob,\n tenantContext,\n tr,\n ]\n );\n\n const handleViewLogs = useCallback((task: VibeCalendarTask) => {\n setSelectedTask(task);\n setSelectedExecution(null);\n setShowLogsDrawer(true);\n }, []);\n\n const handleViewDetail = useCallback(\n (taskId: string) => {\n // A tile that originated from a ScheduledJob has no row in\n // wspace-calendar-svc, so VibeTaskDetailPage would 404 on it.\n // Route to the matching in-product detail page instead.\n //\n // Carry the existing query string so the support deep-link\n // flow (`?org=…&tenant=…`) keeps its tenant context across\n // the navigation — without it the detail query runs against\n // the default/stored tenant and returns a wrong-context view.\n // Encode the id for defense-in-depth in case the scheduler\n // ever emits ids that contain URL-reserved characters.\n const encoded = encodeURIComponent(taskId);\n const search = searchParams.toString();\n const suffix = search ? `?${search}` : '';\n const path =\n sourceForTask(taskId) === 'scheduledJob'\n ? `/calendar/scheduled-job/${encoded}`\n : `/calendar/task/${encoded}`;\n navigate(`${path}${suffix}`);\n },\n [navigate, sourceForTask, searchParams]\n );\n\n const handleCreateTask = useCallback(\n async (input: ScheduleTaskInput) => {\n try {\n // Calendar entries always delegate execution to a VibeControls\n // Action. The scheduler resolves agent/command/webhook details\n // from the Action definition at run time — we only send\n // actionId + calendar/schedule config from this modal.\n await createTaskMutation({\n variables: {\n input: {\n calendarId: input.calendarId || 'default',\n title: input.title,\n description: input.description,\n actionId: input.actionId,\n scheduleType: input.scheduleType,\n scheduledAt: input.scheduledAt,\n cronExpression: input.cronExpression,\n interval: input.interval,\n intervalUnit: input.intervalUnit,\n timezone: input.timezone,\n },\n },\n });\n await refetch();\n setShowCreateModal(false);\n // Drop the actionId query param after successful creation\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n } catch (err) {\n console.error('Failed to create task:', err);\n }\n },\n [createTaskMutation, refetch, searchParams, setSearchParams]\n );\n\n if (permissionsLoading)\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <LoadingState message={tr('calendar.loadingPermissions', 'Loading permissions...')} />\n </MotionPage>\n );\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canList) {\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'calendar.accessDeniedDescription',\n \"You don't have permission to view the Calendar.\"\n )}\n />\n </MotionPage>\n );\n }\n\n // Show the empty-state CTA only after the first successful load returned\n // an empty list — never while loading, and never when an error blocks the\n // distinction between \"no data\" and \"couldn't fetch\".\n const showEmptyState = jobsLoaded && !rangeError && jobs.length === 0;\n\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n {/* Error banner (non-blocking) — friendly message, never raw GraphQL */}\n {rangeError && jobs.length === 0 && (\n <div className=\"flex items-center gap-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2\">\n <AlertCircle size={16} className=\"text-status-error-text flex-shrink-0\" />\n <p className=\"flex-1 text-sm text-status-error-text\">\n {tr(\n 'calendar.couldNotLoadEvents',\n \"Could not load events. We'll keep trying — you can retry below.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={jobsLoading}\n className=\"rounded px-3 py-1 text-xs text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {tr('calendar.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {triggerError && (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n <AlertCircle size={16} className=\"mt-0.5 flex-shrink-0\" />\n <p className=\"flex-1 break-words\">{triggerError}</p>\n <button\n type=\"button\"\n onClick={() => setTriggerError(null)}\n className=\"p-0.5 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('common.dismiss', 'Dismiss')}\n >\n <X size={14} />\n </button>\n </div>\n )}\n\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-2\">\n <CalendarClock size={24} className=\"text-text-primary\" />\n <div>\n <h1 className=\"text-xl font-bold text-text-primary\">\n {tr('nav.vibeCalendar', 'Calendar')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('calendar.description', 'Schedule and manage tasks across your agents')}\n </p>\n </div>\n </div>\n\n {/* Empty-state CTA shown when the user has no scheduled tasks yet. */}\n {showEmptyState && (\n <div className=\"flex flex-col sm:flex-row sm:items-center gap-3 rounded-lg border border-border-default bg-bg-surface px-4 py-3\">\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('calendar.empty.title', 'No scheduled tasks yet')}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {tr(\n 'calendar.empty.description',\n 'Schedule your first task to start automating actions across your agents.'\n )}\n </p>\n </div>\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setShowCreateModal(true)}\n className=\"px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('calendar.empty.cta', 'Schedule your first task')}\n </button>\n )}\n </div>\n )}\n\n {/* Calendar Header with nav + view switcher */}\n <div data-tour=\"calendar-header\">\n <VibeCalendarHeader\n currentDate={currentDate}\n viewMode={viewMode}\n onDateChange={setCurrentDate}\n onViewModeChange={handleViewModeChange}\n onCreateTask={canCreate ? () => setShowCreateModal(true) : undefined}\n />\n </div>\n\n {/* Calendar Body */}\n <div data-tour=\"calendar-view-container\" className=\"flex-1 overflow-hidden\">\n {viewMode === 'month' && (\n <VibeCalMonthView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'week' && (\n <VibeCalWeekView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'day' && (\n <VibeCalDayView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'agenda' && (\n <VibeCalAgendaView\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n </div>\n\n {/* Schedule Task Modal */}\n <ScheduleTaskModal\n isOpen={showCreateModal}\n onClose={() => {\n setShowCreateModal(false);\n // Strip the actionId query param on dismiss so reopening the\n // modal without a fresh navigation doesn't silently resume\n // Action-linked mode.\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n }}\n onSubmit={handleCreateTask}\n loading={createLoading}\n prefillActionId={prefillActionId}\n />\n\n {/* Logs Drawer */}\n <LogsDrawer\n isOpen={showLogsDrawer}\n onClose={() => setShowLogsDrawer(false)}\n execution={selectedExecution}\n taskTitle={selectedTask?.title || ''}\n agentId={selectedTask?.agentId || ''}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VibeCalendarPage.js","names":[],"sources":["../../src/pages/VibeCalendarPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport { CalendarClock, AlertCircle, X } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { useTenantContext } from '@/hooks/useTenantContext';\nimport {\n VibeCalendarHeader,\n VibeCalMonthView,\n VibeCalWeekView,\n VibeCalDayView,\n VibeCalAgendaView,\n ScheduleTaskModal,\n LogsDrawer,\n} from '../components/vibecalendar';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n CalendarViewMode,\n VibeCalendarTask,\n VibeCalendarExecution,\n ScheduleTaskInput,\n} from '../components/vibecalendar';\nimport {\n expandJobsToOccurrences,\n type SchedulerJobLike,\n} from '../components/vibecalendar/expandOccurrences';\nimport {\n useCreateVibeCalendarTaskMutation,\n useExecuteScheduledJobNowMutation,\n useTriggerVibeCalendarTaskNowMutation,\n} from '@/generated/wspace-operations';\n\n/**\n * The /scheduler page writes ScheduledJobs through the WSPACE gateway —\n * `scheduledJobs` is only published by `wspace-scheduler-svc` and is\n * workspace-scoped (the `$workspaceId: ID!` argument is required). The\n * Calendar renders those jobs alongside its own VibeCalendarTasks. We use\n * `graphqlFetch` rather than Apollo to keep the Calendar's local-state\n * timeline decoupled from the global Apollo cache that powers other\n * VibeControls pages.\n */\nconst WORKSPACE_SCHEDULED_JOBS_QUERY = /* GraphQL */ `\n query GetWorkspaceScheduledJobsForCalendar($workspaceId: ID!) {\n scheduledJobs(workspaceId: $workspaceId, pagination: { first: 500 }) {\n jobs {\n id\n name\n description\n status\n scheduleType\n cronExpression\n rrule\n interval\n scheduledAt\n nextExecutionAt\n lastExecutionAt\n actionId\n timezone\n startDate\n endDate\n }\n total\n }\n }\n`;\n\ninterface WorkspaceScheduledJobsResponse {\n scheduledJobs: {\n jobs: SchedulerJobLike[];\n total: number;\n };\n}\n\n/**\n * Calendar Page\n *\n * Main page for scheduling and managing tasks on VibeControls agents.\n * Supports month and agenda views with task creation, on-demand triggering,\n * and log viewing via the LogsDrawer.\n */\nexport function VibeCalendarPage() {\n const navigate = useVibeNavigate();\n const { workspaceId } = useVibeControls();\n const { t } = useI18n();\n const { viewType } = useParams<{ viewType?: string }>();\n const [searchParams, setSearchParams] = useSearchParams();\n // When navigated here from the Actions page via \"Add to calendar\",\n // the URL carries ?actionId=... — open the modal in Action-linked mode.\n const prefillActionId = searchParams.get('actionId') ?? undefined;\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n // The unified-source migration moved the calendar's only list\n // path onto `scheduledJobs(workspaceId)` (the scheduler subgraph) —\n // there's no parallel `vibeCalendarTasks` query firing here\n // anymore. The page-level access gate is therefore\n // `schedule:read` specifically; admitting calendar-task-only\n // readers would let them past the UI only to hit an empty\n // calendar / scheduler RBAC denial on the actual fetch.\n // calendar-agent-task permissions are still consumed downstream\n // (CREATE for the schedule button, EXECUTE for the per-tile ▶\n // branch) but they don't gate the page mount.\n const canList = hasPermission(VIBE_PERMISSIONS.SCHEDULE_READ);\n // Required by the @include gate on `output`/`errorMessage` in\n // both run-now mutations — without this Apollo rejects the\n // operation before it leaves the browser.\n const canReadExecutionLogs = hasPermission(VIBE_PERMISSIONS.LOG_READ);\n const canCreate = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_CREATE);\n // The agenda exclusively renders ScheduledJob tiles in the\n // current unified-source path, so the prop-level gate on\n // `onTriggerNow` is `schedule:execute` — admitting\n // `calendar-agent-task:execute`-only users would show ▶ on every\n // tile just to bounce off a friendly denial banner. Both\n // permissions stay tracked because handleTriggerNow's per-source\n // pre-check uses them defensively in case a future mixed-source\n // render lands here.\n const canExecuteCalendarTask = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_EXECUTE);\n const canExecuteScheduledJob = hasPermission(VIBE_PERMISSIONS.SCHEDULE_EXECUTE);\n const canExecute = canExecuteScheduledJob;\n\n // URL overrides reach Apollo via per-request headers so tile-▶\n // mutations stay in the same tenant the support deep-link list\n // loaded against.\n const tenantContext = useTenantContext();\n\n const [currentDate, setCurrentDate] = useState(new Date());\n const [viewMode, setViewMode] = useState<CalendarViewMode>(\n (viewType as CalendarViewMode) || 'month'\n );\n const [showCreateModal, setShowCreateModal] = useState(false);\n\n // Auto-open the modal when arriving with an actionId query param.\n useEffect(() => {\n if (prefillActionId) {\n setShowCreateModal(true);\n }\n }, [prefillActionId]);\n const [showLogsDrawer, setShowLogsDrawer] = useState(false);\n const [selectedTask, setSelectedTask] = useState<VibeCalendarTask | null>(null);\n const [selectedExecution, setSelectedExecution] = useState<VibeCalendarExecution | null>(null);\n const [triggerError, setTriggerError] = useState<string | null>(null);\n\n // Memoized so downstream useCallback deps stay referentially\n // stable across renders.\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n // Calculate date range for current view\n const dateRange = useMemo(() => {\n const start = new Date(currentDate);\n const end = new Date(currentDate);\n\n switch (viewMode) {\n case 'month':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'week':\n start.setDate(start.getDate() - start.getDay());\n start.setHours(0, 0, 0, 0);\n end.setDate(start.getDate() + 6);\n end.setHours(23, 59, 59, 999);\n break;\n case 'day':\n start.setHours(0, 0, 0, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'agenda':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n }\n\n return { startDate: start.toISOString(), endDate: end.toISOString() };\n }, [currentDate, viewMode]);\n\n // Unified calendar view: fetch every ScheduledJob the workspace owns\n // (where /scheduler writes). The calendar renders jobs regardless of how\n // they were created, so all schedule-to-Action tasks show up here as a\n // single source of truth.\n //\n // `scheduledJobs` is published by `wspace-scheduler-svc` and is workspace-\n // scoped — `workspaceId` is required as a query variable. `graphqlFetch`\n // auto-resolves the workspace/org/tenant context from the shell storage\n // and sets the matching headers; URL params (`?org=`, `?tenant=`)\n // override that resolution for the support-tooling deep-link flow.\n const [jobs, setJobs] = useState<SchedulerJobLike[]>([]);\n const [rangeError, setRangeError] = useState<Error | undefined>(undefined);\n const [jobsLoaded, setJobsLoaded] = useState(false);\n const [jobsLoading, setJobsLoading] = useState(false);\n const refetch = useCallback(async () => {\n // Guard: scheduledJobs requires workspaceId — if it isn't resolved yet,\n // skip rather than firing a request that's guaranteed to fail validation.\n // The effect below re-invokes refetch once workspaceId becomes available.\n // (The state-reset on workspace transitions lives in the effect itself,\n // not here, because the effect early-returns when workspaceId is falsy\n // and refetch never gets called in that path.)\n if (!workspaceId) return;\n setJobsLoading(true);\n try {\n // graphqlFetch reads workspace/org/tenant context from storage and sets\n // the proper x-workspace-id / x-org-id / x-tenant-id headers itself —\n // passing `true` opts in to that auto-resolution. URL params override\n // storage for the org/tenant context (used by the support tooling\n // when impersonating a different tenant via deep-link).\n const urlParams = new URLSearchParams(\n typeof window === 'undefined' ? '' : window.location.search\n );\n const orgFromUrl = urlParams.get('org') ?? undefined;\n const tenantFromUrl = urlParams.get('tenant') ?? undefined;\n\n const res = await graphqlFetch<WorkspaceScheduledJobsResponse>({\n gateway: 'workspace',\n query: WORKSPACE_SCHEDULED_JOBS_QUERY,\n operationName: 'GetWorkspaceScheduledJobsForCalendar',\n variables: { workspaceId },\n // Pass the resolved workspaceId STRING (not `true`) so the\n // `x-workspace-id` header matches the GraphQL variable even when\n // ActiveContextStorage is stale / not hydrated yet. Otherwise the\n // gateway can authorize/federate against the previous workspace's\n // context while the resolver receives the new one.\n workspaceId,\n organizationId: orgFromUrl ?? true,\n tenantId: tenantFromUrl ?? true,\n });\n\n if (res.errors && res.errors.length) {\n // Wrap GraphQL/network errors in a generic, UI-safe Error so we\n // never leak raw resolver / federation messages into the banner.\n throw new Error(res.errors[0]?.message ?? 'Unable to load scheduled tasks');\n }\n setJobs(res.data?.scheduledJobs?.jobs ?? []);\n setRangeError(undefined);\n setJobsLoaded(true);\n } catch (err) {\n console.error('[VibeCalendarPage] Failed to load scheduled jobs:', err);\n setRangeError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setJobsLoading(false);\n }\n }, [workspaceId]);\n\n useEffect(() => {\n if (!workspaceId) {\n // Reset on workspace transitions so the empty-state CTA doesn't\n // flash stale data from the previous workspace while the new one\n // is still resolving. Lives here, not in refetch, because refetch\n // is only called from this effect — when workspaceId is falsy,\n // refetch never runs.\n setJobs([]);\n setRangeError(undefined);\n setJobsLoaded(false);\n return;\n }\n void refetch();\n }, [workspaceId, refetch]);\n\n // `sourceById` built in the same pass so handleTriggerNow does\n // an O(1) Map lookup per click — month/agenda views can mint\n // hundreds of occurrences from a single recurring job.\n const { occurrences, sourceById } = useMemo(() => {\n if (!jobs.length) {\n return {\n occurrences: [],\n sourceById: new Map<string, VibeCalendarTask['source']>(),\n };\n }\n const occs = expandJobsToOccurrences(\n jobs,\n new Date(dateRange.startDate),\n new Date(dateRange.endDate)\n );\n const map = new Map<string, VibeCalendarTask['source']>();\n for (const o of occs) {\n // Always populate with an explicit discriminator. Future\n // shapes (e.g. an externalTrigger source) would otherwise\n // collapse into the default branch silently — `undefined`\n // here means \"id no longer in the visible window\", nothing\n // more.\n map.set(o.task.id, o.task.source ?? 'vibeCalendarTask');\n }\n return { occurrences: occs, sourceById: map };\n }, [jobs, dateRange.startDate, dateRange.endDate]);\n\n // Agents are no longer collected in the modal — the scheduler resolves\n // them from the referenced Action at execution time.\n\n // Mutations\n const [createTaskMutation, { loading: createLoading }] = useCreateVibeCalendarTaskMutation();\n const [triggerNowMutation] = useTriggerVibeCalendarTaskNowMutation();\n // Scheduled-job tiles route to the scheduler subgraph rather than\n // the calendar one (different id space, different RBAC resource).\n const [executeScheduledJobNowMutation] = useExecuteScheduledJobNowMutation();\n\n const handleViewModeChange = useCallback((mode: CalendarViewMode) => {\n setViewMode(mode);\n }, []);\n\n // `undefined` return signals \"no longer in the visible window\" —\n // callers should treat that as the safe default `'vibeCalendarTask'`.\n const sourceForTask = useCallback(\n (taskId: string): VibeCalendarTask['source'] => {\n return sourceById.get(taskId);\n },\n [sourceById]\n );\n\n const handleTriggerNow = useCallback(\n async (taskId: string) => {\n // Clear any prior failure banner before re-attempting — same\n // pattern as ScheduledJobDetailPage.handleExecuteNow.\n setTriggerError(null);\n // Branch on the discriminator set by `jobToVibeCalendarTask`.\n // The view passes `onTriggerNow` to every tile when EITHER\n // permission is held (combined `canExecute` gate above), so we\n // pre-check the per-source permission here and surface a\n // friendly inline banner if missing rather than firing the\n // mutation just to bounce off a server-side RBAC denial.\n const source = sourceForTask(taskId);\n if (source === 'scheduledJob' && !canExecuteScheduledJob) {\n setTriggerError(\n tr(\n 'calendar.scheduleExecuteDenied',\n 'You do not have permission to run scheduled jobs (schedule:execute).'\n )\n );\n return;\n }\n if (source !== 'scheduledJob' && !canExecuteCalendarTask) {\n setTriggerError(\n tr(\n 'calendar.calendarTaskExecuteDenied',\n 'You do not have permission to run calendar tasks (calendar-agent-task:execute).'\n )\n );\n return;\n }\n // VibeControlsProvider sets `errorPolicy: 'all'` so RBAC /\n // GraphQL errors resolve into `result.error` (ApolloError)\n // instead of throwing. Inspect both paths consistently —\n // denials would otherwise be silently dropped. Same pattern\n // ActionsPage already uses for quota-exhaust handling.\n const fallbackMsg = tr(\n 'calendar.triggerFailedFallback',\n 'Failed to trigger task. Try again or contact support.'\n );\n try {\n const result =\n source === 'scheduledJob'\n ? await executeScheduledJobNowMutation({\n variables: { id: taskId, canReadLogs: canReadExecutionLogs },\n context: tenantContext,\n })\n : await triggerNowMutation({\n variables: { id: taskId },\n context: tenantContext,\n });\n if (result.error) {\n console.error('Trigger mutation returned an Apollo error:', result.error);\n setTriggerError(result.error.message || fallbackMsg);\n }\n } catch (err) {\n console.error('Failed to trigger task:', err);\n const msg = err instanceof Error && err.message ? err.message : fallbackMsg;\n setTriggerError(msg);\n }\n },\n [\n triggerNowMutation,\n executeScheduledJobNowMutation,\n sourceForTask,\n canReadExecutionLogs,\n canExecuteCalendarTask,\n canExecuteScheduledJob,\n tenantContext,\n tr,\n ]\n );\n\n const handleViewLogs = useCallback((task: VibeCalendarTask) => {\n setSelectedTask(task);\n setSelectedExecution(null);\n setShowLogsDrawer(true);\n }, []);\n\n const handleViewDetail = useCallback(\n (taskId: string) => {\n // A tile that originated from a ScheduledJob has no row in\n // wspace-calendar-svc, so VibeTaskDetailPage would 404 on it.\n // Route to the matching in-product detail page instead.\n //\n // Carry the existing query string so the support deep-link\n // flow (`?org=…&tenant=…`) keeps its tenant context across\n // the navigation — without it the detail query runs against\n // the default/stored tenant and returns a wrong-context view.\n // Encode the id for defense-in-depth in case the scheduler\n // ever emits ids that contain URL-reserved characters.\n const encoded = encodeURIComponent(taskId);\n const search = searchParams.toString();\n const suffix = search ? `?${search}` : '';\n const path =\n sourceForTask(taskId) === 'scheduledJob'\n ? `/calendar/scheduled-job/${encoded}`\n : `/calendar/task/${encoded}`;\n navigate(`${path}${suffix}`);\n },\n [navigate, sourceForTask, searchParams]\n );\n\n const handleCreateTask = useCallback(\n async (input: ScheduleTaskInput) => {\n try {\n // Calendar entries always delegate execution to a VibeControls\n // Action. The scheduler resolves agent/command/webhook details\n // from the Action definition at run time — we only send\n // actionId + calendar/schedule config from this modal.\n await createTaskMutation({\n variables: {\n input: {\n calendarId: input.calendarId || 'default',\n title: input.title,\n description: input.description,\n actionId: input.actionId,\n scheduleType: input.scheduleType,\n scheduledAt: input.scheduledAt,\n cronExpression: input.cronExpression,\n interval: input.interval,\n intervalUnit: input.intervalUnit,\n timezone: input.timezone,\n },\n },\n });\n await refetch();\n setShowCreateModal(false);\n // Drop the actionId query param after successful creation\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n } catch (err) {\n console.error('Failed to create task:', err);\n }\n },\n [createTaskMutation, refetch, searchParams, setSearchParams]\n );\n\n if (permissionsLoading)\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <LoadingState message={tr('calendar.loadingPermissions', 'Loading permissions...')} />\n </MotionPage>\n );\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canList) {\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'calendar.accessDeniedDescription',\n \"You don't have permission to view the Calendar.\"\n )}\n />\n </MotionPage>\n );\n }\n\n // Show the empty-state CTA only after the first successful load returned\n // an empty list — never while loading, and never when an error blocks the\n // distinction between \"no data\" and \"couldn't fetch\".\n const showEmptyState = jobsLoaded && !rangeError && jobs.length === 0;\n\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n {/* Error banner (non-blocking) — friendly message, never raw GraphQL */}\n {rangeError && jobs.length === 0 && (\n <div className=\"flex items-center gap-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2\">\n <AlertCircle size={16} className=\"text-status-error-text flex-shrink-0\" />\n <p className=\"flex-1 text-sm text-status-error-text\">\n {tr(\n 'calendar.couldNotLoadEvents',\n \"Could not load events. We'll keep trying — you can retry below.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={jobsLoading}\n className=\"rounded px-3 py-1 text-xs text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {tr('calendar.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {triggerError && (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n <AlertCircle size={16} className=\"mt-0.5 flex-shrink-0\" />\n <p className=\"flex-1 break-words\">{triggerError}</p>\n <button\n type=\"button\"\n onClick={() => setTriggerError(null)}\n className=\"p-0.5 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('common.dismiss', 'Dismiss')}\n >\n <X size={14} />\n </button>\n </div>\n )}\n\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-2\">\n <CalendarClock size={24} className=\"text-text-primary\" />\n <div>\n <h1 className=\"text-xl font-bold text-text-primary\">\n {tr('nav.vibeCalendar', 'Calendar')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('calendar.description', 'Schedule and manage tasks across your agents')}\n </p>\n </div>\n </div>\n\n <PagePurpose icon={<CalendarClock className=\"size-4\" />}>\n {tr(\n 'calendar.purpose',\n 'The calendar is where you schedule work to run on its own — recurring or one-off tasks that fire actions on your agents at set times (nightly builds, periodic checks, cleanups). Plan it here once and VibeControls runs it for you, so routine jobs happen on time without anyone remembering to kick them off.'\n )}\n </PagePurpose>\n\n {/* Empty-state CTA shown when the user has no scheduled tasks yet. */}\n {showEmptyState && (\n <div className=\"flex flex-col sm:flex-row sm:items-center gap-3 rounded-lg border border-border-default bg-bg-surface px-4 py-3\">\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('calendar.empty.title', 'No scheduled tasks yet')}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {tr(\n 'calendar.empty.description',\n 'Schedule your first task to start automating actions across your agents.'\n )}\n </p>\n </div>\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setShowCreateModal(true)}\n className=\"px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('calendar.empty.cta', 'Schedule your first task')}\n </button>\n )}\n </div>\n )}\n\n {/* Calendar Header with nav + view switcher */}\n <div data-tour=\"calendar-header\">\n <VibeCalendarHeader\n currentDate={currentDate}\n viewMode={viewMode}\n onDateChange={setCurrentDate}\n onViewModeChange={handleViewModeChange}\n onCreateTask={canCreate ? () => setShowCreateModal(true) : undefined}\n />\n </div>\n\n {/* Calendar Body */}\n <div data-tour=\"calendar-view-container\" className=\"flex-1 overflow-hidden\">\n {viewMode === 'month' && (\n <VibeCalMonthView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'week' && (\n <VibeCalWeekView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'day' && (\n <VibeCalDayView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'agenda' && (\n <VibeCalAgendaView\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n </div>\n\n {/* Schedule Task Modal */}\n <ScheduleTaskModal\n isOpen={showCreateModal}\n onClose={() => {\n setShowCreateModal(false);\n // Strip the actionId query param on dismiss so reopening the\n // modal without a fresh navigation doesn't silently resume\n // Action-linked mode.\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n }}\n onSubmit={handleCreateTask}\n loading={createLoading}\n prefillActionId={prefillActionId}\n />\n\n {/* Logs Drawer */}\n <LogsDrawer\n isOpen={showLogsDrawer}\n onClose={() => setShowLogsDrawer(false)}\n execution={selectedExecution}\n taskTitle={selectedTask?.title || ''}\n agentId={selectedTask?.agentId || ''}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}