Commit graph

62 commits

Author SHA1 Message Date
6a6f036877 refactor(admin/members): dedupe STATUS_LABELS + reactive row update
Promote inline STATUS_LABELS copies (admin/members/index.vue,
member/account.vue) into app/config/memberStatus.js, matching the
app/config/circles.js pattern. Drive admin/members/[id].vue status
select from the same constant — completes the alignment started in
441a5f5.

Use the softer member-facing copy as canonical: "Paused" / "Closed"
instead of "Suspended" / "Cancelled".

Also fix markSlackInvited's non-reactive Object.assign on a plain
object inside a useFetch array — replace with index-find + element
reassignment so the row UI refreshes without a manual reload.
2026-04-30 22:25:49 +01:00
313b8598df fix(launch-flow): align Slack-wait copy across join, dashboard, welcome email
- /join "How membership works" lists community (not Slack) as a benefit;
  adds a note that Slack invitations come in monthly onboarding waves.
- Dashboard slack-coming note drops "2–3 weeks" timeline; uses the same
  monthly-waves phrasing.
- Welcome email no longer points new members to Slack (which they don't
  yet have access to); directs them to reply instead.
2026-04-30 14:39:47 +01:00
955217a941 chore(admin): rename pending_payment label and tier→contribution
Backlog cleanup from docs/LAUNCH_READINESS.md:
- B4: admin status filter + form options + STATUS_LABELS now read
  "Payment setup incomplete" so admins stop conflating with membership state
- CSV import preview header "Tier" → "Contribution"
- handleUpdateTier → handleUpdateContribution on /member/account
- update-contribution error log "tier" → "amount"
2026-04-29 17:54:53 +01:00
7b326f879d feat(dashboard): one-line note for active members awaiting Slack invite
Renders only when status==='active' && !slackInvited. Hidden for
pending_payment, suspended, cancelled, guest, and any member already
flagged as invited. Lives inside the existing ClientOnly tree at the
top of the dashboard so it never SSRs.

Plain inline text in the welcome region — no banner, no callout. The
2–3 week window is admin-side workflow; the copy avoids cohort/wave
language.
2026-04-29 12:26:51 +01:00
ac5e979c78 feat(payments): persist helcimCustomerCode + skip getOrCreateCustomer on card-on-file 2026-04-27 19:16:32 +01:00
00073ec52c E2e tests
Some checks failed
Test / vitest (push) Successful in 12m20s
Test / playwright (push) Failing after 9m52s
Test / visual (push) Failing after 9m22s
Test / Notify on failure (push) Successful in 2s
2026-04-27 14:51:25 +01:00
0c489cf2c3 style: underline contributor links + timezone select placeholder color
- join.vue: underline links inside .checkbox-label
- profile.vue: underline .posts-empty-link by default; remove hover-only
  underline rule; tint timezone select placeholder via :deep slot
2026-04-26 17:55:54 +01:00
0f841912e2 fix(helcim): skip HelcimPay verify when a card is already on file
Some checks failed
Test / vitest (push) Successful in 11m5s
Test / playwright (push) Failing after 9m18s
Test / visual (push) Failing after 9m24s
Test / Notify on failure (push) Successful in 2s
Helcim refuses paymentType:'verify' for cards already saved on a
customer ("A new card must be entered for saving the payment method"),
breaking every "Complete Payment" retry after a partial-failed signup.

Add GET /api/helcim/existing-card and short-circuit HelcimPay verify in
useMemberPayment + payment-setup.vue when a saved card is found, going
straight to subscription creation. The two existence-check fetches run
in parallel with get-or-create-customer so no extra round-trip latency
in the common path.
2026-04-26 17:27:40 +01:00
335a4db7cc fix(account): show payment history + next-charge for paid-then-$0 members
Three related changes on /member/account:

1. Payment History section now renders when contributionAmount > 0 OR
   past payments exist. Previously a paid member who switched to $0 lost
   visibility of their own past charges.

2. New "Next charge: $X on DATE" row renders above the transaction list
   when nextPaymentDate is available, using --candle dashed border.

3. server/api/helcim/subscription.get.js now reads dateBilling from
   Helcim's GET response and handles data as either object or array.
   Helcim's real shape is {data: {id, dateBilling, ...}} — the old code
   expected {data: [{nextBillingDate}]} and returned empty strings, so
   the Membership-card "Next payment" row never rendered for members
   whose cached date was missing. subscription.post.js and
   update-contribution.post.js have the same wrong field name in their
   CREATE flows; left for a follow-up — the GET refresh masks it.

