@burdenoff/microfe-movethewheels 2026.716.2 → 2026.719.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/generated/wspace-operations.d.ts +0 -42
- package/dist/hooks/index.js +0 -1
- package/dist/index.js +25 -26
- package/dist/movethewheels/hooks/index.d.ts +0 -1
- package/dist/movethewheels/pages/AIAssistantPage.d.ts +19 -1
- package/dist/node_modules/lucide-react/dist/esm/icons/message-circle.js +8 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/shield-check.js +11 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wallet.js +11 -0
- package/dist/pages/AIAssistantPage.js +74 -201
- package/package.json +1 -1
- package/src/generated/wspace-operations.ts +0 -52
- package/src/movethewheels/hooks/index.ts +0 -3
- package/src/movethewheels/index.ts +1 -1
- package/src/movethewheels/pages/AIAssistantPage.tsx +81 -185
- package/src/operations/wspace/movethewheels/queries/dashboard.graphql +0 -8
- package/dist/hooks/useAssistant.js +0 -39
- package/dist/movethewheels/hooks/useAssistant.d.ts +0 -17
- package/dist/node_modules/lucide-react/dist/esm/icons/lightbulb.js +0 -18
- package/dist/node_modules/lucide-react/dist/esm/icons/send.js +0 -11
- package/dist/node_modules/lucide-react/dist/esm/icons/sparkles.js +0 -26
- package/src/movethewheels/hooks/useAssistant.ts +0 -74
|
@@ -1147,18 +1147,6 @@ export type GetMovethewheelsDashboardQuery = {
|
|
|
1147
1147
|
}>;
|
|
1148
1148
|
};
|
|
1149
1149
|
};
|
|
1150
|
-
export type GetAssistantInsightsQueryVariables = Types.Exact<{
|
|
1151
|
-
question: Types.Scalars['String']['input'];
|
|
1152
|
-
}>;
|
|
1153
|
-
export type GetAssistantInsightsQuery = {
|
|
1154
|
-
__typename?: 'Query';
|
|
1155
|
-
assistantInsights: {
|
|
1156
|
-
__typename?: 'AssistantInsights';
|
|
1157
|
-
question: string;
|
|
1158
|
-
answer: string;
|
|
1159
|
-
generatedAt: string;
|
|
1160
|
-
};
|
|
1161
|
-
};
|
|
1162
1150
|
export type ListCustomersQueryVariables = Types.Exact<{
|
|
1163
1151
|
filter?: Types.InputMaybe<Types.CustomerFilterInput>;
|
|
1164
1152
|
pagination?: Types.InputMaybe<Types.PaginationInput>;
|
|
@@ -2964,36 +2952,6 @@ export type GetMovethewheelsDashboardQueryHookResult = ReturnType<typeof useGetM
|
|
|
2964
2952
|
export type GetMovethewheelsDashboardLazyQueryHookResult = ReturnType<typeof useGetMovethewheelsDashboardLazyQuery>;
|
|
2965
2953
|
export type GetMovethewheelsDashboardSuspenseQueryHookResult = ReturnType<typeof useGetMovethewheelsDashboardSuspenseQuery>;
|
|
2966
2954
|
export type GetMovethewheelsDashboardQueryResult = Apollo.QueryResult<Types.GetMovethewheelsDashboardQuery, Types.GetMovethewheelsDashboardQueryVariables>;
|
|
2967
|
-
export declare const GetAssistantInsightsDocument: Apollo.DocumentNode;
|
|
2968
|
-
/**
|
|
2969
|
-
* __useGetAssistantInsightsQuery__
|
|
2970
|
-
*
|
|
2971
|
-
* To run a query within a React component, call `useGetAssistantInsightsQuery` and pass it any options that fit your needs.
|
|
2972
|
-
* When your component renders, `useGetAssistantInsightsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
|
2973
|
-
* you can use to render your UI.
|
|
2974
|
-
*
|
|
2975
|
-
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
|
2976
|
-
*
|
|
2977
|
-
* @example
|
|
2978
|
-
* const { data, loading, error } = useGetAssistantInsightsQuery({
|
|
2979
|
-
* variables: {
|
|
2980
|
-
* question: // value for 'question'
|
|
2981
|
-
* },
|
|
2982
|
-
* });
|
|
2983
|
-
*/
|
|
2984
|
-
export declare function useGetAssistantInsightsQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables> & ({
|
|
2985
|
-
variables: Types.GetAssistantInsightsQueryVariables;
|
|
2986
|
-
skip?: boolean;
|
|
2987
|
-
} | {
|
|
2988
|
-
skip: boolean;
|
|
2989
|
-
})): ApolloReactHooks.useQuery.Result<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables, "empty" | "complete" | "streaming", Types.GetAssistantInsightsQueryVariables>;
|
|
2990
|
-
export declare function useGetAssistantInsightsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>): ApolloReactHooks.useLazyQuery.ResultTuple<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables, "empty" | "complete" | "streaming">;
|
|
2991
|
-
export declare function useGetAssistantInsightsSuspenseQuery(baseOptions?: ApolloReactHooks.SuspenseQueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>): ApolloReactHooks.UseSuspenseQueryResult<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>;
|
|
2992
|
-
export declare function useGetAssistantInsightsSuspenseQuery(baseOptions?: ApolloReactHooks.SkipToken | ApolloReactHooks.SuspenseQueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>): ApolloReactHooks.UseSuspenseQueryResult<Types.GetAssistantInsightsQuery | undefined, Types.GetAssistantInsightsQueryVariables>;
|
|
2993
|
-
export type GetAssistantInsightsQueryHookResult = ReturnType<typeof useGetAssistantInsightsQuery>;
|
|
2994
|
-
export type GetAssistantInsightsLazyQueryHookResult = ReturnType<typeof useGetAssistantInsightsLazyQuery>;
|
|
2995
|
-
export type GetAssistantInsightsSuspenseQueryHookResult = ReturnType<typeof useGetAssistantInsightsSuspenseQuery>;
|
|
2996
|
-
export type GetAssistantInsightsQueryResult = Apollo.QueryResult<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>;
|
|
2997
2955
|
export declare const ListCustomersDocument: Apollo.DocumentNode;
|
|
2998
2956
|
/**
|
|
2999
2957
|
* __useListCustomersQuery__
|
package/dist/hooks/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -9,31 +9,30 @@ import { useCustomer as b, useCustomerStats as x, useCustomers as S } from "./ho
|
|
|
9
9
|
import { useRoute as C, useRouteStats as w, useRoutes as T } from "./hooks/useRoutes.js";
|
|
10
10
|
import { useAnalytics as E, useDashboardMetrics as D, useFinanceMetrics as O, useFleetAnalytics as k, useOrderAnalytics as A, usePerformanceAnalytics as j, useRevenueAnalytics as M } from "./hooks/useAnalytics.js";
|
|
11
11
|
import { useDriverTracking as N, useGeofence as P, useLiveTracking as F, useOrderTracking as I, useTracking as L, useTrackingAlerts as R, useVehicleTracking as z } from "./hooks/useTracking.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { useEntityDocuments as W } from "./hooks/useEntityDocuments.js";
|
|
12
|
+
import { useReports as B } from "./hooks/useReports.js";
|
|
13
|
+
import { useImportExport as V } from "./hooks/useImportExport.js";
|
|
14
|
+
import { useModuleSettings as H } from "./hooks/useSettings.js";
|
|
15
|
+
import { useEntityDocuments as U } from "./hooks/useEntityDocuments.js";
|
|
17
16
|
import "./hooks/index.js";
|
|
18
|
-
import { Avatar as
|
|
19
|
-
import { EmptyState as
|
|
20
|
-
import { chartTokens as
|
|
21
|
-
import { Sparkline as
|
|
22
|
-
import { MetricCard as
|
|
23
|
-
import { GlassPanel as
|
|
24
|
-
import { ChartEmpty as
|
|
25
|
-
import { TrendChart as
|
|
26
|
-
import { VolumeBar as
|
|
27
|
-
import { StatusDonut as
|
|
28
|
-
import { createCurrentIcon as
|
|
29
|
-
import { MapCanvas as
|
|
30
|
-
import { LogisticsMap as
|
|
31
|
-
import { RouteMap as
|
|
32
|
-
import { TrackingMap as
|
|
33
|
-
import { PaymentStatusBadge as
|
|
34
|
-
import { DataTable as
|
|
35
|
-
import { FormError as
|
|
36
|
-
import { Notice as
|
|
37
|
-
import { DocumentsCard as
|
|
17
|
+
import { Avatar as W, AvatarFallback as G, AvatarImage as K, Badge as q, Button as J, Card as Y, CardContent as X, CardDescription as Z, CardFooter as Q, CardHeader as $, CardTitle as ee, Checkbox as te, Dialog as ne, DialogContent as re, DialogDescription as ie, DialogFooter as ae, DialogHeader as oe, DialogTitle as se, DialogTrigger as ce, DropdownMenu as le, DropdownMenuContent as ue, DropdownMenuItem as de, DropdownMenuSeparator as fe, DropdownMenuTrigger as pe, Input as me, Label as he, Progress as ge, ScrollArea as _e, Select as ve, SelectContent as ye, SelectItem as be, SelectTrigger as xe, SelectValue as Se, Separator as Ce, Skeleton as we, Switch as Te, Table as Ee, TableBody as De, TableCell as Oe, TableHead as ke, TableHeader as Ae, TableRow as je, Tabs as Me, TabsContent as Ne, TabsList as Pe, TabsTrigger as Fe, Textarea as Ie, Tooltip as Le, TooltipContent as Re, TooltipProvider as ze, TooltipTrigger as Be } from "./components/ui.js";
|
|
18
|
+
import { EmptyState as Ve, ErrorState as He, LoadingState as Ue, PageLayout as We, PageSection as Ge } from "./components/PageLayout.js";
|
|
19
|
+
import { chartTokens as Ke, seriesColor as qe } from "./components/charts/chartTokens.js";
|
|
20
|
+
import { Sparkline as Je } from "./components/charts/Sparkline.js";
|
|
21
|
+
import { MetricCard as Ye, MetricCardSkeleton as Xe, MetricGrid as Ze } from "./components/MetricCard.js";
|
|
22
|
+
import { GlassPanel as Qe } from "./components/GlassPanel.js";
|
|
23
|
+
import { ChartEmpty as $e, ChartTooltip as et } from "./components/charts/ChartShell.js";
|
|
24
|
+
import { TrendChart as tt } from "./components/charts/TrendChart.js";
|
|
25
|
+
import { VolumeBar as nt } from "./components/charts/VolumeBar.js";
|
|
26
|
+
import { StatusDonut as rt } from "./components/charts/StatusDonut.js";
|
|
27
|
+
import { createCurrentIcon as it, createPinIcon as at, createStopIcon as ot, isRealCoord as st } from "./components/maps/leaflet-setup.js";
|
|
28
|
+
import { MapCanvas as ct } from "./components/maps/MapCanvas.js";
|
|
29
|
+
import { LogisticsMap as lt } from "./components/maps/LogisticsMap.js";
|
|
30
|
+
import { RouteMap as ut } from "./components/maps/RouteMap.js";
|
|
31
|
+
import { TrackingMap as dt } from "./components/maps/TrackingMap.js";
|
|
32
|
+
import { PaymentStatusBadge as ft, PriorityBadge as pt, StatusBadge as mt, TypeBadge as ht, VehicleTypeBadge as gt } from "./components/StatusBadge.js";
|
|
33
|
+
import { DataTable as _t } from "./components/DataTable.js";
|
|
34
|
+
import { FormError as vt } from "./components/FormError.js";
|
|
35
|
+
import { Notice as yt, useNotice as bt } from "./components/Notice.js";
|
|
36
|
+
import { DocumentsCard as xt } from "./components/DocumentsCard.js";
|
|
38
37
|
import "./components/index.js";
|
|
39
|
-
export {
|
|
38
|
+
export { W as Avatar, G as AvatarFallback, K as AvatarImage, q as Badge, J as Button, Y as Card, X as CardContent, Z as CardDescription, Q as CardFooter, $ as CardHeader, ee as CardTitle, $e as ChartEmpty, et as ChartTooltip, te as Checkbox, _t as DataTable, ne as Dialog, re as DialogContent, ie as DialogDescription, ae as DialogFooter, oe as DialogHeader, se as DialogTitle, ce as DialogTrigger, xt as DocumentsCard, le as DropdownMenu, ue as DropdownMenuContent, de as DropdownMenuItem, fe as DropdownMenuSeparator, pe as DropdownMenuTrigger, Ve as EmptyState, He as ErrorState, vt as FormError, Qe as GlassPanel, me as Input, he as Label, Ue as LoadingState, lt as LogisticsMap, ct as MapCanvas, Ye as MetricCard, Xe as MetricCardSkeleton, Ze as MetricGrid, n as MoveTheWheelsRoot, yt as Notice, We as PageLayout, Ge as PageSection, ft as PaymentStatusBadge, pt as PriorityBadge, ge as Progress, ut as RouteMap, _e as ScrollArea, ve as Select, ye as SelectContent, be as SelectItem, xe as SelectTrigger, Se as SelectValue, Ce as Separator, we as Skeleton, Je as Sparkline, mt as StatusBadge, rt as StatusDonut, Te as Switch, Ee as Table, De as TableBody, Oe as TableCell, ke as TableHead, Ae as TableHeader, je as TableRow, Me as Tabs, Ne as TabsContent, Pe as TabsList, Fe as TabsTrigger, Ie as Textarea, Le as Tooltip, Re as TooltipContent, ze as TooltipProvider, Be as TooltipTrigger, dt as TrackingMap, tt as TrendChart, ht as TypeBadge, gt as VehicleTypeBadge, nt as VolumeBar, Ke as chartTokens, it as createCurrentIcon, at as createPinIcon, ot as createStopIcon, st as isRealCoord, qe as seriesColor, E as useAnalytics, b as useCustomer, x as useCustomerStats, S as useCustomers, D as useDashboardMetrics, c as useDriver, N as useDriverTracking, l as useDrivers, U as useEntityDocuments, O as useFinanceMetrics, u as useFleet, k as useFleetAnalytics, d as useFleetStats, P as useGeofence, V as useImportExport, m as useInventory, h as useInventoryStats, F as useLiveTracking, H as useModuleSettings, e as useMoveTheWheels, t as useMoveTheWheelsNavigate, r as useMoveTheWheelsStore, bt as useNotice, a as useOrder, A as useOrderAnalytics, o as useOrderStats, I as useOrderTracking, s as useOrders, j as usePerformanceAnalytics, g as useProduct, _ as useProducts, B as useReports, M as useRevenueAnalytics, C as useRoute, w as useRouteStats, T as useRoutes, i as useSandboxAssistantTransport, L as useTracking, R as useTrackingAlerts, f as useVehicle, z as useVehicleTracking, p as useVehicles, v as useWarehouse, y as useWarehouses };
|
|
@@ -10,7 +10,6 @@ export { useCustomers, useCustomer, useCustomerStats } from './useCustomers';
|
|
|
10
10
|
export { useRoutes, useRoute, useRouteStats } from './useRoutes';
|
|
11
11
|
export { useAnalytics, useDashboardMetrics, useRevenueAnalytics, useOrderAnalytics, useFleetAnalytics, usePerformanceAnalytics, useFinanceMetrics, type FinanceMetrics, } from './useAnalytics';
|
|
12
12
|
export { useTracking, useOrderTracking, useVehicleTracking, useDriverTracking, useLiveTracking, useTrackingAlerts, useGeofence, } from './useTracking';
|
|
13
|
-
export { useAssistant } from './useAssistant';
|
|
14
13
|
export { useReports } from './useReports';
|
|
15
14
|
export { useImportExport } from './useImportExport';
|
|
16
15
|
export { useModuleSettings } from './useSettings';
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI
|
|
2
|
+
* AI Operations Page
|
|
3
|
+
*
|
|
4
|
+
* This page previously shipped a second, fabricated chat surface
|
|
5
|
+
* (`useAssistant` → `Query.assistantInsights`) whose "capabilities" list
|
|
6
|
+
* advertised route optimization, demand forecasting, fleet analysis, cost
|
|
7
|
+
* optimization and natural-language queries — none of which the backend
|
|
8
|
+
* implemented; every question routed to a fixed template built from four
|
|
9
|
+
* Prisma counts. See BOFF-3897.
|
|
10
|
+
*
|
|
11
|
+
* The real conversational assistant is the shared `AssistantWidget`, already
|
|
12
|
+
* mounted globally in the app shell (`AppShell.tsx`) on the sandbox
|
|
13
|
+
* transport — this page must never contain a second chat box.
|
|
14
|
+
*
|
|
15
|
+
* The full "AI Operations" surface this page is meant to become — a model
|
|
16
|
+
* picker, an agent-activity feed reading the shared AgentRun ledger,
|
|
17
|
+
* accept/reject decision cards for AI dispatch proposals, and per-workspace
|
|
18
|
+
* AI credit visibility — depends on the shared multi-model AI gateway
|
|
19
|
+
* (BOFF-3601), which has not landed yet. Until it does, this page shows an
|
|
20
|
+
* honest roadmap state instead of fabricating any of that content.
|
|
3
21
|
*/
|
|
4
22
|
export default function AIAssistantPage(): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createLucideIcon as e } from "../createLucideIcon.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/message-circle.js
|
|
3
|
+
var t = e("MessageCircle", [["path", {
|
|
4
|
+
d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z",
|
|
5
|
+
key: "vv11sd"
|
|
6
|
+
}]]);
|
|
7
|
+
//#endregion
|
|
8
|
+
export { t as MessageCircle };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createLucideIcon as e } from "../createLucideIcon.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/shield-check.js
|
|
3
|
+
var t = e("ShieldCheck", [["path", {
|
|
4
|
+
d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",
|
|
5
|
+
key: "oel41y"
|
|
6
|
+
}], ["path", {
|
|
7
|
+
d: "m9 12 2 2 4-4",
|
|
8
|
+
key: "dzmm74"
|
|
9
|
+
}]]);
|
|
10
|
+
//#endregion
|
|
11
|
+
export { t as ShieldCheck };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createLucideIcon as e } from "../createLucideIcon.js";
|
|
2
|
+
//#region node_modules/lucide-react/dist/esm/icons/wallet.js
|
|
3
|
+
var t = e("Wallet", [["path", {
|
|
4
|
+
d: "M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1",
|
|
5
|
+
key: "18etb6"
|
|
6
|
+
}], ["path", {
|
|
7
|
+
d: "M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4",
|
|
8
|
+
key: "xoc0q4"
|
|
9
|
+
}]]);
|
|
10
|
+
//#endregion
|
|
11
|
+
export { t as Wallet };
|
|
@@ -1,206 +1,79 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { PageLayout as g } from "../components/PageLayout.js";
|
|
11
|
-
import { useState as _ } from "react";
|
|
12
|
-
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
13
|
-
import { GlassCard as b, PagePurpose as x } from "@burdenoff/fe-libs/ui";
|
|
1
|
+
import { Activity as e } from "../node_modules/lucide-react/dist/esm/icons/activity.js";
|
|
2
|
+
import { Bot as t } from "../node_modules/lucide-react/dist/esm/icons/bot.js";
|
|
3
|
+
import { MessageCircle as n } from "../node_modules/lucide-react/dist/esm/icons/message-circle.js";
|
|
4
|
+
import { ShieldCheck as r } from "../node_modules/lucide-react/dist/esm/icons/shield-check.js";
|
|
5
|
+
import { Wallet as i } from "../node_modules/lucide-react/dist/esm/icons/wallet.js";
|
|
6
|
+
import { Card as a, CardContent as o, CardHeader as s, CardTitle as c } from "../components/ui.js";
|
|
7
|
+
import { EmptyState as l, PageLayout as u } from "../components/PageLayout.js";
|
|
8
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
9
|
+
import { GlassCard as p, PagePurpose as m } from "@burdenoff/fe-libs/ui";
|
|
14
10
|
//#region src/movethewheels/pages/AIAssistantPage.tsx
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
content: e.answer
|
|
58
|
-
}]);
|
|
59
|
-
} catch (e) {
|
|
60
|
-
D((t) => [...t, {
|
|
61
|
-
role: "assistant",
|
|
62
|
-
content: e instanceof Error ? `Sorry, I couldn't answer that: ${e.message}` : "Sorry, I couldn't reach the assistant. Please try again.",
|
|
63
|
-
isError: !0
|
|
64
|
-
}]);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}, j = () => {
|
|
68
|
-
A(w);
|
|
69
|
-
};
|
|
70
|
-
return /* @__PURE__ */ y(g, {
|
|
71
|
-
title: "AI Assistant",
|
|
72
|
-
subtitle: "Intelligent logistics support",
|
|
73
|
-
icon: /* @__PURE__ */ v(e, { size: 24 }),
|
|
74
|
-
children: [/* @__PURE__ */ v(x, {
|
|
75
|
-
icon: /* @__PURE__ */ v(e, { size: 18 }),
|
|
76
|
-
children: "Ask plain-English questions about your operation and get answers backed by your own data. Use it to optimize routes, forecast demand, spot likely delays, and run what-if scenarios (e.g. “what if I add two vehicles?”) — like having a logistics analyst on call."
|
|
77
|
-
}), /* @__PURE__ */ y("div", {
|
|
78
|
-
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
79
|
-
children: [/* @__PURE__ */ v("div", {
|
|
80
|
-
className: "lg:col-span-2 space-y-4",
|
|
81
|
-
children: /* @__PURE__ */ y(b, {
|
|
82
|
-
treatment: "glass",
|
|
83
|
-
glow: !0,
|
|
84
|
-
className: "h-[600px] flex flex-col",
|
|
85
|
-
children: [
|
|
86
|
-
/* @__PURE__ */ v(f, {
|
|
87
|
-
className: "border-b border-border-seam",
|
|
88
|
-
children: /* @__PURE__ */ y(p, {
|
|
89
|
-
className: "flex items-center gap-2",
|
|
90
|
-
children: [/* @__PURE__ */ v(a, {
|
|
91
|
-
size: 18,
|
|
92
|
-
className: "text-primary"
|
|
93
|
-
}), " Chat with AI"]
|
|
94
|
-
})
|
|
95
|
-
}),
|
|
96
|
-
/* @__PURE__ */ v(h, {
|
|
97
|
-
className: "flex-1 p-4",
|
|
98
|
-
children: /* @__PURE__ */ y("div", {
|
|
99
|
-
className: "space-y-4",
|
|
100
|
-
children: [E.map((e, t) => /* @__PURE__ */ v("div", {
|
|
101
|
-
className: `flex ${e.role === "user" ? "justify-end" : "justify-start"}`,
|
|
102
|
-
children: /* @__PURE__ */ v("div", {
|
|
103
|
-
className: `max-w-[80%] p-3 rounded-lg ${e.role === "user" ? "bg-action-primary-bg text-action-primary-text" : e.isError ? "bg-status-error-bg-subtle text-status-error-text" : "bg-bg-sunken"}`,
|
|
104
|
-
children: /* @__PURE__ */ v("p", {
|
|
105
|
-
className: "whitespace-pre-line text-sm",
|
|
106
|
-
children: e.content
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
}, t)), C && /* @__PURE__ */ v("div", {
|
|
110
|
-
className: "flex justify-start",
|
|
111
|
-
"aria-live": "polite",
|
|
112
|
-
children: /* @__PURE__ */ y("div", {
|
|
113
|
-
className: "max-w-[80%] p-3 rounded-lg bg-bg-sunken flex items-center gap-2",
|
|
114
|
-
children: [/* @__PURE__ */ v(a, {
|
|
115
|
-
size: 14,
|
|
116
|
-
className: "text-primary motion-safe:animate-pulse"
|
|
117
|
-
}), /* @__PURE__ */ v("span", {
|
|
118
|
-
className: "text-sm text-text-secondary",
|
|
119
|
-
children: "Analyzing your data…"
|
|
120
|
-
})]
|
|
121
|
-
})
|
|
122
|
-
})]
|
|
123
|
-
})
|
|
124
|
-
}),
|
|
125
|
-
/* @__PURE__ */ v("div", {
|
|
126
|
-
className: "p-4 border-t",
|
|
127
|
-
children: /* @__PURE__ */ y("div", {
|
|
128
|
-
className: "flex gap-2",
|
|
129
|
-
children: [/* @__PURE__ */ v(m, {
|
|
130
|
-
value: w,
|
|
131
|
-
onChange: (e) => T(e.target.value),
|
|
132
|
-
placeholder: "Ask me anything about your logistics operations...",
|
|
133
|
-
disabled: C,
|
|
134
|
-
onKeyPress: (e) => e.key === "Enter" && j()
|
|
135
|
-
}), /* @__PURE__ */ v(l, {
|
|
136
|
-
onClick: j,
|
|
137
|
-
disabled: C || !w.trim(),
|
|
138
|
-
children: /* @__PURE__ */ v(i, { size: 16 })
|
|
139
|
-
})]
|
|
140
|
-
})
|
|
141
|
-
})
|
|
142
|
-
]
|
|
11
|
+
var h = [
|
|
12
|
+
{
|
|
13
|
+
icon: e,
|
|
14
|
+
title: "Agent activity feed",
|
|
15
|
+
description: "A live log of what the AI decided, on which shipments, with trigger, model, latency and cost — so an autonomous run is never a black box."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
icon: r,
|
|
19
|
+
title: "Dispatch decision cards",
|
|
20
|
+
description: "Proposed driver/vehicle assignments and carrier recommendations with rationale and confidence, requiring an explicit accept or reject before anything is written."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
icon: i,
|
|
24
|
+
title: "AI credit visibility",
|
|
25
|
+
description: "Per-workspace AI spend for admins, so cost is visible before the invoice."
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
function g() {
|
|
29
|
+
return /* @__PURE__ */ f(u, {
|
|
30
|
+
title: "AI Operations",
|
|
31
|
+
subtitle: "What the AI is doing on your workspace, and why",
|
|
32
|
+
icon: /* @__PURE__ */ d(t, { size: 24 }),
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ d(m, {
|
|
35
|
+
icon: /* @__PURE__ */ d(t, { size: 18 }),
|
|
36
|
+
children: "This page will surface AI-driven dispatch decisions and their outcomes once the shared AI runtime lands. It does not host a chat box — chat with your live operational data is always available from the assistant icon anywhere in the app."
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ d(p, {
|
|
39
|
+
treatment: "glass",
|
|
40
|
+
className: "p-4",
|
|
41
|
+
children: /* @__PURE__ */ f("div", {
|
|
42
|
+
className: "flex items-start gap-3",
|
|
43
|
+
children: [/* @__PURE__ */ d(n, {
|
|
44
|
+
size: 20,
|
|
45
|
+
className: "text-primary flex-shrink-0 mt-0.5"
|
|
46
|
+
}), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
|
|
47
|
+
className: "text-sm font-medium text-text-primary",
|
|
48
|
+
children: "Looking to ask a question about your operations?"
|
|
49
|
+
}), /* @__PURE__ */ d("p", {
|
|
50
|
+
className: "text-sm text-text-secondary mt-1",
|
|
51
|
+
children: "Open the assistant from its icon in the corner of the screen — it answers from your live orders, shipments, fleet and inventory data, and it is the only chat surface in this product."
|
|
52
|
+
})] })]
|
|
143
53
|
})
|
|
144
|
-
}),
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
children: e.category
|
|
167
|
-
})
|
|
168
|
-
]
|
|
169
|
-
}, t))
|
|
170
|
-
})] }),
|
|
171
|
-
/* @__PURE__ */ y(u, { children: [/* @__PURE__ */ v(f, { children: /* @__PURE__ */ v(p, { children: "Capabilities" }) }), /* @__PURE__ */ v(d, { children: /* @__PURE__ */ v("ul", {
|
|
172
|
-
className: "space-y-2 text-sm",
|
|
173
|
-
children: k.map((e, t) => /* @__PURE__ */ y("li", {
|
|
174
|
-
className: "flex items-start gap-2",
|
|
175
|
-
children: [/* @__PURE__ */ v(a, {
|
|
176
|
-
size: 14,
|
|
177
|
-
className: "text-primary mt-0.5 flex-shrink-0"
|
|
178
|
-
}), /* @__PURE__ */ v("span", { children: e })]
|
|
179
|
-
}, t))
|
|
180
|
-
}) })] }),
|
|
181
|
-
/* @__PURE__ */ v(u, {
|
|
182
|
-
className: "bg-action-primary-bg/5 border-primary/20",
|
|
183
|
-
children: /* @__PURE__ */ v(d, {
|
|
184
|
-
className: "p-4",
|
|
185
|
-
children: /* @__PURE__ */ y("div", {
|
|
186
|
-
className: "flex items-start gap-3",
|
|
187
|
-
children: [/* @__PURE__ */ v(n, {
|
|
188
|
-
size: 20,
|
|
189
|
-
className: "text-primary flex-shrink-0"
|
|
190
|
-
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
191
|
-
className: "font-medium text-sm",
|
|
192
|
-
children: "Pro Tip"
|
|
193
|
-
}), /* @__PURE__ */ v("p", {
|
|
194
|
-
className: "text-xs text-text-secondary",
|
|
195
|
-
children: "You can ask complex questions like \"What would happen if I added 2 more vehicles to my fleet?\" for scenario analysis."
|
|
196
|
-
})] })]
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
})
|
|
200
|
-
]
|
|
201
|
-
})]
|
|
202
|
-
})]
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ f(a, { children: [/* @__PURE__ */ d(s, { children: /* @__PURE__ */ d(c, { children: "On the roadmap" }) }), /* @__PURE__ */ f(o, { children: [/* @__PURE__ */ d(l, {
|
|
56
|
+
illustration: "empty-agents",
|
|
57
|
+
title: "Autonomous dispatch operations aren't live yet",
|
|
58
|
+
description: "These capabilities depend on the shared multi-model AI gateway and agent-run ledger (BOFF-3601), which hasn't shipped across the platform yet. Nothing below is available today."
|
|
59
|
+
}), /* @__PURE__ */ d("ul", {
|
|
60
|
+
className: "mt-4 space-y-4",
|
|
61
|
+
children: h.map((e) => /* @__PURE__ */ f("li", {
|
|
62
|
+
className: "flex items-start gap-3",
|
|
63
|
+
children: [/* @__PURE__ */ d(e.icon, {
|
|
64
|
+
size: 18,
|
|
65
|
+
className: "text-text-secondary flex-shrink-0 mt-0.5"
|
|
66
|
+
}), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
|
|
67
|
+
className: "text-sm font-medium text-text-primary",
|
|
68
|
+
children: e.title
|
|
69
|
+
}), /* @__PURE__ */ d("p", {
|
|
70
|
+
className: "text-xs text-text-secondary",
|
|
71
|
+
children: e.description
|
|
72
|
+
})] })]
|
|
73
|
+
}, e.title))
|
|
74
|
+
})] })] })
|
|
75
|
+
]
|
|
203
76
|
});
|
|
204
77
|
}
|
|
205
78
|
//#endregion
|
|
206
|
-
export {
|
|
79
|
+
export { g as default };
|
package/package.json
CHANGED
|
@@ -387,13 +387,6 @@ export type GetMovethewheelsDashboardQueryVariables = Types.Exact<{ [key: string
|
|
|
387
387
|
|
|
388
388
|
export type GetMovethewheelsDashboardQuery = { __typename?: 'Query', dashboardMetrics: { __typename?: 'DashboardMetrics', totalOrders: number, activeDeliveries: number, availableDrivers: number, activeVehicles: number, lowStockItems: number, totalRevenue: number, recentOrders: Array<{ __typename?: 'MovethewheelsOrder', id: string, orderNumber: string, orderType: Types.OrderType, status: Types.OrderStatus, priority: Types.OrderPriority, customerId: string, trackingNumber?: string | null, pickupAddress: Record<string, unknown>, deliveryAddress: Record<string, unknown>, items: Record<string, unknown>, pricing?: Record<string, unknown> | null, scheduledDelivery?: string | null, vehicleId?: string | null, driverId?: string | null, createdAt: string, updatedAt: string }> }, orderStats: { __typename?: 'OrderStats', total: number, pending: number, inTransit: number, delivered: number, cancelled: number, failed: number }, fleetStats: { __typename?: 'FleetStats', totalVehicles: number, activeVehicles: number, inTransitVehicles: number, maintenanceVehicles: number, totalDrivers: number, availableDrivers: number, onDeliveryDrivers: number }, financeMetrics: { __typename?: 'FinanceMetrics', fuelCost: number, openInvoices: number, revenue: { __typename?: 'RevenueMetrics', totalRevenue: number, averageOrderValue: number, deliveredRevenue: number } }, analytics: { __typename?: 'AnalyticsSeries', deliveryTrend: Array<{ __typename?: 'AnalyticsPoint', label: string, value: number }>, orderTrend: Array<{ __typename?: 'AnalyticsPoint', label: string, value: number }>, revenueTrend: Array<{ __typename?: 'AnalyticsPoint', label: string, value: number }> } };
|
|
389
389
|
|
|
390
|
-
export type GetAssistantInsightsQueryVariables = Types.Exact<{
|
|
391
|
-
question: Types.Scalars['String']['input'];
|
|
392
|
-
}>;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
export type GetAssistantInsightsQuery = { __typename?: 'Query', assistantInsights: { __typename?: 'AssistantInsights', question: string, answer: string, generatedAt: string } };
|
|
396
|
-
|
|
397
390
|
export type ListCustomersQueryVariables = Types.Exact<{
|
|
398
391
|
filter?: Types.InputMaybe<Types.CustomerFilterInput>;
|
|
399
392
|
pagination?: Types.InputMaybe<Types.PaginationInput>;
|
|
@@ -2466,51 +2459,6 @@ export type GetMovethewheelsDashboardQueryHookResult = ReturnType<typeof useGetM
|
|
|
2466
2459
|
export type GetMovethewheelsDashboardLazyQueryHookResult = ReturnType<typeof useGetMovethewheelsDashboardLazyQuery>;
|
|
2467
2460
|
export type GetMovethewheelsDashboardSuspenseQueryHookResult = ReturnType<typeof useGetMovethewheelsDashboardSuspenseQuery>;
|
|
2468
2461
|
export type GetMovethewheelsDashboardQueryResult = Apollo.QueryResult<Types.GetMovethewheelsDashboardQuery, Types.GetMovethewheelsDashboardQueryVariables>;
|
|
2469
|
-
export const GetAssistantInsightsDocument = gql`
|
|
2470
|
-
query GetAssistantInsights($question: String!) {
|
|
2471
|
-
assistantInsights(question: $question) {
|
|
2472
|
-
question
|
|
2473
|
-
answer
|
|
2474
|
-
generatedAt
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
`;
|
|
2478
|
-
|
|
2479
|
-
/**
|
|
2480
|
-
* __useGetAssistantInsightsQuery__
|
|
2481
|
-
*
|
|
2482
|
-
* To run a query within a React component, call `useGetAssistantInsightsQuery` and pass it any options that fit your needs.
|
|
2483
|
-
* When your component renders, `useGetAssistantInsightsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
|
2484
|
-
* you can use to render your UI.
|
|
2485
|
-
*
|
|
2486
|
-
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
|
2487
|
-
*
|
|
2488
|
-
* @example
|
|
2489
|
-
* const { data, loading, error } = useGetAssistantInsightsQuery({
|
|
2490
|
-
* variables: {
|
|
2491
|
-
* question: // value for 'question'
|
|
2492
|
-
* },
|
|
2493
|
-
* });
|
|
2494
|
-
*/
|
|
2495
|
-
export function useGetAssistantInsightsQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables> & ({ variables: Types.GetAssistantInsightsQueryVariables; skip?: boolean; } | { skip: boolean; }) ) {
|
|
2496
|
-
const options = {...defaultOptions, ...baseOptions}
|
|
2497
|
-
return ApolloReactHooks.useQuery<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>(GetAssistantInsightsDocument, options);
|
|
2498
|
-
}
|
|
2499
|
-
export function useGetAssistantInsightsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>) {
|
|
2500
|
-
const options = {...defaultOptions, ...baseOptions}
|
|
2501
|
-
return ApolloReactHooks.useLazyQuery<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>(GetAssistantInsightsDocument, options);
|
|
2502
|
-
}
|
|
2503
|
-
// @ts-ignore
|
|
2504
|
-
export function useGetAssistantInsightsSuspenseQuery(baseOptions?: ApolloReactHooks.SuspenseQueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>): ApolloReactHooks.UseSuspenseQueryResult<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>;
|
|
2505
|
-
export function useGetAssistantInsightsSuspenseQuery(baseOptions?: ApolloReactHooks.SkipToken | ApolloReactHooks.SuspenseQueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>): ApolloReactHooks.UseSuspenseQueryResult<Types.GetAssistantInsightsQuery | undefined, Types.GetAssistantInsightsQueryVariables>;
|
|
2506
|
-
export function useGetAssistantInsightsSuspenseQuery(baseOptions?: ApolloReactHooks.SkipToken | ApolloReactHooks.SuspenseQueryHookOptions<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>) {
|
|
2507
|
-
const options = baseOptions === ApolloReactHooks.skipToken ? baseOptions : {...defaultOptions, ...baseOptions}
|
|
2508
|
-
return ApolloReactHooks.useSuspenseQuery<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>(GetAssistantInsightsDocument, options);
|
|
2509
|
-
}
|
|
2510
|
-
export type GetAssistantInsightsQueryHookResult = ReturnType<typeof useGetAssistantInsightsQuery>;
|
|
2511
|
-
export type GetAssistantInsightsLazyQueryHookResult = ReturnType<typeof useGetAssistantInsightsLazyQuery>;
|
|
2512
|
-
export type GetAssistantInsightsSuspenseQueryHookResult = ReturnType<typeof useGetAssistantInsightsSuspenseQuery>;
|
|
2513
|
-
export type GetAssistantInsightsQueryResult = Apollo.QueryResult<Types.GetAssistantInsightsQuery, Types.GetAssistantInsightsQueryVariables>;
|
|
2514
2462
|
export const ListCustomersDocument = gql`
|
|
2515
2463
|
query ListCustomers($filter: CustomerFilterInput, $pagination: PaginationInput) {
|
|
2516
2464
|
customers(filter: $filter, pagination: $pagination) {
|
|
@@ -61,7 +61,7 @@ export type {
|
|
|
61
61
|
export { useMoveTheWheels, useMoveTheWheelsNavigate } from './providers/MoveTheWheelsProvider';
|
|
62
62
|
export { useMoveTheWheelsStore } from './store/movethewheelsStore';
|
|
63
63
|
|
|
64
|
-
// Transport adapter for the shared fe-libs AssistantWidget
|
|
64
|
+
// Transport adapter for the shared fe-libs AssistantWidget.
|
|
65
65
|
// Consumers import it from the dedicated subpath
|
|
66
66
|
// `@burdenoff/microfe-movethewheels/services/createSandboxAssistantTransport`
|
|
67
67
|
// (see package.json exports) — that path has a clean, self-contained `.d.ts`.
|
|
@@ -1,206 +1,102 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI
|
|
2
|
+
* AI Operations Page
|
|
3
|
+
*
|
|
4
|
+
* This page previously shipped a second, fabricated chat surface
|
|
5
|
+
* (`useAssistant` → `Query.assistantInsights`) whose "capabilities" list
|
|
6
|
+
* advertised route optimization, demand forecasting, fleet analysis, cost
|
|
7
|
+
* optimization and natural-language queries — none of which the backend
|
|
8
|
+
* implemented; every question routed to a fixed template built from four
|
|
9
|
+
* Prisma counts. See BOFF-3897.
|
|
10
|
+
*
|
|
11
|
+
* The real conversational assistant is the shared `AssistantWidget`, already
|
|
12
|
+
* mounted globally in the app shell (`AppShell.tsx`) on the sandbox
|
|
13
|
+
* transport — this page must never contain a second chat box.
|
|
14
|
+
*
|
|
15
|
+
* The full "AI Operations" surface this page is meant to become — a model
|
|
16
|
+
* picker, an agent-activity feed reading the shared AgentRun ledger,
|
|
17
|
+
* accept/reject decision cards for AI dispatch proposals, and per-workspace
|
|
18
|
+
* AI credit visibility — depends on the shared multi-model AI gateway
|
|
19
|
+
* (BOFF-3601), which has not landed yet. Until it does, this page shows an
|
|
20
|
+
* honest roadmap state instead of fabricating any of that content.
|
|
3
21
|
*/
|
|
4
22
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
Card,
|
|
10
|
-
CardContent,
|
|
11
|
-
CardHeader,
|
|
12
|
-
CardTitle,
|
|
13
|
-
Button,
|
|
14
|
-
Input,
|
|
15
|
-
Badge,
|
|
16
|
-
ScrollArea,
|
|
17
|
-
} from '../components/ui';
|
|
23
|
+
import { Bot, MessageCircle, Activity, ShieldCheck, Wallet } from 'lucide-react';
|
|
24
|
+
import { PageLayout, EmptyState } from '../components/PageLayout';
|
|
25
|
+
import { Card, CardContent, CardHeader, CardTitle } from '../components/ui';
|
|
18
26
|
import { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
19
|
-
import { useAssistant } from '../hooks/useAssistant';
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
const roadmapItems = [
|
|
29
|
+
{
|
|
30
|
+
icon: Activity,
|
|
31
|
+
title: 'Agent activity feed',
|
|
32
|
+
description:
|
|
33
|
+
'A live log of what the AI decided, on which shipments, with trigger, model, latency and cost — so an autonomous run is never a black box.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
icon: ShieldCheck,
|
|
37
|
+
title: 'Dispatch decision cards',
|
|
38
|
+
description:
|
|
39
|
+
'Proposed driver/vehicle assignments and carrier recommendations with rationale and confidence, requiring an explicit accept or reject before anything is written.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
icon: Wallet,
|
|
43
|
+
title: 'AI credit visibility',
|
|
44
|
+
description: 'Per-workspace AI spend for admins, so cost is visible before the invoice.',
|
|
45
|
+
},
|
|
46
|
+
];
|
|
26
47
|
|
|
27
48
|
export default function AIAssistantPage() {
|
|
28
|
-
const { ask, isLoading } = useAssistant();
|
|
29
|
-
const [message, setMessage] = useState('');
|
|
30
|
-
const [messages, setMessages] = useState<ChatMessage[]>([
|
|
31
|
-
{
|
|
32
|
-
role: 'assistant',
|
|
33
|
-
content:
|
|
34
|
-
"Hello! I'm your logistics assistant. Ask me about your orders, deliveries, fleet or inventory and I'll answer from your live operational data.",
|
|
35
|
-
},
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
const suggestions = [
|
|
39
|
-
{ icon: Route, text: "Optimize today's delivery routes", category: 'Route Planning' },
|
|
40
|
-
{ icon: TrendingUp, text: 'Show demand forecast for next week', category: 'Analytics' },
|
|
41
|
-
{ icon: AlertCircle, text: 'Identify potential delivery delays', category: 'Alerts' },
|
|
42
|
-
{ icon: Lightbulb, text: 'Suggest fleet efficiency improvements', category: 'Insights' },
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
const capabilities = [
|
|
46
|
-
'Route Optimization - Find the most efficient delivery routes',
|
|
47
|
-
'Demand Forecasting - Predict order volumes and trends',
|
|
48
|
-
'Fleet Analysis - Insights on vehicle and driver performance',
|
|
49
|
-
'Cost Optimization - Identify ways to reduce operational costs',
|
|
50
|
-
'Natural Language Queries - Ask questions in plain English',
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const submit = async (question: string) => {
|
|
54
|
-
const trimmed = question.trim();
|
|
55
|
-
if (!trimmed || isLoading) return;
|
|
56
|
-
setMessage('');
|
|
57
|
-
setMessages((previous) => [...previous, { role: 'user', content: trimmed }]);
|
|
58
|
-
try {
|
|
59
|
-
const result = await ask(trimmed);
|
|
60
|
-
setMessages((previous) => [...previous, { role: 'assistant', content: result.answer }]);
|
|
61
|
-
} catch (fetchError) {
|
|
62
|
-
setMessages((previous) => [
|
|
63
|
-
...previous,
|
|
64
|
-
{
|
|
65
|
-
role: 'assistant',
|
|
66
|
-
content:
|
|
67
|
-
fetchError instanceof Error
|
|
68
|
-
? `Sorry, I couldn't answer that: ${fetchError.message}`
|
|
69
|
-
: "Sorry, I couldn't reach the assistant. Please try again.",
|
|
70
|
-
isError: true,
|
|
71
|
-
},
|
|
72
|
-
]);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const handleSend = () => {
|
|
77
|
-
void submit(message);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
49
|
return (
|
|
81
50
|
<PageLayout
|
|
82
|
-
title="AI
|
|
83
|
-
subtitle="
|
|
51
|
+
title="AI Operations"
|
|
52
|
+
subtitle="What the AI is doing on your workspace, and why"
|
|
84
53
|
icon={<Bot size={24} />}
|
|
85
54
|
>
|
|
86
55
|
<PagePurpose icon={<Bot size={18} />}>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
56
|
+
This page will surface AI-driven dispatch decisions and their outcomes once the shared AI
|
|
57
|
+
runtime lands. It does not host a chat box — chat with your live operational data is always
|
|
58
|
+
available from the assistant icon anywhere in the app.
|
|
90
59
|
</PagePurpose>
|
|
91
60
|
|
|
92
|
-
<
|
|
93
|
-
<div className="
|
|
94
|
-
{
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
key={i}
|
|
106
|
-
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
|
107
|
-
>
|
|
108
|
-
<div
|
|
109
|
-
className={`max-w-[80%] p-3 rounded-lg ${
|
|
110
|
-
msg.role === 'user'
|
|
111
|
-
? 'bg-action-primary-bg text-action-primary-text'
|
|
112
|
-
: msg.isError
|
|
113
|
-
? 'bg-status-error-bg-subtle text-status-error-text'
|
|
114
|
-
: 'bg-bg-sunken'
|
|
115
|
-
}`}
|
|
116
|
-
>
|
|
117
|
-
<p className="whitespace-pre-line text-sm">{msg.content}</p>
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
))}
|
|
121
|
-
{isLoading && (
|
|
122
|
-
<div className="flex justify-start" aria-live="polite">
|
|
123
|
-
<div className="max-w-[80%] p-3 rounded-lg bg-bg-sunken flex items-center gap-2">
|
|
124
|
-
<Sparkles size={14} className="text-primary motion-safe:animate-pulse" />
|
|
125
|
-
<span className="text-sm text-text-secondary">Analyzing your data…</span>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
)}
|
|
129
|
-
</div>
|
|
130
|
-
</ScrollArea>
|
|
131
|
-
<div className="p-4 border-t">
|
|
132
|
-
<div className="flex gap-2">
|
|
133
|
-
<Input
|
|
134
|
-
value={message}
|
|
135
|
-
onChange={(e) => setMessage(e.target.value)}
|
|
136
|
-
placeholder="Ask me anything about your logistics operations..."
|
|
137
|
-
disabled={isLoading}
|
|
138
|
-
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
|
|
139
|
-
/>
|
|
140
|
-
<Button onClick={handleSend} disabled={isLoading || !message.trim()}>
|
|
141
|
-
<Send size={16} />
|
|
142
|
-
</Button>
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
</GlassCard>
|
|
61
|
+
<GlassCard treatment="glass" className="p-4">
|
|
62
|
+
<div className="flex items-start gap-3">
|
|
63
|
+
<MessageCircle size={20} className="text-primary flex-shrink-0 mt-0.5" />
|
|
64
|
+
<div>
|
|
65
|
+
<p className="text-sm font-medium text-text-primary">
|
|
66
|
+
Looking to ask a question about your operations?
|
|
67
|
+
</p>
|
|
68
|
+
<p className="text-sm text-text-secondary mt-1">
|
|
69
|
+
Open the assistant from its icon in the corner of the screen — it answers from your
|
|
70
|
+
live orders, shipments, fleet and inventory data, and it is the only chat surface in
|
|
71
|
+
this product.
|
|
72
|
+
</p>
|
|
73
|
+
</div>
|
|
146
74
|
</div>
|
|
75
|
+
</GlassCard>
|
|
147
76
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
<s.icon size={16} className="mr-2 flex-shrink-0" />
|
|
163
|
-
<span className="flex-1">{s.text}</span>
|
|
164
|
-
<Badge variant="secondary" className="text-xs">
|
|
165
|
-
{s.category}
|
|
166
|
-
</Badge>
|
|
167
|
-
</Button>
|
|
168
|
-
))}
|
|
169
|
-
</CardContent>
|
|
170
|
-
</Card>
|
|
171
|
-
|
|
172
|
-
<Card>
|
|
173
|
-
<CardHeader>
|
|
174
|
-
<CardTitle>Capabilities</CardTitle>
|
|
175
|
-
</CardHeader>
|
|
176
|
-
<CardContent>
|
|
177
|
-
<ul className="space-y-2 text-sm">
|
|
178
|
-
{capabilities.map((cap, i) => (
|
|
179
|
-
<li key={i} className="flex items-start gap-2">
|
|
180
|
-
<Sparkles size={14} className="text-primary mt-0.5 flex-shrink-0" />
|
|
181
|
-
<span>{cap}</span>
|
|
182
|
-
</li>
|
|
183
|
-
))}
|
|
184
|
-
</ul>
|
|
185
|
-
</CardContent>
|
|
186
|
-
</Card>
|
|
187
|
-
|
|
188
|
-
<Card className="bg-action-primary-bg/5 border-primary/20">
|
|
189
|
-
<CardContent className="p-4">
|
|
190
|
-
<div className="flex items-start gap-3">
|
|
191
|
-
<Lightbulb size={20} className="text-primary flex-shrink-0" />
|
|
77
|
+
<Card>
|
|
78
|
+
<CardHeader>
|
|
79
|
+
<CardTitle>On the roadmap</CardTitle>
|
|
80
|
+
</CardHeader>
|
|
81
|
+
<CardContent>
|
|
82
|
+
<EmptyState
|
|
83
|
+
illustration="empty-agents"
|
|
84
|
+
title="Autonomous dispatch operations aren't live yet"
|
|
85
|
+
description="These capabilities depend on the shared multi-model AI gateway and agent-run ledger (BOFF-3601), which hasn't shipped across the platform yet. Nothing below is available today."
|
|
86
|
+
/>
|
|
87
|
+
<ul className="mt-4 space-y-4">
|
|
88
|
+
{roadmapItems.map((item) => (
|
|
89
|
+
<li key={item.title} className="flex items-start gap-3">
|
|
90
|
+
<item.icon size={18} className="text-text-secondary flex-shrink-0 mt-0.5" />
|
|
192
91
|
<div>
|
|
193
|
-
<p className="font-medium text-
|
|
194
|
-
<p className="text-xs text-text-secondary">
|
|
195
|
-
You can ask complex questions like "What would happen if I added 2 more vehicles
|
|
196
|
-
to my fleet?" for scenario analysis.
|
|
197
|
-
</p>
|
|
92
|
+
<p className="text-sm font-medium text-text-primary">{item.title}</p>
|
|
93
|
+
<p className="text-xs text-text-secondary">{item.description}</p>
|
|
198
94
|
</div>
|
|
199
|
-
</
|
|
200
|
-
|
|
201
|
-
</
|
|
202
|
-
</
|
|
203
|
-
</
|
|
95
|
+
</li>
|
|
96
|
+
))}
|
|
97
|
+
</ul>
|
|
98
|
+
</CardContent>
|
|
99
|
+
</Card>
|
|
204
100
|
</PageLayout>
|
|
205
101
|
);
|
|
206
102
|
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
|
|
2
|
-
import { executeGraphQL as t } from "../utils/graphqlClient.js";
|
|
3
|
-
import { useCallback as n, useMemo as r, useState as i } from "react";
|
|
4
|
-
//#region src/movethewheels/hooks/useAssistant.ts
|
|
5
|
-
var a = "\n query MovethewheelsAssistantInsights($question: String!) {\n assistantInsights(question: $question) {\n answer\n generatedAt\n question\n }\n }\n";
|
|
6
|
-
function o() {
|
|
7
|
-
let { apiGatewayUrl: o, authToken: s, workspaceToken: c, workspaceId: l, tenantId: u, currentUser: d } = e(), [f, p] = i(!1), [m, h] = i(null), g = r(() => ({
|
|
8
|
-
apiGatewayUrl: o,
|
|
9
|
-
authToken: s,
|
|
10
|
-
workspaceToken: c,
|
|
11
|
-
workspaceId: l,
|
|
12
|
-
tenantId: u,
|
|
13
|
-
actorId: d?.id
|
|
14
|
-
}), [
|
|
15
|
-
o,
|
|
16
|
-
s,
|
|
17
|
-
c,
|
|
18
|
-
l,
|
|
19
|
-
u,
|
|
20
|
-
d?.id
|
|
21
|
-
]);
|
|
22
|
-
return {
|
|
23
|
-
ask: n(async (e) => {
|
|
24
|
-
p(!0), h(null);
|
|
25
|
-
try {
|
|
26
|
-
return (await t(g, a, { question: e })).assistantInsights;
|
|
27
|
-
} catch (e) {
|
|
28
|
-
let t = e instanceof Error ? e : /* @__PURE__ */ Error("Failed to get assistant response");
|
|
29
|
-
throw h(t), t;
|
|
30
|
-
} finally {
|
|
31
|
-
p(!1);
|
|
32
|
-
}
|
|
33
|
-
}, [g]),
|
|
34
|
-
isLoading: f,
|
|
35
|
-
error: m
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
export { o as useAssistant };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export interface AssistantAnswer {
|
|
2
|
-
answer: string;
|
|
3
|
-
generatedAt: string;
|
|
4
|
-
question: string;
|
|
5
|
-
}
|
|
6
|
-
interface UseAssistantResult {
|
|
7
|
-
ask: (question: string) => Promise<AssistantAnswer>;
|
|
8
|
-
isLoading: boolean;
|
|
9
|
-
error: Error | null;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* useAssistant — queries the real logistics assistant (Query.assistantInsights).
|
|
13
|
-
* The backend answers from the caller's own operational data; there is no
|
|
14
|
-
* client-side canned response.
|
|
15
|
-
*/
|
|
16
|
-
export declare function useAssistant(): UseAssistantResult;
|
|
17
|
-
export {};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createLucideIcon as e } from "../createLucideIcon.js";
|
|
2
|
-
//#region node_modules/lucide-react/dist/esm/icons/lightbulb.js
|
|
3
|
-
var t = e("Lightbulb", [
|
|
4
|
-
["path", {
|
|
5
|
-
d: "M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5",
|
|
6
|
-
key: "1gvzjb"
|
|
7
|
-
}],
|
|
8
|
-
["path", {
|
|
9
|
-
d: "M9 18h6",
|
|
10
|
-
key: "x1upvd"
|
|
11
|
-
}],
|
|
12
|
-
["path", {
|
|
13
|
-
d: "M10 22h4",
|
|
14
|
-
key: "ceow96"
|
|
15
|
-
}]
|
|
16
|
-
]);
|
|
17
|
-
//#endregion
|
|
18
|
-
export { t as Lightbulb };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { createLucideIcon as e } from "../createLucideIcon.js";
|
|
2
|
-
//#region node_modules/lucide-react/dist/esm/icons/send.js
|
|
3
|
-
var t = e("Send", [["path", {
|
|
4
|
-
d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",
|
|
5
|
-
key: "1ffxy3"
|
|
6
|
-
}], ["path", {
|
|
7
|
-
d: "m21.854 2.147-10.94 10.939",
|
|
8
|
-
key: "12cjpa"
|
|
9
|
-
}]]);
|
|
10
|
-
//#endregion
|
|
11
|
-
export { t as Send };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { createLucideIcon as e } from "../createLucideIcon.js";
|
|
2
|
-
//#region node_modules/lucide-react/dist/esm/icons/sparkles.js
|
|
3
|
-
var t = e("Sparkles", [
|
|
4
|
-
["path", {
|
|
5
|
-
d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",
|
|
6
|
-
key: "4pj2yx"
|
|
7
|
-
}],
|
|
8
|
-
["path", {
|
|
9
|
-
d: "M20 3v4",
|
|
10
|
-
key: "1olli1"
|
|
11
|
-
}],
|
|
12
|
-
["path", {
|
|
13
|
-
d: "M22 5h-4",
|
|
14
|
-
key: "1gvqau"
|
|
15
|
-
}],
|
|
16
|
-
["path", {
|
|
17
|
-
d: "M4 17v2",
|
|
18
|
-
key: "vumght"
|
|
19
|
-
}],
|
|
20
|
-
["path", {
|
|
21
|
-
d: "M5 18H3",
|
|
22
|
-
key: "zchphs"
|
|
23
|
-
}]
|
|
24
|
-
]);
|
|
25
|
-
//#endregion
|
|
26
|
-
export { t as Sparkles };
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { useCallback, useMemo, useState } from 'react';
|
|
2
|
-
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
3
|
-
import { executeGraphQL } from '../utils/graphqlClient';
|
|
4
|
-
|
|
5
|
-
export interface AssistantAnswer {
|
|
6
|
-
answer: string;
|
|
7
|
-
generatedAt: string;
|
|
8
|
-
question: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const ASSISTANT_INSIGHTS_QUERY = `
|
|
12
|
-
query MovethewheelsAssistantInsights($question: String!) {
|
|
13
|
-
assistantInsights(question: $question) {
|
|
14
|
-
answer
|
|
15
|
-
generatedAt
|
|
16
|
-
question
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
|
-
interface UseAssistantResult {
|
|
22
|
-
ask: (question: string) => Promise<AssistantAnswer>;
|
|
23
|
-
isLoading: boolean;
|
|
24
|
-
error: Error | null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* useAssistant — queries the real logistics assistant (Query.assistantInsights).
|
|
29
|
-
* The backend answers from the caller's own operational data; there is no
|
|
30
|
-
* client-side canned response.
|
|
31
|
-
*/
|
|
32
|
-
export function useAssistant(): UseAssistantResult {
|
|
33
|
-
const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
|
|
34
|
-
useMoveTheWheels();
|
|
35
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
36
|
-
const [error, setError] = useState<Error | null>(null);
|
|
37
|
-
|
|
38
|
-
const graphQLOptions = useMemo(
|
|
39
|
-
() => ({
|
|
40
|
-
apiGatewayUrl,
|
|
41
|
-
authToken,
|
|
42
|
-
workspaceToken,
|
|
43
|
-
workspaceId,
|
|
44
|
-
tenantId,
|
|
45
|
-
actorId: currentUser?.id,
|
|
46
|
-
}),
|
|
47
|
-
[apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
const ask = useCallback(
|
|
51
|
-
async (question: string): Promise<AssistantAnswer> => {
|
|
52
|
-
setIsLoading(true);
|
|
53
|
-
setError(null);
|
|
54
|
-
try {
|
|
55
|
-
const data = await executeGraphQL<{ assistantInsights: AssistantAnswer }>(
|
|
56
|
-
graphQLOptions,
|
|
57
|
-
ASSISTANT_INSIGHTS_QUERY,
|
|
58
|
-
{ question }
|
|
59
|
-
);
|
|
60
|
-
return data.assistantInsights;
|
|
61
|
-
} catch (fetchError) {
|
|
62
|
-
const normalized =
|
|
63
|
-
fetchError instanceof Error ? fetchError : new Error('Failed to get assistant response');
|
|
64
|
-
setError(normalized);
|
|
65
|
-
throw normalized;
|
|
66
|
-
} finally {
|
|
67
|
-
setIsLoading(false);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
[graphQLOptions]
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
return { ask, isLoading, error };
|
|
74
|
-
}
|