Commit graph

419 commits

Author SHA1 Message Date
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
15329e3e84 refactor(events): gate member benefits on hasMemberAccess
Extracts hasMemberAccess(member) in tickets.js and uses it across event
registration, ticket purchase, and series purchase flows so guest, suspended,
and cancelled records no longer count as members while pending_payment still
does.
2026-04-18 17:06:17 +01:00
c5e901ed24 feat(signup): community guidelines agreement and policies routes
Introduces /community-guidelines and /policies/{privacy,terms,[slug]} pages,
swaps the signup/invite checkbox from agreedToTerms to agreedToGuidelines,
adds Member.agreement.acceptedAt, and stamps the field when a Helcim
customer is created.
2026-04-18 17:06:10 +01:00
e0d11e47f4 chore: remove admin series-management stub actions 2026-04-17 17:27:27 +01:00
2c834da40a chore: remove placeholder payment block from members/create 2026-04-17 17:16:32 +01:00
3ba633cce2 chore: remove dead guest-register event route
The /api/events/[id]/guest-register endpoint has no production
callers: it's superseded by tickets/purchase.post.js, which
handles guest Member upsert via status:"guest" when
body.createAccount is true. Drops the route file, its
source-assertion tests, guestRegisterSchema, and its validation
coverage.
2026-04-17 16:36:34 +01:00
5fb2f18cab test: align board-channels and wiki-sync mocks with current source
Some checks failed
Test / vitest (push) Successful in 12m0s
Test / playwright (push) Failing after 10m1s
Test / visual (push) Failing after 9m30s
Test / Notify on failure (push) Successful in 2s
board-channels: source renamed getSlackServiceNoVetting → getSlackAdminService.
wiki-sync: syncWikiArticles now also calls fetchCollections; URLs starting
with / are normalized to https://wiki.ghostguild.org.
2026-04-17 09:50:50 +01:00
e96d493024 Merge branch 'feature/guest-event-accounts' 2026-04-17 09:36:11 +01:00
b6f6c95c3b Helcim testing config changes 2026-04-17 09:36:01 +01:00
6f9e6a3d98 feat(events): guest accounts for public event registration
Non-members who register for an event now get a persistent identity:
with consent, a status:"guest" Member is upserted and an auth cookie is
set so the "You're Registered" state survives a page refresh.

Tiered auto-login matches passwordless-auth norms — auto-login is only
safe when the account holds no privileges:
- New email → create guest + cookie
- Returning guest → cookie
- Existing non-guest (active/pending/etc.) → attach ticket only, no
  cookie, confirmation email includes a sign-in link

Guests are gated on status === "guest", so admin/middleware code that
keys on status === "active" naturally excludes them. Guests are also
treated as non-members for ticket pricing/validation to prevent picking
up member-only pricing on their second registration.
2026-04-16 21:23:31 +01:00
7e7672d52b New SiteContent. 2026-04-16 21:11:14 +01:00
02222a5c16 Copy and layout improvements. 2026-04-16 21:11:05 +01:00
39eb9e039a fix(auth): auto-submit OIDC logout form to eliminate xsrf desync
Some checks failed
Test / vitest (push) Failing after 6m9s
Test / playwright (push) Has been skipped
Test / visual (push) Has been skipped
Test / Notify on failure (push) Successful in 2s
Users clicking sign-out in the wiki were getting 'xsrf token invalid'.
The old logoutSource extracted the xsrf from oidc-provider's form into
a separate short-lived cookie and bounced through /auth/logout-confirm,
but that dance kept desyncing — the xsrf on the eventual submit didn't
always match the session state on /oidc/session/end/confirm.

Drop the custom confirmation page and auto-submit oidc-provider's own
form inline from logoutSource. The xsrf stays inside the original form
HTML the provider generated, so the validation is guaranteed to match.
Clicking sign-out in the wiki is already confirmation enough.

Also clear the Ghost Guild auth-token cookie in postLogoutSuccessSource
so signing out of the wiki fully signs the user out rather than leaving
a stale ghostguild.org session behind.
2026-04-15 18:26:51 +01:00
3ad22a8b67 fix(auth): survive missing OIDC interaction cookie on magic-link click
Some checks failed
Test / vitest (push) Failing after 6m13s
Test / visual (push) Has been skipped
Test / playwright (push) Has been skipped
Test / Notify on failure (push) Successful in 3s
Clicking the wiki magic-link email was producing SessionNotFound:
'interaction session id cookie not found' from
provider.interactionFinished, because that call requires the short-lived
_interaction cookie to be present on the request. It isn't, when:

