@7365admin1/core 3.52.0 → 3.52.1-staging.231

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.d.ts CHANGED
@@ -4664,6 +4664,7 @@ type TVisitorTransaction = {
4664
4664
  status?: VisitorStatus;
4665
4665
  nric?: string;
4666
4666
  remarks?: string;
4667
+ invitationNote?: string;
4667
4668
  org?: string | ObjectId;
4668
4669
  site?: string | ObjectId;
4669
4670
  plateNumber?: string;
@@ -4743,6 +4744,7 @@ declare function MVisitorTransaction(value: TVisitorTransaction): {
4743
4744
  checkOut: string | Date | null | undefined;
4744
4745
  status: VisitorStatus | undefined;
4745
4746
  remarks: string | undefined;
4747
+ invitationNote: string | undefined;
4746
4748
  deliveryType: string | undefined;
4747
4749
  attachments: string[] | undefined;
4748
4750
  snapshotEntryImage: string | undefined;
@@ -4822,7 +4824,7 @@ declare function useVisitorTransactionRepo(): {
4822
4824
  checkOutBySiteAndPlate: (site: string | ObjectId, plateNumber: string, session?: ClientSession) => Promise<mongodb.WithId<bson.Document> | null>;
4823
4825
  resendEmailInvitedResident: (visitorId: string, updatedBy: string | ObjectId) => Promise<any>;
4824
4826
  getSelfServiceVisitorPreview: (id: string | ObjectId) => Promise<mongodb.WithId<bson.Document> | null>;
4825
- patchFields: (id: string | ObjectId, fields: Record<string, any>) => Promise<void>;
4827
+ patchFields: (id: string | ObjectId, fields: Record<string, any>, session?: ClientSession) => Promise<void>;
4826
4828
  updateStatusByLeadId: (leadId: string | ObjectId, status: string) => Promise<void>;
4827
4829
  };
4828
4830
 
@@ -4831,6 +4833,7 @@ declare function useVisitorTransactionService(): {
4831
4833
  updateVisitorTransactionById: (id: string | ObjectId, value: TVisitorTransaction) => Promise<string>;
4832
4834
  processTransactionDahuaStatus: () => Promise<void>;
4833
4835
  inviteVisitor: (value: TVisitorTransaction, userId: string) => Promise<ObjectId[]>;
4836
+ createInvitedMemberDocuments: (leadId: string | ObjectId, status: VisitorStatus) => Promise<ObjectId[]>;
4834
4837
  deleteVisitorTransaction: (id: string) => Promise<void>;
4835
4838
  };
4836
4839
 
@@ -7022,6 +7025,10 @@ interface AdditionalSettings {
7022
7025
  vendorId: string | null | number;
7023
7026
  productId: string | null | number;
7024
7027
  };
7028
+ invitationNote?: {
7029
+ isEnabled: boolean;
7030
+ text: string;
7031
+ };
7025
7032
  };
7026
7033
  }
