@7365admin1/module-hygiene 4.21.0 → 4.23.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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +35 -11
- package/dist/index.js +713 -408
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +728 -422
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
BadRequestError,
|
|
18
18
|
useCache,
|
|
19
19
|
makeCacheKey,
|
|
20
|
+
paginate,
|
|
20
21
|
logger
|
|
21
22
|
} from "@7365admin1/node-server-utils";
|
|
22
23
|
function useHygieneDashboardRepository() {
|
|
@@ -25,72 +26,57 @@ function useHygieneDashboardRepository() {
|
|
|
25
26
|
throw new InternalServerError("Unable to connect to server.");
|
|
26
27
|
}
|
|
27
28
|
const dashboard_namespace_collection = "hygiene.dashboard";
|
|
28
|
-
const feedback_namespace_collection = "feedbacks";
|
|
29
29
|
const cleaning_schedule_namespace_collection = "site.cleaning.schedules";
|
|
30
30
|
const area_checklist_namespace_collection = "site.cleaning.schedule.areas";
|
|
31
|
-
const schedule_task_namespace_collection = "site.schedule-tasks";
|
|
32
31
|
const supply_namespace_collection = "site.supplies";
|
|
33
|
-
const
|
|
34
|
-
const feedbackCollection = db.collection(feedback_namespace_collection);
|
|
32
|
+
const work_order_namespace_collection = "work-orders2";
|
|
35
33
|
const areaChecklistCollection = db.collection(
|
|
36
34
|
area_checklist_namespace_collection
|
|
37
35
|
);
|
|
38
|
-
const scheduleTaskCollection = db.collection(
|
|
39
|
-
schedule_task_namespace_collection
|
|
40
|
-
);
|
|
41
36
|
const supplyCollection = db.collection(supply_namespace_collection);
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
);
|
|
37
|
+
const workOrderCollection = db.collection(work_order_namespace_collection);
|
|
38
|
+
const attendanceCollection = db.collection("site.attendances");
|
|
39
|
+
const feedbackCollection = db.collection("feedbacks2");
|
|
45
40
|
const { setCache, getCache } = useCache(dashboard_namespace_collection);
|
|
41
|
+
const workOrderServiceMap = {
|
|
42
|
+
security_agency: "Security",
|
|
43
|
+
cleaning_services: "Cleaning",
|
|
44
|
+
mechanical_electrical_services: "M&E",
|
|
45
|
+
landscaping_services: "Landscape",
|
|
46
|
+
pest_control_services: "Pest Control",
|
|
47
|
+
pool_maintenance_services: "Pool Maintenance"
|
|
48
|
+
};
|
|
49
|
+
function areaChecklistScheduleLookup(site, serviceType, createdAtRange) {
|
|
50
|
+
return [
|
|
51
|
+
{ $match: { serviceType } },
|
|
52
|
+
{
|
|
53
|
+
$lookup: {
|
|
54
|
+
from: cleaning_schedule_namespace_collection,
|
|
55
|
+
localField: "schedule",
|
|
56
|
+
foreignField: "_id",
|
|
57
|
+
as: "scheduleDoc",
|
|
58
|
+
pipeline: [
|
|
59
|
+
{
|
|
60
|
+
$match: {
|
|
61
|
+
site,
|
|
62
|
+
serviceType,
|
|
63
|
+
createdAt: createdAtRange
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{ $unwind: { path: "$scheduleDoc", preserveNullAndEmptyArrays: false } }
|
|
70
|
+
];
|
|
71
|
+
}
|
|
46
72
|
async function getHygieneDashboard({
|
|
47
73
|
site,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
toiletAreaPeriod = "today",
|
|
51
|
-
scheduleTaskPeriod = "today",
|
|
52
|
-
supplyPeriod = "today",
|
|
53
|
-
requestItemPeriod = "today"
|
|
74
|
+
serviceType,
|
|
75
|
+
period = "today"
|
|
54
76
|
}) {
|
|
55
|
-
const
|
|
56
|
-
feedback: {},
|
|
57
|
-
commonArea: {},
|
|
58
|
-
toiletArea: {},
|
|
59
|
-
scheduleTask: {},
|
|
60
|
-
supply: {},
|
|
61
|
-
requestItem: {}
|
|
62
|
-
};
|
|
63
|
-
const yesterDayQueries = {
|
|
64
|
-
feedback: {},
|
|
65
|
-
commonArea: {},
|
|
66
|
-
toiletArea: {},
|
|
67
|
-
scheduleTask: {},
|
|
68
|
-
supply: {},
|
|
69
|
-
requestItem: {}
|
|
70
|
-
};
|
|
71
|
-
const todayQueries = {
|
|
72
|
-
feedback: {},
|
|
73
|
-
commonArea: {},
|
|
74
|
-
toiletArea: {},
|
|
75
|
-
scheduleTask: {},
|
|
76
|
-
supply: {},
|
|
77
|
-
requestItem: {}
|
|
78
|
-
};
|
|
79
|
-
const cacheOptions = {
|
|
80
|
-
feedbackPeriod,
|
|
81
|
-
commonAreaPeriod,
|
|
82
|
-
toiletAreaPeriod,
|
|
83
|
-
scheduleTaskPeriod,
|
|
84
|
-
supplyPeriod,
|
|
85
|
-
requestItemPeriod
|
|
86
|
-
};
|
|
77
|
+
const cacheOptions = { serviceType, period };
|
|
87
78
|
try {
|
|
88
79
|
site = new ObjectId(site);
|
|
89
|
-
Object.keys(countQueries).forEach((key) => {
|
|
90
|
-
countQueries[key].site = site;
|
|
91
|
-
yesterDayQueries[key].site = site;
|
|
92
|
-
todayQueries[key].site = site;
|
|
93
|
-
});
|
|
94
80
|
cacheOptions.site = site.toString();
|
|
95
81
|
} catch (error) {
|
|
96
82
|
throw new BadRequestError("Invalid site ID format.");
|
|
@@ -118,32 +104,28 @@ function useHygieneDashboardRepository() {
|
|
|
118
104
|
});
|
|
119
105
|
return singaporeMoment.toDate();
|
|
120
106
|
}
|
|
121
|
-
function
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
$gte: today,
|
|
127
|
-
$lte: formatEndDate(today)
|
|
128
|
-
};
|
|
129
|
-
} else if (period === "thisWeek" || period === "thisMonth") {
|
|
130
|
-
const filterDate = period === "thisWeek" ? 7 : 30;
|
|
131
|
-
const filterStartDate = /* @__PURE__ */ new Date();
|
|
132
|
-
filterStartDate.setDate(today.getDate() - filterDate);
|
|
133
|
-
filterStartDate.setHours(0, 0, 0, 0);
|
|
134
|
-
query.createdAt = {
|
|
135
|
-
$gte: filterStartDate,
|
|
136
|
-
$lte: formatEndDate(/* @__PURE__ */ new Date())
|
|
137
|
-
};
|
|
107
|
+
function getDateRange(p) {
|
|
108
|
+
const start = /* @__PURE__ */ new Date();
|
|
109
|
+
start.setHours(0, 0, 0, 0);
|
|
110
|
+
if (p === "today") {
|
|
111
|
+
return { $gte: start, $lte: formatEndDate(start) };
|
|
138
112
|
}
|
|
139
|
-
|
|
113
|
+
const days = p === "thisWeek" ? 7 : 30;
|
|
114
|
+
const rangeStart = /* @__PURE__ */ new Date();
|
|
115
|
+
rangeStart.setDate(start.getDate() - days);
|
|
116
|
+
rangeStart.setHours(0, 0, 0, 0);
|
|
117
|
+
return { $gte: rangeStart, $lte: formatEndDate(/* @__PURE__ */ new Date()) };
|
|
118
|
+
}
|
|
119
|
+
const calculatePercentageChange = (currentCount, previousCount) => {
|
|
120
|
+
if (previousCount === 0) {
|
|
121
|
+
return currentCount > 0 ? 100 : 0;
|
|
122
|
+
}
|
|
123
|
+
return Math.round(
|
|
124
|
+
(currentCount - previousCount) / previousCount * 100 * 100
|
|
125
|
+
) / 100;
|
|
126
|
+
};
|
|
127
|
+
const workOrderService = workOrderServiceMap[serviceType] ?? serviceType;
|
|
140
128
|
try {
|
|
141
|
-
applyPeriodToQuery(countQueries.feedback, feedbackPeriod);
|
|
142
|
-
applyPeriodToQuery(countQueries.commonArea, commonAreaPeriod);
|
|
143
|
-
applyPeriodToQuery(countQueries.toiletArea, toiletAreaPeriod);
|
|
144
|
-
applyPeriodToQuery(countQueries.scheduleTask, scheduleTaskPeriod);
|
|
145
|
-
applyPeriodToQuery(countQueries.supply, supplyPeriod);
|
|
146
|
-
applyPeriodToQuery(countQueries.requestItem, requestItemPeriod);
|
|
147
129
|
const today = /* @__PURE__ */ new Date();
|
|
148
130
|
today.setHours(0, 0, 0, 0);
|
|
149
131
|
const yesterday = /* @__PURE__ */ new Date();
|
|
@@ -151,359 +133,566 @@ function useHygieneDashboardRepository() {
|
|
|
151
133
|
yesterday.setHours(0, 0, 0, 0);
|
|
152
134
|
const yesterdayEnd = new Date(yesterday);
|
|
153
135
|
yesterdayEnd.setHours(23, 59, 59, 999);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
$gte: yesterday,
|
|
157
|
-
$lte: yesterdayEnd
|
|
158
|
-
};
|
|
159
|
-
});
|
|
160
|
-
Object.keys(todayQueries).forEach((key) => {
|
|
161
|
-
todayQueries[key].createdAt = {
|
|
162
|
-
$gte: today,
|
|
163
|
-
$lte: formatEndDate(/* @__PURE__ */ new Date())
|
|
164
|
-
};
|
|
165
|
-
});
|
|
166
|
-
const calculatePercentageChange = (currentCount, previousCount) => {
|
|
167
|
-
if (previousCount === 0) {
|
|
168
|
-
return currentCount > 0 ? 100 : 0;
|
|
169
|
-
}
|
|
170
|
-
return Math.round(
|
|
171
|
-
(currentCount - previousCount) / previousCount * 100 * 100
|
|
172
|
-
) / 100;
|
|
173
|
-
};
|
|
136
|
+
const todayEnd = formatEndDate(/* @__PURE__ */ new Date());
|
|
137
|
+
const periodRange = getDateRange(period);
|
|
174
138
|
const [
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
139
|
+
workOrderReport,
|
|
140
|
+
supplyAlertReport,
|
|
141
|
+
taskCompletedReport,
|
|
142
|
+
yesterdayWorkOrderReport,
|
|
143
|
+
todayWorkOrderReport,
|
|
144
|
+
yesterdayTaskCompletedReport,
|
|
145
|
+
todayTaskCompletedReport
|
|
181
146
|
] = await Promise.all([
|
|
182
|
-
|
|
147
|
+
// 1. Open work orders (period-filtered) with inProgress breakdown
|
|
148
|
+
workOrderCollection.aggregate([
|
|
183
149
|
{
|
|
184
150
|
$match: {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
151
|
+
site,
|
|
152
|
+
service: workOrderService,
|
|
153
|
+
createdAt: periodRange,
|
|
154
|
+
status: { $nin: ["completed"] }
|
|
188
155
|
}
|
|
189
156
|
},
|
|
157
|
+
{
|
|
158
|
+
$facet: {
|
|
159
|
+
total: [{ $count: "count" }],
|
|
160
|
+
inProgress: [
|
|
161
|
+
{ $match: { status: "in-progress" } },
|
|
162
|
+
{ $count: "count" }
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
]).toArray(),
|
|
167
|
+
// 2. Supply alert — out-of-stock active supplies for site+serviceType
|
|
168
|
+
supplyCollection.aggregate([
|
|
169
|
+
{ $match: { site, serviceType, qty: 0, status: "active" } },
|
|
190
170
|
{ $count: "count" }
|
|
191
171
|
]).toArray(),
|
|
172
|
+
// 3. Task completed — area checklists (period-filtered via schedule join)
|
|
192
173
|
areaChecklistCollection.aggregate([
|
|
193
|
-
|
|
174
|
+
...areaChecklistScheduleLookup(site, serviceType, periodRange),
|
|
194
175
|
{
|
|
195
|
-
$
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
] : [{ $match: { site } }]
|
|
176
|
+
$facet: {
|
|
177
|
+
total: [{ $count: "count" }],
|
|
178
|
+
completed: [
|
|
179
|
+
{ $match: { status: "completed" } },
|
|
180
|
+
{ $count: "count" }
|
|
181
|
+
],
|
|
182
|
+
inProgress: [
|
|
183
|
+
{ $match: { status: { $in: ["open", "ongoing"] } } },
|
|
184
|
+
{ $count: "count" }
|
|
185
|
+
]
|
|
208
186
|
}
|
|
209
|
-
}
|
|
187
|
+
}
|
|
188
|
+
]).toArray(),
|
|
189
|
+
// 4. Yesterday open work orders (for % change)
|
|
190
|
+
workOrderCollection.aggregate([
|
|
210
191
|
{
|
|
211
|
-
$
|
|
212
|
-
|
|
213
|
-
|
|
192
|
+
$match: {
|
|
193
|
+
site,
|
|
194
|
+
service: workOrderService,
|
|
195
|
+
createdAt: { $gte: yesterday, $lte: yesterdayEnd },
|
|
196
|
+
status: { $nin: ["completed"] }
|
|
214
197
|
}
|
|
215
198
|
},
|
|
216
199
|
{ $count: "count" }
|
|
217
200
|
]).toArray(),
|
|
218
|
-
|
|
219
|
-
|
|
201
|
+
// 5. Today open work orders (for % change)
|
|
202
|
+
workOrderCollection.aggregate([
|
|
220
203
|
{
|
|
221
|
-
$
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
pipeline: countQueries.toiletArea.createdAt ? [
|
|
227
|
-
{
|
|
228
|
-
$match: {
|
|
229
|
-
site,
|
|
230
|
-
createdAt: countQueries.toiletArea.createdAt
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
] : [{ $match: { site } }]
|
|
234
|
-
}
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
$unwind: {
|
|
238
|
-
path: "$scheduleDoc",
|
|
239
|
-
preserveNullAndEmptyArrays: false
|
|
204
|
+
$match: {
|
|
205
|
+
site,
|
|
206
|
+
service: workOrderService,
|
|
207
|
+
createdAt: { $gte: today, $lte: todayEnd },
|
|
208
|
+
status: { $nin: ["completed"] }
|
|
240
209
|
}
|
|
241
210
|
},
|
|
242
211
|
{ $count: "count" }
|
|
243
212
|
]).toArray(),
|
|
244
|
-
|
|
245
|
-
|
|
213
|
+
// 6. Yesterday task completed — area checklists (for % change)
|
|
214
|
+
areaChecklistCollection.aggregate([
|
|
215
|
+
...areaChecklistScheduleLookup(site, serviceType, {
|
|
216
|
+
$gte: yesterday,
|
|
217
|
+
$lte: yesterdayEnd
|
|
218
|
+
}),
|
|
219
|
+
{ $match: { status: "completed" } },
|
|
246
220
|
{ $count: "count" }
|
|
247
221
|
]).toArray(),
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
222
|
+
// 7. Today task completed — area checklists (for % change)
|
|
223
|
+
areaChecklistCollection.aggregate([
|
|
224
|
+
...areaChecklistScheduleLookup(site, serviceType, {
|
|
225
|
+
$gte: today,
|
|
226
|
+
$lte: todayEnd
|
|
227
|
+
}),
|
|
228
|
+
{ $match: { status: "completed" } },
|
|
251
229
|
{ $count: "count" }
|
|
252
230
|
]).toArray()
|
|
253
231
|
]);
|
|
254
|
-
const [
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
232
|
+
const wFacet = workOrderReport[0] ?? { total: [], inProgress: [] };
|
|
233
|
+
const tFacet = taskCompletedReport[0] ?? {
|
|
234
|
+
total: [],
|
|
235
|
+
completed: [],
|
|
236
|
+
inProgress: []
|
|
237
|
+
};
|
|
238
|
+
const result = {
|
|
239
|
+
openWorkOrder: {
|
|
240
|
+
count: wFacet.total[0]?.count ?? 0,
|
|
241
|
+
inProgress: wFacet.inProgress[0]?.count ?? 0,
|
|
242
|
+
percentage: calculatePercentageChange(
|
|
243
|
+
todayWorkOrderReport[0]?.count ?? 0,
|
|
244
|
+
yesterdayWorkOrderReport[0]?.count ?? 0
|
|
245
|
+
)
|
|
246
|
+
},
|
|
247
|
+
supplyAlert: {
|
|
248
|
+
count: supplyAlertReport[0]?.count ?? 0,
|
|
249
|
+
percentage: 0
|
|
250
|
+
},
|
|
251
|
+
taskCompleted: {
|
|
252
|
+
completed: tFacet.completed[0]?.count ?? 0,
|
|
253
|
+
total: tFacet.total[0]?.count ?? 0,
|
|
254
|
+
inProgress: tFacet.inProgress[0]?.count ?? 0,
|
|
255
|
+
percentage: calculatePercentageChange(
|
|
256
|
+
todayTaskCompletedReport[0]?.count ?? 0,
|
|
257
|
+
yesterdayTaskCompletedReport[0]?.count ?? 0
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
setCache(dashboardCacheKey, result, 15 * 60).then(() => {
|
|
262
|
+
logger.info(`Cache set for dashboard: ${dashboardCacheKey}`);
|
|
263
|
+
}).catch((err) => {
|
|
264
|
+
logger.error(
|
|
265
|
+
`Failed to set cache for dashboard: ${dashboardCacheKey}`,
|
|
266
|
+
err
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
return result;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async function getWeeklyActivity({
|
|
275
|
+
site,
|
|
276
|
+
serviceType
|
|
277
|
+
}) {
|
|
278
|
+
const cacheOptions = { serviceType };
|
|
279
|
+
try {
|
|
280
|
+
site = new ObjectId(site);
|
|
281
|
+
cacheOptions.site = site.toString();
|
|
282
|
+
} catch {
|
|
283
|
+
throw new BadRequestError("Invalid site ID format.");
|
|
284
|
+
}
|
|
285
|
+
const cacheKey = makeCacheKey(
|
|
286
|
+
`${dashboard_namespace_collection}:weeklyActivity`,
|
|
287
|
+
cacheOptions
|
|
288
|
+
);
|
|
289
|
+
const cachedData = await getCache(cacheKey);
|
|
290
|
+
if (cachedData) {
|
|
291
|
+
logger.info(`Cache hit for weeklyActivity: ${cacheKey}`);
|
|
292
|
+
return cachedData;
|
|
293
|
+
}
|
|
294
|
+
const workOrderService = workOrderServiceMap[serviceType] ?? serviceType;
|
|
295
|
+
const weekStart = moment.tz("Asia/Singapore").startOf("isoWeek").toDate();
|
|
296
|
+
const weekEnd = moment.tz("Asia/Singapore").endOf("isoWeek").toDate();
|
|
297
|
+
const weekRange = { $gte: weekStart, $lte: weekEnd };
|
|
298
|
+
const dayLabels = {
|
|
299
|
+
1: "Sun",
|
|
300
|
+
2: "Mon",
|
|
301
|
+
3: "Tue",
|
|
302
|
+
4: "Wed",
|
|
303
|
+
5: "Thu",
|
|
304
|
+
6: "Fri",
|
|
305
|
+
7: "Sat"
|
|
306
|
+
};
|
|
307
|
+
const dayOrder = [2, 3, 4, 5, 6, 7, 1];
|
|
308
|
+
try {
|
|
309
|
+
const [workOrderByDay, taskCompletedByDay] = await Promise.all([
|
|
310
|
+
workOrderCollection.aggregate([
|
|
263
311
|
{
|
|
264
|
-
$match: {
|
|
265
|
-
...yesterDayQueries.feedback,
|
|
266
|
-
category: "cleaning_services",
|
|
267
|
-
status: "to-do"
|
|
268
|
-
}
|
|
312
|
+
$match: { site, service: workOrderService, createdAt: weekRange }
|
|
269
313
|
},
|
|
270
|
-
{
|
|
314
|
+
{
|
|
315
|
+
$group: { _id: { $dayOfWeek: "$createdAt" }, count: { $sum: 1 } }
|
|
316
|
+
}
|
|
271
317
|
]).toArray(),
|
|
272
318
|
areaChecklistCollection.aggregate([
|
|
273
|
-
|
|
319
|
+
...areaChecklistScheduleLookup(site, serviceType, weekRange),
|
|
320
|
+
{ $match: { status: "completed" } },
|
|
274
321
|
{
|
|
275
|
-
$
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
foreignField: "_id",
|
|
279
|
-
as: "scheduleDoc",
|
|
280
|
-
pipeline: [
|
|
281
|
-
{
|
|
282
|
-
$match: {
|
|
283
|
-
site,
|
|
284
|
-
createdAt: yesterDayQueries.commonArea.createdAt
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
]
|
|
322
|
+
$group: {
|
|
323
|
+
_id: { $dayOfWeek: "$completedAt" },
|
|
324
|
+
count: { $sum: 1 }
|
|
288
325
|
}
|
|
289
|
-
}
|
|
326
|
+
}
|
|
327
|
+
]).toArray()
|
|
328
|
+
]);
|
|
329
|
+
const workOrderMap = new Map(
|
|
330
|
+
workOrderByDay.map((d) => [d._id, d.count])
|
|
331
|
+
);
|
|
332
|
+
const taskMap = new Map(
|
|
333
|
+
taskCompletedByDay.map((d) => [d._id, d.count])
|
|
334
|
+
);
|
|
335
|
+
const result = dayOrder.map((dow) => ({
|
|
336
|
+
day: dayLabels[dow],
|
|
337
|
+
workOrder: workOrderMap.get(dow) ?? 0,
|
|
338
|
+
taskCompleted: taskMap.get(dow) ?? 0
|
|
339
|
+
}));
|
|
340
|
+
setCache(cacheKey, result, 15 * 60).then(() => {
|
|
341
|
+
logger.info(`Cache set for weeklyActivity: ${cacheKey}`);
|
|
342
|
+
}).catch((err) => {
|
|
343
|
+
logger.error(
|
|
344
|
+
`Failed to set cache for weeklyActivity: ${cacheKey}`,
|
|
345
|
+
err
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
return result;
|
|
349
|
+
} catch (error) {
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
async function getTodayTaskSchedule({
|
|
354
|
+
site,
|
|
355
|
+
serviceType,
|
|
356
|
+
page = 1,
|
|
357
|
+
limit = 10
|
|
358
|
+
}) {
|
|
359
|
+
page = page > 0 ? page - 1 : 0;
|
|
360
|
+
const cacheOptions = { serviceType, page, limit };
|
|
361
|
+
try {
|
|
362
|
+
site = new ObjectId(site);
|
|
363
|
+
cacheOptions.site = site.toString();
|
|
364
|
+
} catch {
|
|
365
|
+
throw new BadRequestError("Invalid site ID format.");
|
|
366
|
+
}
|
|
367
|
+
const cacheKey = makeCacheKey(
|
|
368
|
+
`${dashboard_namespace_collection}:todayTaskSchedule`,
|
|
369
|
+
cacheOptions
|
|
370
|
+
);
|
|
371
|
+
const cachedData = await getCache(cacheKey);
|
|
372
|
+
if (cachedData) {
|
|
373
|
+
logger.info(`Cache hit for todayTaskSchedule: ${cacheKey}`);
|
|
374
|
+
return cachedData;
|
|
375
|
+
}
|
|
376
|
+
const todayStart = moment.tz("Asia/Singapore").startOf("day").toDate();
|
|
377
|
+
const todayEnd = moment.tz("Asia/Singapore").endOf("day").toDate();
|
|
378
|
+
const todayRange = { $gte: todayStart, $lte: todayEnd };
|
|
379
|
+
try {
|
|
380
|
+
const [items, countResult] = await Promise.all([
|
|
381
|
+
areaChecklistCollection.aggregate([
|
|
382
|
+
...areaChecklistScheduleLookup(site, serviceType, todayRange),
|
|
290
383
|
{
|
|
291
|
-
$
|
|
292
|
-
|
|
293
|
-
|
|
384
|
+
$addFields: {
|
|
385
|
+
_assigneeId: {
|
|
386
|
+
$reduce: {
|
|
387
|
+
input: { $ifNull: ["$checklist", []] },
|
|
388
|
+
initialValue: null,
|
|
389
|
+
in: {
|
|
390
|
+
$cond: {
|
|
391
|
+
if: { $ne: ["$$value", null] },
|
|
392
|
+
then: "$$value",
|
|
393
|
+
else: {
|
|
394
|
+
$reduce: {
|
|
395
|
+
input: { $ifNull: ["$$this.units", []] },
|
|
396
|
+
initialValue: null,
|
|
397
|
+
in: {
|
|
398
|
+
$cond: {
|
|
399
|
+
if: { $ne: ["$$value", null] },
|
|
400
|
+
then: "$$value",
|
|
401
|
+
else: {
|
|
402
|
+
$cond: {
|
|
403
|
+
if: { $gt: ["$$this.completedBy", null] },
|
|
404
|
+
then: "$$this.completedBy",
|
|
405
|
+
else: null
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
294
416
|
}
|
|
295
417
|
},
|
|
296
|
-
{ $count: "count" }
|
|
297
|
-
]).toArray(),
|
|
298
|
-
areaChecklistCollection.aggregate([
|
|
299
|
-
{ $match: { type: "toilet" } },
|
|
300
418
|
{
|
|
301
419
|
$lookup: {
|
|
302
|
-
from:
|
|
303
|
-
localField: "
|
|
420
|
+
from: "users",
|
|
421
|
+
localField: "_assigneeId",
|
|
304
422
|
foreignField: "_id",
|
|
305
|
-
as: "
|
|
306
|
-
pipeline: [
|
|
307
|
-
{
|
|
308
|
-
$match: {
|
|
309
|
-
site,
|
|
310
|
-
createdAt: yesterDayQueries.toiletArea.createdAt
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
]
|
|
423
|
+
as: "_assigneeDoc"
|
|
314
424
|
}
|
|
315
425
|
},
|
|
426
|
+
{ $sort: { createdAt: -1, _id: -1 } },
|
|
316
427
|
{
|
|
317
|
-
$
|
|
318
|
-
|
|
319
|
-
|
|
428
|
+
$project: {
|
|
429
|
+
name: 1,
|
|
430
|
+
type: 1,
|
|
431
|
+
status: 1,
|
|
432
|
+
assigneeName: { $arrayElemAt: ["$_assigneeDoc.name", 0] }
|
|
320
433
|
}
|
|
321
434
|
},
|
|
322
|
-
{ $
|
|
435
|
+
{ $skip: page * limit },
|
|
436
|
+
{ $limit: limit }
|
|
323
437
|
]).toArray(),
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
{ $count: "count" }
|
|
327
|
-
]).toArray(),
|
|
328
|
-
supplyCollection.aggregate([{ $match: yesterDayQueries.supply }, { $count: "count" }]).toArray(),
|
|
329
|
-
requestItemCollection.aggregate([
|
|
330
|
-
{ $match: yesterDayQueries.requestItem },
|
|
438
|
+
areaChecklistCollection.aggregate([
|
|
439
|
+
...areaChecklistScheduleLookup(site, serviceType, todayRange),
|
|
331
440
|
{ $count: "count" }
|
|
332
441
|
]).toArray()
|
|
333
442
|
]);
|
|
334
|
-
const [
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
443
|
+
const length = countResult[0]?.count ?? 0;
|
|
444
|
+
const data = paginate(items, page, limit, length);
|
|
445
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
446
|
+
logger.info(`Cache set for todayTaskSchedule: ${cacheKey}`);
|
|
447
|
+
}).catch((err) => {
|
|
448
|
+
logger.error(
|
|
449
|
+
`Failed to set cache for todayTaskSchedule: ${cacheKey}`,
|
|
450
|
+
err
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
return data;
|
|
454
|
+
} catch (error) {
|
|
455
|
+
throw error;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
async function getStaffAttendance({
|
|
459
|
+
site,
|
|
460
|
+
serviceType,
|
|
461
|
+
page = 1,
|
|
462
|
+
limit = 10
|
|
463
|
+
}) {
|
|
464
|
+
page = page > 0 ? page - 1 : 0;
|
|
465
|
+
const cacheOptions = { serviceType, page, limit };
|
|
466
|
+
try {
|
|
467
|
+
site = new ObjectId(site);
|
|
468
|
+
cacheOptions.site = site.toString();
|
|
469
|
+
} catch {
|
|
470
|
+
throw new BadRequestError("Invalid site ID format.");
|
|
471
|
+
}
|
|
472
|
+
const cacheKey = makeCacheKey(
|
|
473
|
+
`${dashboard_namespace_collection}:staffAttendance`,
|
|
474
|
+
cacheOptions
|
|
475
|
+
);
|
|
476
|
+
const cachedData = await getCache(cacheKey);
|
|
477
|
+
if (cachedData) {
|
|
478
|
+
logger.info(`Cache hit for staffAttendance: ${cacheKey}`);
|
|
479
|
+
return cachedData;
|
|
480
|
+
}
|
|
481
|
+
const todayStart = moment.tz("Asia/Singapore").startOf("day").toDate();
|
|
482
|
+
const todayEnd = moment.tz("Asia/Singapore").endOf("day").toDate();
|
|
483
|
+
const todayStartStr = todayStart.toISOString();
|
|
484
|
+
const todayEndStr = todayEnd.toISOString();
|
|
485
|
+
try {
|
|
486
|
+
const [items, length] = await Promise.all([
|
|
487
|
+
attendanceCollection.aggregate([
|
|
343
488
|
{
|
|
344
489
|
$match: {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
490
|
+
site,
|
|
491
|
+
serviceType,
|
|
492
|
+
$or: [
|
|
493
|
+
{ createdAt: { $gte: todayStart, $lte: todayEnd } },
|
|
494
|
+
{ createdAt: { $gte: todayStartStr, $lte: todayEndStr } }
|
|
495
|
+
]
|
|
348
496
|
}
|
|
349
497
|
},
|
|
350
|
-
{ $count: "count" }
|
|
351
|
-
]).toArray(),
|
|
352
|
-
areaChecklistCollection.aggregate([
|
|
353
|
-
{ $match: { type: "common" } },
|
|
354
498
|
{
|
|
355
499
|
$lookup: {
|
|
356
|
-
from:
|
|
357
|
-
localField: "
|
|
500
|
+
from: "users",
|
|
501
|
+
localField: "user",
|
|
358
502
|
foreignField: "_id",
|
|
359
|
-
as: "
|
|
503
|
+
as: "_userDoc"
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
$lookup: {
|
|
508
|
+
from: "members",
|
|
509
|
+
let: { userId: "$user", siteId: "$site", type: "$serviceType" },
|
|
360
510
|
pipeline: [
|
|
361
511
|
{
|
|
362
512
|
$match: {
|
|
363
|
-
|
|
364
|
-
|
|
513
|
+
$expr: {
|
|
514
|
+
$and: [
|
|
515
|
+
{ $eq: ["$user", "$$userId"] },
|
|
516
|
+
{ $eq: ["$siteId", "$$siteId"] },
|
|
517
|
+
{ $eq: ["$type", "$$type"] },
|
|
518
|
+
{ $ne: ["$status", "deleted"] }
|
|
519
|
+
]
|
|
520
|
+
}
|
|
365
521
|
}
|
|
366
522
|
}
|
|
367
|
-
]
|
|
523
|
+
],
|
|
524
|
+
as: "_memberDoc"
|
|
368
525
|
}
|
|
369
526
|
},
|
|
370
527
|
{
|
|
371
528
|
$unwind: {
|
|
372
|
-
path: "$
|
|
373
|
-
preserveNullAndEmptyArrays:
|
|
529
|
+
path: "$_memberDoc",
|
|
530
|
+
preserveNullAndEmptyArrays: true
|
|
374
531
|
}
|
|
375
532
|
},
|
|
376
|
-
{ $count: "count" }
|
|
377
|
-
]).toArray(),
|
|
378
|
-
areaChecklistCollection.aggregate([
|
|
379
|
-
{ $match: { type: "toilet" } },
|
|
380
533
|
{
|
|
381
534
|
$lookup: {
|
|
382
|
-
from:
|
|
383
|
-
localField: "
|
|
535
|
+
from: "roles",
|
|
536
|
+
localField: "_memberDoc.role",
|
|
384
537
|
foreignField: "_id",
|
|
385
|
-
as: "
|
|
386
|
-
pipeline: [
|
|
387
|
-
{
|
|
388
|
-
$match: {
|
|
389
|
-
site,
|
|
390
|
-
createdAt: todayQueries.toiletArea.createdAt
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
]
|
|
538
|
+
as: "_roleDoc"
|
|
394
539
|
}
|
|
395
540
|
},
|
|
396
541
|
{
|
|
397
542
|
$unwind: {
|
|
398
|
-
path: "$
|
|
399
|
-
preserveNullAndEmptyArrays:
|
|
543
|
+
path: "$_roleDoc",
|
|
544
|
+
preserveNullAndEmptyArrays: true
|
|
400
545
|
}
|
|
401
546
|
},
|
|
402
|
-
{ $
|
|
547
|
+
{ $sort: { createdAt: -1, _id: -1 } },
|
|
548
|
+
{
|
|
549
|
+
$project: {
|
|
550
|
+
checkIn: 1,
|
|
551
|
+
checkOut: 1,
|
|
552
|
+
createdAt: 1,
|
|
553
|
+
userName: { $arrayElemAt: ["$_userDoc.name", 0] },
|
|
554
|
+
userRole: { $ifNull: ["$_roleDoc.name", ""] },
|
|
555
|
+
totalHours: {
|
|
556
|
+
$round: [
|
|
557
|
+
{
|
|
558
|
+
$divide: [
|
|
559
|
+
{
|
|
560
|
+
$subtract: [
|
|
561
|
+
{
|
|
562
|
+
$toDate: {
|
|
563
|
+
$ifNull: ["$checkOut.timestamp", "$$NOW"]
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
{ $toDate: "$checkIn.timestamp" }
|
|
567
|
+
]
|
|
568
|
+
},
|
|
569
|
+
36e5
|
|
570
|
+
]
|
|
571
|
+
},
|
|
572
|
+
2
|
|
573
|
+
]
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
{ $skip: page * limit },
|
|
578
|
+
{ $limit: limit }
|
|
403
579
|
]).toArray(),
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
580
|
+
attendanceCollection.countDocuments({
|
|
581
|
+
site,
|
|
582
|
+
serviceType,
|
|
583
|
+
$or: [
|
|
584
|
+
{ createdAt: { $gte: todayStart, $lte: todayEnd } },
|
|
585
|
+
{ createdAt: { $gte: todayStartStr, $lte: todayEndStr } }
|
|
586
|
+
]
|
|
587
|
+
})
|
|
588
|
+
]);
|
|
589
|
+
const data = paginate(items, page, limit, length);
|
|
590
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
591
|
+
logger.info(`Cache set for staffAttendance: ${cacheKey}`);
|
|
592
|
+
}).catch((err) => {
|
|
593
|
+
logger.error(
|
|
594
|
+
`Failed to set cache for staffAttendance: ${cacheKey}`,
|
|
595
|
+
err
|
|
596
|
+
);
|
|
597
|
+
});
|
|
598
|
+
return data;
|
|
599
|
+
} catch (error) {
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
async function getRecentFeedbacks({
|
|
604
|
+
site,
|
|
605
|
+
serviceType,
|
|
606
|
+
page = 1,
|
|
607
|
+
limit = 10
|
|
608
|
+
}) {
|
|
609
|
+
page = page > 0 ? page - 1 : 0;
|
|
610
|
+
const cacheOptions = { serviceType, page, limit };
|
|
611
|
+
try {
|
|
612
|
+
site = new ObjectId(site);
|
|
613
|
+
cacheOptions.site = site.toString();
|
|
614
|
+
} catch {
|
|
615
|
+
throw new BadRequestError("Invalid site ID format.");
|
|
616
|
+
}
|
|
617
|
+
const cacheKey = makeCacheKey(
|
|
618
|
+
`${dashboard_namespace_collection}:recentFeedbacks`,
|
|
619
|
+
cacheOptions
|
|
620
|
+
);
|
|
621
|
+
const cachedData = await getCache(cacheKey);
|
|
622
|
+
if (cachedData) {
|
|
623
|
+
logger.info(`Cache hit for recentFeedbacks: ${cacheKey}`);
|
|
624
|
+
return cachedData;
|
|
625
|
+
}
|
|
626
|
+
const workOrderService = workOrderServiceMap[serviceType] ?? serviceType;
|
|
627
|
+
try {
|
|
628
|
+
const [items, length] = await Promise.all([
|
|
629
|
+
feedbackCollection.aggregate([
|
|
630
|
+
{ $match: { site, service: workOrderService } },
|
|
631
|
+
{ $sort: { createdAt: -1, _id: -1 } },
|
|
632
|
+
{
|
|
633
|
+
$lookup: {
|
|
634
|
+
from: "users",
|
|
635
|
+
localField: "createdBy",
|
|
636
|
+
foreignField: "_id",
|
|
637
|
+
as: "_createdByDoc"
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
$project: {
|
|
642
|
+
description: 1,
|
|
643
|
+
subject: 1,
|
|
644
|
+
app: 1,
|
|
645
|
+
status: 1,
|
|
646
|
+
createdAt: 1,
|
|
647
|
+
createdByName: { $arrayElemAt: ["$_createdByDoc.name", 0] }
|
|
648
|
+
}
|
|
649
|
+
},
|
|
650
|
+
{ $skip: page * limit },
|
|
651
|
+
{ $limit: limit }
|
|
407
652
|
]).toArray(),
|
|
408
|
-
|
|
409
|
-
requestItemCollection.aggregate([
|
|
410
|
-
{ $match: todayQueries.requestItem },
|
|
411
|
-
{ $count: "count" }
|
|
412
|
-
]).toArray()
|
|
653
|
+
feedbackCollection.countDocuments({ site, service: workOrderService })
|
|
413
654
|
]);
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
toiletAreaChecklistCount: yesterdayToiletAreaChecklistCount[0]?.count || 0,
|
|
418
|
-
scheduleTaskCount: yesterdayScheduledTaskCount[0]?.count || 0,
|
|
419
|
-
supplyCount: yesterdaySupplyCount[0]?.count || 0,
|
|
420
|
-
requestItemCount: yesterdayRequestItemCount[0]?.count || 0
|
|
421
|
-
};
|
|
422
|
-
const resultToday = {
|
|
423
|
-
feedbackCount: todayFeedbackCount[0]?.count || 0,
|
|
424
|
-
commonAreaChecklistCount: todayCommonAreaChecklistCount[0]?.count || 0,
|
|
425
|
-
toiletAreaChecklistCount: todayToiletAreaChecklistCount[0]?.count || 0,
|
|
426
|
-
scheduleTaskCount: todayScheduledTaskCount[0]?.count || 0,
|
|
427
|
-
supplyCount: todaySupplyCount[0]?.count || 0,
|
|
428
|
-
requestItemCount: todayRequestItemCount[0]?.count || 0
|
|
429
|
-
};
|
|
430
|
-
const result = {
|
|
431
|
-
feedback: {
|
|
432
|
-
count: feedbackReport[0]?.count || 0,
|
|
433
|
-
percentage: calculatePercentageChange(
|
|
434
|
-
resultToday.feedbackCount,
|
|
435
|
-
resultYesterday.feedbackCount
|
|
436
|
-
)
|
|
437
|
-
},
|
|
438
|
-
commonAreaChecklist: {
|
|
439
|
-
count: commonAreaChecklistReport[0]?.count || 0,
|
|
440
|
-
percentage: calculatePercentageChange(
|
|
441
|
-
resultToday.commonAreaChecklistCount,
|
|
442
|
-
resultYesterday.commonAreaChecklistCount
|
|
443
|
-
)
|
|
444
|
-
},
|
|
445
|
-
toiletAreaChecklist: {
|
|
446
|
-
count: toiletAreaChecklistReport[0]?.count || 0,
|
|
447
|
-
percentage: calculatePercentageChange(
|
|
448
|
-
resultToday.toiletAreaChecklistCount,
|
|
449
|
-
resultYesterday.toiletAreaChecklistCount
|
|
450
|
-
)
|
|
451
|
-
},
|
|
452
|
-
scheduleTask: {
|
|
453
|
-
count: scheduleTaskReport[0]?.count || 0,
|
|
454
|
-
percentage: calculatePercentageChange(
|
|
455
|
-
resultToday.scheduleTaskCount,
|
|
456
|
-
resultYesterday.scheduleTaskCount
|
|
457
|
-
)
|
|
458
|
-
},
|
|
459
|
-
supply: {
|
|
460
|
-
count: supplyReport[0]?.count || 0,
|
|
461
|
-
percentage: calculatePercentageChange(
|
|
462
|
-
resultToday.supplyCount,
|
|
463
|
-
resultYesterday.supplyCount
|
|
464
|
-
)
|
|
465
|
-
},
|
|
466
|
-
requestItem: {
|
|
467
|
-
count: requestItemReport[0]?.count || 0,
|
|
468
|
-
percentage: calculatePercentageChange(
|
|
469
|
-
resultToday.requestItemCount,
|
|
470
|
-
resultYesterday.requestItemCount
|
|
471
|
-
)
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
setCache(dashboardCacheKey, result, 15 * 60).then(() => {
|
|
475
|
-
logger.info(`Cache set for dashboard: ${dashboardCacheKey}`);
|
|
655
|
+
const data = paginate(items, page, limit, length);
|
|
656
|
+
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
657
|
+
logger.info(`Cache set for recentFeedbacks: ${cacheKey}`);
|
|
476
658
|
}).catch((err) => {
|
|
477
659
|
logger.error(
|
|
478
|
-
`Failed to set cache for
|
|
660
|
+
`Failed to set cache for recentFeedbacks: ${cacheKey}`,
|
|
479
661
|
err
|
|
480
662
|
);
|
|
481
663
|
});
|
|
482
|
-
return
|
|
664
|
+
return data;
|
|
483
665
|
} catch (error) {
|
|
484
666
|
throw error;
|
|
485
667
|
}
|
|
486
668
|
}
|
|
487
669
|
return {
|
|
488
|
-
getHygieneDashboard
|
|
670
|
+
getHygieneDashboard,
|
|
671
|
+
getWeeklyActivity,
|
|
672
|
+
getTodayTaskSchedule,
|
|
673
|
+
getStaffAttendance,
|
|
674
|
+
getRecentFeedbacks
|
|
489
675
|
};
|
|
490
676
|
}
|
|
491
677
|
|
|
492
678
|
// src/controllers/hygiene-dashboard.controller.ts
|
|
493
679
|
import Joi from "joi";
|
|
494
680
|
import { BadRequestError as BadRequestError2, logger as logger2 } from "@7365admin1/node-server-utils";
|
|
681
|
+
import { AppServiceType } from "@7365admin1/core";
|
|
495
682
|
function useHygieneDashboardController() {
|
|
496
|
-
const {
|
|
683
|
+
const {
|
|
684
|
+
getHygieneDashboard: _getHygieneDashboard,
|
|
685
|
+
getWeeklyActivity: _getWeeklyActivity,
|
|
686
|
+
getTodayTaskSchedule: _getTodayTaskSchedule,
|
|
687
|
+
getStaffAttendance: _getStaffAttendance,
|
|
688
|
+
getRecentFeedbacks: _getRecentFeedbacks
|
|
689
|
+
} = useHygieneDashboardRepository();
|
|
497
690
|
async function getHygieneDashboard(req, res, next) {
|
|
498
691
|
const query = { ...req.query, ...req.params };
|
|
499
692
|
const validation = Joi.object({
|
|
500
693
|
site: Joi.string().hex().required(),
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
toiletAreaPeriod: Joi.string().valid(...allowedPeriods).optional(),
|
|
504
|
-
scheduleTaskPeriod: Joi.string().valid(...allowedPeriods).optional(),
|
|
505
|
-
supplyPeriod: Joi.string().valid(...allowedPeriods).optional(),
|
|
506
|
-
requestItemPeriod: Joi.string().valid(...allowedPeriods).optional()
|
|
694
|
+
serviceType: Joi.string().valid(...Object.values(AppServiceType)).required(),
|
|
695
|
+
period: Joi.string().valid(...allowedPeriods).optional()
|
|
507
696
|
});
|
|
508
697
|
const { error } = validation.validate(query);
|
|
509
698
|
if (error) {
|
|
@@ -512,21 +701,136 @@ function useHygieneDashboardController() {
|
|
|
512
701
|
return;
|
|
513
702
|
}
|
|
514
703
|
const site = req.params.site ?? "";
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
const toiletAreaPeriod = req.query.toiletAreaPeriod || "today";
|
|
518
|
-
const scheduleTaskPeriod = req.query.scheduleTaskPeriod || "today";
|
|
519
|
-
const supplyPeriod = req.query.supplyPeriod || "today";
|
|
520
|
-
const requestItemPeriod = req.query.requestItemPeriod || "today";
|
|
704
|
+
const serviceType = req.params.serviceType;
|
|
705
|
+
const period = req.query.period || "today";
|
|
521
706
|
try {
|
|
522
707
|
const data = await _getHygieneDashboard({
|
|
523
708
|
site,
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
709
|
+
period,
|
|
710
|
+
serviceType
|
|
711
|
+
});
|
|
712
|
+
res.json(data);
|
|
713
|
+
return;
|
|
714
|
+
} catch (error2) {
|
|
715
|
+
logger2.log({ level: "error", message: error2.message });
|
|
716
|
+
next(error2);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
async function getWeeklyActivity(req, res, next) {
|
|
721
|
+
const query = { ...req.params };
|
|
722
|
+
const validation = Joi.object({
|
|
723
|
+
site: Joi.string().hex().required(),
|
|
724
|
+
serviceType: Joi.string().valid(...Object.values(AppServiceType)).required()
|
|
725
|
+
});
|
|
726
|
+
const { error } = validation.validate(query);
|
|
727
|
+
if (error) {
|
|
728
|
+
logger2.log({ level: "error", message: error.message });
|
|
729
|
+
next(new BadRequestError2(error.message));
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const site = req.params.site;
|
|
733
|
+
const serviceType = req.params.serviceType;
|
|
734
|
+
try {
|
|
735
|
+
const data = await _getWeeklyActivity({ site, serviceType });
|
|
736
|
+
res.json(data);
|
|
737
|
+
return;
|
|
738
|
+
} catch (error2) {
|
|
739
|
+
logger2.log({ level: "error", message: error2.message });
|
|
740
|
+
next(error2);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
async function getTodayTaskSchedule(req, res, next) {
|
|
745
|
+
const query = { ...req.query, ...req.params };
|
|
746
|
+
const validation = Joi.object({
|
|
747
|
+
site: Joi.string().hex().required(),
|
|
748
|
+
serviceType: Joi.string().valid(...Object.values(AppServiceType)).required(),
|
|
749
|
+
page: Joi.number().min(1).optional().allow("", null),
|
|
750
|
+
limit: Joi.number().min(1).optional().allow("", null)
|
|
751
|
+
});
|
|
752
|
+
const { error } = validation.validate(query);
|
|
753
|
+
if (error) {
|
|
754
|
+
logger2.log({ level: "error", message: error.message });
|
|
755
|
+
next(new BadRequestError2(error.message));
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
const site = req.params.site;
|
|
759
|
+
const serviceType = req.params.serviceType;
|
|
760
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
761
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
762
|
+
try {
|
|
763
|
+
const data = await _getTodayTaskSchedule({
|
|
764
|
+
site,
|
|
765
|
+
serviceType,
|
|
766
|
+
page,
|
|
767
|
+
limit
|
|
768
|
+
});
|
|
769
|
+
res.json(data);
|
|
770
|
+
return;
|
|
771
|
+
} catch (error2) {
|
|
772
|
+
logger2.log({ level: "error", message: error2.message });
|
|
773
|
+
next(error2);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
async function getStaffAttendance(req, res, next) {
|
|
778
|
+
const query = { ...req.query, ...req.params };
|
|
779
|
+
const validation = Joi.object({
|
|
780
|
+
site: Joi.string().hex().required(),
|
|
781
|
+
serviceType: Joi.string().valid(...Object.values(AppServiceType)).required(),
|
|
782
|
+
page: Joi.number().min(1).optional().allow("", null),
|
|
783
|
+
limit: Joi.number().min(1).optional().allow("", null)
|
|
784
|
+
});
|
|
785
|
+
const { error } = validation.validate(query);
|
|
786
|
+
if (error) {
|
|
787
|
+
logger2.log({ level: "error", message: error.message });
|
|
788
|
+
next(new BadRequestError2(error.message));
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
const site = req.params.site;
|
|
792
|
+
const serviceType = req.params.serviceType;
|
|
793
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
794
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
795
|
+
try {
|
|
796
|
+
const data = await _getStaffAttendance({
|
|
797
|
+
site,
|
|
798
|
+
serviceType,
|
|
799
|
+
page,
|
|
800
|
+
limit
|
|
801
|
+
});
|
|
802
|
+
res.json(data);
|
|
803
|
+
return;
|
|
804
|
+
} catch (error2) {
|
|
805
|
+
logger2.log({ level: "error", message: error2.message });
|
|
806
|
+
next(error2);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
async function getRecentFeedbacks(req, res, next) {
|
|
811
|
+
const query = { ...req.query, ...req.params };
|
|
812
|
+
const validation = Joi.object({
|
|
813
|
+
site: Joi.string().hex().required(),
|
|
814
|
+
serviceType: Joi.string().valid(...Object.values(AppServiceType)).required(),
|
|
815
|
+
page: Joi.number().min(1).optional().allow("", null),
|
|
816
|
+
limit: Joi.number().min(1).optional().allow("", null)
|
|
817
|
+
});
|
|
818
|
+
const { error } = validation.validate(query);
|
|
819
|
+
if (error) {
|
|
820
|
+
logger2.log({ level: "error", message: error.message });
|
|
821
|
+
next(new BadRequestError2(error.message));
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const site = req.params.site;
|
|
825
|
+
const serviceType = req.params.serviceType;
|
|
826
|
+
const page = parseInt(req.query.page) ?? 1;
|
|
827
|
+
const limit = parseInt(req.query.limit) ?? 10;
|
|
828
|
+
try {
|
|
829
|
+
const data = await _getRecentFeedbacks({
|
|
830
|
+
site,
|
|
831
|
+
serviceType,
|
|
832
|
+
page,
|
|
833
|
+
limit
|
|
530
834
|
});
|
|
531
835
|
res.json(data);
|
|
532
836
|
return;
|
|
@@ -537,7 +841,11 @@ function useHygieneDashboardController() {
|
|
|
537
841
|
}
|
|
538
842
|
}
|
|
539
843
|
return {
|
|
540
|
-
getHygieneDashboard
|
|
844
|
+
getHygieneDashboard,
|
|
845
|
+
getWeeklyActivity,
|
|
846
|
+
getTodayTaskSchedule,
|
|
847
|
+
getStaffAttendance,
|
|
848
|
+
getRecentFeedbacks
|
|
541
849
|
};
|
|
542
850
|
}
|
|
543
851
|
|
|
@@ -545,10 +853,10 @@ function useHygieneDashboardController() {
|
|
|
545
853
|
import Joi2 from "joi";
|
|
546
854
|
import { ObjectId as ObjectId2 } from "mongodb";
|
|
547
855
|
import { BadRequestError as BadRequestError3, logger as logger3 } from "@7365admin1/node-server-utils";
|
|
548
|
-
import { AppServiceType } from "@7365admin1/core";
|
|
856
|
+
import { AppServiceType as AppServiceType2 } from "@7365admin1/core";
|
|
549
857
|
var areaSchema = Joi2.object({
|
|
550
858
|
site: Joi2.string().hex().required(),
|
|
551
|
-
serviceType: Joi2.string().valid(...Object.values(
|
|
859
|
+
serviceType: Joi2.string().valid(...Object.values(AppServiceType2)).required(),
|
|
552
860
|
name: Joi2.string().required(),
|
|
553
861
|
type: Joi2.string().valid(...allowedTypes).required(),
|
|
554
862
|
set: Joi2.number().min(0).optional(),
|
|
@@ -605,7 +913,7 @@ import { ObjectId as ObjectId3 } from "mongodb";
|
|
|
605
913
|
import {
|
|
606
914
|
useAtlas as useAtlas2,
|
|
607
915
|
InternalServerError as InternalServerError2,
|
|
608
|
-
paginate,
|
|
916
|
+
paginate as paginate2,
|
|
609
917
|
BadRequestError as BadRequestError4,
|
|
610
918
|
useCache as useCache2,
|
|
611
919
|
logger as logger4,
|
|
@@ -740,7 +1048,7 @@ function useAreaRepo() {
|
|
|
740
1048
|
{ $limit: limit }
|
|
741
1049
|
]).toArray();
|
|
742
1050
|
const length = await collection.countDocuments(query);
|
|
743
|
-
const data =
|
|
1051
|
+
const data = paginate2(items, page, limit, length);
|
|
744
1052
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
745
1053
|
logger4.info(`Cache set for key: ${cacheKey}`);
|
|
746
1054
|
}).catch((err) => {
|
|
@@ -1062,7 +1370,7 @@ import { ObjectId as ObjectId5 } from "mongodb";
|
|
|
1062
1370
|
import {
|
|
1063
1371
|
useAtlas as useAtlas3,
|
|
1064
1372
|
InternalServerError as InternalServerError3,
|
|
1065
|
-
paginate as
|
|
1373
|
+
paginate as paginate3,
|
|
1066
1374
|
BadRequestError as BadRequestError6,
|
|
1067
1375
|
useCache as useCache3,
|
|
1068
1376
|
logger as logger7,
|
|
@@ -1073,10 +1381,10 @@ import {
|
|
|
1073
1381
|
import Joi3 from "joi";
|
|
1074
1382
|
import { ObjectId as ObjectId4 } from "mongodb";
|
|
1075
1383
|
import { BadRequestError as BadRequestError5, logger as logger6 } from "@7365admin1/node-server-utils";
|
|
1076
|
-
import { AppServiceType as
|
|
1384
|
+
import { AppServiceType as AppServiceType3 } from "@7365admin1/core";
|
|
1077
1385
|
var unitSchema = Joi3.object({
|
|
1078
1386
|
site: Joi3.string().hex().required(),
|
|
1079
|
-
serviceType: Joi3.string().valid(...Object.values(
|
|
1387
|
+
serviceType: Joi3.string().valid(...Object.values(AppServiceType3)).required(),
|
|
1080
1388
|
name: Joi3.string().required()
|
|
1081
1389
|
});
|
|
1082
1390
|
function MUnit(value) {
|
|
@@ -1208,7 +1516,7 @@ function useUnitRepository() {
|
|
|
1208
1516
|
{ $limit: limit }
|
|
1209
1517
|
]).toArray();
|
|
1210
1518
|
const length = await collection.countDocuments(query);
|
|
1211
|
-
const data =
|
|
1519
|
+
const data = paginate3(items, page, limit, length);
|
|
1212
1520
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
1213
1521
|
logger7.info(`Cache set for key: ${cacheKey}`);
|
|
1214
1522
|
}).catch((err) => {
|
|
@@ -1497,7 +1805,7 @@ function useAreaService() {
|
|
|
1497
1805
|
// src/controllers/hygiene-area.controller.ts
|
|
1498
1806
|
import Joi4 from "joi";
|
|
1499
1807
|
import { BadRequestError as BadRequestError8, logger as logger9 } from "@7365admin1/node-server-utils";
|
|
1500
|
-
import { AppServiceType as
|
|
1808
|
+
import { AppServiceType as AppServiceType4 } from "@7365admin1/core";
|
|
1501
1809
|
|
|
1502
1810
|
// src/utils/convert-excel.util.ts
|
|
1503
1811
|
import { Readable } from "stream";
|
|
@@ -1562,7 +1870,7 @@ function useAreaController() {
|
|
|
1562
1870
|
search: Joi4.string().optional().allow("", null),
|
|
1563
1871
|
type: Joi4.string().valid("all", ...allowedTypes).optional().allow("", null),
|
|
1564
1872
|
site: Joi4.string().hex().required(),
|
|
1565
|
-
serviceType: Joi4.string().valid(...Object.values(
|
|
1873
|
+
serviceType: Joi4.string().valid(...Object.values(AppServiceType4)).required()
|
|
1566
1874
|
});
|
|
1567
1875
|
const { error } = validation.validate(query);
|
|
1568
1876
|
if (error) {
|
|
@@ -1671,7 +1979,7 @@ function useAreaController() {
|
|
|
1671
1979
|
const query = { ...req.query, ...req.params };
|
|
1672
1980
|
const validation = Joi4.object({
|
|
1673
1981
|
site: Joi4.string().hex().required(),
|
|
1674
|
-
serviceType: Joi4.string().valid(...Object.values(
|
|
1982
|
+
serviceType: Joi4.string().valid(...Object.values(AppServiceType4)).required()
|
|
1675
1983
|
});
|
|
1676
1984
|
const { error, value } = validation.validate(query);
|
|
1677
1985
|
if (error) {
|
|
@@ -1694,7 +2002,7 @@ function useAreaController() {
|
|
|
1694
2002
|
const query = { ...req.query, ...req.params };
|
|
1695
2003
|
const validation = Joi4.object({
|
|
1696
2004
|
site: Joi4.string().hex().required(),
|
|
1697
|
-
serviceType: Joi4.string().valid(...Object.values(
|
|
2005
|
+
serviceType: Joi4.string().valid(...Object.values(AppServiceType4)).required()
|
|
1698
2006
|
});
|
|
1699
2007
|
const { error, value } = validation.validate(query);
|
|
1700
2008
|
if (error) {
|
|
@@ -1977,7 +2285,7 @@ function useUnitService() {
|
|
|
1977
2285
|
// src/controllers/hygiene-unit.controller.ts
|
|
1978
2286
|
import Joi5 from "joi";
|
|
1979
2287
|
import { BadRequestError as BadRequestError10, logger as logger12 } from "@7365admin1/node-server-utils";
|
|
1980
|
-
import { AppServiceType as
|
|
2288
|
+
import { AppServiceType as AppServiceType5 } from "@7365admin1/core";
|
|
1981
2289
|
function useUnitController() {
|
|
1982
2290
|
const { createUnit: _createUnit, getUnits: _getUnits } = useUnitRepository();
|
|
1983
2291
|
const {
|
|
@@ -2011,7 +2319,7 @@ function useUnitController() {
|
|
|
2011
2319
|
limit: Joi5.number().min(1).optional().allow("", null),
|
|
2012
2320
|
search: Joi5.string().optional().allow("", null),
|
|
2013
2321
|
site: Joi5.string().hex().required(),
|
|
2014
|
-
serviceType: Joi5.string().valid(...Object.values(
|
|
2322
|
+
serviceType: Joi5.string().valid(...Object.values(AppServiceType5)).required()
|
|
2015
2323
|
});
|
|
2016
2324
|
const { error } = validation.validate(query);
|
|
2017
2325
|
if (error) {
|
|
@@ -2092,7 +2400,7 @@ function useUnitController() {
|
|
|
2092
2400
|
const query = { ...req.query, ...req.params };
|
|
2093
2401
|
const validation = Joi5.object({
|
|
2094
2402
|
site: Joi5.string().hex().required(),
|
|
2095
|
-
serviceType: Joi5.string().valid(...Object.values(
|
|
2403
|
+
serviceType: Joi5.string().valid(...Object.values(AppServiceType5)).required()
|
|
2096
2404
|
});
|
|
2097
2405
|
const { error, value } = validation.validate(query);
|
|
2098
2406
|
if (error) {
|
|
@@ -2115,7 +2423,7 @@ function useUnitController() {
|
|
|
2115
2423
|
const query = { ...req.query, ...req.params };
|
|
2116
2424
|
const validation = Joi5.object({
|
|
2117
2425
|
site: Joi5.string().hex().required(),
|
|
2118
|
-
serviceType: Joi5.string().valid(...Object.values(
|
|
2426
|
+
serviceType: Joi5.string().valid(...Object.values(AppServiceType5)).required()
|
|
2119
2427
|
});
|
|
2120
2428
|
const { error, value } = validation.validate(query);
|
|
2121
2429
|
if (error) {
|
|
@@ -2161,11 +2469,11 @@ function useUnitController() {
|
|
|
2161
2469
|
import Joi6 from "joi";
|
|
2162
2470
|
import { ObjectId as ObjectId6 } from "mongodb";
|
|
2163
2471
|
import { BadRequestError as BadRequestError11, logger as logger13 } from "@7365admin1/node-server-utils";
|
|
2164
|
-
import { AppServiceType as
|
|
2472
|
+
import { AppServiceType as AppServiceType6 } from "@7365admin1/core";
|
|
2165
2473
|
var parentChecklistSchema = Joi6.object({
|
|
2166
2474
|
createdAt: Joi6.alternatives().try(Joi6.date(), Joi6.string()).optional().allow("", null),
|
|
2167
2475
|
site: Joi6.string().hex().required(),
|
|
2168
|
-
serviceType: Joi6.string().valid(...Object.values(
|
|
2476
|
+
serviceType: Joi6.string().valid(...Object.values(AppServiceType6)).required()
|
|
2169
2477
|
});
|
|
2170
2478
|
function MParentChecklist(value) {
|
|
2171
2479
|
const { error } = parentChecklistSchema.validate(value);
|
|
@@ -2191,16 +2499,17 @@ function MParentChecklist(value) {
|
|
|
2191
2499
|
|
|
2192
2500
|
// src/repositories/hygiene-parent-checklist.repository.ts
|
|
2193
2501
|
import { ObjectId as ObjectId7 } from "mongodb";
|
|
2502
|
+
import moment2 from "moment-timezone";
|
|
2194
2503
|
import {
|
|
2195
2504
|
useAtlas as useAtlas5,
|
|
2196
2505
|
InternalServerError as InternalServerError4,
|
|
2197
|
-
paginate as
|
|
2506
|
+
paginate as paginate4,
|
|
2198
2507
|
useCache as useCache4,
|
|
2199
2508
|
logger as logger14,
|
|
2200
2509
|
makeCacheKey as makeCacheKey4,
|
|
2201
2510
|
BadRequestError as BadRequestError12
|
|
2202
2511
|
} from "@7365admin1/node-server-utils";
|
|
2203
|
-
import { AppServiceType as
|
|
2512
|
+
import { AppServiceType as AppServiceType7 } from "@7365admin1/core";
|
|
2204
2513
|
function useParentChecklistRepo() {
|
|
2205
2514
|
const db = useAtlas5.getDb();
|
|
2206
2515
|
if (!db) {
|
|
@@ -2228,11 +2537,9 @@ function useParentChecklistRepo() {
|
|
|
2228
2537
|
async function createParentChecklist(value, session) {
|
|
2229
2538
|
try {
|
|
2230
2539
|
const currentDate = value.createdAt ? new Date(value.createdAt) : /* @__PURE__ */ new Date();
|
|
2231
|
-
const startOfDay =
|
|
2232
|
-
|
|
2233
|
-
const
|
|
2234
|
-
endOfDay.setUTCHours(23, 59, 59, 999);
|
|
2235
|
-
const allServiceTypes = Object.values(AppServiceType6);
|
|
2540
|
+
const startOfDay = moment2(currentDate).tz("Asia/Singapore").startOf("day").toDate();
|
|
2541
|
+
const endOfDay = moment2(currentDate).tz("Asia/Singapore").endOf("day").toDate();
|
|
2542
|
+
const allServiceTypes = Object.values(AppServiceType7);
|
|
2236
2543
|
const dateStr = currentDate.toISOString().split("T")[0];
|
|
2237
2544
|
if (value.site) {
|
|
2238
2545
|
let siteObjectId;
|
|
@@ -2456,7 +2763,7 @@ function useParentChecklistRepo() {
|
|
|
2456
2763
|
);
|
|
2457
2764
|
const items = await collection.aggregate(pipeline).toArray();
|
|
2458
2765
|
const length = await collection.countDocuments(query);
|
|
2459
|
-
const data =
|
|
2766
|
+
const data = paginate4(items, page, limit, length);
|
|
2460
2767
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
2461
2768
|
logger14.info(`Cache set for key: ${cacheKey}`);
|
|
2462
2769
|
}).catch((err) => {
|
|
@@ -2582,11 +2889,8 @@ function useParentChecklistRepo() {
|
|
|
2582
2889
|
}
|
|
2583
2890
|
}
|
|
2584
2891
|
async function getTodayParentChecklists() {
|
|
2585
|
-
const
|
|
2586
|
-
const
|
|
2587
|
-
start.setUTCHours(0, 0, 0, 0);
|
|
2588
|
-
const end = new Date(now);
|
|
2589
|
-
end.setUTCHours(23, 59, 59, 999);
|
|
2892
|
+
const start = moment2().tz("Asia/Singapore").startOf("day").toDate();
|
|
2893
|
+
const end = moment2().tz("Asia/Singapore").endOf("day").toDate();
|
|
2590
2894
|
try {
|
|
2591
2895
|
const items = await collection.find(
|
|
2592
2896
|
{ createdAt: { $gte: start, $lte: end } },
|
|
@@ -2599,11 +2903,8 @@ function useParentChecklistRepo() {
|
|
|
2599
2903
|
}
|
|
2600
2904
|
}
|
|
2601
2905
|
async function getTodayParentChecklistsForAreaGen() {
|
|
2602
|
-
const
|
|
2603
|
-
const
|
|
2604
|
-
start.setUTCHours(0, 0, 0, 0);
|
|
2605
|
-
const end = new Date(now);
|
|
2606
|
-
end.setUTCHours(23, 59, 59, 999);
|
|
2906
|
+
const start = moment2().tz("Asia/Singapore").startOf("day").toDate();
|
|
2907
|
+
const end = moment2().tz("Asia/Singapore").endOf("day").toDate();
|
|
2607
2908
|
try {
|
|
2608
2909
|
const items = await collection.find(
|
|
2609
2910
|
{
|
|
@@ -2637,7 +2938,7 @@ function useParentChecklistRepo() {
|
|
|
2637
2938
|
// src/controllers/hygiene-parent-checklist.controller.ts
|
|
2638
2939
|
import Joi7 from "joi";
|
|
2639
2940
|
import { BadRequestError as BadRequestError13, logger as logger15 } from "@7365admin1/node-server-utils";
|
|
2640
|
-
import { AppServiceType as
|
|
2941
|
+
import { AppServiceType as AppServiceType8 } from "@7365admin1/core";
|
|
2641
2942
|
function useParentChecklistController() {
|
|
2642
2943
|
const {
|
|
2643
2944
|
createParentChecklist: _createParentChecklist,
|
|
@@ -2648,7 +2949,7 @@ function useParentChecklistController() {
|
|
|
2648
2949
|
const validation = Joi7.object({
|
|
2649
2950
|
createdAt: Joi7.alternatives().try(Joi7.date(), Joi7.string()).optional().allow("", null),
|
|
2650
2951
|
site: Joi7.string().hex().required(),
|
|
2651
|
-
serviceType: Joi7.string().valid(...Object.values(
|
|
2952
|
+
serviceType: Joi7.string().valid(...Object.values(AppServiceType8)).optional().allow("", null)
|
|
2652
2953
|
});
|
|
2653
2954
|
const { error } = validation.validate(payload);
|
|
2654
2955
|
if (error) {
|
|
@@ -2673,7 +2974,7 @@ function useParentChecklistController() {
|
|
|
2673
2974
|
limit: Joi7.number().min(1).optional().allow("", null),
|
|
2674
2975
|
search: Joi7.string().optional().allow("", null),
|
|
2675
2976
|
site: Joi7.string().hex().required(),
|
|
2676
|
-
serviceType: Joi7.string().valid(...Object.values(
|
|
2977
|
+
serviceType: Joi7.string().valid(...Object.values(AppServiceType8)).required(),
|
|
2677
2978
|
startDate: Joi7.alternatives().try(Joi7.date(), Joi7.string()).optional().allow("", null),
|
|
2678
2979
|
endDate: Joi7.alternatives().try(Joi7.date(), Joi7.string()).optional().allow("", null),
|
|
2679
2980
|
status: Joi7.string().valid(...allowedStatus, "all").optional().allow("", null)
|
|
@@ -2721,11 +3022,11 @@ function useParentChecklistController() {
|
|
|
2721
3022
|
import Joi8 from "joi";
|
|
2722
3023
|
import { ObjectId as ObjectId8 } from "mongodb";
|
|
2723
3024
|
import { BadRequestError as BadRequestError14, logger as logger16 } from "@7365admin1/node-server-utils";
|
|
2724
|
-
import { AppServiceType as
|
|
3025
|
+
import { AppServiceType as AppServiceType9 } from "@7365admin1/core";
|
|
2725
3026
|
var allowedChecklistStatus = ["open", "completed", "closed"];
|
|
2726
3027
|
var areaChecklistSchema = Joi8.object({
|
|
2727
3028
|
schedule: Joi8.string().hex().required(),
|
|
2728
|
-
serviceType: Joi8.string().valid(...Object.values(
|
|
3029
|
+
serviceType: Joi8.string().valid(...Object.values(AppServiceType9)).required(),
|
|
2729
3030
|
area: Joi8.string().hex().required(),
|
|
2730
3031
|
name: Joi8.string().required(),
|
|
2731
3032
|
type: Joi8.string().valid(...allowedTypes).required(),
|
|
@@ -2812,7 +3113,7 @@ import {
|
|
|
2812
3113
|
InternalServerError as InternalServerError5,
|
|
2813
3114
|
logger as logger17,
|
|
2814
3115
|
makeCacheKey as makeCacheKey5,
|
|
2815
|
-
paginate as
|
|
3116
|
+
paginate as paginate5,
|
|
2816
3117
|
useAtlas as useAtlas6,
|
|
2817
3118
|
useCache as useCache5
|
|
2818
3119
|
} from "@7365admin1/node-server-utils";
|
|
@@ -3003,7 +3304,7 @@ function useAreaChecklistRepo() {
|
|
|
3003
3304
|
];
|
|
3004
3305
|
const items = await collection.aggregate(pipeline, { session }).toArray();
|
|
3005
3306
|
const length = await collection.countDocuments(query, { session });
|
|
3006
|
-
const data =
|
|
3307
|
+
const data = paginate5(items, page, limit, length);
|
|
3007
3308
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3008
3309
|
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
3009
3310
|
}).catch((err) => {
|
|
@@ -3111,7 +3412,7 @@ function useAreaChecklistRepo() {
|
|
|
3111
3412
|
];
|
|
3112
3413
|
const items = await collection.aggregate(pipeline).toArray();
|
|
3113
3414
|
const length = await collection.countDocuments(query);
|
|
3114
|
-
const data =
|
|
3415
|
+
const data = paginate5(items, page, limit, length);
|
|
3115
3416
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3116
3417
|
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
3117
3418
|
}).catch((err) => {
|
|
@@ -3498,7 +3799,7 @@ function useAreaChecklistRepo() {
|
|
|
3498
3799
|
collection.aggregate(countPipeline, { session }).toArray()
|
|
3499
3800
|
]);
|
|
3500
3801
|
const length = countResult.length > 0 ? countResult[0].total : 0;
|
|
3501
|
-
const data =
|
|
3802
|
+
const data = paginate5(items, page, limit, length);
|
|
3502
3803
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
3503
3804
|
logger17.info(`Cache set for key: ${cacheKey}`);
|
|
3504
3805
|
}).catch((err) => {
|
|
@@ -4177,7 +4478,7 @@ import {
|
|
|
4177
4478
|
InternalServerError as InternalServerError7,
|
|
4178
4479
|
logger as logger20
|
|
4179
4480
|
} from "@7365admin1/node-server-utils";
|
|
4180
|
-
import { AppServiceType as
|
|
4481
|
+
import { AppServiceType as AppServiceType10 } from "@7365admin1/core";
|
|
4181
4482
|
|
|
4182
4483
|
// src/services/hygiene-checklist-pdf.service.ts
|
|
4183
4484
|
import { launch } from "puppeteer";
|
|
@@ -4444,7 +4745,7 @@ function useAreaChecklistController() {
|
|
|
4444
4745
|
type: Joi9.string().valid(...allowedTypes, "all").optional().allow("", null),
|
|
4445
4746
|
status: Joi9.string().valid(...allowedStatus, "all").optional().allow("", null),
|
|
4446
4747
|
schedule: Joi9.string().hex().required(),
|
|
4447
|
-
serviceType: Joi9.string().valid(...Object.values(
|
|
4748
|
+
serviceType: Joi9.string().valid(...Object.values(AppServiceType10)).required()
|
|
4448
4749
|
});
|
|
4449
4750
|
const { error } = validation.validate(query);
|
|
4450
4751
|
if (error) {
|
|
@@ -4544,7 +4845,7 @@ function useAreaChecklistController() {
|
|
|
4544
4845
|
page: Joi9.number().min(1).optional().allow("", null),
|
|
4545
4846
|
limit: Joi9.number().min(1).optional().allow("", null),
|
|
4546
4847
|
search: Joi9.string().optional().allow("", null),
|
|
4547
|
-
serviceType: Joi9.string().valid(...Object.values(
|
|
4848
|
+
serviceType: Joi9.string().valid(...Object.values(AppServiceType10)).required(),
|
|
4548
4849
|
id: Joi9.string().hex().required()
|
|
4549
4850
|
});
|
|
4550
4851
|
const { error } = validation.validate(query);
|
|
@@ -4673,12 +4974,13 @@ function useAreaChecklistController() {
|
|
|
4673
4974
|
import Joi10 from "joi";
|
|
4674
4975
|
import { ObjectId as ObjectId12 } from "mongodb";
|
|
4675
4976
|
import { BadRequestError as BadRequestError17, logger as logger21 } from "@7365admin1/node-server-utils";
|
|
4676
|
-
import { AppServiceType as
|
|
4977
|
+
import { AppServiceType as AppServiceType11 } from "@7365admin1/core";
|
|
4677
4978
|
var supplySchema = Joi10.object({
|
|
4678
4979
|
site: Joi10.string().hex().required(),
|
|
4679
|
-
serviceType: Joi10.string().valid(...Object.values(
|
|
4980
|
+
serviceType: Joi10.string().valid(...Object.values(AppServiceType11)).required(),
|
|
4680
4981
|
name: Joi10.string().required(),
|
|
4681
|
-
unitOfMeasurement: Joi10.string().required()
|
|
4982
|
+
unitOfMeasurement: Joi10.string().required(),
|
|
4983
|
+
attachment: Joi10.string().allow(null, "").optional()
|
|
4682
4984
|
});
|
|
4683
4985
|
function MSupply(value) {
|
|
4684
4986
|
const { error } = supplySchema.validate(value);
|
|
@@ -4699,6 +5001,7 @@ function MSupply(value) {
|
|
|
4699
5001
|
name: value.name,
|
|
4700
5002
|
unitOfMeasurement: value.unitOfMeasurement,
|
|
4701
5003
|
qty: 0,
|
|
5004
|
+
attachment: value.attachment,
|
|
4702
5005
|
status: "active",
|
|
4703
5006
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4704
5007
|
updatedAt: "",
|
|
@@ -4711,7 +5014,7 @@ import { ObjectId as ObjectId13 } from "mongodb";
|
|
|
4711
5014
|
import {
|
|
4712
5015
|
useAtlas as useAtlas9,
|
|
4713
5016
|
InternalServerError as InternalServerError8,
|
|
4714
|
-
paginate as
|
|
5017
|
+
paginate as paginate6,
|
|
4715
5018
|
BadRequestError as BadRequestError18,
|
|
4716
5019
|
useCache as useCache6,
|
|
4717
5020
|
logger as logger22,
|
|
@@ -4819,6 +5122,7 @@ function useSupplyRepository() {
|
|
|
4819
5122
|
{ $match: query },
|
|
4820
5123
|
{
|
|
4821
5124
|
$project: {
|
|
5125
|
+
attachment: 1,
|
|
4822
5126
|
name: 1,
|
|
4823
5127
|
qty: 1,
|
|
4824
5128
|
status: 1
|
|
@@ -4829,7 +5133,7 @@ function useSupplyRepository() {
|
|
|
4829
5133
|
{ $limit: limit }
|
|
4830
5134
|
]).toArray();
|
|
4831
5135
|
const length = await collection.countDocuments(query);
|
|
4832
|
-
const data =
|
|
5136
|
+
const data = paginate6(items, page, limit, length);
|
|
4833
5137
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
4834
5138
|
logger22.info(`Cache set for key: ${cacheKey}`);
|
|
4835
5139
|
}).catch((err) => {
|
|
@@ -4867,6 +5171,7 @@ function useSupplyRepository() {
|
|
|
4867
5171
|
{ $match: query },
|
|
4868
5172
|
{
|
|
4869
5173
|
$project: {
|
|
5174
|
+
attachment: 1,
|
|
4870
5175
|
name: 1,
|
|
4871
5176
|
unitOfMeasurement: 1,
|
|
4872
5177
|
qty: 1
|
|
@@ -4967,7 +5272,7 @@ function useSupplyRepository() {
|
|
|
4967
5272
|
// src/controllers/hygiene-supply.controller.ts
|
|
4968
5273
|
import Joi11 from "joi";
|
|
4969
5274
|
import { BadRequestError as BadRequestError19, logger as logger23 } from "@7365admin1/node-server-utils";
|
|
4970
|
-
import { AppServiceType as
|
|
5275
|
+
import { AppServiceType as AppServiceType12 } from "@7365admin1/core";
|
|
4971
5276
|
function useSupplyController() {
|
|
4972
5277
|
const {
|
|
4973
5278
|
createSupply: _createSupply,
|
|
@@ -5001,7 +5306,7 @@ function useSupplyController() {
|
|
|
5001
5306
|
limit: Joi11.number().min(1).optional().allow("", null),
|
|
5002
5307
|
search: Joi11.string().optional().allow("", null),
|
|
5003
5308
|
site: Joi11.string().hex().required(),
|
|
5004
|
-
serviceType: Joi11.string().valid(...Object.values(
|
|
5309
|
+
serviceType: Joi11.string().valid(...Object.values(AppServiceType12)).required()
|
|
5005
5310
|
});
|
|
5006
5311
|
const { error } = validation.validate(query);
|
|
5007
5312
|
if (error) {
|
|
@@ -5055,7 +5360,8 @@ function useSupplyController() {
|
|
|
5055
5360
|
id: Joi11.string().hex().required(),
|
|
5056
5361
|
name: Joi11.string().optional().allow("", null),
|
|
5057
5362
|
unitOfMeasurement: Joi11.string().optional().allow("", null),
|
|
5058
|
-
qty: Joi11.number().min(0).optional().allow("", null)
|
|
5363
|
+
qty: Joi11.number().min(0).optional().allow("", null),
|
|
5364
|
+
attachment: Joi11.string().optional().allow("", null)
|
|
5059
5365
|
});
|
|
5060
5366
|
const { error } = validation.validate(payload);
|
|
5061
5367
|
if (error) {
|
|
@@ -5106,10 +5412,10 @@ function useSupplyController() {
|
|
|
5106
5412
|
import Joi12 from "joi";
|
|
5107
5413
|
import { ObjectId as ObjectId14 } from "mongodb";
|
|
5108
5414
|
import { BadRequestError as BadRequestError20, logger as logger24 } from "@7365admin1/node-server-utils";
|
|
5109
|
-
import { AppServiceType as
|
|
5415
|
+
import { AppServiceType as AppServiceType13 } from "@7365admin1/core";
|
|
5110
5416
|
var stockSchema = Joi12.object({
|
|
5111
5417
|
site: Joi12.string().hex().required(),
|
|
5112
|
-
serviceType: Joi12.string().valid(...Object.values(
|
|
5418
|
+
serviceType: Joi12.string().valid(...Object.values(AppServiceType13)).required(),
|
|
5113
5419
|
supply: Joi12.string().hex().required(),
|
|
5114
5420
|
in: Joi12.number().min(0).optional(),
|
|
5115
5421
|
out: Joi12.number().min(0).optional(),
|
|
@@ -5160,7 +5466,7 @@ import {
|
|
|
5160
5466
|
useCache as useCache7,
|
|
5161
5467
|
logger as logger25,
|
|
5162
5468
|
makeCacheKey as makeCacheKey7,
|
|
5163
|
-
paginate as
|
|
5469
|
+
paginate as paginate7
|
|
5164
5470
|
} from "@7365admin1/node-server-utils";
|
|
5165
5471
|
function useStockRepository() {
|
|
5166
5472
|
const db = useAtlas10.getDb();
|
|
@@ -5268,7 +5574,7 @@ function useStockRepository() {
|
|
|
5268
5574
|
{ $limit: limit }
|
|
5269
5575
|
]).toArray();
|
|
5270
5576
|
const length = await collection.countDocuments(query);
|
|
5271
|
-
const data =
|
|
5577
|
+
const data = paginate7(items, page, limit, length);
|
|
5272
5578
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
5273
5579
|
logger25.info(`Cache set for key: ${cacheKey}`);
|
|
5274
5580
|
}).catch((err) => {
|
|
@@ -5343,7 +5649,7 @@ function useStockService() {
|
|
|
5343
5649
|
// src/controllers/hygiene-stock.controller.ts
|
|
5344
5650
|
import Joi13 from "joi";
|
|
5345
5651
|
import { BadRequestError as BadRequestError23, logger as logger26 } from "@7365admin1/node-server-utils";
|
|
5346
|
-
import { AppServiceType as
|
|
5652
|
+
import { AppServiceType as AppServiceType14 } from "@7365admin1/core";
|
|
5347
5653
|
function useStockController() {
|
|
5348
5654
|
const { getStocksBySupplyId: _getStocksBySupplyId } = useStockRepository();
|
|
5349
5655
|
const { createStock: _createStock } = useStockService();
|
|
@@ -5351,7 +5657,7 @@ function useStockController() {
|
|
|
5351
5657
|
const payload = { ...req.body, ...req.params };
|
|
5352
5658
|
const validation = Joi13.object({
|
|
5353
5659
|
site: Joi13.string().hex().required(),
|
|
5354
|
-
serviceType: Joi13.string().valid(...Object.values(
|
|
5660
|
+
serviceType: Joi13.string().valid(...Object.values(AppServiceType14)).required(),
|
|
5355
5661
|
supply: Joi13.string().hex().required(),
|
|
5356
5662
|
qty: Joi13.number().min(0).required(),
|
|
5357
5663
|
remarks: Joi13.string().optional().allow("", null)
|
|
@@ -5379,7 +5685,7 @@ function useStockController() {
|
|
|
5379
5685
|
limit: Joi13.number().min(1).optional().allow("", null),
|
|
5380
5686
|
search: Joi13.string().optional().allow("", null),
|
|
5381
5687
|
site: Joi13.string().hex().required(),
|
|
5382
|
-
serviceType: Joi13.string().valid(...Object.values(
|
|
5688
|
+
serviceType: Joi13.string().valid(...Object.values(AppServiceType14)).required(),
|
|
5383
5689
|
supply: Joi13.string().hex().required()
|
|
5384
5690
|
});
|
|
5385
5691
|
const { error } = validation.validate(query);
|
|
@@ -5421,11 +5727,11 @@ function useStockController() {
|
|
|
5421
5727
|
import Joi14 from "joi";
|
|
5422
5728
|
import { ObjectId as ObjectId16 } from "mongodb";
|
|
5423
5729
|
import { BadRequestError as BadRequestError24, logger as logger27 } from "@7365admin1/node-server-utils";
|
|
5424
|
-
import { AppServiceType as
|
|
5730
|
+
import { AppServiceType as AppServiceType15 } from "@7365admin1/core";
|
|
5425
5731
|
var allowedCheckOutItemStatus = ["pending", "completed"];
|
|
5426
5732
|
var checkOutItemSchema = Joi14.object({
|
|
5427
5733
|
site: Joi14.string().hex().required(),
|
|
5428
|
-
serviceType: Joi14.string().valid(...Object.values(
|
|
5734
|
+
serviceType: Joi14.string().valid(...Object.values(AppServiceType15)).required(),
|
|
5429
5735
|
supply: Joi14.string().hex().required(),
|
|
5430
5736
|
supplyName: Joi14.string().required(),
|
|
5431
5737
|
qty: Joi14.number().min(0).required(),
|
|
@@ -5477,7 +5783,7 @@ import {
|
|
|
5477
5783
|
useCache as useCache8,
|
|
5478
5784
|
logger as logger28,
|
|
5479
5785
|
makeCacheKey as makeCacheKey8,
|
|
5480
|
-
paginate as
|
|
5786
|
+
paginate as paginate8,
|
|
5481
5787
|
BadRequestError as BadRequestError25,
|
|
5482
5788
|
NotFoundError as NotFoundError6
|
|
5483
5789
|
} from "@7365admin1/node-server-utils";
|
|
@@ -5636,7 +5942,7 @@ function useCheckOutItemRepository() {
|
|
|
5636
5942
|
{ $limit: limit }
|
|
5637
5943
|
]).toArray();
|
|
5638
5944
|
const length = await collection.countDocuments(query);
|
|
5639
|
-
const data =
|
|
5945
|
+
const data = paginate8(items, page, limit, length);
|
|
5640
5946
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
5641
5947
|
logger28.info(`Cache set for key: ${cacheKey}`);
|
|
5642
5948
|
}).catch((err) => {
|
|
@@ -5859,7 +6165,7 @@ function useCheckOutItemService() {
|
|
|
5859
6165
|
// src/controllers/hygiene-checkout-item.controller.ts
|
|
5860
6166
|
import Joi15 from "joi";
|
|
5861
6167
|
import { BadRequestError as BadRequestError27, logger as logger29 } from "@7365admin1/node-server-utils";
|
|
5862
|
-
import { AppServiceType as
|
|
6168
|
+
import { AppServiceType as AppServiceType16 } from "@7365admin1/core";
|
|
5863
6169
|
function useCheckOutItemController() {
|
|
5864
6170
|
const {
|
|
5865
6171
|
getCheckOutItems: _getCheckOutItems,
|
|
@@ -5882,7 +6188,7 @@ function useCheckOutItemController() {
|
|
|
5882
6188
|
};
|
|
5883
6189
|
const validation = Joi15.object({
|
|
5884
6190
|
site: Joi15.string().hex().required(),
|
|
5885
|
-
serviceType: Joi15.string().valid(...Object.values(
|
|
6191
|
+
serviceType: Joi15.string().valid(...Object.values(AppServiceType16)).required(),
|
|
5886
6192
|
supply: Joi15.string().hex().required(),
|
|
5887
6193
|
qty: Joi15.number().min(0).required(),
|
|
5888
6194
|
attachment: Joi15.array().items(Joi15.string()).optional().allow(null),
|
|
@@ -5917,7 +6223,7 @@ function useCheckOutItemController() {
|
|
|
5917
6223
|
};
|
|
5918
6224
|
const validation = Joi15.object({
|
|
5919
6225
|
site: Joi15.string().hex().required(),
|
|
5920
|
-
serviceType: Joi15.string().valid(...Object.values(
|
|
6226
|
+
serviceType: Joi15.string().valid(...Object.values(AppServiceType16)).required(),
|
|
5921
6227
|
createdBy: Joi15.string().hex().required(),
|
|
5922
6228
|
items: Joi15.array().items(
|
|
5923
6229
|
Joi15.object({
|
|
@@ -5950,7 +6256,7 @@ function useCheckOutItemController() {
|
|
|
5950
6256
|
limit: Joi15.number().min(1).optional().allow("", null),
|
|
5951
6257
|
search: Joi15.string().optional().allow("", null),
|
|
5952
6258
|
site: Joi15.string().hex().required(),
|
|
5953
|
-
serviceType: Joi15.string().valid(...Object.values(
|
|
6259
|
+
serviceType: Joi15.string().valid(...Object.values(AppServiceType16)).required()
|
|
5954
6260
|
});
|
|
5955
6261
|
const { error } = validation.validate(query);
|
|
5956
6262
|
if (error) {
|
|
@@ -6008,12 +6314,12 @@ function useCheckOutItemController() {
|
|
|
6008
6314
|
|
|
6009
6315
|
// src/models/hygiene-schedule-task.model.ts
|
|
6010
6316
|
import { BadRequestError as BadRequestError28, logger as logger30 } from "@7365admin1/node-server-utils";
|
|
6011
|
-
import { AppServiceType as
|
|
6317
|
+
import { AppServiceType as AppServiceType17 } from "@7365admin1/core";
|
|
6012
6318
|
import Joi16 from "joi";
|
|
6013
6319
|
import { ObjectId as ObjectId18 } from "mongodb";
|
|
6014
6320
|
var scheduleTaskSchema = Joi16.object({
|
|
6015
6321
|
site: Joi16.string().hex().required(),
|
|
6016
|
-
serviceType: Joi16.string().valid(...Object.values(
|
|
6322
|
+
serviceType: Joi16.string().valid(...Object.values(AppServiceType17)).required(),
|
|
6017
6323
|
title: Joi16.string().required(),
|
|
6018
6324
|
time: Joi16.string().pattern(/^([0-1]\d|2[0-3]):([0-5]\d)$/).required(),
|
|
6019
6325
|
dates: Joi16.array().min(1).items(
|
|
@@ -6072,7 +6378,7 @@ import { ObjectId as ObjectId19 } from "mongodb";
|
|
|
6072
6378
|
import {
|
|
6073
6379
|
useAtlas as useAtlas14,
|
|
6074
6380
|
InternalServerError as InternalServerError11,
|
|
6075
|
-
paginate as
|
|
6381
|
+
paginate as paginate9,
|
|
6076
6382
|
BadRequestError as BadRequestError29,
|
|
6077
6383
|
useCache as useCache9,
|
|
6078
6384
|
logger as logger31,
|
|
@@ -6175,7 +6481,7 @@ function useScheduleTaskRepository() {
|
|
|
6175
6481
|
{ $limit: limit }
|
|
6176
6482
|
]).toArray();
|
|
6177
6483
|
const length = await collection.countDocuments(query);
|
|
6178
|
-
const data =
|
|
6484
|
+
const data = paginate9(items, page, limit, length);
|
|
6179
6485
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
6180
6486
|
logger31.info(`Cache set for key: ${cacheKey}`);
|
|
6181
6487
|
}).catch((err) => {
|
|
@@ -6628,7 +6934,7 @@ function useScheduleTaskService() {
|
|
|
6628
6934
|
// src/controllers/hygiene-schedule-task.controller.ts
|
|
6629
6935
|
import Joi17 from "joi";
|
|
6630
6936
|
import { BadRequestError as BadRequestError30, logger as logger33 } from "@7365admin1/node-server-utils";
|
|
6631
|
-
import { AppServiceType as
|
|
6937
|
+
import { AppServiceType as AppServiceType18 } from "@7365admin1/core";
|
|
6632
6938
|
function useScheduleTaskController() {
|
|
6633
6939
|
const {
|
|
6634
6940
|
createScheduleTask: _createScheduleTask,
|
|
@@ -6662,7 +6968,7 @@ function useScheduleTaskController() {
|
|
|
6662
6968
|
limit: Joi17.number().min(1).optional().allow("", null),
|
|
6663
6969
|
search: Joi17.string().optional().allow("", null),
|
|
6664
6970
|
site: Joi17.string().hex().required(),
|
|
6665
|
-
serviceType: Joi17.string().valid(...Object.values(
|
|
6971
|
+
serviceType: Joi17.string().valid(...Object.values(AppServiceType18)).required()
|
|
6666
6972
|
});
|
|
6667
6973
|
const { error } = validation.validate(query);
|
|
6668
6974
|
if (error) {
|