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.
This commit is contained in:
parent
a0f60bcdc0
commit
707447fc88
1 changed files with 198 additions and 0 deletions
198
docs/superpowers/specs/2026-04-14-board-classifieds-design.md
Normal file
198
docs/superpowers/specs/2026-04-14-board-classifieds-design.md
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue