Problem
There is no logical, easy way for an admin to add a ministry-school student (a current Member of the congregation who attends but isn't yet a declared publisher). Today:
/publishers/new is the only entry point on the Publishers screen. The form's schema (app/features/publishers/schemas/publisher.schema.ts) has no isPublisher field, and createMember hardcodes isPublisher: true in app/features/publishers/server/member.aggregate.ts (the write path moved to the aggregate — the file/line referenced by the original body no longer applies). No toggle on the form.
- The only way to end up with a non-publisher Member is to create them as a publisher, then demote them via a
POST /publishers/:memberId/make-student action. That route exists but has no visible button on the publisher detail page. The lifecycle cluster on the detail page now surfaces the inverse /make-publisher action (a UserCheck button shown when isPublisher === false), so an admin who lands on an already-demoted profile can promote them — but the demote path stays hidden.
- The one place that exposes a clear
isPublisher checkbox in the UI is app/features/settings/routes/users/add-to-congregation.tsx, which only fires when a UserAccount exists but isn't yet linked to a Member — a niche admin flow (e.g. a circuit overseer login becoming a local member), not the common case.
Underlying mismatch: the domain has two independent axes — Type (publisher / student) and Lifecycle (present / inactive / left / anonymized) — but the UI presents one axis. The list is also labelled "Publishers" even though it already contains both publishers and ministry-school students (see docs/product/publishers.md).
Idea (not decided — needs more design work)
The shape of the redesign we've been kicking around:
- Rename the section "Membres" / "Members" to match what the list already contains. Keep the
/publishers URL for now to avoid link/test churn.
- Single "Ajouter un membre" CTA, with a segmented control inside the form (Publisher / Ministry-school student, defaulting to Publisher). The choice would hide baptism date, anointed, the elder/servant nomination card, and the publisher type select for students; the group selector would stay visible.
- Move publisher ↔ student switching out of the lifecycle icon cluster into a dedicated "Status" card on the publisher view, with a single labelled action ("Convertir en étudiant" / "Déclarer proclamateur"). The lifecycle cluster would keep only inactive / mark-as-left / mark-as-returned — cleaner separation between "type of member" and "lifecycle state".
createMember (in member.aggregate.ts) would accept isPublisher: boolean and force-zero baptism-gated flags when the kind is student, so DB CHECK constraints (servant/elder/anointed → baptism + male) can't be violated by construction.
Estimate
~2–3 days once the design is settled. The write-side change is a one-parameter addition on createMember in member.aggregate.ts; most of the effort is UI (segmented control, "Status" card on the detail page, section rename) and confirm-dialog copy.
Out of scope (current thinking)
- Renaming the
/publishers URL — link rot, e2e tests, deep-links from territories. Sidebar/in-page copy rename only.
- Per-kind limits (
students vs publishers). The existing members limit already covers both.
- Renaming audit actions (
PublisherCreated, PublisherStatusChanged). Historical analytics rely on them.
- A list-row "convert" shortcut. Forcing the admin into the profile first is the right friction.
Status
Not decided. The above is one direction, not a committed design. Open questions before this is ready to implement:
- Is "segmented control inside the form" the right entry-point shape, or is a split button / two CTAs / a separate route preferable?
- Should the section actually be renamed, or should the label stay "Publishers" and the IA be fixed elsewhere?
- Where exactly does the publisher↔student action live on the profile (dedicated card vs. a settings-style menu vs. somewhere else)?
- Confirm-dialog copy (FR + EN) needs review before it ships.
- Are there flows we're forgetting — e.g. bulk import, archive restore — that would also need a "kind" field?
More design work needed before opening a PR. Feedback welcome.
Problem
There is no logical, easy way for an admin to add a ministry-school student (a current Member of the congregation who attends but isn't yet a declared publisher). Today:
/publishers/newis the only entry point on the Publishers screen. The form's schema (app/features/publishers/schemas/publisher.schema.ts) has noisPublisherfield, andcreateMemberhardcodesisPublisher: trueinapp/features/publishers/server/member.aggregate.ts(the write path moved to the aggregate — the file/line referenced by the original body no longer applies). No toggle on the form.POST /publishers/:memberId/make-studentaction. That route exists but has no visible button on the publisher detail page. The lifecycle cluster on the detail page now surfaces the inverse/make-publisheraction (aUserCheckbutton shown whenisPublisher === false), so an admin who lands on an already-demoted profile can promote them — but the demote path stays hidden.isPublishercheckbox in the UI isapp/features/settings/routes/users/add-to-congregation.tsx, which only fires when aUserAccountexists but isn't yet linked to aMember— a niche admin flow (e.g. a circuit overseer login becoming a local member), not the common case.Underlying mismatch: the domain has two independent axes — Type (publisher / student) and Lifecycle (present / inactive / left / anonymized) — but the UI presents one axis. The list is also labelled "Publishers" even though it already contains both publishers and ministry-school students (see
docs/product/publishers.md).Idea (not decided — needs more design work)
The shape of the redesign we've been kicking around:
/publishersURL for now to avoid link/test churn.createMember(inmember.aggregate.ts) would acceptisPublisher: booleanand force-zero baptism-gated flags when the kind is student, so DB CHECK constraints (servant/elder/anointed → baptism + male) can't be violated by construction.Estimate
~2–3 days once the design is settled. The write-side change is a one-parameter addition on
createMemberinmember.aggregate.ts; most of the effort is UI (segmented control, "Status" card on the detail page, section rename) and confirm-dialog copy.Out of scope (current thinking)
/publishersURL — link rot, e2e tests, deep-links from territories. Sidebar/in-page copy rename only.studentsvspublishers). The existingmemberslimit already covers both.PublisherCreated,PublisherStatusChanged). Historical analytics rely on them.Status
Not decided. The above is one direction, not a committed design. Open questions before this is ready to implement:
More design work needed before opening a PR. Feedback welcome.