@burdenoff/microfe-movethewheels 2026.614.1 → 2026.625.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AIAssistantPage-FGGTN-DA.js → AIAssistantPage-CNyoKtID.js} +8 -8
- package/dist/{AnalyticsPage-DSLYCnln.js → AnalyticsPage-Ck615ox7.js} +21 -21
- package/dist/{CreateOrderPage-BgjA_QFT.js → CreateOrderPage-C9Vb57Yn.js} +8 -8
- package/dist/{CustomerDetailsPage-Cfvfw12U.js → CustomerDetailsPage-BVkHRZvi.js} +24 -24
- package/dist/CustomersPage-DhQg-6h_.js +118 -0
- package/dist/{DashboardPage-BeyvN3Mr.js → DashboardPage-DTmyuC11.js} +134 -127
- package/dist/{DataTable-oNjXEcUO.js → DataTable-CfWvFw5B.js} +13 -13
- package/dist/{DriverDetailsPage-DGTUZG5S.js → DriverDetailsPage-DaaCQ-2O.js} +27 -27
- package/dist/DriversPage-D0RMBB_t.js +129 -0
- package/dist/{FinancePage-Cyi75iWa.js → FinancePage-BUXqW1IM.js} +11 -11
- package/dist/{FleetPage-Dlupg1NZ.js → FleetPage-DCfOmBa2.js} +74 -73
- package/dist/{ImportExportPage-D81vnXbH.js → ImportExportPage-BM1BoRcE.js} +9 -9
- package/dist/{InventoryPage-D0u2nxRd.js → InventoryPage-BW0QXWuf.js} +68 -73
- package/dist/{LiveTrackingPage-CKIlbMBA.js → LiveTrackingPage-DqcGXdkw.js} +26 -26
- package/dist/{MarketplacePage-3NpZ0_SK.js → MarketplacePage-DFqsxNYU.js} +13 -13
- package/dist/MetricCard-B7yMyvMX.js +146 -0
- package/dist/{OrderDetailsPage-BeGWzAws.js → OrderDetailsPage-Dx1m3cVA.js} +34 -34
- package/dist/{OrdersListPage-DczM6NXa.js → OrdersListPage-DHYBGrWf.js} +78 -79
- package/dist/{PageLayout-3DR4pMQ9.js → PageLayout-D3N8MCTm.js} +628 -568
- package/dist/{ProductDetailsPage-Czcrqe2F.js → ProductDetailsPage-DOXO90kb.js} +15 -15
- package/dist/{ProductsPage-DJhdrfox.js → ProductsPage-RLMTwWN0.js} +42 -40
- package/dist/{ReportsPage-DPqE4Q3D.js → ReportsPage-Df3hciPy.js} +10 -10
- package/dist/{RouteDetailsPage-j6ZczLRT.js → RouteDetailsPage-xzFDeR-X.js} +22 -22
- package/dist/{RoutesPage-OZM7lJDi.js → RoutesPage-DDFy1QVi.js} +32 -30
- package/dist/{SettingsPage-v6vRyIAp.js → SettingsPage-JH_-UPbf.js} +7 -7
- package/dist/{StatusBadge-elLZA4Wl.js → StatusBadge-CijYQXtb.js} +5 -2
- package/dist/{TrackingPage-CBmGvTW_.js → TrackingPage-CU0Vk4mx.js} +31 -31
- package/dist/{VehicleDetailsPage-DJXYkTN2.js → VehicleDetailsPage-P4xDttKw.js} +21 -21
- package/dist/VehiclesPage-tJZ64QKV.js +129 -0
- package/dist/{WarehouseDetailsPage-nMZvp56W.js → WarehouseDetailsPage-Bt_w2Yf7.js} +19 -19
- package/dist/{WarehousesPage-BcJ_QrK3.js → WarehousesPage-HsPpEFeG.js} +40 -35
- package/dist/arrow-left-CBpZKkpq.js +11 -0
- package/dist/{box-CnErOGkD.js → box-CAaiElsb.js} +2 -2
- package/dist/{chart-column-CFT0jC9N.js → chart-column-D1Ym15-S.js} +2 -2
- package/dist/chevron-right-BIFGi0UA.js +8 -0
- package/dist/{circle-alert-B2pZQFzu.js → circle-alert-DjmNo5_L.js} +2 -2
- package/dist/circle-check-big-CjuPVo3_.js +11 -0
- package/dist/{clock-DP-76dyQ.js → clock-BjkdnddD.js} +2 -2
- package/dist/{dollar-sign-CrwqdoEC.js → dollar-sign-IB4O0wuQ.js} +2 -2
- package/dist/{download-CQqBpPk5.js → download-Dpa9vqoG.js} +2 -2
- package/dist/{file-text-CnHcsdsc.js → file-text-C8Uioy11.js} +2 -2
- package/dist/filter-Da5OZaHI.js +8 -0
- package/dist/index.js +52 -50
- package/dist/{map-qljBUQ1N.js → map-_Per4sT9.js} +2 -2
- package/dist/{map-pin-4a-IA2NP.js → map-pin-BSaf5IDL.js} +2 -2
- package/dist/navigation-DaTbrhw2.js +8 -0
- package/dist/{package-CPePjBwW.js → package-BE7U0wQE.js} +2 -2
- package/dist/{phone-2rkA8w_3.js → phone-DV4StZiA.js} +2 -2
- package/dist/plus-Dl1rLeJR.js +11 -0
- package/dist/{refresh-cw-oWEqfHnS.js → refresh-cw-CWfERAit.js} +2 -2
- package/dist/{route-DBljW-z4.js → route-Dp9JiN_E.js} +2 -2
- package/dist/{save-Cjsx5R5Y.js → save-XH8vydOn.js} +2 -2
- package/dist/search-OxoIiL_C.js +13 -0
- package/dist/{settings-CX94mtOc.js → settings-3Rw5NwFF.js} +2 -2
- package/dist/{square-pen-BIgbw83U.js → square-pen-hLIbFQFV.js} +2 -2
- package/dist/{star-BW23nRLp.js → star-YsVV3MNQ.js} +2 -2
- package/dist/{store-Cdk0UEPT.js → store-RCBl7HB-.js} +2 -2
- package/dist/{trending-up-DYQ3Xns5.js → trending-up-C-85AwGT.js} +2 -2
- package/dist/{triangle-alert-Ck_oBZ7n.js → triangle-alert-BOhfGgmh.js} +2 -2
- package/dist/{truck-Cd54agV-.js → truck-CtDjNFbi.js} +2 -2
- package/dist/{user-CUEmQUkJ.js → user-Dz5TJZFs.js} +2 -2
- package/dist/{users-e-wRlQnX.js → users-g2N0eL-Z.js} +2 -2
- package/dist/{warehouse-DXnNb4kf.js → warehouse-D3WWDeTX.js} +2 -2
- package/dist/{wrench-DYJo0U8_.js → wrench-CmxY1n4I.js} +2 -2
- package/package.json +2 -2
- package/src/dev/main.tsx +4 -4
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +1 -1
- package/src/movethewheels/components/DataTable.tsx +10 -10
- package/src/movethewheels/components/MetricCard.tsx +52 -23
- package/src/movethewheels/components/PageLayout.tsx +70 -15
- package/src/movethewheels/components/StatusBadge.tsx +2 -1
- package/src/movethewheels/components/ui.tsx +3 -3
- package/src/movethewheels/pages/AIAssistantPage.tsx +3 -3
- package/src/movethewheels/pages/AnalyticsPage.tsx +13 -13
- package/src/movethewheels/pages/CreateOrderPage.tsx +2 -2
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +14 -14
- package/src/movethewheels/pages/CustomersPage.tsx +14 -13
- package/src/movethewheels/pages/DashboardPage.tsx +71 -64
- package/src/movethewheels/pages/DriverDetailsPage.tsx +17 -17
- package/src/movethewheels/pages/DriversPage.tsx +12 -15
- package/src/movethewheels/pages/FinancePage.tsx +4 -4
- package/src/movethewheels/pages/FleetPage.tsx +23 -18
- package/src/movethewheels/pages/ImportExportPage.tsx +5 -5
- package/src/movethewheels/pages/InventoryPage.tsx +25 -18
- package/src/movethewheels/pages/LiveTrackingPage.tsx +18 -18
- package/src/movethewheels/pages/MarketplacePage.tsx +6 -6
- package/src/movethewheels/pages/OrderDetailsPage.tsx +20 -20
- package/src/movethewheels/pages/OrdersListPage.tsx +22 -16
- package/src/movethewheels/pages/ProductDetailsPage.tsx +8 -8
- package/src/movethewheels/pages/ProductsPage.tsx +10 -13
- package/src/movethewheels/pages/ReportsPage.tsx +5 -5
- package/src/movethewheels/pages/RouteDetailsPage.tsx +13 -13
- package/src/movethewheels/pages/RoutesPage.tsx +12 -9
- package/src/movethewheels/pages/SettingsPage.tsx +2 -2
- package/src/movethewheels/pages/TrackingPage.tsx +23 -23
- package/src/movethewheels/pages/VehicleDetailsPage.tsx +13 -13
- package/src/movethewheels/pages/VehiclesPage.tsx +11 -17
- package/src/movethewheels/pages/WarehouseDetailsPage.tsx +11 -11
- package/src/movethewheels/pages/WarehousesPage.tsx +11 -12
- package/dist/CustomersPage-DXnx04Au.js +0 -119
- package/dist/DriversPage-CRursDRZ.js +0 -127
- package/dist/MetricCard-BDcT-7Ei.js +0 -135
- package/dist/VehiclesPage-r_snhuEj.js +0 -127
- package/dist/arrow-left-Crb7WKr1.js +0 -11
- package/dist/chevron-right-Bzo6Rr8_.js +0 -8
- package/dist/circle-check-big-R_fjEgxM.js +0 -11
- package/dist/filter-CgTF21j9.js +0 -8
- package/dist/navigation-CqRTj3vI.js +0 -8
- package/dist/plus-Dew5yoQK.js +0 -11
- package/dist/search-FWYzDbBx.js +0 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/star.js
|
|
3
3
|
var t = e("Star", [["path", {
|
|
4
4
|
d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",
|
|
5
5
|
key: "r04s7s"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/store.js
|
|
3
3
|
var t = e("Store", [
|
|
4
4
|
["path", {
|
|
5
5
|
d: "m2 7 4.41-4.41A2 2 0 0 1 7.83 2h8.34a2 2 0 0 1 1.42.59L22 7",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/trending-up.js
|
|
3
3
|
var t = e("TrendingUp", [["polyline", {
|
|
4
4
|
points: "22 7 13.5 15.5 8.5 10.5 2 17",
|
|
5
5
|
key: "126l90"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/triangle-alert.js
|
|
3
3
|
var t = e("TriangleAlert", [
|
|
4
4
|
["path", {
|
|
5
5
|
d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/truck.js
|
|
3
3
|
var t = e("Truck", [
|
|
4
4
|
["path", {
|
|
5
5
|
d: "M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/user.js
|
|
3
3
|
var t = e("User", [["path", {
|
|
4
4
|
d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",
|
|
5
5
|
key: "975kel"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/users.js
|
|
3
3
|
var t = e("Users", [
|
|
4
4
|
["path", {
|
|
5
5
|
d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/warehouse.js
|
|
3
3
|
var t = e("Warehouse", [
|
|
4
4
|
["path", {
|
|
5
5
|
d: "M22 8.35V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8.35A2 2 0 0 1 3.26 6.5l8-3.2a2 2 0 0 1 1.48 0l8 3.2A2 2 0 0 1 22 8.35Z",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { it as e } from "./PageLayout-
|
|
2
|
-
//#region
|
|
1
|
+
import { it as e } from "./PageLayout-D3N8MCTm.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/fuel.js
|
|
3
3
|
var t = e("Fuel", [
|
|
4
4
|
["line", {
|
|
5
5
|
x1: "3",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-movethewheels",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.625.1",
|
|
4
4
|
"description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@apollo/client": "^4.0.11",
|
|
43
|
-
"@burdenoff/fe-libs": "
|
|
43
|
+
"@burdenoff/fe-libs": "2026.625.3",
|
|
44
44
|
"@tanstack/react-query": "^5.90.16",
|
|
45
45
|
"clsx": "^2.1.1",
|
|
46
46
|
"graphql": "^16.12.0",
|
package/src/dev/main.tsx
CHANGED
|
@@ -65,13 +65,13 @@ function MoveTheWheelsApp() {
|
|
|
65
65
|
function Dashboard() {
|
|
66
66
|
const navigate = useNavigate();
|
|
67
67
|
return (
|
|
68
|
-
<div className="min-h-screen flex items-center justify-center bg-
|
|
69
|
-
<div className="bg-
|
|
68
|
+
<div className="min-h-screen flex items-center justify-center bg-bg-sunken/50">
|
|
69
|
+
<div className="bg-bg-surface p-8 rounded-xl shadow-lg text-center border">
|
|
70
70
|
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
|
|
71
|
-
<p className="text-
|
|
71
|
+
<p className="text-text-secondary mb-6">Welcome to MoveTheWheels!</p>
|
|
72
72
|
<button
|
|
73
73
|
onClick={() => navigate('/movethewheels')}
|
|
74
|
-
className="px-4 py-2 bg-primary text-primary-
|
|
74
|
+
className="px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors"
|
|
75
75
|
>
|
|
76
76
|
Go to MoveTheWheels
|
|
77
77
|
</button>
|
|
@@ -14,7 +14,7 @@ function PageLoader() {
|
|
|
14
14
|
<div className="flex items-center justify-center min-h-[400px]">
|
|
15
15
|
<div className="flex flex-col items-center gap-3">
|
|
16
16
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
|
17
|
-
<p className="text-sm text-
|
|
17
|
+
<p className="text-sm text-text-secondary">Loading...</p>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
);
|
|
@@ -157,7 +157,7 @@ export function DataTable<T extends object>({
|
|
|
157
157
|
// Get sort icon
|
|
158
158
|
const getSortIcon = (columnId: string) => {
|
|
159
159
|
if (sortColumn !== columnId) {
|
|
160
|
-
return <ArrowUpDown className="w-4 h-4 text-
|
|
160
|
+
return <ArrowUpDown className="w-4 h-4 text-text-secondary/50" />;
|
|
161
161
|
}
|
|
162
162
|
if (sortDirection === 'asc') {
|
|
163
163
|
return <ArrowUp className="w-4 h-4 text-primary" />;
|
|
@@ -188,13 +188,13 @@ export function DataTable<T extends object>({
|
|
|
188
188
|
<Skeleton className="h-10 w-64" />
|
|
189
189
|
</div>
|
|
190
190
|
)}
|
|
191
|
-
<div className="rounded-
|
|
191
|
+
<div className="rounded-card border border-border-seam bg-bg-surface shadow-elevation-1">
|
|
192
192
|
<Table>
|
|
193
193
|
<TableHeader>
|
|
194
194
|
<TableRow>
|
|
195
195
|
{columns.map((column) => (
|
|
196
196
|
<TableHead key={column.id}>
|
|
197
|
-
<Skeleton className="h-4 w-20" />
|
|
197
|
+
<Skeleton className="h-4 w-20 motion-safe:animate-vc-shimmer" />
|
|
198
198
|
</TableHead>
|
|
199
199
|
))}
|
|
200
200
|
</TableRow>
|
|
@@ -204,7 +204,7 @@ export function DataTable<T extends object>({
|
|
|
204
204
|
<TableRow key={i}>
|
|
205
205
|
{columns.map((column) => (
|
|
206
206
|
<TableCell key={column.id}>
|
|
207
|
-
<Skeleton className="h-4 w-full max-w-[100px]" />
|
|
207
|
+
<Skeleton className="h-4 w-full max-w-[100px] motion-safe:animate-vc-shimmer" />
|
|
208
208
|
</TableCell>
|
|
209
209
|
))}
|
|
210
210
|
</TableRow>
|
|
@@ -222,7 +222,7 @@ export function DataTable<T extends object>({
|
|
|
222
222
|
{searchable && (
|
|
223
223
|
<div className="flex items-center gap-4">
|
|
224
224
|
<div className="relative flex-1 max-w-sm">
|
|
225
|
-
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-
|
|
225
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-text-secondary" />
|
|
226
226
|
<Input
|
|
227
227
|
placeholder={searchPlaceholder}
|
|
228
228
|
value={searchQuery}
|
|
@@ -237,7 +237,7 @@ export function DataTable<T extends object>({
|
|
|
237
237
|
)}
|
|
238
238
|
|
|
239
239
|
{/* Table */}
|
|
240
|
-
<div className="rounded-
|
|
240
|
+
<div className="overflow-x-auto rounded-card border border-border-seam bg-bg-surface shadow-elevation-1">
|
|
241
241
|
<Table>
|
|
242
242
|
<TableHeader>
|
|
243
243
|
<TableRow>
|
|
@@ -245,7 +245,7 @@ export function DataTable<T extends object>({
|
|
|
245
245
|
<TableHead
|
|
246
246
|
key={column.id}
|
|
247
247
|
className={cn(
|
|
248
|
-
column.sortable && 'cursor-pointer select-none hover:bg-
|
|
248
|
+
column.sortable && 'cursor-pointer select-none hover:bg-bg-sunken/50',
|
|
249
249
|
column.headerClassName
|
|
250
250
|
)}
|
|
251
251
|
onClick={() => column.sortable && handleSort(column.id)}
|
|
@@ -262,7 +262,7 @@ export function DataTable<T extends object>({
|
|
|
262
262
|
{paginatedData.length === 0 ? (
|
|
263
263
|
<TableRow>
|
|
264
264
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
265
|
-
<p className="text-
|
|
265
|
+
<p className="text-text-secondary">{emptyMessage}</p>
|
|
266
266
|
</TableCell>
|
|
267
267
|
</TableRow>
|
|
268
268
|
) : (
|
|
@@ -274,7 +274,7 @@ export function DataTable<T extends object>({
|
|
|
274
274
|
return (
|
|
275
275
|
<TableRow
|
|
276
276
|
key={rowId}
|
|
277
|
-
className={cn(onRowClick && 'cursor-pointer hover:bg-
|
|
277
|
+
className={cn(onRowClick && 'cursor-pointer hover:bg-bg-sunken/50', rowClasses)}
|
|
278
278
|
onClick={() => onRowClick?.(row)}
|
|
279
279
|
>
|
|
280
280
|
{columns.map((column) => (
|
|
@@ -293,7 +293,7 @@ export function DataTable<T extends object>({
|
|
|
293
293
|
{/* Pagination */}
|
|
294
294
|
{pagination && sortedData.length > 0 && (
|
|
295
295
|
<div className="flex items-center justify-between">
|
|
296
|
-
<div className="flex items-center gap-2 text-sm text-
|
|
296
|
+
<div className="flex items-center gap-2 text-sm text-text-secondary">
|
|
297
297
|
<span>
|
|
298
298
|
Showing {(currentPage - 1) * pageSize + 1} to{' '}
|
|
299
299
|
{Math.min(currentPage * pageSize, sortedData.length)} of {sortedData.length}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import { GlassCard } from '@burdenoff/fe-libs/ui';
|
|
2
3
|
import { cn } from '../utils/cn';
|
|
3
|
-
import { Card, CardContent } from './ui';
|
|
4
4
|
|
|
5
5
|
interface MetricCardProps {
|
|
6
6
|
/** Main value to display */
|
|
@@ -23,6 +23,10 @@ interface MetricCardProps {
|
|
|
23
23
|
onClick?: () => void;
|
|
24
24
|
/** Additional CSS classes */
|
|
25
25
|
className?: string;
|
|
26
|
+
/** Boff UI 2.0 surface treatment. Defaults to the soft accent-wash metric surface. */
|
|
27
|
+
treatment?: 'metric' | 'glass';
|
|
28
|
+
/** Featured/active accent glow (used for the single hero metric per page). */
|
|
29
|
+
glow?: boolean;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
const sizeClasses = {
|
|
@@ -60,13 +64,15 @@ export function MetricCard({
|
|
|
60
64
|
size = 'md',
|
|
61
65
|
onClick,
|
|
62
66
|
className,
|
|
67
|
+
treatment = 'metric',
|
|
68
|
+
glow = false,
|
|
63
69
|
}: MetricCardProps) {
|
|
64
70
|
const styles = sizeClasses[size];
|
|
65
71
|
|
|
66
72
|
const changeColors = {
|
|
67
|
-
positive: 'text-
|
|
68
|
-
negative: 'text-
|
|
69
|
-
neutral: 'text-
|
|
73
|
+
positive: 'text-status-success-text',
|
|
74
|
+
negative: 'text-status-error-text',
|
|
75
|
+
neutral: 'text-text-secondary',
|
|
70
76
|
};
|
|
71
77
|
|
|
72
78
|
const trendIcons = {
|
|
@@ -98,24 +104,44 @@ export function MetricCard({
|
|
|
98
104
|
};
|
|
99
105
|
|
|
100
106
|
return (
|
|
101
|
-
<
|
|
107
|
+
<GlassCard
|
|
108
|
+
treatment={treatment}
|
|
109
|
+
glow={glow}
|
|
102
110
|
className={cn(
|
|
103
|
-
'overflow-hidden
|
|
104
|
-
onClick &&
|
|
111
|
+
'overflow-hidden border-border-seam',
|
|
112
|
+
onClick &&
|
|
113
|
+
'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg focus-visible:ring-offset-2 focus-visible:ring-offset-bg-base',
|
|
105
114
|
className
|
|
106
115
|
)}
|
|
107
116
|
onClick={onClick}
|
|
117
|
+
{...(onClick
|
|
118
|
+
? {
|
|
119
|
+
role: 'button',
|
|
120
|
+
tabIndex: 0,
|
|
121
|
+
onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
122
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
onClick();
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
: {})}
|
|
108
129
|
>
|
|
109
|
-
<
|
|
130
|
+
<div className={styles.card}>
|
|
110
131
|
<div className="flex items-start justify-between">
|
|
111
132
|
<div className="space-y-1">
|
|
112
|
-
<p className={cn('text-
|
|
113
|
-
<p className={cn('text-
|
|
133
|
+
<p className={cn('text-text-secondary', styles.label)}>{label}</p>
|
|
134
|
+
<p className={cn('text-text-primary tracking-tight tabular-nums', styles.value)}>
|
|
114
135
|
{typeof value === 'number' ? value.toLocaleString() : value}
|
|
115
136
|
</p>
|
|
116
|
-
{subtitle && <p className="text-xs text-
|
|
137
|
+
{subtitle && <p className="text-xs text-text-secondary">{subtitle}</p>}
|
|
117
138
|
{change && (
|
|
118
|
-
<div
|
|
139
|
+
<div
|
|
140
|
+
className={cn(
|
|
141
|
+
'flex items-center gap-1 text-sm tabular-nums',
|
|
142
|
+
changeColors[changeType]
|
|
143
|
+
)}
|
|
144
|
+
>
|
|
119
145
|
{trend && trendIcons[trend]}
|
|
120
146
|
<span>{change}</span>
|
|
121
147
|
</div>
|
|
@@ -123,14 +149,17 @@ export function MetricCard({
|
|
|
123
149
|
</div>
|
|
124
150
|
{icon && (
|
|
125
151
|
<div
|
|
126
|
-
className={cn(
|
|
152
|
+
className={cn(
|
|
153
|
+
'flex-shrink-0 p-2 rounded-lg bg-action-primary-bg/10 text-action-primary-bg',
|
|
154
|
+
styles.icon
|
|
155
|
+
)}
|
|
127
156
|
>
|
|
128
157
|
{icon}
|
|
129
158
|
</div>
|
|
130
159
|
)}
|
|
131
160
|
</div>
|
|
132
|
-
</
|
|
133
|
-
</
|
|
161
|
+
</div>
|
|
162
|
+
</GlassCard>
|
|
134
163
|
);
|
|
135
164
|
}
|
|
136
165
|
|
|
@@ -141,18 +170,18 @@ export function MetricCardSkeleton({ size = 'md' }: { size?: 'sm' | 'md' | 'lg'
|
|
|
141
170
|
const styles = sizeClasses[size];
|
|
142
171
|
|
|
143
172
|
return (
|
|
144
|
-
<
|
|
145
|
-
<
|
|
173
|
+
<GlassCard treatment="metric" className="overflow-hidden border-border-seam">
|
|
174
|
+
<div className={styles.card}>
|
|
146
175
|
<div className="flex items-start justify-between">
|
|
147
176
|
<div className="space-y-2 flex-1">
|
|
148
|
-
<div className="h-4 w-24 bg-
|
|
149
|
-
<div className="h-8 w-32 bg-
|
|
150
|
-
<div className="h-3 w-20 bg-
|
|
177
|
+
<div className="h-4 w-24 bg-bg-sunken motion-safe:animate-vc-shimmer rounded" />
|
|
178
|
+
<div className="h-8 w-32 bg-bg-sunken motion-safe:animate-vc-shimmer rounded" />
|
|
179
|
+
<div className="h-3 w-20 bg-bg-sunken motion-safe:animate-vc-shimmer rounded" />
|
|
151
180
|
</div>
|
|
152
|
-
<div className={cn('bg-
|
|
181
|
+
<div className={cn('bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg', styles.icon)} />
|
|
153
182
|
</div>
|
|
154
|
-
</
|
|
155
|
-
</
|
|
183
|
+
</div>
|
|
184
|
+
</GlassCard>
|
|
156
185
|
);
|
|
157
186
|
}
|
|
158
187
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import { AuroraBackground, GradientText, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';
|
|
2
3
|
import { cn } from '../utils/cn';
|
|
3
4
|
|
|
4
5
|
interface PageLayoutProps {
|
|
@@ -21,6 +22,16 @@ interface PageLayoutProps {
|
|
|
21
22
|
className?: string;
|
|
22
23
|
/** Additional CSS classes for the content area */
|
|
23
24
|
contentClassName?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Boff UI 2.0 — render a subtle living aurora backdrop behind the page header.
|
|
27
|
+
* Reserved for the MFE's single primary page (the Dashboard). One per MFE.
|
|
28
|
+
*/
|
|
29
|
+
hero?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Boff UI 2.0 — the slice of the title to emphasise with a brand gradient.
|
|
32
|
+
* When provided, the title renders as `…<GradientText>{titleAccent}</GradientText>`.
|
|
33
|
+
*/
|
|
34
|
+
titleAccent?: string;
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
const maxWidthClasses = {
|
|
@@ -53,7 +64,24 @@ export function PageLayout({
|
|
|
53
64
|
showHeader = true,
|
|
54
65
|
className,
|
|
55
66
|
contentClassName,
|
|
67
|
+
hero = false,
|
|
68
|
+
titleAccent,
|
|
56
69
|
}: PageLayoutProps) {
|
|
70
|
+
// Render the title, optionally clipping a gradient into the accent slice.
|
|
71
|
+
const renderTitle = (value: string) => {
|
|
72
|
+
if (!titleAccent || !value.includes(titleAccent)) {
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
const [before, after] = value.split(titleAccent);
|
|
76
|
+
return (
|
|
77
|
+
<>
|
|
78
|
+
{before}
|
|
79
|
+
<GradientText>{titleAccent}</GradientText>
|
|
80
|
+
{after}
|
|
81
|
+
</>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
57
85
|
return (
|
|
58
86
|
<div
|
|
59
87
|
className={cn(
|
|
@@ -72,26 +100,33 @@ export function PageLayout({
|
|
|
72
100
|
<div className={cn('mx-auto w-full', maxWidthClasses[maxWidth], contentClassName)}>
|
|
73
101
|
{/* Page Header */}
|
|
74
102
|
{showHeader && (title || headerActions) && (
|
|
75
|
-
<header
|
|
103
|
+
<header
|
|
104
|
+
className={cn(
|
|
105
|
+
'mb-6 sm:mb-8',
|
|
106
|
+
hero &&
|
|
107
|
+
'relative overflow-hidden rounded-card border border-border-seam bg-hero-mesh px-5 py-6 sm:px-7 sm:py-8'
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
{hero && <AuroraBackground intensity="subtle" />}
|
|
76
111
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
|
77
112
|
{/* Title Section */}
|
|
78
113
|
<div className="space-y-1">
|
|
79
114
|
{(title || icon) && (
|
|
80
115
|
<div className="flex items-center gap-3">
|
|
81
116
|
{icon && (
|
|
82
|
-
<div className="flex-shrink-0 p-2.5 rounded-xl bg-primary/10 text-primary">
|
|
117
|
+
<div className="flex-shrink-0 p-2.5 rounded-xl bg-action-primary-bg/10 text-action-primary-bg">
|
|
83
118
|
{icon}
|
|
84
119
|
</div>
|
|
85
120
|
)}
|
|
86
121
|
{title && (
|
|
87
|
-
<h1 className="text-2xl sm:text-3xl font-bold tracking-tight text-
|
|
88
|
-
{title}
|
|
122
|
+
<h1 className="text-2xl sm:text-3xl font-bold tracking-tight text-text-primary">
|
|
123
|
+
{renderTitle(title)}
|
|
89
124
|
</h1>
|
|
90
125
|
)}
|
|
91
126
|
</div>
|
|
92
127
|
)}
|
|
93
128
|
{subtitle && (
|
|
94
|
-
<p className="text-sm sm:text-base text-
|
|
129
|
+
<p className="text-sm sm:text-base text-text-secondary max-w-2xl">{subtitle}</p>
|
|
95
130
|
)}
|
|
96
131
|
</div>
|
|
97
132
|
|
|
@@ -136,8 +171,8 @@ export function PageSection({
|
|
|
136
171
|
{(title || actions) && (
|
|
137
172
|
<div className="flex items-center justify-between">
|
|
138
173
|
<div>
|
|
139
|
-
{title && <h2 className="text-lg font-semibold text-
|
|
140
|
-
{description && <p className="text-sm text-
|
|
174
|
+
{title && <h2 className="text-lg font-semibold text-text-primary">{title}</h2>}
|
|
175
|
+
{description && <p className="text-sm text-text-secondary">{description}</p>}
|
|
141
176
|
</div>
|
|
142
177
|
{actions && <div className="flex items-center gap-2">{actions}</div>}
|
|
143
178
|
</div>
|
|
@@ -148,7 +183,13 @@ export function PageSection({
|
|
|
148
183
|
}
|
|
149
184
|
|
|
150
185
|
/**
|
|
151
|
-
* EmptyState - A consistent empty state component
|
|
186
|
+
* EmptyState - A consistent empty/zero state component.
|
|
187
|
+
*
|
|
188
|
+
* Boff UI 2.0: when an `illustration` name is provided (one of the standard
|
|
189
|
+
* host-served names — empty-data, empty-search, empty-files, empty-people,
|
|
190
|
+
* empty-notifications, onboarding, empty-generic), it renders the on-brand
|
|
191
|
+
* IllustratedEmptyState. Otherwise it falls back to the legacy icon layout so
|
|
192
|
+
* existing callers keep working unchanged.
|
|
152
193
|
*/
|
|
153
194
|
export function EmptyState({
|
|
154
195
|
icon,
|
|
@@ -156,13 +197,27 @@ export function EmptyState({
|
|
|
156
197
|
description,
|
|
157
198
|
action,
|
|
158
199
|
className,
|
|
200
|
+
illustration = 'empty-data',
|
|
159
201
|
}: {
|
|
160
202
|
icon?: ReactNode;
|
|
161
203
|
title: string;
|
|
162
204
|
description?: string;
|
|
163
205
|
action?: ReactNode;
|
|
164
206
|
className?: string;
|
|
207
|
+
/** Standard illustration name served by the host at /illustrations/<name>.webp. */
|
|
208
|
+
illustration?: string;
|
|
165
209
|
}) {
|
|
210
|
+
if (!icon) {
|
|
211
|
+
return (
|
|
212
|
+
<IllustratedEmptyState
|
|
213
|
+
illustration={illustration}
|
|
214
|
+
title={title}
|
|
215
|
+
description={description}
|
|
216
|
+
action={action}
|
|
217
|
+
className={cn('animate-in fade-in-0 zoom-in-95 duration-300', className)}
|
|
218
|
+
/>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
166
221
|
return (
|
|
167
222
|
<div
|
|
168
223
|
className={cn(
|
|
@@ -173,9 +228,9 @@ export function EmptyState({
|
|
|
173
228
|
className
|
|
174
229
|
)}
|
|
175
230
|
>
|
|
176
|
-
|
|
177
|
-
<h3 className="text-lg font-semibold text-
|
|
178
|
-
{description && <p className="text-sm text-
|
|
231
|
+
<div className="mb-4 p-4 rounded-2xl bg-bg-sunken/50 text-text-secondary">{icon}</div>
|
|
232
|
+
<h3 className="text-lg font-semibold text-text-primary mb-1">{title}</h3>
|
|
233
|
+
{description && <p className="text-sm text-text-secondary max-w-sm mb-4">{description}</p>}
|
|
179
234
|
{action}
|
|
180
235
|
</div>
|
|
181
236
|
);
|
|
@@ -189,7 +244,7 @@ export function LoadingState({ message = 'Loading...' }: { message?: string }) {
|
|
|
189
244
|
<div className="flex items-center justify-center min-h-[400px]">
|
|
190
245
|
<div className="flex flex-col items-center gap-3">
|
|
191
246
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
|
192
|
-
<p className="text-sm text-
|
|
247
|
+
<p className="text-sm text-text-secondary">{message}</p>
|
|
193
248
|
</div>
|
|
194
249
|
</div>
|
|
195
250
|
);
|
|
@@ -209,7 +264,7 @@ export function ErrorState({
|
|
|
209
264
|
}) {
|
|
210
265
|
return (
|
|
211
266
|
<div className="flex flex-col items-center justify-center py-12 px-4 text-center">
|
|
212
|
-
<div className="mb-4 p-4 rounded-2xl bg-
|
|
267
|
+
<div className="mb-4 p-4 rounded-2xl bg-status-error-bg/10 text-status-error-text">
|
|
213
268
|
<svg className="h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
214
269
|
<path
|
|
215
270
|
strokeLinecap="round"
|
|
@@ -219,8 +274,8 @@ export function ErrorState({
|
|
|
219
274
|
/>
|
|
220
275
|
</svg>
|
|
221
276
|
</div>
|
|
222
|
-
<h3 className="text-lg font-semibold text-
|
|
223
|
-
{message && <p className="text-sm text-
|
|
277
|
+
<h3 className="text-lg font-semibold text-text-primary mb-1">{title}</h3>
|
|
278
|
+
{message && <p className="text-sm text-text-secondary max-w-sm mb-4">{message}</p>}
|
|
224
279
|
{onRetry && (
|
|
225
280
|
<button
|
|
226
281
|
onClick={onRetry}
|
|
@@ -78,8 +78,9 @@ export function StatusBadge({
|
|
|
78
78
|
>
|
|
79
79
|
{showDot && (
|
|
80
80
|
<span
|
|
81
|
+
aria-hidden="true"
|
|
81
82
|
className={cn(
|
|
82
|
-
'w-1.5 h-1.5 rounded-full mr-1.5 flex-shrink-0',
|
|
83
|
+
'w-1.5 h-1.5 rounded-full mr-1.5 flex-shrink-0 motion-safe:animate-vc-glow-pulse',
|
|
83
84
|
colors.text.replace('text-', 'bg-')
|
|
84
85
|
)}
|
|
85
86
|
/>
|
|
@@ -69,11 +69,11 @@ const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
|
69
69
|
return (
|
|
70
70
|
<div
|
|
71
71
|
ref={ref}
|
|
72
|
-
className={cn('relative h-2 w-full overflow-hidden rounded-full bg-
|
|
72
|
+
className={cn('relative h-2 w-full overflow-hidden rounded-full bg-bg-sunken', className)}
|
|
73
73
|
{...props}
|
|
74
74
|
>
|
|
75
75
|
<div
|
|
76
|
-
className="h-full bg-primary transition-all duration-300 ease-in-out"
|
|
76
|
+
className="h-full bg-action-primary-bg transition-all duration-300 ease-in-out"
|
|
77
77
|
style={{ width: `${percentage}%` }}
|
|
78
78
|
/>
|
|
79
79
|
</div>
|
|
@@ -112,7 +112,7 @@ const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>(
|
|
|
112
112
|
<div
|
|
113
113
|
ref={ref}
|
|
114
114
|
className={cn(
|
|
115
|
-
'flex h-full w-full items-center justify-center rounded-full bg-
|
|
115
|
+
'flex h-full w-full items-center justify-center rounded-full bg-bg-sunken',
|
|
116
116
|
className
|
|
117
117
|
)}
|
|
118
118
|
{...props}
|
|
@@ -76,7 +76,7 @@ export default function AIAssistantPage() {
|
|
|
76
76
|
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
|
77
77
|
>
|
|
78
78
|
<div
|
|
79
|
-
className={`max-w-[80%] p-3 rounded-lg ${msg.role === 'user' ? 'bg-primary text-primary-
|
|
79
|
+
className={`max-w-[80%] p-3 rounded-lg ${msg.role === 'user' ? 'bg-action-primary-bg text-action-primary-text' : 'bg-bg-sunken'}`}
|
|
80
80
|
>
|
|
81
81
|
<p className="whitespace-pre-line text-sm">{msg.content}</p>
|
|
82
82
|
</div>
|
|
@@ -139,13 +139,13 @@ export default function AIAssistantPage() {
|
|
|
139
139
|
</CardContent>
|
|
140
140
|
</Card>
|
|
141
141
|
|
|
142
|
-
<Card className="bg-primary/5 border-primary/20">
|
|
142
|
+
<Card className="bg-action-primary-bg/5 border-primary/20">
|
|
143
143
|
<CardContent className="p-4">
|
|
144
144
|
<div className="flex items-start gap-3">
|
|
145
145
|
<Lightbulb size={20} className="text-primary flex-shrink-0" />
|
|
146
146
|
<div>
|
|
147
147
|
<p className="font-medium text-sm">Pro Tip</p>
|
|
148
|
-
<p className="text-xs text-
|
|
148
|
+
<p className="text-xs text-text-secondary">
|
|
149
149
|
You can ask complex questions like "What would happen if I added 2 more vehicles
|
|
150
150
|
to my fleet?" for scenario analysis.
|
|
151
151
|
</p>
|