wip
This commit is contained in:
191
docs/ui-review.md
Normal file
191
docs/ui-review.md
Normal file
@@ -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: <web service>" 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/<service-id>/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 <slice type>" 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 `<source>` 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 — `<template>` block has dangling fallback.** `resources/js/pages/servers/Show.vue:217` reads `<template> Something else </template>` outside any `v-if`/`v-else-if` chain. This is a stray Vue `<template>` rendered literally for any status that isn't `active` or `provisioning` — clean up or convert to `<template v-else>`.
|
||||
- **Partial — provisioning UI is decorative.** `servers/Show.vue:27-39` cycles through fake messages on an interval. No actual progress, no association to the running `server_provision` operation (spec §3 OperationKind). Tie the cycling messages to real `operation_steps` events.
|
||||
- **Missing — server delete / decommission.** Not wired anywhere; only `index/show/create/store` routes registered (`routes/web.php:43-47`).
|
||||
- **Missing — firewall-rule UI.** `app/Models/FirewallRule.php` and `Server::firewallRules()` exist. Spec §4 step 8 says UFW with SSH open, but additional rules (e.g. for Caddy 80/443, private network) are not surfaced. Add a Firewall tab on `servers/Show.vue`.
|
||||
- **Missing — credential persistence/regen.** "WILL NOT BE SHOWN AGAIN" (`servers/Show.vue:219-224`) appears via flash only; if the user navigates away without copying, the only credential they have is the SSH key Keystone manages. Acceptable, but should explicitly say "Keystone uses an SSH key for all subsequent operations; this password is informational."
|
||||
- **Missing — re-provision/heal action.** If provisioning fails there is no retry button on the show page.
|
||||
- **Missing — service add gated on server `active` status.** `servers/Show.vue:75-94` only renders the Add button when status is `active`, but during `provisioning` there is no informative message about why services can't be added (the cycling messages run but the section disappears entirely). Show a disabled "Add" with tooltip.
|
||||
- **Missing — operations list does not show parent-child structure.** `servers/Show.vue:138-196` shows steps under each operation but flat. Spec §3 emphasises parent-child (environment_deploy → service_deploy → replica_deploy). Render as a tree.
|
||||
|
||||
## 10. Operations & Logs (Spec §3)
|
||||
|
||||
- **Partial — step log dialog only on server show.** `servers/Show.vue:226-245` is the only place a user can read full step logs. Service Show and Environment Show both list operations without log expansion. Promote the log dialog to a shared component and use it everywhere operations appear.
|
||||
- **Missing — operation detail page.** Spec §3 implies operations are first-class. There is no `operations.show` page. Cannot view secrets used, parent op, retry, cancel, or download logs.
|
||||
- **Missing — retry / abort actions.** Failed operations are terminal in the UI; spec doesn't forbid retry. Add at least a "Re-run operation" button on the operation detail page.
|
||||
- **Missing — operation hash / kind / target column.** `Operation::hash` is generated but never displayed; useful for support and correlation with server-side `/home/keystone/operations/<operation-hash>/` directories (spec §16).
|
||||
- **Missing — live progress.** Operations require a refresh to update. Inertia v2 `WhenVisible` + polling exists in this app (used in `organisations/Show.vue:126`); apply to operations.
|
||||
|
||||
## 11. Build Artifacts & Registry (Spec §6)
|
||||
|
||||
- **Missing — no build artifact UI.** `BuildArtifact` model + `PlanBuildArtifact`/`BuildApplicationArtifact` actions exist, but there is no listing per environment or per registry. Users cannot see whether a build is pending, building, available, or failed.
|
||||
- **Missing — registry usage indicator.** Spec §19: "If an environment spans more than one server and no registry exists, deployment should be blocked with a registry setup prompt." UI does not surface this precondition anywhere — deploy button is always live on `applications/Show.vue:139-153`. Add a pre-deploy check.
|
||||
- **Missing — build strategy selector.** Spec §6: `target_server` / `dedicated_builder` / `external_registry`. There is no UI to choose. Currently the strategy lives in `BuildArtifact.metadata['build_strategy']` only.
|
||||
- **Missing — registry detail page.** No way to see which artifacts have been pushed where.
|
||||
|
||||
## 12. Gateway / Caddy (Spec §15)
|
||||
|
||||
- **Missing — domain / route configuration UI.** Caddy attachment is supported (`environment-attachments/Create.vue:39-48`) but the form does not collect a domain, TLS preference, or path prefix. Spec §15 requires explicit gateway routes; spec §12 says "Caddy/domain attachment: Create route slice. Wire gateway route to environment web service." There is no domain input anywhere in the UI.
|
||||
- **Missing — TLS / certificate status view.** No surface to see whether Caddy has issued a cert.
|
||||
- **Missing — Caddyfile preview.** Similar to compose preview — spec §16 places it at `/home/keystone/gateway/Caddyfile`. Should be viewable.
|
||||
- **Missing — cutover visualisation.** Spec §15 lays out the cutover sequence (render → health → upstream add → reload → drain → stop). UI has no visualisation; users get a generic deploy progress only.
|
||||
|
||||
## 13. Networking (Spec §14)
|
||||
|
||||
- **Missing — endpoint surface entirely.** Spec defines an endpoint model with `scope/hostname/ip_address/port/priority/health_status`. The model class exists (`app/Models/ServiceEndpoint.php`) but is never rendered.
|
||||
- **Missing — private-network membership view.** `Network` model and `Server::network()` exist; spec §14 prefers same-provider private IPs. UI never shows which servers share which networks.
|
||||
|
||||
## 14. Dashboard & Welcome
|
||||
|
||||
- **Partial — Dashboard is a single card.** `Dashboard.vue:27-44` lists organisations only. Once an org is picked, no summary of pending operations, recent deploys, or failing services on the main dashboard. Add "Recent operations" + "Unhealthy services" panels.
|
||||
- **Missing — no aggregated health.** Organisation Show (`organisations/Show.vue:65-123`) shows counts but not health (provisioning servers, failed last deploy, locked attachments).
|
||||
|
||||
## 15. Visual & UX Polish
|
||||
|
||||
- **Inconsistent — script tags mix `<script setup lang="ts">` and `<script setup>`.** E.g. `pages/environments/Show.vue:1` is plain JS while `pages/applications/Show.vue:1` is TypeScript. Pick a default and apply.
|
||||
- **Inconsistent — typed props.** Several Show pages declare `defineProps({ application: { type: Object, required: true }, ... })` with no TS interfaces. New pages should use `defineProps<{...}>()` shapes.
|
||||
- **Inconsistent — breadcrumb depth.** Service Show breadcrumb (`services/Show.vue:18-33`) goes Servers → server → service, but an environment-scoped service should also show the environment in the trail.
|
||||
- **Accessibility — radio button groups in Create flows.** `servers/Create.vue` and `services/Create.vue` use `RadioButton` but render via `v-for` with no `:key` on the radios. Visual-only impact today, but a11y-wise the labelling could be tightened (associate description text with `aria-describedby`).
|
||||
- **Accessibility — colour-only status.** `ServiceCard.vue:38-58` uses small dots + colour classes for status with no text alternative beyond the status string. Verify contrast in light mode.
|
||||
- **Empty states — Applications/Servers indexes have none.** A new organisation hits a blank grid. Render an illustration + CTA pointing to onboarding.
|
||||
|
||||
## 16. Routes & Controllers Backing the UI
|
||||
|
||||
These backend gaps directly produce the UI gaps listed above. Filling the UI will require new routes/controllers (or destroy actions on existing ones):
|
||||
|
||||
- `providers.create/store/destroy` (organisation provider mgmt)
|
||||
- `registries.index/edit/update/destroy`
|
||||
- `source-providers.index/edit/update/destroy`
|
||||
- `applications.edit/update/destroy`
|
||||
- `environments.create/store/edit/update/destroy`
|
||||
- `environments.scheduler` (settings sub-resource)
|
||||
- `services.destroy` and service-level slice mgmt (`services/{service}/slices.*`)
|
||||
- `service-replicas.show/restart`
|
||||
- `environment-variables.update/destroy/index`
|
||||
- `environment-attachments.destroy/update`
|
||||
- `operations.index/show/retry`
|
||||
- `build-artifacts.index/show`
|
||||
- `servers.destroy`, `servers.firewall-rules.*`
|
||||
- `domains.*` / `gateway.routes.*` for Caddy
|
||||
|
||||
---
|
||||
|
||||
## 17. Suggested Priorities
|
||||
|
||||
To bring the UI in line with spec without inflating scope:
|
||||
|
||||
1. **Make environments the primary surface (spec §20 Phase 6).** Sidebar entry, environment overview tab, environment edit page covering branch + scheduler + build config + migration policy.
|
||||
2. **Fix the deploy preconditions and visibility loop.** Block deploy when no registry + multi-server (§19). Show build artifact + commit + image digest after deploy.
|
||||
3. **Promote operations to first-class.** `operations.index`, `operations.show`, log dialog reused across pages, parent-child tree, retry.
|
||||
4. **Round out CRUD for org-level resources** (providers, registries, source providers, members). Currently all "configured once via DB" surfaces.
|
||||
5. **Slice + attachment maintenance.** Edit/detach/preview env-var exports.
|
||||
6. **Gateway/domain UX.** Domain input on Caddy attachment, route slice view, Caddyfile preview.
|
||||
7. **Polish:** fix `servers/Show.vue` dangling `<template>`, fix `applications/Index.vue` `:key`, add empty states, unify script lang.
|
||||
Reference in New Issue
Block a user