There was no UI to assign permissions to the Organization Admin role,
even though the backend's Role→Permission endpoints
(@tria-plc/iamapi-common's role-permissions routes) were already live
and unused. Adds a dedicated page — application picker + the existing
permission checklist — that assigns/reads permissions for the fixed
Organization Admin role, linked from the Organization Admins page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The "Copy Permissions From" dropdown in the position-type form only
filtered candidates by the selected organization's full unit set,
ignoring the form's own "Select Unit" field entirely — so switching
units never re-scoped the list, unlike the equivalent unit filter on
the position-management table.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"Copy permissions from" listed every position type in every
organization, because the dropdown read an unfiltered GET
/position-types. IAM exposes no organization-scoped route and carries no
organizationId on a position type, so the list is now narrowed
client-side to the built-in (isSystem) types plus those whose unit
belongs to the selected organization, with the type being edited
excluded.
Also in position management:
- Invalidate every position-type cache key root after a mutation. React
Query matches prefixes element by element, so ["position-type"] never
reached ["position-types-common", ...] and the department pickers kept
serving a stale list. invalidatePositionTypeQueries() covers all three
roots and is shared by the hook and the form.
- Drop getByOrganizationId and getCommonTypesByOrganizationId. Both
issued the same requests as their unit counterparts and had no callers.
- Surface errors that were being swallowed. Three mutations had empty
onError handlers, hiding IAM's 403 for built-in position types, and
CreatePositionForm's bare catch discarded the reason for every failure.
- Move organization and unit into the zod schema so they validate with
translated messages and inline errors instead of an ad-hoc toast, and
keep submit disabled through the permission-assignment call that
follows the save.
- Report the two outcomes the form used to hide: a save that succeeded
while permission assignment failed, and clearing every permission,
which assign-seconds-for-first cannot express.
- Fix the list page's loading and error states, which rendered the
"Add User" string as a spinner, ignored the unit-scoped query, and
left the export button stuck after a failed download.
- Halve PermissionSearch's requests. It fetched 50 rows, read the total
off the response and immediately refetched, and it re-filtered results
on the undebounced term, blanking the list while typing.
Remove the three record toggles. They never worked: IAM's
PositionTypeConfiguration holds only { id, organizationId,
positionTypeId, timeframe } in every published build, canAssignRecord
and canCreateBankRecord exist nowhere in the package, and the global
ValidationPipe runs with forbidNonWhitelisted, so every write was a 400.
The reads were broken too, passing a positionTypeId to a route that
filters on organizationId. A TODO records where the real flag lives:
PositionConfiguration.canReceiveRecord, keyed by positionId.
Delete ActionsColumn.tsx, which had no references.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Creating a department under a unit required picking a position type,
which is not always known at that point. Sub-department creation is
unchanged and still requires one.
PositionPayload.positionTypeId becomes optional and the field is omitted
from the request body when unset rather than sent as an empty string.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>