@7365admin1/module-hygiene 4.8.0 → 4.9.0

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.js CHANGED
@@ -84,7 +84,12 @@ module.exports = __toCommonJS(src_exports);
84
84
 
85
85
  // src/models/hygiene-base.model.ts
86
86
  var allowedTypes = ["common", "toilet"];
87
- var allowedStatus = ["ready", "ongoing", "completed"];
87
+ var allowedStatus = [
88
+ "open",
89
+ "ongoing",
90
+ "completed",
91
+ "closed"
92
+ ];
88
93
  var allowedPeriods = ["today", "thisWeek", "thisMonth"];
89
94
 
90
95
  // src/repositories/hygiene-dashboard.repository.ts
@@ -1366,10 +1371,14 @@ function useAreaService() {
1366
1371
  try {
1367
1372
  dataArray = JSON.parse(dataJson);
1368
1373
  } catch (error) {
1369
- throw new import_node_server_utils8.BadRequestError("Invalid JSON format for data in excel");
1374
+ throw new import_node_server_utils8.BadRequestError(
1375
+ "We couldn't read the uploaded file. Please make sure you're uploading the correct file and try again."
1376
+ );
1370
1377
  }
1371
1378
  if (!dataArray || dataArray.length === 0) {
1372
- throw new import_node_server_utils8.NotFoundError("No data found in the uploaded file");
1379
+ throw new import_node_server_utils8.NotFoundError(
1380
+ "The uploaded file is empty. Please make sure your file contains data and try again."
1381
+ );
1373
1382
  }
1374
1383
  let availableUnits = [];
1375
1384
  try {
@@ -1470,33 +1479,35 @@ function useAreaService() {
1470
1479
  }
1471
1480
  }
1472
1481
  }
1473
- let message = `Upload completed: ${insertedAreaIds.length} areas successfully created`;
1482
+ let message = `Upload complete! ${insertedAreaIds.length} ${insertedAreaIds.length === 1 ? "area was" : "areas were"} successfully added`;
1474
1483
  if (duplicateAreas.length > 0) {
1475
- message += `, ${duplicateAreas.length} areas skipped (already exist)`;
1484
+ message += `, ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area was" : "areas were"} skipped because they already exist`;
1476
1485
  }
1477
1486
  if (failedAreas.length > 0) {
1478
- message += `, ${failedAreas.length} areas failed`;
1487
+ message += `, ${failedAreas.length} ${failedAreas.length === 1 ? "area" : "areas"} could not be saved`;
1479
1488
  }
1480
1489
  if (skippedRows.length > 0) {
1481
- message += `, ${skippedRows.length} rows skipped (invalid data)`;
1490
+ message += `, ${skippedRows.length} ${skippedRows.length === 1 ? "row was" : "rows were"} skipped due to missing information`;
1482
1491
  }
1483
1492
  import_node_server_utils8.logger.info(message);
1484
1493
  if (insertedAreaIds.length === 0) {
1485
1494
  if (duplicateAreas.length > 0 && failedAreas.length === 0 && skippedRows.length === 0) {
1486
1495
  return {
1487
- message: `No new areas were created. All ${duplicateAreas.length} areas already exist in the system: ${duplicateAreas.join(", ")}`
1496
+ message: `No new areas were added. All ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area" : "areas"} in your file already exist in the system: ${duplicateAreas.join(
1497
+ ", "
1498
+ )}.`
1488
1499
  };
1489
1500
  } else if (failedAreas.length > 0) {
1490
1501
  throw new import_node_server_utils8.BadRequestError(
1491
- `No areas were created. ${failedAreas.length} areas failed due to errors. Please check your data format and ensure area names are valid.`
1502
+ `No areas were added. ${failedAreas.length} ${failedAreas.length === 1 ? "area" : "areas"} could not be saved. Please review the area names and information in your file, then try again.`
1492
1503
  );
1493
1504
  } else if (skippedRows.length > 0 && duplicateAreas.length === 0) {
1494
1505
  throw new import_node_server_utils8.BadRequestError(
1495
- `No areas were created. All ${skippedRows.length} rows contained invalid or missing data.`
1506
+ `No areas were added. ${skippedRows.length} ${skippedRows.length === 1 ? "row" : "rows"} in your file had missing or incomplete information. Please fill in all required fields and try again.`
1496
1507
  );
1497
1508
  } else {
1498
1509
  return {
1499
- message: `No new areas were created. ${duplicateAreas.length} areas already exist, ${skippedRows.length} rows had invalid data.`
1510
+ message: `No new areas were added. ${duplicateAreas.length} ${duplicateAreas.length === 1 ? "area" : "areas"} already exist${skippedRows.length > 0 ? `, and ${skippedRows.length} ${skippedRows.length === 1 ? "row" : "rows"} had missing or incomplete information` : ""}.`
1500
1511
  };
1501
1512
  }
1502
1513
  }