Manual edit-flow and admin-flow tests also recorded in
docs/LAUNCH_READINESS.md.
2026-04-20 12:36:18 +01:00
a80728f0a8 feat(signup): unify cadence UX across accept-invite, join, and account
Extract shared SignupFlowOverlay component. Static "Monthly Contribution"
label on all three contribution inputs (was misleadingly dynamic).
"Per Year"/"Per Month" toggle copy; Per Year default on accept-invite,
Per Month default on join. Live billing-summary card on both signup
flows. Welcome-heading on dashboard via ?welcome=1 for new signups.
$0-member polish on account page (hide payment-history + Solidarity
Fund prompts). State-aware contribution-change hint. Invite accept now
creates Helcim customer and sets auth cookie server-side for both free
and paid branches. Pre-registrant invite + /join signup flows manually
verified against Cleo Nguyen preReg and $0-$50 variants.
2026-04-20 12:34:59 +01:00
7704557f16 merge: catch up with feature/helcim-plan-consolidation base
# Conflicts:
#	server/api/auth/member.get.js
#	server/api/members/update-contribution.post.js
#	tests/server/api/update-contribution.test.js
2026-04-19 21:33:40 +01:00
dfc03f851b fix(review): accept arbitrary amounts in payment-setup; rename m.tier → m.amount in activity text 2026-04-19 19:15:32 +01:00
b17e006d65 feat(frontend): rename contributionTier → contributionAmount across remaining pages 2026-04-19 19:08:57 +01:00
5ef0cc845f feat(account): replace tier control with amount input + guidance chips 2026-04-19 19:03:08 +01:00
5d6fcdd78d feat(account): show next payment date with lazy Helcim refresh
Persist nextBillingDate on subscription create/update; unset on
cancel or downgrade to free. Account page displays the cached
date and lazily refreshes from Helcim when the cached value is
within 24h of now (or missing).
2026-04-19 18:32:04 +01:00
19c77a3ab6 feat(account): in-app payment history + change card
Add Payment history section (live-read from Helcim, with loading/empty/error states)
and Change card flow (HelcimPay.js zero-dollar auth -> POST /api/helcim/update-card)
to /member/account. Relabel Helcim portal link to "Advanced billing in Helcim →"
and demote it to a secondary link at the bottom of the billing group.
2026-04-19 16:36:19 +01:00
fd9ce5bc2c fix(ui): disambiguate annual tier labels
"$50/yr" was ambiguous — could mean the $5 tier in annual mode or the
$50 tier in monthly mode. On /join the dropdown now shows both prices
("$5/mo → $50/yr") in annual mode. On the account page TierPicker
gains a subtitle slot; annual mode shows "$N/mo tier" beneath the
annual price so members recognize which tier they're on.
2026-04-18 22:06:38 +01:00
fb337a4277 feat(account): display cadence and annual pricing in tier selector 2026-04-18 18:08:10 +01:00
cd0d3f7167 feat(join): cadence selector with annual pricing (monthly×10)
Radio-pair cadence selector (Monthly / Annual) added to the join form,
reusing the existing .circle-radio styling. contributionItems computed
reactively; all tier labels and the left-column price list update on
toggle. cadence submitted with the subscription payload. payment-setup
hardcoded to monthly (annual upgrades go through /join).
2026-04-18 17:59:10 +01:00
c816d4b373 style(payment-setup): drop ColumnsLayout wrapper 2026-04-18 17:06:34 +01:00
37a58cb0eb feat(member): pending_payment retains access, soften status copy
pending_payment now grants the same RSVP/peer-support capabilities as active,
and status banner/label copy is rewritten to be non-threatening ("Setting up
payment", "Paused", "Closed"). Aligns member-facing copy across the account
page with the capability model.
2026-04-18 17:06:22 +01:00
02222a5c16 Copy and layout improvements. 2026-04-16 21:11:05 +01:00
2394248d53 Updates
Some checks failed
Test / vitest (push) Failing after 6m9s
Test / visual (push) Has been skipped
Test / playwright (push) Has been skipped
Test / Notify on failure (push) Successful in 2s
2026-04-15 17:45:09 +01:00
7292b11c0b feat(member): account/profile polish + tier upgrade flow
- Timezone: curated USelectMenu dropdown (app/config/timezones.js), preserves unknown saved values
- Profile save now uses useToast() for success/error; remove inline save banner
- Nav onboarding dot nudged down 1px for optical alignment with lowercase text
- Onboarding: skip a suggestion with POST /api/onboarding/track {skip}; member.onboarding.skipped map; does not affect graduation
- CirclePicker takes :saved-value so 'Current' badge stays until save completes
- PrivacyToggle is binary (USwitch labeled Private); member schema enum reduced to ['members','private']; zod coerces legacy 'public'
- New /member/payment-setup page: HelcimPay $0 verify + update-contribution, wired from account.vue via requiresPaymentSetup redirect
- Helcim portal: NUXT_PUBLIC_HELCIM_PORTAL_URL env + account.vue 'Manage billing in Helcim' link
- Migration script: scripts/migrate-privacy-public-to-members.js
2026-04-14 20:35:37 +01:00
9a560f2a3b feat(board): redesign classifieds + Slack channel creation
Adds AdminGhost bot token for admin-only Slack channel creation, refreshes
BoardPostCard/Form layouts, and expands admin board-channels management.
2026-04-14 20:20:17 +01:00
698f786951 refactor(board): accept refresh params in useBoardPosts mutators 2026-04-14 17:19:48 +01:00
61d33f5db3 feat(board): replace profile board section with posts list 2026-04-14 17:17:09 +01:00
cdef868256 Rename communityEcology → board across frontend, add Board nav, update redirects
- Add Board to exploreItems in AppNavigation
- Update ecology.vue + connections.vue redirects to /board
- Rename all communityEcology refs to board in member profiles, dashboard, admin, onboarding
- Update API path /api/members/me/community-ecology → /api/members/me/board
2026-04-14 12:15:51 +01:00
59d6e97787 Member/Ecology revamp.
Some checks failed
Test / vitest (push) Failing after 7m23s
Test / playwright (push) Has been skipped
Test / visual (push) Has been skipped
Test / Notify on failure (push) Successful in 2s
2026-04-14 09:25:09 +01:00
c6b970a621 Design token updates.
Some checks failed
Test / vitest (push) Successful in 10m47s
Test / playwright (push) Failing after 9m11s
Test / visual (push) Failing after 9m11s
Test / Notify on failure (push) Successful in 2s
2026-04-11 23:24:38 +01:00
f585fabf21 Merge branch 'worktree-agent-a975576d' 2026-04-09 22:46:52 +01:00
7c3a10232d feat(onboarding): add tracking calls to event, ecology, and wiki pages 2026-04-09 22:46:41 +01:00
3ce559a24c feat(onboarding): integrate widget into dashboard and sidebar indicator 2026-04-09 22:46:39 +01:00
b234b8483f refactor(peer-support): update remaining UI references (Phase 2)
Dashboard: replace "Peer Support" card and "Book a peer session"
quick action with community ecology links.

Welcome: replace "Peer Support" resource card with "Community
Ecology" heading and link to /ecology.

Members index: rename active filter tag from "Offering Peer
Support" to "Offering Support". The underlying filter still works
correctly (queries communityEcology.offerPeerSupport).
2026-04-09 09:16:40 +01:00
0b3896d984 refactor(community): rename Community Connections → Community Ecology
Some checks failed
Test / vitest (push) Successful in 11m42s
Test / playwright (push) Failing after 9m27s
Test / visual (push) Failing after 9m53s
Test / Notify on failure (push) Successful in 2s
Simplify the feature to pure discovery (filter by topic, see matching
members, copy Slack handle). Drop the connection request/confirm flow
entirely — Connection model, 7 API endpoints, useConnections composable,
and TagInput component deleted.

- Rename communityConnections → communityEcology in schema, API, pages
- Delete legacy fields: offering, lookingFor, peerSupport
- New /ecology page, /api/ecology/suggestions, community-ecology.patch
- Nav: "Connections" → "Ecology", remove pending-count badge
- Fix auth/member.get.js missing craftTags + communityEcology
- Add community_ecology_updated activity log type
- Expose slackHandle conditionally when offerPeerSupport is true
- Add migration script at scripts/migrate-to-ecology.js (run before deploy)
2026-04-09 09:07:15 +01:00
3b5b0d831d refactor: phase 4 cleanup — delete SidebarLayout, drop dashboard-body
Now that all member-area pages have migrated to PageShell +
ColumnsLayout, SidebarLayout has no consumers and can be deleted.
PageShell owns the flex chain, so the .dashboard-body wrapper on
member/dashboard.vue (flex: 1; display: flex; flex-direction: column;
min-height: 0) is redundant. Update stale SidebarLayout comments on
members/[id].vue to reference ColumnsLayout.
2026-04-08 17:58:01 +01:00
4a5b129eeb fix(profile,account): wrap auth-conditional UI in ClientOnly
Vue hydration silently drops class attribute updates when SSR and client
render different branches of a v-if chain — per the project's Auth SSR
Pattern, useAuth is client-only and server always renders unauthenticated,
so PageHeader (v-else branch) was rendering inside a leftover .loading /
.loading-state div from the v-else-if branch. On mobile that div was
being masked by the visual-test commonMasks (.loading-state), producing
a large fuchsia block in the snapshot.

Wrapping the v-if/v-else-if/v-else chain in <ClientOnly> ensures the
server renders nothing for the auth-gated content and the client performs
a clean first render, matching the pattern already used in dashboard.vue.

Also update admin-dashboard-desktop for minor anti-aliasing drift.
2026-04-08 17:41:01 +01:00
8365feb970 refactor(profile): migrate member/profile to PageShell vocabulary
- Replace .profile-page wrapper + nested form with <PageShell as=form @submit.prevent>
- Replace .profile-columns grid with <ColumnsLayout cols=2> + named slots
- Replace all 5 .profile-col-inset wrappers with <PageSection> components
- Replace <hr class=section-divider> separators with <PageSection divider=top>
- Add type=button to Sign In CTA (prevent accidental form submit)
- Delete .profile-page, .profile-authenticated, .page-content, .profile-main, .profile-columns, .profile-col-left/right, .profile-col-inset asymmetric padding, and collapse rules
2026-04-08 17:10:21 +01:00
37fceac3fd refactor(account): migrate member/account to PageShell vocabulary
- Replace .member-account-page + .account-authenticated flex chains with <PageShell>
- Replace <SidebarLayout> with <ColumnsLayout cols=events-sidebar>
- Replace .account-columns grid with nested <ColumnsLayout cols=2> + named slots
- Replace all 5 .account-col-inset wrappers with <PageSection> components
- Rename .account-section--danger → .danger-section
- Delete ~75 lines of layout CSS (flex chains, grid, asymmetric paddings, collapse rules)
2026-04-08 17:07:30 +01:00
b93c8c7b2f refactor(dashboard): migrate member/dashboard to PageShell + ColumnsLayout
- Replace outer .dashboard wrapper with <PageShell>
- Replace <SidebarLayout> with <ColumnsLayout cols=events-sidebar :limit=5>
- Replace bespoke <div class=welcome> with <PageHeader> containing slotted meta
- Drop .dashboard and .welcome scoped CSS (flex chain + bespoke header)
- Update visual snapshots (welcome header now uses canonical PageHeader padding)
2026-04-08 16:59:26 +01:00
eb2544a42d refactor(activity): migrate to PageShell + ColumnsLayout
Replace .activity-page wrapper + SidebarLayout with PageShell +
ColumnsLayout cols="events-sidebar". Delete the now-redundant
flex-chain CSS that PageShell owns. Adds .loading-state to the
visual mask list to handle the connections-mobile race between
the loading and loaded states.
2026-04-08 16:45:12 +01:00
4271ed0c6f fix: add auth middleware to profile page and update visual snapshots
- Add `middleware: 'auth'` to member/profile.vue (was missing)
- Harden loginAsAdmin helper to wait for networkidle after redirect so
  auth-init plugin and admin middleware finish before tests navigate
- Regenerate visual baselines to reflect updated profile page UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 15:00:23 +01:00
689548e389 Merge feature/community-connections into main
Adds Community Connections system: predefined tags with engagement states,
suggested connections page, and member discovery based on shared interests.
2026-04-05 17:05:58 +01:00
bd07172093 fix: add connectionRequests to notification schema, remove dead notifyPeerRequests 2026-04-05 16:31:49 +01:00
2aa29ba64b feat: restructure profile page for community connections
Replace Skills Exchange section with CraftTagSelector in About You.
Replace Peer Support section with Community Connections using
CooperativeTagSelector. Update form data, load/save logic, and
notifications to use new field names with backward-compatible
fallbacks to old peerSupport data.
2026-04-05 16:28:34 +01:00
88c94aaaf4 Accessibility fixes.
Some checks are pending
Test / vitest (push) Waiting to run
Test / playwright (push) Blocked by required conditions
Test / visual (push) Blocked by required conditions
2026-04-05 16:03:10 +01:00
0ae18f495e Tests, UX improvements. 2026-04-05 14:25:29 +01:00
418d3cc402 UI/UX tweaks and improvements. 2026-04-05 12:28:41 +01:00
d31b5b4dac fix: use private helcimApiToken for all server-side Helcim API calls 2026-04-04 13:37:34 +01:00
fcd6f4cdf4 feat: reskin admin pages to zine design system
Migrate the entire admin section from the dark guild-* Tailwind theme
to the zine design system (dashed borders, CSS custom properties,
Brygada 1918 + Commit Mono, cream/dark mode palette).

- Replace admin top-nav layout with sidebar matching default layout
- Reskin dashboard, members, events, series management pages
- Reskin events/create and series/create form pages
- Add dev-only test login endpoint (GET /api/dev/test-login)
- Redirect duplicate admin/dashboard.vue to /admin
- Update CLAUDE.md design system docs
2026-04-03 10:56:01 +01:00