@optifye/dashboard-core 6.10.51 → 6.10.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +82 -22
- package/dist/index.mjs +82 -22
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -5590,6 +5590,25 @@ var CLIP_COUNTS_CACHE_TTL = 30 * 1e3;
|
|
|
5590
5590
|
var CLIP_BY_ID_CACHE_TTL = 2 * 60 * 1e3;
|
|
5591
5591
|
var CLIP_BY_ID_CACHE_MAX = 200;
|
|
5592
5592
|
var CLIPS_INIT_CACHE_TTL = 30 * 1e3;
|
|
5593
|
+
var parseFiniteNumber = (value) => {
|
|
5594
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5595
|
+
return value;
|
|
5596
|
+
}
|
|
5597
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
5598
|
+
const parsed = Number(value);
|
|
5599
|
+
if (Number.isFinite(parsed)) {
|
|
5600
|
+
return parsed;
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
return void 0;
|
|
5604
|
+
};
|
|
5605
|
+
var getClipCycleTimeFrames = (metadata) => {
|
|
5606
|
+
const flattenedCycleTime = parseFiniteNumber(metadata?.cycle_time);
|
|
5607
|
+
if (flattenedCycleTime !== void 0) {
|
|
5608
|
+
return flattenedCycleTime;
|
|
5609
|
+
}
|
|
5610
|
+
return parseFiniteNumber(metadata?.request?.metadata?.cycle_time);
|
|
5611
|
+
};
|
|
5593
5612
|
var getSupabaseClient = () => {
|
|
5594
5613
|
const existing = _getSupabaseInstanceOptional();
|
|
5595
5614
|
if (existing) {
|
|
@@ -6099,20 +6118,31 @@ var S3ClipsSupabaseService = class {
|
|
|
6099
6118
|
limit
|
|
6100
6119
|
});
|
|
6101
6120
|
console.log(`[S3ClipsSupabase] Fetched ${response.clips?.length || 0} ${type} clips`);
|
|
6102
|
-
const transformedClips = (response.clips || []).map((clip) =>
|
|
6103
|
-
id
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6121
|
+
const transformedClips = (response.clips || []).map((clip) => {
|
|
6122
|
+
const clipId = clip.id ?? clip.clip_id;
|
|
6123
|
+
const clipType = clip.type ?? clip.clip_type_name;
|
|
6124
|
+
const cycleTimeSeconds = parseFiniteNumber(clip.cycle_time_seconds) ?? (() => {
|
|
6125
|
+
const cycleTimeFrames = getClipCycleTimeFrames(clip.metadata);
|
|
6126
|
+
return cycleTimeFrames !== void 0 ? cycleTimeFrames / (clip.metadata?.playlist?.fps || 20) : void 0;
|
|
6127
|
+
})();
|
|
6128
|
+
return {
|
|
6129
|
+
id: clipId,
|
|
6130
|
+
src: clip.src ?? clip.playlist,
|
|
6131
|
+
// Current API returns src; keep playlist fallback for legacy responses
|
|
6132
|
+
timestamp: clip.timestamp,
|
|
6133
|
+
// Use pre-formatted timestamp from API (already in 12-hour format with seconds)
|
|
6134
|
+
severity: clip.severity ?? this.getSeverityFromClipType(clipType),
|
|
6135
|
+
description: clip.description ?? this.getDescriptionFromClipType(clipType),
|
|
6136
|
+
type: clipType,
|
|
6137
|
+
originalUri: clip.originalUri ?? `clips:${clipId}`,
|
|
6138
|
+
cycle_time_seconds: cycleTimeSeconds,
|
|
6139
|
+
creation_timestamp: clip.creation_timestamp ?? clip.created_at,
|
|
6140
|
+
percentile: clip.percentile ?? clip.cycle_time_percentile ?? clip.idle_time_percentile,
|
|
6141
|
+
duration: clip.duration,
|
|
6142
|
+
idle_start_time: clip.idle_start_time,
|
|
6143
|
+
idle_end_time: clip.idle_end_time
|
|
6144
|
+
};
|
|
6145
|
+
});
|
|
6116
6146
|
return {
|
|
6117
6147
|
clips: transformedClips,
|
|
6118
6148
|
total: response.total || transformedClips.length,
|
|
@@ -6142,10 +6172,13 @@ var S3ClipsSupabaseService = class {
|
|
|
6142
6172
|
*/
|
|
6143
6173
|
getSeverityFromClipType(clipType) {
|
|
6144
6174
|
switch (clipType) {
|
|
6175
|
+
case "long_cycle_time":
|
|
6176
|
+
case "worst_cycle_time":
|
|
6145
6177
|
case "sop_deviations":
|
|
6146
6178
|
return "high";
|
|
6147
6179
|
case "idle_time":
|
|
6148
6180
|
return "medium";
|
|
6181
|
+
case "best_cycle_time":
|
|
6149
6182
|
case "cycle_completion":
|
|
6150
6183
|
default:
|
|
6151
6184
|
return "low";
|
|
@@ -6157,11 +6190,14 @@ var S3ClipsSupabaseService = class {
|
|
|
6157
6190
|
*/
|
|
6158
6191
|
getDescriptionFromClipType(clipType) {
|
|
6159
6192
|
const descriptions = {
|
|
6193
|
+
"long_cycle_time": "Long Cycle Time Detected",
|
|
6160
6194
|
"idle_time": "Idle Time Detected",
|
|
6195
|
+
"best_cycle_time": "Best Cycle Time Performance",
|
|
6196
|
+
"worst_cycle_time": "Worst Cycle Time Performance",
|
|
6161
6197
|
"sop_deviations": "SOP Deviations",
|
|
6162
6198
|
"cycle_completion": "Cycle Completion"
|
|
6163
6199
|
};
|
|
6164
|
-
return descriptions[clipType] || "Analysis Clip";
|
|
6200
|
+
return clipType ? descriptions[clipType] || "Analysis Clip" : "Analysis Clip";
|
|
6165
6201
|
}
|
|
6166
6202
|
};
|
|
6167
6203
|
|
|
@@ -62452,6 +62488,21 @@ var ShiftsView = ({
|
|
|
62452
62488
|
}
|
|
62453
62489
|
const factoryId = existingLineThreshold?.factory_id || lineFactoryId;
|
|
62454
62490
|
if (factoryId) {
|
|
62491
|
+
let resolvedLineThresholdSkuId = existingLineThreshold?.sku_id || null;
|
|
62492
|
+
if (!resolvedLineThresholdSkuId) {
|
|
62493
|
+
const { data: dummySkuRows, error: dummySkuError } = await supabase.from("skus").select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
|
|
62494
|
+
if (dummySkuError) {
|
|
62495
|
+
throw new Error(
|
|
62496
|
+
`Failed to resolve dummy SKU for line ${lineId}, shift ${shift.shiftId}: ${dummySkuError.message}`
|
|
62497
|
+
);
|
|
62498
|
+
}
|
|
62499
|
+
resolvedLineThresholdSkuId = dummySkuRows?.[0]?.id || null;
|
|
62500
|
+
}
|
|
62501
|
+
if (!resolvedLineThresholdSkuId) {
|
|
62502
|
+
throw new Error(
|
|
62503
|
+
`No active SKU found for line ${lineId}, shift ${shift.shiftId} while updating line thresholds`
|
|
62504
|
+
);
|
|
62505
|
+
}
|
|
62455
62506
|
const lineThresholdPayload = {
|
|
62456
62507
|
factory_id: factoryId,
|
|
62457
62508
|
line_id: lineId,
|
|
@@ -62459,12 +62510,10 @@ var ShiftsView = ({
|
|
|
62459
62510
|
shift_id: shift.shiftId,
|
|
62460
62511
|
product_code: existingLineThreshold?.product_code || "",
|
|
62461
62512
|
threshold_day_output: thresholdDayOutput,
|
|
62462
|
-
threshold_pph: thresholdPPH
|
|
62513
|
+
threshold_pph: thresholdPPH,
|
|
62514
|
+
sku_id: resolvedLineThresholdSkuId
|
|
62463
62515
|
};
|
|
62464
|
-
|
|
62465
|
-
lineThresholdPayload.sku_id = existingLineThreshold.sku_id;
|
|
62466
|
-
}
|
|
62467
|
-
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
62516
|
+
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id,sku_id" });
|
|
62468
62517
|
if (lineThresholdUpsertError) {
|
|
62469
62518
|
throw new Error(
|
|
62470
62519
|
`Failed to update line thresholds for line ${lineId}, shift ${shift.shiftId}: ${lineThresholdUpsertError.message}`
|
|
@@ -64252,6 +64301,17 @@ var TargetsView = ({
|
|
|
64252
64301
|
await workspaceService.updateActionThresholds(workspaceThresholdUpdates);
|
|
64253
64302
|
console.log(`[handleSaveLine] Successfully updated action thresholds for ${lineId}`);
|
|
64254
64303
|
const packagingWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "packaging");
|
|
64304
|
+
let resolvedLineThresholdSkuId = lineDataToSave.selectedSKU?.id || null;
|
|
64305
|
+
if (!resolvedLineThresholdSkuId) {
|
|
64306
|
+
const { data: dummySkuRows, error: dummySkuError } = await supabase.from("skus").select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
|
|
64307
|
+
if (dummySkuError) {
|
|
64308
|
+
throw dummySkuError;
|
|
64309
|
+
}
|
|
64310
|
+
resolvedLineThresholdSkuId = dummySkuRows?.[0]?.id || null;
|
|
64311
|
+
}
|
|
64312
|
+
if (!resolvedLineThresholdSkuId) {
|
|
64313
|
+
throw new Error(`No active SKU found for line ${lineId} while saving line thresholds`);
|
|
64314
|
+
}
|
|
64255
64315
|
const lineThresholdData = {
|
|
64256
64316
|
factory_id: currentFactoryId,
|
|
64257
64317
|
line_id: lineId,
|
|
@@ -64261,10 +64321,10 @@ var TargetsView = ({
|
|
|
64261
64321
|
threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
|
|
64262
64322
|
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
|
|
64263
64323
|
// Round each PPH value
|
|
64264
|
-
|
|
64324
|
+
sku_id: resolvedLineThresholdSkuId
|
|
64265
64325
|
};
|
|
64266
64326
|
console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
|
|
64267
|
-
const { error: lineUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdData, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
64327
|
+
const { error: lineUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdData, { onConflict: "factory_id,line_id,date,shift_id,sku_id" });
|
|
64268
64328
|
if (lineUpsertError) {
|
|
64269
64329
|
console.error(`[handleSaveLine] Error upserting line_thresholds for ${lineId}:`, lineUpsertError);
|
|
64270
64330
|
sonner.toast.error(`Failed to save line thresholds for ${mergedLineNames[lineId] || lineId}`);
|
package/dist/index.mjs
CHANGED
|
@@ -5561,6 +5561,25 @@ var CLIP_COUNTS_CACHE_TTL = 30 * 1e3;
|
|
|
5561
5561
|
var CLIP_BY_ID_CACHE_TTL = 2 * 60 * 1e3;
|
|
5562
5562
|
var CLIP_BY_ID_CACHE_MAX = 200;
|
|
5563
5563
|
var CLIPS_INIT_CACHE_TTL = 30 * 1e3;
|
|
5564
|
+
var parseFiniteNumber = (value) => {
|
|
5565
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5566
|
+
return value;
|
|
5567
|
+
}
|
|
5568
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
5569
|
+
const parsed = Number(value);
|
|
5570
|
+
if (Number.isFinite(parsed)) {
|
|
5571
|
+
return parsed;
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
return void 0;
|
|
5575
|
+
};
|
|
5576
|
+
var getClipCycleTimeFrames = (metadata) => {
|
|
5577
|
+
const flattenedCycleTime = parseFiniteNumber(metadata?.cycle_time);
|
|
5578
|
+
if (flattenedCycleTime !== void 0) {
|
|
5579
|
+
return flattenedCycleTime;
|
|
5580
|
+
}
|
|
5581
|
+
return parseFiniteNumber(metadata?.request?.metadata?.cycle_time);
|
|
5582
|
+
};
|
|
5564
5583
|
var getSupabaseClient = () => {
|
|
5565
5584
|
const existing = _getSupabaseInstanceOptional();
|
|
5566
5585
|
if (existing) {
|
|
@@ -6070,20 +6089,31 @@ var S3ClipsSupabaseService = class {
|
|
|
6070
6089
|
limit
|
|
6071
6090
|
});
|
|
6072
6091
|
console.log(`[S3ClipsSupabase] Fetched ${response.clips?.length || 0} ${type} clips`);
|
|
6073
|
-
const transformedClips = (response.clips || []).map((clip) =>
|
|
6074
|
-
id
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6092
|
+
const transformedClips = (response.clips || []).map((clip) => {
|
|
6093
|
+
const clipId = clip.id ?? clip.clip_id;
|
|
6094
|
+
const clipType = clip.type ?? clip.clip_type_name;
|
|
6095
|
+
const cycleTimeSeconds = parseFiniteNumber(clip.cycle_time_seconds) ?? (() => {
|
|
6096
|
+
const cycleTimeFrames = getClipCycleTimeFrames(clip.metadata);
|
|
6097
|
+
return cycleTimeFrames !== void 0 ? cycleTimeFrames / (clip.metadata?.playlist?.fps || 20) : void 0;
|
|
6098
|
+
})();
|
|
6099
|
+
return {
|
|
6100
|
+
id: clipId,
|
|
6101
|
+
src: clip.src ?? clip.playlist,
|
|
6102
|
+
// Current API returns src; keep playlist fallback for legacy responses
|
|
6103
|
+
timestamp: clip.timestamp,
|
|
6104
|
+
// Use pre-formatted timestamp from API (already in 12-hour format with seconds)
|
|
6105
|
+
severity: clip.severity ?? this.getSeverityFromClipType(clipType),
|
|
6106
|
+
description: clip.description ?? this.getDescriptionFromClipType(clipType),
|
|
6107
|
+
type: clipType,
|
|
6108
|
+
originalUri: clip.originalUri ?? `clips:${clipId}`,
|
|
6109
|
+
cycle_time_seconds: cycleTimeSeconds,
|
|
6110
|
+
creation_timestamp: clip.creation_timestamp ?? clip.created_at,
|
|
6111
|
+
percentile: clip.percentile ?? clip.cycle_time_percentile ?? clip.idle_time_percentile,
|
|
6112
|
+
duration: clip.duration,
|
|
6113
|
+
idle_start_time: clip.idle_start_time,
|
|
6114
|
+
idle_end_time: clip.idle_end_time
|
|
6115
|
+
};
|
|
6116
|
+
});
|
|
6087
6117
|
return {
|
|
6088
6118
|
clips: transformedClips,
|
|
6089
6119
|
total: response.total || transformedClips.length,
|
|
@@ -6113,10 +6143,13 @@ var S3ClipsSupabaseService = class {
|
|
|
6113
6143
|
*/
|
|
6114
6144
|
getSeverityFromClipType(clipType) {
|
|
6115
6145
|
switch (clipType) {
|
|
6146
|
+
case "long_cycle_time":
|
|
6147
|
+
case "worst_cycle_time":
|
|
6116
6148
|
case "sop_deviations":
|
|
6117
6149
|
return "high";
|
|
6118
6150
|
case "idle_time":
|
|
6119
6151
|
return "medium";
|
|
6152
|
+
case "best_cycle_time":
|
|
6120
6153
|
case "cycle_completion":
|
|
6121
6154
|
default:
|
|
6122
6155
|
return "low";
|
|
@@ -6128,11 +6161,14 @@ var S3ClipsSupabaseService = class {
|
|
|
6128
6161
|
*/
|
|
6129
6162
|
getDescriptionFromClipType(clipType) {
|
|
6130
6163
|
const descriptions = {
|
|
6164
|
+
"long_cycle_time": "Long Cycle Time Detected",
|
|
6131
6165
|
"idle_time": "Idle Time Detected",
|
|
6166
|
+
"best_cycle_time": "Best Cycle Time Performance",
|
|
6167
|
+
"worst_cycle_time": "Worst Cycle Time Performance",
|
|
6132
6168
|
"sop_deviations": "SOP Deviations",
|
|
6133
6169
|
"cycle_completion": "Cycle Completion"
|
|
6134
6170
|
};
|
|
6135
|
-
return descriptions[clipType] || "Analysis Clip";
|
|
6171
|
+
return clipType ? descriptions[clipType] || "Analysis Clip" : "Analysis Clip";
|
|
6136
6172
|
}
|
|
6137
6173
|
};
|
|
6138
6174
|
|
|
@@ -62423,6 +62459,21 @@ var ShiftsView = ({
|
|
|
62423
62459
|
}
|
|
62424
62460
|
const factoryId = existingLineThreshold?.factory_id || lineFactoryId;
|
|
62425
62461
|
if (factoryId) {
|
|
62462
|
+
let resolvedLineThresholdSkuId = existingLineThreshold?.sku_id || null;
|
|
62463
|
+
if (!resolvedLineThresholdSkuId) {
|
|
62464
|
+
const { data: dummySkuRows, error: dummySkuError } = await supabase.from("skus").select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
|
|
62465
|
+
if (dummySkuError) {
|
|
62466
|
+
throw new Error(
|
|
62467
|
+
`Failed to resolve dummy SKU for line ${lineId}, shift ${shift.shiftId}: ${dummySkuError.message}`
|
|
62468
|
+
);
|
|
62469
|
+
}
|
|
62470
|
+
resolvedLineThresholdSkuId = dummySkuRows?.[0]?.id || null;
|
|
62471
|
+
}
|
|
62472
|
+
if (!resolvedLineThresholdSkuId) {
|
|
62473
|
+
throw new Error(
|
|
62474
|
+
`No active SKU found for line ${lineId}, shift ${shift.shiftId} while updating line thresholds`
|
|
62475
|
+
);
|
|
62476
|
+
}
|
|
62426
62477
|
const lineThresholdPayload = {
|
|
62427
62478
|
factory_id: factoryId,
|
|
62428
62479
|
line_id: lineId,
|
|
@@ -62430,12 +62481,10 @@ var ShiftsView = ({
|
|
|
62430
62481
|
shift_id: shift.shiftId,
|
|
62431
62482
|
product_code: existingLineThreshold?.product_code || "",
|
|
62432
62483
|
threshold_day_output: thresholdDayOutput,
|
|
62433
|
-
threshold_pph: thresholdPPH
|
|
62484
|
+
threshold_pph: thresholdPPH,
|
|
62485
|
+
sku_id: resolvedLineThresholdSkuId
|
|
62434
62486
|
};
|
|
62435
|
-
|
|
62436
|
-
lineThresholdPayload.sku_id = existingLineThreshold.sku_id;
|
|
62437
|
-
}
|
|
62438
|
-
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
62487
|
+
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id,sku_id" });
|
|
62439
62488
|
if (lineThresholdUpsertError) {
|
|
62440
62489
|
throw new Error(
|
|
62441
62490
|
`Failed to update line thresholds for line ${lineId}, shift ${shift.shiftId}: ${lineThresholdUpsertError.message}`
|
|
@@ -64223,6 +64272,17 @@ var TargetsView = ({
|
|
|
64223
64272
|
await workspaceService.updateActionThresholds(workspaceThresholdUpdates);
|
|
64224
64273
|
console.log(`[handleSaveLine] Successfully updated action thresholds for ${lineId}`);
|
|
64225
64274
|
const packagingWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "packaging");
|
|
64275
|
+
let resolvedLineThresholdSkuId = lineDataToSave.selectedSKU?.id || null;
|
|
64276
|
+
if (!resolvedLineThresholdSkuId) {
|
|
64277
|
+
const { data: dummySkuRows, error: dummySkuError } = await supabase.from("skus").select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
|
|
64278
|
+
if (dummySkuError) {
|
|
64279
|
+
throw dummySkuError;
|
|
64280
|
+
}
|
|
64281
|
+
resolvedLineThresholdSkuId = dummySkuRows?.[0]?.id || null;
|
|
64282
|
+
}
|
|
64283
|
+
if (!resolvedLineThresholdSkuId) {
|
|
64284
|
+
throw new Error(`No active SKU found for line ${lineId} while saving line thresholds`);
|
|
64285
|
+
}
|
|
64226
64286
|
const lineThresholdData = {
|
|
64227
64287
|
factory_id: currentFactoryId,
|
|
64228
64288
|
line_id: lineId,
|
|
@@ -64232,10 +64292,10 @@ var TargetsView = ({
|
|
|
64232
64292
|
threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
|
|
64233
64293
|
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
|
|
64234
64294
|
// Round each PPH value
|
|
64235
|
-
|
|
64295
|
+
sku_id: resolvedLineThresholdSkuId
|
|
64236
64296
|
};
|
|
64237
64297
|
console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
|
|
64238
|
-
const { error: lineUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdData, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
64298
|
+
const { error: lineUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdData, { onConflict: "factory_id,line_id,date,shift_id,sku_id" });
|
|
64239
64299
|
if (lineUpsertError) {
|
|
64240
64300
|
console.error(`[handleSaveLine] Error upserting line_thresholds for ${lineId}:`, lineUpsertError);
|
|
64241
64301
|
toast.error(`Failed to save line thresholds for ${mergedLineNames[lineId] || lineId}`);
|