@@ -1507,11 +1518,11 @@ function useAreaService() {
1507
1518
  throw error;
1508
1519
  } else if (error.message.includes("validation")) {
1509
1520
  throw new import_node_server_utils8.BadRequestError(
1510
- "Upload failed due to invalid data format. Please check that all required fields are properly filled."
1521
+ "Upload failed because some required information is missing or incorrect. Please review your file and make sure all fields are filled in properly."
1511
1522
  );
1512
1523
  } else {
1513
1524
  throw new import_node_server_utils8.BadRequestError(
1514
- `Upload failed: ${error.message || "Please check your data format and try again."}`
1525
+ "Something went wrong while uploading. Please check your file and try again."
1515
1526
  );
1516
1527
  }
1517
1528
  }
@@ -1520,11 +1531,15 @@ function useAreaService() {
1520
1531
  try {
1521
1532
  const areas = await getAreasForChecklist(site);
1522
1533
  if (!areas || !Array.isArray(areas) || areas.length === 0) {
1523
- throw new import_node_server_utils8.BadRequestError("No data found to export");
1534
+ throw new import_node_server_utils8.BadRequestError(
1535
+ "There are no areas to export yet. Please add some areas first, then try again."
1536
+ );
1524
1537
  }
1525
1538
  const excelBuffer = await generateAreaExcel(areas);
1526
1539
  if (!excelBuffer || excelBuffer.length === 0) {
1527
- throw new Error("Generated Excel file is empty or invalid.");
1540
+ throw new Error(
1541
+ "Something went wrong while preparing your export file. Please try again."
1542
+ );
1528
1543
  }
1529
1544
  return excelBuffer;
1530
1545
  } catch (error) {
@@ -1824,10 +1839,14 @@ function useUnitService() {
1824
1839
  try {
1825
1840
  dataArray = JSON.parse(dataJson);
1826
1841
  } catch (error) {
1827
- throw new import_node_server_utils11.BadRequestError("Invalid JSON format for data in excel");
1842
+ throw new import_node_server_utils11.BadRequestError(
1843
+ "We couldn't read the uploaded file. Please make sure you're uploading the correct file and try again."
1844
+ );
1828
1845
  }
1829
1846
  if (!dataArray || dataArray.length === 0) {
1830
- throw new import_node_server_utils11.NotFoundError("No data found in the uploaded file");
1847
+ throw new import_node_server_utils11.NotFoundError(
1848
+ "The uploaded file is empty. Please make sure your file contains data and try again."
1849
+ );
1831
1850
  }
1832
1851
  const insertedUnitIds = [];
1833
1852
  const duplicateUnits = [];
@@ -1868,29 +1887,31 @@ function useUnitService() {
1868
1887
  }
1869
1888
  }
1870
1889
  }
1871
- let message = `Upload completed: ${insertedUnitIds.length} units successfully created`;
1890
+ let message = `Upload complete! ${insertedUnitIds.length} ${insertedUnitIds.length === 1 ? "unit was" : "units were"} successfully added`;
1872
1891
  if (duplicateUnits.length > 0) {
1873
- message += `, ${duplicateUnits.length} units skipped (already exist)`;
1892
+ message += `, ${duplicateUnits.length} ${duplicateUnits.length === 1 ? "unit was" : "units were"} skipped because they already exist`;
1874
1893
  }
1875
1894
  if (failedUnits.length > 0) {
1876
- message += `, ${failedUnits.length} units failed`;
1895
+ message += `, ${failedUnits.length} ${failedUnits.length === 1 ? "unit" : "units"} could not be saved`;
1877
1896
  }
1878
1897
  if (skippedRows.length > 0) {
1879
- message += `, ${skippedRows.length} rows skipped (invalid data)`;
1898
+ message += `, ${skippedRows.length} ${skippedRows.length === 1 ? "row was" : "rows were"} skipped due to missing information`;
1880
1899
  }
1881
1900
  import_node_server_utils11.logger.info(message);
1882
1901
  if (insertedUnitIds.length === 0) {
1883
1902
  if (duplicateUnits.length > 0 && failedUnits.length === 0) {
1884
1903
  throw new import_node_server_utils11.BadRequestError(
1885
- `No new units were created. All ${duplicateUnits.length} units already exist in the system: ${duplicateUnits.join(", ")}`
1904
+ `No new units were added. All ${duplicateUnits.length} ${duplicateUnits.length === 1 ? "unit" : "units"} in your file already exist in the system: ${duplicateUnits.join(
1905
+ ", "
1906
+ )}.`
1886
1907
  );
1887
1908
  } else if (failedUnits.length > 0) {
1888
1909
  throw new import_node_server_utils11.BadRequestError(
1889
- `No units were created. Please check your data format and ensure unit names are valid.`
1910
+ `No units were added. ${failedUnits.length} ${failedUnits.length === 1 ? "unit" : "units"} could not be saved. Please review the unit names in your file and try again.`
1890
1911
  );
1891
1912
  } else if (skippedRows.length > 0) {
1892
1913
  throw new import_node_server_utils11.BadRequestError(
1893
- `No units were created. All rows contained invalid or missing unit names.`
1914
+ `No units were added. ${skippedRows.length} ${skippedRows.length === 1 ? "row" : "rows"} in your file had missing or incomplete information. Please fill in all required fields and try again.`
1894
1915
  );
1895
1916
  }
1896
1917
  }
@@ -1901,15 +1922,15 @@ function useUnitService() {
1901
1922
  throw error;
1902
1923
  } else if (error.message.includes("duplicate")) {
1903
1924
  throw new import_node_server_utils11.BadRequestError(
1904
- "Upload failed due to duplicate unit names. Please ensure all unit names are unique."
1925
+ "Upload failed because some unit names are already taken. Please make sure each unit has a unique name and try again."
1905
1926
  );
1906
1927
  } else if (error.message.includes("validation")) {
1907
1928
  throw new import_node_server_utils11.BadRequestError(
1908
- "Upload failed due to invalid data format. Please check that all required fields are properly filled."
1929
+ "Upload failed because some required information is missing or incorrect. Please review your file and make sure all fields are filled in properly."
1909
1930
  );
1910
1931
  } else {
1911
1932
  throw new import_node_server_utils11.BadRequestError(
1912
- `Upload failed: ${error.message || "Please check your data format and try again."}`
1933
+ "Something went wrong while uploading. Please check your file and try again."
1913
1934
  );
1914
1935
  }
1915
1936
  }
