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