@burdenoff/microfe-timecampus 2026.626.2 → 2026.703.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/wspace-operations.js +996 -816
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js +28 -0
- package/dist/generated/wspace-types.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/timecampus/TimeCampusRoutes.js +16 -12
- package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
- package/dist/timecampus/components/Modal.js +65 -0
- package/dist/timecampus/components/Modal.js.map +1 -0
- package/dist/timecampus/components/StatusBadge.js +28 -0
- package/dist/timecampus/components/StatusBadge.js.map +1 -0
- package/dist/timecampus/components/Toast.js +87 -0
- package/dist/timecampus/components/Toast.js.map +1 -0
- package/dist/timecampus/index.js +1 -1
- package/dist/timecampus/pages/HomePage.js +143 -135
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +374 -92
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +461 -87
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.js +156 -0
- package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +367 -65
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +4 -3
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +229 -97
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +315 -110
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js +250 -0
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -0
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/dist/timecampus/store/timecampusStore.js +13 -1
- package/dist/timecampus/store/timecampusStore.js.map +1 -1
- package/dist/timecampus/utils/helpers.js +20 -10
- package/dist/timecampus/utils/helpers.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,156 @@
|
|
|
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 { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
//#region src/timecampus/pages/meetings/MeetingFormModal.tsx
|
|
6
|
+
var o = [
|
|
7
|
+
{
|
|
8
|
+
value: e.Team,
|
|
9
|
+
label: "Team"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
value: e.Standup,
|
|
13
|
+
label: "Standup"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: e.OneOnOne,
|
|
17
|
+
label: "One-on-one"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: e.Planning,
|
|
21
|
+
label: "Planning"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: e.Review,
|
|
25
|
+
label: "Review"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: e.Retrospective,
|
|
29
|
+
label: "Retrospective"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
value: e.External,
|
|
33
|
+
label: "External"
|
|
34
|
+
}
|
|
35
|
+
], s = "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", c = "block text-sm font-medium text-text-primary";
|
|
36
|
+
function l(e) {
|
|
37
|
+
if (!e) return "";
|
|
38
|
+
let t = new Date(e);
|
|
39
|
+
if (Number.isNaN(t.getTime())) return "";
|
|
40
|
+
let n = (e) => String(e).padStart(2, "0");
|
|
41
|
+
return `${t.getFullYear()}-${n(t.getMonth() + 1)}-${n(t.getDate())}T${n(t.getHours())}:${n(t.getMinutes())}`;
|
|
42
|
+
}
|
|
43
|
+
var u = ({ open: u, onClose: d, meeting: f, workspaceId: p, submitting: m, error: h, onCreate: g, onUpdate: _ }) => {
|
|
44
|
+
let v = !!f, [y, b] = n(f?.title ?? ""), [x, S] = n(f?.meetingType ?? e.Team), [C, w] = n(l(f?.startTime)), [T, E] = n(f?.duration ?? 30), [D, O] = n(f?.notes ?? ""), [k, A] = n(f?.recordingUrl ?? ""), j = !!(y.trim() && C) && !m;
|
|
45
|
+
return /* @__PURE__ */ a(t, {
|
|
46
|
+
open: u,
|
|
47
|
+
onClose: d,
|
|
48
|
+
title: v ? "Edit Meeting" : "Schedule Meeting",
|
|
49
|
+
description: v ? "Update this meeting's details." : "Add a meeting to track its time, cost and follow-ups.",
|
|
50
|
+
size: "xl",
|
|
51
|
+
footer: /* @__PURE__ */ a(r, { children: [/* @__PURE__ */ i("button", {
|
|
52
|
+
onClick: d,
|
|
53
|
+
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
54
|
+
children: "Cancel"
|
|
55
|
+
}), /* @__PURE__ */ i("button", {
|
|
56
|
+
onClick: async () => {
|
|
57
|
+
if (!j) return;
|
|
58
|
+
let e = new Date(C).toISOString(), t = T > 0 ? new Date(new Date(C).getTime() + T * 6e4).toISOString() : void 0;
|
|
59
|
+
v && _ ? await _({
|
|
60
|
+
title: y.trim(),
|
|
61
|
+
meetingType: x,
|
|
62
|
+
startTime: e,
|
|
63
|
+
endTime: t,
|
|
64
|
+
notes: D.trim() || void 0,
|
|
65
|
+
recordingUrl: k.trim() || void 0
|
|
66
|
+
}) : g && await g({
|
|
67
|
+
workspaceId: p,
|
|
68
|
+
title: y.trim(),
|
|
69
|
+
meetingType: x,
|
|
70
|
+
startTime: e,
|
|
71
|
+
endTime: t,
|
|
72
|
+
notes: D.trim() || void 0,
|
|
73
|
+
recordingUrl: k.trim() || void 0
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
disabled: !j,
|
|
77
|
+
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",
|
|
78
|
+
children: m ? v ? "Saving..." : "Scheduling..." : v ? "Save Changes" : "Schedule"
|
|
79
|
+
})] }),
|
|
80
|
+
children: [h && /* @__PURE__ */ i("div", {
|
|
81
|
+
className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
|
|
82
|
+
children: v ? "Failed to update meeting. Please try again." : "Failed to schedule meeting. Please try again."
|
|
83
|
+
}), /* @__PURE__ */ a("div", {
|
|
84
|
+
className: "space-y-4",
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
|
|
87
|
+
className: c,
|
|
88
|
+
children: "Meeting Title"
|
|
89
|
+
}), /* @__PURE__ */ i("input", {
|
|
90
|
+
type: "text",
|
|
91
|
+
placeholder: "Enter meeting title",
|
|
92
|
+
value: y,
|
|
93
|
+
onChange: (e) => b(e.target.value),
|
|
94
|
+
className: s
|
|
95
|
+
})] }),
|
|
96
|
+
/* @__PURE__ */ a("div", {
|
|
97
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
98
|
+
children: [/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
|
|
99
|
+
className: c,
|
|
100
|
+
children: "Type"
|
|
101
|
+
}), /* @__PURE__ */ i("select", {
|
|
102
|
+
value: x,
|
|
103
|
+
onChange: (e) => S(e.target.value),
|
|
104
|
+
className: s,
|
|
105
|
+
children: o.map((e) => /* @__PURE__ */ i("option", {
|
|
106
|
+
value: e.value,
|
|
107
|
+
children: e.label
|
|
108
|
+
}, e.value))
|
|
109
|
+
})] }), /* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
|
|
110
|
+
className: c,
|
|
111
|
+
children: "Duration (minutes)"
|
|
112
|
+
}), /* @__PURE__ */ i("input", {
|
|
113
|
+
type: "number",
|
|
114
|
+
min: 0,
|
|
115
|
+
value: T,
|
|
116
|
+
onChange: (e) => E(Number(e.target.value)),
|
|
117
|
+
className: s
|
|
118
|
+
})] })]
|
|
119
|
+
}),
|
|
120
|
+
/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
|
|
121
|
+
className: c,
|
|
122
|
+
children: "Start"
|
|
123
|
+
}), /* @__PURE__ */ i("input", {
|
|
124
|
+
type: "datetime-local",
|
|
125
|
+
value: C,
|
|
126
|
+
onChange: (e) => w(e.target.value),
|
|
127
|
+
className: s
|
|
128
|
+
})] }),
|
|
129
|
+
/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
|
|
130
|
+
className: c,
|
|
131
|
+
children: "Recording URL"
|
|
132
|
+
}), /* @__PURE__ */ i("input", {
|
|
133
|
+
type: "url",
|
|
134
|
+
placeholder: "https://...",
|
|
135
|
+
value: k,
|
|
136
|
+
onChange: (e) => A(e.target.value),
|
|
137
|
+
className: s
|
|
138
|
+
})] }),
|
|
139
|
+
/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
|
|
140
|
+
className: c,
|
|
141
|
+
children: "Notes / Agenda"
|
|
142
|
+
}), /* @__PURE__ */ i("textarea", {
|
|
143
|
+
placeholder: "Meeting agenda...",
|
|
144
|
+
rows: 3,
|
|
145
|
+
value: D,
|
|
146
|
+
onChange: (e) => O(e.target.value),
|
|
147
|
+
className: s
|
|
148
|
+
})] })
|
|
149
|
+
]
|
|
150
|
+
})]
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
//#endregion
|
|
154
|
+
export { u as MeetingFormModal };
|
|
155
|
+
|
|
156
|
+
//# 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 { 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\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\n const canSubmit = Boolean(title.trim() && start) && !submitting;\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 URL</label>\n <input\n type=\"url\"\n placeholder=\"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
|
|
3
|
-
import { LoadingSkeleton as
|
|
4
|
-
import { useListMeetingsQuery as
|
|
5
|
-
import { PageLayout as
|
|
6
|
-
import { EmptyState as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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
|
-
|
|
12
|
-
|
|
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:
|
|
15
|
-
limit:
|
|
18
|
+
workspaceId: C,
|
|
19
|
+
limit: T,
|
|
20
|
+
offset: 0
|
|
16
21
|
},
|
|
17
|
-
skip: !
|
|
18
|
-
|
|
19
|
-
|
|
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:
|
|
23
|
-
|
|
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
|
-
}) :
|
|
56
|
+
}) : F ? /* @__PURE__ */ y(c, {
|
|
31
57
|
title: "Meetings",
|
|
32
58
|
description: "Schedule and track meetings",
|
|
33
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
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:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
}
|
|
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 {
|
|
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)} ·{\" \"}\n {meeting.duration ?? 0}min ·{\" \"}\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)} ·{\" \"}\n {meeting.duration ?? 0}min ·{\" \"}\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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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { MeetingParticipantRole as e, MeetingParticipantStatus as t } from "../../../generated/wspace-types.js";
|
|
2
|
+
import { Modal as n } from "../../components/Modal.js";
|
|
3
|
+
import { useState as r } from "react";
|
|
4
|
+
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
|
+
//#region src/timecampus/pages/meetings/ParticipantFormModal.tsx
|
|
6
|
+
var s = "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", c = "block text-sm font-medium text-text-primary", l = [
|
|
7
|
+
{
|
|
8
|
+
value: e.Required,
|
|
9
|
+
label: "Required"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
value: e.Optional,
|
|
13
|
+
label: "Optional"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: e.Organizer,
|
|
17
|
+
label: "Organizer"
|
|
18
|
+
}
|
|
19
|
+
], u = [
|
|
20
|
+
{
|
|
21
|
+
value: t.Pending,
|
|
22
|
+
label: "Pending"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: t.Accepted,
|
|
26
|
+
label: "Accepted"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
value: t.Tentative,
|
|
30
|
+
label: "Tentative"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: t.Declined,
|
|
34
|
+
label: "Declined"
|
|
35
|
+
}
|
|
36
|
+
], d = ({ open: d, onClose: f, defaultUserId: p, submitting: m, error: h, onSubmit: g }) => {
|
|
37
|
+
let [_, v] = r(p ?? ""), [y, b] = r(e.Required), [x, S] = r(t.Pending), C = !!_.trim() && !m;
|
|
38
|
+
return /* @__PURE__ */ o(n, {
|
|
39
|
+
open: d,
|
|
40
|
+
onClose: f,
|
|
41
|
+
title: "Add participant",
|
|
42
|
+
description: "Record who attended so their time counts toward this meeting's cost.",
|
|
43
|
+
footer: /* @__PURE__ */ o(i, { children: [/* @__PURE__ */ a("button", {
|
|
44
|
+
onClick: f,
|
|
45
|
+
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
46
|
+
children: "Cancel"
|
|
47
|
+
}), /* @__PURE__ */ a("button", {
|
|
48
|
+
onClick: async () => {
|
|
49
|
+
C && await g({
|
|
50
|
+
userId: _.trim(),
|
|
51
|
+
role: y,
|
|
52
|
+
status: x
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
disabled: !C,
|
|
56
|
+
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",
|
|
57
|
+
children: m ? "Adding..." : "Add participant"
|
|
58
|
+
})] }),
|
|
59
|
+
children: [h && /* @__PURE__ */ a("div", {
|
|
60
|
+
className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
|
|
61
|
+
children: "Failed to add participant. Please try again."
|
|
62
|
+
}), /* @__PURE__ */ o("div", {
|
|
63
|
+
className: "space-y-4",
|
|
64
|
+
children: [/* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("label", {
|
|
65
|
+
className: c,
|
|
66
|
+
children: "User ID"
|
|
67
|
+
}), /* @__PURE__ */ a("input", {
|
|
68
|
+
type: "text",
|
|
69
|
+
placeholder: "User ID",
|
|
70
|
+
value: _,
|
|
71
|
+
onChange: (e) => v(e.target.value),
|
|
72
|
+
className: s
|
|
73
|
+
})] }), /* @__PURE__ */ o("div", {
|
|
74
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
75
|
+
children: [/* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("label", {
|
|
76
|
+
className: c,
|
|
77
|
+
children: "Role"
|
|
78
|
+
}), /* @__PURE__ */ a("select", {
|
|
79
|
+
value: y,
|
|
80
|
+
onChange: (e) => b(e.target.value),
|
|
81
|
+
className: s,
|
|
82
|
+
children: l.map((e) => /* @__PURE__ */ a("option", {
|
|
83
|
+
value: e.value,
|
|
84
|
+
children: e.label
|
|
85
|
+
}, e.value))
|
|
86
|
+
})] }), /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("label", {
|
|
87
|
+
className: c,
|
|
88
|
+
children: "Status"
|
|
89
|
+
}), /* @__PURE__ */ a("select", {
|
|
90
|
+
value: x,
|
|
91
|
+
onChange: (e) => S(e.target.value),
|
|
92
|
+
className: s,
|
|
93
|
+
children: u.map((e) => /* @__PURE__ */ a("option", {
|
|
94
|
+
value: e.value,
|
|
95
|
+
children: e.label
|
|
96
|
+
}, e.value))
|
|
97
|
+
})] })]
|
|
98
|
+
})]
|
|
99
|
+
})]
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
//#endregion
|
|
103
|
+
export { d as ParticipantFormModal };
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=ParticipantFormModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParticipantFormModal.js","names":[],"sources":["../../../../src/timecampus/pages/meetings/ParticipantFormModal.tsx"],"sourcesContent":["import { useState, type FC } from \"react\";\nimport { Modal } from \"../../components/Modal\";\nimport {\n MeetingParticipantRole,\n MeetingParticipantStatus,\n type AddMeetingParticipantInput,\n} from \"@/generated/wspace-types\";\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\nconst ROLES: { value: MeetingParticipantRole; label: string }[] = [\n { value: MeetingParticipantRole.Required, label: \"Required\" },\n { value: MeetingParticipantRole.Optional, label: \"Optional\" },\n { value: MeetingParticipantRole.Organizer, label: \"Organizer\" },\n];\n\nconst STATUSES: { value: MeetingParticipantStatus; label: string }[] = [\n { value: MeetingParticipantStatus.Pending, label: \"Pending\" },\n { value: MeetingParticipantStatus.Accepted, label: \"Accepted\" },\n { value: MeetingParticipantStatus.Tentative, label: \"Tentative\" },\n { value: MeetingParticipantStatus.Declined, label: \"Declined\" },\n];\n\ninterface ParticipantFormModalProps {\n open: boolean;\n onClose: () => void;\n defaultUserId?: string;\n submitting: boolean;\n error?: boolean;\n onSubmit: (input: AddMeetingParticipantInput) => Promise<void> | void;\n}\n\nexport const ParticipantFormModal: FC<ParticipantFormModalProps> = ({\n open,\n onClose,\n defaultUserId,\n submitting,\n error,\n onSubmit,\n}) => {\n const [userId, setUserId] = useState(defaultUserId ?? \"\");\n const [role, setRole] = useState<MeetingParticipantRole>(\n MeetingParticipantRole.Required,\n );\n const [status, setStatus] = useState<MeetingParticipantStatus>(\n MeetingParticipantStatus.Pending,\n );\n\n const canSubmit = Boolean(userId.trim()) && !submitting;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n await onSubmit({ userId: userId.trim(), role, status });\n };\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n title=\"Add participant\"\n description=\"Record who attended so their time counts toward this meeting's cost.\"\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 ? \"Adding...\" : \"Add participant\"}\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 Failed to add participant. Please try again.\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className={labelClass}>User ID</label>\n <input\n type=\"text\"\n placeholder=\"User ID\"\n value={userId}\n onChange={(e) => setUserId(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}>Role</label>\n <select\n value={role}\n onChange={(e) =>\n setRole(e.target.value as MeetingParticipantRole)\n }\n className={inputClass}\n >\n {ROLES.map((r) => (\n <option key={r.value} value={r.value}>\n {r.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className={labelClass}>Status</label>\n <select\n value={status}\n onChange={(e) =>\n setStatus(e.target.value as MeetingParticipantStatus)\n }\n className={inputClass}\n >\n {STATUSES.map((s) => (\n <option key={s.value} value={s.value}>\n {s.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n </div>\n </Modal>\n );\n};\n"],"mappings":";;;;;AAQA,IAAM,IACJ,wIACI,IAAa,+CAEb,IAA4D;CAChE;EAAE,OAAO,EAAuB;EAAU,OAAO;EAAY;CAC7D;EAAE,OAAO,EAAuB;EAAU,OAAO;EAAY;CAC7D;EAAE,OAAO,EAAuB;EAAW,OAAO;EAAa;CAChE,EAEK,IAAiE;CACrE;EAAE,OAAO,EAAyB;EAAS,OAAO;EAAW;CAC7D;EAAE,OAAO,EAAyB;EAAU,OAAO;EAAY;CAC/D;EAAE,OAAO,EAAyB;EAAW,OAAO;EAAa;CACjE;EAAE,OAAO,EAAyB;EAAU,OAAO;EAAY;CAChE,EAWY,KAAuD,EAClE,SACA,YACA,kBACA,eACA,UACA,kBACI;CACJ,IAAM,CAAC,GAAQ,KAAa,EAAS,KAAiB,GAAG,EACnD,CAAC,GAAM,KAAW,EACtB,EAAuB,SACxB,EACK,CAAC,GAAQ,KAAa,EAC1B,EAAyB,QAC1B,EAEK,IAAY,EAAQ,EAAO,MAAM,IAAK,CAAC;AAO7C,QACE,kBAAC,GAAD;EACQ;EACG;EACT,OAAM;EACN,aAAY;EACZ,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,UAAD;GACE,SAAS;GACT,WAAU;aACX;GAEQ,CAAA,EACT,kBAAC,UAAD;GACE,SApBW,YAAY;AAC1B,SACL,MAAM,EAAS;KAAE,QAAQ,EAAO,MAAM;KAAE;KAAM;KAAQ,CAAC;;GAmB/C,UAAU,CAAC;GACX,WAAU;aAET,IAAa,cAAc;GACrB,CAAA,CACR,EAAA,CAAA;YApBP,CAuBG,KACC,kBAAC,OAAD;GAAK,WAAU;aAAyG;GAElH,CAAA,EAER,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IAAO,WAAW;cAAY;IAAe,CAAA,EAC7C,kBAAC,SAAD;IACE,MAAK;IACL,aAAY;IACZ,OAAO;IACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;IAC1C,WAAW;IACX,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,WAAW;eAAY;KAAY,CAAA,EAC1C,kBAAC,UAAD;KACE,OAAO;KACP,WAAW,MACT,EAAQ,EAAE,OAAO,MAAgC;KAEnD,WAAW;eAEV,EAAM,KAAK,MACV,kBAAC,UAAD;MAAsB,OAAO,EAAE;gBAC5B,EAAE;MACI,EAFI,EAAE,MAEN,CACT;KACK,CAAA,CACL,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,WAAW;eAAY;KAAc,CAAA,EAC5C,kBAAC,UAAD;KACE,OAAO;KACP,WAAW,MACT,EAAU,EAAE,OAAO,MAAkC;KAEvD,WAAW;eAEV,EAAS,KAAK,MACb,kBAAC,UAAD;MAAsB,OAAO,EAAE;gBAC5B,EAAE;MACI,EAFI,EAAE,MAEN,CACT;KACK,CAAA,CACL,EAAA,CAAA,CACF;MACF;KACA"}
|