@burdenoff/microfe-comradecircle 2026.704.2 → 2026.713.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 (163) hide show
  1. package/dist/comradecircle/ComradeCircleRoot.d.ts +5 -0
  2. package/dist/comradecircle/ComradeCircleRoot.d.ts.map +1 -0
  3. package/dist/comradecircle/ComradeCircleRoutes.d.ts +4 -0
  4. package/dist/comradecircle/ComradeCircleRoutes.d.ts.map +1 -0
  5. package/dist/comradecircle/components/Avatar.d.ts +20 -0
  6. package/dist/comradecircle/components/Avatar.d.ts.map +1 -0
  7. package/dist/comradecircle/components/Avatar.js +40 -17
  8. package/dist/comradecircle/components/Avatar.js.map +1 -1
  9. package/dist/comradecircle/components/AvatarUploadField.d.ts +21 -0
  10. package/dist/comradecircle/components/AvatarUploadField.d.ts.map +1 -0
  11. package/dist/comradecircle/components/AvatarUploadField.js +25 -14
  12. package/dist/comradecircle/components/AvatarUploadField.js.map +1 -1
  13. package/dist/comradecircle/components/CircleCard.d.ts +10 -0
  14. package/dist/comradecircle/components/CircleCard.d.ts.map +1 -0
  15. package/dist/comradecircle/components/CircleCard.js +54 -52
  16. package/dist/comradecircle/components/CircleCard.js.map +1 -1
  17. package/dist/comradecircle/components/CommentThread.d.ts +14 -0
  18. package/dist/comradecircle/components/CommentThread.d.ts.map +1 -0
  19. package/dist/comradecircle/components/CommentThread.js +80 -64
  20. package/dist/comradecircle/components/CommentThread.js.map +1 -1
  21. package/dist/comradecircle/components/CreateCircleDialog.d.ts +10 -0
  22. package/dist/comradecircle/components/CreateCircleDialog.d.ts.map +1 -0
  23. package/dist/comradecircle/components/CreateEventDialog.d.ts +12 -0
  24. package/dist/comradecircle/components/CreateEventDialog.d.ts.map +1 -0
  25. package/dist/comradecircle/components/DataTable.d.ts +28 -0
  26. package/dist/comradecircle/components/DataTable.d.ts.map +1 -0
  27. package/dist/comradecircle/components/EmptyState.d.ts +18 -0
  28. package/dist/comradecircle/components/EmptyState.d.ts.map +1 -0
  29. package/dist/comradecircle/components/EventCard.d.ts +9 -0
  30. package/dist/comradecircle/components/EventCard.d.ts.map +1 -0
  31. package/dist/comradecircle/components/EventCard.js +84 -58
  32. package/dist/comradecircle/components/EventCard.js.map +1 -1
  33. package/dist/comradecircle/components/FileImage.d.ts +18 -0
  34. package/dist/comradecircle/components/FileImage.d.ts.map +1 -0
  35. package/dist/comradecircle/components/FilterTabs.d.ts +18 -0
  36. package/dist/comradecircle/components/FilterTabs.d.ts.map +1 -0
  37. package/dist/comradecircle/components/ImageAttachments.d.ts +20 -0
  38. package/dist/comradecircle/components/ImageAttachments.d.ts.map +1 -0
  39. package/dist/comradecircle/components/MemberIdentity.d.ts +16 -0
  40. package/dist/comradecircle/components/MemberIdentity.d.ts.map +1 -0
  41. package/dist/comradecircle/components/MemberIdentity.js +2 -1
  42. package/dist/comradecircle/components/MemberIdentity.js.map +1 -1
  43. package/dist/comradecircle/components/MetricsCard.d.ts +23 -0
  44. package/dist/comradecircle/components/MetricsCard.d.ts.map +1 -0
  45. package/dist/comradecircle/components/MiniBar.d.ts +16 -0
  46. package/dist/comradecircle/components/MiniBar.d.ts.map +1 -0
  47. package/dist/comradecircle/components/PageLayout.d.ts +16 -0
  48. package/dist/comradecircle/components/PageLayout.d.ts.map +1 -0
  49. package/dist/comradecircle/components/Pagination.d.ts +22 -0
  50. package/dist/comradecircle/components/Pagination.d.ts.map +1 -0
  51. package/dist/comradecircle/components/PollCard.d.ts +10 -0
  52. package/dist/comradecircle/components/PollCard.d.ts.map +1 -0
  53. package/dist/comradecircle/components/PollCard.js +65 -44
  54. package/dist/comradecircle/components/PollCard.js.map +1 -1
  55. package/dist/comradecircle/components/PostCard.d.ts +10 -0
  56. package/dist/comradecircle/components/PostCard.d.ts.map +1 -0
  57. package/dist/comradecircle/components/PostCard.js +1 -1
  58. package/dist/comradecircle/components/PostCard.js.map +1 -1
  59. package/dist/comradecircle/components/PostComposer.d.ts +16 -0
  60. package/dist/comradecircle/components/PostComposer.d.ts.map +1 -0
  61. package/dist/comradecircle/components/ReactionBar.d.ts +8 -0
  62. package/dist/comradecircle/components/ReactionBar.d.ts.map +1 -0
  63. package/dist/comradecircle/components/ReactionBar.js +1 -1
  64. package/dist/comradecircle/components/ReactionBar.js.map +1 -1
  65. package/dist/comradecircle/components/StateViews.d.ts +24 -0
  66. package/dist/comradecircle/components/StateViews.d.ts.map +1 -0
  67. package/dist/comradecircle/components/StateViews.js +1 -16
  68. package/dist/comradecircle/components/StateViews.js.map +1 -1
  69. package/dist/comradecircle/components/StatusBadge.d.ts +9 -0
  70. package/dist/comradecircle/components/StatusBadge.d.ts.map +1 -0
  71. package/dist/comradecircle/components/StatusBadge.js +1 -1
  72. package/dist/comradecircle/components/StatusBadge.js.map +1 -1
  73. package/dist/comradecircle/components/Tabs.d.ts +17 -0
  74. package/dist/comradecircle/components/Tabs.d.ts.map +1 -0
  75. package/dist/comradecircle/components/TagInput.d.ts +18 -0
  76. package/dist/comradecircle/components/TagInput.d.ts.map +1 -0
  77. package/dist/comradecircle/components/index.d.ts +30 -0
  78. package/dist/comradecircle/components/index.d.ts.map +1 -0
  79. package/dist/comradecircle/context/ComradeCircleContext.d.ts +16 -0
  80. package/dist/comradecircle/context/ComradeCircleContext.d.ts.map +1 -0
  81. package/dist/comradecircle/context/index.d.ts +6 -0
  82. package/dist/comradecircle/context/index.d.ts.map +1 -0
  83. package/dist/comradecircle/hooks/index.d.ts +8 -0
  84. package/dist/comradecircle/hooks/index.d.ts.map +1 -0
  85. package/dist/comradecircle/hooks/useComradeCircleApi.d.ts +103 -0
  86. package/dist/comradecircle/hooks/useComradeCircleApi.d.ts.map +1 -0
  87. package/dist/comradecircle/hooks/useComradeCircleApi.js +1 -1
  88. package/dist/comradecircle/hooks/useComradeCircleApi.js.map +1 -1
  89. package/dist/comradecircle/hooks/useCursorPager.d.ts +33 -0
  90. package/dist/comradecircle/hooks/useCursorPager.d.ts.map +1 -0
  91. package/dist/comradecircle/hooks/useFiles.d.ts +28 -0
  92. package/dist/comradecircle/hooks/useFiles.d.ts.map +1 -0
  93. package/dist/comradecircle/index.d.ts +10 -0
  94. package/dist/comradecircle/index.d.ts.map +1 -0
  95. package/dist/comradecircle/index.js +1 -0
  96. package/dist/comradecircle/lib/graphql-cache.d.ts +26 -0
  97. package/dist/comradecircle/lib/graphql-cache.d.ts.map +1 -0
  98. package/dist/comradecircle/lib/toast.d.ts +23 -0
  99. package/dist/comradecircle/lib/toast.d.ts.map +1 -0
  100. package/dist/comradecircle/pages/FeedPage.d.ts +10 -0
  101. package/dist/comradecircle/pages/FeedPage.d.ts.map +1 -0
  102. package/dist/comradecircle/pages/FeedPage.js +79 -33
  103. package/dist/comradecircle/pages/FeedPage.js.map +1 -1
  104. package/dist/comradecircle/pages/circles/CircleDetailPage.d.ts +11 -0
  105. package/dist/comradecircle/pages/circles/CircleDetailPage.d.ts.map +1 -0
  106. package/dist/comradecircle/pages/circles/CircleDetailPage.js +354 -243
  107. package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -1
  108. package/dist/comradecircle/pages/circles/CirclesPage.d.ts +11 -0
  109. package/dist/comradecircle/pages/circles/CirclesPage.d.ts.map +1 -0
  110. package/dist/comradecircle/pages/circles/CirclesPage.js +97 -47
  111. package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -1
  112. package/dist/comradecircle/pages/events/EventDetailPage.d.ts +11 -0
  113. package/dist/comradecircle/pages/events/EventDetailPage.d.ts.map +1 -0
  114. package/dist/comradecircle/pages/events/EventDetailPage.js +193 -126
  115. package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -1
  116. package/dist/comradecircle/pages/events/EventsPage.d.ts +11 -0
  117. package/dist/comradecircle/pages/events/EventsPage.d.ts.map +1 -0
  118. package/dist/comradecircle/pages/events/EventsPage.js +74 -34
  119. package/dist/comradecircle/pages/events/EventsPage.js.map +1 -1
  120. package/dist/comradecircle/pages/members/MemberProfilePage.d.ts +13 -0
  121. package/dist/comradecircle/pages/members/MemberProfilePage.d.ts.map +1 -0
  122. package/dist/comradecircle/pages/members/MemberProfilePage.js +154 -82
  123. package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -1
  124. package/dist/comradecircle/pages/members/MembersPage.d.ts +13 -0
  125. package/dist/comradecircle/pages/members/MembersPage.d.ts.map +1 -0
  126. package/dist/comradecircle/pages/members/MembersPage.js +140 -71
  127. package/dist/comradecircle/pages/members/MembersPage.js.map +1 -1
  128. package/dist/comradecircle/pages/profile/MyProfilePage.d.ts +17 -0
  129. package/dist/comradecircle/pages/profile/MyProfilePage.d.ts.map +1 -0
  130. package/dist/comradecircle/pages/profile/MyProfilePage.js +298 -202
  131. package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -1
  132. package/dist/comradecircle/types/index.d.ts +362 -0
  133. package/dist/comradecircle/types/index.d.ts.map +1 -0
  134. package/dist/comradecircle/utils/cn.d.ts +4 -0
  135. package/dist/comradecircle/utils/cn.d.ts.map +1 -0
  136. package/dist/comradecircle/utils/formatters.d.ts +26 -0
  137. package/dist/comradecircle/utils/formatters.d.ts.map +1 -0
  138. package/dist/comradecircle/utils/i18n.d.ts +6 -0
  139. package/dist/comradecircle/utils/i18n.d.ts.map +1 -0
  140. package/dist/comradecircle/utils/index.d.ts +10 -0
  141. package/dist/comradecircle/utils/index.d.ts.map +1 -0
  142. package/dist/comradecircle/utils/navigation.d.ts +14 -0
  143. package/dist/comradecircle/utils/navigation.d.ts.map +1 -0
  144. package/dist/comradecircle/utils/slug.d.ts +6 -0
  145. package/dist/comradecircle/utils/slug.d.ts.map +1 -0
  146. package/dist/dev/main.d.ts +2 -0
  147. package/dist/dev/main.d.ts.map +1 -0
  148. package/dist/generated/global-operations.d.ts +2 -0
  149. package/dist/generated/global-operations.d.ts.map +1 -0
  150. package/dist/generated/global-types.d.ts +18199 -0
  151. package/dist/generated/global-types.d.ts.map +1 -0
  152. package/dist/generated/wspace-operations.d.ts +2 -0
  153. package/dist/generated/wspace-operations.d.ts.map +1 -0
  154. package/dist/generated/wspace-types.d.ts +43046 -0
  155. package/dist/generated/wspace-types.d.ts.map +1 -0
  156. package/dist/index.d.ts +2 -0
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +2 -1
  159. package/dist/services/createSandboxAssistantTransport.d.ts +24 -0
  160. package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -0
  161. package/dist/services/createSandboxAssistantTransport.js +12 -0
  162. package/dist/services/createSandboxAssistantTransport.js.map +1 -0
  163. package/package.json +6 -2