@@ -1941,7 +1962,9 @@ function useUnitService() {
1941
1962
  session?.startTransaction();
1942
1963
  const isExistingArea = await verifyAreaByUnitId(_id);
1943
1964
  if (isExistingArea) {
1944
- throw new import_node_server_utils11.BadRequestError("Failed to delete unit, unit is in use.");
1965
+ throw new import_node_server_utils11.BadRequestError(
1966
+ "This unit can't be deleted because it's currently being used in one or more areas. Please remove it from those areas first, then try again."
1967
+ );
1945
1968
  }
1946
1969
  const result = await _deleteUnit(_id, session);
1947
1970
  await session?.commitTransaction();
@@ -1967,11 +1990,15 @@ function useUnitService() {
1967
1990
  site
1968
1991
  });
1969
1992
  if (!data || !data.items || data.items.length === 0) {
1970
- throw new import_node_server_utils11.BadRequestError("No data found to export");
1993
+ throw new import_node_server_utils11.BadRequestError(
1994
+ "There are no units to export yet. Please add some units first, then try again."
1995
+ );
1971
1996
  }
1972
1997
  const excelBuffer = await _generateUnitExcel(data.items);
1973
1998
  if (!excelBuffer || excelBuffer.length === 0) {
1974
- throw new Error("Generated Excel file is empty or invalid.");
1999
+ throw new Error(
2000
+ "Something went wrong while preparing your export file. Please try again."
2001
+ );
1975
2002
  }
1976
2003
  return excelBuffer;
1977
2004
  } catch (error) {
@@ -2178,7 +2205,7 @@ function MParentChecklist(value) {
2178
2205
  }
2179
2206
  return {
2180
2207
  site: value.site,
2181
- status: "ready",
2208
+ status: "open",
2182
2209
  createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
2183
2210
  updatedAt: value.updatedAt ?? ""
2184
2211
  };
@@ -2368,18 +2395,23 @@ function useParentChecklistRepo() {
2368
2395
  status: {
2369
2396
  $switch: {
2370
2397
  branches: [
2371
- { case: { $eq: ["$status", "ready"] }, then: "Ready" },
2398
+ { case: { $eq: ["$status", "open"] }, then: "Open" },
2372
2399
  {
2373
2400
  case: { $eq: ["$status", "ongoing"] },
2374
2401
  then: "Ongoing"
2375
2402
  },
2376
- { case: { $eq: ["$status", "completed"] }, then: "Completed" }
2403
+ { case: { $eq: ["$status", "completed"] }, then: "Completed" },
2404
+ {
2405
+ case: { $eq: ["$status", "closed"] },
2406
+ then: "Closed"
2407
+ }
2377
2408
  ],
2378
2409
  default: "$status"
2379
2410
  }
2380
2411
  },
2381
2412
  completedAt: 1,
2382
- createdAt: 1
2413
+ createdAt: 1,
2414
+ closeIn: { $add: ["$createdAt", 24 * 60 * 60 * 1e3] }
2383
2415
  }
2384
2416
  });
