@burdenoff/microfe-timecampus 2026.703.1 → 2026.703.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 (85) hide show
  1. package/dist/generated/wspace-types.js +28 -0
  2. package/dist/generated/wspace-types.js.map +1 -0
  3. package/dist/index.js +4 -4
  4. package/dist/timecampus/components/Modal.js +65 -0
  5. package/dist/timecampus/components/Modal.js.map +1 -0
  6. package/dist/timecampus/components/StatusBadge.js +28 -0
  7. package/dist/timecampus/components/StatusBadge.js.map +1 -0
  8. package/dist/timecampus/components/Toast.js +87 -0
  9. package/dist/timecampus/components/Toast.js.map +1 -0
  10. package/dist/timecampus/index.js +1 -1
  11. package/dist/timecampus/pages/HomePage.js +143 -135
  12. package/dist/timecampus/pages/HomePage.js.map +1 -1
  13. package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
  14. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  15. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
  16. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  17. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
  18. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  19. package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
  20. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  21. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +432 -118
  22. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  23. package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
  24. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  25. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
  26. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  27. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +562 -85
  28. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  29. package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
  30. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  31. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
  32. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  33. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
  34. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  35. package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
  36. package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
  37. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
  38. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  39. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
  40. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  41. package/dist/timecampus/pages/meetings/MeetingFormModal.js +208 -0
  42. package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
  43. package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
  44. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
  46. package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
  47. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
  48. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  49. package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
  50. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  51. package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
  52. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  53. package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
  54. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  55. package/dist/timecampus/pages/projects/TasksPage.js +367 -65
  56. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  57. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
  58. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  59. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
  60. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  61. package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
  62. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  63. package/dist/timecampus/pages/team/TeamPage.js +4 -3
  64. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  65. package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
  66. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  67. package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
  68. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  69. package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
  70. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  71. package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
  72. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  73. package/dist/timecampus/pages/time/TimerPage.js +229 -97
  74. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  75. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +339 -110
  76. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  77. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
  78. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  79. package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
  80. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  81. package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
  82. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  83. package/dist/timecampus/store/timecampusStore.js +13 -1
  84. package/dist/timecampus/store/timecampusStore.js.map +1 -1
  85. package/package.json +1 -1
