Creating org units β
Once your levels exist, you fill them with units β the actual things that get a leader, a member count, and a pin on the map. A unit is one row in the org_units table; the page you land on for it is your hub for everything that unit does.
Two ways in β
GCM lets you create units from two surfaces. They write to the same table; pick whichever fits the moment.
| Surface | Best for | URL |
|---|---|---|
| Org Structure tree | The full top-down picture. Drag-free, but you see every level at once | /org-structure |
| Level page | Bulk-adding peers at one level (12 cells in a row, say) | /hierarchy/3 |
The tree page is the default landing for the module. It shows level 1 units at the root and lets you expand to drill into their children.

From the tree page β
Click Add Branch (or whatever your level 1 is named) in the top right to create a top-level unit. For lower levels, expand a parent, click the three-dot menu on the row, and pick Add Center (or whatever the next level is named).
The dialog asks for:
- Name (required) β e.g. Downtown Branch. Up to two lines wrap on the cards, so descriptive names are fine.
- Parent (required for levels > 1) β picked from a searchable dropdown of units one level up. The picker is filtered to the right level automatically.
- Address (optional) β free-text street address. Powers the map module if you also set a map location.
- Map location (optional) β opens a Leaflet picker. Drop a pin or paste lat/lng. If you set an address but not a map location, GCM queues a background geocode and fills
estimated_map_locationonce the result lands.
Click Create. The new row appears in the tree, the parent's child count ticks up, the dashboard's "X units" stat refreshes.
From a level page β
If you're adding many units at the same level β say twelve cell-groups for a branch β drill in via the URL /hierarchy/3 (substitute the level you're working on). You'll see a card grid of every unit at that level filtered by the parent picker at the top.
The level-page workflow is the same dialog under the hood, but you get:
- A parent filter so newly added units default to the right parent.
- Bulk actions β select 3 units and reparent them to a new branch in one click.
- Sort controls β name AβZ, member count desc, recently added.
This is the faster path when you're seeding lots of leaf-level units after a restructure.
What gets stored β
Every unit row has these columns the UI surfaces:
| Column | Type | Notes |
|---|---|---|
id | UUID | Generated client-side |
organization_id | UUID | Set automatically from your session |
level_index | int | The tier the unit belongs to |
parent_unit_id | UUID | Null on level 1, required below |
name | text | Display name |
address | text | Optional postal address |
map_location | text | Optional lat/lng β wins over estimated |
estimated_map_location | text | Filled by geocoder when address is set |
leader_member_id | UUID | Optional β see Assign leaders |
archived_at | timestamptz | Null for active rows |
deleted_at | timestamptz | Null for visible rows; soft-delete |
Created and updated timestamps + actor IDs are written automatically. You can see them on the unit detail page in the "audit" line just under the name.
Editing a unit β
Open the unit (click its row in the tree or its card on a level page). Click Edit in the header. The same dialog comes back populated. You can change:
- The name.
- The parent β moving the unit moves all its descendants with it. Members on the unit stay where they are; the cascade only changes the tree shape.
- The address and map location.
Saving doesn't ask for confirmation. Reports refresh the next time they re-query.
Moving units changes report scope
If you reparent Downtown Center from South Branch to North Branch, every member assigned to Downtown Center (and its children) now rolls up to North Branch. Sunday attendance dashboards may shift overnight β communicate the change before doing it.
Map location and the map module β
Setting map_location on a unit gives it a pin on the Map module. Pin color is determined by level. Pin clustering kicks in around level 3+ if you have many units in a small area.
You can also leave map_location blank and let the address geocode in the background. The estimated location populates estimated_map_location and the map shows the pin in a slightly muted color to indicate it's a guess.
If geocoding fails (bad address, rate-limit), the status badge on the unit page shows pending β failed. You can fix the address and click into the unit again to re-trigger the geocoder.
Auto-generated member counts β
The member count badge on each unit row is computed via the count_org_unit_members RPC, not by reading raw rows. The RPC handles cascade β it sums the unit's own assignments plus every descendant. That's why moving a unit doesn't break the count.
The badge updates within a few seconds of an assignment change. If it looks stale, refresh the page; React Query revalidates on focus.
Common questions β
Why can't I drag units to reparent? The tree intentionally has no drag affordance because hierarchy moves cascade in non-obvious ways. The deliberate Edit β change parent β Save flow forces a beat of thought.
Can I have two units with the same name? Yes. The UI doesn't enforce uniqueness because North Cell might legitimately exist under two different branches. If you want to dedupe, use the parent name as a prefix.
What's the practical maximum number of units? A few thousand per org has been tested. The tree-render virtualizes when you expand a heavy subtree; the queries paginate. Beyond ~10,000 units consider whether some are actually members (e.g. if every household is a unit) and restructure.
Next steps β
- Wire up parent-child relationships β read the tree, expand and collapse, drill in.
- Assign members β link people to your shiny new units.
- Assign leaders β give shepherds a scope.
