toga-ai 1.0.77 → 1.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ # toga2-hub (TOGA Hub) — 2.0 knowledge
2
+
3
+ | Doc | Summary | Files |
4
+ |-----|---------|-------|
5
+ | [TOGA Hub Architecture](architecture.md) | toga2-hub (TOGA Hub) is a React + TypeScript single-page admin portal for the TOGA platform. | toga2-hub/src/main.tsx, toga2-hub/src/routes.tsx, toga2-hub/src/App.tsx, toga2-hub/src/api/axiosInstance.ts, toga2-hub/src/api/apiFunctions.ts, toga2-hub/src/utils/queryHelpers.ts, toga2-hub/src/contexts/AuthContext.tsx, toga2-hub/src/stores/useUserStore.ts |
6
+ | [ACL Record Permissions — Rendering Semantics (Permissions Page)](features/acl-permissions-rendering.md) | How the TOGA Hub Permissions page must interpret the api2 endpoint `GET /v2/acl-record-permissions/getAclRecordPermissionsByRecordAndRole` and turn its response | toga2-hub/src/pages/Permissions/api/permissionsApi.ts, toga2-hub/src/pages/Permissions/utils/permissionsUtils.ts, toga2-hub/src/pages/Permissions/viewModel/useRolesViewModel.ts, toga2-hub/src/hooks/useAclPermissionsByRoleQuery.ts, toga2-hub/src/pages/Permissions/view/RolesView/RoleDetailView/LogicGroupView.tsx, toga2-hub/src/pages/Permissions/view/RecordsView/RecordDetailView/RecordDetailView.tsx, toga2-hub/src/hooks/useQueryData.ts |
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: TOGA Hub Architecture
3
+ framework: "2.0"
4
+ repo: toga2-hub
5
+ project: TOGA Hub
6
+ client: shared
7
+ type: architecture
8
+ status: active
9
+ updated: 2026-06-15
10
+ owners: [tcox]
11
+ files:
12
+ - toga2-hub/src/main.tsx
13
+ - toga2-hub/src/routes.tsx
14
+ - toga2-hub/src/App.tsx
15
+ - toga2-hub/src/api/axiosInstance.ts
16
+ - toga2-hub/src/api/apiFunctions.ts
17
+ - toga2-hub/src/utils/queryHelpers.ts
18
+ - toga2-hub/src/contexts/AuthContext.tsx
19
+ - toga2-hub/src/stores/useUserStore.ts
20
+ related:
21
+ - 2.0/apps/toga2-hub/features/acl-permissions-rendering.md
22
+ ---
23
+
24
+ ## Summary
25
+
26
+ toga2-hub (TOGA Hub) is a React + TypeScript single-page admin portal for the TOGA
27
+ platform. It is a pure frontend client — all business logic and persistence live in the
28
+ `api2` backend (framework 2.0), which Hub talks to exclusively over REST. The portal is
29
+ multi-tenant: it selects both its API endpoint and theme from the hostname it is served
30
+ from (e.g. `toga2-hub`, `compass.togahub`).
31
+
32
+ Primary surfaces: **Login** (auth), **Permissions** (manage roles, records, and ACL
33
+ field permissions), and **Users** (user management). The Permissions surface is the most
34
+ involved; its rendering rules are documented separately in
35
+ [ACL Record Permissions — Rendering Semantics](features/acl-permissions-rendering.md).
36
+
37
+ ## Stack
38
+
39
+ - React 18.3 + TypeScript 5.9, built with **Vite 5.4** (no Webpack).
40
+ - `@tanstack/react-query` 5.90 — server state, fetching, mutations.
41
+ - `zustand` 4.5 with `persist` — lightweight client state.
42
+ - `react-router-dom` — routing. `react-hook-form` 7.72 — forms.
43
+ - `axios` — HTTP client.
44
+ - `@agilant/toga-blox` — shared Agilant UI library (provides `EnvironmentBadge`,
45
+ `performLogout`, shared components).
46
+ - Tailwind CSS 3.4 (large custom palette: `hub-primary-*`, `supply-blue-*`, `navy-*`,
47
+ `crimson-*` — prefer these over stock Tailwind) + SCSS via `sass-embedded`.
48
+ - Cypress 15.5 for E2E. No unit-test runner is configured (despite `@types/jest`).
49
+
50
+ ## API layer (how it talks to api2)
51
+
52
+ Entry points: `src/api/axiosInstance.ts` (instance + interceptors), `src/api/apiFunctions.ts`
53
+ (`apiGet / apiPost / apiPut / apiDelete<T>(route, [data], options, params)`).
54
+
55
+ **Base-URL resolution is hostname-aware.** axiosInstance reads `window.location.hostname`,
56
+ uppercases the first segment, and looks up `VITE_API_<SEGMENT>` (e.g. host
57
+ `compass.togahub` → `VITE_API_COMPASS`), falling back to `VITE_API`. A new tenant hostname
58
+ needs a matching `VITE_API_*` env var.
59
+
60
+ **Auth tokens** (localStorage keys `accessToken`, `refreshToken`, `user`):
61
+ - No user yet → `POST /auth/public` obtains a public token.
62
+ - Logged in → uses the access token from login.
63
+ - On `401` → `POST /auth/refresh`; retry the original request. If refresh fails,
64
+ `performLogout()` (from toga-blox) runs.
65
+
66
+ **Interceptors**: every request receives a UUID `transactionId` query param (tracing) and
67
+ an `Authorization: Bearer <token>` header.
68
+
69
+ **Query language**: api2 uses a structured query object (`fields`, `join`, `ojoin`,
70
+ `where`, `sort`) serialized to a query string by `src/utils/queryHelpers.ts`
71
+ (`assembleOptions`). This is **not GraphQL**. Every read goes through it, so learn it early.
72
+ The `fields` parameter is an allowlist enforced by api2 (EV-8 validation in api2 `V2.php`):
73
+ a field not in the record's allowlist silently won't return, and a single prohibited field
74
+ can fail the whole fetch (see the ACL feature doc for the `Roles.id` / `EZ-2` example).
75
+ Casing must match exactly what api2 expects.
76
+
77
+ ## State management
78
+
79
+ - **AuthContext** (`src/contexts/AuthContext.tsx`) — `isAuthenticated`, `user`, `login()`,
80
+ `logout()`, `setUser()`, `isAuthInitialized`; syncs localStorage and listens for cross-tab
81
+ `storage` events.
82
+ - **Zustand** `useUserStore` (`src/stores/useUserStore.ts`) — persisted under key `zu-user`;
83
+ holds the user alongside AuthContext.
84
+ - **React Query** — configured in `main.tsx` with `retry: false`,
85
+ `refetchOnWindowFocus: false`.
86
+ - No Redux. Logout clears `accessToken`, `refreshToken`, `user`, and the `zu-user` store. If
87
+ logout cleanup ever grows, centralize the wipe rather than maintaining per-key lists
88
+ (per-key lists drift — a lesson learned the hard way in sibling repo toga2-commerce).
89
+
90
+ ## Routing & layout
91
+
92
+ - `src/main.tsx` — bootstraps the render tree: `QueryClientProvider` → `AuthProvider` →
93
+ `RouterProvider`.
94
+ - `src/routes.tsx` — routes: `/login` (unauthenticated), `/permissions`, `/users`; root `/`
95
+ redirects to `/permissions?tab=roles`. Permissions carries UI state in query params
96
+ (`?tab=roles|records&roleUuid=…&recordId=…`).
97
+ - `src/App.tsx` — authenticated layout: `AppHeader`, `EnvironmentBadge` (shows BETA/GAMMA/…),
98
+ `VerticalNavBar`, and the route `Outlet`.
99
+
100
+ ## Critical flows
101
+
102
+ - **Login** — `pages/Login/LoginPage.tsx` → `pages/Login/api/LoginApi.tsx`
103
+ (`validateEmailDomain`, `useSubmitLoginPassword`) → `viewModel/useLoginPageViewModel.ts`.
104
+ Flow: enter email → local validate → `validateEmailDomain` (`/domains`) →
105
+ `POST /auth/login` → store tokens + user → navigate to `/`. Login copy is data-driven from
106
+ `LOGINFIELDS.json` (edit the JSON, not the component).
107
+ - **Permissions** — `pages/Permissions/view/PermissionsPage.tsx` tabs into `RolesView/` and
108
+ `RecordsView/`. Rendering rules in the ACL feature doc.
109
+ - **Users** — `pages/Users/view/UsersPage.tsx` → `UsersView/`, `UserDetailView/`.
110
+
111
+ ## Build, environments & deploy
112
+
113
+ Scripts (`package.json`): `npm run dev`; `npm run togahub` / `npm run compass` (dev on a
114
+ specific hostname); `npm run build` = `tsc -b && vite build` (type-checks then bundles to
115
+ `dist/`); `npm run lint`; `npm run cypress`.
116
+
117
+ Environment config via `.env.<mode>` files; only `VITE_`-prefixed vars reach the client.
118
+
119
+ | Env | API base URL |
120
+ |---|---|
121
+ | production | `https://api.togahub.com/v2` |
122
+ | beta | `https://api.beta.togahub.com/v2` |
123
+ | alpha/gamma | `https://api.gamma.togahub.com/v2` |
124
+ | development | beta API by default |
125
+
126
+ > **Dev points at the BETA API by default.** When debugging data, confirm whether you're
127
+ > looking at beta or prod — beta and prod also differ in DB schema naming (the Core DB with
128
+ > RecordFields/ACL lives in a separate `prod-core` cluster; beta tenant schemas are named
129
+ > `Client`, prod tenant schemas `Client_<Tenant>`).
130
+
131
+ Hosting: Elastic Beanstalk (Agilant standard; use `/create-elastic-beanstalk` for new envs).
132
+ Served as a static SPA build; no custom `.ebextensions` in-repo.
133
+
134
+ ## Where to start (read in order)
135
+
136
+ 1. `src/main.tsx` — render tree, React Query + Auth providers.
137
+ 2. `src/routes.tsx` — routes.
138
+ 3. `src/App.tsx` — layout.
139
+ 4. `src/api/axiosInstance.ts` — hostname-aware base URL + auth tokens.
140
+ 5. `src/api/apiFunctions.ts` + `src/utils/queryHelpers.ts` — the api2 query language.
141
+ 6. `src/contexts/AuthContext.tsx` — session state.
142
+ 7. `src/pages/Permissions/view/PermissionsPage.tsx` — main surface (+ the ACL feature doc).
143
+ 8. `tailwind.config.js` — design tokens.
144
+
145
+ ## Open questions (unverified — confirm before relying on)
146
+
147
+ - axiosInstance has a deliberate ~1ms pre-request delay whose purpose is undocumented —
148
+ understand before removing.
149
+ - Possible query-param name inconsistency in the Permissions Records detail
150
+ (`recordUuid` written vs `recordId` read) — verify when next touching that path.
151
+ - `src/dummyData/*.json` mocks exist; some surfaces (e.g. the Records-tab permissions list)
152
+ may still render mock data — confirm a component hits the live API before debugging data.
153
+
154
+ ## Related docs
155
+
156
+ - [ACL Record Permissions — Rendering Semantics](features/acl-permissions-rendering.md)
@@ -0,0 +1,111 @@
1
+ ---
2
+ title: ACL Record Permissions — Rendering Semantics (Permissions Page)
3
+ framework: "2.0"
4
+ repo: toga2-hub
5
+ project: TOGA Hub
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-10
10
+ owners: [tcox]
11
+ files:
12
+ - toga2-hub/src/pages/Permissions/api/permissionsApi.ts
13
+ - toga2-hub/src/pages/Permissions/utils/permissionsUtils.ts
14
+ - toga2-hub/src/pages/Permissions/viewModel/useRolesViewModel.ts
15
+ - toga2-hub/src/hooks/useAclPermissionsByRoleQuery.ts
16
+ - toga2-hub/src/pages/Permissions/view/RolesView/RoleDetailView/LogicGroupView.tsx
17
+ - toga2-hub/src/pages/Permissions/view/RecordsView/RecordDetailView/RecordDetailView.tsx
18
+ - toga2-hub/src/hooks/useQueryData.ts
19
+ related: []
20
+ ---
21
+
22
+ ## Summary
23
+
24
+ How the TOGA Hub Permissions page must interpret the api2 endpoint
25
+ `GET /v2/acl-record-permissions/getAclRecordPermissionsByRecordAndRole` and turn its
26
+ response into the green-check / red-X permissions grid. Semantics confirmed in a design
27
+ review with jcardinal (Naperville, 2026-06-09); an earlier implementation over-thought
28
+ the rules and had to be corrected back to the simple reading below.
29
+
30
+ ## Endpoint usage
31
+
32
+ - `?recordId=N` (no roleId) → returns logic groups for **every role** on that record.
33
+ This feeds the **Roles tab record detail** (pick a role, then click a record): every
34
+ element of the response renders as its own table, labeled by role — the role
35
+ selection does NOT filter the tables ("that's useful for this rendering of this
36
+ page" — the unscoped call, per the 2026-06-09 review).
37
+ - The **Records tab** record detail does NOT render these tables — its permissions tab
38
+ is an app-sectioned permissions list (dummy data as of 2026-06-10, real data TBD).
39
+ - `?recordId=N&roleId=M` → returns only that role's logic groups. Supported by the API
40
+ for role-scoped use cases, but not currently used by the Hub UI.
41
+ - `recordId`/`roleId` are the numeric backend PKs, not UUIDs.
42
+ - **Gotcha:** `Roles.id` is not field-ACL-readable for hub users — requesting it on
43
+ `GET /roles` returns `EZ-2` 403 (`fields: ["id"]`, route `roles`, `aclDatabase:
44
+ CLIENT`), and a single prohibited field fails the whole fetch. Don't request `id` on
45
+ `/roles`. Suspect the same for `Apps.id` (needed for the appId → column mapping on
46
+ `/apps`) — if app-specific permissions render all-red, check the `/apps` response
47
+ first. Backend grant requests pending with jcardinal.
48
+
49
+ ## Response shape
50
+
51
+ `data.aclRecordPermissions.getAclRecordPermissionsByRecordAndRole` is an array of
52
+ `{ logicGroup, roles[] }`:
53
+
54
+ - `logicGroup` — the condition tree shown above the grid. `logicGroupExpressions` items
55
+ are `type: "EXPRESSION"` (leaf, with `recordExpression.description` as display label)
56
+ or `type: "GROUP"` (nested group with its own `operator` + expressions). A logic group
57
+ with `uuid: null` and no expressions means "no conditions" (e.g. the Public role).
58
+ - `roles[].permissions[]` — flat list of permission entries:
59
+ `{ uuid, appId, indirectRecordId, recordId, allowCreate, allowRead, allowUpdate, allowDelete }`.
60
+
61
+ ## Rendering rules (the part that was corrected)
62
+
63
+ The grid has one column per app (Supply, Hub, View, …) and one row per CRUD action.
64
+ Resolve a role's `permissions[]` array like this:
65
+
66
+ 1. **`appId: null` = ALL apps.** An entry with `appId: null` and all CRUD true means
67
+ green checks across the board, every column.
68
+ 2. **`appId: <id>` = that app's column only.** App id → column via `Apps.slug`
69
+ (`toga-supply` → supply column; app id 5 = Supply).
70
+ 3. **No `appId: null` entry present** → columns without an app-specific entry are all
71
+ false → **red X's**. E.g. a role whose only entry is `appId: 5` all-true renders
72
+ Supply fully green and every other column fully red.
73
+ 4. **Effective cell value = OR across all matching entries.** Redundant entries are
74
+ normal and harmless (e.g. `appId: null` all-true *plus* `appId: 5` all-true — the
75
+ second adds nothing). A role can also carry multiple `appId: null` entries; OR them
76
+ all in.
77
+ 5. Don't over-derive: the backend data is not normalized/human-curated yet (cleanup is
78
+ planned once Hub can visualize it). Render what the entries say, nothing more.
79
+ 6. **Every logic group is its own table under the record — never filter.** On the
80
+ Roles tab record detail, each element of the unscoped response renders as its own
81
+ conditions box + CRUD table, labeled with its role name. "Only one table shows" was
82
+ a recurring bug caused by client-side role filtering of the response — the original
83
+ build (multiple tables per record) was correct. Do not reintroduce a roleUuid filter
84
+ on the logic-group array.
85
+
86
+ ## indirectRecordId
87
+
88
+ Entries with `indirectRecordId` set are indirect grants managed by the backend
89
+ (observed: Compass Base role carries a direct `appId: null` entry and a second
90
+ `appId: null, indirectRecordId: 2` entry). The Hub UI ORs them into the rendered grid
91
+ but must **never POST/PUT/DELETE them** from the permissions save flow — only direct
92
+ entries (`indirectRecordId: null`) are editable. Exact backend semantics still to be
93
+ confirmed.
94
+
95
+ ## Save/cleanup conventions (Hub-side)
96
+
97
+ - All columns fully checked → collapse to a single `appId: null` entry and delete
98
+ redundant app-specific entries.
99
+ - Mixed state → delete any existing `appId: null` entry and write per-app entries.
100
+ - All unchecked → delete every direct entry for that role/record.
101
+
102
+ ## Validating against the database
103
+
104
+ Permissions live in Core DB table `AclRecordPermissions` (per-role CRUD), conditions in
105
+ the logic-group/record-expression tables, field-level in `AclFieldPermissions` (see api2
106
+ architecture doc). The API response is the source of truth for rendering; SQL is the
107
+ fallback for verifying unexpected data.
108
+
109
+ ## Change history
110
+ - 2026-06-15 — Added to the team knowledge base alongside the toga2-hub architecture doc. (tcox)
111
+ - 2026-06-09 — Rendering/save semantics confirmed in design review (appId:null collapse, mixed-state per-app writes, Core DB validation path). (jcardinal)
@@ -18,6 +18,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
18
18
  - **toga2-supply** (TOGa Supply) — 2 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
