@burdenoff/microfe-planmagnet 2026.1001.2 → 2026.1001.3

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 (62) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +160 -153
  3. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.js +121 -109
  6. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  7. package/dist/planmagnet/controls/MilestoneControls.d.ts +2 -0
  8. package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -1
  9. package/dist/planmagnet/controls/MilestoneControls.js +89 -72
  10. package/dist/planmagnet/controls/MilestoneControls.js.map +1 -1
  11. package/dist/planmagnet/meetings/MeetingActionItemsPanel.d.ts +17 -0
  12. package/dist/planmagnet/meetings/MeetingActionItemsPanel.d.ts.map +1 -0
  13. package/dist/planmagnet/meetings/MeetingActionItemsPanel.js +218 -0
  14. package/dist/planmagnet/meetings/MeetingActionItemsPanel.js.map +1 -0
  15. package/dist/planmagnet/meetings/MeetingsCard.d.ts +7 -0
  16. package/dist/planmagnet/meetings/MeetingsCard.d.ts.map +1 -0
  17. package/dist/planmagnet/meetings/MeetingsCard.js +108 -0
  18. package/dist/planmagnet/meetings/MeetingsCard.js.map +1 -0
  19. package/dist/planmagnet/meetings/ScheduleCeremoniesDialog.d.ts +9 -0
  20. package/dist/planmagnet/meetings/ScheduleCeremoniesDialog.d.ts.map +1 -0
  21. package/dist/planmagnet/meetings/ScheduleCeremoniesDialog.js +232 -0
  22. package/dist/planmagnet/meetings/ScheduleCeremoniesDialog.js.map +1 -0
  23. package/dist/planmagnet/meetings/ScheduleMeetingDialog.d.ts +19 -0
  24. package/dist/planmagnet/meetings/ScheduleMeetingDialog.d.ts.map +1 -0
  25. package/dist/planmagnet/meetings/ScheduleMeetingDialog.js +226 -0
  26. package/dist/planmagnet/meetings/ScheduleMeetingDialog.js.map +1 -0
  27. package/dist/planmagnet/meetings/UnlinkMeetingDialog.d.ts +14 -0
  28. package/dist/planmagnet/meetings/UnlinkMeetingDialog.d.ts.map +1 -0
  29. package/dist/planmagnet/meetings/UnlinkMeetingDialog.js +81 -0
  30. package/dist/planmagnet/meetings/UnlinkMeetingDialog.js.map +1 -0
  31. package/dist/planmagnet/meetings/meetingActions.d.ts +57 -0
  32. package/dist/planmagnet/meetings/meetingActions.d.ts.map +1 -0
  33. package/dist/planmagnet/meetings/meetingActions.js +51 -0
  34. package/dist/planmagnet/meetings/meetingActions.js.map +1 -0
  35. package/dist/planmagnet/meetings/meetingGraphql.d.ts +29 -0
  36. package/dist/planmagnet/meetings/meetingGraphql.d.ts.map +1 -0
  37. package/dist/planmagnet/meetings/meetingGraphql.js +166 -0
  38. package/dist/planmagnet/meetings/meetingGraphql.js.map +1 -0
  39. package/dist/planmagnet/meetings/meetingModel.d.ts +74 -0
  40. package/dist/planmagnet/meetings/meetingModel.d.ts.map +1 -0
  41. package/dist/planmagnet/meetings/meetingModel.js +88 -0
  42. package/dist/planmagnet/meetings/meetingModel.js.map +1 -0
  43. package/dist/planmagnet/pages/MeetingsMeetingDetailPagePage.d.ts +2 -0
  44. package/dist/planmagnet/pages/MeetingsMeetingDetailPagePage.d.ts.map +1 -0
  45. package/dist/planmagnet/pages/MeetingsMeetingDetailPagePage.js +197 -0
  46. package/dist/planmagnet/pages/MeetingsMeetingDetailPagePage.js.map +1 -0
  47. package/dist/planmagnet/pages/MeetingsMeetingsPagePage.d.ts +2 -0
  48. package/dist/planmagnet/pages/MeetingsMeetingsPagePage.d.ts.map +1 -0
  49. package/dist/planmagnet/pages/MeetingsMeetingsPagePage.js +173 -0
  50. package/dist/planmagnet/pages/MeetingsMeetingsPagePage.js.map +1 -0
  51. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.d.ts.map +1 -1
  52. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js +73 -52
  53. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js.map +1 -1
  54. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  55. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +205 -190
  56. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  57. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  58. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +192 -187
  59. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  60. package/dist/planmagnet/reports/ShareDialog.js +65 -65
  61. package/dist/planmagnet/reports/ShareDialog.js.map +1 -1
  62. package/package.json +1 -1
