@7365admin1/core 3.53.9 → 3.55.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
@@ -537,13 +537,13 @@ var require_logger = __commonJS({
537
537
  "use strict";
538
538
  exports.__esModule = true;
539
539
  var _utils = require_utils();
540
- var logger220 = {
540
+ var logger222 = {
541
541
  methodMap: ["debug", "info", "warn", "error"],
542
542
  level: "info",
543
543
  // Maps a given level value to the `methodMap` indexes above.
544
544
  lookupLevel: function lookupLevel(level) {
545
545
  if (typeof level === "string") {
546
- var levelMap = _utils.indexOf(logger220.methodMap, level.toLowerCase());
546
+ var levelMap = _utils.indexOf(logger222.methodMap, level.toLowerCase());
547
547
  if (levelMap >= 0) {
548
548
  level = levelMap;
549
549
  } else {
@@ -554,9 +554,9 @@ var require_logger = __commonJS({
554
554
  },
555
555
  // Can be overridden in the host environment
556
556
  log: function log(level) {
557
- level = logger220.lookupLevel(level);
558
- if (typeof console !== "undefined" && logger220.lookupLevel(logger220.level) <= level) {
559
- var method = logger220.methodMap[level];
557
+ level = logger222.lookupLevel(level);
558
+ if (typeof console !== "undefined" && logger222.lookupLevel(logger222.level) <= level) {
559
+ var method = logger222.methodMap[level];
560
560
  if (!console[method]) {
561
561
  method = "log";
562
562
  }
@@ -567,7 +567,7 @@ var require_logger = __commonJS({
567
567
  }
568
568
  }
569
569
  };
570
- exports["default"] = logger220;
570
+ exports["default"] = logger222;
571
571
  module.exports = exports["default"];
572
572
  }
573
573
  });
@@ -13658,6 +13658,8 @@ var NotificationModule = /* @__PURE__ */ ((NotificationModule2) => {
13658
13658
  NotificationModule2["FACILITY_BOOKING"] = "facilityBooking";
13659
13659
  NotificationModule2["ONLINE_FORM"] = "onlineForm";
13660
13660
  NotificationModule2["PRELOVED_MARKETPLACE"] = "prelovedMarketplace";
13661
+ NotificationModule2["MARKETPLACE_ORDER"] = "marketplace-order";
13662
+ NotificationModule2["MARKETPLACE_VENDOR_ORDER"] = "marketplace-vendor-order";
13661
13663
  NotificationModule2["INCIDENT_REPORT"] = "incidentReport";
13662
13664
  NotificationModule2["EMERGENCY_CONTACT"] = "emergencyContact";
13663
13665
  NotificationModule2["VIRTUAL_PATROL"] = "virtualPatrol";
@@ -14126,7 +14128,7 @@ var NOTIFICATION_CATEGORIES = [
14126
14128
  {
14127
14129
  key: "facilityBooking",
14128
14130
  label: "Facility Bookings",
14129
- description: "When a facility booking is made or its status changes.",
14131
+ description: "When a facility booking is made or its status changes. The confirmation email goes to the address entered on the booking form itself.",
14130
14132
  channels: { inApp: true, push: true, email: false },
14131
14133
  channelNote: {
14132
14134
  email: "Booking confirmations go to the email address entered on the booking form itself. To change where a confirmation lands, change that address when the booking is made."
@@ -14138,7 +14140,7 @@ var NOTIFICATION_CATEGORIES = [
14138
14140
  {
14139
14141
  key: "visitors",
14140
14142
  label: "Visitors",
14141
- description: "When a visitor you invited checks in or checks out.",
14143
+ description: "When a visitor you invited checks in or checks out. The invitation email goes to the address entered for the visitor.",
14142
14144
  channels: { inApp: true, push: true, email: false },
14143
14145
  channelNote: {
14144
14146
  email: "Visitor invitations go to the email address entered for the visitor when they are invited. To change where one lands, change that address on the invitation."
@@ -14212,7 +14214,7 @@ var NOTIFICATION_CATEGORIES = [
14212
14214
  {
14213
14215
  key: "serviceProviderInvite",
14214
14216
  label: "Service Provider Invitations",
14215
- description: "When a property manager invites your company to provide services at a site.",
14217
+ description: "When a property manager invites your company to provide services at a site. The invitation email is the invitation itself and is always sent to the address it was addressed to; these settings control the copy that reaches you inside the app.",
14216
14218
  channels: { inApp: true, push: true, email: false },
14217
14219
  channelNote: {
14218
14220
  email: "The invitation email is the invitation itself \u2014 for a company that has no account here yet it is the only copy, so it is always sent to the address the invitation was addressed to. The switches above control the copy that reaches you inside the app."
@@ -14243,12 +14245,15 @@ var NOTIFICATION_CATEGORIES = [
14243
14245
  channelNote: { email: NO_EMAIL_RECIPIENT },
14244
14246
  permissionResources: ["emergency-contact"],
14245
14247
  safety: true,
14246
- // Nothing sends this. The sender below exists, but the only place emergency
14247
- // contacts are written is `iservice365-API-core`, and the change that would
14248
- // have called it from there (API-core #997) was closed, not merged. Until a
14249
- // caller exists this stays out of the screen rather than offering a switch
14250
- // over a message that never arrives.
14251
- rendered: false
14248
+ // A caller exists again. `iservice365-API-core` is still the only place a
14249
+ // site's emergency contacts are written, and its `emergency.service.ts`
14250
+ // now calls the sender below on each of the three writes. That caller
14251
+ // reads THIS flag before it sends (`notificationCategory("emergencyContact")
14252
+ // ?.rendered`), so the two halves cannot land out of order and leave a
14253
+ // safety alert arriving with no switch to turn it off: whichever repository
14254
+ // merges first, the alert only starts once a published core carrying this
14255
+ // line is installed there.
14256
+ rendered: true
14252
14257
  },
14253
14258
  {
14254
14259
  key: "virtualPatrol",
@@ -29392,7 +29397,7 @@ function usePromoCodeRepo() {
29392
29397
  }
29393
29398
  }
29394
29399
  async function updateById(_id, value) {
29395
- const objectId2 = toObjectId25(_id);
29400
+ const objectId2 = toObjectId26(_id);
29396
29401
  const changes = promoCodeUpdate(value);
29397
29402
  const res = await collection.updateOne(
29398
29403
  { _id: objectId2, deletedAt: null },
@@ -29405,7 +29410,7 @@ function usePromoCodeRepo() {
29405
29410
  return res;
29406
29411
  }
29407
29412
  async function updateStatusById(_id, status) {
29408
- const objectId2 = toObjectId25(_id);
29413
+ const objectId2 = toObjectId26(_id);
29409
29414
  const res = await collection.updateOne(
29410
29415
  { _id: objectId2, deletedAt: null },
29411
29416
  { $set: { status } }
@@ -29417,7 +29422,7 @@ function usePromoCodeRepo() {
29417
29422
  return res;
29418
29423
  }
29419
29424
  async function softDeleteById(_id, deletedBy) {
29420
- const objectId2 = toObjectId25(_id);
29425
+ const objectId2 = toObjectId26(_id);
29421
29426
  const res = await collection.updateOne(
29422
29427
  { _id: objectId2, deletedAt: null },
29423
29428
  {
@@ -29434,7 +29439,7 @@ function usePromoCodeRepo() {
29434
29439
  await invalidate(`delete ${objectId2}`);
29435
29440
  return res;
29436
29441
  }
29437
- function toObjectId25(_id) {
29442
+ function toObjectId26(_id) {
29438
29443
  const { error } = Joi34.object({
29439
29444
  _id: Joi34.string().hex().length(24).required()
29440
29445
  }).validate({ _id: String(_id) });
@@ -92179,8 +92184,10 @@ function useNotificationPreferenceService() {
92179
92184
  categoriesForUser(id)
92180
92185
  ]);
92181
92186
  const isOff = (category, channel) => off.some((o) => o.category === category && o.channel === channel);
92187
+ const supportedIn = (category) => NOTIFICATION_CHANNELS.filter((channel) => category.channels[channel]);
92188
+ const offered = new Set(categories.flatMap((c) => supportedIn(c)));
92182
92189
  return {
92183
- channels: NOTIFICATION_CHANNELS.map((c) => ({
92190
+ channels: NOTIFICATION_CHANNELS.filter((c) => offered.has(c)).map((c) => ({
92184
92191
  key: c,
92185
92192
  label: NOTIFICATION_CHANNEL_LABELS[c]
92186
92193
  })),
@@ -92189,16 +92196,14 @@ function useNotificationPreferenceService() {
92189
92196
  label: category.label,
92190
92197
  description: category.description,
92191
92198
  safety: Boolean(category.safety),
92192
- channels: NOTIFICATION_CHANNELS.map((channel) => {
92193
- const supported = category.channels[channel];
92194
- return {
92195
- key: channel,
92196
- label: NOTIFICATION_CHANNEL_LABELS[channel],
92197
- supported,
92198
- note: supported ? void 0 : category.channelNote?.[channel],
92199
- enabled: supported && !isOff(category.key, channel)
92200
- };
92201
- })
92199
+ channels: supportedIn(category).map((channel) => ({
92200
+ key: channel,
92201
+ label: NOTIFICATION_CHANNEL_LABELS[channel],
92202
+ // kept so a client written against the old shape still filters
92203
+ // correctly; every channel sent is now a supported one.
92204
+ supported: true,
92205
+ enabled: !isOff(category.key, channel)
92206
+ }))
92202
92207
  }))
92203
92208
  };
92204
92209
  }
@@ -92261,6 +92266,228 @@ function useNotificationPreferenceController() {
92261
92266
  }
92262
92267
  return { get, update };
92263
92268
  }
92269
+
92270
+ // src/models/personal-emergency-chain.model.ts
92271
+ import { BadRequestError as BadRequestError262 } from "@7365admin1/node-server-utils";
92272
+ import { ObjectId as ObjectId184 } from "mongodb";
92273
+ import Joi167 from "joi";
92274
+ var MAX_PERSONAL_EMERGENCY_CONTACTS = 10;
92275
+ var MIN_RING_SECONDS = 5;
92276
+ var MAX_RING_SECONDS = 120;
92277
+ var DEFAULT_RING_SECONDS = 20;
92278
+ var E164 = /^\+[1-9]\d{1,14}$/;
92279
+ var schemaPersonalEmergencyContact = Joi167.object({
92280
+ name: Joi167.string().trim().min(1).max(120).required(),
92281
+ phone: Joi167.string().trim().pattern(E164).required().messages({
92282
+ "string.pattern.base": "Phone number must be in international format, e.g. +6591234567."
92283
+ }),
92284
+ relationship: Joi167.string().trim().max(60).allow("", null).default(""),
92285
+ order: Joi167.number().integer().min(1).max(MAX_PERSONAL_EMERGENCY_CONTACTS).required(),
92286
+ active: Joi167.boolean().default(true)
92287
+ });
92288
+ var schemaUpdatePersonalEmergencyChain = Joi167.object({
92289
+ contacts: Joi167.array().items(schemaPersonalEmergencyContact).unique("order").max(MAX_PERSONAL_EMERGENCY_CONTACTS).required().messages({
92290
+ "array.unique": "Two contacts cannot share the same position in the chain."
92291
+ }),
92292
+ ringSeconds: Joi167.number().integer().min(MIN_RING_SECONDS).max(MAX_RING_SECONDS).default(DEFAULT_RING_SECONDS),
92293
+ siteDirectoryFallback: Joi167.boolean().default(true),
92294
+ org: Joi167.string().hex().length(24).allow("", null).default(null),
92295
+ site: Joi167.string().hex().length(24).allow("", null).default(null)
92296
+ });
92297
+ var toObjectId25 = (value, what) => {
92298
+ if (value === null || value === void 0 || value === "")
92299
+ return null;
92300
+ const id = value.toString();
92301
+ if (!ObjectId184.isValid(id))
92302
+ throw new BadRequestError262(`Invalid ${what}.`);
92303
+ return new ObjectId184(id);
92304
+ };
92305
+ function MPersonalEmergencyChain(value) {
92306
+ const user = toObjectId25(value.user, "user ID");
92307
+ if (!user)
92308
+ throw new BadRequestError262("Invalid user ID.");
92309
+ const contacts = [...value.contacts ?? []].map((c) => ({
92310
+ name: c.name.trim(),
92311
+ phone: c.phone.trim(),
92312
+ relationship: (c.relationship ?? "").trim(),
92313
+ order: c.order,
92314
+ active: c.active ?? true
92315
+ })).sort((a, b) => a.order - b.order);
92316
+ const seen = /* @__PURE__ */ new Set();
92317
+ for (const contact of contacts) {
92318
+ if (seen.has(contact.order)) {
92319
+ throw new BadRequestError262(
92320
+ "Two contacts cannot share the same position in the chain."
92321
+ );
92322
+ }
92323
+ seen.add(contact.order);
92324
+ }
92325
+ const now = /* @__PURE__ */ new Date();
92326
+ return {
92327
+ user,
92328
+ org: toObjectId25(value.org, "organization ID"),
92329
+ site: toObjectId25(value.site, "site ID"),
92330
+ contacts,
92331
+ ringSeconds: value.ringSeconds ?? DEFAULT_RING_SECONDS,
92332
+ siteDirectoryFallback: value.siteDirectoryFallback ?? true,
92333
+ createdAt: value.createdAt ?? now,
92334
+ updatedAt: now
92335
+ };
92336
+ }
92337
+ function emptyPersonalEmergencyChain(user) {
92338
+ return {
92339
+ user,
92340
+ org: null,
92341
+ site: null,
92342
+ contacts: [],
92343
+ ringSeconds: DEFAULT_RING_SECONDS,
92344
+ siteDirectoryFallback: true
92345
+ };
92346
+ }
92347
+
92348
+ // src/repositories/personal-emergency-chain.repo.ts
92349
+ import { ObjectId as ObjectId185 } from "mongodb";
92350
+ import {
92351
+ AppError as AppError34,
92352
+ BadRequestError as BadRequestError263,
92353
+ InternalServerError as InternalServerError97,
92354
+ logger as logger220,
92355
+ useAtlas as useAtlas152
92356
+ } from "@7365admin1/node-server-utils";
92357
+ function usePersonalEmergencyChainRepo() {
92358
+ const namespace_collection = "personal-emergency-contacts";
92359
+ const getDB2 = () => {
92360
+ const db2 = useAtlas152.getDb();
92361
+ if (!db2) {
92362
+ throw new InternalServerError97("Unable to connect to server.");
92363
+ }
92364
+ return db2;
92365
+ };
92366
+ const collection = () => getDB2().collection(namespace_collection);
92367
+ async function createIndexes() {
92368
+ try {
92369
+ await collection().createIndexes([
92370
+ { key: { user: 1 }, name: "one_chain_per_user", unique: true }
92371
+ ]);
92372
+ return `Successfully created indexes for ${namespace_collection}.`;
92373
+ } catch (error) {
92374
+ logger220.log({ level: "error", message: error.message });
92375
+ throw new InternalServerError97(
92376
+ `Failed to create indexes for ${namespace_collection}.`
92377
+ );
92378
+ }
92379
+ }
92380
+ async function getByUser(user) {
92381
+ if (!ObjectId185.isValid(user)) {
92382
+ throw new BadRequestError263("Invalid user ID format.");
92383
+ }
92384
+ try {
92385
+ const doc = await collection().findOne({ user: new ObjectId185(user) });
92386
+ return doc ?? emptyPersonalEmergencyChain(user);
92387
+ } catch (error) {
92388
+ if (error instanceof AppError34)
92389
+ throw error;
92390
+ logger220.log({ level: "error", message: error.message });
92391
+ throw new InternalServerError97("Failed to retrieve emergency contacts.");
92392
+ }
92393
+ }
92394
+ async function replaceForUser(user, value) {
92395
+ const doc = MPersonalEmergencyChain({ ...value, user });
92396
+ try {
92397
+ await collection().updateOne(
92398
+ { user: doc.user },
92399
+ {
92400
+ $set: {
92401
+ contacts: doc.contacts,
92402
+ ringSeconds: doc.ringSeconds,
92403
+ siteDirectoryFallback: doc.siteDirectoryFallback,
92404
+ org: doc.org,
92405
+ site: doc.site,
92406
+ updatedAt: doc.updatedAt
92407
+ },
92408
+ $setOnInsert: { user: doc.user, createdAt: doc.createdAt }
92409
+ },
92410
+ { upsert: true }
92411
+ );
92412
+ return doc;
92413
+ } catch (error) {
92414
+ if (error instanceof AppError34)
92415
+ throw error;
92416
+ logger220.log({ level: "error", message: error.message });
92417
+ throw new InternalServerError97("Failed to save emergency contacts.");
92418
+ }
92419
+ }
92420
+ return { createIndexes, getByUser, replaceForUser };
92421
+ }
92422
+
92423
+ // src/services/personal-emergency-chain.service.ts
92424
+ import { BadRequestError as BadRequestError264 } from "@7365admin1/node-server-utils";
92425
+ function usePersonalEmergencyChainService() {
92426
+ async function getForUser(user) {
92427
+ if (!user)
92428
+ throw new BadRequestError264("No user in session.");
92429
+ return await usePersonalEmergencyChainRepo().getByUser(user);
92430
+ }
92431
+ async function updateForUser(user, value) {
92432
+ if (!user)
92433
+ throw new BadRequestError264("No user in session.");
92434
+ return await usePersonalEmergencyChainRepo().replaceForUser(
92435
+ user,
92436
+ value
92437
+ );
92438
+ }
92439
+ return { getForUser, updateForUser };
92440
+ }
92441
+
92442
+ // src/controllers/personal-emergency-chain.controller.ts
92443
+ import { BadRequestError as BadRequestError265, logger as logger221 } from "@7365admin1/node-server-utils";
92444
+ function usePersonalEmergencyChainController() {
92445
+ const { getForUser, updateForUser } = usePersonalEmergencyChainService();
92446
+ async function get(req, res, next) {
92447
+ const userId = req.params.userId;
92448
+ try {
92449
+ await requireSelfOrPlatformStaff(req, userId, {
92450
+ resource: "users",
92451
+ action: "see-user-details"
92452
+ });
92453
+ res.status(200).json({ data: await getForUser(userId) });
92454
+ } catch (error) {
92455
+ logger221.log({ level: "error", message: error.message });
92456
+ next(error);
92457
+ }
92458
+ }
92459
+ async function update(req, res, next) {
92460
+ const userId = req.params.userId;
92461
+ try {
92462
+ await requireSelfOrPlatformStaff(req, userId, {
92463
+ resource: "users",
92464
+ action: "see-user-details"
92465
+ });
92466
+ } catch (error2) {
92467
+ logger221.log({ level: "error", message: error2.message });
92468
+ next(error2);
92469
+ return;
92470
+ }
92471
+ const { error, value } = schemaUpdatePersonalEmergencyChain.validate(
92472
+ req.body,
92473
+ { convert: true }
92474
+ );
92475
+ if (error) {
92476
+ next(new BadRequestError265(error.details[0].message));
92477
+ return;
92478
+ }
92479
+ try {
92480
+ res.status(200).json({
92481
+ message: "success",
92482
+ data: await updateForUser(userId, value)
92483
+ });
92484
+ } catch (err) {
92485
+ logger221.log({ level: "error", message: err.message });
92486
+ next(err);
92487
+ }
92488
+ }
92489
+ return { get, update };
92490
+ }
92264
92491
  export {
92265
92492
  ANPRMode,
92266
92493
  APP_BASE_URLS,
@@ -92306,11 +92533,13 @@ export {
92306
92533
  CameraType,
92307
92534
  ConsoleAuditAction,
92308
92535
  ConsoleAuditTarget,
92536
+ DEFAULT_RING_SECONDS,
92309
92537
  DEFAULT_SITE_TIMEZONE,
92310
92538
  DEVICE_STATUS,
92311
92539
  DOBStatus,
92312
92540
  DUPLICATE_TERMS_VERSION_MESSAGE,
92313
92541
  DayOfWeek,
92542
+ E164,
92314
92543
  EAccessCardTypes,
92315
92544
  EAccessCardUserTypes,
92316
92545
  EmailSender,
@@ -92333,6 +92562,8 @@ export {
92333
92562
  MAX_BULK_CAMERA_ROWS,
92334
92563
  MAX_CAMERA_CHANNEL,
92335
92564
  MAX_CAMERA_NAME_LENGTH,
92565
+ MAX_PERSONAL_EMERGENCY_CONTACTS,
92566
+ MAX_RING_SECONDS,
92336
92567
  MAccessCard,
92337
92568
  MAccessCardTransaction,
92338
92569
  MAddress,
@@ -92366,6 +92597,7 @@ export {
92366
92597
  MHidAmicoReader,
92367
92598
  MHidSipAccount,
92368
92599
  MHidSitePermissions,
92600
+ MIN_RING_SECONDS,
92369
92601
  MIncidentReport,
92370
92602
  MManpowerDesignations,
92371
92603
  MManpowerMonitoring,
@@ -92391,6 +92623,7 @@ export {
92391
92623
  MPatrolQuestion,
92392
92624
  MPatrolRoute,
92393
92625
  MPerson,
92626
+ MPersonalEmergencyChain,
92394
92627
  MPlatformTerms,
92395
92628
  MPost,
92396
92629
  MPostFavorite,
@@ -92533,6 +92766,7 @@ export {
92533
92766
  deviceHttpTimeoutMs,
92534
92767
  deviceProbeTtlSeconds,
92535
92768
  emitNotificationCreated,
92769
+ emptyPersonalEmergencyChain,
92536
92770
  encodeHidPacsCard,
92537
92771
  entitledSiteScope,
92538
92772
  events_namespace_collection,
@@ -92715,6 +92949,7 @@ export {
92715
92949
  schemaPatrolQuestion,
92716
92950
  schemaPatrolRoute,
92717
92951
  schemaPerson,
92952
+ schemaPersonalEmergencyContact,
92718
92953
  schemaPlate,
92719
92954
  schemaPlatformTerms,
92720
92955
  schemaPost,
@@ -92760,6 +92995,7 @@ export {
92760
92995
  schemaUpdatePatrolQuestion,
92761
92996
  schemaUpdatePatrolRoute,
92762
92997
  schemaUpdatePerson,
92998
+ schemaUpdatePersonalEmergencyChain,
92763
92999
  schemaUpdatePost,
92764
93000
  schemaUpdatePostFavorite,
92765
93001
  schemaUpdateServiceProviderBilling,
@@ -92940,6 +93176,9 @@ export {
92940
93176
  usePatrolRouteRepo,
92941
93177
  usePersonController,
92942
93178
  usePersonRepo,
93179
+ usePersonalEmergencyChainController,
93180
+ usePersonalEmergencyChainRepo,
93181
+ usePersonalEmergencyChainService,
92943
93182
  usePlatformTermsController,
92944
93183
  usePlatformTermsRepo,
92945
93184
  usePlatformTermsService,