@7365admin1/core 3.31.0 → 3.32.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
@@ -42320,7 +42320,11 @@ function useBulletinBoardRepo() {
42320
42320
  );
42321
42321
  async function createIndexes() {
42322
42322
  try {
42323
- await collection.createIndexes([{ key: { site: 1, status: 1 } }]);
42323
+ await collection.createIndexes([
42324
+ { key: { site: 1, status: 1, _id: -1 } },
42325
+ { key: { status: 1, startDate: 1 } },
42326
+ { key: { status: 1, endDate: 1 } }
42327
+ ]);
42324
42328
  } catch (error) {
42325
42329
  throw new InternalServerError43("Failed to create index on site.");
42326
42330
  }
@@ -47383,6 +47387,7 @@ function UseAccessManagementRepo() {
47383
47387
  const userType = params.userType;
47384
47388
  const type = params.type;
47385
47389
  const search = params.search;
47390
+ const typeFilter = type.toLowerCase() === "all" ? [] : [{ $eq: ["$type", type] }];
47386
47391
  const query = {
47387
47392
  site: { $in: [site] }
47388
47393
  };
@@ -47409,7 +47414,7 @@ function UseAccessManagementRepo() {
47409
47414
  $lookup: {
47410
47415
  from: "building-levels",
47411
47416
  localField: "_id",
47412
- foreignField: "block",
47417
+ foreignField: "blockId",
47413
47418
  pipeline: [
47414
47419
  { $match: { status: { $ne: "deleted" } } },
47415
47420
  {
@@ -47423,14 +47428,29 @@ function UseAccessManagementRepo() {
47423
47428
  {
47424
47429
  $lookup: {
47425
47430
  from: "access-cards",
47426
- localField: "_id",
47427
- foreignField: "assignedUnit",
47431
+ let: { unit: "$_id" },
47428
47432
  pipeline: [
47429
47433
  {
47430
47434
  $match: {
47431
- isActivated: true,
47432
- userType,
47433
- type
47435
+ $expr: {
47436
+ $and: [
47437
+ {
47438
+ $in: [
47439
+ "$$unit",
47440
+ {
47441
+ $cond: [
47442
+ { $isArray: "$assignedUnit" },
47443
+ "$assignedUnit",
47444
+ ["$assignedUnit"]
47445
+ ]
47446
+ }
47447
+ ]
47448
+ },
47449
+ { $eq: ["$isActivated", true] },
47450
+ { $eq: ["$userType", userType] },
47451
+ ...typeFilter
47452
+ ]
47453
+ }
47434
47454
  }
47435
47455
  },
47436
47456
  {
@@ -47485,7 +47505,7 @@ function UseAccessManagementRepo() {
47485
47505
  {
47486
47506
  $project: {
47487
47507
  _id: 1,
47488
- level: 1,
47508
+ level: "$name",
47489
47509
  units: 1
47490
47510
  }
47491
47511
  }
@@ -47534,10 +47554,11 @@ function UseAccessManagementRepo() {
47534
47554
  },
47535
47555
  {
47536
47556
  $project: {
47537
- name: 1,
47538
- "level.level": 1,
47539
- "level.units.name": 1,
47540
- "level.units.fAccessCards": 1
47557
+ _id: 0,
47558
+ unitId: "$level.units._id",
47559
+ block: "$name",
47560
+ level: "$level.level",
47561
+ unit: "$level.units.name"
47541
47562
  }
47542
47563
  }
47543
47564
  ],
@@ -47548,6 +47569,138 @@ function UseAccessManagementRepo() {
47548
47569
  throw new Error(error.message);
47549
47570
  }
47550
47571
  }
47572
+ async function assignedAccessCardsByUnitRepo(params) {
47573
+ try {
47574
+ const site = new ObjectId97(params.site);
47575
+ const unitId = new ObjectId97(params.unitId);
47576
+ const userType = params.userType;
47577
+ const type = params.type;
47578
+ const search = params.search;
47579
+ const typeFilter = type.toLowerCase() === "all" ? [] : [{ $eq: ["$type", type] }];
47580
+ const searchFilter = search ? [
47581
+ {
47582
+ $or: [
47583
+ { cardNo: { $regex: search.trim(), $options: "i" } },
47584
+ { accessLevel: { $regex: search.trim(), $options: "i" } },
47585
+ { doorName: { $regex: search.trim(), $options: "i" } },
47586
+ { liftName: { $regex: search.trim(), $options: "i" } }
47587
+ ]
47588
+ }
47589
+ ] : [];
47590
+ const result = await collectionName("building-units").aggregate(
47591
+ [
47592
+ {
47593
+ $match: {
47594
+ _id: unitId,
47595
+ site,
47596
+ status: { $ne: "deleted" }
47597
+ }
47598
+ },
47599
+ {
47600
+ $lookup: {
47601
+ from: "building-levels",
47602
+ localField: "level",
47603
+ foreignField: "_id",
47604
+ pipeline: [{ $project: { _id: 1, name: 1 } }],
47605
+ as: "levelInfo"
47606
+ }
47607
+ },
47608
+ {
47609
+ $unwind: {
47610
+ path: "$levelInfo",
47611
+ preserveNullAndEmptyArrays: true
47612
+ }
47613
+ },
47614
+ {
47615
+ $lookup: {
47616
+ from: "buildings",
47617
+ localField: "building",
47618
+ foreignField: "_id",
47619
+ pipeline: [{ $project: { _id: 1, name: 1 } }],
47620
+ as: "buildingInfo"
47621
+ }
47622
+ },
47623
+ {
47624
+ $unwind: {
47625
+ path: "$buildingInfo",
47626
+ preserveNullAndEmptyArrays: true
47627
+ }
47628
+ },
47629
+ {
47630
+ $lookup: {
47631
+ from: "access-cards",
47632
+ let: { unit: "$_id" },
47633
+ pipeline: [
47634
+ {
47635
+ $match: {
47636
+ $expr: {
47637
+ $and: [
47638
+ {
47639
+ $in: [
47640
+ "$$unit",
47641
+ {
47642
+ $cond: [
47643
+ { $isArray: "$assignedUnit" },
47644
+ "$assignedUnit",
47645
+ ["$assignedUnit"]
47646
+ ]
47647
+ }
47648
+ ]
47649
+ },
47650
+ { $eq: ["$site", site] },
47651
+ { $eq: ["$isActivated", true] },
47652
+ { $eq: ["$userType", userType] },
47653
+ ...typeFilter
47654
+ ]
47655
+ },
47656
+ ...searchFilter[0]
47657
+ }
47658
+ },
47659
+ { $sort: { _id: -1 } },
47660
+ {
47661
+ $project: {
47662
+ _id: 1,
47663
+ type: 1,
47664
+ cardNo: 1,
47665
+ accessLevel: 1,
47666
+ accessGroup: 1,
47667
+ accessType: 1,
47668
+ qrData: 1,
47669
+ startDate: 1,
47670
+ endDate: 1,
47671
+ isActivated: 1,
47672
+ isAntiPassBack: 1,
47673
+ isLiftCard: 1,
47674
+ liftAccessLevel: 1,
47675
+ doorName: 1,
47676
+ liftName: 1,
47677
+ replacementStatus: 1,
47678
+ qrTag: 1,
47679
+ qrTagCardNo: 1
47680
+ }
47681
+ }
47682
+ ],
47683
+ as: "accessCards"
47684
+ }
47685
+ },
47686
+ {
47687
+ $project: {
47688
+ _id: 0,
47689
+ unitId: "$_id",
47690
+ block: { $ifNull: ["$buildingInfo.name", "$buildingName"] },
47691
+ level: "$levelInfo.name",
47692
+ unit: "$name",
47693
+ accessCards: 1
47694
+ }
47695
+ }
47696
+ ],
47697
+ { allowDiskUse: true }
47698
+ ).toArray();
47699
+ return result[0] ?? null;
47700
+ } catch (error) {
47701
+ throw new Error(error.message);
47702
+ }
47703
+ }
47551
47704
  async function acknowlegdeCardRepo(params) {
47552
47705
  const session = useAtlas78.getClient()?.startSession();
47553
47706
  try {
@@ -49011,7 +49164,8 @@ function UseAccessManagementRepo() {
49011
49164
  assignees,
49012
49165
  unit,
49013
49166
  type,
49014
- acm_url
49167
+ acm_url,
49168
+ id
49015
49169
  }) {
49016
49170
  const session = useAtlas78.getClient()?.startSession();
49017
49171
  try {
@@ -49026,31 +49180,35 @@ function UseAccessManagementRepo() {
49026
49180
  let availableCards = [];
49027
49181
  let update = [];
49028
49182
  let cards = [];
49029
- availableCards = await collection().aggregate([
49030
- {
49031
- $match: {
49032
- $expr: {
49033
- $and: [
49034
- {
49035
- $in: [
49036
- unit,
49037
- {
49038
- $cond: [
49039
- { $isArray: "$assignedUnit" },
49040
- "$assignedUnit",
49041
- ["$assignedUnit"]
49042
- ]
49043
- }
49044
- ]
49045
- },
49046
- { $eq: ["$userId", null] },
49047
- { $eq: ["$type", type] },
49048
- { $eq: ["$isActivated", true] }
49049
- ]
49183
+ if (id) {
49184
+ availableCards = await collection().find({ _id: new ObjectId97(id) }).toArray();
49185
+ } else {
49186
+ availableCards = await collection().aggregate([
49187
+ {
49188
+ $match: {
49189
+ $expr: {
49190
+ $and: [
49191
+ {
49192
+ $in: [
49193
+ unit,
49194
+ {
49195
+ $cond: [
49196
+ { $isArray: "$assignedUnit" },
49197
+ "$assignedUnit",
49198
+ ["$assignedUnit"]
49199
+ ]
49200
+ }
49201
+ ]
49202
+ },
49203
+ { $eq: ["$userId", null] },
49204
+ { $eq: ["$type", type] },
49205
+ { $eq: ["$isActivated", true] }
49206
+ ]
49207
+ }
49050
49208
  }
49051
49209
  }
49052
- }
49053
- ]).toArray();
49210
+ ]).toArray();
49211
+ }
49054
49212
  if (assignees.length > availableCards.length) {
49055
49213
  throw new Error(`Not enough ${type} cards available.`);
49056
49214
  }
@@ -49106,13 +49264,34 @@ function UseAccessManagementRepo() {
49106
49264
  throw new Error("Command failed, server error.");
49107
49265
  }
49108
49266
  for (const { _id, userId } of update) {
49267
+ const user = userId ? await collectionName("users").findOne(
49268
+ { _id: userId },
49269
+ { projection: { name: 1, email: 1 } }
49270
+ ) : null;
49109
49271
  await collection().updateOne(
49110
49272
  { _id },
49111
49273
  {
49112
- $set: { userId, staffNo: `STAFF-${userId.toString().slice(-10)}` }
49274
+ $set: {
49275
+ userId,
49276
+ staffNo: `STAFF-${userId.toString().slice(-10)}`,
49277
+ userCred: user ? { ...user } : null
49278
+ }
49113
49279
  },
49114
49280
  { session }
49115
49281
  );
49282
+ if (user) {
49283
+ const { _id: userRefId, ...userInfo } = user;
49284
+ await collectionName("access-cards-history").insertOne(
49285
+ {
49286
+ ...userInfo,
49287
+ cardId: _id,
49288
+ userId,
49289
+ createdAt: /* @__PURE__ */ new Date(),
49290
+ type: "assigned"
49291
+ },
49292
+ { session }
49293
+ );
49294
+ }
49116
49295
  }
49117
49296
  await session?.commitTransaction();
49118
49297
  return "Cards assigned successfully.";
@@ -49277,6 +49456,7 @@ function UseAccessManagementRepo() {
49277
49456
  availableAccessCardsRepo,
49278
49457
  userTypeAccessCardsRepo,
49279
49458
  assignedAccessCardsRepo,
49459
+ assignedAccessCardsByUnitRepo,
49280
49460
  acknowlegdeCardRepo,
49281
49461
  accessandLiftCardsRepo,
49282
49462
  replaceCardRepo,
@@ -49328,6 +49508,7 @@ function useAccessManagementSvc() {
49328
49508
  availableAccessCardsRepo,
49329
49509
  userTypeAccessCardsRepo,
49330
49510
  assignedAccessCardsRepo,
49511
+ assignedAccessCardsByUnitRepo,
49331
49512
  acknowlegdeCardRepo,
49332
49513
  accessandLiftCardsRepo,
49333
49514
  replaceCardRepo,
@@ -49502,6 +49683,14 @@ function useAccessManagementSvc() {
49502
49683
  throw new Error(err.message);
49503
49684
  }
49504
49685
  };
49686
+ const assignedAccessCardsByUnitSvc = async (params) => {
49687
+ try {
49688
+ const response = await assignedAccessCardsByUnitRepo({ ...params });
49689
+ return response;
49690
+ } catch (err) {
49691
+ throw new Error(err.message);
49692
+ }
49693
+ };
49505
49694
  const acknowlegdeCardSvc = async (params) => {
49506
49695
  try {
49507
49696
  const response = await acknowlegdeCardRepo({ ...params });
@@ -49717,14 +49906,16 @@ function useAccessManagementSvc() {
49717
49906
  assignees,
49718
49907
  unit,
49719
49908
  type,
49720
- acm_url
49909
+ acm_url,
49910
+ id
49721
49911
  }) => {
49722
49912
  try {
49723
49913
  const response = await assignMultipleCardsRepo({
49724
49914
  assignees,
49725
49915
  unit,
49726
49916
  type,
49727
- acm_url
49917
+ acm_url,
49918
+ id
49728
49919
  });
49729
49920
  return response;
49730
49921
  } catch (err) {
@@ -49818,6 +50009,7 @@ function useAccessManagementSvc() {
49818
50009
  availableAccessCardsSvc,
49819
50010
  userTypeAccessCardsSvc,
49820
50011
  assignedAccessCardsSvc,
50012
+ assignedAccessCardsByUnitSvc,
49821
50013
  acknowlegdeCardSvc,
49822
50014
  accessandLiftCardsSvc,
49823
50015
  replaceCardSvc,
@@ -49867,6 +50059,7 @@ function useAccessManagementController() {
49867
50059
  availableAccessCardsSvc,
49868
50060
  userTypeAccessCardsSvc,
49869
50061
  assignedAccessCardsSvc,
50062
+ assignedAccessCardsByUnitSvc,
49870
50063
  acknowlegdeCardSvc,
49871
50064
  accessandLiftCardsSvc,
49872
50065
  replaceCardSvc,
@@ -50205,6 +50398,47 @@ function useAccessManagementController() {
50205
50398
  });
50206
50399
  }
50207
50400
  };
50401
+ const assignedAccessCardsByUnit = async (req, res) => {
50402
+ try {
50403
+ const {
50404
+ site,
50405
+ unitId,
50406
+ userType,
50407
+ type,
50408
+ search = ""
50409
+ } = req.query;
50410
+ const schema2 = Joi87.object({
50411
+ site: Joi87.string().hex().required(),
50412
+ unitId: Joi87.string().hex().required(),
50413
+ userType: Joi87.string().required(),
50414
+ type: Joi87.string().valid("all", ...Object.values(EAccessCardTypes)).required(),
50415
+ search: Joi87.string().optional().allow("", null)
50416
+ });
50417
+ const { error } = schema2.validate({
50418
+ site,
50419
+ unitId,
50420
+ userType,
50421
+ type,
50422
+ search
50423
+ });
50424
+ if (error) {
50425
+ return res.status(400).json({ message: error.message });
50426
+ }
50427
+ const result = await assignedAccessCardsByUnitSvc({
50428
+ site,
50429
+ unitId,
50430
+ userType,
50431
+ type,
50432
+ search
50433
+ });
50434
+ return res.status(200).json({ message: "Success", data: result });
50435
+ } catch (error) {
50436
+ return res.status(500).json({
50437
+ data: null,
50438
+ message: error.message
50439
+ });
50440
+ }
50441
+ };
50208
50442
  const acknowlegdeCard = async (req, res) => {
50209
50443
  try {
50210
50444
  const { userId, site, cardId } = req.body;
@@ -50849,7 +51083,7 @@ function useAccessManagementController() {
50849
51083
  };
50850
51084
  const assignMultipleCards = async (req, res) => {
50851
51085
  try {
50852
- const { assignees, unit, type, acm_url } = req.body;
51086
+ const { assignees, unit, type, acm_url, id } = req.body;
50853
51087
  const schema2 = Joi87.object({
50854
51088
  assignees: Joi87.array().items(Joi87.string().hex()).required(),
50855
51089
  type: Joi87.string().required(),
@@ -50863,7 +51097,8 @@ function useAccessManagementController() {
50863
51097
  assignees,
50864
51098
  unit,
50865
51099
  type,
50866
- acm_url
51100
+ acm_url,
51101
+ id
50867
51102
  });
50868
51103
  return res.status(200).json({ message: "Success", data: result });
50869
51104
  } catch (error) {
@@ -51044,6 +51279,7 @@ function useAccessManagementController() {
51044
51279
  availableAccessCards,
51045
51280
  userTypeAccessCards,
51046
51281
  assignedAccessCards,
51282
+ assignedAccessCardsByUnit,
51047
51283
  acknowlegdeCard,
51048
51284
  accessandLiftCards,
51049
51285
  replaceCard,
@@ -52433,25 +52669,13 @@ function useBulletinVideoRepo() {
52433
52669
  }
52434
52670
  async function createIndexes() {
52435
52671
  try {
52436
- await collection.createIndexes([{ key: { site: 1 } }]);
52672
+ await collection.createIndexes([{ key: { site: 1, createdAt: -1 } }]);
52437
52673
  } catch (error) {
52438
52674
  throw new InternalServerError51(
52439
52675
  "Failed to create index on bulletin videos."
52440
52676
  );
52441
52677
  }
52442
52678
  }
52443
- async function createTextIndex() {
52444
- try {
52445
- await collection.createIndex({
52446
- title: "text",
52447
- description: "text"
52448
- });
52449
- } catch (error) {
52450
- throw new InternalServerError51(
52451
- "Failed to create text index on bulletin videos."
52452
- );
52453
- }
52454
- }
52455
52679
  const namespace_collection = "bulletin-videos";
52456
52680
  const collection = db.collection(namespace_collection);
52457
52681
  const { delNamespace, getCache, setCache } = useCache51(namespace_collection);
@@ -52497,26 +52721,16 @@ function useBulletinVideoRepo() {
52497
52721
  startingDate.setUTCHours(0, 0, 0, 0);
52498
52722
  const endingDate = new Date(endDate);
52499
52723
  endingDate.setUTCHours(23, 59, 59, 999);
52500
- const parsedCreatedAt = {
52501
- $dateFromString: {
52502
- dateString: "$createdAt",
52503
- onError: /* @__PURE__ */ new Date(0),
52504
- onNull: /* @__PURE__ */ new Date(0)
52505
- }
52506
- };
52507
52724
  dateExpr = {
52508
- $expr: {
52509
- $and: [
52510
- { $gte: [parsedCreatedAt, startingDate] },
52511
- { $lte: [parsedCreatedAt, endingDate] }
52512
- ]
52725
+ createdAt: {
52726
+ $gte: startingDate.toISOString(),
52727
+ $lte: endingDate.toISOString()
52513
52728
  }
52514
52729
  };
52515
52730
  }
52516
52731
  const query = {
52517
52732
  site,
52518
52733
  status: { $ne: "deleted" },
52519
- ...search && { $text: { $search: search } },
52520
52734
  ...dateExpr
52521
52735
  };
52522
52736
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
@@ -52670,8 +52884,7 @@ function useBulletinVideoRepo() {
52670
52884
  getBulletinVideoById,
52671
52885
  updateBulletinVideoById,
52672
52886
  deleteBulletinVideoById,
52673
- createIndexes,
52674
- createTextIndex
52887
+ createIndexes
52675
52888
  };
52676
52889
  }
52677
52890
 
@@ -58242,6 +58455,7 @@ function useNfcPatrolLogRepo() {
58242
58455
  limit = 10,
58243
58456
  site,
58244
58457
  date,
58458
+ type,
58245
58459
  route
58246
58460
  }, session) {
58247
58461
  const pageIndex = page > 0 ? page - 1 : 0;
@@ -58255,7 +58469,18 @@ function useNfcPatrolLogRepo() {
58255
58469
  site: siteId
58256
58470
  };
58257
58471
  if (date) {
58258
- query.date = date;
58472
+ if (type === "month") {
58473
+ const [year, month] = date.split("-").map(Number);
58474
+ const endDay = new Date(year, month, 0).getDate();
58475
+ query.date = {
58476
+ $gte: `${year}-${String(month).padStart(2, "0")}-01`,
58477
+ $lte: `${year}-${String(month).padStart(2, "0")}-${String(
58478
+ endDay
58479
+ ).padStart(2, "0")}`
58480
+ };
58481
+ } else {
58482
+ query.date = date;
58483
+ }
58259
58484
  }
58260
58485
  if (route?._id) {
58261
58486
  query["route._id"] = typeof route._id === "string" ? new ObjectId121(route._id) : route._id;
@@ -58268,13 +58493,17 @@ function useNfcPatrolLogRepo() {
58268
58493
  page: pageIndex,
58269
58494
  limit
58270
58495
  };
58271
- if (date)
58496
+ if (date) {
58272
58497
  cacheOptions.date = date;
58498
+ }
58499
+ if (type) {
58500
+ cacheOptions.type = type;
58501
+ }
58273
58502
  if (route?._id) {
58274
- cacheOptions.routeId = route?._id;
58503
+ cacheOptions.routeId = typeof route._id === "string" ? route._id : route._id.toString();
58275
58504
  }
58276
58505
  if (route?.startTime) {
58277
- cacheOptions.routeStartTime = route?.startTime;
58506
+ cacheOptions.routeStartTime = route.startTime;
58278
58507
  }
58279
58508
  const cacheKey = makeCacheKey58(namespace_collection, cacheOptions);
58280
58509
  const cachedData = await getCache(cacheKey);
@@ -58282,27 +58511,25 @@ function useNfcPatrolLogRepo() {
58282
58511
  logger157.info(`Cache hit for key: ${cacheKey}`);
58283
58512
  return cachedData;
58284
58513
  }
58285
- try {
58286
- const items = await collection.aggregate(
58287
- [
58288
- { $match: query },
58289
- { $sort: { date: -1 } },
58290
- { $skip: pageIndex * limit },
58291
- { $limit: limit }
58292
- ],
58293
- { session }
58294
- ).toArray();
58295
- const length = await collection.countDocuments(query, { session });
58296
- const data = paginate50(items, pageIndex, limit, length);
58297
- setCache(cacheKey, data, 15 * 60).then(() => {
58298
- logger157.info(`Cache set for key: ${cacheKey}`);
58299
- }).catch((err) => {
58300
- logger157.error(`Failed to set cache for key: ${cacheKey}`, err);
58301
- });
58302
- return data;
58303
- } catch (error) {
58304
- throw error;
58305
- }
58514
+ const items = await collection.aggregate(
58515
+ [
58516
+ { $match: query },
58517
+ { $sort: { date: -1 } },
58518
+ { $skip: pageIndex * limit },
58519
+ { $limit: limit }
58520
+ ],
58521
+ { session }
58522
+ ).toArray();
58523
+ const length = await collection.countDocuments(query, {
58524
+ session
58525
+ });
58526
+ const data = paginate50(items, pageIndex, limit, length);
58527
+ setCache(cacheKey, data, 15 * 60).then(() => {
58528
+ logger157.info(`Cache set for key: ${cacheKey}`);
58529
+ }).catch((err) => {
58530
+ logger157.error(`Failed to set cache for key: ${cacheKey}`, err);
58531
+ });
58532
+ return data;
58306
58533
  }
58307
58534
  function delCachedData() {
58308
58535
  delNamespace().then(() => {
@@ -58574,17 +58801,16 @@ function useNfcPatrolLogController() {
58574
58801
  }
58575
58802
  async function getAllBySite(req, res, next) {
58576
58803
  const validation = Joi111.object({
58577
- page: Joi111.number().integer().min(1).allow("", null).default(1),
58578
- limit: Joi111.number().integer().min(1).max(100).allow("", null).default(10),
58804
+ page: Joi111.number().integer().min(1).default(1),
58805
+ limit: Joi111.number().integer().min(1).max(100).default(10),
58579
58806
  site: Joi111.string().length(24).hex().required(),
58580
- date: Joi111.string().regex(/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/).required().messages({
58581
- "string.pattern.base": "Date must be in YYYY-MM-DD format (e.g., 2025-12-30)"
58582
- }).required(),
58807
+ date: Joi111.string().pattern(/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/).required().messages({
58808
+ "string.pattern.base": "Date must be in YYYY-MM-DD format (e.g. 2026-07-17)"
58809
+ }),
58810
+ type: Joi111.string().valid("month").optional(),
58583
58811
  route: Joi111.object({
58584
58812
  _id: Joi111.string().length(24).hex().required(),
58585
- startTime: Joi111.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).messages({
58586
- "string.pattern.base": "Each startTime must be in HH:mm 24-hour format"
58587
- }).required()
58813
+ startTime: Joi111.string().pattern(/^([01]\d|2[0-3]):([0-5]\d)$/).required()
58588
58814
  }).required()
58589
58815
  });
58590
58816
  const query = {