mAi
2ed0ef3177
feat(team-admin): t-paliad-223 Slice A — Project Admin role + inheritable role-edit gate
...
#48 — adds 'admin' as fifth project_teams.responsibility value, plumbs an
inheritable role-edit gate via the materialised ltree path.
- migration 110: ALTER responsibility CHECK, CREATE paliad.effective_project_admin(uuid,uuid) STABLE SECURITY DEFINER (mirrors can_see_project shape), REPLACE project_teams_update / _insert / _delete RLS policies. Idempotent + down-mig provided. Dry-run BEGIN..ROLLBACK clean on live supabase.
- services/approval_levels.go: ResponsibilityAdmin const + IsValidResponsibility extension. responsibilityOpensGate UNCHANGED — admin is orthogonal to the 4-Augen approval gate.
- services/team_service.go: ChangeResponsibility() with last-admin guard inside tx (counts admins on project + ancestor chain, excludes the row being changed). RemoveMember() also runs the guard when removing an admin row. New IsEffectiveProjectAdmin() driving the frontend affordance. legacyRoleFromResponsibility: admin → 'lead' (deprecated shadow column).
- services/project_service.go: ErrLastProjectAdmin sentinel mapped to 409 in writeServiceError.
- handlers/teams.go: new PATCH /api/projects/{id}/team/{user_id}. RLS-enforced; non-admins get 404 to avoid existence leakage.
- handlers/projects.go: GET /api/projects/{id} now wraps the payload with effective_admin bool so the frontend drives the inline-select affordance without a second round-trip.
- frontend/src/projects-detail.tsx + client/projects-detail.ts: admin appears as 5th option in 'Mitglied hinzufügen' dropdown. Team-list Rolle cell switches to an inline <select> for callers with effective_admin (read-only span otherwise). Optimistic PATCH with rollback on error (last-admin guard / 403 from RLS / etc.) surfaced as transient toast in #team-msg.
- i18n: +6 keys (admin label + admin.hint + 3 error toasts × 2 langs).
- tests: TestIsValidResponsibility now covers admin; new TestLegacyRoleFromResponsibility pins the mapping table.
go build && go test -short ./internal/... && bun run build all clean.
2026-05-20 14:46:36 +02:00
..
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-30 03:42:25 +02:00
2026-04-16 13:54:19 +02:00
2026-04-30 03:42:25 +02:00
2026-04-16 13:54:19 +02:00
2026-04-30 03:42:25 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 17:11:02 +02:00
2026-04-16 17:11:02 +02:00
2026-04-17 11:59:49 +02:00
2026-04-17 11:59:49 +02:00
2026-04-17 13:54:32 +02:00
2026-04-17 13:54:32 +02:00
2026-04-18 20:26:11 +02:00
2026-04-18 20:26:11 +02:00
2026-04-20 12:34:38 +02:00
2026-04-20 12:34:38 +02:00
2026-04-20 13:17:24 +02:00
2026-04-20 13:17:24 +02:00
2026-04-20 17:35:38 +02:00
2026-04-20 17:35:38 +02:00
2026-04-20 17:35:38 +02:00
2026-04-20 17:35:38 +02:00
2026-04-23 01:00:31 +02:00
2026-04-23 01:00:31 +02:00
2026-04-25 23:37:51 +02:00
2026-04-25 23:37:51 +02:00
2026-04-27 11:47:10 +02:00
2026-04-27 11:47:10 +02:00
2026-04-27 14:59:03 +02:00
2026-04-27 14:59:03 +02:00
2026-04-27 19:34:56 +02:00
2026-04-27 19:34:56 +02:00
2026-04-28 13:05:22 +02:00
2026-04-28 13:05:22 +02:00
2026-04-29 22:09:39 +02:00
2026-04-29 22:09:39 +02:00
2026-04-30 03:42:25 +02:00
2026-04-29 22:17:32 +02:00
2026-04-30 10:54:46 +02:00
2026-04-30 10:54:46 +02:00
2026-04-30 11:11:47 +02:00
2026-04-30 11:11:47 +02:00
2026-04-30 12:49:04 +02:00
2026-04-30 12:49:04 +02:00
2026-04-30 16:41:38 +02:00
2026-04-30 16:41:38 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:57:54 +02:00
2026-05-04 14:57:54 +02:00
2026-05-04 17:03:58 +02:00
2026-05-04 17:03:58 +02:00
2026-05-04 17:03:58 +02:00
2026-05-04 17:03:58 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 01:25:03 +02:00
2026-05-05 01:25:03 +02:00
2026-05-05 01:49:01 +02:00
2026-05-05 01:49:01 +02:00
2026-05-05 02:19:37 +02:00
2026-05-05 02:19:37 +02:00
2026-05-05 02:48:31 +02:00
2026-05-05 02:48:31 +02:00
2026-05-05 03:17:46 +02:00
2026-05-05 03:17:46 +02:00
2026-05-05 03:46:45 +02:00
2026-05-05 03:46:45 +02:00
2026-05-05 04:32:50 +02:00
2026-05-05 04:32:50 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:22:14 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:53:13 +02:00
2026-05-05 11:53:13 +02:00
2026-05-05 13:29:47 +02:00
2026-05-05 13:29:47 +02:00
2026-05-06 12:37:08 +02:00
2026-05-06 12:37:08 +02:00
2026-05-06 15:13:26 +02:00
2026-05-06 15:13:26 +02:00
2026-05-06 16:45:19 +02:00
2026-05-06 16:45:19 +02:00
2026-05-07 12:51:37 +02:00
2026-05-07 12:51:37 +02:00
2026-05-07 20:58:57 +02:00
2026-05-07 20:58:57 +02:00
2026-05-07 21:49:33 +02:00
2026-05-07 21:49:33 +02:00
2026-05-07 22:00:26 +02:00
2026-05-07 22:00:26 +02:00
2026-05-07 22:21:45 +02:00
2026-05-07 22:21:45 +02:00
2026-05-07 22:41:18 +02:00
2026-05-07 22:41:18 +02:00
2026-05-08 02:13:58 +02:00
2026-05-08 02:13:58 +02:00
2026-05-08 16:00:21 +02:00
2026-05-08 16:00:21 +02:00
2026-05-08 16:23:12 +02:00
2026-05-08 16:23:12 +02:00
2026-05-08 16:54:34 +02:00
2026-05-08 16:54:34 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 19:15:44 +02:00
2026-05-08 19:15:44 +02:00
2026-05-08 19:20:52 +02:00
2026-05-08 19:20:52 +02:00
2026-05-08 19:42:05 +02:00
2026-05-08 19:42:05 +02:00
2026-05-08 20:21:13 +02:00
2026-05-08 20:21:13 +02:00
2026-05-08 21:52:50 +02:00
2026-05-08 21:52:50 +02:00
2026-05-08 22:01:44 +02:00
2026-05-08 22:01:44 +02:00
2026-05-08 22:16:55 +02:00
2026-05-08 22:16:55 +02:00
2026-05-08 23:33:53 +02:00
2026-05-08 23:33:53 +02:00
2026-05-09 15:33:20 +02:00
2026-05-09 15:33:20 +02:00
2026-05-09 16:07:17 +02:00
2026-05-09 16:07:17 +02:00
2026-05-15 00:19:19 +02:00
2026-05-15 00:19:19 +02:00
2026-05-15 00:19:31 +02:00
2026-05-15 00:19:31 +02:00
2026-05-15 00:19:37 +02:00
2026-05-15 00:19:37 +02:00
2026-05-15 00:28:38 +02:00
2026-05-15 00:28:38 +02:00
2026-05-15 00:28:49 +02:00
2026-05-15 00:28:49 +02:00
2026-05-15 00:29:00 +02:00
2026-05-15 00:29:00 +02:00
2026-05-15 00:40:50 +02:00
2026-05-15 00:40:50 +02:00
2026-05-15 00:40:59 +02:00
2026-05-15 00:40:59 +02:00
2026-05-15 01:01:08 +02:00
2026-05-15 01:01:08 +02:00
2026-05-15 01:01:17 +02:00
2026-05-15 01:01:17 +02:00
2026-05-15 01:37:34 +02:00
2026-05-15 01:37:34 +02:00
2026-05-15 01:37:57 +02:00
2026-05-15 01:37:57 +02:00
2026-05-15 17:53:08 +02:00
2026-05-15 17:53:08 +02:00
2026-05-16 01:17:23 +02:00
2026-05-16 01:17:23 +02:00
2026-05-16 01:29:31 +02:00
2026-05-16 01:29:31 +02:00
2026-05-17 12:30:53 +02:00
2026-05-17 12:30:53 +02:00
2026-05-18 11:46:12 +02:00
2026-05-18 11:46:12 +02:00
2026-05-18 12:13:13 +02:00
2026-05-18 12:13:13 +02:00
2026-05-18 15:39:03 +02:00
2026-05-18 15:39:03 +02:00
2026-05-18 15:05:46 +02:00
2026-05-18 17:28:19 +02:00
2026-05-18 17:29:21 +02:00
2026-05-18 17:33:01 +02:00
2026-05-18 17:46:08 +02:00
2026-05-18 17:46:08 +02:00
2026-05-19 12:44:27 +02:00
2026-05-19 12:44:27 +02:00
2026-05-19 12:51:52 +02:00
2026-05-19 12:51:52 +02:00
2026-05-20 09:50:07 +02:00
2026-05-20 09:50:07 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:52:28 +02:00
2026-05-20 09:52:28 +02:00
2026-05-20 13:05:27 +02:00
2026-05-20 13:05:27 +02:00
2026-05-20 13:26:23 +02:00
2026-05-20 13:26:23 +02:00
2026-05-20 13:55:56 +02:00
2026-05-20 13:55:56 +02:00
2026-05-20 14:43:42 +02:00
2026-05-20 14:43:42 +02:00
2026-05-20 14:46:36 +02:00
2026-05-20 14:46:36 +02:00