@iservice365/module-hygiene 0.0.1 → 0.1.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.mjs CHANGED
@@ -1 +1,2781 @@
1
+ // src/models/hygiene-area.model.ts
2
+ import { BadRequestError, logger } from "@iservice365/node-server-utils";
3
+ import Joi from "joi";
4
+ import { ObjectId } from "mongodb";
5
+ var areaSchema = Joi.object({
6
+ name: Joi.string().required(),
7
+ site: Joi.string().hex().required(),
8
+ createdBy: Joi.string().hex().required(),
9
+ checklist: Joi.array().items(
10
+ Joi.object({
11
+ _id: Joi.string().hex().required(),
12
+ name: Joi.string().required()
13
+ })
14
+ ).optional()
15
+ });
16
+ function MArea(value) {
17
+ const { error } = areaSchema.validate(value);
18
+ if (error) {
19
+ logger.info(`Hygiene Area Model: ${error.message}`);
20
+ throw new BadRequestError(error.message);
21
+ }
22
+ if (value.site) {
23
+ try {
24
+ value.site = new ObjectId(value.site);
25
+ } catch (error2) {
26
+ throw new BadRequestError("Invalid site ID format.");
27
+ }
28
+ }
29
+ if (value.createdBy) {
30
+ try {
31
+ value.createdBy = new ObjectId(value.createdBy);
32
+ } catch (error2) {
33
+ throw new BadRequestError("Invalid createdBy ID format.");
34
+ }
35
+ }
36
+ if (value.checklist && Array.isArray(value.checklist)) {
37
+ value.checklist = value.checklist.map((item) => {
38
+ try {
39
+ return {
40
+ ...item,
41
+ _id: new ObjectId(item._id)
42
+ };
43
+ } catch (error2) {
44
+ throw new BadRequestError(
45
+ `Invalid checklist item ID format: ${item._id}`
46
+ );
47
+ }
48
+ });
49
+ }
50
+ return {
51
+ name: value.name,
52
+ site: value.site,
53
+ createdBy: value.createdBy,
54
+ checklist: value.checklist,
55
+ status: value.status ?? "active",
56
+ createdAt: /* @__PURE__ */ new Date(),
57
+ updatedAt: value.updatedAt ?? "",
58
+ deletedAt: value.deletedAt ?? ""
59
+ };
60
+ }
61
+
62
+ // src/repositories/hygiene-area.repository.ts
63
+ import { ObjectId as ObjectId2 } from "mongodb";
64
+ import {
65
+ useAtlas,
66
+ InternalServerError,
67
+ paginate,
68
+ BadRequestError as BadRequestError2,
69
+ useCache,
70
+ logger as logger2,
71
+ makeCacheKey,
72
+ NotFoundError
73
+ } from "@iservice365/node-server-utils";
74
+ function useAreaRepository() {
75
+ const db = useAtlas.getDb();
76
+ if (!db) {
77
+ throw new InternalServerError("Unable to connect to server.");
78
+ }
79
+ const namespace_collection = "hygiene-areas";
80
+ const collection = db.collection(namespace_collection);
81
+ const { delNamespace, setCache, getCache } = useCache(namespace_collection);
82
+ async function createIndex() {
83
+ try {
84
+ await collection.createIndexes([
85
+ { key: { site: 1 } },
86
+ { key: { createdBy: 1 } },
87
+ { key: { status: 1 } }
88
+ ]);
89
+ } catch (error) {
90
+ throw new InternalServerError("Failed to create index on hygiene area.");
91
+ }
92
+ }
93
+ async function createTextIndex() {
94
+ try {
95
+ await collection.createIndex({ name: "text" });
96
+ } catch (error) {
97
+ throw new InternalServerError(
98
+ "Failed to create text index on hygiene area."
99
+ );
100
+ }
101
+ }
102
+ async function createUniqueIndex() {
103
+ try {
104
+ await collection.createIndex(
105
+ { name: 1, site: 1, deletedAt: 1 },
106
+ { unique: true }
107
+ );
108
+ } catch (error) {
109
+ throw new InternalServerError(
110
+ "Failed to create unique index on hygiene area."
111
+ );
112
+ }
113
+ }
114
+ async function createArea(value, session) {
115
+ try {
116
+ value = MArea(value);
117
+ const res = await collection.insertOne(value, { session });
118
+ delNamespace().then(() => {
119
+ logger2.info(`Cache cleared for namespace: ${namespace_collection}`);
120
+ }).catch((err) => {
121
+ logger2.error(
122
+ `Failed to clear cache for namespace: ${namespace_collection}`,
123
+ err
124
+ );
125
+ });
126
+ return res.insertedId;
127
+ } catch (error) {
128
+ const isDuplicated = error.message.includes("duplicate");
129
+ if (isDuplicated) {
130
+ throw new BadRequestError2("Area already exists.");
131
+ }
132
+ throw error;
133
+ }
134
+ }
135
+ async function getAreas({
136
+ page = 1,
137
+ limit = 10,
138
+ search = "",
139
+ startDate = "",
140
+ endDate = "",
141
+ site = ""
142
+ }) {
143
+ page = page > 0 ? page - 1 : 0;
144
+ const query = {
145
+ status: { $ne: "deleted" }
146
+ };
147
+ const cacheOptions = {
148
+ page,
149
+ limit
150
+ };
151
+ if (site) {
152
+ try {
153
+ site = new ObjectId2(site);
154
+ cacheOptions.site = site.toString();
155
+ } catch (error) {
156
+ throw new BadRequestError2("Invalid site ID format.");
157
+ }
158
+ }
159
+ if (search) {
160
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
161
+ cacheOptions.search = search;
162
+ }
163
+ if (startDate && endDate) {
164
+ query.createdAt = {
165
+ $gte: new Date(startDate),
166
+ $lte: new Date(endDate)
167
+ };
168
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
169
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
170
+ } else if (startDate) {
171
+ query.createdAt = { $gte: new Date(startDate) };
172
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
173
+ } else if (endDate) {
174
+ query.createdAt = { $lte: new Date(endDate) };
175
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
176
+ }
177
+ const cacheKey = makeCacheKey(namespace_collection, cacheOptions);
178
+ const cachedData = await getCache(cacheKey);
179
+ if (cachedData) {
180
+ logger2.info(`Cache hit for key: ${cacheKey}`);
181
+ return cachedData;
182
+ }
183
+ try {
184
+ const items = await collection.aggregate([
185
+ { $match: query },
186
+ {
187
+ $lookup: {
188
+ from: "sites",
189
+ localField: "site",
190
+ foreignField: "_id",
191
+ pipeline: [{ $project: { name: 1 } }],
192
+ as: "site"
193
+ }
194
+ },
195
+ {
196
+ $unwind: {
197
+ path: "$site",
198
+ preserveNullAndEmptyArrays: true
199
+ }
200
+ },
201
+ {
202
+ $lookup: {
203
+ from: "users",
204
+ localField: "createdBy",
205
+ foreignField: "_id",
206
+ pipeline: [{ $project: { name: 1 } }],
207
+ as: "createdBy"
208
+ }
209
+ },
210
+ {
211
+ $unwind: {
212
+ path: "$createdBy",
213
+ preserveNullAndEmptyArrays: true
214
+ }
215
+ },
216
+ {
217
+ $project: {
218
+ name: 1,
219
+ site: "$site._id",
220
+ siteName: "$site.name",
221
+ createdByName: "$createdBy.name",
222
+ checklist: 1,
223
+ status: 1,
224
+ createdAt: 1
225
+ }
226
+ },
227
+ { $sort: { _id: -1 } },
228
+ { $skip: page * limit },
229
+ { $limit: limit }
230
+ ]).toArray();
231
+ const length = await collection.countDocuments(query);
232
+ const data = paginate(items, page, limit, length);
233
+ setCache(cacheKey, data, 15 * 60).then(() => {
234
+ logger2.info(`Cache set for key: ${cacheKey}`);
235
+ }).catch((err) => {
236
+ logger2.error(`Failed to set cache for key: ${cacheKey}`, err);
237
+ });
238
+ return data;
239
+ } catch (error) {
240
+ throw error;
241
+ }
242
+ }
243
+ async function getAreaById(_id) {
244
+ try {
245
+ _id = new ObjectId2(_id);
246
+ } catch (error) {
247
+ throw new BadRequestError2("Invalid area ID format.");
248
+ }
249
+ const query = {
250
+ _id,
251
+ status: { $ne: "deleted" }
252
+ };
253
+ const cacheKey = makeCacheKey(namespace_collection, {
254
+ _id: _id.toString()
255
+ });
256
+ const cachedData = await getCache(cacheKey);
257
+ if (cachedData) {
258
+ logger2.info(`Cache hit for key: ${cacheKey}`);
259
+ return cachedData;
260
+ }
261
+ try {
262
+ const data = await collection.aggregate([
263
+ { $match: query },
264
+ {
265
+ $project: {
266
+ name: 1,
267
+ checklist: 1
268
+ }
269
+ }
270
+ ]).toArray();
271
+ if (!data || !data.length) {
272
+ throw new NotFoundError("Area not found.");
273
+ }
274
+ setCache(cacheKey, data[0], 15 * 60).then(() => {
275
+ logger2.info(`Cache set for key: ${cacheKey}`);
276
+ }).catch((err) => {
277
+ logger2.error(`Failed to set cache for key: ${cacheKey}`, err);
278
+ });
279
+ return data[0];
280
+ } catch (error) {
281
+ throw error;
282
+ }
283
+ }
284
+ async function getAreaByName(name, site) {
285
+ try {
286
+ if (site)
287
+ site = new ObjectId2(site);
288
+ } catch (error) {
289
+ throw new BadRequestError2("Invalid site ID format.");
290
+ }
291
+ try {
292
+ return await collection.findOne({
293
+ name: { $regex: new RegExp(`^${name}$`, "i") },
294
+ ...site && { site }
295
+ });
296
+ } catch (error) {
297
+ throw new BadRequestError2("Unable to fetch area by name.");
298
+ }
299
+ }
300
+ async function updateArea(_id, value) {
301
+ try {
302
+ _id = new ObjectId2(_id);
303
+ } catch (error) {
304
+ throw new BadRequestError2("Invalid area ID format.");
305
+ }
306
+ try {
307
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
308
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
309
+ if (res.modifiedCount === 0) {
310
+ throw new InternalServerError("Unable to update cleaning area.");
311
+ }
312
+ delNamespace().then(() => {
313
+ logger2.info(`Cache cleared for namespace: ${namespace_collection}`);
314
+ }).catch((err) => {
315
+ logger2.error(
316
+ `Failed to clear cache for namespace: ${namespace_collection}`,
317
+ err
318
+ );
319
+ });
320
+ return res.modifiedCount;
321
+ } catch (error) {
322
+ const isDuplicated = error.message.includes("duplicate");
323
+ if (isDuplicated) {
324
+ throw new BadRequestError2("Area already exists.");
325
+ }
326
+ throw error;
327
+ }
328
+ }
329
+ async function updateAreaChecklist(_id, value) {
330
+ try {
331
+ _id = new ObjectId2(_id);
332
+ } catch (error) {
333
+ throw new BadRequestError2("Invalid area ID format.");
334
+ }
335
+ if (value.checklist && Array.isArray(value.checklist)) {
336
+ value.checklist = value.checklist.map((item) => {
337
+ try {
338
+ return {
339
+ ...item,
340
+ _id: new ObjectId2(item._id)
341
+ };
342
+ } catch (error) {
343
+ throw new BadRequestError2(
344
+ `Invalid checklist item ID format: ${item._id}`
345
+ );
346
+ }
347
+ });
348
+ }
349
+ try {
350
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
351
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
352
+ if (res.modifiedCount === 0) {
353
+ throw new InternalServerError("Unable to update cleaning area.");
354
+ }
355
+ delNamespace().then(() => {
356
+ logger2.info(`Cache cleared for namespace: ${namespace_collection}`);
357
+ }).catch((err) => {
358
+ logger2.error(
359
+ `Failed to clear cache for namespace: ${namespace_collection}`,
360
+ err
361
+ );
362
+ });
363
+ return res.modifiedCount;
364
+ } catch (error) {
365
+ throw error;
366
+ }
367
+ }
368
+ async function deleteArea(_id, session) {
369
+ try {
370
+ _id = new ObjectId2(_id);
371
+ } catch (error) {
372
+ throw new BadRequestError2("Invalid area ID format.");
373
+ }
374
+ try {
375
+ const updateValue = {
376
+ status: "deleted",
377
+ updatedAt: /* @__PURE__ */ new Date(),
378
+ deletedAt: /* @__PURE__ */ new Date()
379
+ };
380
+ const res = await collection.updateOne(
381
+ { _id },
382
+ { $set: updateValue },
383
+ { session }
384
+ );
385
+ if (res.modifiedCount === 0) {
386
+ throw new InternalServerError("Unable to delete area.");
387
+ }
388
+ delNamespace().then(() => {
389
+ logger2.info(`Cache cleared for namespace: ${namespace_collection}`);
390
+ }).catch((err) => {
391
+ logger2.error(
392
+ `Failed to clear cache for namespace: ${namespace_collection}`,
393
+ err
394
+ );
395
+ });
396
+ return res.modifiedCount;
397
+ } catch (error) {
398
+ throw error;
399
+ }
400
+ }
401
+ return {
402
+ createIndex,
403
+ createTextIndex,
404
+ createUniqueIndex,
405
+ createArea,
406
+ getAreas,
407
+ getAreaById,
408
+ getAreaByName,
409
+ updateArea,
410
+ updateAreaChecklist,
411
+ deleteArea
412
+ };
413
+ }
414
+
415
+ // src/services/hygiene-area.service.ts
416
+ import {
417
+ BadRequestError as BadRequestError3,
418
+ logger as logger3,
419
+ NotFoundError as NotFoundError2,
420
+ useAtlas as useAtlas2
421
+ } from "@iservice365/node-server-utils";
422
+ function useAreaService() {
423
+ const { createArea: _createArea } = useAreaRepository();
424
+ async function uploadByFile({
425
+ dataJson,
426
+ createdBy,
427
+ site
428
+ }) {
429
+ let dataArray;
430
+ try {
431
+ dataArray = JSON.parse(dataJson);
432
+ } catch (error) {
433
+ throw new BadRequestError3("Invalid JSON format for data in excel");
434
+ }
435
+ if (!dataArray || dataArray.length === 0) {
436
+ throw new NotFoundError2("No data found in the uploaded file");
437
+ }
438
+ const session = useAtlas2.getClient()?.startSession();
439
+ const insertedAreaIds = [];
440
+ try {
441
+ session?.startTransaction();
442
+ for (const row of dataArray) {
443
+ if (!row?.AREA_NAME) {
444
+ logger3.warn("Skipping row with missing AREA_NAME:", row);
445
+ continue;
446
+ }
447
+ try {
448
+ const insertedId = await _createArea(
449
+ {
450
+ name: String(row.AREA_NAME).trim(),
451
+ site,
452
+ createdBy
453
+ },
454
+ session
455
+ );
456
+ insertedAreaIds.push(insertedId);
457
+ } catch (error) {
458
+ logger3.error(
459
+ `Error creating area "${row.AREA_NAME}":`,
460
+ error.message
461
+ );
462
+ continue;
463
+ }
464
+ }
465
+ await session?.commitTransaction();
466
+ logger3.info(`Successfully uploaded ${insertedAreaIds.length} areas`);
467
+ return {
468
+ message: `Successfully uploaded ${insertedAreaIds.length} areas`
469
+ };
470
+ } catch (error) {
471
+ await session?.abortTransaction();
472
+ logger3.error("Error while uploading area information", error);
473
+ throw error;
474
+ } finally {
475
+ session?.endSession();
476
+ }
477
+ }
478
+ return {
479
+ uploadByFile
480
+ };
481
+ }
482
+
483
+ // src/controllers/hygiene-area.controller.ts
484
+ import { BadRequestError as BadRequestError4, logger as logger4 } from "@iservice365/node-server-utils";
485
+ import Joi2 from "joi";
486
+
487
+ // src/utils/convert-excel.util.ts
488
+ import { Readable } from "stream";
489
+ import * as xlsx from "xlsx";
490
+ function convertBufferFile(bufferFile) {
491
+ return new Promise((resolve, reject) => {
492
+ const fileStream = Readable.from(bufferFile);
493
+ let fileBuffer = Buffer.alloc(0);
494
+ fileStream.on("data", (chunk) => {
495
+ fileBuffer = Buffer.concat([fileBuffer, chunk]);
496
+ });
497
+ fileStream.on("end", () => {
498
+ try {
499
+ const workbook = xlsx.read(fileBuffer, { type: "buffer" });
500
+ const sheetName = workbook.SheetNames[0];
501
+ const sheet = workbook.Sheets[sheetName];
502
+ const jsonData = xlsx.utils.sheet_to_json(sheet);
503
+ resolve(jsonData);
504
+ } catch (error) {
505
+ reject("Error parsing file");
506
+ }
507
+ });
508
+ fileStream.on("error", (err) => {
509
+ reject("Error Reading File: " + err.message);
510
+ });
511
+ });
512
+ }
513
+
514
+ // src/controllers/hygiene-area.controller.ts
515
+ function useAreaController() {
516
+ const {
517
+ createArea: _createArea,
518
+ getAreas: _getAll,
519
+ getAreaById: _getAreaById,
520
+ updateArea: _updateArea,
521
+ updateAreaChecklist: _updateAreaChecklist,
522
+ deleteArea: _deleteById
523
+ } = useAreaRepository();
524
+ const { uploadByFile: _uploadByFile } = useAreaService();
525
+ async function createArea(req, res, next) {
526
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
527
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
528
+ {}
529
+ ) : {};
530
+ const createdBy = cookies["user"] || "";
531
+ const payload = { ...req.body, createdBy };
532
+ const { error } = areaSchema.validate(payload);
533
+ if (error) {
534
+ logger4.log({ level: "error", message: error.message });
535
+ next(new BadRequestError4(error.message));
536
+ return;
537
+ }
538
+ try {
539
+ const id = await _createArea(payload);
540
+ res.status(201).json({ message: "Area created successfully.", id });
541
+ return;
542
+ } catch (error2) {
543
+ logger4.log({ level: "error", message: error2.message });
544
+ next(error2);
545
+ return;
546
+ }
547
+ }
548
+ async function getAll(req, res, next) {
549
+ const query = req.query;
550
+ const validation = Joi2.object({
551
+ page: Joi2.number().min(1).optional().allow("", null),
552
+ limit: Joi2.number().min(1).optional().allow("", null),
553
+ search: Joi2.string().optional().allow("", null),
554
+ startDate: Joi2.alternatives().try(Joi2.date(), Joi2.string()).optional().allow("", null),
555
+ endDate: Joi2.alternatives().try(Joi2.date(), Joi2.string()).optional().allow("", null),
556
+ site: Joi2.string().hex().optional().allow("", null)
557
+ });
558
+ const { error } = validation.validate(query);
559
+ if (error) {
560
+ next(new BadRequestError4(error.message));
561
+ return;
562
+ }
563
+ const page = parseInt(req.query.page) ?? 1;
564
+ const limit = parseInt(req.query.limit) ?? 20;
565
+ const search = req.query.search ?? "";
566
+ const site = req.query.site ?? "";
567
+ const startDate = req.query.startDate ?? "";
568
+ const endDate = req.query.endDate ?? "";
569
+ try {
570
+ const data = await _getAll({
571
+ page,
572
+ limit,
573
+ search,
574
+ site,
575
+ startDate,
576
+ endDate
577
+ });
578
+ res.json(data);
579
+ return;
580
+ } catch (error2) {
581
+ next(error2);
582
+ return;
583
+ }
584
+ }
585
+ async function getAreaById(req, res, next) {
586
+ const validation = Joi2.string().hex().required();
587
+ const _id = req.params.id;
588
+ const { error } = validation.validate(_id);
589
+ if (error) {
590
+ logger4.log({ level: "error", message: error.message });
591
+ next(new BadRequestError4(error.message));
592
+ return;
593
+ }
594
+ try {
595
+ const data = await _getAreaById(_id);
596
+ res.json(data);
597
+ return;
598
+ } catch (error2) {
599
+ logger4.log({ level: "error", message: error2.message });
600
+ next(error2);
601
+ return;
602
+ }
603
+ }
604
+ async function updateArea(req, res, next) {
605
+ const payload = { id: req.params.id, ...req.body };
606
+ const schema = Joi2.object({
607
+ id: Joi2.string().hex().required(),
608
+ name: Joi2.string().required()
609
+ });
610
+ const { error } = schema.validate(payload);
611
+ if (error) {
612
+ logger4.log({ level: "error", message: error.message });
613
+ next(new BadRequestError4(error.message));
614
+ return;
615
+ }
616
+ try {
617
+ const { id, ...value } = payload;
618
+ await _updateArea(id, value);
619
+ res.json({ message: "Area updated successfully." });
620
+ return;
621
+ } catch (error2) {
622
+ logger4.log({ level: "error", message: error2.message });
623
+ next(error2);
624
+ return;
625
+ }
626
+ }
627
+ async function updateAreaChecklist(req, res, next) {
628
+ const payload = { id: req.params.id, ...req.body };
629
+ const schema = Joi2.object({
630
+ id: Joi2.string().hex().required(),
631
+ checklist: Joi2.array().items(
632
+ Joi2.object({
633
+ _id: Joi2.string().hex().required(),
634
+ name: Joi2.string().required()
635
+ }).required()
636
+ ).min(1).unique("_id", { ignoreUndefined: true }).messages({
637
+ "array.unique": "Duplicate checklist items are not allowed"
638
+ })
639
+ });
640
+ const { error } = schema.validate(payload);
641
+ if (error) {
642
+ logger4.log({ level: "error", message: error.message });
643
+ next(new BadRequestError4(error.message));
644
+ return;
645
+ }
646
+ try {
647
+ const { id, ...value } = payload;
648
+ await _updateAreaChecklist(id, value);
649
+ res.json({ message: "Area updated successfully." });
650
+ return;
651
+ } catch (error2) {
652
+ logger4.log({ level: "error", message: error2.message });
653
+ next(error2);
654
+ return;
655
+ }
656
+ }
657
+ async function deleteArea(req, res, next) {
658
+ const id = req.params.id;
659
+ const validation = Joi2.object({
660
+ id: Joi2.string().hex().required()
661
+ });
662
+ const { error } = validation.validate({ id });
663
+ if (error) {
664
+ logger4.log({ level: "error", message: error.message });
665
+ next(new BadRequestError4(error.message));
666
+ return;
667
+ }
668
+ try {
669
+ await _deleteById(id);
670
+ res.json({ message: "Area deleted successfully." });
671
+ return;
672
+ } catch (error2) {
673
+ logger4.log({ level: "error", message: error2.message });
674
+ next(error2);
675
+ return;
676
+ }
677
+ }
678
+ async function uploadByFile(req, res, next) {
679
+ if (!req.file) {
680
+ next(new BadRequestError4("File is required!"));
681
+ return;
682
+ }
683
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
684
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
685
+ {}
686
+ ) : {};
687
+ const createdBy = cookies["user"] || "";
688
+ const { site } = req.body;
689
+ const schema = Joi2.object({
690
+ site: Joi2.string().hex().optional().allow("", null),
691
+ createdBy: Joi2.string().hex().required()
692
+ });
693
+ const { error } = schema.validate({ site, createdBy });
694
+ if (error) {
695
+ logger4.log({ level: "error", message: error.message });
696
+ next(new BadRequestError4(error.message));
697
+ return;
698
+ }
699
+ try {
700
+ const xlsData = await convertBufferFile(req.file.buffer);
701
+ const dataJson = JSON.stringify(xlsData);
702
+ const result = await _uploadByFile({ dataJson, createdBy, site });
703
+ return res.status(201).json(result);
704
+ } catch (error2) {
705
+ logger4.log({ level: "error", message: error2.message });
706
+ next(error2);
707
+ return;
708
+ }
709
+ }
710
+ return {
711
+ createArea,
712
+ getAll,
713
+ getAreaById,
714
+ updateArea,
715
+ updateAreaChecklist,
716
+ deleteArea,
717
+ uploadByFile
718
+ };
719
+ }
720
+
721
+ // src/models/hygiene-toilet-location.model.ts
722
+ import { BadRequestError as BadRequestError5 } from "@iservice365/node-server-utils";
723
+ import Joi3 from "joi";
724
+ import { ObjectId as ObjectId3 } from "mongodb";
725
+ var toiletLocationSchema = Joi3.object({
726
+ name: Joi3.string().required(),
727
+ site: Joi3.string().hex().required(),
728
+ createdBy: Joi3.string().hex().required(),
729
+ checklist: Joi3.array().items(
730
+ Joi3.object({
731
+ _id: Joi3.string().hex().required(),
732
+ name: Joi3.string().required()
733
+ })
734
+ ).optional(),
735
+ updatedAt: Joi3.date().optional().allow("", null),
736
+ status: Joi3.string().allow("", null).optional()
737
+ });
738
+ function MToiletLocation(value) {
739
+ const { error } = toiletLocationSchema.validate(value);
740
+ if (error) {
741
+ throw new BadRequestError5(error.message);
742
+ }
743
+ if (value.site) {
744
+ try {
745
+ value.site = new ObjectId3(value.site);
746
+ } catch (error2) {
747
+ throw new BadRequestError5("Invalid site ID format.");
748
+ }
749
+ }
750
+ if (value.createdBy) {
751
+ try {
752
+ value.createdBy = new ObjectId3(value.createdBy);
753
+ } catch (error2) {
754
+ throw new BadRequestError5("Invalid createdBy ID format.");
755
+ }
756
+ }
757
+ if (value.checklist && Array.isArray(value.checklist)) {
758
+ value.checklist = value.checklist.map((item) => {
759
+ try {
760
+ return {
761
+ ...item,
762
+ _id: new ObjectId3(item._id)
763
+ };
764
+ } catch (error2) {
765
+ throw new BadRequestError5(
766
+ `Invalid checklist item ID format: ${item._id}`
767
+ );
768
+ }
769
+ });
770
+ }
771
+ return {
772
+ name: value.name,
773
+ site: value.site,
774
+ createdBy: value.createdBy,
775
+ checklist: value.checklist,
776
+ status: value.status ?? "",
777
+ createdAt: /* @__PURE__ */ new Date(),
778
+ updatedAt: value.updatedAt ?? "",
779
+ deletedAt: value.deletedAt ?? ""
780
+ };
781
+ }
782
+
783
+ // src/repositories/hygiene-toilet-location.repository.ts
784
+ import { ObjectId as ObjectId4 } from "mongodb";
785
+ import {
786
+ useAtlas as useAtlas3,
787
+ InternalServerError as InternalServerError2,
788
+ paginate as paginate2,
789
+ BadRequestError as BadRequestError6,
790
+ NotFoundError as NotFoundError3,
791
+ useCache as useCache2,
792
+ logger as logger5,
793
+ makeCacheKey as makeCacheKey2
794
+ } from "@iservice365/node-server-utils";
795
+ function useToiletLocationRepository() {
796
+ const db = useAtlas3.getDb();
797
+ if (!db) {
798
+ throw new InternalServerError2("Unable to connect to server.");
799
+ }
800
+ const namespace_collection = "hygiene-toilet-locations";
801
+ const collection = db.collection(namespace_collection);
802
+ const { delNamespace, setCache, getCache, delCache } = useCache2(namespace_collection);
803
+ async function createIndexes() {
804
+ try {
805
+ await collection.createIndexes([
806
+ { key: { site: 1 } },
807
+ { key: { name: "text" } }
808
+ ]);
809
+ } catch (error) {
810
+ throw new InternalServerError2("Failed to create index on site.");
811
+ }
812
+ }
813
+ async function createUniqueIndex() {
814
+ try {
815
+ await collection.createIndex(
816
+ { name: 1, site: 1, deletedAt: 1 },
817
+ { unique: true }
818
+ );
819
+ } catch (error) {
820
+ throw new InternalServerError2(
821
+ "Failed to create unique index on hygiene area."
822
+ );
823
+ }
824
+ }
825
+ async function create(value, session) {
826
+ try {
827
+ value = MToiletLocation(value);
828
+ const res = await collection.insertOne(value, { session });
829
+ delNamespace().then(() => {
830
+ logger5.info(`Cache cleared for namespace: ${namespace_collection}`);
831
+ }).catch((err) => {
832
+ logger5.error(
833
+ `Failed to clear cache for namespace: ${namespace_collection}`,
834
+ err
835
+ );
836
+ });
837
+ return res.insertedId;
838
+ } catch (error) {
839
+ const isDuplicated = error.message.includes("duplicate");
840
+ if (isDuplicated) {
841
+ throw new BadRequestError6("Toilet location already exists.");
842
+ }
843
+ throw error;
844
+ }
845
+ }
846
+ async function updateToiletLocation(_id, value) {
847
+ try {
848
+ _id = new ObjectId4(_id);
849
+ } catch (error) {
850
+ throw new BadRequestError6("Invalid area ID format.");
851
+ }
852
+ if (value.checklist && Array.isArray(value.checklist)) {
853
+ value.checklist = value.checklist.map((item) => {
854
+ try {
855
+ return {
856
+ ...item,
857
+ _id: new ObjectId4(item._id)
858
+ };
859
+ } catch (error) {
860
+ throw new BadRequestError6(
861
+ `Invalid checklist item ID format: ${item._id}`
862
+ );
863
+ }
864
+ });
865
+ }
866
+ try {
867
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
868
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
869
+ if (res.modifiedCount === 0) {
870
+ throw new InternalServerError2("Unable to update toilet location.");
871
+ }
872
+ delNamespace().then(() => {
873
+ logger5.info(`Cache cleared for namespace: ${namespace_collection}`);
874
+ }).catch((err) => {
875
+ logger5.error(
876
+ `Failed to clear cache for namespace: ${namespace_collection}`,
877
+ err
878
+ );
879
+ });
880
+ return res.modifiedCount;
881
+ } catch (error) {
882
+ const isDuplicated = error.message.includes("duplicate");
883
+ if (isDuplicated) {
884
+ throw new BadRequestError6("Toilet location already exists.");
885
+ }
886
+ throw error;
887
+ }
888
+ }
889
+ async function deleteToiletLocation(_id, session) {
890
+ try {
891
+ _id = new ObjectId4(_id);
892
+ } catch (error) {
893
+ throw new BadRequestError6("Invalid area ID format.");
894
+ }
895
+ try {
896
+ const updateValue = {
897
+ status: "deleted",
898
+ updatedAt: /* @__PURE__ */ new Date(),
899
+ deletedAt: /* @__PURE__ */ new Date()
900
+ };
901
+ const res = await collection.updateOne(
902
+ { _id },
903
+ { $set: updateValue },
904
+ { session }
905
+ );
906
+ if (res.modifiedCount === 0) {
907
+ throw new InternalServerError2("Unable to delete toilet location.");
908
+ }
909
+ delNamespace().then(() => {
910
+ logger5.info(`Cache cleared for namespace: ${namespace_collection}`);
911
+ }).catch((err) => {
912
+ logger5.error(
913
+ `Failed to clear cache for namespace: ${namespace_collection}`,
914
+ err
915
+ );
916
+ });
917
+ return res.modifiedCount;
918
+ } catch (error) {
919
+ throw error;
920
+ }
921
+ }
922
+ async function getToiletLocations({
923
+ page = 1,
924
+ limit = 10,
925
+ search = "",
926
+ sort = {},
927
+ startDate = "",
928
+ endDate = "",
929
+ site = ""
930
+ }) {
931
+ page = page > 0 ? page - 1 : 0;
932
+ let dateFilter = {};
933
+ try {
934
+ site = new ObjectId4(site);
935
+ } catch (error) {
936
+ throw new BadRequestError6("Invalid site ID format.");
937
+ }
938
+ const query = {
939
+ status: { $ne: "deleted" }
940
+ };
941
+ const cacheOptions = {
942
+ site: site.toString()
943
+ };
944
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
945
+ cacheOptions.sort = JSON.stringify(sort);
946
+ if (search) {
947
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
948
+ cacheOptions.search = search;
949
+ }
950
+ delNamespace().then(() => {
951
+ logger5.info(`Cache cleared for namespace: ${namespace_collection}`);
952
+ }).catch((err) => {
953
+ logger5.error(
954
+ `Failed to clear cache for namespace: ${namespace_collection}`,
955
+ err
956
+ );
957
+ });
958
+ const cacheKey = makeCacheKey2(namespace_collection, cacheOptions);
959
+ const cachedData = await getCache(cacheKey);
960
+ if (cachedData) {
961
+ logger5.info(`Cache hit for key: ${cacheKey}`);
962
+ return cachedData;
963
+ }
964
+ if (startDate && endDate) {
965
+ dateFilter = {
966
+ createdAt: {
967
+ $gte: new Date(startDate),
968
+ $lte: new Date(endDate)
969
+ }
970
+ };
971
+ } else if (startDate) {
972
+ dateFilter = { createdAt: { $gte: new Date(startDate) } };
973
+ } else if (endDate) {
974
+ dateFilter = { createdAt: { $lte: new Date(endDate) } };
975
+ }
976
+ try {
977
+ const items = await collection.aggregate([
978
+ {
979
+ $match: {
980
+ ...dateFilter,
981
+ ...query
982
+ }
983
+ },
984
+ {
985
+ $lookup: {
986
+ from: "sites",
987
+ localField: "site",
988
+ foreignField: "_id",
989
+ pipeline: [{ $project: { name: 1 } }],
990
+ as: "site"
991
+ }
992
+ },
993
+ {
994
+ $unwind: {
995
+ path: "$site",
996
+ preserveNullAndEmptyArrays: true
997
+ }
998
+ },
999
+ {
1000
+ $lookup: {
1001
+ from: "users",
1002
+ localField: "createdBy",
1003
+ foreignField: "_id",
1004
+ pipeline: [{ $project: { name: 1 } }],
1005
+ as: "createdBy"
1006
+ }
1007
+ },
1008
+ {
1009
+ $unwind: {
1010
+ path: "$createdBy",
1011
+ preserveNullAndEmptyArrays: true
1012
+ }
1013
+ },
1014
+ {
1015
+ $project: {
1016
+ name: 1,
1017
+ site: "$site._id",
1018
+ siteName: "$site.name",
1019
+ createdByName: "$createdBy.name",
1020
+ checklist: 1,
1021
+ status: 1,
1022
+ createdAt: 1
1023
+ }
1024
+ },
1025
+ { $sort: { _id: -1 } },
1026
+ { $skip: page * limit },
1027
+ { $limit: limit }
1028
+ ]).toArray();
1029
+ const length = await collection.countDocuments(query);
1030
+ const data = paginate2(items, page, limit, length);
1031
+ setCache(cacheKey, data, 15 * 60).then(() => {
1032
+ logger5.info(`Cache set for key: ${cacheKey}`);
1033
+ }).catch((err) => {
1034
+ logger5.error(`Failed to set cache for key: ${cacheKey}`, err);
1035
+ });
1036
+ return data;
1037
+ } catch (error) {
1038
+ throw error;
1039
+ }
1040
+ }
1041
+ async function getToiletLocationByName(name, site) {
1042
+ try {
1043
+ if (site)
1044
+ site = new ObjectId4(site);
1045
+ } catch (error) {
1046
+ throw new BadRequestError6("Invalid site ID format.");
1047
+ }
1048
+ try {
1049
+ return await collection.findOne({
1050
+ name: { $regex: new RegExp(`^${name}$`, "i") },
1051
+ ...site && { site }
1052
+ });
1053
+ } catch (error) {
1054
+ throw new BadRequestError6("Unable to fetch toilet location by name.");
1055
+ }
1056
+ }
1057
+ async function updateToiletLocationChecklist(_id, value) {
1058
+ try {
1059
+ _id = new ObjectId4(_id);
1060
+ } catch (error) {
1061
+ throw new BadRequestError6("Invalid area ID format.");
1062
+ }
1063
+ if (value.checklist && Array.isArray(value.checklist)) {
1064
+ value.checklist = value.checklist.map((item) => {
1065
+ try {
1066
+ return {
1067
+ ...item,
1068
+ _id: new ObjectId4(item._id)
1069
+ };
1070
+ } catch (error) {
1071
+ throw new BadRequestError6(
1072
+ `Invalid checklist item ID format: ${item._id}`
1073
+ );
1074
+ }
1075
+ });
1076
+ }
1077
+ try {
1078
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
1079
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
1080
+ if (res.modifiedCount === 0) {
1081
+ throw new InternalServerError2("Unable to update toilet location.");
1082
+ }
1083
+ delNamespace().then(() => {
1084
+ logger5.info(`Cache cleared for namespace: ${namespace_collection}`);
1085
+ }).catch((err) => {
1086
+ logger5.error(
1087
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1088
+ err
1089
+ );
1090
+ });
1091
+ return res.modifiedCount;
1092
+ } catch (error) {
1093
+ throw error;
1094
+ }
1095
+ }
1096
+ async function getToiletLocationById(_id) {
1097
+ try {
1098
+ _id = new ObjectId4(_id);
1099
+ } catch (error) {
1100
+ throw new BadRequestError6("Invalid area ID format.");
1101
+ }
1102
+ const query = {
1103
+ _id,
1104
+ status: { $ne: "deleted" }
1105
+ };
1106
+ const cacheKey = makeCacheKey2(namespace_collection, {
1107
+ _id: _id.toString()
1108
+ });
1109
+ const cachedData = await getCache(cacheKey);
1110
+ if (cachedData) {
1111
+ logger5.info(`Cache hit for key: ${cacheKey}`);
1112
+ return cachedData;
1113
+ }
1114
+ try {
1115
+ const data = await collection.aggregate([
1116
+ { $match: query },
1117
+ {
1118
+ $project: {
1119
+ name: 1,
1120
+ checklist: 1
1121
+ }
1122
+ }
1123
+ ]).toArray();
1124
+ if (!data || !data.length) {
1125
+ throw new NotFoundError3("Area not found.");
1126
+ }
1127
+ setCache(cacheKey, data[0], 15 * 60).then(() => {
1128
+ logger5.info(`Cache set for key: ${cacheKey}`);
1129
+ }).catch((err) => {
1130
+ logger5.error(`Failed to set cache for key: ${cacheKey}`, err);
1131
+ });
1132
+ return data[0];
1133
+ } catch (error) {
1134
+ throw error;
1135
+ }
1136
+ }
1137
+ return {
1138
+ createIndexes,
1139
+ createUniqueIndex,
1140
+ getToiletLocations,
1141
+ create,
1142
+ updateToiletLocation,
1143
+ deleteToiletLocation,
1144
+ getToiletLocationByName,
1145
+ getToiletLocationById,
1146
+ updateToiletLocationChecklist
1147
+ };
1148
+ }
1149
+
1150
+ // src/services/hygiene-toilet-location.service.ts
1151
+ import {
1152
+ BadRequestError as BadRequestError7,
1153
+ logger as logger6,
1154
+ NotFoundError as NotFoundError4,
1155
+ useAtlas as useAtlas4
1156
+ } from "@iservice365/node-server-utils";
1157
+ function useToiletLocationService() {
1158
+ const { create: _createToilet } = useToiletLocationRepository();
1159
+ async function uploadByFile({
1160
+ dataJson,
1161
+ createdBy,
1162
+ site
1163
+ }) {
1164
+ let dataArray;
1165
+ try {
1166
+ dataArray = JSON.parse(dataJson);
1167
+ } catch (error) {
1168
+ throw new BadRequestError7("Invalid JSON format for data in excel");
1169
+ }
1170
+ if (!dataArray || dataArray.length === 0) {
1171
+ throw new NotFoundError4("No data found in the uploaded file");
1172
+ }
1173
+ const session = useAtlas4.getClient()?.startSession();
1174
+ const insertedAreaIds = [];
1175
+ try {
1176
+ session?.startTransaction();
1177
+ for (const row of dataArray) {
1178
+ if (!row?.AREA_NAME) {
1179
+ logger6.warn("Skipping row with missing TOILET NAME:", row);
1180
+ continue;
1181
+ }
1182
+ try {
1183
+ const insertedId = await _createToilet(
1184
+ {
1185
+ name: String(row.TOILET_NAME).trim(),
1186
+ site,
1187
+ createdBy
1188
+ },
1189
+ session
1190
+ );
1191
+ insertedAreaIds.push(insertedId);
1192
+ } catch (error) {
1193
+ logger6.error(
1194
+ `Error creating area "${row.AREA_NAME}":`,
1195
+ error.message
1196
+ );
1197
+ continue;
1198
+ }
1199
+ }
1200
+ await session?.commitTransaction();
1201
+ logger6.info(`Successfully uploaded ${insertedAreaIds.length} areas`);
1202
+ return {
1203
+ message: `Successfully uploaded ${insertedAreaIds.length} areas`
1204
+ };
1205
+ } catch (error) {
1206
+ await session?.abortTransaction();
1207
+ logger6.error("Error while uploading area information", error);
1208
+ throw error;
1209
+ } finally {
1210
+ session?.endSession();
1211
+ }
1212
+ }
1213
+ return {
1214
+ uploadByFile
1215
+ };
1216
+ }
1217
+
1218
+ // src/controllers/hygiene-toilet-location.controller.ts
1219
+ import Joi4 from "joi";
1220
+ import { BadRequestError as BadRequestError8, logger as logger7 } from "@iservice365/node-server-utils";
1221
+ function useToiletLocationController() {
1222
+ const { uploadByFile: _uploadByFile } = useToiletLocationService();
1223
+ const {
1224
+ create: _createToiletLocation,
1225
+ updateToiletLocation: _updateToiletLocation,
1226
+ updateToiletLocationChecklist: _updateToiletLocationChecklist,
1227
+ getToiletLocationById: _getToiletLocationById,
1228
+ getToiletLocations: _getAll,
1229
+ deleteToiletLocation: _deleteById
1230
+ } = useToiletLocationRepository();
1231
+ async function getAll(req, res, next) {
1232
+ const query = req.query;
1233
+ const validation = Joi4.object({
1234
+ page: Joi4.number().min(1).optional().allow("", null),
1235
+ limit: Joi4.number().min(1).optional().allow("", null),
1236
+ search: Joi4.string().optional().allow("", null),
1237
+ site: Joi4.string().hex().optional().allow("", null)
1238
+ });
1239
+ const { error } = validation.validate(query);
1240
+ if (error) {
1241
+ next(new BadRequestError8(error.message));
1242
+ return;
1243
+ }
1244
+ const page = parseInt(req.query.page) ?? 1;
1245
+ let limit = parseInt(req.query.limit) ?? 20;
1246
+ limit = isNaN(limit) ? 20 : limit;
1247
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
1248
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
1249
+ const sortObj = {};
1250
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
1251
+ sort.forEach((field, index) => {
1252
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
1253
+ });
1254
+ }
1255
+ const site = req.query.site ?? "";
1256
+ const search = req.query.search ?? "";
1257
+ try {
1258
+ const buildings = await _getAll({
1259
+ page,
1260
+ limit,
1261
+ sort: sortObj,
1262
+ site,
1263
+ search
1264
+ });
1265
+ res.json(buildings);
1266
+ return;
1267
+ } catch (error2) {
1268
+ next(error2);
1269
+ }
1270
+ }
1271
+ async function createToiletLocation(req, res, next) {
1272
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
1273
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
1274
+ {}
1275
+ ) : {};
1276
+ const createdBy = cookies["user"] || "";
1277
+ const payload = { ...req.body, createdBy };
1278
+ const schema = Joi4.object({
1279
+ name: Joi4.string().required(),
1280
+ site: Joi4.string().hex().optional().allow("", null),
1281
+ status: Joi4.string().optional().allow("", null),
1282
+ createdBy: Joi4.string().hex().optional().allow("", null)
1283
+ });
1284
+ const { error } = schema.validate(payload);
1285
+ if (error) {
1286
+ next(new BadRequestError8(error.message));
1287
+ logger7.info(`Controller: ${error.message}`);
1288
+ return;
1289
+ }
1290
+ try {
1291
+ const result = await _createToiletLocation(payload);
1292
+ res.status(201).json(result);
1293
+ return;
1294
+ } catch (error2) {
1295
+ next(error2);
1296
+ }
1297
+ }
1298
+ async function updateToiletLocation(req, res, next) {
1299
+ const payload = { id: req.params.id, ...req.body };
1300
+ const schema = Joi4.object({
1301
+ id: Joi4.string().hex().required(),
1302
+ name: Joi4.string().required(),
1303
+ checklist: Joi4.array().items(
1304
+ Joi4.object({
1305
+ _id: Joi4.string().hex().required(),
1306
+ name: Joi4.string().required()
1307
+ }).optional()
1308
+ ),
1309
+ site: Joi4.string().hex().optional().allow("", null),
1310
+ status: Joi4.string().optional().allow("", null)
1311
+ });
1312
+ const { error } = schema.validate(payload);
1313
+ if (error) {
1314
+ logger7.log({ level: "error", message: error.message });
1315
+ next(new BadRequestError8(error.message));
1316
+ return;
1317
+ }
1318
+ try {
1319
+ const { id, ...value } = payload;
1320
+ await _updateToiletLocation(id, value);
1321
+ res.status(201).json({ message: "Toilet location updated successfully." });
1322
+ return;
1323
+ } catch (error2) {
1324
+ logger7.log({ level: "error", message: error2.message });
1325
+ next(error2);
1326
+ }
1327
+ }
1328
+ async function deleteToiletLocation(req, res, next) {
1329
+ const id = req.params.id;
1330
+ const validation = Joi4.object({
1331
+ id: Joi4.string().hex().required()
1332
+ });
1333
+ const { error } = validation.validate({ id });
1334
+ if (error) {
1335
+ next(new BadRequestError8(error.message));
1336
+ return;
1337
+ }
1338
+ try {
1339
+ const message = await _deleteById(id);
1340
+ res.json(message);
1341
+ return;
1342
+ } catch (error2) {
1343
+ next(error2);
1344
+ }
1345
+ }
1346
+ async function updateToiletLocationChecklist(req, res, next) {
1347
+ const payload = { id: req.params.id, ...req.body };
1348
+ const schema = Joi4.object({
1349
+ id: Joi4.string().hex().required(),
1350
+ checklist: Joi4.array().items(
1351
+ Joi4.object({
1352
+ _id: Joi4.string().hex().required(),
1353
+ name: Joi4.string().required()
1354
+ }).required()
1355
+ ).min(1).unique("_id", { ignoreUndefined: true }).messages({
1356
+ "array.unique": "Duplicate checklist items are not allowed"
1357
+ })
1358
+ });
1359
+ const { error } = schema.validate(payload);
1360
+ if (error) {
1361
+ logger7.log({ level: "error", message: error.message });
1362
+ next(new BadRequestError8(error.message));
1363
+ return;
1364
+ }
1365
+ try {
1366
+ const { id, ...value } = payload;
1367
+ await _updateToiletLocationChecklist(id, value);
1368
+ res.json({ message: "Toilet location updated successfully." });
1369
+ return;
1370
+ } catch (error2) {
1371
+ logger7.log({ level: "error", message: error2.message });
1372
+ next(error2);
1373
+ return;
1374
+ }
1375
+ }
1376
+ async function getToiletLocationById(req, res, next) {
1377
+ const validation = Joi4.string().hex().required();
1378
+ const _id = req.params.id;
1379
+ const { error } = validation.validate(_id);
1380
+ if (error) {
1381
+ logger7.log({ level: "error", message: error.message });
1382
+ next(new BadRequestError8(error.message));
1383
+ return;
1384
+ }
1385
+ try {
1386
+ const data = await _getToiletLocationById(_id);
1387
+ res.json(data);
1388
+ return;
1389
+ } catch (error2) {
1390
+ logger7.log({ level: "error", message: error2.message });
1391
+ next(error2);
1392
+ return;
1393
+ }
1394
+ }
1395
+ async function uploadByFile(req, res, next) {
1396
+ if (!req.file) {
1397
+ next(new BadRequestError8("File is required!"));
1398
+ return;
1399
+ }
1400
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
1401
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
1402
+ {}
1403
+ ) : {};
1404
+ const createdBy = cookies["user"] || "";
1405
+ const { site } = req.body;
1406
+ const schema = Joi4.object({
1407
+ site: Joi4.string().hex().optional().allow("", null),
1408
+ createdBy: Joi4.string().hex().required()
1409
+ });
1410
+ const { error } = schema.validate({ site, createdBy });
1411
+ if (error) {
1412
+ logger7.log({ level: "error", message: error.message });
1413
+ next(new BadRequestError8(error.message));
1414
+ return;
1415
+ }
1416
+ try {
1417
+ const xlsData = await convertBufferFile(req.file.buffer);
1418
+ const dataJson = JSON.stringify(xlsData);
1419
+ const result = await _uploadByFile({ dataJson, createdBy, site });
1420
+ return res.status(201).json(result);
1421
+ } catch (error2) {
1422
+ logger7.log({ level: "error", message: error2.message });
1423
+ next(error2);
1424
+ return;
1425
+ }
1426
+ }
1427
+ return {
1428
+ getAll,
1429
+ createToiletLocation,
1430
+ updateToiletLocation,
1431
+ deleteToiletLocation,
1432
+ updateToiletLocationChecklist,
1433
+ getToiletLocationById,
1434
+ uploadByFile
1435
+ };
1436
+ }
1437
+
1438
+ // src/models/hygiene-parent-checklist.model.ts
1439
+ import { BadRequestError as BadRequestError9 } from "@iservice365/node-server-utils";
1440
+ import Joi5 from "joi";
1441
+ var parentChecklistSchema = Joi5.object({
1442
+ date: Joi5.date().required(),
1443
+ status: Joi5.array().items(
1444
+ Joi5.object({
1445
+ site: Joi5.string().hex().required(),
1446
+ status: Joi5.string().required(),
1447
+ completedAt: Joi5.date().required(),
1448
+ type: Joi5.string().required()
1449
+ })
1450
+ ).optional(),
1451
+ updatedAt: Joi5.date().optional().allow("", null)
1452
+ });
1453
+ function MParentChecklist(value) {
1454
+ const { error } = parentChecklistSchema.validate(value);
1455
+ if (error) {
1456
+ throw new BadRequestError9(error.message);
1457
+ }
1458
+ return {
1459
+ date: value.date,
1460
+ status: value.status,
1461
+ createdAt: /* @__PURE__ */ new Date(),
1462
+ updatedAt: value.updatedAt ?? "",
1463
+ deletedAt: value.deletedAt ?? ""
1464
+ };
1465
+ }
1466
+
1467
+ // src/repositories/hygiene-parent-checklist.repository.ts
1468
+ import {
1469
+ useAtlas as useAtlas5,
1470
+ InternalServerError as InternalServerError3,
1471
+ paginate as paginate3,
1472
+ useCache as useCache3,
1473
+ logger as logger8,
1474
+ makeCacheKey as makeCacheKey3
1475
+ } from "@iservice365/node-server-utils";
1476
+ function useParentChecklistRepo() {
1477
+ const db = useAtlas5.getDb();
1478
+ if (!db) {
1479
+ throw new InternalServerError3("Unable to connect to server.");
1480
+ }
1481
+ const namespace_collection = "hygiene-parent-checklist";
1482
+ const collection = db.collection(namespace_collection);
1483
+ const { delNamespace, setCache, getCache, delCache } = useCache3(namespace_collection);
1484
+ async function createIndexes() {
1485
+ try {
1486
+ await collection.createIndexes([
1487
+ { key: { date: "text" } }
1488
+ ]);
1489
+ } catch (error) {
1490
+ throw new InternalServerError3("Failed to create index on site.");
1491
+ }
1492
+ }
1493
+ async function create(value, session) {
1494
+ try {
1495
+ const currentDate = /* @__PURE__ */ new Date();
1496
+ const startOfDay = new Date(currentDate);
1497
+ startOfDay.setDate(currentDate.getDate());
1498
+ startOfDay.setUTCHours(0, 0, 0, 0);
1499
+ const endOfDay = new Date(currentDate);
1500
+ endOfDay.setDate(currentDate.getDate());
1501
+ endOfDay.setUTCHours(23, 59, 59, 999);
1502
+ const checklistRecord = await collection.findOne({
1503
+ date: {
1504
+ $gte: startOfDay,
1505
+ $lte: endOfDay
1506
+ }
1507
+ });
1508
+ delNamespace().then(() => {
1509
+ logger8.info(`Cache cleared for namespace: ${namespace_collection}`);
1510
+ }).catch((err) => {
1511
+ logger8.error(
1512
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1513
+ err
1514
+ );
1515
+ });
1516
+ if (!checklistRecord) {
1517
+ value = MParentChecklist(value);
1518
+ const res = await collection.insertOne(value, { session });
1519
+ return res.insertedId;
1520
+ }
1521
+ return checklistRecord;
1522
+ } catch (error) {
1523
+ throw error;
1524
+ }
1525
+ }
1526
+ async function get({
1527
+ page = 1,
1528
+ limit = 10,
1529
+ search = "",
1530
+ sort = {},
1531
+ startDate = "",
1532
+ endDate = ""
1533
+ }) {
1534
+ page = page > 0 ? page - 1 : 0;
1535
+ let dateFilter = {};
1536
+ const query = {
1537
+ createdAt: {
1538
+ createdAt: {
1539
+ $gte: new Date(startDate),
1540
+ $lte: new Date(endDate)
1541
+ }
1542
+ }
1543
+ };
1544
+ const cacheOptions = {
1545
+ createdAt: {
1546
+ createdAt: {
1547
+ $gte: new Date(startDate),
1548
+ $lte: new Date(endDate)
1549
+ }
1550
+ }
1551
+ };
1552
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
1553
+ cacheOptions.sort = JSON.stringify(sort);
1554
+ if (search) {
1555
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
1556
+ cacheOptions.search = search;
1557
+ }
1558
+ delNamespace().then(() => {
1559
+ logger8.info(`Cache cleared for namespace: ${namespace_collection}`);
1560
+ }).catch((err) => {
1561
+ logger8.error(
1562
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1563
+ err
1564
+ );
1565
+ });
1566
+ const cacheKey = makeCacheKey3(namespace_collection, cacheOptions);
1567
+ const cachedData = await getCache(cacheKey);
1568
+ if (cachedData) {
1569
+ logger8.info(`Cache hit for key: ${cacheKey}`);
1570
+ return cachedData;
1571
+ }
1572
+ if (startDate && endDate) {
1573
+ dateFilter = {
1574
+ createdAt: {
1575
+ $gte: new Date(startDate),
1576
+ $lte: new Date(endDate)
1577
+ }
1578
+ };
1579
+ } else if (startDate) {
1580
+ dateFilter = { createdAt: { $gte: new Date(startDate) } };
1581
+ } else if (endDate) {
1582
+ dateFilter = { createdAt: { $lte: new Date(endDate) } };
1583
+ }
1584
+ try {
1585
+ const items = await collection.aggregate([
1586
+ {
1587
+ $match: {
1588
+ ...dateFilter
1589
+ }
1590
+ },
1591
+ {
1592
+ $facet: {
1593
+ totalCount: [{ $count: "count" }],
1594
+ items: [
1595
+ { $sort: { createdAt: -1 } },
1596
+ { $skip: page * limit },
1597
+ { $limit: limit }
1598
+ ]
1599
+ }
1600
+ }
1601
+ ]).toArray();
1602
+ const length = await collection.countDocuments(dateFilter);
1603
+ const data = paginate3(items, page, limit, length);
1604
+ setCache(cacheKey, data, 15 * 60).then(() => {
1605
+ logger8.info(`Cache set for key: ${cacheKey}`);
1606
+ }).catch((err) => {
1607
+ logger8.error(`Failed to set cache for key: ${cacheKey}`, err);
1608
+ });
1609
+ return data;
1610
+ } catch (error) {
1611
+ throw error;
1612
+ }
1613
+ }
1614
+ return {
1615
+ createIndexes,
1616
+ create,
1617
+ get
1618
+ };
1619
+ }
1620
+
1621
+ // src/controllers/hygiene-parent-checklist.controller.ts
1622
+ import { BadRequestError as BadRequestError11, logger as logger9 } from "@iservice365/node-server-utils";
1623
+ import Joi6 from "joi";
1624
+ function useParentCheckilstController() {
1625
+ const {
1626
+ get: _getAll,
1627
+ create: _create
1628
+ } = useParentChecklistRepo();
1629
+ async function getAll(req, res, next) {
1630
+ const query = req.query;
1631
+ const validation = Joi6.object({
1632
+ page: Joi6.number().min(1).optional().allow("", null),
1633
+ limit: Joi6.number().min(1).optional().allow("", null)
1634
+ });
1635
+ const { error } = validation.validate(query);
1636
+ if (error) {
1637
+ next(new BadRequestError11(error.message));
1638
+ return;
1639
+ }
1640
+ const page = parseInt(req.query.page) ?? 1;
1641
+ let limit = parseInt(req.query.limit) ?? 20;
1642
+ limit = isNaN(limit) ? 20 : limit;
1643
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
1644
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
1645
+ const sortObj = {};
1646
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
1647
+ sort.forEach((field, index) => {
1648
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
1649
+ });
1650
+ }
1651
+ const site = req.query.site ?? "";
1652
+ const search = req.query.search ?? "";
1653
+ try {
1654
+ const buildings = await _getAll({
1655
+ page,
1656
+ limit,
1657
+ sort: sortObj,
1658
+ site,
1659
+ search
1660
+ });
1661
+ res.json(buildings);
1662
+ return;
1663
+ } catch (error2) {
1664
+ next(error2);
1665
+ }
1666
+ }
1667
+ async function create(req, res, next) {
1668
+ const value = req.body;
1669
+ const schema = Joi6.object({
1670
+ date: Joi6.string().required()
1671
+ });
1672
+ const { error } = schema.validate(value);
1673
+ if (error) {
1674
+ next(new BadRequestError11(error.message));
1675
+ logger9.info(`Controller: ${error.message}`);
1676
+ return;
1677
+ }
1678
+ try {
1679
+ const result = await _create(value);
1680
+ res.status(201).json(result);
1681
+ return;
1682
+ } catch (error2) {
1683
+ next(error2);
1684
+ }
1685
+ }
1686
+ return {
1687
+ getAll,
1688
+ create
1689
+ };
1690
+ }
1691
+
1692
+ // src/models/hygiene-unit.model.ts
1693
+ import { BadRequestError as BadRequestError12, logger as logger10 } from "@iservice365/node-server-utils";
1694
+ import Joi7 from "joi";
1695
+ import { ObjectId as ObjectId5 } from "mongodb";
1696
+ var unitSchema = Joi7.object({
1697
+ name: Joi7.string().required(),
1698
+ site: Joi7.string().hex().required(),
1699
+ createdBy: Joi7.string().hex().required()
1700
+ });
1701
+ function MUnit(value) {
1702
+ const { error } = unitSchema.validate(value);
1703
+ if (error) {
1704
+ logger10.info(`Hygiene Unit Model: ${error.message}`);
1705
+ throw new BadRequestError12(error.message);
1706
+ }
1707
+ if (value.site) {
1708
+ try {
1709
+ value.site = new ObjectId5(value.site);
1710
+ } catch (error2) {
1711
+ throw new BadRequestError12("Invalid site ID format.");
1712
+ }
1713
+ }
1714
+ if (value.createdBy) {
1715
+ try {
1716
+ value.createdBy = new ObjectId5(value.createdBy);
1717
+ } catch (error2) {
1718
+ throw new BadRequestError12("Invalid createdBy ID format.");
1719
+ }
1720
+ }
1721
+ return {
1722
+ name: value.name,
1723
+ createdBy: value.createdBy,
1724
+ site: value.site,
1725
+ status: "active",
1726
+ createdAt: /* @__PURE__ */ new Date(),
1727
+ updatedAt: value.updatedAt ?? "",
1728
+ deletedAt: value.deletedAt ?? ""
1729
+ };
1730
+ }
1731
+
1732
+ // src/services/hygiene-unit.service.ts
1733
+ import {
1734
+ BadRequestError as BadRequestError14,
1735
+ logger as logger12,
1736
+ NotFoundError as NotFoundError6,
1737
+ useAtlas as useAtlas7
1738
+ } from "@iservice365/node-server-utils";
1739
+
1740
+ // src/repositories/hygiene-unit.repository.ts
1741
+ import { ObjectId as ObjectId6 } from "mongodb";
1742
+ import {
1743
+ useAtlas as useAtlas6,
1744
+ InternalServerError as InternalServerError4,
1745
+ paginate as paginate4,
1746
+ BadRequestError as BadRequestError13,
1747
+ useCache as useCache4,
1748
+ logger as logger11,
1749
+ makeCacheKey as makeCacheKey4
1750
+ } from "@iservice365/node-server-utils";
1751
+ function useUnitRepository() {
1752
+ const db = useAtlas6.getDb();
1753
+ if (!db) {
1754
+ throw new InternalServerError4("Unable to connect to server.");
1755
+ }
1756
+ const namespace_collection = "hygiene-units";
1757
+ const collection = db.collection(namespace_collection);
1758
+ const { delNamespace, setCache, getCache } = useCache4(namespace_collection);
1759
+ async function createIndex() {
1760
+ try {
1761
+ await collection.createIndexes([
1762
+ { key: { site: 1 } },
1763
+ { key: { createdBy: 1 } },
1764
+ { key: { status: 1 } }
1765
+ ]);
1766
+ } catch (error) {
1767
+ throw new InternalServerError4("Failed to create index on hygiene unit.");
1768
+ }
1769
+ }
1770
+ async function createTextIndex() {
1771
+ try {
1772
+ await collection.createIndex({ name: "text" });
1773
+ } catch (error) {
1774
+ throw new InternalServerError4(
1775
+ "Failed to create text index on hygiene unit."
1776
+ );
1777
+ }
1778
+ }
1779
+ async function createUniqueIndex() {
1780
+ try {
1781
+ await collection.createIndex(
1782
+ { name: 1, site: 1, deletedAt: 1 },
1783
+ { unique: true }
1784
+ );
1785
+ } catch (error) {
1786
+ throw new InternalServerError4(
1787
+ "Failed to create unique index on hygiene unit."
1788
+ );
1789
+ }
1790
+ }
1791
+ async function createUnit(value, session) {
1792
+ try {
1793
+ value = MUnit(value);
1794
+ const res = await collection.insertOne(value, { session });
1795
+ delNamespace().then(() => {
1796
+ logger11.info(`Cache cleared for namespace: ${namespace_collection}`);
1797
+ }).catch((err) => {
1798
+ logger11.error(
1799
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1800
+ err
1801
+ );
1802
+ });
1803
+ return res.insertedId;
1804
+ } catch (error) {
1805
+ const isDuplicated = error.message.includes("duplicate");
1806
+ if (isDuplicated) {
1807
+ throw new BadRequestError13("Unit already exists.");
1808
+ }
1809
+ throw error;
1810
+ }
1811
+ }
1812
+ async function getUnits({
1813
+ page = 1,
1814
+ limit = 10,
1815
+ search = "",
1816
+ startDate = "",
1817
+ endDate = "",
1818
+ site = ""
1819
+ }) {
1820
+ page = page > 0 ? page - 1 : 0;
1821
+ try {
1822
+ site = new ObjectId6(site);
1823
+ } catch (error) {
1824
+ throw new BadRequestError13("Invalid site ID format.");
1825
+ }
1826
+ const query = {
1827
+ status: { $ne: "deleted" },
1828
+ site
1829
+ };
1830
+ const cacheOptions = {
1831
+ page,
1832
+ limit,
1833
+ site: site.toString()
1834
+ };
1835
+ if (search) {
1836
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
1837
+ cacheOptions.search = search;
1838
+ }
1839
+ if (startDate && endDate) {
1840
+ query.createdAt = {
1841
+ $gte: new Date(startDate),
1842
+ $lte: new Date(endDate)
1843
+ };
1844
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
1845
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
1846
+ } else if (startDate) {
1847
+ query.createdAt = { $gte: new Date(startDate) };
1848
+ cacheOptions.startDate = new Date(startDate).toISOString().split("T")[0];
1849
+ } else if (endDate) {
1850
+ query.createdAt = { $lte: new Date(endDate) };
1851
+ cacheOptions.endDate = new Date(endDate).toISOString().split("T")[0];
1852
+ }
1853
+ const cacheKey = makeCacheKey4(namespace_collection, cacheOptions);
1854
+ const cachedData = await getCache(cacheKey);
1855
+ if (cachedData) {
1856
+ logger11.info(`Cache hit for key: ${cacheKey}`);
1857
+ return cachedData;
1858
+ }
1859
+ try {
1860
+ const items = await collection.aggregate([
1861
+ { $match: query },
1862
+ {
1863
+ $lookup: {
1864
+ from: "sites",
1865
+ localField: "site",
1866
+ foreignField: "_id",
1867
+ pipeline: [{ $project: { name: 1 } }],
1868
+ as: "site"
1869
+ }
1870
+ },
1871
+ {
1872
+ $unwind: {
1873
+ path: "$site",
1874
+ preserveNullAndEmptyArrays: true
1875
+ }
1876
+ },
1877
+ {
1878
+ $lookup: {
1879
+ from: "users",
1880
+ localField: "createdBy",
1881
+ foreignField: "_id",
1882
+ pipeline: [{ $project: { name: 1 } }],
1883
+ as: "createdBy"
1884
+ }
1885
+ },
1886
+ {
1887
+ $unwind: {
1888
+ path: "$createdBy",
1889
+ preserveNullAndEmptyArrays: true
1890
+ }
1891
+ },
1892
+ {
1893
+ $project: {
1894
+ name: 1,
1895
+ site: "$site._id",
1896
+ siteName: "$site.name",
1897
+ createdByName: "$createdBy.name",
1898
+ checklist: 1,
1899
+ status: 1,
1900
+ createdAt: 1
1901
+ }
1902
+ },
1903
+ { $sort: { _id: -1 } },
1904
+ { $skip: page * limit },
1905
+ { $limit: limit }
1906
+ ]).toArray();
1907
+ const length = await collection.countDocuments(query);
1908
+ const data = paginate4(items, page, limit, length);
1909
+ setCache(cacheKey, data, 15 * 60).then(() => {
1910
+ logger11.info(`Cache set for key: ${cacheKey}`);
1911
+ }).catch((err) => {
1912
+ logger11.error(`Failed to set cache for key: ${cacheKey}`, err);
1913
+ });
1914
+ return data;
1915
+ } catch (error) {
1916
+ throw error;
1917
+ }
1918
+ }
1919
+ async function getUnitByName(name, site) {
1920
+ try {
1921
+ if (site)
1922
+ site = new ObjectId6(site);
1923
+ } catch (error) {
1924
+ throw new BadRequestError13("Invalid site ID format.");
1925
+ }
1926
+ try {
1927
+ return await collection.findOne({
1928
+ name: { $regex: new RegExp(`^${name}$`, "i") },
1929
+ ...site && { site }
1930
+ });
1931
+ } catch (error) {
1932
+ throw new BadRequestError13("Unable to fetch unit by name.");
1933
+ }
1934
+ }
1935
+ async function getUnitById(id, site) {
1936
+ try {
1937
+ id = typeof id === "string" ? new ObjectId6(id) : id;
1938
+ } catch (error) {
1939
+ throw new BadRequestError13("Invalid unit ID format.");
1940
+ }
1941
+ try {
1942
+ if (site)
1943
+ site = new ObjectId6(site);
1944
+ } catch (error) {
1945
+ throw new BadRequestError13(
1946
+ "Unable to fetch unit by ID, Invalid site ID format."
1947
+ );
1948
+ }
1949
+ try {
1950
+ return await collection.findOne({ _id: id, ...site && { site } });
1951
+ } catch (error) {
1952
+ throw new BadRequestError13("Unable to fetch unit by id.");
1953
+ }
1954
+ }
1955
+ async function updateUnit(_id, value) {
1956
+ try {
1957
+ _id = new ObjectId6(_id);
1958
+ } catch (error) {
1959
+ throw new BadRequestError13("Invalid unit ID format.");
1960
+ }
1961
+ try {
1962
+ const updateValue = { ...value, updatedAt: /* @__PURE__ */ new Date() };
1963
+ const res = await collection.updateOne({ _id }, { $set: updateValue });
1964
+ if (res.modifiedCount === 0) {
1965
+ throw new InternalServerError4("Unable to update cleaning unit.");
1966
+ }
1967
+ delNamespace().then(() => {
1968
+ logger11.info(`Cache cleared for namespace: ${namespace_collection}`);
1969
+ }).catch((err) => {
1970
+ logger11.error(
1971
+ `Failed to clear cache for namespace: ${namespace_collection}`,
1972
+ err
1973
+ );
1974
+ });
1975
+ return res.modifiedCount;
1976
+ } catch (error) {
1977
+ const isDuplicated = error.message.includes("duplicate");
1978
+ if (isDuplicated) {
1979
+ throw new BadRequestError13("Area already exists.");
1980
+ }
1981
+ throw error;
1982
+ }
1983
+ }
1984
+ async function deleteUnit(_id, session) {
1985
+ try {
1986
+ _id = new ObjectId6(_id);
1987
+ } catch (error) {
1988
+ throw new BadRequestError13("Invalid unit ID format.");
1989
+ }
1990
+ try {
1991
+ const updateValue = {
1992
+ status: "deleted",
1993
+ updatedAt: /* @__PURE__ */ new Date(),
1994
+ deletedAt: /* @__PURE__ */ new Date()
1995
+ };
1996
+ const res = await collection.updateOne(
1997
+ { _id },
1998
+ { $set: updateValue },
1999
+ { session }
2000
+ );
2001
+ if (res.modifiedCount === 0) {
2002
+ throw new InternalServerError4("Unable to delete unit.");
2003
+ }
2004
+ delNamespace().then(() => {
2005
+ logger11.info(`Cache cleared for namespace: ${namespace_collection}`);
2006
+ }).catch((err) => {
2007
+ logger11.error(
2008
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2009
+ err
2010
+ );
2011
+ });
2012
+ return res.modifiedCount;
2013
+ } catch (error) {
2014
+ throw error;
2015
+ }
2016
+ }
2017
+ return {
2018
+ createIndex,
2019
+ createTextIndex,
2020
+ createUniqueIndex,
2021
+ createUnit,
2022
+ getUnits,
2023
+ getUnitByName,
2024
+ getUnitById,
2025
+ updateUnit,
2026
+ deleteUnit
2027
+ };
2028
+ }
2029
+
2030
+ // src/services/hygiene-unit.service.ts
2031
+ function useUnitService() {
2032
+ const { createUnit: _createUnit } = useUnitRepository();
2033
+ async function uploadByFile({
2034
+ dataJson,
2035
+ createdBy,
2036
+ site
2037
+ }) {
2038
+ let dataArray;
2039
+ try {
2040
+ dataArray = JSON.parse(dataJson);
2041
+ } catch (error) {
2042
+ throw new BadRequestError14("Invalid JSON format for data in excel");
2043
+ }
2044
+ if (!dataArray || dataArray.length === 0) {
2045
+ throw new NotFoundError6("No data found in the uploaded file");
2046
+ }
2047
+ const session = useAtlas7.getClient()?.startSession();
2048
+ const insertedUnitIds = [];
2049
+ try {
2050
+ session?.startTransaction();
2051
+ for (const row of dataArray) {
2052
+ if (!row?.UNIT_NAME) {
2053
+ logger12.warn("Skipping row with missing UNIT_NAME:", row);
2054
+ continue;
2055
+ }
2056
+ try {
2057
+ const insertedId = await _createUnit(
2058
+ {
2059
+ name: String(row.UNIT_NAME).trim(),
2060
+ site,
2061
+ createdBy
2062
+ },
2063
+ session
2064
+ );
2065
+ insertedUnitIds.push(insertedId);
2066
+ } catch (error) {
2067
+ logger12.error(
2068
+ `Error creating unit "${row.UNIT_NAME}":`,
2069
+ error.message
2070
+ );
2071
+ continue;
2072
+ }
2073
+ }
2074
+ await session?.commitTransaction();
2075
+ logger12.info(`Successfully uploaded ${insertedUnitIds.length} units`);
2076
+ return {
2077
+ message: `Successfully uploaded ${insertedUnitIds.length} units`
2078
+ };
2079
+ } catch (error) {
2080
+ await session?.abortTransaction();
2081
+ logger12.error("Error while uploading unit information", error);
2082
+ throw error;
2083
+ } finally {
2084
+ session?.endSession();
2085
+ }
2086
+ }
2087
+ return {
2088
+ uploadByFile
2089
+ };
2090
+ }
2091
+
2092
+ // src/controllers/hygiene-unit.controller.ts
2093
+ import { BadRequestError as BadRequestError15, logger as logger13 } from "@iservice365/node-server-utils";
2094
+ import Joi8 from "joi";
2095
+ function useUnitController() {
2096
+ const {
2097
+ createUnit: _createUnit,
2098
+ getUnits: _getUnits,
2099
+ updateUnit: _updateUnit,
2100
+ deleteUnit: _deleteUnit
2101
+ } = useUnitRepository();
2102
+ const { uploadByFile: _uploadByFile } = useUnitService();
2103
+ async function createUnit(req, res, next) {
2104
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2105
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
2106
+ {}
2107
+ ) : {};
2108
+ const createdBy = cookies["user"] || "";
2109
+ const payload = { ...req.body, createdBy };
2110
+ const { error } = unitSchema.validate(payload);
2111
+ if (error) {
2112
+ logger13.log({ level: "error", message: error.message });
2113
+ next(new BadRequestError15(error.message));
2114
+ return;
2115
+ }
2116
+ try {
2117
+ const id = await _createUnit(payload);
2118
+ res.status(201).json({ message: "Unit created successfully.", id });
2119
+ return;
2120
+ } catch (error2) {
2121
+ logger13.log({ level: "error", message: error2.message });
2122
+ next(error2);
2123
+ return;
2124
+ }
2125
+ }
2126
+ async function getAll(req, res, next) {
2127
+ const query = req.query;
2128
+ const validation = Joi8.object({
2129
+ page: Joi8.number().min(1).optional().allow("", null),
2130
+ limit: Joi8.number().min(1).optional().allow("", null),
2131
+ search: Joi8.string().optional().allow("", null),
2132
+ startDate: Joi8.alternatives().try(Joi8.date(), Joi8.string()).optional().allow("", null),
2133
+ endDate: Joi8.alternatives().try(Joi8.date(), Joi8.string()).optional().allow("", null),
2134
+ site: Joi8.string().hex().optional().allow("", null)
2135
+ });
2136
+ const { error } = validation.validate(query);
2137
+ if (error) {
2138
+ logger13.log({ level: "error", message: error.message });
2139
+ next(new BadRequestError15(error.message));
2140
+ return;
2141
+ }
2142
+ const page = parseInt(req.query.page) ?? 1;
2143
+ const limit = parseInt(req.query.limit) ?? 20;
2144
+ const search = req.query.search ?? "";
2145
+ const site = req.query.site ?? "";
2146
+ const startDate = req.query.startDate ?? "";
2147
+ const endDate = req.query.endDate ?? "";
2148
+ try {
2149
+ const data = await _getUnits({
2150
+ page,
2151
+ limit,
2152
+ search,
2153
+ site,
2154
+ startDate,
2155
+ endDate
2156
+ });
2157
+ res.json(data);
2158
+ return;
2159
+ } catch (error2) {
2160
+ logger13.log({ level: "error", message: error2.message });
2161
+ next(error2);
2162
+ return;
2163
+ }
2164
+ }
2165
+ async function updateUnit(req, res, next) {
2166
+ const payload = { id: req.params.id, ...req.body };
2167
+ const schema = Joi8.object({
2168
+ id: Joi8.string().hex().required(),
2169
+ name: Joi8.string().required()
2170
+ });
2171
+ const { error } = schema.validate(payload);
2172
+ if (error) {
2173
+ logger13.log({ level: "error", message: error.message });
2174
+ next(new BadRequestError15(error.message));
2175
+ return;
2176
+ }
2177
+ try {
2178
+ const { id, ...value } = payload;
2179
+ await _updateUnit(id, value);
2180
+ res.json({ message: "Unit updated successfully." });
2181
+ return;
2182
+ } catch (error2) {
2183
+ logger13.log({ level: "error", message: error2.message });
2184
+ next(error2);
2185
+ return;
2186
+ }
2187
+ }
2188
+ async function deleteUnit(req, res, next) {
2189
+ const id = req.params.id;
2190
+ const validation = Joi8.object({
2191
+ id: Joi8.string().hex().required()
2192
+ });
2193
+ const { error } = validation.validate({ id });
2194
+ if (error) {
2195
+ logger13.log({ level: "error", message: error.message });
2196
+ next(new BadRequestError15(error.message));
2197
+ return;
2198
+ }
2199
+ try {
2200
+ await _deleteUnit(id);
2201
+ res.json({ message: "Unit deleted successfully." });
2202
+ return;
2203
+ } catch (error2) {
2204
+ logger13.log({ level: "error", message: error2.message });
2205
+ next(error2);
2206
+ return;
2207
+ }
2208
+ }
2209
+ async function uploadByFile(req, res, next) {
2210
+ if (!req.file) {
2211
+ next(new BadRequestError15("File is required!"));
2212
+ return;
2213
+ }
2214
+ const cookies = req.headers.cookie ? req.headers.cookie.split(";").map((cookie) => cookie.trim().split("=")).reduce(
2215
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
2216
+ {}
2217
+ ) : {};
2218
+ const createdBy = cookies["user"] || "";
2219
+ const { site } = req.body;
2220
+ const schema = Joi8.object({
2221
+ site: Joi8.string().hex().optional().allow("", null),
2222
+ createdBy: Joi8.string().hex().required()
2223
+ });
2224
+ const { error } = schema.validate({ site, createdBy });
2225
+ if (error) {
2226
+ logger13.log({ level: "error", message: error.message });
2227
+ next(new BadRequestError15(error.message));
2228
+ return;
2229
+ }
2230
+ try {
2231
+ const xlsData = await convertBufferFile(req.file.buffer);
2232
+ const dataJson = JSON.stringify(xlsData);
2233
+ const result = await _uploadByFile({ dataJson, createdBy, site });
2234
+ return res.status(201).json(result);
2235
+ } catch (error2) {
2236
+ logger13.log({ level: "error", message: error2.message });
2237
+ next(error2);
2238
+ return;
2239
+ }
2240
+ }
2241
+ return {
2242
+ createUnit,
2243
+ getAll,
2244
+ updateUnit,
2245
+ deleteUnit,
2246
+ uploadByFile
2247
+ };
2248
+ }
2249
+
2250
+ // src/models/hygiene-schedule-task-area.model.ts
2251
+ import { BadRequestError as BadRequestError16 } from "@iservice365/node-server-utils";
2252
+ import Joi9 from "joi";
2253
+ import { ObjectId as ObjectId7 } from "mongodb";
2254
+ var scheduleTaskAreaSchema = Joi9.object({
2255
+ name: Joi9.string().required(),
2256
+ site: Joi9.string().hex().required(),
2257
+ createdBy: Joi9.string().hex().required(),
2258
+ checklist: Joi9.array().items(
2259
+ Joi9.object({
2260
+ _id: Joi9.string().hex().required(),
2261
+ name: Joi9.string().required()
2262
+ })
2263
+ ).optional(),
2264
+ updatedAt: Joi9.date().optional().allow("", null),
2265
+ status: Joi9.string().allow("", null).optional()
2266
+ });
2267
+ function MScheduleTaskArea(value) {
2268
+ const { error } = scheduleTaskAreaSchema.validate(value);
2269
+ if (error) {
2270
+ throw new BadRequestError16(error.message);
2271
+ }
2272
+ if (value.site) {
2273
+ try {
2274
+ value.site = new ObjectId7(value.site);
2275
+ } catch (error2) {
2276
+ throw new BadRequestError16("Invalid site ID format.");
2277
+ }
2278
+ }
2279
+ if (value.createdBy) {
2280
+ try {
2281
+ value.createdBy = new ObjectId7(value.createdBy);
2282
+ } catch (error2) {
2283
+ throw new BadRequestError16("Invalid createdBy ID format.");
2284
+ }
2285
+ }
2286
+ if (value.checklist && Array.isArray(value.checklist)) {
2287
+ value.checklist = value.checklist.map((item) => {
2288
+ try {
2289
+ return {
2290
+ ...item,
2291
+ _id: new ObjectId7(item._id)
2292
+ };
2293
+ } catch (error2) {
2294
+ throw new BadRequestError16(
2295
+ `Invalid checklist item ID format: ${item._id}`
2296
+ );
2297
+ }
2298
+ });
2299
+ }
2300
+ return {
2301
+ name: value.name,
2302
+ site: value.site,
2303
+ createdBy: value.createdBy,
2304
+ checklist: value.checklist,
2305
+ status: value.status ?? "",
2306
+ createdAt: /* @__PURE__ */ new Date(),
2307
+ updatedAt: value.updatedAt ?? "",
2308
+ deletedAt: value.deletedAt ?? ""
2309
+ };
2310
+ }
2311
+
2312
+ // src/repositories/hygiene-schedule-task-area.repository.ts
2313
+ import { ObjectId as ObjectId8 } from "mongodb";
2314
+ import {
2315
+ useAtlas as useAtlas8,
2316
+ InternalServerError as InternalServerError5,
2317
+ paginate as paginate5,
2318
+ BadRequestError as BadRequestError17,
2319
+ useCache as useCache5,
2320
+ logger as logger14,
2321
+ makeCacheKey as makeCacheKey5
2322
+ } from "@iservice365/node-server-utils";
2323
+ function useScheduleTaskAreaRepository() {
2324
+ const db = useAtlas8.getDb();
2325
+ if (!db) {
2326
+ throw new InternalServerError5("Unable to connect to server.");
2327
+ }
2328
+ const namespace_collection = "hygiene-schedule-task-areas";
2329
+ const collection = db.collection(namespace_collection);
2330
+ const { delNamespace, setCache, getCache, delCache } = useCache5(namespace_collection);
2331
+ async function createIndexes() {
2332
+ try {
2333
+ await collection.createIndexes([
2334
+ { key: { site: 1 } },
2335
+ { key: { name: "text" } }
2336
+ ]);
2337
+ } catch (error) {
2338
+ throw new InternalServerError5("Failed to create index on site.");
2339
+ }
2340
+ }
2341
+ async function createScheduleTaskArea(value, session) {
2342
+ try {
2343
+ value = MScheduleTaskArea(value);
2344
+ const res = await collection.insertOne(value, { session });
2345
+ delNamespace().then(() => {
2346
+ logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
2347
+ }).catch((err) => {
2348
+ logger14.error(
2349
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2350
+ err
2351
+ );
2352
+ });
2353
+ return res.insertedId;
2354
+ } catch (error) {
2355
+ throw error;
2356
+ }
2357
+ }
2358
+ async function updateScheduleTaskArea(_id, params) {
2359
+ try {
2360
+ _id = new ObjectId8(_id);
2361
+ } catch (error) {
2362
+ throw new BadRequestError17("Invalid area ID format.");
2363
+ }
2364
+ try {
2365
+ const value = MScheduleTaskArea({ ...params, updatedAt: /* @__PURE__ */ new Date() });
2366
+ const res = await collection.updateOne({ _id }, { $set: value });
2367
+ if (res.modifiedCount === 0) {
2368
+ throw new InternalServerError5("Unable to update cleaning area.");
2369
+ }
2370
+ delNamespace().then(() => {
2371
+ logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
2372
+ }).catch((err) => {
2373
+ logger14.error(
2374
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2375
+ err
2376
+ );
2377
+ });
2378
+ return res.modifiedCount;
2379
+ } catch (error) {
2380
+ throw error;
2381
+ }
2382
+ }
2383
+ async function deleteScheduleTaskArea(_id, session) {
2384
+ try {
2385
+ _id = new ObjectId8(_id);
2386
+ } catch (error) {
2387
+ throw new BadRequestError17("Invalid area ID format.");
2388
+ }
2389
+ try {
2390
+ const updateValue = {
2391
+ status: "deleted",
2392
+ updatedAt: /* @__PURE__ */ new Date(),
2393
+ deletedAt: /* @__PURE__ */ new Date()
2394
+ };
2395
+ const res = await collection.updateOne(
2396
+ { _id },
2397
+ { $set: updateValue },
2398
+ { session }
2399
+ );
2400
+ if (res.modifiedCount === 0)
2401
+ throw new InternalServerError5("Unable to delete area.");
2402
+ const cacheKey = makeCacheKey5(namespace_collection, { _id });
2403
+ delCache(cacheKey).then(() => {
2404
+ logger14.info(`Cache deleted for key: ${cacheKey}`);
2405
+ }).catch((err) => {
2406
+ logger14.error(`Failed to delete cache for key: ${cacheKey}`, err);
2407
+ });
2408
+ return res.modifiedCount;
2409
+ } catch (error) {
2410
+ throw error;
2411
+ }
2412
+ }
2413
+ async function getScheduleTaskAreas({
2414
+ page = 1,
2415
+ limit = 10,
2416
+ search = "",
2417
+ sort = {},
2418
+ startDate = "",
2419
+ endDate = "",
2420
+ site = ""
2421
+ }) {
2422
+ page = page > 0 ? page - 1 : 0;
2423
+ let dateFilter = {};
2424
+ try {
2425
+ site = new ObjectId8(site);
2426
+ } catch (error) {
2427
+ throw new BadRequestError17("Invalid site ID format.");
2428
+ }
2429
+ const query = {
2430
+ status: { $ne: "deleted" }
2431
+ };
2432
+ const cacheOptions = {
2433
+ site: site.toString()
2434
+ };
2435
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
2436
+ cacheOptions.sort = JSON.stringify(sort);
2437
+ if (search) {
2438
+ query.$or = [{ name: { $regex: search, $options: "i" } }];
2439
+ cacheOptions.search = search;
2440
+ }
2441
+ delNamespace().then(() => {
2442
+ logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
2443
+ }).catch((err) => {
2444
+ logger14.error(
2445
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2446
+ err
2447
+ );
2448
+ });
2449
+ const cacheKey = makeCacheKey5(namespace_collection, cacheOptions);
2450
+ const cachedData = await getCache(cacheKey);
2451
+ if (cachedData) {
2452
+ logger14.info(`Cache hit for key: ${cacheKey}`);
2453
+ return cachedData;
2454
+ }
2455
+ if (startDate && endDate) {
2456
+ dateFilter = {
2457
+ createdAt: {
2458
+ $gte: new Date(startDate),
2459
+ $lte: new Date(endDate)
2460
+ }
2461
+ };
2462
+ } else if (startDate) {
2463
+ dateFilter = { createdAt: { $gte: new Date(startDate) } };
2464
+ } else if (endDate) {
2465
+ dateFilter = { createdAt: { $lte: new Date(endDate) } };
2466
+ }
2467
+ try {
2468
+ const items = await collection.aggregate([
2469
+ {
2470
+ $match: {
2471
+ ...dateFilter,
2472
+ ...query
2473
+ }
2474
+ },
2475
+ {
2476
+ $lookup: {
2477
+ from: "sites",
2478
+ localField: "site",
2479
+ foreignField: "_id",
2480
+ pipeline: [{ $project: { name: 1 } }],
2481
+ as: "site"
2482
+ }
2483
+ },
2484
+ {
2485
+ $unwind: {
2486
+ path: "$site",
2487
+ preserveNullAndEmptyArrays: true
2488
+ }
2489
+ },
2490
+ {
2491
+ $lookup: {
2492
+ from: "users",
2493
+ localField: "createdBy",
2494
+ foreignField: "_id",
2495
+ pipeline: [{ $project: { name: 1 } }],
2496
+ as: "createdBy"
2497
+ }
2498
+ },
2499
+ {
2500
+ $unwind: {
2501
+ path: "$createdBy",
2502
+ preserveNullAndEmptyArrays: true
2503
+ }
2504
+ },
2505
+ {
2506
+ $facet: {
2507
+ totalCount: [{ $count: "count" }],
2508
+ items: [
2509
+ { $sort: { createdAt: -1 } },
2510
+ { $skip: page * limit },
2511
+ { $limit: limit }
2512
+ ]
2513
+ }
2514
+ }
2515
+ ]).toArray();
2516
+ const length = await collection.countDocuments(query);
2517
+ const data = paginate5(items, page, limit, length);
2518
+ setCache(cacheKey, data, 15 * 60).then(() => {
2519
+ logger14.info(`Cache set for key: ${cacheKey}`);
2520
+ }).catch((err) => {
2521
+ logger14.error(`Failed to set cache for key: ${cacheKey}`, err);
2522
+ });
2523
+ return data;
2524
+ } catch (error) {
2525
+ throw error;
2526
+ }
2527
+ }
2528
+ async function getScheduleTaskAreaByName(name, site) {
2529
+ try {
2530
+ if (site)
2531
+ site = new ObjectId8(site);
2532
+ } catch (error) {
2533
+ throw new BadRequestError17("Invalid site ID format.");
2534
+ }
2535
+ try {
2536
+ return await collection.findOne({
2537
+ name: { $regex: new RegExp(`^${name}$`, "i") },
2538
+ ...site && { site }
2539
+ });
2540
+ } catch (error) {
2541
+ throw new BadRequestError17("Unable to fetch schedule task area by name.");
2542
+ }
2543
+ }
2544
+ async function getScheduleTaskAreaById(id, site) {
2545
+ try {
2546
+ id = typeof id === "string" ? new ObjectId8(id) : id;
2547
+ } catch (error) {
2548
+ throw new BadRequestError17("Invalid unit ID format.");
2549
+ }
2550
+ try {
2551
+ if (site)
2552
+ site = new ObjectId8(site);
2553
+ } catch (error) {
2554
+ throw new BadRequestError17(
2555
+ "Unable to fetch schedule task area by ID, Invalid site ID format."
2556
+ );
2557
+ }
2558
+ try {
2559
+ return await collection.findOne({ _id: id, ...site && { site } });
2560
+ } catch (error) {
2561
+ throw new BadRequestError17("Unable to fetch schedule task area by id.");
2562
+ }
2563
+ }
2564
+ return {
2565
+ createScheduleTaskArea,
2566
+ updateScheduleTaskArea,
2567
+ deleteScheduleTaskArea,
2568
+ getScheduleTaskAreas,
2569
+ createIndexes,
2570
+ getScheduleTaskAreaByName,
2571
+ getScheduleTaskAreaById
2572
+ };
2573
+ }
2574
+
2575
+ // src/services/hygiene-schedule-task-area.service.ts
2576
+ import {
2577
+ InternalServerError as InternalServerError6
2578
+ } from "@iservice365/node-server-utils";
2579
+ function useScheduleTaskAreaService() {
2580
+ const {
2581
+ getScheduleTaskAreas: _getAll,
2582
+ createScheduleTaskArea: _create,
2583
+ updateScheduleTaskArea: _updateById,
2584
+ deleteScheduleTaskArea: _deleteById,
2585
+ getScheduleTaskAreaByName: _getScheduleTaskAreaByName,
2586
+ getScheduleTaskAreaById: _getScheduleTaskAreaById
2587
+ } = useScheduleTaskAreaRepository();
2588
+ async function getAll(query) {
2589
+ return await _getAll(query);
2590
+ }
2591
+ async function create(scheduleTaskArea) {
2592
+ const result = await _getScheduleTaskAreaByName(
2593
+ scheduleTaskArea.name,
2594
+ scheduleTaskArea.site
2595
+ );
2596
+ if (result)
2597
+ throw new InternalServerError6(
2598
+ "Schedule Task Area name already exists in this site."
2599
+ );
2600
+ return await _create(scheduleTaskArea);
2601
+ }
2602
+ async function updateById(id, scheduleTaskArea) {
2603
+ const result = await _getScheduleTaskAreaByName(
2604
+ scheduleTaskArea.name,
2605
+ scheduleTaskArea.site
2606
+ );
2607
+ const resultId = result?._id.toString() ?? "";
2608
+ if (result && id != resultId) {
2609
+ throw new InternalServerError6(
2610
+ "Schedule Task Area name already exists in this site."
2611
+ );
2612
+ }
2613
+ return await _updateById(id, scheduleTaskArea);
2614
+ }
2615
+ async function deleteById(id) {
2616
+ return await _deleteById(id);
2617
+ }
2618
+ return {
2619
+ getAll,
2620
+ create,
2621
+ updateById,
2622
+ deleteById
2623
+ };
2624
+ }
2625
+
2626
+ // src/controllers/hygiene-schedule-task-area.controller.ts
2627
+ import { BadRequestError as BadRequestError19, logger as logger16 } from "@iservice365/node-server-utils";
2628
+ import Joi10 from "joi";
2629
+ function useScheduleTaskAreaController() {
2630
+ const {
2631
+ getAll: _getAll,
2632
+ create: _createScheduleTaskArea,
2633
+ updateById: _updateScheduleTaskArea,
2634
+ deleteById: _deleteById
2635
+ } = useScheduleTaskAreaService();
2636
+ async function getAll(req, res, next) {
2637
+ const query = req.query;
2638
+ const validation = Joi10.object({
2639
+ page: Joi10.number().min(1).optional().allow("", null),
2640
+ limit: Joi10.number().min(1).optional().allow("", null),
2641
+ search: Joi10.string().optional().allow("", null),
2642
+ site: Joi10.string().hex().optional().allow("", null)
2643
+ });
2644
+ const { error } = validation.validate(query);
2645
+ if (error) {
2646
+ next(new BadRequestError19(error.message));
2647
+ return;
2648
+ }
2649
+ const page = parseInt(req.query.page) ?? 1;
2650
+ let limit = parseInt(req.query.limit) ?? 20;
2651
+ limit = isNaN(limit) ? 20 : limit;
2652
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
2653
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
2654
+ const sortObj = {};
2655
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
2656
+ sort.forEach((field, index) => {
2657
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
2658
+ });
2659
+ }
2660
+ const site = req.query.site ?? "";
2661
+ const search = req.query.search ?? "";
2662
+ try {
2663
+ const scheduleTaskAreas = await _getAll({
2664
+ page,
2665
+ limit,
2666
+ sort: sortObj,
2667
+ site,
2668
+ search
2669
+ });
2670
+ res.json(scheduleTaskAreas);
2671
+ return;
2672
+ } catch (error2) {
2673
+ next(error2);
2674
+ }
2675
+ }
2676
+ async function createScheduleTaskArea(req, res, next) {
2677
+ const value = req.body;
2678
+ const schema = Joi10.object({
2679
+ name: Joi10.string().required(),
2680
+ site: Joi10.string().hex().optional().allow("", null),
2681
+ createdBy: Joi10.string().hex().optional().allow("", null)
2682
+ });
2683
+ const { error } = schema.validate(value);
2684
+ if (error) {
2685
+ next(new BadRequestError19(error.message));
2686
+ logger16.info(`Controller: ${error.message}`);
2687
+ return;
2688
+ }
2689
+ try {
2690
+ const result = await _createScheduleTaskArea(value);
2691
+ res.status(201).json(result);
2692
+ return;
2693
+ } catch (error2) {
2694
+ next(error2);
2695
+ }
2696
+ }
2697
+ async function updateScheduleTaskArea(req, res, next) {
2698
+ const { name, site, createdBy } = req.body;
2699
+ const id = req.params.id;
2700
+ const schema = Joi10.object({
2701
+ id: Joi10.string().hex().required(),
2702
+ name: Joi10.string().required(),
2703
+ site: Joi10.string().required(),
2704
+ checklist: Joi10.array().optional().items(
2705
+ Joi10.object({
2706
+ _id: Joi10.string().optional().required(),
2707
+ name: Joi10.string().optional().required()
2708
+ })
2709
+ ),
2710
+ createdBy: Joi10.string().hex().optional().allow("", null)
2711
+ });
2712
+ const { error } = schema.validate({ id, name, createdBy, site });
2713
+ if (error) {
2714
+ next(new BadRequestError19(error.message));
2715
+ logger16.info(`Controller: ${error.message}`);
2716
+ return;
2717
+ }
2718
+ try {
2719
+ const result = await _updateScheduleTaskArea(id, {
2720
+ name,
2721
+ createdBy,
2722
+ site
2723
+ });
2724
+ res.json(result);
2725
+ return;
2726
+ } catch (error2) {
2727
+ next(error2);
2728
+ }
2729
+ }
2730
+ async function deleteScheduleTaskArea(req, res, next) {
2731
+ const id = req.params.id;
2732
+ const validation = Joi10.object({
2733
+ id: Joi10.string().hex().required()
2734
+ });
2735
+ const { error } = validation.validate({ id });
2736
+ if (error) {
2737
+ next(new BadRequestError19(error.message));
2738
+ return;
2739
+ }
2740
+ try {
2741
+ const message = await _deleteById(id);
2742
+ res.json(message);
2743
+ return;
2744
+ } catch (error2) {
2745
+ next(error2);
2746
+ }
2747
+ }
2748
+ return {
2749
+ getAll,
2750
+ createScheduleTaskArea,
2751
+ updateScheduleTaskArea,
2752
+ deleteScheduleTaskArea
2753
+ };
2754
+ }
2755
+ export {
2756
+ MArea,
2757
+ MParentChecklist,
2758
+ MScheduleTaskArea,
2759
+ MToiletLocation,
2760
+ MUnit,
2761
+ areaSchema,
2762
+ parentChecklistSchema,
2763
+ scheduleTaskAreaSchema,
2764
+ toiletLocationSchema,
2765
+ unitSchema,
2766
+ useAreaController,
2767
+ useAreaRepository,
2768
+ useAreaService,
2769
+ useParentCheckilstController,
2770
+ useParentChecklistRepo,
2771
+ useScheduleTaskAreaController,
2772
+ useScheduleTaskAreaRepository,
2773
+ useScheduleTaskAreaService,
2774
+ useToiletLocationController,
2775
+ useToiletLocationRepository,
2776
+ useToiletLocationService,
2777
+ useUnitController,
2778
+ useUnitRepository,
2779
+ useUnitService
2780
+ };
1
2781
  //# sourceMappingURL=index.mjs.map