@@ -1,17 +1,20 @@
1
- import { cn as e } from "../utils/cn.js";
2
- import { useComAddComment as t, useComComments as n } from "../hooks/useComradeCircleApi.js";
1
+ import { useComradeCircle as e } from "../context/ComradeCircleContext.js";
2
+ import { cn as t } from "../utils/cn.js";
3
+ import "../context/index.js";
4
+ import { useComAddComment as n, useComComments as r } from "../hooks/useComradeCircleApi.js";
3
5
  import "../hooks/index.js";
4
- import { MemberIdentity as r } from "./MemberIdentity.js";
5
- import { describeError as i, notify as a } from "../lib/toast.js";
6
- import { formatRelativeTime as o } from "../utils/formatters.js";
7
- import { useMemo as s, useState as c } from "react";
8
- import { CornerDownRight as l, Send as u } from "lucide-react";
9
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
10
- import { Button as p, Spinner as m, Textarea as h } from "@burdenoff/fe-libs/ui";
6
+ import { Avatar as i } from "./Avatar.js";
7
+ import { MemberIdentity as a } from "./MemberIdentity.js";
8
+ import { describeError as o, notify as s } from "../lib/toast.js";
9
+ import { formatRelativeTime as c } from "../utils/formatters.js";
10
+ import { useMemo as l, useState as u } from "react";
11
+ import { CornerDownRight as d, MessageCircle as f, Send as p } from "lucide-react";
12
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
13
+ import { Button as g, Spinner as _, Textarea as v } from "@burdenoff/fe-libs/ui";
11
14
  //#region src/comradecircle/components/CommentThread.tsx
