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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
13 KiB
13 KiB
Hudu Integration — Phase 2: Assets & Documents
- Status: Draft (pending scope confirmation)
- Phase: 2 (still pull-only, Hudu → AlgaPSA)
- Predecessor:
ee/docs/plans/2026-06-08-hudu-integration(Phase 1, complete)
Overview
Phase 1 connected Hudu and surfaced a mapped company's assets, articles, and passwords read-only on the client page. Phase 2 deepens two of those surfaces:
- Assets become first-class: Hudu assets can be mapped to existing AlgaPSA assets, imported (created in Alga from Hudu), and kept current with a manual pull sync that updates synced fields on mapped assets.
- Articles meet Alga's Documents surfaces (link-only, no content copy): a "Hudu Documentation" section on the client's Documents tab, and a "Hudu" tab on the main Documents page listing/searching articles across all mapped companies.
Phase 1 principles that still hold: EE-only behind the hudu-integration
flag; pull-only; deep-link to Hudu for content; never persist article bodies
or passwords; system_settings RBAC for integration administration.
Problem / User Value
- Technicians track devices in both systems by hand. A Hudu asset and its Alga asset (used for tickets/billing) have no link, so device context is re-discovered on every ticket.
- MSPs onboarding to AlgaPSA already have their device inventory in Hudu; re-entering it manually is the single biggest adoption blocker for Alga's asset module.
- Runbooks/KB articles live in Hudu but technicians work documents from Alga's Documents surfaces; today they must remember which system holds what.
Goals
- G1. Per-asset mapping between Hudu assets and Alga assets (per client).
- G2. One-click import of unmatched Hudu assets into Alga (single + bulk).
- G3. Manual "Sync from Hudu" per client that pull-updates synced fields on mapped assets and flags Hudu-side disappearances.
- G4. Link-only "Hudu Documentation" section in the client Documents tab.
- G5. "Hudu" tab on the main Documents page: cross-client article list with search, client resolution, and deep-links.
Non-goals (Phase 2)
- Push (Alga → Hudu) in any form; the data model stays direction-agnostic.
- Scheduled/background sync (Temporal or otherwise). Sync is manual this phase; a low-frequency scheduled refresh remains a documented future step.
- Importing article content as Alga documents (link-only by decision).
- Custom-field sync beyond the core field set (name, serial number, asset type via layout map). Hudu layout custom fields are displayed in Phase 1's read-only view only.
- Deleting or archiving Alga assets in response to Hudu changes — sync never destroys Alga data; it only flags.
- Mapping Hudu passwords to assets (unchanged from Phase 1; company-scoped).
Personas & Primary Flows
- MSP Admin — configures the asset-layout→asset-type map; runs bulk import.
- MSP Technician — maps/imports individual assets, runs Sync, browses Hudu documentation from Documents surfaces.
Flows:
- Configure layout map (Admin): Settings → Integrations → Hudu →
"Asset layouts" block → each Hudu asset layout gets an Alga asset type
(heuristic prefill,
unknownfallback) → Save. - Map/import assets (Technician): Client → Hudu tab → Assets section now shows per-asset state (Mapped / Suggested / Unmapped) with an Alga-asset picker per row, an Import action per unmatched row, and "Import all unmatched". Staged changes commit via an explicit Save bar (same confirmation pattern as Phase 1 company mappings).
- Sync (Technician): Client → Hudu tab → "Sync from Hudu" → re-fetches the company's assets, updates synced fields on mapped Alga assets, reports created/updated/stale counts.
- Client documents (Technician): Client → Documents tab → "Hudu Documentation" section lists the mapped company's articles (name, updated_at) → click → opens in Hudu.
- Global documents (Technician): Documents page → "Hudu" tab → paged, searchable article list across companies; each row shows the resolved Alga client (or "unmapped") and deep-links to Hudu.
UX / UI Notes
- Settings — Asset layouts block (inside
HuduIntegrationSettings, below Company Mappings): table of Hudu asset layouts (fetched live) × Alga asset type select (workstation | network_device | server | mobile_device | printer | unknown). Heuristic prefill by layout-name keywords (server→server; workstation/desktop/laptop→workstation; printer→printer; phone/mobile/tablet→mobile_device; network/switch/router/firewall/access point→network_device; else unknown). Explicit Save; persisted per tenant. - Client Hudu tab — Assets section becomes an asset mapping manager, visually consistent with the company mapping manager: counters (mapped/suggested/unmapped), per-row Alga asset picker + status badge, row actions (revert / unmap / dismiss suggestion), Import per row, "Import all unmatched" button, staged-changes Save/Discard bar, and a "Sync from Hudu" button with last-synced timestamp and result summary. Mapped rows whose Hudu asset disappeared/archived show a Stale badge.
- Client Documents tab — Hudu section: collapsed-by-default section "Hudu Documentation (N)" listing articles name + updated_at + external-link icon. Only rendered when the Phase 1 client-tab gate passes (EE + flag + connected + mapped). No upload/edit affordances.
- Documents page — Hudu tab: tab visible when EE + flag + connected. Search box (server-side, passed to Hudu), paged table (25/page mirroring Hudu pages): article name, company name → resolved client name (or "Unmapped" badge), updated_at, open-in-Hudu. No bulk fetch of all pages.
- All new strings via i18n (en + de/es/fr/it/nl/pl/pt), following Phase 1
key families (
integrations.hudu.assets.*,integrations.hudu.documents.*,documents.huduTab.*,clientDetails.huduDocs.*as applicable).
Functional Requirements
Assets — mapping:
- FR1. Asset mapping rows reuse
tenant_external_entity_mappingswithintegration_type='hudu',alga_entity_type='asset',external_entity_id=<hudu asset id>; metadata carrieshudu_asset_name,hudu_company_id,asset_layout_id,asset_layout_name,primary_serial,url. One-to-one both directions per tenant (existing unique indexes are scoped byalga_entity_type). - FR2. Auto-suggest matches per Hudu asset against the client's Alga assets: serial exact (confidence 1.0) → name exact (0.9) → name fuzzy ≥0.8 (reusing the Phase 1 normalized-Levenshtein matcher incl. suffix rules); one-to-one greedy claiming, mapped rows/assets excluded.
- FR3. Mapping UI follows the staged-changes pattern: picker stages, Save commits (clear+set for replace), Discard reverts, suggestions are confirmed by Save and dismissible per row.
Assets — import:
- FR4. Import creates an Alga asset via the existing
createAssetaction:client_id= the mapped client,name= Hudu asset name,serial_number=primary_serial(when present),asset_type= layout map lookup (fallbackunknown),asset_tag=primary_serialif unique elsehudu-<hudu asset id>,status= the tenant's default/first asset status (same default the manual create form uses). A mapping row is created atomically with the asset. - FR5. "Import all unmatched" imports every unmapped, unsuggested Hudu asset for the client sequentially, reporting created/failed counts; individual failures don't abort the batch.
- FR6. Import requires the
assetRBAC create permission (in addition to the Phase 1 client-tab gate); the UI hides Import affordances without it.
Assets — sync:
- FR7. "Sync from Hudu" re-fetches the company's Hudu assets and, for each
mapped pair, updates the Alga asset's synced fields —
name,serial_number— when they differ (Hudu wins on synced fields only; other Alga fields untouched).asset_typeis not retro-changed. - FR8. Sync flags mappings whose Hudu asset is archived/absent as stale
(metadata
stale: true+ UI badge); it never deletes Alga assets or mappings. Unflagging happens automatically if the asset reappears. - FR9. Sync records
last_synced_aton affected mapping rows and surfaces a result summary (updated / unchanged / stale counts) in the UI. - FR10. Sync requires the
assetRBAC update permission.
Layout→type map:
- FR11.
hudu_integrations.settings.asset_layout_type_map(jsonb:{ "<layout_id>": "<alga asset_type>" }) with server action get/set (system_settings update RBAC) and live layout listing viaGET /api/v1/asset_layouts. - FR12. Heuristic prefill computes suggested types for unconfigured layouts
(UI-side); unconfigured layouts import as
unknown.
Documents — client section:
- FR13. Client Documents tab renders a link-only "Hudu Documentation" section (name, updated_at, deep-link) using the Phase 1 per-company articles fetch/cache; same gate as the client Hudu tab; collapsed by default; independent error/empty states that never break the native documents UI.
Documents — global tab:
- FR14. A server action lists Hudu articles across companies (no
company_idfilter), one Hudu page per request (25 items), passing the user's search term to Hudu, and resolves each article'scompany_idto the mapped Alga client via the companies cache + mapping rows. - FR15. Documents page "Hudu" tab renders the paged list with search, resolved client names ("Unmapped" badge otherwise), and deep-links. Visible only when EE + flag + connected; CE/flag-off tenants see no trace.
- FR16. All new server surfaces enforce the Phase 1 guard chain (EE add-on + tier + flag + RBAC) server-side, not just in the UI.
Non-functional Requirements
- NFR1. Pull-only; no Hudu writes anywhere.
- NFR2. Respect Hudu limits: never fan out unbounded page fetches; bulk import/sync fetch the company's assets with bounded pagination and stop on rate-limit (429) with a typed, user-visible error.
- NFR3. Article content is never persisted; only ids/names/timestamps already cached for lists.
- NFR4. Mapping rows remain direction-agnostic (no pull-only columns).
- NFR5. New UI strings fully translated (8 locales) and pass the Phase 1 i18n static checks (extended to new components).
Data / Integration Notes
- No new tables. Asset mappings reuse
tenant_external_entity_mappings(indexes verified: uniqueness is peralga_entity_type, so client and asset mappings coexist). Layout map lives inhudu_integrations.settings. - New
HuduClientsurfaces:GET /api/v1/asset_layouts, globalGET /api/v1/articles?page=&search=(verify Hudu's article search param name during implementation; fall back to?name=filter if needed). - Alga asset creation:
createAssetinpackages/assets/src/actions/ assetActions.ts(CreateAssetRequest: fixedasset_typeenum, requiredasset_tag/status). - Client Documents tab host:
ClientDetails.tsxtab iddocuments; EE injection follows the Phase 1useHuduClientTabprecedent. - Documents page:
packages/documents/src/components/DocumentsPage.tsx.
Risks
- R1. Hudu asset layouts are arbitrary per instance; the heuristic prefill
will misclassify exotic layouts → mitigated by explicit admin map +
unknownfallback. - R2.
asset_taguniqueness semantics in Alga need verification before usingprimary_serialas tag (fallbackhudu-<id>is always unique). - R3. Cross-client article listing's client resolution depends on the companies cache freshness; stale cache shows "Unmapped" — acceptable, refreshable.
- R4. DocumentsPage is a CE package surface; the EE tab must be injected without breaking CE builds (follow the ClientDetails gate-hook pattern).
Open Questions
- OQ1. Hudu global article search: exact query param (
searchvsname) and whether it spans body or title only — verify against the local instance during implementation. - OQ2. Asset status default on import: confirm the tenant's default status source used by the manual create form and reuse it.
Acceptance Criteria
- AC1. A technician can map, import (single + bulk), and sync a client's Hudu assets entirely from the client Hudu tab, with explicit Save confirmation and no destructive surprises (stale ≠ deleted).
- AC2. An imported asset appears in Alga's asset module with correct client, name, serial, and type per the layout map, and is immediately mapped.
- AC3. Sync updates renamed/re-serialed Hudu assets' mapped Alga twins and flags disappeared ones, reporting counts.
- AC4. The client Documents tab shows the mapped company's Hudu articles and deep-links correctly; unmapped/disconnected clients show nothing extra.
- AC5. The Documents page Hudu tab lists and searches articles across companies with correct client resolution and pagination; invisible on CE or with the flag off.
- AC6. All new strings translated in 8 locales; i18n static checks extended and green; full Hudu unit suite green.