@7365admin1/module-hygiene 4.20.0 → 4.22.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
@@ -102,72 +102,57 @@ function useHygieneDashboardRepository() {
102
102
  throw new import_node_server_utils.InternalServerError("Unable to connect to server.");
103
103
  }
104
104
  const dashboard_namespace_collection = "hygiene.dashboard";
105
- const feedback_namespace_collection = "feedbacks";
106
105
  const cleaning_schedule_namespace_collection = "site.cleaning.schedules";
107
106
  const area_checklist_namespace_collection = "site.cleaning.schedule.areas";
108
- const schedule_task_namespace_collection = "site.schedule-tasks";
109
107
  const supply_namespace_collection = "site.supplies";
110
- const request_item_namespace_collection = "site.supply.requests";
111
- const feedbackCollection = db.collection(feedback_namespace_collection);
108
+ const work_order_namespace_collection = "work-orders2";
112
109
  const areaChecklistCollection = db.collection(
113
110
  area_checklist_namespace_collection
114
111
  );
115
- const scheduleTaskCollection = db.collection(
116
- schedule_task_namespace_collection
117
- );
118
112
  const supplyCollection = db.collection(supply_namespace_collection);
119
- const requestItemCollection = db.collection(
120
- request_item_namespace_collection
121
- );
113
+ const workOrderCollection = db.collection(work_order_namespace_collection);
114
+ const attendanceCollection = db.collection("site.attendances");
115
+ const feedbackCollection = db.collection("feedbacks2");
122
116
  const { setCache, getCache } = (0, import_node_server_utils.useCache)(dashboard_namespace_collection);
117
+ const workOrderServiceMap = {
118
+ security_agency: "Security",
119
+ cleaning_services: "Cleaning",
120
+ mechanical_electrical_services: "M&E",
121
+ landscaping_services: "Landscape",
122
+ pest_control_services: "Pest Control",
123
+ pool_maintenance_services: "Pool Maintenance"
124
+ };
125
+ function areaChecklistScheduleLookup(site, serviceType, createdAtRange) {
126
+ return [
127
+ { $match: { serviceType } },
128
+ {
129
+ $lookup: {
130
+ from: cleaning_schedule_namespace_collection,
131
+ localField: "schedule",
132
+ foreignField: "_id",
133
+ as: "scheduleDoc",
134
+ pipeline: [
135
+ {
136
+ $match: {
137
+ site,
138
+ serviceType,
139
+ createdAt: createdAtRange
140
+ }
141
+ }
142
+ ]
143
+ }
144
+ },
145
+ { $unwind: { path: "$scheduleDoc", preserveNullAndEmptyArrays: false } }
146
+ ];
147
+ }
123
148
  async function getHygieneDashboard({
124
149
  site,
125
- feedbackPeriod = "today",
126
- commonAreaPeriod = "today",
127
- toiletAreaPeriod = "today",
128
- scheduleTaskPeriod = "today",
129
- supplyPeriod = "today",
130
- requestItemPeriod = "today"
150
+ serviceType,
151
+ period = "today"
131
152
  }) {
132
- const countQueries = {
133
- feedback: {},
134
- commonArea: {},
135
- toiletArea: {},
136
- scheduleTask: {},
137
- supply: {},
138
- requestItem: {}
139
- };
140
- const yesterDayQueries = {
141
- feedback: {},
142
- commonArea: {},
143
- toiletArea: {},
144
- scheduleTask: {},
145
- supply: {},
146
- requestItem: {}
147
- };
148
- const todayQueries = {
149
- feedback: {},
150
- commonArea: {},
151
- toiletArea: {},
152
- scheduleTask: {},
153
- supply: {},
154
- requestItem: {}
155
- };
156
- const cacheOptions = {
157
- feedbackPeriod,
158
- commonAreaPeriod,
159
- toiletAreaPeriod,
160
- scheduleTaskPeriod,
161
- supplyPeriod,
162
- requestItemPeriod
163
- };
153
+ const cacheOptions = { serviceType, period };
164
154
  try {
165
155
  site = new import_mongodb.ObjectId(site);
166
- Object.keys(countQueries).forEach((key) => {
167
- countQueries[key].site = site;
168
- yesterDayQueries[key].site = site;
169
- todayQueries[key].site = site;
170
- });
171
156
  cacheOptions.site = site.toString();
172
157
  } catch (error) {
173
158
  throw new import_node_server_utils.BadRequestError("Invalid site ID format.");
@@ -195,32 +180,28 @@ function useHygieneDashboardRepository() {
195
180
  });
196
181
  return singaporeMoment.toDate();
197
182
  }