2385
2417
  pipeline.push(
@@ -2467,11 +2499,42 @@ function useParentChecklistRepo() {
2467
2499
  throw error;
2468
2500
  }
2469
2501
  }
2502
+ async function closeExpiredParentChecklists() {
2503
+ try {
2504
+ const expiryDate = new Date(Date.now() - 24 * 60 * 60 * 1e3);
2505
+ const result = await collection.updateMany(
2506
+ {
2507
+ status: { $in: ["open", "ongoing", "completed"] },
2508
+ createdAt: { $lte: expiryDate }
2509
+ },
2510
+ {
2511
+ $set: {
2512
+ status: "closed",
2513
+ updatedAt: /* @__PURE__ */ new Date()
2514
+ }
2515
+ }
2516
+ );
2517
+ if (result.modifiedCount > 0) {
2518
+ delNamespace().catch((err) => {
2519
+ import_node_server_utils14.logger.error(
2520
+ "Failed to invalidate cache after closing expired parent checklists",
2521
+ err
2522
+ );
2523
+ });
2524
+ import_node_server_utils14.logger.info(`Closed ${result.modifiedCount} expired parent checklists`);
2525
+ }
2526
+ return result.modifiedCount;
2527
+ } catch (error) {
2528
+ import_node_server_utils14.logger.error("Failed to close expired parent checklists", error);
2529
+ throw error;
2530
+ }
2531
+ }
2470
2532
  return {
2471
2533
  createIndex,
2472
2534
  createParentChecklist,
2473
2535
  getAllParentChecklist,
2474
- updateParentChecklistStatuses
2536
+ updateParentChecklistStatuses,
2537
+ closeExpiredParentChecklists
2475
2538
  };
2476
2539
  }
2477
2540
 
