Commit Graph

23 Commits (b605e0cd4fabb0dab5e5f3a5a473c3d0c3ffd83d)

Author SHA1 Message Date
Boban Blaskovic b605e0cd4f Move dev bind-mounts to docker-compose.override.yml (keep Portainer stack clean)
docker-compose.yml no longer contains ./app:/var/www/html bind-mounts.
Local dev bind-mounts live in docker-compose.override.yml which docker
compose CLI auto-merges but Portainer does not read (Portainer uses
either docker-compose.yml or docker-compose.portainer.yml).

This prevents future Portainer 'Limited' state caused by CLI deploys
overwriting Portainer-managed containers.
2026-05-31 23:37:04 +02:00
Boban Blaskovic da90b39085 Fix: chart.js race, burger desktop visibility, modal glass-themed
User-reported issues + browser-verified:

1. **Chart.js race condition** — inline @push('scripts') ran before
   the @vite type=module script that imports Chart. Wrapped Chart init
   in a poll loop that retries every 30ms until window.Chart resolves.
   Re-fires on livewire:navigated for SPA navigation.

2. **Hamburger always visible on desktop** — .clu-icon-btn defined
   display:grid AFTER .clu-burger {display:none}. Chained selector
   '.clu-burger.clu-icon-btn' (specificity 0,2,0) now wins both at
   default and inside the <920px media query.

3. **Modal in CluPilot glass style** —
   - app/Livewire/Modals/ConfirmDelete.php (class extends ModalComponent)
     with title/message/confirmLabel/confirmEvent/payload props,
     confirm() dispatches the named event + closes the modal.
   - resources/views/livewire/modals/confirm-delete.blade.php
     renders clu-modal-card with icon + title/message head + foot
     containing cancel (clu-ghost-btn) + delete (clu-btn-primary
     clu-btn-danger with red gradient).
   - CSS overrides for the wire-elements default Tailwind v2 markup:
     bg-gray-500 backdrop dimmer, transparent modal-container, z-index
     bumped to 100 so it sits above sticky topbar + sidebar.
   - @source hints in app.css so Tailwind v4 scans
     vendor/wire-elements/modal blade templates and generates the
     sm:inline-block / sm:h-screen utilities the modal needs to center.
   - Demo: Settings → Danger Zone → 'Workspace löschen' opens modal.

4. **AppServiceProvider Vite::usePreloadTagAttributes(false)** kept.

5. Browser console clean across dashboard / sites / sites/{id} /
   settings + open modal (verified via Chrome MCP).
2026-05-31 23:33:07 +02:00
Boban Blaskovic 665ca4e40b UI: sidebar fixed-header/scroll-mid/fixed-foot + mobile drawer + ChartJS
Sidebar layout (per user request: header+footer fix, only nav scrollable):
- Restructured clu-sidebar into 3 flex-children: clu-sidebar-head (brand +
  ws-switch fixed at top), clu-sidebar-scroll (nav, overflow-y:auto in
  middle), clu-sidebar-foot (plan-box fixed at bottom).

Mobile sidebar (per user request: drawer + hamburger):
- <920px: sidebar position fixed, translateX(-110%) hidden, .open class
  slides in. clu-sidebar-backdrop overlay dimms main + click-to-close.
- New x-clu.burger component with hamburger icon, visible only <920px
  via display:grid in media query. Injected into all 9 topbars.
- Alpine state sidebarOpen on <body>, nav-item click closes drawer
  after navigation.

Linking (per user request: sites detail unreachable):
- Sites index table + dashboard top-sites table: domain cell now <a>
  to route('sites.show', $site) with wire:navigate.

Charts (per user request: chart.js):
- composer/npm: chart.js ^4.5.
- resources/js/app.js: register all chart.js plugins, expose window.Chart
  for inline page scripts.
- Dashboard: added Traffic-24h line chart (Chart.js line+gradient fill)
  between metrics and 2-col row. Init re-runs on livewire:navigated for
  wire:navigate compatibility.

