@burdenoff/microfe-comradecircle 2026.525.1 → 2026.704.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/comradecircle/ComradeCircleRoot.js +75 -95
  2. package/dist/comradecircle/ComradeCircleRoot.js.map +1 -1
  3. package/dist/comradecircle/ComradeCircleRoutes.js +58 -37
  4. package/dist/comradecircle/ComradeCircleRoutes.js.map +1 -1
  5. package/dist/comradecircle/components/Avatar.js +47 -0
  6. package/dist/comradecircle/components/Avatar.js.map +1 -0
  7. package/dist/comradecircle/components/AvatarUploadField.js +80 -0
  8. package/dist/comradecircle/components/AvatarUploadField.js.map +1 -0
  9. package/dist/comradecircle/components/CircleCard.js +118 -0
  10. package/dist/comradecircle/components/CircleCard.js.map +1 -0
  11. package/dist/comradecircle/components/CommentThread.js +105 -0
  12. package/dist/comradecircle/components/CommentThread.js.map +1 -0
  13. package/dist/comradecircle/components/CreateCircleDialog.js +159 -0
  14. package/dist/comradecircle/components/CreateCircleDialog.js.map +1 -0
  15. package/dist/comradecircle/components/CreateEventDialog.js +210 -0
  16. package/dist/comradecircle/components/CreateEventDialog.js.map +1 -0
  17. package/dist/comradecircle/components/EmptyState.js +35 -0
  18. package/dist/comradecircle/components/EmptyState.js.map +1 -0
  19. package/dist/comradecircle/components/EventCard.js +93 -0
  20. package/dist/comradecircle/components/EventCard.js.map +1 -0
  21. package/dist/comradecircle/components/FileImage.js +20 -0
  22. package/dist/comradecircle/components/FileImage.js.map +1 -0
  23. package/dist/comradecircle/components/FilterTabs.js +27 -0
  24. package/dist/comradecircle/components/FilterTabs.js.map +1 -0
  25. package/dist/comradecircle/components/ImageAttachments.js +97 -0
  26. package/dist/comradecircle/components/ImageAttachments.js.map +1 -0
  27. package/dist/comradecircle/components/MemberIdentity.js +44 -0
  28. package/dist/comradecircle/components/MemberIdentity.js.map +1 -0
  29. package/dist/comradecircle/components/MiniBar.js +22 -0
  30. package/dist/comradecircle/components/MiniBar.js.map +1 -0
  31. package/dist/comradecircle/components/PageLayout.js +38 -0
  32. package/dist/comradecircle/components/PageLayout.js.map +1 -0
  33. package/dist/comradecircle/components/Pagination.js +50 -0
  34. package/dist/comradecircle/components/Pagination.js.map +1 -0
  35. package/dist/comradecircle/components/PollCard.js +97 -0
  36. package/dist/comradecircle/components/PollCard.js.map +1 -0
  37. package/dist/comradecircle/components/PostCard.js +163 -0
  38. package/dist/comradecircle/components/PostCard.js.map +1 -0
  39. package/dist/comradecircle/components/PostComposer.js +87 -0
  40. package/dist/comradecircle/components/PostComposer.js.map +1 -0
  41. package/dist/comradecircle/components/ReactionBar.js +122 -0
  42. package/dist/comradecircle/components/ReactionBar.js.map +1 -0
  43. package/dist/comradecircle/components/StateViews.js +49 -0
  44. package/dist/comradecircle/components/StateViews.js.map +1 -0
  45. package/dist/comradecircle/components/StatusBadge.js +68 -0
  46. package/dist/comradecircle/components/StatusBadge.js.map +1 -0
  47. package/dist/comradecircle/components/Tabs.js +31 -0
  48. package/dist/comradecircle/components/Tabs.js.map +1 -0
  49. package/dist/comradecircle/components/TagInput.js +49 -0
  50. package/dist/comradecircle/components/TagInput.js.map +1 -0
  51. package/dist/comradecircle/context/ComradeCircleContext.js +85 -0
  52. package/dist/comradecircle/context/ComradeCircleContext.js.map +1 -0
  53. package/dist/comradecircle/context/index.js +1 -0
  54. package/dist/comradecircle/hooks/index.js +3 -0
  55. package/dist/comradecircle/hooks/useComradeCircleApi.js +419 -0
  56. package/dist/comradecircle/hooks/useComradeCircleApi.js.map +1 -0
  57. package/dist/comradecircle/hooks/useCursorPager.js +25 -0
  58. package/dist/comradecircle/hooks/useCursorPager.js.map +1 -0
  59. package/dist/comradecircle/hooks/useFiles.js +78 -0
  60. package/dist/comradecircle/hooks/useFiles.js.map +1 -0
  61. package/dist/comradecircle/index.js +5 -2
  62. package/dist/comradecircle/lib/graphql-cache.js +9 -0
  63. package/dist/comradecircle/lib/graphql-cache.js.map +1 -0
  64. package/dist/comradecircle/lib/toast.js +39 -0
  65. package/dist/comradecircle/lib/toast.js.map +1 -0
  66. package/dist/comradecircle/pages/FeedPage.js +65 -0
  67. package/dist/comradecircle/pages/FeedPage.js.map +1 -0
  68. package/dist/comradecircle/pages/circles/CircleDetailPage.js +651 -0
  69. package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -0
  70. package/dist/comradecircle/pages/circles/CirclesPage.js +129 -0
  71. package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -0
  72. package/dist/comradecircle/pages/events/EventDetailPage.js +276 -0
  73. package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -0
  74. package/dist/comradecircle/pages/events/EventsPage.js +102 -0
  75. package/dist/comradecircle/pages/events/EventsPage.js.map +1 -0
  76. package/dist/comradecircle/pages/members/MemberProfilePage.js +138 -0
  77. package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -0
  78. package/dist/comradecircle/pages/members/MembersPage.js +153 -0
  79. package/dist/comradecircle/pages/members/MembersPage.js.map +1 -0
  80. package/dist/comradecircle/pages/profile/MyProfilePage.js +266 -0
  81. package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -0
  82. package/dist/comradecircle/utils/cn.js +10 -0
  83. package/dist/comradecircle/utils/cn.js.map +1 -0
  84. package/dist/comradecircle/utils/formatters.js +51 -0
  85. package/dist/comradecircle/utils/formatters.js.map +1 -0
  86. package/dist/comradecircle/utils/navigation.js +2 -1
  87. package/dist/comradecircle/utils/navigation.js.map +1 -1
  88. package/dist/comradecircle/utils/slug.js +8 -0
  89. package/dist/comradecircle/utils/slug.js.map +1 -0
  90. package/dist/index.js +6 -4
  91. package/dist/node_modules/clsx/dist/clsx.js +18 -0
  92. package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
  93. package/dist/node_modules/sonner/dist/index.js +163 -0
  94. package/dist/node_modules/sonner/dist/index.js.map +1 -0
  95. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
  96. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  97. package/package.json +3 -2
  98. package/dist/comradecircle/pages/CommunitiesPage.js +0 -299
  99. package/dist/comradecircle/pages/CommunitiesPage.js.map +0 -1
  100. package/dist/comradecircle/pages/CreatorPage.js +0 -399
  101. package/dist/comradecircle/pages/CreatorPage.js.map +0 -1
  102. package/dist/comradecircle/pages/DashboardPage.js +0 -265
  103. package/dist/comradecircle/pages/DashboardPage.js.map +0 -1
  104. package/dist/comradecircle/providers/ComradeCircleProvider.js +0 -27
  105. package/dist/comradecircle/providers/ComradeCircleProvider.js.map +0 -1
  106. package/dist/comradecircle/store/comradecircleStore.js +0 -345
  107. package/dist/comradecircle/store/comradecircleStore.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CircleCard.js","names":[],"sources":["../../../src/comradecircle/components/CircleCard.tsx"],"sourcesContent":["/**\n * CircleCard — a discovery/overview tile for a community circle.\n * @module comradecircle/components\n *\n * Writes `comJoinCircle` / `comLeaveCircle`. When the caller already belongs to\n * the circle the card shows their role and an Open action; otherwise a Join\n * action. Membership is supplied by the parent (which knows the caller's set).\n */\n\nimport { Users, Hash } from 'lucide-react';\nimport { GlassCard, Button } from '@burdenoff/fe-libs/ui';\nimport { Avatar } from './Avatar';\nimport { StatusBadge } from './StatusBadge';\nimport { useComJoinCircle, useComLeaveCircle } from '../hooks';\nimport { formatEnumLabel, formatNumber } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComCircle, ComMembership } from '../types';\n\ninterface CircleCardProps {\n circle: ComCircle;\n membership?: ComMembership | null;\n onOpen: () => void;\n}\n\nexport function CircleCard({ circle, membership, onOpen }: CircleCardProps) {\n const join = useComJoinCircle();\n const leave = useComLeaveCircle();\n const isMember = membership?.status === 'ACTIVE';\n\n const handleJoin = async () => {\n try {\n await join.mutateAsync(circle.id);\n notify.success('Joined circle', `You are now a member of ${circle.name}.`);\n } catch (err) {\n notify.error('Could not join', describeError(err));\n }\n };\n\n const handleLeave = async () => {\n try {\n await leave.mutateAsync(circle.id);\n notify.success('Left circle', `You have left ${circle.name}.`);\n } catch (err) {\n notify.error('Could not leave', describeError(err));\n }\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col p-5\">\n <div className=\"flex items-start gap-3\">\n <Avatar name={circle.name} size=\"lg\" fileId={circle.avatar?.id} />\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"truncate text-left text-base font-semibold text-text-primary hover:underline\"\n >\n {circle.name}\n </button>\n {circle.topic && <p className=\"truncate text-sm text-text-secondary\">{circle.topic}</p>}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={circle.visibility} />\n {circle.status !== 'ACTIVE' && <StatusBadge status={circle.status} />}\n {isMember && membership && <StatusBadge status={membership.role} />}\n </div>\n </div>\n </div>\n\n {circle.description && (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{circle.description}</p>\n )}\n\n {circle.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {circle.tags.slice(0, 4).map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n <div className=\"mt-4 flex items-center justify-between gap-3 border-t border-border-seam pt-3\">\n <div className=\"flex items-center gap-3 text-xs text-text-tertiary\">\n <span className=\"inline-flex items-center gap-1\">\n <Users className=\"h-3.5 w-3.5\" /> {formatNumber(circle.memberCount)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Hash className=\"h-3.5 w-3.5\" /> {circle.channels?.length ?? 0}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n {isMember ? (\n <>\n <Button size=\"sm\" variant=\"outline\" onClick={handleLeave} disabled={leave.isPending}>\n Leave\n </Button>\n <Button size=\"sm\" onClick={onOpen}>\n Open\n </Button>\n </>\n ) : circle.status === 'ACTIVE' ? (\n <Button size=\"sm\" onClick={handleJoin} disabled={join.isPending}>\n Join\n </Button>\n ) : (\n <span className=\"text-xs text-text-tertiary\">{formatEnumLabel(circle.status)}</span>\n )}\n </div>\n </div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,EAAW,EAAE,WAAQ,eAAY,aAA2B;CAC1E,IAAM,IAAO,GAAkB,EACzB,IAAQ,GAAmB,EAC3B,IAAW,GAAY,WAAW;AAoBxC,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAQ,MAAM,EAAO;KAAM,MAAK;KAAK,QAAQ,EAAO,QAAQ;KAAM,CAAA,EAClE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBAET,EAAO;OACD,CAAA;MACR,EAAO,SAAS,kBAAC,KAAD;OAAG,WAAU;iBAAwC,EAAO;OAAU,CAAA;MACvF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAa,QAAQ,EAAO,YAAc,CAAA;QACzC,EAAO,WAAW,YAAY,kBAAC,GAAD,EAAa,QAAQ,EAAO,QAAU,CAAA;QACpE,KAAY,KAAc,kBAAC,GAAD,EAAa,QAAQ,EAAW,MAAQ,CAAA;QAC/D;;MACF;OACF;;GAEL,EAAO,eACN,kBAAC,KAAD;IAAG,WAAU;cAAiD,EAAO;IAAgB,CAAA;GAGtF,EAAO,KAAK,SAAS,KACpB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAC5B,kBAAC,QAAD;KAEE,WAAU;eAFZ,CAGC,KACG,EACG;OAJA,EAIA,CACP;IACE,CAAA;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;;OAAE,EAAa,EAAO,YAAY;OAC9D;SACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA;;OAAE,EAAO,UAAU,UAAU;OACxD;QACH;QACN,kBAAC,OAAD;KAAK,WAAU;eACZ,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAU,SA3D5B,YAAY;AAC9B,WAAI;AAEF,QADA,MAAM,EAAM,YAAY,EAAO,GAAG,EAClC,EAAO,QAAQ,eAAe,iBAAiB,EAAO,KAAK,GAAG;gBACvD,GAAK;AACZ,UAAO,MAAM,mBAAmB,EAAc,EAAI,CAAC;;;MAsDe,UAAU,EAAM;gBAAW;MAE5E,CAAA,EACT,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAS;gBAAQ;MAE1B,CAAA,CACR,EAAA,CAAA,GACD,EAAO,WAAW,WACpB,kBAAC,GAAD;MAAQ,MAAK;MAAK,SA5ET,YAAY;AAC7B,WAAI;AAEF,QADA,MAAM,EAAK,YAAY,EAAO,GAAG,EACjC,EAAO,QAAQ,iBAAiB,2BAA2B,EAAO,KAAK,GAAG;gBACnE,GAAK;AACZ,UAAO,MAAM,kBAAkB,EAAc,EAAI,CAAC;;;MAuEL,UAAU,EAAK;gBAAW;MAExD,CAAA,GAET,kBAAC,QAAD;MAAM,WAAU;gBAA8B,EAAgB,EAAO,OAAO;MAAQ,CAAA;KAElF,CAAA,CACF;;GACI"}
@@ -0,0 +1,105 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { useComAddComment as t, useComComments as n } from "../hooks/useComradeCircleApi.js";
3
+ 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";
11
+ //#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
+ for (let r of e) if (r.parentId) {
16
+ let e = t.get(r.parentId) ?? [];
17
+ e.push(r), t.set(r.parentId, e);
18
+ } else n.push(r);
19
+ return {
20
+ roots: n,
21
+ repliesByParent: t
22
+ };
23
+ }, [_]), E = async () => {
24
+ let e = b.trim();
25
+ if (e) try {
26
+ await y.mutateAsync({
27
+ postId: g,
28
+ body: e,
29
+ parentId: S?.id ?? null
30
+ }), x(""), C(null);
31
+ } catch (e) {
32
+ a.error("Could not add comment", i(e));
33
+ }
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
+ className: "rounded-xl bg-bg-sunken/60 px-3 py-2",
38
+ children: [/* @__PURE__ */ f("div", {
39
+ className: "flex items-center justify-between gap-2",
40
+ children: [/* @__PURE__ */ d(r, {
41
+ memberId: t.authorId,
42
+ size: "xs",
43
+ suffix: o(t.createdAt)
44
+ }), !n && /* @__PURE__ */ f("button", {
45
+ type: "button",
46
+ onClick: () => C(t),
47
+ 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"]
49
+ })]
50
+ }), /* @__PURE__ */ d("p", {
51
+ className: "mt-1 whitespace-pre-wrap break-words text-sm text-text-primary",
52
+ children: t.body
53
+ })]
54
+ }), (T.get(t.id) ?? []).map((e) => D(e, !0))]
55
+ }, t.id);
56
+ return /* @__PURE__ */ f("div", {
57
+ className: "mt-3 border-t border-border-seam pt-3",
58
+ children: [v ? /* @__PURE__ */ d("div", {
59
+ 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", {
65
+ className: "flex flex-col gap-3",
66
+ children: w.map((e) => D(e))
67
+ }), /* @__PURE__ */ f("div", {
68
+ className: "mt-3 flex flex-col gap-2",
69
+ children: [S && /* @__PURE__ */ f("div", {
70
+ 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", {
72
+ className: "truncate",
73
+ children: "Replying to a comment"
74
+ }), /* @__PURE__ */ d("button", {
75
+ type: "button",
76
+ onClick: () => C(null),
77
+ className: "text-text-tertiary hover:text-text-primary",
78
+ children: "Cancel"
79
+ })]
80
+ }), /* @__PURE__ */ f("div", {
81
+ 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
+ })]
98
+ })]
99
+ })]
100
+ });
101
+ }
102
+ //#endregion
103
+ export { g as CommentThread };
104
+
105
+ //# sourceMappingURL=CommentThread.js.map
@@ -0,0 +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"}
@@ -0,0 +1,159 @@
1
+ import { useComCreateCircle as e } from "../hooks/useComradeCircleApi.js";
2
+ import "../hooks/index.js";
3
+ import { describeError as t, notify as n } from "../lib/toast.js";
4
+ import { formatEnumLabel as r } from "../utils/formatters.js";
5
+ import { slugify as i } from "../utils/slug.js";
6
+ import { TagInput as a } from "./TagInput.js";
7
+ import { AvatarUploadField as o } from "./AvatarUploadField.js";
8
+ import { useState as s } from "react";
9
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
10
+ import { Button as u, Input as d, Label as f, ResponsiveDialog as p, ResponsiveDialogContent as m, ResponsiveDialogDescription as h, ResponsiveDialogFooter as g, ResponsiveDialogHeader as _, ResponsiveDialogTitle as v, Select as y, SelectContent as b, SelectItem as x, SelectTrigger as S, SelectValue as C, Textarea as w } from "@burdenoff/fe-libs/ui";
11
+ //#region src/comradecircle/components/CreateCircleDialog.tsx
12
+ var T = [
13
+ "PUBLIC",
14
+ "PRIVATE",
15
+ "SECRET"
16
+ ];
17
+ function E({ open: E, onOpenChange: D, onCreated: O }) {
18
+ let k = e(), [A, j] = s(""), [M, N] = s(""), [P, F] = s(!1), [I, L] = s(""), [R, z] = s(""), [B, V] = s("PUBLIC"), [H, U] = s([]), [W, G] = s(null), [K, q] = s(null), J = P ? M : i(A), Y = () => {
19
+ j(""), N(""), F(!1), L(""), z(""), V("PUBLIC"), U([]), G(null), q(null);
20
+ };
21
+ return /* @__PURE__ */ c(p, {
22
+ open: E,
23
+ onOpenChange: D,
24
+ children: /* @__PURE__ */ l(m, {
25
+ className: "sm:max-w-lg",
26
+ children: [
27
+ /* @__PURE__ */ l(_, { children: [/* @__PURE__ */ c(v, { children: "Create a circle" }), /* @__PURE__ */ c(h, { children: "A circle is a community space with its own channels, members, events, and polls." })] }),
28
+ /* @__PURE__ */ l("div", {
29
+ className: "flex flex-col gap-4 py-2",
30
+ children: [
31
+ /* @__PURE__ */ l("div", {
32
+ className: "grid gap-1.5",
33
+ children: [/* @__PURE__ */ c(f, { children: "Avatar" }), /* @__PURE__ */ c(o, {
34
+ fileId: W,
35
+ name: A || "Circle",
36
+ onUploaded: G,
37
+ label: "Upload circle avatar"
38
+ })]
39
+ }),
40
+ /* @__PURE__ */ l("div", {
41
+ className: "grid gap-1.5",
42
+ children: [/* @__PURE__ */ c(f, {
43
+ htmlFor: "circle-name",
44
+ children: "Name"
45
+ }), /* @__PURE__ */ c(d, {
46
+ id: "circle-name",
47
+ value: A,
48
+ onChange: (e) => j(e.target.value),
49
+ placeholder: "e.g. Product Guild"
50
+ })]
51
+ }),
52
+ /* @__PURE__ */ l("div", {
53
+ className: "grid gap-1.5",
54
+ children: [/* @__PURE__ */ c(f, {
55
+ htmlFor: "circle-slug",
56
+ children: "URL slug"
57
+ }), /* @__PURE__ */ c(d, {
58
+ id: "circle-slug",
59
+ value: J,
60
+ onChange: (e) => {
61
+ F(!0), N(i(e.target.value));
62
+ },
63
+ placeholder: "product-guild"
64
+ })]
65
+ }),
66
+ /* @__PURE__ */ l("div", {
67
+ className: "grid gap-1.5",
68
+ children: [/* @__PURE__ */ c(f, {
69
+ htmlFor: "circle-topic",
70
+ children: "Topic"
71
+ }), /* @__PURE__ */ c(d, {
72
+ id: "circle-topic",
73
+ value: I,
74
+ onChange: (e) => L(e.target.value),
75
+ placeholder: "A short one-line topic"
76
+ })]
77
+ }),
78
+ /* @__PURE__ */ l("div", {
79
+ className: "grid gap-1.5",
80
+ children: [/* @__PURE__ */ c(f, {
81
+ htmlFor: "circle-desc",
82
+ children: "Description"
83
+ }), /* @__PURE__ */ c(w, {
84
+ id: "circle-desc",
85
+ value: R,
86
+ onChange: (e) => z(e.target.value),
87
+ placeholder: "What is this circle about?",
88
+ rows: 3
89
+ })]
90
+ }),
91
+ /* @__PURE__ */ l("div", {
92
+ className: "grid gap-1.5",
93
+ children: [/* @__PURE__ */ c(f, { children: "Visibility" }), /* @__PURE__ */ l(y, {
94
+ value: B,
95
+ onValueChange: (e) => V(e),
96
+ children: [/* @__PURE__ */ c(S, { children: /* @__PURE__ */ c(C, {}) }), /* @__PURE__ */ c(b, { children: T.map((e) => /* @__PURE__ */ c(x, {
97
+ value: e,
98
+ children: r(e)
99
+ }, e)) })]
100
+ })]
101
+ }),
102
+ /* @__PURE__ */ l("div", {
103
+ className: "grid gap-1.5",
104
+ children: [/* @__PURE__ */ c(f, {
105
+ htmlFor: "circle-tags",
106
+ children: "Tags"
107
+ }), /* @__PURE__ */ c(a, {
108
+ id: "circle-tags",
109
+ value: H,
110
+ onChange: U,
111
+ placeholder: "Add tags…"
112
+ })]
113
+ }),
114
+ K && /* @__PURE__ */ c("p", {
115
+ className: "text-sm text-status-error-text",
116
+ children: K
117
+ })
118
+ ]
119
+ }),
120
+ /* @__PURE__ */ l(g, { children: [/* @__PURE__ */ c(u, {
121
+ variant: "outline",
122
+ onClick: () => D(!1),
123
+ children: "Cancel"
124
+ }), /* @__PURE__ */ c(u, {
125
+ onClick: async () => {
126
+ if (!A.trim()) {
127
+ q("Circle name is required.");
128
+ return;
129
+ }
130
+ if (!J) {
131
+ q("A URL slug is required.");
132
+ return;
133
+ }
134
+ try {
135
+ let e = await k.mutateAsync({
136
+ name: A.trim(),
137
+ slug: J,
138
+ topic: I.trim() || null,
139
+ description: R.trim() || null,
140
+ visibility: B,
141
+ avatarFileId: W,
142
+ tags: H.length ? H : null
143
+ });
144
+ n.success("Circle created", `${e.name} is ready.`), D(!1), Y(), O?.(e);
145
+ } catch (e) {
146
+ q(t(e));
147
+ }
148
+ },
149
+ disabled: k.isPending,
150
+ children: "Create circle"
151
+ })] })
152
+ ]
153
+ })
154
+ });
155
+ }
156
+ //#endregion
157
+ export { E as CreateCircleDialog };
158
+
159
+ //# sourceMappingURL=CreateCircleDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateCircleDialog.js","names":[],"sources":["../../../src/comradecircle/components/CreateCircleDialog.tsx"],"sourcesContent":["/**\n * CreateCircleDialog — create a new community circle.\n * @module comradecircle/components\n *\n * Writes `comCreateCircle`. Slug auto-derives from the name (editable).\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 { AvatarUploadField } from './AvatarUploadField';\nimport { useComCreateCircle } from '../hooks';\nimport { slugify } from '../utils/slug';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComCircle, ComCircleVisibility } from '../types';\n\nconst VISIBILITIES: ComCircleVisibility[] = ['PUBLIC', 'PRIVATE', 'SECRET'];\n\ninterface CreateCircleDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCreated?: (circle: ComCircle) => void;\n}\n\nexport function CreateCircleDialog({ open, onOpenChange, onCreated }: CreateCircleDialogProps) {\n const createCircle = useComCreateCircle();\n const [name, setName] = useState('');\n const [slug, setSlug] = useState('');\n const [slugTouched, setSlugTouched] = useState(false);\n const [topic, setTopic] = useState('');\n const [description, setDescription] = useState('');\n const [visibility, setVisibility] = useState<ComCircleVisibility>('PUBLIC');\n const [tags, setTags] = useState<string[]>([]);\n const [avatarFileId, setAvatarFileId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const effectiveSlug = slugTouched ? slug : slugify(name);\n\n const reset = () => {\n setName('');\n setSlug('');\n setSlugTouched(false);\n setTopic('');\n setDescription('');\n setVisibility('PUBLIC');\n setTags([]);\n setAvatarFileId(null);\n setError(null);\n };\n\n const submit = async () => {\n if (!name.trim()) {\n setError('Circle name is required.');\n return;\n }\n if (!effectiveSlug) {\n setError('A URL slug is required.');\n return;\n }\n try {\n const created = await createCircle.mutateAsync({\n name: name.trim(),\n slug: effectiveSlug,\n topic: topic.trim() || null,\n description: description.trim() || null,\n visibility,\n avatarFileId,\n tags: tags.length ? tags : null,\n });\n notify.success('Circle created', `${created.name} is ready.`);\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>Create a circle</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n A circle is a community space with its own channels, members, events, and polls.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex flex-col gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label>Avatar</Label>\n <AvatarUploadField\n fileId={avatarFileId}\n name={name || 'Circle'}\n onUploaded={setAvatarFileId}\n label=\"Upload circle avatar\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-name\">Name</Label>\n <Input\n id=\"circle-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Product Guild\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-slug\">URL slug</Label>\n <Input\n id=\"circle-slug\"\n value={effectiveSlug}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(slugify(e.target.value));\n }}\n placeholder=\"product-guild\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-topic\">Topic</Label>\n <Input\n id=\"circle-topic\"\n value={topic}\n onChange={(e) => setTopic(e.target.value)}\n placeholder=\"A short one-line topic\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-desc\">Description</Label>\n <Textarea\n id=\"circle-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"What is this circle about?\"\n rows={3}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label>Visibility</Label>\n <Select\n value={visibility}\n onValueChange={(v) => setVisibility(v as ComCircleVisibility)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {VISIBILITIES.map((v) => (\n <SelectItem key={v} value={v}>\n {formatEnumLabel(v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-tags\">Tags</Label>\n <TagInput id=\"circle-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={createCircle.isPending}>\n Create circle\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,210 @@
1
+ import { useComCircles as e, useComCreateEvent as t } from "../hooks/useComradeCircleApi.js";
2
+ import "../hooks/index.js";
3
+ import { describeError as n, notify as r } from "../lib/toast.js";
4
+ import { formatEnumLabel as i } from "../utils/formatters.js";
5
+ import { TagInput as a } from "./TagInput.js";
6
+ import { useState as o } from "react";
7
+ 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
+ //#region src/comradecircle/components/CreateEventDialog.tsx
10
+ var C = [
11
+ "ONLINE",
12
+ "IN_PERSON",
13
+ "HYBRID"
14
+ ], w = (e) => {
15
+ if (!e) return null;
16
+ let t = new Date(e);
17
+ return Number.isNaN(t.getTime()) ? null : t.toISOString();
18
+ };
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 = () => {
21
+ M(""), P(""), I(""), R("ONLINE"), B(""), H(""), W(""), K(""), J(""), X([]), Q(null);
22
+ };
23
+ return /* @__PURE__ */ s(f, {
24
+ open: T,
25
+ onOpenChange: E,
26
+ children: /* @__PURE__ */ c(p, {
27
+ className: "sm:max-w-lg",
28
+ 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("div", {
31
+ className: "flex max-h-[60vh] flex-col gap-4 overflow-y-auto py-2",
32
+ children: [
33
+ !D && /* @__PURE__ */ c("div", {
34
+ className: "grid gap-1.5",
35
+ children: [/* @__PURE__ */ s(d, { children: "Circle" }), /* @__PURE__ */ c(_, {
36
+ value: j,
37
+ 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
+ value: e.id,
40
+ children: e.name
41
+ }, e.id)) })]
42
+ })]
43
+ }),
44
+ /* @__PURE__ */ c("div", {
45
+ className: "grid gap-1.5",
46
+ children: [/* @__PURE__ */ s(d, {
47
+ htmlFor: "event-title",
48
+ children: "Title"
49
+ }), /* @__PURE__ */ s(u, {
50
+ id: "event-title",
51
+ value: N,
52
+ onChange: (e) => P(e.target.value),
53
+ placeholder: "e.g. Monthly community call"
54
+ })]
55
+ }),
56
+ /* @__PURE__ */ c("div", {
57
+ className: "grid gap-1.5",
58
+ children: [/* @__PURE__ */ s(d, {
59
+ htmlFor: "event-desc",
60
+ children: "Description"
61
+ }), /* @__PURE__ */ s(S, {
62
+ id: "event-desc",
63
+ value: F,
64
+ onChange: (e) => I(e.target.value),
65
+ rows: 2
66
+ })]
67
+ }),
68
+ /* @__PURE__ */ c("div", {
69
+ className: "grid grid-cols-2 gap-3",
70
+ children: [/* @__PURE__ */ c("div", {
71
+ className: "grid gap-1.5",
72
+ children: [/* @__PURE__ */ s(d, { children: "Mode" }), /* @__PURE__ */ c(_, {
73
+ value: L,
74
+ onValueChange: (e) => R(e),
75
+ children: [/* @__PURE__ */ s(b, { children: /* @__PURE__ */ s(x, {}) }), /* @__PURE__ */ s(v, { children: C.map((e) => /* @__PURE__ */ s(y, {
76
+ value: e,
77
+ children: i(e)
78
+ }, e)) })]
79
+ })]
80
+ }), /* @__PURE__ */ c("div", {
81
+ className: "grid gap-1.5",
82
+ children: [/* @__PURE__ */ s(d, {
83
+ htmlFor: "event-capacity",
84
+ children: "Capacity"
85
+ }), /* @__PURE__ */ s(u, {
86
+ id: "event-capacity",
87
+ type: "number",
88
+ min: 0,
89
+ value: U,
90
+ onChange: (e) => W(e.target.value),
91
+ placeholder: "Optional"
92
+ })]
93
+ })]
94
+ }),
95
+ L !== "ONLINE" && /* @__PURE__ */ c("div", {
96
+ className: "grid gap-1.5",
97
+ children: [/* @__PURE__ */ s(d, {
98
+ htmlFor: "event-location",
99
+ children: "Location"
100
+ }), /* @__PURE__ */ s(u, {
101
+ id: "event-location",
102
+ value: z,
103
+ onChange: (e) => B(e.target.value),
104
+ placeholder: "Venue / address"
105
+ })]
106
+ }),
107
+ L !== "IN_PERSON" && /* @__PURE__ */ c("div", {
108
+ className: "grid gap-1.5",
109
+ children: [/* @__PURE__ */ s(d, {
110
+ htmlFor: "event-join",
111
+ children: "Join URL"
112
+ }), /* @__PURE__ */ s(u, {
113
+ id: "event-join",
114
+ value: V,
115
+ onChange: (e) => H(e.target.value),
116
+ placeholder: "https://…"
117
+ })]
118
+ }),
119
+ /* @__PURE__ */ c("div", {
120
+ className: "grid grid-cols-2 gap-3",
121
+ children: [/* @__PURE__ */ c("div", {
122
+ className: "grid gap-1.5",
123
+ children: [/* @__PURE__ */ s(d, {
124
+ htmlFor: "event-start",
125
+ children: "Starts"
126
+ }), /* @__PURE__ */ s(u, {
127
+ id: "event-start",
128
+ type: "datetime-local",
129
+ value: G,
130
+ onChange: (e) => K(e.target.value)
131
+ })]
132
+ }), /* @__PURE__ */ c("div", {
133
+ className: "grid gap-1.5",
134
+ children: [/* @__PURE__ */ s(d, {
135
+ htmlFor: "event-end",
136
+ children: "Ends"
137
+ }), /* @__PURE__ */ s(u, {
138
+ id: "event-end",
139
+ type: "datetime-local",
140
+ value: q,
141
+ onChange: (e) => J(e.target.value)
142
+ })]
143
+ })]
144
+ }),
145
+ /* @__PURE__ */ c("div", {
146
+ className: "grid gap-1.5",
147
+ children: [/* @__PURE__ */ s(d, {
148
+ htmlFor: "event-tags",
149
+ children: "Tags"
150
+ }), /* @__PURE__ */ s(a, {
151
+ id: "event-tags",
152
+ value: Y,
153
+ onChange: X,
154
+ placeholder: "Add tags…"
155
+ })]
156
+ }),
157
+ Z && /* @__PURE__ */ s("p", {
158
+ className: "text-sm text-status-error-text",
159
+ children: Z
160
+ })
161
+ ]
162
+ }),
163
+ /* @__PURE__ */ c(m, { children: [/* @__PURE__ */ s(l, {
164
+ variant: "outline",
165
+ onClick: () => E(!1),
166
+ children: "Cancel"
167
+ }), /* @__PURE__ */ s(l, {
168
+ onClick: async () => {
169
+ if (!$) {
170
+ Q("Please choose a circle for this event.");
171
+ return;
172
+ }
173
+ if (!N.trim()) {
174
+ Q("Event title is required.");
175
+ return;
176
+ }
177
+ let e = w(G);
178
+ if (!e) {
179
+ Q("A valid start date & time is required.");
180
+ return;
181
+ }
182
+ try {
183
+ let t = await k.mutateAsync({
184
+ circleId: $,
185
+ title: N.trim(),
186
+ description: F.trim() || null,
187
+ mode: L,
188
+ location: z.trim() || null,
189
+ joinUrl: V.trim() || null,
190
+ capacity: U ? Number(U) : null,
191
+ startsAt: e,
192
+ endsAt: w(q),
193
+ tags: Y.length ? Y : null
194
+ });
195
+ r.success("Event scheduled", t.title), E(!1), te(), O?.(t);
196
+ } catch (e) {
197
+ Q(n(e));
198
+ }
199
+ },
200
+ disabled: k.isPending,
201
+ children: "Schedule event"
202
+ })] })
203
+ ]
204
+ })
205
+ });
206
+ }
207
+ //#endregion
208
+ export { T as CreateEventDialog };
209
+
210
+ //# sourceMappingURL=CreateEventDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateEventDialog.js","names":[],"sources":["../../../src/comradecircle/components/CreateEventDialog.tsx"],"sourcesContent":["/**\n * CreateEventDialog — schedule a new event in a circle.\n * @module comradecircle/components\n *\n * Writes `comCreateEvent`. When no `circleId` is fixed by the caller, a circle\n * picker (active circles) is shown. Datetime-local inputs are converted to ISO\n * strings for the `DateTime` scalar.\n */\n\nimport { useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { TagInput } from './TagInput';\nimport { useComCreateEvent, useComCircles } from '../hooks';\nimport { formatEnumLabel } from '../utils/formatters';\nimport { notify, describeError } from '../lib/toast';\nimport type { ComEvent, ComEventMode } from '../types';\n\nconst MODES: ComEventMode[] = ['ONLINE', 'IN_PERSON', 'HYBRID'];\n\nconst toIso = (local: string): string | null => {\n if (!local) return null;\n const d = new Date(local);\n return Number.isNaN(d.getTime()) ? null : d.toISOString();\n};\n\ninterface CreateEventDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Fixed circle — when omitted the dialog shows an active-circle picker. */\n circleId?: string;\n onCreated?: (event: ComEvent) => void;\n}\n\nexport function CreateEventDialog({\n open,\n onOpenChange,\n circleId,\n onCreated,\n}: CreateEventDialogProps) {\n const createEvent = useComCreateEvent();\n const circlesQuery = useComCircles({ status: 'ACTIVE' });\n const [pickedCircle, setPickedCircle] = useState('');\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [mode, setMode] = useState<ComEventMode>('ONLINE');\n const [location, setLocation] = useState('');\n const [joinUrl, setJoinUrl] = useState('');\n const [capacity, setCapacity] = useState('');\n const [startsAt, setStartsAt] = useState('');\n const [endsAt, setEndsAt] = useState('');\n const [tags, setTags] = useState<string[]>([]);\n const [error, setError] = useState<string | null>(null);\n\n const effectiveCircle = circleId ?? pickedCircle;\n\n const reset = () => {\n setPickedCircle('');\n setTitle('');\n setDescription('');\n setMode('ONLINE');\n setLocation('');\n setJoinUrl('');\n setCapacity('');\n setStartsAt('');\n setEndsAt('');\n setTags([]);\n setError(null);\n };\n\n const submit = async () => {\n if (!effectiveCircle) {\n setError('Please choose a circle for this event.');\n return;\n }\n if (!title.trim()) {\n setError('Event title is required.');\n return;\n }\n const startIso = toIso(startsAt);\n if (!startIso) {\n setError('A valid start date & time is required.');\n return;\n }\n try {\n const created = await createEvent.mutateAsync({\n circleId: effectiveCircle,\n title: title.trim(),\n description: description.trim() || null,\n mode,\n location: location.trim() || null,\n joinUrl: joinUrl.trim() || null,\n capacity: capacity ? Number(capacity) : null,\n startsAt: startIso,\n endsAt: toIso(endsAt),\n tags: tags.length ? tags : null,\n });\n notify.success('Event scheduled', created.title);\n onOpenChange(false);\n reset();\n onCreated?.(created);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule an event</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Host a gathering for your circle — online, in person, or hybrid.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex max-h-[60vh] flex-col gap-4 overflow-y-auto py-2\">\n {!circleId && (\n <div className=\"grid gap-1.5\">\n <Label>Circle</Label>\n <Select value={pickedCircle} onValueChange={setPickedCircle}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a circle\" />\n </SelectTrigger>\n <SelectContent>\n {(circlesQuery.data?.items ?? []).map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-title\">Title</Label>\n <Input\n id=\"event-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Monthly community call\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-desc\">Description</Label>\n <Textarea\n id=\"event-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label>Mode</Label>\n <Select value={mode} onValueChange={(v) => setMode(v as ComEventMode)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {MODES.map((m) => (\n <SelectItem key={m} value={m}>\n {formatEnumLabel(m)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-capacity\">Capacity</Label>\n <Input\n id=\"event-capacity\"\n type=\"number\"\n min={0}\n value={capacity}\n onChange={(e) => setCapacity(e.target.value)}\n placeholder=\"Optional\"\n />\n </div>\n </div>\n {mode !== 'ONLINE' && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-location\">Location</Label>\n <Input\n id=\"event-location\"\n value={location}\n onChange={(e) => setLocation(e.target.value)}\n placeholder=\"Venue / address\"\n />\n </div>\n )}\n {mode !== 'IN_PERSON' && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-join\">Join URL</Label>\n <Input\n id=\"event-join\"\n value={joinUrl}\n onChange={(e) => setJoinUrl(e.target.value)}\n placeholder=\"https://…\"\n />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-start\">Starts</Label>\n <Input\n id=\"event-start\"\n type=\"datetime-local\"\n value={startsAt}\n onChange={(e) => setStartsAt(e.target.value)}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-end\">Ends</Label>\n <Input\n id=\"event-end\"\n type=\"datetime-local\"\n value={endsAt}\n onChange={(e) => setEndsAt(e.target.value)}\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-tags\">Tags</Label>\n <TagInput id=\"event-tags\" value={tags} onChange={setTags} placeholder=\"Add tags…\" />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={createEvent.isPending}>\n Schedule event\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ import { IllustratedEmptyState as r } from "@burdenoff/fe-libs/ui";
4
+ //#region src/comradecircle/components/EmptyState.tsx
5
+ var i = ({ icon: i, illustration: a, title: o, description: s, action: c, className: l }) => a ? /* @__PURE__ */ t(r, {
6
+ illustration: a,
7
+ title: o,
8
+ description: s,
9
+ action: c,
10
+ className: l
11
+ }) : /* @__PURE__ */ n("div", {
12
+ className: e("flex flex-col items-center justify-center py-12 px-4 text-center", l),
13
+ children: [
14
+ i && /* @__PURE__ */ t("div", {
15
+ className: "mb-4 text-text-secondary opacity-50",
16
+ children: i
17
+ }),
18
+ /* @__PURE__ */ t("h3", {
19
+ className: "text-lg font-medium text-text-primary",
20
+ children: o
21
+ }),
22
+ s && /* @__PURE__ */ t("p", {
23
+ className: "mt-2 text-sm text-text-secondary max-w-sm",
24
+ children: s
25
+ }),
26
+ c && /* @__PURE__ */ t("div", {
27
+ className: "mt-6",
28
+ children: c
29
+ })
30
+ ]
31
+ });
32
+ //#endregion
33
+ export { i as EmptyState };
34
+
35
+ //# sourceMappingURL=EmptyState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmptyState.js","names":[],"sources":["../../../src/comradecircle/components/EmptyState.tsx"],"sourcesContent":["/**\n * Empty State Component\n * @module comradecircle/components\n *\n * Boff UI 2.0 zero-state. When an `illustration` name is provided it renders the\n * fe-libs {@link IllustratedEmptyState} (on-brand glass illustration); otherwise\n * it falls back to the icon-based layout.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface EmptyStateProps {\n icon?: ReactNode;\n /**\n * On-brand illustration name → `/illustrations/<name>.webp`. Standard names:\n * empty-data, empty-search, empty-files, empty-people, empty-notifications,\n * onboarding, empty-generic.\n */\n illustration?: string;\n title: string;\n description?: string;\n action?: ReactNode;\n className?: string;\n}\n\nexport const EmptyState: FC<EmptyStateProps> = ({\n icon,\n illustration,\n title,\n description,\n action,\n className,\n}) => {\n if (illustration) {\n return (\n <IllustratedEmptyState\n illustration={illustration}\n title={title}\n description={description}\n action={action}\n className={className}\n />\n );\n }\n\n return (\n <div\n className={cn('flex flex-col items-center justify-center py-12 px-4 text-center', className)}\n >\n {icon && <div className=\"mb-4 text-text-secondary opacity-50\">{icon}</div>}\n <h3 className=\"text-lg font-medium text-text-primary\">{title}</h3>\n {description && <p className=\"mt-2 text-sm text-text-secondary max-w-sm\">{description}</p>}\n {action && <div className=\"mt-6\">{action}</div>}\n </div>\n );\n};\n"],"mappings":";;;;AA2BA,IAAa,KAAmC,EAC9C,SACA,iBACA,UACA,gBACA,WACA,mBAEI,IAEA,kBAAC,GAAD;CACgB;CACP;CACM;CACL;CACG;CACX,CAAA,GAKJ,kBAAC,OAAD;CACE,WAAW,EAAG,oEAAoE,EAAU;WAD9F;EAGG,KAAQ,kBAAC,OAAD;GAAK,WAAU;aAAuC;GAAW,CAAA;EAC1E,kBAAC,MAAD;GAAI,WAAU;aAAyC;GAAW,CAAA;EACjE,KAAe,kBAAC,KAAD;GAAG,WAAU;aAA6C;GAAgB,CAAA;EACzF,KAAU,kBAAC,OAAD;GAAK,WAAU;aAAQ;GAAa,CAAA;EAC3C"}