> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aquaops.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Swim Lessons

> Run swim lesson classes and rosters, author curriculum levels, and put student report cards through a two-stage review before publishing them to families.

Swim Lessons is a self-contained module for facilities that run structured swim lesson programs — classes, rosters, a skills-based curriculum, and report cards that go through instructor review before a family can see them.

> [!NOTE]
>
> The module is feature-gated (`swim_lessons`). If your organization doesn't have it enabled, `Swim Lessons` won't appear in navigation and its pages redirect or show a permission message instead.

## Purpose

Use this module to:

* organize students into classes at a location, each pinned to one swim level
* build and maintain rosters, including bulk CSV import
* author the skill checklist each swim level evaluates against
* record student report cards and route them through co-teacher and final-approver review before they publish
* let a parent or guardian look up their child's published report card without an account

## Best for

* season-based or ongoing swim lesson programs with assigned instructors
* facilities that want sign-off on a report card before a family sees it
* programs that already have a structured curriculum (e.g. a Red Cross–style progression) they want to adopt instead of building from scratch

## Classes

Go to:

```text theme={null}
Swim Lessons
```

The list page shows every active class (or, without `classes:manage`, just the classes you instruct) with its swim level, grade band, location, instructor count, and active student count.

### Creating a class

**1. Open Swim Lessons**

Select `New class`. Requires `classes:manage`.

**2. Fill in the basics**

Enter a class name and grade band, then pick a location and a swim level.

**3. Assign instructors**

Check every instructor for this class. Every instructor assigned gets equal access to record and review report cards for the class's roster — there's no primary/secondary instructor distinction.

**4. Create**

The class is created and you're taken to its detail page to start building the roster.

### Managing a class

The class detail page has two cards:

* **Instructors** — add or remove instructors (`classes:manage`). Without that permission you see a read-only list.
* **Roster** — every actively-enrolled student, with an **Enroll student** dialog to add one at a time or **Import roster** to bulk-add via CSV.

Enrollment is exclusive: enrolling a student who already has an active enrollment elsewhere automatically ends that prior enrollment first (a transfer, not a duplicate). A student can only be actively enrolled in one class at a time.

### Bulk roster import

From a class's detail page, **Import roster** creates students and enrolls them into that one class in a single step — meant for standing up a whole season's roster at once, not for adding a student who might already exist.