@@ -0,0 +1,232 @@
1
+ import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
+ import { ErrorLine as n } from "../controls/ConfirmButton.js";
3
+ import { detailPath as r } from "../utils/rowNavigation.js";
4
+ import { DURATION_OPTIONS_MINUTES as i, addMinutesIso as a, localInputValueToISO as o, rruleUntil as s, viewerTimeZone as c } from "./meetingModel.js";
5
+ import { CREATE_CALENDAR_EVENT as l, DELETE_CALENDAR_EVENT as u, LINK_PLANMAGNET_MEETING as d, MEETING_QUERIES as f, SCHEDULE_MEETING_CALENDARS as p } from "./meetingGraphql.js";
6
+ import { createAndLinkMeeting as m, meetingScheduleErrorMessage as h } from "./meetingActions.js";
7
+ import { useState as g } from "react";
8
+ import { CalendarRange as _, CheckCircle2 as v, XCircle as y } from "lucide-react";
9
+ import { useMutation as b, useQuery as x } from "@apollo/client/react";
10
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
11
+ import { Button as w, Input as T, ResponsiveDialog as E, ResponsiveDialogContent as D, ResponsiveDialogDescription as O, ResponsiveDialogFooter as k, ResponsiveDialogHeader as ee, ResponsiveDialogTitle as te, ResponsiveDialogTrigger as ne } from "@burdenoff/fe-libs/ui";
12
+ //#region src/planmagnet/meetings/ScheduleCeremoniesDialog.tsx
13
+ var A = "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60";
14
+ function j(e, t) {
15
+ return `${e.slice(0, 10)}T${t}`;
16
+ }
17
+ function M(e, t) {
18
+ return [
19
+ {
20
+ key: "PLANNING",
21
+ label: "Planning",
22
+ kind: "PLANNING",
23
+ startLocal: j(e, "10:00"),
24
+ durationMinutes: 60,
25
+ recurs: !1
26
+ },
27
+ {
28
+ key: "STANDUP",
29
+ label: "Daily standup",
30
+ kind: "STANDUP",
31
+ startLocal: j(e, "09:30"),
32
+ durationMinutes: 15,
33
+ recurs: !0
34
+ },
35
+ {
36
+ key: "REVIEW",
37
+ label: "Review",
38
+ kind: "REVIEW",
39
+ startLocal: j(t, "14:00"),
40
+ durationMinutes: 60,
41
+ recurs: !1
42
+ },
43
+ {
44
+ key: "RETROSPECTIVE",
45
+ label: "Retrospective",
46
+ kind: "RETROSPECTIVE",
47
+ startLocal: j(t, "15:15"),
48
+ durationMinutes: 45,
49
+ recurs: !1
50
+ }
51
+ ];
52
+ }
53
+ function N({ projectId: j, sprintId: N, startDate: P, endDate: F }) {
54
+ let { workspaceId: I } = e(), L = t(), [R, z] = g(!1), [B, V] = g(""), [H, U] = g([]), [W, G] = g(!1), [K, q] = g(null), [J, Y] = g(null), { data: X, loading: Z } = x(p, {
55
+ variables: { pagination: { limit: 100 } },
56
+ skip: !R
57
+ }), re = X?.calendars.calendars ?? [], [ie] = b(l), [ae] = b(d, {
58
+ refetchQueries: f,
59
+ awaitRefetchQueries: !0
60
+ }), [oe] = b(u), Q = !!(P && F), se = (e) => {
61
+ z(e), Y(null), q(null), G(!1), e && P && F && (U(M(P, F)), V(""));
62
+ }, $ = (e, t) => U((n) => n.map((n) => n.key === e ? {
63
+ ...n,
64
+ ...t
65
+ } : n)), ce = async () => {
66
+ if (!I || !B || !P || !F) {
67
+ Y("Pick a calendar first.");
68
+ return;
69
+ }
70
+ Y(null), G(!0);
71
+ let e = [];
72
+ for (let t of H) {
73
+ let n = o(t.startLocal);
74
+ if (!n) {
75
+ e.push({
76
+ key: t.key,
77
+ label: t.label,
78
+ status: "error",
79
+ message: "Invalid start time."
80
+ });
81
+ continue;
82
+ }
83
+ let r = a(n, t.durationMinutes), i = t.recurs ? /* @__PURE__ */ new Date(`${F.slice(0, 10)}T23:59:59.000Z`) : null, l = t.recurs ? `FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR;UNTIL=${s(i)}` : void 0;
84
+ try {
85
+ let i = await m({
86
+ createEvent: ie,
87
+ linkMeeting: ae,
88
+ deleteEvent: oe,
89
+ workspaceId: I,
90
+ calendarId: B,
91
+ title: t.label,
92
+ startIso: n,
93
+ endIso: r,
94
+ kind: t.kind,
95
+ projectId: j,
96
+ sprintId: N,
97
+ agenda: "",
98
+ rrule: l,
99
+ timezone: c()
100
+ });
101
+ e.push({
102
+ key: t.key,
103
+ label: t.label,
104
+ status: "success",
105
+ meetingId: i.id
106
+ });
107
+ } catch (n) {
108
+ e.push({
109
+ key: t.key,
110
+ label: t.label,
111
+ status: "error",
112
+ message: h(n)
113
+ });
114
+ }
115
+ }
116
+ q(e), G(!1);
117
+ };
118
+ return /* @__PURE__ */ C(E, {
119
+ open: R,
120
+ onOpenChange: se,
121
+ children: [/* @__PURE__ */ C("div", {
122
+ className: "flex flex-col items-end gap-1",
123
+ children: [/* @__PURE__ */ S(ne, {
124
+ asChild: !0,
125
+ children: /* @__PURE__ */ C(w, {
126
+ size: "sm",
127
+ variant: "outline",
128
+ disabled: !Q,
129
+ "data-testid": "schedule-ceremonies-trigger",
130
+ children: [/* @__PURE__ */ S(_, { className: "mr-1 h-4 w-4" }), "Schedule ceremonies"]
131
+ })
132
+ }), !Q && /* @__PURE__ */ S("span", {
133
+ className: "text-xs text-muted-foreground text-right max-w-[14rem]",
134
+ children: "Set this sprint's start and end dates to propose its ceremonies."
135
+ })]
136
+ }), /* @__PURE__ */ C(D, {
137
+ className: "max-w-xl",
138
+ children: [
139
+ /* @__PURE__ */ C(ee, { children: [/* @__PURE__ */ S(te, { children: "Schedule ceremonies" }), /* @__PURE__ */ S(O, { children: "Planning at the sprint's start, a weekday daily standup through its length, and Review + Retrospective at its end. Edit any time before scheduling." })] }),
140
+ K ? /* @__PURE__ */ S("div", {
141
+ className: "space-y-2",
142
+ "data-testid": "schedule-ceremonies-results",
143
+ children: K.map((e) => /* @__PURE__ */ C("div", {
144
+ className: "flex items-center justify-between gap-2 rounded-md border p-2 text-sm",
145
+ "data-testid": "schedule-ceremonies-result-row",
146
+ children: [/* @__PURE__ */ C("span", {
147
+ className: "flex items-center gap-2",
148
+ children: [e.status === "success" ? /* @__PURE__ */ S(v, { className: "h-4 w-4 text-status-success-text" }) : /* @__PURE__ */ S(y, { className: "h-4 w-4 text-destructive" }), e.label]
149
+ }), e.status === "success" ? /* @__PURE__ */ S("button", {
150
+ type: "button",
151
+ className: "text-primary hover:underline",
152
+ onClick: () => {
153
+ z(!1), L(r("/meetings/meeting-detail-page", e.meetingId));
154
+ },
155
+ children: "View"
156
+ }) : /* @__PURE__ */ S("span", {
157
+ className: "text-destructive text-xs",
158
+ children: e.message
159
+ })]
160
+ }, e.key))
161
+ }) : /* @__PURE__ */ C("div", {
162
+ className: "space-y-3",
163
+ children: [
164
+ /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("label", {
165
+ htmlFor: "ceremonies-calendar",
166
+ className: "text-sm font-medium",
167
+ children: "Calendar"
168
+ }), /* @__PURE__ */ C("select", {
169
+ id: "ceremonies-calendar",
170
+ className: A,
171
+ value: B,
172
+ disabled: Z,
173
+ onChange: (e) => V(e.target.value),
174
+ "data-testid": "ceremonies-calendar",
175
+ children: [/* @__PURE__ */ S("option", {
176
+ value: "",
177
+ children: Z ? "Loading calendars…" : "Choose a calendar"
178
+ }), re.map((e) => /* @__PURE__ */ C("option", {
179
+ value: e.id,
180
+ children: [e.name, e.isDefault ? " (default)" : ""]
181
+ }, e.id))]
182
+ })] }),
183
+ H.map((e) => /* @__PURE__ */ C("div", {
184
+ className: "grid grid-cols-[1fr_auto_auto] items-end gap-2",
185
+ children: [/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ C("label", {
186
+ htmlFor: `ceremony-start-${e.key}`,
187
+ className: "text-xs text-muted-foreground",
188
+ children: [e.label, e.recurs ? " (weekdays, first occurrence)" : ""]
189
+ }), /* @__PURE__ */ S(T, {
190
+ id: `ceremony-start-${e.key}`,
191
+ type: "datetime-local",
192
+ className: "mt-1",
193
+ value: e.startLocal,
194
+ onChange: (t) => $(e.key, { startLocal: t.target.value }),
195
+ "data-testid": `ceremony-start-${e.key}`
196
+ })] }), /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("label", {
197
+ htmlFor: `ceremony-duration-${e.key}`,
198
+ className: "text-xs text-muted-foreground",
199
+ children: "Duration"
200
+ }), /* @__PURE__ */ S("select", {
201
+ id: `ceremony-duration-${e.key}`,
202
+ className: A,
203
+ value: e.durationMinutes,
204
+ onChange: (t) => $(e.key, { durationMinutes: Number(t.target.value) }),
205
+ "data-testid": `ceremony-duration-${e.key}`,
206
+ children: i.map((e) => /* @__PURE__ */ C("option", {
207
+ value: e,
208
+ children: [e, "m"]
209
+ }, e))
210
+ })] })]
211
+ }, e.key)),
212
+ J && /* @__PURE__ */ S(n, { message: J })
213
+ ]
214
+ }),
215
+ /* @__PURE__ */ C(k, { children: [/* @__PURE__ */ S(w, {
216
+ variant: "outline",
217
+ onClick: () => z(!1),
218
+ children: K ? "Close" : "Cancel"
219
+ }), !K && /* @__PURE__ */ S(w, {
220
+ onClick: () => void ce(),
221
+ disabled: W || !B,
222
+ "data-testid": "schedule-ceremonies-submit",
223
+ children: W ? "Scheduling…" : `Schedule ${H.length} ceremonies`
224
+ })] })
225
+ ]
226
+ })]
227
+ });
228
+ }
229
+ //#endregion
230
+ export { N as ScheduleCeremoniesDialog };
231
+
232
+ //# sourceMappingURL=ScheduleCeremoniesDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScheduleCeremoniesDialog.js","names":[],"sources":["../../../src/planmagnet/meetings/ScheduleCeremoniesDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { CalendarRange, CheckCircle2, XCircle } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { ErrorLine } from '../controls/ConfirmButton';\nimport {\n type MeetingKind,\n DURATION_OPTIONS_MINUTES,\n localInputValueToISO,\n addMinutesIso,\n viewerTimeZone,\n rruleUntil,\n} from './meetingModel';\nimport {\n SCHEDULE_MEETING_CALENDARS,\n CREATE_CALENDAR_EVENT,\n DELETE_CALENDAR_EVENT,\n LINK_PLANMAGNET_MEETING,\n MEETING_QUERIES,\n} from './meetingGraphql';\nimport { createAndLinkMeeting, meetingScheduleErrorMessage } from './meetingActions';\n\n/**\n * CWF-23: propose a sprint's standard ceremonies from its start/end dates (Planning at start, a weekday Daily\n * standup for the sprint's length, Review and Retrospective at the end), let the times be edited, then create\n * them one at a time — reporting each one's own success or failure, since a partial result (three ceremonies\n * scheduled, one refused) is a real possible outcome, never collapsed into one pass/fail.\n */\n\nconst fieldClass =\n 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60';\n\n/** A sprint's planned start/end are UTC-midnight calendar days (`SprintDates.tsx`'s convention) — combine the\n * day as-is with a local time-of-day rather than converting the UTC instant to local, which could shift the\n * calendar day itself near a time-zone boundary. */\nfunction combineDayAndTime(dayIso: string, hhmm: string): string {\n return `${dayIso.slice(0, 10)}T${hhmm}`;\n}\n\ninterface CeremonyDraft {\n key: 'PLANNING' | 'STANDUP' | 'REVIEW' | 'RETROSPECTIVE';\n label: string;\n kind: MeetingKind;\n startLocal: string;\n durationMinutes: number;\n /** Only the standup repeats — every weekday through the sprint's last day. */\n recurs: boolean;\n}\n\nfunction defaultCeremonies(startDate: string, endDate: string): CeremonyDraft[] {\n return [\n {\n key: 'PLANNING',\n label: 'Planning',\n kind: 'PLANNING',\n startLocal: combineDayAndTime(startDate, '10:00'),\n durationMinutes: 60,\n recurs: false,\n },\n {\n key: 'STANDUP',\n label: 'Daily standup',\n kind: 'STANDUP',\n startLocal: combineDayAndTime(startDate, '09:30'),\n durationMinutes: 15,\n recurs: true,\n },\n {\n key: 'REVIEW',\n label: 'Review',\n kind: 'REVIEW',\n startLocal: combineDayAndTime(endDate, '14:00'),\n durationMinutes: 60,\n recurs: false,\n },\n {\n key: 'RETROSPECTIVE',\n label: 'Retrospective',\n kind: 'RETROSPECTIVE',\n startLocal: combineDayAndTime(endDate, '15:15'),\n durationMinutes: 45,\n recurs: false,\n },\n ];\n}\n\ninterface CalendarOption {\n id: string;\n name: string;\n isDefault: boolean;\n}\n\ntype CeremonyResult = {\n key: CeremonyDraft['key'];\n label: string;\n} & ({ status: 'success'; meetingId: string } | { status: 'error'; message: string });\n\nexport function ScheduleCeremoniesDialog({\n projectId,\n sprintId,\n startDate,\n endDate,\n}: {\n projectId: string;\n sprintId: string;\n /** Planned start/end (UTC-midnight calendar days). Null disables the whole flow — a sprint with no dates has\n * nothing to propose against. */\n startDate: string | null;\n endDate: string | null;\n}) {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [open, setOpen] = useState(false);\n const [calendarId, setCalendarId] = useState('');\n const [ceremonies, setCeremonies] = useState<CeremonyDraft[]>([]);\n const [submitting, setSubmitting] = useState(false);\n const [results, setResults] = useState<CeremonyResult[] | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const { data: calendarsData, loading: calendarsLoading } = useQuery<{\n calendars: { calendars: CalendarOption[] };\n }>(SCHEDULE_MEETING_CALENDARS, { variables: { pagination: { limit: 100 } }, skip: !open });\n const calendars = calendarsData?.calendars.calendars ?? [];\n\n const [createEvent] = useMutation<{ createEvent: { id: string } }>(CREATE_CALENDAR_EVENT);\n const [linkMeeting] = useMutation<{ planmagnetLinkMeeting: { id: string } }>(\n LINK_PLANMAGNET_MEETING,\n { refetchQueries: MEETING_QUERIES, awaitRefetchQueries: true }\n );\n const [deleteEvent] = useMutation<{ deleteEvent: boolean }>(DELETE_CALENDAR_EVENT);\n\n const canSchedule = Boolean(startDate && endDate);\n\n const openDialog = (next: boolean) => {\n setOpen(next);\n setError(null);\n setResults(null);\n setSubmitting(false);\n if (next && startDate && endDate) {\n setCeremonies(defaultCeremonies(startDate, endDate));\n setCalendarId('');\n }\n };\n\n const updateCeremony = (key: CeremonyDraft['key'], patch: Partial<CeremonyDraft>) =>\n setCeremonies((prev) => prev.map((c) => (c.key === key ? { ...c, ...patch } : c)));\n\n const scheduleAll = async () => {\n if (!workspaceId || !calendarId || !startDate || !endDate) {\n setError('Pick a calendar first.');\n return;\n }\n setError(null);\n setSubmitting(true);\n const outcomes: CeremonyResult[] = [];\n // One at a time (not Promise.all): a partial result must still report which ceremonies actually succeeded,\n // and a later failure must never be mistaken for an earlier one.\n for (const c of ceremonies) {\n const startIso = localInputValueToISO(c.startLocal);\n if (!startIso) {\n outcomes.push({\n key: c.key,\n label: c.label,\n status: 'error',\n message: 'Invalid start time.',\n });\n continue;\n }\n const endIso = addMinutesIso(startIso, c.durationMinutes);\n // The standup's series runs through the sprint's own last day — end of that UTC calendar day, so the\n // final standup still falls on the sprint's actual end date regardless of the viewer's own time zone.\n const until = c.recurs ? new Date(`${endDate.slice(0, 10)}T23:59:59.000Z`) : null;\n const rrule = c.recurs\n ? `FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR;UNTIL=${rruleUntil(until!)}`\n : undefined;\n try {\n const meeting = await createAndLinkMeeting({\n createEvent,\n linkMeeting,\n deleteEvent,\n workspaceId,\n calendarId,\n title: c.label,\n startIso,\n endIso,\n kind: c.kind,\n projectId,\n sprintId,\n agenda: '',\n rrule,\n timezone: viewerTimeZone(),\n });\n outcomes.push({ key: c.key, label: c.label, status: 'success', meetingId: meeting.id });\n } catch (e) {\n outcomes.push({\n key: c.key,\n label: c.label,\n status: 'error',\n message: meetingScheduleErrorMessage(e),\n });\n }\n }\n setResults(outcomes);\n setSubmitting(false);\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={openDialog}>\n <div className=\"flex flex-col items-end gap-1\">\n {/* Not wrapped together with the explanation text in one `asChild` child: that would make clicking the\n text (outside the disabled button but inside a shared wrapper) still fire the Radix trigger. A plain\n disabled <button> never dispatches a click at all, so keeping it as the trigger's one child is what\n actually blocks opening the dialog — the explanation stays a separate sibling. */}\n <ResponsiveDialogTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={!canSchedule}\n data-testid=\"schedule-ceremonies-trigger\"\n >\n <CalendarRange className=\"mr-1 h-4 w-4\" />\n Schedule ceremonies\n </Button>\n </ResponsiveDialogTrigger>\n {!canSchedule && (\n <span className=\"text-xs text-muted-foreground text-right max-w-[14rem]\">\n Set this sprint&apos;s start and end dates to propose its ceremonies.\n </span>\n )}\n </div>\n <ResponsiveDialogContent className=\"max-w-xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule ceremonies</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Planning at the sprint&apos;s start, a weekday daily standup through its length, and\n Review + Retrospective at its end. Edit any time before scheduling.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n {results ? (\n <div className=\"space-y-2\" data-testid=\"schedule-ceremonies-results\">\n {results.map((r) => (\n <div\n key={r.key}\n className=\"flex items-center justify-between gap-2 rounded-md border p-2 text-sm\"\n data-testid=\"schedule-ceremonies-result-row\"\n >\n <span className=\"flex items-center gap-2\">\n {r.status === 'success' ? (\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" />\n ) : (\n <XCircle className=\"h-4 w-4 text-destructive\" />\n )}\n {r.label}\n </span>\n {r.status === 'success' ? (\n <button\n type=\"button\"\n className=\"text-primary hover:underline\"\n onClick={() => {\n setOpen(false);\n navigate(detailPath('/meetings/meeting-detail-page', r.meetingId));\n }}\n >\n View\n </button>\n ) : (\n <span className=\"text-destructive text-xs\">{r.message}</span>\n )}\n </div>\n ))}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <div>\n <label htmlFor=\"ceremonies-calendar\" className=\"text-sm font-medium\">\n Calendar\n </label>\n <select\n id=\"ceremonies-calendar\"\n className={fieldClass}\n value={calendarId}\n disabled={calendarsLoading}\n onChange={(e) => setCalendarId(e.target.value)}\n data-testid=\"ceremonies-calendar\"\n >\n <option value=\"\">\n {calendarsLoading ? 'Loading calendars…' : 'Choose a calendar'}\n </option>\n {calendars.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n {c.isDefault ? ' (default)' : ''}\n </option>\n ))}\n </select>\n </div>\n {ceremonies.map((c) => (\n <div key={c.key} className=\"grid grid-cols-[1fr_auto_auto] items-end gap-2\">\n <div>\n <label\n htmlFor={`ceremony-start-${c.key}`}\n className=\"text-xs text-muted-foreground\"\n >\n {c.label}\n {c.recurs ? ' (weekdays, first occurrence)' : ''}\n </label>\n <Input\n id={`ceremony-start-${c.key}`}\n type=\"datetime-local\"\n className=\"mt-1\"\n value={c.startLocal}\n onChange={(e) => updateCeremony(c.key, { startLocal: e.target.value })}\n data-testid={`ceremony-start-${c.key}`}\n />\n </div>\n <div>\n <label\n htmlFor={`ceremony-duration-${c.key}`}\n className=\"text-xs text-muted-foreground\"\n >\n Duration\n </label>\n <select\n id={`ceremony-duration-${c.key}`}\n className={fieldClass}\n value={c.durationMinutes}\n onChange={(e) =>\n updateCeremony(c.key, { durationMinutes: Number(e.target.value) })\n }\n data-testid={`ceremony-duration-${c.key}`}\n >\n {DURATION_OPTIONS_MINUTES.map((m) => (\n <option key={m} value={m}>\n {m}m\n </option>\n ))}\n </select>\n </div>\n </div>\n ))}\n {error && <ErrorLine message={error} />}\n </div>\n )}\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n {results ? 'Close' : 'Cancel'}\n </Button>\n {!results && (\n <Button\n onClick={() => void scheduleAll()}\n disabled={submitting || !calendarId}\n data-testid=\"schedule-ceremonies-submit\"\n >\n {submitting ? 'Scheduling…' : `Schedule ${ceremonies.length} ceremonies`}\n </Button>\n )}\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAyCA,IAAM,IACJ;AAKF,SAAS,EAAkB,GAAgB,GAAsB;AAC/D,QAAO,GAAG,EAAO,MAAM,GAAG,GAAG,CAAC,GAAG;;AAanC,SAAS,EAAkB,GAAmB,GAAkC;AAC9E,QAAO;EACL;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,YAAY,EAAkB,GAAW,QAAQ;GACjD,iBAAiB;GACjB,QAAQ;GACT;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,YAAY,EAAkB,GAAW,QAAQ;GACjD,iBAAiB;GACjB,QAAQ;GACT;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,YAAY,EAAkB,GAAS,QAAQ;GAC/C,iBAAiB;GACjB,QAAQ;GACT;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,YAAY,EAAkB,GAAS,QAAQ;GAC/C,iBAAiB;GACjB,QAAQ;GACT;EACF;;AAcH,SAAgB,EAAyB,EACvC,cACA,aACA,cACA,cAQC;CACD,IAAM,EAAE,mBAAgB,GAAe,EACjC,IAAW,GAAuB,EAClC,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAY,KAAiB,EAA0B,EAAE,CAAC,EAC3D,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAS,KAAc,EAAkC,KAAK,EAC/D,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,EAAE,MAAM,GAAe,SAAS,MAAqB,EAExD,GAA4B;EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE;EAAE,MAAM,CAAC;EAAM,CAAC,EACpF,KAAY,GAAe,UAAU,aAAa,EAAE,EAEpD,CAAC,MAAe,EAA6C,EAAsB,EACnF,CAAC,MAAe,EACpB,GACA;EAAE,gBAAgB;EAAiB,qBAAqB;EAAM,CAC/D,EACK,CAAC,MAAe,EAAsC,EAAsB,EAE5E,IAAc,GAAQ,KAAa,IAEnC,MAAc,MAAkB;AAKpC,EAJA,EAAQ,EAAK,EACb,EAAS,KAAK,EACd,EAAW,KAAK,EAChB,EAAc,GAAM,EAChB,KAAQ,KAAa,MACvB,EAAc,EAAkB,GAAW,EAAQ,CAAC,EACpD,EAAc,GAAG;IAIf,KAAkB,GAA2B,MACjD,GAAe,MAAS,EAAK,KAAK,MAAO,EAAE,QAAQ,IAAM;EAAE,GAAG;EAAG,GAAG;EAAO,GAAG,EAAG,CAAC,EAE9E,KAAc,YAAY;AAC9B,MAAI,CAAC,KAAe,CAAC,KAAc,CAAC,KAAa,CAAC,GAAS;AACzD,KAAS,yBAAyB;AAClC;;AAGF,EADA,EAAS,KAAK,EACd,EAAc,GAAK;EACnB,IAAM,IAA6B,EAAE;AAGrC,OAAK,IAAM,KAAK,GAAY;GAC1B,IAAM,IAAW,EAAqB,EAAE,WAAW;AACnD,OAAI,CAAC,GAAU;AACb,MAAS,KAAK;KACZ,KAAK,EAAE;KACP,OAAO,EAAE;KACT,QAAQ;KACR,SAAS;KACV,CAAC;AACF;;GAEF,IAAM,IAAS,EAAc,GAAU,EAAE,gBAAgB,EAGnD,IAAQ,EAAE,yBAAS,IAAI,KAAK,GAAG,EAAQ,MAAM,GAAG,GAAG,CAAC,gBAAgB,GAAG,MACvE,IAAQ,EAAE,SACZ,yCAAyC,EAAW,EAAO,KAC3D,KAAA;AACJ,OAAI;IACF,IAAM,IAAU,MAAM,EAAqB;KACzC;KACA;KACA;KACA;KACA;KACA,OAAO,EAAE;KACT;KACA;KACA,MAAM,EAAE;KACR;KACA;KACA,QAAQ;KACR;KACA,UAAU,GAAgB;KAC3B,CAAC;AACF,MAAS,KAAK;KAAE,KAAK,EAAE;KAAK,OAAO,EAAE;KAAO,QAAQ;KAAW,WAAW,EAAQ;KAAI,CAAC;YAChF,GAAG;AACV,MAAS,KAAK;KACZ,KAAK,EAAE;KACP,OAAO,EAAE;KACT,QAAQ;KACR,SAAS,EAA4B,EAAE;KACxC,CAAC;;;AAIN,EADA,EAAW,EAAS,EACpB,EAAc,GAAM;;AAGtB,QACE,kBAAC,GAAD;EAAwB;EAAM,cAAc;YAA5C,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CAKE,kBAAC,IAAD;IAAyB,SAAA;cACvB,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,UAAU,CAAC;KACX,eAAY;eAJd,CAME,kBAAC,GAAD,EAAe,WAAU,gBAAiB,CAAA,EAAA,sBAEnC;;IACe,CAAA,EACzB,CAAC,KACA,kBAAC,QAAD;IAAM,WAAU;cAAyD;IAElE,CAAA,CAEL;MACN,kBAAC,GAAD;GAAyB,WAAU;aAAnC;IACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAuB,uBAA2C,CAAA,EAClE,kBAAC,GAAD,EAAA,UAA6B,uJAGC,CAAA,CACP,EAAA,CAAA;IACxB,IACC,kBAAC,OAAD;KAAK,WAAU;KAAY,eAAY;eACpC,EAAQ,KAAK,MACZ,kBAAC,OAAD;MAEE,WAAU;MACV,eAAY;gBAHd,CAKE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACG,EAAE,WAAW,YACZ,kBAAC,GAAD,EAAc,WAAU,oCAAqC,CAAA,GAE7D,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,EAEjD,EAAE,MACE;UACN,EAAE,WAAW,YACZ,kBAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,eAAe;AAEb,QADA,EAAQ,GAAM,EACd,EAAS,EAAW,iCAAiC,EAAE,UAAU,CAAC;;iBAErE;OAEQ,CAAA,GAET,kBAAC,QAAD;OAAM,WAAU;iBAA4B,EAAE;OAAe,CAAA,CAE3D;QA1BC,EAAE,IA0BH,CACN;KACE,CAAA,GAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAsB,WAAU;iBAAsB;OAE7D,CAAA,EACR,kBAAC,UAAD;OACE,IAAG;OACH,WAAW;OACX,OAAO;OACP,UAAU;OACV,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;OAC9C,eAAY;iBANd,CAQE,kBAAC,UAAD;QAAQ,OAAM;kBACX,IAAmB,uBAAuB;QACpC,CAAA,EACR,GAAU,KAAK,MACd,kBAAC,UAAD;QAAmB,OAAO,EAAE;kBAA5B,CACG,EAAE,MACF,EAAE,YAAY,eAAe,GACvB;UAHI,EAAE,GAGN,CACT,CACK;SACL,EAAA,CAAA;MACL,EAAW,KAAK,MACf,kBAAC,OAAD;OAAiB,WAAU;iBAA3B,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAS,kBAAkB,EAAE;QAC7B,WAAU;kBAFZ,CAIG,EAAE,OACF,EAAE,SAAS,kCAAkC,GACxC;WACR,kBAAC,GAAD;QACE,IAAI,kBAAkB,EAAE;QACxB,MAAK;QACL,WAAU;QACV,OAAO,EAAE;QACT,WAAW,MAAM,EAAe,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,OAAO,CAAC;QACtE,eAAa,kBAAkB,EAAE;QACjC,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAS,qBAAqB,EAAE;QAChC,WAAU;kBACX;QAEO,CAAA,EACR,kBAAC,UAAD;QACE,IAAI,qBAAqB,EAAE;QAC3B,WAAW;QACX,OAAO,EAAE;QACT,WAAW,MACT,EAAe,EAAE,KAAK,EAAE,iBAAiB,OAAO,EAAE,OAAO,MAAM,EAAE,CAAC;QAEpE,eAAa,qBAAqB,EAAE;kBAEnC,EAAyB,KAAK,MAC7B,kBAAC,UAAD;SAAgB,OAAO;mBAAvB,CACG,GAAE,IACI;WAFI,EAEJ,CACT;QACK,CAAA,CACL,EAAA,CAAA,CACF;SAzCI,EAAE,IAyCN,CACN;MACD,KAAS,kBAAC,GAAD,EAAW,SAAS,GAAS,CAAA;MACnC;;IAER,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,eAAe,EAAQ,GAAM;eACpD,IAAU,UAAU;KACd,CAAA,EACR,CAAC,KACA,kBAAC,GAAD;KACE,eAAe,KAAK,IAAa;KACjC,UAAU,KAAc,CAAC;KACzB,eAAY;eAEX,IAAa,gBAAgB,YAAY,EAAW,OAAO;KACrD,CAAA,CAEY,EAAA,CAAA;IACD;KACT"}
@@ -0,0 +1,19 @@
1
+ import { ReactNode } from 'react';
2
+ import { MeetingKind } from './meetingModel';
3
+
4
+ export declare function ScheduleMeetingDialog({ projectId, sprintId, milestoneId, defaultKind, lockKind, defaultTitle, trigger, triggerLabel, onScheduled, }: {
5
+ projectId: string;
6
+ sprintId?: string | null;
7
+ milestoneId?: string | null;
8
+ /** Pre-selects a kind — e.g. MILESTONE_CELEBRATION/DEMO from a milestone's own page. */
9
+ defaultKind?: MeetingKind;
10
+ /** Hides the kind picker, for a trigger whose kind the context already decided (Schedule celebration/demo). */
11
+ lockKind?: boolean;
12
+ defaultTitle?: string;
13
+ trigger?: ReactNode;
14
+ triggerLabel?: string;
15
+ onScheduled?: (meeting: {
16
+ id: string;
17
+ }) => void;
18
+ }): import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=ScheduleMeetingDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScheduleMeetingDialog.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/meetings/ScheduleMeetingDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAkBjD,OAAO,EAEL,KAAK,WAAW,EAWjB,MAAM,gBAAgB,CAAC;AA0BxB,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,QAAe,EACf,WAAkB,EAClB,WAAqB,EACrB,QAAgB,EAChB,YAAiB,EACjB,OAAO,EACP,YAAiC,EACjC,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,wFAAwF;IACxF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,+GAA+G;IAC/G,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjD,2CAmRA"}
@@ -0,0 +1,226 @@
1
+ import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
2
+ import { ErrorLine as t } from "../controls/ConfirmButton.js";
3
+ import { enumLabel as n } from "../controls/WorkItemControls.js";
4
+ import { DURATION_OPTIONS_MINUTES as r, MEETING_KINDS as ee, RECURRENCE_LABELS as i, RECURRENCE_OPTIONS as a, addMinutesIso as o, buildRRule as te, defaultLocalStart as s, localInputValueToISO as ne, validateMeetingDraft as re, viewerTimeZone as ie } from "./meetingModel.js";
5
+ import { CREATE_CALENDAR_EVENT as ae, DELETE_CALENDAR_EVENT as oe, LINK_PLANMAGNET_MEETING as se, MEETING_QUERIES as ce, SCHEDULE_MEETING_CALENDARS as c } from "./meetingGraphql.js";
6
+ import { createAndLinkMeeting as l, meetingScheduleErrorMessage as u } from "./meetingActions.js";
7
+ import { useState as d } from "react";
8
+ import { CalendarPlus as f } from "lucide-react";
9
+ import { useMutation as p, useQuery as m } from "@apollo/client/react";
10
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
11
+ import { Button as _, Input as v, ResponsiveDialog as le, ResponsiveDialogContent as ue, ResponsiveDialogDescription as de, ResponsiveDialogFooter as fe, ResponsiveDialogHeader as pe, ResponsiveDialogTitle as me, ResponsiveDialogTrigger as he, Textarea as ge } from "@burdenoff/fe-libs/ui";
12
+ //#region src/planmagnet/meetings/ScheduleMeetingDialog.tsx
13
+ var y = "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60";
14
+ function b({ projectId: b, sprintId: x = null, milestoneId: S = null, defaultKind: C = "OTHER", lockKind: _e = !1, defaultTitle: w = "", trigger: ve, triggerLabel: T = "Schedule meeting", onScheduled: E }) {
15
+ let { workspaceId: D } = e(), [O, k] = d(!1), [A, j] = d(w), [M, N] = d(C), [P, F] = d(""), [I, L] = d(() => s()), [R, z] = d(30), [B, V] = d("NONE"), [H, U] = d(""), [W, G] = d(null), [K, q] = d(!1), { data: ye, loading: J } = m(c, {
16
+ variables: { pagination: { limit: 100 } },
17
+ skip: !O
18
+ }), Y = ye?.calendars.calendars ?? [], X = (Y.find((e) => e.isDefault) ?? Y[0])?.id ?? "", Z = P || X, [be] = p(ae), [xe] = p(se, {
19
+ refetchQueries: ce,
20
+ awaitRefetchQueries: !0
21
+ }), [Se] = p(oe), Q = () => {
22
+ j(w), N(C), F(""), L(s()), z(30), V("NONE"), U(""), G(null);
23
+ }, Ce = async () => {
24
+ let e = re({
25
+ title: A,
26
+ calendarId: Z,
27
+ startLocal: I,
28
+ durationMinutes: R,
29
+ kind: M,
30
+ milestoneId: S
31
+ });
32
+ if (e) {
33
+ G(e);
34
+ return;
35
+ }
36
+ let t = ne(I);
37
+ if (!t || !D) {
38
+ G("Pick a start time.");
39
+ return;
40
+ }
41
+ G(null), q(!0);
42
+ try {
43
+ let e = o(t, R), n = te(B, I), r = await l({
44
+ createEvent: be,
45
+ linkMeeting: xe,
46
+ deleteEvent: Se,
47
+ workspaceId: D,
48
+ calendarId: Z,
49
+ title: A.trim(),
50
+ startIso: t,
51
+ endIso: e,
52
+ kind: M,
53
+ projectId: b,
54
+ sprintId: x,
55
+ milestoneId: S,
56
+ agenda: H.trim(),
57
+ rrule: n,
58
+ timezone: ie()
59
+ });
60
+ k(!1), Q(), E?.(r);
61
+ } catch (e) {
62
+ G(u(e));
63
+ } finally {
64
+ q(!1);
65
+ }
66
+ }, $ = O && !J && Y.length === 0;
67
+ return /* @__PURE__ */ g(le, {
68
+ open: O,
69
+ onOpenChange: (e) => {
70
+ k(e), e ? Q() : G(null);
71
+ },
72
+ children: [/* @__PURE__ */ h(he, {
73
+ asChild: !0,
74
+ children: ve ?? /* @__PURE__ */ g(_, {
75
+ size: "sm",
76
+ variant: "outline",
77
+ "data-testid": "schedule-meeting-trigger",
78
+ children: [/* @__PURE__ */ h(f, { className: "mr-1 h-4 w-4" }), T]
79
+ })
80
+ }), /* @__PURE__ */ g(ue, {
81
+ className: "max-w-lg",
82
+ children: [
83
+ /* @__PURE__ */ g(pe, { children: [/* @__PURE__ */ h(me, { children: "Schedule a meeting" }), /* @__PURE__ */ g(de, { children: [
84
+ "Creates a calendar event and links it to this ",
85
+ S ? "milestone" : x ? "sprint" : "project",
86
+ "."
87
+ ] })] }),
88
+ $ ? /* @__PURE__ */ g("div", {
89
+ className: "space-y-2 text-sm",
90
+ "data-testid": "schedule-meeting-no-calendars",
91
+ children: [/* @__PURE__ */ h("p", {
92
+ className: "text-muted-foreground",
93
+ children: "You have no calendars yet. Create one in Calendar settings, then come back to schedule this meeting."
94
+ }), /* @__PURE__ */ h("a", {
95
+ href: "/calendar/settings",
96
+ className: "text-primary hover:underline",
97
+ "data-testid": "schedule-meeting-calendar-settings-link",
98
+ children: "Open Calendar settings"
99
+ })]
100
+ }) : /* @__PURE__ */ g("div", {
101
+ className: "space-y-3",
102
+ children: [
103
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
104
+ htmlFor: "schedule-meeting-title",
105
+ className: "text-sm font-medium",
106
+ children: "Title"
107
+ }), /* @__PURE__ */ h(v, {
108
+ id: "schedule-meeting-title",
109
+ className: "mt-1",
110
+ value: A,
111
+ onChange: (e) => j(e.target.value),
112
+ "data-testid": "schedule-meeting-title"
113
+ })] }),
114
+ /* @__PURE__ */ g("div", {
115
+ className: "grid grid-cols-2 gap-3",
116
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
117
+ htmlFor: "schedule-meeting-kind",
118
+ className: "text-sm font-medium",
119
+ children: "Kind"
120
+ }), /* @__PURE__ */ h("select", {
121
+ id: "schedule-meeting-kind",
122
+ className: y,
123
+ value: M,
124
+ disabled: _e,
125
+ onChange: (e) => N(e.target.value),
126
+ "data-testid": "schedule-meeting-kind",
127
+ children: ee.map((e) => /* @__PURE__ */ h("option", {
128
+ value: e,
129
+ children: n(e)
130
+ }, e))
131
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
132
+ htmlFor: "schedule-meeting-calendar",
133
+ className: "text-sm font-medium",
134
+ children: "Calendar"
135
+ }), /* @__PURE__ */ g("select", {
136
+ id: "schedule-meeting-calendar",
137
+ className: y,
138
+ value: Z,
139
+ disabled: J,
140
+ onChange: (e) => F(e.target.value),
141
+ "data-testid": "schedule-meeting-calendar",
142
+ children: [J && /* @__PURE__ */ h("option", {
143
+ value: "",
144
+ children: "Loading calendars…"
145
+ }), Y.map((e) => /* @__PURE__ */ g("option", {
146
+ value: e.id,
147
+ children: [e.name, e.isDefault ? " (default)" : ""]
148
+ }, e.id))]
149
+ })] })]
150
+ }),
151
+ /* @__PURE__ */ g("div", {
152
+ className: "grid grid-cols-2 gap-3",
153
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
154
+ htmlFor: "schedule-meeting-start",
155
+ className: "text-sm font-medium",
156
+ children: "Start"
157
+ }), /* @__PURE__ */ h(v, {
158
+ id: "schedule-meeting-start",
159
+ type: "datetime-local",
160
+ className: "mt-1",
161
+ value: I,
162
+ onChange: (e) => L(e.target.value),
163
+ "data-testid": "schedule-meeting-start"
164
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
165
+ htmlFor: "schedule-meeting-duration",
166
+ className: "text-sm font-medium",
167
+ children: "Duration"
168
+ }), /* @__PURE__ */ h("select", {
169
+ id: "schedule-meeting-duration",
170
+ className: y,
171
+ value: R,
172
+ onChange: (e) => z(Number(e.target.value)),
173
+ "data-testid": "schedule-meeting-duration",
174
+ children: r.map((e) => /* @__PURE__ */ h("option", {
175
+ value: e,
176
+ children: e < 60 ? `${e} minutes` : `${e / 60} hour${e === 60 ? "" : "s"}`
177
+ }, e))
178
+ })] })]
179
+ }),
180
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
181
+ htmlFor: "schedule-meeting-recurrence",
182
+ className: "text-sm font-medium",
183
+ children: "Repeats"
184
+ }), /* @__PURE__ */ h("select", {
185
+ id: "schedule-meeting-recurrence",
186
+ className: y,
187
+ value: B,
188
+ onChange: (e) => V(e.target.value),
189
+ "data-testid": "schedule-meeting-recurrence",
190
+ children: a.map((e) => /* @__PURE__ */ h("option", {
191
+ value: e,
192
+ children: i[e]
193
+ }, e))
194
+ })] }),
195
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
196
+ htmlFor: "schedule-meeting-agenda",
197
+ className: "text-sm font-medium",
198
+ children: "Agenda (optional)"
199
+ }), /* @__PURE__ */ h(ge, {
200
+ id: "schedule-meeting-agenda",
201
+ className: "mt-1 min-h-[72px]",
202
+ value: H,
203
+ onChange: (e) => U(e.target.value),
204
+ "data-testid": "schedule-meeting-agenda"
205
+ })] }),
206
+ W && /* @__PURE__ */ h(t, { message: W })
207
+ ]
208
+ }),
209
+ /* @__PURE__ */ g(fe, { children: [/* @__PURE__ */ h(_, {
210
+ variant: "outline",
211
+ onClick: () => k(!1),
212
+ children: "Cancel"
213
+ }), !$ && /* @__PURE__ */ h(_, {
214
+ onClick: () => void Ce(),
215
+ disabled: K,
216
+ "data-testid": "schedule-meeting-submit",
217
+ children: K ? "Scheduling…" : "Schedule meeting"
218
+ })] })
219
+ ]
220
+ })]
221
+ });
222
+ }
223
+ //#endregion
224
+ export { b as ScheduleMeetingDialog };
225
+
226
+ //# sourceMappingURL=ScheduleMeetingDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScheduleMeetingDialog.js","names":[],"sources":["../../../src/planmagnet/meetings/ScheduleMeetingDialog.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Input,\n Textarea,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { CalendarPlus } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { ErrorLine } from '../controls/ConfirmButton';\nimport {\n MEETING_KINDS,\n type MeetingKind,\n RECURRENCE_OPTIONS,\n RECURRENCE_LABELS,\n type RecurrenceOption,\n DURATION_OPTIONS_MINUTES,\n localInputValueToISO,\n addMinutesIso,\n defaultLocalStart,\n viewerTimeZone,\n buildRRule,\n validateMeetingDraft,\n} from './meetingModel';\nimport {\n SCHEDULE_MEETING_CALENDARS,\n CREATE_CALENDAR_EVENT,\n DELETE_CALENDAR_EVENT,\n LINK_PLANMAGNET_MEETING,\n MEETING_QUERIES,\n} from './meetingGraphql';\nimport { createAndLinkMeeting, meetingScheduleErrorMessage } from './meetingActions';\n\n/**\n * Reusable \"schedule a meeting\" dialog (GAP-06, CWF-22). Opened with a FIXED project and an optional sprint or\n * milestone — see `createAndLinkMeeting` for the create-event-then-link flow, including what happens when the\n * link is refused after the calendar event was already created.\n */\n\nconst fieldClass =\n 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60';\n\ninterface CalendarOption {\n id: string;\n name: string;\n isDefault: boolean;\n calendarType: string;\n}\n\nexport function ScheduleMeetingDialog({\n projectId,\n sprintId = null,\n milestoneId = null,\n defaultKind = 'OTHER',\n lockKind = false,\n defaultTitle = '',\n trigger,\n triggerLabel = 'Schedule meeting',\n onScheduled,\n}: {\n projectId: string;\n sprintId?: string | null;\n milestoneId?: string | null;\n /** Pre-selects a kind — e.g. MILESTONE_CELEBRATION/DEMO from a milestone's own page. */\n defaultKind?: MeetingKind;\n /** Hides the kind picker, for a trigger whose kind the context already decided (Schedule celebration/demo). */\n lockKind?: boolean;\n defaultTitle?: string;\n trigger?: ReactNode;\n triggerLabel?: string;\n onScheduled?: (meeting: { id: string }) => void;\n}) {\n const { workspaceId } = usePlanMagnet();\n const [open, setOpen] = useState(false);\n const [title, setTitle] = useState(defaultTitle);\n const [kind, setKind] = useState<MeetingKind>(defaultKind);\n const [calendarId, setCalendarId] = useState('');\n const [startLocal, setStartLocal] = useState<string>(() => defaultLocalStart());\n const [duration, setDuration] = useState<number>(30);\n const [recurrence, setRecurrence] = useState<RecurrenceOption>('NONE');\n const [agenda, setAgenda] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n\n const { data: calendarsData, loading: calendarsLoading } = useQuery<{\n calendars: { calendars: CalendarOption[]; total: number };\n }>(SCHEDULE_MEETING_CALENDARS, {\n variables: { pagination: { limit: 100 } },\n skip: !open,\n });\n const calendars = calendarsData?.calendars.calendars ?? [];\n // The workspace default calendar, derived rather than synced through an effect: `calendarId` only ever holds\n // an EXPLICIT user choice (empty until they pick one), and this fills in the default for both the `<select>`\n // and submission until they do — so the default appears the moment calendars arrive, with no separate\n // render pass, and never fights a choice the user already made.\n const defaultCalendarId = (calendars.find((c) => c.isDefault) ?? calendars[0])?.id ?? '';\n const effectiveCalendarId = calendarId || defaultCalendarId;\n\n const [createEvent] = useMutation<{ createEvent: { id: string } }>(CREATE_CALENDAR_EVENT);\n const [linkMeeting] = useMutation<{ planmagnetLinkMeeting: { id: string } }>(\n LINK_PLANMAGNET_MEETING,\n { refetchQueries: MEETING_QUERIES, awaitRefetchQueries: true }\n );\n const [deleteEvent] = useMutation<{ deleteEvent: boolean }>(DELETE_CALENDAR_EVENT);\n\n const reset = () => {\n setTitle(defaultTitle);\n setKind(defaultKind);\n setCalendarId('');\n setStartLocal(defaultLocalStart());\n setDuration(30);\n setRecurrence('NONE');\n setAgenda('');\n setError(null);\n };\n\n const schedule = async () => {\n const problem = validateMeetingDraft({\n title,\n calendarId: effectiveCalendarId,\n startLocal,\n durationMinutes: duration,\n kind,\n milestoneId,\n });\n if (problem) {\n setError(problem);\n return;\n }\n const startIso = localInputValueToISO(startLocal);\n if (!startIso || !workspaceId) {\n setError('Pick a start time.');\n return;\n }\n setError(null);\n setSubmitting(true);\n try {\n const endIso = addMinutesIso(startIso, duration);\n const rrule = buildRRule(recurrence, startLocal);\n const meeting = await createAndLinkMeeting({\n createEvent,\n linkMeeting,\n deleteEvent,\n workspaceId,\n calendarId: effectiveCalendarId,\n title: title.trim(),\n startIso,\n endIso,\n kind,\n projectId,\n sprintId,\n milestoneId,\n agenda: agenda.trim(),\n rrule,\n timezone: viewerTimeZone(),\n });\n setOpen(false);\n reset();\n onScheduled?.(meeting);\n } catch (e) {\n setError(meetingScheduleErrorMessage(e));\n } finally {\n setSubmitting(false);\n }\n };\n\n const noCalendars = open && !calendarsLoading && calendars.length === 0;\n const contextLabel = milestoneId ? 'milestone' : sprintId ? 'sprint' : 'project';\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) reset();\n else setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n {trigger ?? (\n <Button size=\"sm\" variant=\"outline\" data-testid=\"schedule-meeting-trigger\">\n <CalendarPlus className=\"mr-1 h-4 w-4\" />\n {triggerLabel}\n </Button>\n )}\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule a meeting</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Creates a calendar event and links it to this {contextLabel}.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n {noCalendars ? (\n <div className=\"space-y-2 text-sm\" data-testid=\"schedule-meeting-no-calendars\">\n <p className=\"text-muted-foreground\">\n You have no calendars yet. Create one in Calendar settings, then come back to schedule\n this meeting.\n </p>\n <a\n href=\"/calendar/settings\"\n className=\"text-primary hover:underline\"\n data-testid=\"schedule-meeting-calendar-settings-link\"\n >\n Open Calendar settings\n </a>\n </div>\n ) : (\n <div className=\"space-y-3\">\n <div>\n <label htmlFor=\"schedule-meeting-title\" className=\"text-sm font-medium\">\n Title\n </label>\n <Input\n id=\"schedule-meeting-title\"\n className=\"mt-1\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n data-testid=\"schedule-meeting-title\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"schedule-meeting-kind\" className=\"text-sm font-medium\">\n Kind\n </label>\n <select\n id=\"schedule-meeting-kind\"\n className={fieldClass}\n value={kind}\n disabled={lockKind}\n onChange={(e) => setKind(e.target.value as MeetingKind)}\n data-testid=\"schedule-meeting-kind\"\n >\n {MEETING_KINDS.map((k) => (\n <option key={k} value={k}>\n {enumLabel(k)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"schedule-meeting-calendar\" className=\"text-sm font-medium\">\n Calendar\n </label>\n <select\n id=\"schedule-meeting-calendar\"\n className={fieldClass}\n value={effectiveCalendarId}\n disabled={calendarsLoading}\n onChange={(e) => setCalendarId(e.target.value)}\n data-testid=\"schedule-meeting-calendar\"\n >\n {calendarsLoading && <option value=\"\">Loading calendars…</option>}\n {calendars.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n {c.isDefault ? ' (default)' : ''}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"schedule-meeting-start\" className=\"text-sm font-medium\">\n Start\n </label>\n <Input\n id=\"schedule-meeting-start\"\n type=\"datetime-local\"\n className=\"mt-1\"\n value={startLocal}\n onChange={(e) => setStartLocal(e.target.value)}\n data-testid=\"schedule-meeting-start\"\n />\n </div>\n <div>\n <label htmlFor=\"schedule-meeting-duration\" className=\"text-sm font-medium\">\n Duration\n </label>\n <select\n id=\"schedule-meeting-duration\"\n className={fieldClass}\n value={duration}\n onChange={(e) => setDuration(Number(e.target.value))}\n data-testid=\"schedule-meeting-duration\"\n >\n {DURATION_OPTIONS_MINUTES.map((m) => (\n <option key={m} value={m}>\n {m < 60 ? `${m} minutes` : `${m / 60} hour${m === 60 ? '' : 's'}`}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label htmlFor=\"schedule-meeting-recurrence\" className=\"text-sm font-medium\">\n Repeats\n </label>\n <select\n id=\"schedule-meeting-recurrence\"\n className={fieldClass}\n value={recurrence}\n onChange={(e) => setRecurrence(e.target.value as RecurrenceOption)}\n data-testid=\"schedule-meeting-recurrence\"\n >\n {RECURRENCE_OPTIONS.map((r) => (\n <option key={r} value={r}>\n {RECURRENCE_LABELS[r]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"schedule-meeting-agenda\" className=\"text-sm font-medium\">\n Agenda (optional)\n </label>\n <Textarea\n id=\"schedule-meeting-agenda\"\n className=\"mt-1 min-h-[72px]\"\n value={agenda}\n onChange={(e) => setAgenda(e.target.value)}\n data-testid=\"schedule-meeting-agenda\"\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n )}\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n {!noCalendars && (\n <Button\n onClick={() => void schedule()}\n disabled={submitting}\n data-testid=\"schedule-meeting-submit\"\n >\n {submitting ? 'Scheduling…' : 'Schedule meeting'}\n </Button>\n )}\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Unlinking a meeting removes PlanMagnet's own record of it (agenda/notes/action-item links) but — by
3
+ * default — leaves the calendar event itself alone, since the event may still matter to its attendees even
4
+ * once PlanMagnet stops tracking it. The opt-in checkbox deletes that event too, AS THE USER (same
5
+ * `deleteEvent` the create-link compensation path uses), so an "also delete" refusal is shown exactly like any
6
+ * other mutation refusal rather than silently leaving the event behind.
7
+ */
8
+ export declare function UnlinkMeetingDialog({ meetingId, calendarEventId, meetingTitle, onUnlinked, }: {
9
+ meetingId: string;
10
+ calendarEventId: string;
11
+ meetingTitle: string;
12
+ onUnlinked: () => void;
13
+ }): import("react/jsx-runtime").JSX.Element;
14
+ //# sourceMappingURL=UnlinkMeetingDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnlinkMeetingDialog.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/meetings/UnlinkMeetingDialog.tsx"],"names":[],"mappings":"AAwBA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,eAAe,EACf,YAAY,EACZ,UAAU,GACX,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,2CA2GA"}