19
19
  - **saml** (SAML SSO Gateway) — 2 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
20
20
  - **toga2-view** (TOGa View Frontend) — 0 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
21
+ - **toga2-hub** (TOGA Hub) — 2 doc(s) → [2.0/apps/toga2-hub/INDEX.md](2.0/apps/toga2-hub/INDEX.md)
21
22
 
22
23
  ## Clients
23
24
 
@@ -9,5 +9,7 @@
9
9
  { "repo": "saml", "project": "SAML SSO Gateway", "framework": "2.0", "role": "app", "dependsOn": [] },
10
10
  { "repo": "toga2-view", "project": "TOGa View Frontend", "framework": "2.0", "role": "app", "dependsOn": ["api2"] },
11
11
  { "repo": "togadesk", "project": "TOGa Desk", "framework": "1.0", "role": "app", "dependsOn": [] },
12
- { "repo": "togaview", "project": "TOGa View", "framework": "1.0", "role": "app", "dependsOn": [] }
12
+ { "repo": "togaview", "project": "TOGa View", "framework": "1.0", "role": "app", "dependsOn": [] },
13
+ { "repo": "toga2-hub", "project": "TOGA Hub", "framework": "2.0", "role": "app", "dependsOn": ["api2"] }
14
+
13
15
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.77",
3
+ "version": "1.0.78",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",