@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.
Files changed (89) hide show
  1. package/dist/comradecircle/components/AvatarUploadField.js +8 -8
  2. package/dist/comradecircle/components/CircleCard.js +4 -4
  3. package/dist/comradecircle/components/CommentThread.d.ts.map +1 -1
  4. package/dist/comradecircle/components/CommentThread.js +66 -61
  5. package/dist/comradecircle/components/CommentThread.js.map +1 -1
  6. package/dist/comradecircle/components/CreateCircleDialog.d.ts.map +1 -1
  7. package/dist/comradecircle/components/CreateCircleDialog.js +89 -88
  8. package/dist/comradecircle/components/CreateCircleDialog.js.map +1 -1
  9. package/dist/comradecircle/components/CreateEventDialog.d.ts.map +1 -1
  10. package/dist/comradecircle/components/CreateEventDialog.js +28 -27
  11. package/dist/comradecircle/components/CreateEventDialog.js.map +1 -1
  12. package/dist/comradecircle/components/DraftsPanel.d.ts +32 -0
  13. package/dist/comradecircle/components/DraftsPanel.d.ts.map +1 -0
  14. package/dist/comradecircle/components/DraftsPanel.js +176 -0
  15. package/dist/comradecircle/components/DraftsPanel.js.map +1 -0
  16. package/dist/comradecircle/components/EventCard.js +5 -5
  17. package/dist/comradecircle/components/FileImage.js +5 -5
  18. package/dist/comradecircle/components/ImageAttachments.js +21 -21
  19. package/dist/comradecircle/components/MemberIdentity.js +4 -4
  20. package/dist/comradecircle/components/MobileBottomNav.d.ts.map +1 -1
  21. package/dist/comradecircle/components/MobileBottomNav.js +37 -30
  22. package/dist/comradecircle/components/MobileBottomNav.js.map +1 -1
  23. package/dist/comradecircle/components/Pagination.d.ts.map +1 -1
  24. package/dist/comradecircle/components/Pagination.js +37 -28
  25. package/dist/comradecircle/components/Pagination.js.map +1 -1
  26. package/dist/comradecircle/components/PollCard.js +13 -13
  27. package/dist/comradecircle/components/PostCard.d.ts.map +1 -1
  28. package/dist/comradecircle/components/PostCard.js +102 -89
  29. package/dist/comradecircle/components/PostCard.js.map +1 -1
  30. package/dist/comradecircle/components/PostComposer.d.ts +11 -5
  31. package/dist/comradecircle/components/PostComposer.d.ts.map +1 -1
  32. package/dist/comradecircle/components/PostComposer.js +60 -48
  33. package/dist/comradecircle/components/PostComposer.js.map +1 -1
  34. package/dist/comradecircle/components/ReactionBar.d.ts.map +1 -1
  35. package/dist/comradecircle/components/ReactionBar.js +65 -55
  36. package/dist/comradecircle/components/ReactionBar.js.map +1 -1
  37. package/dist/comradecircle/components/StateViews.d.ts.map +1 -1
  38. package/dist/comradecircle/components/StateViews.js +38 -30
  39. package/dist/comradecircle/components/StateViews.js.map +1 -1
  40. package/dist/comradecircle/components/UpcomingEvents.d.ts +26 -0
  41. package/dist/comradecircle/components/UpcomingEvents.d.ts.map +1 -0
  42. package/dist/comradecircle/components/UpcomingEvents.js +126 -0
  43. package/dist/comradecircle/components/UpcomingEvents.js.map +1 -0
  44. package/dist/comradecircle/components/index.d.ts +2 -0
  45. package/dist/comradecircle/components/index.d.ts.map +1 -1
  46. package/dist/comradecircle/hooks/index.d.ts +1 -0
  47. package/dist/comradecircle/hooks/index.d.ts.map +1 -1
  48. package/dist/comradecircle/hooks/index.js +1 -0
  49. package/dist/comradecircle/hooks/useTr.d.ts +18 -0
  50. package/dist/comradecircle/hooks/useTr.d.ts.map +1 -0
  51. package/dist/comradecircle/hooks/useTr.js +12 -0
  52. package/dist/comradecircle/hooks/useTr.js.map +1 -0
  53. package/dist/comradecircle/pages/FeedPage.d.ts +5 -0
  54. package/dist/comradecircle/pages/FeedPage.d.ts.map +1 -1
  55. package/dist/comradecircle/pages/FeedPage.js +76 -62
  56. package/dist/comradecircle/pages/FeedPage.js.map +1 -1
  57. package/dist/comradecircle/pages/circles/CircleDetailPage.d.ts.map +1 -1
  58. package/dist/comradecircle/pages/circles/CircleDetailPage.js +454 -409
  59. package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -1
  60. package/dist/comradecircle/pages/circles/CirclesPage.d.ts.map +1 -1
  61. package/dist/comradecircle/pages/circles/CirclesPage.js +124 -117
  62. package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -1
  63. package/dist/comradecircle/pages/events/EventDetailPage.d.ts.map +1 -1
  64. package/dist/comradecircle/pages/events/EventDetailPage.js +182 -153
  65. package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -1
  66. package/dist/comradecircle/pages/events/EventsPage.d.ts.map +1 -1
  67. package/dist/comradecircle/pages/events/EventsPage.js +100 -91
  68. package/dist/comradecircle/pages/events/EventsPage.js.map +1 -1
  69. package/dist/comradecircle/pages/members/MemberProfilePage.d.ts.map +1 -1
  70. package/dist/comradecircle/pages/members/MemberProfilePage.js +172 -159
  71. package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -1
  72. package/dist/comradecircle/pages/members/MembersPage.d.ts.map +1 -1
  73. package/dist/comradecircle/pages/members/MembersPage.js +103 -104
  74. package/dist/comradecircle/pages/members/MembersPage.js.map +1 -1
  75. package/dist/comradecircle/pages/monetization/MonetizationPage.d.ts.map +1 -1
  76. package/dist/comradecircle/pages/monetization/MonetizationPage.js +319 -297
  77. package/dist/comradecircle/pages/monetization/MonetizationPage.js.map +1 -1
  78. package/dist/comradecircle/pages/posts/PostThreadPage.d.ts.map +1 -1
  79. package/dist/comradecircle/pages/posts/PostThreadPage.js +37 -32
  80. package/dist/comradecircle/pages/posts/PostThreadPage.js.map +1 -1
  81. package/dist/comradecircle/pages/profile/MyProfilePage.d.ts.map +1 -1
  82. package/dist/comradecircle/pages/profile/MyProfilePage.js +182 -177
  83. package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -1
  84. package/dist/comradecircle/utils/i18n.js +15 -0
  85. package/dist/comradecircle/utils/i18n.js.map +1 -0
  86. package/dist/comradecircle/utils/pageContext.d.ts.map +1 -1
  87. package/dist/comradecircle/utils/pageContext.js +3 -1
  88. package/dist/comradecircle/utils/pageContext.js.map +1 -1
  89. 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,2CAyMxB"}
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 p, ResponsiveDialogDescription as ee, 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
+ 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: O }) {
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, te = () => {
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(p, {
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(ee, { children: "Host a gathering for your circle — online, in person, or hybrid." })] }),
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), te(), O?.(t);
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":";;;;;;;;;AAiCA,IAAM,IAAwB;CAAC;CAAU;CAAa;AAAQ,GAExD,KAAS,MAAiC;CAC9C,IAAI,CAAC,GAAO,OAAO;CACnB,IAAM,IAAI,IAAI,KAAK,CAAK;CACxB,OAAO,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO,EAAE,YAAY;AAC1D;AAUA,SAAgB,EAAkB,EAChC,SACA,iBACA,aACA,gBACyB;CACzB,IAAM,IAAc,EAAkB,GAChC,IAAe,EAAc,EAAE,QAAQ,SAAS,CAAC,GACjD,CAAC,GAAc,KAAmB,EAAS,EAAE,GAC7C,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAa,KAAkB,EAAS,EAAE,GAC3C,CAAC,GAAM,KAAW,EAAuB,QAAQ,GACjD,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,CAAC,GAAM,KAAW,EAAmB,CAAC,CAAC,GACvC,CAAC,GAAO,KAAY,EAAwB,IAAI,GAEhD,IAAkB,KAAY,GAE9B,WAAc;EAWlB,AAVA,EAAgB,EAAE,GAClB,EAAS,EAAE,GACX,EAAe,EAAE,GACjB,EAAQ,QAAQ,GAChB,EAAY,EAAE,GACd,EAAW,EAAE,GACb,EAAY,EAAE,GACd,EAAY,EAAE,GACd,EAAU,EAAE,GACZ,EAAQ,CAAC,CAAC,GACV,EAAS,IAAI;CACf;CAsCA,OACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,GAAD;GAAyB,WAAU;aAAnC;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,oBAAwC,CAAA,GAC/D,kBAAC,IAAD,EAAA,UAA6B,mEAEA,CAAA,CACP,EAAA,CAAA;IAExB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,CAAC,KACA,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,SAAa,CAAA,GACpB,kBAAC,GAAD;QAAQ,OAAO;QAAc,eAAe;kBAA5C,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAa,aAAY,kBAAmB,CAAA,EAC/B,CAAA,GACf,kBAAC,GAAD,EAAA,WACI,EAAa,MAAM,SAAS,CAAC,GAAG,KAAK,MACrC,kBAAC,GAAD;SAAuB,OAAO,EAAE;mBAC7B,EAAE;QACO,GAFK,EAAE,EAEP,CACb,EACY,CAAA,CACT;SACL;;MAEP,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAc;OAAY,CAAA,GACzC,kBAAC,GAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAS,EAAE,OAAO,KAAK;QACxC,aAAY;OACb,CAAA,CACE;;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAa;OAAkB,CAAA,GAC9C,kBAAC,GAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAe,EAAE,OAAO,KAAK;QAC9C,MAAM;OACP,CAAA,CACE;;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,OAAW,CAAA,GAClB,kBAAC,GAAD;SAAQ,OAAO;SAAM,gBAAgB,MAAM,EAAQ,CAAiB;mBAApE,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,CAAc,CAAA,EACD,CAAA,GACf,kBAAC,GAAD,EAAA,UACG,EAAM,KAAK,MACV,kBAAC,GAAD;UAAoB,OAAO;oBACxB,EAAgB,CAAC;SACR,GAFK,CAEL,CACb,EACY,CAAA,CACT;UACL;WACL,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAiB;QAAe,CAAA,GAC/C,kBAAC,GAAD;SACE,IAAG;SACH,MAAK;SACL,KAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAY,EAAE,OAAO,KAAK;SAC3C,aAAY;QACb,CAAA,CACE;SACF;;MACJ,MAAS,YACR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAiB;OAAe,CAAA,GAC/C,kBAAC,GAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAY,EAAE,OAAO,KAAK;QAC3C,aAAY;OACb,CAAA,CACE;;MAEN,MAAS,eACR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAa;OAAe,CAAA,GAC3C,kBAAC,GAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAW,EAAE,OAAO,KAAK;QAC1C,aAAY;OACb,CAAA,CACE;;MAEP,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAc;QAAa,CAAA,GAC1C,kBAAC,GAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAY,EAAE,OAAO,KAAK;QAC5C,CAAA,CACE;WACL,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAY;QAAW,CAAA,GACtC,kBAAC,GAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;QAC1C,CAAA,CACE;SACF;;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAa;OAAW,CAAA,GACvC,kBAAC,GAAD;QAAU,IAAG;QAAa,OAAO;QAAM,UAAU;QAAS,aAAY;OAAa,CAAA,CAChF;;MACJ,KAAS,kBAAC,KAAD;OAAG,WAAU;iBAAkC;MAAS,CAAA;KAC/D;;IAEL,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,eAAe,EAAa,EAAK;eAAG;IAEtD,CAAA,GACR,kBAAC,GAAD;KAAQ,SAAS,YAnKE;MACzB,IAAI,CAAC,GAAiB;OACpB,EAAS,wCAAwC;OACjD;MACF;MACA,IAAI,CAAC,EAAM,KAAK,GAAG;OACjB,EAAS,0BAA0B;OACnC;MACF;MACA,IAAM,IAAW,EAAM,CAAQ;MAC/B,IAAI,CAAC,GAAU;OACb,EAAS,wCAAwC;OACjD;MACF;MACA,IAAI;OACF,IAAM,IAAU,MAAM,EAAY,YAAY;QAC5C,UAAU;QACV,OAAO,EAAM,KAAK;QAClB,aAAa,EAAY,KAAK,KAAK;QACnC;QACA,UAAU,EAAS,KAAK,KAAK;QAC7B,SAAS,EAAQ,KAAK,KAAK;QAC3B,UAAU,IAAW,OAAO,CAAQ,IAAI;QACxC,UAAU;QACV,QAAQ,EAAM,CAAM;QACpB,MAAM,EAAK,SAAS,IAAO;OAC7B,CAAC;OAID,AAHA,EAAO,QAAQ,mBAAmB,EAAQ,KAAK,GAC/C,EAAa,EAAK,GAClB,GAAM,GACN,IAAY,CAAO;MACrB,SAAS,GAAK;OACZ,EAAS,EAAc,CAAG,CAAC;MAC7B;KACF;KAiIiC,UAAU,EAAY;eAAW;IAElD,CAAA,CACc,EAAA,CAAA;GACD;;CACT,CAAA;AAEtB"}
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&apos;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 { cn as e } from "../utils/cn.js";
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 = t(), [w, T] = l(null), E = x.status === "SCHEDULED" || x.status === "LIVE", D = x.status === "LIVE", O = async (e) => {
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: e("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)]"),
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: e(A >= 100 ? "text-status-error-text" : "text-status-warning-text"),
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 { cn as e } from "../utils/cn.js";
2
- import { useFileUrl as t } from "../hooks/useFiles.js";
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 } = t(o), [p, m] = n(!1);
9
- return o ? d ? /* @__PURE__ */ i(a, { className: e("h-full w-full", c) }) : f || p || !u ? /* @__PURE__ */ i(r, { children: l }) : /* @__PURE__ */ i("img", {
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: e("object-cover", c)
14
+ className: t("object-cover", c)
15
15
  }) : /* @__PURE__ */ i(r, { children: l });
16
16
  }
17
17
  //#endregion