@burdenoff/microfe-comradecircle 2026.819.1 → 2026.830.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/comradecircle/components/AvatarUploadField.js +8 -8
  2. package/dist/comradecircle/components/CircleCard.js +4 -4
  3. package/dist/comradecircle/components/CommentThread.d.ts.map +1 -1
  4. package/dist/comradecircle/components/CommentThread.js +66 -61
  5. package/dist/comradecircle/components/CommentThread.js.map +1 -1
  6. package/dist/comradecircle/components/CreateCircleDialog.d.ts.map +1 -1
  7. package/dist/comradecircle/components/CreateCircleDialog.js +89 -88
  8. package/dist/comradecircle/components/CreateCircleDialog.js.map +1 -1
  9. package/dist/comradecircle/components/CreateEventDialog.d.ts.map +1 -1
  10. package/dist/comradecircle/components/CreateEventDialog.js +28 -27
  11. package/dist/comradecircle/components/CreateEventDialog.js.map +1 -1
  12. package/dist/comradecircle/components/DraftsPanel.d.ts +32 -0
  13. package/dist/comradecircle/components/DraftsPanel.d.ts.map +1 -0
  14. package/dist/comradecircle/components/DraftsPanel.js +176 -0
  15. package/dist/comradecircle/components/DraftsPanel.js.map +1 -0
  16. package/dist/comradecircle/components/EventCard.js +5 -5
  17. package/dist/comradecircle/components/FileImage.js +5 -5
  18. package/dist/comradecircle/components/ImageAttachments.js +21 -21
  19. package/dist/comradecircle/components/MemberIdentity.js +4 -4
  20. package/dist/comradecircle/components/MobileBottomNav.d.ts.map +1 -1
  21. package/dist/comradecircle/components/MobileBottomNav.js +37 -30
  22. package/dist/comradecircle/components/MobileBottomNav.js.map +1 -1
  23. package/dist/comradecircle/components/Pagination.d.ts.map +1 -1
  24. package/dist/comradecircle/components/Pagination.js +37 -28
  25. package/dist/comradecircle/components/Pagination.js.map +1 -1
  26. package/dist/comradecircle/components/PollCard.js +13 -13
  27. package/dist/comradecircle/components/PostCard.d.ts.map +1 -1
  28. package/dist/comradecircle/components/PostCard.js +102 -89
  29. package/dist/comradecircle/components/PostCard.js.map +1 -1
  30. package/dist/comradecircle/components/PostComposer.d.ts +11 -5
  31. package/dist/comradecircle/components/PostComposer.d.ts.map +1 -1
  32. package/dist/comradecircle/components/PostComposer.js +60 -48
  33. package/dist/comradecircle/components/PostComposer.js.map +1 -1
  34. package/dist/comradecircle/components/ReactionBar.d.ts.map +1 -1
  35. package/dist/comradecircle/components/ReactionBar.js +65 -55
  36. package/dist/comradecircle/components/ReactionBar.js.map +1 -1
  37. package/dist/comradecircle/components/StateViews.d.ts.map +1 -1
  38. package/dist/comradecircle/components/StateViews.js +38 -30
  39. package/dist/comradecircle/components/StateViews.js.map +1 -1
  40. package/dist/comradecircle/components/UpcomingEvents.d.ts +26 -0
  41. package/dist/comradecircle/components/UpcomingEvents.d.ts.map +1 -0
  42. package/dist/comradecircle/components/UpcomingEvents.js +126 -0
  43. package/dist/comradecircle/components/UpcomingEvents.js.map +1 -0
  44. package/dist/comradecircle/components/index.d.ts +2 -0
  45. package/dist/comradecircle/components/index.d.ts.map +1 -1
  46. package/dist/comradecircle/hooks/index.d.ts +1 -0
  47. package/dist/comradecircle/hooks/index.d.ts.map +1 -1
  48. package/dist/comradecircle/hooks/index.js +1 -0
  49. package/dist/comradecircle/hooks/useTr.d.ts +18 -0
  50. package/dist/comradecircle/hooks/useTr.d.ts.map +1 -0
  51. package/dist/comradecircle/hooks/useTr.js +12 -0
  52. package/dist/comradecircle/hooks/useTr.js.map +1 -0
  53. package/dist/comradecircle/pages/FeedPage.d.ts +5 -0
  54. package/dist/comradecircle/pages/FeedPage.d.ts.map +1 -1
  55. package/dist/comradecircle/pages/FeedPage.js +76 -62
  56. package/dist/comradecircle/pages/FeedPage.js.map +1 -1
  57. package/dist/comradecircle/pages/circles/CircleDetailPage.d.ts.map +1 -1
  58. package/dist/comradecircle/pages/circles/CircleDetailPage.js +454 -409
  59. package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -1
  60. package/dist/comradecircle/pages/circles/CirclesPage.d.ts.map +1 -1
  61. package/dist/comradecircle/pages/circles/CirclesPage.js +124 -117
  62. package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -1
  63. package/dist/comradecircle/pages/events/EventDetailPage.d.ts.map +1 -1
  64. package/dist/comradecircle/pages/events/EventDetailPage.js +182 -153
  65. package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -1
  66. package/dist/comradecircle/pages/events/EventsPage.d.ts.map +1 -1
  67. package/dist/comradecircle/pages/events/EventsPage.js +100 -91
  68. package/dist/comradecircle/pages/events/EventsPage.js.map +1 -1
  69. package/dist/comradecircle/pages/members/MemberProfilePage.d.ts.map +1 -1
  70. package/dist/comradecircle/pages/members/MemberProfilePage.js +172 -159
  71. package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -1
  72. package/dist/comradecircle/pages/members/MembersPage.d.ts.map +1 -1
  73. package/dist/comradecircle/pages/members/MembersPage.js +103 -104
  74. package/dist/comradecircle/pages/members/MembersPage.js.map +1 -1
  75. package/dist/comradecircle/pages/monetization/MonetizationPage.d.ts.map +1 -1
  76. package/dist/comradecircle/pages/monetization/MonetizationPage.js +319 -297
  77. package/dist/comradecircle/pages/monetization/MonetizationPage.js.map +1 -1
  78. package/dist/comradecircle/pages/posts/PostThreadPage.d.ts.map +1 -1
  79. package/dist/comradecircle/pages/posts/PostThreadPage.js +37 -32
  80. package/dist/comradecircle/pages/posts/PostThreadPage.js.map +1 -1
  81. package/dist/comradecircle/pages/profile/MyProfilePage.d.ts.map +1 -1
  82. package/dist/comradecircle/pages/profile/MyProfilePage.js +182 -177
  83. package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -1
  84. package/dist/comradecircle/utils/i18n.js +15 -0
  85. package/dist/comradecircle/utils/i18n.js.map +1 -0
  86. package/dist/comradecircle/utils/pageContext.d.ts.map +1 -1
  87. package/dist/comradecircle/utils/pageContext.js +3 -1
  88. package/dist/comradecircle/utils/pageContext.js.map +1 -1
  89. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { cn as e } from "../utils/cn.js";
