feat: Phase 3a — Shuffle org picker dropdown in integration form (#831)
* feat(shuffle): Phase 3a — org picker dropdown in integration form Replaces the manual Shuffle Org-Id paste in the integration form with a dropdown populated from Shuffle's `/api/v1/orgs` endpoint, queried through the deployment's admin Bearer key (the existing Shuffle connector). Manual entry stays as a one-checkbox escape hatch for cases where the listing call fails or the org is too new to appear. - `dispatchers.list_shuffle_orgs(base_url, api_key)` — un-Org-Id-scoped GET against `/api/v1/orgs`. Tolerates both flat-list and `{"orgs": [...]}` response shapes. - `services.list_orgs(session)` — fetches Shuffle connector creds, calls the dispatcher, trims each org to {id, name, role, org_type}. - `routes.list_shuffle_orgs_route` — `GET /api/notifications/shuffle/orgs`. Deployment-scoped (not customer-scoped); admin/analyst auth gate. - `schema.ShuffleOrg` + `ShuffleOrgListResponse` — wire shapes. - `types.ShuffleOrg` mirrors the backend shape. - `api.notifications.listShuffleOrgs()` calls the new endpoint. - `CustomerShuffleIntegrationForm.vue`: - Replaced the bare Org-Id text input with an `n-select` filled from the orgs endpoint, label format "Org Name (uuid8…)" so admins can disambiguate same-named orgs. - Added a "Refresh list" button + a "Don't see your org? Enter manually" checkbox that toggles back to the legacy text input. - On edit, if the integration's existing org_id isn't in the list (e.g. fetched-once-then-deleted-from-Shuffle), the form auto-flips to manual entry so the row stays editable. - Loads orgs on mount (`onBeforeMount`); failures fall back to manual entry with the error surfaced inline. The manual-entry fallback isn't dead UI — it's the recovery path for when Shuffle is unreachable or the API response shape changes unexpectedly. Cleanest way to keep the form usable in degraded modes without a separate admin override. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(shuffle-orgs): align ShuffleOrg shape with actual Shuffle response Tested GET /api/v1/orgs against california.shuffler.io and confirmed the response is a flat array (matching the dispatcher's primary expectation). Cleaned up the field mapping based on the real shape: - Drop `org_type` — Shuffle doesn't return that field. Was always None. - Add `description` — present on every org row, useful as a future tooltip. - Add `creator_org` — set to the parent UUID on sub-orgs, empty/None on top-level orgs. The dispatcher normalizes the placeholder "PARENT_ORG_ID" string (seen in the canned docs example) and the literal empty string back to None so the frontend hint stays clean. Frontend dropdown now appends "· sub-org" to the label when `creator_org` is set, so admins can tell at a glance which rows are children of the parent org. Typical Shuffle pattern is one parent per MSP + one sub-org per customer, so this hint matters for selection. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>