- the user clicks the email on a different device or browser
- the interaction cookie already expired
- the user is in private/incognito browsing

Those unhandled errors previously bounced to /coming-soon via the
coming-soon middleware, stranding users on the pre-register page.

Instead of relying on the interaction cookie at the magic-link step:

1. Verify the JWT, look up the member, set the auth-token cookie.
2. Redirect the user back to https://wiki.ghostguild.org.
3. Outline re-initiates OIDC, which creates a fresh interaction whose
   cookie IS present on the same request, and [uid].get.ts SSOs the user
   in via the auth-token cookie we just set.

Also swap the createError throws for sendRedirect to /auth/oidc-error so
token/member/status failures land on the styled error page rather than
Nitro's default unhandled-error response.
2026-04-15 18:18:33 +01:00
1e9e9c4d97 fix(auth): stop wiki login loop to coming-soon and surface non-member state
Some checks failed
Test / vitest (push) Failing after 6m9s
Test / playwright (push) Has been skipped
Test / visual (push) Has been skipped
Test / Notify on failure (push) Successful in 2s
Members (and pre-registrants) hitting wiki.ghostguild.org were getting bounced
to /coming-soon with a "Pre-Register" link, even when the OIDC flow was
working correctly.

- Allowlist /auth/oidc-error, /auth/logout-confirm, /auth/logout-success,
  and /verify in the coming-soon middleware so OIDC errors and main-site
  magic links stop redirecting to the pre-register page.
- Raise OIDC Interaction TTL from 10m to 15m so it outlives the magic-link
  JWT and legitimate members don't hit expired-interaction errors when they
  click the email a few minutes late.
- Differentiate the "email isn't a registered member" response on the wiki
  login route and show a dedicated "Not a member yet" state with a
  pre-register link and contact email, instead of the misleading
  "Check your inbox" that silently failed.
2026-04-15 17:55:55 +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
28040f44f4 refactor(board): atomic delete + query limit + composable cleanup
Some checks failed
Test / vitest (push) Failing after 7m17s
Test / playwright (push) Has been skipped
Test / visual (push) Has been skipped
Test / Notify on failure (push) Successful in 1s
Delete uses findOneAndDelete with author match (no TOCTOU window);
existence check only runs on miss to distinguish 403 vs 404. Posts
list capped at 200. Drop unused resolveTagChannel and refreshParams;
route slack URL building through the composable's slackUrl helper.
2026-04-15 12:47:53 +01:00
d1a1484daf chore(gitignore): ignore .claude directory 2026-04-15 12:47:47 +01:00
f691f095dc feat(board): inline delete confirmation + a11y polish
Some checks failed
Test / vitest (push) Failing after 6m2s
Test / playwright (push) Has been skipped
Test / visual (push) Has been skipped
Test / Notify on failure (push) Successful in 2s
Replace window.confirm with an inline Delete? / Cancel / Confirm flow on
post cards. Add focus-visible outlines, initials in avatar placeholders,
and promote post/form titles from h3 to h2 for heading order.
2026-04-14 22:15:50 +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
08fc3884da Merge branch 'board-classifieds-redesign'
Some checks failed
Test / vitest (push) Failing after 6m5s
Test / playwright (push) Has been skipped
Test / visual (push) Has been skipped
Test / Notify on failure (push) Successful in 2s
2026-04-14 20:20:31 +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
6f3d088763 fix(board): surface delete errors via toast 2026-04-14 17:38:32 +01:00
5fb069a80e test(board): unit + e2e tests for board posts and channels 2026-04-14 17:36:12 +01:00
f3df1945bd chore(board): remove old board tests, update seed + onboarding tests 2026-04-14 17:31:46 +01:00
7707068f36 feat(board): admin page for managing board channels 2026-04-14 17:27:06 +01:00
4b3ba411dd fix(board): unwrap API envelope in composables, isolate member profile fetch 2026-04-14 17:24:30 +01:00
f8bc5502ba feat(board): replace board/peer support with posts list on member profile 2026-04-14 17:22:04 +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
5bdc3244bd fix(board): handle submit errors + tolerate tag fetch failure 2026-04-14 17:14:22 +01:00
c06cdd71fd feat(board): rewrite board.vue with classifieds layout 2026-04-14 17:11:45 +01:00
4d9eb3c198 fix(board): address review feedback on components 2026-04-14 17:08:52 +01:00
33d27c5d9e feat(board): BoardPostCard, BoardPostForm, simplify CooperativeTagSelector 2026-04-14 17:06:25 +01:00
78db4be7ba feat: add useBoardPosts + useBoardChannels composables, remove useBoard
- useBoardPosts: CRUD with useState('board.posts','board.loading')
- useBoardChannels: fetch + resolveTagChannel + slackUrl helpers
- useBoard.js removed (old suggestions wrapper); only app/pages/board.vue still imports it, will be rewritten in Phase 5
2026-04-14 17:02:07 +01:00
1fc937a26a refactor(board): delete old board routes, absorb slackHandle into profile PATCH
- Delete server/api/members/me/board.patch.js and server/api/board/suggestions.get.js
- Add boardSlackHandle to memberProfileUpdateSchema; remove boardPrivacy
- profile.patch.js: write boardSlackHandle -> board.slackHandle; drop boardPrivacy
- Remove privacy.board field from Member model
- onboarding/status.get.js: hasProfileTags now requires only craftTags; hasEngagedBoard uses BoardPost.exists
- onboarding/track.post.js: graduation check uses BoardPost.exists instead of board.topics elemMatch
- members/[id].get.js and directory.get.js: reduce board response to slackHandle only; drop connectionTag and peerSupport filters
2026-04-14 16:29:45 +01:00
6a440a846d feat: board post + channel API routes
Implements Phase 2a of board classifieds redesign:

- GET/POST /api/board/posts (list with tag/author filters, create)
- PATCH/DELETE /api/board/posts/:id (author-only)
- GET /api/board/channels (member)
- POST /api/admin/board-channels (admin)
- PATCH/DELETE /api/admin/board-channels/:id (admin)

Adds board_post_created activity type.
2026-04-14 16:25:42 +01:00
8e5f4a2d7c add unique index on slackChannelId in BoardChannel model 2026-04-14 16:23:23 +01:00
1da59021a3 feat(board): add BoardPost + BoardChannel models and zod schemas
- Add BoardPost model (author, title, seeking/offering, note, tags) with
  validator requiring at least one of seeking/offering
- Add BoardChannel model (name, slackChannelId, tagSlugs)
- Add boardPost/boardChannel create+update Zod schemas
- Trim Member.board subdoc to only slackHandle (drop topics, details,
  offerPeerSupport, availability, personalMessage)
- Remove old boardUpdateSchema
2026-04-14 16:21:04 +01:00
19d519b153 Event fixes 2026-04-14 16:17:55 +01:00
707447fc88 spec: board classifieds redesign
Replace passive tag-matching with active classifieds posts.
Corkboard/zine card UI, Slack topic channel integration,
admin channel mapping, simplified profile board section.
2026-04-14 15:09:40 +01:00
a0f60bcdc0 fix: rename hasEngagedEcology → hasEngagedBoard in onboarding status, clean up stale ecology references 2026-04-14 12:25:24 +01:00
74b2287d48 feat: update tests + seed script, add ecology→board migration
- useOnboarding.test.js: hasEngagedEcology→hasEngagedBoard, /api/ecology/suggestions→/api/board/suggestions, ecology key/route→board in test assertions
- onboarding-status.test.js: stale description strings updated
- seed-welcome-tester.cjs: communityEcology→board, ecologyPageVisited→boardPageVisited
- migrate-ecology-to-board.cjs: one-time migration renames three member fields and activity log action values
2026-04-14 12:20:46 +01:00
49c54764c6 rename ecologyTopics → boardTopics in member detail page 2026-04-14 12:18:16 +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
3e5cedb1a6 refactor(board): rename ecology-prefixed vars to board-prefixed, remove duplicate count div
- Renamed ecologyTagOptions, ecologyFilterTags, ecologyTagLabel → board* throughout refs, computed, helpers, and template
- Removed .filter-bar div (duplicate count display)
- Updated pageSubtitle to use filteredSuggestions.length so subtitle reflects active tag filtering
2026-04-14 12:11:47 +01:00
f43fff0ba0 Extract ecology view into standalone /board page, simplify members to directory-only
- Create app/pages/board.vue with ecology suggestions, tag filtering, clipboard
- Create app/composables/useBoard.js (calls /api/board/suggestions)
- Delete app/composables/useEcology.js
- Strip all ecology code from members/index.vue (view toggle, ecology state,
  ecology template, ecology styles, conditional computeds)
2026-04-14 12:08:58 +01:00
091ec58073 rename communityEcology → board across backend
Model, schemas, API routes, activity log, and all server handlers
updated. Old ecology/ and community-ecology routes removed, new
board/ routes added. Tests updated and new board-suggestions tests
written (10 cases).
2026-04-14 12:00:15 +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
fc7ec52574 restrict members page to authenticated users only, remove public access 2026-04-13 22:26:14 +01:00