2
- import { FILE_FOLDERS as t, uploadImageToFiles as n } from "../hooks/useFiles.js";
1
+ import { FILE_FOLDERS as e, uploadImageToFiles as t } from "../hooks/useFiles.js";
2
+ import { cn as n } from "../utils/cn.js";
3
3
  import { Avatar as r } from "./Avatar.js";
4
4
  import { describeError as i, notify as a } from "../lib/toast.js";
5
5
  import { useId as o, useRef as s, useState as c } from "react";
@@ -43,9 +43,9 @@ function m({ fileId: m, name: h, onUploaded: g, label: _ = "Upload avatar", disa
43
43
  type: "file",
44
44
  accept: "image/*",
45
45
  className: "sr-only",
46
- onChange: async (e) => {
47
- let r = e.target.files?.[0];
48
- if (e.target.value = "", r) {
46
+ onChange: async (n) => {
47
+ let r = n.target.files?.[0];
48
+ if (n.target.value = "", r) {
49
49
  if (!r.type.startsWith("image/")) {
50
50
  a.error("Unsupported file", "Please choose an image.");
51
51
  return;
@@ -56,8 +56,8 @@ function m({ fileId: m, name: h, onUploaded: g, label: _ = "Upload avatar", disa
56
56
  }
57
57
  S(!0);
58
58
  try {
59
- let e = await n(r, t.avatars);
60
- w(e.url), g(e.id), a.success("Avatar updated");
59
+ let n = await t(r, e.avatars);
60
+ w(n.url), g(n.id), a.success("Avatar updated");
61
61
  } catch (e) {
62
62
  a.error("Upload failed", i(e));
63
63
  } finally {
@@ -71,7 +71,7 @@ function m({ fileId: m, name: h, onUploaded: g, label: _ = "Upload avatar", disa
71
71
  type: "button",
72
72
  onClick: () => y.current?.click(),
73
73
  disabled: v || x,
74
- className: e("inline-flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken", (v || x) && "cursor-not-allowed opacity-60"),
74
+ className: n("inline-flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken", (v || x) && "cursor-not-allowed opacity-60"),
75
75
  children: [
76
76
  /* @__PURE__ */ d(l, { className: "h-4 w-4" }),
77
77
  " ",
@@ -1,6 +1,6 @@
1
- import { cn as e } from "../utils/cn.js";
2
- import { useComJoinCircle as t, useComLeaveCircle as n } from "../hooks/useComradeCircleApi.js";
1
+ import { useComJoinCircle as e, useComLeaveCircle as t } from "../hooks/useComradeCircleApi.js";
3
2
  import "../hooks/index.js";
3
+ import { cn as n } from "../utils/cn.js";
4
4
  import { Avatar as r } from "./Avatar.js";
5
5
  import { describeError as i, notify as a } from "../lib/toast.js";
6
6
  import { formatEnumLabel as o, formatNumber as s } from "../utils/formatters.js";
@@ -10,10 +10,10 @@ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
10
10
  import { Button as m, GlassCard as h } from "@burdenoff/fe-libs/ui";
11
11
  //#region src/comradecircle/components/CircleCard.tsx
12
12
  function g({ circle: g, membership: _, onOpen: v }) {
13
- let y = t(), b = n(), x = _?.status === "ACTIVE";
13
+ let y = e(), b = t(), x = _?.status === "ACTIVE";
14
14
  return /* @__PURE__ */ p(h, {
15
15
  treatment: "plain",
16
- className: e("flex flex-col p-5 transition-all duration-200 motion-safe:hover:-translate-y-0.5 hover:border-[var(--color-accent-soft)]", (_?.role === "OWNER" || _?.role === "ADMIN") && "ring-1 ring-[var(--color-accent-soft)]"),
16
+ className: n("flex flex-col p-5 transition-all duration-200 motion-safe:hover:-translate-y-0.5 hover:border-[var(--color-accent-soft)]", (_?.role === "OWNER" || _?.role === "ADMIN") && "ring-1 ring-[var(--color-accent-soft)]"),
17
17
  children: [
18
18
  /* @__PURE__ */ p("div", {
19
19
  className: "flex items-start gap-3",
@@ -1 +1 @@
1
- {"version":3,"file":"CommentThread.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/CommentThread.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CAyH3D"}
1
+ {"version":3,"file":"CommentThread.d.ts","sourceRoot":"","sources":["../../../src/comradecircle/components/CommentThread.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CAuI3D"}
@@ -1,20 +1,21 @@
1
1
  import { useComradeCircle as e } from "../context/ComradeCircleContext.js";
2
2
  import "../context/index.js";
3
- import { cn as t } from "../utils/cn.js";
4
- import { useComAddComment as n, useComComments as r } from "../hooks/useComradeCircleApi.js";
3
+ import { useComAddComment as t, useComComments as n } from "../hooks/useComradeCircleApi.js";
4
+ import { useTr as r } from "../hooks/useTr.js";
5
5
  import "../hooks/index.js";
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";
6
+ import { cn as i } from "../utils/cn.js";
7
+ import { Avatar as a } from "./Avatar.js";
8
+ import { MemberIdentity as o } from "./MemberIdentity.js";
9
+ import { describeError as s, notify as c } from "../lib/toast.js";
10
+ import { formatRelativeTime as l } from "../utils/formatters.js";
11
+ import { useMemo as u, useState as d } from "react";
12
+ import { CornerDownRight as f, MessageCircle as p, Send as m } from "lucide-react";
13
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
14
+ import { Button as _, Spinner as v, Textarea as y } from "@burdenoff/fe-libs/ui";
14
15
  //#region src/comradecircle/components/CommentThread.tsx
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 = [];
16
+ function b({ postId: b }) {
17
+ let { currentUser: x } = e(), { data: S, isLoading: C } = n(b), w = t(), T = r(), [E, D] = d(""), [O, k] = d(null), { roots: A, repliesByParent: j } = u(() => {
18
+ let e = S ?? [], t = /* @__PURE__ */ new Map(), n = [];
18
19
  for (let r of e) if (r.parentId) {
19
20
  let e = t.get(r.parentId) ?? [];
20
21
  e.push(r), t.set(r.parentId, e);
@@ -23,92 +24,96 @@ function y({ postId: y }) {
23
24
  roots: n,
24
25
  repliesByParent: t
25
26
  };
26
- }, [x]), A = async () => {
27
- let e = w.trim();
27
+ }, [S]), M = async () => {
28
+ let e = E.trim();
28
29
  if (e) try {
29
- await C.mutateAsync({
30
- postId: y,
30
+ await w.mutateAsync({
31
+ postId: b,
31
32
  body: e,
32
- parentId: E?.id ?? null
33
- }), T(""), D(null);
33
+ parentId: O?.id ?? null
34
+ }), D(""), k(null);
34
35
  } catch (e) {
35
- s.error("Could not add comment", o(e));
36
+ c.error(T("comradecircle.components.commentThread.addError", "Could not add comment"), s(e));
36
37
  }
37
- }, j = (e, n = !1) => /* @__PURE__ */ h("div", {
38
- className: t("flex flex-col gap-1", n && "ml-8"),
39
- children: [/* @__PURE__ */ h("div", {
38
+ }, N = (e, t = !1) => /* @__PURE__ */ g("div", {
39
+ className: i("flex flex-col gap-1", t && "ml-8"),
40
+ children: [/* @__PURE__ */ g("div", {
40
41
  className: "rounded-xl bg-bg-sunken/60 px-3 py-2",
41
- children: [/* @__PURE__ */ h("div", {
42
+ children: [/* @__PURE__ */ g("div", {
42
43
  className: "flex items-center justify-between gap-2",
43
- children: [/* @__PURE__ */ m(a, {
44
+ children: [/* @__PURE__ */ h(o, {
44
45
  memberId: e.authorId,
45
46
  size: "xs",
46
- suffix: c(e.createdAt)
47
- }), !n && /* @__PURE__ */ h("button", {
47
+ suffix: l(e.createdAt)
48
+ }), !t && /* @__PURE__ */ g("button", {
48
49
  type: "button",
49
- onClick: () => D(e),
50
+ onClick: () => k(e),
50
51
  className: "inline-flex items-center gap-1 text-xs text-text-tertiary transition-colors hover:text-text-primary",
51
- children: [/* @__PURE__ */ m(d, { className: "h-3 w-3" }), " Reply"]
52
+ children: [
53
+ /* @__PURE__ */ h(f, { className: "h-3 w-3" }),
54
+ " ",
55
+ T("common.reply", "Reply")
56
+ ]
52
57
  })]
53
- }), /* @__PURE__ */ m("p", {
58
+ }), /* @__PURE__ */ h("p", {
54
59
  className: "mt-1 whitespace-pre-wrap break-words text-sm text-text-primary",
55
60
  children: e.body
56
61
  })]
57
- }), (k.get(e.id) ?? []).map((e) => j(e, !0))]
62
+ }), (j.get(e.id) ?? []).map((e) => N(e, !0))]
58
63
  }, e.id);
59
- return /* @__PURE__ */ h("div", {
64
+ return /* @__PURE__ */ g("div", {
60
65
  className: "mt-3 border-t border-border-seam pt-3",
61
- children: [S ? /* @__PURE__ */ m("div", {
66
+ children: [C ? /* @__PURE__ */ h("div", {
62
67
  className: "flex justify-center py-4",
63
- children: /* @__PURE__ */ m(_, { size: "sm" })
64
- }) : O.length === 0 ? /* @__PURE__ */ h("div", {
68
+ children: /* @__PURE__ */ h(v, { size: "sm" })
69
+ }) : A.length === 0 ? /* @__PURE__ */ g("div", {
65
70
  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", {
71
+ children: [/* @__PURE__ */ h(p, { className: "h-6 w-6 text-text-tertiary opacity-60" }), /* @__PURE__ */ h("p", {
67
72
  className: "text-sm text-text-tertiary",
68
- children: "No comments yet — start the conversation."
73
+ children: T("comradecircle.components.commentThread.empty", "No comments yet — start the conversation.")
69
74
  })]
70
- }) : /* @__PURE__ */ m("div", {
75
+ }) : /* @__PURE__ */ h("div", {
71
76
  className: "flex flex-col gap-3",
72
- children: O.map((e) => j(e))
73
- }), /* @__PURE__ */ h("div", {
77
+ children: A.map((e) => N(e))
78
+ }), /* @__PURE__ */ g("div", {
74
79
  className: "mt-3 flex flex-col gap-2",
75
- children: [E && /* @__PURE__ */ h("div", {
80
+ children: [O && /* @__PURE__ */ g("div", {
76
81
  className: "flex items-center justify-between rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary",
77
- children: [/* @__PURE__ */ m("span", {
82
+ children: [/* @__PURE__ */ h("span", {
78
83
  className: "truncate",
79
- children: "Replying to a comment"
80
- }), /* @__PURE__ */ m("button", {
84
+ children: T("comradecircle.components.commentThread.replyingTo", "Replying to a comment")
85
+ }), /* @__PURE__ */ h("button", {
81
86
  type: "button",
82
- onClick: () => D(null),
87
+ onClick: () => k(null),
83
88
  className: "text-text-tertiary hover:text-text-primary",
84
- children: "Cancel"
89
+ children: T("common.cancel", "Cancel")
85
90
  })]
86
- }), /* @__PURE__ */ h("div", {
91
+ }), /* @__PURE__ */ g("div", {
87
92
  className: "flex items-end gap-2",
88
93
  children: [
89
- /* @__PURE__ */ m(i, {
90
- name: b?.name ?? b?.email ?? "You",
91
- seed: b?.id,
94
+ /* @__PURE__ */ h(a, {
95
+ name: x?.name ?? x?.email ?? T("common.you", "You"),
96
+ seed: x?.id,
92
97
  size: "sm",
93
98
  className: "mb-1"
94
99
  }),
95
- /* @__PURE__ */ m(v, {
96
- value: w,
97
- onChange: (e) => T(e.target.value),
98
- placeholder: "Add a comment…",
100
+ /* @__PURE__ */ h(y, {
101
+ value: E,
102
+ onChange: (e) => D(e.target.value),
103
+ placeholder: T("comradecircle.components.commentThread.placeholder", "Add a comment…"),
99
104
  rows: 1,
100
105
  className: "min-h-[40px] resize-none",
101
106
  onKeyDown: (e) => {
102
- e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), A());
107
+ e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), M());
103
108
  }
104
109
  }),
105
- /* @__PURE__ */ m(g, {
110
+ /* @__PURE__ */ h(_, {
106
111
  size: "icon",
107
- onClick: A,
108
- disabled: !w.trim() || C.isPending,
109
- "aria-label": "Post comment",
112
+ onClick: M,
113
+ disabled: !E.trim() || w.isPending,
114
+ "aria-label": T("comradecircle.components.commentThread.postCommentAriaLabel", "Post comment"),
110
115
  className: "shrink-0 rounded-full",
111
- children: /* @__PURE__ */ m(p, { className: "h-4 w-4" })
116
+ children: /* @__PURE__ */ h(m, { className: "h-4 w-4" })
112
117
  })
113
118
  ]
114
119
  })]
@@ -116,6 +121,6 @@ function y({ postId: y }) {
116
121
  });
117
122
  }
118
123
  //#endregion
119
- export { y as CommentThread };
124
+ export { b as CommentThread };
120
125
 
121
126
  //# 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, 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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"}
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, useTr } 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 tr = useTr();\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(\n tr('comradecircle.components.commentThread.addError', 'Could not add comment'),\n describeError(err)\n );\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\" /> {tr('common.reply', '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\">\n {tr(\n 'comradecircle.components.commentThread.empty',\n 'No comments yet — start the conversation.'\n )}\n </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\">\n {tr('comradecircle.components.commentThread.replyingTo', 'Replying to a comment')}\n </span>\n <button\n type=\"button\"\n onClick={() => setReplyTo(null)}\n className=\"text-text-tertiary hover:text-text-primary\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <Avatar\n name={currentUser?.name ?? currentUser?.email ?? tr('common.you', '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={tr('comradecircle.components.commentThread.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={tr(\n 'comradecircle.components.commentThread.postCommentAriaLabel',\n 'Post comment'\n )}\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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"}
@@ -1 +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"}
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,2CA4L5F"}
@@ -1,159 +1,160 @@
1
1
  import { useComCreateCircle as e } from "../hooks/useComradeCircleApi.js";
2
+ import { useTr as t } from "../hooks/useTr.js";
2
3
  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";
4
+ import { describeError as n, notify as r } from "../lib/toast.js";
5
+ import { formatEnumLabel as i } from "../utils/formatters.js";
6
+ import { slugify as a } from "../utils/slug.js";
7
+ import { TagInput as o } from "./TagInput.js";
8
+ import { AvatarUploadField as s } from "./AvatarUploadField.js";
9
+ import { useState as c } from "react";
10
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
11
+ import { Button as d, Input as f, Label as p, ResponsiveDialog as m, ResponsiveDialogContent as h, ResponsiveDialogDescription as g, ResponsiveDialogFooter as _, ResponsiveDialogHeader as v, ResponsiveDialogTitle as y, Select as b, SelectContent as x, SelectItem as S, SelectTrigger as C, SelectValue as w, Textarea as T } from "@burdenoff/fe-libs/ui";
11
12
  //#region src/comradecircle/components/CreateCircleDialog.tsx
12
- var T = [
13
+ var E = [
13
14
  "PUBLIC",
14
15
  "PRIVATE",
15
16
  "SECRET"
16
17
  ];
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);
18
+ function D({ open: D, onOpenChange: O, onCreated: k }) {
19
+ let A = t(), j = e(), [M, N] = c(""), [P, F] = c(""), [I, L] = c(!1), [R, z] = c(""), [B, V] = c(""), [H, U] = c("PUBLIC"), [W, G] = c([]), [K, q] = c(null), [J, Y] = c(null), X = I ? P : a(M), Z = () => {
20
+ N(""), F(""), L(!1), z(""), V(""), U("PUBLIC"), G([]), q(null), Y(null);
20
21
  };
21
- return /* @__PURE__ */ c(p, {
22
- open: E,
23
- onOpenChange: D,
24
- children: /* @__PURE__ */ l(m, {
22
+ return /* @__PURE__ */ l(m, {
23
+ open: D,
24
+ onOpenChange: O,
25
+ children: /* @__PURE__ */ u(h, {
25
26
  className: "sm:max-w-lg",
26
27
  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", {
28
+ /* @__PURE__ */ u(v, { children: [/* @__PURE__ */ l(y, { children: A("comradecircle.dialogs.createCircle.title", "Create a circle") }), /* @__PURE__ */ l(g, { children: A("comradecircle.dialogs.createCircle.description", "A circle is a community space with its own channels, members, events, and polls.") })] }),
29
+ /* @__PURE__ */ u("div", {
29
30
  className: "flex flex-col gap-4 py-2",
30
31
  children: [
31
- /* @__PURE__ */ l("div", {
32
+ /* @__PURE__ */ u("div", {
32
33
  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"
34
+ children: [/* @__PURE__ */ l(p, { children: A("comradecircle.dialogs.createCircle.avatarLabel", "Avatar") }), /* @__PURE__ */ l(s, {
35
+ fileId: K,
36
+ name: M || A("comradecircle.dialogs.createCircle.avatarFallback", "Circle"),
37
+ onUploaded: q,
38
+ label: A("comradecircle.dialogs.createCircle.uploadAvatar", "Upload circle avatar")
38
39
  })]
39
40
  }),
40
- /* @__PURE__ */ l("div", {
41
+ /* @__PURE__ */ u("div", {
41
42
  className: "grid gap-1.5",
42
- children: [/* @__PURE__ */ c(f, {
43
+ children: [/* @__PURE__ */ l(p, {
43
44
  htmlFor: "circle-name",
44
- children: "Name"
45
- }), /* @__PURE__ */ c(d, {
45
+ children: A("comradecircle.dialogs.createCircle.nameLabel", "Name")
46
+ }), /* @__PURE__ */ l(f, {
46
47
  id: "circle-name",
47
- value: A,
48
- onChange: (e) => j(e.target.value),
49
- placeholder: "e.g. Product Guild"
48
+ value: M,
49
+ onChange: (e) => N(e.target.value),
50
+ placeholder: A("comradecircle.dialogs.createCircle.namePlaceholder", "e.g. Product Guild")
50
51
  })]
51
52
  }),
52
- /* @__PURE__ */ l("div", {
53
+ /* @__PURE__ */ u("div", {
53
54
  className: "grid gap-1.5",
54
- children: [/* @__PURE__ */ c(f, {
55
+ children: [/* @__PURE__ */ l(p, {
55
56
  htmlFor: "circle-slug",
56
- children: "URL slug"
57
- }), /* @__PURE__ */ c(d, {
57
+ children: A("comradecircle.dialogs.createCircle.slugLabel", "URL slug")
58
+ }), /* @__PURE__ */ l(f, {
58
59
  id: "circle-slug",
59
- value: J,
60
+ value: X,
60
61
  onChange: (e) => {
61
- F(!0), N(i(e.target.value));
62
+ L(!0), F(a(e.target.value));
62
63
  },
63
- placeholder: "product-guild"
64
+ placeholder: A("comradecircle.dialogs.createCircle.slugPlaceholder", "product-guild")
64
65
  })]
65
66
  }),
66
- /* @__PURE__ */ l("div", {
67
+ /* @__PURE__ */ u("div", {
67
68
  className: "grid gap-1.5",
68
- children: [/* @__PURE__ */ c(f, {
69
+ children: [/* @__PURE__ */ l(p, {
69
70
  htmlFor: "circle-topic",
70
- children: "Topic"
71
- }), /* @__PURE__ */ c(d, {
71
+ children: A("comradecircle.dialogs.createCircle.topicLabel", "Topic")
72
+ }), /* @__PURE__ */ l(f, {
72
73
  id: "circle-topic",
73
- value: I,
74
- onChange: (e) => L(e.target.value),
75
- placeholder: "A short one-line topic"
74
+ value: R,
75
+ onChange: (e) => z(e.target.value),
76
+ placeholder: A("comradecircle.dialogs.createCircle.topicPlaceholder", "A short one-line topic")
76
77
  })]
77
78
  }),
78
- /* @__PURE__ */ l("div", {
79
+ /* @__PURE__ */ u("div", {
79
80
  className: "grid gap-1.5",
80
- children: [/* @__PURE__ */ c(f, {
81
+ children: [/* @__PURE__ */ l(p, {
81
82
  htmlFor: "circle-desc",
82
- children: "Description"
83
- }), /* @__PURE__ */ c(w, {
83
+ children: A("comradecircle.dialogs.createCircle.descriptionLabel", "Description")
84
+ }), /* @__PURE__ */ l(T, {
84
85
  id: "circle-desc",
85
- value: R,
86
- onChange: (e) => z(e.target.value),
87
- placeholder: "What is this circle about?",
86
+ value: B,
87
+ onChange: (e) => V(e.target.value),
88
+ placeholder: A("comradecircle.dialogs.createCircle.descriptionPlaceholder", "What is this circle about?"),
88
89
  rows: 3
89
90
  })]
90
91
  }),
91
- /* @__PURE__ */ l("div", {
92
+ /* @__PURE__ */ u("div", {
92
93
  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, {
94
+ children: [/* @__PURE__ */ l(p, { children: A("comradecircle.dialogs.createCircle.visibilityLabel", "Visibility") }), /* @__PURE__ */ u(b, {
95
+ value: H,
96
+ onValueChange: (e) => U(e),
97
+ children: [/* @__PURE__ */ l(C, { children: /* @__PURE__ */ l(w, {}) }), /* @__PURE__ */ l(x, { children: E.map((e) => /* @__PURE__ */ l(S, {
97
98
  value: e,
98
- children: r(e)
99
+ children: i(e)
99
100
  }, e)) })]
100
101
  })]
101
102
  }),
102
- /* @__PURE__ */ l("div", {
103
+ /* @__PURE__ */ u("div", {
103
104
  className: "grid gap-1.5",
104
- children: [/* @__PURE__ */ c(f, {
105
+ children: [/* @__PURE__ */ l(p, {
105
106
  htmlFor: "circle-tags",
106
- children: "Tags"
107
- }), /* @__PURE__ */ c(a, {
107
+ children: A("comradecircle.dialogs.createCircle.tagsLabel", "Tags")
108
+ }), /* @__PURE__ */ l(o, {
108
109
  id: "circle-tags",
109
- value: H,
110
- onChange: U,
111
- placeholder: "Add tags…"
110
+ value: W,
111
+ onChange: G,
112
+ placeholder: A("comradecircle.dialogs.createCircle.tagsPlaceholder", "Add tags…")
112
113
  })]
113
114
  }),
114
- K && /* @__PURE__ */ c("p", {
115
+ J && /* @__PURE__ */ l("p", {
115
116
  className: "text-sm text-status-error-text",
116
- children: K
117
+ children: J
117
118
  })
118
119
  ]
119
120
  }),
120
- /* @__PURE__ */ l(g, { children: [/* @__PURE__ */ c(u, {
121
+ /* @__PURE__ */ u(_, { children: [/* @__PURE__ */ l(d, {
121
122
  variant: "outline",
122
- onClick: () => D(!1),
123
- children: "Cancel"
124
- }), /* @__PURE__ */ c(u, {
123
+ onClick: () => O(!1),
124
+ children: A("common.cancel", "Cancel")
125
+ }), /* @__PURE__ */ l(d, {
125
126
  onClick: async () => {
126
- if (!A.trim()) {
127
- q("Circle name is required.");
127
+ if (!M.trim()) {
128
+ Y(A("comradecircle.dialogs.createCircle.nameRequired", "Circle name is required."));
128
129
  return;
129
130
  }
130
- if (!J) {
131
- q("A URL slug is required.");
131
+ if (!X) {
132
+ Y(A("comradecircle.dialogs.createCircle.slugRequired", "A URL slug is required."));
132
133
  return;
133
134
  }
134
135
  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
136
+ let e = await j.mutateAsync({
137
+ name: M.trim(),
138
+ slug: X,
139
+ topic: R.trim() || null,
140
+ description: B.trim() || null,
141
+ visibility: H,
142
+ avatarFileId: K,
143
+ tags: W.length ? W : null
143
144
  });
144
- n.success("Circle created", `${e.name} is ready.`), D(!1), Y(), O?.(e);
145
+ r.success(A("comradecircle.dialogs.createCircle.created", "Circle created"), A("comradecircle.dialogs.createCircle.createdHint", "{{name}} is ready.", { name: e.name })), O(!1), Z(), k?.(e);
145
146
  } catch (e) {
146
- q(t(e));
147
+ Y(n(e));
147
148
  }
148
149
  },
149
- disabled: k.isPending,
150
- children: "Create circle"
150
+ disabled: j.isPending,
151
+ children: A("comradecircle.dialogs.createCircle.create", "Create circle")
151
152
  })] })
152
153
  ]
153
154
  })
154
155
  });
155
156
  }
156
157
  //#endregion
157
- export { E as CreateCircleDialog };
158
+ export { D as CreateCircleDialog };
158
159
 
159
160
  //# sourceMappingURL=CreateCircleDialog.js.map
@@ -1 +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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"}
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, useTr } 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 tr = useTr();\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(tr('comradecircle.dialogs.createCircle.nameRequired', 'Circle name is required.'));\n return;\n }\n if (!effectiveSlug) {\n setError(tr('comradecircle.dialogs.createCircle.slugRequired', '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(\n tr('comradecircle.dialogs.createCircle.created', 'Circle created'),\n tr('comradecircle.dialogs.createCircle.createdHint', '{{name}} is ready.', {\n name: created.name,\n })\n );\n onOpenChange(false);\n reset();\n onCreated?.(created);\n } catch (err) {\n setError(describeError(err));\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('comradecircle.dialogs.createCircle.title', 'Create a circle')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'comradecircle.dialogs.createCircle.description',\n 'A circle is a community space with its own channels, members, events, and polls.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"flex flex-col gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label>{tr('comradecircle.dialogs.createCircle.avatarLabel', 'Avatar')}</Label>\n <AvatarUploadField\n fileId={avatarFileId}\n name={name || tr('comradecircle.dialogs.createCircle.avatarFallback', 'Circle')}\n onUploaded={setAvatarFileId}\n label={tr('comradecircle.dialogs.createCircle.uploadAvatar', 'Upload circle avatar')}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-name\">\n {tr('comradecircle.dialogs.createCircle.nameLabel', 'Name')}\n </Label>\n <Input\n id=\"circle-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={tr(\n 'comradecircle.dialogs.createCircle.namePlaceholder',\n 'e.g. Product Guild'\n )}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-slug\">\n {tr('comradecircle.dialogs.createCircle.slugLabel', 'URL slug')}\n </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={tr(\n 'comradecircle.dialogs.createCircle.slugPlaceholder',\n 'product-guild'\n )}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-topic\">\n {tr('comradecircle.dialogs.createCircle.topicLabel', 'Topic')}\n </Label>\n <Input\n id=\"circle-topic\"\n value={topic}\n onChange={(e) => setTopic(e.target.value)}\n placeholder={tr(\n 'comradecircle.dialogs.createCircle.topicPlaceholder',\n 'A short one-line topic'\n )}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"circle-desc\">\n {tr('comradecircle.dialogs.createCircle.descriptionLabel', 'Description')}\n </Label>\n <Textarea\n id=\"circle-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder={tr(\n 'comradecircle.dialogs.createCircle.descriptionPlaceholder',\n 'What is this circle about?'\n )}\n rows={3}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label>{tr('comradecircle.dialogs.createCircle.visibilityLabel', '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\">\n {tr('comradecircle.dialogs.createCircle.tagsLabel', 'Tags')}\n </Label>\n <TagInput\n id=\"circle-tags\"\n value={tags}\n onChange={setTags}\n placeholder={tr('comradecircle.dialogs.createCircle.tagsPlaceholder', 'Add tags…')}\n />\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button onClick={submit} disabled={createCircle.isPending}>\n {tr('comradecircle.dialogs.createCircle.create', 'Create circle')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}