@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 CHANGED
@@ -1298,6 +1298,7 @@ interface S3ListObjectsParams {
1298
1298
  interface VideoMetadata {
1299
1299
  camera_id?: string;
1300
1300
  workspace_id?: string;
1301
+ cycle_time?: number;
1301
1302
  sop_name?: string;
1302
1303
  violation_start_frame?: number;
1303
1304
  violation_end_frame?: number;
package/dist/index.d.ts CHANGED
@@ -1298,6 +1298,7 @@ interface S3ListObjectsParams {
1298
1298
  interface VideoMetadata {
1299
1299
  camera_id?: string;
1300
1300
  workspace_id?: string;
1301
+ cycle_time?: number;
1301
1302
  sop_name?: string;
1302
1303
  violation_start_frame?: number;
1303
1304
  violation_end_frame?: number;
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: clip.clip_id,
6104
- src: clip.playlist,
6105
- // Raw playlist content
6106
- timestamp: clip.timestamp,
6107
- // Use pre-formatted timestamp from API (already in 12-hour format with seconds)
6108
- severity: this.getSeverityFromClipType(clip.clip_type_name),
6109
- description: this.getDescriptionFromClipType(clip.clip_type_name),
6110
- type: clip.clip_type_name,
6111
- originalUri: `clips:${clip.clip_id}`,
6112
- cycle_time_seconds: clip.metadata?.request?.metadata?.cycle_time ? clip.metadata.request.metadata.cycle_time / (clip.metadata?.playlist?.fps || 20) : void 0,
6113
- creation_timestamp: clip.created_at,
6114
- percentile: clip.cycle_time_percentile || clip.idle_time_percentile
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
- if (existingLineThreshold?.sku_id) {
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
- ...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
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: clip.clip_id,
6075
- src: clip.playlist,
6076
- // Raw playlist content
6077
- timestamp: clip.timestamp,
6078
- // Use pre-formatted timestamp from API (already in 12-hour format with seconds)
6079
- severity: this.getSeverityFromClipType(clip.clip_type_name),
6080
- description: this.getDescriptionFromClipType(clip.clip_type_name),
6081
- type: clip.clip_type_name,
6082
- originalUri: `clips:${clip.clip_id}`,
6083
- cycle_time_seconds: clip.metadata?.request?.metadata?.cycle_time ? clip.metadata.request.metadata.cycle_time / (clip.metadata?.playlist?.fps || 20) : void 0,
6084
- creation_timestamp: clip.created_at,
6085
- percentile: clip.cycle_time_percentile || clip.idle_time_percentile
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
- if (existingLineThreshold?.sku_id) {
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
- ...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
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}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.10.51",
3
+ "version": "6.10.53",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",