@burdenoff/microfe-movethewheels 2026.1102.1 → 2026.1102.2
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/components/freight-cost/FreightCommercialFactEditor.js +46 -46
- package/dist/components/freight-cost/FreightCommercialHistory.js +7 -7
- package/dist/components/freight-cost/FreightDraftForm.js +20 -20
- package/dist/components/freight-invoice/InvoiceReviewPanel.js +255 -0
- package/dist/components/freight-invoice/InvoiceReviewQueue.js +93 -0
- package/dist/generated/freight-invoice-operations.d.ts +461 -0
- package/dist/hooks/useInvoiceComparisonRecovery.js +26 -26
- package/dist/hooks/useInvoiceReviewPermissions.js +14 -0
- package/dist/movethewheels/components/freight-invoice/InvoiceReviewPanel.d.ts +11 -0
- package/dist/movethewheels/components/freight-invoice/InvoiceReviewQueue.d.ts +22 -0
- package/dist/movethewheels/hooks/useInvoiceReviewPermissions.d.ts +5 -0
- package/dist/movethewheels/pages/FinancePage.d.ts +3 -1
- package/dist/movethewheels/utils/invoiceReview.d.ts +26 -0
- package/dist/pages/FinancePage.js +109 -67
- package/dist/pages/FreightEstimatePage.js +89 -89
- package/dist/pages/FreightInvoicePage.js +138 -120
- package/dist/src/generated/freight-invoice-operations.js +2100 -1
- package/dist/utils/invoiceReview.js +84 -0
- package/package.json +2 -2
- package/src/generated/freight-invoice-operations.ts +245 -1
- package/src/movethewheels/components/freight-invoice/InvoiceReviewPanel.tsx +319 -0
- package/src/movethewheels/components/freight-invoice/InvoiceReviewQueue.tsx +96 -0
- package/src/movethewheels/hooks/useInvoiceReviewPermissions.ts +25 -0
- package/src/movethewheels/pages/FinancePage.tsx +34 -2
- package/src/movethewheels/pages/FreightInvoicePage.tsx +44 -3
- package/src/movethewheels/utils/invoiceReview.ts +146 -0
- package/src/operations/freight-invoice/review.graphql +26 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
|
|
2
2
|
import { resolveGraphqlUrl as t } from "../utils/graphqlClient.js";
|
|
3
3
|
import { useConditionApi as n } from "./useContainerCondition.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { InvoiceComparisonExactDocument as r, RecordInvoiceComparisonDocument as i, RecoverInvoiceComparisonDocument as a } from "../src/generated/freight-invoice-operations.js";
|
|
5
|
+
import { INVOICE_RECOVERY_STORAGE as o, decodeInvoiceIntent as s, encodeInvoiceIntent as c, invoiceCanonical as l, invoiceRecordInput as u, matchesInvoiceReceipt as d, sameInvoiceScope as f } from "../utils/invoiceComparison.js";
|
|
6
6
|
import { useLayoutEffect as p, useRef as m, useState as h } from "react";
|
|
7
7
|
import { useActiveContext as g } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
8
8
|
//#region src/movethewheels/hooks/useInvoiceComparisonRecovery.ts
|
|
9
9
|
function _() {
|
|
10
10
|
try {
|
|
11
|
-
let e = sessionStorage.getItem(
|
|
11
|
+
let e = sessionStorage.getItem(o), t = s(e);
|
|
12
12
|
return {
|
|
13
13
|
intent: t,
|
|
14
14
|
blocked: e !== null && t === null
|
|
@@ -32,7 +32,7 @@ function v(n) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
function y(e, t) {
|
|
35
|
-
let g = n("SINGLE_ATTEMPT"), [v] = h(_), [y, b] = h(v.intent), x = m(y), [S, C] = h(v.blocked), w = m(!1), [T, E] = h(!1), [D, O] = h(0), k =
|
|
35
|
+
let g = n("SINGLE_ATTEMPT"), [v] = h(_), [y, b] = h(v.intent), x = m(y), [S, C] = h(v.blocked), w = m(!1), [T, E] = h(!1), [D, O] = h(0), k = l(e), A = m(e), [j, M] = h(null);
|
|
36
36
|
p(() => (A.current = e, () => {
|
|
37
37
|
A.current = null;
|
|
38
38
|
}), [k]);
|
|
@@ -41,37 +41,37 @@ function y(e, t) {
|
|
|
41
41
|
message: e,
|
|
42
42
|
error: t
|
|
43
43
|
}), P = (e, t) => {
|
|
44
|
-
if (!e || !
|
|
45
|
-
let n =
|
|
46
|
-
if (!n ||
|
|
47
|
-
return sessionStorage.removeItem(
|
|
44
|
+
if (!e || !d(e, t) || !f(t.scope, A.current)) throw Error("UNCONFIRMED");
|
|
45
|
+
let n = s(sessionStorage.getItem(o));
|
|
46
|
+
if (!n || l(n) !== l(t)) throw Error("RETAINED_INTENT_CHANGED");
|
|
47
|
+
return sessionStorage.removeItem(o), x.current = null, b(null), C(!1), O((e) => e + 1), N("The exact immutable comparison receipt is confirmed.", !1), e;
|
|
48
48
|
};
|
|
49
49
|
return {
|
|
50
|
-
run: async (n,
|
|
50
|
+
run: async (n, r) => {
|
|
51
51
|
if (w.current || x.current || S) return N("Resolve the original request before starting another."), null;
|
|
52
|
-
if (!e || !
|
|
53
|
-
let
|
|
52
|
+
if (!e || !f(e, A.current) || !t()) return N("Current shipment update permission and workspace context are required. Nothing was sent."), null;
|
|
53
|
+
let a;
|
|
54
54
|
try {
|
|
55
55
|
if (sessionStorage.getItem("movethewheels.freight-invoice.unresolved.v1") !== null) throw Error("EXISTING");
|
|
56
|
-
|
|
56
|
+
a = {
|
|
57
57
|
version: 1,
|
|
58
58
|
scope: structuredClone(e),
|
|
59
|
-
input:
|
|
59
|
+
input: u({
|
|
60
60
|
...n,
|
|
61
61
|
idempotencyKey: crypto.randomUUID()
|
|
62
62
|
}),
|
|
63
|
-
estimate: structuredClone(
|
|
64
|
-
}, sessionStorage.setItem(
|
|
63
|
+
estimate: structuredClone(r)
|
|
64
|
+
}, sessionStorage.setItem(o, c(a)), x.current = a, b(a);
|
|
65
65
|
} catch {
|
|
66
66
|
return N("The original request could not be retained safely. Nothing was sent; reload and inspect any retained request."), null;
|
|
67
67
|
}
|
|
68
68
|
w.current = !0, E(!0), M(null);
|
|
69
69
|
try {
|
|
70
|
-
if (!
|
|
71
|
-
let e =
|
|
72
|
-
return e &&
|
|
70
|
+
if (!f(a.scope, A.current) || !t()) {
|
|
71
|
+
let e = s(sessionStorage.getItem(o));
|
|
72
|
+
return e && l(e) === l(a) && (sessionStorage.removeItem(o), x.current = null, b(null)), N("Context or permission changed before dispatch. Nothing was sent."), null;
|
|
73
73
|
}
|
|
74
|
-
return P((await g(
|
|
74
|
+
return P((await g(i, { input: a.input })).compareMtwFreightInvoice, a);
|
|
75
75
|
} catch {
|
|
76
76
|
return N("Save outcome is UNKNOWN. Keep the original intent and use read-only recovery; do not resend it."), null;
|
|
77
77
|
} finally {
|
|
@@ -81,21 +81,21 @@ function y(e, t) {
|
|
|
81
81
|
reconcile: async () => {
|
|
82
82
|
let e = x.current;
|
|
83
83
|
if (w.current || !e) return null;
|
|
84
|
-
if (!
|
|
84
|
+
if (!f(e.scope, A.current)) return N("Return to the original account, organization, workspace and project to inspect its request."), null;
|
|
85
85
|
w.current = !0, E(!0), M(null);
|
|
86
86
|
try {
|
|
87
|
-
let t = (await g(
|
|
87
|
+
let t = (await g(a, {
|
|
88
88
|
shipmentId: e.input.shipmentId,
|
|
89
89
|
requestKey: e.input.idempotencyKey
|
|
90
90
|
})).mtwFreightInvoiceComparisonByRequestKey;
|
|
91
91
|
if (!t) return N("No original-context receipt was returned. This does not prove no effect; the request stays UNKNOWN and cannot be resent."), null;
|
|
92
|
-
if (!
|
|
93
|
-
if (!
|
|
94
|
-
let n = await g(
|
|
92
|
+
if (!d(t, e)) throw Error("MISMATCH");
|
|
93
|
+
if (!f(e.scope, A.current)) throw Error("CONTEXT_CHANGED");
|
|
94
|
+
let n = await g(r, {
|
|
95
95
|
shipmentId: e.input.shipmentId,
|
|
96
96
|
id: t.id
|
|
97
97
|
});
|
|
98
|
-
if (!
|
|
98
|
+
if (!d(n.mtwFreightInvoiceComparison, e) || n.mtwFreightInvoiceComparison.requestFingerprint !== t.requestFingerprint || l(n.mtwFreightInvoiceComparison) !== l(t)) throw Error("MISMATCH");
|
|
99
99
|
return P(n.mtwFreightInvoiceComparison, e);
|
|
100
100
|
} catch {
|
|
101
101
|
return N("Read-only recovery could not confirm the exact original receipt. Keep the intent; no save was sent."), null;
|
|
@@ -109,7 +109,7 @@ function y(e, t) {
|
|
|
109
109
|
uncertain: y !== null || S,
|
|
110
110
|
revision: D,
|
|
111
111
|
scopeKey: k,
|
|
112
|
-
canReconcile: !!y &&
|
|
112
|
+
canReconcile: !!y && f(y.scope, e),
|
|
113
113
|
feedback: j?.scopeKey === k ? j : null,
|
|
114
114
|
refresh: () => O((e) => e + 1)
|
|
115
115
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
|
|
2
|
+
import { usePermissions as t } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
3
|
+
//#region src/movethewheels/hooks/useInvoiceReviewPermissions.ts
|
|
4
|
+
function n() {
|
|
5
|
+
let n = t(), { workspaceId: r, currentUser: i } = e(), a = !!(r && i?.id && n.scopeId === r && (n.isSuperAdmin || n.permissions?.scopeId === r && n.permissions.actorId === i.id)) && !n.isLoading && !n.error && n.hasPermission("mtw-freight-invoice:read");
|
|
6
|
+
return {
|
|
7
|
+
canRead: a,
|
|
8
|
+
canUpdate: a && n.hasPermission("mtw-freight-invoice:update"),
|
|
9
|
+
canApprove: a && n.hasPermission("mtw-freight-invoice:approve"),
|
|
10
|
+
loading: n.isLoading
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { n as useInvoiceReviewPermissions };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InvoiceReceiptFragment } from '../../../generated/freight-invoice-operations';
|
|
2
|
+
import { ReviewPermissions } from '../../utils/invoiceReview';
|
|
3
|
+
/**
|
|
4
|
+
* Review, dispute and Billing hand-off for one exact comparison revision
|
|
5
|
+
* (BOFF-10588). Every step appends to the revision's history; nothing is edited.
|
|
6
|
+
*/
|
|
7
|
+
export declare function InvoiceReviewPanel({ record, permissions, onChanged, }: {
|
|
8
|
+
record: Pick<InvoiceReceiptFragment, 'id' | 'shipmentId' | 'facts'>;
|
|
9
|
+
permissions: ReviewPermissions;
|
|
10
|
+
onChanged?: () => void;
|
|
11
|
+
}): import('react').JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { InvoiceReviewFragment } from '../../../generated/freight-invoice-operations';
|
|
2
|
+
/** The latest revision of every recorded carrier invoice, filtered by review status (BOFF-10588). */
|
|
3
|
+
export declare function InvoiceReviewQueue({ revision, onOpen, locked, }: {
|
|
4
|
+
revision: number;
|
|
5
|
+
onOpen: (review: InvoiceReviewFragment) => void;
|
|
6
|
+
/** True while an invoice or correction draft is open: opening a review would discard it. */
|
|
7
|
+
locked?: boolean;
|
|
8
|
+
}): import('react').JSX.Element;
|
|
9
|
+
/** Review counts over the latest revision of every recorded carrier invoice. */
|
|
10
|
+
export declare function useInvoiceReviewSummary(enabled: boolean): {
|
|
11
|
+
summary: {
|
|
12
|
+
total: number;
|
|
13
|
+
awaitingReview: number;
|
|
14
|
+
accepted: number;
|
|
15
|
+
acceptedWithVariance: number;
|
|
16
|
+
disputed: number;
|
|
17
|
+
supplierResponded: number;
|
|
18
|
+
settlementHandedOff: number;
|
|
19
|
+
} | null;
|
|
20
|
+
loading: boolean;
|
|
21
|
+
error: string | null;
|
|
22
|
+
};
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* settlement are handled by the platform Billing module, which this page
|
|
8
8
|
* deep-links to rather than re-implementing an Invoice ledger here. Carrier
|
|
9
9
|
* invoices are checked against the agreed freight estimate on the freight
|
|
10
|
-
* invoice comparison page, which this page links to (BOFF-4395).
|
|
10
|
+
* invoice comparison page, which this page links to (BOFF-4395). The review
|
|
11
|
+
* counts (awaiting review, disputed, accepted, handed to Billing) come from
|
|
12
|
+
* Query.mtwFreightInvoiceReviewSummary over real recorded invoices (BOFF-10588).
|
|
11
13
|
*/
|
|
12
14
|
export default function FinancePage(): import('react').JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DecideMtwFreightInvoiceInput, HandOffMtwFreightInvoiceSettlementInput, InvoiceReviewFragment, MtwFreightInvoiceDecision, MtwFreightInvoiceResolution, MtwFreightInvoiceReviewAction, MtwFreightInvoiceReviewStatus, RecordMtwFreightInvoiceSupplierResponseInput, ResolveMtwFreightInvoiceDisputeInput } from '../../generated/freight-invoice-operations';
|
|
2
|
+
export declare const REVIEW_STATUS_LABELS: Record<MtwFreightInvoiceReviewStatus, string>;
|
|
3
|
+
export declare const REVIEW_EVENT_LABELS: Record<InvoiceReviewFragment['events'][number]['kind'], string>;
|
|
4
|
+
/** Statuses a reviewer can filter the queue by; SUPERSEDED never appears in the queue. */
|
|
5
|
+
export declare const REVIEW_QUEUE_STATUSES: MtwFreightInvoiceReviewStatus[];
|
|
6
|
+
export interface ReviewPermissions {
|
|
7
|
+
canRead: boolean;
|
|
8
|
+
canUpdate: boolean;
|
|
9
|
+
canApprove: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Steps the server says are open (author separation included) that this viewer is also permitted to take. */
|
|
12
|
+
export declare function availableReviewActions(review: Pick<InvoiceReviewFragment, 'allowedActions'>, permissions: ReviewPermissions): MtwFreightInvoiceReviewAction[];
|
|
13
|
+
/** Why the author sees no decision: the server withholds it, so say so instead of showing nothing. */
|
|
14
|
+
export declare function authorNotice(review: Pick<InvoiceReviewFragment, 'viewerIsAuthor' | 'status'>): string | null;
|
|
15
|
+
interface Common {
|
|
16
|
+
review: Pick<InvoiceReviewFragment, 'shipmentId' | 'comparisonId' | 'sequence'>;
|
|
17
|
+
idempotencyKey: string;
|
|
18
|
+
}
|
|
19
|
+
export type DraftProblem = 'REASON_REQUIRED' | 'LINES_REQUIRED' | 'RESPONSE_REQUIRED';
|
|
20
|
+
export declare const DRAFT_PROBLEM_LABELS: Record<DraftProblem, string>;
|
|
21
|
+
export declare function decisionInput(base: Common, decision: MtwFreightInvoiceDecision, reason: string, disputedLineIds: readonly string[]): DecideMtwFreightInvoiceInput | DraftProblem;
|
|
22
|
+
export declare function supplierResponseInput(base: Common, response: string): RecordMtwFreightInvoiceSupplierResponseInput | DraftProblem;
|
|
23
|
+
export declare function resolutionInput(base: Common, resolution: MtwFreightInvoiceResolution, reason: string): ResolveMtwFreightInvoiceDisputeInput | DraftProblem;
|
|
24
|
+
export declare function handOffInput(base: Common, note: string): HandOffMtwFreightInvoiceSettlementInput;
|
|
25
|
+
export declare function isDraftProblem(value: unknown): value is DraftProblem;
|
|
26
|
+
export {};
|
|
@@ -17,87 +17,128 @@ import { MetricCard as b, MetricGrid as x } from "../components/MetricCard.js";
|
|
|
17
17
|
import { TrendChart as S } from "../components/charts/TrendChart.js";
|
|
18
18
|
import { VolumeBar as C } from "../components/charts/VolumeBar.js";
|
|
19
19
|
import "../components/charts/index.js";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
20
|
+
import { useInvoiceReviewSummary as w } from "../components/freight-invoice/InvoiceReviewQueue.js";
|
|
21
|
+
import { useMemo as T } from "react";
|
|
22
|
+
import { jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
23
|
+
import { EmphasisPanel as O, PagePurpose as k } from "@burdenoff/fe-libs/ui";
|
|
23
24
|
//#region src/movethewheels/pages/FinancePage.tsx
|
|
24
|
-
function
|
|
25
|
-
let {
|
|
25
|
+
function A() {
|
|
26
|
+
let { summary: e, loading: t, error: n } = w(!0);
|
|
27
|
+
return t ? /* @__PURE__ */ E("p", {
|
|
28
|
+
className: "text-sm text-text-secondary",
|
|
29
|
+
children: "Loading invoice review counts…"
|
|
30
|
+
}) : n || !e ? /* @__PURE__ */ E("p", {
|
|
31
|
+
className: "text-sm text-text-secondary",
|
|
32
|
+
children: "Invoice review counts need freight invoice read permission."
|
|
33
|
+
}) : /* @__PURE__ */ E("dl", {
|
|
34
|
+
className: "grid grid-cols-2 gap-2",
|
|
35
|
+
"aria-label": "Carrier invoice review",
|
|
36
|
+
children: [
|
|
37
|
+
{
|
|
38
|
+
label: "Awaiting review",
|
|
39
|
+
value: e.awaitingReview
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
label: "Disputed",
|
|
43
|
+
value: e.disputed + e.supplierResponded
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: "Accepted",
|
|
47
|
+
value: e.accepted + e.acceptedWithVariance
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
label: "Handed to Billing",
|
|
51
|
+
value: e.settlementHandedOff
|
|
52
|
+
}
|
|
53
|
+
].map((e) => /* @__PURE__ */ D("div", {
|
|
54
|
+
className: "rounded-lg border border-border-default p-3",
|
|
55
|
+
children: [/* @__PURE__ */ E("dt", {
|
|
56
|
+
className: "text-xs text-text-secondary",
|
|
57
|
+
children: e.label
|
|
58
|
+
}), /* @__PURE__ */ E("dd", {
|
|
59
|
+
className: "text-lg font-semibold text-text-primary",
|
|
60
|
+
children: e.value
|
|
61
|
+
})]
|
|
62
|
+
}, e.label))
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function j() {
|
|
66
|
+
let { analytics: w } = d(), { metrics: j } = f(), { navigate: M, basePath: N } = l(), P = () => M("/billing"), F = () => M(c(N, "finance/freight-estimates")), I = () => M(c(N, "finance/freight-invoice-comparisons")), L = T(() => w.trends.revenue.map((e) => ({
|
|
26
67
|
month: e.month,
|
|
27
68
|
revenue: e.revenue
|
|
28
|
-
})), [
|
|
69
|
+
})), [w.trends.revenue]), R = T(() => [
|
|
29
70
|
{
|
|
30
71
|
label: "Total revenue",
|
|
31
|
-
amount:
|
|
72
|
+
amount: j.totalRevenue
|
|
32
73
|
},
|
|
33
74
|
{
|
|
34
75
|
label: "Collected",
|
|
35
|
-
amount:
|
|
76
|
+
amount: j.deliveredRevenue
|
|
36
77
|
},
|
|
37
78
|
{
|
|
38
79
|
label: "Fuel cost",
|
|
39
|
-
amount:
|
|
80
|
+
amount: j.fuelCost
|
|
40
81
|
}
|
|
41
82
|
], [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
]),
|
|
46
|
-
return /* @__PURE__ */
|
|
83
|
+
j.totalRevenue,
|
|
84
|
+
j.deliveredRevenue,
|
|
85
|
+
j.fuelCost
|
|
86
|
+
]), z = R.some((e) => e.amount > 0);
|
|
87
|
+
return /* @__PURE__ */ D(v, {
|
|
47
88
|
title: "Finance",
|
|
48
89
|
subtitle: "Financial overview and reports",
|
|
49
|
-
icon: /* @__PURE__ */
|
|
50
|
-
headerActions: /* @__PURE__ */
|
|
90
|
+
icon: /* @__PURE__ */ E(n, { size: 24 }),
|
|
91
|
+
headerActions: /* @__PURE__ */ D(p, {
|
|
51
92
|
variant: "outline",
|
|
52
|
-
onClick:
|
|
53
|
-
children: [/* @__PURE__ */
|
|
93
|
+
onClick: P,
|
|
94
|
+
children: [/* @__PURE__ */ E(i, { size: 16 }), " Invoices & Billing"]
|
|
54
95
|
}),
|
|
55
96
|
children: [
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
icon: /* @__PURE__ */
|
|
97
|
+
/* @__PURE__ */ E(k, {
|
|
98
|
+
icon: /* @__PURE__ */ E(n, { size: 18 }),
|
|
58
99
|
children: "Follow the money behind your deliveries. See total revenue, how much has already been collected on delivered orders, fuel cost and how many invoices are still open — then head to Billing to raise and settle invoices."
|
|
59
100
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ D(x, {
|
|
61
102
|
columns: 4,
|
|
62
103
|
children: [
|
|
63
|
-
/* @__PURE__ */
|
|
104
|
+
/* @__PURE__ */ E(b, {
|
|
64
105
|
treatment: "glass",
|
|
65
106
|
glow: !0,
|
|
66
107
|
label: "Total Revenue",
|
|
67
|
-
value: u(
|
|
68
|
-
icon: /* @__PURE__ */
|
|
108
|
+
value: u(j.totalRevenue),
|
|
109
|
+
icon: /* @__PURE__ */ E(n, { size: 20 }),
|
|
69
110
|
accent: "success"
|
|
70
111
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
112
|
+
/* @__PURE__ */ E(b, {
|
|
72
113
|
label: "Collected (Delivered)",
|
|
73
|
-
value: u(
|
|
74
|
-
icon: /* @__PURE__ */
|
|
114
|
+
value: u(j.deliveredRevenue),
|
|
115
|
+
icon: /* @__PURE__ */ E(s, { size: 20 }),
|
|
75
116
|
accent: "brand"
|
|
76
117
|
}),
|
|
77
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ E(b, {
|
|
78
119
|
label: "Fuel Cost",
|
|
79
|
-
value: u(
|
|
80
|
-
icon: /* @__PURE__ */
|
|
120
|
+
value: u(j.fuelCost),
|
|
121
|
+
icon: /* @__PURE__ */ E(a, { size: 20 }),
|
|
81
122
|
accent: "warning"
|
|
82
123
|
}),
|
|
83
|
-
/* @__PURE__ */
|
|
124
|
+
/* @__PURE__ */ E(b, {
|
|
84
125
|
label: "Open Invoices",
|
|
85
|
-
value: String(
|
|
86
|
-
icon: /* @__PURE__ */
|
|
126
|
+
value: String(j.openInvoices),
|
|
127
|
+
icon: /* @__PURE__ */ E(t, { size: 20 }),
|
|
87
128
|
accent: "blue"
|
|
88
129
|
})
|
|
89
130
|
]
|
|
90
131
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
132
|
+
/* @__PURE__ */ D("div", {
|
|
92
133
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
93
|
-
children: [/* @__PURE__ */
|
|
134
|
+
children: [/* @__PURE__ */ D(m, {
|
|
94
135
|
className: "lg:col-span-2",
|
|
95
|
-
children: [/* @__PURE__ */
|
|
136
|
+
children: [/* @__PURE__ */ E(g, { children: /* @__PURE__ */ E(_, { children: "Revenue trend" }) }), /* @__PURE__ */ E(h, { children: /* @__PURE__ */ E(S, {
|
|
96
137
|
xKey: "month",
|
|
97
138
|
variant: "area",
|
|
98
139
|
height: 280,
|
|
99
140
|
showLegend: !1,
|
|
100
|
-
data:
|
|
141
|
+
data: L,
|
|
101
142
|
series: [{
|
|
102
143
|
dataKey: "revenue",
|
|
103
144
|
name: "Revenue",
|
|
@@ -106,65 +147,66 @@ function k() {
|
|
|
106
147
|
valueFormatter: (e) => u(Number(e)),
|
|
107
148
|
emptyMessage: "No revenue recorded yet. Revenue appears here as orders are delivered."
|
|
108
149
|
}) })]
|
|
109
|
-
}), /* @__PURE__ */
|
|
150
|
+
}), /* @__PURE__ */ D(O, {
|
|
110
151
|
className: "space-y-4",
|
|
111
152
|
children: [
|
|
112
|
-
/* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ E("h2", {
|
|
113
154
|
className: "text-base font-semibold text-text-primary",
|
|
114
155
|
children: "Invoices & Settlement"
|
|
115
156
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ E("p", {
|
|
117
158
|
className: "text-sm text-text-secondary",
|
|
118
159
|
children: "Invoicing, payments and settlement run through the platform Billing module — the single source of truth for what's been billed and paid. MoveTheWheels doesn't keep its own invoice ledger."
|
|
119
160
|
}),
|
|
120
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ D("div", {
|
|
121
162
|
className: "flex items-center justify-between p-4 bg-status-warning-bg/10 rounded-lg",
|
|
122
|
-
children: [/* @__PURE__ */
|
|
163
|
+
children: [/* @__PURE__ */ D("div", {
|
|
123
164
|
className: "flex items-center gap-3",
|
|
124
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ E("div", {
|
|
125
166
|
className: "w-10 h-10 rounded-full bg-status-warning-bg/20 flex items-center justify-center",
|
|
126
|
-
children: /* @__PURE__ */
|
|
167
|
+
children: /* @__PURE__ */ E(t, {
|
|
127
168
|
size: 20,
|
|
128
169
|
className: "text-status-warning-text"
|
|
129
170
|
})
|
|
130
|
-
}), /* @__PURE__ */
|
|
171
|
+
}), /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("p", {
|
|
131
172
|
className: "font-medium",
|
|
132
173
|
children: "Open invoices"
|
|
133
|
-
}), /* @__PURE__ */
|
|
174
|
+
}), /* @__PURE__ */ D("p", {
|
|
134
175
|
className: "text-sm text-text-secondary",
|
|
135
|
-
children: [
|
|
176
|
+
children: [j.openInvoices, " awaiting settlement"]
|
|
136
177
|
})] })]
|
|
137
|
-
}), /* @__PURE__ */
|
|
178
|
+
}), /* @__PURE__ */ E("span", {
|
|
138
179
|
className: "text-xl font-bold text-status-warning-text",
|
|
139
|
-
children:
|
|
180
|
+
children: j.openInvoices
|
|
140
181
|
})]
|
|
141
182
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ D(p, {
|
|
143
184
|
className: "w-full",
|
|
144
|
-
onClick:
|
|
145
|
-
children: ["Open Billing ", /* @__PURE__ */
|
|
185
|
+
onClick: P,
|
|
186
|
+
children: ["Open Billing ", /* @__PURE__ */ E(e, { size: 16 })]
|
|
146
187
|
}),
|
|
147
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ D("div", {
|
|
148
189
|
className: "space-y-2 border-t border-border-default pt-4",
|
|
149
190
|
children: [
|
|
150
|
-
/* @__PURE__ */
|
|
191
|
+
/* @__PURE__ */ E("h3", {
|
|
151
192
|
className: "text-sm font-semibold text-text-primary",
|
|
152
193
|
children: "Freight cost control"
|
|
153
194
|
}),
|
|
154
|
-
/* @__PURE__ */
|
|
195
|
+
/* @__PURE__ */ E("p", {
|
|
155
196
|
className: "text-sm text-text-secondary",
|
|
156
|
-
children: "Record the agreed freight estimate for a shipment, then check the carrier's invoice against it line by line
|
|
197
|
+
children: "Record the agreed freight estimate for a shipment, then check the carrier's invoice against it line by line. Someone other than its author accepts or disputes it, and accepted invoices are handed to Billing for settlement."
|
|
157
198
|
}),
|
|
158
|
-
/* @__PURE__ */ E(
|
|
199
|
+
/* @__PURE__ */ E(A, {}),
|
|
200
|
+
/* @__PURE__ */ D("div", {
|
|
159
201
|
className: "grid grid-cols-1 gap-2 sm:grid-cols-2",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
161
|
-
variant: "outline",
|
|
162
|
-
onClick: P,
|
|
163
|
-
children: [/* @__PURE__ */ T(o, { size: 16 }), " Freight estimates"]
|
|
164
|
-
}), /* @__PURE__ */ E(p, {
|
|
202
|
+
children: [/* @__PURE__ */ D(p, {
|
|
165
203
|
variant: "outline",
|
|
166
204
|
onClick: F,
|
|
167
|
-
children: [/* @__PURE__ */
|
|
205
|
+
children: [/* @__PURE__ */ E(o, { size: 16 }), " Freight estimates"]
|
|
206
|
+
}), /* @__PURE__ */ D(p, {
|
|
207
|
+
variant: "outline",
|
|
208
|
+
onClick: I,
|
|
209
|
+
children: [/* @__PURE__ */ E(r, { size: 16 }), " Invoice comparisons"]
|
|
168
210
|
})]
|
|
169
211
|
})
|
|
170
212
|
]
|
|
@@ -172,17 +214,17 @@ function k() {
|
|
|
172
214
|
]
|
|
173
215
|
})]
|
|
174
216
|
}),
|
|
175
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ D(m, { children: [/* @__PURE__ */ E(g, { children: /* @__PURE__ */ E(_, { children: "Revenue vs cost" }) }), /* @__PURE__ */ E(h, { children: z ? /* @__PURE__ */ E(C, {
|
|
176
218
|
xKey: "label",
|
|
177
219
|
height: 260,
|
|
178
220
|
showLegend: !1,
|
|
179
|
-
data:
|
|
221
|
+
data: R,
|
|
180
222
|
series: [{
|
|
181
223
|
dataKey: "amount",
|
|
182
224
|
name: "Amount"
|
|
183
225
|
}],
|
|
184
226
|
valueFormatter: (e) => u(Number(e))
|
|
185
|
-
}) : /* @__PURE__ */
|
|
227
|
+
}) : /* @__PURE__ */ E("p", {
|
|
186
228
|
className: "py-8 text-center text-sm text-text-secondary",
|
|
187
229
|
children: "Revenue and cost figures appear here as orders are delivered."
|
|
188
230
|
}) })] })
|
|
@@ -190,4 +232,4 @@ function k() {
|
|
|
190
232
|
});
|
|
191
233
|
}
|
|
192
234
|
//#endregion
|
|
193
|
-
export {
|
|
235
|
+
export { j as default };
|