Modal (per user request: wire-elements):
- @livewire('wire-elements-modal') already in layouts/app.blade.php
  master layout per wire-elements/modal v3 docs (single mount). Open
  pattern: wire:click="$dispatch('openModal', { component: 'name' })".

Full Pest regression: 47/47 green.
2026-05-31 23:17:29 +02:00
Boban Blaskovic 4f0c787f89 Pages: Cluster Wizard (4-step) + Site Details (6 tabs) — final 12/12
- pages/clusters/create: 4-step wizard (Basis/Region+Size/WordPress/Review),
  sticky summary card with live monthly price (size × nodes), creates real
  Cluster on submit, redirects to servers index. 6 tests.
- pages/sites/show: 6-tab interface (Übersicht/WordPress/PHP/Files/Cron/Logs)
  via Volt setTab(), URL-bound tab state. Overview metrics + Quick-Action
  toggles (maintenance/cache/CDN) persist directly. WP+PHP config forms,
  file tree + wp-config preview, cron table, log console with level coloring.
  5 tests.
- Routes: Volt for clusters.create + sites.show with route-model-binding.
- CSS: clu-detail-tabs, clu-log-console, clu-log-level (info/warn/error),
  clu-file-tree, clu-file-row.
- Full regression: 47/47 Pest tests green (214 assertions).

ALL 12 templates ported to Volt + Pest.
2026-05-31 23:08:15 +02:00
Boban Blaskovic d57d456d02 Pages: Backups + Team + Security + Settings (14 Pest tests)
- backups: 4 metrics (last/storage/recovery/failed), recent backups
  table with pagination, sticky config card (toggles for daily/snapshots/
  replication + storage meter). 4 tests.
- team: 4 metrics (members/admins/invites/2fa), members table with
  avatar+role+scope+2fa+last-active, open invitations table with
  resend/revoke actions. 3 tests.
- security: 4 metrics (blocked24h/firewall-rules/ssl-expiring/2fa),
  advisories panel (high+critical events), threat feed (live), SSL
  certificates table with days-to-expiry. 3 tests.
- settings: 2-col layout with sticky left nav (workspace/notifications/
  billing/api/danger), reactive section switching, workspace form,
  billing card with quota meter, danger-zone with delete CTA. 4 tests.
- CSS additions: clu-settings-grid, clu-settings-nav, clu-danger.
- Routes: 4 Volt routes (backups/team/security/settings).
2026-05-31 23:04:18 +02:00
Boban Blaskovic 9ea55e33bd Dev: bind-mount ./app + vite dev mode + drop asset preload tags
- docker-compose.yml: bind-mount ./app:/var/www/html on nginx + all PHP
  services. CLI dev workflow now uses host code directly (no rebuild
  needed for code or CSS changes). Portainer-stack untouched (uses
  docker-compose.portainer.yml without bind-mounts).
- AppServiceProvider: Vite::usePreloadTagAttributes(false) drops the
  <link rel=preload> tags that triggered 'preloaded but not used within
  a few seconds' warnings under wire:navigate.
- vendor/ + composer.lock synced from container to host so bind-mount
  exposes a complete tree.
2026-05-31 22:58:45 +02:00
Boban Blaskovic b15e6f2146 Page: Servers — Volt cards grid with gauges + 3 Pest tests
- pages/servers/index Volt: 4 metrics (active nodes, avg CPU, avg RAM,
  maintenance), env segmented filter (all/prod/edge/staging), region select.
- Server card grid with country flag, name, env, spec line (vCPU/RAM/disk),
  3 horizontal gauges (CPU/RAM/disk) color-coded warn>70 / bad>90,
  sites_count footer, SSH + more row actions.
- CSS additions: clu-server-card/head/flag/name/meta/spec/foot, clu-gauge*.
- Routes: Volt::route servers.index.
- 3 Pest tests green (redirect, chrome+grid, env filter).
2026-05-31 22:52:01 +02:00
Boban Blaskovic 1622ac3a18 Page: Sites — Volt with search/filter/sort/pagination + 5 Pest tests
- pages/sites/index Volt: search by domain (debounced), 4-segment
  status filter (all/updates/warnings/critical), server + sort selects,
  paginated table (15/page).
