@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.
- package/dist/comradecircle/components/AvatarUploadField.js +8 -8
- package/dist/comradecircle/components/CircleCard.js +4 -4
- package/dist/comradecircle/components/CommentThread.d.ts.map +1 -1
- package/dist/comradecircle/components/CommentThread.js +66 -61
- package/dist/comradecircle/components/CommentThread.js.map +1 -1
- package/dist/comradecircle/components/CreateCircleDialog.d.ts.map +1 -1
- package/dist/comradecircle/components/CreateCircleDialog.js +89 -88
- package/dist/comradecircle/components/CreateCircleDialog.js.map +1 -1
- package/dist/comradecircle/components/CreateEventDialog.d.ts.map +1 -1
- package/dist/comradecircle/components/CreateEventDialog.js +28 -27
- package/dist/comradecircle/components/CreateEventDialog.js.map +1 -1
- package/dist/comradecircle/components/DraftsPanel.d.ts +32 -0
- package/dist/comradecircle/components/DraftsPanel.d.ts.map +1 -0
- package/dist/comradecircle/components/DraftsPanel.js +176 -0
- package/dist/comradecircle/components/DraftsPanel.js.map +1 -0
- package/dist/comradecircle/components/EventCard.js +5 -5
- package/dist/comradecircle/components/FileImage.js +5 -5
- package/dist/comradecircle/components/ImageAttachments.js +21 -21
- package/dist/comradecircle/components/MemberIdentity.js +4 -4
- package/dist/comradecircle/components/MobileBottomNav.d.ts.map +1 -1
- package/dist/comradecircle/components/MobileBottomNav.js +37 -30
- package/dist/comradecircle/components/MobileBottomNav.js.map +1 -1
- package/dist/comradecircle/components/Pagination.d.ts.map +1 -1
- package/dist/comradecircle/components/Pagination.js +37 -28
- package/dist/comradecircle/components/Pagination.js.map +1 -1
- package/dist/comradecircle/components/PollCard.js +13 -13
- package/dist/comradecircle/components/PostCard.d.ts.map +1 -1
- package/dist/comradecircle/components/PostCard.js +102 -89
- package/dist/comradecircle/components/PostCard.js.map +1 -1
- package/dist/comradecircle/components/PostComposer.d.ts +11 -5
- package/dist/comradecircle/components/PostComposer.d.ts.map +1 -1
- package/dist/comradecircle/components/PostComposer.js +60 -48
- package/dist/comradecircle/components/PostComposer.js.map +1 -1
- package/dist/comradecircle/components/ReactionBar.d.ts.map +1 -1
- package/dist/comradecircle/components/ReactionBar.js +65 -55
- package/dist/comradecircle/components/ReactionBar.js.map +1 -1
- package/dist/comradecircle/components/StateViews.d.ts.map +1 -1
- package/dist/comradecircle/components/StateViews.js +38 -30
- package/dist/comradecircle/components/StateViews.js.map +1 -1
- package/dist/comradecircle/components/UpcomingEvents.d.ts +26 -0
- package/dist/comradecircle/components/UpcomingEvents.d.ts.map +1 -0
- package/dist/comradecircle/components/UpcomingEvents.js +126 -0
- package/dist/comradecircle/components/UpcomingEvents.js.map +1 -0
- package/dist/comradecircle/components/index.d.ts +2 -0
- package/dist/comradecircle/components/index.d.ts.map +1 -1
- package/dist/comradecircle/hooks/index.d.ts +1 -0
- package/dist/comradecircle/hooks/index.d.ts.map +1 -1
- package/dist/comradecircle/hooks/index.js +1 -0
- package/dist/comradecircle/hooks/useTr.d.ts +18 -0
- package/dist/comradecircle/hooks/useTr.d.ts.map +1 -0
- package/dist/comradecircle/hooks/useTr.js +12 -0
- package/dist/comradecircle/hooks/useTr.js.map +1 -0
- package/dist/comradecircle/pages/FeedPage.d.ts +5 -0
- package/dist/comradecircle/pages/FeedPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/FeedPage.js +76 -62
- package/dist/comradecircle/pages/FeedPage.js.map +1 -1
- package/dist/comradecircle/pages/circles/CircleDetailPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/circles/CircleDetailPage.js +454 -409
- package/dist/comradecircle/pages/circles/CircleDetailPage.js.map +1 -1
- package/dist/comradecircle/pages/circles/CirclesPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/circles/CirclesPage.js +124 -117
- package/dist/comradecircle/pages/circles/CirclesPage.js.map +1 -1
- package/dist/comradecircle/pages/events/EventDetailPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/events/EventDetailPage.js +182 -153
- package/dist/comradecircle/pages/events/EventDetailPage.js.map +1 -1
- package/dist/comradecircle/pages/events/EventsPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/events/EventsPage.js +100 -91
- package/dist/comradecircle/pages/events/EventsPage.js.map +1 -1
- package/dist/comradecircle/pages/members/MemberProfilePage.d.ts.map +1 -1
- package/dist/comradecircle/pages/members/MemberProfilePage.js +172 -159
- package/dist/comradecircle/pages/members/MemberProfilePage.js.map +1 -1
- package/dist/comradecircle/pages/members/MembersPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/members/MembersPage.js +103 -104
- package/dist/comradecircle/pages/members/MembersPage.js.map +1 -1
- package/dist/comradecircle/pages/monetization/MonetizationPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/monetization/MonetizationPage.js +319 -297
- package/dist/comradecircle/pages/monetization/MonetizationPage.js.map +1 -1
- package/dist/comradecircle/pages/posts/PostThreadPage.d.ts.map +1 -1
- package/dist/comradecircle/pages/posts/PostThreadPage.js +37 -32
- package/dist/comradecircle/pages/posts/PostThreadPage.js.map +1 -1
- package/dist/comradecircle/pages/profile/MyProfilePage.d.ts.map +1 -1
- package/dist/comradecircle/pages/profile/MyProfilePage.js +182 -177
- package/dist/comradecircle/pages/profile/MyProfilePage.js.map +1 -1
- package/dist/comradecircle/utils/i18n.js +15 -0
- package/dist/comradecircle/utils/i18n.js.map +1 -0
- package/dist/comradecircle/utils/pageContext.d.ts.map +1 -1
- package/dist/comradecircle/utils/pageContext.js +3 -1
- package/dist/comradecircle/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MonetizationPage.js","names":[],"sources":["../../../../src/comradecircle/pages/monetization/MonetizationPage.tsx"],"sourcesContent":["/**\n * MonetizationPage — creator-facing monetization management console (BOFF-4454).\n * @module comradecircle/pages/monetization\n *\n * Bounded pricing/catalog CATALOG core: a creator manages their own membership\n * tiers, paid events, and digital products (create + archive), and reviews the\n * tip intents they've received. Nothing here talks to a payment processor —\n * `comCreatorHasPaymentsEnabled` always resolves false today, so this page\n * never claims a real checkout exists. Live payment processing and payout/\n * revenue-split accounting are a separate, larger follow-up ticket.\n */\n\nimport { useState, type FormEvent, type ReactNode } from 'react';\nimport { Coins, Gift, Package, Plus, Ticket, Trash2, Users } from 'lucide-react';\nimport { Button, GlassCard, Input, Label, Textarea, Badge } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { ErrorView, LoadingView } from '../../components/StateViews';\nimport {\n useComMyMembershipTiers,\n useComCreateMembershipTier,\n useComArchiveMembershipTier,\n useComMyPaidEvents,\n useComCreatePaidEvent,\n useComUpdatePaidEvent,\n useComArchivePaidEvent,\n useComMyDigitalProducts,\n useComCreateDigitalProduct,\n useComArchiveDigitalProduct,\n useComMyReceivedTips,\n} from '../../hooks';\nimport { notify, describeError } from '../../lib/toast';\nimport { cn } from '../../utils/cn';\nimport type {\n ComCreatorMembershipTier,\n ComPaidEvent,\n ComDigitalProduct,\n ComTipIntent,\n} from '../../types';\n\ntype Tab = 'tiers' | 'events' | 'products' | 'tips';\n\nconst TABS: { id: Tab; label: string; icon: typeof Users }[] = [\n { id: 'tiers', label: 'Membership tiers', icon: Users },\n { id: 'events', label: 'Paid events', icon: Ticket },\n { id: 'products', label: 'Digital products', icon: Package },\n { id: 'tips', label: 'Tips received', icon: Gift },\n];\n\nfunction formatMoney(amount: number, currency: string): string {\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(amount);\n } catch {\n return `${currency} ${amount.toFixed(2)}`;\n }\n}\n\n/** A quiet uppercase section header. */\nfunction SectionLabel({ children }: { children: ReactNode }) {\n return (\n <p className=\"text-xs font-semibold uppercase tracking-[0.08em] text-text-tertiary\">\n {children}\n </p>\n );\n}\n\n/** Every listing row shares this shell: name/title, price, status badge, archive. */\nfunction CatalogRow({\n title,\n subtitle,\n price,\n statusLabel,\n statusTone,\n onArchive,\n archiving,\n archiveLabel = 'Archive',\n}: {\n title: string;\n subtitle?: string | null;\n price: string;\n statusLabel: string;\n statusTone: 'positive' | 'neutral' | 'negative';\n onArchive?: () => void;\n archiving?: boolean;\n archiveLabel?: string;\n}) {\n return (\n <div className=\"flex flex-col gap-3 rounded-xl border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h4 className=\"truncate text-sm font-semibold text-text-primary\">{title}</h4>\n <Badge\n variant={\n statusTone === 'positive'\n ? 'default'\n : statusTone === 'negative'\n ? 'destructive'\n : 'secondary'\n }\n >\n {statusLabel}\n </Badge>\n </div>\n {subtitle && <p className=\"mt-1 truncate text-xs text-text-secondary\">{subtitle}</p>}\n </div>\n <div className=\"flex shrink-0 items-center gap-3\">\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">{price}</span>\n {onArchive && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onArchive} disabled={archiving}>\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" /> {archiveLabel}\n </Button>\n )}\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Membership tiers\n// ---------------------------------------------------------------------------\n\nfunction CreateTierForm({ onDone }: { onDone: () => void }) {\n const create = useComCreateMembershipTier();\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [price, setPrice] = useState('');\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const priceAmount = Number(price);\n if (!name.trim()) {\n notify.error('Name required', 'Give your tier a name.');\n return;\n }\n if (!Number.isFinite(priceAmount) || priceAmount < 0) {\n notify.error('Invalid price', 'Enter a price of 0 or more.');\n return;\n }\n try {\n await create.mutateAsync({\n name: name.trim(),\n description: description.trim() || null,\n priceAmount,\n billingInterval: 'MONTHLY',\n });\n notify.success('Membership tier created');\n setName('');\n setDescription('');\n setPrice('');\n onDone();\n } catch (err) {\n notify.error('Could not create tier', describeError(err));\n }\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className=\"grid gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid gap-3 sm:grid-cols-[2fr_1fr]\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"tier-name\">Tier name</Label>\n <Input\n id=\"tier-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Supporter\"\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"tier-price\">Price / month (USD)</Label>\n <Input\n id=\"tier-price\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n placeholder=\"4.99\"\n required\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"tier-description\">Perks description</Label>\n <Textarea\n id=\"tier-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n placeholder=\"What supporters get at this tier\"\n />\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={create.isPending}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> {create.isPending ? 'Creating…' : 'Create tier'}\n </Button>\n </div>\n </form>\n );\n}\n\nfunction TiersPanel() {\n const { data, isLoading, isError, refetch } = useComMyMembershipTiers();\n const archive = useComArchiveMembershipTier();\n const [showForm, setShowForm] = useState(false);\n const tiers = data?.items ?? [];\n\n const handleArchive = async (tier: ComCreatorMembershipTier) => {\n try {\n await archive.mutateAsync(tier.id);\n notify.success('Tier archived', tier.name);\n } catch (err) {\n notify.error('Could not archive tier', describeError(err));\n }\n };\n\n if (isLoading) return <LoadingView label=\"Loading your membership tiers…\" />;\n if (isError)\n return (\n <ErrorView message=\"We couldn't load your membership tiers.\" onRetry={() => refetch()} />\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <SectionLabel>Your membership tiers</SectionLabel>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowForm((v) => !v)}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" /> New tier\n </Button>\n </div>\n {showForm && <CreateTierForm onDone={() => setShowForm(false)} />}\n {tiers.length === 0 ? (\n <EmptyState\n icon={<Users className=\"h-10 w-10\" />}\n title=\"No membership tiers yet\"\n description=\"Create a tier so fans can support you on a recurring basis.\"\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {tiers.map((tier) => (\n <CatalogRow\n key={tier.id}\n title={tier.name}\n subtitle={tier.description}\n price={`${formatMoney(tier.priceAmount, tier.currency)}/${tier.billingInterval === 'YEARLY' ? 'yr' : 'mo'}`}\n statusLabel={tier.status}\n statusTone={tier.status === 'ACTIVE' ? 'positive' : 'neutral'}\n onArchive={tier.status === 'ACTIVE' ? () => handleArchive(tier) : undefined}\n archiving={archive.isPending}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Paid events\n// ---------------------------------------------------------------------------\n\nfunction CreateEventForm({ onDone }: { onDone: () => void }) {\n const create = useComCreatePaidEvent();\n const [title, setTitle] = useState('');\n const [price, setPrice] = useState('');\n const [capacity, setCapacity] = useState('');\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const priceAmount = Number(price);\n if (!title.trim()) {\n notify.error('Title required', 'Give your event a title.');\n return;\n }\n if (!Number.isFinite(priceAmount) || priceAmount < 0) {\n notify.error('Invalid price', 'Enter a ticket price of 0 or more.');\n return;\n }\n try {\n await create.mutateAsync({\n title: title.trim(),\n priceAmount,\n capacity: capacity.trim() ? Number(capacity) : null,\n });\n notify.success('Paid event created as a draft', 'Publish it once you’re ready.');\n setTitle('');\n setPrice('');\n setCapacity('');\n onDone();\n } catch (err) {\n notify.error('Could not create event', describeError(err));\n }\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className=\"grid gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid gap-3 sm:grid-cols-[2fr_1fr_1fr]\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-title\">Event title</Label>\n <Input\n id=\"event-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-price\">Ticket price (USD)</Label>\n <Input\n id=\"event-price\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-capacity\">Capacity (optional)</Label>\n <Input\n id=\"event-capacity\"\n type=\"number\"\n min=\"1\"\n value={capacity}\n onChange={(e) => setCapacity(e.target.value)}\n />\n </div>\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={create.isPending}>\n <Plus className=\"mr-1.5 h-4 w-4\" />{' '}\n {create.isPending ? 'Creating…' : 'Create event (draft)'}\n </Button>\n </div>\n </form>\n );\n}\n\nfunction EventsPanel() {\n const { data, isLoading, isError, refetch } = useComMyPaidEvents();\n const update = useComUpdatePaidEvent();\n const archive = useComArchivePaidEvent();\n const [showForm, setShowForm] = useState(false);\n const events = data?.items ?? [];\n\n const publish = async (event: ComPaidEvent) => {\n try {\n await update.mutateAsync({ id: event.id, input: { status: 'PUBLISHED' } });\n notify.success('Event published', event.title);\n } catch (err) {\n notify.error('Could not publish event', describeError(err));\n }\n };\n\n const handleArchive = async (event: ComPaidEvent) => {\n try {\n await archive.mutateAsync(event.id);\n notify.success('Event cancelled', event.title);\n } catch (err) {\n notify.error('Could not cancel event', describeError(err));\n }\n };\n\n if (isLoading) return <LoadingView label=\"Loading your paid events…\" />;\n if (isError)\n return <ErrorView message=\"We couldn't load your paid events.\" onRetry={() => refetch()} />;\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <SectionLabel>Your paid events</SectionLabel>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowForm((v) => !v)}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" /> New event\n </Button>\n </div>\n {showForm && <CreateEventForm onDone={() => setShowForm(false)} />}\n {events.length === 0 ? (\n <EmptyState\n icon={<Ticket className=\"h-10 w-10\" />}\n title=\"No paid events yet\"\n description=\"Create a ticketed event — it starts as a draft until you publish it.\"\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {events.map((event) => (\n <div\n key={event.id}\n className=\"flex flex-col gap-3 rounded-xl border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h4 className=\"truncate text-sm font-semibold text-text-primary\">\n {event.title}\n </h4>\n <Badge\n variant={\n event.status === 'PUBLISHED'\n ? 'default'\n : event.status === 'CANCELLED'\n ? 'destructive'\n : 'secondary'\n }\n >\n {event.status}\n </Badge>\n </div>\n {event.capacity != null && (\n <p className=\"mt-1 text-xs text-text-secondary\">Capacity: {event.capacity}</p>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">\n {formatMoney(event.priceAmount, event.currency)}\n </span>\n {event.status === 'DRAFT' && (\n <Button size=\"sm\" onClick={() => publish(event)} disabled={update.isPending}>\n Publish\n </Button>\n )}\n {event.status !== 'CANCELLED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handleArchive(event)}\n disabled={archive.isPending}\n >\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" /> Cancel\n </Button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Digital products\n// ---------------------------------------------------------------------------\n\nfunction CreateProductForm({ onDone }: { onDone: () => void }) {\n const create = useComCreateDigitalProduct();\n const [title, setTitle] = useState('');\n const [price, setPrice] = useState('');\n const [description, setDescription] = useState('');\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const priceAmount = Number(price);\n if (!title.trim()) {\n notify.error('Title required', 'Give your product a title.');\n return;\n }\n if (!Number.isFinite(priceAmount) || priceAmount < 0) {\n notify.error('Invalid price', 'Enter a price of 0 or more.');\n return;\n }\n try {\n await create.mutateAsync({\n title: title.trim(),\n description: description.trim() || null,\n priceAmount,\n productType: 'DOWNLOAD',\n });\n notify.success('Digital product created');\n setTitle('');\n setPrice('');\n setDescription('');\n onDone();\n } catch (err) {\n notify.error('Could not create product', describeError(err));\n }\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className=\"grid gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid gap-3 sm:grid-cols-[2fr_1fr]\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"product-title\">Product title</Label>\n <Input\n id=\"product-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"product-price\">Price (USD)</Label>\n <Input\n id=\"product-price\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n required\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"product-description\">Description</Label>\n <Textarea\n id=\"product-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n placeholder=\"What's included in this download\"\n />\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={create.isPending}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> {create.isPending ? 'Creating…' : 'Create product'}\n </Button>\n </div>\n </form>\n );\n}\n\nfunction ProductsPanel() {\n const { data, isLoading, isError, refetch } = useComMyDigitalProducts();\n const archive = useComArchiveDigitalProduct();\n const [showForm, setShowForm] = useState(false);\n const products = data?.items ?? [];\n\n const handleArchive = async (product: ComDigitalProduct) => {\n try {\n await archive.mutateAsync(product.id);\n notify.success('Product archived', product.title);\n } catch (err) {\n notify.error('Could not archive product', describeError(err));\n }\n };\n\n if (isLoading) return <LoadingView label=\"Loading your digital products…\" />;\n if (isError)\n return (\n <ErrorView message=\"We couldn't load your digital products.\" onRetry={() => refetch()} />\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <SectionLabel>Your digital products</SectionLabel>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowForm((v) => !v)}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" /> New product\n </Button>\n </div>\n {showForm && <CreateProductForm onDone={() => setShowForm(false)} />}\n {products.length === 0 ? (\n <EmptyState\n icon={<Package className=\"h-10 w-10\" />}\n title=\"No digital products yet\"\n description=\"List a downloadable or access-gated product with a price.\"\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {products.map((product) => (\n <CatalogRow\n key={product.id}\n title={product.title}\n subtitle={product.description}\n price={formatMoney(product.priceAmount, product.currency)}\n statusLabel={product.status}\n statusTone={product.status === 'ACTIVE' ? 'positive' : 'neutral'}\n onArchive={product.status === 'ACTIVE' ? () => handleArchive(product) : undefined}\n archiving={archive.isPending}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Tips received\n// ---------------------------------------------------------------------------\n\nfunction TipsPanel() {\n const { data, isLoading, isError, refetch } = useComMyReceivedTips();\n const tips = data?.items ?? [];\n const total = tips.filter((t) => t.status === 'RECORDED').reduce((sum, t) => sum + t.amount, 0);\n\n if (isLoading) return <LoadingView label=\"Loading tips received…\" />;\n if (isError) return <ErrorView message=\"We couldn't load your tips.\" onRetry={() => refetch()} />;\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"rounded-xl border border-border-seam bg-bg-surface p-4\">\n <SectionLabel>Total recorded (not yet payable)</SectionLabel>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {formatMoney(total, tips[0]?.currency ?? 'USD')}\n </p>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n These are recorded tip intents only — payment integration is pending, so no funds have\n actually moved yet.\n </p>\n </div>\n {tips.length === 0 ? (\n <EmptyState\n icon={<Gift className=\"h-10 w-10\" />}\n title=\"No tips recorded yet\"\n description=\"When fans tip you, the recorded intents will show up here.\"\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {tips.map((tip: ComTipIntent) => (\n <div\n key={tip.id}\n className=\"flex items-center justify-between gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatMoney(tip.amount, tip.currency)}\n <span className=\"ml-2 text-xs font-normal text-text-tertiary\">\n {new Date(tip.createdAt).toLocaleDateString()}\n </span>\n </p>\n {tip.message && (\n <p className=\"mt-1 truncate text-xs text-text-secondary\">\n “{tip.message}”\n </p>\n )}\n </div>\n <Badge variant={tip.status === 'RECORDED' ? 'secondary' : 'destructive'}>\n {tip.status}\n </Badge>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function MonetizationPage() {\n const [tab, setTab] = useState<Tab>('tiers');\n\n return (\n <PageLayout\n title=\"Monetization\"\n description=\"Manage the membership tiers, paid events, and digital products you offer — and see the tips you've received. This is a pricing/catalog core: payment integration is pending, so no checkout is live yet.\"\n >\n <GlassCard treatment=\"plain\" className=\"mb-5 flex flex-wrap gap-1 p-1.5\">\n {TABS.map(({ id, label, icon: Icon }) => (\n <button\n key={id}\n type=\"button\"\n onClick={() => setTab(id)}\n className={cn(\n 'flex items-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium transition-colors',\n tab === id\n ? 'bg-[var(--color-accent-soft)] text-accent'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n <Icon className=\"h-4 w-4\" /> {label}\n </button>\n ))}\n </GlassCard>\n\n <div className=\"mb-5 flex items-center gap-2 rounded-xl border border-status-warning-border bg-status-warning-bg-subtle px-4 py-3 text-xs text-status-warning-text\">\n <Coins className=\"h-4 w-4 shrink-0\" />\n Payment integration pending — prices are informational until checkout goes live.\n </div>\n\n {tab === 'tiers' && <TiersPanel />}\n {tab === 'events' && <EventsPanel />}\n {tab === 'products' && <ProductsPanel />}\n {tab === 'tips' && <TipsPanel />}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MonetizationPage.js","names":[],"sources":["../../../../src/comradecircle/pages/monetization/MonetizationPage.tsx"],"sourcesContent":["/**\n * MonetizationPage — creator-facing monetization management console (BOFF-4454).\n * @module comradecircle/pages/monetization\n *\n * Bounded pricing/catalog CATALOG core: a creator manages their own membership\n * tiers, paid events, and digital products (create + archive), and reviews the\n * tip intents they've received. Nothing here talks to a payment processor —\n * `comCreatorHasPaymentsEnabled` always resolves false today, so this page\n * never claims a real checkout exists. Live payment processing and payout/\n * revenue-split accounting are a separate, larger follow-up ticket.\n */\n\nimport { useState, type FormEvent, type ReactNode } from 'react';\nimport { Coins, Gift, Package, Plus, Ticket, Trash2, Users } from 'lucide-react';\nimport { Button, GlassCard, Input, Label, Textarea, Badge } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { ErrorView, LoadingView } from '../../components/StateViews';\nimport {\n useComMyMembershipTiers,\n useComCreateMembershipTier,\n useComArchiveMembershipTier,\n useComMyPaidEvents,\n useComCreatePaidEvent,\n useComUpdatePaidEvent,\n useComArchivePaidEvent,\n useComMyDigitalProducts,\n useComCreateDigitalProduct,\n useComArchiveDigitalProduct,\n useComMyReceivedTips,\n useTr,\n} from '../../hooks';\nimport { notify, describeError } from '../../lib/toast';\nimport { cn } from '../../utils/cn';\nimport type {\n ComCreatorMembershipTier,\n ComPaidEvent,\n ComDigitalProduct,\n ComTipIntent,\n} from '../../types';\n\ntype Tab = 'tiers' | 'events' | 'products' | 'tips';\n\nfunction formatMoney(amount: number, currency: string): string {\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(amount);\n } catch {\n return `${currency} ${amount.toFixed(2)}`;\n }\n}\n\n/** A quiet uppercase section header. */\nfunction SectionLabel({ children }: { children: ReactNode }) {\n return (\n <p className=\"text-xs font-semibold uppercase tracking-[0.08em] text-text-tertiary\">\n {children}\n </p>\n );\n}\n\n/** Every listing row shares this shell: name/title, price, status badge, archive. */\nfunction CatalogRow({\n title,\n subtitle,\n price,\n statusLabel,\n statusTone,\n onArchive,\n archiving,\n archiveLabel,\n}: {\n title: string;\n subtitle?: string | null;\n price: string;\n statusLabel: string;\n statusTone: 'positive' | 'neutral' | 'negative';\n onArchive?: () => void;\n archiving?: boolean;\n archiveLabel: string;\n}) {\n return (\n <div className=\"flex flex-col gap-3 rounded-xl border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h4 className=\"truncate text-sm font-semibold text-text-primary\">{title}</h4>\n <Badge\n variant={\n statusTone === 'positive'\n ? 'default'\n : statusTone === 'negative'\n ? 'destructive'\n : 'secondary'\n }\n >\n {statusLabel}\n </Badge>\n </div>\n {subtitle && <p className=\"mt-1 truncate text-xs text-text-secondary\">{subtitle}</p>}\n </div>\n <div className=\"flex shrink-0 items-center gap-3\">\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">{price}</span>\n {onArchive && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onArchive} disabled={archiving}>\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" /> {archiveLabel}\n </Button>\n )}\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Membership tiers\n// ---------------------------------------------------------------------------\n\nfunction CreateTierForm({ onDone }: { onDone: () => void }) {\n const tr = useTr();\n const create = useComCreateMembershipTier();\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [price, setPrice] = useState('');\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const priceAmount = Number(price);\n if (!name.trim()) {\n notify.error(\n tr('comradecircle.pages.monetization.tierNameRequired', 'Name required'),\n tr('comradecircle.pages.monetization.tierNameRequiredHint', 'Give your tier a name.')\n );\n return;\n }\n if (!Number.isFinite(priceAmount) || priceAmount < 0) {\n notify.error(\n tr('comradecircle.pages.monetization.tierPriceInvalid', 'Invalid price'),\n tr('comradecircle.pages.monetization.tierPriceInvalidHint', 'Enter a price of 0 or more.')\n );\n return;\n }\n try {\n await create.mutateAsync({\n name: name.trim(),\n description: description.trim() || null,\n priceAmount,\n billingInterval: 'MONTHLY',\n });\n notify.success(tr('comradecircle.pages.monetization.tierCreated', 'Membership tier created'));\n setName('');\n setDescription('');\n setPrice('');\n onDone();\n } catch (err) {\n notify.error(\n tr('comradecircle.pages.monetization.tierCreateError', 'Could not create tier'),\n describeError(err)\n );\n }\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className=\"grid gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid gap-3 sm:grid-cols-[2fr_1fr]\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"tier-name\">\n {tr('comradecircle.pages.monetization.tierNameLabel', 'Tier name')}\n </Label>\n <Input\n id=\"tier-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={tr(\n 'comradecircle.pages.monetization.tierNamePlaceholder',\n 'e.g. Supporter'\n )}\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"tier-price\">\n {tr('comradecircle.pages.monetization.tierPriceLabel', 'Price / month (USD)')}\n </Label>\n <Input\n id=\"tier-price\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n placeholder={tr('comradecircle.pages.monetization.tierPricePlaceholder', '4.99')}\n required\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"tier-description\">\n {tr('comradecircle.pages.monetization.tierDescriptionLabel', 'Perks description')}\n </Label>\n <Textarea\n id=\"tier-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n placeholder={tr(\n 'comradecircle.pages.monetization.tierDescriptionPlaceholder',\n 'What supporters get at this tier'\n )}\n />\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={create.isPending}>\n <Plus className=\"mr-1.5 h-4 w-4\" />{' '}\n {create.isPending\n ? tr('comradecircle.pages.monetization.creating', 'Creating…')\n : tr('comradecircle.pages.monetization.createTier', 'Create tier')}\n </Button>\n </div>\n </form>\n );\n}\n\nfunction TiersPanel() {\n const tr = useTr();\n const { data, isLoading, isError, refetch } = useComMyMembershipTiers();\n const archive = useComArchiveMembershipTier();\n const [showForm, setShowForm] = useState(false);\n const tiers = data?.items ?? [];\n\n const handleArchive = async (tier: ComCreatorMembershipTier) => {\n try {\n await archive.mutateAsync(tier.id);\n notify.success(\n tr('comradecircle.pages.monetization.tierArchived', 'Tier archived'),\n tier.name\n );\n } catch (err) {\n notify.error(\n tr('comradecircle.pages.monetization.tierArchiveError', 'Could not archive tier'),\n describeError(err)\n );\n }\n };\n\n if (isLoading)\n return (\n <LoadingView\n label={tr(\n 'comradecircle.pages.monetization.loadingTiers',\n 'Loading your membership tiers…'\n )}\n />\n );\n if (isError)\n return (\n <ErrorView\n message={tr(\n 'comradecircle.pages.monetization.tiersError',\n \"We couldn't load your membership tiers.\"\n )}\n onRetry={() => refetch()}\n />\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <SectionLabel>\n {tr('comradecircle.pages.monetization.tiersSectionLabel', 'Your membership tiers')}\n </SectionLabel>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowForm((v) => !v)}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" />{' '}\n {tr('comradecircle.pages.monetization.newTier', 'New tier')}\n </Button>\n </div>\n {showForm && <CreateTierForm onDone={() => setShowForm(false)} />}\n {tiers.length === 0 ? (\n <EmptyState\n icon={<Users className=\"h-10 w-10\" />}\n title={tr('comradecircle.pages.monetization.noTiersTitle', 'No membership tiers yet')}\n description={tr(\n 'comradecircle.pages.monetization.noTiersDescription',\n 'Create a tier so fans can support you on a recurring basis.'\n )}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {tiers.map((tier) => (\n <CatalogRow\n key={tier.id}\n title={tier.name}\n subtitle={tier.description}\n price={`${formatMoney(tier.priceAmount, tier.currency)}/${tier.billingInterval === 'YEARLY' ? 'yr' : 'mo'}`}\n statusLabel={tier.status}\n statusTone={tier.status === 'ACTIVE' ? 'positive' : 'neutral'}\n onArchive={tier.status === 'ACTIVE' ? () => handleArchive(tier) : undefined}\n archiving={archive.isPending}\n archiveLabel={tr('comradecircle.pages.monetization.archive', 'Archive')}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Paid events\n// ---------------------------------------------------------------------------\n\nfunction CreateEventForm({ onDone }: { onDone: () => void }) {\n const tr = useTr();\n const create = useComCreatePaidEvent();\n const [title, setTitle] = useState('');\n const [price, setPrice] = useState('');\n const [capacity, setCapacity] = useState('');\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const priceAmount = Number(price);\n if (!title.trim()) {\n notify.error(\n tr('comradecircle.pages.monetization.eventTitleRequired', 'Title required'),\n tr('comradecircle.pages.monetization.eventTitleRequiredHint', 'Give your event a title.')\n );\n return;\n }\n if (!Number.isFinite(priceAmount) || priceAmount < 0) {\n notify.error(\n tr('comradecircle.pages.monetization.eventPriceInvalid', 'Invalid price'),\n tr(\n 'comradecircle.pages.monetization.eventPriceInvalidHint',\n 'Enter a ticket price of 0 or more.'\n )\n );\n return;\n }\n try {\n await create.mutateAsync({\n title: title.trim(),\n priceAmount,\n capacity: capacity.trim() ? Number(capacity) : null,\n });\n notify.success(\n tr('comradecircle.pages.monetization.eventCreated', 'Paid event created as a draft'),\n tr('comradecircle.pages.monetization.eventCreatedHint', 'Publish it once you’re ready.')\n );\n setTitle('');\n setPrice('');\n setCapacity('');\n onDone();\n } catch (err) {\n notify.error(\n tr('comradecircle.pages.monetization.eventCreateError', 'Could not create event'),\n describeError(err)\n );\n }\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className=\"grid gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid gap-3 sm:grid-cols-[2fr_1fr_1fr]\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-title\">\n {tr('comradecircle.pages.monetization.eventTitleLabel', 'Event title')}\n </Label>\n <Input\n id=\"event-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-price\">\n {tr('comradecircle.pages.monetization.eventTicketPriceLabel', 'Ticket price (USD)')}\n </Label>\n <Input\n id=\"event-price\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"event-capacity\">\n {tr('comradecircle.pages.monetization.eventCapacityLabel', 'Capacity (optional)')}\n </Label>\n <Input\n id=\"event-capacity\"\n type=\"number\"\n min=\"1\"\n value={capacity}\n onChange={(e) => setCapacity(e.target.value)}\n />\n </div>\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={create.isPending}>\n <Plus className=\"mr-1.5 h-4 w-4\" />{' '}\n {create.isPending\n ? tr('comradecircle.pages.monetization.creating', 'Creating…')\n : tr('comradecircle.pages.monetization.createEventDraft', 'Create event (draft)')}\n </Button>\n </div>\n </form>\n );\n}\n\nfunction EventsPanel() {\n const tr = useTr();\n const { data, isLoading, isError, refetch } = useComMyPaidEvents();\n const update = useComUpdatePaidEvent();\n const archive = useComArchivePaidEvent();\n const [showForm, setShowForm] = useState(false);\n const events = data?.items ?? [];\n\n const publish = async (event: ComPaidEvent) => {\n try {\n await update.mutateAsync({ id: event.id, input: { status: 'PUBLISHED' } });\n notify.success(\n tr('comradecircle.pages.monetization.eventPublished', 'Event published'),\n event.title\n );\n } catch (err) {\n notify.error(\n tr('comradecircle.pages.monetization.eventPublishError', 'Could not publish event'),\n describeError(err)\n );\n }\n };\n\n const handleArchive = async (event: ComPaidEvent) => {\n try {\n await archive.mutateAsync(event.id);\n notify.success(\n tr('comradecircle.pages.monetization.eventCancelled', 'Event cancelled'),\n event.title\n );\n } catch (err) {\n notify.error(\n tr('comradecircle.pages.monetization.eventCancelError', 'Could not cancel event'),\n describeError(err)\n );\n }\n };\n\n if (isLoading)\n return (\n <LoadingView\n label={tr('comradecircle.pages.monetization.loadingEvents', 'Loading your paid events…')}\n />\n );\n if (isError)\n return (\n <ErrorView\n message={tr(\n 'comradecircle.pages.monetization.eventsError',\n \"We couldn't load your paid events.\"\n )}\n onRetry={() => refetch()}\n />\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <SectionLabel>\n {tr('comradecircle.pages.monetization.eventsSectionLabel', 'Your paid events')}\n </SectionLabel>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowForm((v) => !v)}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" />{' '}\n {tr('comradecircle.pages.monetization.newEvent', 'New event')}\n </Button>\n </div>\n {showForm && <CreateEventForm onDone={() => setShowForm(false)} />}\n {events.length === 0 ? (\n <EmptyState\n icon={<Ticket className=\"h-10 w-10\" />}\n title={tr('comradecircle.pages.monetization.noEventsTitle', 'No paid events yet')}\n description={tr(\n 'comradecircle.pages.monetization.noEventsDescription',\n 'Create a ticketed event — it starts as a draft until you publish it.'\n )}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {events.map((event) => (\n <div\n key={event.id}\n className=\"flex flex-col gap-3 rounded-xl border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h4 className=\"truncate text-sm font-semibold text-text-primary\">\n {event.title}\n </h4>\n <Badge\n variant={\n event.status === 'PUBLISHED'\n ? 'default'\n : event.status === 'CANCELLED'\n ? 'destructive'\n : 'secondary'\n }\n >\n {event.status}\n </Badge>\n </div>\n {event.capacity != null && (\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tr(\n 'comradecircle.pages.monetization.eventCapacity',\n 'Capacity: {{capacity}}',\n {\n capacity: event.capacity,\n }\n )}\n </p>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">\n {formatMoney(event.priceAmount, event.currency)}\n </span>\n {event.status === 'DRAFT' && (\n <Button size=\"sm\" onClick={() => publish(event)} disabled={update.isPending}>\n {tr('comradecircle.pages.monetization.publish', 'Publish')}\n </Button>\n )}\n {event.status !== 'CANCELLED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handleArchive(event)}\n disabled={archive.isPending}\n >\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" />{' '}\n {tr('comradecircle.pages.monetization.cancel', 'Cancel')}\n </Button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Digital products\n// ---------------------------------------------------------------------------\n\nfunction CreateProductForm({ onDone }: { onDone: () => void }) {\n const tr = useTr();\n const create = useComCreateDigitalProduct();\n const [title, setTitle] = useState('');\n const [price, setPrice] = useState('');\n const [description, setDescription] = useState('');\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n const priceAmount = Number(price);\n if (!title.trim()) {\n notify.error(\n tr('comradecircle.pages.monetization.productTitleRequired', 'Title required'),\n tr(\n 'comradecircle.pages.monetization.productTitleRequiredHint',\n 'Give your product a title.'\n )\n );\n return;\n }\n if (!Number.isFinite(priceAmount) || priceAmount < 0) {\n notify.error(\n tr('comradecircle.pages.monetization.productPriceInvalid', 'Invalid price'),\n tr(\n 'comradecircle.pages.monetization.productPriceInvalidHint',\n 'Enter a price of 0 or more.'\n )\n );\n return;\n }\n try {\n await create.mutateAsync({\n title: title.trim(),\n description: description.trim() || null,\n priceAmount,\n productType: 'DOWNLOAD',\n });\n notify.success(\n tr('comradecircle.pages.monetization.productCreated', 'Digital product created')\n );\n setTitle('');\n setPrice('');\n setDescription('');\n onDone();\n } catch (err) {\n notify.error(\n tr('comradecircle.pages.monetization.productCreateError', 'Could not create product'),\n describeError(err)\n );\n }\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className=\"grid gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"grid gap-3 sm:grid-cols-[2fr_1fr]\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"product-title\">\n {tr('comradecircle.pages.monetization.productTitleLabel', 'Product title')}\n </Label>\n <Input\n id=\"product-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n required\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"product-price\">\n {tr('comradecircle.pages.monetization.productPriceLabel', 'Price (USD)')}\n </Label>\n <Input\n id=\"product-price\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n required\n />\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"product-description\">\n {tr('comradecircle.pages.monetization.productDescriptionLabel', 'Description')}\n </Label>\n <Textarea\n id=\"product-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n placeholder={tr(\n 'comradecircle.pages.monetization.productDescriptionPlaceholder',\n \"What's included in this download\"\n )}\n />\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={create.isPending}>\n <Plus className=\"mr-1.5 h-4 w-4\" />{' '}\n {create.isPending\n ? tr('comradecircle.pages.monetization.creating', 'Creating…')\n : tr('comradecircle.pages.monetization.createProduct', 'Create product')}\n </Button>\n </div>\n </form>\n );\n}\n\nfunction ProductsPanel() {\n const tr = useTr();\n const { data, isLoading, isError, refetch } = useComMyDigitalProducts();\n const archive = useComArchiveDigitalProduct();\n const [showForm, setShowForm] = useState(false);\n const products = data?.items ?? [];\n\n const handleArchive = async (product: ComDigitalProduct) => {\n try {\n await archive.mutateAsync(product.id);\n notify.success(\n tr('comradecircle.pages.monetization.productArchived', 'Product archived'),\n product.title\n );\n } catch (err) {\n notify.error(\n tr('comradecircle.pages.monetization.productArchiveError', 'Could not archive product'),\n describeError(err)\n );\n }\n };\n\n if (isLoading)\n return (\n <LoadingView\n label={tr(\n 'comradecircle.pages.monetization.loadingProducts',\n 'Loading your digital products…'\n )}\n />\n );\n if (isError)\n return (\n <ErrorView\n message={tr(\n 'comradecircle.pages.monetization.productsError',\n \"We couldn't load your digital products.\"\n )}\n onRetry={() => refetch()}\n />\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <SectionLabel>\n {tr('comradecircle.pages.monetization.productsSectionLabel', 'Your digital products')}\n </SectionLabel>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowForm((v) => !v)}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" />{' '}\n {tr('comradecircle.pages.monetization.newProduct', 'New product')}\n </Button>\n </div>\n {showForm && <CreateProductForm onDone={() => setShowForm(false)} />}\n {products.length === 0 ? (\n <EmptyState\n icon={<Package className=\"h-10 w-10\" />}\n title={tr('comradecircle.pages.monetization.noProductsTitle', 'No digital products yet')}\n description={tr(\n 'comradecircle.pages.monetization.noProductsDescription',\n 'List a downloadable or access-gated product with a price.'\n )}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {products.map((product) => (\n <CatalogRow\n key={product.id}\n title={product.title}\n subtitle={product.description}\n price={formatMoney(product.priceAmount, product.currency)}\n statusLabel={product.status}\n statusTone={product.status === 'ACTIVE' ? 'positive' : 'neutral'}\n onArchive={product.status === 'ACTIVE' ? () => handleArchive(product) : undefined}\n archiving={archive.isPending}\n archiveLabel={tr('comradecircle.pages.monetization.archive', 'Archive')}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Tips received\n// ---------------------------------------------------------------------------\n\nfunction TipsPanel() {\n const tr = useTr();\n const { data, isLoading, isError, refetch } = useComMyReceivedTips();\n const tips = data?.items ?? [];\n const total = tips.filter((t) => t.status === 'RECORDED').reduce((sum, t) => sum + t.amount, 0);\n\n if (isLoading)\n return (\n <LoadingView\n label={tr('comradecircle.pages.monetization.loadingTips', 'Loading tips received…')}\n />\n );\n if (isError)\n return (\n <ErrorView\n message={tr('comradecircle.pages.monetization.tipsError', \"We couldn't load your tips.\")}\n onRetry={() => refetch()}\n />\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"rounded-xl border border-border-seam bg-bg-surface p-4\">\n <SectionLabel>\n {tr('comradecircle.pages.monetization.totalRecorded', 'Total recorded (not yet payable)')}\n </SectionLabel>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {formatMoney(total, tips[0]?.currency ?? 'USD')}\n </p>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n {tr(\n 'comradecircle.pages.monetization.tipsPendingDisclaimer',\n 'These are recorded tip intents only — payment integration is pending, so no funds have actually moved yet.'\n )}\n </p>\n </div>\n {tips.length === 0 ? (\n <EmptyState\n icon={<Gift className=\"h-10 w-10\" />}\n title={tr('comradecircle.pages.monetization.noTipsTitle', 'No tips recorded yet')}\n description={tr(\n 'comradecircle.pages.monetization.noTipsDescription',\n 'When fans tip you, the recorded intents will show up here.'\n )}\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {tips.map((tip: ComTipIntent) => (\n <div\n key={tip.id}\n className=\"flex items-center justify-between gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatMoney(tip.amount, tip.currency)}\n <span className=\"ml-2 text-xs font-normal text-text-tertiary\">\n {new Date(tip.createdAt).toLocaleDateString()}\n </span>\n </p>\n {tip.message && (\n <p className=\"mt-1 truncate text-xs text-text-secondary\">\n “{tip.message}”\n </p>\n )}\n </div>\n <Badge variant={tip.status === 'RECORDED' ? 'secondary' : 'destructive'}>\n {tip.status}\n </Badge>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function MonetizationPage() {\n const tr = useTr();\n const [tab, setTab] = useState<Tab>('tiers');\n\n const TABS: { id: Tab; labelKey: string; fallback: string; icon: typeof Users }[] = [\n {\n id: 'tiers',\n labelKey: 'comradecircle.pages.monetization.tabTiers',\n fallback: 'Membership tiers',\n icon: Users,\n },\n {\n id: 'events',\n labelKey: 'comradecircle.pages.monetization.tabEvents',\n fallback: 'Paid events',\n icon: Ticket,\n },\n {\n id: 'products',\n labelKey: 'comradecircle.pages.monetization.tabProducts',\n fallback: 'Digital products',\n icon: Package,\n },\n {\n id: 'tips',\n labelKey: 'comradecircle.pages.monetization.tabTips',\n fallback: 'Tips received',\n icon: Gift,\n },\n ];\n\n return (\n <PageLayout\n title={tr('comradecircle.pages.monetization.title', 'Monetization')}\n description={tr(\n 'comradecircle.pages.monetization.description',\n \"Manage the membership tiers, paid events, and digital products you offer — and see the tips you've received. This is a pricing/catalog core: payment integration is pending, so no checkout is live yet.\"\n )}\n >\n <GlassCard treatment=\"plain\" className=\"mb-5 flex flex-wrap gap-1 p-1.5\">\n {TABS.map(({ id, labelKey, fallback, icon: Icon }) => (\n <button\n key={id}\n type=\"button\"\n onClick={() => setTab(id)}\n className={cn(\n 'flex items-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium transition-colors',\n tab === id\n ? 'bg-[var(--color-accent-soft)] text-accent'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n <Icon className=\"h-4 w-4\" /> {tr(labelKey, fallback)}\n </button>\n ))}\n </GlassCard>\n\n <div className=\"mb-5 flex items-center gap-2 rounded-xl border border-status-warning-border bg-status-warning-bg-subtle px-4 py-3 text-xs text-status-warning-text\">\n <Coins className=\"h-4 w-4 shrink-0\" />\n {tr(\n 'comradecircle.pages.monetization.paymentPendingBanner',\n 'Payment integration pending — prices are informational until checkout goes live.'\n )}\n </div>\n\n {tab === 'tiers' && <TiersPanel />}\n {tab === 'events' && <EventsPanel />}\n {tab === 'products' && <ProductsPanel />}\n {tab === 'tips' && <TipsPanel />}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostThreadPage.d.ts","sourceRoot":"","sources":["../../../../src/comradecircle/pages/posts/PostThreadPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,wBAAgB,cAAc,
|
|
1
|
+
{"version":3,"file":"PostThreadPage.d.ts","sourceRoot":"","sources":["../../../../src/comradecircle/pages/posts/PostThreadPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,wBAAgB,cAAc,4CAiD7B"}
|
|
@@ -1,43 +1,48 @@
|
|
|
1
1
|
import { useComradeCircle as e } from "../../context/ComradeCircleContext.js";
|
|
2
2
|
import "../../context/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { useComCommentAddedLive as t, useComPost as n } from "../../hooks/useComradeCircleApi.js";
|
|
4
|
+
import { useTr as r } from "../../hooks/useTr.js";
|
|
5
5
|
import "../../hooks/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
7
|
+
import { PostCard as a } from "../../components/PostCard.js";
|
|
8
|
+
import { ErrorView as o, LoadingView as s } from "../../components/StateViews.js";
|
|
9
|
+
import { ArrowLeft as c } from "lucide-react";
|
|
10
|
+
import { useParams as l } from "react-router";
|
|
11
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
12
|
+
import { Button as f } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
//#region src/comradecircle/pages/posts/PostThreadPage.tsx
|
|
13
|
-
function
|
|
14
|
-
let { postId:
|
|
15
|
-
|
|
16
|
-
let
|
|
14
|
+
function p() {
|
|
15
|
+
let p = r(), { postId: m } = l(), { navigateTo: h } = e(), { data: g, isLoading: _, isError: v, refetch: y } = n(m);
|
|
16
|
+
t(m);
|
|
17
|
+
let b = /* @__PURE__ */ d(f, {
|
|
17
18
|
variant: "ghost",
|
|
18
19
|
size: "sm",
|
|
19
|
-
onClick: () =>
|
|
20
|
-
children: [
|
|
20
|
+
onClick: () => h(""),
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ u(c, { className: "mr-1.5 h-4 w-4" }),
|
|
23
|
+
" ",
|
|
24
|
+
p("common.back", "Back")
|
|
25
|
+
]
|
|
21
26
|
});
|
|
22
|
-
return
|
|
23
|
-
title: "Thread",
|
|
24
|
-
actions:
|
|
25
|
-
children: /* @__PURE__ */
|
|
26
|
-
}) :
|
|
27
|
-
title: "Thread",
|
|
28
|
-
actions:
|
|
29
|
-
children: /* @__PURE__ */
|
|
30
|
-
title: "Post not found",
|
|
31
|
-
message: "This post may have been removed, or you may not have access to it.",
|
|
32
|
-
onRetry: () =>
|
|
27
|
+
return _ ? /* @__PURE__ */ u(i, {
|
|
28
|
+
title: p("comradecircle.pages.postThread.title", "Thread"),
|
|
29
|
+
actions: b,
|
|
30
|
+
children: /* @__PURE__ */ u(s, { label: p("comradecircle.pages.postThread.loadingPost", "Loading post…") })
|
|
31
|
+
}) : v || !g ? /* @__PURE__ */ u(i, {
|
|
32
|
+
title: p("comradecircle.pages.postThread.title", "Thread"),
|
|
33
|
+
actions: b,
|
|
34
|
+
children: /* @__PURE__ */ u(o, {
|
|
35
|
+
title: p("comradecircle.pages.postThread.notFoundTitle", "Post not found"),
|
|
36
|
+
message: p("comradecircle.pages.postThread.notFoundMessage", "This post may have been removed, or you may not have access to it."),
|
|
37
|
+
onRetry: () => y()
|
|
33
38
|
})
|
|
34
|
-
}) : /* @__PURE__ */
|
|
35
|
-
title: "Thread",
|
|
36
|
-
actions:
|
|
37
|
-
children: /* @__PURE__ */
|
|
39
|
+
}) : /* @__PURE__ */ u(i, {
|
|
40
|
+
title: p("comradecircle.pages.postThread.title", "Thread"),
|
|
41
|
+
actions: b,
|
|
42
|
+
children: /* @__PURE__ */ u("div", {
|
|
38
43
|
className: "mx-auto max-w-2xl",
|
|
39
|
-
children: /* @__PURE__ */
|
|
40
|
-
post:
|
|
44
|
+
children: /* @__PURE__ */ u(a, {
|
|
45
|
+
post: g,
|
|
41
46
|
defaultExpanded: !0,
|
|
42
47
|
hideThreadLink: !0
|
|
43
48
|
})
|
|
@@ -45,6 +50,6 @@ function f() {
|
|
|
45
50
|
});
|
|
46
51
|
}
|
|
47
52
|
//#endregion
|
|
48
|
-
export {
|
|
53
|
+
export { p as PostThreadPage };
|
|
49
54
|
|
|
50
55
|
//# sourceMappingURL=PostThreadPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostThreadPage.js","names":[],"sources":["../../../../src/comradecircle/pages/posts/PostThreadPage.tsx"],"sourcesContent":["/**\n * PostThreadPage — a dedicated, focused view of a single post and its thread.\n * @module comradecircle/pages/posts\n *\n * BOFF-3955: the mocks (`comradecircle-14-channel-post-thread.png`) show a post\n * opened into its own surface — author, body, reactions, and comments — rather\n * than only the inline collapsible thread inside the channel feed. Reads\n * `comPost(id)` (post + its comments in one round trip) and renders the\n * existing {@link PostCard} (which already composes {@link ReactionBar} +\n * {@link CommentThread} when expanded) so the thread view stays in lock-step\n * with the inline one instead of duplicating rendering logic.\n */\n\nimport { ArrowLeft } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useParams } from 'react-router';\nimport { PageLayout } from '../../components/PageLayout';\nimport { PostCard } from '../../components/PostCard';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useComPost, useComCommentAddedLive } from '../../hooks';\nimport { useComradeCircle } from '../../context';\n\nexport function PostThreadPage() {\n const { postId } = useParams<{ postId: string }>();\n const { navigateTo } = useComradeCircle();\n const { data: post, isLoading, isError, refetch } = useComPost(postId);\n // BOFF-4455: live-update this thread's comments without a manual refresh.\n useComCommentAddedLive(postId);\n\n // Home feed is the one destination every thread is always reachable from\n // (channel feeds link here too), matching the fixed-destination back-button\n // convention already used by EventDetailPage / MemberProfilePage rather\n // than relying on browser history depth, which isn't reliable once this\n // MFE is mounted inside the host shell's own router.\n const backButton = (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n );\n\n if (isLoading) {\n return (\n <PageLayout title
|
|
1
|
+
{"version":3,"file":"PostThreadPage.js","names":[],"sources":["../../../../src/comradecircle/pages/posts/PostThreadPage.tsx"],"sourcesContent":["/**\n * PostThreadPage — a dedicated, focused view of a single post and its thread.\n * @module comradecircle/pages/posts\n *\n * BOFF-3955: the mocks (`comradecircle-14-channel-post-thread.png`) show a post\n * opened into its own surface — author, body, reactions, and comments — rather\n * than only the inline collapsible thread inside the channel feed. Reads\n * `comPost(id)` (post + its comments in one round trip) and renders the\n * existing {@link PostCard} (which already composes {@link ReactionBar} +\n * {@link CommentThread} when expanded) so the thread view stays in lock-step\n * with the inline one instead of duplicating rendering logic.\n */\n\nimport { ArrowLeft } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useParams } from 'react-router';\nimport { PageLayout } from '../../components/PageLayout';\nimport { PostCard } from '../../components/PostCard';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useComPost, useComCommentAddedLive, useTr } from '../../hooks';\nimport { useComradeCircle } from '../../context';\n\nexport function PostThreadPage() {\n const tr = useTr();\n const { postId } = useParams<{ postId: string }>();\n const { navigateTo } = useComradeCircle();\n const { data: post, isLoading, isError, refetch } = useComPost(postId);\n // BOFF-4455: live-update this thread's comments without a manual refresh.\n useComCommentAddedLive(postId);\n\n // Home feed is the one destination every thread is always reachable from\n // (channel feeds link here too), matching the fixed-destination back-button\n // convention already used by EventDetailPage / MemberProfilePage rather\n // than relying on browser history depth, which isn't reliable once this\n // MFE is mounted inside the host shell's own router.\n const backButton = (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> {tr('common.back', 'Back')}\n </Button>\n );\n\n if (isLoading) {\n return (\n <PageLayout title={tr('comradecircle.pages.postThread.title', 'Thread')} actions={backButton}>\n <LoadingView label={tr('comradecircle.pages.postThread.loadingPost', 'Loading post…')} />\n </PageLayout>\n );\n }\n\n if (isError || !post) {\n return (\n <PageLayout title={tr('comradecircle.pages.postThread.title', 'Thread')} actions={backButton}>\n <ErrorView\n title={tr('comradecircle.pages.postThread.notFoundTitle', 'Post not found')}\n message={tr(\n 'comradecircle.pages.postThread.notFoundMessage',\n 'This post may have been removed, or you may not have access to it.'\n )}\n onRetry={() => refetch()}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout title={tr('comradecircle.pages.postThread.title', 'Thread')} actions={backButton}>\n <div className=\"mx-auto max-w-2xl\">\n <PostCard post={post} defaultExpanded hideThreadLink />\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAsBA,SAAgB,IAAiB;CAC/B,IAAM,IAAK,EAAM,GACX,EAAE,cAAW,EAA8B,GAC3C,EAAE,kBAAe,EAAiB,GAClC,EAAE,MAAM,GAAM,cAAW,YAAS,eAAY,EAAW,CAAM;CAErE,EAAuB,CAAM;CAO7B,IAAM,IACJ,kBAAC,GAAD;EAAQ,SAAQ;EAAQ,MAAK;EAAK,eAAe,EAAW,EAAE;YAA9D;GACE,kBAAC,GAAD,EAAW,WAAU,iBAAkB,CAAA;GAAC;GAAE,EAAG,eAAe,MAAM;EAC5D;;CA0BV,OAvBI,IAEA,kBAAC,GAAD;EAAY,OAAO,EAAG,wCAAwC,QAAQ;EAAG,SAAS;YAChF,kBAAC,GAAD,EAAa,OAAO,EAAG,8CAA8C,eAAe,EAAI,CAAA;CAC9E,CAAA,IAIZ,KAAW,CAAC,IAEZ,kBAAC,GAAD;EAAY,OAAO,EAAG,wCAAwC,QAAQ;EAAG,SAAS;YAChF,kBAAC,GAAD;GACE,OAAO,EAAG,gDAAgD,gBAAgB;GAC1E,SAAS,EACP,kDACA,oEACF;GACA,eAAe,EAAQ;EACxB,CAAA;CACS,CAAA,IAKd,kBAAC,GAAD;EAAY,OAAO,EAAG,wCAAwC,QAAQ;EAAG,SAAS;YAChF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD;IAAgB;IAAM,iBAAA;IAAgB,gBAAA;GAAgB,CAAA;EACnD,CAAA;CACK,CAAA;AAEhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MyProfilePage.d.ts","sourceRoot":"","sources":["../../../../src/comradecircle/pages/profile/MyProfilePage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgCH,wBAAgB,aAAa,
|
|
1
|
+
{"version":3,"file":"MyProfilePage.d.ts","sourceRoot":"","sources":["../../../../src/comradecircle/pages/profile/MyProfilePage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgCH,wBAAgB,aAAa,4CA2D5B"}
|