diff --git a/docs/superpowers/specs/2026-04-14-board-classifieds-design.md b/docs/superpowers/specs/2026-04-14-board-classifieds-design.md new file mode 100644 index 0000000..98c1f31 --- /dev/null +++ b/docs/superpowers/specs/2026-04-14-board-classifieds-design.md @@ -0,0 +1,198 @@ +# Board Classifieds Redesign + +## Overview + +Replace the Board's passive tag-matching system with an active classifieds board where members post what they're seeking and offering. Posts are the single source of truth for board presence. The UI follows a corkboard/zine card layout. All communication happens on Slack via curated topic channels. + +## Goals + +- Give members a reason to browse and return to the Board +- Make the Board feel like a BBS — fun, personal, alive +- Push all conversation to Slack (no in-app messaging) +- Replace the abstract tag-state system with concrete, human-readable posts + +## Design Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Visual style | Corkboard / zine cards | Fits existing design language, gives each post personality | +| Posts vs matching | Posts replace tag-matching entirely | Single source of truth, simpler mental model | +| Post lifecycle | Evergreen until removed by author | Simple, member-managed | +| Posts per member | Unlimited | Community will self-regulate | +| Slack integration | Web URL links to curated topic channels | `gammaspace.slack.com/archives/{channelId}` — tested, works reliably | +| Slack deep links | Protocol (`slack://`) and app links do not work | Tested — only web URL format opens the correct channel | +| Channel management | Admin-managed, curated set with tag mapping | Admin UI to map cooperative tags to Slack channels | +| Unmapped tags | No Slack link shown | No fallback channel | +| Visibility | All members see all posts | Behind `members-auth` middleware | +| Migration | None needed | Pre-launch, test data only | + +## Data Model + +### New: `BoardPost` + +``` +author ObjectId, ref Member, required +title String, required, max 120 +seeking String, optional, max 500 +offering String, optional, max 500 +note String, optional, max 300 +tags [String] — slugs from cooperative tag pool +createdAt Date +updatedAt Date +``` + +Validation: at least one of `seeking` or `offering` is required. + +### New: `BoardChannel` + +``` +name String, required — display name (e.g. "Structure & Governance") +slackChannelId String, required — Slack channel ID (e.g. "C09DDGZGXAP") +tagSlugs [String] — cooperative tag slugs mapped to this channel +createdAt Date +updatedAt Date +``` + +### Member Model Changes + +**Remove:** +- `board.topics` (array of tag/state objects) +- `board.details` +- `board.offerPeerSupport` +- `board.availability` +- `board.personalMessage` + +**Keep:** +- `board.slackHandle` — author's Slack identity, shown on posts +- Privacy toggle for Slack handle visibility + +## Board Page + +### Layout + +Corkboard card grid. 2 columns on desktop, 1 column at ≤1024px. Newest posts first. + +### Header + +- Page title "Board" with post count subtitle +- "+ New Post" button +- Tag filter drawer (existing pattern — toggleable, filters posts by tag) + +### Post Card + +Each card displays: + +- **Type indicator:** SEEKING (gold) / OFFERING (green) / SEEKING ↔ OFFERING (ember) — derived from which fields are filled +- **Title** — prominent, Brygada 1918 +- **Seeking text** — if present +- **Offering text** — if present +- **Note** — personal touch, slightly different visual treatment +- **Tags** — dashed-border pills +- **Footer:** author avatar + name + circle badge +- **Slack link:** "Discuss on Slack →" linking to mapped channel. Only shown if post's tags map to a channel. Uses `https://gammaspace.slack.com/archives/{channelId}`. If tags map to multiple channels, use the first match. + +### New Post Form + +Inline at top of the Board page (not a modal). Fields: + +- Title (required, 120 chars) +- Seeking (optional, 500 chars) +- Offering (optional, 500 chars) +- Note (optional, 300 chars) +- Tags (multi-select from cooperative tag pool) + +Validation: at least one of seeking/offering required. Form appears on "+ New Post" click, collapses after submission. + +### Empty State + +Friendly prompt to be the first to post, with link to create. + +## Profile Board Section + +Replaces the current cooperative tag selector, details textarea, and peer support section. + +### Shows + +- List of member's active posts (compact card previews) +- Edit and delete actions per post +- "+ New Post" button (navigates to Board page or opens same inline form) +- Slack handle setting (identity-level, not per-post) +- Privacy toggle for Slack handle + +### Removes + +- `CooperativeTagSelector` three-state tag picker +- Details textarea +- Offer Peer Support toggle + conditional fields (availability, personal message) + +### No Posts State + +Prompt to visit the Board and post something. + +## Admin: Board Channels + +New admin page for managing Slack channel mappings. + +### UI + +- List of board channels showing: display name, Slack channel ID, mapped tags +- Add / edit / remove channels +- Tag mapping: multi-select from cooperative tags +- Unmapped tag indicator: shows cooperative tags not yet assigned to any channel + +### Behavior + +- Admins create channels in Slack manually, then register them here by pasting the channel ID +- Frontend uses the channel list to build Slack links on post cards + +## API Routes + +### New + +| Method | Path | Auth | Purpose | +|--------|------|------|---------| +| GET | `/api/board/posts` | member | List all posts, newest first. Tag filtering via query params. Populates author name/avatar/circle/slackHandle. | +| POST | `/api/board/posts` | member | Create a post. Validates at least one of seeking/offering. | +| PATCH | `/api/board/posts/:id` | member (own post) | Edit a post. | +| DELETE | `/api/board/posts/:id` | member (own post) | Delete a post. | +| GET | `/api/board/channels` | member | List channels with tag mappings (for building Slack links). | +| GET | `/api/admin/board-channels` | admin | List channels for admin UI. | +| POST | `/api/admin/board-channels` | admin | Create channel mapping. | +| PATCH | `/api/admin/board-channels/:id` | admin | Update channel mapping. | +| DELETE | `/api/admin/board-channels/:id` | admin | Remove channel mapping. | + +### Remove + +| Path | Reason | +|------|--------| +| `GET /api/board/suggestions` | Replaced by posts | +| `PATCH /api/members/me/board` | Board profile fields removed (slackHandle stays on existing member profile patch) | + +## Components + +### Stays (repurposed) + +- `CooperativeTagSelector` — simplified to a plain tag picker (no three-state toggle) for use in post creation form + +### Goes + +- Match-card UI on Board page +- Peer support section on profile page + +### New + +- `BoardPostCard` — the corkboard card component +- `BoardPostForm` — inline creation/edit form +- `BoardPostList` — grid layout for post cards (used on Board page and profile) +- Admin channel management components + +## Composables + +### Remove + +- `useBoard` (the old `getSuggestions` wrapper) + +### New + +- `useBoardPosts` — CRUD for posts, tag filtering +- `useBoardChannels` — fetch channel mappings, resolve tag→channel for Slack links