12
- function g({ postId: g }) {
13
- let { data: _, isLoading: v } = n(g), y = t(), [b, x] = c(""), [S, C] = c(null), { roots: w, repliesByParent: T } = s(() => {
14
- let e = _ ?? [], t = /* @__PURE__ */ new Map(), n = [];
15
+ function y({ postId: y }) {
16
+ let { currentUser: b } = e(), { data: x, isLoading: S } = r(y), C = n(), [w, T] = u(""), [E, D] = u(null), { roots: O, repliesByParent: k } = l(() => {
17
+ let e = x ?? [], t = /* @__PURE__ */ new Map(), n = [];
15
18
  for (let r of e) if (r.parentId) {
16
19
  let e = t.get(r.parentId) ?? [];
17
20
  e.push(r), t.set(r.parentId, e);
@@ -20,86 +23,99 @@ function g({ postId: g }) {
20
23
  roots: n,
21
24
  repliesByParent: t
22
25
  };
23
- }, [_]), E = async () => {
24
- let e = b.trim();
26
+ }, [x]), A = async () => {
27
+ let e = w.trim();
25
28
  if (e) try {
26
- await y.mutateAsync({
27
- postId: g,
29
+ await C.mutateAsync({
30
+ postId: y,
28
31
  body: e,
29
- parentId: S?.id ?? null
30
- }), x(""), C(null);
32
+ parentId: E?.id ?? null
33
+ }), T(""), D(null);
31
34
  } catch (e) {
32
- a.error("Could not add comment", i(e));
35
+ s.error("Could not add comment", o(e));
33
36
  }
34
- }, D = (t, n = !1) => /* @__PURE__ */ f("div", {
35
- className: e("flex flex-col gap-1", n && "ml-8"),
36
- children: [/* @__PURE__ */ f("div", {
37
+ }, j = (e, n = !1) => /* @__PURE__ */ h("div", {
38
+ className: t("flex flex-col gap-1", n && "ml-8"),
39
+ children: [/* @__PURE__ */ h("div", {
37
40
  className: "rounded-xl bg-bg-sunken/60 px-3 py-2",
38
- children: [/* @__PURE__ */ f("div", {
41
+ children: [/* @__PURE__ */ h("div", {
39
42
  className: "flex items-center justify-between gap-2",
40
- children: [/* @__PURE__ */ d(r, {
41
- memberId: t.authorId,
43
+ children: [/* @__PURE__ */ m(a, {
44
+ memberId: e.authorId,
42
45
  size: "xs",
43
- suffix: o(t.createdAt)
44
- }), !n && /* @__PURE__ */ f("button", {
46
+ suffix: c(e.createdAt)
47
+ }), !n && /* @__PURE__ */ h("button", {
45
48
  type: "button",
46
- onClick: () => C(t),
49
+ onClick: () => D(e),
47
50
  className: "inline-flex items-center gap-1 text-xs text-text-tertiary transition-colors hover:text-text-primary",
48
- children: [/* @__PURE__ */ d(l, { className: "h-3 w-3" }), " Reply"]
51
+ children: [/* @__PURE__ */ m(d, { className: "h-3 w-3" }), " Reply"]
49
52
  })]
50
- }), /* @__PURE__ */ d("p", {
53
+ }), /* @__PURE__ */ m("p", {
51
54
  className: "mt-1 whitespace-pre-wrap break-words text-sm text-text-primary",
52
- children: t.body
55
+ children: e.body
53
56
  })]
54
- }), (T.get(t.id) ?? []).map((e) => D(e, !0))]
55
- }, t.id);
56
- return /* @__PURE__ */ f("div", {
57
+ }), (k.get(e.id) ?? []).map((e) => j(e, !0))]
58
+ }, e.id);
59
+ return /* @__PURE__ */ h("div", {
57
60
  className: "mt-3 border-t border-border-seam pt-3",
58
- children: [v ? /* @__PURE__ */ d("div", {
61
+ children: [S ? /* @__PURE__ */ m("div", {
59
62
  className: "flex justify-center py-4",
60
- children: /* @__PURE__ */ d(m, { size: "sm" })
61
- }) : w.length === 0 ? /* @__PURE__ */ d("p", {
62
- className: "py-2 text-center text-sm text-text-tertiary",
63
- children: "No comments yet. Start the conversation."
64
- }) : /* @__PURE__ */ d("div", {
63
+ children: /* @__PURE__ */ m(_, { size: "sm" })
64
+ }) : O.length === 0 ? /* @__PURE__ */ h("div", {
65
+ className: "flex flex-col items-center gap-1.5 py-4 text-center",
66
+ children: [/* @__PURE__ */ m(f, { className: "h-6 w-6 text-text-tertiary opacity-60" }), /* @__PURE__ */ m("p", {
67
+ className: "text-sm text-text-tertiary",
68
+ children: "No comments yet — start the conversation."
69
+ })]
70
+ }) : /* @__PURE__ */ m("div", {
65
71
  className: "flex flex-col gap-3",
66
- children: w.map((e) => D(e))
67
- }), /* @__PURE__ */ f("div", {
72
+ children: O.map((e) => j(e))
73
+ }), /* @__PURE__ */ h("div", {
68
74
  className: "mt-3 flex flex-col gap-2",
69
- children: [S && /* @__PURE__ */ f("div", {
75
+ children: [E && /* @__PURE__ */ h("div", {
70
76
  className: "flex items-center justify-between rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary",
71
- children: [/* @__PURE__ */ d("span", {
77
+ children: [/* @__PURE__ */ m("span", {
72
78
  className: "truncate",
73
79
  children: "Replying to a comment"
74
- }), /* @__PURE__ */ d("button", {
80
+ }), /* @__PURE__ */ m("button", {
75
81
  type: "button",
76
- onClick: () => C(null),
82
+ onClick: () => D(null),
77
83
  className: "text-text-tertiary hover:text-text-primary",
78
84
  children: "Cancel"
79
85
  })]
80
- }), /* @__PURE__ */ f("div", {
86
+ }), /* @__PURE__ */ h("div", {
81
87
  className: "flex items-end gap-2",
82
- children: [/* @__PURE__ */ d(h, {
83
- value: b,
84
- onChange: (e) => x(e.target.value),
85
- placeholder: "Add a comment…",
86
- rows: 1,
87
- className: "min-h-[40px] resize-none",
88
- onKeyDown: (e) => {
89
- e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), E());
90
- }
91
- }), /* @__PURE__ */ d(p, {
92
- size: "sm",
93
- onClick: E,
94
- disabled: !b.trim() || y.isPending,
95
- "aria-label": "Post comment",
96
- children: /* @__PURE__ */ d(u, { className: "h-4 w-4" })
97
- })]
88
+ children: [
89
+ /* @__PURE__ */ m(i, {
90
+ name: b?.name ?? b?.email ?? "You",
91
+ seed: b?.id,
92
+ size: "sm",
93
+ className: "mb-1"
94
+ }),
95
+ /* @__PURE__ */ m(v, {
96
+ value: w,
97
+ onChange: (e) => T(e.target.value),
98
+ placeholder: "Add a comment…",
99
+ rows: 1,
100
+ className: "min-h-[40px] resize-none",
101
+ onKeyDown: (e) => {
102
+ e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), A());
103
+ }
104
+ }),
105
+ /* @__PURE__ */ m(g, {
106
+ size: "icon",
107
+ onClick: A,
108
+ disabled: !w.trim() || C.isPending,
109
+ "aria-label": "Post comment",
110
+ className: "shrink-0 rounded-full",
111
+ children: /* @__PURE__ */ m(p, { className: "h-4 w-4" })
112
+ })
113
+ ]
98
114
  })]
99
115
  })]
100
116
  });
101
117
  }
102
118
  //#endregion
103
- export { g as CommentThread };
119
+ export { y as CommentThread };
104
120
 
105
121
  //# sourceMappingURL=CommentThread.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CommentThread.js","names":[],"sources":["../../../src/comradecircle/components/CommentThread.tsx"],"sourcesContent":["/**\n * CommentThread — threaded comments for a post.\n * @module comradecircle/components\n *\n * Reads `comListComments(postId)`; writes `comAddComment`. Top-level comments\n * (parentId == null) render replies one level deep. Author identity comes from\n * `ComProfile` via {@link MemberIdentity}.\n */\n\nimport { useMemo, useState } from 'react';\nimport { CornerDownRight, Send } from 'lucide-react';\nimport { Button, Textarea, Spinner } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport { useComComments, useComAddComment } from '../hooks';\nimport { MemberIdentity } from './MemberIdentity';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComComment } from '../types';\n\ninterface CommentThreadProps {\n postId: string;\n}\n\nexport function CommentThread({ postId }: CommentThreadProps) {\n const { data: comments, isLoading } = useComComments(postId);\n const addComment = useComAddComment();\n const [body, setBody] = useState('');\n const [replyTo, setReplyTo] = useState<ComComment | null>(null);\n\n const { roots, repliesByParent } = useMemo(() => {\n const list = comments ?? [];\n const repliesMap = new Map<string, ComComment[]>();\n const top: ComComment[] = [];\n for (const c of list) {\n if (c.parentId) {\n const arr = repliesMap.get(c.parentId) ?? [];\n arr.push(c);\n repliesMap.set(c.parentId, arr);\n } else {\n top.push(c);\n }\n }\n return { roots: top, repliesByParent: repliesMap };\n }, [comments]);\n\n const submit = async () => {\n const text = body.trim();\n if (!text) return;\n try {\n await addComment.mutateAsync({ postId, body: text, parentId: replyTo?.id ?? null });\n setBody('');\n setReplyTo(null);\n } catch (err) {\n notify.error('Could not add comment', describeError(err));\n }\n };\n\n const renderComment = (c: ComComment, isReply = false) => (\n <div key={c.id} className={cn('flex flex-col gap-1', isReply && 'ml-8')}>\n <div className=\"rounded-xl bg-bg-sunken/60 px-3 py-2\">\n <div className=\"flex items-center justify-between gap-2\">\n <MemberIdentity\n memberId={c.authorId}\n size=\"xs\"\n suffix={formatRelativeTime(c.createdAt)}\n />\n {!isReply && (\n <button\n type=\"button\"\n onClick={() => setReplyTo(c)}\n className=\"inline-flex items-center gap-1 text-xs text-text-tertiary transition-colors hover:text-text-primary\"\n >\n <CornerDownRight className=\"h-3 w-3\" /> Reply\n </button>\n )}\n </div>\n <p className=\"mt-1 whitespace-pre-wrap break-words text-sm text-text-primary\">{c.body}</p>\n </div>\n {(repliesByParent.get(c.id) ?? []).map((r) => renderComment(r, true))}\n </div>\n );\n\n return (\n <div className=\"mt-3 border-t border-border-seam pt-3\">\n {isLoading ? (\n <div className=\"flex justify-center py-4\">\n <Spinner size=\"sm\" />\n </div>\n ) : roots.length === 0 ? (\n <p className=\"py-2 text-center text-sm text-text-tertiary\">\n No comments yet. Start the conversation.\n </p>\n ) : (\n <div className=\"flex flex-col gap-3\">{roots.map((c) => renderComment(c))}</div>\n )}\n\n <div className=\"mt-3 flex flex-col gap-2\">\n {replyTo && (\n <div className=\"flex items-center justify-between rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary\">\n <span className=\"truncate\">Replying to a comment</span>\n <button\n type=\"button\"\n onClick={() => setReplyTo(null)}\n className=\"text-text-tertiary hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <Textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n placeholder=\"Add a comment…\"\n rows={1}\n className=\"min-h-[40px] resize-none\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n void submit();\n }\n }}\n />\n <Button\n size=\"sm\"\n onClick={submit}\n disabled={!body.trim() || addComment.isPending}\n aria-label=\"Post comment\"\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAuBA,SAAgB,EAAc,EAAE,aAA8B;CAC5D,IAAM,EAAE,MAAM,GAAU,iBAAc,EAAe,EAAO,EACtD,IAAa,GAAkB,EAC/B,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAS,KAAc,EAA4B,KAAK,EAEzD,EAAE,UAAO,uBAAoB,QAAc;EAC/C,IAAM,IAAO,KAAY,EAAE,EACrB,oBAAa,IAAI,KAA2B,EAC5C,IAAoB,EAAE;AAC5B,OAAK,IAAM,KAAK,EACd,KAAI,EAAE,UAAU;GACd,IAAM,IAAM,EAAW,IAAI,EAAE,SAAS,IAAI,EAAE;AAE5C,GADA,EAAI,KAAK,EAAE,EACX,EAAW,IAAI,EAAE,UAAU,EAAI;QAE/B,GAAI,KAAK,EAAE;AAGf,SAAO;GAAE,OAAO;GAAK,iBAAiB;GAAY;IACjD,CAAC,EAAS,CAAC,EAER,IAAS,YAAY;EACzB,IAAM,IAAO,EAAK,MAAM;AACnB,QACL,KAAI;AAGF,GAFA,MAAM,EAAW,YAAY;IAAE;IAAQ,MAAM;IAAM,UAAU,GAAS,MAAM;IAAM,CAAC,EACnF,EAAQ,GAAG,EACX,EAAW,KAAK;WACT,GAAK;AACZ,KAAO,MAAM,yBAAyB,EAAc,EAAI,CAAC;;IAIvD,KAAiB,GAAe,IAAU,OAC9C,kBAAC,OAAD;EAAgB,WAAW,EAAG,uBAAuB,KAAW,OAAO;YAAvE,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KACE,UAAU,EAAE;KACZ,MAAK;KACL,QAAQ,EAAmB,EAAE,UAAU;KACvC,CAAA,EACD,CAAC,KACA,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,EAAE;KAC5B,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAiB,WAAU,WAAY,CAAA,EAAA,SAChC;OAEP;OACN,kBAAC,KAAD;IAAG,WAAU;cAAkE,EAAE;IAAS,CAAA,CACtF;OACJ,EAAgB,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,MAAM,EAAc,GAAG,GAAK,CAAC,CACjE;IArBI,EAAE,GAqBN;AAGR,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,IACC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAS,MAAK,MAAO,CAAA;GACjB,CAAA,GACJ,EAAM,WAAW,IACnB,kBAAC,KAAD;GAAG,WAAU;aAA8C;GAEvD,CAAA,GAEJ,kBAAC,OAAD;GAAK,WAAU;aAAuB,EAAM,KAAK,MAAM,EAAc,EAAE,CAAC;GAAO,CAAA,EAGjF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAW;KAA4B,CAAA,EACvD,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,KAAK;KAC/B,WAAU;eACX;KAEQ,CAAA,CACL;OAER,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KACE,OAAO;KACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;KACxC,aAAY;KACZ,MAAM;KACN,WAAU;KACV,YAAY,MAAM;AAChB,MAAI,EAAE,QAAQ,YAAY,EAAE,WAAW,EAAE,aACvC,EAAE,gBAAgB,EACb,GAAQ;;KAGjB,CAAA,EACF,kBAAC,GAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU,CAAC,EAAK,MAAM,IAAI,EAAW;KACrC,cAAW;eAEX,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;KACrB,CAAA,CACL;MACF;KACF"}
1
+ {"version":3,"file":"CommentThread.js","names":[],"sources":["../../../src/comradecircle/components/CommentThread.tsx"],"sourcesContent":["/**\n * CommentThread — threaded comments for a post.\n * @module comradecircle/components\n *\n * Reads `comListComments(postId)`; writes `comAddComment`. Top-level comments\n * (parentId == null) render replies one level deep. Author identity comes from\n * `ComProfile` via {@link MemberIdentity}.\n */\n\nimport { useMemo, useState } from 'react';\nimport { CornerDownRight, Send, MessageCircle } from 'lucide-react';\nimport { Button, Textarea, Spinner } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport { useComComments, useComAddComment } from '../hooks';\nimport { useComradeCircle } from '../context';\nimport { MemberIdentity } from './MemberIdentity';\nimport { Avatar } from './Avatar';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComComment } from '../types';\n\ninterface CommentThreadProps {\n postId: string;\n}\n\nexport function CommentThread({ postId }: CommentThreadProps) {\n const { currentUser } = useComradeCircle();\n const { data: comments, isLoading } = useComComments(postId);\n const addComment = useComAddComment();\n const [body, setBody] = useState('');\n const [replyTo, setReplyTo] = useState<ComComment | null>(null);\n\n const { roots, repliesByParent } = useMemo(() => {\n const list = comments ?? [];\n const repliesMap = new Map<string, ComComment[]>();\n const top: ComComment[] = [];\n for (const c of list) {\n if (c.parentId) {\n const arr = repliesMap.get(c.parentId) ?? [];\n arr.push(c);\n repliesMap.set(c.parentId, arr);\n } else {\n top.push(c);\n }\n }\n return { roots: top, repliesByParent: repliesMap };\n }, [comments]);\n\n const submit = async () => {\n const text = body.trim();\n if (!text) return;\n try {\n await addComment.mutateAsync({ postId, body: text, parentId: replyTo?.id ?? null });\n setBody('');\n setReplyTo(null);\n } catch (err) {\n notify.error('Could not add comment', describeError(err));\n }\n };\n\n const renderComment = (c: ComComment, isReply = false) => (\n <div key={c.id} className={cn('flex flex-col gap-1', isReply && 'ml-8')}>\n <div className=\"rounded-xl bg-bg-sunken/60 px-3 py-2\">\n <div className=\"flex items-center justify-between gap-2\">\n <MemberIdentity\n memberId={c.authorId}\n size=\"xs\"\n suffix={formatRelativeTime(c.createdAt)}\n />\n {!isReply && (\n <button\n type=\"button\"\n onClick={() => setReplyTo(c)}\n className=\"inline-flex items-center gap-1 text-xs text-text-tertiary transition-colors hover:text-text-primary\"\n >\n <CornerDownRight className=\"h-3 w-3\" /> Reply\n </button>\n )}\n </div>\n <p className=\"mt-1 whitespace-pre-wrap break-words text-sm text-text-primary\">{c.body}</p>\n </div>\n {(repliesByParent.get(c.id) ?? []).map((r) => renderComment(r, true))}\n </div>\n );\n\n return (\n <div className=\"mt-3 border-t border-border-seam pt-3\">\n {isLoading ? (\n <div className=\"flex justify-center py-4\">\n <Spinner size=\"sm\" />\n </div>\n ) : roots.length === 0 ? (\n <div className=\"flex flex-col items-center gap-1.5 py-4 text-center\">\n <MessageCircle className=\"h-6 w-6 text-text-tertiary opacity-60\" />\n <p className=\"text-sm text-text-tertiary\">No comments yet — start the conversation.</p>\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">{roots.map((c) => renderComment(c))}</div>\n )}\n\n <div className=\"mt-3 flex flex-col gap-2\">\n {replyTo && (\n <div className=\"flex items-center justify-between rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary\">\n <span className=\"truncate\">Replying to a comment</span>\n <button\n type=\"button\"\n onClick={() => setReplyTo(null)}\n className=\"text-text-tertiary hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <Avatar\n name={currentUser?.name ?? currentUser?.email ?? 'You'}\n seed={currentUser?.id}\n size=\"sm\"\n className=\"mb-1\"\n />\n <Textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n placeholder=\"Add a comment…\"\n rows={1}\n className=\"min-h-[40px] resize-none\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n void submit();\n }\n }}\n />\n <Button\n size=\"icon\"\n onClick={submit}\n disabled={!body.trim() || addComment.isPending}\n aria-label=\"Post comment\"\n className=\"shrink-0 rounded-full\"\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { ComCircle } from '../types';
2
+
3
+ interface CreateCircleDialogProps {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ onCreated?: (circle: ComCircle) => void;
7
+ }
8
+ export declare function CreateCircleDialog({ open, onOpenChange, onCreated }: CreateCircleDialogProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=CreateCircleDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateCircleDialog.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/CreateCircleDialog.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AA0BH,OAAO,KAAK,EAAE,SAAS,EAAuB,MAAM,UAAU,CAAC;AAI/D,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;CACzC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CAsJ5F"}
@@ -0,0 +1,12 @@
1
+ import { ComEvent } from '../types';
2
+
3
+ interface CreateEventDialogProps {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ /** Fixed circle — when omitted the dialog shows an active-circle picker. */
7
+ circleId?: string;
8
+ onCreated?: (event: ComEvent) => void;
9
+ }
10
+ export declare function CreateEventDialog({ open, onOpenChange, circleId, onCreated, }: CreateEventDialogProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=CreateEventDialog.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,28 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export interface Column<T> {
4
+ key: string;
5
+ header: ReactNode;
6
+ render: (row: T) => ReactNode;
7
+ align?: 'left' | 'right' | 'center';
8
+ className?: string;
9
+ /** Hide below the `md` breakpoint for a mobile-friendly layout. */
10
+ hideOnMobile?: boolean;
11
+ /** Provide to make this column click-sortable (returns a comparable value). */
12
+ sortValue?: (row: T) => string | number;
13
+ }
14
+ interface DataTableProps<T> {
15
+ columns: Column<T>[];
16
+ rows: T[];
17
+ rowKey: (row: T) => string;
18
+ onRowClick?: (row: T) => void;
19
+ className?: string;
20
+ /** Default sort: a column key + direction. */
21
+ defaultSort?: {
22
+ key: string;
23
+ dir: 'asc' | 'desc';
24
+ };
25
+ }
26
+ export declare function DataTable<T>({ columns, rows, rowKey, onRowClick, className, defaultSort, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
27
+ export {};
28
+ //# sourceMappingURL=DataTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/DataTable.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK1D,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAC;CACzC;AAED,UAAU,cAAc,CAAC,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAC;CACpD;AAQD,wBAAgB,SAAS,CAAC,CAAC,EAAE,EAC3B,OAAO,EACP,IAAI,EACJ,MAAM,EACN,UAAU,EACV,SAAS,EACT,WAAW,GACZ,EAAE,cAAc,CAAC,CAAC,CAAC,2CAqGnB"}
@@ -0,0 +1,18 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ interface EmptyStateProps {
4
+ icon?: ReactNode;
5
+ /**
6
+ * On-brand illustration name → `/illustrations/<name>.webp`. Standard names:
7
+ * empty-data, empty-search, empty-files, empty-people, empty-notifications,
8
+ * onboarding, empty-generic.
9
+ */
10
+ illustration?: string;
11
+ title: string;
12
+ description?: string;
13
+ action?: ReactNode;
14
+ className?: string;
15
+ }
16
+ export declare const EmptyState: FC<EmptyStateProps>;
17
+ export {};
18
+ //# sourceMappingURL=EmptyState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/EmptyState.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3C,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CA8B1C,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { ComEvent } from '../types';
2
+
3
+ interface EventCardProps {
4
+ event: ComEvent;
5
+ onOpen: () => void;
6
+ }
7
+ export declare function EventCard({ event, onOpen }: EventCardProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=EventCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventCard.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/EventCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,UAAU,CAAC;AAExD,UAAU,cAAc;IACtB,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAOD,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,cAAc,2CAgI1D"}
@@ -1,93 +1,119 @@
1
- import { useComRsvpEvent as e } from "../hooks/useComradeCircleApi.js";
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { useComRsvpEvent as t } from "../hooks/useComradeCircleApi.js";
2
3
  import "../hooks/index.js";
3
- import { describeError as t, notify as n } from "../lib/toast.js";
4
- import { formatDateTime as r, formatNumber as i } from "../utils/formatters.js";
5
- import { StatusBadge as a } from "./StatusBadge.js";
6
- import { Calendar as o, ExternalLink as s, MapPin as c, Users as l, Video as u } from "lucide-react";
7
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
8
- import { Button as p, GlassCard as m } from "@burdenoff/fe-libs/ui";
4
+ import { describeError as n, notify as r } from "../lib/toast.js";
5
+ import { formatDateTime as i, formatNumber as a, formatRelativeTime as o } from "../utils/formatters.js";
6
+ import { StatusBadge as s } from "./StatusBadge.js";
7
+ import { MiniBar as c } from "./MiniBar.js";
8
+ import { useState as l } from "react";
9
+ import { Calendar as u, Check as d, ExternalLink as f, MapPin as p, Users as m, Video as h } from "lucide-react";
10
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
11
+ import { Button as v, GlassCard as y } from "@burdenoff/fe-libs/ui";
9
12
  //#region src/comradecircle/components/EventCard.tsx
10
- var h = [{
13
+ var b = [{
11
14
  status: "GOING",
12
15
  label: "Going"
13
16
  }, {
14
17
  status: "INTERESTED",
15
18
  label: "Interested"
16
19
  }];
17
- function g({ event: g, onOpen: _ }) {
18
- let v = e(), y = g.status === "SCHEDULED" || g.status === "LIVE", b = async (e) => {
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) => {
19
22
  try {
20
- await v.mutateAsync({
21
- eventId: g.id,
23
+ await C.mutateAsync({
24
+ eventId: x.id,
22
25
  status: e
23
- }), n.success(`RSVP: ${e === "GOING" ? "Going" : "Interested"}`, g.title);
26
+ }), T(e), r.success(`RSVP: ${e === "GOING" ? "Going" : "Interested"}`, x.title);
24
27
  } catch (e) {
25
- n.error("Could not RSVP", t(e));
28
+ r.error("Could not RSVP", n(e));
26
29
  }
27
- }, x = g.mode === "IN_PERSON" ? c : u;
28
- return /* @__PURE__ */ f(m, {
30
+ }, k = x.mode === "IN_PERSON" ? p : h, A = x.capacity ? Math.min(100, x.rsvpCount / x.capacity * 100) : 0, j = A >= 80;
31
+ return /* @__PURE__ */ _(y, {
29
32
  treatment: "plain",
30
- className: "flex flex-col p-5",
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)]"),
31
34
  children: [
32
- /* @__PURE__ */ f("div", {
35
+ /* @__PURE__ */ _("div", {
33
36
  className: "flex items-start justify-between gap-3",
34
- children: [/* @__PURE__ */ d("button", {
35
- type: "button",
36
- onClick: _,
37
- className: "text-left text-base font-semibold text-text-primary hover:underline",
38
- children: g.title
39
- }), /* @__PURE__ */ d(a, { status: g.status })]
37
+ children: [/* @__PURE__ */ g(s, { status: x.status }), E && /* @__PURE__ */ g("span", {
38
+ className: "text-xs font-medium text-text-tertiary",
39
+ children: o(x.startsAt)
40
+ })]
41
+ }),
42
+ /* @__PURE__ */ g("button", {
43
+ type: "button",
44
+ onClick: S,
45
+ className: "mt-2 text-left text-base font-semibold text-text-primary transition-colors hover:text-accent",
46
+ children: x.title
40
47
  }),
41
- g.description && /* @__PURE__ */ d("p", {
42
- className: "mt-2 line-clamp-2 text-sm text-text-secondary",
43
- children: g.description
48
+ x.description && /* @__PURE__ */ g("p", {
49
+ className: "mt-1.5 line-clamp-2 text-sm text-text-secondary",
50
+ children: x.description
44
51
  }),
45
- /* @__PURE__ */ f("div", {
52
+ /* @__PURE__ */ _("div", {
46
53
  className: "mt-3 flex flex-col gap-1.5 text-sm text-text-secondary",
47
- children: [/* @__PURE__ */ f("span", {
54
+ children: [/* @__PURE__ */ _("span", {
48
55
  className: "inline-flex items-center gap-2",
49
56
  children: [
50
- /* @__PURE__ */ d(o, { className: "h-4 w-4 text-text-tertiary" }),
57
+ /* @__PURE__ */ g(u, { className: "h-4 w-4 text-text-tertiary" }),
51
58
  " ",
52
- r(g.startsAt)
59
+ i(x.startsAt)
53
60
  ]
54
- }), /* @__PURE__ */ f("span", {
61
+ }), /* @__PURE__ */ _("span", {
55
62
  className: "inline-flex items-center gap-2",
56
- children: [/* @__PURE__ */ d(x, { className: "h-4 w-4 text-text-tertiary" }), g.mode === "IN_PERSON" ? g.location || "In person" : g.mode === "HYBRID" ? `Hybrid${g.location ? ` · ${g.location}` : ""}` : "Online"]
63
+ children: [/* @__PURE__ */ g(k, { className: "h-4 w-4 text-text-tertiary" }), x.mode === "IN_PERSON" ? x.location || "In person" : x.mode === "HYBRID" ? `Hybrid${x.location ? ` · ${x.location}` : ""}` : "Online"]
57
64
  })]
58
65
  }),
59
- /* @__PURE__ */ f("div", {
66
+ /* @__PURE__ */ _("div", {
67
+ className: "mt-3",
68
+ children: [/* @__PURE__ */ _("div", {
69
+ className: "flex items-center justify-between text-xs text-text-tertiary",
70
+ children: [/* @__PURE__ */ _("span", {
71
+ className: "inline-flex items-center gap-1",
72
+ children: [
73
+ /* @__PURE__ */ g(m, { className: "h-3.5 w-3.5" }),
74
+ " ",
75
+ a(x.rsvpCount),
76
+ x.capacity ? ` / ${a(x.capacity)}` : "",
77
+ " attending"
78
+ ]
79
+ }), x.capacity && j && /* @__PURE__ */ g("span", {
80
+ className: e(A >= 100 ? "text-status-error-text" : "text-status-warning-text"),
81
+ children: A >= 100 ? "Full" : `${Math.round(A)}% full`
82
+ })]
83
+ }), x.capacity ? /* @__PURE__ */ g(c, {
84
+ value: A,
85
+ className: "mt-1.5",
86
+ height: 4,
87
+ barClassName: A >= 100 ? "bg-status-error-text" : j ? "bg-status-warning-text" : "bg-accent"
88
+ }) : null]
89
+ }),
90
+ /* @__PURE__ */ _("div", {
60
91
  className: "mt-4 flex items-center justify-between gap-3 border-t border-border-seam pt-3",
61
- children: [/* @__PURE__ */ f("span", {
62
- className: "inline-flex items-center gap-1 text-xs text-text-tertiary",
63
- children: [
64
- /* @__PURE__ */ d(l, { className: "h-3.5 w-3.5" }),
65
- " ",
66
- i(g.rsvpCount),
67
- g.capacity ? ` / ${i(g.capacity)}` : "",
68
- " attending"
69
- ]
70
- }), /* @__PURE__ */ f("div", {
92
+ children: [D && x.joinUrl ? /* @__PURE__ */ _("a", {
93
+ href: x.joinUrl,
94
+ target: "_blank",
95
+ rel: "noreferrer noopener",
96
+ className: "inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline",
97
+ children: ["Join live ", /* @__PURE__ */ g(f, { className: "h-3 w-3" })]
98
+ }) : /* @__PURE__ */ g("span", {}), /* @__PURE__ */ g("div", {
71
99
  className: "flex items-center gap-2",
72
- children: [g.status === "LIVE" && g.joinUrl && /* @__PURE__ */ f("a", {
73
- href: g.joinUrl,
74
- target: "_blank",
75
- rel: "noreferrer noopener",
76
- className: "inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline",
77
- children: ["Join ", /* @__PURE__ */ d(s, { className: "h-3 w-3" })]
78
- }), y && h.map((e) => /* @__PURE__ */ d(p, {
79
- size: "sm",
80
- variant: e.status === "GOING" ? "default" : "outline",
81
- onClick: () => b(e.status),
82
- disabled: v.isPending,
83
- children: e.label
84
- }, e.status))]
100
+ children: E && b.map((e) => {
101
+ let t = w === e.status;
102
+ return /* @__PURE__ */ _(v, {
103
+ size: "sm",
104
+ variant: t || e.status === "GOING" ? "default" : "outline",
105
+ onClick: () => O(e.status),
106
+ disabled: C.isPending,
107
+ "aria-pressed": t,
108
+ children: [t && /* @__PURE__ */ g(d, { className: "mr-1 h-3.5 w-3.5" }), e.label]
109
+ }, e.status);
110
+ })
85
111
  })]
86
112
  })
87
113
  ]
88
114
  });
89
115
  }
90
116
  //#endregion
91
- export { g as EventCard };
117
+ export { x as EventCard };
92
118
 
93
119
  //# sourceMappingURL=EventCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EventCard.js","names":[],"sources":["../../../src/comradecircle/components/EventCard.tsx"],"sourcesContent":["/**\n * EventCard — a tile for a community event with a quick RSVP control.\n * @module comradecircle/components\n *\n * Writes `comRsvpEvent`. Shows mode + lifecycle status, schedule, attendee\n * count, and (for online/hybrid) a join link when the event is live.\n */\n\nimport { Calendar, MapPin, Video, Users, ExternalLink } from 'lucide-react';\nimport { GlassCard, Button } from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { useComRsvpEvent } from '../hooks';\nimport { formatDateTime, formatNumber } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComEvent, ComRsvpStatus } from '../types';\n\ninterface EventCardProps {\n event: ComEvent;\n onOpen: () => void;\n}\n\nconst RSVP_ACTIONS: { status: ComRsvpStatus; label: string }[] = [\n { status: 'GOING', label: 'Going' },\n { status: 'INTERESTED', label: 'Interested' },\n];\n\nexport function EventCard({ event, onOpen }: EventCardProps) {\n const rsvp = useComRsvpEvent();\n const canRsvp = event.status === 'SCHEDULED' || event.status === 'LIVE';\n\n const doRsvp = async (status: ComRsvpStatus) => {\n try {\n await rsvp.mutateAsync({ eventId: event.id, status });\n notify.success(`RSVP: ${status === 'GOING' ? 'Going' : 'Interested'}`, event.title);\n } catch (err) {\n notify.error('Could not RSVP', describeError(err));\n }\n };\n\n const ModeIcon = event.mode === 'IN_PERSON' ? MapPin : Video;\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"text-left text-base font-semibold text-text-primary hover:underline\"\n >\n {event.title}\n </button>\n <StatusBadge status={event.status} />\n </div>\n\n {event.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">{event.description}</p>\n )}\n\n <div className=\"mt-3 flex flex-col gap-1.5 text-sm text-text-secondary\">\n <span className=\"inline-flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-text-tertiary\" /> {formatDateTime(event.startsAt)}\n </span>\n <span className=\"inline-flex items-center gap-2\">\n <ModeIcon className=\"h-4 w-4 text-text-tertiary\" />\n {event.mode === 'IN_PERSON'\n ? event.location || 'In person'\n : event.mode === 'HYBRID'\n ? `Hybrid${event.location ? ` · ${event.location}` : ''}`\n : 'Online'}\n </span>\n </div>\n\n <div className=\"mt-4 flex items-center justify-between gap-3 border-t border-border-seam pt-3\">\n <span className=\"inline-flex items-center gap-1 text-xs text-text-tertiary\">\n <Users className=\"h-3.5 w-3.5\" /> {formatNumber(event.rsvpCount)}\n {event.capacity ? ` / ${formatNumber(event.capacity)}` : ''} attending\n </span>\n <div className=\"flex items-center gap-2\">\n {event.status === 'LIVE' && event.joinUrl && (\n <a\n href={event.joinUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline\"\n >\n Join <ExternalLink className=\"h-3 w-3\" />\n </a>\n )}\n {canRsvp &&\n RSVP_ACTIONS.map((a) => (\n <Button\n key={a.status}\n size=\"sm\"\n variant={a.status === 'GOING' ? 'default' : 'outline'}\n onClick={() => doRsvp(a.status)}\n disabled={rsvp.isPending}\n >\n {a.label}\n </Button>\n ))}\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;;;AAqBA,IAAM,IAA2D,CAC/D;CAAE,QAAQ;CAAS,OAAO;CAAS,EACnC;CAAE,QAAQ;CAAc,OAAO;CAAc,CAC9C;AAED,SAAgB,EAAU,EAAE,UAAO,aAA0B;CAC3D,IAAM,IAAO,GAAiB,EACxB,IAAU,EAAM,WAAW,eAAe,EAAM,WAAW,QAE3D,IAAS,OAAO,MAA0B;AAC9C,MAAI;AAEF,GADA,MAAM,EAAK,YAAY;IAAE,SAAS,EAAM;IAAI;IAAQ,CAAC,EACrD,EAAO,QAAQ,SAAS,MAAW,UAAU,UAAU,gBAAgB,EAAM,MAAM;WAC5E,GAAK;AACZ,KAAO,MAAM,kBAAkB,EAAc,EAAI,CAAC;;IAIhD,IAAW,EAAM,SAAS,cAAc,IAAS;AAEvD,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,EAAM;KACA,CAAA,EACT,kBAAC,GAAD,EAAa,QAAQ,EAAM,QAAU,CAAA,CACjC;;GAEL,EAAM,eACL,kBAAC,KAAD;IAAG,WAAU;cAAiD,EAAM;IAAgB,CAAA;GAGtF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACE,kBAAC,GAAD,EAAU,WAAU,8BAA+B,CAAA;;MAAE,EAAe,EAAM,SAAS;MAC9E;QACP,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAU,WAAU,8BAA+B,CAAA,EAClD,EAAM,SAAS,cACZ,EAAM,YAAY,cAClB,EAAM,SAAS,WACb,SAAS,EAAM,WAAW,MAAM,EAAM,aAAa,OACnD,SACD;OACH;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;;MAAE,EAAa,EAAM,UAAU;MAC/D,EAAM,WAAW,MAAM,EAAa,EAAM,SAAS,KAAK;MAAG;MACvD;QACP,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAM,WAAW,UAAU,EAAM,WAChC,kBAAC,KAAD;MACE,MAAM,EAAM;MACZ,QAAO;MACP,KAAI;MACJ,WAAU;gBAJZ,CAKC,SACM,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,CACvC;SAEL,KACC,EAAa,KAAK,MAChB,kBAAC,GAAD;MAEE,MAAK;MACL,SAAS,EAAE,WAAW,UAAU,YAAY;MAC5C,eAAe,EAAO,EAAE,OAAO;MAC/B,UAAU,EAAK;gBAEd,EAAE;MACI,EAPF,EAAE,OAOA,CACT,CACA;OACF;;GACI"}
1
+ {"version":3,"file":"EventCard.js","names":[],"sources":["../../../src/comradecircle/components/EventCard.tsx"],"sourcesContent":["/**\n * EventCard — a tile for a community event with a quick RSVP control.\n * @module comradecircle/components\n *\n * Writes `comRsvpEvent`. Shows mode + lifecycle status, a relative-time cue, a\n * capacity fill bar, the caller's selected RSVP (tracked client-side after a\n * successful mutation — the list query returns no viewer RSVP), and a live-join\n * link. LIVE events get an accent ring so they pop out of the grid.\n */\n\nimport { useState } from 'react';\nimport { Calendar, MapPin, Video, Users, ExternalLink, Check } from 'lucide-react';\nimport { GlassCard, Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport { MiniBar } from './MiniBar';\nimport { StatusBadge } from './StatusBadge';\nimport { useComRsvpEvent } from '../hooks';\nimport { formatDateTime, formatNumber, formatRelativeTime } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComEvent, ComRsvpStatus } from '../types';\n\ninterface EventCardProps {\n event: ComEvent;\n onOpen: () => void;\n}\n\nconst RSVP_ACTIONS: { status: ComRsvpStatus; label: string }[] = [\n { status: 'GOING', label: 'Going' },\n { status: 'INTERESTED', label: 'Interested' },\n];\n\nexport function EventCard({ event, onOpen }: EventCardProps) {\n const rsvp = useComRsvpEvent();\n const [myStatus, setMyStatus] = useState<ComRsvpStatus | null>(null);\n const canRsvp = event.status === 'SCHEDULED' || event.status === 'LIVE';\n const isLive = event.status === 'LIVE';\n\n const doRsvp = async (status: ComRsvpStatus) => {\n try {\n await rsvp.mutateAsync({ eventId: event.id, status });\n setMyStatus(status);\n notify.success(`RSVP: ${status === 'GOING' ? 'Going' : 'Interested'}`, event.title);\n } catch (err) {\n notify.error('Could not RSVP', describeError(err));\n }\n };\n\n const ModeIcon = event.mode === 'IN_PERSON' ? MapPin : Video;\n const fillPct = event.capacity ? Math.min(100, (event.rsvpCount / event.capacity) * 100) : 0;\n const nearFull = fillPct >= 80;\n\n return (\n <GlassCard\n treatment=\"plain\"\n className={cn(\n 'flex flex-col p-5 transition-all duration-200 motion-safe:hover:-translate-y-0.5 hover:border-[var(--color-accent-soft)]',\n isLive && 'ring-1 ring-[var(--color-accent-soft)]'\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <StatusBadge status={event.status} />\n {canRsvp && (\n <span className=\"text-xs font-medium text-text-tertiary\">\n {formatRelativeTime(event.startsAt)}\n </span>\n )}\n </div>\n\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"mt-2 text-left text-base font-semibold text-text-primary transition-colors hover:text-accent\"\n >\n {event.title}\n </button>\n\n {event.description && (\n <p className=\"mt-1.5 line-clamp-2 text-sm text-text-secondary\">{event.description}</p>\n )}\n\n <div className=\"mt-3 flex flex-col gap-1.5 text-sm text-text-secondary\">\n <span className=\"inline-flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-text-tertiary\" /> {formatDateTime(event.startsAt)}\n </span>\n <span className=\"inline-flex items-center gap-2\">\n <ModeIcon className=\"h-4 w-4 text-text-tertiary\" />\n {event.mode === 'IN_PERSON'\n ? event.location || 'In person'\n : event.mode === 'HYBRID'\n ? `Hybrid${event.location ? ` · ${event.location}` : ''}`\n : 'Online'}\n </span>\n </div>\n\n <div className=\"mt-3\">\n <div className=\"flex items-center justify-between text-xs text-text-tertiary\">\n <span className=\"inline-flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" /> {formatNumber(event.rsvpCount)}\n {event.capacity ? ` / ${formatNumber(event.capacity)}` : ''} attending\n </span>\n {event.capacity && nearFull && (\n <span\n className={cn(fillPct >= 100 ? 'text-status-error-text' : 'text-status-warning-text')}\n >\n {fillPct >= 100 ? 'Full' : `${Math.round(fillPct)}% full`}\n </span>\n )}\n </div>\n {event.capacity ? (\n <MiniBar\n value={fillPct}\n className=\"mt-1.5\"\n height={4}\n barClassName={\n fillPct >= 100\n ? 'bg-status-error-text'\n : nearFull\n ? 'bg-status-warning-text'\n : 'bg-accent'\n }\n />\n ) : null}\n </div>\n\n <div className=\"mt-4 flex items-center justify-between gap-3 border-t border-border-seam pt-3\">\n {isLive && event.joinUrl ? (\n <a\n href={event.joinUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline\"\n >\n Join live <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-2\">\n {canRsvp &&\n RSVP_ACTIONS.map((a) => {\n const active = myStatus === a.status;\n return (\n <Button\n key={a.status}\n size=\"sm\"\n variant={active || a.status === 'GOING' ? 'default' : 'outline'}\n onClick={() => doRsvp(a.status)}\n disabled={rsvp.isPending}\n aria-pressed={active}\n >\n {active && <Check className=\"mr-1 h-3.5 w-3.5\" />}\n {a.label}\n </Button>\n );\n })}\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * FileImage — render a wspace-files-svc file id as an image.
3
+ * @module comradecircle/components
4
+ *
5
+ * Resolves the id to a presigned URL via {@link useFileUrl}. Shows a skeleton
6
+ * while resolving and renders nothing (so the caller's fallback shows) if the id
7
+ * is absent or the URL fails to resolve.
8
+ */
9
+ interface FileImageProps {
10
+ fileId?: string | null;
11
+ alt: string;
12
+ className?: string;
13
+ /** Rendered when there is no fileId, resolution fails, or the image errors. */
14
+ fallback?: React.ReactNode;
15
+ }
16
+ export declare function FileImage({ fileId, alt, className, fallback }: FileImageProps): import("react/jsx-runtime").JSX.Element;
17
+ export {};
18
+ //# sourceMappingURL=FileImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileImage.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/FileImage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,QAAe,EAAE,EAAE,cAAc,2CAiBpF"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * FilterTabs — a segmented status/enum filter control.
3
+ * @module comradecircle/components
4
+ */
5
+ export interface FilterOption<V extends string> {
6
+ value: V;
7
+ label: string;
8
+ count?: number;
9
+ }
10
+ interface FilterTabsProps<V extends string> {
11
+ options: FilterOption<V>[];
12
+ value: V;
13
+ onChange: (value: V) => void;
14
+ className?: string;
15
+ }
16
+ export declare function FilterTabs<V extends string>({ options, value, onChange, className, }: FilterTabsProps<V>): import("react/jsx-runtime").JSX.Element;
17
+ export {};
18
+ //# sourceMappingURL=FilterTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterTabs.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/FilterTabs.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM;IAC5C,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe,CAAC,CAAC,SAAS,MAAM;IACxC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,EAC3C,OAAO,EACP,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE,eAAe,CAAC,CAAC,CAAC,2CAyCpB"}