198
- function applyPeriodToQuery(query, period) {
199
- const today = /* @__PURE__ */ new Date();
200
- today.setHours(0, 0, 0, 0);
201
- if (period === "today") {
202
- query.createdAt = {
203
- $gte: today,
204
- $lte: formatEndDate(today)
205
- };
206
- } else if (period === "thisWeek" || period === "thisMonth") {
207
- const filterDate = period === "thisWeek" ? 7 : 30;
208
- const filterStartDate = /* @__PURE__ */ new Date();
209
- filterStartDate.setDate(today.getDate() - filterDate);
210
- filterStartDate.setHours(0, 0, 0, 0);
211
- query.createdAt = {
212
- $gte: filterStartDate,
213
- $lte: formatEndDate(/* @__PURE__ */ new Date())
214
- };
183
+ function getDateRange(p) {
184
+ const start = /* @__PURE__ */ new Date();
185
+ start.setHours(0, 0, 0, 0);
186
+ if (p === "today") {
187
+ return { $gte: start, $lte: formatEndDate(start) };
215
188
  }
216
- }
189
+ const days = p === "thisWeek" ? 7 : 30;
190
+ const rangeStart = /* @__PURE__ */ new Date();
191
+ rangeStart.setDate(start.getDate() - days);
192
+ rangeStart.setHours(0, 0, 0, 0);
193
+ return { $gte: rangeStart, $lte: formatEndDate(/* @__PURE__ */ new Date()) };
194
+ }
195
+ const calculatePercentageChange = (currentCount, previousCount) => {
196
+ if (previousCount === 0) {
197
+ return currentCount > 0 ? 100 : 0;
198
+ }
199
+ return Math.round(
200
+ (currentCount - previousCount) / previousCount * 100 * 100
201
+ ) / 100;
202
+ };
203
+ const workOrderService = workOrderServiceMap[serviceType] ?? serviceType;
217
204
  try {
218
- applyPeriodToQuery(countQueries.feedback, feedbackPeriod);
219
- applyPeriodToQuery(countQueries.commonArea, commonAreaPeriod);
220
- applyPeriodToQuery(countQueries.toiletArea, toiletAreaPeriod);
221
- applyPeriodToQuery(countQueries.scheduleTask, scheduleTaskPeriod);
222
- applyPeriodToQuery(countQueries.supply, supplyPeriod);
223
- applyPeriodToQuery(countQueries.requestItem, requestItemPeriod);
224
205
  const today = /* @__PURE__ */ new Date();
225
206
  today.setHours(0, 0, 0, 0);
226
207
  const yesterday = /* @__PURE__ */ new Date();
@@ -228,359 +209,566 @@ function useHygieneDashboardRepository() {
228
209
  yesterday.setHours(0, 0, 0, 0);
229
210
  const yesterdayEnd = new Date(yesterday);
230
211
  yesterdayEnd.setHours(23, 59, 59, 999);
231
- Object.keys(yesterDayQueries).forEach((key) => {
232
- yesterDayQueries[key].createdAt = {
233
- $gte: yesterday,
234
- $lte: yesterdayEnd
235
- };
236
- });
237
- Object.keys(todayQueries).forEach((key) => {
238
- todayQueries[key].createdAt = {
239
- $gte: today,
240
- $lte: formatEndDate(/* @__PURE__ */ new Date())
241
- };
242
- });
243
- const calculatePercentageChange = (currentCount, previousCount) => {
244
- if (previousCount === 0) {
245
- return currentCount > 0 ? 100 : 0;
246
- }
247
- return Math.round(
248
- (currentCount - previousCount) / previousCount * 100 * 100
249
- ) / 100;
250
- };
212
+ const todayEnd = formatEndDate(/* @__PURE__ */ new Date());
213
+ const periodRange = getDateRange(period);
251
214
  const [
252
- feedbackReport,
253
- commonAreaChecklistReport,
254
- toiletAreaChecklistReport,
255
- scheduleTaskReport,
256
- supplyReport,
257
- requestItemReport
215
+ workOrderReport,
216
+ supplyAlertReport,
217
+ taskCompletedReport,
218
+ yesterdayWorkOrderReport,
219
+ todayWorkOrderReport,
220
+ yesterdayTaskCompletedReport,
221
+ todayTaskCompletedReport
258
222
  ] = await Promise.all([
259
- feedbackCollection.aggregate([
223
+ // 1. Open work orders (period-filtered) with inProgress breakdown
224
+ workOrderCollection.aggregate([
260
225
  {
261
226
  $match: {
262
- ...countQueries.feedback,
263
- category: "cleaning_services",
264
- status: "to-do"
227
+ site,
228
+ service: workOrderService,
229
+ createdAt: periodRange,
230
+ status: { $nin: ["completed"] }
265
231
  }
266
232
  },
233
+ {
234
+ $facet: {
235
+ total: [{ $count: "count" }],
236
+ inProgress: [
237
+ { $match: { status: "in-progress" } },
238
+ { $count: "count" }
239
+ ]
240
+ }
241
+ }
242
+ ]).toArray(),
243
+ // 2. Supply alert — out-of-stock active supplies for site+serviceType
244
+ supplyCollection.aggregate([
245
+ { $match: { site, serviceType, qty: 0, status: "active" } },
267
246
  { $count: "count" }
268
247
  ]).toArray(),
248
+ // 3. Task completed — area checklists (period-filtered via schedule join)
269
249
  areaChecklistCollection.aggregate([
270
- { $match: { type: "common" } },
250
+ ...areaChecklistScheduleLookup(site, serviceType, periodRange),
271
251
  {
272
- $lookup: {
273
- from: cleaning_schedule_namespace_collection,
274
- localField: "schedule",
275
- foreignField: "_id",
276
- as: "scheduleDoc",
277
- pipeline: countQueries.commonArea.createdAt ? [
278
- {
279
- $match: {
280
- site,
281
- createdAt: countQueries.commonArea.createdAt
282
- }
283
- }
284
- ] : [{ $match: { site } }]
252
+ $facet: {
253
+ total: [{ $count: "count" }],
254
+ completed: [
255
+ { $match: { status: "completed" } },
256
+ { $count: "count" }
257
+ ],
258
+ inProgress: [
259
+ { $match: { status: { $in: ["open", "ongoing"] } } },
260
+ { $count: "count" }
261
+ ]
285
262
  }
286
- },
263
+ }
264
+ ]).toArray(),
265
+ // 4. Yesterday open work orders (for % change)
266
+ workOrderCollection.aggregate([
287
267
  {
288
- $unwind: {
289
- path: "$scheduleDoc",
290
- preserveNullAndEmptyArrays: false
268
+ $match: {
269
+ site,
270
+ service: workOrderService,
271
+ createdAt: { $gte: yesterday, $lte: yesterdayEnd },
272
+ status: { $nin: ["completed"] }
291
273
  }
292
274
  },
293
275
  { $count: "count" }
294
276
  ]).toArray(),
295
- areaChecklistCollection.aggregate([
296
- { $match: { type: "toilet" } },
277
+ // 5. Today open work orders (for % change)
278
+ workOrderCollection.aggregate([
297
279
  {
298
- $lookup: {
299
- from: cleaning_schedule_namespace_collection,
300
- localField: "schedule",
301
- foreignField: "_id",
302
- as: "scheduleDoc",
303
- pipeline: countQueries.toiletArea.createdAt ? [
304
- {
305
- $match: {
306
- site,
307
- createdAt: countQueries.toiletArea.createdAt
308
- }
309
- }
310
- ] : [{ $match: { site } }]
311
- }
312
- },
313
- {
314
- $unwind: {
315
- path: "$scheduleDoc",
316
- preserveNullAndEmptyArrays: false
280
+ $match: {
281
+ site,
282
+ service: workOrderService,
283
+ createdAt: { $gte: today, $lte: todayEnd },
284
+ status: { $nin: ["completed"] }
317
285
  }
318
286
  },
319
287
  { $count: "count" }
320
288
  ]).toArray(),
321
- scheduleTaskCollection.aggregate([
322
- { $match: countQueries.scheduleTask },
289
+ // 6. Yesterday task completed — area checklists (for % change)
290
+ areaChecklistCollection.aggregate([
291
+ ...areaChecklistScheduleLookup(site, serviceType, {
292
+ $gte: yesterday,
293
+ $lte: yesterdayEnd
294
+ }),
295
+ { $match: { status: "completed" } },
323
296
  { $count: "count" }
324
297
  ]).toArray(),
325
- supplyCollection.aggregate([{ $match: countQueries.supply }, { $count: "count" }]).toArray(),
326
- requestItemCollection.aggregate([
327
- { $match: countQueries.requestItem },
298
+ // 7. Today task completed area checklists (for % change)
299
+ areaChecklistCollection.aggregate([
300
+ ...areaChecklistScheduleLookup(site, serviceType, {
301
+ $gte: today,
302
+ $lte: todayEnd
303
+ }),
304
+ { $match: { status: "completed" } },
328
305
  { $count: "count" }
329
306
  ]).toArray()
330
307
  ]);
331
- const [
332
- yesterdayFeedbackCount,
333
- yesterdayCommonAreaChecklistCount,
334
- yesterdayToiletAreaChecklistCount,
335
- yesterdayScheduledTaskCount,
336
- yesterdaySupplyCount,
337
- yesterdayRequestItemCount
338
- ] = await Promise.all([
339
- feedbackCollection.aggregate([
308
+ const wFacet = workOrderReport[0] ?? { total: [], inProgress: [] };
309
+ const tFacet = taskCompletedReport[0] ?? {
310
+ total: [],
311
+ completed: [],
312
+ inProgress: []
313
+ };
314
+ const result = {
315
+ openWorkOrder: {
316
+ count: wFacet.total[0]?.count ?? 0,
317
+ inProgress: wFacet.inProgress[0]?.count ?? 0,
318
+ percentage: calculatePercentageChange(
319
+ todayWorkOrderReport[0]?.count ?? 0,
320
+ yesterdayWorkOrderReport[0]?.count ?? 0
321
+ )
322
+ },
323
+ supplyAlert: {
324
+ count: supplyAlertReport[0]?.count ?? 0,
325
+ percentage: 0
326
+ },
327
+ taskCompleted: {
328
+ completed: tFacet.completed[0]?.count ?? 0,
329
+ total: tFacet.total[0]?.count ?? 0,
330
+ inProgress: tFacet.inProgress[0]?.count ?? 0,
331
+ percentage: calculatePercentageChange(
332
+ todayTaskCompletedReport[0]?.count ?? 0,
333
+ yesterdayTaskCompletedReport[0]?.count ?? 0
334
+ )
335
+ }
336
+ };
337
+ setCache(dashboardCacheKey, result, 15 * 60).then(() => {
338
+ import_node_server_utils.logger.info(`Cache set for dashboard: ${dashboardCacheKey}`);
339
+ }).catch((err) => {
340
+ import_node_server_utils.logger.error(
341
+ `Failed to set cache for dashboard: ${dashboardCacheKey}`,
342
+ err
343
+ );
344
+ });
345
+ return result;
346
+ } catch (error) {
347
+ throw error;
348
+ }
349
+ }
350
+ async function getWeeklyActivity({
351
+ site,
352
+ serviceType
353
+ }) {
354
+ const cacheOptions = { serviceType };
355
+ try {
356
+ site = new import_mongodb.ObjectId(site);
357
+ cacheOptions.site = site.toString();
358
+ } catch {
359
+ throw new import_node_server_utils.BadRequestError("Invalid site ID format.");
360
+ }
361
+ const cacheKey = (0, import_node_server_utils.makeCacheKey)(
362
+ `${dashboard_namespace_collection}:weeklyActivity`,
363
+ cacheOptions
364
+ );
365
+ const cachedData = await getCache(cacheKey);
366
+ if (cachedData) {
367
+ import_node_server_utils.logger.info(`Cache hit for weeklyActivity: ${cacheKey}`);
368
+ return cachedData;
369
+ }
370
+ const workOrderService = workOrderServiceMap[serviceType] ?? serviceType;
371
+ const weekStart = import_moment_timezone.default.tz("Asia/Singapore").startOf("isoWeek").toDate();
372
+ const weekEnd = import_moment_timezone.default.tz("Asia/Singapore").endOf("isoWeek").toDate();
373
+ const weekRange = { $gte: weekStart, $lte: weekEnd };
374
+ const dayLabels = {
375
+ 1: "Sun",
376
+ 2: "Mon",
377
+ 3: "Tue",
378
+ 4: "Wed",
379
+ 5: "Thu",
380
+ 6: "Fri",
381
+ 7: "Sat"
382
+ };
383
+ const dayOrder = [2, 3, 4, 5, 6, 7, 1];
384
+ try {
385
+ const [workOrderByDay, taskCompletedByDay] = await Promise.all([
386
+ workOrderCollection.aggregate([
340
387
  {
341
- $match: {
342
- ...yesterDayQueries.feedback,
343
- category: "cleaning_services",
344
- status: "to-do"
345
- }
388
+ $match: { site, service: workOrderService, createdAt: weekRange }
346
389
  },
347
- { $count: "count" }
390
+ {
391
+ $group: { _id: { $dayOfWeek: "$createdAt" }, count: { $sum: 1 } }
392
+ }
348
393
  ]).toArray(),
349
394
  areaChecklistCollection.aggregate([
350
- { $match: { type: "common" } },
395
+ ...areaChecklistScheduleLookup(site, serviceType, weekRange),
396
+ { $match: { status: "completed" } },
351
397
  {
352
- $lookup: {
353
- from: cleaning_schedule_namespace_collection,
354
- localField: "schedule",
355
- foreignField: "_id",
356
- as: "scheduleDoc",
357
- pipeline: [
358
- {
359
- $match: {
360
- site,
361
- createdAt: yesterDayQueries.commonArea.createdAt
362
- }
363
- }
364
- ]
398
+ $group: {
399
+ _id: { $dayOfWeek: "$completedAt" },
400
+ count: { $sum: 1 }
365
401
  }
366
- },
402
+ }
403
+ ]).toArray()
404
+ ]);
405
+ const workOrderMap = new Map(
406
+ workOrderByDay.map((d) => [d._id, d.count])
407
+ );
408
+ const taskMap = new Map(
409
+ taskCompletedByDay.map((d) => [d._id, d.count])
410
+ );
411
+ const result = dayOrder.map((dow) => ({
412
+ day: dayLabels[dow],
413
+ workOrder: workOrderMap.get(dow) ?? 0,
414
+ taskCompleted: taskMap.get(dow) ?? 0
415
+ }));
416
+ setCache(cacheKey, result, 15 * 60).then(() => {
417
+ import_node_server_utils.logger.info(`Cache set for weeklyActivity: ${cacheKey}`);
418
+ }).catch((err) => {
419
+ import_node_server_utils.logger.error(
420
+ `Failed to set cache for weeklyActivity: ${cacheKey}`,
421
+ err
422
+ );
423
+ });
424
+ return result;
425
+ } catch (error) {
426
+ throw error;
427
+ }
428
+ }
429
+ async function getTodayTaskSchedule({
430
+ site,
431
+ serviceType,
432
+ page = 1,
433
+ limit = 10
434
+ }) {
435
+ page = page > 0 ? page - 1 : 0;
436
+ const cacheOptions = { serviceType, page, limit };
437
+ try {
438
+ site = new import_mongodb.ObjectId(site);
439
+ cacheOptions.site = site.toString();
440
+ } catch {
441
+ throw new import_node_server_utils.BadRequestError("Invalid site ID format.");
442
+ }
443
+ const cacheKey = (0, import_node_server_utils.makeCacheKey)(
444
+ `${dashboard_namespace_collection}:todayTaskSchedule`,
445
+ cacheOptions
446
+ );
447
+ const cachedData = await getCache(cacheKey);
448
+ if (cachedData) {
449
+ import_node_server_utils.logger.info(`Cache hit for todayTaskSchedule: ${cacheKey}`);
450
+ return cachedData;
451
+ }
452
+ const todayStart = import_moment_timezone.default.tz("Asia/Singapore").startOf("day").toDate();
453
+ const todayEnd = import_moment_timezone.default.tz("Asia/Singapore").endOf("day").toDate();
454
+ const todayRange = { $gte: todayStart, $lte: todayEnd };
455
+ try {
456
+ const [items, countResult] = await Promise.all([
457
+ areaChecklistCollection.aggregate([
458
+ ...areaChecklistScheduleLookup(site, serviceType, todayRange),
367
459
  {
368
- $unwind: {
369
- path: "$scheduleDoc",
370
- preserveNullAndEmptyArrays: false
460
+ $addFields: {
461
+ _assigneeId: {
462
+ $reduce: {
463
+ input: { $ifNull: ["$checklist", []] },
464
+ initialValue: null,
465
+ in: {
466
+ $cond: {
467
+ if: { $ne: ["$$value", null] },
468
+ then: "$$value",
469
+ else: {
470
+ $reduce: {
471
+ input: { $ifNull: ["$$this.units", []] },
472
+ initialValue: null,
473
+ in: {
474
+ $cond: {
475
+ if: { $ne: ["$$value", null] },
476
+ then: "$$value",
477
+ else: {
478
+ $cond: {
479
+ if: { $gt: ["$$this.completedBy", null] },
480
+ then: "$$this.completedBy",
481
+ else: null
482
+ }
483
+ }
484
+ }
485
+ }
486
+ }
487
+ }
488
+ }
489
+ }
490
+ }
491
+ }
371
492
  }
372
493
  },
373
- { $count: "count" }
374
- ]).toArray(),
375
- areaChecklistCollection.aggregate([
376
- { $match: { type: "toilet" } },
377
494
  {
378
495
  $lookup: {
379
- from: cleaning_schedule_namespace_collection,
380
- localField: "schedule",
496
+ from: "users",
497
+ localField: "_assigneeId",
381
498
  foreignField: "_id",
382
- as: "scheduleDoc",
383
- pipeline: [
384
- {
385
- $match: {
386
- site,
387
- createdAt: yesterDayQueries.toiletArea.createdAt
388
- }
389
- }
390
- ]
499
+ as: "_assigneeDoc"
391
500
  }
392
501
  },
502
+ { $sort: { createdAt: -1, _id: -1 } },
393
503
  {
394
- $unwind: {
395
- path: "$scheduleDoc",
396
- preserveNullAndEmptyArrays: false
504
+ $project: {
505
+ name: 1,
506
+ type: 1,
507
+ status: 1,
508
+ assigneeName: { $arrayElemAt: ["$_assigneeDoc.name", 0] }
397
509
  }
398
510
  },
399
- { $count: "count" }
400
- ]).toArray(),
401
- scheduleTaskCollection.aggregate([
402
- { $match: yesterDayQueries.scheduleTask },
403
- { $count: "count" }
511
+ { $skip: page * limit },
512
+ { $limit: limit }
404
513
  ]).toArray(),
405
- supplyCollection.aggregate([{ $match: yesterDayQueries.supply }, { $count: "count" }]).toArray(),
406
- requestItemCollection.aggregate([
407
- { $match: yesterDayQueries.requestItem },
514
+ areaChecklistCollection.aggregate([
515
+ ...areaChecklistScheduleLookup(site, serviceType, todayRange),
408
516
  { $count: "count" }
409
517
  ]).toArray()
410
518
  ]);
411
- const [
412
- todayFeedbackCount,
413
- todayCommonAreaChecklistCount,
414
- todayToiletAreaChecklistCount,
415
- todayScheduledTaskCount,
416
- todaySupplyCount,
417
- todayRequestItemCount
418
- ] = await Promise.all([
419
- feedbackCollection.aggregate([
519
+ const length = countResult[0]?.count ?? 0;
520
+ const data = (0, import_node_server_utils.paginate)(items, page, limit, length);
521
+ setCache(cacheKey, data, 15 * 60).then(() => {
522
+ import_node_server_utils.logger.info(`Cache set for todayTaskSchedule: ${cacheKey}`);
523
+ }).catch((err) => {
524
+ import_node_server_utils.logger.error(
525
+ `Failed to set cache for todayTaskSchedule: ${cacheKey}`,
526
+ err
527
+ );
528
+ });
529
+ return data;
530
+ } catch (error) {
531
+ throw error;
532
+ }
533
+ }
534
+ async function getStaffAttendance({
535
+ site,
536
+ serviceType,
537
+ page = 1,
538
+ limit = 10
539
+ }) {
540
+ page = page > 0 ? page - 1 : 0;
541
+ const cacheOptions = { serviceType, page, limit };
542
+ try {
543
+ site = new import_mongodb.ObjectId(site);
544
+ cacheOptions.site = site.toString();
545
+ } catch {
546
+ throw new import_node_server_utils.BadRequestError("Invalid site ID format.");
547
+ }
548
+ const cacheKey = (0, import_node_server_utils.makeCacheKey)(
549
+ `${dashboard_namespace_collection}:staffAttendance`,
550
+ cacheOptions
551
+ );
552
+ const cachedData = await getCache(cacheKey);
553
+ if (cachedData) {
554
+ import_node_server_utils.logger.info(`Cache hit for staffAttendance: ${cacheKey}`);
555
+ return cachedData;
556
+ }
557
+ const todayStart = import_moment_timezone.default.tz("Asia/Singapore").startOf("day").toDate();
558
+ const todayEnd = import_moment_timezone.default.tz("Asia/Singapore").endOf("day").toDate();
559
+ const todayStartStr = todayStart.toISOString();
560
+ const todayEndStr = todayEnd.toISOString();
561
+ try {
562
+ const [items, length] = await Promise.all([
563
+ attendanceCollection.aggregate([
420
564
  {
421
565
  $match: {
422
- ...todayQueries.feedback,
423
- category: "cleaning_services",
424
- status: "to-do"
566
+ site,
567
+ serviceType,
568
+ $or: [
569
+ { createdAt: { $gte: todayStart, $lte: todayEnd } },
570
+ { createdAt: { $gte: todayStartStr, $lte: todayEndStr } }
571
+ ]
425
572
  }
426
573
  },
427
- { $count: "count" }
428
- ]).toArray(),
429
- areaChecklistCollection.aggregate([
430
- { $match: { type: "common" } },
431
574
  {
432
575
  $lookup: {
433
- from: cleaning_schedule_namespace_collection,
434
- localField: "schedule",
576
+ from: "users",
577
+ localField: "user",
435
578
  foreignField: "_id",
436
- as: "scheduleDoc",
579
+ as: "_userDoc"
580
+ }
581
+ },
582
+ {
583
+ $lookup: {
584
+ from: "members",
585
+ let: { userId: "$user", siteId: "$site", type: "$serviceType" },
437
586
  pipeline: [
438
587
  {
439
588
  $match: {
440
- site,
441
- createdAt: todayQueries.commonArea.createdAt
589
+ $expr: {
590
+ $and: [
591
+ { $eq: ["$user", "$$userId"] },
592
+ { $eq: ["$siteId", "$$siteId"] },
593
+ { $eq: ["$type", "$$type"] },
594
+ { $ne: ["$status", "deleted"] }
595
+ ]
596
+ }
442
597
  }
443
598
  }
444
- ]
599
+ ],
600
+ as: "_memberDoc"
445
601
  }
446
602
  },
447
603
  {
448
604
  $unwind: {
449
- path: "$scheduleDoc",
450
- preserveNullAndEmptyArrays: false
605
+ path: "$_memberDoc",
606
+ preserveNullAndEmptyArrays: true
451
607
  }
452
608
  },
453
- { $count: "count" }
454
- ]).toArray(),
455
- areaChecklistCollection.aggregate([
456
- { $match: { type: "toilet" } },
457
609
  {
458
610
  $lookup: {
459
- from: cleaning_schedule_namespace_collection,
460
- localField: "schedule",
611
+ from: "roles",
612
+ localField: "_memberDoc.role",
461
613
  foreignField: "_id",
462
- as: "scheduleDoc",
463
- pipeline: [
464
- {
465
- $match: {
466
- site,
467
- createdAt: todayQueries.toiletArea.createdAt
468
- }
469
- }
470
- ]
614
+ as: "_roleDoc"
471
615
  }
472
616
  },
473
617
  {
474
618
  $unwind: {
475
- path: "$scheduleDoc",
476
- preserveNullAndEmptyArrays: false
619
+ path: "$_roleDoc",
620
+ preserveNullAndEmptyArrays: true
477
621
  }
478
622
  },
479
- { $count: "count" }
623
+ { $sort: { createdAt: -1, _id: -1 } },
624
+ {
625
+ $project: {
626
+ checkIn: 1,
627
+ checkOut: 1,
628
+ createdAt: 1,
629
+ userName: { $arrayElemAt: ["$_userDoc.name", 0] },
630
+ userRole: { $ifNull: ["$_roleDoc.name", ""] },
631
+ totalHours: {
632
+ $round: [
633
+ {
634
+ $divide: [
635
+ {
636
+ $subtract: [
637
+ {
638
+ $toDate: {
639
+ $ifNull: ["$checkOut.timestamp", "$$NOW"]
640
+ }
641
+ },
642
+ { $toDate: "$checkIn.timestamp" }
643
+ ]
644
+ },
645
+ 36e5
646
+ ]
647
+ },
648
+ 2
649
+ ]
650
+ }
651
+ }
652
+ },
653
+ { $skip: page * limit },
654
+ { $limit: limit }
480
655
  ]).toArray(),
481
- scheduleTaskCollection.aggregate([
482
- { $match: todayQueries.scheduleTask },
483
- { $count: "count" }
656
+ attendanceCollection.countDocuments({
657
+ site,
658
+ serviceType,
659
+ $or: [
660
+ { createdAt: { $gte: todayStart, $lte: todayEnd } },
661
+ { createdAt: { $gte: todayStartStr, $lte: todayEndStr } }
662
+ ]
663
+ })
664
+ ]);
665
+ const data = (0, import_node_server_utils.paginate)(items, page, limit, length);
666
+ setCache(cacheKey, data, 15 * 60).then(() => {
667
+ import_node_server_utils.logger.info(`Cache set for staffAttendance: ${cacheKey}`);
668
+ }).catch((err) => {
669
+ import_node_server_utils.logger.error(
670
+ `Failed to set cache for staffAttendance: ${cacheKey}`,
671
+ err
672
+ );
673
+ });
674
+ return data;
675
+ } catch (error) {
676
+ throw error;
677
+ }
678
+ }
679
+ async function getRecentFeedbacks({
680
+ site,
681
+ serviceType,
682
+ page = 1,
683
+ limit = 10
684
+ }) {
685
+ page = page > 0 ? page - 1 : 0;
686
+ const cacheOptions = { serviceType, page, limit };
687
+ try {
688
+ site = new import_mongodb.ObjectId(site);
689
+ cacheOptions.site = site.toString();
690
+ } catch {
691
+ throw new import_node_server_utils.BadRequestError("Invalid site ID format.");
692
+ }
693
+ const cacheKey = (0, import_node_server_utils.makeCacheKey)(
694
+ `${dashboard_namespace_collection}:recentFeedbacks`,
695
+ cacheOptions
696
+ );
697
+ const cachedData = await getCache(cacheKey);
698
+ if (cachedData) {
699
+ import_node_server_utils.logger.info(`Cache hit for recentFeedbacks: ${cacheKey}`);
700
+ return cachedData;
701
+ }
702
+ const workOrderService = workOrderServiceMap[serviceType] ?? serviceType;
703
+ try {
704
+ const [items, length] = await Promise.all([
705
+ feedbackCollection.aggregate([
706
+ { $match: { site, service: workOrderService } },
707
+ { $sort: { createdAt: -1, _id: -1 } },
708
+ {
709
+ $lookup: {
710
+ from: "users",
711
+ localField: "createdBy",
712
+ foreignField: "_id",
713
+ as: "_createdByDoc"
714
+ }
715
+ },
716
+ {
717
+ $project: {
718
+ description: 1,
719
+ subject: 1,
720
+ app: 1,
721
+ status: 1,
722
+ createdAt: 1,
723
+ createdByName: { $arrayElemAt: ["$_createdByDoc.name", 0] }
724
+ }
725
+ },
726
+ { $skip: page * limit },
727
+ { $limit: limit }
484
728
  ]).toArray(),
485
- supplyCollection.aggregate([{ $match: todayQueries.supply }, { $count: "count" }]).toArray(),
486
- requestItemCollection.aggregate([
487
- { $match: todayQueries.requestItem },
488
- { $count: "count" }
489
- ]).toArray()
729
+ feedbackCollection.countDocuments({ site, service: workOrderService })
490
730
  ]);
491
- const resultYesterday = {
492
- feedbackCount: yesterdayFeedbackCount[0]?.count || 0,
493
- commonAreaChecklistCount: yesterdayCommonAreaChecklistCount[0]?.count || 0,
494
- toiletAreaChecklistCount: yesterdayToiletAreaChecklistCount[0]?.count || 0,
495
- scheduleTaskCount: yesterdayScheduledTaskCount[0]?.count || 0,
496
- supplyCount: yesterdaySupplyCount[0]?.count || 0,
497
- requestItemCount: yesterdayRequestItemCount[0]?.count || 0
498
- };
499
- const resultToday = {
500
- feedbackCount: todayFeedbackCount[0]?.count || 0,
501
- commonAreaChecklistCount: todayCommonAreaChecklistCount[0]?.count || 0,
502
- toiletAreaChecklistCount: todayToiletAreaChecklistCount[0]?.count || 0,
503
- scheduleTaskCount: todayScheduledTaskCount[0]?.count || 0,
504
- supplyCount: todaySupplyCount[0]?.count || 0,
505
- requestItemCount: todayRequestItemCount[0]?.count || 0
506
- };
507
- const result = {
508
- feedback: {
509
- count: feedbackReport[0]?.count || 0,
510
- percentage: calculatePercentageChange(
511
- resultToday.feedbackCount,
512
- resultYesterday.feedbackCount
513
- )
514
- },
515
- commonAreaChecklist: {
516
- count: commonAreaChecklistReport[0]?.count || 0,
517
- percentage: calculatePercentageChange(
518
- resultToday.commonAreaChecklistCount,
519
- resultYesterday.commonAreaChecklistCount
520
- )
521
- },
522
- toiletAreaChecklist: {
523
- count: toiletAreaChecklistReport[0]?.count || 0,
524
- percentage: calculatePercentageChange(
525
- resultToday.toiletAreaChecklistCount,
526
- resultYesterday.toiletAreaChecklistCount
527
- )
528
- },
529
- scheduleTask: {
530
- count: scheduleTaskReport[0]?.count || 0,
531
- percentage: calculatePercentageChange(
532
- resultToday.scheduleTaskCount,
533
- resultYesterday.scheduleTaskCount
534
- )
535
- },
536
- supply: {
537
- count: supplyReport[0]?.count || 0,
538
- percentage: calculatePercentageChange(
539
- resultToday.supplyCount,
540
- resultYesterday.supplyCount
541
- )
542
- },
543
- requestItem: {
544
- count: requestItemReport[0]?.count || 0,
545
- percentage: calculatePercentageChange(
546
- resultToday.requestItemCount,
547
- resultYesterday.requestItemCount
548
- )
549
- }
550
- };
551
- setCache(dashboardCacheKey, result, 15 * 60).then(() => {
552
- import_node_server_utils.logger.info(`Cache set for dashboard: ${dashboardCacheKey}`);
731
+ const data = (0, import_node_server_utils.paginate)(items, page, limit, length);
732
+ setCache(cacheKey, data, 15 * 60).then(() => {
733
+ import_node_server_utils.logger.info(`Cache set for recentFeedbacks: ${cacheKey}`);
553
734
  }).catch((err) => {
554
735
  import_node_server_utils.logger.error(
555
- `Failed to set cache for dashboard: ${dashboardCacheKey}`,
736
+ `Failed to set cache for recentFeedbacks: ${cacheKey}`,
556
737
  err
557
738
  );
558
739
  });
559
- return result;
740
+ return data;
560
741
  } catch (error) {
561
742
  throw error;
562
743
  }
563
744
  }
564
745
  return {
565
- getHygieneDashboard
746
+ getHygieneDashboard,
747
+ getWeeklyActivity,
748
+ getTodayTaskSchedule,
749
+ getStaffAttendance,
750
+ getRecentFeedbacks
566
751
  };
567
752
  }
568
753
 
569
754
  // src/controllers/hygiene-dashboard.controller.ts
570
755
  var import_joi = __toESM(require("joi"));
571
756
  var import_node_server_utils2 = require("@7365admin1/node-server-utils");
757
+ var import_core = require("@7365admin1/core");
572
758
  function useHygieneDashboardController() {
573
- const { getHygieneDashboard: _getHygieneDashboard } = useHygieneDashboardRepository();
759
+ const {
760
+ getHygieneDashboard: _getHygieneDashboard,
761
+ getWeeklyActivity: _getWeeklyActivity,
762
+ getTodayTaskSchedule: _getTodayTaskSchedule,
763
+ getStaffAttendance: _getStaffAttendance,
764
+ getRecentFeedbacks: _getRecentFeedbacks
765
+ } = useHygieneDashboardRepository();
574
766
  async function getHygieneDashboard(req, res, next) {
575
767
  const query = { ...req.query, ...req.params };
576
768
  const validation = import_joi.default.object({
577
769
  site: import_joi.default.string().hex().required(),
578
- feedbackPeriod: import_joi.default.string().valid(...allowedPeriods).optional(),
579
- commonAreaPeriod: import_joi.default.string().valid(...allowedPeriods).optional(),
580
- toiletAreaPeriod: import_joi.default.string().valid(...allowedPeriods).optional(),
581
- scheduleTaskPeriod: import_joi.default.string().valid(...allowedPeriods).optional(),
582
- supplyPeriod: import_joi.default.string().valid(...allowedPeriods).optional(),
583
- requestItemPeriod: import_joi.default.string().valid(...allowedPeriods).optional()
770
+ serviceType: import_joi.default.string().valid(...Object.values(import_core.AppServiceType)).required(),
771
+ period: import_joi.default.string().valid(...allowedPeriods).optional()
584
772
  });
585
773
  const { error } = validation.validate(query);
586
774
  if (error) {
@@ -589,21 +777,136 @@ function useHygieneDashboardController() {
589
777
  return;
590
778
  }
591
779
  const site = req.params.site ?? "";
592
- const feedbackPeriod = req.query.feedbackPeriod || "today";
593
- const commonAreaPeriod = req.query.commonAreaPeriod || "today";
594
- const toiletAreaPeriod = req.query.toiletAreaPeriod || "today";
595
- const scheduleTaskPeriod = req.query.scheduleTaskPeriod || "today";
596
- const supplyPeriod = req.query.supplyPeriod || "today";
597
- const requestItemPeriod = req.query.requestItemPeriod || "today";
780
+ const serviceType = req.params.serviceType;
781
+ const period = req.query.period || "today";
598
782
  try {
599
783
  const data = await _getHygieneDashboard({
600
784
  site,
601
- feedbackPeriod,
602
- commonAreaPeriod,
603
- toiletAreaPeriod,
604
- scheduleTaskPeriod,
605
- supplyPeriod,
606
- requestItemPeriod
785
+ period,
786
+ serviceType
787
+ });
788
+ res.json(data);
789
+ return;
790
+ } catch (error2) {
791
+ import_node_server_utils2.logger.log({ level: "error", message: error2.message });
792
+ next(error2);
793
+ return;
794
+ }
795
+ }
796
+ async function getWeeklyActivity(req, res, next) {
797
+ const query = { ...req.params };
798
+ const validation = import_joi.default.object({
799
+ site: import_joi.default.string().hex().required(),
800
+ serviceType: import_joi.default.string().valid(...Object.values(import_core.AppServiceType)).required()
801
+ });
802
+ const { error } = validation.validate(query);
803
+ if (error) {
804
+ import_node_server_utils2.logger.log({ level: "error", message: error.message });
805
+ next(new import_node_server_utils2.BadRequestError(error.message));
806
+ return;
807
+ }
808
+ const site = req.params.site;
809
+ const serviceType = req.params.serviceType;
810
+ try {
811
+ const data = await _getWeeklyActivity({ site, serviceType });
812
+ res.json(data);
813
+ return;
814
+ } catch (error2) {
815
+ import_node_server_utils2.logger.log({ level: "error", message: error2.message });
816
+ next(error2);
817
+ return;
818
+ }
819
+ }
820
+ async function getTodayTaskSchedule(req, res, next) {
821
+ const query = { ...req.query, ...req.params };
822
+ const validation = import_joi.default.object({
823
+ site: import_joi.default.string().hex().required(),
824
+ serviceType: import_joi.default.string().valid(...Object.values(import_core.AppServiceType)).required(),
825
+ page: import_joi.default.number().min(1).optional().allow("", null),
826
+ limit: import_joi.default.number().min(1).optional().allow("", null)
827
+ });
828
+ const { error } = validation.validate(query);
829
+ if (error) {
830
+ import_node_server_utils2.logger.log({ level: "error", message: error.message });
831
+ next(new import_node_server_utils2.BadRequestError(error.message));
832
+ return;
833
+ }
834
+ const site = req.params.site;
835
+ const serviceType = req.params.serviceType;
836
+ const page = parseInt(req.query.page) ?? 1;
837
+ const limit = parseInt(req.query.limit) ?? 10;
838
+ try {
839
+ const data = await _getTodayTaskSchedule({
840
+ site,
841
+ serviceType,
842
+ page,
843
+ limit
844
+ });
845
+ res.json(data);
846
+ return;
847
+ } catch (error2) {
848
+ import_node_server_utils2.logger.log({ level: "error", message: error2.message });
849
+ next(error2);
850
+ return;
851
+ }
852
+ }
853
+ async function getStaffAttendance(req, res, next) {
854
+ const query = { ...req.query, ...req.params };
855
+ const validation = import_joi.default.object({
856
+ site: import_joi.default.string().hex().required(),
857
+ serviceType: import_joi.default.string().valid(...Object.values(import_core.AppServiceType)).required(),
858
+ page: import_joi.default.number().min(1).optional().allow("", null),
859
+ limit: import_joi.default.number().min(1).optional().allow("", null)
860
+ });
861
+ const { error } = validation.validate(query);
862
+ if (error) {
863
+ import_node_server_utils2.logger.log({ level: "error", message: error.message });
864
+ next(new import_node_server_utils2.BadRequestError(error.message));
865
+ return;
866
+ }
867
+ const site = req.params.site;
868
+ const serviceType = req.params.serviceType;
869
+ const page = parseInt(req.query.page) ?? 1;
870
+ const limit = parseInt(req.query.limit) ?? 10;
871
+ try {
872
+ const data = await _getStaffAttendance({
873
+ site,
874
+ serviceType,
875
+ page,
876
+ limit
877
+ });
878
+ res.json(data);
879
+ return;
880
+ } catch (error2) {
881
+ import_node_server_utils2.logger.log({ level: "error", message: error2.message });
882
+ next(error2);
883
+ return;
884
+ }
885
+ }
886
+ async function getRecentFeedbacks(req, res, next) {
887
+ const query = { ...req.query, ...req.params };
888
+ const validation = import_joi.default.object({
889
+ site: import_joi.default.string().hex().required(),
890
+ serviceType: import_joi.default.string().valid(...Object.values(import_core.AppServiceType)).required(),
891
+ page: import_joi.default.number().min(1).optional().allow("", null),
892
+ limit: import_joi.default.number().min(1).optional().allow("", null)
893
+ });
894
+ const { error } = validation.validate(query);
895
+ if (error) {
896
+ import_node_server_utils2.logger.log({ level: "error", message: error.message });
897
+ next(new import_node_server_utils2.BadRequestError(error.message));
898
+ return;
899
+ }
900
+ const site = req.params.site;
901
+ const serviceType = req.params.serviceType;
902
+ const page = parseInt(req.query.page) ?? 1;
903
+ const limit = parseInt(req.query.limit) ?? 10;
904
+ try {
905
+ const data = await _getRecentFeedbacks({
906
+ site,
907
+ serviceType,
908
+ page,
909
+ limit
607
910
  });
608
911
  res.json(data);
609
912
  return;
@@ -614,7 +917,11 @@ function useHygieneDashboardController() {
614
917
  }
615
918
  }
616
919
  return {
617
- getHygieneDashboard
920
+ getHygieneDashboard,
921
+ getWeeklyActivity,
922
+ getTodayTaskSchedule,
923
+ getStaffAttendance,
924
+ getRecentFeedbacks
618
925
  };
619
926
  }
620
927
 
@@ -622,10 +929,10 @@ function useHygieneDashboardController() {
622
929
  var import_joi2 = __toESM(require("joi"));
623
930
  var import_mongodb2 = require("mongodb");
624
931
  var import_node_server_utils3 = require("@7365admin1/node-server-utils");
625
- var import_core = require("@7365admin1/core");
932
+ var import_core2 = require("@7365admin1/core");
626
933
  var areaSchema = import_joi2.default.object({
627
934
  site: import_joi2.default.string().hex().required(),
628
- serviceType: import_joi2.default.string().valid(...Object.values(import_core.AppServiceType)).required(),
935
+ serviceType: import_joi2.default.string().valid(...Object.values(import_core2.AppServiceType)).required(),
629
936
  name: import_joi2.default.string().required(),
630
937
  type: import_joi2.default.string().valid(...allowedTypes).required(),
631
938
  set: import_joi2.default.number().min(0).optional(),
@@ -1129,10 +1436,10 @@ var import_node_server_utils7 = require("@7365admin1/node-server-utils");
1129
1436
  var import_joi3 = __toESM(require("joi"));
1130
1437
  var import_mongodb4 = require("mongodb");
1131
1438
  var import_node_server_utils6 = require("@7365admin1/node-server-utils");
1132
- var import_core2 = require("@7365admin1/core");
1439
+ var import_core3 = require("@7365admin1/core");
1133
1440
  var unitSchema = import_joi3.default.object({
1134
1441
  site: import_joi3.default.string().hex().required(),
1135
- serviceType: import_joi3.default.string().valid(...Object.values(import_core2.AppServiceType)).required(),
1442
+ serviceType: import_joi3.default.string().valid(...Object.values(import_core3.AppServiceType)).required(),
1136
1443
  name: import_joi3.default.string().required()
1137
1444
  });
1138
1445
  function MUnit(value) {
@@ -1553,7 +1860,7 @@ function useAreaService() {
1553
1860
  // src/controllers/hygiene-area.controller.ts
1554
1861
  var import_joi4 = __toESM(require("joi"));
1555
1862
  var import_node_server_utils9 = require("@7365admin1/node-server-utils");
1556
- var import_core3 = require("@7365admin1/core");
1863
+ var import_core4 = require("@7365admin1/core");
1557
1864
 
1558
1865
  // src/utils/convert-excel.util.ts
1559
1866
  var import_stream = require("stream");
@@ -1618,7 +1925,7 @@ function useAreaController() {
1618
1925
  search: import_joi4.default.string().optional().allow("", null),
1619
1926
  type: import_joi4.default.string().valid("all", ...allowedTypes).optional().allow("", null),
1620
1927
  site: import_joi4.default.string().hex().required(),
1621
- serviceType: import_joi4.default.string().valid(...Object.values(import_core3.AppServiceType)).required()
1928
+ serviceType: import_joi4.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
1622
1929
  });
1623
1930
  const { error } = validation.validate(query);
1624
1931
  if (error) {
@@ -1727,7 +2034,7 @@ function useAreaController() {
1727
2034
  const query = { ...req.query, ...req.params };
1728
2035
  const validation = import_joi4.default.object({
1729
2036
  site: import_joi4.default.string().hex().required(),
1730
- serviceType: import_joi4.default.string().valid(...Object.values(import_core3.AppServiceType)).required()
2037
+ serviceType: import_joi4.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
1731
2038
  });
1732
2039
  const { error, value } = validation.validate(query);
1733
2040
  if (error) {
@@ -1750,7 +2057,7 @@ function useAreaController() {
1750
2057
  const query = { ...req.query, ...req.params };
1751
2058
  const validation = import_joi4.default.object({
1752
2059
  site: import_joi4.default.string().hex().required(),
1753
- serviceType: import_joi4.default.string().valid(...Object.values(import_core3.AppServiceType)).required()
2060
+ serviceType: import_joi4.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
1754
2061
  });
1755
2062
  const { error, value } = validation.validate(query);
1756
2063
  if (error) {
@@ -2028,7 +2335,7 @@ function useUnitService() {
2028
2335
  // src/controllers/hygiene-unit.controller.ts
2029
2336
  var import_joi5 = __toESM(require("joi"));
2030
2337
  var import_node_server_utils12 = require("@7365admin1/node-server-utils");
2031
- var import_core4 = require("@7365admin1/core");
2338
+ var import_core5 = require("@7365admin1/core");
2032
2339
  function useUnitController() {
2033
2340
  const { createUnit: _createUnit, getUnits: _getUnits } = useUnitRepository();
2034
2341
  const {
@@ -2062,7 +2369,7 @@ function useUnitController() {
2062
2369
  limit: import_joi5.default.number().min(1).optional().allow("", null),
2063
2370
  search: import_joi5.default.string().optional().allow("", null),
2064
2371
  site: import_joi5.default.string().hex().required(),
2065
- serviceType: import_joi5.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
2372
+ serviceType: import_joi5.default.string().valid(...Object.values(import_core5.AppServiceType)).required()
2066
2373
  });
2067
2374
  const { error } = validation.validate(query);
2068
2375
  if (error) {
@@ -2143,7 +2450,7 @@ function useUnitController() {
2143
2450
  const query = { ...req.query, ...req.params };
2144
2451
  const validation = import_joi5.default.object({
2145
2452
  site: import_joi5.default.string().hex().required(),
2146
- serviceType: import_joi5.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
2453
+ serviceType: import_joi5.default.string().valid(...Object.values(import_core5.AppServiceType)).required()
2147
2454
  });
2148
2455
  const { error, value } = validation.validate(query);
2149
2456
  if (error) {
@@ -2166,7 +2473,7 @@ function useUnitController() {
2166
2473
  const query = { ...req.query, ...req.params };
2167
2474
  const validation = import_joi5.default.object({
2168
2475
  site: import_joi5.default.string().hex().required(),
2169
- serviceType: import_joi5.default.string().valid(...Object.values(import_core4.AppServiceType)).required()
2476
+ serviceType: import_joi5.default.string().valid(...Object.values(import_core5.AppServiceType)).required()
2170
2477
  });
2171
2478
  const { error, value } = validation.validate(query);
2172
2479
  if (error) {
@@ -2212,11 +2519,11 @@ function useUnitController() {
2212
2519
  var import_joi6 = __toESM(require("joi"));
2213
2520
  var import_mongodb6 = require("mongodb");
2214
2521
  var import_node_server_utils13 = require("@7365admin1/node-server-utils");
2215
- var import_core5 = require("@7365admin1/core");
2522
+ var import_core6 = require("@7365admin1/core");
2216
2523
  var parentChecklistSchema = import_joi6.default.object({
2217
2524
  createdAt: import_joi6.default.alternatives().try(import_joi6.default.date(), import_joi6.default.string()).optional().allow("", null),
2218
2525
  site: import_joi6.default.string().hex().required(),
2219
- serviceType: import_joi6.default.string().valid(...Object.values(import_core5.AppServiceType)).required()
2526
+ serviceType: import_joi6.default.string().valid(...Object.values(import_core6.AppServiceType)).required()
2220
2527
  });
2221
2528
  function MParentChecklist(value) {
2222
2529
  const { error } = parentChecklistSchema.validate(value);
@@ -2243,7 +2550,7 @@ function MParentChecklist(value) {
2243
2550
  // src/repositories/hygiene-parent-checklist.repository.ts
2244
2551
  var import_mongodb7 = require("mongodb");
2245
2552
  var import_node_server_utils14 = require("@7365admin1/node-server-utils");
2246
- var import_core6 = require("@7365admin1/core");
2553
+ var import_core7 = require("@7365admin1/core");
2247
2554
  function useParentChecklistRepo() {
2248
2555
  const db = import_node_server_utils14.useAtlas.getDb();
2249
2556
  if (!db) {
@@ -2275,7 +2582,7 @@ function useParentChecklistRepo() {
2275
2582
  startOfDay.setUTCHours(0, 0, 0, 0);
2276
2583
  const endOfDay = new Date(currentDate);
2277
2584
  endOfDay.setUTCHours(23, 59, 59, 999);
2278
- const allServiceTypes = Object.values(import_core6.AppServiceType);
2585
+ const allServiceTypes = Object.values(import_core7.AppServiceType);
2279
2586
  const dateStr = currentDate.toISOString().split("T")[0];
2280
2587
  if (value.site) {
2281
2588
  let siteObjectId;
@@ -2680,7 +2987,7 @@ function useParentChecklistRepo() {
2680
2987
  // src/controllers/hygiene-parent-checklist.controller.ts
2681
2988
  var import_joi7 = __toESM(require("joi"));
2682
2989
  var import_node_server_utils15 = require("@7365admin1/node-server-utils");
2683
- var import_core7 = require("@7365admin1/core");
2990
+ var import_core8 = require("@7365admin1/core");
2684
2991
  function useParentChecklistController() {
2685
2992
  const {
2686
2993
  createParentChecklist: _createParentChecklist,
@@ -2691,7 +2998,7 @@ function useParentChecklistController() {
2691
2998
  const validation = import_joi7.default.object({
2692
2999
  createdAt: import_joi7.default.alternatives().try(import_joi7.default.date(), import_joi7.default.string()).optional().allow("", null),
2693
3000
  site: import_joi7.default.string().hex().required(),
2694
- serviceType: import_joi7.default.string().valid(...Object.values(import_core7.AppServiceType)).optional().allow("", null)
3001
+ serviceType: import_joi7.default.string().valid(...Object.values(import_core8.AppServiceType)).optional().allow("", null)
2695
3002
  });
2696
3003
  const { error } = validation.validate(payload);
2697
3004
  if (error) {
@@ -2716,7 +3023,7 @@ function useParentChecklistController() {
2716
3023
  limit: import_joi7.default.number().min(1).optional().allow("", null),
2717
3024
  search: import_joi7.default.string().optional().allow("", null),
2718
3025
  site: import_joi7.default.string().hex().required(),
2719
- serviceType: import_joi7.default.string().valid(...Object.values(import_core7.AppServiceType)).required(),
3026
+ serviceType: import_joi7.default.string().valid(...Object.values(import_core8.AppServiceType)).required(),
2720
3027
  startDate: import_joi7.default.alternatives().try(import_joi7.default.date(), import_joi7.default.string()).optional().allow("", null),
2721
3028
  endDate: import_joi7.default.alternatives().try(import_joi7.default.date(), import_joi7.default.string()).optional().allow("", null),
2722
3029
  status: import_joi7.default.string().valid(...allowedStatus, "all").optional().allow("", null)
@@ -2764,11 +3071,11 @@ function useParentChecklistController() {
2764
3071
  var import_joi8 = __toESM(require("joi"));
2765
3072
  var import_mongodb8 = require("mongodb");
2766
3073
  var import_node_server_utils16 = require("@7365admin1/node-server-utils");
2767
- var import_core8 = require("@7365admin1/core");
3074
+ var import_core9 = require("@7365admin1/core");
2768
3075
  var allowedChecklistStatus = ["open", "completed", "closed"];
2769
3076
  var areaChecklistSchema = import_joi8.default.object({
2770
3077
  schedule: import_joi8.default.string().hex().required(),
2771
- serviceType: import_joi8.default.string().valid(...Object.values(import_core8.AppServiceType)).required(),
3078
+ serviceType: import_joi8.default.string().valid(...Object.values(import_core9.AppServiceType)).required(),
2772
3079
  area: import_joi8.default.string().hex().required(),
2773
3080
  name: import_joi8.default.string().required(),
2774
3081
  type: import_joi8.default.string().valid(...allowedTypes).required(),
@@ -2842,7 +3149,7 @@ function MAreaChecklist(value) {
2842
3149
  // src/services/hygiene-area-checklist.service.ts
2843
3150
  var import_mongodb10 = require("mongodb");
2844
3151
  var import_node_server_utils18 = require("@7365admin1/node-server-utils");
2845
- var import_core9 = require("@7365admin1/core");
3152
+ var import_core10 = require("@7365admin1/core");
2846
3153
 
2847
3154
  // src/repositories/hygiene-area-checklist.repository.ts
2848
3155
  var import_mongodb9 = require("mongodb");
@@ -3978,7 +4285,7 @@ function useAreaChecklistService() {
3978
4285
  insertAutoGenSets
3979
4286
  } = useAreaChecklistRepo();
3980
4287
  const { updateParentChecklistStatuses, getParentChecklistById } = useParentChecklistRepo();
3981
- const { getUserById } = (0, import_core9.useUserRepo)();
4288
+ const { getUserById } = (0, import_core10.useUserRepo)();
3982
4289
  async function createAreaChecklist(value) {
3983
4290
  const results = [];
3984
4291
  let totalChecklistsCreated = 0;
@@ -4204,7 +4511,7 @@ function useAreaChecklistService() {
4204
4511
  // src/controllers/hygiene-area-checklist.controller.ts
4205
4512
  var import_joi9 = __toESM(require("joi"));
4206
4513
  var import_node_server_utils20 = require("@7365admin1/node-server-utils");
4207
- var import_core10 = require("@7365admin1/core");
4514
+ var import_core11 = require("@7365admin1/core");
4208
4515
 
4209
4516
  // src/services/hygiene-checklist-pdf.service.ts
4210
4517
  var import_puppeteer = require("puppeteer");
@@ -4471,7 +4778,7 @@ function useAreaChecklistController() {
4471
4778
  type: import_joi9.default.string().valid(...allowedTypes, "all").optional().allow("", null),
4472
4779
  status: import_joi9.default.string().valid(...allowedStatus, "all").optional().allow("", null),
4473
4780
  schedule: import_joi9.default.string().hex().required(),
4474
- serviceType: import_joi9.default.string().valid(...Object.values(import_core10.AppServiceType)).required()
4781
+ serviceType: import_joi9.default.string().valid(...Object.values(import_core11.AppServiceType)).required()
4475
4782
  });
4476
4783
  const { error } = validation.validate(query);
4477
4784
  if (error) {
@@ -4571,7 +4878,7 @@ function useAreaChecklistController() {
4571
4878
  page: import_joi9.default.number().min(1).optional().allow("", null),
4572
4879
  limit: import_joi9.default.number().min(1).optional().allow("", null),
4573
4880
  search: import_joi9.default.string().optional().allow("", null),
4574
- serviceType: import_joi9.default.string().valid(...Object.values(import_core10.AppServiceType)).required(),
4881
+ serviceType: import_joi9.default.string().valid(...Object.values(import_core11.AppServiceType)).required(),
4575
4882
  id: import_joi9.default.string().hex().required()
4576
4883
  });
4577
4884
  const { error } = validation.validate(query);
@@ -4700,10 +5007,10 @@ function useAreaChecklistController() {
4700
5007
  var import_joi10 = __toESM(require("joi"));
4701
5008
  var import_mongodb12 = require("mongodb");
4702
5009
  var import_node_server_utils21 = require("@7365admin1/node-server-utils");
4703
- var import_core11 = require("@7365admin1/core");
5010
+ var import_core12 = require("@7365admin1/core");
4704
5011
  var supplySchema = import_joi10.default.object({
4705
5012
  site: import_joi10.default.string().hex().required(),
4706
- serviceType: import_joi10.default.string().valid(...Object.values(import_core11.AppServiceType)).required(),
5013
+ serviceType: import_joi10.default.string().valid(...Object.values(import_core12.AppServiceType)).required(),
4707
5014
  name: import_joi10.default.string().required(),
4708
5015
  unitOfMeasurement: import_joi10.default.string().required()
4709
5016
  });
@@ -4985,7 +5292,7 @@ function useSupplyRepository() {
4985
5292
  // src/controllers/hygiene-supply.controller.ts
4986
5293
  var import_joi11 = __toESM(require("joi"));
4987
5294
  var import_node_server_utils23 = require("@7365admin1/node-server-utils");
4988
- var import_core12 = require("@7365admin1/core");
5295
+ var import_core13 = require("@7365admin1/core");
4989
5296
  function useSupplyController() {
4990
5297
  const {
4991
5298
  createSupply: _createSupply,
@@ -5019,7 +5326,7 @@ function useSupplyController() {
5019
5326
  limit: import_joi11.default.number().min(1).optional().allow("", null),
5020
5327
  search: import_joi11.default.string().optional().allow("", null),
5021
5328
  site: import_joi11.default.string().hex().required(),
5022
- serviceType: import_joi11.default.string().valid(...Object.values(import_core12.AppServiceType)).required()
5329
+ serviceType: import_joi11.default.string().valid(...Object.values(import_core13.AppServiceType)).required()
5023
5330
  });
5024
5331
  const { error } = validation.validate(query);
5025
5332
  if (error) {
@@ -5124,10 +5431,10 @@ function useSupplyController() {
5124
5431
  var import_joi12 = __toESM(require("joi"));
5125
5432
  var import_mongodb14 = require("mongodb");
5126
5433
  var import_node_server_utils24 = require("@7365admin1/node-server-utils");
5127
- var import_core13 = require("@7365admin1/core");
5434
+ var import_core14 = require("@7365admin1/core");
5128
5435
  var stockSchema = import_joi12.default.object({
5129
5436
  site: import_joi12.default.string().hex().required(),
5130
- serviceType: import_joi12.default.string().valid(...Object.values(import_core13.AppServiceType)).required(),
5437
+ serviceType: import_joi12.default.string().valid(...Object.values(import_core14.AppServiceType)).required(),
5131
5438
  supply: import_joi12.default.string().hex().required(),
5132
5439
  in: import_joi12.default.number().min(0).optional(),
5133
5440
  out: import_joi12.default.number().min(0).optional(),
@@ -5349,7 +5656,7 @@ function useStockService() {
5349
5656
  // src/controllers/hygiene-stock.controller.ts
5350
5657
  var import_joi13 = __toESM(require("joi"));
5351
5658
  var import_node_server_utils27 = require("@7365admin1/node-server-utils");
5352
- var import_core14 = require("@7365admin1/core");
5659
+ var import_core15 = require("@7365admin1/core");
5353
5660
  function useStockController() {
5354
5661
  const { getStocksBySupplyId: _getStocksBySupplyId } = useStockRepository();
5355
5662
  const { createStock: _createStock } = useStockService();
@@ -5357,7 +5664,7 @@ function useStockController() {
5357
5664
  const payload = { ...req.body, ...req.params };
5358
5665
  const validation = import_joi13.default.object({
5359
5666
  site: import_joi13.default.string().hex().required(),
5360
- serviceType: import_joi13.default.string().valid(...Object.values(import_core14.AppServiceType)).required(),
5667
+ serviceType: import_joi13.default.string().valid(...Object.values(import_core15.AppServiceType)).required(),
5361
5668
  supply: import_joi13.default.string().hex().required(),
5362
5669
  qty: import_joi13.default.number().min(0).required(),
5363
5670
  remarks: import_joi13.default.string().optional().allow("", null)
@@ -5385,7 +5692,7 @@ function useStockController() {
5385
5692
  limit: import_joi13.default.number().min(1).optional().allow("", null),
5386
5693
  search: import_joi13.default.string().optional().allow("", null),
5387
5694
  site: import_joi13.default.string().hex().required(),
5388
- serviceType: import_joi13.default.string().valid(...Object.values(import_core14.AppServiceType)).required(),
5695
+ serviceType: import_joi13.default.string().valid(...Object.values(import_core15.AppServiceType)).required(),
5389
5696
  supply: import_joi13.default.string().hex().required()
5390
5697
  });
5391
5698
  const { error } = validation.validate(query);
@@ -5427,11 +5734,11 @@ function useStockController() {
5427
5734
  var import_joi14 = __toESM(require("joi"));
5428
5735
  var import_mongodb16 = require("mongodb");
5429
5736
  var import_node_server_utils28 = require("@7365admin1/node-server-utils");
5430
- var import_core15 = require("@7365admin1/core");
5737
+ var import_core16 = require("@7365admin1/core");
5431
5738
  var allowedCheckOutItemStatus = ["pending", "completed"];
5432
5739
  var checkOutItemSchema = import_joi14.default.object({
5433
5740
  site: import_joi14.default.string().hex().required(),
5434
- serviceType: import_joi14.default.string().valid(...Object.values(import_core15.AppServiceType)).required(),
5741
+ serviceType: import_joi14.default.string().valid(...Object.values(import_core16.AppServiceType)).required(),
5435
5742
  supply: import_joi14.default.string().hex().required(),
5436
5743
  supplyName: import_joi14.default.string().required(),
5437
5744
  qty: import_joi14.default.number().min(0).required(),
@@ -5492,6 +5799,7 @@ function useCheckOutItemRepository() {
5492
5799
  { key: { site: 1 } },
5493
5800
  { key: { serviceType: 1 } },
5494
5801
  { key: { supply: 1 } },
5802
+ { key: { createdBy: 1 } },
5495
5803
  { key: { status: 1 } }
5496
5804
  ]);
5497
5805
  } catch (error) {
@@ -5523,7 +5831,11 @@ function useCheckOutItemRepository() {
5523
5831
  });
5524
5832
  return res.insertedId;
5525
5833
  } catch (error) {
5526
- throw error;
5834
+ if (error.code === 11e3) {
5835
+ throw "The Item is already exisits";
5836
+ } else {
5837
+ throw error;
5838
+ }
5527
5839
  }
5528
5840
  }
5529
5841
  async function getCheckOutItems({
@@ -5589,11 +5901,35 @@ function useCheckOutItemRepository() {
5589
5901
  preserveNullAndEmptyArrays: true
5590
5902
  }
5591
5903
  },
5904
+ {
5905
+ $lookup: {
5906
+ from: "users",
5907
+ let: { createdByStr: "$createdBy" },
5908
+ pipeline: [
5909
+ {
5910
+ $match: {
5911
+ $expr: {
5912
+ $eq: [{ $toString: "$_id" }, "$$createdByStr"]
5913
+ }
5914
+ }
5915
+ },
5916
+ { $project: { profile: 1 } }
5917
+ ],
5918
+ as: "createdByDoc"
5919
+ }
5920
+ },
5921
+ {
5922
+ $unwind: {
5923
+ path: "$createdByDoc",
5924
+ preserveNullAndEmptyArrays: true
5925
+ }
5926
+ },
5592
5927
  {
5593
5928
  $project: {
5594
5929
  supplyName: 1,
5595
5930
  supplyQty: "$supplyDoc.qty",
5596
5931
  checkOutByName: "$createdByName",
5932
+ profile: "$createdByDoc.profile",
5597
5933
  checkOutQty: "$qty",
5598
5934
  createdAt: 1,
5599
5935
  status: 1
@@ -5723,7 +6059,7 @@ function useCheckOutItemRepository() {
5723
6059
  }
5724
6060
 
5725
6061
  // src/services/hygiene-checkout-item.service.ts
5726
- var import_core16 = require("@7365admin1/core");
6062
+ var import_core17 = require("@7365admin1/core");
5727
6063
  var import_node_server_utils30 = require("@7365admin1/node-server-utils");
5728
6064
  function useCheckOutItemService() {
5729
6065
  const {
@@ -5732,7 +6068,7 @@ function useCheckOutItemService() {
5732
6068
  completeCheckOutItem
5733
6069
  } = useCheckOutItemRepository();
5734
6070
  const { getSupplyById } = useSupplyRepository();
5735
- const { getUserById } = (0, import_core16.useUserRepo)();
6071
+ const { getUserById } = (0, import_core17.useUserRepo)();
5736
6072
  const { createStock } = useStockService();
5737
6073
  async function createCheckOutItem(value) {
5738
6074
  const session = import_node_server_utils30.useAtlas.getClient()?.startSession();
@@ -5827,7 +6163,7 @@ function useCheckOutItemService() {
5827
6163
  // src/controllers/hygiene-checkout-item.controller.ts
5828
6164
  var import_joi15 = __toESM(require("joi"));
5829
6165
  var import_node_server_utils31 = require("@7365admin1/node-server-utils");
5830
- var import_core17 = require("@7365admin1/core");
6166
+ var import_core18 = require("@7365admin1/core");
5831
6167
  function useCheckOutItemController() {
5832
6168
  const {
5833
6169
  getCheckOutItems: _getCheckOutItems,
@@ -5850,7 +6186,7 @@ function useCheckOutItemController() {
5850
6186
  };
5851
6187
  const validation = import_joi15.default.object({
5852
6188
  site: import_joi15.default.string().hex().required(),
5853
- serviceType: import_joi15.default.string().valid(...Object.values(import_core17.AppServiceType)).required(),
6189
+ serviceType: import_joi15.default.string().valid(...Object.values(import_core18.AppServiceType)).required(),
5854
6190
  supply: import_joi15.default.string().hex().required(),
5855
6191
  qty: import_joi15.default.number().min(0).required(),
5856
6192
  attachment: import_joi15.default.array().items(import_joi15.default.string()).optional().allow(null),
@@ -5885,7 +6221,7 @@ function useCheckOutItemController() {
5885
6221
  };
5886
6222
  const validation = import_joi15.default.object({
5887
6223
  site: import_joi15.default.string().hex().required(),
5888
- serviceType: import_joi15.default.string().valid(...Object.values(import_core17.AppServiceType)).required(),
6224
+ serviceType: import_joi15.default.string().valid(...Object.values(import_core18.AppServiceType)).required(),
5889
6225
  createdBy: import_joi15.default.string().hex().required(),
5890
6226
  items: import_joi15.default.array().items(
5891
6227
  import_joi15.default.object({
@@ -5918,7 +6254,7 @@ function useCheckOutItemController() {
5918
6254
  limit: import_joi15.default.number().min(1).optional().allow("", null),
5919
6255
  search: import_joi15.default.string().optional().allow("", null),
5920
6256
  site: import_joi15.default.string().hex().required(),
5921
- serviceType: import_joi15.default.string().valid(...Object.values(import_core17.AppServiceType)).required()
6257
+ serviceType: import_joi15.default.string().valid(...Object.values(import_core18.AppServiceType)).required()
5922
6258
  });
5923
6259
  const { error } = validation.validate(query);
5924
6260
  if (error) {
@@ -5976,12 +6312,12 @@ function useCheckOutItemController() {
5976
6312
 
5977
6313
  // src/models/hygiene-schedule-task.model.ts
5978
6314
  var import_node_server_utils32 = require("@7365admin1/node-server-utils");
5979
- var import_core18 = require("@7365admin1/core");
6315
+ var import_core19 = require("@7365admin1/core");
5980
6316
  var import_joi16 = __toESM(require("joi"));
5981
6317
  var import_mongodb18 = require("mongodb");
5982
6318
  var scheduleTaskSchema = import_joi16.default.object({
5983
6319
  site: import_joi16.default.string().hex().required(),
5984
- serviceType: import_joi16.default.string().valid(...Object.values(import_core18.AppServiceType)).required(),
6320
+ serviceType: import_joi16.default.string().valid(...Object.values(import_core19.AppServiceType)).required(),
5985
6321
  title: import_joi16.default.string().required(),
5986
6322
  time: import_joi16.default.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
5987
6323
  dates: import_joi16.default.array().min(1).items(
@@ -6587,7 +6923,7 @@ function useScheduleTaskService() {
6587
6923
  // src/controllers/hygiene-schedule-task.controller.ts
6588
6924
  var import_joi17 = __toESM(require("joi"));
6589
6925
  var import_node_server_utils35 = require("@7365admin1/node-server-utils");
6590
- var import_core19 = require("@7365admin1/core");
6926
+ var import_core20 = require("@7365admin1/core");
6591
6927
  function useScheduleTaskController() {
6592
6928
  const {
6593
6929
  createScheduleTask: _createScheduleTask,
@@ -6621,7 +6957,7 @@ function useScheduleTaskController() {
6621
6957
  limit: import_joi17.default.number().min(1).optional().allow("", null),
6622
6958
  search: import_joi17.default.string().optional().allow("", null),
6623
6959
  site: import_joi17.default.string().hex().required(),
6624
- serviceType: import_joi17.default.string().valid(...Object.values(import_core19.AppServiceType)).required()
6960
+ serviceType: import_joi17.default.string().valid(...Object.values(import_core20.AppServiceType)).required()
6625
6961
  });
6626
6962
  const { error } = validation.validate(query);
6627
6963
  if (error) {