diff --git a/app/package-lock.json b/app/package-lock.json index c9688b7..ae2e867 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "chart.js": "^4.5.1", "laravel-echo": "^2.3.4", "pusher-js": "^8.5.0" }, @@ -512,6 +513,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", @@ -1369,6 +1376,18 @@ "node": ">=8" } }, + "node_modules/chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", diff --git a/app/package.json b/app/package.json index 32e86e9..0df156a 100644 --- a/app/package.json +++ b/app/package.json @@ -18,6 +18,7 @@ "vite": "^7.0.7" }, "dependencies": { + "chart.js": "^4.5.1", "laravel-echo": "^2.3.4", "pusher-js": "^8.5.0" } diff --git a/app/resources/css/app.css b/app/resources/css/app.css index 0f233c2..4c38d4e 100644 --- a/app/resources/css/app.css +++ b/app/resources/css/app.css @@ -630,20 +630,55 @@ /* ───── App layout (sidebar + main) ───── */ .clu-app-grid { display: grid; grid-template-columns: 236px 1fr; min-height: 100vh; } - @media (max-width: 920px) { .clu-app-grid { grid-template-columns: 1fr; } .clu-sidebar { display: none; } } - /* Sidebar */ + /* Sidebar — fixed brand+ws-switch header, scrollable nav, fixed plan-box footer */ .clu-sidebar { - position: sticky; top: 0; height: 100vh; + position: sticky; top: 0; + height: 100vh; margin: 14px 0 14px 14px; border-radius: var(--radius); display: flex; flex-direction: column; - padding: 18px 14px; gap: 18px; + padding: 18px 14px; background: var(--color-glass-strong); backdrop-filter: blur(30px) saturate(180%); -webkit-backdrop-filter: blur(30px) saturate(180%); border: 1px solid var(--color-glass-border); box-shadow: var(--shadow-glass), var(--shadow-glass-inset); + z-index: 30; + } + .clu-sidebar-head { flex: none; display: flex; flex-direction: column; gap: 18px; } + .clu-sidebar-scroll { flex: 1; min-height: 0; overflow-y: auto; margin-top: 18px; padding-right: 4px; } + .clu-sidebar-scroll::-webkit-scrollbar { width: 6px; } + .clu-sidebar-scroll::-webkit-scrollbar-thumb { background: rgba(30,35,60,0.15); border-radius: 3px; } + .clu-sidebar-foot { flex: none; margin-top: 14px; } + + /* Sidebar backdrop (mobile only) */ + .clu-sidebar-backdrop { + position: fixed; inset: 0; + background: rgba(15,18,30,0.45); + backdrop-filter: blur(2px); + z-index: 28; + display: none; + } + + /* Hamburger button in topbar (mobile only) */ + .clu-burger { display: none; } + + @media (max-width: 920px) { + .clu-app-grid { grid-template-columns: 1fr; } + .clu-sidebar { + position: fixed; + top: 0; left: 0; + height: 100vh; + width: 256px; + margin: 0; + border-radius: 0 var(--radius) var(--radius) 0; + transform: translateX(-110%); + transition: transform .25s ease; + } + .clu-sidebar.open { transform: translateX(0); } + .clu-sidebar-backdrop.open { display: block; } + .clu-burger { display: grid; } } .clu-brand-row { display: flex; align-items: center; gap: 10px; padding: 2px 4px; } .clu-brand-name { font-weight: 600; font-size: 15px; letter-spacing: -0.01em; } diff --git a/app/resources/js/app.js b/app/resources/js/app.js index 4a70671..b819b7e 100644 --- a/app/resources/js/app.js +++ b/app/resources/js/app.js @@ -1,4 +1,12 @@ import './bootstrap'; import './login-effects'; import './register-effects'; +import { Chart, registerables } from 'chart.js'; + +Chart.register(...registerables); + +// Expose Chart globally so Blade inline + @endpush + {{-- ───── 2-col: Sites table + Activity feed ───── --}}