@burdenoff/microfe-comradecircle 2026.819.1 → 2026.830.1
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/comradecircle/components/AvatarUploadField.js +8 -8
- package/dist/comradecircle/components/CircleCard.js +4 -4
- package/dist/comradecircle/components/CommentThread.d.ts.map +1 -1
- package/dist/comradecircle/components/CommentThread.js +66 -61
- package/dist/comradecircle/components/CommentThread.js.map +1 -1
- package/dist/comradecircle/components/CreateCircleDialog.d.ts.map +1 -1
- package/dist/comradecircle/components/CreateCircleDialog.js +89 -88
- package/dist/comradecircle/components/CreateCircleDialog.js.map +1 -1
- package/dist/comradecircle/components/CreateEventDialog.d.ts.map +1 -1
- package/dist/comradecircle/components/CreateEventDialog.js +28 -27
- package/dist/comradecircle/components/CreateEventDialog.js.map +1 -1
- package/dist/comradecircle/components/DraftsPanel.d.ts +32 -0
- package/dist/comradecircle/components/DraftsPanel.d.ts.map +1 -0
- package/dist/comradecircle/components/DraftsPanel.js +176 -0
- package/dist/comradecircle/components/DraftsPanel.js.map +1 -0
- package/dist/comradecircle/components/EventCard.js +5 -5
- package/dist/comradecircle/components/FileImage.js +5 -5
- package/dist/comradecircle/components/ImageAttachments.js +21 -21
- package/dist/comradecircle/components/MemberIdentity.js +4 -4
- package/dist/comradecircle/components/MobileBottomNav.d.ts.map +1 -1
- package/dist/comradecircle/components/MobileBottomNav.js +37 -30
- package/dist/comradecircle/components/MobileBottomNav.js.map +1 -1
- package/dist/comradecircle/components/Pagination.d.ts.map +1 -1
- package/dist/comradecircle/components/Pagination.js +37 -28
- package/dist/comradecircle/components/Pagination.js.map +1 -1
- package/dist/comradecircle/components/PollCard.js +13 -13
- package/dist/comradecircle/components/PostCard.d.ts.map +1 -1
- package/dist/comradecircle/components/PostCard.js +102 -89
- package/dist/comradecircle/components/PostCard.js.map +1 -1
- package/dist/comradecircle/components/PostComposer.d.ts +11 -5
- package/dist/comradecircle/components/PostComposer.d.ts.map +1 -1
- package/dist/comradecircle/components/PostComposer.js +60 -48
- package/dist/comradecircle/components/PostComposer.js.map +1 -1
- package/dist/comradecircle/components/ReactionBar.d.ts.map +1 -1
- package/dist/comradecircle/components/ReactionBar.js +65 -55
- package/dist/comradecircle/components/ReactionBar.js.map +1 -1
- package/dist/comradecircle/components/StateViews.d.ts.map +1 -1
- package/dist/comradecircle/components/StateViews.js +38 -30
- package/dist/comradecircle/components/StateViews.js.map +1 -1
- package/dist/comradecircle/components/UpcomingEvents.d.ts +26 -0
- package/dist/comradecircle/components/UpcomingEvents.d.ts.map +1 -0
- package/dist/comradecircle/components/UpcomingEvents.js +126 -0
- package/dist/comradecircle/components/UpcomingEvents.js.map +1 -0
- package/dist/comradecircle/components/index.d.ts +2 -0
- package/dist/comradecircle/components/index.d.ts.map +1 -1
- package/dist/comradecircle/hooks/index.d.ts +1 -0
- package/dist/comradecircle/hooks/index.d.ts.map +1 -1
- package/dist/comradecircle/hooks/index.js +1 -0
- package/dist/comradecircle/hooks/useTr.d.ts +18 -0
- package/dist/comradecircle/hooks/useTr.d.ts.map +1 -0
- package/dist/comradecircle/hooks/useTr.js +12 -0
- package/dist/comradecircle/hooks/useTr.js.map +1 -0
- package/dist/comradecircle/pages/FeedPage.d.ts +5 -0
- package/dist/comradecircle/pages/FeedPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/FeedPage.js +76 -62
- package/dist/comradecircle/pages/FeedPage.js.map +1 -1
- package/dist/comradecircle/pages/circles/CircleDetailPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/circles/CircleDetailPage.js +454 -409
- package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -1
- package/dist/comradecircle/pages/circles/CirclesPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/circles/CirclesPage.js +124 -117
- package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -1
- package/dist/comradecircle/pages/events/EventDetailPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/events/EventDetailPage.js +182 -153
- package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -1
- package/dist/comradecircle/pages/events/EventsPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/events/EventsPage.js +100 -91
- package/dist/comradecircle/pages/events/EventsPage.js.map +1 -1
- package/dist/comradecircle/pages/members/MemberProfilePage.d.ts.map +1 -1
- package/dist/comradecircle/pages/members/MemberProfilePage.js +172 -159
- package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -1
- package/dist/comradecircle/pages/members/MembersPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/members/MembersPage.js +103 -104
- package/dist/comradecircle/pages/members/MembersPage.js.map +1 -1
- package/dist/comradecircle/pages/monetization/MonetizationPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/monetization/MonetizationPage.js +319 -297
- package/dist/comradecircle/pages/monetization/MonetizationPage.js.map +1 -1
- package/dist/comradecircle/pages/posts/PostThreadPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/posts/PostThreadPage.js +37 -32
- package/dist/comradecircle/pages/posts/PostThreadPage.js.map +1 -1
- package/dist/comradecircle/pages/profile/MyProfilePage.d.ts.map +1 -1
- package/dist/comradecircle/pages/profile/MyProfilePage.js +182 -177
- package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -1
- package/dist/comradecircle/utils/i18n.js +15 -0
- package/dist/comradecircle/utils/i18n.js.map +1 -0
- package/dist/comradecircle/utils/pageContext.d.ts.map +1 -1
- package/dist/comradecircle/utils/pageContext.js +3 -1
- package/dist/comradecircle/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateEventDialog.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/CreateEventDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwBH,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,UAAU,CAAC;AAUvD,UAAU,sBAAsB;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CACvC;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,SAAS,GACV,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"CreateEventDialog.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/CreateEventDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwBH,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,UAAU,CAAC;AAUvD,UAAU,sBAAsB;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CACvC;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,SAAS,GACV,EAAE,sBAAsB,2CAkQxB"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { useComCircles as e, useComCreateEvent as t } from "../hooks/useComradeCircleApi.js";
|
|
2
|
+
import { useTr as ee } from "../hooks/useTr.js";
|
|
2
3
|
import "../hooks/index.js";
|
|
3
4
|
import { describeError as n, notify as r } from "../lib/toast.js";
|
|
4
5
|
import { formatEnumLabel as i } from "../utils/formatters.js";
|
|
5
6
|
import { TagInput as a } from "./TagInput.js";
|
|
6
7
|
import { useState as o } from "react";
|
|
7
8
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
8
|
-
import { Button as l, Input as u, Label as d, ResponsiveDialog as f, ResponsiveDialogContent as
|
|
9
|
+
import { Button as l, Input as u, Label as d, ResponsiveDialog as f, ResponsiveDialogContent as te, ResponsiveDialogDescription as p, ResponsiveDialogFooter as m, ResponsiveDialogHeader as h, ResponsiveDialogTitle as g, Select as _, SelectContent as v, SelectItem as y, SelectTrigger as b, SelectValue as x, Textarea as S } from "@burdenoff/fe-libs/ui";
|
|
9
10
|
//#region src/comradecircle/components/CreateEventDialog.tsx
|
|
10
11
|
var C = [
|
|
11
12
|
"ONLINE",
|
|
@@ -16,26 +17,26 @@ var C = [
|
|
|
16
17
|
let t = new Date(e);
|
|
17
18
|
return Number.isNaN(t.getTime()) ? null : t.toISOString();
|
|
18
19
|
};
|
|
19
|
-
function T({ open: T, onOpenChange: E, circleId: D, onCreated:
|
|
20
|
-
let k = t(), A = e({ status: "ACTIVE" }), [j, M] = o(""), [N, P] = o(""), [F, I] = o(""), [L, R] = o("ONLINE"), [z, B] = o(""), [V, H] = o(""), [U, W] = o(""), [G, K] = o(""), [q, J] = o(""), [Y, X] = o([]), [Z, Q] = o(null), $ = D ?? j,
|
|
20
|
+
function T({ open: T, onOpenChange: E, circleId: D, onCreated: ne }) {
|
|
21
|
+
let O = ee(), k = t(), A = e({ status: "ACTIVE" }), [j, M] = o(""), [N, P] = o(""), [F, I] = o(""), [L, R] = o("ONLINE"), [z, B] = o(""), [V, H] = o(""), [U, W] = o(""), [G, K] = o(""), [q, J] = o(""), [Y, X] = o([]), [Z, Q] = o(null), $ = D ?? j, re = () => {
|
|
21
22
|
M(""), P(""), I(""), R("ONLINE"), B(""), H(""), W(""), K(""), J(""), X([]), Q(null);
|
|
22
23
|
};
|
|
23
24
|
return /* @__PURE__ */ s(f, {
|
|
24
25
|
open: T,
|
|
25
26
|
onOpenChange: E,
|
|
26
|
-
children: /* @__PURE__ */ c(
|
|
27
|
+
children: /* @__PURE__ */ c(te, {
|
|
27
28
|
className: "sm:max-w-lg",
|
|
28
29
|
children: [
|
|
29
|
-
/* @__PURE__ */ c(h, { children: [/* @__PURE__ */ s(g, { children: "Schedule an event" }), /* @__PURE__ */ s(
|
|
30
|
+
/* @__PURE__ */ c(h, { children: [/* @__PURE__ */ s(g, { children: O("comradecircle.dialogs.createEvent.title", "Schedule an event") }), /* @__PURE__ */ s(p, { children: O("comradecircle.dialogs.createEvent.description", "Host a gathering for your circle — online, in person, or hybrid.") })] }),
|
|
30
31
|
/* @__PURE__ */ c("div", {
|
|
31
32
|
className: "flex max-h-[60vh] flex-col gap-4 overflow-y-auto py-2",
|
|
32
33
|
children: [
|
|
33
34
|
!D && /* @__PURE__ */ c("div", {
|
|
34
35
|
className: "grid gap-1.5",
|
|
35
|
-
children: [/* @__PURE__ */ s(d, { children: "Circle" }), /* @__PURE__ */ c(_, {
|
|
36
|
+
children: [/* @__PURE__ */ s(d, { children: O("comradecircle.dialogs.createEvent.circleLabel", "Circle") }), /* @__PURE__ */ c(_, {
|
|
36
37
|
value: j,
|
|
37
38
|
onValueChange: M,
|
|
38
|
-
children: [/* @__PURE__ */ s(b, { children: /* @__PURE__ */ s(x, { placeholder: "Choose a circle" }) }), /* @__PURE__ */ s(v, { children: (A.data?.items ?? []).map((e) => /* @__PURE__ */ s(y, {
|
|
39
|
+
children: [/* @__PURE__ */ s(b, { children: /* @__PURE__ */ s(x, { placeholder: O("comradecircle.dialogs.createEvent.circlePlaceholder", "Choose a circle") }) }), /* @__PURE__ */ s(v, { children: (A.data?.items ?? []).map((e) => /* @__PURE__ */ s(y, {
|
|
39
40
|
value: e.id,
|
|
40
41
|
children: e.name
|
|
41
42
|
}, e.id)) })]
|
|
@@ -45,19 +46,19 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
45
46
|
className: "grid gap-1.5",
|
|
46
47
|
children: [/* @__PURE__ */ s(d, {
|
|
47
48
|
htmlFor: "event-title",
|
|
48
|
-
children: "Title"
|
|
49
|
+
children: O("comradecircle.dialogs.createEvent.titleLabel", "Title")
|
|
49
50
|
}), /* @__PURE__ */ s(u, {
|
|
50
51
|
id: "event-title",
|
|
51
52
|
value: N,
|
|
52
53
|
onChange: (e) => P(e.target.value),
|
|
53
|
-
placeholder: "e.g. Monthly community call"
|
|
54
|
+
placeholder: O("comradecircle.dialogs.createEvent.titlePlaceholder", "e.g. Monthly community call")
|
|
54
55
|
})]
|
|
55
56
|
}),
|
|
56
57
|
/* @__PURE__ */ c("div", {
|
|
57
58
|
className: "grid gap-1.5",
|
|
58
59
|
children: [/* @__PURE__ */ s(d, {
|
|
59
60
|
htmlFor: "event-desc",
|
|
60
|
-
children: "Description"
|
|
61
|
+
children: O("comradecircle.dialogs.createEvent.descriptionLabel", "Description")
|
|
61
62
|
}), /* @__PURE__ */ s(S, {
|
|
62
63
|
id: "event-desc",
|
|
63
64
|
value: F,
|
|
@@ -69,7 +70,7 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
69
70
|
className: "grid grid-cols-2 gap-3",
|
|
70
71
|
children: [/* @__PURE__ */ c("div", {
|
|
71
72
|
className: "grid gap-1.5",
|
|
72
|
-
children: [/* @__PURE__ */ s(d, { children: "Mode" }), /* @__PURE__ */ c(_, {
|
|
73
|
+
children: [/* @__PURE__ */ s(d, { children: O("comradecircle.dialogs.createEvent.modeLabel", "Mode") }), /* @__PURE__ */ c(_, {
|
|
73
74
|
value: L,
|
|
74
75
|
onValueChange: (e) => R(e),
|
|
75
76
|
children: [/* @__PURE__ */ s(b, { children: /* @__PURE__ */ s(x, {}) }), /* @__PURE__ */ s(v, { children: C.map((e) => /* @__PURE__ */ s(y, {
|
|
@@ -81,14 +82,14 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
81
82
|
className: "grid gap-1.5",
|
|
82
83
|
children: [/* @__PURE__ */ s(d, {
|
|
83
84
|
htmlFor: "event-capacity",
|
|
84
|
-
children: "Capacity"
|
|
85
|
+
children: O("comradecircle.dialogs.createEvent.capacityLabel", "Capacity")
|
|
85
86
|
}), /* @__PURE__ */ s(u, {
|
|
86
87
|
id: "event-capacity",
|
|
87
88
|
type: "number",
|
|
88
89
|
min: 0,
|
|
89
90
|
value: U,
|
|
90
91
|
onChange: (e) => W(e.target.value),
|
|
91
|
-
placeholder: "Optional"
|
|
92
|
+
placeholder: O("comradecircle.dialogs.createEvent.capacityPlaceholder", "Optional")
|
|
92
93
|
})]
|
|
93
94
|
})]
|
|
94
95
|
}),
|
|
@@ -96,24 +97,24 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
96
97
|
className: "grid gap-1.5",
|
|
97
98
|
children: [/* @__PURE__ */ s(d, {
|
|
98
99
|
htmlFor: "event-location",
|
|
99
|
-
children: "Location"
|
|
100
|
+
children: O("comradecircle.dialogs.createEvent.locationLabel", "Location")
|
|
100
101
|
}), /* @__PURE__ */ s(u, {
|
|
101
102
|
id: "event-location",
|
|
102
103
|
value: z,
|
|
103
104
|
onChange: (e) => B(e.target.value),
|
|
104
|
-
placeholder: "Venue / address"
|
|
105
|
+
placeholder: O("comradecircle.dialogs.createEvent.locationPlaceholder", "Venue / address")
|
|
105
106
|
})]
|
|
106
107
|
}),
|
|
107
108
|
L !== "IN_PERSON" && /* @__PURE__ */ c("div", {
|
|
108
109
|
className: "grid gap-1.5",
|
|
109
110
|
children: [/* @__PURE__ */ s(d, {
|
|
110
111
|
htmlFor: "event-join",
|
|
111
|
-
children: "Join URL"
|
|
112
|
+
children: O("comradecircle.dialogs.createEvent.joinUrlLabel", "Join URL")
|
|
112
113
|
}), /* @__PURE__ */ s(u, {
|
|
113
114
|
id: "event-join",
|
|
114
115
|
value: V,
|
|
115
116
|
onChange: (e) => H(e.target.value),
|
|
116
|
-
placeholder: "https://…"
|
|
117
|
+
placeholder: O("comradecircle.dialogs.createEvent.joinUrlPlaceholder", "https://…")
|
|
117
118
|
})]
|
|
118
119
|
}),
|
|
119
120
|
/* @__PURE__ */ c("div", {
|
|
@@ -122,7 +123,7 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
122
123
|
className: "grid gap-1.5",
|
|
123
124
|
children: [/* @__PURE__ */ s(d, {
|
|
124
125
|
htmlFor: "event-start",
|
|
125
|
-
children: "Starts"
|
|
126
|
+
children: O("comradecircle.dialogs.createEvent.startsLabel", "Starts")
|
|
126
127
|
}), /* @__PURE__ */ s(u, {
|
|
127
128
|
id: "event-start",
|
|
128
129
|
type: "datetime-local",
|
|
@@ -133,7 +134,7 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
133
134
|
className: "grid gap-1.5",
|
|
134
135
|
children: [/* @__PURE__ */ s(d, {
|
|
135
136
|
htmlFor: "event-end",
|
|
136
|
-
children: "Ends"
|
|
137
|
+
children: O("comradecircle.dialogs.createEvent.endsLabel", "Ends")
|
|
137
138
|
}), /* @__PURE__ */ s(u, {
|
|
138
139
|
id: "event-end",
|
|
139
140
|
type: "datetime-local",
|
|
@@ -146,12 +147,12 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
146
147
|
className: "grid gap-1.5",
|
|
147
148
|
children: [/* @__PURE__ */ s(d, {
|
|
148
149
|
htmlFor: "event-tags",
|
|
149
|
-
children: "Tags"
|
|
150
|
+
children: O("comradecircle.dialogs.createEvent.tagsLabel", "Tags")
|
|
150
151
|
}), /* @__PURE__ */ s(a, {
|
|
151
152
|
id: "event-tags",
|
|
152
153
|
value: Y,
|
|
153
154
|
onChange: X,
|
|
154
|
-
placeholder: "Add tags…"
|
|
155
|
+
placeholder: O("comradecircle.dialogs.createEvent.tagsPlaceholder", "Add tags…")
|
|
155
156
|
})]
|
|
156
157
|
}),
|
|
157
158
|
Z && /* @__PURE__ */ s("p", {
|
|
@@ -163,20 +164,20 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
163
164
|
/* @__PURE__ */ c(m, { children: [/* @__PURE__ */ s(l, {
|
|
164
165
|
variant: "outline",
|
|
165
166
|
onClick: () => E(!1),
|
|
166
|
-
children: "Cancel"
|
|
167
|
+
children: O("common.cancel", "Cancel")
|
|
167
168
|
}), /* @__PURE__ */ s(l, {
|
|
168
169
|
onClick: async () => {
|
|
169
170
|
if (!$) {
|
|
170
|
-
Q("Please choose a circle for this event.");
|
|
171
|
+
Q(O("comradecircle.dialogs.createEvent.circleRequired", "Please choose a circle for this event."));
|
|
171
172
|
return;
|
|
172
173
|
}
|
|
173
174
|
if (!N.trim()) {
|
|
174
|
-
Q("Event title is required.");
|
|
175
|
+
Q(O("comradecircle.dialogs.createEvent.titleRequired", "Event title is required."));
|
|
175
176
|
return;
|
|
176
177
|
}
|
|
177
178
|
let e = w(G);
|
|
178
179
|
if (!e) {
|
|
179
|
-
Q("A valid start date & time is required.");
|
|
180
|
+
Q(O("comradecircle.dialogs.createEvent.startRequired", "A valid start date & time is required."));
|
|
180
181
|
return;
|
|
181
182
|
}
|
|
182
183
|
try {
|
|
@@ -192,13 +193,13 @@ function T({ open: T, onOpenChange: E, circleId: D, onCreated: O }) {
|
|
|
192
193
|
endsAt: w(q),
|
|
193
194
|
tags: Y.length ? Y : null
|
|
194
195
|
});
|
|
195
|
-
r.success("Event scheduled", t.title), E(!1),
|
|
196
|
+
r.success(O("comradecircle.dialogs.createEvent.scheduled", "Event scheduled"), t.title), E(!1), re(), ne?.(t);
|
|
196
197
|
} catch (e) {
|
|
197
198
|
Q(n(e));
|
|
198
199
|
}
|
|
199
200
|
},
|
|
200
201
|
disabled: k.isPending,
|
|
201
|
-
children: "Schedule event"
|
|
202
|
+
children: O("comradecircle.dialogs.createEvent.schedule", "Schedule event")
|
|
202
203
|
})] })
|
|
203
204
|
]
|
|
204
205
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateEventDialog.js","names":[],"sources":["../../../src/comradecircle/components/CreateEventDialog.tsx"],"sourcesContent":["/**\n * CreateEventDialog — schedule a new event in a circle.\n * @module comradecircle/components\n *\n * Writes `comCreateEvent`. When no `circleId` is fixed by the caller, a circle\n * picker (active circles) is shown. Datetime-local inputs are converted to ISO\n * strings for the `DateTime` scalar.\n */\n\nimport { useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { TagInput } from './TagInput';\nimport { useComCreateEvent, useComCircles } from '../hooks';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComEvent, ComEventMode } from '../types';\n\nconst MODES: ComEventMode[] = ['ONLINE', 'IN_PERSON', 'HYBRID'];\n\nconst toIso = (local: string): string | null => {\n if (!local) return null;\n const d = new Date(local);\n return Number.isNaN(d.getTime()) ? null : d.toISOString();\n};\n\ninterface CreateEventDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Fixed circle — when omitted the dialog shows an active-circle picker. */\n circleId?: string;\n onCreated?: (event: ComEvent) => void;\n}\n\nexport function CreateEventDialog({\n open,\n onOpenChange,\n circleId,\n onCreated,\n}: CreateEventDialogProps) {\n const createEvent = useComCreateEvent();\n const circlesQuery = useComCircles({ status: 'ACTIVE' });\n const [pickedCircle, setPickedCircle] = useState('');\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [mode, setMode] = useState<ComEventMode>('ONLINE');\n const [location, setLocation] = useState('');\n const [joinUrl, setJoinUrl] = useState('');\n const [capacity, setCapacity] = useState('');\n const [startsAt, setStartsAt] = useState('');\n const [endsAt, setEndsAt] = useState('');\n const [tags, setTags] = useState<string[]>([]);\n const [error, setError] = useState<string | null>(null);\n\n const effectiveCircle = circleId ?? pickedCircle;\n\n const reset = () => {\n setPickedCircle('');\n setTitle('');\n setDescription('');\n setMode('ONLINE');\n setLocation('');\n setJoinUrl('');\n setCapacity('');\n setStartsAt('');\n setEndsAt('');\n setTags([]);\n setError(null);\n };\n\n const submit = async () => {\n if (!effectiveCircle) {\n setError('Please choose a circle for this event.');\n return;\n }\n if (!title.trim()) {\n setError('Event title is required.');\n return;\n }\n const startIso = toIso(startsAt);\n if (!startIso) {\n setError('A valid start date & time is required.');\n return;\n }\n try {\n const created = await createEvent.mutateAsync({\n circleId: effectiveCircle,\n title: title.trim(),\n description: description.trim() || null,\n mode,\n location: location.trim() || null,\n joinUrl: joinUrl.trim() || null,\n capacity: capacity ? Number(capacity) : null,\n startsAt: startIso,\n endsAt: toIso(endsAt),\n tags: tags.length ? tags : null,\n });\n notify.success('Event scheduled', created.title);\n onOpenChange(false);\n reset();\n onCreated?.(created);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule an event</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Host a gathering for your circle — online, in person, or hybrid.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[60vh] flex-col gap-4 overflow-y-auto py-2\">\n {!circleId && (\n <div className=\"grid gap-1.5\">\n <Label>Circle</Label>\n <Select value={pickedCircle} onValueChange={setPickedCircle}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a circle\" />\n </SelectTrigger>\n <SelectContent>\n {(circlesQuery.data?.items ?? []).map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-title\">Title</Label>\n <Input\n id=\"event-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Monthly community call\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-desc\">Description</Label>\n <Textarea\n id=\"event-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label>Mode</Label>\n <Select value={mode} onValueChange={(v) => setMode(v as ComEventMode)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {MODES.map((m) => (\n <SelectItem key={m} value={m}>\n {formatEnumLabel(m)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-capacity\">Capacity</Label>\n <Input\n id=\"event-capacity\"\n type=\"number\"\n min={0}\n value={capacity}\n onChange={(e) => setCapacity(e.target.value)}\n placeholder=\"Optional\"\n />\n </div>\n </div>\n {mode !== 'ONLINE' && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-location\">Location</Label>\n <Input\n id=\"event-location\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n placeholder=\"Venue / address\"\n />\n </div>\n )}\n {mode !== 'IN_PERSON' && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-join\">Join URL</Label>\n <Input\n id=\"event-join\"\n value={joinUrl}\n onChange={(e) => setJoinUrl(e.target.value)}\n placeholder=\"https://…\"\n />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-start\">Starts</Label>\n <Input\n id=\"event-start\"\n type=\"datetime-local\"\n value={startsAt}\n onChange={(e) => setStartsAt(e.target.value)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-end\">Ends</Label>\n <Input\n id=\"event-end\"\n type=\"datetime-local\"\n value={endsAt}\n onChange={(e) => setEndsAt(e.target.value)}\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-tags\">Tags</Label>\n <TagInput id=\"event-tags\" value={tags} onChange={setTags} placeholder=\"Add tags…\" />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={createEvent.isPending}>\n Schedule event\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateEventDialog.js","names":[],"sources":["../../../src/comradecircle/components/CreateEventDialog.tsx"],"sourcesContent":["/**\n * CreateEventDialog — schedule a new event in a circle.\n * @module comradecircle/components\n *\n * Writes `comCreateEvent`. When no `circleId` is fixed by the caller, a circle\n * picker (active circles) is shown. Datetime-local inputs are converted to ISO\n * strings for the `DateTime` scalar.\n */\n\nimport { useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { TagInput } from './TagInput';\nimport { useComCreateEvent, useComCircles, useTr } from '../hooks';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComEvent, ComEventMode } from '../types';\n\nconst MODES: ComEventMode[] = ['ONLINE', 'IN_PERSON', 'HYBRID'];\n\nconst toIso = (local: string): string | null => {\n if (!local) return null;\n const d = new Date(local);\n return Number.isNaN(d.getTime()) ? null : d.toISOString();\n};\n\ninterface CreateEventDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Fixed circle — when omitted the dialog shows an active-circle picker. */\n circleId?: string;\n onCreated?: (event: ComEvent) => void;\n}\n\nexport function CreateEventDialog({\n open,\n onOpenChange,\n circleId,\n onCreated,\n}: CreateEventDialogProps) {\n const tr = useTr();\n const createEvent = useComCreateEvent();\n const circlesQuery = useComCircles({ status: 'ACTIVE' });\n const [pickedCircle, setPickedCircle] = useState('');\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [mode, setMode] = useState<ComEventMode>('ONLINE');\n const [location, setLocation] = useState('');\n const [joinUrl, setJoinUrl] = useState('');\n const [capacity, setCapacity] = useState('');\n const [startsAt, setStartsAt] = useState('');\n const [endsAt, setEndsAt] = useState('');\n const [tags, setTags] = useState<string[]>([]);\n const [error, setError] = useState<string | null>(null);\n\n const effectiveCircle = circleId ?? pickedCircle;\n\n const reset = () => {\n setPickedCircle('');\n setTitle('');\n setDescription('');\n setMode('ONLINE');\n setLocation('');\n setJoinUrl('');\n setCapacity('');\n setStartsAt('');\n setEndsAt('');\n setTags([]);\n setError(null);\n };\n\n const submit = async () => {\n if (!effectiveCircle) {\n setError(\n tr(\n 'comradecircle.dialogs.createEvent.circleRequired',\n 'Please choose a circle for this event.'\n )\n );\n return;\n }\n if (!title.trim()) {\n setError(tr('comradecircle.dialogs.createEvent.titleRequired', 'Event title is required.'));\n return;\n }\n const startIso = toIso(startsAt);\n if (!startIso) {\n setError(\n tr(\n 'comradecircle.dialogs.createEvent.startRequired',\n 'A valid start date & time is required.'\n )\n );\n return;\n }\n try {\n const created = await createEvent.mutateAsync({\n circleId: effectiveCircle,\n title: title.trim(),\n description: description.trim() || null,\n mode,\n location: location.trim() || null,\n joinUrl: joinUrl.trim() || null,\n capacity: capacity ? Number(capacity) : null,\n startsAt: startIso,\n endsAt: toIso(endsAt),\n tags: tags.length ? tags : null,\n });\n notify.success(\n tr('comradecircle.dialogs.createEvent.scheduled', 'Event scheduled'),\n created.title\n );\n onOpenChange(false);\n reset();\n onCreated?.(created);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('comradecircle.dialogs.createEvent.title', 'Schedule an event')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'comradecircle.dialogs.createEvent.description',\n 'Host a gathering for your circle — online, in person, or hybrid.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[60vh] flex-col gap-4 overflow-y-auto py-2\">\n {!circleId && (\n <div className=\"grid gap-1.5\">\n <Label>{tr('comradecircle.dialogs.createEvent.circleLabel', 'Circle')}</Label>\n <Select value={pickedCircle} onValueChange={setPickedCircle}>\n <SelectTrigger>\n <SelectValue\n placeholder={tr(\n 'comradecircle.dialogs.createEvent.circlePlaceholder',\n 'Choose a circle'\n )}\n />\n </SelectTrigger>\n <SelectContent>\n {(circlesQuery.data?.items ?? []).map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-title\">\n {tr('comradecircle.dialogs.createEvent.titleLabel', 'Title')}\n </Label>\n <Input\n id=\"event-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder={tr(\n 'comradecircle.dialogs.createEvent.titlePlaceholder',\n 'e.g. Monthly community call'\n )}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-desc\">\n {tr('comradecircle.dialogs.createEvent.descriptionLabel', 'Description')}\n </Label>\n <Textarea\n id=\"event-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label>{tr('comradecircle.dialogs.createEvent.modeLabel', 'Mode')}</Label>\n <Select value={mode} onValueChange={(v) => setMode(v as ComEventMode)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {MODES.map((m) => (\n <SelectItem key={m} value={m}>\n {formatEnumLabel(m)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-capacity\">\n {tr('comradecircle.dialogs.createEvent.capacityLabel', 'Capacity')}\n </Label>\n <Input\n id=\"event-capacity\"\n type=\"number\"\n min={0}\n value={capacity}\n onChange={(e) => setCapacity(e.target.value)}\n placeholder={tr(\n 'comradecircle.dialogs.createEvent.capacityPlaceholder',\n 'Optional'\n )}\n />\n </div>\n </div>\n {mode !== 'ONLINE' && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-location\">\n {tr('comradecircle.dialogs.createEvent.locationLabel', 'Location')}\n </Label>\n <Input\n id=\"event-location\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n placeholder={tr(\n 'comradecircle.dialogs.createEvent.locationPlaceholder',\n 'Venue / address'\n )}\n />\n </div>\n )}\n {mode !== 'IN_PERSON' && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-join\">\n {tr('comradecircle.dialogs.createEvent.joinUrlLabel', 'Join URL')}\n </Label>\n <Input\n id=\"event-join\"\n value={joinUrl}\n onChange={(e) => setJoinUrl(e.target.value)}\n placeholder={tr(\n 'comradecircle.dialogs.createEvent.joinUrlPlaceholder',\n 'https://…'\n )}\n />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-start\">\n {tr('comradecircle.dialogs.createEvent.startsLabel', 'Starts')}\n </Label>\n <Input\n id=\"event-start\"\n type=\"datetime-local\"\n value={startsAt}\n onChange={(e) => setStartsAt(e.target.value)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-end\">\n {tr('comradecircle.dialogs.createEvent.endsLabel', 'Ends')}\n </Label>\n <Input\n id=\"event-end\"\n type=\"datetime-local\"\n value={endsAt}\n onChange={(e) => setEndsAt(e.target.value)}\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-tags\">\n {tr('comradecircle.dialogs.createEvent.tagsLabel', 'Tags')}\n </Label>\n <TagInput\n id=\"event-tags\"\n value={tags}\n onChange={setTags}\n placeholder={tr('comradecircle.dialogs.createEvent.tagsPlaceholder', 'Add tags…')}\n />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button onClick={submit} disabled={createEvent.isPending}>\n {tr('comradecircle.dialogs.createEvent.schedule', 'Schedule event')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DraftsPanel — the caller's own unpublished posts in one channel.
|
|
3
|
+
* @module comradecircle/components
|
|
4
|
+
*
|
|
5
|
+
* The composer can store a post as `status: DRAFT` (`ComCreatePostInput.status`);
|
|
6
|
+
* this is where those drafts come back. It reads
|
|
7
|
+
* `comListPosts(channelId, status: DRAFT)` and writes
|
|
8
|
+
* `comUpdatePost(id, { body })` (edit) / `comUpdatePost(id, { status: PUBLISHED })`
|
|
9
|
+
* (publish) / `comRemovePost(id)` (discard) — every one an existing `com*`
|
|
10
|
+
* capability, and `comUpdatePost`'s server-side gate already accepts the post's
|
|
11
|
+
* own author (`requirePostModeration` returns early on `authorId === actorId`).
|
|
12
|
+
*
|
|
13
|
+
* Scoping note: `comListPosts` filters by channel + status only — it does NOT
|
|
14
|
+
* scope drafts to their author — so the returned page can contain other
|
|
15
|
+
* members' drafts. This panel therefore renders ONLY rows whose `authorId`
|
|
16
|
+
* matches the calling member, and renders nothing at all when the shell has not
|
|
17
|
+
* supplied a current user (no identity ⇒ no way to tell whose draft is whose).
|
|
18
|
+
* The same bound means the panel is a "your recent drafts" view, not a
|
|
19
|
+
* guaranteed-complete one: it looks at one page of the channel's drafts, so it
|
|
20
|
+
* never claims a count it hasn't verified beyond that window.
|
|
21
|
+
*/
|
|
22
|
+
interface DraftsPanelProps {
|
|
23
|
+
channelId: string;
|
|
24
|
+
/** Whether the drafts list is expanded (controlled by the channel feed). */
|
|
25
|
+
open: boolean;
|
|
26
|
+
onOpenChange: (open: boolean) => void;
|
|
27
|
+
/** Announcement channels label the publish action "Broadcast". */
|
|
28
|
+
variant?: 'default' | 'announcement';
|
|
29
|
+
}
|
|
30
|
+
export declare function DraftsPanel({ channelId, open, onOpenChange, variant, }: DraftsPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=DraftsPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DraftsPanel.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/DraftsPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAmBH,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,kEAAkE;IAClE,OAAO,CAAC,EAAE,SAAS,GAAG,cAAc,CAAC;CACtC;AAED,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,IAAI,EACJ,YAAY,EACZ,OAAmB,GACpB,EAAE,gBAAgB,kDAuElB"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { useComradeCircle as e } from "../context/ComradeCircleContext.js";
|
|
2
|
+
import "../context/index.js";
|
|
3
|
+
import { useComPosts as t, useComRemovePost as n, useComUpdatePost as r } from "../hooks/useComradeCircleApi.js";
|
|
4
|
+
import "../hooks/index.js";
|
|
5
|
+
import { cn as i } from "../utils/cn.js";
|
|
6
|
+
import { describeError as a, notify as o } from "../lib/toast.js";
|
|
7
|
+
import { formatNumber as s, formatRelativeTime as c } from "../utils/formatters.js";
|
|
8
|
+
import { useMemo as l, useState as u } from "react";
|
|
9
|
+
import { ChevronDown as d, FileText as f, Megaphone as p, Paperclip as m, Send as h, Trash2 as g } from "lucide-react";
|
|
10
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
11
|
+
import { Button as b, Textarea as x } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/comradecircle/components/DraftsPanel.tsx
|
|
13
|
+
var S = 50;
|
|
14
|
+
function C({ channelId: n, open: r, onOpenChange: a, variant: o = "default" }) {
|
|
15
|
+
let { currentUser: c } = e(), u = c?.id, { data: p, isLoading: m, isError: h, refetch: g } = t({
|
|
16
|
+
channelId: n,
|
|
17
|
+
status: "DRAFT",
|
|
18
|
+
pagination: { limit: S }
|
|
19
|
+
}), _ = l(() => u ? (p?.items ?? []).filter((e) => e.authorId === u) : [], [p, u]);
|
|
20
|
+
return u ? h ? /* @__PURE__ */ y("div", {
|
|
21
|
+
className: "flex flex-wrap items-center justify-between gap-2 rounded-xl border border-dashed border-border-seam px-4 py-2.5 text-sm text-text-secondary",
|
|
22
|
+
children: [/* @__PURE__ */ y("span", {
|
|
23
|
+
className: "inline-flex items-center gap-2",
|
|
24
|
+
children: [/* @__PURE__ */ v(f, { className: "h-4 w-4 text-text-tertiary" }), " We couldn't load your drafts."]
|
|
25
|
+
}), /* @__PURE__ */ v(b, {
|
|
26
|
+
size: "sm",
|
|
27
|
+
variant: "outline",
|
|
28
|
+
onClick: () => void g(),
|
|
29
|
+
children: "Retry"
|
|
30
|
+
})]
|
|
31
|
+
}) : m || _.length === 0 ? null : /* @__PURE__ */ y("div", {
|
|
32
|
+
className: "overflow-hidden rounded-xl border border-border-seam bg-bg-surface",
|
|
33
|
+
children: [/* @__PURE__ */ y("button", {
|
|
34
|
+
type: "button",
|
|
35
|
+
onClick: () => a(!r),
|
|
36
|
+
"aria-expanded": r,
|
|
37
|
+
className: "flex w-full items-center justify-between gap-3 px-4 py-2.5 text-left transition-colors hover:bg-bg-sunken",
|
|
38
|
+
children: [/* @__PURE__ */ y("span", {
|
|
39
|
+
className: "inline-flex items-center gap-2 text-sm font-medium text-text-primary",
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ v(f, { className: "h-4 w-4 text-text-tertiary" }),
|
|
42
|
+
"Your drafts",
|
|
43
|
+
/* @__PURE__ */ v("span", {
|
|
44
|
+
className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs tabular-nums text-text-secondary",
|
|
45
|
+
children: s(_.length)
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
}), /* @__PURE__ */ v(d, {
|
|
49
|
+
className: i("h-4 w-4 shrink-0 text-text-tertiary transition-transform", r && "rotate-180"),
|
|
50
|
+
"aria-hidden": "true"
|
|
51
|
+
})]
|
|
52
|
+
}), r && /* @__PURE__ */ v("ul", {
|
|
53
|
+
className: "border-t border-border-seam",
|
|
54
|
+
children: _.map((e) => /* @__PURE__ */ v("li", {
|
|
55
|
+
className: "border-b border-border-seam last:border-b-0",
|
|
56
|
+
children: /* @__PURE__ */ v(w, {
|
|
57
|
+
draft: e,
|
|
58
|
+
variant: o
|
|
59
|
+
})
|
|
60
|
+
}, e.id))
|
|
61
|
+
})]
|
|
62
|
+
}) : null;
|
|
63
|
+
}
|
|
64
|
+
function w({ draft: e, variant: t }) {
|
|
65
|
+
let i = r(), l = n(), [d, f] = u(!1), [S, C] = u(e.body), w = t === "announcement", T = i.isPending || l.isPending, E = async () => {
|
|
66
|
+
let t = S.trim();
|
|
67
|
+
if (!t || t === e.body) {
|
|
68
|
+
C(e.body), f(!1);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await i.mutateAsync({
|
|
73
|
+
id: e.id,
|
|
74
|
+
input: { body: t }
|
|
75
|
+
}), f(!1), o.success("Draft updated");
|
|
76
|
+
} catch (e) {
|
|
77
|
+
o.error("Could not update draft", a(e));
|
|
78
|
+
}
|
|
79
|
+
}, D = async () => {
|
|
80
|
+
try {
|
|
81
|
+
await i.mutateAsync({
|
|
82
|
+
id: e.id,
|
|
83
|
+
input: { status: "PUBLISHED" }
|
|
84
|
+
}), o.success(w ? "Announcement published" : "Draft published");
|
|
85
|
+
} catch (e) {
|
|
86
|
+
o.error("Could not publish draft", a(e));
|
|
87
|
+
}
|
|
88
|
+
}, O = async () => {
|
|
89
|
+
try {
|
|
90
|
+
await l.mutateAsync({
|
|
91
|
+
id: e.id,
|
|
92
|
+
channelId: e.channelId
|
|
93
|
+
}), o.success("Draft discarded");
|
|
94
|
+
} catch (e) {
|
|
95
|
+
o.error("Could not discard draft", a(e));
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
return /* @__PURE__ */ v("div", {
|
|
99
|
+
className: "px-4 py-3",
|
|
100
|
+
children: d ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(x, {
|
|
101
|
+
value: S,
|
|
102
|
+
onChange: (e) => C(e.target.value),
|
|
103
|
+
rows: 3,
|
|
104
|
+
className: "resize-none",
|
|
105
|
+
"aria-label": "Draft body"
|
|
106
|
+
}), /* @__PURE__ */ y("div", {
|
|
107
|
+
className: "mt-2 flex justify-end gap-2",
|
|
108
|
+
children: [/* @__PURE__ */ v(b, {
|
|
109
|
+
size: "sm",
|
|
110
|
+
variant: "outline",
|
|
111
|
+
onClick: () => {
|
|
112
|
+
C(e.body), f(!1);
|
|
113
|
+
},
|
|
114
|
+
children: "Cancel"
|
|
115
|
+
}), /* @__PURE__ */ v(b, {
|
|
116
|
+
size: "sm",
|
|
117
|
+
onClick: () => void E(),
|
|
118
|
+
disabled: i.isPending,
|
|
119
|
+
children: "Save"
|
|
120
|
+
})]
|
|
121
|
+
})] }) : /* @__PURE__ */ y(_, { children: [
|
|
122
|
+
/* @__PURE__ */ v("p", {
|
|
123
|
+
className: "line-clamp-3 whitespace-pre-wrap break-words text-sm text-text-primary",
|
|
124
|
+
children: e.body || "Empty draft"
|
|
125
|
+
}),
|
|
126
|
+
/* @__PURE__ */ y("div", {
|
|
127
|
+
className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-tertiary",
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ y("span", { children: ["Saved ", c(e.updatedAt)] }),
|
|
130
|
+
e.attachmentFileIds.length > 0 && /* @__PURE__ */ y("span", {
|
|
131
|
+
className: "inline-flex items-center gap-1",
|
|
132
|
+
children: [
|
|
133
|
+
/* @__PURE__ */ v(m, { className: "h-3 w-3" }),
|
|
134
|
+
s(e.attachmentFileIds.length),
|
|
135
|
+
e.attachmentFileIds.length === 1 ? " attachment" : " attachments"
|
|
136
|
+
]
|
|
137
|
+
}),
|
|
138
|
+
e.tags.map((e) => /* @__PURE__ */ y("span", {
|
|
139
|
+
className: "text-text-secondary",
|
|
140
|
+
children: ["#", e]
|
|
141
|
+
}, e))
|
|
142
|
+
]
|
|
143
|
+
}),
|
|
144
|
+
/* @__PURE__ */ y("div", {
|
|
145
|
+
className: "mt-2.5 flex flex-wrap items-center justify-end gap-2",
|
|
146
|
+
children: [
|
|
147
|
+
/* @__PURE__ */ y(b, {
|
|
148
|
+
size: "sm",
|
|
149
|
+
variant: "ghost",
|
|
150
|
+
onClick: () => void O(),
|
|
151
|
+
disabled: T,
|
|
152
|
+
className: "text-status-error-text hover:text-status-error-text",
|
|
153
|
+
children: [/* @__PURE__ */ v(g, { className: "mr-1.5 h-3.5 w-3.5" }), " Discard"]
|
|
154
|
+
}),
|
|
155
|
+
/* @__PURE__ */ v(b, {
|
|
156
|
+
size: "sm",
|
|
157
|
+
variant: "outline",
|
|
158
|
+
onClick: () => f(!0),
|
|
159
|
+
disabled: T,
|
|
160
|
+
children: "Edit"
|
|
161
|
+
}),
|
|
162
|
+
/* @__PURE__ */ v(b, {
|
|
163
|
+
size: "sm",
|
|
164
|
+
onClick: () => void D(),
|
|
165
|
+
disabled: T,
|
|
166
|
+
children: w ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(p, { className: "mr-1.5 h-3.5 w-3.5" }), " Broadcast"] }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(h, { className: "mr-1.5 h-3.5 w-3.5" }), " Publish"] })
|
|
167
|
+
})
|
|
168
|
+
]
|
|
169
|
+
})
|
|
170
|
+
] })
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { C as DraftsPanel };
|
|
175
|
+
|
|
176
|
+
//# sourceMappingURL=DraftsPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DraftsPanel.js","names":[],"sources":["../../../src/comradecircle/components/DraftsPanel.tsx"],"sourcesContent":["/**\n * DraftsPanel — the caller's own unpublished posts in one channel.\n * @module comradecircle/components\n *\n * The composer can store a post as `status: DRAFT` (`ComCreatePostInput.status`);\n * this is where those drafts come back. It reads\n * `comListPosts(channelId, status: DRAFT)` and writes\n * `comUpdatePost(id, { body })` (edit) / `comUpdatePost(id, { status: PUBLISHED })`\n * (publish) / `comRemovePost(id)` (discard) — every one an existing `com*`\n * capability, and `comUpdatePost`'s server-side gate already accepts the post's\n * own author (`requirePostModeration` returns early on `authorId === actorId`).\n *\n * Scoping note: `comListPosts` filters by channel + status only — it does NOT\n * scope drafts to their author — so the returned page can contain other\n * members' drafts. This panel therefore renders ONLY rows whose `authorId`\n * matches the calling member, and renders nothing at all when the shell has not\n * supplied a current user (no identity ⇒ no way to tell whose draft is whose).\n * The same bound means the panel is a \"your recent drafts\" view, not a\n * guaranteed-complete one: it looks at one page of the channel's drafts, so it\n * never claims a count it hasn't verified beyond that window.\n */\n\nimport { useMemo, useState } from 'react';\nimport { ChevronDown, FileText, Megaphone, Paperclip, Send, Trash2 } from 'lucide-react';\nimport { Button, Textarea } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport { useComPosts, useComRemovePost, useComUpdatePost } from '../hooks';\nimport { useComradeCircle } from '../context';\nimport { formatNumber, formatRelativeTime } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComPost } from '../types';\n\n/**\n * One page of the channel's drafts. Deliberately larger than a visible list\n * needs to be: the page is shared with every other member's drafts and only the\n * caller's survive the filter below.\n */\nconst DRAFT_WINDOW = 50;\n\ninterface DraftsPanelProps {\n channelId: string;\n /** Whether the drafts list is expanded (controlled by the channel feed). */\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Announcement channels label the publish action \"Broadcast\". */\n variant?: 'default' | 'announcement';\n}\n\nexport function DraftsPanel({\n channelId,\n open,\n onOpenChange,\n variant = 'default',\n}: DraftsPanelProps) {\n const { currentUser } = useComradeCircle();\n const meId = currentUser?.id;\n\n const { data, isLoading, isError, refetch } = useComPosts({\n channelId,\n status: 'DRAFT',\n pagination: { limit: DRAFT_WINDOW },\n });\n\n const drafts = useMemo(\n () => (meId ? (data?.items ?? []).filter((post) => post.authorId === meId) : []),\n [data, meId]\n );\n\n // No identity → the author filter above cannot run, so show nothing rather\n // than risk surfacing someone else's unpublished post.\n if (!meId) return null;\n\n if (isError) {\n return (\n <div className=\"flex flex-wrap items-center justify-between gap-2 rounded-xl border border-dashed border-border-seam px-4 py-2.5 text-sm text-text-secondary\">\n <span className=\"inline-flex items-center gap-2\">\n <FileText className=\"h-4 w-4 text-text-tertiary\" /> We couldn't load your drafts.\n </span>\n <Button size=\"sm\" variant=\"outline\" onClick={() => void refetch()}>\n Retry\n </Button>\n </div>\n );\n }\n\n // While the first page is in flight there is nothing truthful to announce —\n // the count is unknown — so the panel stays out of the layout entirely.\n if (isLoading || drafts.length === 0) return null;\n\n return (\n <div className=\"overflow-hidden rounded-xl border border-border-seam bg-bg-surface\">\n <button\n type=\"button\"\n onClick={() => onOpenChange(!open)}\n aria-expanded={open}\n className=\"flex w-full items-center justify-between gap-3 px-4 py-2.5 text-left transition-colors hover:bg-bg-sunken\"\n >\n <span className=\"inline-flex items-center gap-2 text-sm font-medium text-text-primary\">\n <FileText className=\"h-4 w-4 text-text-tertiary\" />\n Your drafts\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs tabular-nums text-text-secondary\">\n {formatNumber(drafts.length)}\n </span>\n </span>\n <ChevronDown\n className={cn(\n 'h-4 w-4 shrink-0 text-text-tertiary transition-transform',\n open && 'rotate-180'\n )}\n aria-hidden=\"true\"\n />\n </button>\n\n {open && (\n <ul className=\"border-t border-border-seam\">\n {drafts.map((draft) => (\n <li key={draft.id} className=\"border-b border-border-seam last:border-b-0\">\n <DraftRow draft={draft} variant={variant} />\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n\nfunction DraftRow({ draft, variant }: { draft: ComPost; variant: 'default' | 'announcement' }) {\n const updatePost = useComUpdatePost();\n const removePost = useComRemovePost();\n const [editing, setEditing] = useState(false);\n const [body, setBody] = useState(draft.body);\n\n const isAnnouncement = variant === 'announcement';\n const busy = updatePost.isPending || removePost.isPending;\n\n const saveEdit = async () => {\n const text = body.trim();\n if (!text || text === draft.body) {\n setBody(draft.body);\n setEditing(false);\n return;\n }\n try {\n await updatePost.mutateAsync({ id: draft.id, input: { body: text } });\n setEditing(false);\n notify.success('Draft updated');\n } catch (err) {\n notify.error('Could not update draft', describeError(err));\n }\n };\n\n const publish = async () => {\n try {\n await updatePost.mutateAsync({ id: draft.id, input: { status: 'PUBLISHED' } });\n notify.success(isAnnouncement ? 'Announcement published' : 'Draft published');\n } catch (err) {\n notify.error('Could not publish draft', describeError(err));\n }\n };\n\n const discard = async () => {\n try {\n await removePost.mutateAsync({ id: draft.id, channelId: draft.channelId });\n notify.success('Draft discarded');\n } catch (err) {\n notify.error('Could not discard draft', describeError(err));\n }\n };\n\n return (\n <div className=\"px-4 py-3\">\n {editing ? (\n <>\n <Textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n rows={3}\n className=\"resize-none\"\n aria-label=\"Draft body\"\n />\n <div className=\"mt-2 flex justify-end gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n setBody(draft.body);\n setEditing(false);\n }}\n >\n Cancel\n </Button>\n <Button size=\"sm\" onClick={() => void saveEdit()} disabled={updatePost.isPending}>\n Save\n </Button>\n </div>\n </>\n ) : (\n <>\n <p className=\"line-clamp-3 whitespace-pre-wrap break-words text-sm text-text-primary\">\n {draft.body || 'Empty draft'}\n </p>\n\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-tertiary\">\n <span>Saved {formatRelativeTime(draft.updatedAt)}</span>\n {draft.attachmentFileIds.length > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Paperclip className=\"h-3 w-3\" />\n {formatNumber(draft.attachmentFileIds.length)}\n {draft.attachmentFileIds.length === 1 ? ' attachment' : ' attachments'}\n </span>\n )}\n {draft.tags.map((tag) => (\n <span key={tag} className=\"text-text-secondary\">\n #{tag}\n </span>\n ))}\n </div>\n\n <div className=\"mt-2.5 flex flex-wrap items-center justify-end gap-2\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => void discard()}\n disabled={busy}\n className=\"text-status-error-text hover:text-status-error-text\"\n >\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" /> Discard\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => setEditing(true)} disabled={busy}>\n Edit\n </Button>\n <Button size=\"sm\" onClick={() => void publish()} disabled={busy}>\n {isAnnouncement ? (\n <>\n <Megaphone className=\"mr-1.5 h-3.5 w-3.5\" /> Broadcast\n </>\n ) : (\n <>\n <Send className=\"mr-1.5 h-3.5 w-3.5\" /> Publish\n </>\n )}\n </Button>\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useComRsvpEvent as t } from "../hooks/useComradeCircleApi.js";
|
|
1
|
+
import { useComRsvpEvent as e } from "../hooks/useComradeCircleApi.js";
|
|
3
2
|
import "../hooks/index.js";
|
|
3
|
+
import { cn as t } from "../utils/cn.js";
|
|
4
4
|
import { describeError as n, notify as r } from "../lib/toast.js";
|
|
5
5
|
import { formatDateTime as i, formatNumber as a, formatRelativeTime as o } from "../utils/formatters.js";
|
|
6
6
|
import { StatusBadge as s } from "./StatusBadge.js";
|
|
@@ -18,7 +18,7 @@ var b = [{
|
|
|
18
18
|
label: "Interested"
|
|
19
19
|
}];
|
|
20
20
|
function x({ event: x, onOpen: S }) {
|
|
21
|
-
let C =
|
|
21
|
+
let C = e(), [w, T] = l(null), E = x.status === "SCHEDULED" || x.status === "LIVE", D = x.status === "LIVE", O = async (e) => {
|
|
22
22
|
try {
|
|
23
23
|
await C.mutateAsync({
|
|
24
24
|
eventId: x.id,
|
|
@@ -30,7 +30,7 @@ function x({ event: x, onOpen: S }) {
|
|
|
30
30
|
}, k = x.mode === "IN_PERSON" ? p : h, A = x.capacity ? Math.min(100, x.rsvpCount / x.capacity * 100) : 0, j = A >= 80;
|
|
31
31
|
return /* @__PURE__ */ _(y, {
|
|
32
32
|
treatment: "plain",
|
|
33
|
-
className:
|
|
33
|
+
className: t("flex flex-col p-5 transition-all duration-200 motion-safe:hover:-translate-y-0.5 hover:border-[var(--color-accent-soft)]", D && "ring-1 ring-[var(--color-accent-soft)]"),
|
|
34
34
|
children: [
|
|
35
35
|
/* @__PURE__ */ _("div", {
|
|
36
36
|
className: "flex items-start justify-between gap-3",
|
|
@@ -77,7 +77,7 @@ function x({ event: x, onOpen: S }) {
|
|
|
77
77
|
" attending"
|
|
78
78
|
]
|
|
79
79
|
}), x.capacity && j && /* @__PURE__ */ g("span", {
|
|
80
|
-
className:
|
|
80
|
+
className: t(A >= 100 ? "text-status-error-text" : "text-status-warning-text"),
|
|
81
81
|
children: A >= 100 ? "Full" : `${Math.round(A)}% full`
|
|
82
82
|
})]
|
|
83
83
|
}), x.capacity ? /* @__PURE__ */ g(c, {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useFileUrl as e } from "../hooks/useFiles.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
3
|
import { useState as n } from "react";
|
|
4
4
|
import { Fragment as r, jsx as i } from "react/jsx-runtime";
|
|
5
5
|
import { Skeleton as a } from "@burdenoff/fe-libs/ui";
|
|
6
6
|
//#region src/comradecircle/components/FileImage.tsx
|
|
7
7
|
function o({ fileId: o, alt: s, className: c, fallback: l = null }) {
|
|
8
|
-
let { data: u, isLoading: d, isError: f } =
|
|
9
|
-
return o ? d ? /* @__PURE__ */ i(a, { className:
|
|
8
|
+
let { data: u, isLoading: d, isError: f } = e(o), [p, m] = n(!1);
|
|
9
|
+
return o ? d ? /* @__PURE__ */ i(a, { className: t("h-full w-full", c) }) : f || p || !u ? /* @__PURE__ */ i(r, { children: l }) : /* @__PURE__ */ i("img", {
|
|
10
10
|
src: u,
|
|
11
11
|
alt: s,
|
|
12
12
|
loading: "lazy",
|
|
13
13
|
onError: () => m(!0),
|
|
14
|
-
className:
|
|
14
|
+
className: t("object-cover", c)
|
|
15
15
|
}) : /* @__PURE__ */ i(r, { children: l });
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|