Skip to content

Latest commit

 

History

History
115 lines (88 loc) · 5.32 KB

adding-a-module.md

File metadata and controls

115 lines (88 loc) · 5.32 KB

Adding a module

A module is a folder in src/modules/. The app finds it through src/modules/index.ts, and the home page and the navigation build themselves from that list.

1. Copy the example

Copy src/modules/example/ to a new folder named after your module, in lowercase with dashes, for example src/modules/pit-map/.

The id becomes the address: app.team4096.org/#/pit-map.

2. Rename things

In your new folder:

  • index.ts: change example to your module's name, and set id, name and purpose. Point load at your page file. Set group to the menu it belongs under in the top bar: build, competition, team, software (code and widgets) or more (the menus are listed in src/core/registry.ts).
  • Rename ExamplePage.tsx to match, such as PitMapPage.tsx.
  • data.ts: replace the notes with your module's data. Give the table a new name that starts with your module's, such as pit_map_spots (the first argument to createTable).
  • data.test.ts: replace the tests with tests for your functions.
  • README.md: say what the module does.

3. Add its table to the database

Each table in data.ts needs a matching table in the team database, made by a migration with its permission rules. Follow Adding a table: copy the example's migration and its tests in supabase/, and change them for your table.

npm test runs your migration and its tests on your computer, and checks that your example rows fit the table. A mentor reviews everything in supabase/ before it's merged; when it is, the database gets the new table automatically.

4. Register it

Add one import and one entry to src/modules/index.ts:

import { pitMap } from './pit-map'

export const modules: ModuleDefinition[] = [inventory, bom, scouting, status, pitMap, calendar, attendance, members, training, feedback, example]

This file is shared, so this line needs a mentor's approval.

Your module shows up in the top bar under the menu its group names, and on the home page under the same heading. Inside a menu, modules appear in the order of this list.

5. Check it

npm run check

The module tests in src/modules/modules.test.tsx check your module automatically: a unique id, a name, a purpose, and a page that draws for every role that can open it.

The pattern to follow

File Job
index.ts Tells the app about the module, including which menu it sits under (group). Optional useSummary puts a status on the home page tile; optional useAttention adds lines to "Needs attention". Both are hooks from data.ts. Optional notifications lists the kinds of notification it sends (see below).
data.ts The module's table (createTable), its rules, and the functions that change it. Pages never save anything themselves.
data.test.ts Tests for every function in data.ts. They run in demo mode.
SomethingPage.tsx The page. Reads rows with a use...() hook and changes them with functions from data.ts.
supabase/migrations/..._your_table.sql Makes the table in the team database, with its permission rules.
supabase/tests/your-table.test.ts Tests for the permission rules: who can and can't read, add, change and delete.

Tables. createTable('pit_map_spots', { demo: [...] }) gives you useRows() for pages, rows() for data.ts, and insert, update and remove. On your computer it uses your example rows; on the team site it uses the database, and changes from other people show up by themselves. Every row has id and created_at, and usually created_by, which the database fills in.

Functions that can fail return { ok: true, value } or { ok: false, error }, with the error written for the person using the app. insert, update and remove already work this way, so data.ts can check its own rules first and then return what they return.

Roles. useSession() gives the signed-in person's id, name and role. isMentor(role) is true for mentors and admins. isLeader(role) is true for student leaders, mentors and admins: use it for what student leaders can do too. Student leaders only get what a module gives them with isLeader() (and private.is_leader() in its migration), so anything checked with isMentor() stays with mentors. To limit a whole module to some roles, set roles in index.ts, for example roles: ['mentor', 'admin']. Hiding a button only keeps the page tidy: the database's permission rules are what stop people.

Showing who did something. useMemberNames() from src/core/members.ts turns a created_by id into a name.

Sending notifications. List each kind in index.ts, like Purchasing's { key: 'purchasing.request_updates', name: 'My requests', description: '...', on: true }. It gets its own switch under "What to notify me about" on the Notifications page by itself, for people who can open your module. The trigger in your migration that adds the notifications checks the switch first with private.wants_notification(member_id, 'your-module.kind', true) (the last value is the same as on), and fills in topic with the key. See supabase/migrations/..._notification_settings.sql. Adding a trigger needs a mentor.

Reading another module's data. Import from that module's data.ts, never from its pages. The BOM will read stock levels from ../inventory/data.