From 8f603122e236a0ade3fba9053e0d405ef93305ff Mon Sep 17 00:00:00 2001 From: Harry Bayliss Date: Sun, 24 May 2026 13:55:30 +0100 Subject: [PATCH] wip --- docs/ui-review.md | 191 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 docs/ui-review.md diff --git a/docs/ui-review.md b/docs/ui-review.md new file mode 100644 index 0000000..be32731 --- /dev/null +++ b/docs/ui-review.md @@ -0,0 +1,191 @@ +# Keystone UI Gap Review + +A full audit of the current Vue/Inertia UI in `resources/js/pages/` against +`docs/implementation-spec.md`. Findings are grouped by spec section/feature. +Each gap lists what is missing, where the relevant code lives today, and a +suggested resolution. + +Conventions: + +- **Missing** — feature has no UI surface at all. +- **Partial** — surface exists but does not cover the spec. +- **Broken** — surface exists but has a bug or dead code path. + +--- + +## 1. Global Navigation & Information Architecture + +- **Partial — sidebar only exposes Dashboard + Servers.** `resources/js/components/AppSidebar.vue:19-37` builds a `mainNavItems` array with only `Dashboard` and `Servers`. There are no entries for `Applications`, `Operations`, `Onboarding`, or organisation `Settings`. The spec frames environments as the primary surface (§20 Phase 6: "Present environments as the primary application surface"). Add at minimum `Applications` and `Operations` items, plus a context switcher / link to onboarding while incomplete. +- **Missing — no organisation switcher.** Multiple organisations are modeled (`Organisation::members()` on `app/Models/Organisation.php`), and the dashboard already supports multiple orgs (`resources/js/pages/Dashboard.vue:8-13`). After picking an org there is no way to switch without going back to `/dashboard`. Add a switcher in the sidebar header. +- **Missing — no Operations index.** Operations are the spec's audit/execution backbone (§3) but the UI only surfaces them inline on `environments/Show.vue` and `servers/Show.vue`. There is no organisation-wide operations feed for triage. Add an `operations.index` view with filters (kind, status, target). +- **Missing — no global empty/help state.** A fresh org with no servers/apps has no "Get started" CTA in the sidebar; user must guess to visit `/onboarding`. Promote the onboarding link until all onboarding steps are complete. + +## 2. Onboarding (Spec §19) + +- **Partial — onboarding page exists but is unreachable from primary nav.** `resources/js/pages/onboarding/Show.vue` is only reachable via the URL `/organisations/{id}/onboarding`. There is no link from `Dashboard`, `AppSidebar`, or `organisations/Show.vue`. Surface a persistent banner or sidebar entry while `nextStep` is non-terminal. +- **Partial — onboarding "Provider" step routes to organisation show.** `app/Http/Controllers/OnboardingController.php:25` sets the Provider step `href` to `organisations.show`, but the Server Providers list there (`resources/js/pages/organisations/Show.vue:202-220`) has no Add button. There is no `providers.create` route or page. Either add a `ProviderController@create` + Vue page or make the step open an inline dialog. +- **Missing — registry/source/server-create steps don't enforce a single org-level "default" once configured.** Spec §19 says registry is required for multi-server. The UI never blocks deployment on this — see Deployment Flow gap below. +- **Missing — onboarding doesn't reflect deploy-key install step (§5).** The spec lists "Deploy key installation and verification" as a discrete step; onboarding shows none. Add a step gated on `applications.deploy_key_installed_at`. + +## 3. Organisation & Provider Management + +- **Missing — server-provider UI is read-only.** `resources/js/pages/organisations/Show.vue:202-220` lists providers but offers no add/edit/delete. There is no `ProviderController` registered (see `routes/web.php`). The Hetzner provider must currently be seeded outside the UI. +- **Missing — registry/source-provider lists have no edit/delete.** Only `create` and `store` are wired (`routes/web.php:33-41`). Users can produce duplicate registries with bad credentials and never repair them. +- **Missing — no organisation member management.** `organisations/Show.vue:111-122` shows a members **count** with no roster, invite flow, role editor, or removal. The `members()` BelongsToMany on `Organisation` is unused by the UI. +- **Partial — registry credentials cannot be rotated.** No edit page; the only fix is delete-and-recreate, but delete is also missing. Add `registries.edit` + `registries.update`. + +## 4. Source Providers & Repository Access (Spec §5) + +- **Partial — deploy-key card disappears once installed.** `resources/js/pages/applications/Show.vue:46-75` only shows the deploy-key card when `application.deploy_key_public && !application.deploy_key_installed_at`. After install there is no way to view or rotate the key. Show key + `deploy_key_fingerprint` and a "Rotate" action permanently in an Application Settings tab. +- **Missing — no fingerprint display.** The model stores `deploy_key_fingerprint` (`app/Models/Application.php`), but the UI never renders it. Surface beside the public key for verification. +- **Missing — no way to re-run `git ls-remote` verification after install.** Verify button is gated by the same conditional in `applications/Show.vue:46`. Move it to an always-available action; spec §5 implies verification can be re-run. +- **Missing — application creation does not pick a source provider.** `resources/js/pages/applications/Create.vue` collects `repository_url` as a free string. Source providers exist (§5: Gitea/GitHub/generic Git) but the form never references them — users have no guidance for which provider corresponds to the URL, and `application.source_provider_id` is not captured. +- **Missing — repository type selector.** Spec lists `repository_type` (§2 Application). UI hardcodes `RepositoryType::GIT` (`app/Http/Controllers/ApplicationController.php:39`). Even if Git is the only v1 type, the form should display the resolved type. + +## 5. Applications & Environments (Spec §2, §6, §17) + +### Applications + +- **Missing — no Application edit/delete.** Only `index/show/create/store` are routed (`routes/web.php:62-66`). Renaming or removing an app requires DB intervention. +- **Missing — environment creation UI.** The initial environment is auto-created in `ApplicationController@store` (`CreateLaravelEnvironment`). There is no "Add environment" affordance on `applications/Show.vue`, despite the spec describing multiple environments (production/staging/dev) as the primary deployment unit (§2). +- **Broken — Applications/Index uses wrong key + has no empty state.** `resources/js/pages/applications/Index.vue:46` writes `:key=\`application{$applications.id}\`` (literal `$applications`). Replace with `:key="application.id"` and render an empty card when `applications.length === 0`. +- **Partial — Application Show has no overview.** No "last deployed at", no current commit, no current image digest. Spec §6 stores `current_image_digest` on Service and `BuildArtifact` per environment but neither is rendered. + +### Environments + +- **Missing — environment-level edit page.** Cannot change `branch`, `name`, `status`, `scheduler_enabled`, `scheduler_target_service_id`, `scheduler_mode`, or `build_config` (spec §2). All are dead fields in the DB from the UI's perspective. +- **Missing — branch change / redeploy with a specific commit.** Deploy is a fire-and-forget POST (`environments/Show.vue:63-76` and `applications/Show.vue:139-153`). The spec resolves a "target commit" (§17 step 1) — there is no commit picker or branch switcher in the UI. +- **Missing — build artifact view per environment.** `BuildArtifact` exists with status/digest/registry_ref but is not rendered on `environments/Show.vue`. Add a "Builds" section showing recent artifacts and their status. +- **Missing — scheduler controls (Spec §8).** No UI for `scheduler_enabled`, `scheduler_target_service_id`, or `scheduler_mode`. The spec explicitly says scheduler is a role/capability and that v1 should expose it. Add to environment settings tab and surface "Scheduler runs on: " on the environment overview. +- **Missing — migration policy controls (Spec §18).** `migration_mode`, `migration_timing`, `migration_command` are spec fields on a service. The UI has only a one-off `Migrate` button (`environments/Show.vue:77-91`); there is no form to inspect/change defaults per service. +- **Partial — environment header buttons are crowded.** `resources/js/pages/applications/Show.vue:123-211` packs Open / Deploy / Migrate / Env / Worker / Attach into a single card row. On md screens this overflows. Move secondary actions into a dropdown. +- **Missing — environment delete.** Spec implies environments are mutable units (§2); UI has no delete. + +## 6. Services (Spec §2 Service, §7, §9, §11) + +### Service list / show + +- **Missing — no service-by-environment scoping.** `services/Show.vue` does not show its parent environment (only `server` + `service`). Reaching a service from an environment requires `service.server_id` to be set (`environments/Show.vue:138-153`); environments where the service is server-less (replicas only) cannot be opened. Show environment breadcrumb when present. +- **Missing — replica detail view.** `services/Show.vue:62-79` lists replicas but they are inert. Spec §2 ServiceReplica has `cpu_limit`, `memory_limit_mb`, `container_id`, `internal_host`, `internal_port`, `public_port`, `image_digest`, `health_status`. Tap-through to a replica detail with logs/restart action is missing. +- **Missing — start/stop/restart actions on replicas.** Status badges show `status` and `health_status` (`services/Show.vue:73-77`) but no buttons. +- **Missing — endpoint listing.** `app/Models/ServiceEndpoint.php` exists and Spec §14 defines an endpoint model (`scope: docker_network/private_network/public`, hostname/ip/port/priority). UI never renders endpoints. Add an "Endpoints" card on `services/Show.vue`. +- **Missing — compose preview / generated artifact view.** Spec §16 establishes generated Compose under `/home/keystone/services//compose.yml`. There is no UI to inspect/render it. Add a "Compose" tab on `services/Show.vue` (read-only, syntax-highlighted). +- **Missing — process_roles editor.** `Service` has `process_roles` JSON (spec §2). Used by scheduler role attachment (§8). No UI exposes it. + +### Service edit (`services/Edit.vue`) + +- **Partial — only name, desired_replicas, default_cpu_limit, default_memory_limit_mb editable.** Missing: `deploy_policy`, `version_track`, `available_image_digest` (acknowledge available update), `config` overrides (migration_mode/timing/command per §18), `scheduler` flags for app services. +- **Missing — health-check / health-path override.** Spec §7 defaults health path to `/up`. No UI surfaces or overrides. + +### Service create + +- **Missing — builder service category in practice.** `ServiceCategory::BUILDER` exists and Spec §6 says "A dedicated builder is represented as a `Service` with category `builder`". The Create flow shows the BUILDER radio (it loops over the enum) but `services/Create.vue:111-122` only lists `services[form.category]` from `config('keystone.services')` — there is no entry for builder there, so selecting `builder` shows an empty type list with no message. Either remove `BUILDER` from the radio set or wire builder driver options. +- **Missing — selecting category doesn't surface deploy_policy defaults.** Spec §2 defines deploy_policy defaults per category. The form does not show or let the user accept them. + +### Stateful service updates (Spec §11) + +- **Partial — update form exists but exposes raw digest.** `resources/js/pages/services/updates/Create.vue:74-82` requires the user to paste an image digest. The available digest is pre-filled from `service.available_image_digest`, but Postgres/Valkey users will not know how to obtain a different digest. Replace with a "latest minor" resolver and a manual override (spec §9: "resolving image tags to digests"; §11 implies guided update). +- **Partial — backup checkbox visible only when `backup_enabled` config flag is set.** Spec §11 step 3 says "Optional backup checkbox appears only if backup capability exists" — current code matches the letter, but the UI never lets the user enable backups in the first place, so the checkbox is unreachable. +- **Missing — downtime acknowledgement.** Spec §11 step 2 says "Keystone warns about downtime and data risk." Current warning is generic. Show a hard confirmation step ("type the service name to confirm"). + +## 7. Slices & Attachments (Spec §12, §13) + +- **Missing — slice index per service.** `services/Show.vue:81-95` lists slices with `name + type` only — no operations, no credentials view, no detail page, no create from service-detail (spec §12: "Advanced users can select existing slices or create slices manually from service detail pages"). +- **Missing — slice create UI.** No `slices.create` route or page. Spec §12 explicitly requires this for advanced users. +- **Missing — slice operations are not independent in the UI.** Spec §12: "Slice operations should be independent from service container deployments." Backend distinguishes `slice_provision`/`slice_configure` operations (§3), but UI only shows them mixed into the service's operations list (`services/Show.vue:97-117`). +- **Missing — env-var preview for attachments.** Spec §13 enumerates `DB_*` / `REDIS_*` exports. The attachment form (`environment-attachments/Create.vue`) shows only "Generated " with no preview of what variables will be written. Add a preview block. +- **Missing — attachment edit/detach.** `environments/Show.vue:184-204` lists attachments inertly — no detach button, no edit. Recovering from a wrong attachment requires DB intervention. +- **Partial — attachment form's role/service compatibility is hardcoded in JS.** `environment-attachments/Create.vue:37-48` keeps role-to-service-type mapping client-side. If the backend adds queue support via Postgres listen/notify or storage via Caddy, this map drifts. Push the compatibility matrix from the controller. +- **Missing — `is_primary` semantics aren't explained.** A checkbox labeled "Primary attachment" with no helper text; users don't know when to uncheck. + +## 8. Environment Variables (Spec §13) + +- **Partial — variables UI is add-only.** `environment-variables/Create.vue` only POSTs. There is no edit, no delete, no value reveal, no bulk import (`.env` paste). Managed values show as badges (`environments/Show.vue:206-235`) but cannot be inspected or overridden. +- **Missing — `overridable` toggle.** Spec §2 EnvironmentVariable has `overridable`. The UI never surfaces it; all user-created variables silently become `overridable: true` (`EnvironmentVariableController::store`). +- **Missing — source/slice provenance display.** Managed/system variables should "show their source and whether they are overridable" per spec §13. Currently rendered as `` text only; no link back to the slice/attachment that generated them. +- **Missing — secret vs. plain value distinction.** No marker, no masking, no copy button. + +## 9. Servers (Spec §4) + +- **Broken — `