@burdenoff/microfe-planmagnet 2026.1001.3 → 2026.1001.4
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.
- package/dist/planmagnet/meetings/UnlinkMeetingDialog.d.ts.map +1 -1
- package/dist/planmagnet/meetings/UnlinkMeetingDialog.js +1 -4
- package/dist/planmagnet/meetings/UnlinkMeetingDialog.js.map +1 -1
- package/dist/planmagnet/meetings/meetingActions.d.ts +4 -3
- package/dist/planmagnet/meetings/meetingActions.d.ts.map +1 -1
- package/dist/planmagnet/meetings/meetingActions.js +1 -4
- package/dist/planmagnet/meetings/meetingActions.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +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,
|
|
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,2CA6GA"}
|
|
@@ -22,10 +22,7 @@ function C({ meetingId: C, calendarEventId: w, meetingTitle: T, onUnlinked: E })
|
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
if (k) try {
|
|
25
|
-
await n(I({ variables: {
|
|
26
|
-
id: w,
|
|
27
|
-
scope: "ALL_EVENTS"
|
|
28
|
-
} }));
|
|
25
|
+
await n(I({ variables: { id: w } }));
|
|
29
26
|
} catch (t) {
|
|
30
27
|
S(), N.refetchQueries({ include: ["GetPlanmagnetMeeting"] }), M("This meeting was unlinked, but the calendar event could not be deleted: " + e(t, "unknown reason"));
|
|
31
28
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnlinkMeetingDialog.js","names":[],"sources":["../../../src/planmagnet/meetings/UnlinkMeetingDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useApolloClient, useMutation } from '@apollo/client/react';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql';\nimport {\n Button,\n Checkbox,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { Unlink } from 'lucide-react';\nimport { ErrorLine } from '../controls/ConfirmButton';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport {\n UNLINK_PLANMAGNET_MEETING,\n DELETE_CALENDAR_EVENT,\n MEETING_QUERIES,\n} from './meetingGraphql';\n\n/**\n * Unlinking a meeting removes PlanMagnet's own record of it (agenda/notes/action-item links) but — by\n * default — leaves the calendar event itself alone, since the event may still matter to its attendees even\n * once PlanMagnet stops tracking it. The opt-in checkbox deletes that event too, AS THE USER (same\n * `deleteEvent` the create-link compensation path uses), so an \"also delete\" refusal is shown exactly like any\n * other mutation refusal rather than silently leaving the event behind.\n */\nexport function UnlinkMeetingDialog({\n meetingId,\n calendarEventId,\n meetingTitle,\n onUnlinked,\n}: {\n meetingId: string;\n calendarEventId: string;\n meetingTitle: string;\n onUnlinked: () => void;\n}) {\n const [open, setOpen] = useState(false);\n const [alsoDeleteEvent, setAlsoDeleteEvent] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const client = useApolloClient();\n // Excludes 'GetPlanmagnetMeeting' — this PAGE's own query, for the meeting we are about to leave. Refetching\n // it would re-run against a now-unlinked meeting (see `LEAVES_PAGE_CONTEXT`'s doc comment); the list page\n // this navigates to reads 'ListPlanmagnetMeetings' fresh on its own mount regardless. That reasoning only\n // holds on the SUCCESS path, though — when the opt-in `deleteEvent` compensation below fails, we deliberately\n // stay on this page, and `GetPlanmagnetMeeting` is force-refetched there instead (see that catch block).\n const [unlink, unlinkState] = useMutation(UNLINK_PLANMAGNET_MEETING, {\n refetchQueries: MEETING_QUERIES.filter((q) => q !== 'GetPlanmagnetMeeting'),\n context: LEAVES_PAGE_CONTEXT,\n });\n // Only ever called right after a successful unlink, on the way OUT of this page (see `confirm` below) — same\n // `LEAVES_PAGE_CONTEXT` reasoning as `unlink` above applies here too.\n const [deleteEvent, deleteState] = useMutation(DELETE_CALENDAR_EVENT, {\n context: LEAVES_PAGE_CONTEXT,\n });\n const busy = unlinkState.loading || deleteState.loading;\n\n const confirm = async () => {\n setError(null);\n try {\n await throwOnError(unlink({ variables: { id: meetingId } }));\n } catch (e) {\n setError(mutationErrorMessage(e, 'The meeting could not be unlinked.'));\n return;\n }\n if (alsoDeleteEvent) {\n try {\n await throwOnError(\n deleteEvent({ variables: { id: calendarEventId
|
|
1
|
+
{"version":3,"file":"UnlinkMeetingDialog.js","names":[],"sources":["../../../src/planmagnet/meetings/UnlinkMeetingDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useApolloClient, useMutation } from '@apollo/client/react';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql';\nimport {\n Button,\n Checkbox,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { Unlink } from 'lucide-react';\nimport { ErrorLine } from '../controls/ConfirmButton';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport {\n UNLINK_PLANMAGNET_MEETING,\n DELETE_CALENDAR_EVENT,\n MEETING_QUERIES,\n} from './meetingGraphql';\n\n/**\n * Unlinking a meeting removes PlanMagnet's own record of it (agenda/notes/action-item links) but — by\n * default — leaves the calendar event itself alone, since the event may still matter to its attendees even\n * once PlanMagnet stops tracking it. The opt-in checkbox deletes that event too, AS THE USER (same\n * `deleteEvent` the create-link compensation path uses), so an \"also delete\" refusal is shown exactly like any\n * other mutation refusal rather than silently leaving the event behind.\n */\nexport function UnlinkMeetingDialog({\n meetingId,\n calendarEventId,\n meetingTitle,\n onUnlinked,\n}: {\n meetingId: string;\n calendarEventId: string;\n meetingTitle: string;\n onUnlinked: () => void;\n}) {\n const [open, setOpen] = useState(false);\n const [alsoDeleteEvent, setAlsoDeleteEvent] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const client = useApolloClient();\n // Excludes 'GetPlanmagnetMeeting' — this PAGE's own query, for the meeting we are about to leave. Refetching\n // it would re-run against a now-unlinked meeting (see `LEAVES_PAGE_CONTEXT`'s doc comment); the list page\n // this navigates to reads 'ListPlanmagnetMeetings' fresh on its own mount regardless. That reasoning only\n // holds on the SUCCESS path, though — when the opt-in `deleteEvent` compensation below fails, we deliberately\n // stay on this page, and `GetPlanmagnetMeeting` is force-refetched there instead (see that catch block).\n const [unlink, unlinkState] = useMutation(UNLINK_PLANMAGNET_MEETING, {\n refetchQueries: MEETING_QUERIES.filter((q) => q !== 'GetPlanmagnetMeeting'),\n context: LEAVES_PAGE_CONTEXT,\n });\n // Only ever called right after a successful unlink, on the way OUT of this page (see `confirm` below) — same\n // `LEAVES_PAGE_CONTEXT` reasoning as `unlink` above applies here too.\n const [deleteEvent, deleteState] = useMutation(DELETE_CALENDAR_EVENT, {\n context: LEAVES_PAGE_CONTEXT,\n });\n const busy = unlinkState.loading || deleteState.loading;\n\n const confirm = async () => {\n setError(null);\n try {\n await throwOnError(unlink({ variables: { id: meetingId } }));\n } catch (e) {\n setError(mutationErrorMessage(e, 'The meeting could not be unlinked.'));\n return;\n }\n if (alsoDeleteEvent) {\n try {\n await throwOnError(\n // No scope: the calendar refuses ALL_EVENTS on a recurring event, and its default delete removes the\n // whole event, series included (see meetingActions.ts).\n deleteEvent({ variables: { id: calendarEventId } })\n );\n } catch (e) {\n // The unlink already succeeded — say so, rather than leaving a \"nothing happened\" impression when\n // PlanMagnet in fact no longer tracks this meeting; only the calendar event itself stayed behind.\n // Force this page's own `GetPlanmagnetMeeting` to refetch now (it was deliberately excluded from\n // `unlink`'s refetchQueries above, for the success path that navigates away before a refetch would\n // matter — this path doesn't navigate away, so the page must not keep showing the fully-populated,\n // now-stale meeting). `planmagnetMeeting(id)` is nullable and resolves to null for an unlinked\n // meeting, so this refetch falls through to the page's existing \"Meeting not found\" state. The cache\n // drop must come first: fe-libs' 15 s fetch-layer response cache would otherwise replay the\n // pre-unlink read if the refetch lands inside that window (same reasoning as `useLiveRefresh.ts`).\n clearGraphqlFetchResponseCache();\n void client.refetchQueries({ include: ['GetPlanmagnetMeeting'] });\n setError(\n `This meeting was unlinked, but the calendar event could not be deleted: ` +\n mutationErrorMessage(e, 'unknown reason')\n );\n return;\n }\n }\n setOpen(false);\n onUnlinked();\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) {\n setAlsoDeleteEvent(false);\n setError(null);\n }\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\" variant=\"outline\" data-testid=\"unlink-meeting-trigger\">\n <Unlink className=\"mr-1 h-4 w-4\" />\n Unlink\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Unlink "{meetingTitle}"?</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n PlanMagnet stops tracking this meeting — its agenda, notes and action-item links are\n gone. The calendar event itself stays, unless you also delete it below.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <label className=\"flex cursor-pointer items-start gap-2 text-sm\">\n <Checkbox\n checked={alsoDeleteEvent}\n onCheckedChange={(checked) => setAlsoDeleteEvent(checked === true)}\n data-testid=\"unlink-meeting-also-delete-event\"\n />\n <span>Also delete the calendar event (cannot be undone here).</span>\n </label>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)} disabled={busy}>\n Keep it\n </Button>\n <Button\n onClick={() => void confirm()}\n disabled={busy}\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n data-testid=\"unlink-meeting-confirm\"\n >\n {busy ? 'Unlinking…' : 'Unlink meeting'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;;;;;AA+BA,SAAgB,EAAoB,EAClC,cACA,oBACA,iBACA,iBAMC;CACD,IAAM,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAS,GAAiB,EAM1B,CAAC,GAAQ,KAAe,EAAY,GAA2B;EACnE,gBAAgB,EAAgB,QAAQ,MAAM,MAAM,uBAAuB;EAC3E,SAAS;EACV,CAAC,EAGI,CAAC,GAAa,KAAe,EAAY,GAAuB,EACpE,SAAS,GACV,CAAC,EACI,IAAO,EAAY,WAAW,EAAY,SAE1C,IAAU,YAAY;AAC1B,IAAS,KAAK;AACd,MAAI;AACF,SAAM,EAAa,EAAO,EAAE,WAAW,EAAE,IAAI,GAAW,EAAE,CAAC,CAAC;WACrD,GAAG;AACV,KAAS,EAAqB,GAAG,qCAAqC,CAAC;AACvE;;AAEF,MAAI,EACF,KAAI;AACF,SAAM,EAGJ,EAAY,EAAE,WAAW,EAAE,IAAI,GAAiB,EAAE,CAAC,CACpD;WACM,GAAG;AAYV,GAFA,GAAgC,EAC3B,EAAO,eAAe,EAAE,SAAS,CAAC,uBAAuB,EAAE,CAAC,EACjE,EACE,6EACE,EAAqB,GAAG,iBAAiB,CAC5C;AACD;;AAIJ,EADA,EAAQ,GAAM,EACd,GAAY;;AAGd,QACE,kBAAC,GAAD;EACQ;EACN,eAAe,MAAS;AAEtB,GADA,EAAQ,EAAK,EACR,MACH,EAAmB,GAAM,EACzB,EAAS,KAAK;;YANpB,CAUE,kBAAC,GAAD;GAAyB,SAAA;aACvB,kBAAC,GAAD;IAAQ,MAAK;IAAK,SAAQ;IAAU,eAAY;cAAhD,CACE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,SAE5B;;GACe,CAAA,EAC1B,kBAAC,GAAD,EAAA,UAAA;GACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA;IAAuB;IAAc;IAAa;IAA+B,EAAA,CAAA,EACjF,kBAAC,GAAD,EAAA,UAA6B,gKAGC,CAAA,CACP,EAAA,CAAA;GACzB,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,GAAD;KACE,SAAS;KACT,kBAAkB,MAAY,EAAmB,MAAY,GAAK;KAClE,eAAY;KACZ,CAAA,EACF,kBAAC,QAAD,EAAA,UAAM,2DAA8D,CAAA,CAC9D;;GACP,KAAS,kBAAC,GAAD,EAAW,SAAS,GAAS,CAAA;GACvC,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAQ,SAAQ;IAAU,eAAe,EAAQ,GAAM;IAAE,UAAU;cAAM;IAEhE,CAAA,EACT,kBAAC,GAAD;IACE,eAAe,KAAK,GAAS;IAC7B,UAAU;IACV,WAAU;IACV,eAAY;cAEX,IAAO,eAAe;IAChB,CAAA,CACc,EAAA,CAAA;GACD,EAAA,CAAA,CACT"}
|
|
@@ -40,9 +40,10 @@ export interface CreateAndLinkMeetingParams {
|
|
|
40
40
|
* feature in this MFE is built on (`PlanmagnetMeeting`'s doc comment, `graphql/wspace.graphql`:
|
|
41
41
|
* `planmagnetLinkMeeting` "never calls the Calendar API itself; it only validates and stores the link").
|
|
42
42
|
*
|
|
43
|
-
* If the link is refused, the event just created is deleted (best-effort
|
|
44
|
-
*
|
|
45
|
-
*
|
|
43
|
+
* If the link is refused, the event just created is deleted (best-effort, with no scope: the platform calendar
|
|
44
|
+
* refuses `ALL_EVENTS` on a recurring event — "not yet fully implemented" — while its default delete removes the
|
|
45
|
+
* whole event, series included; verified in prod 2026-10-01) so a failed "schedule" never leaves an orphaned
|
|
46
|
+
* calendar event behind. A compensation failure is swallowed deliberately: the caller must see the LINK refusal (the real
|
|
46
47
|
* reason scheduling failed), never a secondary "cleanup also failed" message in its place.
|
|
47
48
|
*
|
|
48
49
|
* Both call sites (`ScheduleMeetingDialog`, `ScheduleCeremoniesDialog`) await this already wrapped in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meetingActions.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/meetings/meetingActions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;yEACyE;AACzE,KAAK,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE;IAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,KAAK,OAAO,CAAC;IAAE,IAAI,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEjD,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,QAAQ,CAAC;QAAE,WAAW,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IACvD,WAAW,EAAE,QAAQ,CAAC;QAAE,qBAAqB,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IACjE,WAAW,EAAE,QAAQ,CAAC;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED
|
|
1
|
+
{"version":3,"file":"meetingActions.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/meetings/meetingActions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;yEACyE;AACzE,KAAK,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE;IAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,KAAK,OAAO,CAAC;IAAE,IAAI,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEjD,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,QAAQ,CAAC;QAAE,WAAW,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IACvD,WAAW,EAAE,QAAQ,CAAC;QAAE,qBAAqB,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IACjE,WAAW,EAAE,QAAQ,CAAC;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,0BAA0B,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDjG;AAED,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAE9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meetingActions.js","names":[],"sources":["../../../src/planmagnet/meetings/meetingActions.ts"],"sourcesContent":["import { throwOnError } from '../utils/mutationResult';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport type { MeetingKind } from './meetingModel';\n\n/** The slice of an Apollo `useMutation` tuple's first element this helper needs — typed narrowly so a caller can\n * pass its real mutate function straight through without an adapter. */\ntype MutateFn<TData> = (options: {\n variables: Record<string, unknown>;\n}) => Promise<{ data?: TData; error?: unknown }>;\n\nexport interface CreateAndLinkMeetingParams {\n createEvent: MutateFn<{ createEvent: { id: string } }>;\n linkMeeting: MutateFn<{ planmagnetLinkMeeting: { id: string } }>;\n deleteEvent: MutateFn<{ deleteEvent: boolean }>;\n workspaceId: string;\n calendarId: string;\n title: string;\n startIso: string;\n endIso: string;\n kind: MeetingKind;\n projectId: string;\n sprintId?: string | null;\n milestoneId?: string | null;\n agenda?: string;\n rrule?: string;\n timezone: string;\n}\n\n/**\n * Creates the calendar event AS THE USER, then records the PlanMagnet link — the architecture every meeting\n * feature in this MFE is built on (`PlanmagnetMeeting`'s doc comment, `graphql/wspace.graphql`:\n * `planmagnetLinkMeeting` \"never calls the Calendar API itself; it only validates and stores the link\").\n *\n * If the link is refused, the event just created is deleted (best-effort
|
|
1
|
+
{"version":3,"file":"meetingActions.js","names":[],"sources":["../../../src/planmagnet/meetings/meetingActions.ts"],"sourcesContent":["import { throwOnError } from '../utils/mutationResult';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport type { MeetingKind } from './meetingModel';\n\n/** The slice of an Apollo `useMutation` tuple's first element this helper needs — typed narrowly so a caller can\n * pass its real mutate function straight through without an adapter. */\ntype MutateFn<TData> = (options: {\n variables: Record<string, unknown>;\n}) => Promise<{ data?: TData; error?: unknown }>;\n\nexport interface CreateAndLinkMeetingParams {\n createEvent: MutateFn<{ createEvent: { id: string } }>;\n linkMeeting: MutateFn<{ planmagnetLinkMeeting: { id: string } }>;\n deleteEvent: MutateFn<{ deleteEvent: boolean }>;\n workspaceId: string;\n calendarId: string;\n title: string;\n startIso: string;\n endIso: string;\n kind: MeetingKind;\n projectId: string;\n sprintId?: string | null;\n milestoneId?: string | null;\n agenda?: string;\n rrule?: string;\n timezone: string;\n}\n\n/**\n * Creates the calendar event AS THE USER, then records the PlanMagnet link — the architecture every meeting\n * feature in this MFE is built on (`PlanmagnetMeeting`'s doc comment, `graphql/wspace.graphql`:\n * `planmagnetLinkMeeting` \"never calls the Calendar API itself; it only validates and stores the link\").\n *\n * If the link is refused, the event just created is deleted (best-effort, with no scope: the platform calendar\n * refuses `ALL_EVENTS` on a recurring event — \"not yet fully implemented\" — while its default delete removes the\n * whole event, series included; verified in prod 2026-10-01) so a failed \"schedule\" never leaves an orphaned\n * calendar event behind. A compensation failure is swallowed deliberately: the caller must see the LINK refusal (the real\n * reason scheduling failed), never a secondary \"cleanup also failed\" message in its place.\n *\n * Both call sites (`ScheduleMeetingDialog`, `ScheduleCeremoniesDialog`) await this already wrapped in\n * `throwOnError` semantics — every mutation inside is itself awaited through `throwOnError`, so nothing here\n * can resolve silently on a refusal.\n */\nexport async function createAndLinkMeeting(p: CreateAndLinkMeetingParams): Promise<{ id: string }> {\n const eventResult = await throwOnError(\n p.createEvent({\n variables: {\n input: {\n calendarId: p.calendarId,\n title: p.title,\n startTime: p.startIso,\n endTime: p.endIso,\n eventType: 'MEETING',\n rrule: p.rrule,\n isRecurring: Boolean(p.rrule),\n timezone: p.timezone,\n description: p.agenda || undefined,\n },\n },\n })\n );\n const calendarEventId = eventResult.data?.createEvent.id;\n if (!calendarEventId) throw new Error('The calendar event was not created.');\n\n try {\n const linkResult = await throwOnError(\n p.linkMeeting({\n variables: {\n workspaceId: p.workspaceId,\n input: {\n calendarEventId,\n title: p.title,\n kind: p.kind,\n projectId: p.projectId,\n sprintId: p.sprintId || undefined,\n milestoneId: p.milestoneId || undefined,\n startsAt: p.startIso,\n endsAt: p.endIso,\n agenda: p.agenda || undefined,\n recurrenceRule: p.rrule,\n },\n },\n })\n );\n const meeting = linkResult.data?.planmagnetLinkMeeting;\n if (!meeting) throw new Error('The meeting could not be linked.');\n return meeting;\n } catch (linkError) {\n try {\n await throwOnError(p.deleteEvent({ variables: { id: calendarEventId } }));\n } catch {\n // Best-effort compensation only — see doc comment above.\n }\n throw linkError;\n }\n}\n\nexport function meetingScheduleErrorMessage(e: unknown): string {\n return mutationErrorMessage(e, 'The meeting could not be scheduled.');\n}\n"],"mappings":";;;AA2CA,eAAsB,EAAqB,GAAwD;CAkBjG,IAAM,KAjBc,MAAM,EACxB,EAAE,YAAY,EACZ,WAAW,EACT,OAAO;EACL,YAAY,EAAE;EACd,OAAO,EAAE;EACT,WAAW,EAAE;EACb,SAAS,EAAE;EACX,WAAW;EACX,OAAO,EAAE;EACT,aAAa,EAAQ,EAAE;EACvB,UAAU,EAAE;EACZ,aAAa,EAAE,UAAU,KAAA;EAC1B,EACF,EACF,CAAC,CACH,EACmC,MAAM,YAAY;AACtD,KAAI,CAAC,EAAiB,OAAU,MAAM,sCAAsC;AAE5E,KAAI;EAoBF,IAAM,KAnBa,MAAM,EACvB,EAAE,YAAY,EACZ,WAAW;GACT,aAAa,EAAE;GACf,OAAO;IACL;IACA,OAAO,EAAE;IACT,MAAM,EAAE;IACR,WAAW,EAAE;IACb,UAAU,EAAE,YAAY,KAAA;IACxB,aAAa,EAAE,eAAe,KAAA;IAC9B,UAAU,EAAE;IACZ,QAAQ,EAAE;IACV,QAAQ,EAAE,UAAU,KAAA;IACpB,gBAAgB,EAAE;IACnB;GACF,EACF,CAAC,CACH,EAC0B,MAAM;AACjC,MAAI,CAAC,EAAS,OAAU,MAAM,mCAAmC;AACjE,SAAO;UACA,GAAW;AAClB,MAAI;AACF,SAAM,EAAa,EAAE,YAAY,EAAE,WAAW,EAAE,IAAI,GAAiB,EAAE,CAAC,CAAC;UACnE;AAGR,QAAM;;;AAIV,SAAgB,EAA4B,GAAoB;AAC9D,QAAO,EAAqB,GAAG,sCAAsC"}
|