reactbridge-sdk 0.2.6 → 0.2.8
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/README.md +85 -1
- package/dist/components/ReactBridgeChatbox.d.ts.map +1 -1
- package/dist/components/ReactBridgeSearch.d.ts +2 -2
- package/dist/components/ReactBridgeSearch.d.ts.map +1 -1
- package/dist/components/ReportsDashboard.d.ts +13 -0
- package/dist/components/ReportsDashboard.d.ts.map +1 -0
- package/dist/components/analytics/AnalyticsDashboard.d.ts.map +1 -1
- package/dist/components/analytics/AnalyticsDrawer.d.ts +3 -3
- package/dist/components/analytics/AnalyticsDrawer.d.ts.map +1 -1
- package/dist/components/analytics/AnalyticsReport.d.ts +3 -3
- package/dist/components/analytics/AnalyticsReport.d.ts.map +1 -1
- package/dist/components/analytics/AnalyticsWidget.d.ts +4 -4
- package/dist/components/analytics/DirectivesPanel.d.ts +3 -3
- package/dist/components/analytics/DirectivesPanel.d.ts.map +1 -1
- package/dist/components/analytics/MetricsPanel.d.ts +3 -3
- package/dist/components/analytics/MetricsPanel.d.ts.map +1 -1
- package/dist/components/analytics/ObservationsPanel.d.ts +3 -3
- package/dist/components/analytics/ObservationsPanel.d.ts.map +1 -1
- package/dist/components/reports/DataQualityAutomation.d.ts +12 -0
- package/dist/components/reports/DataQualityAutomation.d.ts.map +1 -0
- package/dist/components/reports/DataTable.d.ts +24 -0
- package/dist/components/reports/DataTable.d.ts.map +1 -0
- package/dist/components/reports/DateRangeSelector.d.ts +14 -0
- package/dist/components/reports/DateRangeSelector.d.ts.map +1 -0
- package/dist/components/reports/ExecutionAuditTrail.d.ts +12 -0
- package/dist/components/reports/ExecutionAuditTrail.d.ts.map +1 -0
- package/dist/components/reports/ExecutiveDashboard.d.ts +12 -0
- package/dist/components/reports/ExecutiveDashboard.d.ts.map +1 -0
- package/dist/components/reports/HealthGauge.d.ts +14 -0
- package/dist/components/reports/HealthGauge.d.ts.map +1 -0
- package/dist/components/reports/LoadingState.d.ts +12 -0
- package/dist/components/reports/LoadingState.d.ts.map +1 -0
- package/dist/components/reports/MetricCard.d.ts +18 -0
- package/dist/components/reports/MetricCard.d.ts.map +1 -0
- package/dist/components/reports/PortfolioHealth.d.ts +12 -0
- package/dist/components/reports/PortfolioHealth.d.ts.map +1 -0
- package/dist/components/reports/ProgressBar.d.ts +15 -0
- package/dist/components/reports/ProgressBar.d.ts.map +1 -0
- package/dist/components/reports/ReportLayout.d.ts +19 -0
- package/dist/components/reports/ReportLayout.d.ts.map +1 -0
- package/dist/components/reports/RiskMatrix.d.ts +21 -0
- package/dist/components/reports/RiskMatrix.d.ts.map +1 -0
- package/dist/components/reports/StatusBadge.d.ts +14 -0
- package/dist/components/reports/StatusBadge.d.ts.map +1 -0
- package/dist/components/reports/SupplyChainRisk.d.ts +12 -0
- package/dist/components/reports/SupplyChainRisk.d.ts.map +1 -0
- package/dist/components/reports/TrendChart.d.ts +19 -0
- package/dist/components/reports/TrendChart.d.ts.map +1 -0
- package/dist/hooks/analytics/useAnalyticsConfigs.d.ts +1 -1
- package/dist/hooks/analytics/useAnalyticsResult.d.ts +1 -1
- package/dist/hooks/analytics/useDateRange.d.ts +18 -0
- package/dist/hooks/analytics/useDateRange.d.ts.map +1 -0
- package/dist/hooks/analytics/useDirectiveAction.d.ts +2 -2
- package/dist/hooks/analytics/useDirectiveAction.d.ts.map +1 -1
- package/dist/hooks/analytics/useReportData.d.ts +24 -0
- package/dist/hooks/analytics/useReportData.d.ts.map +1 -0
- package/dist/hooks/useReactBridge.d.ts +1 -1
- package/dist/hooks/useReactBridge.d.ts.map +1 -1
- package/dist/index.d.ts +67 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +2338 -389
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2355 -387
- package/dist/index.js.map +1 -1
- package/dist/provider/ReactBridgeProvider.d.ts +3 -3
- package/dist/provider/ReactBridgeProvider.d.ts.map +1 -1
- package/dist/themes/dark.d.ts +1 -1
- package/dist/themes/index.d.ts +3 -3
- package/dist/themes/index.d.ts.map +1 -1
- package/dist/themes/light.d.ts +1 -1
- package/dist/types/analytics.d.ts +5 -5
- package/dist/types/index.d.ts +5 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/reports.d.ts +158 -0
- package/dist/types/reports.d.ts.map +1 -0
- package/dist/utils/analytics-api.d.ts +3 -3
- package/dist/utils/analytics-api.d.ts.map +1 -1
- package/dist/utils/api.d.ts +12 -1
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/contextDiff.d.ts +1 -1
- package/dist/utils/contextDiff.d.ts.map +1 -1
- package/dist/utils/date-range.d.ts +32 -0
- package/dist/utils/date-range.d.ts.map +1 -0
- package/dist/utils/reports-api.d.ts +19 -0
- package/dist/utils/reports-api.d.ts.map +1 -0
- package/dist/utils/request-cache.d.ts +32 -0
- package/dist/utils/request-cache.d.ts.map +1 -0
- package/dist/utils/voice.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useState, useContext, useRef, useEffect, useCallback } from 'react';
|
|
1
|
+
import React, { createContext, useState, useContext, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
/******************************************************************************
|
|
4
4
|
Copyright (c) Microsoft Corporation.
|
|
@@ -32,7 +32,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
32
32
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
const DEFAULT_BASE_URL$1 =
|
|
35
|
+
const DEFAULT_BASE_URL$1 = "https://react-bridge-api-14089992360.northamerica-northeast2.run.app"; // 'http://localhost:5093';
|
|
36
36
|
class ReactBridgeAPI {
|
|
37
37
|
constructor(config) {
|
|
38
38
|
this.config = config;
|
|
@@ -45,13 +45,15 @@ class ReactBridgeAPI {
|
|
|
45
45
|
// Configure abort signal for fetch with robust fallback.
|
|
46
46
|
// `ReactBridgeConfig.timeout` is milliseconds. If it's undefined, no timeout is applied.
|
|
47
47
|
// If it's 0 or a non-positive number, timeout is disabled.
|
|
48
|
-
const timeoutMs = typeof this.config.timeout ===
|
|
48
|
+
const timeoutMs = typeof this.config.timeout === "number"
|
|
49
|
+
? this.config.timeout
|
|
50
|
+
: undefined;
|
|
49
51
|
let controller = null;
|
|
50
52
|
let timeoutId = null;
|
|
51
53
|
let signal;
|
|
52
|
-
if (typeof timeoutMs ===
|
|
54
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
53
55
|
// Prefer built-in AbortSignal.timeout when available
|
|
54
|
-
if (typeof AbortSignal.timeout ===
|
|
56
|
+
if (typeof AbortSignal.timeout === "function") {
|
|
55
57
|
signal = AbortSignal.timeout(timeoutMs);
|
|
56
58
|
}
|
|
57
59
|
else {
|
|
@@ -62,43 +64,43 @@ class ReactBridgeAPI {
|
|
|
62
64
|
}
|
|
63
65
|
// Check if this is a multimodal request (has image or document)
|
|
64
66
|
const hasFile = request.image || request.document;
|
|
65
|
-
let headers = Object.assign({
|
|
67
|
+
let headers = Object.assign({ "X-API-Key": this.config.apiKey }, this.config.headers);
|
|
66
68
|
let body;
|
|
67
69
|
if (hasFile) {
|
|
68
70
|
// Use FormData for file uploads
|
|
69
71
|
const formData = new FormData();
|
|
70
72
|
// Add text fields
|
|
71
|
-
formData.append(
|
|
72
|
-
formData.append(
|
|
73
|
+
formData.append("userId", request.userId);
|
|
74
|
+
formData.append("query", request.query);
|
|
73
75
|
if (request.userName)
|
|
74
|
-
formData.append(
|
|
76
|
+
formData.append("userName", request.userName);
|
|
75
77
|
if (request.userPreferences)
|
|
76
|
-
formData.append(
|
|
78
|
+
formData.append("userPreferences", request.userPreferences);
|
|
77
79
|
if (request.userRecentActivity)
|
|
78
|
-
formData.append(
|
|
80
|
+
formData.append("userRecentActivity", request.userRecentActivity);
|
|
79
81
|
if (request.interfaceState)
|
|
80
82
|
formData.append("InterfaceStateJson", JSON.stringify(request.interfaceState));
|
|
81
83
|
if (request.sessionId)
|
|
82
|
-
formData.append(
|
|
84
|
+
formData.append("sessionId", request.sessionId);
|
|
83
85
|
if (request.modalityHint)
|
|
84
|
-
formData.append(
|
|
86
|
+
formData.append("modalityHint", request.modalityHint);
|
|
85
87
|
// Add files
|
|
86
88
|
if (request.image)
|
|
87
|
-
formData.append(
|
|
89
|
+
formData.append("image", request.image);
|
|
88
90
|
if (request.document)
|
|
89
|
-
formData.append(
|
|
91
|
+
formData.append("document", request.document);
|
|
90
92
|
body = formData;
|
|
91
93
|
// Don't set Content-Type for FormData - let browser set it with boundary
|
|
92
|
-
delete headers[
|
|
94
|
+
delete headers["Content-Type"];
|
|
93
95
|
}
|
|
94
96
|
else {
|
|
95
97
|
// Use JSON for text-only requests
|
|
96
|
-
headers[
|
|
98
|
+
headers["Content-Type"] = "application/json";
|
|
97
99
|
body = JSON.stringify(request);
|
|
98
100
|
}
|
|
99
101
|
// console.log('Sending request to ReactBridge API:', { url, headers, body, timeoutMs });
|
|
100
102
|
const response = yield fetch(url, {
|
|
101
|
-
method:
|
|
103
|
+
method: "POST",
|
|
102
104
|
headers,
|
|
103
105
|
body,
|
|
104
106
|
signal,
|
|
@@ -118,7 +120,7 @@ class ReactBridgeAPI {
|
|
|
118
120
|
if (error instanceof Error) {
|
|
119
121
|
throw new Error(`ReactBridge API Error: ${error.message}`);
|
|
120
122
|
}
|
|
121
|
-
throw new Error(
|
|
123
|
+
throw new Error("ReactBridge API Error: Unknown error occurred");
|
|
122
124
|
}
|
|
123
125
|
});
|
|
124
126
|
}
|
|
@@ -138,65 +140,164 @@ class ReactBridgeAPI {
|
|
|
138
140
|
});
|
|
139
141
|
}
|
|
140
142
|
sendImageRequest(userId_1, imageFile_1) {
|
|
141
|
-
return __awaiter(this, arguments, void 0, function* (userId, imageFile, query =
|
|
143
|
+
return __awaiter(this, arguments, void 0, function* (userId, imageFile, query = "", context) {
|
|
142
144
|
const request = Object.assign({ userId,
|
|
143
|
-
query, image: imageFile, modalityHint:
|
|
145
|
+
query, image: imageFile, modalityHint: "image" }, context);
|
|
144
146
|
return this.sendMessage(request);
|
|
145
147
|
});
|
|
146
148
|
}
|
|
147
149
|
sendDocumentRequest(userId_1, documentFile_1) {
|
|
148
|
-
return __awaiter(this, arguments, void 0, function* (userId, documentFile, query =
|
|
150
|
+
return __awaiter(this, arguments, void 0, function* (userId, documentFile, query = "", context) {
|
|
149
151
|
const request = Object.assign({ userId,
|
|
150
|
-
query, document: documentFile, modalityHint:
|
|
152
|
+
query, document: documentFile, modalityHint: "document" }, context);
|
|
151
153
|
return this.sendMessage(request);
|
|
152
154
|
});
|
|
153
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Report API Methods - Call analytics endpoints directly
|
|
158
|
+
*/
|
|
159
|
+
fetchReport(endpoint, params) {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
const baseURL = this.config.baseURL || DEFAULT_BASE_URL$1;
|
|
162
|
+
const url = new URL(`${baseURL}/api/reports/${endpoint}`);
|
|
163
|
+
// Add query parameters if provided
|
|
164
|
+
if (params) {
|
|
165
|
+
for (const key in params) {
|
|
166
|
+
if (params.hasOwnProperty(key)) {
|
|
167
|
+
url.searchParams.append(key, params[key]);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const response = yield fetch(url.toString(), {
|
|
173
|
+
method: "GET",
|
|
174
|
+
headers: Object.assign({ "X-API-Key": this.config.apiKey, "Content-Type": "application/json" }, this.config.headers),
|
|
175
|
+
});
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
const errorMessage = this.getErrorMessage(response.status);
|
|
178
|
+
throw new Error(errorMessage);
|
|
179
|
+
}
|
|
180
|
+
return (yield response.json());
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (error instanceof Error) {
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
throw new Error("Failed to fetch report data");
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
getErrorMessage(status) {
|
|
191
|
+
switch (status) {
|
|
192
|
+
case 401:
|
|
193
|
+
return "Unauthorized: Invalid API key";
|
|
194
|
+
case 404:
|
|
195
|
+
return "Report endpoint not found";
|
|
196
|
+
case 500:
|
|
197
|
+
return "Server error: Unable to fetch report data";
|
|
198
|
+
default:
|
|
199
|
+
return `API Error: ${status}`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
getExecutiveDashboard(params) {
|
|
203
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
+
const queryParams = {};
|
|
205
|
+
if (params === null || params === void 0 ? void 0 : params.startDate)
|
|
206
|
+
queryParams.startDate = params.startDate;
|
|
207
|
+
if (params === null || params === void 0 ? void 0 : params.endDate)
|
|
208
|
+
queryParams.endDate = params.endDate;
|
|
209
|
+
return this.fetchReport("executive-dashboard", queryParams);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
getSupplyChainRisk(params) {
|
|
213
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
214
|
+
const queryParams = {};
|
|
215
|
+
if (params === null || params === void 0 ? void 0 : params.startDate)
|
|
216
|
+
queryParams.startDate = params.startDate;
|
|
217
|
+
if (params === null || params === void 0 ? void 0 : params.endDate)
|
|
218
|
+
queryParams.endDate = params.endDate;
|
|
219
|
+
return this.fetchReport("supply-chain-risk", queryParams);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
getExecutionAuditTrail(params) {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
const queryParams = {};
|
|
225
|
+
if (params === null || params === void 0 ? void 0 : params.startDate)
|
|
226
|
+
queryParams.startDate = params.startDate;
|
|
227
|
+
if (params === null || params === void 0 ? void 0 : params.endDate)
|
|
228
|
+
queryParams.endDate = params.endDate;
|
|
229
|
+
if (params === null || params === void 0 ? void 0 : params.directiveType)
|
|
230
|
+
queryParams.directiveType = params.directiveType;
|
|
231
|
+
return this.fetchReport("execution-audit-trail", queryParams);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
getPortfolioHealth(params) {
|
|
235
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
236
|
+
const queryParams = {};
|
|
237
|
+
if (params === null || params === void 0 ? void 0 : params.startDate)
|
|
238
|
+
queryParams.startDate = params.startDate;
|
|
239
|
+
if (params === null || params === void 0 ? void 0 : params.endDate)
|
|
240
|
+
queryParams.endDate = params.endDate;
|
|
241
|
+
return this.fetchReport("portfolio-health", queryParams);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
getDataQualityAutomation(params) {
|
|
245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
const queryParams = {};
|
|
247
|
+
if (params === null || params === void 0 ? void 0 : params.startDate)
|
|
248
|
+
queryParams.startDate = params.startDate;
|
|
249
|
+
if (params === null || params === void 0 ? void 0 : params.endDate)
|
|
250
|
+
queryParams.endDate = params.endDate;
|
|
251
|
+
return this.fetchReport("data-quality-automation", queryParams);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
154
254
|
}
|
|
155
255
|
|
|
156
256
|
const lightTheme = {
|
|
157
|
-
name:
|
|
257
|
+
name: "light",
|
|
158
258
|
colors: {
|
|
159
|
-
primary:
|
|
160
|
-
secondary:
|
|
161
|
-
background:
|
|
162
|
-
surface:
|
|
163
|
-
text:
|
|
164
|
-
textSecondary:
|
|
165
|
-
border:
|
|
166
|
-
error:
|
|
167
|
-
success:
|
|
259
|
+
primary: "#1976d2",
|
|
260
|
+
secondary: "#dc004e",
|
|
261
|
+
background: "#ffffff",
|
|
262
|
+
surface: "#f5f5f5",
|
|
263
|
+
text: "#000000",
|
|
264
|
+
textSecondary: "#666666",
|
|
265
|
+
border: "#e0e0e0",
|
|
266
|
+
error: "#f44336",
|
|
267
|
+
success: "#4caf50",
|
|
168
268
|
},
|
|
169
269
|
spacing: {
|
|
170
|
-
xs:
|
|
171
|
-
sm:
|
|
172
|
-
md:
|
|
173
|
-
lg:
|
|
174
|
-
xl:
|
|
270
|
+
xs: "4px",
|
|
271
|
+
sm: "8px",
|
|
272
|
+
md: "16px",
|
|
273
|
+
lg: "24px",
|
|
274
|
+
xl: "32px",
|
|
175
275
|
},
|
|
176
276
|
fontSizes: {
|
|
177
|
-
xs:
|
|
178
|
-
sm:
|
|
179
|
-
md:
|
|
180
|
-
lg:
|
|
181
|
-
xl:
|
|
277
|
+
xs: "12px",
|
|
278
|
+
sm: "14px",
|
|
279
|
+
md: "16px",
|
|
280
|
+
lg: "18px",
|
|
281
|
+
xl: "24px",
|
|
182
282
|
},
|
|
183
|
-
borderRadius:
|
|
184
|
-
boxShadow:
|
|
283
|
+
borderRadius: "8px",
|
|
284
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
|
|
185
285
|
};
|
|
186
286
|
|
|
187
287
|
// Default STT Provider using Web Speech API
|
|
188
288
|
class WebSpeechSTTProvider {
|
|
189
289
|
constructor() {
|
|
190
|
-
if (typeof window !==
|
|
191
|
-
const SpeechRecognitionCtor = window.SpeechRecognition ||
|
|
290
|
+
if (typeof window !== "undefined") {
|
|
291
|
+
const SpeechRecognitionCtor = window.SpeechRecognition ||
|
|
292
|
+
window.webkitSpeechRecognition;
|
|
192
293
|
if (SpeechRecognitionCtor) {
|
|
193
294
|
this.recognition = new SpeechRecognitionCtor();
|
|
194
295
|
if (!this.recognition) {
|
|
195
|
-
throw new Error(
|
|
296
|
+
throw new Error("Failed to initialize SpeechRecognition");
|
|
196
297
|
}
|
|
197
298
|
this.recognition.continuous = false;
|
|
198
299
|
this.recognition.interimResults = false;
|
|
199
|
-
this.recognition.lang =
|
|
300
|
+
this.recognition.lang = "en-US";
|
|
200
301
|
this.recognition.onresult = (event) => {
|
|
201
302
|
var _a;
|
|
202
303
|
const transcript = event.results[0][0].transcript;
|
|
@@ -208,7 +309,7 @@ class WebSpeechSTTProvider {
|
|
|
208
309
|
};
|
|
209
310
|
}
|
|
210
311
|
else {
|
|
211
|
-
console.warn(
|
|
312
|
+
console.warn("SpeechRecognition API not supported in this browser.");
|
|
212
313
|
}
|
|
213
314
|
}
|
|
214
315
|
}
|
|
@@ -217,7 +318,7 @@ class WebSpeechSTTProvider {
|
|
|
217
318
|
this.recognition.start();
|
|
218
319
|
}
|
|
219
320
|
else {
|
|
220
|
-
throw new Error(
|
|
321
|
+
throw new Error("Speech recognition not supported");
|
|
221
322
|
}
|
|
222
323
|
}
|
|
223
324
|
stopRecognition() {
|
|
@@ -234,16 +335,16 @@ class WebSpeechSTTProvider {
|
|
|
234
335
|
// Default TTS Provider using Web Speech API
|
|
235
336
|
class WebSpeechTTSProvider {
|
|
236
337
|
speak(text) {
|
|
237
|
-
if (typeof window !==
|
|
338
|
+
if (typeof window !== "undefined" && "speechSynthesis" in window) {
|
|
238
339
|
const utterance = new SpeechSynthesisUtterance(text);
|
|
239
340
|
window.speechSynthesis.speak(utterance);
|
|
240
341
|
}
|
|
241
342
|
else {
|
|
242
|
-
console.warn(
|
|
343
|
+
console.warn("Text-to-speech not supported");
|
|
243
344
|
}
|
|
244
345
|
}
|
|
245
346
|
stop() {
|
|
246
|
-
if (typeof window !==
|
|
347
|
+
if (typeof window !== "undefined" && "speechSynthesis" in window) {
|
|
247
348
|
window.speechSynthesis.cancel();
|
|
248
349
|
}
|
|
249
350
|
}
|
|
@@ -255,14 +356,10 @@ function ReactBridgeProvider({ apiKey, config = {}, theme = lightTheme, sttProvi
|
|
|
255
356
|
apiKey,
|
|
256
357
|
baseURL: config.baseURL,
|
|
257
358
|
// Preserve explicit 0 (to disable timeouts). If not provided, default to 30s
|
|
258
|
-
timeout: typeof config.timeout ===
|
|
359
|
+
timeout: typeof config.timeout === "number" ? config.timeout : 30000,
|
|
259
360
|
headers: config.headers,
|
|
260
361
|
};
|
|
261
|
-
const api = React.useMemo(() => new ReactBridgeAPI(fullConfig), [
|
|
262
|
-
fullConfig.apiKey,
|
|
263
|
-
fullConfig.baseURL,
|
|
264
|
-
fullConfig.timeout,
|
|
265
|
-
]);
|
|
362
|
+
const api = React.useMemo(() => new ReactBridgeAPI(fullConfig), [fullConfig.apiKey, fullConfig.baseURL, fullConfig.timeout]);
|
|
266
363
|
const [sttProvider, setSTTProvider] = useState(initialSTTProvider || new WebSpeechSTTProvider());
|
|
267
364
|
const [ttsProvider, setTTSProvider] = useState(initialTTSProvider || new WebSpeechTTSProvider());
|
|
268
365
|
const value = {
|
|
@@ -279,7 +376,7 @@ function ReactBridgeProvider({ apiKey, config = {}, theme = lightTheme, sttProvi
|
|
|
279
376
|
function useReactBridgeContext() {
|
|
280
377
|
const context = useContext(ReactBridgeContext);
|
|
281
378
|
if (!context) {
|
|
282
|
-
throw new Error(
|
|
379
|
+
throw new Error("useReactBridgeContext must be used within a ReactBridgeProvider");
|
|
283
380
|
}
|
|
284
381
|
return context;
|
|
285
382
|
}
|
|
@@ -288,7 +385,8 @@ function hasContextChanged(previous, current) {
|
|
|
288
385
|
if (!previous)
|
|
289
386
|
return true;
|
|
290
387
|
// Check if page changed
|
|
291
|
-
if (previous.interfaceState.currentPageName !==
|
|
388
|
+
if (previous.interfaceState.currentPageName !==
|
|
389
|
+
current.interfaceState.currentPageName) {
|
|
292
390
|
return true;
|
|
293
391
|
}
|
|
294
392
|
// Check if viewing items changed
|
|
@@ -298,8 +396,8 @@ function hasContextChanged(previous, current) {
|
|
|
298
396
|
return true;
|
|
299
397
|
}
|
|
300
398
|
// Check if item IDs changed
|
|
301
|
-
const prevIds = prevItems.map(item => item.id).sort();
|
|
302
|
-
const currIds = currItems.map(item => item.id).sort();
|
|
399
|
+
const prevIds = prevItems.map((item) => item.id).sort();
|
|
400
|
+
const currIds = currItems.map((item) => item.id).sort();
|
|
303
401
|
return JSON.stringify(prevIds) !== JSON.stringify(currIds);
|
|
304
402
|
}
|
|
305
403
|
function getContextSummary(context) {
|
|
@@ -308,14 +406,14 @@ function getContextSummary(context) {
|
|
|
308
406
|
parts.push(`Page: ${interfaceState.currentPageName}`);
|
|
309
407
|
if (interfaceState.viewingItems && interfaceState.viewingItems.length > 0) {
|
|
310
408
|
parts.push(`Viewing ${interfaceState.viewingItems.length} items:`);
|
|
311
|
-
interfaceState.viewingItems.slice(0, 3).forEach(item => {
|
|
409
|
+
interfaceState.viewingItems.slice(0, 3).forEach((item) => {
|
|
312
410
|
parts.push(` - ${item.name} ($${item.price})`);
|
|
313
411
|
});
|
|
314
412
|
if (interfaceState.viewingItems.length > 3) {
|
|
315
413
|
parts.push(` ... and ${interfaceState.viewingItems.length - 3} more`);
|
|
316
414
|
}
|
|
317
415
|
}
|
|
318
|
-
return parts.join(
|
|
416
|
+
return parts.join("\n");
|
|
319
417
|
}
|
|
320
418
|
|
|
321
419
|
function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechStart, onSpeechEnd, onTranscript, onAgentResponse, }) {
|
|
@@ -333,17 +431,17 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
|
|
|
333
431
|
const contextSummary = getContextSummary(currentContext);
|
|
334
432
|
const contextMessage = {
|
|
335
433
|
id: `context-${Date.now()}`,
|
|
336
|
-
role:
|
|
434
|
+
role: "system",
|
|
337
435
|
content: `[Context Update]\n${contextSummary}`,
|
|
338
436
|
timestamp: new Date(),
|
|
339
437
|
};
|
|
340
|
-
setMessages(prev => [...prev, contextMessage]);
|
|
438
|
+
setMessages((prev) => [...prev, contextMessage]);
|
|
341
439
|
previousContextRef.current = currentContext;
|
|
342
440
|
}
|
|
343
441
|
}, [currentContext]);
|
|
344
442
|
const sendChatQuery = useCallback((queryOrOptions_1, ...args_1) => __awaiter(this, [queryOrOptions_1, ...args_1], void 0, function* (queryOrOptions, fromVoiceInput = false) {
|
|
345
|
-
const isMultimodal = typeof queryOrOptions ===
|
|
346
|
-
const query = isMultimodal ?
|
|
443
|
+
const isMultimodal = typeof queryOrOptions === "object";
|
|
444
|
+
const query = isMultimodal ? queryOrOptions.query || "" : queryOrOptions;
|
|
347
445
|
const image = isMultimodal ? queryOrOptions.image : undefined;
|
|
348
446
|
const document = isMultimodal ? queryOrOptions.document : undefined;
|
|
349
447
|
if (!query.trim() && !image && !document)
|
|
@@ -353,18 +451,22 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
|
|
|
353
451
|
// Add user message with file info if present
|
|
354
452
|
let messageContent = query;
|
|
355
453
|
if (image) {
|
|
356
|
-
messageContent = messageContent
|
|
454
|
+
messageContent = messageContent
|
|
455
|
+
? `${messageContent} [Image: ${image.name}]`
|
|
456
|
+
: `[Image: ${image.name}]`;
|
|
357
457
|
}
|
|
358
458
|
else if (document) {
|
|
359
|
-
messageContent = messageContent
|
|
459
|
+
messageContent = messageContent
|
|
460
|
+
? `${messageContent} [Document: ${document.name}]`
|
|
461
|
+
: `[Document: ${document.name}]`;
|
|
360
462
|
}
|
|
361
463
|
const userMessage = {
|
|
362
464
|
id: `user-${Date.now()}`,
|
|
363
|
-
role:
|
|
465
|
+
role: "user",
|
|
364
466
|
content: messageContent,
|
|
365
467
|
timestamp: new Date(),
|
|
366
468
|
};
|
|
367
|
-
setMessages(prev => [...prev, userMessage]);
|
|
469
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
368
470
|
try {
|
|
369
471
|
// Step 1: Send initial request to orchestrator
|
|
370
472
|
const request = {
|
|
@@ -379,12 +481,12 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
|
|
|
379
481
|
// Add multimodal fields
|
|
380
482
|
image,
|
|
381
483
|
document,
|
|
382
|
-
modalityHint: image ?
|
|
484
|
+
modalityHint: image ? "image" : document ? "document" : undefined,
|
|
383
485
|
};
|
|
384
486
|
lastRequestRef.current = request;
|
|
385
487
|
const response = yield api.sendMessage(request);
|
|
386
488
|
if (!response.success) {
|
|
387
|
-
throw new Error(response.error ||
|
|
489
|
+
throw new Error(response.error || "Request failed");
|
|
388
490
|
}
|
|
389
491
|
// Update session ID
|
|
390
492
|
if (response.sessionId) {
|
|
@@ -393,12 +495,12 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
|
|
|
393
495
|
// Add assistant message
|
|
394
496
|
const assistantMessage = {
|
|
395
497
|
id: `assistant-${Date.now()}`,
|
|
396
|
-
role:
|
|
498
|
+
role: "assistant",
|
|
397
499
|
content: response.message,
|
|
398
500
|
timestamp: new Date(),
|
|
399
501
|
toolCall: response.toolCall,
|
|
400
502
|
};
|
|
401
|
-
setMessages(prev => [...prev, assistantMessage]);
|
|
503
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
402
504
|
// Trigger TTS and callback only if input came from voice
|
|
403
505
|
if (fromVoiceInput) {
|
|
404
506
|
ttsProvider.speak(response.message);
|
|
@@ -416,11 +518,11 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
|
|
|
416
518
|
// Add final assistant message with tool result context
|
|
417
519
|
const finalMessage = {
|
|
418
520
|
id: `assistant-final-${Date.now()}`,
|
|
419
|
-
role:
|
|
521
|
+
role: "assistant",
|
|
420
522
|
content: resultResponse.message,
|
|
421
523
|
timestamp: new Date(),
|
|
422
524
|
};
|
|
423
|
-
setMessages(prev => [...prev, finalMessage]);
|
|
525
|
+
setMessages((prev) => [...prev, finalMessage]);
|
|
424
526
|
// Trigger TTS for final response only if input came from voice
|
|
425
527
|
if (fromVoiceInput) {
|
|
426
528
|
ttsProvider.speak(resultResponse.message);
|
|
@@ -433,30 +535,32 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
|
|
|
433
535
|
catch (toolError) {
|
|
434
536
|
const errorMessage = toolError instanceof Error
|
|
435
537
|
? toolError.message
|
|
436
|
-
:
|
|
538
|
+
: "Tool execution failed";
|
|
437
539
|
const errorMsg = {
|
|
438
540
|
id: `error-${Date.now()}`,
|
|
439
|
-
role:
|
|
541
|
+
role: "assistant",
|
|
440
542
|
content: `I encountered an error: ${errorMessage}`,
|
|
441
543
|
timestamp: new Date(),
|
|
442
544
|
};
|
|
443
|
-
setMessages(prev => [...prev, errorMsg]);
|
|
545
|
+
setMessages((prev) => [...prev, errorMsg]);
|
|
444
546
|
if (onError) {
|
|
445
|
-
onError(toolError instanceof Error
|
|
547
|
+
onError(toolError instanceof Error
|
|
548
|
+
? toolError
|
|
549
|
+
: new Error(errorMessage));
|
|
446
550
|
}
|
|
447
551
|
}
|
|
448
552
|
}
|
|
449
553
|
}
|
|
450
554
|
catch (err) {
|
|
451
|
-
const error = err instanceof Error ? err : new Error(
|
|
555
|
+
const error = err instanceof Error ? err : new Error("Unknown error");
|
|
452
556
|
setError(error);
|
|
453
557
|
const errorMessage = {
|
|
454
558
|
id: `error-${Date.now()}`,
|
|
455
|
-
role:
|
|
559
|
+
role: "assistant",
|
|
456
560
|
content: `Sorry, I encountered an error: ${error.message}`,
|
|
457
561
|
timestamp: new Date(),
|
|
458
562
|
};
|
|
459
|
-
setMessages(prev => [...prev, errorMessage]);
|
|
563
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
460
564
|
if (onError) {
|
|
461
565
|
onError(error);
|
|
462
566
|
}
|
|
@@ -497,7 +601,14 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
|
|
|
497
601
|
});
|
|
498
602
|
}
|
|
499
603
|
sttProvider.startRecognition();
|
|
500
|
-
}, [
|
|
604
|
+
}, [
|
|
605
|
+
sttProvider,
|
|
606
|
+
sendChatQuery,
|
|
607
|
+
onSpeechStart,
|
|
608
|
+
onSpeechEnd,
|
|
609
|
+
onTranscript,
|
|
610
|
+
onError,
|
|
611
|
+
]);
|
|
501
612
|
const stopVoiceInput = useCallback(() => {
|
|
502
613
|
setIsListening(false);
|
|
503
614
|
sttProvider.stopRecognition();
|
|
@@ -955,7 +1066,9 @@ toggleButtonClass = defaultToggleButtonClass, toggleButtonTitle = "Open chat ass
|
|
|
955
1066
|
padding: theme.spacing.md,
|
|
956
1067
|
backgroundColor: theme.colors.background,
|
|
957
1068
|
} },
|
|
958
|
-
messages.map((message) => renderMessage
|
|
1069
|
+
messages.map((message) => renderMessage
|
|
1070
|
+
? renderMessage(message)
|
|
1071
|
+
: defaultRenderMessage(message)),
|
|
959
1072
|
isLoading && React.createElement(TypingIndicator, { theme: theme }),
|
|
960
1073
|
React.createElement("div", { ref: messagesEndRef })),
|
|
961
1074
|
React.createElement("form", { onSubmit: handleSubmit, style: {
|
|
@@ -1110,10 +1223,10 @@ const MIC_ICON_SVG = (React.createElement("svg", { xmlns: "http://www.w3.org/200
|
|
|
1110
1223
|
// Plus Icon SVG
|
|
1111
1224
|
const PLUS_ICON_SVG = (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "currentColor" },
|
|
1112
1225
|
React.createElement("path", { d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" })));
|
|
1113
|
-
function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder =
|
|
1226
|
+
function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = "Search...", width = "100%", maxResults = 5, theme: themeOverride, onError, onSpeechStart, onSpeechEnd, onTranscript, onAgentResponse, }) {
|
|
1114
1227
|
const { theme: contextTheme } = useReactBridgeContext();
|
|
1115
1228
|
const theme = Object.assign(Object.assign({}, contextTheme), themeOverride);
|
|
1116
|
-
const { messages, isLoading, sendChatQuery, isListening, startVoiceInput, stopVoiceInput } = useReactBridge({
|
|
1229
|
+
const { messages, isLoading, sendChatQuery, isListening, startVoiceInput, stopVoiceInput, } = useReactBridge({
|
|
1117
1230
|
onIntentDetected,
|
|
1118
1231
|
currentContext,
|
|
1119
1232
|
onError,
|
|
@@ -1122,7 +1235,7 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1122
1235
|
onTranscript,
|
|
1123
1236
|
onAgentResponse,
|
|
1124
1237
|
});
|
|
1125
|
-
const [inputValue, setInputValue] = useState(
|
|
1238
|
+
const [inputValue, setInputValue] = useState("");
|
|
1126
1239
|
const [isOpen, setIsOpen] = useState(false);
|
|
1127
1240
|
const [isUploadMenuOpen, setIsUploadMenuOpen] = useState(false);
|
|
1128
1241
|
const [selectedFile, setSelectedFile] = useState(null);
|
|
@@ -1132,13 +1245,14 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1132
1245
|
// Close dropdown and upload menu when clicking outside
|
|
1133
1246
|
useEffect(() => {
|
|
1134
1247
|
const handleClickOutside = (event) => {
|
|
1135
|
-
if (containerRef.current &&
|
|
1248
|
+
if (containerRef.current &&
|
|
1249
|
+
!containerRef.current.contains(event.target)) {
|
|
1136
1250
|
setIsOpen(false);
|
|
1137
1251
|
setIsUploadMenuOpen(false);
|
|
1138
1252
|
}
|
|
1139
1253
|
};
|
|
1140
|
-
document.addEventListener(
|
|
1141
|
-
return () => document.removeEventListener(
|
|
1254
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1255
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
1142
1256
|
}, []);
|
|
1143
1257
|
// Open dropdown when there are messages
|
|
1144
1258
|
useEffect(() => {
|
|
@@ -1148,9 +1262,10 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1148
1262
|
}, [messages]);
|
|
1149
1263
|
const handleFileSelect = (type) => {
|
|
1150
1264
|
if (fileInputRef.current) {
|
|
1151
|
-
fileInputRef.current.accept =
|
|
1152
|
-
|
|
1153
|
-
|
|
1265
|
+
fileInputRef.current.accept =
|
|
1266
|
+
type === "image"
|
|
1267
|
+
? "image/png,image/jpeg,image/jpg,image/webp"
|
|
1268
|
+
: "application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/plain";
|
|
1154
1269
|
fileInputRef.current.click();
|
|
1155
1270
|
}
|
|
1156
1271
|
setIsUploadMenuOpen(false);
|
|
@@ -1161,12 +1276,19 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1161
1276
|
if (!file)
|
|
1162
1277
|
return;
|
|
1163
1278
|
// Validate file type
|
|
1164
|
-
const imageTypes = [
|
|
1165
|
-
const documentTypes = [
|
|
1166
|
-
|
|
1279
|
+
const imageTypes = ["image/png", "image/jpeg", "image/jpg", "image/webp"];
|
|
1280
|
+
const documentTypes = [
|
|
1281
|
+
"application/pdf",
|
|
1282
|
+
"application/msword",
|
|
1283
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1284
|
+
"text/plain",
|
|
1285
|
+
];
|
|
1286
|
+
const allowedTypes = file.type.startsWith("image/")
|
|
1287
|
+
? imageTypes
|
|
1288
|
+
: documentTypes;
|
|
1167
1289
|
if (!allowedTypes.includes(file.type)) {
|
|
1168
1290
|
if (onError) {
|
|
1169
|
-
onError(new Error(`Invalid file type. Please select a valid ${file.type.startsWith(
|
|
1291
|
+
onError(new Error(`Invalid file type. Please select a valid ${file.type.startsWith("image/") ? "image" : "document"} file.`));
|
|
1170
1292
|
}
|
|
1171
1293
|
return;
|
|
1172
1294
|
}
|
|
@@ -1174,13 +1296,13 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1174
1296
|
const maxSize = 10 * 1024 * 1024; // 10MB
|
|
1175
1297
|
if (file.size > maxSize) {
|
|
1176
1298
|
if (onError) {
|
|
1177
|
-
onError(new Error(
|
|
1299
|
+
onError(new Error("File size too large. Please select a file smaller than 10MB."));
|
|
1178
1300
|
}
|
|
1179
1301
|
return;
|
|
1180
1302
|
}
|
|
1181
1303
|
setSelectedFile(file);
|
|
1182
1304
|
// Create preview for images
|
|
1183
|
-
if (file.type.startsWith(
|
|
1305
|
+
if (file.type.startsWith("image/")) {
|
|
1184
1306
|
const reader = new FileReader();
|
|
1185
1307
|
reader.onload = (e) => {
|
|
1186
1308
|
var _a;
|
|
@@ -1197,7 +1319,7 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1197
1319
|
setSelectedFile(null);
|
|
1198
1320
|
setFilePreview(null);
|
|
1199
1321
|
if (fileInputRef.current) {
|
|
1200
|
-
fileInputRef.current.value =
|
|
1322
|
+
fileInputRef.current.value = "";
|
|
1201
1323
|
}
|
|
1202
1324
|
};
|
|
1203
1325
|
const handleSubmit = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1205,13 +1327,17 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1205
1327
|
if ((!inputValue.trim() && !selectedFile) || isLoading)
|
|
1206
1328
|
return;
|
|
1207
1329
|
const query = inputValue.trim();
|
|
1208
|
-
setInputValue(
|
|
1330
|
+
setInputValue("");
|
|
1209
1331
|
// Handle multimodal request
|
|
1210
1332
|
if (selectedFile && filePreview) {
|
|
1211
1333
|
yield sendChatQuery({
|
|
1212
1334
|
query,
|
|
1213
|
-
image: selectedFile.type.startsWith(
|
|
1214
|
-
|
|
1335
|
+
image: selectedFile.type.startsWith("image/")
|
|
1336
|
+
? selectedFile
|
|
1337
|
+
: undefined,
|
|
1338
|
+
document: selectedFile.type.startsWith("image/")
|
|
1339
|
+
? undefined
|
|
1340
|
+
: selectedFile,
|
|
1215
1341
|
});
|
|
1216
1342
|
removeFile();
|
|
1217
1343
|
}
|
|
@@ -1221,33 +1347,33 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1221
1347
|
});
|
|
1222
1348
|
// Get only assistant messages (not system messages)
|
|
1223
1349
|
const displayMessages = messages
|
|
1224
|
-
.filter((msg) => msg.role ===
|
|
1350
|
+
.filter((msg) => msg.role === "assistant")
|
|
1225
1351
|
.slice(-maxResults);
|
|
1226
1352
|
return (React.createElement("div", { ref: containerRef, style: {
|
|
1227
|
-
position:
|
|
1353
|
+
position: "relative",
|
|
1228
1354
|
width,
|
|
1229
|
-
fontFamily:
|
|
1355
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1230
1356
|
} },
|
|
1231
1357
|
React.createElement("form", { onSubmit: handleSubmit },
|
|
1232
|
-
React.createElement("div", { style: { position:
|
|
1233
|
-
React.createElement("input", { ref: fileInputRef, type: "file", onChange: handleFileChange, style: { display:
|
|
1358
|
+
React.createElement("div", { style: { position: "relative" } },
|
|
1359
|
+
React.createElement("input", { ref: fileInputRef, type: "file", onChange: handleFileChange, style: { display: "none" } }),
|
|
1234
1360
|
React.createElement("input", { type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onFocus: () => displayMessages.length > 0 && setIsOpen(true), placeholder: selectedFile ? `Search with ${selectedFile.name}...` : placeholder, disabled: isLoading, style: {
|
|
1235
|
-
width:
|
|
1361
|
+
width: "100%",
|
|
1236
1362
|
padding: theme.spacing.md,
|
|
1237
|
-
paddingRight:
|
|
1363
|
+
paddingRight: "180px", // Increased to make room for plus, mic, and search buttons
|
|
1238
1364
|
fontSize: theme.fontSizes.md,
|
|
1239
1365
|
border: `1px solid ${theme.colors.border}`,
|
|
1240
1366
|
borderRadius: theme.borderRadius,
|
|
1241
1367
|
backgroundColor: theme.colors.background,
|
|
1242
1368
|
color: theme.colors.text,
|
|
1243
|
-
outline:
|
|
1244
|
-
boxSizing:
|
|
1369
|
+
outline: "none",
|
|
1370
|
+
boxSizing: "border-box",
|
|
1245
1371
|
} }),
|
|
1246
1372
|
React.createElement("button", { type: "button", onClick: () => setIsUploadMenuOpen(!isUploadMenuOpen), disabled: isLoading, title: "Upload file", style: {
|
|
1247
|
-
position:
|
|
1248
|
-
right:
|
|
1249
|
-
top:
|
|
1250
|
-
transform:
|
|
1373
|
+
position: "absolute",
|
|
1374
|
+
right: "105px", // Position before the mic button
|
|
1375
|
+
top: "50%",
|
|
1376
|
+
transform: "translateY(-50%)",
|
|
1251
1377
|
padding: theme.spacing.sm,
|
|
1252
1378
|
marginRight: theme.spacing.xs,
|
|
1253
1379
|
color: theme.colors.border,
|
|
@@ -1262,42 +1388,42 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1262
1388
|
height: "32px",
|
|
1263
1389
|
} }, PLUS_ICON_SVG),
|
|
1264
1390
|
isUploadMenuOpen && (React.createElement("div", { style: {
|
|
1265
|
-
position:
|
|
1266
|
-
right:
|
|
1267
|
-
top:
|
|
1391
|
+
position: "absolute",
|
|
1392
|
+
right: "105px",
|
|
1393
|
+
top: "100%",
|
|
1268
1394
|
marginTop: theme.spacing.xs,
|
|
1269
1395
|
backgroundColor: theme.colors.background,
|
|
1270
1396
|
border: `1px solid ${theme.colors.border}`,
|
|
1271
1397
|
borderRadius: theme.borderRadius,
|
|
1272
1398
|
boxShadow: theme.boxShadow,
|
|
1273
1399
|
zIndex: 1000,
|
|
1274
|
-
minWidth:
|
|
1400
|
+
minWidth: "120px",
|
|
1275
1401
|
} },
|
|
1276
|
-
React.createElement("button", { type: "button", onClick: () => handleFileSelect(
|
|
1277
|
-
width:
|
|
1402
|
+
React.createElement("button", { type: "button", onClick: () => handleFileSelect("image"), style: {
|
|
1403
|
+
width: "100%",
|
|
1278
1404
|
padding: theme.spacing.sm,
|
|
1279
|
-
backgroundColor:
|
|
1280
|
-
border:
|
|
1405
|
+
backgroundColor: "transparent",
|
|
1406
|
+
border: "none",
|
|
1281
1407
|
color: theme.colors.text,
|
|
1282
|
-
cursor:
|
|
1283
|
-
textAlign:
|
|
1408
|
+
cursor: "pointer",
|
|
1409
|
+
textAlign: "left",
|
|
1284
1410
|
fontSize: theme.fontSizes.sm,
|
|
1285
1411
|
} }, "\uD83D\uDCF7 Image"),
|
|
1286
|
-
React.createElement("button", { type: "button", onClick: () => handleFileSelect(
|
|
1287
|
-
width:
|
|
1412
|
+
React.createElement("button", { type: "button", onClick: () => handleFileSelect("document"), style: {
|
|
1413
|
+
width: "100%",
|
|
1288
1414
|
padding: theme.spacing.sm,
|
|
1289
|
-
backgroundColor:
|
|
1290
|
-
border:
|
|
1415
|
+
backgroundColor: "transparent",
|
|
1416
|
+
border: "none",
|
|
1291
1417
|
color: theme.colors.text,
|
|
1292
|
-
cursor:
|
|
1293
|
-
textAlign:
|
|
1418
|
+
cursor: "pointer",
|
|
1419
|
+
textAlign: "left",
|
|
1294
1420
|
fontSize: theme.fontSizes.sm,
|
|
1295
1421
|
} }, "\uD83D\uDCC4 Document"))),
|
|
1296
1422
|
React.createElement("button", { type: "button", onClick: isListening ? stopVoiceInput : startVoiceInput, disabled: isLoading, title: isListening ? "Stop recording" : "Start voice input", style: {
|
|
1297
|
-
position:
|
|
1298
|
-
right:
|
|
1299
|
-
top:
|
|
1300
|
-
transform:
|
|
1423
|
+
position: "absolute",
|
|
1424
|
+
right: "70px", // Position before the search button
|
|
1425
|
+
top: "50%",
|
|
1426
|
+
transform: "translateY(-50%)",
|
|
1301
1427
|
padding: theme.spacing.sm,
|
|
1302
1428
|
marginRight: theme.spacing.xs,
|
|
1303
1429
|
color: isListening ? theme.colors.primary : theme.colors.border,
|
|
@@ -1312,67 +1438,73 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1312
1438
|
height: "32px",
|
|
1313
1439
|
} }, MIC_ICON_SVG),
|
|
1314
1440
|
React.createElement("button", { type: "submit", disabled: isLoading || (!inputValue.trim() && !selectedFile), style: {
|
|
1315
|
-
position:
|
|
1441
|
+
position: "absolute",
|
|
1316
1442
|
right: theme.spacing.sm,
|
|
1317
|
-
top:
|
|
1318
|
-
transform:
|
|
1443
|
+
top: "50%",
|
|
1444
|
+
transform: "translateY(-50%)",
|
|
1319
1445
|
padding: `${theme.spacing.xs} ${theme.spacing.md}`,
|
|
1320
1446
|
fontSize: theme.fontSizes.sm,
|
|
1321
1447
|
backgroundColor: theme.colors.primary,
|
|
1322
1448
|
color: theme.colors.background,
|
|
1323
|
-
border:
|
|
1449
|
+
border: "none",
|
|
1324
1450
|
borderRadius: theme.borderRadius,
|
|
1325
|
-
cursor: isLoading || (!inputValue.trim() && !selectedFile)
|
|
1451
|
+
cursor: isLoading || (!inputValue.trim() && !selectedFile)
|
|
1452
|
+
? "not-allowed"
|
|
1453
|
+
: "pointer",
|
|
1326
1454
|
opacity: isLoading || (!inputValue.trim() && !selectedFile) ? 0.5 : 1,
|
|
1327
|
-
} }, isLoading ?
|
|
1455
|
+
} }, isLoading ? "Searching..." : "Search")),
|
|
1328
1456
|
selectedFile && filePreview && (React.createElement("div", { style: {
|
|
1329
1457
|
marginTop: theme.spacing.sm,
|
|
1330
1458
|
padding: theme.spacing.sm,
|
|
1331
1459
|
backgroundColor: theme.colors.surface,
|
|
1332
1460
|
border: `1px solid ${theme.colors.border}`,
|
|
1333
1461
|
borderRadius: theme.borderRadius,
|
|
1334
|
-
display:
|
|
1335
|
-
alignItems:
|
|
1462
|
+
display: "flex",
|
|
1463
|
+
alignItems: "center",
|
|
1336
1464
|
gap: theme.spacing.sm,
|
|
1337
1465
|
} },
|
|
1338
|
-
selectedFile.type.startsWith(
|
|
1339
|
-
width:
|
|
1340
|
-
height:
|
|
1341
|
-
objectFit:
|
|
1466
|
+
selectedFile.type.startsWith("image/") ? (React.createElement("img", { src: filePreview, alt: "Preview", style: {
|
|
1467
|
+
width: "40px",
|
|
1468
|
+
height: "40px",
|
|
1469
|
+
objectFit: "cover",
|
|
1342
1470
|
borderRadius: theme.borderRadius,
|
|
1343
1471
|
} })) : (React.createElement("div", { style: {
|
|
1344
|
-
width:
|
|
1345
|
-
height:
|
|
1472
|
+
width: "40px",
|
|
1473
|
+
height: "40px",
|
|
1346
1474
|
backgroundColor: theme.colors.primary,
|
|
1347
1475
|
borderRadius: theme.borderRadius,
|
|
1348
|
-
display:
|
|
1349
|
-
alignItems:
|
|
1350
|
-
justifyContent:
|
|
1476
|
+
display: "flex",
|
|
1477
|
+
alignItems: "center",
|
|
1478
|
+
justifyContent: "center",
|
|
1351
1479
|
color: theme.colors.background,
|
|
1352
1480
|
fontSize: theme.fontSizes.lg,
|
|
1353
1481
|
} }, "\uD83D\uDCC4")),
|
|
1354
|
-
React.createElement("div", { style: {
|
|
1355
|
-
|
|
1482
|
+
React.createElement("div", { style: {
|
|
1483
|
+
flex: 1,
|
|
1484
|
+
fontSize: theme.fontSizes.sm,
|
|
1485
|
+
color: theme.colors.text,
|
|
1486
|
+
} },
|
|
1487
|
+
React.createElement("div", { style: { fontWeight: "bold" } }, selectedFile.name),
|
|
1356
1488
|
React.createElement("div", { style: { color: theme.colors.textSecondary } },
|
|
1357
1489
|
(selectedFile.size / 1024 / 1024).toFixed(2),
|
|
1358
1490
|
" MB")),
|
|
1359
1491
|
React.createElement("button", { type: "button", onClick: removeFile, style: {
|
|
1360
1492
|
padding: theme.spacing.xs,
|
|
1361
|
-
backgroundColor:
|
|
1362
|
-
border:
|
|
1493
|
+
backgroundColor: "transparent",
|
|
1494
|
+
border: "none",
|
|
1363
1495
|
color: theme.colors.textSecondary,
|
|
1364
|
-
cursor:
|
|
1496
|
+
cursor: "pointer",
|
|
1365
1497
|
borderRadius: theme.borderRadius,
|
|
1366
|
-
width:
|
|
1367
|
-
height:
|
|
1368
|
-
display:
|
|
1369
|
-
alignItems:
|
|
1370
|
-
justifyContent:
|
|
1371
|
-
fontSize:
|
|
1498
|
+
width: "24px",
|
|
1499
|
+
height: "24px",
|
|
1500
|
+
display: "flex",
|
|
1501
|
+
alignItems: "center",
|
|
1502
|
+
justifyContent: "center",
|
|
1503
|
+
fontSize: "18px",
|
|
1372
1504
|
}, title: "Remove file" }, "\u00D7")))),
|
|
1373
1505
|
isOpen && displayMessages.length > 0 && (React.createElement("div", { style: {
|
|
1374
|
-
position:
|
|
1375
|
-
top:
|
|
1506
|
+
position: "absolute",
|
|
1507
|
+
top: "100%",
|
|
1376
1508
|
left: 0,
|
|
1377
1509
|
right: 0,
|
|
1378
1510
|
marginTop: theme.spacing.xs,
|
|
@@ -1380,15 +1512,15 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1380
1512
|
border: `1px solid ${theme.colors.border}`,
|
|
1381
1513
|
borderRadius: theme.borderRadius,
|
|
1382
1514
|
boxShadow: theme.boxShadow,
|
|
1383
|
-
maxHeight:
|
|
1384
|
-
overflowY:
|
|
1515
|
+
maxHeight: "300px",
|
|
1516
|
+
overflowY: "auto",
|
|
1385
1517
|
zIndex: 1000,
|
|
1386
1518
|
} }, displayMessages.map((message) => (React.createElement("div", { key: message.id, style: {
|
|
1387
1519
|
padding: theme.spacing.md,
|
|
1388
1520
|
borderBottom: `1px solid ${theme.colors.border}`,
|
|
1389
1521
|
fontSize: theme.fontSizes.sm,
|
|
1390
1522
|
color: theme.colors.text,
|
|
1391
|
-
cursor:
|
|
1523
|
+
cursor: "pointer",
|
|
1392
1524
|
}, onMouseEnter: (e) => {
|
|
1393
1525
|
e.currentTarget.style.backgroundColor = theme.colors.surface;
|
|
1394
1526
|
}, onMouseLeave: (e) => {
|
|
@@ -1397,47 +1529,47 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = 'Se
|
|
|
1397
1529
|
}
|
|
1398
1530
|
|
|
1399
1531
|
const darkTheme = {
|
|
1400
|
-
name:
|
|
1532
|
+
name: "dark",
|
|
1401
1533
|
colors: {
|
|
1402
|
-
primary:
|
|
1403
|
-
secondary:
|
|
1404
|
-
background:
|
|
1405
|
-
surface:
|
|
1406
|
-
text:
|
|
1407
|
-
textSecondary:
|
|
1408
|
-
border:
|
|
1409
|
-
error:
|
|
1410
|
-
success:
|
|
1534
|
+
primary: "#90caf9",
|
|
1535
|
+
secondary: "#f48fb1",
|
|
1536
|
+
background: "#121212",
|
|
1537
|
+
surface: "#1e1e1e",
|
|
1538
|
+
text: "#ffffff",
|
|
1539
|
+
textSecondary: "#b0b0b0",
|
|
1540
|
+
border: "#333333",
|
|
1541
|
+
error: "#f44336",
|
|
1542
|
+
success: "#4caf50",
|
|
1411
1543
|
},
|
|
1412
1544
|
spacing: {
|
|
1413
|
-
xs:
|
|
1414
|
-
sm:
|
|
1415
|
-
md:
|
|
1416
|
-
lg:
|
|
1417
|
-
xl:
|
|
1545
|
+
xs: "4px",
|
|
1546
|
+
sm: "8px",
|
|
1547
|
+
md: "16px",
|
|
1548
|
+
lg: "24px",
|
|
1549
|
+
xl: "32px",
|
|
1418
1550
|
},
|
|
1419
1551
|
fontSizes: {
|
|
1420
|
-
xs:
|
|
1421
|
-
sm:
|
|
1422
|
-
md:
|
|
1423
|
-
lg:
|
|
1424
|
-
xl:
|
|
1552
|
+
xs: "12px",
|
|
1553
|
+
sm: "14px",
|
|
1554
|
+
md: "16px",
|
|
1555
|
+
lg: "18px",
|
|
1556
|
+
xl: "24px",
|
|
1425
1557
|
},
|
|
1426
|
-
borderRadius:
|
|
1427
|
-
boxShadow:
|
|
1558
|
+
borderRadius: "8px",
|
|
1559
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.5)",
|
|
1428
1560
|
};
|
|
1429
1561
|
|
|
1430
1562
|
function getTheme(mode, customTheme) {
|
|
1431
|
-
if (mode ===
|
|
1563
|
+
if (mode === "custom" && customTheme) {
|
|
1432
1564
|
return customTheme;
|
|
1433
1565
|
}
|
|
1434
|
-
return mode ===
|
|
1566
|
+
return mode === "dark" ? darkTheme : lightTheme;
|
|
1435
1567
|
}
|
|
1436
1568
|
function createCustomTheme(baseTheme, overrides) {
|
|
1437
1569
|
return Object.assign(Object.assign(Object.assign({}, baseTheme), overrides), { colors: Object.assign(Object.assign({}, baseTheme.colors), (overrides.colors || {})), spacing: Object.assign(Object.assign({}, baseTheme.spacing), (overrides.spacing || {})), fontSizes: Object.assign(Object.assign({}, baseTheme.fontSizes), (overrides.fontSizes || {})) });
|
|
1438
1570
|
}
|
|
1439
1571
|
|
|
1440
|
-
const DEFAULT_BASE_URL =
|
|
1572
|
+
const DEFAULT_BASE_URL = "https://react-bridge-api-14089992360.northamerica-northeast2.run.app";
|
|
1441
1573
|
class AnalyticsAPI {
|
|
1442
1574
|
constructor(config) {
|
|
1443
1575
|
this.config = config;
|
|
@@ -1450,8 +1582,8 @@ class AnalyticsAPI {
|
|
|
1450
1582
|
const baseURL = this.config.baseURL || DEFAULT_BASE_URL;
|
|
1451
1583
|
const url = `${baseURL}/api/analytics/configs`;
|
|
1452
1584
|
const response = yield fetch(url, {
|
|
1453
|
-
method:
|
|
1454
|
-
headers: Object.assign({
|
|
1585
|
+
method: "GET",
|
|
1586
|
+
headers: Object.assign({ "X-API-Key": this.config.apiKey }, this.config.headers),
|
|
1455
1587
|
});
|
|
1456
1588
|
if (!response.ok) {
|
|
1457
1589
|
throw new Error(`Failed to fetch analytics configs: ${response.statusText}`);
|
|
@@ -1467,8 +1599,8 @@ class AnalyticsAPI {
|
|
|
1467
1599
|
const baseURL = this.config.baseURL || DEFAULT_BASE_URL;
|
|
1468
1600
|
const url = `${baseURL}/api/analytics/${analyticsType}/latest`;
|
|
1469
1601
|
const response = yield fetch(url, {
|
|
1470
|
-
method:
|
|
1471
|
-
headers: Object.assign({
|
|
1602
|
+
method: "GET",
|
|
1603
|
+
headers: Object.assign({ "X-API-Key": this.config.apiKey, "Content-Type": "application/json" }, this.config.headers),
|
|
1472
1604
|
});
|
|
1473
1605
|
if (!response.ok) {
|
|
1474
1606
|
if (response.status === 404) {
|
|
@@ -1487,8 +1619,8 @@ class AnalyticsAPI {
|
|
|
1487
1619
|
const baseURL = this.config.baseURL || DEFAULT_BASE_URL;
|
|
1488
1620
|
const url = `${baseURL}/api/analytics/directives/${directiveId}/status`;
|
|
1489
1621
|
const response = yield fetch(url, {
|
|
1490
|
-
method:
|
|
1491
|
-
headers: Object.assign({
|
|
1622
|
+
method: "PATCH",
|
|
1623
|
+
headers: Object.assign({ "X-API-Key": this.config.apiKey, "Content-Type": "application/json" }, this.config.headers),
|
|
1492
1624
|
body: JSON.stringify({ status }),
|
|
1493
1625
|
});
|
|
1494
1626
|
if (!response.ok) {
|
|
@@ -1564,14 +1696,14 @@ function useAnalyticsResult(analyticsType) {
|
|
|
1564
1696
|
};
|
|
1565
1697
|
}
|
|
1566
1698
|
|
|
1567
|
-
const MetricsPanel = ({ metrics, theme }) => {
|
|
1699
|
+
const MetricsPanel = ({ metrics, theme, }) => {
|
|
1568
1700
|
const getStatusColor = (status) => {
|
|
1569
1701
|
switch (status) {
|
|
1570
|
-
case
|
|
1702
|
+
case "ok":
|
|
1571
1703
|
return theme.colors.success;
|
|
1572
|
-
case
|
|
1573
|
-
return
|
|
1574
|
-
case
|
|
1704
|
+
case "warning":
|
|
1705
|
+
return "#FFA500"; // Orange
|
|
1706
|
+
case "critical":
|
|
1575
1707
|
return theme.colors.error;
|
|
1576
1708
|
default:
|
|
1577
1709
|
return theme.colors.textSecondary;
|
|
@@ -1585,8 +1717,8 @@ const MetricsPanel = ({ metrics, theme }) => {
|
|
|
1585
1717
|
color: theme.colors.text,
|
|
1586
1718
|
} }, "Metrics"),
|
|
1587
1719
|
React.createElement("div", { style: {
|
|
1588
|
-
display:
|
|
1589
|
-
gridTemplateColumns:
|
|
1720
|
+
display: "grid",
|
|
1721
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
|
1590
1722
|
gap: theme.spacing.md,
|
|
1591
1723
|
} }, metrics.map((metric, index) => (React.createElement("div", { key: index, style: {
|
|
1592
1724
|
padding: theme.spacing.md,
|
|
@@ -1595,28 +1727,28 @@ const MetricsPanel = ({ metrics, theme }) => {
|
|
|
1595
1727
|
border: `2px solid ${getStatusColor(metric.status)}`,
|
|
1596
1728
|
} },
|
|
1597
1729
|
React.createElement("div", { style: {
|
|
1598
|
-
display:
|
|
1599
|
-
justifyContent:
|
|
1600
|
-
alignItems:
|
|
1730
|
+
display: "flex",
|
|
1731
|
+
justifyContent: "space-between",
|
|
1732
|
+
alignItems: "center",
|
|
1601
1733
|
marginBottom: theme.spacing.xs,
|
|
1602
1734
|
} },
|
|
1603
1735
|
React.createElement("span", { style: {
|
|
1604
1736
|
fontSize: theme.fontSizes.sm,
|
|
1605
1737
|
color: theme.colors.textSecondary,
|
|
1606
|
-
textTransform:
|
|
1607
|
-
letterSpacing:
|
|
1608
|
-
} }, metric.name.replace(/_/g,
|
|
1738
|
+
textTransform: "uppercase",
|
|
1739
|
+
letterSpacing: "0.5px",
|
|
1740
|
+
} }, metric.name.replace(/_/g, " ")),
|
|
1609
1741
|
React.createElement("span", { style: {
|
|
1610
1742
|
fontSize: theme.fontSizes.xs,
|
|
1611
|
-
padding:
|
|
1612
|
-
borderRadius:
|
|
1743
|
+
padding: "2px 8px",
|
|
1744
|
+
borderRadius: "12px",
|
|
1613
1745
|
backgroundColor: getStatusColor(metric.status),
|
|
1614
|
-
color:
|
|
1615
|
-
textTransform:
|
|
1746
|
+
color: "#fff",
|
|
1747
|
+
textTransform: "uppercase",
|
|
1616
1748
|
} }, metric.status)),
|
|
1617
1749
|
React.createElement("div", { style: {
|
|
1618
1750
|
fontSize: theme.fontSizes.xl,
|
|
1619
|
-
fontWeight:
|
|
1751
|
+
fontWeight: "bold",
|
|
1620
1752
|
color: theme.colors.text,
|
|
1621
1753
|
marginBottom: theme.spacing.xs,
|
|
1622
1754
|
} },
|
|
@@ -1632,11 +1764,11 @@ const MetricsPanel = ({ metrics, theme }) => {
|
|
|
1632
1764
|
const ObservationsPanel = ({ observations, theme, }) => {
|
|
1633
1765
|
const getSeverityColor = (severity) => {
|
|
1634
1766
|
switch (severity) {
|
|
1635
|
-
case
|
|
1767
|
+
case "low":
|
|
1636
1768
|
return theme.colors.success;
|
|
1637
|
-
case
|
|
1638
|
-
return
|
|
1639
|
-
case
|
|
1769
|
+
case "medium":
|
|
1770
|
+
return "#FFA500"; // Orange
|
|
1771
|
+
case "high":
|
|
1640
1772
|
return theme.colors.error;
|
|
1641
1773
|
default:
|
|
1642
1774
|
return theme.colors.textSecondary;
|
|
@@ -1649,7 +1781,11 @@ const ObservationsPanel = ({ observations, theme, }) => {
|
|
|
1649
1781
|
fontSize: theme.fontSizes.lg,
|
|
1650
1782
|
color: theme.colors.text,
|
|
1651
1783
|
} }, "Observations"),
|
|
1652
|
-
React.createElement("div", { style: {
|
|
1784
|
+
React.createElement("div", { style: {
|
|
1785
|
+
display: "flex",
|
|
1786
|
+
flexDirection: "column",
|
|
1787
|
+
gap: theme.spacing.md,
|
|
1788
|
+
} }, observations.map((observation, index) => (React.createElement("div", { key: index, style: {
|
|
1653
1789
|
padding: theme.spacing.md,
|
|
1654
1790
|
backgroundColor: theme.colors.surface,
|
|
1655
1791
|
borderRadius: theme.borderRadius,
|
|
@@ -1657,18 +1793,18 @@ const ObservationsPanel = ({ observations, theme, }) => {
|
|
|
1657
1793
|
borderLeft: `4px solid ${getSeverityColor(observation.severity)}`,
|
|
1658
1794
|
} },
|
|
1659
1795
|
React.createElement("div", { style: {
|
|
1660
|
-
display:
|
|
1661
|
-
justifyContent:
|
|
1662
|
-
alignItems:
|
|
1796
|
+
display: "flex",
|
|
1797
|
+
justifyContent: "space-between",
|
|
1798
|
+
alignItems: "center",
|
|
1663
1799
|
marginBottom: theme.spacing.xs,
|
|
1664
1800
|
} },
|
|
1665
1801
|
React.createElement("span", { style: {
|
|
1666
1802
|
fontSize: theme.fontSizes.xs,
|
|
1667
|
-
padding:
|
|
1668
|
-
borderRadius:
|
|
1803
|
+
padding: "2px 8px",
|
|
1804
|
+
borderRadius: "12px",
|
|
1669
1805
|
backgroundColor: getSeverityColor(observation.severity),
|
|
1670
|
-
color:
|
|
1671
|
-
textTransform:
|
|
1806
|
+
color: "#fff",
|
|
1807
|
+
textTransform: "uppercase",
|
|
1672
1808
|
} }, observation.severity),
|
|
1673
1809
|
React.createElement("span", { style: {
|
|
1674
1810
|
fontSize: theme.fontSizes.xs,
|
|
@@ -1677,7 +1813,7 @@ const ObservationsPanel = ({ observations, theme, }) => {
|
|
|
1677
1813
|
React.createElement("div", { style: {
|
|
1678
1814
|
fontSize: theme.fontSizes.md,
|
|
1679
1815
|
color: theme.colors.text,
|
|
1680
|
-
lineHeight:
|
|
1816
|
+
lineHeight: "1.5",
|
|
1681
1817
|
} }, observation.text)))))));
|
|
1682
1818
|
};
|
|
1683
1819
|
|
|
@@ -1695,19 +1831,19 @@ function useDirectiveAction(onDirectiveAction) {
|
|
|
1695
1831
|
const result = yield onDirectiveAction(directive, action);
|
|
1696
1832
|
if (result.success) {
|
|
1697
1833
|
// Update status to 'executed' if action succeeded
|
|
1698
|
-
yield api.updateDirectiveStatus(directive.directiveId,
|
|
1834
|
+
yield api.updateDirectiveStatus(directive.directiveId, "executed");
|
|
1699
1835
|
}
|
|
1700
1836
|
else {
|
|
1701
1837
|
// Update status to 'failed' if action failed
|
|
1702
|
-
yield api.updateDirectiveStatus(directive.directiveId,
|
|
1703
|
-
throw new Error(result.error ||
|
|
1838
|
+
yield api.updateDirectiveStatus(directive.directiveId, "failed");
|
|
1839
|
+
throw new Error(result.error || "Directive execution failed");
|
|
1704
1840
|
}
|
|
1705
1841
|
return { success: true };
|
|
1706
1842
|
}
|
|
1707
1843
|
else {
|
|
1708
1844
|
// No callback provided, just update status to 'declined'
|
|
1709
|
-
if (action ===
|
|
1710
|
-
yield api.updateDirectiveStatus(directive.directiveId,
|
|
1845
|
+
if (action === "decline") {
|
|
1846
|
+
yield api.updateDirectiveStatus(directive.directiveId, "declined");
|
|
1711
1847
|
}
|
|
1712
1848
|
return { success: true };
|
|
1713
1849
|
}
|
|
@@ -1734,28 +1870,28 @@ const DirectivesPanel = ({ directives, theme, onDirectiveAction, onUpdate, }) =>
|
|
|
1734
1870
|
if (priority >= 3)
|
|
1735
1871
|
return theme.colors.error; // High/Critical
|
|
1736
1872
|
if (priority === 2)
|
|
1737
|
-
return
|
|
1873
|
+
return "#FFA500"; // Medium
|
|
1738
1874
|
return theme.colors.success; // Low
|
|
1739
1875
|
};
|
|
1740
1876
|
const getPriorityLabel = (priority) => {
|
|
1741
1877
|
if (priority >= 3)
|
|
1742
|
-
return
|
|
1878
|
+
return "Critical";
|
|
1743
1879
|
if (priority === 2)
|
|
1744
|
-
return
|
|
1880
|
+
return "High";
|
|
1745
1881
|
if (priority === 1)
|
|
1746
|
-
return
|
|
1747
|
-
return
|
|
1882
|
+
return "Medium";
|
|
1883
|
+
return "Low";
|
|
1748
1884
|
};
|
|
1749
1885
|
const getStatusLabel = (status) => {
|
|
1750
1886
|
switch (status) {
|
|
1751
|
-
case
|
|
1752
|
-
return
|
|
1753
|
-
case
|
|
1754
|
-
return
|
|
1755
|
-
case
|
|
1756
|
-
return
|
|
1757
|
-
case
|
|
1758
|
-
return
|
|
1887
|
+
case "proposed":
|
|
1888
|
+
return "Pending";
|
|
1889
|
+
case "executed":
|
|
1890
|
+
return "Executed ✓";
|
|
1891
|
+
case "declined":
|
|
1892
|
+
return "Declined";
|
|
1893
|
+
case "failed":
|
|
1894
|
+
return "Failed ✗";
|
|
1759
1895
|
default:
|
|
1760
1896
|
return status;
|
|
1761
1897
|
}
|
|
@@ -1775,8 +1911,12 @@ const DirectivesPanel = ({ directives, theme, onDirectiveAction, onUpdate, }) =>
|
|
|
1775
1911
|
fontSize: theme.fontSizes.lg,
|
|
1776
1912
|
color: theme.colors.text,
|
|
1777
1913
|
} }, "Directives"),
|
|
1778
|
-
React.createElement("div", { style: {
|
|
1779
|
-
|
|
1914
|
+
React.createElement("div", { style: {
|
|
1915
|
+
display: "flex",
|
|
1916
|
+
flexDirection: "column",
|
|
1917
|
+
gap: theme.spacing.md,
|
|
1918
|
+
} }, directives.map((directive) => {
|
|
1919
|
+
const isDisabled = directive.status !== "proposed" ||
|
|
1780
1920
|
isProcessing ||
|
|
1781
1921
|
processingId === directive.directiveId;
|
|
1782
1922
|
return (React.createElement("div", { key: directive.directiveId, style: {
|
|
@@ -1786,32 +1926,32 @@ const DirectivesPanel = ({ directives, theme, onDirectiveAction, onUpdate, }) =>
|
|
|
1786
1926
|
border: `1px solid ${theme.colors.border}`,
|
|
1787
1927
|
} },
|
|
1788
1928
|
React.createElement("div", { style: {
|
|
1789
|
-
display:
|
|
1790
|
-
justifyContent:
|
|
1791
|
-
alignItems:
|
|
1929
|
+
display: "flex",
|
|
1930
|
+
justifyContent: "space-between",
|
|
1931
|
+
alignItems: "center",
|
|
1792
1932
|
marginBottom: theme.spacing.sm,
|
|
1793
1933
|
} },
|
|
1794
1934
|
React.createElement("span", { style: {
|
|
1795
1935
|
fontSize: theme.fontSizes.xs,
|
|
1796
|
-
padding:
|
|
1797
|
-
borderRadius:
|
|
1936
|
+
padding: "2px 8px",
|
|
1937
|
+
borderRadius: "12px",
|
|
1798
1938
|
backgroundColor: getPriorityColor(directive.priority),
|
|
1799
|
-
color:
|
|
1800
|
-
textTransform:
|
|
1939
|
+
color: "#fff",
|
|
1940
|
+
textTransform: "uppercase",
|
|
1801
1941
|
} }, getPriorityLabel(directive.priority)),
|
|
1802
1942
|
React.createElement("span", { style: {
|
|
1803
1943
|
fontSize: theme.fontSizes.xs,
|
|
1804
|
-
padding:
|
|
1805
|
-
borderRadius:
|
|
1944
|
+
padding: "2px 8px",
|
|
1945
|
+
borderRadius: "12px",
|
|
1806
1946
|
backgroundColor: theme.colors.textSecondary,
|
|
1807
|
-
color:
|
|
1947
|
+
color: "#fff",
|
|
1808
1948
|
} }, getStatusLabel(directive.status))),
|
|
1809
1949
|
React.createElement("div", { style: {
|
|
1810
1950
|
fontSize: theme.fontSizes.lg,
|
|
1811
|
-
fontWeight:
|
|
1951
|
+
fontWeight: "bold",
|
|
1812
1952
|
color: theme.colors.text,
|
|
1813
1953
|
marginBottom: theme.spacing.xs,
|
|
1814
|
-
} }, directive.action.replace(/_/g,
|
|
1954
|
+
} }, directive.action.replace(/_/g, " ").toUpperCase()),
|
|
1815
1955
|
directive.targets.length > 0 && (React.createElement("div", { style: {
|
|
1816
1956
|
fontSize: theme.fontSizes.sm,
|
|
1817
1957
|
color: theme.colors.textSecondary,
|
|
@@ -1819,7 +1959,7 @@ const DirectivesPanel = ({ directives, theme, onDirectiveAction, onUpdate, }) =>
|
|
|
1819
1959
|
} },
|
|
1820
1960
|
React.createElement("strong", null, "Targets:"),
|
|
1821
1961
|
" ",
|
|
1822
|
-
directive.targets.join(
|
|
1962
|
+
directive.targets.join(", "))),
|
|
1823
1963
|
directive.parameters.length > 0 && (React.createElement("div", { style: {
|
|
1824
1964
|
marginBottom: theme.spacing.sm,
|
|
1825
1965
|
padding: theme.spacing.sm,
|
|
@@ -1828,7 +1968,7 @@ const DirectivesPanel = ({ directives, theme, onDirectiveAction, onUpdate, }) =>
|
|
|
1828
1968
|
} }, directive.parameters.map((param, index) => (React.createElement("div", { key: index, style: {
|
|
1829
1969
|
fontSize: theme.fontSizes.sm,
|
|
1830
1970
|
color: theme.colors.text,
|
|
1831
|
-
marginBottom:
|
|
1971
|
+
marginBottom: "4px",
|
|
1832
1972
|
} },
|
|
1833
1973
|
React.createElement("strong", null,
|
|
1834
1974
|
param.name,
|
|
@@ -1842,35 +1982,37 @@ const DirectivesPanel = ({ directives, theme, onDirectiveAction, onUpdate, }) =>
|
|
|
1842
1982
|
fontSize: theme.fontSizes.sm,
|
|
1843
1983
|
color: theme.colors.textSecondary,
|
|
1844
1984
|
marginBottom: theme.spacing.md,
|
|
1845
|
-
lineHeight:
|
|
1985
|
+
lineHeight: "1.5",
|
|
1846
1986
|
} }, directive.rationale),
|
|
1847
|
-
directive.status ===
|
|
1848
|
-
React.createElement("button", { onClick: () => handleDirectiveAction(directive,
|
|
1987
|
+
directive.status === "proposed" && (React.createElement("div", { style: { display: "flex", gap: theme.spacing.sm } },
|
|
1988
|
+
React.createElement("button", { onClick: () => handleDirectiveAction(directive, "execute"), disabled: isDisabled, style: {
|
|
1849
1989
|
flex: 1,
|
|
1850
1990
|
padding: theme.spacing.sm,
|
|
1851
1991
|
backgroundColor: isDisabled
|
|
1852
1992
|
? theme.colors.textSecondary
|
|
1853
1993
|
: theme.colors.success,
|
|
1854
|
-
color:
|
|
1855
|
-
border:
|
|
1994
|
+
color: "#fff",
|
|
1995
|
+
border: "none",
|
|
1856
1996
|
borderRadius: theme.borderRadius,
|
|
1857
|
-
cursor: isDisabled ?
|
|
1997
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
1858
1998
|
fontSize: theme.fontSizes.sm,
|
|
1859
|
-
fontWeight:
|
|
1999
|
+
fontWeight: "bold",
|
|
1860
2000
|
opacity: isDisabled ? 0.6 : 1,
|
|
1861
|
-
} }, processingId === directive.directiveId
|
|
1862
|
-
|
|
2001
|
+
} }, processingId === directive.directiveId
|
|
2002
|
+
? "Processing..."
|
|
2003
|
+
: "Execute"),
|
|
2004
|
+
React.createElement("button", { onClick: () => handleDirectiveAction(directive, "decline"), disabled: isDisabled, style: {
|
|
1863
2005
|
flex: 1,
|
|
1864
2006
|
padding: theme.spacing.sm,
|
|
1865
2007
|
backgroundColor: isDisabled
|
|
1866
2008
|
? theme.colors.textSecondary
|
|
1867
2009
|
: theme.colors.error,
|
|
1868
|
-
color:
|
|
1869
|
-
border:
|
|
2010
|
+
color: "#fff",
|
|
2011
|
+
border: "none",
|
|
1870
2012
|
borderRadius: theme.borderRadius,
|
|
1871
|
-
cursor: isDisabled ?
|
|
2013
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
1872
2014
|
fontSize: theme.fontSizes.sm,
|
|
1873
|
-
fontWeight:
|
|
2015
|
+
fontWeight: "bold",
|
|
1874
2016
|
opacity: isDisabled ? 0.6 : 1,
|
|
1875
2017
|
} }, "Decline")))));
|
|
1876
2018
|
}))));
|
|
@@ -1896,53 +2038,57 @@ const AnalyticsReport = ({ analyticsType, theme: customTheme, onDirectiveAction,
|
|
|
1896
2038
|
// Use a default theme if none provided
|
|
1897
2039
|
const theme = customTheme || {
|
|
1898
2040
|
colors: {
|
|
1899
|
-
background:
|
|
1900
|
-
surface:
|
|
1901
|
-
text:
|
|
1902
|
-
textSecondary:
|
|
1903
|
-
border:
|
|
1904
|
-
primary:
|
|
1905
|
-
success:
|
|
1906
|
-
error:
|
|
1907
|
-
secondary:
|
|
2041
|
+
background: "#ffffff",
|
|
2042
|
+
surface: "#f5f5f5",
|
|
2043
|
+
text: "#333333",
|
|
2044
|
+
textSecondary: "#666666",
|
|
2045
|
+
border: "#e0e0e0",
|
|
2046
|
+
primary: "#007bff",
|
|
2047
|
+
success: "#28a745",
|
|
2048
|
+
error: "#dc3545",
|
|
2049
|
+
secondary: "#6c757d",
|
|
1908
2050
|
},
|
|
1909
2051
|
spacing: {
|
|
1910
|
-
xs:
|
|
1911
|
-
sm:
|
|
1912
|
-
md:
|
|
1913
|
-
lg:
|
|
1914
|
-
xl:
|
|
2052
|
+
xs: "4px",
|
|
2053
|
+
sm: "8px",
|
|
2054
|
+
md: "16px",
|
|
2055
|
+
lg: "24px",
|
|
2056
|
+
xl: "32px",
|
|
1915
2057
|
},
|
|
1916
2058
|
fontSizes: {
|
|
1917
|
-
xs:
|
|
1918
|
-
sm:
|
|
1919
|
-
md:
|
|
1920
|
-
lg:
|
|
1921
|
-
xl:
|
|
2059
|
+
xs: "12px",
|
|
2060
|
+
sm: "14px",
|
|
2061
|
+
md: "16px",
|
|
2062
|
+
lg: "20px",
|
|
2063
|
+
xl: "24px",
|
|
1922
2064
|
},
|
|
1923
|
-
borderRadius:
|
|
1924
|
-
boxShadow:
|
|
1925
|
-
name:
|
|
2065
|
+
borderRadius: "8px",
|
|
2066
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
|
|
2067
|
+
name: "default",
|
|
1926
2068
|
};
|
|
1927
2069
|
return (React.createElement("div", { style: {
|
|
1928
|
-
height:
|
|
1929
|
-
display:
|
|
1930
|
-
flexDirection:
|
|
2070
|
+
height: "100%",
|
|
2071
|
+
display: "flex",
|
|
2072
|
+
flexDirection: "column",
|
|
1931
2073
|
backgroundColor: theme.colors.background,
|
|
1932
2074
|
} },
|
|
1933
2075
|
React.createElement("div", { style: {
|
|
1934
2076
|
padding: theme.spacing.lg,
|
|
1935
2077
|
borderBottom: `1px solid ${theme.colors.border}`,
|
|
1936
|
-
display:
|
|
1937
|
-
justifyContent:
|
|
1938
|
-
alignItems:
|
|
2078
|
+
display: "flex",
|
|
2079
|
+
justifyContent: "space-between",
|
|
2080
|
+
alignItems: "center",
|
|
1939
2081
|
} },
|
|
1940
|
-
React.createElement("div", { style: {
|
|
2082
|
+
React.createElement("div", { style: {
|
|
2083
|
+
display: "flex",
|
|
2084
|
+
alignItems: "center",
|
|
2085
|
+
gap: theme.spacing.sm,
|
|
2086
|
+
} },
|
|
1941
2087
|
onBack && (React.createElement("button", { onClick: onBack, style: {
|
|
1942
|
-
background:
|
|
1943
|
-
border:
|
|
1944
|
-
cursor:
|
|
1945
|
-
fontSize:
|
|
2088
|
+
background: "none",
|
|
2089
|
+
border: "none",
|
|
2090
|
+
cursor: "pointer",
|
|
2091
|
+
fontSize: "24px",
|
|
1946
2092
|
color: theme.colors.text,
|
|
1947
2093
|
padding: 0,
|
|
1948
2094
|
} }, BACK_ICON_SVG)),
|
|
@@ -1955,33 +2101,33 @@ const AnalyticsReport = ({ analyticsType, theme: customTheme, onDirectiveAction,
|
|
|
1955
2101
|
result && (React.createElement("div", { style: {
|
|
1956
2102
|
fontSize: theme.fontSizes.xs,
|
|
1957
2103
|
color: theme.colors.textSecondary,
|
|
1958
|
-
marginTop:
|
|
2104
|
+
marginTop: "4px",
|
|
1959
2105
|
} },
|
|
1960
2106
|
"Last updated: ",
|
|
1961
2107
|
new Date(result.createdAt).toLocaleString())))),
|
|
1962
|
-
React.createElement("div", { style: { display:
|
|
2108
|
+
React.createElement("div", { style: { display: "flex", gap: theme.spacing.sm } },
|
|
1963
2109
|
React.createElement("button", { onClick: handleRefresh, disabled: isLoading, style: {
|
|
1964
|
-
background:
|
|
1965
|
-
border:
|
|
1966
|
-
cursor: isLoading ?
|
|
1967
|
-
fontSize:
|
|
2110
|
+
background: "none",
|
|
2111
|
+
border: "none",
|
|
2112
|
+
cursor: isLoading ? "not-allowed" : "pointer",
|
|
2113
|
+
fontSize: "24px",
|
|
1968
2114
|
color: theme.colors.text,
|
|
1969
2115
|
padding: 0,
|
|
1970
2116
|
opacity: isLoading ? 0.5 : 1,
|
|
1971
2117
|
} }, REFRESH_ICON_SVG),
|
|
1972
2118
|
onClose && (React.createElement("button", { onClick: onClose, style: {
|
|
1973
|
-
background:
|
|
1974
|
-
border:
|
|
1975
|
-
cursor:
|
|
1976
|
-
fontSize:
|
|
2119
|
+
background: "none",
|
|
2120
|
+
border: "none",
|
|
2121
|
+
cursor: "pointer",
|
|
2122
|
+
fontSize: "24px",
|
|
1977
2123
|
color: theme.colors.text,
|
|
1978
2124
|
padding: 0,
|
|
1979
2125
|
} }, CLOSE_ICON_SVG$1)))),
|
|
1980
2126
|
React.createElement("div", { style: {
|
|
1981
2127
|
flex: 1,
|
|
1982
|
-
overflowY:
|
|
2128
|
+
overflowY: "auto",
|
|
1983
2129
|
padding: theme.spacing.lg,
|
|
1984
|
-
} }, isLoading ? (React.createElement("div", { style: { textAlign:
|
|
2130
|
+
} }, isLoading ? (React.createElement("div", { style: { textAlign: "center", color: theme.colors.textSecondary } }, "Loading analytics result...")) : error ? (React.createElement("div", { style: {
|
|
1985
2131
|
padding: theme.spacing.md,
|
|
1986
2132
|
backgroundColor: theme.colors.surface,
|
|
1987
2133
|
borderRadius: theme.borderRadius,
|
|
@@ -1989,10 +2135,14 @@ const AnalyticsReport = ({ analyticsType, theme: customTheme, onDirectiveAction,
|
|
|
1989
2135
|
color: theme.colors.error,
|
|
1990
2136
|
} },
|
|
1991
2137
|
"Error: ",
|
|
1992
|
-
error.message)) : result ? (React.createElement("div", { style: {
|
|
2138
|
+
error.message)) : result ? (React.createElement("div", { style: {
|
|
2139
|
+
display: "flex",
|
|
2140
|
+
flexDirection: "column",
|
|
2141
|
+
gap: theme.spacing.xl,
|
|
2142
|
+
} },
|
|
1993
2143
|
result.metrics.length > 0 && (React.createElement(MetricsPanel, { metrics: result.metrics, theme: theme })),
|
|
1994
2144
|
result.observations.length > 0 && (React.createElement(ObservationsPanel, { observations: result.observations, theme: theme })),
|
|
1995
|
-
result.directives.length > 0 && (React.createElement(DirectivesPanel, { directives: result.directives, theme: theme, onDirectiveAction: onDirectiveAction, onUpdate: refetch })))) : (React.createElement("div", { style: { textAlign:
|
|
2145
|
+
result.directives.length > 0 && (React.createElement(DirectivesPanel, { directives: result.directives, theme: theme, onDirectiveAction: onDirectiveAction, onUpdate: refetch })))) : (React.createElement("div", { style: { textAlign: "center", color: theme.colors.textSecondary } }, "No results available")))));
|
|
1996
2146
|
};
|
|
1997
2147
|
|
|
1998
2148
|
// Close Icon SVG
|
|
@@ -2001,32 +2151,32 @@ const CLOSE_ICON_SVG = (React.createElement("svg", { xmlns: "http://www.w3.org/2
|
|
|
2001
2151
|
const AnalyticsDrawer = ({ isOpen, onClose, configs, isLoading, theme, onDirectiveAction, }) => {
|
|
2002
2152
|
const [selectedAnalytics, setSelectedAnalytics] = useState(null);
|
|
2003
2153
|
// Check if mobile
|
|
2004
|
-
const isMobile = typeof window !==
|
|
2154
|
+
const isMobile = typeof window !== "undefined" && window.innerWidth < 768;
|
|
2005
2155
|
const drawerStyles = {
|
|
2006
|
-
position:
|
|
2156
|
+
position: "fixed",
|
|
2007
2157
|
top: 0,
|
|
2008
2158
|
right: 0,
|
|
2009
|
-
width: isMobile ?
|
|
2010
|
-
height:
|
|
2159
|
+
width: isMobile ? "100%" : "400px",
|
|
2160
|
+
height: "100%",
|
|
2011
2161
|
backgroundColor: theme.colors.background,
|
|
2012
2162
|
boxShadow: theme.boxShadow,
|
|
2013
2163
|
zIndex: 1001,
|
|
2014
|
-
display:
|
|
2015
|
-
flexDirection:
|
|
2016
|
-
transform: isOpen ?
|
|
2017
|
-
transition:
|
|
2164
|
+
display: "flex",
|
|
2165
|
+
flexDirection: "column",
|
|
2166
|
+
transform: isOpen ? "translateX(0)" : "translateX(100%)",
|
|
2167
|
+
transition: "transform 0.3s ease",
|
|
2018
2168
|
};
|
|
2019
2169
|
const overlayStyles = {
|
|
2020
|
-
position:
|
|
2170
|
+
position: "fixed",
|
|
2021
2171
|
top: 0,
|
|
2022
2172
|
left: 0,
|
|
2023
|
-
width:
|
|
2024
|
-
height:
|
|
2025
|
-
backgroundColor:
|
|
2173
|
+
width: "100%",
|
|
2174
|
+
height: "100%",
|
|
2175
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2026
2176
|
zIndex: 1000,
|
|
2027
2177
|
opacity: isOpen ? 1 : 0,
|
|
2028
|
-
pointerEvents: isOpen ?
|
|
2029
|
-
transition:
|
|
2178
|
+
pointerEvents: isOpen ? "auto" : "none",
|
|
2179
|
+
transition: "opacity 0.3s ease",
|
|
2030
2180
|
};
|
|
2031
2181
|
if (selectedAnalytics) {
|
|
2032
2182
|
return (React.createElement(React.Fragment, null,
|
|
@@ -2040,9 +2190,9 @@ const AnalyticsDrawer = ({ isOpen, onClose, configs, isLoading, theme, onDirecti
|
|
|
2040
2190
|
React.createElement("div", { style: {
|
|
2041
2191
|
padding: theme.spacing.lg,
|
|
2042
2192
|
borderBottom: `1px solid ${theme.colors.border}`,
|
|
2043
|
-
display:
|
|
2044
|
-
justifyContent:
|
|
2045
|
-
alignItems:
|
|
2193
|
+
display: "flex",
|
|
2194
|
+
justifyContent: "space-between",
|
|
2195
|
+
alignItems: "center",
|
|
2046
2196
|
} },
|
|
2047
2197
|
React.createElement("h2", { style: {
|
|
2048
2198
|
margin: 0,
|
|
@@ -2050,36 +2200,41 @@ const AnalyticsDrawer = ({ isOpen, onClose, configs, isLoading, theme, onDirecti
|
|
|
2050
2200
|
color: theme.colors.text,
|
|
2051
2201
|
} }, "Analytics"),
|
|
2052
2202
|
React.createElement("button", { onClick: onClose, style: {
|
|
2053
|
-
background:
|
|
2054
|
-
border:
|
|
2055
|
-
cursor:
|
|
2056
|
-
fontSize:
|
|
2203
|
+
background: "none",
|
|
2204
|
+
border: "none",
|
|
2205
|
+
cursor: "pointer",
|
|
2206
|
+
fontSize: "24px",
|
|
2057
2207
|
color: theme.colors.text,
|
|
2058
2208
|
padding: 0,
|
|
2059
2209
|
} }, CLOSE_ICON_SVG)),
|
|
2060
2210
|
React.createElement("div", { style: {
|
|
2061
2211
|
flex: 1,
|
|
2062
|
-
overflowY:
|
|
2212
|
+
overflowY: "auto",
|
|
2063
2213
|
padding: theme.spacing.lg,
|
|
2064
|
-
} }, isLoading ? (React.createElement("div", { style: { textAlign:
|
|
2214
|
+
} }, isLoading ? (React.createElement("div", { style: { textAlign: "center", color: theme.colors.textSecondary } }, "Loading analytics...")) : configs.length === 0 ? (React.createElement("div", { style: { textAlign: "center", color: theme.colors.textSecondary } }, "No analytics configured")) : (React.createElement("div", { style: {
|
|
2215
|
+
display: "flex",
|
|
2216
|
+
flexDirection: "column",
|
|
2217
|
+
gap: theme.spacing.md,
|
|
2218
|
+
} }, configs.map((config) => (React.createElement("div", { key: config.configId, onClick: () => config.isEnabled &&
|
|
2219
|
+
setSelectedAnalytics(config.analyticsType), style: {
|
|
2065
2220
|
padding: theme.spacing.md,
|
|
2066
2221
|
backgroundColor: theme.colors.surface,
|
|
2067
2222
|
borderRadius: theme.borderRadius,
|
|
2068
2223
|
border: `1px solid ${theme.colors.border}`,
|
|
2069
|
-
cursor: config.isEnabled ?
|
|
2224
|
+
cursor: config.isEnabled ? "pointer" : "not-allowed",
|
|
2070
2225
|
opacity: config.isEnabled ? 1 : 0.6,
|
|
2071
|
-
transition:
|
|
2226
|
+
transition: "transform 0.2s ease",
|
|
2072
2227
|
}, onMouseEnter: (e) => {
|
|
2073
2228
|
if (config.isEnabled) {
|
|
2074
|
-
e.currentTarget.style.transform =
|
|
2229
|
+
e.currentTarget.style.transform = "translateX(5px)";
|
|
2075
2230
|
}
|
|
2076
2231
|
}, onMouseLeave: (e) => {
|
|
2077
|
-
e.currentTarget.style.transform =
|
|
2232
|
+
e.currentTarget.style.transform = "translateX(0)";
|
|
2078
2233
|
} },
|
|
2079
2234
|
React.createElement("div", { style: {
|
|
2080
|
-
display:
|
|
2081
|
-
justifyContent:
|
|
2082
|
-
alignItems:
|
|
2235
|
+
display: "flex",
|
|
2236
|
+
justifyContent: "space-between",
|
|
2237
|
+
alignItems: "center",
|
|
2083
2238
|
marginBottom: theme.spacing.xs,
|
|
2084
2239
|
} },
|
|
2085
2240
|
React.createElement("h3", { style: {
|
|
@@ -2089,13 +2244,13 @@ const AnalyticsDrawer = ({ isOpen, onClose, configs, isLoading, theme, onDirecti
|
|
|
2089
2244
|
} }, config.analyticsType),
|
|
2090
2245
|
React.createElement("span", { style: {
|
|
2091
2246
|
fontSize: theme.fontSizes.xs,
|
|
2092
|
-
padding:
|
|
2093
|
-
borderRadius:
|
|
2247
|
+
padding: "2px 8px",
|
|
2248
|
+
borderRadius: "12px",
|
|
2094
2249
|
backgroundColor: config.isEnabled
|
|
2095
2250
|
? theme.colors.success
|
|
2096
2251
|
: theme.colors.textSecondary,
|
|
2097
|
-
color:
|
|
2098
|
-
} }, config.isEnabled ?
|
|
2252
|
+
color: "#fff",
|
|
2253
|
+
} }, config.isEnabled ? "Enabled" : "Disabled")),
|
|
2099
2254
|
React.createElement("p", { style: {
|
|
2100
2255
|
margin: 0,
|
|
2101
2256
|
fontSize: theme.fontSizes.sm,
|
|
@@ -2110,43 +2265,44 @@ const AnalyticsDrawer = ({ isOpen, onClose, configs, isLoading, theme, onDirecti
|
|
|
2110
2265
|
"Frequency: ",
|
|
2111
2266
|
config.frequency),
|
|
2112
2267
|
config.lastExecutedAt && (React.createElement("div", null,
|
|
2113
|
-
"Last run:
|
|
2268
|
+
"Last run:",
|
|
2269
|
+
" ",
|
|
2114
2270
|
new Date(config.lastExecutedAt).toLocaleString()))))))))))));
|
|
2115
2271
|
};
|
|
2116
2272
|
|
|
2117
2273
|
// Analytics Icon SVG
|
|
2118
2274
|
const ANALYTICS_ICON_SVG = (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "currentColor" },
|
|
2119
2275
|
React.createElement("path", { d: "M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z" })));
|
|
2120
|
-
const AnalyticsWidget = ({ position =
|
|
2276
|
+
const AnalyticsWidget = ({ position = "bottom-right", theme: customTheme, onDirectiveAction, }) => {
|
|
2121
2277
|
const { theme: contextTheme } = useReactBridgeContext();
|
|
2122
2278
|
const theme = customTheme || contextTheme;
|
|
2123
2279
|
const { configs, isLoading } = useAnalyticsConfigs();
|
|
2124
2280
|
const [isOpen, setIsOpen] = useState(false);
|
|
2125
2281
|
const enabledCount = configs.filter((c) => c.isEnabled).length;
|
|
2126
|
-
const positionStyles = position ===
|
|
2282
|
+
const positionStyles = position === "bottom-right"
|
|
2127
2283
|
? { right: theme.spacing.lg, bottom: theme.spacing.lg }
|
|
2128
2284
|
: { left: theme.spacing.lg, bottom: theme.spacing.lg };
|
|
2129
2285
|
return (React.createElement(React.Fragment, null,
|
|
2130
|
-
React.createElement("button", { onClick: () => setIsOpen(!isOpen), style: Object.assign(Object.assign({ position:
|
|
2131
|
-
e.currentTarget.style.transform =
|
|
2286
|
+
React.createElement("button", { onClick: () => setIsOpen(!isOpen), style: Object.assign(Object.assign({ position: "fixed" }, positionStyles), { width: "60px", height: "60px", borderRadius: "50%", backgroundColor: theme.colors.primary, color: "#fff", border: "none", boxShadow: theme.boxShadow, cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "24px", zIndex: 1000, transition: "transform 0.2s ease" }), onMouseEnter: (e) => {
|
|
2287
|
+
e.currentTarget.style.transform = "scale(1.1)";
|
|
2132
2288
|
}, onMouseLeave: (e) => {
|
|
2133
|
-
e.currentTarget.style.transform =
|
|
2289
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
2134
2290
|
} },
|
|
2135
2291
|
ANALYTICS_ICON_SVG,
|
|
2136
2292
|
!isLoading && enabledCount > 0 && (React.createElement("span", { style: {
|
|
2137
|
-
position:
|
|
2138
|
-
top:
|
|
2139
|
-
right:
|
|
2293
|
+
position: "absolute",
|
|
2294
|
+
top: "-5px",
|
|
2295
|
+
right: "-5px",
|
|
2140
2296
|
backgroundColor: theme.colors.error,
|
|
2141
|
-
color:
|
|
2142
|
-
borderRadius:
|
|
2143
|
-
width:
|
|
2144
|
-
height:
|
|
2145
|
-
display:
|
|
2146
|
-
alignItems:
|
|
2147
|
-
justifyContent:
|
|
2148
|
-
fontSize:
|
|
2149
|
-
fontWeight:
|
|
2297
|
+
color: "#fff",
|
|
2298
|
+
borderRadius: "50%",
|
|
2299
|
+
width: "24px",
|
|
2300
|
+
height: "24px",
|
|
2301
|
+
display: "flex",
|
|
2302
|
+
alignItems: "center",
|
|
2303
|
+
justifyContent: "center",
|
|
2304
|
+
fontSize: "12px",
|
|
2305
|
+
fontWeight: "bold",
|
|
2150
2306
|
} }, enabledCount))),
|
|
2151
2307
|
isOpen && (React.createElement(AnalyticsDrawer, { isOpen: isOpen, onClose: () => setIsOpen(false), configs: configs, isLoading: isLoading, theme: theme, onDirectiveAction: onDirectiveAction }))));
|
|
2152
2308
|
};
|
|
@@ -2516,7 +2672,7 @@ function AnalyticsDashboard({ onDirectiveAction, className = "", showRefresh = t
|
|
|
2516
2672
|
"Declined (",
|
|
2517
2673
|
declinedCount,
|
|
2518
2674
|
")")),
|
|
2519
|
-
directiveTypes.length > 1 && (React.createElement("select", { value: directiveType, onChange: e => setDirectiveType(e.target.value), style: {
|
|
2675
|
+
directiveTypes.length > 1 && (React.createElement("select", { value: directiveType, onChange: (e) => setDirectiveType(e.target.value), style: {
|
|
2520
2676
|
padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
|
|
2521
2677
|
borderRadius: theme.borderRadius,
|
|
2522
2678
|
border: `1px solid ${theme.colors.border}`,
|
|
@@ -2530,7 +2686,7 @@ function AnalyticsDashboard({ onDirectiveAction, className = "", showRefresh = t
|
|
|
2530
2686
|
cursor: "pointer",
|
|
2531
2687
|
} },
|
|
2532
2688
|
React.createElement("option", { value: "all" }, "All types"),
|
|
2533
|
-
directiveTypes.map(type => (React.createElement("option", { key: type, value: type }, type)))))),
|
|
2689
|
+
directiveTypes.map((type) => (React.createElement("option", { key: type, value: type }, type)))))),
|
|
2534
2690
|
filteredDirectives.length === 0 && (React.createElement("div", { style: placeholderStyle }, "No directives match the selected filter.")),
|
|
2535
2691
|
React.createElement("div", { style: {
|
|
2536
2692
|
display: "flex",
|
|
@@ -2575,17 +2731,1810 @@ function AnalyticsDashboard({ onDirectiveAction, className = "", showRefresh = t
|
|
|
2575
2731
|
": ",
|
|
2576
2732
|
param.value)))))),
|
|
2577
2733
|
directive.status === "proposed" && (React.createElement("div", { style: { display: "flex", gap: theme.spacing.sm } },
|
|
2578
|
-
React.createElement("button", { type: "button", onClick: () => handleDirectiveAction(directive, "execute"), disabled: isProcessing &&
|
|
2734
|
+
React.createElement("button", { type: "button", onClick: () => handleDirectiveAction(directive, "execute"), disabled: isProcessing &&
|
|
2735
|
+
processingId === directive.directiveId, style: Object.assign(Object.assign({}, pillStyle(theme.colors.success)), { border: "none", cursor: isProcessing &&
|
|
2736
|
+
processingId === directive.directiveId
|
|
2579
2737
|
? "not-allowed"
|
|
2580
|
-
: "pointer" }) }, isProcessing &&
|
|
2738
|
+
: "pointer" }) }, isProcessing &&
|
|
2739
|
+
processingId === directive.directiveId
|
|
2581
2740
|
? "Processing..."
|
|
2582
2741
|
: "Execute"),
|
|
2583
|
-
React.createElement("button", { type: "button", onClick: () => handleDirectiveAction(directive, "decline"), disabled: isProcessing &&
|
|
2742
|
+
React.createElement("button", { type: "button", onClick: () => handleDirectiveAction(directive, "decline"), disabled: isProcessing &&
|
|
2743
|
+
processingId === directive.directiveId, style: Object.assign(Object.assign({}, pillStyle(theme.colors.error)), { border: "none", cursor: isProcessing &&
|
|
2744
|
+
processingId === directive.directiveId
|
|
2584
2745
|
? "not-allowed"
|
|
2585
|
-
: "pointer" }) }, isProcessing &&
|
|
2746
|
+
: "pointer" }) }, isProcessing &&
|
|
2747
|
+
processingId === directive.directiveId
|
|
2586
2748
|
? "Processing..."
|
|
2587
2749
|
: "Decline"))))))))))))));
|
|
2588
2750
|
}
|
|
2589
2751
|
|
|
2590
|
-
|
|
2752
|
+
/**
|
|
2753
|
+
* Request Cache Manager
|
|
2754
|
+
* Implements a simple caching mechanism similar to react-query
|
|
2755
|
+
* Prevents duplicate concurrent requests and caches results
|
|
2756
|
+
*/
|
|
2757
|
+
const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
2758
|
+
class RequestCache {
|
|
2759
|
+
constructor() {
|
|
2760
|
+
this.cache = new Map();
|
|
2761
|
+
this.pendingRequests = new Map();
|
|
2762
|
+
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Generate a cache key from function and parameters
|
|
2765
|
+
*/
|
|
2766
|
+
getCacheKey(fetchFn, params) {
|
|
2767
|
+
const fnString = fetchFn.toString();
|
|
2768
|
+
const paramString = params ? JSON.stringify(params) : '';
|
|
2769
|
+
return `${fnString}::${paramString}`;
|
|
2770
|
+
}
|
|
2771
|
+
/**
|
|
2772
|
+
* Check if cache entry is still valid
|
|
2773
|
+
*/
|
|
2774
|
+
isCacheValid(entry) {
|
|
2775
|
+
return Date.now() - entry.timestamp < entry.ttl;
|
|
2776
|
+
}
|
|
2777
|
+
/**
|
|
2778
|
+
* Fetch or return cached data
|
|
2779
|
+
*/
|
|
2780
|
+
get(fetchFn_1, params_1) {
|
|
2781
|
+
return __awaiter(this, arguments, void 0, function* (fetchFn, params, ttl = DEFAULT_TTL) {
|
|
2782
|
+
const key = this.getCacheKey(fetchFn, params);
|
|
2783
|
+
// Return valid cached data
|
|
2784
|
+
const cached = this.cache.get(key);
|
|
2785
|
+
if (cached && this.isCacheValid(cached)) {
|
|
2786
|
+
return cached.data;
|
|
2787
|
+
}
|
|
2788
|
+
// Return pending request if already in flight
|
|
2789
|
+
const pending = this.pendingRequests.get(key);
|
|
2790
|
+
if (pending) {
|
|
2791
|
+
return pending.promise;
|
|
2792
|
+
}
|
|
2793
|
+
// Create new request
|
|
2794
|
+
const promise = fetchFn().then((data) => {
|
|
2795
|
+
this.cache.set(key, {
|
|
2796
|
+
data,
|
|
2797
|
+
timestamp: Date.now(),
|
|
2798
|
+
ttl,
|
|
2799
|
+
});
|
|
2800
|
+
this.pendingRequests.delete(key);
|
|
2801
|
+
return data;
|
|
2802
|
+
}).catch((error) => {
|
|
2803
|
+
this.pendingRequests.delete(key);
|
|
2804
|
+
throw error;
|
|
2805
|
+
});
|
|
2806
|
+
this.pendingRequests.set(key, { promise });
|
|
2807
|
+
return promise;
|
|
2808
|
+
});
|
|
2809
|
+
}
|
|
2810
|
+
/**
|
|
2811
|
+
* Invalidate cache entry
|
|
2812
|
+
*/
|
|
2813
|
+
invalidate(fetchFn, params) {
|
|
2814
|
+
const key = this.getCacheKey(fetchFn, params);
|
|
2815
|
+
this.cache.delete(key);
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Clear all cache
|
|
2819
|
+
*/
|
|
2820
|
+
clear() {
|
|
2821
|
+
this.cache.clear();
|
|
2822
|
+
this.pendingRequests.clear();
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
// Singleton instance
|
|
2826
|
+
const requestCache = new RequestCache();
|
|
2827
|
+
|
|
2828
|
+
/**
|
|
2829
|
+
* useReportData Hook
|
|
2830
|
+
* Fetches and manages report data with error handling
|
|
2831
|
+
* Implements caching to prevent duplicate requests
|
|
2832
|
+
*/
|
|
2833
|
+
/**
|
|
2834
|
+
* Generic hook for fetching report data
|
|
2835
|
+
* Implements caching and deduplication to prevent infinite requests
|
|
2836
|
+
* @param fetchFn - Async function to fetch report data
|
|
2837
|
+
* @param dependencies - Dependencies that trigger refetch when changed
|
|
2838
|
+
* @param options - Hook options (enabled flag, cache TTL)
|
|
2839
|
+
*/
|
|
2840
|
+
function useReportData(fetchFn, dependencies = [], options = {}) {
|
|
2841
|
+
const [data, setData] = useState(null);
|
|
2842
|
+
const [loading, setLoading] = useState(true);
|
|
2843
|
+
const [error, setError] = useState(null);
|
|
2844
|
+
const isMountedRef = useRef(true);
|
|
2845
|
+
const { enabled = true, ttl = 5 * 60 * 1000 } = options;
|
|
2846
|
+
// Create a stable string representation of dependencies for comparison
|
|
2847
|
+
const depsString = JSON.stringify(dependencies);
|
|
2848
|
+
useEffect(() => {
|
|
2849
|
+
isMountedRef.current = true;
|
|
2850
|
+
if (!enabled) {
|
|
2851
|
+
setLoading(false);
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
let cancelled = false;
|
|
2855
|
+
const fetchData = () => __awaiter(this, void 0, void 0, function* () {
|
|
2856
|
+
try {
|
|
2857
|
+
setLoading(true);
|
|
2858
|
+
setError(null);
|
|
2859
|
+
// Use cache to avoid duplicate requests
|
|
2860
|
+
const result = yield requestCache.get(fetchFn, depsString, ttl);
|
|
2861
|
+
if (!cancelled && isMountedRef.current) {
|
|
2862
|
+
setData(result);
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
catch (err) {
|
|
2866
|
+
if (!cancelled && isMountedRef.current) {
|
|
2867
|
+
const error = err instanceof Error ? err : new Error("Unknown error occurred");
|
|
2868
|
+
setError(error);
|
|
2869
|
+
setData(null);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
finally {
|
|
2873
|
+
if (!cancelled && isMountedRef.current) {
|
|
2874
|
+
setLoading(false);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
});
|
|
2878
|
+
fetchData();
|
|
2879
|
+
return () => {
|
|
2880
|
+
cancelled = true;
|
|
2881
|
+
};
|
|
2882
|
+
}, [depsString, enabled, ttl, fetchFn]);
|
|
2883
|
+
useEffect(() => {
|
|
2884
|
+
return () => {
|
|
2885
|
+
isMountedRef.current = false;
|
|
2886
|
+
};
|
|
2887
|
+
}, []);
|
|
2888
|
+
const refetch = () => __awaiter(this, void 0, void 0, function* () {
|
|
2889
|
+
// Invalidate cache and refetch
|
|
2890
|
+
requestCache.invalidate(fetchFn, depsString);
|
|
2891
|
+
try {
|
|
2892
|
+
setLoading(true);
|
|
2893
|
+
setError(null);
|
|
2894
|
+
const result = yield requestCache.get(fetchFn, depsString, ttl);
|
|
2895
|
+
if (isMountedRef.current) {
|
|
2896
|
+
setData(result);
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
catch (err) {
|
|
2900
|
+
if (isMountedRef.current) {
|
|
2901
|
+
const error = err instanceof Error ? err : new Error("Unknown error occurred");
|
|
2902
|
+
setError(error);
|
|
2903
|
+
setData(null);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
finally {
|
|
2907
|
+
if (isMountedRef.current) {
|
|
2908
|
+
setLoading(false);
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
});
|
|
2912
|
+
return { data, loading, error, refetch };
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
/**
|
|
2916
|
+
* Date Range Service
|
|
2917
|
+
* Handles date range calculations and options
|
|
2918
|
+
*/
|
|
2919
|
+
class DateRangeService {
|
|
2920
|
+
/**
|
|
2921
|
+
* Calculate date range based on predefined options
|
|
2922
|
+
*/
|
|
2923
|
+
static getDateRange(option) {
|
|
2924
|
+
const today = new Date();
|
|
2925
|
+
const endDate = today.toISOString().split("T")[0]; // YYYY-MM-DD format
|
|
2926
|
+
let startDate;
|
|
2927
|
+
switch (option) {
|
|
2928
|
+
case "past-week":
|
|
2929
|
+
startDate = new Date(today);
|
|
2930
|
+
startDate.setDate(today.getDate() - 7);
|
|
2931
|
+
break;
|
|
2932
|
+
case "past-month":
|
|
2933
|
+
startDate = new Date(today);
|
|
2934
|
+
startDate.setMonth(today.getMonth() - 1);
|
|
2935
|
+
break;
|
|
2936
|
+
case "past-3-months":
|
|
2937
|
+
startDate = new Date(today);
|
|
2938
|
+
startDate.setMonth(today.getMonth() - 3);
|
|
2939
|
+
break;
|
|
2940
|
+
case "past-6-months":
|
|
2941
|
+
startDate = new Date(today);
|
|
2942
|
+
startDate.setMonth(today.getMonth() - 6);
|
|
2943
|
+
break;
|
|
2944
|
+
case "past-year":
|
|
2945
|
+
startDate = new Date(today);
|
|
2946
|
+
startDate.setFullYear(today.getFullYear() - 1);
|
|
2947
|
+
break;
|
|
2948
|
+
default:
|
|
2949
|
+
startDate = new Date(today);
|
|
2950
|
+
startDate.setMonth(today.getMonth() - 1);
|
|
2951
|
+
}
|
|
2952
|
+
return {
|
|
2953
|
+
label: this.getLabel(option),
|
|
2954
|
+
startDate: startDate.toISOString().split("T")[0],
|
|
2955
|
+
endDate,
|
|
2956
|
+
};
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Get human-readable label for date range option
|
|
2960
|
+
*/
|
|
2961
|
+
static getLabel(option) {
|
|
2962
|
+
const labels = {
|
|
2963
|
+
"past-week": "Past Week",
|
|
2964
|
+
"past-month": "Past Month",
|
|
2965
|
+
"past-3-months": "Past 3 Months",
|
|
2966
|
+
"past-6-months": "Past 6 Months",
|
|
2967
|
+
"past-year": "Past Year",
|
|
2968
|
+
};
|
|
2969
|
+
return labels[option];
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Get all available date range options
|
|
2973
|
+
*/
|
|
2974
|
+
static getAllOptions() {
|
|
2975
|
+
return [
|
|
2976
|
+
{ value: "past-week", label: "Past Week" },
|
|
2977
|
+
{ value: "past-month", label: "Past Month" },
|
|
2978
|
+
{ value: "past-3-months", label: "Past 3 Months" },
|
|
2979
|
+
{ value: "past-6-months", label: "Past 6 Months" },
|
|
2980
|
+
{ value: "past-year", label: "Past Year" },
|
|
2981
|
+
];
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Format date for display (e.g., "Jan 6 - Feb 5")
|
|
2985
|
+
*/
|
|
2986
|
+
static formatDateRangeForDisplay(startDate, endDate) {
|
|
2987
|
+
try {
|
|
2988
|
+
const start = new Date(startDate);
|
|
2989
|
+
const end = new Date(endDate);
|
|
2990
|
+
const formatPart = (date) => {
|
|
2991
|
+
const month = date.toLocaleString("default", { month: "short" });
|
|
2992
|
+
const day = date.getDate();
|
|
2993
|
+
return `${month} ${day}`;
|
|
2994
|
+
};
|
|
2995
|
+
return `${formatPart(start)} - ${formatPart(end)}`;
|
|
2996
|
+
}
|
|
2997
|
+
catch (_a) {
|
|
2998
|
+
return `${startDate} - ${endDate}`;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
/**
|
|
3004
|
+
* useDateRange Hook
|
|
3005
|
+
* Manages date range state with predefined options
|
|
3006
|
+
*/
|
|
3007
|
+
/**
|
|
3008
|
+
* Hook to manage date range selection for reports
|
|
3009
|
+
* @param defaultOption - Default date range option (default: 'past-month')
|
|
3010
|
+
*/
|
|
3011
|
+
function useDateRange(defaultOption = "past-month") {
|
|
3012
|
+
const [selectedOption, setSelectedOption] = useState(defaultOption);
|
|
3013
|
+
const dateRange = DateRangeService.getDateRange(selectedOption);
|
|
3014
|
+
return {
|
|
3015
|
+
selectedOption,
|
|
3016
|
+
setSelectedOption,
|
|
3017
|
+
startDate: dateRange.startDate,
|
|
3018
|
+
endDate: dateRange.endDate,
|
|
3019
|
+
label: dateRange.label,
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
/**
|
|
3024
|
+
* DateRangeSelector Component
|
|
3025
|
+
* Select dropdown for date range selection
|
|
3026
|
+
*/
|
|
3027
|
+
const DateRangeSelector = ({ selectedOption, onSelect, theme, }) => {
|
|
3028
|
+
const options = DateRangeService.getAllOptions();
|
|
3029
|
+
return (React.createElement("select", { value: selectedOption, onChange: (e) => onSelect(e.target.value), style: {
|
|
3030
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "6px"} ${(theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "10px"}`,
|
|
3031
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "13px",
|
|
3032
|
+
fontWeight: "500",
|
|
3033
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "6px",
|
|
3034
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#d0d0d0"}`,
|
|
3035
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f9f9f9",
|
|
3036
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3037
|
+
cursor: "pointer",
|
|
3038
|
+
transition: "border-color 0.2s ease, box-shadow 0.2s ease",
|
|
3039
|
+
}, onFocus: (e) => {
|
|
3040
|
+
e.currentTarget.style.borderColor =
|
|
3041
|
+
(theme === null || theme === void 0 ? void 0 : theme.colors.primary) || "#1976d2";
|
|
3042
|
+
e.currentTarget.style.boxShadow = `0 0 0 2px ${((theme === null || theme === void 0 ? void 0 : theme.colors.primary) || "#1976d2") + "20"}`;
|
|
3043
|
+
}, onBlur: (e) => {
|
|
3044
|
+
e.currentTarget.style.borderColor =
|
|
3045
|
+
(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#d0d0d0";
|
|
3046
|
+
e.currentTarget.style.boxShadow = "none";
|
|
3047
|
+
} }, options.map((option) => (React.createElement("option", { key: option.value, value: option.value }, option.label)))));
|
|
3048
|
+
};
|
|
3049
|
+
|
|
3050
|
+
/**
|
|
3051
|
+
* LoadingState Component
|
|
3052
|
+
* Skeleton loader for reports
|
|
3053
|
+
*/
|
|
3054
|
+
const Skeleton = ({ theme }) => (React.createElement("div", { style: {
|
|
3055
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0",
|
|
3056
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3057
|
+
height: "16px",
|
|
3058
|
+
marginBottom: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
3059
|
+
animation: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
|
|
3060
|
+
} }));
|
|
3061
|
+
const LoadingState = ({ theme, variant = "minimal", }) => {
|
|
3062
|
+
if (variant === "minimal") {
|
|
3063
|
+
return (React.createElement("div", { style: {
|
|
3064
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px",
|
|
3065
|
+
textAlign: "center",
|
|
3066
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3067
|
+
} },
|
|
3068
|
+
React.createElement("div", { style: {
|
|
3069
|
+
display: "inline-block",
|
|
3070
|
+
width: "40px",
|
|
3071
|
+
height: "40px",
|
|
3072
|
+
border: `3px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3073
|
+
borderTop: `3px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.primary) || "#1976d2"}`,
|
|
3074
|
+
borderRadius: "50%",
|
|
3075
|
+
animation: "spin 0.6s linear infinite",
|
|
3076
|
+
} }),
|
|
3077
|
+
React.createElement("p", { style: { marginTop: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px" } }, "Loading report...")));
|
|
3078
|
+
}
|
|
3079
|
+
if (variant === "cards") {
|
|
3080
|
+
return (React.createElement("div", { style: {
|
|
3081
|
+
display: "grid",
|
|
3082
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
|
3083
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3084
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3085
|
+
} }, [1, 2, 3, 4].map((i) => (React.createElement("div", { key: i, style: {
|
|
3086
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3087
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3088
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3089
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3090
|
+
} },
|
|
3091
|
+
React.createElement(Skeleton, { theme: theme }),
|
|
3092
|
+
React.createElement(Skeleton, { theme: theme }),
|
|
3093
|
+
React.createElement(Skeleton, { theme: theme }))))));
|
|
3094
|
+
}
|
|
3095
|
+
return (React.createElement("div", { style: {
|
|
3096
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3097
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3098
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3099
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3100
|
+
} }, [1, 2, 3, 4, 5].map((i) => (React.createElement("div", { key: i, style: { marginBottom: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px" } },
|
|
3101
|
+
React.createElement(Skeleton, { theme: theme }),
|
|
3102
|
+
React.createElement(Skeleton, { theme: theme }))))));
|
|
3103
|
+
};
|
|
3104
|
+
|
|
3105
|
+
/**
|
|
3106
|
+
* ReportLayout Component
|
|
3107
|
+
* Wrapper for report pages with header and date range selector
|
|
3108
|
+
*/
|
|
3109
|
+
const ReportLayout = ({ title, description, selectedDateRange, onDateRangeChange, loading, error, children, theme: themeProp, }) => {
|
|
3110
|
+
const { theme: contextTheme } = useReactBridgeContext();
|
|
3111
|
+
const theme = themeProp || contextTheme;
|
|
3112
|
+
return (React.createElement("div", { style: {
|
|
3113
|
+
display: "flex",
|
|
3114
|
+
flexDirection: "column",
|
|
3115
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "12px",
|
|
3116
|
+
} },
|
|
3117
|
+
React.createElement("div", { style: {
|
|
3118
|
+
display: "flex",
|
|
3119
|
+
alignItems: "flex-start",
|
|
3120
|
+
justifyContent: "space-between",
|
|
3121
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "12px",
|
|
3122
|
+
flexWrap: "wrap",
|
|
3123
|
+
} },
|
|
3124
|
+
React.createElement("div", { style: { flex: 1, minWidth: "200px" } },
|
|
3125
|
+
React.createElement("h1", { style: {
|
|
3126
|
+
margin: 0,
|
|
3127
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.lg) || "20px",
|
|
3128
|
+
fontWeight: "700",
|
|
3129
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3130
|
+
} }, title),
|
|
3131
|
+
description && (React.createElement("p", { style: {
|
|
3132
|
+
margin: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0 0 0`,
|
|
3133
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "12px",
|
|
3134
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3135
|
+
} }, description))),
|
|
3136
|
+
React.createElement(DateRangeSelector, { selectedOption: selectedDateRange, onSelect: onDateRangeChange, theme: theme })),
|
|
3137
|
+
loading ? (React.createElement(LoadingState, { theme: theme })) : error ? (React.createElement("div", { style: {
|
|
3138
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "12px",
|
|
3139
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "6px",
|
|
3140
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.error)
|
|
3141
|
+
? `${theme.colors.error}15`
|
|
3142
|
+
: "#FEE2E2",
|
|
3143
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.error) || "#f44336"}`,
|
|
3144
|
+
} },
|
|
3145
|
+
React.createElement("p", { style: {
|
|
3146
|
+
margin: 0,
|
|
3147
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "12px",
|
|
3148
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.error) || "#f44336",
|
|
3149
|
+
fontWeight: "500",
|
|
3150
|
+
} },
|
|
3151
|
+
"Error loading report: ",
|
|
3152
|
+
error.message))) : (children)));
|
|
3153
|
+
};
|
|
3154
|
+
|
|
3155
|
+
/**
|
|
3156
|
+
* MetricCard Component
|
|
3157
|
+
* Displays a single KPI metric with optional trend indicator
|
|
3158
|
+
*/
|
|
3159
|
+
const MetricCard = ({ label, value, unit, trend, trendValue, icon, onClick, theme, }) => {
|
|
3160
|
+
const getTrendColor = () => {
|
|
3161
|
+
if (!theme)
|
|
3162
|
+
return "inherit";
|
|
3163
|
+
if (trend === "up")
|
|
3164
|
+
return theme.colors.success;
|
|
3165
|
+
if (trend === "down")
|
|
3166
|
+
return theme.colors.error;
|
|
3167
|
+
return theme.colors.textSecondary;
|
|
3168
|
+
};
|
|
3169
|
+
const getTrendIcon = () => {
|
|
3170
|
+
if (trend === "up")
|
|
3171
|
+
return "↑";
|
|
3172
|
+
if (trend === "down")
|
|
3173
|
+
return "↓";
|
|
3174
|
+
return "→";
|
|
3175
|
+
};
|
|
3176
|
+
const containerStyles = {
|
|
3177
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "10px",
|
|
3178
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "6px",
|
|
3179
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3180
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3181
|
+
cursor: onClick ? "pointer" : "default",
|
|
3182
|
+
transition: "all 0.2s ease",
|
|
3183
|
+
};
|
|
3184
|
+
return (React.createElement("div", { style: containerStyles, onClick: onClick, onMouseEnter: (e) => {
|
|
3185
|
+
if (onClick) {
|
|
3186
|
+
e.currentTarget.style.boxShadow =
|
|
3187
|
+
(theme === null || theme === void 0 ? void 0 : theme.boxShadow) || "0 2px 8px rgba(0, 0, 0, 0.1)";
|
|
3188
|
+
}
|
|
3189
|
+
}, onMouseLeave: (e) => {
|
|
3190
|
+
if (onClick) {
|
|
3191
|
+
e.currentTarget.style.boxShadow = "none";
|
|
3192
|
+
}
|
|
3193
|
+
} },
|
|
3194
|
+
React.createElement("div", { style: {
|
|
3195
|
+
display: "flex",
|
|
3196
|
+
alignItems: "flex-start",
|
|
3197
|
+
justifyContent: "space-between",
|
|
3198
|
+
} },
|
|
3199
|
+
React.createElement("div", { style: { flex: 1 } },
|
|
3200
|
+
React.createElement("p", { style: {
|
|
3201
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.xs) || "11px",
|
|
3202
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3203
|
+
margin: "0 0 6px 0",
|
|
3204
|
+
} }, label),
|
|
3205
|
+
React.createElement("div", { style: {
|
|
3206
|
+
display: "flex",
|
|
3207
|
+
alignItems: "baseline",
|
|
3208
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
3209
|
+
} },
|
|
3210
|
+
React.createElement("p", { style: {
|
|
3211
|
+
fontSize: "18px",
|
|
3212
|
+
fontWeight: "bold",
|
|
3213
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3214
|
+
margin: 0,
|
|
3215
|
+
} }, value),
|
|
3216
|
+
unit && (React.createElement("p", { style: {
|
|
3217
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.xs) || "11px",
|
|
3218
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3219
|
+
margin: 0,
|
|
3220
|
+
} }, unit))),
|
|
3221
|
+
trend && trendValue !== undefined && (React.createElement("div", { style: {
|
|
3222
|
+
fontSize: "10px",
|
|
3223
|
+
color: getTrendColor(),
|
|
3224
|
+
marginTop: "4px",
|
|
3225
|
+
} },
|
|
3226
|
+
getTrendIcon(),
|
|
3227
|
+
" ",
|
|
3228
|
+
trendValue,
|
|
3229
|
+
"%"))),
|
|
3230
|
+
icon && (React.createElement("div", { style: {
|
|
3231
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#999",
|
|
3232
|
+
fontSize: "18px",
|
|
3233
|
+
} }, icon)))));
|
|
3234
|
+
};
|
|
3235
|
+
|
|
3236
|
+
/**
|
|
3237
|
+
* TrendChart Component
|
|
3238
|
+
* Renders a simple line/area chart for trend data
|
|
3239
|
+
*/
|
|
3240
|
+
const TrendChart = ({ data, title, height = 200, theme, showArea = true, }) => {
|
|
3241
|
+
if (!data || data.length === 0) {
|
|
3242
|
+
return (React.createElement("div", { style: {
|
|
3243
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px",
|
|
3244
|
+
textAlign: "center",
|
|
3245
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3246
|
+
} }, "No data available"));
|
|
3247
|
+
}
|
|
3248
|
+
const padding = 40;
|
|
3249
|
+
const width = 600;
|
|
3250
|
+
const innerHeight = height - padding * 2;
|
|
3251
|
+
const innerWidth = width - padding * 2;
|
|
3252
|
+
const maxValue = Math.max(...data.map((d) => d.value));
|
|
3253
|
+
const minValue = Math.min(...data.map((d) => d.value));
|
|
3254
|
+
const range = maxValue - minValue || 1;
|
|
3255
|
+
const points = data.map((d, i) => {
|
|
3256
|
+
const x = padding + (i / (data.length - 1 || 1)) * innerWidth;
|
|
3257
|
+
const y = padding + innerHeight - ((d.value - minValue) / range) * innerHeight;
|
|
3258
|
+
return Object.assign({ x, y }, d);
|
|
3259
|
+
});
|
|
3260
|
+
const pathD = points
|
|
3261
|
+
.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`)
|
|
3262
|
+
.join(" ");
|
|
3263
|
+
const areaPathD = `${pathD} L ${points[points.length - 1].x} ${padding + innerHeight} L ${points[0].x} ${padding + innerHeight} Z`;
|
|
3264
|
+
const lineColor = (theme === null || theme === void 0 ? void 0 : theme.colors.primary) || "#1976d2";
|
|
3265
|
+
const areaColor = (theme === null || theme === void 0 ? void 0 : theme.colors.primary) || "#1976d2";
|
|
3266
|
+
return (React.createElement("div", null,
|
|
3267
|
+
title && (React.createElement("h3", { style: {
|
|
3268
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
3269
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
3270
|
+
fontWeight: "600",
|
|
3271
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3272
|
+
} }, title)),
|
|
3273
|
+
React.createElement("svg", { width: "100%", height: height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "xMidYMid meet", style: { overflow: "visible" } },
|
|
3274
|
+
[0, 0.25, 0.5, 0.75, 1].map((factor) => {
|
|
3275
|
+
const y = padding + innerHeight - factor * innerHeight;
|
|
3276
|
+
return (React.createElement("line", { key: `grid-${factor}`, x1: padding, y1: y, x2: width - padding, y2: y, stroke: (theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0", strokeWidth: "1", strokeDasharray: "4", opacity: "0.5" }));
|
|
3277
|
+
}),
|
|
3278
|
+
showArea && React.createElement("path", { d: areaPathD, fill: areaColor, fillOpacity: "0.1" }),
|
|
3279
|
+
React.createElement("path", { d: pathD, fill: "none", stroke: lineColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
3280
|
+
points.map((p, i) => (React.createElement("circle", { key: `point-${i}`, cx: p.x, cy: p.y, r: "3", fill: lineColor, opacity: "0.8" }))),
|
|
3281
|
+
points.map((p, i) => {
|
|
3282
|
+
if (data.length <= 6 || i % Math.ceil(data.length / 6) === 0) {
|
|
3283
|
+
return (React.createElement("text", { key: `label-${i}`, x: p.x, y: height - padding + 20, textAnchor: "middle", fontSize: "12", fill: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" }, p.label));
|
|
3284
|
+
}
|
|
3285
|
+
}))));
|
|
3286
|
+
};
|
|
3287
|
+
|
|
3288
|
+
/**
|
|
3289
|
+
* Executive Dashboard Report
|
|
3290
|
+
* High-level directive execution metrics, trends, and cost impact
|
|
3291
|
+
*/
|
|
3292
|
+
const ExecutiveDashboard = ({ theme: themeProp, defaultDateRange = "past-month", }) => {
|
|
3293
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
3294
|
+
const { api, theme: contextTheme } = useReactBridgeContext();
|
|
3295
|
+
const theme = themeProp || contextTheme;
|
|
3296
|
+
const dateRange = useDateRange(defaultDateRange);
|
|
3297
|
+
const { data, loading, error } = useReportData(() => api.getExecutiveDashboard({
|
|
3298
|
+
startDate: dateRange.startDate,
|
|
3299
|
+
endDate: dateRange.endDate,
|
|
3300
|
+
}), [dateRange.startDate, dateRange.endDate]);
|
|
3301
|
+
const trendData = ((_a = data === null || data === void 0 ? void 0 : data.monthlyTrends) === null || _a === void 0 ? void 0 : _a.map((m) => ({
|
|
3302
|
+
label: m.month,
|
|
3303
|
+
value: m.successPercentage,
|
|
3304
|
+
}))) || [];
|
|
3305
|
+
return (React.createElement(ReportLayout, { title: "Executive Dashboard", description: "High-level directive execution metrics and trends", selectedDateRange: dateRange.selectedOption, onDateRangeChange: dateRange.setSelectedOption, loading: loading, error: error, theme: theme },
|
|
3306
|
+
React.createElement("div", { style: {
|
|
3307
|
+
display: "grid",
|
|
3308
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
3309
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "10px",
|
|
3310
|
+
} },
|
|
3311
|
+
React.createElement(MetricCard, { label: "Total Directives", value: (_b = data === null || data === void 0 ? void 0 : data.totalDirectives) !== null && _b !== void 0 ? _b : 0, theme: theme }),
|
|
3312
|
+
React.createElement(MetricCard, { label: "Success Rate", value: (_d = (_c = data === null || data === void 0 ? void 0 : data.successRate) === null || _c === void 0 ? void 0 : _c.toFixed(2)) !== null && _d !== void 0 ? _d : 0, unit: "%", trend: ((_e = data === null || data === void 0 ? void 0 : data.successRate) !== null && _e !== void 0 ? _e : 0 > 80) ? "up" : "neutral", theme: theme }),
|
|
3313
|
+
React.createElement(MetricCard, { label: "Auto-Executed", value: (_f = data === null || data === void 0 ? void 0 : data.autoExecutedDirectives) !== null && _f !== void 0 ? _f : 0, theme: theme }),
|
|
3314
|
+
React.createElement(MetricCard, { label: "Cost Impact", value: (_h = (_g = data === null || data === void 0 ? void 0 : data.totalCostImpact) === null || _g === void 0 ? void 0 : _g.toFixed(2)) !== null && _h !== void 0 ? _h : 0, unit: "$", theme: theme })),
|
|
3315
|
+
trendData.length > 0 && (React.createElement("div", { style: {
|
|
3316
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "10px",
|
|
3317
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3318
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "6px",
|
|
3319
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3320
|
+
} },
|
|
3321
|
+
React.createElement(TrendChart, { data: trendData, title: "Success Rate Trend", theme: theme, showArea: true }))),
|
|
3322
|
+
React.createElement("div", { style: {
|
|
3323
|
+
display: "grid",
|
|
3324
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
|
|
3325
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "10px",
|
|
3326
|
+
} },
|
|
3327
|
+
React.createElement("div", { style: {
|
|
3328
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "10px",
|
|
3329
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3330
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "6px",
|
|
3331
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3332
|
+
} },
|
|
3333
|
+
React.createElement("h3", { style: {
|
|
3334
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px"} 0`,
|
|
3335
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "13px",
|
|
3336
|
+
fontWeight: "600",
|
|
3337
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3338
|
+
} }, "Execution Summary"),
|
|
3339
|
+
React.createElement("div", { style: {
|
|
3340
|
+
display: "flex",
|
|
3341
|
+
flexDirection: "column",
|
|
3342
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
3343
|
+
} },
|
|
3344
|
+
React.createElement("div", { style: {
|
|
3345
|
+
display: "flex",
|
|
3346
|
+
justifyContent: "space-between",
|
|
3347
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "3px"} 0`,
|
|
3348
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.xs) || "11px",
|
|
3349
|
+
} },
|
|
3350
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Successful:"),
|
|
3351
|
+
React.createElement("span", { style: {
|
|
3352
|
+
fontWeight: "600",
|
|
3353
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.success) || "#4caf50",
|
|
3354
|
+
} }, (_j = data === null || data === void 0 ? void 0 : data.successfulExecutions) !== null && _j !== void 0 ? _j : 0)),
|
|
3355
|
+
React.createElement("div", { style: {
|
|
3356
|
+
display: "flex",
|
|
3357
|
+
justifyContent: "space-between",
|
|
3358
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "3px"} 0`,
|
|
3359
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.xs) || "11px",
|
|
3360
|
+
} },
|
|
3361
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Failed:"),
|
|
3362
|
+
React.createElement("span", { style: {
|
|
3363
|
+
fontWeight: "600",
|
|
3364
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.error) || "#f44336",
|
|
3365
|
+
} }, (_k = data === null || data === void 0 ? void 0 : data.failedExecutions) !== null && _k !== void 0 ? _k : 0)),
|
|
3366
|
+
React.createElement("div", { style: {
|
|
3367
|
+
display: "flex",
|
|
3368
|
+
justifyContent: "space-between",
|
|
3369
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "3px"} 0`,
|
|
3370
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.xs) || "11px",
|
|
3371
|
+
} },
|
|
3372
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Avg Cost:"),
|
|
3373
|
+
React.createElement("span", { style: {
|
|
3374
|
+
fontWeight: "600",
|
|
3375
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3376
|
+
} },
|
|
3377
|
+
"$", (_m = (_l = data === null || data === void 0 ? void 0 : data.averageCostPerDirective) === null || _l === void 0 ? void 0 : _l.toFixed(2)) !== null && _m !== void 0 ? _m : 0)))))));
|
|
3378
|
+
};
|
|
3379
|
+
|
|
3380
|
+
/**
|
|
3381
|
+
* ProgressBar Component
|
|
3382
|
+
* Displays percentage with visual bar
|
|
3383
|
+
*/
|
|
3384
|
+
const ProgressBar = ({ percentage, label, showPercentage = true, height = "medium", theme, }) => {
|
|
3385
|
+
const normalizedPercentage = Math.min(Math.max(percentage, 0), 100);
|
|
3386
|
+
const getBarColor = () => {
|
|
3387
|
+
if (!theme) {
|
|
3388
|
+
if (normalizedPercentage >= 80)
|
|
3389
|
+
return "#10B981";
|
|
3390
|
+
if (normalizedPercentage >= 60)
|
|
3391
|
+
return "#F59E0B";
|
|
3392
|
+
return "#EF4444";
|
|
3393
|
+
}
|
|
3394
|
+
if (normalizedPercentage >= 80)
|
|
3395
|
+
return theme.colors.success;
|
|
3396
|
+
if (normalizedPercentage >= 60)
|
|
3397
|
+
return "#F59E0B"; // warning
|
|
3398
|
+
return theme.colors.error;
|
|
3399
|
+
};
|
|
3400
|
+
const heightConfig = {
|
|
3401
|
+
small: "4px",
|
|
3402
|
+
medium: "8px",
|
|
3403
|
+
large: "12px",
|
|
3404
|
+
};
|
|
3405
|
+
return (React.createElement("div", null,
|
|
3406
|
+
(label || showPercentage) && (React.createElement("div", { style: {
|
|
3407
|
+
display: "flex",
|
|
3408
|
+
justifyContent: "space-between",
|
|
3409
|
+
alignItems: "center",
|
|
3410
|
+
marginBottom: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
3411
|
+
} },
|
|
3412
|
+
label && (React.createElement("span", { style: {
|
|
3413
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
3414
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3415
|
+
fontWeight: "500",
|
|
3416
|
+
} }, label)),
|
|
3417
|
+
showPercentage && (React.createElement("span", { style: {
|
|
3418
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.xs) || "12px",
|
|
3419
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3420
|
+
} },
|
|
3421
|
+
normalizedPercentage.toFixed(1),
|
|
3422
|
+
"%")))),
|
|
3423
|
+
React.createElement("div", { style: {
|
|
3424
|
+
width: "100%",
|
|
3425
|
+
height: heightConfig[height],
|
|
3426
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0",
|
|
3427
|
+
borderRadius: "4px",
|
|
3428
|
+
overflow: "hidden",
|
|
3429
|
+
} },
|
|
3430
|
+
React.createElement("div", { style: {
|
|
3431
|
+
height: "100%",
|
|
3432
|
+
width: `${normalizedPercentage}%`,
|
|
3433
|
+
backgroundColor: getBarColor(),
|
|
3434
|
+
transition: "width 0.3s ease",
|
|
3435
|
+
} }))));
|
|
3436
|
+
};
|
|
3437
|
+
|
|
3438
|
+
/**
|
|
3439
|
+
* DataTable Component
|
|
3440
|
+
* Generic sortable/paginated table for report data
|
|
3441
|
+
*/
|
|
3442
|
+
const DataTable = React.forwardRef(({ columns, data, title, pageSize = 10, theme }, ref) => {
|
|
3443
|
+
const [sortKey, setSortKey] = useState(null);
|
|
3444
|
+
const [sortOrder, setSortOrder] = useState("asc");
|
|
3445
|
+
const [currentPage, setCurrentPage] = useState(0);
|
|
3446
|
+
const sortedData = useMemo(() => {
|
|
3447
|
+
if (!sortKey)
|
|
3448
|
+
return data;
|
|
3449
|
+
const sorted = [...data].sort((a, b) => {
|
|
3450
|
+
const aVal = a[sortKey];
|
|
3451
|
+
const bVal = b[sortKey];
|
|
3452
|
+
if (aVal < bVal)
|
|
3453
|
+
return sortOrder === "asc" ? -1 : 1;
|
|
3454
|
+
if (aVal > bVal)
|
|
3455
|
+
return sortOrder === "asc" ? 1 : -1;
|
|
3456
|
+
return 0;
|
|
3457
|
+
});
|
|
3458
|
+
return sorted;
|
|
3459
|
+
}, [data, sortKey, sortOrder]);
|
|
3460
|
+
const paginatedData = useMemo(() => {
|
|
3461
|
+
const start = currentPage * pageSize;
|
|
3462
|
+
return sortedData.slice(start, start + pageSize);
|
|
3463
|
+
}, [sortedData, currentPage, pageSize]);
|
|
3464
|
+
const totalPages = Math.ceil(sortedData.length / pageSize);
|
|
3465
|
+
const handleSort = (key) => {
|
|
3466
|
+
if (sortKey === key) {
|
|
3467
|
+
setSortOrder(sortOrder === "asc" ? "desc" : "asc");
|
|
3468
|
+
}
|
|
3469
|
+
else {
|
|
3470
|
+
setSortKey(key);
|
|
3471
|
+
setSortOrder("asc");
|
|
3472
|
+
}
|
|
3473
|
+
setCurrentPage(0);
|
|
3474
|
+
};
|
|
3475
|
+
return (React.createElement("div", { ref: ref },
|
|
3476
|
+
title && (React.createElement("h3", { style: {
|
|
3477
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
3478
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
3479
|
+
fontWeight: "600",
|
|
3480
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3481
|
+
} }, title)),
|
|
3482
|
+
data.length === 0 ? (React.createElement("div", { style: {
|
|
3483
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px",
|
|
3484
|
+
textAlign: "center",
|
|
3485
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3486
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3487
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3488
|
+
} }, "No data available")) : (React.createElement(React.Fragment, null,
|
|
3489
|
+
React.createElement("div", { style: {
|
|
3490
|
+
overflowX: "auto",
|
|
3491
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3492
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3493
|
+
} },
|
|
3494
|
+
React.createElement("table", { style: {
|
|
3495
|
+
width: "100%",
|
|
3496
|
+
borderCollapse: "collapse",
|
|
3497
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
3498
|
+
} },
|
|
3499
|
+
React.createElement("thead", null,
|
|
3500
|
+
React.createElement("tr", { style: {
|
|
3501
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3502
|
+
borderBottom: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3503
|
+
} }, columns.map((col) => (React.createElement("th", { key: String(col.key), onClick: () => col.sortable && handleSort(String(col.key)), style: {
|
|
3504
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
3505
|
+
textAlign: "left",
|
|
3506
|
+
fontWeight: "600",
|
|
3507
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3508
|
+
cursor: col.sortable ? "pointer" : "default",
|
|
3509
|
+
userSelect: "none",
|
|
3510
|
+
width: col.width,
|
|
3511
|
+
whiteSpace: "nowrap",
|
|
3512
|
+
}, onMouseEnter: (e) => {
|
|
3513
|
+
if (col.sortable) {
|
|
3514
|
+
e.currentTarget.style.backgroundColor =
|
|
3515
|
+
(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0";
|
|
3516
|
+
}
|
|
3517
|
+
}, onMouseLeave: (e) => {
|
|
3518
|
+
e.currentTarget.style.backgroundColor =
|
|
3519
|
+
(theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5";
|
|
3520
|
+
} },
|
|
3521
|
+
React.createElement("div", { style: {
|
|
3522
|
+
display: "flex",
|
|
3523
|
+
alignItems: "center",
|
|
3524
|
+
gap: "4px",
|
|
3525
|
+
} },
|
|
3526
|
+
col.label,
|
|
3527
|
+
col.sortable && sortKey === String(col.key) && (React.createElement("span", null, sortOrder === "asc" ? "↑" : "↓")))))))),
|
|
3528
|
+
React.createElement("tbody", null, paginatedData.map((row, idx) => (React.createElement("tr", { key: row.id || idx, style: {
|
|
3529
|
+
borderBottom: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3530
|
+
backgroundColor: idx % 2 === 0
|
|
3531
|
+
? "transparent"
|
|
3532
|
+
: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3533
|
+
}, onMouseEnter: (e) => {
|
|
3534
|
+
e.currentTarget.style.backgroundColor =
|
|
3535
|
+
(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0";
|
|
3536
|
+
}, onMouseLeave: (e) => {
|
|
3537
|
+
e.currentTarget.style.backgroundColor =
|
|
3538
|
+
idx % 2 === 0
|
|
3539
|
+
? "transparent"
|
|
3540
|
+
: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5";
|
|
3541
|
+
} }, columns.map((col) => (React.createElement("td", { key: String(col.key), style: {
|
|
3542
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
3543
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3544
|
+
} }, col.render
|
|
3545
|
+
? col.render(row[col.key], row)
|
|
3546
|
+
: String(row[col.key])))))))))),
|
|
3547
|
+
totalPages > 1 && (React.createElement("div", { style: {
|
|
3548
|
+
marginTop: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3549
|
+
display: "flex",
|
|
3550
|
+
justifyContent: "space-between",
|
|
3551
|
+
alignItems: "center",
|
|
3552
|
+
} },
|
|
3553
|
+
React.createElement("span", { style: {
|
|
3554
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
3555
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
3556
|
+
} },
|
|
3557
|
+
"Page ",
|
|
3558
|
+
currentPage + 1,
|
|
3559
|
+
" of ",
|
|
3560
|
+
totalPages),
|
|
3561
|
+
React.createElement("div", { style: { display: "flex", gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px" } },
|
|
3562
|
+
React.createElement("button", { onClick: () => setCurrentPage(0), disabled: currentPage === 0, style: {
|
|
3563
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} ${(theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px"}`,
|
|
3564
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
3565
|
+
borderRadius: "4px",
|
|
3566
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3567
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3568
|
+
cursor: currentPage === 0 ? "not-allowed" : "pointer",
|
|
3569
|
+
opacity: currentPage === 0 ? 0.5 : 1,
|
|
3570
|
+
} }, "First"),
|
|
3571
|
+
React.createElement("button", { onClick: () => setCurrentPage(Math.max(0, currentPage - 1)), disabled: currentPage === 0, style: {
|
|
3572
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} ${(theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px"}`,
|
|
3573
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
3574
|
+
borderRadius: "4px",
|
|
3575
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3576
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3577
|
+
cursor: currentPage === 0 ? "not-allowed" : "pointer",
|
|
3578
|
+
opacity: currentPage === 0 ? 0.5 : 1,
|
|
3579
|
+
} }, "Previous"),
|
|
3580
|
+
React.createElement("button", { onClick: () => setCurrentPage(Math.min(totalPages - 1, currentPage + 1)), disabled: currentPage === totalPages - 1, style: {
|
|
3581
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} ${(theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px"}`,
|
|
3582
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
3583
|
+
borderRadius: "4px",
|
|
3584
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3585
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3586
|
+
cursor: currentPage === totalPages - 1
|
|
3587
|
+
? "not-allowed"
|
|
3588
|
+
: "pointer",
|
|
3589
|
+
opacity: currentPage === totalPages - 1 ? 0.5 : 1,
|
|
3590
|
+
} }, "Next"),
|
|
3591
|
+
React.createElement("button", { onClick: () => setCurrentPage(totalPages - 1), disabled: currentPage === totalPages - 1, style: {
|
|
3592
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} ${(theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px"}`,
|
|
3593
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
3594
|
+
borderRadius: "4px",
|
|
3595
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3596
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3597
|
+
cursor: currentPage === totalPages - 1
|
|
3598
|
+
? "not-allowed"
|
|
3599
|
+
: "pointer",
|
|
3600
|
+
opacity: currentPage === totalPages - 1 ? 0.5 : 1,
|
|
3601
|
+
} }, "Last"))))))));
|
|
3602
|
+
});
|
|
3603
|
+
DataTable.displayName = "DataTable";
|
|
3604
|
+
|
|
3605
|
+
/**
|
|
3606
|
+
* Supply Chain Risk Report
|
|
3607
|
+
* Supply chain risks including inventory, supplier, and cost analysis
|
|
3608
|
+
*/
|
|
3609
|
+
const SupplyChainRisk = ({ theme: themeProp, defaultDateRange = "past-month", }) => {
|
|
3610
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
|
|
3611
|
+
const { api, theme: contextTheme } = useReactBridgeContext();
|
|
3612
|
+
const theme = themeProp || contextTheme;
|
|
3613
|
+
const dateRange = useDateRange(defaultDateRange);
|
|
3614
|
+
const { data, loading, error } = useReportData(() => api.getSupplyChainRisk({
|
|
3615
|
+
startDate: dateRange.startDate,
|
|
3616
|
+
endDate: dateRange.endDate,
|
|
3617
|
+
}), [dateRange.startDate, dateRange.endDate]);
|
|
3618
|
+
const riskPercentage = (_a = data === null || data === void 0 ? void 0 : data.overallRiskScore) !== null && _a !== void 0 ? _a : 0;
|
|
3619
|
+
((_b = data === null || data === void 0 ? void 0 : data.highRiskCount) !== null && _b !== void 0 ? _b : 0) +
|
|
3620
|
+
((_c = data === null || data === void 0 ? void 0 : data.mediumRiskCount) !== null && _c !== void 0 ? _c : 0) +
|
|
3621
|
+
((_d = data === null || data === void 0 ? void 0 : data.lowRiskCount) !== null && _d !== void 0 ? _d : 0);
|
|
3622
|
+
const tableColumns = [
|
|
3623
|
+
{
|
|
3624
|
+
key: "issueType",
|
|
3625
|
+
label: "Issue Type",
|
|
3626
|
+
sortable: true,
|
|
3627
|
+
},
|
|
3628
|
+
{
|
|
3629
|
+
key: "description",
|
|
3630
|
+
label: "Description",
|
|
3631
|
+
sortable: true,
|
|
3632
|
+
},
|
|
3633
|
+
{
|
|
3634
|
+
key: "affectedRecords",
|
|
3635
|
+
label: "Affected Records",
|
|
3636
|
+
sortable: true,
|
|
3637
|
+
},
|
|
3638
|
+
{
|
|
3639
|
+
key: "severity",
|
|
3640
|
+
label: "Severity",
|
|
3641
|
+
sortable: true,
|
|
3642
|
+
render: (value) => {
|
|
3643
|
+
const color = value === "high"
|
|
3644
|
+
? theme === null || theme === void 0 ? void 0 : theme.colors.error
|
|
3645
|
+
: value === "medium"
|
|
3646
|
+
? "#F59E0B"
|
|
3647
|
+
: theme === null || theme === void 0 ? void 0 : theme.colors.success;
|
|
3648
|
+
return (React.createElement("span", { style: { color, fontWeight: "600" } }, value.toUpperCase()));
|
|
3649
|
+
},
|
|
3650
|
+
},
|
|
3651
|
+
];
|
|
3652
|
+
return (React.createElement(ReportLayout, { title: "Supply Chain Risk", description: "Inventory, supplier, and cost risk analysis", selectedDateRange: dateRange.selectedOption, onDateRangeChange: dateRange.setSelectedOption, loading: loading, error: error, theme: theme },
|
|
3653
|
+
React.createElement("div", { style: {
|
|
3654
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3655
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3656
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3657
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3658
|
+
} },
|
|
3659
|
+
React.createElement("h3", { style: {
|
|
3660
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
3661
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.lg) || "18px",
|
|
3662
|
+
fontWeight: "600",
|
|
3663
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3664
|
+
} }, "Overall Risk Assessment"),
|
|
3665
|
+
React.createElement(ProgressBar, { percentage: riskPercentage, label: "Risk Score", theme: theme, height: "large" })),
|
|
3666
|
+
React.createElement("div", { style: {
|
|
3667
|
+
display: "grid",
|
|
3668
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
|
3669
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3670
|
+
} },
|
|
3671
|
+
React.createElement(MetricCard, { label: "High Risk Items", value: (_e = data === null || data === void 0 ? void 0 : data.highRiskCount) !== null && _e !== void 0 ? _e : 0, theme: theme }),
|
|
3672
|
+
React.createElement(MetricCard, { label: "Medium Risk Items", value: (_f = data === null || data === void 0 ? void 0 : data.mediumRiskCount) !== null && _f !== void 0 ? _f : 0, theme: theme }),
|
|
3673
|
+
React.createElement(MetricCard, { label: "Low Risk Items", value: (_g = data === null || data === void 0 ? void 0 : data.lowRiskCount) !== null && _g !== void 0 ? _g : 0, theme: theme })),
|
|
3674
|
+
React.createElement("div", { style: {
|
|
3675
|
+
display: "grid",
|
|
3676
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))",
|
|
3677
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3678
|
+
} },
|
|
3679
|
+
React.createElement("div", { style: {
|
|
3680
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3681
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3682
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3683
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3684
|
+
} },
|
|
3685
|
+
React.createElement("h4", { style: {
|
|
3686
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
3687
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
3688
|
+
fontWeight: "600",
|
|
3689
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3690
|
+
} }, "Inventory Risk"),
|
|
3691
|
+
React.createElement("div", { style: {
|
|
3692
|
+
display: "flex",
|
|
3693
|
+
flexDirection: "column",
|
|
3694
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
3695
|
+
} },
|
|
3696
|
+
React.createElement("div", { style: {
|
|
3697
|
+
display: "flex",
|
|
3698
|
+
justifyContent: "space-between",
|
|
3699
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3700
|
+
} },
|
|
3701
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Low Stock Items:"),
|
|
3702
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_j = (_h = data === null || data === void 0 ? void 0 : data.inventoryRisk) === null || _h === void 0 ? void 0 : _h.lowStockItems) !== null && _j !== void 0 ? _j : 0)),
|
|
3703
|
+
React.createElement("div", { style: {
|
|
3704
|
+
display: "flex",
|
|
3705
|
+
justifyContent: "space-between",
|
|
3706
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3707
|
+
} },
|
|
3708
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Overstocked Items:"),
|
|
3709
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_l = (_k = data === null || data === void 0 ? void 0 : data.inventoryRisk) === null || _k === void 0 ? void 0 : _k.overstockedItems) !== null && _l !== void 0 ? _l : 0)),
|
|
3710
|
+
React.createElement(ProgressBar, { percentage: (_o = (_m = data === null || data === void 0 ? void 0 : data.inventoryRisk) === null || _m === void 0 ? void 0 : _m.averageStockHealth) !== null && _o !== void 0 ? _o : 0, label: "Stock Health", theme: theme }))),
|
|
3711
|
+
React.createElement("div", { style: {
|
|
3712
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3713
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3714
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3715
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3716
|
+
} },
|
|
3717
|
+
React.createElement("h4", { style: {
|
|
3718
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
3719
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
3720
|
+
fontWeight: "600",
|
|
3721
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3722
|
+
} }, "Supplier Risk"),
|
|
3723
|
+
React.createElement("div", { style: {
|
|
3724
|
+
display: "flex",
|
|
3725
|
+
flexDirection: "column",
|
|
3726
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
3727
|
+
} },
|
|
3728
|
+
React.createElement("div", { style: {
|
|
3729
|
+
display: "flex",
|
|
3730
|
+
justifyContent: "space-between",
|
|
3731
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3732
|
+
} },
|
|
3733
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Unreliable Suppliers:"),
|
|
3734
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_q = (_p = data === null || data === void 0 ? void 0 : data.supplierRisk) === null || _p === void 0 ? void 0 : _p.unreliableSuppliers) !== null && _q !== void 0 ? _q : 0)),
|
|
3735
|
+
React.createElement("div", { style: {
|
|
3736
|
+
display: "flex",
|
|
3737
|
+
justifyContent: "space-between",
|
|
3738
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3739
|
+
} },
|
|
3740
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "High Defect Rate:"),
|
|
3741
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_s = (_r = data === null || data === void 0 ? void 0 : data.supplierRisk) === null || _r === void 0 ? void 0 : _r.highDefectRateSuppliers) !== null && _s !== void 0 ? _s : 0)),
|
|
3742
|
+
React.createElement(ProgressBar, { percentage: (_u = (_t = data === null || data === void 0 ? void 0 : data.supplierRisk) === null || _t === void 0 ? void 0 : _t.averageSupplierScore) !== null && _u !== void 0 ? _u : 0, label: "Supplier Score", theme: theme }))),
|
|
3743
|
+
React.createElement("div", { style: {
|
|
3744
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3745
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3746
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3747
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3748
|
+
} },
|
|
3749
|
+
React.createElement("h4", { style: {
|
|
3750
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
3751
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
3752
|
+
fontWeight: "600",
|
|
3753
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3754
|
+
} }, "Cost Risk"),
|
|
3755
|
+
React.createElement("div", { style: {
|
|
3756
|
+
display: "flex",
|
|
3757
|
+
flexDirection: "column",
|
|
3758
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
3759
|
+
} },
|
|
3760
|
+
React.createElement("div", { style: {
|
|
3761
|
+
display: "flex",
|
|
3762
|
+
justifyContent: "space-between",
|
|
3763
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3764
|
+
} },
|
|
3765
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Cost Variance:"),
|
|
3766
|
+
React.createElement("span", { style: {
|
|
3767
|
+
fontWeight: "600",
|
|
3768
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.error) || "#f44336",
|
|
3769
|
+
} },
|
|
3770
|
+
"$", (_x = (_w = (_v = data === null || data === void 0 ? void 0 : data.costRisk) === null || _v === void 0 ? void 0 : _v.unexpectedCostVariance) === null || _w === void 0 ? void 0 : _w.toFixed(2)) !== null && _x !== void 0 ? _x : 0)),
|
|
3771
|
+
React.createElement("div", { style: {
|
|
3772
|
+
display: "flex",
|
|
3773
|
+
justifyContent: "space-between",
|
|
3774
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3775
|
+
} },
|
|
3776
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Potential Savings:"),
|
|
3777
|
+
React.createElement("span", { style: {
|
|
3778
|
+
fontWeight: "600",
|
|
3779
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.success) || "#4caf50",
|
|
3780
|
+
} },
|
|
3781
|
+
"$", (_0 = (_z = (_y = data === null || data === void 0 ? void 0 : data.costRisk) === null || _y === void 0 ? void 0 : _y.potentialSavings) === null || _z === void 0 ? void 0 : _z.toFixed(2)) !== null && _0 !== void 0 ? _0 : 0)),
|
|
3782
|
+
React.createElement("div", { style: {
|
|
3783
|
+
display: "flex",
|
|
3784
|
+
justifyContent: "space-between",
|
|
3785
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3786
|
+
} },
|
|
3787
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Volatility Index:"),
|
|
3788
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_3 = (_2 = (_1 = data === null || data === void 0 ? void 0 : data.costRisk) === null || _1 === void 0 ? void 0 : _1.costVolatilityIndex) === null || _2 === void 0 ? void 0 : _2.toFixed(2)) !== null && _3 !== void 0 ? _3 : 0,
|
|
3789
|
+
"%"))))),
|
|
3790
|
+
(data === null || data === void 0 ? void 0 : data.dataQualityIssues) && data.dataQualityIssues.length > 0 && (React.createElement("div", null,
|
|
3791
|
+
React.createElement(DataTable, { title: "Data Quality Issues", columns: tableColumns, data: data.dataQualityIssues.map((issue, idx) => (Object.assign({ id: idx }, issue))), pageSize: 5, theme: theme })))));
|
|
3792
|
+
};
|
|
3793
|
+
|
|
3794
|
+
/**
|
|
3795
|
+
* StatusBadge Component
|
|
3796
|
+
* Displays status with color-coded background
|
|
3797
|
+
*/
|
|
3798
|
+
const getStatusConfig = (status, theme) => {
|
|
3799
|
+
const baseConfig = {
|
|
3800
|
+
ok: { label: "OK" },
|
|
3801
|
+
warning: { label: "Warning" },
|
|
3802
|
+
critical: { label: "Critical" },
|
|
3803
|
+
proposed: { label: "Proposed" },
|
|
3804
|
+
executed: { label: "Executed" },
|
|
3805
|
+
declined: { label: "Declined" },
|
|
3806
|
+
failed: { label: "Failed" },
|
|
3807
|
+
"auto-executed": { label: "Auto Executed" },
|
|
3808
|
+
};
|
|
3809
|
+
const config = baseConfig[status];
|
|
3810
|
+
if (theme) {
|
|
3811
|
+
const statusColors = {
|
|
3812
|
+
ok: { bg: "rgba(16, 185, 129, 0.15)", text: theme.colors.success },
|
|
3813
|
+
warning: { bg: "rgba(245, 158, 11, 0.15)", text: "#F59E0B" },
|
|
3814
|
+
critical: { bg: "rgba(239, 68, 68, 0.15)", text: theme.colors.error },
|
|
3815
|
+
proposed: { bg: "rgba(59, 130, 246, 0.15)", text: theme.colors.primary },
|
|
3816
|
+
executed: { bg: "rgba(16, 185, 129, 0.15)", text: theme.colors.success },
|
|
3817
|
+
declined: {
|
|
3818
|
+
bg: "rgba(156, 163, 175, 0.15)",
|
|
3819
|
+
text: theme.colors.textSecondary,
|
|
3820
|
+
},
|
|
3821
|
+
failed: { bg: "rgba(239, 68, 68, 0.15)", text: theme.colors.error },
|
|
3822
|
+
"auto-executed": {
|
|
3823
|
+
bg: "rgba(16, 185, 129, 0.15)",
|
|
3824
|
+
text: theme.colors.success,
|
|
3825
|
+
},
|
|
3826
|
+
};
|
|
3827
|
+
return {
|
|
3828
|
+
bg: statusColors[status].bg,
|
|
3829
|
+
text: statusColors[status].text,
|
|
3830
|
+
label: config.label,
|
|
3831
|
+
};
|
|
3832
|
+
}
|
|
3833
|
+
const defaultColors = {
|
|
3834
|
+
ok: { bg: "#D1FAE5", text: "#059669" },
|
|
3835
|
+
warning: { bg: "#FEF3C7", text: "#D97706" },
|
|
3836
|
+
critical: { bg: "#FEE2E2", text: "#DC2626" },
|
|
3837
|
+
proposed: { bg: "#DBEAFE", text: "#1D4ED8" },
|
|
3838
|
+
executed: { bg: "#D1FAE5", text: "#059669" },
|
|
3839
|
+
declined: { bg: "#E5E7EB", text: "#4B5563" },
|
|
3840
|
+
failed: { bg: "#FEE2E2", text: "#DC2626" },
|
|
3841
|
+
"auto-executed": { bg: "#D1FAE5", text: "#059669" },
|
|
3842
|
+
};
|
|
3843
|
+
return {
|
|
3844
|
+
bg: defaultColors[status].bg,
|
|
3845
|
+
text: defaultColors[status].text,
|
|
3846
|
+
label: config.label,
|
|
3847
|
+
};
|
|
3848
|
+
};
|
|
3849
|
+
const StatusBadge = ({ status, label, theme, }) => {
|
|
3850
|
+
const config = getStatusConfig(status, theme);
|
|
3851
|
+
return (React.createElement("span", { style: {
|
|
3852
|
+
display: "inline-flex",
|
|
3853
|
+
alignItems: "center",
|
|
3854
|
+
paddingLeft: "10px",
|
|
3855
|
+
paddingRight: "10px",
|
|
3856
|
+
paddingTop: "4px",
|
|
3857
|
+
paddingBottom: "4px",
|
|
3858
|
+
borderRadius: "4px",
|
|
3859
|
+
fontSize: "12px",
|
|
3860
|
+
fontWeight: "500",
|
|
3861
|
+
backgroundColor: config.bg,
|
|
3862
|
+
color: config.text,
|
|
3863
|
+
whiteSpace: "nowrap",
|
|
3864
|
+
} }, label || config.label));
|
|
3865
|
+
};
|
|
3866
|
+
|
|
3867
|
+
/**
|
|
3868
|
+
* Execution Audit Trail Report
|
|
3869
|
+
* Complete execution history and audit information
|
|
3870
|
+
*/
|
|
3871
|
+
const ExecutionAuditTrail = ({ theme: themeProp, defaultDateRange = "past-month", }) => {
|
|
3872
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
3873
|
+
const { api, theme: contextTheme } = useReactBridgeContext();
|
|
3874
|
+
const theme = themeProp || contextTheme;
|
|
3875
|
+
const dateRange = useDateRange(defaultDateRange);
|
|
3876
|
+
const [directiveTypeFilter, setDirectiveTypeFilter] = useState("");
|
|
3877
|
+
const { data, loading, error } = useReportData(() => api.getExecutionAuditTrail({
|
|
3878
|
+
startDate: dateRange.startDate,
|
|
3879
|
+
endDate: dateRange.endDate,
|
|
3880
|
+
directiveType: directiveTypeFilter || undefined,
|
|
3881
|
+
}), [dateRange.startDate, dateRange.endDate, directiveTypeFilter]);
|
|
3882
|
+
const tableColumns = [
|
|
3883
|
+
{
|
|
3884
|
+
key: "directiveAction",
|
|
3885
|
+
label: "Directive Action",
|
|
3886
|
+
sortable: true,
|
|
3887
|
+
width: "200px",
|
|
3888
|
+
},
|
|
3889
|
+
{
|
|
3890
|
+
key: "status",
|
|
3891
|
+
label: "Status",
|
|
3892
|
+
sortable: true,
|
|
3893
|
+
render: (value) => React.createElement(StatusBadge, { status: value, theme: theme }),
|
|
3894
|
+
},
|
|
3895
|
+
{
|
|
3896
|
+
key: "executedAt",
|
|
3897
|
+
label: "Executed At",
|
|
3898
|
+
sortable: true,
|
|
3899
|
+
render: (value) => {
|
|
3900
|
+
try {
|
|
3901
|
+
return new Date(value).toLocaleString();
|
|
3902
|
+
}
|
|
3903
|
+
catch (_a) {
|
|
3904
|
+
return value;
|
|
3905
|
+
}
|
|
3906
|
+
},
|
|
3907
|
+
},
|
|
3908
|
+
{
|
|
3909
|
+
key: "executionDurationMinutes",
|
|
3910
|
+
label: "Duration (min)",
|
|
3911
|
+
sortable: true,
|
|
3912
|
+
},
|
|
3913
|
+
{
|
|
3914
|
+
key: "executedBy",
|
|
3915
|
+
label: "Executed By",
|
|
3916
|
+
sortable: true,
|
|
3917
|
+
},
|
|
3918
|
+
{
|
|
3919
|
+
key: "outcome",
|
|
3920
|
+
label: "Outcome",
|
|
3921
|
+
sortable: false,
|
|
3922
|
+
},
|
|
3923
|
+
];
|
|
3924
|
+
return (React.createElement(ReportLayout, { title: "Execution Audit Trail", description: "Directive execution history and audit information", selectedDateRange: dateRange.selectedOption, onDateRangeChange: dateRange.setSelectedOption, loading: loading, error: error, theme: theme },
|
|
3925
|
+
React.createElement("div", { style: {
|
|
3926
|
+
display: "grid",
|
|
3927
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
|
3928
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3929
|
+
} },
|
|
3930
|
+
React.createElement(MetricCard, { label: "Total Directives", value: (_a = data === null || data === void 0 ? void 0 : data.totalDirectives) !== null && _a !== void 0 ? _a : 0, theme: theme }),
|
|
3931
|
+
React.createElement(MetricCard, { label: "Executed", value: (_b = data === null || data === void 0 ? void 0 : data.executedCount) !== null && _b !== void 0 ? _b : 0, theme: theme }),
|
|
3932
|
+
React.createElement(MetricCard, { label: "Declined", value: (_c = data === null || data === void 0 ? void 0 : data.declinedCount) !== null && _c !== void 0 ? _c : 0, theme: theme }),
|
|
3933
|
+
React.createElement(MetricCard, { label: "Failed", value: (_d = data === null || data === void 0 ? void 0 : data.failedCount) !== null && _d !== void 0 ? _d : 0, theme: theme }),
|
|
3934
|
+
React.createElement(MetricCard, { label: "Auto-Executed", value: (_e = data === null || data === void 0 ? void 0 : data.autoExecutedCount) !== null && _e !== void 0 ? _e : 0, theme: theme })),
|
|
3935
|
+
React.createElement("div", { style: {
|
|
3936
|
+
display: "grid",
|
|
3937
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
|
3938
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3939
|
+
} },
|
|
3940
|
+
React.createElement("div", { style: {
|
|
3941
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
3942
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
3943
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
3944
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
3945
|
+
} },
|
|
3946
|
+
React.createElement("h4", { style: {
|
|
3947
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
3948
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
3949
|
+
fontWeight: "600",
|
|
3950
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
3951
|
+
} }, "Execution Statistics"),
|
|
3952
|
+
React.createElement("div", { style: {
|
|
3953
|
+
display: "flex",
|
|
3954
|
+
flexDirection: "column",
|
|
3955
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
3956
|
+
} },
|
|
3957
|
+
React.createElement("div", { style: {
|
|
3958
|
+
display: "flex",
|
|
3959
|
+
justifyContent: "space-between",
|
|
3960
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3961
|
+
} },
|
|
3962
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Avg. Duration:"),
|
|
3963
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_h = (_g = (_f = data === null || data === void 0 ? void 0 : data.stats) === null || _f === void 0 ? void 0 : _f.averageExecutionTimeMinutes) === null || _g === void 0 ? void 0 : _g.toFixed(1)) !== null && _h !== void 0 ? _h : 0,
|
|
3964
|
+
" min")),
|
|
3965
|
+
React.createElement("div", { style: {
|
|
3966
|
+
display: "flex",
|
|
3967
|
+
justifyContent: "space-between",
|
|
3968
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3969
|
+
} },
|
|
3970
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Success Rate:"),
|
|
3971
|
+
React.createElement("span", { style: {
|
|
3972
|
+
fontWeight: "600",
|
|
3973
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.success) || "#4caf50",
|
|
3974
|
+
} }, (_l = (_k = (_j = data === null || data === void 0 ? void 0 : data.stats) === null || _j === void 0 ? void 0 : _j.successRate) === null || _k === void 0 ? void 0 : _k.toFixed(2)) !== null && _l !== void 0 ? _l : 0,
|
|
3975
|
+
"%")),
|
|
3976
|
+
React.createElement("div", { style: {
|
|
3977
|
+
display: "flex",
|
|
3978
|
+
justifyContent: "space-between",
|
|
3979
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3980
|
+
} },
|
|
3981
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Fastest Execution:"),
|
|
3982
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_o = (_m = data === null || data === void 0 ? void 0 : data.stats) === null || _m === void 0 ? void 0 : _m.fastestExecutionMinutes) !== null && _o !== void 0 ? _o : 0,
|
|
3983
|
+
" min")),
|
|
3984
|
+
React.createElement("div", { style: {
|
|
3985
|
+
display: "flex",
|
|
3986
|
+
justifyContent: "space-between",
|
|
3987
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
3988
|
+
} },
|
|
3989
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Slowest Execution:"),
|
|
3990
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_q = (_p = data === null || data === void 0 ? void 0 : data.stats) === null || _p === void 0 ? void 0 : _p.slowestExecutionMinutes) !== null && _q !== void 0 ? _q : 0,
|
|
3991
|
+
" min"))))),
|
|
3992
|
+
(data === null || data === void 0 ? void 0 : data.executionHistory) && data.executionHistory.length > 0 && (React.createElement("div", null,
|
|
3993
|
+
React.createElement(DataTable, { title: "Execution History", columns: tableColumns, data: data.executionHistory.map((record, idx) => (Object.assign({ id: idx }, record))), pageSize: 10, theme: theme })))));
|
|
3994
|
+
};
|
|
3995
|
+
|
|
3996
|
+
/**
|
|
3997
|
+
* HealthGauge Component
|
|
3998
|
+
* Displays health/score on a 0-10 scale with color-coded circle
|
|
3999
|
+
*/
|
|
4000
|
+
const HealthGauge = ({ score, label, size = "medium", theme, }) => {
|
|
4001
|
+
const getColor = () => {
|
|
4002
|
+
if (!theme) {
|
|
4003
|
+
if (score >= 8)
|
|
4004
|
+
return "#10B981";
|
|
4005
|
+
if (score >= 6)
|
|
4006
|
+
return "#F59E0B";
|
|
4007
|
+
return "#EF4444";
|
|
4008
|
+
}
|
|
4009
|
+
if (score >= 8)
|
|
4010
|
+
return theme.colors.success;
|
|
4011
|
+
if (score >= 6)
|
|
4012
|
+
return "#F59E0B"; // warning
|
|
4013
|
+
return theme.colors.error;
|
|
4014
|
+
};
|
|
4015
|
+
const getBackgroundColor = () => {
|
|
4016
|
+
if (!theme) {
|
|
4017
|
+
if (score >= 8)
|
|
4018
|
+
return "rgba(16, 185, 129, 0.1)";
|
|
4019
|
+
if (score >= 6)
|
|
4020
|
+
return "rgba(245, 158, 11, 0.1)";
|
|
4021
|
+
return "rgba(239, 68, 68, 0.1)";
|
|
4022
|
+
}
|
|
4023
|
+
if (score >= 8)
|
|
4024
|
+
return "rgba(16, 185, 129, 0.15)";
|
|
4025
|
+
if (score >= 6)
|
|
4026
|
+
return "rgba(245, 158, 11, 0.15)";
|
|
4027
|
+
return "rgba(239, 68, 68, 0.15)";
|
|
4028
|
+
};
|
|
4029
|
+
const sizeConfig = {
|
|
4030
|
+
small: { width: 64, height: 64, fontSize: "14px", labelSize: "11px" },
|
|
4031
|
+
medium: { width: 96, height: 96, fontSize: "24px", labelSize: "12px" },
|
|
4032
|
+
large: { width: 128, height: 128, fontSize: "32px", labelSize: "13px" },
|
|
4033
|
+
};
|
|
4034
|
+
const config = sizeConfig[size];
|
|
4035
|
+
return (React.createElement("div", { style: {
|
|
4036
|
+
display: "flex",
|
|
4037
|
+
flexDirection: "column",
|
|
4038
|
+
alignItems: "center",
|
|
4039
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
4040
|
+
} },
|
|
4041
|
+
React.createElement("div", { style: {
|
|
4042
|
+
width: `${config.width}px`,
|
|
4043
|
+
height: `${config.height}px`,
|
|
4044
|
+
borderRadius: "50%",
|
|
4045
|
+
backgroundColor: getBackgroundColor(),
|
|
4046
|
+
display: "flex",
|
|
4047
|
+
alignItems: "center",
|
|
4048
|
+
justifyContent: "center",
|
|
4049
|
+
flex: "none",
|
|
4050
|
+
} },
|
|
4051
|
+
React.createElement("span", { style: {
|
|
4052
|
+
fontSize: config.fontSize,
|
|
4053
|
+
fontWeight: "bold",
|
|
4054
|
+
color: getColor(),
|
|
4055
|
+
} }, score.toFixed(1))),
|
|
4056
|
+
React.createElement("p", { style: {
|
|
4057
|
+
fontSize: config.labelSize,
|
|
4058
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
4059
|
+
textAlign: "center",
|
|
4060
|
+
margin: 0,
|
|
4061
|
+
maxWidth: `${config.width + 20}px`,
|
|
4062
|
+
} }, label)));
|
|
4063
|
+
};
|
|
4064
|
+
|
|
4065
|
+
/**
|
|
4066
|
+
* Portfolio Health Report
|
|
4067
|
+
* Aggregated metric health across the entire portfolio
|
|
4068
|
+
*/
|
|
4069
|
+
const PortfolioHealth = ({ theme: themeProp, defaultDateRange = "past-month", }) => {
|
|
4070
|
+
var _a, _b, _c, _d;
|
|
4071
|
+
const { api, theme: contextTheme } = useReactBridgeContext();
|
|
4072
|
+
const theme = themeProp || contextTheme;
|
|
4073
|
+
const dateRange = useDateRange(defaultDateRange);
|
|
4074
|
+
const { data, loading, error } = useReportData(() => api.getPortfolioHealth({
|
|
4075
|
+
startDate: dateRange.startDate,
|
|
4076
|
+
endDate: dateRange.endDate,
|
|
4077
|
+
}), [dateRange.startDate, dateRange.endDate]);
|
|
4078
|
+
const metricTableColumns = [
|
|
4079
|
+
{
|
|
4080
|
+
key: "metricName",
|
|
4081
|
+
label: "Metric Name",
|
|
4082
|
+
sortable: true,
|
|
4083
|
+
},
|
|
4084
|
+
{
|
|
4085
|
+
key: "score",
|
|
4086
|
+
label: "Score (0-10)",
|
|
4087
|
+
sortable: true,
|
|
4088
|
+
render: (value) => value.toFixed(2),
|
|
4089
|
+
},
|
|
4090
|
+
{
|
|
4091
|
+
key: "status",
|
|
4092
|
+
label: "Status",
|
|
4093
|
+
sortable: true,
|
|
4094
|
+
render: (value) => {
|
|
4095
|
+
const color = value === "ok"
|
|
4096
|
+
? theme === null || theme === void 0 ? void 0 : theme.colors.success
|
|
4097
|
+
: value === "warning"
|
|
4098
|
+
? "#F59E0B"
|
|
4099
|
+
: theme === null || theme === void 0 ? void 0 : theme.colors.error;
|
|
4100
|
+
return (React.createElement("span", { style: { color, fontWeight: "600" } }, value.toUpperCase()));
|
|
4101
|
+
},
|
|
4102
|
+
},
|
|
4103
|
+
{
|
|
4104
|
+
key: "currentValue",
|
|
4105
|
+
label: "Current Value",
|
|
4106
|
+
sortable: true,
|
|
4107
|
+
render: (value) => value.toFixed(2),
|
|
4108
|
+
},
|
|
4109
|
+
{
|
|
4110
|
+
key: "targetValue",
|
|
4111
|
+
label: "Target Value",
|
|
4112
|
+
sortable: true,
|
|
4113
|
+
render: (value) => value.toFixed(2),
|
|
4114
|
+
},
|
|
4115
|
+
{
|
|
4116
|
+
key: "variance",
|
|
4117
|
+
label: "Variance",
|
|
4118
|
+
sortable: true,
|
|
4119
|
+
render: (value) => {
|
|
4120
|
+
const color = value > 0 ? theme === null || theme === void 0 ? void 0 : theme.colors.success : theme === null || theme === void 0 ? void 0 : theme.colors.error;
|
|
4121
|
+
return (React.createElement("span", { style: { color, fontWeight: "600" } }, value.toFixed(2)));
|
|
4122
|
+
},
|
|
4123
|
+
},
|
|
4124
|
+
];
|
|
4125
|
+
const criticalTableColumns = [
|
|
4126
|
+
{
|
|
4127
|
+
key: "metricName",
|
|
4128
|
+
label: "Metric Name",
|
|
4129
|
+
sortable: true,
|
|
4130
|
+
},
|
|
4131
|
+
{
|
|
4132
|
+
key: "description",
|
|
4133
|
+
label: "Description",
|
|
4134
|
+
sortable: true,
|
|
4135
|
+
},
|
|
4136
|
+
{
|
|
4137
|
+
key: "recommendedAction",
|
|
4138
|
+
label: "Recommended Action",
|
|
4139
|
+
sortable: false,
|
|
4140
|
+
},
|
|
4141
|
+
];
|
|
4142
|
+
return (React.createElement(ReportLayout, { title: "Portfolio Health", description: "Aggregated metric health and critical alerts", selectedDateRange: dateRange.selectedOption, onDateRangeChange: dateRange.setSelectedOption, loading: loading, error: error, theme: theme },
|
|
4143
|
+
React.createElement("div", { style: {
|
|
4144
|
+
display: "flex",
|
|
4145
|
+
justifyContent: "center",
|
|
4146
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px",
|
|
4147
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4148
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
4149
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
4150
|
+
} },
|
|
4151
|
+
React.createElement(HealthGauge, { score: (_a = data === null || data === void 0 ? void 0 : data.overallHealthScore) !== null && _a !== void 0 ? _a : 0, label: "Overall Portfolio Health", size: "large", theme: theme })),
|
|
4152
|
+
React.createElement("div", { style: {
|
|
4153
|
+
display: "grid",
|
|
4154
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))",
|
|
4155
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4156
|
+
} },
|
|
4157
|
+
React.createElement("div", { style: {
|
|
4158
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4159
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4160
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
4161
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
4162
|
+
textAlign: "center",
|
|
4163
|
+
} },
|
|
4164
|
+
React.createElement("p", { style: {
|
|
4165
|
+
margin: 0,
|
|
4166
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
4167
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
4168
|
+
marginBottom: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
4169
|
+
} }, "Healthy Metrics"),
|
|
4170
|
+
React.createElement("p", { style: {
|
|
4171
|
+
margin: 0,
|
|
4172
|
+
fontSize: "24px",
|
|
4173
|
+
fontWeight: "bold",
|
|
4174
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.success) || "#4caf50",
|
|
4175
|
+
} }, (_b = data === null || data === void 0 ? void 0 : data.healthyMetricsCount) !== null && _b !== void 0 ? _b : 0)),
|
|
4176
|
+
React.createElement("div", { style: {
|
|
4177
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4178
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4179
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
4180
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
4181
|
+
textAlign: "center",
|
|
4182
|
+
} },
|
|
4183
|
+
React.createElement("p", { style: {
|
|
4184
|
+
margin: 0,
|
|
4185
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
4186
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
4187
|
+
marginBottom: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
4188
|
+
} }, "Warning Metrics"),
|
|
4189
|
+
React.createElement("p", { style: {
|
|
4190
|
+
margin: 0,
|
|
4191
|
+
fontSize: "24px",
|
|
4192
|
+
fontWeight: "bold",
|
|
4193
|
+
color: "#F59E0B",
|
|
4194
|
+
} }, (_c = data === null || data === void 0 ? void 0 : data.warningMetricsCount) !== null && _c !== void 0 ? _c : 0)),
|
|
4195
|
+
React.createElement("div", { style: {
|
|
4196
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4197
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4198
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
4199
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
4200
|
+
textAlign: "center",
|
|
4201
|
+
} },
|
|
4202
|
+
React.createElement("p", { style: {
|
|
4203
|
+
margin: 0,
|
|
4204
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
4205
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
4206
|
+
marginBottom: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
4207
|
+
} }, "Critical Metrics"),
|
|
4208
|
+
React.createElement("p", { style: {
|
|
4209
|
+
margin: 0,
|
|
4210
|
+
fontSize: "24px",
|
|
4211
|
+
fontWeight: "bold",
|
|
4212
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.error) || "#f44336",
|
|
4213
|
+
} }, (_d = data === null || data === void 0 ? void 0 : data.criticalMetricsCount) !== null && _d !== void 0 ? _d : 0))),
|
|
4214
|
+
(data === null || data === void 0 ? void 0 : data.metricHealthScores) && data.metricHealthScores.length > 0 && (React.createElement("div", null,
|
|
4215
|
+
React.createElement(DataTable, { title: "Metric Health Scores", columns: metricTableColumns, data: data.metricHealthScores.map((metric, idx) => (Object.assign({ id: idx }, metric))), pageSize: 10, theme: theme }))),
|
|
4216
|
+
(data === null || data === void 0 ? void 0 : data.criticalMetrics) && data.criticalMetrics.length > 0 && (React.createElement("div", null,
|
|
4217
|
+
React.createElement(DataTable, { title: "Critical Metrics Requiring Attention", columns: criticalTableColumns, data: data.criticalMetrics.map((metric, idx) => (Object.assign({ id: idx }, metric))), pageSize: 5, theme: theme })))));
|
|
4218
|
+
};
|
|
4219
|
+
|
|
4220
|
+
/**
|
|
4221
|
+
* Data Quality & Automation Report
|
|
4222
|
+
* Data gaps, automation opportunities, and actionable recommendations
|
|
4223
|
+
*/
|
|
4224
|
+
const DataQualityAutomation = ({ theme: themeProp, defaultDateRange = "past-month", }) => {
|
|
4225
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
4226
|
+
const { api, theme: contextTheme } = useReactBridgeContext();
|
|
4227
|
+
const theme = themeProp || contextTheme;
|
|
4228
|
+
const dateRange = useDateRange(defaultDateRange);
|
|
4229
|
+
const { data, loading, error } = useReportData(() => api.getDataQualityAutomation({
|
|
4230
|
+
startDate: dateRange.startDate,
|
|
4231
|
+
endDate: dateRange.endDate,
|
|
4232
|
+
}), [dateRange.startDate, dateRange.endDate]);
|
|
4233
|
+
const dataGapColumns = [
|
|
4234
|
+
{
|
|
4235
|
+
key: "fieldName",
|
|
4236
|
+
label: "Field Name",
|
|
4237
|
+
sortable: true,
|
|
4238
|
+
},
|
|
4239
|
+
{
|
|
4240
|
+
key: "missingRecords",
|
|
4241
|
+
label: "Missing Records",
|
|
4242
|
+
sortable: true,
|
|
4243
|
+
},
|
|
4244
|
+
{
|
|
4245
|
+
key: "missingPercentage",
|
|
4246
|
+
label: "Missing %",
|
|
4247
|
+
sortable: true,
|
|
4248
|
+
render: (value) => `${value.toFixed(2)}%`,
|
|
4249
|
+
},
|
|
4250
|
+
{
|
|
4251
|
+
key: "impact",
|
|
4252
|
+
label: "Impact",
|
|
4253
|
+
sortable: false,
|
|
4254
|
+
},
|
|
4255
|
+
];
|
|
4256
|
+
const recommendationColumns = [
|
|
4257
|
+
{
|
|
4258
|
+
key: "category",
|
|
4259
|
+
label: "Category",
|
|
4260
|
+
sortable: true,
|
|
4261
|
+
},
|
|
4262
|
+
{
|
|
4263
|
+
key: "recommendation",
|
|
4264
|
+
label: "Recommendation",
|
|
4265
|
+
sortable: true,
|
|
4266
|
+
},
|
|
4267
|
+
{
|
|
4268
|
+
key: "priority",
|
|
4269
|
+
label: "Priority",
|
|
4270
|
+
sortable: true,
|
|
4271
|
+
render: (value) => {
|
|
4272
|
+
const color = value === "High"
|
|
4273
|
+
? theme === null || theme === void 0 ? void 0 : theme.colors.error
|
|
4274
|
+
: value === "Medium"
|
|
4275
|
+
? "#F59E0B"
|
|
4276
|
+
: theme === null || theme === void 0 ? void 0 : theme.colors.success;
|
|
4277
|
+
return React.createElement("span", { style: { color, fontWeight: "600" } }, value);
|
|
4278
|
+
},
|
|
4279
|
+
},
|
|
4280
|
+
{
|
|
4281
|
+
key: "expectedBenefit",
|
|
4282
|
+
label: "Expected Benefit",
|
|
4283
|
+
sortable: false,
|
|
4284
|
+
},
|
|
4285
|
+
];
|
|
4286
|
+
return (React.createElement(ReportLayout, { title: "Data Quality & Automation", description: "Data quality assessment and automation opportunities", selectedDateRange: dateRange.selectedOption, onDateRangeChange: dateRange.setSelectedOption, loading: loading, error: error, theme: theme },
|
|
4287
|
+
React.createElement("div", { style: {
|
|
4288
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4289
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4290
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
4291
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
4292
|
+
} },
|
|
4293
|
+
React.createElement("h3", { style: {
|
|
4294
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
4295
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.lg) || "18px",
|
|
4296
|
+
fontWeight: "600",
|
|
4297
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
4298
|
+
} }, "Data Quality Assessment"),
|
|
4299
|
+
React.createElement("div", { style: {
|
|
4300
|
+
display: "flex",
|
|
4301
|
+
flexDirection: "column",
|
|
4302
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4303
|
+
} },
|
|
4304
|
+
React.createElement(ProgressBar, { percentage: (_a = data === null || data === void 0 ? void 0 : data.overallDataQualityScore) !== null && _a !== void 0 ? _a : 0, label: "Overall Quality Score", theme: theme, height: "large" }),
|
|
4305
|
+
React.createElement(ProgressBar, { percentage: (_b = data === null || data === void 0 ? void 0 : data.completenessScore) !== null && _b !== void 0 ? _b : 0, label: "Completeness", theme: theme }),
|
|
4306
|
+
React.createElement(ProgressBar, { percentage: (_c = data === null || data === void 0 ? void 0 : data.accuracyScore) !== null && _c !== void 0 ? _c : 0, label: "Accuracy", theme: theme }),
|
|
4307
|
+
React.createElement(ProgressBar, { percentage: (_d = data === null || data === void 0 ? void 0 : data.consistencyScore) !== null && _d !== void 0 ? _d : 0, label: "Consistency", theme: theme })),
|
|
4308
|
+
React.createElement("div", { style: {
|
|
4309
|
+
display: "grid",
|
|
4310
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))",
|
|
4311
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4312
|
+
marginTop: (theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px",
|
|
4313
|
+
} },
|
|
4314
|
+
React.createElement("div", { style: {
|
|
4315
|
+
textAlign: "center",
|
|
4316
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
4317
|
+
} },
|
|
4318
|
+
React.createElement("p", { style: {
|
|
4319
|
+
margin: 0,
|
|
4320
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
4321
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
4322
|
+
} }, "Total Records Analyzed"),
|
|
4323
|
+
React.createElement("p", { style: {
|
|
4324
|
+
margin: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0 0 0`,
|
|
4325
|
+
fontSize: "20px",
|
|
4326
|
+
fontWeight: "600",
|
|
4327
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
4328
|
+
} }, (_e = data === null || data === void 0 ? void 0 : data.totalRecordsAnalyzed) !== null && _e !== void 0 ? _e : 0)),
|
|
4329
|
+
React.createElement("div", { style: {
|
|
4330
|
+
textAlign: "center",
|
|
4331
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
4332
|
+
} },
|
|
4333
|
+
React.createElement("p", { style: {
|
|
4334
|
+
margin: 0,
|
|
4335
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
4336
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
4337
|
+
} }, "Records with Issues"),
|
|
4338
|
+
React.createElement("p", { style: {
|
|
4339
|
+
margin: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0 0 0`,
|
|
4340
|
+
fontSize: "20px",
|
|
4341
|
+
fontWeight: "600",
|
|
4342
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.error) || "#f44336",
|
|
4343
|
+
} }, (_f = data === null || data === void 0 ? void 0 : data.recordsWithIssues) !== null && _f !== void 0 ? _f : 0)))),
|
|
4344
|
+
React.createElement("div", { style: {
|
|
4345
|
+
display: "grid",
|
|
4346
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
|
4347
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4348
|
+
} },
|
|
4349
|
+
React.createElement("div", { style: {
|
|
4350
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4351
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4352
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
4353
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
4354
|
+
} },
|
|
4355
|
+
React.createElement("h4", { style: {
|
|
4356
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
4357
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
4358
|
+
fontWeight: "600",
|
|
4359
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
4360
|
+
} }, "Automation Readiness"),
|
|
4361
|
+
React.createElement("div", { style: {
|
|
4362
|
+
marginBottom: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4363
|
+
} },
|
|
4364
|
+
React.createElement(ProgressBar, { percentage: (_h = (_g = data === null || data === void 0 ? void 0 : data.automationOpportunities) === null || _g === void 0 ? void 0 : _g.automationReadinessScore) !== null && _h !== void 0 ? _h : 0, label: "Readiness Score", theme: theme, height: "large" })),
|
|
4365
|
+
React.createElement("div", { style: {
|
|
4366
|
+
display: "flex",
|
|
4367
|
+
flexDirection: "column",
|
|
4368
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px",
|
|
4369
|
+
} },
|
|
4370
|
+
React.createElement("div", { style: {
|
|
4371
|
+
display: "flex",
|
|
4372
|
+
justifyContent: "space-between",
|
|
4373
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
4374
|
+
} },
|
|
4375
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Fully Automatable:"),
|
|
4376
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_k = (_j = data === null || data === void 0 ? void 0 : data.automationOpportunities) === null || _j === void 0 ? void 0 : _j.fullyAutomatable) !== null && _k !== void 0 ? _k : 0)),
|
|
4377
|
+
React.createElement("div", { style: {
|
|
4378
|
+
display: "flex",
|
|
4379
|
+
justifyContent: "space-between",
|
|
4380
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
4381
|
+
} },
|
|
4382
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Partially Automatable:"),
|
|
4383
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_m = (_l = data === null || data === void 0 ? void 0 : data.automationOpportunities) === null || _l === void 0 ? void 0 : _l.partiallyAutomatable) !== null && _m !== void 0 ? _m : 0)),
|
|
4384
|
+
React.createElement("div", { style: {
|
|
4385
|
+
display: "flex",
|
|
4386
|
+
justifyContent: "space-between",
|
|
4387
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px"} 0`,
|
|
4388
|
+
} },
|
|
4389
|
+
React.createElement("span", { style: { color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666" } }, "Manual Only:"),
|
|
4390
|
+
React.createElement("span", { style: { fontWeight: "600" } }, (_p = (_o = data === null || data === void 0 ? void 0 : data.automationOpportunities) === null || _o === void 0 ? void 0 : _o.manualOnly) !== null && _p !== void 0 ? _p : 0))))),
|
|
4391
|
+
(data === null || data === void 0 ? void 0 : data.identifiedDataGaps) && data.identifiedDataGaps.length > 0 && (React.createElement("div", null,
|
|
4392
|
+
React.createElement(DataTable, { title: "Identified Data Gaps", columns: dataGapColumns, data: data.identifiedDataGaps.map((gap, idx) => (Object.assign({ id: idx }, gap))), pageSize: 10, theme: theme }))),
|
|
4393
|
+
(data === null || data === void 0 ? void 0 : data.recommendations) && data.recommendations.length > 0 && (React.createElement("div", null,
|
|
4394
|
+
React.createElement(DataTable, { title: "Recommendations", columns: recommendationColumns, data: data.recommendations.map((rec, idx) => (Object.assign({ id: idx }, rec))), pageSize: 10, theme: theme })))));
|
|
4395
|
+
};
|
|
4396
|
+
|
|
4397
|
+
/**
|
|
4398
|
+
* Reports Dashboard
|
|
4399
|
+
* Main container that displays all 5 reports with navigation
|
|
4400
|
+
*/
|
|
4401
|
+
const REPORTS = [
|
|
4402
|
+
{
|
|
4403
|
+
id: "executive",
|
|
4404
|
+
label: "Executive Dashboard",
|
|
4405
|
+
icon: "📊",
|
|
4406
|
+
description: "High-level directive execution metrics",
|
|
4407
|
+
},
|
|
4408
|
+
{
|
|
4409
|
+
id: "risk",
|
|
4410
|
+
label: "Supply Chain Risk",
|
|
4411
|
+
icon: "⚠️",
|
|
4412
|
+
description: "Risk analysis and supply chain health",
|
|
4413
|
+
},
|
|
4414
|
+
{
|
|
4415
|
+
id: "audit",
|
|
4416
|
+
label: "Execution Audit",
|
|
4417
|
+
icon: "✓",
|
|
4418
|
+
description: "Directive execution history and audit trail",
|
|
4419
|
+
},
|
|
4420
|
+
{
|
|
4421
|
+
id: "health",
|
|
4422
|
+
label: "Portfolio Health",
|
|
4423
|
+
icon: "❤️",
|
|
4424
|
+
description: "Portfolio metric health and critical alerts",
|
|
4425
|
+
},
|
|
4426
|
+
{
|
|
4427
|
+
id: "quality",
|
|
4428
|
+
label: "Data Quality",
|
|
4429
|
+
icon: "📈",
|
|
4430
|
+
description: "Data quality and automation assessment",
|
|
4431
|
+
},
|
|
4432
|
+
];
|
|
4433
|
+
const ReportsDashboard = ({ theme, initialReport = "executive", }) => {
|
|
4434
|
+
const [activeReport, setActiveReport] = useState(initialReport);
|
|
4435
|
+
const containerStyle = {
|
|
4436
|
+
minHeight: "100vh",
|
|
4437
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.background) || "#fff",
|
|
4438
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
4439
|
+
};
|
|
4440
|
+
const navContainerStyle = {
|
|
4441
|
+
borderBottom: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4442
|
+
backgroundColor: (theme === null || theme === void 0 ? void 0 : theme.colors.surface) || "#f5f5f5",
|
|
4443
|
+
};
|
|
4444
|
+
const navWrapperStyle = {
|
|
4445
|
+
maxWidth: "1400px",
|
|
4446
|
+
margin: "0 auto",
|
|
4447
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} ${(theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px"}`,
|
|
4448
|
+
display: "flex",
|
|
4449
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px",
|
|
4450
|
+
overflowX: "auto",
|
|
4451
|
+
scrollBehavior: "smooth",
|
|
4452
|
+
};
|
|
4453
|
+
const navButtonStyle = (isActive) => ({
|
|
4454
|
+
padding: `${(theme === null || theme === void 0 ? void 0 : theme.spacing.sm) || "8px"} ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"}`,
|
|
4455
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.sm) || "14px",
|
|
4456
|
+
fontWeight: "500",
|
|
4457
|
+
borderRadius: (theme === null || theme === void 0 ? void 0 : theme.borderRadius) || "8px",
|
|
4458
|
+
border: "none",
|
|
4459
|
+
cursor: "pointer",
|
|
4460
|
+
transition: "all 0.2s ease",
|
|
4461
|
+
backgroundColor: isActive
|
|
4462
|
+
? (theme === null || theme === void 0 ? void 0 : theme.colors.primary) || "#1976d2"
|
|
4463
|
+
: (theme === null || theme === void 0 ? void 0 : theme.colors.background) || "#fff",
|
|
4464
|
+
color: isActive ? "#fff" : (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
4465
|
+
whiteSpace: "nowrap",
|
|
4466
|
+
display: "flex",
|
|
4467
|
+
alignItems: "center",
|
|
4468
|
+
gap: (theme === null || theme === void 0 ? void 0 : theme.spacing.xs) || "4px",
|
|
4469
|
+
});
|
|
4470
|
+
const contentStyle = {
|
|
4471
|
+
maxWidth: "1400px",
|
|
4472
|
+
margin: "0 auto",
|
|
4473
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px",
|
|
4474
|
+
};
|
|
4475
|
+
return (React.createElement("div", { style: containerStyle },
|
|
4476
|
+
React.createElement("div", { style: navContainerStyle },
|
|
4477
|
+
React.createElement("div", { style: navWrapperStyle }, REPORTS.map((report) => (React.createElement("button", { key: report.id, onClick: () => setActiveReport(report.id), style: navButtonStyle(activeReport === report.id), title: report.description },
|
|
4478
|
+
React.createElement("span", null, report.icon),
|
|
4479
|
+
React.createElement("span", null, report.label)))))),
|
|
4480
|
+
React.createElement("div", { style: contentStyle },
|
|
4481
|
+
activeReport === "executive" && React.createElement(ExecutiveDashboard, { theme: theme }),
|
|
4482
|
+
activeReport === "risk" && React.createElement(SupplyChainRisk, { theme: theme }),
|
|
4483
|
+
activeReport === "audit" && React.createElement(ExecutionAuditTrail, { theme: theme }),
|
|
4484
|
+
activeReport === "health" && React.createElement(PortfolioHealth, { theme: theme }),
|
|
4485
|
+
activeReport === "quality" && React.createElement(DataQualityAutomation, { theme: theme }))));
|
|
4486
|
+
};
|
|
4487
|
+
|
|
4488
|
+
/**
|
|
4489
|
+
* RiskMatrix Component
|
|
4490
|
+
* 2D visualization of risk items (probability vs impact)
|
|
4491
|
+
*/
|
|
4492
|
+
const RiskMatrix = ({ items, title, height = 300, theme, }) => {
|
|
4493
|
+
if (!items || items.length === 0) {
|
|
4494
|
+
return (React.createElement("div", { style: {
|
|
4495
|
+
padding: (theme === null || theme === void 0 ? void 0 : theme.spacing.lg) || "24px",
|
|
4496
|
+
textAlign: "center",
|
|
4497
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666",
|
|
4498
|
+
} }, "No risk data available"));
|
|
4499
|
+
}
|
|
4500
|
+
const padding = 50;
|
|
4501
|
+
const width = 500;
|
|
4502
|
+
const innerWidth = width - padding * 2;
|
|
4503
|
+
const innerHeight = height - padding * 2;
|
|
4504
|
+
const getRiskColor = (severity) => {
|
|
4505
|
+
if (severity === "high")
|
|
4506
|
+
return (theme === null || theme === void 0 ? void 0 : theme.colors.error) || "#ef4444";
|
|
4507
|
+
if (severity === "medium")
|
|
4508
|
+
return "#F59E0B";
|
|
4509
|
+
return (theme === null || theme === void 0 ? void 0 : theme.colors.success) || "#10B981";
|
|
4510
|
+
};
|
|
4511
|
+
return (React.createElement("div", null,
|
|
4512
|
+
title && (React.createElement("h3", { style: {
|
|
4513
|
+
margin: `0 0 ${(theme === null || theme === void 0 ? void 0 : theme.spacing.md) || "16px"} 0`,
|
|
4514
|
+
fontSize: (theme === null || theme === void 0 ? void 0 : theme.fontSizes.md) || "16px",
|
|
4515
|
+
fontWeight: "600",
|
|
4516
|
+
color: (theme === null || theme === void 0 ? void 0 : theme.colors.text) || "#000",
|
|
4517
|
+
} }, title)),
|
|
4518
|
+
React.createElement("svg", { width: "100%", height: height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "xMidYMid meet", style: {
|
|
4519
|
+
border: `1px solid ${(theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0"}`,
|
|
4520
|
+
borderRadius: "8px",
|
|
4521
|
+
} },
|
|
4522
|
+
React.createElement("rect", { x: padding + innerWidth / 2, y: padding, width: innerWidth / 2, height: innerHeight / 2, fill: "#ef4444", opacity: "0.05" }),
|
|
4523
|
+
React.createElement("rect", { x: padding, y: padding, width: innerWidth / 2, height: innerHeight / 2, fill: "#F59E0B", opacity: "0.05" }),
|
|
4524
|
+
React.createElement("rect", { x: padding + innerWidth / 2, y: padding + innerHeight / 2, width: innerWidth / 2, height: innerHeight / 2, fill: "#F59E0B", opacity: "0.05" }),
|
|
4525
|
+
React.createElement("rect", { x: padding, y: padding + innerHeight / 2, width: innerWidth / 2, height: innerHeight / 2, fill: "#10B981", opacity: "0.05" }),
|
|
4526
|
+
React.createElement("line", { x1: padding, y1: padding + innerHeight / 2, x2: width - padding, y2: padding + innerHeight / 2, stroke: (theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0", strokeWidth: "2" }),
|
|
4527
|
+
React.createElement("line", { x1: padding + innerWidth / 2, y1: padding, x2: padding + innerWidth / 2, y2: height - padding, stroke: (theme === null || theme === void 0 ? void 0 : theme.colors.border) || "#e0e0e0", strokeWidth: "2" }),
|
|
4528
|
+
React.createElement("text", { x: width / 2, y: height - 5, textAnchor: "middle", fontSize: "12", fill: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666", fontWeight: "500" }, "Probability"),
|
|
4529
|
+
React.createElement("text", { x: 15, y: height / 2, textAnchor: "middle", fontSize: "12", fill: (theme === null || theme === void 0 ? void 0 : theme.colors.textSecondary) || "#666", fontWeight: "500", transform: `rotate(-90 15 ${height / 2})` }, "Impact"),
|
|
4530
|
+
items.map((item) => {
|
|
4531
|
+
const x = padding + (item.probability / 100) * innerWidth;
|
|
4532
|
+
const y = padding + innerHeight - (item.impact / 100) * innerHeight;
|
|
4533
|
+
return (React.createElement("g", { key: item.id },
|
|
4534
|
+
React.createElement("circle", { cx: x, cy: y, r: "6", fill: getRiskColor(item.severity), opacity: "0.7", style: { cursor: "pointer" } }),
|
|
4535
|
+
React.createElement("circle", { cx: x, cy: y, r: "6", fill: "none", stroke: getRiskColor(item.severity), strokeWidth: "2", opacity: "0.4" })));
|
|
4536
|
+
}))));
|
|
4537
|
+
};
|
|
4538
|
+
|
|
4539
|
+
export { AnalyticsAPI, AnalyticsDashboard, AnalyticsReport, AnalyticsWidget, DataQualityAutomation, DataTable, DateRangeSelector, DateRangeService, DirectivesPanel, ExecutionAuditTrail, ExecutiveDashboard, HealthGauge, LoadingState, MetricCard, MetricsPanel, ObservationsPanel, PortfolioHealth, ProgressBar, ReactBridgeAPI, ReactBridgeChatbox, ReactBridgeProvider, ReactBridgeSearch, ReportLayout, ReportsDashboard, RiskMatrix, StatusBadge, SupplyChainRisk, TrendChart, WebSpeechSTTProvider, WebSpeechTTSProvider, createCustomTheme, darkTheme, getTheme, lightTheme, useAnalyticsConfigs, useAnalyticsResult, useDateRange, useDirectiveAction, useReactBridge, useReactBridgeContext, useReportData };
|
|
2591
4540
|
//# sourceMappingURL=index.esm.js.map
|