PSA/ee/docs/plans/client-portal-extension-support.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

73 lines
4.2 KiB
Markdown

# Plan: Client Portal Extension Support
## Objective
Extend the existing extension system to support the Client Portal. This involves adding a new hook for the client portal menu, implementing the discovery logic, adding the navigation UI, and creating the runtime page for client portal extensions.
Status update (2025-11-21):
- Manifest schema includes `ui.hooks.clientPortalMenu`; backend discovery and client portal menu rendering are implemented (`server/src/app/client-portal/extensions/[id]/page.tsx`, `ClientPortalLayout`, menu component).
- Runtime iframe uses `buildExtUiSrc` and `bootstrapIframe` to load Runner-hosted UI; client portal page exists and matches MSP flow.
- Follow-up: add automated verification/tests and doc links from client portal docs.
## Context
The current extension system works for the MSP portal (`/msp/*`). We need to replicate/adapt this for the Client Portal (`/client-portal/*`).
- **Manifest:** Extensions declare `ui.hooks.appMenu` for MSP. We will add `ui.hooks.clientPortalMenu` for Client Portal.
- **Discovery:** `listAppMenuItemsForTenant` finds MSP extensions. We need `listClientPortalMenuItemsForTenant`.
- **Navigation:** MSP has a dynamic sidebar. Client Portal has a top navbar with hardcoded links.
- **Runtime:** Extensions run in iframes (`/msp/extensions/[id]`). We need `/client-portal/extensions/[id]`.
## Phase 1: Manifest & Schema Updates
- [x] **Update Manifest Schema:**
- Modify `ee/server/src/lib/extensions/schemas/manifest-v2.schema.ts`.
- Add `clientPortalMenu` to `uiHooksSchema`.
- Structure: `{ label: string }`.
## Phase 2: Backend Discovery Logic
- [x] **Create Server Action:**
- Create `ee/server/src/lib/actions/clientPortalExtActions.ts` (or similar).
- Implement `listClientPortalMenuItemsForTenant()`:
- Query `tenant_extension_install` + `extension_version` + `extension_registry`.
- Filter by `is_enabled` and `ui.hooks.clientPortalMenu`.
- Return `{ id, label }`.
- Ensure strictly typed return values.
## Phase 3: Frontend Navigation
- [x] **Create Menu Component:**
- Create `server/src/components/client-portal/ClientExtensionsMenu.tsx`.
- Fetch items using `listClientPortalMenuItemsForTenant`.
- Render as a dropdown or list of links in the top navbar.
- Use `ClientPortalLayout` styling (Tailwind).
- [x] **Integrate into Layout:**
- Modify `server/src/components/layout/ClientPortalLayout.tsx`.
- Add `ClientExtensionsMenu` to the navigation bar (e.g., next to "Appointments" or in a "More" dropdown if needed, or just append to the list).
## Phase 4: Runtime Implementation
- [x] **Create Extension Page:**
- Create `server/src/app/client-portal/extensions/[id]/page.tsx`.
- **Auth Check:** Ensure the user is a valid client portal user.
- **Extension Info:** Fetch extension install info (reuse `getInstallInfo` if compatible, or create client-specific version if permissions differ).
- **Render:** Use `DockerExtensionIframe` (or a wrapped version) to render the extension.
- **URL Construction:** Use `buildExtUiSrc` with appropriate paths.
- [x] **Verify Iframe Context:**
- Ensure the extension receives appropriate context (if any) for the client portal.
- *Note:* Current `bootstrapIframe` is minimal. If the extension needs to know it's in "Client Mode", we might need to pass query params or handle it in the extension itself. For now, we assume the extension uses the `clientPortalMenu` hook implies it knows how to behave.
## Phase 5: Verification
- [x] **Test Case 1:** Extension with `clientPortalMenu`.
- Verify it appears in the menu.
- Verify clicking loads the iframe.
- [x] **Test Case 2:** Extension with `appMenu` ONLY.
- Verify it does NOT appear in the client portal menu.
- [x] **Test Case 3:** Extension with BOTH.
- Verify it appears in both (if logged in as appropriate user).
- [x] **Security:** Verify a client user cannot access an MSP-only extension by guessing the ID (if we implement permission checks in the page loader).
## Technical Details
- **Directories:**
- `ee/server/src/lib/extensions/`
- `server/src/components/layout/`
- `server/src/app/client-portal/`
- **Key Files:**
- `manifest-v2.schema.ts`
- `ClientPortalLayout.tsx`
- `DockerExtensionIframe.tsx`