Stakeholder invites + Ask an organizer (modal, API, email) #51

Merged
an.di merged 7 commits from adilallo/feature/StakeholderAndAskOrganizer into main 2026-05-12 00:18:42 +00:00
Owner

Overview

This PR adds two product-facing flows on the create and marketing surfaces.

Stakeholder management extends publish and post-publish editing: invite stakeholders by email, resend or revoke invites, and accept invites via a one-time verify link. The create flow gains a manage-stakeholders path from confirm-stakeholders and profile, with API coverage and Prisma persistence.

Ask an organizer (CR-107) replaces link-based marketing CTAs with a Create-shell modal (email + question). Submissions go to POST /api/organizer-inquiry with validation, rate limits, and a honeypot; delivery is email-only via ORGANIZER_INQUIRY_TO and existing SMTP. Staging inbox wiring is tracked in CR-114.

Changes

  • Stakeholders: RuleStakeholder model and migration; stakeholder routes under app/api/rules/[id]/stakeholders (list, invite, revoke, resend); GET /api/invites/rule-stakeholder/verify; publish path sends stakeholder invite mail; PublishedStakeholdersManagePanel and confirm-stakeholders updates; create-flow navigation and lib/create/api client helpers; mail helper for stakeholder invites.
  • Ask an organizer: AskOrganizerInquiry modal (Create shell, TextInput / TextArea, optional showHelpIcon={false} on email); modal-only AskOrganizer CTA on home, learn, and blog; POST /api/organizer-inquiry with Zod schema, per-IP and per-email rate limits, honeypot silent success; sendOrganizerInquiryNotification with replyTo; ORGANIZER_INQUIRY_TO documented in .env.example; optional footerClassName on Create for full-width footer actions.
  • Create / export: Rule export and TextBlock row media handling; progress bar removed on edit-rule screens; small create-flow and profile polish.
  • i18n: messages/en/modals/askOrganizerInquiry.json and related marketing / askOrganizer copy; success copy without em dash or semicolon.
  • Tests: Unit coverage for organizer-inquiry route, stakeholder routes, publish with invites, and verify route; component and page tests for AskOrganizer and create-flow surfaces; E2E critical journey opens the organizer dialog.

Screenshots

Screenshot 2026-05-11 at 6.12.58 PM.png Screenshot 2026-05-11 at 6.14.32 PM.png Screenshot 2026-05-11 at 6.14.56 PM.png Screenshot 2026-05-11 at 6.15.06 PM.png

How to Test

  1. Copy .env.example to .env. Set SESSION_SECRET, DATABASE_URL, and ORGANIZER_INQUIRY_TO. For real SMTP locally: docker compose up -d postgres mailhog, SMTP_URL=smtp://localhost:1025, Mailhog at http://localhost:8025. Run npx prisma migrate dev, then npm run dev.
  2. Ask an organizer: On /, /learn, or a blog post, click Ask an organizer. Confirm email has no help icon, Confirm Question is centered, validation on bad input, and success copy reads smoothly. Submit a valid question; expect POST /api/organizer-inquiry → 200 { ok: true } and a message to ORGANIZER_INQUIRY_TO with visitor reply-to and request id (or a dev log if SMTP_URL is unset).
  3. Stakeholders: Publish a rule with stakeholder emails (signed in, backend sync on if drafts use the API). Exercise manage-stakeholders from create or profile: invite, resend, revoke. Open the verify link in the same browser; confirm access and mail in Mailhog or logs.
  4. Automated: npx tsc --noEmit, npx vitest run, npx next build. Optionally npm run e2e for the homepage organizer dialog step.

Notes

  • Deploy: Set ORGANIZER_INQUIRY_TO per environment; staging follow-up is CR-114. Production needs a real support or organizer inbox at cutover.
  • Organizer inquiries are email-only (no Prisma row); rate limits stay in-memory per instance.
  • Stakeholder invites reuse the magic-link mail pattern (hashed token, 15-minute TTL).
  • Storybook LinkCta on AskOrganizer keeps a legacy link CTA for comparison; marketing pages use the modal path only.
## Overview This PR adds two product-facing flows on the create and marketing surfaces. **Stakeholder management** extends publish and post-publish editing: invite stakeholders by email, resend or revoke invites, and accept invites via a one-time verify link. The create flow gains a manage-stakeholders path from confirm-stakeholders and profile, with API coverage and Prisma persistence. **Ask an organizer ([CR-107](https://linear.app/community-rule/issue/CR-107/feature-ask-an-organizer))** replaces link-based marketing CTAs with a Create-shell modal (email + question). Submissions go to `POST /api/organizer-inquiry` with validation, rate limits, and a honeypot; delivery is email-only via `ORGANIZER_INQUIRY_TO` and existing SMTP. Staging inbox wiring is tracked in [CR-114](https://linear.app/community-rule/issue/CR-114/backend-staging-wire-ask-an-organizer-smtp-test-inbox). ## Changes - **Stakeholders:** `RuleStakeholder` model and migration; stakeholder routes under `app/api/rules/[id]/stakeholders` (list, invite, revoke, resend); `GET /api/invites/rule-stakeholder/verify`; publish path sends stakeholder invite mail; `PublishedStakeholdersManagePanel` and confirm-stakeholders updates; create-flow navigation and `lib/create/api` client helpers; mail helper for stakeholder invites. - **Ask an organizer:** `AskOrganizerInquiry` modal (Create shell, `TextInput` / `TextArea`, optional `showHelpIcon={false}` on email); modal-only `AskOrganizer` CTA on home, learn, and blog; `POST /api/organizer-inquiry` with Zod schema, per-IP and per-email rate limits, honeypot silent success; `sendOrganizerInquiryNotification` with `replyTo`; `ORGANIZER_INQUIRY_TO` documented in `.env.example`; optional `footerClassName` on Create for full-width footer actions. - **Create / export:** Rule export and `TextBlock` row media handling; progress bar removed on edit-rule screens; small create-flow and profile polish. - **i18n:** `messages/en/modals/askOrganizerInquiry.json` and related marketing / askOrganizer copy; success copy without em dash or semicolon. - **Tests:** Unit coverage for organizer-inquiry route, stakeholder routes, publish with invites, and verify route; component and page tests for AskOrganizer and create-flow surfaces; E2E critical journey opens the organizer dialog. ## Screenshots <img width="1126" alt="Screenshot 2026-05-11 at 6.12.58 PM.png" src="attachments/822613e9-9ce5-44e4-9fd2-688c058d0bda"> <img width="1126" alt="Screenshot 2026-05-11 at 6.14.32 PM.png" src="attachments/2ebd6dea-8363-405b-9705-8caaf1b30786"> <img width="1126" alt="Screenshot 2026-05-11 at 6.14.56 PM.png" src="attachments/1c64a742-5d25-4a48-b7c3-c0aa8fd864cc"> <img width="1126" alt="Screenshot 2026-05-11 at 6.15.06 PM.png" src="attachments/9a74d16f-982f-49d5-b19b-433cadf88bd9"> ## How to Test 1. Copy `.env.example` to `.env`. Set `SESSION_SECRET`, `DATABASE_URL`, and `ORGANIZER_INQUIRY_TO`. For real SMTP locally: `docker compose up -d postgres mailhog`, `SMTP_URL=smtp://localhost:1025`, Mailhog at http://localhost:8025. Run `npx prisma migrate dev`, then `npm run dev`. 2. **Ask an organizer:** On `/`, `/learn`, or a blog post, click **Ask an organizer**. Confirm email has no help icon, **Confirm Question** is centered, validation on bad input, and success copy reads smoothly. Submit a valid question; expect `POST /api/organizer-inquiry` → 200 `{ ok: true }` and a message to `ORGANIZER_INQUIRY_TO` with visitor reply-to and request id (or a dev log if `SMTP_URL` is unset). 3. **Stakeholders:** Publish a rule with stakeholder emails (signed in, backend sync on if drafts use the API). Exercise manage-stakeholders from create or profile: invite, resend, revoke. Open the verify link in the same browser; confirm access and mail in Mailhog or logs. 4. **Automated:** `npx tsc --noEmit`, `npx vitest run`, `npx next build`. Optionally `npm run e2e` for the homepage organizer dialog step. ## Notes - **Deploy:** Set `ORGANIZER_INQUIRY_TO` per environment; staging follow-up is [CR-114](https://linear.app/community-rule/issue/CR-114/backend-staging-wire-ask-an-organizer-smtp-test-inbox). Production needs a real support or organizer inbox at cutover. - Organizer inquiries are **email-only** (no Prisma row); rate limits stay in-memory per instance. - Stakeholder invites reuse the magic-link mail pattern (hashed token, 15-minute TTL). - Storybook **LinkCta** on AskOrganizer keeps a legacy link CTA for comparison; marketing pages use the modal path only.
an.di added 6 commits 2026-05-12 00:16:04 +00:00
an.di self-assigned this 2026-05-12 00:16:14 +00:00
an.di added 1 commit 2026-05-12 00:17:55 +00:00
an.di merged commit d2dfa099a2 into main 2026-05-12 00:18:42 +00:00
an.di deleted branch adilallo/feature/StakeholderAndAskOrganizer 2026-05-12 00:18:43 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: CommunityRule/community-rule#51