feat(docs): add a Diátaxis documentation section to the public site

A new public /docs area covering the whole platform, organized by Diátaxis:
tutorials (learning), how-to guides (tasks), reference (facts), and explanation
(the why). 29 pages, each grounded in the actual routes, actions, schema, and
UI — verified against the code and corrected for accuracy (e.g. admin-only vs
admin+board gating, where the permissions matrix actually lives, how Stripe
reconciliation matches).

Implementation:
- lib/docs/nav.ts is the single source of truth (categories + pages), driving
  the sidebar, static params, and prev/next.
- Content authored as content/docs/<category>/<slug>.md, read at build time
  (the routes are statically generated), rendered by <DocsMarkdown> — reuses the
  existing react-markdown/GFM stack, no new dependencies.
- (docs) route group with its own theme (docs.css) matching the public site's
  dark / monospace / lime aesthetic; sidebar with active-state, breadcrumb,
  prev/next, and a Diátaxis home. Public — signed-in users can read it too.
- "Docs" link added to the landing nav.

typecheck 0 · lint 0 errors · build ok (all /docs pages prerendered)
This commit is contained in:
Moheeb Zara
2026-08-05 00:42:42 -07:00
parent fc6adaf16e
commit 16229ae523
38 changed files with 2928 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
In this tutorial you will set up an external pay-here link, watch a member pay through it, then record that payment and reconcile it to the member so their dues advance. By the end you will have taken one dues payment all the way from "add a link" to a member marked `current`.
You will use three screens: [/settings](/settings) to add the link, [/import](/import) to record the payment, and [/payments](/payments) to reconcile it.
## Before you start
You need the right role for each part:
| Task | Required role |
| --- | --- |
| Add or edit a pay-here link | admin or board |
| Import and reconcile payments | admin, board, or treasurer |
This example uses Venmo, but PayPal and Zeffy work exactly the same way.
## Step 1: Add a pay-here link
1. Go to [/settings](/settings) and open the **Dues** tab.
2. Under **Other ways to pay dues** you will see a card for each platform: PayPal, Zeffy, and Venmo. Find the **Venmo** card.
3. Paste your payment link into the **URL** field. It must be an absolute `https://` URL — anything else is rejected.
4. Optionally add an instruction, for example `Put your member name in the note`. This is a hint that helps you match the payment later.
5. Leave the toggle set to **shown** so members can see it.
6. Click **Save**.
The link is now live for your space. You can add all three platforms, and hide any one later by flipping its toggle to **hidden**.
## Step 2: The member pays externally
There is nothing for you to do here — this is what the member sees.
1. The member opens their [/me](/me) page and looks at the **Dues** section.
2. While they still owe dues, the card shows your active links under **Other ways to pay** (or **Pay your dues** if your space has no card billing set up).
3. They click **Pay with Venmo**, which opens your external payment page in a new tab, and they pay you there — adding their name in the note if you asked them to.
The platform does not record this payment automatically. That is the next step.
## Step 3: Record the payment
When your Venmo activity is available, export it as a CSV and import it.
1. Go to [/import](/import) and switch the **Import type** to **Payments**.
2. Upload your CSV file by dropping it on the upload area or clicking to browse.
3. On **Map Columns**, confirm the required fields are mapped: **Amount**, **Sender / From Name**, and **Date**. Set the **Platform** column to `venmo` so the payment is tagged correctly, and map the note column if you have one.
4. Click **Preview Import**, review the first few rows, then click **Import**.
Every imported row lands as an `unlinked` payment. Rows with a bad amount, platform, or date are skipped and counted, so nothing invalid is silently added.
> Recording a single hand-received payment instead? Use the **Log Cash** button on [/payments](/payments) — but note it always records the platform as `cash`.
## Step 4: Reconcile the payment to the member
1. Go to [/payments](/payments). The header shows a count like **1 unlinked** whenever payments still need a member.
2. Find your Venmo row. You can narrow the list with the **All Platforms** and **All Status** filters at the top of the transactions table.
3. Click **+ Link member** on that row.
4. In the **Link to Member** dialog, pick the member who paid.
The row flips to `linked` and shows the member's name with a check mark.
## Step 5: Confirm the dues advanced
Linking a payment does two things automatically:
- It advances the member's **last paid** date to the payment's date.
- It sets the member's status to `current` — as long as this payment is the most recent one on file for them.
This is **advance-only**: recording an older or backdated payment never moves a member's dues backward. If the member already has a newer payment recorded, linking an older one leaves their status untouched.
To verify, open the member on [/members](/members) and check their status, or have the member reload [/me](/me), where the payment now appears under **My payments**.
## What you did
You added an external pay-here link, a member paid through it, you imported the payment as an `unlinked` row, and you reconciled it to the member — advancing their dues to `current`. Repeat Step 3 and Step 4 whenever new external payments come in.
+69
View File
@@ -0,0 +1,69 @@
This tutorial walks you through the full lifecycle of a class in your space: you will create a class offering, schedule a session with a capacity, watch members sign up (with a waitlist when it fills), mark attendance as the instructor, and complete the session so it can award a certification. By the end you will have run one class from start to finish.
Set aside about fifteen minutes. You will need the `classes.manage` permission to create classes and sessions, and `classes.instruct` to run the session and mark attendance. Both are granted to the `board` role by default. To have completion award a certification, the instructor also needs `certifications.grant`.
## Before you start
- Sign in as a member with `classes.manage` (a board member by default).
- Optional: if you want the class to award a certification, create the certification type first at [/certifications](/certifications). Note its name.
## Step 1: Create a class
A class is the reusable offering ("Woodshop Basics"). Sessions are the individual scheduled occurrences you will add later.
1. Go to [/classes/manage](/classes/manage).
2. Select **New class**.
3. Enter a **Title**, for example `Woodshop Basics`. Add an optional description.
4. Optionally set a **Default capacity**. Leave it blank for unlimited. Each session can override this later.
5. Optionally, in the certification dropdown, choose **Grants: `<your cert>`** so completing a session awards that certification to attendees.
6. Select **Create class**.
You will see a "Class created" confirmation and the class listed with any `cap` and `grants` badges you set.
Two optional fields you can ignore for this tutorial: a **Payment link** (a plain external URL shown to members, with no live payment processing) and a **required form** that gates signup until the member has it on file (for example a liability waiver published under [/forms](/forms)).
## Step 2: Schedule a session
1. On the class card, select **+ Schedule session**.
2. Set **Starts** (and optionally **Ends**). Ends must not be before Starts.
3. Optionally add a **Location**, a **Capacity override**, and **Notes**.
4. Select **Schedule**.
The session appears under the class with its start time and a **Scheduled** status. The effective capacity is the session's capacity override if set, otherwise the class default; if both are blank, the session is open (unlimited).
## Step 3: Members sign up
Members do not need any special permission to sign up, only membership in the space.
1. As a member, go to [/classes](/classes). Upcoming, non-cancelled sessions appear with "X of N spots left" (or "open" when unlimited).
2. Select **Sign up**. You will see "You are signed up" and a **Registered** badge.
When registered signups reach capacity, the button reads **Join waitlist** instead, and new signups get a **Waitlisted** status. A member can select **Cancel signup** at any time; if a registered member cancels, the earliest waitlisted member is automatically promoted to registered and emailed. If the class requires a form the member has not submitted, the button becomes **Complete required form** and links to the form first.
## Step 4: Mark attendance
Now switch back to someone with `classes.instruct`. (A user with only `classes.manage` can open the roster and see who signed up, but cannot check **attended** or run **Complete session** — both require `classes.instruct`.)
1. On [/classes/manage](/classes/manage), find the session and select **Signups**. (Instructors browsing [/classes](/classes) can use **Attendees** on the session instead.)
2. The roster lists each signup with its status. Check the **attended** box next to each member who showed up.
Attendance is what determines who receives a certification in the next step, so check it before completing.
## Step 5: Complete the session and award the certification
1. In the same attendees panel, select **Complete session**.
2. The session status changes to **Completed**.
If the class grants a certification, completion awards it to every member you marked as attended. The result confirms how many certificates were issued. If the instructor lacks `certifications.grant`, the session still completes but you will see "Certificates were NOT issued (requires the certifications.grant permission)" — grant that permission and the members can be certified through [/certifications](/certifications).
Members can see their signups and completed sessions on their own page at [/me](/me), and any certification they earned appears in their certifications.
## What you built
You now have a class with one completed session, an attendance record, and (optionally) certifications awarded to attendees. From here you can:
- Schedule more sessions of the same class — the offering is reusable.
- **Archive** a class you no longer offer instead of deleting it (deleting is only allowed when a class has no sessions).
- **Cancel** a session instead of deleting it to keep the signup history; every signed-up member is emailed when you do.
See the [classes reference](/docs/reference/classes) for every field, status, and permission, and [manage certifications](/docs/how-to/manage-certifications) for setting up the certification types classes can grant.
+74
View File
@@ -0,0 +1,74 @@
This tutorial walks you from a blank signup form to a working space where you land as an admin, then gives you a quick tour of the dashboard and left navigation so you know where everything lives. It takes about five minutes.
By the end you will have a space of your own, an admin membership inside it, and a mental map of the sidebar.
## Before you start
You need an email address and a password of at least 8 characters. If someone has already set up a space for your group and given you an invite code, skip ahead to [Joining an existing space](#joining-an-existing-space) instead.
## Step 1: Create your account
1. Go to `/signup`.
2. Under **Create your account**, fill in **Full Name**, **Email**, and **Password** (minimum 8 characters).
3. Under **What are you doing?**, click the **Create a Space** card.
4. Click **Continue →**.
## Step 2: Name your space
Now you are on the **Set up your Space** step.
1. Enter a **Space Name**, such as `HeatSync Labs`. As you type, a **Slug** is filled in for you automatically — it is the lowercase, letters-and-numbers-only handle for your space (for example `heatsynclabs`). You can edit it.
2. Optionally enter a **City / Location**.
3. Read the note: you will be the admin of this space, and default channels (general, announcements, ops) are created for you.
4. Click **Create Space →**.
Depending on how email confirmation is configured, one of two things happens:
- You go straight to `/dashboard`, already signed in.
- You are sent to `/signup/confirm` to verify your email first. Open the link in that email, and you will land on the dashboard afterward.
That is it — your space exists. As the founder you are set up as an **admin** on the **plus** tier with a **current** status, and you skip the member onboarding flow entirely.
## Step 3: Get your bearings on the dashboard
The first time you open `/dashboard` in an empty space, you see a **"Let's set up your space"** panel instead of a grid of zeros. It offers four first steps:
| Step | Where it goes |
| --- | --- |
| Invite or add members | `/members` |
| Set up onboarding | `/customize` |
| Create your first task or chore | `/tasks` |
| Connect payments | `/settings` |
That panel disappears on its own once there is real activity. After that, the dashboard shows a stat row — **Active Members**, **Dues Current**, **Open Tasks**, **Unlinked Payments** — plus quick chores, active projects, who is present now, and recent activity. The **Quick Task** button in the header jumps to `/tasks`.
## Step 4: Explore the left navigation
The sidebar groups every screen into sections. As an admin you see all of them:
- **Workspace** — Dashboard, Tasks & Chores, Projects, Ops & Facilities, Comms, Forum
- **Governance** — Proposals, Incidents, Policies
- **Access & Resources** — Classes, Equipment, Doors, Forms
- **People** — Members, Attendance, Contacts, Recruitment
- **Finance** — Payments, Financials
- **Account** — My membership
- **Admin** — Customize, Settings, Import / Sync, and the management screens for forms, certifications, classes, equipment, and doors
The **Admin** section only appears for admins, board members, or members you have granted a specific manage permission. There is also a **Jump to…** button at the top of the sidebar (or press ⌘K) that opens a command palette for fast navigation.
A good next stop is [inviting your first members](/members), or `/customize` to shape what new members see when they join.
## Joining an existing space
If you were given an invite code instead of starting a new space:
1. Go to `/signup` (or open the `/signup?invite=CODE` link your admin shared, which prefills the code for you).
2. Fill in your account details and click the **Join a Space** card, then **Continue →**.
3. Enter the **Invite Code** — it looks like `HSL-2025-XXXX`. Ask your space admin for it if you do not have one; they can find the space's default code under Settings.
4. Click **Join Space →**.
You join as a **member**. If the space requires approval (the default), your status starts as **unverified** and an admin approves you before you gain full access — until then the dashboard is still yours to look around. If the space does not require approval, you are **current** right away.
## What you did
You created (or joined) a space, landed with the right role, and learned where the dashboard and sidebar put things. From here, follow the dashboard's setup steps to add members, build your onboarding flow, and start capturing tasks.
+92
View File
@@ -0,0 +1,92 @@
In this tutorial you will bring your first people into your space two ways — a shareable join link and a manual add — shape what a new member sees when they first open the app, and move everyone to `current` status. Work through it as a space admin; by the end you will have a repeatable way to grow your roster.
## Before you start
You need the **admin** role (a **board** member can copy and share existing invite codes, but not create, disable, or delete them). Everything below happens on the [/members](/members) screen and the onboarding card in [/customize](/customize).
There are two ways in:
- **Invite code / join link** — people sign themselves up, then you approve them.
- **Add member** — you enter someone directly and they are active immediately.
You will use both.
## Step 1: Create an invite code
1. Open [/members](/members) and find the **Invite codes** card.
2. Click **+ New invite**.
3. Leave the code blank to auto-generate one, or type your own. Optionally set a **Label**, an expiry, and a **Max uses** cap. Leave both blank for a permanent code.
4. Pick what the code **grants** — for new members, choose `member`.
5. Tick **Single use** if the code should auto-disable after one join.
6. Click **Create invite**.
Your code appears in the list with a `grants member` tag and a live use count.
## Step 2: Share the join link
On the new code's row you have two buttons:
- **Copy code** — copies just the code (for people who will type it during signup).
- **Copy link** — copies the one-click join link, which looks like:
```
https://your-space.example/join/your-space?code=ABC23XYZ
```
Send the link. Whoever opens it lands on a **Join** page for your space, creates an account, and is added as a member with `unverified` status — waiting for your approval.
If a code is ever shared too widely, an admin can click **Disable** to revoke it, or **Delete** to remove it outright — both controls are admin-only.
## Step 3: Approve people who joined
New self-serve joiners do not get in automatically.
1. Back on [/members](/members), open the **Pending Approval** tab. Everyone who used your link is here.
2. Click **APPROVE** on a row to move that member to `current`.
3. To clear a batch at once, select the rows and click **Approve selected**.
Approving flips the member to `current` and marks them approved, so they count toward your active roster.
## Step 4: Add a member manually
Some people you will enter yourself — a founding member, someone who paid at the door.
1. On [/members](/members), click **Add Member**.
2. Fill in **Full Name** and **Email** (both required). Phone and handle are optional.
3. Choose a **Tier** (`Plus`, `Basic`, `Associate`, or `Admin`) and a **Role** (`Member`, `Board`, `Treasurer`, or `Admin`).
4. Optionally set **Joined At** and tick **Card Access**.
5. Click **Add Member**.
A manually added member starts at `current` immediately — no approval step needed.
## Step 5: Shape the onboarding flow
Now decide what a member sees the first time they open the app.
1. Go to [/customize](/customize) and find the **New member onboarding** card.
2. Every new space starts with four built-in steps in order: **Welcome**, **Code of Conduct** (a required acknowledgement), **Complete your profile**, and **Set up your dues**.
3. Edit any step's title inline, and its body where the step has one (body supports Markdown) — the built-in **Complete your profile** step has only an editable title, no body. Change the order with the number box, and untick **Enabled** to hide a step.
4. On the **Set up your dues** step, paste your **Payment link** so the member gets a working "Set up payment" button.
5. Add your own with **+ Custom step**, or use **+ Form step** to make members sign a published form or waiver as part of onboarding.
Built-in steps can be disabled and reordered but not deleted. Only steps you mark **Required** block a member from finishing.
## Step 6: See what the new member sees
The first time an approved member opens the app they are routed to [/onboarding](/onboarding) and walk your enabled steps in order — a progress bar tracks **Step X of Y**. They must tick the code-of-conduct box and any other required step before the final **Finish** button lets them through to the [/dashboard](/dashboard). If no step is required, a **Skip for now** link is available.
## You are done
You now have members arriving two ways and a first-run flow that greets them. From here:
- Keep an eye on the **Pending Approval** and **Payment Issues** tabs on [/members](/members).
- Refine your onboarding copy and required steps in [/customize](/customize) as your space's norms settle.
### Member statuses at a glance
| Status | Meaning |
| --- | --- |
| `current` | Active member in good standing |
| `late` | Active but behind on dues |
| `inactive` | No longer participating |
| `unverified` | Joined via a link, awaiting approval |