- 4 metric tiles from Site aggregates (online/updates/pagespeed/visitors).
- Site cell with favicon initial, ssl/cache info, server/wp/php pills,
  visitors + response, status health label, row actions.
- routes/web.php: Volt::route for sites.index.
- CSS additions: clu-page-title/sub, clu-seg, clu-ghost-btn, clu-toolbar,
  clu-pager, clu-row-actions.
- 5 Pest tests green (redirect, chrome, list+paginate, search, segment filter).
2026-05-31 22:50:03 +02:00
Boban Blaskovic bb0e07ca47 Fix tailwind build + test-DB isolation
- app.css: CSS quote chars in .clu-quote-text::before/::after were
  literal smart quotes which broke as terminating string delimiters in
  the tailwind v4 parser. Use unicode escapes \201E and \201C.
- tests/TestCase.php: force DB_CONNECTION=sqlite + :memory: in setUp.
  phpunit.xml env section was being ignored by pest 3 runner, so
  RefreshDatabase truncated the real MySQL between turns.
- .env.testing for clarity (sqlite memory).
2026-05-31 22:47:34 +02:00
Boban Blaskovic 21c2f8a63a Page: Dashboard + App-Layout + Sidebar component
- layouts/app.blade.php rewritten as Volt-compatible layout with
  clu-bg-aurora body, clu-app-grid (236px sidebar + main).
- components/clu/sidebar.blade.php: brand, workspace switch (Acme Cluster),
  6 nav items with route-based active state, plan box (PRO, 87/100).
- routes/web.php: Volt::route for dashboard, placeholder routes
  for sites/servers/backups/security/team/settings (sidebar links resolve).
- placeholder.blade.php: holds spaceholder pages with shared chrome.
- CSS: app-grid, sidebar, ws-switch, nav, plan-box, topbar, search,
  icon-btn, metric, card, table, site-cell, favicon, health, feed.
- pages/dashboard Volt: 4 metrics (Sites Online, Ø Response, Updates,
  Blocked/24h) computed from DB, top-8 sites table, top-8 activity feed.
- Test: tests/Feature/Pages/DashboardPageTest.php (3 green)
  redirect-guest, render-auth, sees seeded site.
2026-05-31 22:38:12 +02:00
Boban Blaskovic 7276e27408 Domain models: Cluster/Server/Site/Backup/TeamMember/Invitation/SecurityEvent/Certificate/Activity/Cron
Full Eloquent foundation matching template data:
- 10 migrations (clusters, servers, sites, backups, team_members,
  invitations, security_events, certificates, activities, crons).
- 10 models with relationships + casts + accessors.
- 10 factories.
- DatabaseSeeder seeds matching template stats:
  - Marie Weber (m.weber@acme-cluster.de / clupilot) as owner
  - Admin (admin@clupilot.test / clupilot)
  - 1 Acme Cluster, 6 named servers (eu-prod-01/-02, edge-ams,
    us-east-01, staging, ap-bom-01) with CPU/RAM/disk loads from template
  - 87 sites distributed [23,19,14,18,6,7] per server
  - 102 backups, 87 SSL certificates, 47 cronjobs
  - 128 security events (matches template 'Blocked / 24h')
  - 5 team members + 3 pending invitations
  - 30 activity feed entries.
2026-05-31 22:33:09 +02:00
Boban Blaskovic 78edcef3da Page: Signup Glass — Volt + Pest tests + CSS additions
- pages/auth/register: full re-port of CluPilot Signup Glass.html
  with 3-step stepper, SSO, name+email+workspace+password form,
  live slug preview, password meter (4 rules), terms checkbox,
  right-pane trial card + 4-benefit list + customer quote + logo strip.
- CSS additions: clu-stepper, clu-field-row, clu-input-suffix,
  clu-slug-preview, clu-pw-meter, clu-pw-rules, clu-trial-*,
  clu-benefit-*, clu-quote-*, clu-avatar, clu-logos.
- tests/Feature/Auth/SignupPageTest.php: 6 tests, all green
  (renders chrome, slug preview, empty-form rejects, weak password,
   creates user + logs in, duplicate email).
