CluPilotCloud/docs/specs/2026-07-25-portal-d-service...

5.3 KiB
Raw Permalink Blame History

Phase D — Service framing, redesign & CRUD completeness

Status: approved (brainstorming + Codex consultation 2026-07-25). Branch: feat/portal-design. Rules R1R17 apply.

Follow-up to the second UX review. Codex consultation stored decisions; user confirmed:

  • Plans keep current storage (Start 100 GB / Team 500 GB / Business 1 TB / Enterprise 2 TB); hide vCPU/RAM from customers; add seats (5 / 25 / 100 / 500 — Enterprise is limited too) and a performance class (Standard/Enhanced/High/Dedicated) + feature list.
  • Cancellation: effective at end of billing term, no grace period, irreversible once set; at term end the customer receives a finished data export, then deprovision. Account closure only when no active packages.
  • Build D1 then D2 straight through; report each phase when green.

D1 — Fixes & redesign (low-risk, mostly UI)

  1. Billing button bug. wire:loading.attr="disabled" lacks wire:target, so any purchase spins/disables all buttons. Fix: scope wire:target per button + add wire:key on loop items.
  2. Service framing. config/provisioning.php plans gain seats (5/25/100/500) and performance (standard/enhanced/high/dedicated). Customer views (Billing, Dashboard, Cloud) show storage · seats · performance · features — never vCPU/RAM. A "Technische Infos" block may show Nextcloud/PHP version, region, service URL, backup location. Admin views keep raw specs.
  3. Charts → gradient line. Extend the chart island (resources/js/app.js) to build a canvas linear gradient (line colour → transparent) for fill line datasets. Convert the three time-series bar charts (Admin Overview MRR, Backups 14-day, Invoices spend) to gradient line. Admin Overview "Host-Auslastung" is categorical (per-host) — redesigned as capacity bars inside the host section, not a misleading line.
  4. Dashboard/MRR layout. Chart must fill its card (min-height / aspect) so the box is not larger than the chart; intuitive ordering. /frontend-design.
  5. Host list scaling. Replace the fixed cards with a dense, searchable/filterable (by datacenter + status) table/grid that scales to many hosts. /frontend-design.
  6. Host detail redesign. Show health from last_seen_at (online/stale/offline), status, capacity breakdown (committed/available/reserve), RAM, CPU, hosted-instances list, version, node, mgmt/WG IP, current run + events. /frontend-design.
  7. Datacenter edit + delete (guarded). Edit name/location (code immutable once referenced); delete only when 0 hosts, else block; activate/deactivate already exists.
  8. Admin CRUD audit fixes. Host: edit reserve_pct + maintenance/drain toggle. Customer: edit profile + suspend. Instances/orders/runs stay guarded/append-only.

D2 — Customer self-service (new pages)

  1. Settings (/settings): edit company/billing profile (company name, contact, billing address, VAT ID, phone); cancel package (typed-confirm modal → schedules at term end, irreversible, status cancellation_scheduled, export produced at term end — mocked); close account (guarded: no active packages; sets closed_at; retains financial records).
  2. Branding (in Settings): logo upload (PNG/WebP, MIME-validated, ≤2 MB), display name, primary + accent colour; stored at customer level, NULL → CluPilot defaults via a resolver; snapshot resolved branding into the provisioning run context at start so retries stay deterministic. Live preview.
  3. Users/seats (/users): invite / edit role / revoke / resend against the plan seat limit; guard: cannot revoke the last owner. Seats are portal-managed records (invites mocked).

Data model (additive migrations)

  • customers: billing_address (text, null), vat_id (null), phone (null), contact_name (null), brand_display_name (null), brand_logo_path (null), brand_primary_color (null), brand_accent_color (null), closed_at (timestamp, null).
  • instances: cancel_requested_at (null), service_ends_at (null); status gains cancellation_scheduled.
  • seats table: id, customer_id fk, email, name (null), role (owner/admin/member/readonly), status (invited/active/revoked), invited_at, timestamps; unique(customer_id,email).
  • Provisioning: StartCustomerProvisioning writes context['branding'] = resolved snapshot.

CRUD verdicts (Codex-guided)

Entity Verdict
Datacenters create · edit · activate/deactivate · delete only if unused
Hosts create · edit (reserve, drain/maintenance) · delete only if no instances/active runs
Customers edit profile · suspend · close (guarded) — no routine hard-delete
Instances cancel (scheduled) · deprovision via lifecycle — identity/history immutable
Orders/invoices orders cancellable before fulfilment; finalized invoices immutable
Seats invite · edit role · revoke · resend — cannot remove last owner
Provisioning runs append-only; pending cancellable, failed retryable (new attempt)
Addons/subscriptions add · schedule change · cancel — preserve price snapshots

Verification (per phase + final)

Pest green · DE+EN key parity (R16) · R12 Puppeteer 0 console errors on new/changed pages (production build) · R15 Codex loop --base 80348d6 until clean · migrate/seed dev · push.