@burdenoff/microfe-comradecircle 2026.704.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 (34) hide show
  1. package/dist/comradecircle/components/Avatar.js +27 -15
  2. package/dist/comradecircle/components/Avatar.js.map +1 -1
  3. package/dist/comradecircle/components/AvatarUploadField.js +80 -0
  4. package/dist/comradecircle/components/AvatarUploadField.js.map +1 -0
  5. package/dist/comradecircle/components/CircleCard.js +2 -1
  6. package/dist/comradecircle/components/CircleCard.js.map +1 -1
  7. package/dist/comradecircle/components/CreateCircleDialog.js +74 -63
  8. package/dist/comradecircle/components/CreateCircleDialog.js.map +1 -1
  9. package/dist/comradecircle/components/FileImage.js +20 -0
  10. package/dist/comradecircle/components/FileImage.js.map +1 -0
  11. package/dist/comradecircle/components/ImageAttachments.js +97 -0
  12. package/dist/comradecircle/components/ImageAttachments.js.map +1 -0
  13. package/dist/comradecircle/components/MemberIdentity.js +2 -1
  14. package/dist/comradecircle/components/MemberIdentity.js.map +1 -1
  15. package/dist/comradecircle/components/PostCard.js +7 -14
  16. package/dist/comradecircle/components/PostCard.js.map +1 -1
  17. package/dist/comradecircle/components/PostComposer.js +45 -35
  18. package/dist/comradecircle/components/PostComposer.js.map +1 -1
  19. package/dist/comradecircle/hooks/index.js +1 -0
  20. package/dist/comradecircle/hooks/useComradeCircleApi.js +244 -241
  21. package/dist/comradecircle/hooks/useComradeCircleApi.js.map +1 -1
  22. package/dist/comradecircle/hooks/useFiles.js +78 -0
  23. package/dist/comradecircle/hooks/useFiles.js.map +1 -0
  24. package/dist/comradecircle/lib/graphql-cache.js +9 -0
  25. package/dist/comradecircle/lib/graphql-cache.js.map +1 -0
  26. package/dist/comradecircle/pages/circles/CircleDetailPage.js +2 -1
  27. package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -1
  28. package/dist/comradecircle/pages/members/MemberProfilePage.js +2 -1
  29. package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -1
  30. package/dist/comradecircle/pages/members/MembersPage.js +2 -1
  31. package/dist/comradecircle/pages/members/MembersPage.js.map +1 -1
  32. package/dist/comradecircle/pages/profile/MyProfilePage.js +100 -89
  33. package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -1
  34. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"MemberProfilePage.js","names":[],"sources":["../../../../src/comradecircle/pages/members/MemberProfilePage.tsx"],"sourcesContent":["/**\n * MemberProfilePage — a single member's public social profile.\n * @module comradecircle/pages/members\n *\n * Reads `comProfile(memberId)` (the social-identity layer). The visitor can\n * follow / unfollow the member (state derived from `comMyFollows('MEMBER')`);\n * on their own profile the action becomes an \"Edit profile\" shortcut. Interests\n * and skills render as token-only chips and hide when empty.\n */\n\nimport { ArrowLeft, UserX, UserPlus, UserCheck, Pencil } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Avatar } from '../../components/Avatar';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useComProfile, useComMyFollows, useComFollow, useComUnfollow } from '../../hooks';\nimport { useComradeCircle } from '../../context';\nimport { notify, describeError } from '../../lib/toast';\nimport { useParams } from 'react-router-dom';\n\nfunction ChipList({ items }: { items: string[] }) {\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {items.map((item) => (\n <span\n key={item}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n {item}\n </span>\n ))}\n </div>\n );\n}\n\nexport function MemberProfilePage() {\n const { memberId } = useParams<{ memberId: string }>();\n const { navigateTo, currentUser } = useComradeCircle();\n const { data: profile, isLoading, isError, refetch } = useComProfile(memberId);\n\n const follows = useComMyFollows('MEMBER');\n const follow = useComFollow();\n const unfollow = useComUnfollow();\n\n const back = (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('members')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <LoadingView label=\"Loading profile…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <ErrorView message=\"We couldn't load this member's profile.\" onRetry={() => refetch()} />\n </PageLayout>\n );\n }\n\n if (!profile) {\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <EmptyState\n icon={<UserX className=\"h-10 w-10\" />}\n title=\"No profile\"\n description=\"This member hasn't set up a ComradeCircle profile yet.\"\n />\n </PageLayout>\n );\n }\n\n const isSelf = Boolean(memberId && currentUser?.id === memberId);\n const isFollowing = (follows.data ?? []).some(\n (f) => f.targetId === memberId && f.targetType === 'MEMBER'\n );\n const followPending = follow.isPending || unfollow.isPending;\n\n const toggleFollow = async () => {\n if (!memberId) return;\n try {\n if (isFollowing) {\n await unfollow.mutateAsync({ targetId: memberId, targetType: 'MEMBER' });\n notify.success('Unfollowed', profile.displayName);\n } else {\n await follow.mutateAsync({ targetId: memberId, targetType: 'MEMBER' });\n notify.success('Following', profile.displayName);\n }\n } catch (err) {\n notify.error('Could not update follow', describeError(err));\n }\n };\n\n const action = isSelf ? (\n <Button variant=\"outline\" onClick={() => navigateTo('profile')}>\n <Pencil className=\"mr-1.5 h-4 w-4\" /> Edit profile\n </Button>\n ) : isFollowing ? (\n <Button variant=\"outline\" onClick={toggleFollow} disabled={followPending}>\n <UserCheck className=\"mr-1.5 h-4 w-4\" /> Following\n </Button>\n ) : (\n <Button onClick={toggleFollow} disabled={followPending}>\n <UserPlus className=\"mr-1.5 h-4 w-4\" /> Follow\n </Button>\n );\n\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <div className=\"mx-auto flex max-w-2xl flex-col gap-6\">\n {/* Identity header */}\n <div className=\"rounded-2xl border border-border-seam bg-bg-surface p-6\">\n <div className=\"flex flex-col gap-5 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex flex-col items-center gap-4 sm:flex-row sm:items-start\">\n <Avatar name={profile.displayName} presence={profile.presence} size=\"xl\" />\n <div className=\"min-w-0 text-center sm:text-left\">\n <h2 className=\"text-2xl font-semibold text-text-primary\">{profile.displayName}</h2>\n {profile.headline && (\n <p className=\"mt-1 text-sm text-text-secondary\">{profile.headline}</p>\n )}\n <div className=\"mt-3 flex justify-center sm:justify-start\">\n <StatusBadge status={profile.presence} />\n </div>\n </div>\n </div>\n <div className=\"flex shrink-0 justify-center sm:justify-end\">{action}</div>\n </div>\n\n {profile.bio && (\n <p className=\"mt-5 whitespace-pre-wrap text-sm text-text-secondary\">{profile.bio}</p>\n )}\n </div>\n\n {profile.interests.length > 0 && (\n <Section title=\"Interests\">\n <ChipList items={profile.interests} />\n </Section>\n )}\n\n {profile.skills.length > 0 && (\n <Section title=\"Skills\">\n <ChipList items={profile.skills} />\n </Section>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MemberProfilePage.js","names":[],"sources":["../../../../src/comradecircle/pages/members/MemberProfilePage.tsx"],"sourcesContent":["/**\n * MemberProfilePage — a single member's public social profile.\n * @module comradecircle/pages/members\n *\n * Reads `comProfile(memberId)` (the social-identity layer). The visitor can\n * follow / unfollow the member (state derived from `comMyFollows('MEMBER')`);\n * on their own profile the action becomes an \"Edit profile\" shortcut. Interests\n * and skills render as token-only chips and hide when empty.\n */\n\nimport { ArrowLeft, UserX, UserPlus, UserCheck, Pencil } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Avatar } from '../../components/Avatar';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useComProfile, useComMyFollows, useComFollow, useComUnfollow } from '../../hooks';\nimport { useComradeCircle } from '../../context';\nimport { notify, describeError } from '../../lib/toast';\nimport { useParams } from 'react-router-dom';\n\nfunction ChipList({ items }: { items: string[] }) {\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {items.map((item) => (\n <span\n key={item}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n {item}\n </span>\n ))}\n </div>\n );\n}\n\nexport function MemberProfilePage() {\n const { memberId } = useParams<{ memberId: string }>();\n const { navigateTo, currentUser } = useComradeCircle();\n const { data: profile, isLoading, isError, refetch } = useComProfile(memberId);\n\n const follows = useComMyFollows('MEMBER');\n const follow = useComFollow();\n const unfollow = useComUnfollow();\n\n const back = (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('members')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <LoadingView label=\"Loading profile…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <ErrorView message=\"We couldn't load this member's profile.\" onRetry={() => refetch()} />\n </PageLayout>\n );\n }\n\n if (!profile) {\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <EmptyState\n icon={<UserX className=\"h-10 w-10\" />}\n title=\"No profile\"\n description=\"This member hasn't set up a ComradeCircle profile yet.\"\n />\n </PageLayout>\n );\n }\n\n const isSelf = Boolean(memberId && currentUser?.id === memberId);\n const isFollowing = (follows.data ?? []).some(\n (f) => f.targetId === memberId && f.targetType === 'MEMBER'\n );\n const followPending = follow.isPending || unfollow.isPending;\n\n const toggleFollow = async () => {\n if (!memberId) return;\n try {\n if (isFollowing) {\n await unfollow.mutateAsync({ targetId: memberId, targetType: 'MEMBER' });\n notify.success('Unfollowed', profile.displayName);\n } else {\n await follow.mutateAsync({ targetId: memberId, targetType: 'MEMBER' });\n notify.success('Following', profile.displayName);\n }\n } catch (err) {\n notify.error('Could not update follow', describeError(err));\n }\n };\n\n const action = isSelf ? (\n <Button variant=\"outline\" onClick={() => navigateTo('profile')}>\n <Pencil className=\"mr-1.5 h-4 w-4\" /> Edit profile\n </Button>\n ) : isFollowing ? (\n <Button variant=\"outline\" onClick={toggleFollow} disabled={followPending}>\n <UserCheck className=\"mr-1.5 h-4 w-4\" /> Following\n </Button>\n ) : (\n <Button onClick={toggleFollow} disabled={followPending}>\n <UserPlus className=\"mr-1.5 h-4 w-4\" /> Follow\n </Button>\n );\n\n return (\n <PageLayout title=\"Profile\" actions={back}>\n <div className=\"mx-auto flex max-w-2xl flex-col gap-6\">\n {/* Identity header */}\n <div className=\"rounded-2xl border border-border-seam bg-bg-surface p-6\">\n <div className=\"flex flex-col gap-5 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex flex-col items-center gap-4 sm:flex-row sm:items-start\">\n <Avatar\n name={profile.displayName}\n presence={profile.presence}\n size=\"xl\"\n fileId={profile.avatar?.id}\n />\n <div className=\"min-w-0 text-center sm:text-left\">\n <h2 className=\"text-2xl font-semibold text-text-primary\">{profile.displayName}</h2>\n {profile.headline && (\n <p className=\"mt-1 text-sm text-text-secondary\">{profile.headline}</p>\n )}\n <div className=\"mt-3 flex justify-center sm:justify-start\">\n <StatusBadge status={profile.presence} />\n </div>\n </div>\n </div>\n <div className=\"flex shrink-0 justify-center sm:justify-end\">{action}</div>\n </div>\n\n {profile.bio && (\n <p className=\"mt-5 whitespace-pre-wrap text-sm text-text-secondary\">{profile.bio}</p>\n )}\n </div>\n\n {profile.interests.length > 0 && (\n <Section title=\"Interests\">\n <ChipList items={profile.interests} />\n </Section>\n )}\n\n {profile.skills.length > 0 && (\n <Section title=\"Skills\">\n <ChipList items={profile.skills} />\n </Section>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -88,7 +88,8 @@ function E() {
88
88
  children: [/* @__PURE__ */ b(s, {
89
89
  name: e.displayName,
90
90
  presence: e.presence,
91
- size: "lg"
91
+ size: "lg",
92
+ fileId: e.avatar?.id
92
93
  }), /* @__PURE__ */ x("div", {
93
94
  className: "min-w-0 flex-1",
94
95
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"MembersPage.js","names":[],"sources":["../../../../src/comradecircle/pages/members/MembersPage.tsx"],"sourcesContent":["/**\n * MembersPage — the discoverable member directory.\n * @module comradecircle/pages/members\n *\n * Reads `comListProfiles(discoverable: true)` — every member who has opted into\n * discovery via their {@link ComProfile}. Each card resolves display identity\n * from the social-identity layer (never a raw actor id), links to the member's\n * public profile, and offers a Follow/Following toggle wired to `comFollow` /\n * `comUnfollow` (targetType MEMBER). An optional client-side search narrows the\n * current page by name, headline, skills, or interests.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Search, UserPlus, Check } from 'lucide-react';\nimport { GlassCard, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Avatar } from '../../components/Avatar';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport {\n useComProfiles,\n useComMyFollows,\n useComFollow,\n useComUnfollow,\n useCursorPager,\n} from '../../hooks';\nimport { useComradeCircle } from '../../context';\nimport { notify, describeError } from '../../lib/toast';\nimport type { ComProfile } from '../../types';\n\nconst PAGE_SIZE = 12;\n\nexport function MembersPage() {\n const { navigateTo, currentUser } = useComradeCircle();\n const pager = useCursorPager();\n const [search, setSearch] = useState('');\n const [pendingId, setPendingId] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useComProfiles({\n discoverable: true,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n\n const { data: follows } = useComMyFollows('MEMBER');\n const follow = useComFollow();\n const unfollow = useComUnfollow();\n\n const followSet = useMemo(() => new Set((follows ?? []).map((f) => f.targetId)), [follows]);\n\n const profiles = useMemo(() => data?.items ?? [], [data]);\n\n const visible = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return profiles;\n return profiles.filter((p) => {\n const haystack = [p.displayName, p.headline ?? '', ...p.skills, ...p.interests]\n .join(' ')\n .toLowerCase();\n return haystack.includes(q);\n });\n }, [profiles, search]);\n\n const toggleFollow = async (profile: ComProfile) => {\n const wasFollowing = followSet.has(profile.memberId);\n const input = { targetId: profile.memberId, targetType: 'MEMBER' as const };\n setPendingId(profile.memberId);\n try {\n if (wasFollowing) {\n await unfollow.mutateAsync(input);\n notify.success('Unfollowed', profile.displayName);\n } else {\n await follow.mutateAsync(input);\n notify.success('Following', profile.displayName);\n }\n } catch (err) {\n notify.error(wasFollowing ? 'Could not unfollow' : 'Could not follow', describeError(err));\n } finally {\n setPendingId(null);\n }\n };\n\n return (\n <PageLayout\n title=\"Members\"\n description=\"Discover and follow people across your circles — search by name, headline, or skills.\"\n >\n {isLoading ? (\n <LoadingView label=\"Loading members…\" />\n ) : isError ? (\n <ErrorView message=\"We couldn't load the member directory.\" onRetry={() => refetch()} />\n ) : profiles.length === 0 && pager.page === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No members to discover yet\"\n description=\"When members make their profiles discoverable, they'll show up here for you to follow.\"\n />\n ) : (\n <div className=\"flex flex-col gap-5\">\n <div className=\"relative w-full max-w-sm\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <Input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search by name, headline, or skills\"\n aria-label=\"Search members\"\n className=\"pl-9\"\n />\n </div>\n\n {visible.length === 0 ? (\n <EmptyState\n icon={<Search className=\"h-9 w-9\" />}\n title=\"No matches on this page\"\n description=\"No members on this page match your search. Try a different term or browse another page.\"\n />\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {visible.map((p) => {\n const isSelf = Boolean(currentUser?.id && p.memberId === currentUser.id);\n const isFollowing = followSet.has(p.memberId);\n const chips = Array.from(new Set([...p.skills, ...p.interests])).slice(0, 4);\n\n return (\n <GlassCard key={p.id} treatment=\"plain\" className=\"flex flex-col p-5\">\n <div className=\"flex items-start gap-3\">\n <Avatar name={p.displayName} presence={p.presence} size=\"lg\" />\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`members/${p.memberId}`)}\n className=\"block max-w-full truncate text-left text-base font-semibold text-text-primary hover:underline\"\n >\n {p.displayName}\n </button>\n {p.headline && (\n <p className=\"truncate text-sm text-text-secondary\">{p.headline}</p>\n )}\n <div className=\"mt-1.5\">\n <StatusBadge status={p.presence} />\n </div>\n </div>\n </div>\n\n {chips.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {chips.map((chip) => (\n <span\n key={chip}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n {chip}\n </span>\n ))}\n </div>\n )}\n\n {!isSelf && (\n <div className=\"mt-4 flex items-center justify-end border-t border-border-seam pt-3\">\n {isFollowing ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => toggleFollow(p)}\n disabled={pendingId === p.memberId}\n >\n <Check className=\"mr-1.5 h-4 w-4\" /> Following\n </Button>\n ) : (\n <Button\n size=\"sm\"\n onClick={() => toggleFollow(p)}\n disabled={pendingId === p.memberId}\n >\n <UserPlus className=\"mr-1.5 h-4 w-4\" /> Follow\n </Button>\n )}\n </div>\n )}\n </GlassCard>\n );\n })}\n </div>\n )}\n\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={profiles.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo.cursor)}\n />\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MembersPage.js","names":[],"sources":["../../../../src/comradecircle/pages/members/MembersPage.tsx"],"sourcesContent":["/**\n * MembersPage — the discoverable member directory.\n * @module comradecircle/pages/members\n *\n * Reads `comListProfiles(discoverable: true)` — every member who has opted into\n * discovery via their {@link ComProfile}. Each card resolves display identity\n * from the social-identity layer (never a raw actor id), links to the member's\n * public profile, and offers a Follow/Following toggle wired to `comFollow` /\n * `comUnfollow` (targetType MEMBER). An optional client-side search narrows the\n * current page by name, headline, skills, or interests.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Search, UserPlus, Check } from 'lucide-react';\nimport { GlassCard, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Avatar } from '../../components/Avatar';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Pagination } from '../../components/Pagination';\nimport {\n useComProfiles,\n useComMyFollows,\n useComFollow,\n useComUnfollow,\n useCursorPager,\n} from '../../hooks';\nimport { useComradeCircle } from '../../context';\nimport { notify, describeError } from '../../lib/toast';\nimport type { ComProfile } from '../../types';\n\nconst PAGE_SIZE = 12;\n\nexport function MembersPage() {\n const { navigateTo, currentUser } = useComradeCircle();\n const pager = useCursorPager();\n const [search, setSearch] = useState('');\n const [pendingId, setPendingId] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useComProfiles({\n discoverable: true,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n\n const { data: follows } = useComMyFollows('MEMBER');\n const follow = useComFollow();\n const unfollow = useComUnfollow();\n\n const followSet = useMemo(() => new Set((follows ?? []).map((f) => f.targetId)), [follows]);\n\n const profiles = useMemo(() => data?.items ?? [], [data]);\n\n const visible = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return profiles;\n return profiles.filter((p) => {\n const haystack = [p.displayName, p.headline ?? '', ...p.skills, ...p.interests]\n .join(' ')\n .toLowerCase();\n return haystack.includes(q);\n });\n }, [profiles, search]);\n\n const toggleFollow = async (profile: ComProfile) => {\n const wasFollowing = followSet.has(profile.memberId);\n const input = { targetId: profile.memberId, targetType: 'MEMBER' as const };\n setPendingId(profile.memberId);\n try {\n if (wasFollowing) {\n await unfollow.mutateAsync(input);\n notify.success('Unfollowed', profile.displayName);\n } else {\n await follow.mutateAsync(input);\n notify.success('Following', profile.displayName);\n }\n } catch (err) {\n notify.error(wasFollowing ? 'Could not unfollow' : 'Could not follow', describeError(err));\n } finally {\n setPendingId(null);\n }\n };\n\n return (\n <PageLayout\n title=\"Members\"\n description=\"Discover and follow people across your circles — search by name, headline, or skills.\"\n >\n {isLoading ? (\n <LoadingView label=\"Loading members…\" />\n ) : isError ? (\n <ErrorView message=\"We couldn't load the member directory.\" onRetry={() => refetch()} />\n ) : profiles.length === 0 && pager.page === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No members to discover yet\"\n description=\"When members make their profiles discoverable, they'll show up here for you to follow.\"\n />\n ) : (\n <div className=\"flex flex-col gap-5\">\n <div className=\"relative w-full max-w-sm\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <Input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search by name, headline, or skills\"\n aria-label=\"Search members\"\n className=\"pl-9\"\n />\n </div>\n\n {visible.length === 0 ? (\n <EmptyState\n icon={<Search className=\"h-9 w-9\" />}\n title=\"No matches on this page\"\n description=\"No members on this page match your search. Try a different term or browse another page.\"\n />\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {visible.map((p) => {\n const isSelf = Boolean(currentUser?.id && p.memberId === currentUser.id);\n const isFollowing = followSet.has(p.memberId);\n const chips = Array.from(new Set([...p.skills, ...p.interests])).slice(0, 4);\n\n return (\n <GlassCard key={p.id} treatment=\"plain\" className=\"flex flex-col p-5\">\n <div className=\"flex items-start gap-3\">\n <Avatar\n name={p.displayName}\n presence={p.presence}\n size=\"lg\"\n fileId={p.avatar?.id}\n />\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`members/${p.memberId}`)}\n className=\"block max-w-full truncate text-left text-base font-semibold text-text-primary hover:underline\"\n >\n {p.displayName}\n </button>\n {p.headline && (\n <p className=\"truncate text-sm text-text-secondary\">{p.headline}</p>\n )}\n <div className=\"mt-1.5\">\n <StatusBadge status={p.presence} />\n </div>\n </div>\n </div>\n\n {chips.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {chips.map((chip) => (\n <span\n key={chip}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n {chip}\n </span>\n ))}\n </div>\n )}\n\n {!isSelf && (\n <div className=\"mt-4 flex items-center justify-end border-t border-border-seam pt-3\">\n {isFollowing ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => toggleFollow(p)}\n disabled={pendingId === p.memberId}\n >\n <Check className=\"mr-1.5 h-4 w-4\" /> Following\n </Button>\n ) : (\n <Button\n size=\"sm\"\n onClick={() => toggleFollow(p)}\n disabled={pendingId === p.memberId}\n >\n <UserPlus className=\"mr-1.5 h-4 w-4\" /> Follow\n </Button>\n )}\n </div>\n )}\n </GlassCard>\n );\n })}\n </div>\n )}\n\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={profiles.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo.cursor)}\n />\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -9,54 +9,63 @@ import { formatEnumLabel as s } from "../../utils/formatters.js";
9
9
  import { ErrorView as c, LoadingView as l } from "../../components/StateViews.js";
10
10
  import { StatusBadge as u } from "../../components/StatusBadge.js";
11
11
  import { TagInput as d } from "../../components/TagInput.js";
12
- import { useState as f } from "react";
13
- import { Save as p } from "lucide-react";
14
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
15
- import { Button as g, GlassCard as _, Input as v, Label as y, Select as b, SelectContent as x, SelectItem as S, SelectTrigger as C, SelectValue as w, Switch as T, Textarea as E } from "@burdenoff/fe-libs/ui";
12
+ import { AvatarUploadField as f } from "../../components/AvatarUploadField.js";
13
+ import { useState as p } from "react";
14
+ import { Save as m } from "lucide-react";
15
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
16
+ import { Button as _, GlassCard as v, Input as y, Label as b, Select as x, SelectContent as S, SelectItem as C, SelectTrigger as w, SelectValue as T, Switch as E, Textarea as D } from "@burdenoff/fe-libs/ui";
16
17
  //#region src/comradecircle/pages/profile/MyProfilePage.tsx
17
- var D = [
18
+ var O = [
18
19
  "ONLINE",
19
20
  "AWAY",
20
21
  "BUSY",
21
22
  "OFFLINE"
22
23
  ];
23
- function O() {
24
+ function k() {
24
25
  let { currentUser: r } = e(), i = r?.id, { data: a, isLoading: o, isError: s, refetch: u } = n(i), d = r?.name ?? r?.email ?? "";
25
- return o ? /* @__PURE__ */ m(t, {
26
+ return o ? /* @__PURE__ */ h(t, {
26
27
  title: "My Profile",
27
- children: /* @__PURE__ */ m(l, { label: "Loading your profile…" })
28
- }) : s ? /* @__PURE__ */ m(t, {
28
+ children: /* @__PURE__ */ h(l, { label: "Loading your profile…" })
29
+ }) : s ? /* @__PURE__ */ h(t, {
29
30
  title: "My Profile",
30
- children: /* @__PURE__ */ m(c, {
31
+ children: /* @__PURE__ */ h(c, {
31
32
  title: "We couldn't load your profile",
32
33
  message: "Something went wrong while fetching your profile. Retry before editing so you don't overwrite saved details.",
33
34
  onRetry: () => u()
34
35
  })
35
- }) : /* @__PURE__ */ m(t, {
36
+ }) : /* @__PURE__ */ h(t, {
36
37
  title: "My Profile",
37
38
  description: "Your profile is your identity across every circle — it's how members recognise, find and follow you.",
38
- children: /* @__PURE__ */ m(k, {
39
+ children: /* @__PURE__ */ h(A, {
39
40
  initialProfile: a ?? null,
40
41
  identityName: d
41
42
  }, a?.id ?? "new")
42
43
  });
43
44
  }
44
- function k({ initialProfile: e, identityName: t }) {
45
- let n = r(), [c, l] = f(e?.displayName ?? t), [O, k] = f(e?.headline ?? ""), [A, j] = f(e?.bio ?? ""), [M, N] = f(e?.interests ?? []), [P, F] = f(e?.skills ?? []), [I, L] = f(e?.presence ?? "ONLINE"), [R, z] = f(e?.discoverable ?? !0), B = c.trim();
46
- return /* @__PURE__ */ h("div", {
45
+ function A({ initialProfile: e, identityName: t }) {
46
+ let n = r(), [c, l] = p(e?.displayName ?? t), [k, A] = p(e?.headline ?? ""), [j, M] = p(e?.bio ?? ""), [N, P] = p(e?.interests ?? []), [F, I] = p(e?.skills ?? []), [L, R] = p(e?.presence ?? "ONLINE"), [z, B] = p(e?.discoverable ?? !0), [V, H] = p(e?.avatar?.id ?? null), U = c.trim();
47
+ return /* @__PURE__ */ g("div", {
47
48
  className: "grid gap-6 lg:grid-cols-[1fr_320px]",
48
- children: [/* @__PURE__ */ m(_, {
49
+ children: [/* @__PURE__ */ h(v, {
49
50
  treatment: "plain",
50
51
  className: "order-2 p-5 sm:p-6 lg:order-1",
51
- children: /* @__PURE__ */ h("div", {
52
+ children: /* @__PURE__ */ g("div", {
52
53
  className: "flex flex-col gap-5",
53
54
  children: [
54
- /* @__PURE__ */ h("div", {
55
+ /* @__PURE__ */ g("div", {
56
+ className: "grid gap-1.5",
57
+ children: [/* @__PURE__ */ h(b, { children: "Avatar" }), /* @__PURE__ */ h(f, {
58
+ fileId: V,
59
+ name: U || t,
60
+ onUploaded: H
61
+ })]
62
+ }),
63
+ /* @__PURE__ */ g("div", {
55
64
  className: "grid gap-1.5",
56
- children: [/* @__PURE__ */ m(y, {
65
+ children: [/* @__PURE__ */ h(b, {
57
66
  htmlFor: "profile-name",
58
67
  children: "Display name"
59
- }), /* @__PURE__ */ m(v, {
68
+ }), /* @__PURE__ */ h(y, {
60
69
  id: "profile-name",
61
70
  value: c,
62
71
  onChange: (e) => l(e.target.value),
@@ -65,94 +74,94 @@ function k({ initialProfile: e, identityName: t }) {
65
74
  "aria-required": "true"
66
75
  })]
67
76
  }),
68
- /* @__PURE__ */ h("div", {
77
+ /* @__PURE__ */ g("div", {
69
78
  className: "grid gap-1.5",
70
- children: [/* @__PURE__ */ m(y, {
79
+ children: [/* @__PURE__ */ h(b, {
71
80
  htmlFor: "profile-headline",
72
81
  children: "Headline"
73
- }), /* @__PURE__ */ m(v, {
82
+ }), /* @__PURE__ */ h(y, {
74
83
  id: "profile-headline",
75
- value: O,
76
- onChange: (e) => k(e.target.value),
84
+ value: k,
85
+ onChange: (e) => A(e.target.value),
77
86
  placeholder: "e.g. Community organiser · Photographer"
78
87
  })]
79
88
  }),
80
- /* @__PURE__ */ h("div", {
89
+ /* @__PURE__ */ g("div", {
81
90
  className: "grid gap-1.5",
82
- children: [/* @__PURE__ */ m(y, {
91
+ children: [/* @__PURE__ */ h(b, {
83
92
  htmlFor: "profile-bio",
84
93
  children: "Bio"
85
- }), /* @__PURE__ */ m(E, {
94
+ }), /* @__PURE__ */ h(D, {
86
95
  id: "profile-bio",
87
- value: A,
88
- onChange: (e) => j(e.target.value),
96
+ value: j,
97
+ onChange: (e) => M(e.target.value),
89
98
  rows: 4,
90
99
  placeholder: "Tell circles a little about yourself."
91
100
  })]
92
101
  }),
93
- /* @__PURE__ */ h("div", {
102
+ /* @__PURE__ */ g("div", {
94
103
  className: "grid gap-1.5",
95
- children: [/* @__PURE__ */ m(y, {
104
+ children: [/* @__PURE__ */ h(b, {
96
105
  htmlFor: "profile-interests",
97
106
  children: "Interests"
98
- }), /* @__PURE__ */ m(d, {
107
+ }), /* @__PURE__ */ h(d, {
99
108
  id: "profile-interests",
100
- value: M,
101
- onChange: N,
109
+ value: N,
110
+ onChange: P,
102
111
  placeholder: "Add an interest…"
103
112
  })]
104
113
  }),
105
- /* @__PURE__ */ h("div", {
114
+ /* @__PURE__ */ g("div", {
106
115
  className: "grid gap-1.5",
107
- children: [/* @__PURE__ */ m(y, {
116
+ children: [/* @__PURE__ */ h(b, {
108
117
  htmlFor: "profile-skills",
109
118
  children: "Skills"
110
- }), /* @__PURE__ */ m(d, {
119
+ }), /* @__PURE__ */ h(d, {
111
120
  id: "profile-skills",
112
- value: P,
113
- onChange: F,
121
+ value: F,
122
+ onChange: I,
114
123
  placeholder: "Add a skill…"
115
124
  })]
116
125
  }),
117
- /* @__PURE__ */ h("div", {
126
+ /* @__PURE__ */ g("div", {
118
127
  className: "grid gap-1.5",
119
- children: [/* @__PURE__ */ m(y, {
128
+ children: [/* @__PURE__ */ h(b, {
120
129
  htmlFor: "profile-presence",
121
130
  children: "Presence"
122
- }), /* @__PURE__ */ h(b, {
123
- value: I,
124
- onValueChange: (e) => L(e),
125
- children: [/* @__PURE__ */ m(C, {
131
+ }), /* @__PURE__ */ g(x, {
132
+ value: L,
133
+ onValueChange: (e) => R(e),
134
+ children: [/* @__PURE__ */ h(w, {
126
135
  id: "profile-presence",
127
- children: /* @__PURE__ */ m(w, {})
128
- }), /* @__PURE__ */ m(x, { children: D.map((e) => /* @__PURE__ */ m(S, {
136
+ children: /* @__PURE__ */ h(T, {})
137
+ }), /* @__PURE__ */ h(S, { children: O.map((e) => /* @__PURE__ */ h(C, {
129
138
  value: e,
130
139
  children: s(e)
131
140
  }, e)) })]
132
141
  })]
133
142
  }),
134
- /* @__PURE__ */ h("div", {
143
+ /* @__PURE__ */ g("div", {
135
144
  className: "flex items-start justify-between gap-4 rounded-xl border border-border-seam bg-bg-surface p-3.5",
136
- children: [/* @__PURE__ */ h("div", {
145
+ children: [/* @__PURE__ */ g("div", {
137
146
  className: "min-w-0",
138
- children: [/* @__PURE__ */ m(y, {
147
+ children: [/* @__PURE__ */ h(b, {
139
148
  htmlFor: "profile-discoverable",
140
149
  children: "List me in the member directory"
141
- }), /* @__PURE__ */ m("p", {
150
+ }), /* @__PURE__ */ h("p", {
142
151
  className: "mt-1 text-xs text-text-tertiary",
143
152
  children: "When on, your profile appears in the discoverable member directory so others can find and follow you. Turn it off to stay unlisted."
144
153
  })]
145
- }), /* @__PURE__ */ m(T, {
154
+ }), /* @__PURE__ */ h(E, {
146
155
  id: "profile-discoverable",
147
- checked: R,
148
- onCheckedChange: z,
156
+ checked: z,
157
+ onCheckedChange: B,
149
158
  "aria-label": "List me in the member directory",
150
159
  className: "mt-0.5 shrink-0"
151
160
  })]
152
161
  }),
153
- /* @__PURE__ */ m("div", {
162
+ /* @__PURE__ */ h("div", {
154
163
  className: "flex justify-end pt-1",
155
- children: /* @__PURE__ */ h(g, {
164
+ children: /* @__PURE__ */ g(_, {
156
165
  onClick: async () => {
157
166
  let e = c.trim();
158
167
  if (!e) {
@@ -162,85 +171,87 @@ function k({ initialProfile: e, identityName: t }) {
162
171
  try {
163
172
  await n.mutateAsync({
164
173
  displayName: e,
165
- headline: O.trim() || null,
166
- bio: A.trim() || null,
167
- interests: M,
168
- skills: P,
169
- presence: I,
170
- discoverable: R
174
+ headline: k.trim() || null,
175
+ bio: j.trim() || null,
176
+ avatarFileId: V,
177
+ interests: N,
178
+ skills: F,
179
+ presence: L,
180
+ discoverable: z
171
181
  }), o.success("Profile saved");
172
182
  } catch (e) {
173
183
  o.error("Could not save profile", a(e));
174
184
  }
175
185
  },
176
186
  disabled: n.isPending,
177
- children: [/* @__PURE__ */ m(p, { className: "mr-1.5 h-4 w-4" }), n.isPending ? "Saving…" : "Save profile"]
187
+ children: [/* @__PURE__ */ h(m, { className: "mr-1.5 h-4 w-4" }), n.isPending ? "Saving…" : "Save profile"]
178
188
  })
179
189
  })
180
190
  ]
181
191
  })
182
- }), /* @__PURE__ */ m("div", {
192
+ }), /* @__PURE__ */ h("div", {
183
193
  className: "order-1 lg:order-2",
184
- children: /* @__PURE__ */ h(_, {
194
+ children: /* @__PURE__ */ g(v, {
185
195
  treatment: "plain",
186
196
  className: "p-5 lg:sticky lg:top-6",
187
197
  children: [
188
- /* @__PURE__ */ m("p", {
198
+ /* @__PURE__ */ h("p", {
189
199
  className: "mb-4 text-xs font-semibold uppercase tracking-wide text-text-tertiary",
190
200
  children: "Preview"
191
201
  }),
192
- /* @__PURE__ */ h("div", {
202
+ /* @__PURE__ */ g("div", {
193
203
  className: "flex flex-col items-center gap-3 text-center",
194
204
  children: [
195
- /* @__PURE__ */ m(i, {
196
- name: B || t,
205
+ /* @__PURE__ */ h(i, {
206
+ name: U || t,
197
207
  size: "xl",
198
- presence: I
208
+ presence: L,
209
+ fileId: V
199
210
  }),
200
- /* @__PURE__ */ h("div", {
211
+ /* @__PURE__ */ g("div", {
201
212
  className: "min-w-0",
202
- children: [/* @__PURE__ */ m("h3", {
213
+ children: [/* @__PURE__ */ h("h3", {
203
214
  className: "truncate text-lg font-semibold text-text-primary",
204
- children: B || "Your name"
205
- }), O.trim() && /* @__PURE__ */ m("p", {
215
+ children: U || "Your name"
216
+ }), k.trim() && /* @__PURE__ */ h("p", {
206
217
  className: "mt-0.5 text-sm text-text-secondary",
207
- children: O.trim()
218
+ children: k.trim()
208
219
  })]
209
220
  }),
210
- /* @__PURE__ */ m(u, { status: I })
221
+ /* @__PURE__ */ h(u, { status: L })
211
222
  ]
212
223
  }),
213
- A.trim() && /* @__PURE__ */ m("p", {
224
+ j.trim() && /* @__PURE__ */ h("p", {
214
225
  className: "mt-4 whitespace-pre-wrap break-words text-center text-sm text-text-secondary",
215
- children: A.trim()
226
+ children: j.trim()
216
227
  }),
217
- M.length > 0 && /* @__PURE__ */ h("div", {
228
+ N.length > 0 && /* @__PURE__ */ g("div", {
218
229
  className: "mt-5",
219
- children: [/* @__PURE__ */ m("p", {
230
+ children: [/* @__PURE__ */ h("p", {
220
231
  className: "mb-1.5 text-xs font-medium text-text-tertiary",
221
232
  children: "Interests"
222
- }), /* @__PURE__ */ m("div", {
233
+ }), /* @__PURE__ */ h("div", {
223
234
  className: "flex flex-wrap gap-1.5",
224
- children: M.map((e) => /* @__PURE__ */ m("span", {
235
+ children: N.map((e) => /* @__PURE__ */ h("span", {
225
236
  className: "rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
226
237
  children: e
227
238
  }, e))
228
239
  })]
229
240
  }),
230
- P.length > 0 && /* @__PURE__ */ h("div", {
241
+ F.length > 0 && /* @__PURE__ */ g("div", {
231
242
  className: "mt-4",
232
- children: [/* @__PURE__ */ m("p", {
243
+ children: [/* @__PURE__ */ h("p", {
233
244
  className: "mb-1.5 text-xs font-medium text-text-tertiary",
234
245
  children: "Skills"
235
- }), /* @__PURE__ */ m("div", {
246
+ }), /* @__PURE__ */ h("div", {
236
247
  className: "flex flex-wrap gap-1.5",
237
- children: P.map((e) => /* @__PURE__ */ m("span", {
248
+ children: F.map((e) => /* @__PURE__ */ h("span", {
238
249
  className: "rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary",
239
250
  children: e
240
251
  }, e))
241
252
  })]
242
253
  }),
243
- !R && /* @__PURE__ */ m("p", {
254
+ !z && /* @__PURE__ */ h("p", {
244
255
  className: "mt-5 text-center text-xs text-text-tertiary",
245
256
  children: "Hidden from the member directory."
246
257
  })
@@ -250,6 +261,6 @@ function k({ initialProfile: e, identityName: t }) {
250
261
  });
251
262
  }
252
263
  //#endregion
253
- export { O as MyProfilePage };
264
+ export { k as MyProfilePage };
254
265
 
255
266
  //# sourceMappingURL=MyProfilePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MyProfilePage.js","names":[],"sources":["../../../../src/comradecircle/pages/profile/MyProfilePage.tsx"],"sourcesContent":["/**\n * MyProfilePage — create or edit the caller's own social profile.\n * @module comradecircle/pages/profile\n *\n * A member's {@link ComProfile} is their identity across every circle — the\n * name, headline, presence and avatar initials that `MemberIdentity` resolves\n * everywhere a `memberId` appears. This page reads the caller's own profile\n * (`comProfile` by `currentUser.id`) and upserts it (`comUpsertProfile`). It\n * works both when a profile already exists (edit) and when none does yet\n * (create). The editor is only mounted once the profile query has settled and\n * seeds its fields via lazy state initialisers (keyed on the profile id), so no\n * state-syncing effect is needed. A live preview card mirrors the form.\n */\n\nimport { useState } from 'react';\nimport { Save } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n GlassCard,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Avatar } from '../../components/Avatar';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { TagInput } from '../../components/TagInput';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useComProfile, useComUpsertProfile } from '../../hooks';\nimport { useComradeCircle } from '../../context';\nimport { formatEnumLabel } from '../../utils';\nimport { notify, describeError } from '../../lib/toast';\nimport type { ComPresence, ComProfile } from '../../types';\n\nconst PRESENCE_OPTIONS: ComPresence[] = ['ONLINE', 'AWAY', 'BUSY', 'OFFLINE'];\n\nexport function MyProfilePage() {\n const { currentUser } = useComradeCircle();\n const memberId = currentUser?.id;\n const { data: profile, isLoading, isError, refetch } = useComProfile(memberId);\n\n // Seed create-mode from the shell identity so the name field is not blank.\n const identityName = currentUser?.name ?? currentUser?.email ?? '';\n\n if (isLoading) {\n return (\n <PageLayout title=\"My Profile\">\n <LoadingView label=\"Loading your profile…\" />\n </PageLayout>\n );\n }\n\n // A missing profile is NOT an error (the resolver returns null → create mode).\n // Only a genuine fetch failure lands here — surface it rather than silently\n // showing a blank form, which an upsert save could use to overwrite an\n // existing profile the caller never actually saw.\n if (isError) {\n return (\n <PageLayout title=\"My Profile\">\n <ErrorView\n title=\"We couldn't load your profile\"\n message=\"Something went wrong while fetching your profile. Retry before editing so you don't overwrite saved details.\"\n onRetry={() => refetch()}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"My Profile\"\n description=\"Your profile is your identity across every circle — it's how members recognise, find and follow you.\"\n >\n <ProfileEditor\n key={profile?.id ?? 'new'}\n initialProfile={profile ?? null}\n identityName={identityName}\n />\n </PageLayout>\n );\n}\n\ninterface ProfileEditorProps {\n initialProfile: ComProfile | null;\n identityName: string;\n}\n\nfunction ProfileEditor({ initialProfile, identityName }: ProfileEditorProps) {\n const upsert = useComUpsertProfile();\n\n const [displayName, setDisplayName] = useState(initialProfile?.displayName ?? identityName);\n const [headline, setHeadline] = useState(initialProfile?.headline ?? '');\n const [bio, setBio] = useState(initialProfile?.bio ?? '');\n const [interests, setInterests] = useState<string[]>(initialProfile?.interests ?? []);\n const [skills, setSkills] = useState<string[]>(initialProfile?.skills ?? []);\n const [presence, setPresence] = useState<ComPresence>(initialProfile?.presence ?? 'ONLINE');\n const [discoverable, setDiscoverable] = useState(initialProfile?.discoverable ?? true);\n\n const previewName = displayName.trim();\n\n const handleSave = async () => {\n const name = displayName.trim();\n if (!name) {\n notify.error('Display name required', 'Add a name so members can recognise you.');\n return;\n }\n try {\n await upsert.mutateAsync({\n displayName: name,\n headline: headline.trim() || null,\n bio: bio.trim() || null,\n interests,\n skills,\n presence,\n discoverable,\n });\n notify.success('Profile saved');\n } catch (err) {\n notify.error('Could not save profile', describeError(err));\n }\n };\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-[1fr_320px]\">\n {/* Form */}\n <GlassCard treatment=\"plain\" className=\"order-2 p-5 sm:p-6 lg:order-1\">\n <div className=\"flex flex-col gap-5\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-name\">Display name</Label>\n <Input\n id=\"profile-name\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n placeholder=\"How members see you\"\n required\n aria-required=\"true\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-headline\">Headline</Label>\n <Input\n id=\"profile-headline\"\n value={headline}\n onChange={(e) => setHeadline(e.target.value)}\n placeholder=\"e.g. Community organiser · Photographer\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-bio\">Bio</Label>\n <Textarea\n id=\"profile-bio\"\n value={bio}\n onChange={(e) => setBio(e.target.value)}\n rows={4}\n placeholder=\"Tell circles a little about yourself.\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-interests\">Interests</Label>\n <TagInput\n id=\"profile-interests\"\n value={interests}\n onChange={setInterests}\n placeholder=\"Add an interest…\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-skills\">Skills</Label>\n <TagInput\n id=\"profile-skills\"\n value={skills}\n onChange={setSkills}\n placeholder=\"Add a skill…\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-presence\">Presence</Label>\n <Select value={presence} onValueChange={(v) => setPresence(v as ComPresence)}>\n <SelectTrigger id=\"profile-presence\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRESENCE_OPTIONS.map((p) => (\n <SelectItem key={p} value={p}>\n {formatEnumLabel(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-start justify-between gap-4 rounded-xl border border-border-seam bg-bg-surface p-3.5\">\n <div className=\"min-w-0\">\n <Label htmlFor=\"profile-discoverable\">List me in the member directory</Label>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n When on, your profile appears in the discoverable member directory so others can\n find and follow you. Turn it off to stay unlisted.\n </p>\n </div>\n <Switch\n id=\"profile-discoverable\"\n checked={discoverable}\n onCheckedChange={setDiscoverable}\n aria-label=\"List me in the member directory\"\n className=\"mt-0.5 shrink-0\"\n />\n </div>\n\n <div className=\"flex justify-end pt-1\">\n <Button onClick={handleSave} disabled={upsert.isPending}>\n <Save className=\"mr-1.5 h-4 w-4\" />\n {upsert.isPending ? 'Saving…' : 'Save profile'}\n </Button>\n </div>\n </div>\n </GlassCard>\n\n {/* Live preview */}\n <div className=\"order-1 lg:order-2\">\n <GlassCard treatment=\"plain\" className=\"p-5 lg:sticky lg:top-6\">\n <p className=\"mb-4 text-xs font-semibold uppercase tracking-wide text-text-tertiary\">\n Preview\n </p>\n <div className=\"flex flex-col items-center gap-3 text-center\">\n <Avatar name={previewName || identityName} size=\"xl\" presence={presence} />\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary\">\n {previewName || 'Your name'}\n </h3>\n {headline.trim() && (\n <p className=\"mt-0.5 text-sm text-text-secondary\">{headline.trim()}</p>\n )}\n </div>\n <StatusBadge status={presence} />\n </div>\n\n {bio.trim() && (\n <p className=\"mt-4 whitespace-pre-wrap break-words text-center text-sm text-text-secondary\">\n {bio.trim()}\n </p>\n )}\n\n {interests.length > 0 && (\n <div className=\"mt-5\">\n <p className=\"mb-1.5 text-xs font-medium text-text-tertiary\">Interests</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {interests.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {skills.length > 0 && (\n <div className=\"mt-4\">\n <p className=\"mb-1.5 text-xs font-medium text-text-tertiary\">Skills</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {skills.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {!discoverable && (\n <p className=\"mt-5 text-center text-xs text-text-tertiary\">\n Hidden from the member directory.\n </p>\n )}\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MyProfilePage.js","names":[],"sources":["../../../../src/comradecircle/pages/profile/MyProfilePage.tsx"],"sourcesContent":["/**\n * MyProfilePage — create or edit the caller's own social profile.\n * @module comradecircle/pages/profile\n *\n * A member's {@link ComProfile} is their identity across every circle — the\n * name, headline, presence and avatar initials that `MemberIdentity` resolves\n * everywhere a `memberId` appears. This page reads the caller's own profile\n * (`comProfile` by `currentUser.id`) and upserts it (`comUpsertProfile`). It\n * works both when a profile already exists (edit) and when none does yet\n * (create). The editor is only mounted once the profile query has settled and\n * seeds its fields via lazy state initialisers (keyed on the profile id), so no\n * state-syncing effect is needed. A live preview card mirrors the form.\n */\n\nimport { useState } from 'react';\nimport { Save } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n GlassCard,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { Avatar } from '../../components/Avatar';\nimport { AvatarUploadField } from '../../components/AvatarUploadField';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { TagInput } from '../../components/TagInput';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useComProfile, useComUpsertProfile } from '../../hooks';\nimport { useComradeCircle } from '../../context';\nimport { formatEnumLabel } from '../../utils';\nimport { notify, describeError } from '../../lib/toast';\nimport type { ComPresence, ComProfile } from '../../types';\n\nconst PRESENCE_OPTIONS: ComPresence[] = ['ONLINE', 'AWAY', 'BUSY', 'OFFLINE'];\n\nexport function MyProfilePage() {\n const { currentUser } = useComradeCircle();\n const memberId = currentUser?.id;\n const { data: profile, isLoading, isError, refetch } = useComProfile(memberId);\n\n // Seed create-mode from the shell identity so the name field is not blank.\n const identityName = currentUser?.name ?? currentUser?.email ?? '';\n\n if (isLoading) {\n return (\n <PageLayout title=\"My Profile\">\n <LoadingView label=\"Loading your profile…\" />\n </PageLayout>\n );\n }\n\n // A missing profile is NOT an error (the resolver returns null → create mode).\n // Only a genuine fetch failure lands here — surface it rather than silently\n // showing a blank form, which an upsert save could use to overwrite an\n // existing profile the caller never actually saw.\n if (isError) {\n return (\n <PageLayout title=\"My Profile\">\n <ErrorView\n title=\"We couldn't load your profile\"\n message=\"Something went wrong while fetching your profile. Retry before editing so you don't overwrite saved details.\"\n onRetry={() => refetch()}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"My Profile\"\n description=\"Your profile is your identity across every circle — it's how members recognise, find and follow you.\"\n >\n <ProfileEditor\n key={profile?.id ?? 'new'}\n initialProfile={profile ?? null}\n identityName={identityName}\n />\n </PageLayout>\n );\n}\n\ninterface ProfileEditorProps {\n initialProfile: ComProfile | null;\n identityName: string;\n}\n\nfunction ProfileEditor({ initialProfile, identityName }: ProfileEditorProps) {\n const upsert = useComUpsertProfile();\n\n const [displayName, setDisplayName] = useState(initialProfile?.displayName ?? identityName);\n const [headline, setHeadline] = useState(initialProfile?.headline ?? '');\n const [bio, setBio] = useState(initialProfile?.bio ?? '');\n const [interests, setInterests] = useState<string[]>(initialProfile?.interests ?? []);\n const [skills, setSkills] = useState<string[]>(initialProfile?.skills ?? []);\n const [presence, setPresence] = useState<ComPresence>(initialProfile?.presence ?? 'ONLINE');\n const [discoverable, setDiscoverable] = useState(initialProfile?.discoverable ?? true);\n const [avatarFileId, setAvatarFileId] = useState<string | null>(\n initialProfile?.avatar?.id ?? null\n );\n\n const previewName = displayName.trim();\n\n const handleSave = async () => {\n const name = displayName.trim();\n if (!name) {\n notify.error('Display name required', 'Add a name so members can recognise you.');\n return;\n }\n try {\n await upsert.mutateAsync({\n displayName: name,\n headline: headline.trim() || null,\n bio: bio.trim() || null,\n avatarFileId,\n interests,\n skills,\n presence,\n discoverable,\n });\n notify.success('Profile saved');\n } catch (err) {\n notify.error('Could not save profile', describeError(err));\n }\n };\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-[1fr_320px]\">\n {/* Form */}\n <GlassCard treatment=\"plain\" className=\"order-2 p-5 sm:p-6 lg:order-1\">\n <div className=\"flex flex-col gap-5\">\n <div className=\"grid gap-1.5\">\n <Label>Avatar</Label>\n <AvatarUploadField\n fileId={avatarFileId}\n name={previewName || identityName}\n onUploaded={setAvatarFileId}\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-name\">Display name</Label>\n <Input\n id=\"profile-name\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n placeholder=\"How members see you\"\n required\n aria-required=\"true\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-headline\">Headline</Label>\n <Input\n id=\"profile-headline\"\n value={headline}\n onChange={(e) => setHeadline(e.target.value)}\n placeholder=\"e.g. Community organiser · Photographer\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-bio\">Bio</Label>\n <Textarea\n id=\"profile-bio\"\n value={bio}\n onChange={(e) => setBio(e.target.value)}\n rows={4}\n placeholder=\"Tell circles a little about yourself.\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-interests\">Interests</Label>\n <TagInput\n id=\"profile-interests\"\n value={interests}\n onChange={setInterests}\n placeholder=\"Add an interest…\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-skills\">Skills</Label>\n <TagInput\n id=\"profile-skills\"\n value={skills}\n onChange={setSkills}\n placeholder=\"Add a skill…\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"profile-presence\">Presence</Label>\n <Select value={presence} onValueChange={(v) => setPresence(v as ComPresence)}>\n <SelectTrigger id=\"profile-presence\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRESENCE_OPTIONS.map((p) => (\n <SelectItem key={p} value={p}>\n {formatEnumLabel(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-start justify-between gap-4 rounded-xl border border-border-seam bg-bg-surface p-3.5\">\n <div className=\"min-w-0\">\n <Label htmlFor=\"profile-discoverable\">List me in the member directory</Label>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n When on, your profile appears in the discoverable member directory so others can\n find and follow you. Turn it off to stay unlisted.\n </p>\n </div>\n <Switch\n id=\"profile-discoverable\"\n checked={discoverable}\n onCheckedChange={setDiscoverable}\n aria-label=\"List me in the member directory\"\n className=\"mt-0.5 shrink-0\"\n />\n </div>\n\n <div className=\"flex justify-end pt-1\">\n <Button onClick={handleSave} disabled={upsert.isPending}>\n <Save className=\"mr-1.5 h-4 w-4\" />\n {upsert.isPending ? 'Saving…' : 'Save profile'}\n </Button>\n </div>\n </div>\n </GlassCard>\n\n {/* Live preview */}\n <div className=\"order-1 lg:order-2\">\n <GlassCard treatment=\"plain\" className=\"p-5 lg:sticky lg:top-6\">\n <p className=\"mb-4 text-xs font-semibold uppercase tracking-wide text-text-tertiary\">\n Preview\n </p>\n <div className=\"flex flex-col items-center gap-3 text-center\">\n <Avatar\n name={previewName || identityName}\n size=\"xl\"\n presence={presence}\n fileId={avatarFileId}\n />\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary\">\n {previewName || 'Your name'}\n </h3>\n {headline.trim() && (\n <p className=\"mt-0.5 text-sm text-text-secondary\">{headline.trim()}</p>\n )}\n </div>\n <StatusBadge status={presence} />\n </div>\n\n {bio.trim() && (\n <p className=\"mt-4 whitespace-pre-wrap break-words text-center text-sm text-text-secondary\">\n {bio.trim()}\n </p>\n )}\n\n {interests.length > 0 && (\n <div className=\"mt-5\">\n <p className=\"mb-1.5 text-xs font-medium text-text-tertiary\">Interests</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {interests.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {skills.length > 0 && (\n <div className=\"mt-4\">\n <p className=\"mb-1.5 text-xs font-medium text-text-tertiary\">Skills</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {skills.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {!discoverable && (\n <p className=\"mt-5 text-center text-xs text-text-tertiary\">\n Hidden from the member directory.\n </p>\n )}\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-comradecircle",
3
- "version": "2026.704.1",
3
+ "version": "2026.704.2",
4
4
  "description": "Burdenoff ComradeCircle Microfrontend",
5
5
  "type": "module",
6
6
  "files": [