7027
7034
  type TAccessMngmntSettings = {
package/dist/index.js CHANGED
@@ -19589,6 +19589,7 @@ var schemaVisitorTransaction = import_joi21.default.object({
19589
19589
  deliveryType: import_joi21.default.string().optional().allow(null, ""),
19590
19590
  status: import_joi21.default.string().optional().valid(...Object.values(VisitorStatus)).default("registered" /* REGISTERED */),
19591
19591
  remarks: import_joi21.default.string().optional().allow(null, ""),
19592
+ invitationNote: import_joi21.default.string().optional().allow(null, ""),
19592
19593
  members: import_joi21.default.array().items(
19593
19594
  import_joi21.default.object({
19594
19595
  name: import_joi21.default.string().optional().allow(null, ""),
@@ -19899,6 +19900,7 @@ function MVisitorTransaction(value) {
19899
19900
  checkOut: value.checkOut,
19900
19901
  status: value.status,
19901
19902
  remarks: value.remarks,
19903
+ invitationNote: value.invitationNote,
19902
19904
  deliveryType: value.deliveryType,
19903
19905
  attachments: value.attachments,
19904
19906
  snapshotEntryImage: value.snapshotEntryImage,
@@ -20842,6 +20844,7 @@ function useVisitorTransactionRepo() {
20842
20844
  arrivalTime: 1,
20843
20845
  duration: 1,
20844
20846
  purpose: 1,
20847
+ invitationNote: 1,
20845
20848
  checkIn: 1
20846
20849
  }
20847
20850
  }
@@ -20850,10 +20853,10 @@ function useVisitorTransactionRepo() {
20850
20853
  throw new Error(error.message);
20851
20854
  }
20852
20855
  }
20853
- async function patchFields(id, fields) {
20856
+ async function patchFields(id, fields, session) {
20854
20857
  const _id = (0, import_node_server_utils48.toObjectId)(id);
20855
20858
  try {
20856
- await collection.updateOne({ _id }, { $set: fields });
20859
+ await collection.updateOne({ _id }, { $set: fields }, { session });
20857
20860
  } catch (error) {
20858
20861
  throw new Error(error.message);
20859
20862
  }
@@ -50480,7 +50483,8 @@ function useVisitorTransactionService() {
50480
50483
  getExpiredCheckedOutTransactionsBySite: _getExpiredCheckedOutTransactionsBySite,
50481
50484
  updateManyDahuaSyncStatus: _updateManyDahuaSyncStatus,
50482
50485
  getVisitorTransactionById: _getVisitorTransactionById,
50483
- deleteVisitorTransaction: _deleteVisitorTransaction
50486
+ deleteVisitorTransaction: _deleteVisitorTransaction,
50487
+ patchFields: _patchFields
50484
50488
  } = useVisitorTransactionRepo();
50485
50489
  const { getById } = useBuildingUnitRepo();
50486
50490
  const {
@@ -50608,6 +50612,7 @@ function useVisitorTransactionService() {
50608
50612
  type: type2,
50609
50613
  company: company2,
50610
50614
  remarks,
50615
+ invitationNote,
50611
50616
  contractorType,
50612
50617
  unitName
50613
50618
  } = value;
@@ -50654,6 +50659,10 @@ function useVisitorTransactionService() {
50654
50659
  type: type2,
50655
50660
  company: company2,
50656
50661
  remarks,
50662
+ // Every member holds their OWN invitation/pass link, so the
50663
+ // site's note has to ride along to each of them - otherwise
50664
+ // only the lead contractor ever sees it.
50665
+ invitationNote,
50657
50666
  contractorType,
50658
50667
  checkIn: start,
50659
50668
  visitorPass: preparedVisitorPass,
@@ -51321,6 +51330,79 @@ function useVisitorTransactionService() {
51321
51330
  session?.endSession();
51322
51331
  }
51323
51332
  }
51333
+ async function createInvitedMemberDocuments(leadId, status) {
51334
+ const lead = await _getVisitorTransactionById(String(leadId));
51335
+ if (!lead)
51336
+ return [];
51337
+ const members = Array.isArray(lead.members) ? lead.members : [];
51338
+ if (members.length === 0)
51339
+ return [];
51340
+ if (!members.every((member) => !member._id))
51341
+ return [];
51342
+ const session = import_node_server_utils134.useAtlas.getClient()?.startSession();
51343
+ if (!session) {
51344
+ throw new Error("Unable to start session for people service.");
51345
+ }
51346
+ try {
51347
+ session.startTransaction();
51348
+ const memberPayload = {
51349
+ block: lead.block ? String(lead.block) : void 0,
51350
+ level: lead.level ? String(lead.level) : void 0,
51351
+ unit: lead.unit ? String(lead.unit) : void 0,
51352
+ unitName: lead.unitName,
51353
+ org: lead.org ? String(lead.org) : void 0,
51354
+ site: lead.site ? String(lead.site) : void 0,
51355
+ type: lead.type,
51356
+ contractorType: lead.contractorType,
51357
+ company: lead.company,
51358
+ purpose: lead.purpose,
51359
+ expectedCheckIn: lead.expectedCheckIn ? new Date(lead.expectedCheckIn).toISOString() : void 0,
51360
+ arrivalTime: lead.arrivalTime,
51361
+ duration: lead.duration,
51362
+ isOvernightParking: lead.isOvernightParking,
51363
+ numberOfPassengers: lead.numberOfPassengers,
51364
+ // Site-configured note from the self-service settings. Each member
51365
+ // holds their own pass link, so the note has to ride along to all of
51366
+ // them - the same thing the creation-time fan-out does. Absent on
51367
+ // resident invitations, which never set it.
51368
+ invitationNote: lead.invitationNote,
51369
+ status,
51370
+ ...lead.inviterId ? { inviterId: String(lead.inviterId) } : {},
51371
+ leadId: String(lead._id),
51372
+ // Explicit - MVisitorTransaction defaults an undefined checkIn to now,
51373
+ // and an approved invitation hasn't arrived yet.
51374
+ checkIn: null
51375
+ };
51376
+ const insertedIds = [];
51377
+ for (const member of members) {
51378
+ const memberId = await _add(
51379
+ {
51380
+ ...memberPayload,
51381
+ name: member.name ?? "",
51382
+ nric: member.nric ?? "",
51383
+ contact: member.contact ?? "",
51384
+ email: member.email ?? "",
51385
+ plateNumber: member.plateNumber ?? ""
51386
+ },
51387
+ session
51388
+ );
51389
+ member._id = memberId;
51390
+ insertedIds.push(memberId);
51391
+ }
51392
+ await _patchFields(leadId, { members }, session);
51393
+ await session.commitTransaction();
51394
+ return insertedIds;
51395
+ } catch (error) {
51396
+ import_node_server_utils134.logger.error(
51397
+ "Error creating member documents for approved invitation:",
51398
+ error
51399
+ );
51400
+ await session.abortTransaction();
51401
+ throw error;
51402
+ } finally {
51403
+ session?.endSession();
51404
+ }
51405
+ }
51324
51406
  async function deleteVisitorTransaction(id) {
51325
51407
  const visitor = await _getVisitorTransactionById(id);
51326
51408
  if (!visitor) {
@@ -51353,6 +51435,7 @@ function useVisitorTransactionService() {
51353
51435
  updateVisitorTransactionById,
51354
51436
  processTransactionDahuaStatus,
51355
51437
  inviteVisitor,
51438
+ createInvitedMemberDocuments,
51356
51439
  deleteVisitorTransaction
51357
51440
  // changeVisitorTransactionKeysById,
51358
51441
  };
@@ -55312,6 +55395,7 @@ function useVisitorTransactionController() {
55312
55395
  add: _add,
55313
55396
  updateVisitorTransactionById: _updateVisitorTransactionById,
55314
55397
  inviteVisitor: _inviteVisitor,
55398
+ createInvitedMemberDocuments: _createInvitedMemberDocuments,
55315
55399
  deleteVisitorTransaction: _deleteVisitorTransaction
55316
55400
  // changeVisitorTransactionKeysById: _changeVisitorTransactionKeysById,
55317
55401
  } = useVisitorTransactionService();
@@ -55375,12 +55459,17 @@ function useVisitorTransactionController() {
55375
55459
  return;
55376
55460
  }
55377
55461
  const leadId = new import_mongodb88.ObjectId();
55462
+ const invitationNote = value.type === "contractor" /* CONTRACTOR */ && onboardingSettings?.invitationNote?.isEnabled === true ? String(onboardingSettings.invitationNote.text ?? "").trim() : "";
55463
+ const members = Array.isArray(value.members) ? value.members : [];
55464
+ const deferMembers = value.type === "contractor" /* CONTRACTOR */ && members.length > 0 && status !== "approved" /* APPROVED */;
55465
+ const { members: _deferredMembers, ...valueWithoutMembers } = value;
55378
55466
  const insertedId = await _add({
55379
- ...value,
55467
+ ...deferMembers ? valueWithoutMembers : value,
55380
55468
  _id: leadId.toString(),
55381
55469
  org: site.orgId.toString(),
55382
55470
  site: siteId,
55383
55471
  status,
55472
+ ...invitationNote ? { invitationNote } : {},
55384
55473
  // MVisitorTransaction() defaults checkIn to "now" whenever it's
55385
55474
  // undefined - correct for the staff "walk-in" flow, where creating
55386
55475
  // the record IS the check-in event, but wrong here: a self-service
@@ -55389,8 +55478,10 @@ function useVisitorTransactionController() {
55389
55478
  // checked in later (inline button / Guests-tab action menu).
55390
55479
  checkIn: null
55391
55480
  });
55392
- const members = Array.isArray(value.members) ? value.members : [];
55393
- if (value.type === "contractor" /* CONTRACTOR */ && members.length) {
55481
+ if (deferMembers) {
55482
+ await _patchFields(insertedId, { members });
55483
+ }
55484
+ if (!deferMembers && value.type === "contractor" /* CONTRACTOR */ && members.length) {
55394
55485
  await Promise.all(
55395
55486
  members.filter((m) => m._id).map((m) => _patchFields(m._id, { checkIn: null, status }))
55396
55487
  );
@@ -55475,6 +55566,11 @@ function useVisitorTransactionController() {
55475
55566
  arrivalTime: data.arrivalTime || null,
55476
55567
  duration: data.duration ?? null,
55477
55568
  purpose: data.purpose || null,
55569
+ // Set by the site, not the registrant - see the Invitation Note
55570
+ // block in `createSelfServiceVisitor`. Safe to hand back here:
55571
+ // this is exactly the invitation link the note was written for,
55572
+ // and it's only reachable by someone already holding the ID.
55573
+ invitationNote: data.invitationNote || null,
55478
55574
  // Public response exposes whether check-in happened, not the raw
55479
55575
  // timestamp - the preview page needs it to know an already-arrived
55480
55576
  // visitor's lapsed arrival window isn't the same thing as an
@@ -55636,8 +55732,36 @@ function useVisitorTransactionController() {
55636
55732
  const remarks = approval?.remarks ?? req.body.remarks;
55637
55733
  const isApprovalTransition = nextStatus === "approved" /* APPROVED */ || nextStatus === "rejected" /* REJECTED */;
55638
55734
  const current = isApprovalTransition ? await _getVisitorTransactionById(_id) : null;
55639
- const updateBody = approval ? { ...req.body, status: nextStatus } : req.body;
55735
+ const needsMemberFanOut = nextStatus === "approved" /* APPROVED */ && (!!current?.inviterId || current?.type === "contractor" /* CONTRACTOR */) && Array.isArray(current?.members) && current.members.length > 0 && current.members.every((member) => !member._id);
55736
+ const { members: _unusedMembers, ...bodyWithoutMembers } = req.body;
55737
+ const baseBody = needsMemberFanOut ? bodyWithoutMembers : req.body;
55738
+ const updateBody = approval ? { ...baseBody, status: nextStatus } : baseBody;
55640
55739
  await _updateVisitorTransactionById(_id, updateBody);
55740
+ let invitedMemberIds = [];
55741
+ if (needsMemberFanOut) {
55742
+ try {
55743
+ invitedMemberIds = await _createInvitedMemberDocuments(
55744
+ _id,
55745
+ nextStatus
55746
+ );
55747
+ } catch (fanOutError) {
55748
+ import_node_server_utils137.logger.log({
55749
+ level: "error",
55750
+ message: `Failed to create member documents for approved invitation ${_id}: ${fanOutError?.message}`
55751
+ });
55752
+ }
55753
+ }
55754
+ for (const insertedId of current?.inviterId ? invitedMemberIds : []) {
55755
+ resendEmailInvitedResident(
55756
+ String(insertedId),
55757
+ String(current?.inviterId)
55758
+ ).catch((error2) => {
55759
+ import_node_server_utils137.logger.log({
55760
+ level: "error",
55761
+ message: `Failed to send invitation email: ${error2?.message}`
55762
+ });
55763
+ });
55764
+ }
55641
55765
  if (isApprovalTransition && current && current.type === "contractor" /* CONTRACTOR */) {
55642
55766
  try {
55643
55767
  await _updateStatusByLeadId(_id, nextStatus);
@@ -67962,7 +68086,14 @@ function useAccessManagementController() {
67962
68086
  return res.status(400).json({ message: error.message });
67963
68087
  }
67964
68088
  const result = await getOnboardingSettingsSvc(site);
67965
- return res.status(200).json({ message: "Success", data: result });
68089
+ return res.status(200).json({
68090
+ message: "Success",
68091
+ data: {
68092
+ enabled: result?.enabled ?? false,
68093
+ header: result?.header ?? "",
68094
+ subText: result?.subText ?? ""
68095
+ }
68096
+ });
67966
68097
  } catch (error) {
67967
68098
  return res.status(500).json({
67968
68099
  data: null,