@@ -2553,7 +2616,7 @@ function useParentChecklistController() {
2553
2616
  var import_joi8 = __toESM(require("joi"));
2554
2617
  var import_mongodb8 = require("mongodb");
2555
2618
  var import_node_server_utils16 = require("@7365admin1/node-server-utils");
2556
- var allowedChecklistStatus = ["ready", "completed"];
2619
+ var allowedChecklistStatus = ["open", "completed", "closed"];
2557
2620
  var areaChecklistSchema = import_joi8.default.object({
2558
2621
  schedule: import_joi8.default.string().hex().required(),
2559
2622
  area: import_joi8.default.string().hex().required(),
@@ -2601,7 +2664,7 @@ function MAreaChecklist(value) {
2601
2664
  name: unit.name,
2602
2665
  approve: false,
2603
2666
  reject: false,
2604
- status: "ready",
2667
+ status: "open",
2605
2668
  remarks: "",
2606
2669
  completedBy: "",
2607
2670
  timestamp: ""
@@ -2622,7 +2685,7 @@ function MAreaChecklist(value) {
2622
2685
  name: value.name,
2623
2686
  type: value.type,
2624
2687
  checklist: value.checklist || [],
2625
- status: "ready",
2688
+ status: "open",
2626
2689
  createdBy: value.createdBy,
2627
2690
  createdAt: /* @__PURE__ */ new Date(),
2628
2691
  completedAt: "",
@@ -2786,11 +2849,15 @@ function useAreaChecklistRepo() {
2786
2849
  status: {
2787
2850
  $switch: {
2788
2851
  branches: [
2789
- { case: { $eq: ["$status", "ready"] }, then: "Ready" },
2852
+ { case: { $eq: ["$status", "open"] }, then: "Open" },
2790
2853
  { case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
2791
2854
  {
2792
2855
  case: { $eq: ["$status", "completed"] },
2793
2856
  then: "Completed"
2857
+ },
2858
+ {
2859
+ case: { $eq: ["$status", "closed"] },
2860
+ then: "Closed"
2794
2861
  }
2795
2862
  ],
2796
2863
  default: "$status"
@@ -2892,11 +2959,15 @@ function useAreaChecklistRepo() {
2892
2959
  status: {
2893
2960
  $switch: {
2894
2961
  branches: [
2895
- { case: { $eq: ["$status", "ready"] }, then: "Ready" },
2962
+ { case: { $eq: ["$status", "open"] }, then: "Open" },
2896
2963
  { case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
2897
2964
  {
2898
2965
  case: { $eq: ["$status", "completed"] },
2899
2966
  then: "Completed"
2967
+ },
2968
+ {
2969
+ case: { $eq: ["$status", "closed"] },
2970
+ then: "Closed"
2900
2971
  }
2901
2972
  ],
2902
2973
  default: "$status"
@@ -2961,11 +3032,15 @@ function useAreaChecklistRepo() {
2961
3032
  status: {
2962
3033
  $switch: {
2963
3034
  branches: [
2964
- { case: { $eq: ["$status", "ready"] }, then: "Ready" },
3035
+ { case: { $eq: ["$status", "open"] }, then: "Open" },
2965
3036
  { case: { $eq: ["$status", "ongoing"] }, then: "Ongoing" },
2966
3037
  {
2967
3038
  case: { $eq: ["$status", "completed"] },
2968
3039
  then: "Completed"
3040
+ },
3041
+ {
3042
+ case: { $eq: ["$status", "closed"] },
3043
+ then: "Closed"
2969
3044
  }
2970
3045
  ],
2971
3046
  default: "$status"
@@ -3025,12 +3100,16 @@ function useAreaChecklistRepo() {
3025
3100
  $switch: {
3026
3101
  branches: [
3027
3102
  {
3028
- case: { $eq: ["$checklist.units.status", "ready"] },
3029
- then: "Ready"
3103
+ case: { $eq: ["$checklist.units.status", "open"] },
3104
+ then: "Open"
3030
3105
  },
3031
3106
  {
3032
3107
  case: { $eq: ["$checklist.units.status", "completed"] },
3033
3108
  then: "Completed"
3109
+ },
3110
+ {
3111
+ case: { $eq: ["$checklist.units.status", "closed"] },
3112
+ then: "Closed"
3034
3113
  }
3035
3114
  ],
3036
3115
  default: "$checklist.units.status"
@@ -3177,12 +3256,16 @@ function useAreaChecklistRepo() {
3177
3256
  $switch: {
3178
3257
  branches: [
3179
3258
  {
3180
- case: { $eq: ["$checklist.units.status", "ready"] },
3181
- then: "Ready"
3259
+ case: { $eq: ["$checklist.units.status", "open"] },
3260
+ then: "Open"
3182
3261
  },
3183
3262
  {
3184
3263
  case: { $eq: ["$checklist.units.status", "completed"] },
3185
3264
  then: "Completed"
3265
+ },
3266
+ {
3267
+ case: { $eq: ["$checklist.units.status", "closed"] },
3268
+ then: "Closed"
3186
3269
  }
3187
3270
  ],
3188
3271
  default: "$checklist.units.status"
@@ -3315,7 +3398,7 @@ function useAreaChecklistRepo() {
3315
3398
  } else if (value.reject === true) {
3316
3399
  updateValue["checklist.$[checklist].units.$[unit].approve"] = false;
3317
3400
  updateValue["checklist.$[checklist].units.$[unit].reject"] = true;
3318
- updateValue["checklist.$[checklist].units.$[unit].status"] = "ready";
3401
+ updateValue["checklist.$[checklist].units.$[unit].status"] = "open";
3319
3402
  }
3320
3403
  if (value.remarks) {
3321
3404
  updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks;
@@ -3441,6 +3524,42 @@ function useAreaChecklistRepo() {
3441
3524
  return 0;
3442
3525
  }
3443
3526
  }
3527
+ async function closeExpiredAreaChecklists() {
3528
+ try {
3529
+ const expiryDate = new Date(Date.now() - 24 * 60 * 60 * 1e3);
3530
+ const result = await collection.updateMany(
3531
+ {
3532
+ status: { $in: ["open", "ongoing", "completed"] },
3533
+ createdAt: { $lte: expiryDate }
3534
+ },
3535
+ {
3536
+ $set: {
3537
+ status: "closed",
3538
+ updatedAt: /* @__PURE__ */ new Date(),
3539
+ "checklist.$[].units.$[unit].status": "closed"
3540
+ }
3541
+ },
3542
+ {
3543
+ arrayFilters: [{ "unit.status": { $in: ["open", "completed"] } }]
3544
+ }
3545
+ );
3546
+ if (result.modifiedCount > 0) {
3547
+ delNamespace().catch((err) => {
3548
+ import_node_server_utils17.logger.error(
3549
+ "Failed to invalidate cache after closing expired area checklists",
3550
+ err
3551
+ );
3552
+ });
3553
+ import_node_server_utils17.logger.info(
3554
+ `Closed ${result.modifiedCount} expired area checklists and their units`
3555
+ );
3556
+ }
3557
+ return result.modifiedCount;
3558
+ } catch (error) {
3559
+ import_node_server_utils17.logger.error("Failed to close expired area checklists", error);
3560
+ throw error;
3561
+ }
3562
+ }
3444
3563
  return {
3445
3564
  createIndex,
3446
3565
  createTextIndex,
@@ -3454,7 +3573,8 @@ function useAreaChecklistRepo() {
3454
3573
  updateAreaChecklist,
3455
3574
  updateAreaChecklistStatus,
3456
3575
  updateAreaChecklistUnits,
3457
- getMaxSetNumberForArea
3576
+ getMaxSetNumberForArea,
3577
+ closeExpiredAreaChecklists
3458
3578
  };
3459
3579
  }
3460
3580
 
@@ -3550,12 +3670,12 @@ function useAreaChecklistService() {
3550
3670
  },
3551
3671
  session
3552
3672
  );
3553
- let areaStatus = "ready";
3673
+ let areaStatus = "open";
3554
3674
  if (allUnitsResult && allUnitsResult.items && allUnitsResult.items.length > 0) {
3555
3675
  const sets = allUnitsResult.items;
3556
3676
  const allUnits = sets.flatMap((set2) => set2.units || []);
3557
- const readyCount = allUnits.filter(
3558
- (unit) => unit.status === "Ready"
3677
+ const openCount = allUnits.filter(
3678
+ (unit) => unit.status === "Open"
3559
3679
  ).length;
3560
3680
  const completedCount = allUnits.filter(
3561
3681
  (unit) => unit.status === "Completed"
@@ -3563,8 +3683,8 @@ function useAreaChecklistService() {
3563
3683
  const totalCount = allUnits.length;
3564
3684
  if (completedCount === totalCount) {
3565
3685
  areaStatus = "completed";
3566
- } else if (readyCount === totalCount) {
3567
- areaStatus = "ready";
3686
+ } else if (openCount === totalCount) {
3687
+ areaStatus = "open";
3568
3688
  } else {
3569
3689
  areaStatus = "ongoing";
3570
3690
  }
@@ -3583,18 +3703,18 @@ function useAreaChecklistService() {
3583
3703
  );
3584
3704
  if (allAreasResult && allAreasResult.items && allAreasResult.items.length > 0) {
3585
3705
  const areas = allAreasResult.items;
3586
- const readyAreasCount = areas.filter(
3587
- (area) => area.status === "Ready"
3706
+ const openAreasCount = areas.filter(
3707
+ (area) => area.status === "Open"
3588
3708
  ).length;
3589
3709
  const completedAreasCount = areas.filter(
3590
3710
  (area) => area.status === "Completed"
3591
3711
  ).length;
3592
3712
  const totalAreasCount = areas.length;
3593
- let parentStatus = "ready";
3713
+ let parentStatus = "open";
3594
3714
  if (completedAreasCount === totalAreasCount) {
3595
3715
  parentStatus = "completed";
3596
- } else if (readyAreasCount === totalAreasCount) {
3597
- parentStatus = "ready";
3716
+ } else if (openAreasCount === totalAreasCount) {
3717
+ parentStatus = "open";
3598
3718
  } else {
3599
3719
  parentStatus = "ongoing";
3600
3720
  }
@@ -3605,7 +3725,7 @@ function useAreaChecklistService() {
3605
3725
  );
3606
3726
  } else {
3607
3727
  import_node_server_utils18.logger.info(
3608
- "No area checklists found, keeping parent status as ready"
3728
+ "No area checklists found, keeping parent status as open"
3609
3729
  );
3610
3730
  }
3611
3731
  }
@@ -5163,7 +5283,7 @@ function useCheckOutItemService() {
5163
5283
  const session = import_node_server_utils30.useAtlas.getClient()?.startSession();
5164
5284
  try {
5165
5285
  session?.startTransaction();
5166
- const { site, attachment, createdBy, items } = value;
5286
+ const { site, createdBy, items } = value;
5167
5287
  const createdByData = await getUserById(createdBy);
5168
5288
  const createdCheckOutItemIds = [];
5169
5289
  for (const item of items) {
@@ -5174,7 +5294,7 @@ function useCheckOutItemService() {
5174
5294
  supply: item.supply,
5175
5295
  supplyName: supplyData?.name || "",
5176
5296
  qty: item.qty,
5177
- attachment,
5297
+ attachment: item.attachment,
5178
5298
  createdBy,
5179
5299
  createdByName: createdByData?.name || ""
5180
5300
  },
@@ -5265,12 +5385,12 @@ function useCheckOutItemController() {
5265
5385
  };
5266
5386
  const validation = import_joi15.default.object({
5267
5387
  site: import_joi15.default.string().hex().required(),
5268
- attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null),
5269
5388
  createdBy: import_joi15.default.string().hex().required(),
5270
5389
  items: import_joi15.default.array().items(
5271
5390
  import_joi15.default.object({
5272
5391
  supply: import_joi15.default.string().hex().required(),
5273
- qty: import_joi15.default.number().min(0).required()
5392
+ qty: import_joi15.default.number().min(0).required(),
5393
+ attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null)
5274
5394
  })
5275
5395
  ).min(1).required()
5276
5396
  });
@@ -5358,8 +5478,9 @@ var scheduleTaskSchema = import_joi16.default.object({
5358
5478
  site: import_joi16.default.string().hex().required(),
5359
5479
  title: import_joi16.default.string().required(),
5360
5480
  time: import_joi16.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
5361
- startDate: import_joi16.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required(),
5362
- endDate: import_joi16.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).optional().allow("", null),
5481
+ dates: import_joi16.default.array().min(1).items(
5482
+ import_joi16.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
5483
+ ).required(),
5363
5484
  description: import_joi16.default.string().optional().allow("", null),
5364
5485
  areas: import_joi16.default.array().min(1).items(
5365
5486
  import_joi16.default.object({
@@ -5405,8 +5526,7 @@ function MScheduleTask(value) {
5405
5526
  site: value.site,
5406
5527
  title: value.title,
5407
5528
  time: value.time,
5408
- startDate: value.startDate,
5409
- endDate: value.endDate,
5529
+ dates: value.dates,
5410
5530
  description: value.description,
5411
5531
  areas: value.areas,
5412
5532
  status: "active",
@@ -5619,8 +5739,7 @@ function useScheduleTaskRepository() {
5619
5739
  $project: {
5620
5740
  title: 1,
5621
5741
  time: 1,
5622
- startDate: 1,
5623
- endDate: 1,
5742
+ dates: 1,
5624
5743
  description: 1,
5625
5744
  areas: 1,
5626
5745
  status: 1,
@@ -5717,29 +5836,24 @@ function useScheduleTaskService() {
5717
5836
  timeZone: "Asia/Singapore"
5718
5837
  });
5719
5838
  const [currentHour, currentMinute] = timeString.split(":").map(Number);
5720
- const currentDateString = now.toLocaleDateString("en-US", {
5721
- timeZone: "Asia/Singapore"
5722
- });
5723
5839
  import_node_server_utils34.logger.info(
5724
- `Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Current date ${currentDateString}, Schedule time ${schedule.time}, Start date ${schedule.startDate}, End date ${schedule.endDate}`
5840
+ `Checking schedule ${schedule._id}: Current time ${currentHour}:${currentMinute}, Schedule time ${schedule.time}, Dates ${JSON.stringify(schedule.dates)}`
5725
5841
  );
5726
- const startDate = /* @__PURE__ */ new Date(schedule.startDate + "T00:00:00");
5727
- const currentDateOnly = /* @__PURE__ */ new Date(currentDateString + "T00:00:00");
5728
- if (currentDateOnly < startDate) {
5842
+ const currentDateFormatted = now.toLocaleDateString("en-CA", {
5843
+ timeZone: "Asia/Singapore"
5844
+ });
5845
+ if (!schedule.dates || !Array.isArray(schedule.dates) || schedule.dates.length === 0) {
5846
+ import_node_server_utils34.logger.info(`Schedule ${schedule._id}: No dates configured, skipping`);
5847
+ return false;
5848
+ }
5849
+ if (!schedule.dates.includes(currentDateFormatted)) {
5729
5850
  import_node_server_utils34.logger.info(
5730
- `Schedule ${schedule._id}: Current date ${currentDateString} is before start date ${schedule.startDate}`
5851
+ `Schedule ${schedule._id}: Current date ${currentDateFormatted} is not in scheduled dates [${schedule.dates.join(
5852
+ ", "
5853
+ )}]`
5731
5854
  );
5732
5855
  return false;
5733
5856
  }
5734
- if (schedule.endDate) {
5735
- const endDate = /* @__PURE__ */ new Date(schedule.endDate + "T00:00:00");
5736
- if (currentDateOnly > endDate) {
5737
- import_node_server_utils34.logger.info(
5738
- `Schedule ${schedule._id}: Current date ${currentDateString} is after end date ${schedule.endDate}`
5739
- );
5740
- return false;
5741
- }
5742
- }
5743
5857
  const [scheduleHour, scheduleMinute] = schedule.time.split(":").map(Number);
5744
5858
  const timeMatches = currentHour === scheduleHour && currentMinute === scheduleMinute;
5745
5859
  if (!timeMatches) {
@@ -5775,7 +5889,9 @@ function useScheduleTaskService() {
5775
5889
  for (const scheduleTask of scheduleTasks) {
5776
5890
  try {
5777
5891
  import_node_server_utils34.logger.info(
5778
- `Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time}, startDate=${scheduleTask.startDate}, endDate=${scheduleTask.endDate}`
5892
+ `Checking schedule ${scheduleTask._id} - ${scheduleTask.title}: time=${scheduleTask.time}, dates=${JSON.stringify(
5893
+ scheduleTask.dates
5894
+ )}`
5779
5895
  );
5780
5896
  const shouldRun = checkScheduleConditions(scheduleTask, currentDate);
5781
5897
  if (!shouldRun) {
@@ -6085,8 +6201,9 @@ function useScheduleTaskController() {
6085
6201
  id: import_joi17.default.string().hex().required(),
6086
6202
  title: import_joi17.default.string().optional().allow("", null),
6087
6203
  time: import_joi17.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).optional().allow("", null),
6088
- startDate: import_joi17.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).optional().allow("", null),
6089
- endDate: import_joi17.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).optional().allow("", null),
6204
+ dates: import_joi17.default.array().min(1).items(
6205
+ import_joi17.default.string().pattern(/^\d{4}-\d{2}-\d{2}$/).required()
6206
+ ).optional(),
6090
6207
  description: import_joi17.default.string().optional().allow("", null),
6091
6208
  areas: import_joi17.default.array().min(1).items(
6092
6209
  import_joi17.default.object({
@@ -6200,7 +6317,7 @@ function useQRService() {
6200
6317
  throw error;
6201
6318
  }
6202
6319
  }
6203
- async function generateQRPDF(qrUrl, title) {
6320
+ async function generateQRPDF(qrUrl, title, subtitle) {
6204
6321
  try {
6205
6322
  const qrDataUrl = await generateQRDataUrl(qrUrl);
6206
6323
  const browser = await (0, import_puppeteer2.launch)({
@@ -6214,6 +6331,7 @@ function useQRService() {
6214
6331
  height: 1100
6215
6332
  });
6216
6333
  const escapedTitle = (title || "Cleaning Schedule QR Code").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6334
+ const escapedSubtitle = subtitle ? subtitle.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;") : "";
6217
6335
  const html = `
6218
6336
  <!DOCTYPE html>
6219
6337
  <html>
@@ -6241,10 +6359,16 @@ function useQRService() {
6241
6359
  padding: 0;
6242
6360
  }
6243
6361
  h1 {
6244
- font-size: 28px;
6362
+ font-size: 38px;
6245
6363
  color: #333;
6246
- margin-bottom: 20px;
6247
- font-weight: 600;
6364
+ margin-bottom: 8px;
6365
+ font-weight: 700;
6366
+ }
6367
+ .subtitle {
6368
+ font-size: 26px;
6369
+ color: #555;
6370
+ margin-bottom: 12px;
6371
+ font-weight: 500;
6248
6372
  }
6249
6373
  .qr-wrapper {
6250
6374
  display: inline-block;
@@ -6279,6 +6403,7 @@ function useQRService() {
6279
6403
  <body>
6280
6404
  <div class="qr-container">
6281
6405
  <h1>${escapedTitle}</h1>
6406
+ ${escapedSubtitle ? `<p class="subtitle">${escapedSubtitle}</p>` : ""}
6282
6407
  <div class="qr-wrapper">
6283
6408
  <img id="qr-image" src="${qrDataUrl}" alt="QR Code" />
6284
6409
  </div>
@@ -6339,6 +6464,7 @@ function useQRController() {
6339
6464
  url: import_joi18.default.string().uri().required(),
6340
6465
  filename: import_joi18.default.string().optional().allow("", null),
6341
6466
  title: import_joi18.default.string().optional().allow("", null),
6467
+ subtitle: import_joi18.default.string().optional().allow("", null),
6342
6468
  download: import_joi18.default.boolean().optional().default(false)
6343
6469
  });
6344
6470
  const query = { ...req.query };
@@ -6349,9 +6475,9 @@ function useQRController() {
6349
6475
  return;
6350
6476
  }
6351
6477
  try {
6352
- const { url, filename, title, download } = value;
6478
+ const { url, filename, title, subtitle, download } = value;
6353
6479
  if (download) {
6354
- const pdfBuffer = await _generateQRPDF(url, title);
6480
+ const pdfBuffer = await _generateQRPDF(url, title, subtitle);
6355
6481
  if (!pdfBuffer || pdfBuffer.length === 0) {
6356
6482
  throw new Error("Generated QR PDF is empty or invalid.");
6357
6483
  }