Account area
Part of @cobuntu/embed-react: headless, data via props, links through your router.
Three components for a member's account pages: their events, their ledger and
their library. Each takes the payload of one me.* call as it comes back, so
your page fetches on the server and passes the result down. See the
Account area guide for the full page.
My events
<MyEvents
events={await member.me.events()}
eventHref={(e) => `/events/${e.slug ?? e.id}`}
receiptHref={(e) => `/account/orders?txn=${e.transactionId}`}
browseHref="/events"
/>Upcoming and Past tabs, as in the community app. Each tab joins what the member goes to and what they host; an event that is both shows once, marked Hosting. Rows carry the date, time, and a status: Awaiting review for a place the host has not approved yet, Hosting or Hosted, Attended on past events, and Event cancelled. "View receipt" appears only when the event has a purchase.
Profile events
"use client";
<ProfileEvents
events={rows} // me.profile.events(), fetched on your server
isOwner
onOpen={(event, kind) => (kind === "draft" ? openDraftSheet(event) : openManageSheet(event))}
/>The member's own profile Events tab: every event they host on a date timeline (newest day first, undated last), drafts mixed in. For the owner each card shows a listing-state chip, and a status filter appears once there is more than one state to choose from:
| State | From | Chip | Filter |
|---|---|---|---|
LIVE | catalogStatus | Live | Live |
PENDING | catalogStatus | In review | In review |
UNFINISHED | isDraft: true | Draft | Draft |
HIDDEN | catalogStatus | Off-shelf | Off-shelf |
REJECTED, DRAFT | catalogStatus | Unpublished | Unpublished |
DRAFT here means listed nowhere (a finished event without a listing), not an
unfinished one; only isDraft marks a wizard draft. Without isOwner the chips
and the filter are withheld.
A card click calls onOpen(event, kind, state) with kind "draft" (offer
Continue setup, to /events/new?draft=<id>, and Delete, through
DELETE me/drafts/event/:id) or "event" (offer View and Manage). Modifier
clicks follow the card's link instead: eventHref, by default the draft resume
URL or /events/<slug or id>.
Ledger
"use client";
const [filters, setFilters] = useState(EMPTY_LEDGER_FILTERS);
const [offset, setOffset] = useState(0);
// fetch from your server route with toLedgerQuery(filters, { limit: 20, offset })
<Ledger
ledger={ledger}
filters={filters}
onFiltersChange={(f) => { setFilters(f); setOffset(0); }}
offset={offset}
onOffsetChange={setOffset}
invoiceHref={(e) => `/api/invoice/${e.saleId}`}
receiptHref={(e) => `/api/receipt/${e.saleId}`}
/>Summary tiles per currency (spent, refunded, earned, fees), the payout state
(pending, ready, in the holding period), a type select, a date range select, a
search over the rows on the page, and a table of entries. Each entry shows a
signed amount, a status pill, the item, the buyer on a sale, the date, the payout
date on a sale, the refund reason, and Invoice and Receipt links when the entry
has them. The pager shows when you pass onOffsetChange.
toLedgerQuery(filters, { limit, offset, now? }) turns the filter state into
me.ledger params: the range becomes from.
Library
<Library
items={(await member.me.library()).items}
itemHref={(i) => (i.product.sku ? `/shop/${i.product.sku}` : null)}
onDownload={async (i, d) => {
const { url } = await fetch(`/api/download/${i.saleId}/${d.attachmentId}`).then((r) => r.json());
window.location.href = url;
}}
browseHref="/shop"
/>A grid of owned items with image, type, creator, purchase date and downloads.
LINK deliverables open directly; FILE downloads call onDownload, which goes
through your server to me.purchaseDownload. A purchase awaiting the seller's
approval shows dimmed with no downloads. Search, a type select and a status
select appear once there is more than one item, and only when they would divide
the list.
Account props
| Component | Props |
|---|---|
MyEvents | events, loading, tab / defaultTab, onTabChange, eventHref (default /events/<slug or id>), receiptHref, browseHref, renderLink, renderExtra, renderPlaceholder, locale, timeZone, labels (MyEventsLabels) |
ProfileEvents | events, isOwner, loading, status / onStatusChange ("" for all), onOpen, eventHref, renderPlaceholder, renderExtra, leadingEntry, locale, timeZone, labels (ProfileEventsLabels, states per state) |
Ledger | ledger, loading, filters, onFiltersChange, pageSize (default 20), offset, onOffsetChange, invoiceHref, receiptHref, onEntryClick, showPayouts, renderLink, renderExtra, locale, labels (LedgerLabels) |
Library | items, loading, itemHref, onDownload, browseHref, renderLink, renderExtra, locale, labels (LibraryLabels) |
Helpers: mergeMyEvents(groups, tab), profileEventState(row), profileEventBucket(state),
groupProfileEvents(rows), draftResumeHref(row), PROFILE_EVENT_BUCKETS, toLedgerQuery, ledgerEntryStatus(entry),
EMPTY_LEDGER_FILTERS, libraryMatches(item, filters), libraryFacets(items),
EMPTY_LIBRARY_FILTERS, and the default*Labels objects.
Class map
See the class map conventions.
My events
| Class | Element | State attributes |
|---|---|---|
cb-my-events | Root | data-tab="upcoming | past" |
__tabs, __tab | Tabs | data-active, aria-selected |
__list, __skeleton | List, loading rows | aria-busy |
__empty, __empty-title, __empty-hint, __browse | Empty state | |
cb-my-event | One event | data-hosting, data-status="ok | pending | hosting | attended | cancelled" |
__link, __media, __img, __placeholder, __body, __when, __date, __time, __name, __status, __receipt | Row |
Profile events
| Class | Element | State attributes |
|---|---|---|
cb-profile-events | Root | data-owner |
__filter, __filter-option, __filter-count | Status filter (owner) | option: data-bucket, data-active, aria-pressed |
__timeline, __group, __dot, __date, __date-label, __date-day, __cards | Timeline | group: data-first |
__list, __skeleton | Loading rows | aria-busy |
__empty, __empty-title, __empty-hint, __clear, __lead | Empty states | |
cb-profile-event | One event | data-kind="draft | event", data-state="LIVE | PENDING | HIDDEN | REJECTED | DRAFT | UNFINISHED | none" |
__link, __body, __time, __name, __where, __chip, __media, __img, __placeholder | Card | chip also has cb-listing-state and data-state |
Ledger
| Class | Element | State attributes |
|---|---|---|
cb-ledger | Root | aria-busy |
__summary, __totals, __payouts, __tile, __tile-label, __tile-amount | Tiles | totals: data-currency; tile: data-kind="spent | refunded | earned | fees | pending | ready | escrow" |
__filters, __search, __type, __range, __count | Filters | |
__table, __th | Table | th: data-col |
__loading, __empty, __empty-text, __clear | States | |
cb-ledger-entry | One row | data-type="purchase | refund | sale", data-direction="in | out", data-status |
__amount, __sign, __status, __pill, __item, __name, __kind, __reason, __payout, __counterpart, __date, __actions, __invoice, __receipt | Cells | payout: data-payout |
__pager, __prev, __page, __next | Pager | disabled |
Library
| Class | Element | State attributes |
|---|---|---|
cb-library | Root | aria-busy |
__filters, __search, __type, __status, __count | Filters | |
__grid, __skeleton | Grid | |
__empty, __empty-title, __empty-hint, __browse, __no-matches, __no-matches-text, __clear | States | |
cb-library-item | One item | data-pending, data-type |
__link, __media, __img, __placeholder, __type, __name, __creator, __avatar, __meta | Card | |
__downloads, __downloads-title, __download-list, __download, __download-name, __download-action | Downloads | data-kind="file | link" |