@@ -0,0 +1,208 @@
1
+ import { MeetingType as e } from "../../../generated/wspace-types.js";
2
+ import { Modal as t } from "../../components/Modal.js";
3
+ import { useState as n } from "react";
4
+ import { gql as r } from "@apollo/client";
5
+ import { useApolloClient as i } from "@apollo/client/react";
6
+ import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ import { uploadBlobToFiles as c } from "@burdenoff/fe-libs/storage";
8
+ //#region src/timecampus/pages/meetings/MeetingFormModal.tsx
9
+ var l = r`
10
+ query TimeCampus_GetFileDownloadUrl($id: ID!, $expiresIn: Int) {
11
+ getFileDownloadUrl(id: $id, expiresIn: $expiresIn)
12
+ }
13
+ `, u = [
14
+ {
15
+ value: e.Team,
16
+ label: "Team"
17
+ },
18
+ {
19
+ value: e.Standup,
20
+ label: "Standup"
21
+ },
22
+ {
23
+ value: e.OneOnOne,
24
+ label: "One-on-one"
25
+ },
26
+ {
27
+ value: e.Planning,
28
+ label: "Planning"
29
+ },
30
+ {
31
+ value: e.Review,
32
+ label: "Review"
33
+ },
34
+ {
35
+ value: e.Retrospective,
36
+ label: "Retrospective"
37
+ },
38
+ {
39
+ value: e.External,
40
+ label: "External"
41
+ }
42
+ ], d = "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary", f = "block text-sm font-medium text-text-primary";
43
+ function p(e) {
44
+ if (!e) return "";
45
+ let t = new Date(e);
46
+ if (Number.isNaN(t.getTime())) return "";
47
+ let n = (e) => String(e).padStart(2, "0");
48
+ return `${t.getFullYear()}-${n(t.getMonth() + 1)}-${n(t.getDate())}T${n(t.getHours())}:${n(t.getMinutes())}`;
49
+ }
50
+ var m = ({ open: r, onClose: m, meeting: h, workspaceId: g, submitting: _, error: v, onCreate: y, onUpdate: b }) => {
51
+ let x = !!h, [S, C] = n(h?.title ?? ""), [w, T] = n(h?.meetingType ?? e.Team), [E, D] = n(p(h?.startTime)), [O, k] = n(h?.duration ?? 30), [A, j] = n(h?.notes ?? ""), [M, N] = n(h?.recordingUrl ?? ""), P = i(), [F, I] = n(!1), [L, R] = n(null), z = !!(S.trim() && E) && !_, B = async (e) => {
52
+ I(!0), R(null);
53
+ try {
54
+ let t = await c({
55
+ client: P,
56
+ blob: e,
57
+ filename: e.name,
58
+ mimeType: e.type || "application/octet-stream",
59
+ folderId: ""
60
+ }), { data: n } = await P.query({
61
+ query: l,
62
+ variables: {
63
+ id: t.id,
64
+ expiresIn: 604800
65
+ },
66
+ fetchPolicy: "no-cache"
67
+ }), r = n?.getFileDownloadUrl;
68
+ if (!r) throw Error("No download URL returned");
69
+ N(r);
70
+ } catch {
71
+ R("Failed to upload recording. You can paste a URL instead.");
72
+ } finally {
73
+ I(!1);
74
+ }
75
+ };
76
+ return /* @__PURE__ */ s(t, {
77
+ open: r,
78
+ onClose: m,
79
+ title: x ? "Edit Meeting" : "Schedule Meeting",
80
+ description: x ? "Update this meeting's details." : "Add a meeting to track its time, cost and follow-ups.",
81
+ size: "xl",
82
+ footer: /* @__PURE__ */ s(a, { children: [/* @__PURE__ */ o("button", {
83
+ onClick: m,
84
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
85
+ children: "Cancel"
86
+ }), /* @__PURE__ */ o("button", {
87
+ onClick: async () => {
88
+ if (!z) return;
89
+ let e = new Date(E).toISOString(), t = O > 0 ? new Date(new Date(E).getTime() + O * 6e4).toISOString() : void 0;
90
+ x && b ? await b({
91
+ title: S.trim(),
92
+ meetingType: w,
93
+ startTime: e,
94
+ endTime: t,
95
+ notes: A.trim() || void 0,
96
+ recordingUrl: M.trim() || void 0
97
+ }) : y && await y({
98
+ workspaceId: g,
99
+ title: S.trim(),
100
+ meetingType: w,
101
+ startTime: e,
102
+ endTime: t,
103
+ notes: A.trim() || void 0,
104
+ recordingUrl: M.trim() || void 0
105
+ });
106
+ },
107
+ disabled: !z,
108
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
109
+ children: _ ? x ? "Saving..." : "Scheduling..." : x ? "Save Changes" : "Schedule"
110
+ })] }),
111
+ children: [v && /* @__PURE__ */ o("div", {
112
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
113
+ children: x ? "Failed to update meeting. Please try again." : "Failed to schedule meeting. Please try again."
114
+ }), /* @__PURE__ */ s("div", {
115
+ className: "space-y-4",
116
+ children: [
117
+ /* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("label", {
118
+ className: f,
119
+ children: "Meeting Title"
120
+ }), /* @__PURE__ */ o("input", {
121
+ type: "text",
122
+ placeholder: "Enter meeting title",
123
+ value: S,
124
+ onChange: (e) => C(e.target.value),
125
+ className: d
126
+ })] }),
127
+ /* @__PURE__ */ s("div", {
128
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
129
+ children: [/* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("label", {
130
+ className: f,
131
+ children: "Type"
132
+ }), /* @__PURE__ */ o("select", {
133
+ value: w,
134
+ onChange: (e) => T(e.target.value),
135
+ className: d,
136
+ children: u.map((e) => /* @__PURE__ */ o("option", {
137
+ value: e.value,
138
+ children: e.label
139
+ }, e.value))
140
+ })] }), /* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("label", {
141
+ className: f,
142
+ children: "Duration (minutes)"
143
+ }), /* @__PURE__ */ o("input", {
144
+ type: "number",
145
+ min: 0,
146
+ value: O,
147
+ onChange: (e) => k(Number(e.target.value)),
148
+ className: d
149
+ })] })]
150
+ }),
151
+ /* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("label", {
152
+ className: f,
153
+ children: "Start"
154
+ }), /* @__PURE__ */ o("input", {
155
+ type: "datetime-local",
156
+ value: E,
157
+ onChange: (e) => D(e.target.value),
158
+ className: d
159
+ })] }),
160
+ /* @__PURE__ */ s("div", { children: [
161
+ /* @__PURE__ */ o("label", {
162
+ className: f,
163
+ children: "Recording"
164
+ }),
165
+ /* @__PURE__ */ o("input", {
166
+ type: "file",
167
+ accept: "audio/*,video/*",
168
+ disabled: F,
169
+ onChange: (e) => {
170
+ let t = e.target.files?.[0];
171
+ t && B(t), e.target.value = "";
172
+ },
173
+ className: "mt-1 block w-full text-sm text-text-secondary file:mr-3 file:rounded-md file:border-0 file:bg-action-primary-bg file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-action-primary-text hover:file:bg-action-primary-bg/90"
174
+ }),
175
+ F && /* @__PURE__ */ o("p", {
176
+ className: "mt-1 text-xs text-text-secondary",
177
+ children: "Uploading…"
178
+ }),
179
+ L && /* @__PURE__ */ o("p", {
180
+ className: "mt-1 text-xs text-status-error-text",
181
+ children: L
182
+ }),
183
+ /* @__PURE__ */ o("input", {
184
+ type: "url",
185
+ placeholder: "…or paste a recording URL (https://...)",
186
+ value: M,
187
+ onChange: (e) => N(e.target.value),
188
+ className: d
189
+ })
190
+ ] }),
191
+ /* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("label", {
192
+ className: f,
193
+ children: "Notes / Agenda"
194
+ }), /* @__PURE__ */ o("textarea", {
195
+ placeholder: "Meeting agenda...",
196
+ rows: 3,
197
+ value: A,
198
+ onChange: (e) => j(e.target.value),
199
+ className: d
200
+ })] })
201
+ ]
202
+ })]
203
+ });
204
+ };
205
+ //#endregion
206
+ export { m as MeetingFormModal };
207
+
208
+ //# sourceMappingURL=MeetingFormModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MeetingFormModal.js","names":[],"sources":["../../../../src/timecampus/pages/meetings/MeetingFormModal.tsx"],"sourcesContent":["import { useState, type FC } from \"react\";\nimport { gql } from \"@apollo/client\";\nimport { useApolloClient } from \"@apollo/client/react\";\nimport { uploadBlobToFiles } from \"@burdenoff/fe-libs/storage\";\nimport { Modal } from \"../../components/Modal\";\nimport {\n MeetingType,\n type CreateMeetingInput,\n type UpdateMeetingInput,\n} from \"@/generated/wspace-types\";\nimport type { MeetingFieldsFragment } from \"@/generated/wspace-operations\";\n\n/**\n * Resolve a downloadable URL for a Files object. The URL is presigned and\n * expires after 7 days (604800s) — a known limitation: a stored recordingUrl\n * will eventually 403 and the recording must be re-uploaded to refresh it.\n */\nconst GET_FILE_DOWNLOAD_URL = gql`\n query TimeCampus_GetFileDownloadUrl($id: ID!, $expiresIn: Int) {\n getFileDownloadUrl(id: $id, expiresIn: $expiresIn)\n }\n`;\n\nconst MEETING_TYPES: { value: MeetingType; label: string }[] = [\n { value: MeetingType.Team, label: \"Team\" },\n { value: MeetingType.Standup, label: \"Standup\" },\n { value: MeetingType.OneOnOne, label: \"One-on-one\" },\n { value: MeetingType.Planning, label: \"Planning\" },\n { value: MeetingType.Review, label: \"Review\" },\n { value: MeetingType.Retrospective, label: \"Retrospective\" },\n { value: MeetingType.External, label: \"External\" },\n];\n\nconst inputClass =\n \"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\";\nconst labelClass = \"block text-sm font-medium text-text-primary\";\n\n/** Convert an ISO string into a value usable by a datetime-local input. */\nfunction toLocalInput(iso?: string | null): string {\n if (!iso) return \"\";\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return \"\";\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(\n d.getHours(),\n )}:${pad(d.getMinutes())}`;\n}\n\nexport interface MeetingFormValues {\n title: string;\n meetingType: MeetingType;\n startTime: string;\n endTime?: string;\n notes?: string;\n recordingUrl?: string;\n cost?: number;\n}\n\ninterface MeetingFormModalProps {\n open: boolean;\n onClose: () => void;\n /** Existing meeting when editing; undefined when creating. */\n meeting?: MeetingFieldsFragment;\n workspaceId: string;\n submitting: boolean;\n error?: boolean;\n /** Receives a ready-to-send input; caller wires it to the right mutation. */\n onCreate?: (input: CreateMeetingInput) => Promise<void> | void;\n onUpdate?: (input: UpdateMeetingInput) => Promise<void> | void;\n}\n\nexport const MeetingFormModal: FC<MeetingFormModalProps> = ({\n open,\n onClose,\n meeting,\n workspaceId,\n submitting,\n error,\n onCreate,\n onUpdate,\n}) => {\n const isEdit = Boolean(meeting);\n const [title, setTitle] = useState(meeting?.title ?? \"\");\n const [meetingType, setMeetingType] = useState<MeetingType>(\n (meeting?.meetingType as MeetingType | undefined) ?? MeetingType.Team,\n );\n const [start, setStart] = useState(toLocalInput(meeting?.startTime));\n const [duration, setDuration] = useState<number>(meeting?.duration ?? 30);\n const [notes, setNotes] = useState(meeting?.notes ?? \"\");\n const [recordingUrl, setRecordingUrl] = useState(meeting?.recordingUrl ?? \"\");\n const apollo = useApolloClient();\n const [recordingUploading, setRecordingUploading] = useState(false);\n const [recordingError, setRecordingError] = useState<string | null>(null);\n\n const canSubmit = Boolean(title.trim() && start) && !submitting;\n\n /**\n * Upload a recording (audio/video) to platform Files and store its presigned\n * URL in the existing recordingUrl state. Failures surface an inline error\n * but never block submitting the meeting — the recording is optional and the\n * manual URL field remains available as a fallback.\n */\n const handleRecordingUpload = async (file: File) => {\n setRecordingUploading(true);\n setRecordingError(null);\n try {\n const uploaded = await uploadBlobToFiles({\n client: apollo,\n blob: file,\n filename: file.name,\n mimeType: file.type || \"application/octet-stream\",\n // Empty folderId = workspace root; backend treats it as unfoldered.\n folderId: \"\",\n });\n const { data } = await apollo.query({\n query: GET_FILE_DOWNLOAD_URL,\n variables: { id: uploaded.id, expiresIn: 604800 },\n fetchPolicy: \"no-cache\",\n });\n const url = (data as { getFileDownloadUrl?: string } | null | undefined)?.getFileDownloadUrl;\n if (!url) throw new Error(\"No download URL returned\");\n setRecordingUrl(url);\n } catch {\n setRecordingError(\"Failed to upload recording. You can paste a URL instead.\");\n } finally {\n setRecordingUploading(false);\n }\n };\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n const startIso = new Date(start).toISOString();\n const endIso =\n duration > 0\n ? new Date(new Date(start).getTime() + duration * 60_000).toISOString()\n : undefined;\n\n if (isEdit && onUpdate) {\n await onUpdate({\n title: title.trim(),\n meetingType,\n startTime: startIso,\n endTime: endIso,\n notes: notes.trim() || undefined,\n recordingUrl: recordingUrl.trim() || undefined,\n });\n } else if (onCreate) {\n await onCreate({\n workspaceId,\n title: title.trim(),\n meetingType,\n startTime: startIso,\n endTime: endIso,\n notes: notes.trim() || undefined,\n recordingUrl: recordingUrl.trim() || undefined,\n });\n }\n };\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n title={isEdit ? \"Edit Meeting\" : \"Schedule Meeting\"}\n description={\n isEdit\n ? \"Update this meeting's details.\"\n : \"Add a meeting to track its time, cost and follow-ups.\"\n }\n size=\"xl\"\n footer={\n <>\n <button\n onClick={onClose}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleSubmit}\n disabled={!canSubmit}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {submitting\n ? isEdit\n ? \"Saving...\"\n : \"Scheduling...\"\n : isEdit\n ? \"Save Changes\"\n : \"Schedule\"}\n </button>\n </>\n }\n >\n {error && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n {isEdit\n ? \"Failed to update meeting. Please try again.\"\n : \"Failed to schedule meeting. Please try again.\"}\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className={labelClass}>Meeting Title</label>\n <input\n type=\"text\"\n placeholder=\"Enter meeting title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>Type</label>\n <select\n value={meetingType}\n onChange={(e) => setMeetingType(e.target.value as MeetingType)}\n className={inputClass}\n >\n {MEETING_TYPES.map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className={labelClass}>Duration (minutes)</label>\n <input\n type=\"number\"\n min={0}\n value={duration}\n onChange={(e) => setDuration(Number(e.target.value))}\n className={inputClass}\n />\n </div>\n </div>\n <div>\n <label className={labelClass}>Start</label>\n <input\n type=\"datetime-local\"\n value={start}\n onChange={(e) => setStart(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Recording</label>\n <input\n type=\"file\"\n accept=\"audio/*,video/*\"\n disabled={recordingUploading}\n onChange={(e) => {\n const file = e.target.files?.[0];\n if (file) void handleRecordingUpload(file);\n // Reset so re-selecting the same file re-triggers onChange.\n e.target.value = \"\";\n }}\n className=\"mt-1 block w-full text-sm text-text-secondary file:mr-3 file:rounded-md file:border-0 file:bg-action-primary-bg file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-action-primary-text hover:file:bg-action-primary-bg/90\"\n />\n {recordingUploading && (\n <p className=\"mt-1 text-xs text-text-secondary\">Uploading…</p>\n )}\n {recordingError && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {recordingError}\n </p>\n )}\n <input\n type=\"url\"\n placeholder=\"…or paste a recording URL (https://...)\"\n value={recordingUrl}\n onChange={(e) => setRecordingUrl(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Notes / Agenda</label>\n <textarea\n placeholder=\"Meeting agenda...\"\n rows={3}\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
@@ -1,100 +1,201 @@
1
- import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
- import { formatDate as t } from "../../utils/helpers.js";
3
- import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
4
- import { useListMeetingsQuery as r } from "../../../generated/wspace-operations.js";
5
- import { PageLayout as i } from "../../components/PageLayout.js";
6
- import { EmptyState as a } from "../../components/EmptyState.js";
7
- import { CalendarDays as o, Plus as s, Video as c } from "lucide-react";
8
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
9
- import { GlassCard as d } from "@burdenoff/fe-libs/ui";
1
+ import { useTimeCampus as e, useTimeCampusNavigate as t } from "../../providers/TimeCampusProvider.js";
2
+ import { formatDate as n } from "../../utils/helpers.js";
3
+ import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
4
+ import { useCreateMeetingMutation as i, useDeleteMeetingMutation as a, useListMeetingsQuery as o, useUpdateMeetingMutation as s } from "../../../generated/wspace-operations.js";
5
+ import { PageLayout as c } from "../../components/PageLayout.js";
6
+ import { EmptyState as l } from "../../components/EmptyState.js";
7
+ import { Modal as u } from "../../components/Modal.js";
8
+ import { MeetingFormModal as d } from "./MeetingFormModal.js";
9
+ import { useState as f } from "react";
10
+ import { CalendarDays as p, Pencil as m, Plus as h, Trash2 as g, Video as _ } from "lucide-react";
11
+ import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
12
+ import { GlassCard as x } from "@burdenoff/fe-libs/ui";
10
13
  //#region src/timecampus/pages/meetings/MeetingsPage.tsx