The CSV needs a `name` column. Optional columns: `date_of_birth` (`YYYY-MM-DD`), `guardian_name`, `guardian_phone`, plus any org-defined student custom fields (select-type fields must use one of that field's configured options). Up to 500 rows per import.

There's deliberately no matching against existing students by name — a false-positive match could silently merge two different kids' records. Every row always creates a new student, so review the parsed preview table carefully before confirming; the imported students inherit the target class's location.

## Curriculum and levels

Each class points at one **swim level**, and a swim level carries the skill checklist every report card for that class is evaluated against.

### Levels

Manage levels from:

```text theme={null}
Settings > Swim Lessons
```

> [!NOTE]
>
> A legacy `Settings > Lesson Levels` link redirects here — levels, curriculum adoption, approval settings, comment prefills, and student custom fields all live on one **Swim Lessons** settings page.

A level has a name and an ordered list of skills (free-text labels). Requires `lesson_levels:manage` (`lesson_levels:view` for read-only).

Skill checklists can render in grouped sections — **Skills**, **Safety Focus**, **Exit Assessment** — when a level's skills came from a curriculum that categorizes them that way. A level you build from scratch in the editor just gets one flat **Skills** list, since its skills have no category tag.

### Adopting a curriculum

Rather than building a skill checklist by hand, use **Browse curricula** on the Swim Lessons settings page to adopt a shared curriculum template (for example, a Red Cross–style progression) into your org's own levels.

Adopting is fork-on-write and position-based, not a destructive replace:

* template levels are matched to your org's levels by their position (sort order), and each is updated in place with the template's name and skills — a class already pointed at that level keeps working, it just picks up the new content
* any level you've manually edited yourself (or built from scratch, never linked to a template) is left alone on adopt — it's yours now, and re-adopting won't overwrite it
* editing a level's name or skills at any time detaches it from its template going forward, for the same reason
* if your org has more levels than the template does, the extras are deleted only if they're both unlinked-or-unused; a level still assigned to a class, or one you've customized, is left in place and reported back as skipped

> [!NOTE]
>
> The curriculum template catalog itself (`/admin/curricula`) is managed by AquaOps platform staff, not by organization admins. Org admins can browse and adopt from the catalog but can't add or edit templates in it.

## Students

Go to:

```text theme={null}
Swim Lessons > Students
```

The list supports name search and a toggle to show archived students. Requires `students:view` (`students:manage` to create, edit, or archive).

Each student has a name, location, optional date of birth, guardian name, guardian phone, and any org-defined custom fields. The list flags whether a student has a **lookup factor on file** — a date of birth or guardian phone — since that's what a family will need to look up a report card later.

### Student custom fields

Configure org-specific student fields (text, number, or single-select) from `Settings > Swim Lessons` under **Student custom fields** (`students:manage`). These are your organization's own vocabulary — a group name, a unit number, anything not built into the product.

* a field's type can't be changed after creation, since existing stored values might no longer make sense under a new type
* **Archive** hides a field from new entry while keeping its stored values
* **Delete** is permanent and removes every student's value for that field — the confirmation dialog shows how many students currently have a value before you commit

### Enrollment history and report cards

A student's detail page lists every past and current enrollment, each with its report cards (status and overall result badges) and, for the active enrollment, buttons to record a new report card or unenroll. If neither a date of birth nor guardian phone is on file, the page calls this out directly: report cards for that student can't be published (reach `approved`) until one is added.

## Report cards

Each report card belongs to one enrollment and evaluates every skill on that enrollment's class's swim level.

### Recording

Start a report card from a student's active enrollment (**New report card**) or from the [report card queue](#report-card-queue). Requires `report_cards:create`.

**1. Set skill results**

Mark each skill Pass, Needs more time, or Not yet evaluated. A draft can be saved with any subset filled in.

**2. Set an overall status**

Pick the card's overall result: Pass, Needs more time, or Not yet evaluated.

**3. Add comments**

Write free-text comments, optionally starting from a comment prefill (see Settings below) — prefills substitute the student's name in automatically.

**4. Submit for review**

Submitting requires every skill on the level to have a result, and requires the student to have a date of birth or guardian phone on file. Missing either blocks the submit with an explicit error.

Only the recorder can edit or submit a draft — unless you hold `report_cards:manage`, which can edit any report card administratively regardless of status or ownership.

### Two-stage review

A submitted report card moves through up to two review stages before it publishes:

1. **Co-teacher review** — any *other* instructor assigned to the same class (not the recorder) can approve, reject, or request changes. A class with no other instructors skips this stage automatically: the card goes straight to co-teacher-approved on submit.
2. **Final approval** — requires `report_cards:approve` (or `org:manage`) *and* access to the class's location. Approving here publishes the card.

Each stage's approval threshold is configurable per organization under `Settings > Swim Lessons > Report card approval`:

* **Any one** — a single qualifying reviewer's approval advances the card
* **All** — every eligible reviewer for that stage must approve

A rejection or request-for-changes from any single authorized reviewer halts the card immediately, regardless of the any/all setting — a single objection doesn't need consensus, only approval does. **Changes requested** sends the card back to the recorder; resubmitting clears every review from the prior cycle and restarts both stages.

No one can review their own report card at either stage, and the recorder is always excluded from that stage's eligible-reviewer count.

> [!NOTE]
>
> If a class's location has zero eligible final approvers, the card does **not** auto-publish — it's a misconfiguration, not a vacuous approval. The report card detail page shows an explicit warning asking an org admin to grant `report_cards:approve` to someone with access to that location.

### Statuses

| Status | Meaning |
| - | - |
| `draft` | Being recorded; not yet submitted |
| `submitted` | Awaiting co-teacher review |
| `co_teacher_approved` | Co-teacher stage cleared; awaiting final approval |
| `approved` | Published — visible to the family via public lookup |
| `changes_requested` | Sent back to the recorder; resubmitting restarts both review stages |
| `rejected` | Stopped by a reviewer; does not auto-resume |

### Publishing and printing

Once `approved`, a report card shows a **Print** button for a print-friendly copy, and it becomes retrievable through the public parent lookup below.

## Report card queue

Go to:

```text theme={null}
Swim Lessons > Report card queue
```

Requires `report_cards:create`. This is a personal worklist, not a full list of every report card:

* **Awaiting your review** — report cards where you're specifically an eligible reviewer right now (a co-teacher on a `submitted` card, or, if you hold `report_cards:approve`, a `co_teacher_approved` card at a location you have access to)
* **Students without a report card** — for a selected report card period (a due date staff define), every active enrollment in a class you instruct that doesn't have a card for that period yet, with a one-click create

`report_cards:manage` holders can create and manage report card periods from this page.

## Public lookup

Families look up a published report card without an account at:

```text theme={null}
/public/lessons/lookup
```

They enter the student's name plus **exactly one** of:

* date of birth, or
* the last 4 digits of the guardian phone number on file

Both a name match and one second factor are always required — a single field alone is never enough, to resist someone guessing their way to a match. Name comparison ignores case and extra whitespace. Lookups are rate-limited to 5 attempts per 15 minutes per requester (by hashed IP); if that check itself fails, the lookup fails closed rather than risk running unthrottled.

If a name and factor combination matches more than one student, the result is treated as **no match** — the system never guesses between two candidates. Only report cards with status `approved` are returned; drafts and cards still in review never appear here. A match shows the student's name and, per report card, the class, swim level, overall status, skills (grouped the same way the instructor editor groups them), comments, and date.

## Settings

Go to:

```text theme={null}
Settings > Swim Lessons
```

This single page hosts everything below, each gated by its own permission:

* **Report card approval** (`lesson_levels:manage`) — the co-teacher and final-approver any/all thresholds described above
* **Comment prefills** (`report_cards:manage`) — reusable comment snippets recorders can insert into a report card's Comments field; use `{{name}}` in the template text and it's replaced with the actual student's name on insert
* **Student custom fields** (`students:manage`) — org-defined per-student fields, described above
* **Levels** (`lesson_levels:manage`) — edit each level's name and skill checklist, create a level from scratch, or adopt a shared curriculum template

Every section also requires baseline module access (`classes:view` or `classes:manage`) — you can't configure a module you can't otherwise see.

## Permissions

| Permission | Grants |
| - | - |
| `lesson_levels:view` | See the curriculum (levels and skill checklists) |
| `lesson_levels:manage` | Create/edit levels and skills; adopt curriculum templates; configure review approval modes |
| `classes:view` | See classes — scoped to classes you're assigned to unless you also hold `classes:manage` |
| `classes:manage` | Create/edit classes, assign instructors, enroll/transfer students, see every class |
| `students:view` | See the student roster — scoped to students on classes you can see |
| `students:manage` | Add/edit student records and custom fields |
| `report_cards:view` | See report cards — scoped like classes |
| `report_cards:create` | Record and submit report cards for classes you teach |
| `report_cards:manage` | Edit any report card and see every report card (does not by itself grant final-approval authority) |
| `report_cards:approve` | Act as a final-stage approver; also grants `report_cards:manage` |

Assign these on the [Users and Roles](https://docs.aquaops.dev/user-guide/users-and-roles.md) page.

## Best practices

* assign at least two instructors to a class if you want a real co-teacher review, not an auto-skip
* add a date of birth or guardian phone for every student before report card season starts — it blocks publishing otherwise
* decide your any/all approval thresholds before your first review cycle; changing them mid-cycle only affects reviews recorded after the change
* make sure at least one person with `report_cards:approve` has access to every location that runs classes, or final approval will stall with no way to clear it
* review a roster CSV's parsed preview carefully before importing — there's no dedup, so a mistaken duplicate becomes a second, separate student record
* prefer **Archive** over **Delete** for student custom fields you might need historical data from later

## Suggested screenshots

* Swim Lessons class list
* new class form with instructor checklist
* class detail page with roster and instructor manager
* roster CSV import preview with row errors
* Settings > Swim Lessons levels editor
* Browse curricula dialog and the adopt confirmation
* report card editor with grouped skills and comment prefill picker
* report card detail page mid-review, showing the review panel and review history
* the "no eligible final approver" warning state
* report card queue with pending reviews and a due-date period selected
* the public parent lookup page and a found report card


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.