2026-05-31 22:22:32 +02:00
Boban Blaskovic 5287b0024d Foundation + Login Glass page from template
- Tailwind theme tokens rewritten to match template light-glass design
  (color-fg / muted / accent / glass / hairline + radii + animations).
- Component classes: clu-glass, clu-brand-mark, clu-status-chip, clu-sso-btn,
  clu-input, clu-btn-primary, clu-alt-btn, clu-checkbox, clu-stat, clu-term-*,
  clu-live-tag, clu-pill.
- layouts/login.blade.php: clu-bg-aurora body, @stack('scripts') for page JS.
- pages/auth/login: full re-port of templates/akutell/CluPilot Login Glass.html:
  two-col grid, SSO buttons, e-mail+password with eye toggle (Alpine),
  remember checkbox, magic-link/passkey alt buttons, footer with TLS info,
  right-side live cluster panel with stat strip, animated terminal, live tags.
- Pest installed (pest, pest-plugin-laravel, pest-plugin-livewire).
- tests/Pest.php with RefreshDatabase binding.
- tests/Feature/Auth/LoginPageTest.php: 5 tests, all green
  (renders chrome, mounts Volt, rejects empty, authenticates, rejects invalid).
- 12 design templates committed to templates/akutell/.
2026-05-31 22:18:51 +02:00
Boban Blaskovic 2c787a2523 Add image-only compose for Portainer Repository deploys (no buildkit) 2026-05-31 21:40:46 +02:00
Boban Blaskovic 1cf91e2f32 nginx healthcheck: 127.0.0.1 instead of localhost (only IPv4 listener) 2026-05-31 21:18:06 +02:00
Boban Blaskovic 1c8bab0f04 nginx: extra_hosts host-gateway for vite HMR upstream; gitignore root .env
nginx config has static upstream pointing at host.docker.internal:5173 for
Vite dev server. Without extra_hosts the DNS lookup fails at nginx startup
and crashes the container. Added back, harmless in prod (upstream just unused).
2026-05-31 21:01:28 +02:00
Boban Blaskovic fb0fc26a21 Version-pin images: APP_VERSION for own builds, MARIADB/REDIS for services
No more :latest. Per-stack env vars set the version; prod uses Git tags,
dev uses 'dev' (rebuilt each push).
2026-05-31 18:17:09 +02:00
Boban Blaskovic fa84fac440 Skip composer scripts during build; defer package:discover to entrypoint
Build container has no .env / runtime config, so post-autoload-dump's
package:discover step failed. Run discovery at container start instead.
2026-05-31 18:05:05 +02:00
Boban Blaskovic 87f41387ce Bake app into multi-stage image; drop bind mounts for Portainer remote endpoint
- New root Dockerfile: composer-deps + node-build + assembled code stages,
  final PHP-FPM and nginx targets both built from same context.
- docker-compose.yml: all services build/use baked images. No ./app or
  config bind mounts (broke with Portainer on VM-A + Docker daemon on VM-B).
- app-storage named volume preserves Laravel storage across redeploys.
- Laravel env passed via x-php-env anchor from stack env vars.
- Removed per-service docker/php/Dockerfile and docker/nginx/Dockerfile.
- entrypoint.sh: touch .env, ensure storage dirs, idempotent storage:link.
2026-05-31 17:44:35 +02:00
Boban Blaskovic 6681df5a10 nginx: bake config into custom image (no single-file bind mount) 2026-05-31 17:28:17 +02:00
boban 33aac5c852 stack.env.example aktualisiert 2026-05-31 17:23:31 +02:00
Boban Blaskovic 67598ab649 Portainer-ready compose stack + app changes 2026-05-31 17:04:03 +02:00
Boban Blaskovic 624b21fcb9 Initial CluPilot stack setup
Laravel 12 + Livewire v3 + Tailwind v4 + Reverb WebSocket
behind Nginx Proxy Manager (external SSL termination, HTTP-only internal).
MariaDB 11, Redis 7, Mailpit. Dark auth views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 23:00:14 +02:00