11
- function f() {
12
- let { workspaceId: f = "" } = e(), { data: p, loading: m, error: h } = r({
14
+ var S = 25;
15
+ function C() {
16
+ let { workspaceId: C = "" } = e(), w = t(), [T, E] = f(S), [D, O] = f(!1), [k, A] = f(null), [j, M] = f(null), { data: N, loading: P, error: F, refetch: I, fetchMore: L } = o({
13
17
  variables: {
14
- workspaceId: f,
15
- limit: 20
18
+ workspaceId: C,
19
+ limit: T,
20
+ offset: 0
16
21
  },
17
- skip: !f
18
- }), g = p?.meetings ?? [];
19
- return m ? /* @__PURE__ */ l(i, {
22
+ skip: !C,
23
+ notifyOnNetworkStatusChange: !0
24
+ }), [R, { loading: z, error: B }] = i(), [V, { loading: H, error: U }] = s(), [W, { loading: G }] = a(), K = N?.meetings ?? [], q = K.length >= T, J = async (e) => {
25
+ await R({ variables: { input: e } }), await I(), O(!1);
26
+ }, Y = async (e) => {
27
+ k && (await V({ variables: {
28
+ id: k.id,
29
+ workspaceId: C,
30
+ input: e
31
+ } }), await I(), A(null));
32
+ }, X = async () => {
33
+ j && (await W({ variables: {
34
+ id: j.id,
35
+ workspaceId: C
36
+ } }), M(null), await I());
37
+ }, Z = async () => {
38
+ let e = T + S;
39
+ E(e), await L({ variables: {
40
+ limit: e,
41
+ offset: 0
42
+ } });
43
+ }, Q = /* @__PURE__ */ b("button", {
44
+ onClick: () => O(!0),
45
+ className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
46
+ children: [/* @__PURE__ */ y(h, { className: "h-4 w-4" }), "Schedule Meeting"]
47
+ });
48
+ return P && K.length === 0 ? /* @__PURE__ */ y(c, {
20
49
  title: "Meetings",
21
50
  description: "Schedule and track meetings",
22
- actions: /* @__PURE__ */ u("button", {
23
- className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
24
- children: [/* @__PURE__ */ l(s, { className: "h-4 w-4" }), "Schedule Meeting"]
25
- }),
26
- children: /* @__PURE__ */ l(n, {
51
+ actions: Q,
52
+ children: /* @__PURE__ */ y(r, {
27
53
  variant: "list",
28
54
  count: 5
29
55
  })
30
- }) : h ? /* @__PURE__ */ l(i, {
56
+ }) : F ? /* @__PURE__ */ y(c, {
31
57
  title: "Meetings",
32
58
  description: "Schedule and track meetings",
33
- children: /* @__PURE__ */ l("div", {
59
+ children: /* @__PURE__ */ y("div", {
34
60
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
35
61
  children: "Failed to load meetings. Please try again later."
36
62
  })
37
- }) : /* @__PURE__ */ l(i, {
63
+ }) : /* @__PURE__ */ b(c, {
38
64
  title: "Meetings",
39
65
  description: "Schedule and track meetings",
40
66
  purpose: "Keep every meeting and its recording in one place and see how much of your week goes to them — so you can protect focus time, trim low-value calls, and account for meeting hours in your tracking.",
41
- actions: /* @__PURE__ */ u("button", {
42
- className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
43
- children: [/* @__PURE__ */ l(s, { className: "h-4 w-4" }), "Schedule Meeting"]
44
- }),
45
- children: g.length === 0 ? /* @__PURE__ */ l(a, {
46
- illustration: "empty-data",
47
- title: "No meetings found",
48
- description: "Schedule your first meeting to start tracking calls and their time impact.",
49
- action: /* @__PURE__ */ u("button", {
50
- className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
51
- children: [/* @__PURE__ */ l(s, { className: "h-4 w-4" }), "Schedule Meeting"]
52
- })
53
- }) : /* @__PURE__ */ l(d, {
54
- treatment: "glass",
55
- glow: !0,
56
- className: "overflow-hidden",
57
- children: /* @__PURE__ */ l("div", {
58
- className: "divide-y divide-border",
59
- children: g.map((e) => /* @__PURE__ */ u("div", {
60
- className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer",
61
- children: [/* @__PURE__ */ u("div", {
62
- className: "flex items-center gap-3",
63
- children: [/* @__PURE__ */ l(o, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ u("div", {
67
+ actions: Q,
68
+ children: [
69
+ K.length === 0 ? /* @__PURE__ */ y(l, {
70
+ illustration: "empty-data",
71
+ title: "No meetings found",
72
+ description: "Schedule your first meeting to start tracking calls and their time impact.",
73
+ action: Q
74
+ }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(x, {
75
+ treatment: "glass",
76
+ glow: !0,
77
+ className: "overflow-hidden",
78
+ children: /* @__PURE__ */ y("div", {
79
+ className: "divide-y divide-border",
80
+ children: K.map((e) => /* @__PURE__ */ b("div", {
81
+ role: "button",
82
+ tabIndex: 0,
83
+ onClick: () => w(`/meetings/${e.id}`),
84
+ onKeyDown: (t) => {
85
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), w(`/meetings/${e.id}`));
86
+ },
87
+ className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer",
88
+ children: [/* @__PURE__ */ b("div", {
89
+ className: "flex items-center gap-3",
90
+ children: [/* @__PURE__ */ y(p, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ b("div", {
91
+ className: "flex items-center gap-2",
92
+ children: [/* @__PURE__ */ y("p", {
93
+ className: "text-sm font-medium text-text-primary",
94
+ children: e.title
95
+ }), e.recordingUrl && /* @__PURE__ */ y(_, { className: "h-3.5 w-3.5 text-text-secondary" })]
96
+ }), /* @__PURE__ */ b("p", {
97
+ className: "text-xs text-text-secondary",
98
+ children: [
99
+ n(e.startTime),
100
+ " ·",
101
+ " ",
102
+ e.duration ?? 0,
103
+ "min ·",
104
+ " ",
105
+ e.participants.length,
106
+ " participants"
107
+ ]
108
+ })] })]
109
+ }), /* @__PURE__ */ b("div", {
64
110
  className: "flex items-center gap-2",
65
- children: [/* @__PURE__ */ l("p", {
66
- className: "text-sm font-medium text-text-primary",
67
- children: e.title
68
- }), e.recordingUrl && /* @__PURE__ */ l(c, { className: "h-3.5 w-3.5 text-text-secondary" })]
69
- }), /* @__PURE__ */ u("p", {
70
- className: "text-xs text-text-secondary",
71
111
  children: [
72
- t(e.startTime),
73
- " ·",
74
- " ",
75
- e.duration ?? 0,
76
- "min ·",
77
- " ",
78
- e.participants.length,
79
- " participants"
112
+ /* @__PURE__ */ y("span", {
113
+ className: "inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
114
+ children: e.meetingType
115
+ }),
116
+ /* @__PURE__ */ y("span", {
117
+ className: "inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
118
+ children: e.endTime ? "completed" : "scheduled"
119
+ }),
120
+ /* @__PURE__ */ y("button", {
121
+ onClick: (t) => {
122
+ t.stopPropagation(), A(e);
123
+ },
124
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary",
125
+ "aria-label": "Edit meeting",
126
+ children: /* @__PURE__ */ y(m, { className: "h-4 w-4" })
127
+ }),
128
+ /* @__PURE__ */ y("button", {
129
+ onClick: (t) => {
130
+ t.stopPropagation(), M(e);
131
+ },
132
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-status-error-bg hover:text-status-error-text",
133
+ "aria-label": "Delete meeting",
134
+ children: /* @__PURE__ */ y(g, { className: "h-4 w-4" })
135
+ })
80
136
  ]
81
- })] })]
82
- }), /* @__PURE__ */ u("div", {
83
- className: "flex items-center gap-2",
84
- children: [/* @__PURE__ */ l("span", {
85
- className: "inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
86
- children: e.meetingType
87
- }), /* @__PURE__ */ l("span", {
88
- className: "inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
89
- children: e.endTime ? "completed" : "scheduled"
90
137
  })]
91
- })]
92
- }, e.id))
138
+ }, e.id))
139
+ })
140
+ }), q && /* @__PURE__ */ y("div", {
141
+ className: "mt-4 flex justify-center",
142
+ children: /* @__PURE__ */ y("button", {
143
+ onClick: Z,
144
+ disabled: P,
145
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50",
146
+ children: P ? "Loading..." : "Load more"
147
+ })
148
+ })] }),
149
+ D && /* @__PURE__ */ y(d, {
150
+ open: !0,
151
+ onClose: () => O(!1),
152
+ workspaceId: C,
153
+ submitting: z,
154
+ error: !!B,
155
+ onCreate: J
156
+ }),
157
+ k && /* @__PURE__ */ y(d, {
158
+ open: !0,
159
+ onClose: () => A(null),
160
+ workspaceId: C,
161
+ meeting: k,
162
+ submitting: H,
163
+ error: !!U,
164
+ onUpdate: Y
165
+ }),
166
+ /* @__PURE__ */ y(u, {
167
+ open: !!j,
168
+ onClose: () => M(null),
169
+ title: "Delete meeting",
170
+ description: "This permanently removes the meeting and its analytics.",
171
+ size: "sm",
172
+ footer: /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("button", {
173
+ onClick: () => M(null),
174
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
175
+ children: "Cancel"
176
+ }), /* @__PURE__ */ y("button", {
177
+ onClick: X,
178
+ disabled: G,
179
+ className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/90 disabled:opacity-50",
180
+ children: G ? "Deleting..." : "Delete"
181
+ })] }),
182
+ children: /* @__PURE__ */ b("p", {
183
+ className: "text-sm text-text-secondary",
184
+ children: [
185
+ "Are you sure you want to delete",
186
+ " ",
187
+ /* @__PURE__ */ y("span", {
188
+ className: "font-medium text-text-primary",
189
+ children: j?.title
190
+ }),
191
+ "? This action cannot be undone."
192
+ ]
193
+ })
93
194
  })
94
- })
195
+ ]
95
196
  });
96
197
  }
97
198
  //#endregion
98
- export { f as MeetingsPage };
199
+ export { C as MeetingsPage };
99
200
 
100
201
  //# sourceMappingURL=MeetingsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MeetingsPage.js","names":[],"sources":["../../../../src/timecampus/pages/meetings/MeetingsPage.tsx"],"sourcesContent":["import { CalendarDays, Plus, Video } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatDate } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListMeetingsQuery,\n type MeetingFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function MeetingsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useListMeetingsQuery({\n variables: { workspaceId, limit: 20 },\n skip: !workspaceId,\n });\n\n const meetings = data?.meetings ?? [];\n\n if (loading) {\n return (\n <PageLayout\n title=\"Meetings\"\n description=\"Schedule and track meetings\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Schedule Meeting\n </button>\n }\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Meetings\" description=\"Schedule and track meetings\">\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load meetings. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Meetings\"\n description=\"Schedule and track meetings\"\n purpose=\"Keep every meeting and its recording in one place and see how much of your week goes to them — so you can protect focus time, trim low-value calls, and account for meeting hours in your tracking.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Schedule Meeting\n </button>\n }\n >\n {meetings.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No meetings found\"\n description=\"Schedule your first meeting to start tracking calls and their time impact.\"\n action={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Schedule Meeting\n </button>\n }\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {meetings.map((meeting: MeetingFieldsFragment) => (\n <div\n key={meeting.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n >\n <div className=\"flex items-center gap-3\">\n <CalendarDays className=\"h-5 w-5 text-text-secondary\" />\n <div>\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {meeting.title}\n </p>\n {meeting.recordingUrl && (\n <Video className=\"h-3.5 w-3.5 text-text-secondary\" />\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {formatDate(meeting.startTime)} &middot;{\" \"}\n {meeting.duration ?? 0}min &middot;{\" \"}\n {meeting.participants.length} participants\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {meeting.meetingType}\n </span>\n <span className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {meeting.endTime ? \"completed\" : \"scheduled\"}\n </span>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,IAAe;CAC7B,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EAAE,SAAM,YAAS,aAAU,EAAqB;EACpD,WAAW;GAAE;GAAa,OAAO;GAAI;EACrC,MAAM,CAAC;EACR,CAAC,EAEI,IAAW,GAAM,YAAY,EAAE;AA6BrC,QA3BI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,mBAErB;;YAGX,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EAAY,OAAM;EAAW,aAAY;YACvC,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,mBAErB;;YAGV,EAAS,WAAW,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,mBAErB;;GAEX,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAC1C,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KAAK,MACb,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAc,WAAU,+BAAgC,CAAA,EACxD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAQ;QACP,CAAA,EACH,EAAQ,gBACP,kBAAC,GAAD,EAAO,WAAU,mCAAoC,CAAA,CAEnD;UACN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAW,EAAQ,UAAU;QAAC;QAAU;QACxC,EAAQ,YAAY;QAAE;QAAa;QACnC,EAAQ,aAAa;QAAO;QAC3B;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAQ;OACJ,CAAA,EACP,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAQ,UAAU,cAAc;OAC5B,CAAA,CACH;QACF;OA7BC,EAAQ,GA6BT,CACN;IACE,CAAA;GACI,CAAA;EAEH,CAAA"}
1
+ {"version":3,"file":"MeetingsPage.js","names":[],"sources":["../../../../src/timecampus/pages/meetings/MeetingsPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { CalendarDays, Plus, Video, Pencil, Trash2 } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { formatDate } from \"../../utils/helpers\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport {\n useListMeetingsQuery,\n useCreateMeetingMutation,\n useUpdateMeetingMutation,\n useDeleteMeetingMutation,\n type MeetingFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport type {\n CreateMeetingInput,\n UpdateMeetingInput,\n} from \"@/generated/wspace-types\";\nimport { MeetingFormModal } from \"./MeetingFormModal\";\n\nconst PAGE_SIZE = 25;\n\nexport function MeetingsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const [limit, setLimit] = useState(PAGE_SIZE);\n const [createOpen, setCreateOpen] = useState(false);\n const [editing, setEditing] = useState<MeetingFieldsFragment | null>(null);\n const [deleting, setDeleting] = useState<MeetingFieldsFragment | null>(null);\n\n const { data, loading, error, refetch, fetchMore } = useListMeetingsQuery({\n variables: { workspaceId, limit, offset: 0 },\n skip: !workspaceId,\n notifyOnNetworkStatusChange: true,\n });\n\n const [createMeeting, { loading: creating, error: createError }] =\n useCreateMeetingMutation();\n const [updateMeeting, { loading: updating, error: updateError }] =\n useUpdateMeetingMutation();\n const [deleteMeeting, { loading: deletingLoading }] =\n useDeleteMeetingMutation();\n\n const meetings = data?.meetings ?? [];\n const hasMore = meetings.length >= limit;\n\n const handleCreate = async (input: CreateMeetingInput) => {\n await createMeeting({ variables: { input } });\n await refetch();\n setCreateOpen(false);\n };\n\n const handleUpdate = async (input: UpdateMeetingInput) => {\n if (!editing) return;\n await updateMeeting({\n variables: { id: editing.id, workspaceId, input },\n });\n await refetch();\n setEditing(null);\n };\n\n const handleDelete = async () => {\n if (!deleting) return;\n await deleteMeeting({\n variables: { id: deleting.id, workspaceId },\n });\n setDeleting(null);\n await refetch();\n };\n\n const handleLoadMore = async () => {\n const nextLimit = limit + PAGE_SIZE;\n setLimit(nextLimit);\n await fetchMore({ variables: { limit: nextLimit, offset: 0 } });\n };\n\n const addButton = (\n <button\n onClick={() => setCreateOpen(true)}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Schedule Meeting\n </button>\n );\n\n if (loading && meetings.length === 0) {\n return (\n <PageLayout\n title=\"Meetings\"\n description=\"Schedule and track meetings\"\n actions={addButton}\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Meetings\" description=\"Schedule and track meetings\">\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load meetings. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Meetings\"\n description=\"Schedule and track meetings\"\n purpose=\"Keep every meeting and its recording in one place and see how much of your week goes to them — so you can protect focus time, trim low-value calls, and account for meeting hours in your tracking.\"\n actions={addButton}\n >\n {meetings.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No meetings found\"\n description=\"Schedule your first meeting to start tracking calls and their time impact.\"\n action={addButton}\n />\n ) : (\n <>\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {meetings.map((meeting: MeetingFieldsFragment) => (\n <div\n key={meeting.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/meetings/${meeting.id}`)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n navigate(`/meetings/${meeting.id}`);\n }\n }}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n >\n <div className=\"flex items-center gap-3\">\n <CalendarDays className=\"h-5 w-5 text-text-secondary\" />\n <div>\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {meeting.title}\n </p>\n {meeting.recordingUrl && (\n <Video className=\"h-3.5 w-3.5 text-text-secondary\" />\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {formatDate(meeting.startTime)} &middot;{\" \"}\n {meeting.duration ?? 0}min &middot;{\" \"}\n {meeting.participants.length} participants\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {meeting.meetingType}\n </span>\n <span className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {meeting.endTime ? \"completed\" : \"scheduled\"}\n </span>\n <button\n onClick={(e) => {\n e.stopPropagation();\n setEditing(meeting);\n }}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Edit meeting\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={(e) => {\n e.stopPropagation();\n setDeleting(meeting);\n }}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-status-error-bg hover:text-status-error-text\"\n aria-label=\"Delete meeting\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n {hasMore && (\n <div className=\"mt-4 flex justify-center\">\n <button\n onClick={handleLoadMore}\n disabled={loading}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n {loading ? \"Loading...\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n {createOpen && (\n <MeetingFormModal\n open\n onClose={() => setCreateOpen(false)}\n workspaceId={workspaceId}\n submitting={creating}\n error={Boolean(createError)}\n onCreate={handleCreate}\n />\n )}\n\n {editing && (\n <MeetingFormModal\n open\n onClose={() => setEditing(null)}\n workspaceId={workspaceId}\n meeting={editing}\n submitting={updating}\n error={Boolean(updateError)}\n onUpdate={handleUpdate}\n />\n )}\n\n <Modal\n open={Boolean(deleting)}\n onClose={() => setDeleting(null)}\n title=\"Delete meeting\"\n description=\"This permanently removes the meeting and its analytics.\"\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleting(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleDelete}\n disabled={deletingLoading}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/90 disabled:opacity-50\"\n >\n {deletingLoading ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete{\" \"}\n <span className=\"font-medium text-text-primary\">\n {deleting?.title}\n </span>\n ? This action cannot be undone.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}