UNPKG

@fleetbase/ember-ui

Version:

Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.

366 lines (325 loc) 11.6 kB
.widget-title, .fleetbase-dashboard-grid .dashboard-title { @apply flex flex-col lg:flex-row lg:items-center lg:justify-between rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800 shadow-sm px-4 py-2 mb-4; } .widget-count, .fleetbase-dashboard-grid .dashboard-component-count { @apply flex flex-col items-center justify-center rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800 shadow-sm py-4; } .col-span-2 { grid-column: span 2 / span 2; } .col-span-12 { grid-column: span 12 / span 12; } .col-span-full { grid-column: 1 / -1; } .fleetbase-dashboard-grid--sticky { position: sticky; top: var(--fleetbase-dashboard-sticky-top, 0); z-index: var(--fleetbase-dashboard-sticky-z-index, 20); } .fleetbase-dashboard-grid .btn-wrapper > .btn, .fleetbase-dashboard-grid .ember-basic-dropdown-trigger .btn-wrapper > .btn { height: 30px; font-size: 0.875rem; line-height: 1.25rem; } /* * The widget picker overlay opts out of the global `.next-content-overlay-panel-body` * px-1 by adding `class="no-padding"` to the body, and zeroes the inner wrapper's * own padding here. Children of the inner wrapper (sticky header, scrollable list) * own their padding so the sticky element actually stretches edge-to-edge. */ .next-content-overlay-panel-body-inner-wrapper.dashboard-widget-panel-body { @apply px-0 py-0; } .dashboard-widget-card { /* Two-line description with a flexible second line and sane fallback. */ container-type: inline-size; } .dashboard-widget-card .line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } /* * Chart.js needs an explicitly sized parent for `responsive: true + * maintainAspectRatio: false` to work. The shared <Chart> component wraps the * canvas in <div class="ui-chart relative"> with no built-in size, so sparklines * and embedded charts inside widgets render at Chart.js's default 300×150 canvas * and visually overflow under the card. * * Fix at the widget level: any .ui-chart inside a fleet-ops widget fills its * parent, and its canvas is absolutely positioned so Chart.js sizes against * the wrapper's bounding box (the standard Chart.js responsive recipe). */ .fleet-ops-widget .ui-chart { position: relative; width: 100%; height: 100%; min-height: 0; } .fleet-ops-widget .ui-chart > canvas { position: absolute; inset: 0; width: 100% !important; height: 100% !important; } /* * KPI tile sparkline gets a small, fixed slot so it never crowds the * value/badge row above it. */ .fleet-ops-kpi-tile .kpi-tile-sparkline { height: 36px; flex-shrink: 0; } /* ============================================================ * KPI TILE — neon pop styling. * * Three accent variants (neutral / good / bad) driven by the cardAccentClass * getter on the component. Each adds a subtle gradient backdrop tinted by the * trend status, plus a coloured border so the dashboard reads at a glance. * ============================================================ */ .fleet-ops-kpi-tile-pop { /* Smooth lift/scale on hover for a touch of life. */ transition: transform 180ms ease-out, box-shadow 180ms ease-out, border-color 180ms ease-out; background-color: #ffffff; border-color: #e5e7eb; } body[data-theme='dark'] .fleet-ops-kpi-tile-pop { background-color: #1f2937; border-color: #374151; } .fleet-ops-kpi-tile-pop:hover { transform: translateY(-1px); box-shadow: 0 6px 20px -6px rgba(15, 23, 42, 0.18); } body[data-theme='dark'] .fleet-ops-kpi-tile-pop:hover { box-shadow: 0 6px 22px -4px rgba(2, 6, 23, 0.6); } /* ── good (positive trend) ── */ .fleet-ops-kpi-tile-pop.kpi-accent-good { background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.06) 0%, rgba(16, 185, 129, 0) 60%); border-color: rgba(16, 185, 129, 0.35); } body[data-theme='dark'] .fleet-ops-kpi-tile-pop.kpi-accent-good { background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.12) 0%, rgba(16, 185, 129, 0) 60%); border-color: rgba(16, 185, 129, 0.4); } /* ── bad (negative trend) ── */ .fleet-ops-kpi-tile-pop.kpi-accent-bad { background-image: linear-gradient(135deg, rgba(244, 63, 94, 0.06) 0%, rgba(244, 63, 94, 0) 60%); border-color: rgba(244, 63, 94, 0.35); } body[data-theme='dark'] .fleet-ops-kpi-tile-pop.kpi-accent-bad { background-image: linear-gradient(135deg, rgba(244, 63, 94, 0.12) 0%, rgba(244, 63, 94, 0) 60%); border-color: rgba(244, 63, 94, 0.4); } /* ── neutral (no delta / zero) ── */ .fleet-ops-kpi-tile-pop.kpi-accent-neutral { background-image: linear-gradient(135deg, rgba(52, 133, 226, 0.05) 0%, rgba(52, 133, 226, 0) 65%); } body[data-theme='dark'] .fleet-ops-kpi-tile-pop.kpi-accent-neutral { background-image: linear-gradient(135deg, rgba(52, 133, 226, 0.1) 0%, rgba(52, 133, 226, 0) 65%); } /* Value-row entrance: gentle fade-up as data arrives. */ .fleet-ops-kpi-tile-pop .kpi-tile-value-row { animation: fleetops-kpi-pop-in 420ms cubic-bezier(0.16, 1, 0.3, 1) both; } @keyframes fleetops-kpi-pop-in { 0% { opacity: 0; transform: translateY(4px); } 100% { opacity: 1; transform: translateY(0); } } /* Subtle pulse for live/real-time tiles when their counter changes. */ .fleet-ops-kpi-tile-pop.kpi-live .kpi-tile-value-row { animation: fleetops-kpi-pulse 1.2s ease-in-out; } @keyframes fleetops-kpi-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } } /* ============================================================ * Operations Pulse tiles — five colour-coded micro-cards. * * Each tile gets: * - A vertical accent stripe along the left edge (3px) in the tile's hue. * - A soft radial-corner gradient in that hue for ambient colour. * - A tinted icon chip in the top-right. * - A neon-hued value number that pops against the dark theme. * ============================================================ */ .fleet-ops-pulse-tile { background-color: #f9fafb; border-color: #e5e7eb; transition: transform 160ms ease-out, box-shadow 160ms ease-out; } body[data-theme='dark'] .fleet-ops-pulse-tile { background-color: #1f2937; border-color: #374151; } .fleet-ops-pulse-tile:hover { transform: translateY(-1px); box-shadow: 0 4px 14px -4px rgba(15, 23, 42, 0.16); } body[data-theme='dark'] .fleet-ops-pulse-tile:hover { box-shadow: 0 4px 16px -4px rgba(2, 6, 23, 0.55); } .fleet-ops-pulse-stripe { width: 3px; pointer-events: none; } .fleet-ops-pulse-icon { width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; border-radius: 4px; } .fleet-ops-pulse-value-row { animation: fleetops-kpi-pop-in 420ms cubic-bezier(0.16, 1, 0.3, 1) both; } /* ── per-accent palettes ─────────────────────────────────────── */ /* Blue (active orders) */ .fleet-ops-pulse-tile.pulse-accent-blue { background-image: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0) 65%); border-color: rgba(59, 130, 246, 0.25); } body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-blue { background-image: linear-gradient(135deg, rgba(59, 130, 246, 0.18) 0%, rgba(59, 130, 246, 0) 65%); border-color: rgba(59, 130, 246, 0.35); } .pulse-accent-blue .fleet-ops-pulse-stripe { background-color: #3b82f6; } .pulse-accent-blue .fleet-ops-pulse-icon { background-color: rgba(59, 130, 246, 0.18); color: #2563eb; } body[data-theme='dark'] .pulse-accent-blue .fleet-ops-pulse-icon { color: #60a5fa; } .pulse-accent-blue .fleet-ops-pulse-value { color: #2563eb; } body[data-theme='dark'] .pulse-accent-blue .fleet-ops-pulse-value { color: #60a5fa; } /* Green (completed today) */ .fleet-ops-pulse-tile.pulse-accent-green { background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0) 65%); border-color: rgba(16, 185, 129, 0.3); } body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-green { background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.18) 0%, rgba(16, 185, 129, 0) 65%); border-color: rgba(16, 185, 129, 0.4); } .pulse-accent-green .fleet-ops-pulse-stripe { background-color: #10b981; } .pulse-accent-green .fleet-ops-pulse-icon { background-color: rgba(16, 185, 129, 0.18); color: #059669; } body[data-theme='dark'] .pulse-accent-green .fleet-ops-pulse-icon { color: #34d399; } .pulse-accent-green .fleet-ops-pulse-value { color: #059669; } body[data-theme='dark'] .pulse-accent-green .fleet-ops-pulse-value { color: #34d399; } /* Sky (drivers online) */ .fleet-ops-pulse-tile.pulse-accent-sky { background-image: linear-gradient(135deg, rgba(14, 165, 233, 0.1) 0%, rgba(14, 165, 233, 0) 65%); border-color: rgba(14, 165, 233, 0.25); } body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-sky { background-image: linear-gradient(135deg, rgba(14, 165, 233, 0.18) 0%, rgba(14, 165, 233, 0) 65%); border-color: rgba(14, 165, 233, 0.35); } .pulse-accent-sky .fleet-ops-pulse-stripe { background-color: #0ea5e9; } .pulse-accent-sky .fleet-ops-pulse-icon { background-color: rgba(14, 165, 233, 0.18); color: #0284c7; } body[data-theme='dark'] .pulse-accent-sky .fleet-ops-pulse-icon { color: #38bdf8; } .pulse-accent-sky .fleet-ops-pulse-value { color: #0284c7; } body[data-theme='dark'] .pulse-accent-sky .fleet-ops-pulse-value { color: #38bdf8; } /* Indigo (vehicles deployed) */ .fleet-ops-pulse-tile.pulse-accent-indigo { background-image: linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(99, 102, 241, 0) 65%); border-color: rgba(99, 102, 241, 0.25); } body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-indigo { background-image: linear-gradient(135deg, rgba(99, 102, 241, 0.18) 0%, rgba(99, 102, 241, 0) 65%); border-color: rgba(99, 102, 241, 0.35); } .pulse-accent-indigo .fleet-ops-pulse-stripe { background-color: #6366f1; } .pulse-accent-indigo .fleet-ops-pulse-icon { background-color: rgba(99, 102, 241, 0.18); color: #4f46e5; } body[data-theme='dark'] .pulse-accent-indigo .fleet-ops-pulse-icon { color: #818cf8; } .pulse-accent-indigo .fleet-ops-pulse-value { color: #4f46e5; } body[data-theme='dark'] .pulse-accent-indigo .fleet-ops-pulse-value { color: #818cf8; } /* Amber (issues open — lower is better) */ .fleet-ops-pulse-tile.pulse-accent-amber { background-image: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0) 65%); border-color: rgba(245, 158, 11, 0.3); } body[data-theme='dark'] .fleet-ops-pulse-tile.pulse-accent-amber { background-image: linear-gradient(135deg, rgba(245, 158, 11, 0.18) 0%, rgba(245, 158, 11, 0) 65%); border-color: rgba(245, 158, 11, 0.4); } .pulse-accent-amber .fleet-ops-pulse-stripe { background-color: #f59e0b; } .pulse-accent-amber .fleet-ops-pulse-icon { background-color: rgba(245, 158, 11, 0.18); color: #d97706; } body[data-theme='dark'] .pulse-accent-amber .fleet-ops-pulse-icon { color: #fbbf24; } .pulse-accent-amber .fleet-ops-pulse-value { color: #d97706; } body[data-theme='dark'] .pulse-accent-amber .fleet-ops-pulse-value { color: #fbbf24; }