@htlkg/data 0.0.19 → 0.0.21
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/client/index.d.ts +257 -1
- package/dist/client/index.js +59 -1
- package/dist/client/index.js.map +1 -1
- package/dist/common-DSxswsZ3.d.ts +40 -0
- package/dist/hooks/index.d.ts +162 -10
- package/dist/hooks/index.js +191 -33
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +789 -13
- package/dist/index.js.map +1 -1
- package/dist/mutations/index.d.ts +342 -4
- package/dist/mutations/index.js +486 -0
- package/dist/mutations/index.js.map +1 -1
- package/dist/{productInstances-BA3cNsYc.d.ts → productInstances-BpQv1oLS.d.ts} +2 -40
- package/dist/queries/index.d.ts +113 -2
- package/dist/queries/index.js +192 -1
- package/dist/queries/index.js.map +1 -1
- package/dist/reservations-C0FNm__0.d.ts +154 -0
- package/dist/reservations-CdDfkcZ_.d.ts +172 -0
- package/package.json +14 -13
- package/src/client/index.ts +18 -0
- package/src/client/reservations.ts +336 -0
- package/src/hooks/createDataHook.test.ts +534 -0
- package/src/hooks/createDataHook.ts +20 -13
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useContacts.test.ts +159 -0
- package/src/hooks/useContacts.ts +176 -0
- package/src/hooks/useReservations.ts +145 -0
- package/src/mutations/contacts.test.ts +604 -0
- package/src/mutations/contacts.ts +554 -0
- package/src/mutations/index.ts +32 -0
- package/src/mutations/productInstances/productInstances.test.ts +3 -3
- package/src/mutations/reservations.test.ts +459 -0
- package/src/mutations/reservations.ts +452 -0
- package/src/queries/contacts.test.ts +505 -0
- package/src/queries/contacts.ts +237 -0
- package/src/queries/index.ts +21 -0
- package/src/queries/reservations.test.ts +374 -0
- package/src/queries/reservations.ts +247 -0
package/dist/mutations/index.js
CHANGED
|
@@ -509,26 +509,512 @@ async function initializeSystemSettings(client, initializedBy) {
|
|
|
509
509
|
throw error;
|
|
510
510
|
}
|
|
511
511
|
}
|
|
512
|
+
|
|
513
|
+
// src/mutations/reservations.ts
|
|
514
|
+
var STATUS_TRANSITIONS = {
|
|
515
|
+
confirmed: ["checked_in", "cancelled", "no_show"],
|
|
516
|
+
checked_in: ["checked_out", "cancelled"],
|
|
517
|
+
checked_out: [],
|
|
518
|
+
// Terminal state - no transitions allowed
|
|
519
|
+
cancelled: [],
|
|
520
|
+
// Terminal state - no transitions allowed
|
|
521
|
+
no_show: []
|
|
522
|
+
// Terminal state - no transitions allowed
|
|
523
|
+
};
|
|
524
|
+
var ReservationValidationError = class extends Error {
|
|
525
|
+
constructor(message) {
|
|
526
|
+
super(message);
|
|
527
|
+
this.name = "ReservationValidationError";
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
function validateDates(checkIn, checkOut) {
|
|
531
|
+
const checkInDate = new Date(checkIn);
|
|
532
|
+
const checkOutDate = new Date(checkOut);
|
|
533
|
+
if (isNaN(checkInDate.getTime())) {
|
|
534
|
+
throw new ReservationValidationError("Invalid check-in date");
|
|
535
|
+
}
|
|
536
|
+
if (isNaN(checkOutDate.getTime())) {
|
|
537
|
+
throw new ReservationValidationError("Invalid check-out date");
|
|
538
|
+
}
|
|
539
|
+
if (checkOutDate <= checkInDate) {
|
|
540
|
+
throw new ReservationValidationError(
|
|
541
|
+
"Check-out date must be after check-in date"
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
const minStayHours = 4;
|
|
545
|
+
const stayDuration = checkOutDate.getTime() - checkInDate.getTime();
|
|
546
|
+
const hoursDiff = stayDuration / (1e3 * 60 * 60);
|
|
547
|
+
if (hoursDiff < minStayHours) {
|
|
548
|
+
throw new ReservationValidationError(
|
|
549
|
+
`Minimum stay is ${minStayHours} hours`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function validateStatusTransition(currentStatus, newStatus) {
|
|
554
|
+
const allowedTransitions = STATUS_TRANSITIONS[currentStatus];
|
|
555
|
+
if (!allowedTransitions.includes(newStatus)) {
|
|
556
|
+
throw new ReservationValidationError(
|
|
557
|
+
`Invalid status transition from '${currentStatus}' to '${newStatus}'. Allowed transitions: ${allowedTransitions.length > 0 ? allowedTransitions.join(", ") : "none (terminal state)"}`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
async function createReservation(client, input) {
|
|
562
|
+
try {
|
|
563
|
+
validateDates(input.checkIn, input.checkOut);
|
|
564
|
+
if (!input.nights) {
|
|
565
|
+
const checkInDate = new Date(input.checkIn);
|
|
566
|
+
const checkOutDate = new Date(input.checkOut);
|
|
567
|
+
const diffTime = checkOutDate.getTime() - checkInDate.getTime();
|
|
568
|
+
const diffDays = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
|
|
569
|
+
input.nights = diffDays;
|
|
570
|
+
}
|
|
571
|
+
const { data, errors } = await client.models.Reservation.create(input);
|
|
572
|
+
if (errors) {
|
|
573
|
+
console.error("[createReservation] GraphQL errors:", errors);
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
return data;
|
|
577
|
+
} catch (error) {
|
|
578
|
+
if (error instanceof ReservationValidationError) {
|
|
579
|
+
console.error("[createReservation] Validation error:", error.message);
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
582
|
+
console.error("[createReservation] Error creating reservation:", error);
|
|
583
|
+
throw error;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
async function updateReservation(client, input) {
|
|
587
|
+
try {
|
|
588
|
+
if (input.checkIn && input.checkOut) {
|
|
589
|
+
validateDates(input.checkIn, input.checkOut);
|
|
590
|
+
const checkInDate = new Date(input.checkIn);
|
|
591
|
+
const checkOutDate = new Date(input.checkOut);
|
|
592
|
+
const diffTime = checkOutDate.getTime() - checkInDate.getTime();
|
|
593
|
+
const diffDays = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
|
|
594
|
+
input.nights = diffDays;
|
|
595
|
+
}
|
|
596
|
+
if (input.status) {
|
|
597
|
+
const { data: currentReservation, errors: getErrors } = await client.models.Reservation.get({ id: input.id });
|
|
598
|
+
if (getErrors || !currentReservation) {
|
|
599
|
+
console.error("[updateReservation] Error fetching current reservation:", getErrors);
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const currentStatus = currentReservation.status;
|
|
603
|
+
validateStatusTransition(currentStatus, input.status);
|
|
604
|
+
}
|
|
605
|
+
const { data, errors } = await client.models.Reservation.update(input);
|
|
606
|
+
if (errors) {
|
|
607
|
+
console.error("[updateReservation] GraphQL errors:", errors);
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
return data;
|
|
611
|
+
} catch (error) {
|
|
612
|
+
if (error instanceof ReservationValidationError) {
|
|
613
|
+
console.error("[updateReservation] Validation error:", error.message);
|
|
614
|
+
throw error;
|
|
615
|
+
}
|
|
616
|
+
console.error("[updateReservation] Error updating reservation:", error);
|
|
617
|
+
throw error;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function softDeleteReservation(client, id, deletedBy) {
|
|
621
|
+
try {
|
|
622
|
+
const { errors } = await client.models.Reservation.update({
|
|
623
|
+
id,
|
|
624
|
+
deletedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
625
|
+
deletedBy
|
|
626
|
+
});
|
|
627
|
+
if (errors) {
|
|
628
|
+
console.error("[softDeleteReservation] GraphQL errors:", errors);
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
return true;
|
|
632
|
+
} catch (error) {
|
|
633
|
+
console.error("[softDeleteReservation] Error soft-deleting reservation:", error);
|
|
634
|
+
throw error;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
async function restoreReservation(client, id, retentionDays = DEFAULT_SOFT_DELETE_RETENTION_DAYS) {
|
|
638
|
+
try {
|
|
639
|
+
const { data: reservation, errors: getErrors } = await client.models.Reservation.get({ id });
|
|
640
|
+
if (getErrors || !reservation) {
|
|
641
|
+
console.error("[restoreReservation] Error fetching reservation:", getErrors);
|
|
642
|
+
return { success: false, error: "Reservation not found" };
|
|
643
|
+
}
|
|
644
|
+
const eligibility = checkRestoreEligibility(reservation.deletedAt, retentionDays);
|
|
645
|
+
if (!eligibility.canRestore) {
|
|
646
|
+
const errorMsg = `Cannot restore reservation. Retention period of ${retentionDays} days has expired. Item was deleted ${eligibility.daysExpired} days ago.`;
|
|
647
|
+
console.error("[restoreReservation]", errorMsg);
|
|
648
|
+
return { success: false, error: errorMsg };
|
|
649
|
+
}
|
|
650
|
+
const { errors } = await client.models.Reservation.update({
|
|
651
|
+
id,
|
|
652
|
+
deletedAt: null,
|
|
653
|
+
deletedBy: null
|
|
654
|
+
});
|
|
655
|
+
if (errors) {
|
|
656
|
+
console.error("[restoreReservation] GraphQL errors:", errors);
|
|
657
|
+
return { success: false, error: "Failed to restore reservation" };
|
|
658
|
+
}
|
|
659
|
+
return { success: true };
|
|
660
|
+
} catch (error) {
|
|
661
|
+
console.error("[restoreReservation] Error restoring reservation:", error);
|
|
662
|
+
throw error;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async function deleteReservation(client, id) {
|
|
666
|
+
try {
|
|
667
|
+
const { errors } = await client.models.Reservation.delete({ id });
|
|
668
|
+
if (errors) {
|
|
669
|
+
console.error("[deleteReservation] GraphQL errors:", errors);
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
return true;
|
|
673
|
+
} catch (error) {
|
|
674
|
+
console.error("[deleteReservation] Error deleting reservation:", error);
|
|
675
|
+
throw error;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async function updateReservationStatus(client, id, newStatus) {
|
|
679
|
+
try {
|
|
680
|
+
const { data: currentReservation, errors: getErrors } = await client.models.Reservation.get({ id });
|
|
681
|
+
if (getErrors || !currentReservation) {
|
|
682
|
+
console.error("[updateReservationStatus] Error fetching reservation:", getErrors);
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
const currentStatus = currentReservation.status;
|
|
686
|
+
validateStatusTransition(currentStatus, newStatus);
|
|
687
|
+
const { data, errors } = await client.models.Reservation.update({
|
|
688
|
+
id,
|
|
689
|
+
status: newStatus
|
|
690
|
+
});
|
|
691
|
+
if (errors) {
|
|
692
|
+
console.error("[updateReservationStatus] GraphQL errors:", errors);
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
return data;
|
|
696
|
+
} catch (error) {
|
|
697
|
+
if (error instanceof ReservationValidationError) {
|
|
698
|
+
console.error("[updateReservationStatus] Validation error:", error.message);
|
|
699
|
+
throw error;
|
|
700
|
+
}
|
|
701
|
+
console.error("[updateReservationStatus] Error updating status:", error);
|
|
702
|
+
throw error;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// src/mutations/contacts.ts
|
|
707
|
+
import { z } from "zod";
|
|
708
|
+
|
|
709
|
+
// src/queries/contacts.ts
|
|
710
|
+
async function getContact(client, id) {
|
|
711
|
+
try {
|
|
712
|
+
const { data, errors } = await client.models.Contact.get({ id });
|
|
713
|
+
if (errors) {
|
|
714
|
+
console.error("[getContact] GraphQL errors:", errors);
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
return data;
|
|
718
|
+
} catch (error) {
|
|
719
|
+
console.error("[getContact] Error fetching contact:", error);
|
|
720
|
+
throw error;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// src/mutations/contacts.ts
|
|
725
|
+
var createContactSchema = z.object({
|
|
726
|
+
brandId: z.string().min(1, "Brand ID is required"),
|
|
727
|
+
email: z.string().email("Invalid email address"),
|
|
728
|
+
phone: z.string().optional(),
|
|
729
|
+
firstName: z.string().min(1, "First name is required"),
|
|
730
|
+
lastName: z.string().min(1, "Last name is required"),
|
|
731
|
+
locale: z.string().optional(),
|
|
732
|
+
gdprConsent: z.boolean(),
|
|
733
|
+
gdprConsentDate: z.string().optional(),
|
|
734
|
+
marketingOptIn: z.boolean().optional(),
|
|
735
|
+
preferences: z.record(z.any()).optional(),
|
|
736
|
+
tags: z.array(z.string()).optional(),
|
|
737
|
+
totalVisits: z.number().int().min(0).optional(),
|
|
738
|
+
lastVisitDate: z.string().optional(),
|
|
739
|
+
firstVisitDate: z.string().optional(),
|
|
740
|
+
legacyId: z.string().optional(),
|
|
741
|
+
// Audit fields
|
|
742
|
+
createdAt: z.string().optional(),
|
|
743
|
+
createdBy: z.string().optional(),
|
|
744
|
+
updatedAt: z.string().optional(),
|
|
745
|
+
updatedBy: z.string().optional()
|
|
746
|
+
});
|
|
747
|
+
var updateContactSchema = z.object({
|
|
748
|
+
id: z.string().min(1, "Contact ID is required"),
|
|
749
|
+
brandId: z.string().min(1).optional(),
|
|
750
|
+
email: z.string().email("Invalid email address").optional(),
|
|
751
|
+
phone: z.string().optional(),
|
|
752
|
+
firstName: z.string().min(1).optional(),
|
|
753
|
+
lastName: z.string().min(1).optional(),
|
|
754
|
+
locale: z.string().optional(),
|
|
755
|
+
gdprConsent: z.boolean().optional(),
|
|
756
|
+
gdprConsentDate: z.string().optional(),
|
|
757
|
+
marketingOptIn: z.boolean().optional(),
|
|
758
|
+
preferences: z.record(z.any()).optional(),
|
|
759
|
+
tags: z.array(z.string()).optional(),
|
|
760
|
+
totalVisits: z.number().int().min(0).optional(),
|
|
761
|
+
lastVisitDate: z.string().optional(),
|
|
762
|
+
firstVisitDate: z.string().optional(),
|
|
763
|
+
legacyId: z.string().optional(),
|
|
764
|
+
// Audit fields
|
|
765
|
+
updatedAt: z.string().optional(),
|
|
766
|
+
updatedBy: z.string().optional(),
|
|
767
|
+
deletedAt: z.string().nullable().optional(),
|
|
768
|
+
deletedBy: z.string().nullable().optional()
|
|
769
|
+
});
|
|
770
|
+
var mergeContactsSchema = z.object({
|
|
771
|
+
primaryId: z.string().min(1, "Primary contact ID is required"),
|
|
772
|
+
duplicateIds: z.array(z.string().min(1)).min(1, "At least one duplicate ID is required")
|
|
773
|
+
});
|
|
774
|
+
var ContactValidationError = class extends Error {
|
|
775
|
+
issues;
|
|
776
|
+
constructor(message, issues = []) {
|
|
777
|
+
super(message);
|
|
778
|
+
this.name = "ContactValidationError";
|
|
779
|
+
this.issues = issues;
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
async function createContact(client, input) {
|
|
783
|
+
try {
|
|
784
|
+
const validationResult = createContactSchema.safeParse(input);
|
|
785
|
+
if (!validationResult.success) {
|
|
786
|
+
const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
|
|
787
|
+
throw new ContactValidationError(
|
|
788
|
+
`Validation failed: ${errorMessage}`,
|
|
789
|
+
validationResult.error.issues
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
const { data, errors } = await client.models.Contact.create(input);
|
|
793
|
+
if (errors) {
|
|
794
|
+
console.error("[createContact] GraphQL errors:", errors);
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
return data;
|
|
798
|
+
} catch (error) {
|
|
799
|
+
if (error instanceof ContactValidationError) {
|
|
800
|
+
console.error("[createContact] Validation error:", error.message);
|
|
801
|
+
throw error;
|
|
802
|
+
}
|
|
803
|
+
console.error("[createContact] Error creating contact:", error);
|
|
804
|
+
throw error;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async function updateContact(client, input) {
|
|
808
|
+
try {
|
|
809
|
+
const validationResult = updateContactSchema.safeParse(input);
|
|
810
|
+
if (!validationResult.success) {
|
|
811
|
+
const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
|
|
812
|
+
throw new ContactValidationError(
|
|
813
|
+
`Validation failed: ${errorMessage}`,
|
|
814
|
+
validationResult.error.issues
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
const { data, errors } = await client.models.Contact.update(input);
|
|
818
|
+
if (errors) {
|
|
819
|
+
console.error("[updateContact] GraphQL errors:", errors);
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
return data;
|
|
823
|
+
} catch (error) {
|
|
824
|
+
if (error instanceof ContactValidationError) {
|
|
825
|
+
console.error("[updateContact] Validation error:", error.message);
|
|
826
|
+
throw error;
|
|
827
|
+
}
|
|
828
|
+
console.error("[updateContact] Error updating contact:", error);
|
|
829
|
+
throw error;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async function softDeleteContact(client, id, deletedBy) {
|
|
833
|
+
try {
|
|
834
|
+
const { errors } = await client.models.Contact.update({
|
|
835
|
+
id,
|
|
836
|
+
deletedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
837
|
+
deletedBy
|
|
838
|
+
});
|
|
839
|
+
if (errors) {
|
|
840
|
+
console.error("[softDeleteContact] GraphQL errors:", errors);
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
return true;
|
|
844
|
+
} catch (error) {
|
|
845
|
+
console.error("[softDeleteContact] Error soft-deleting contact:", error);
|
|
846
|
+
throw error;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
async function restoreContact(client, id, retentionDays = DEFAULT_SOFT_DELETE_RETENTION_DAYS) {
|
|
850
|
+
try {
|
|
851
|
+
const contact = await getContact(client, id);
|
|
852
|
+
if (!contact) {
|
|
853
|
+
console.error("[restoreContact] Contact not found");
|
|
854
|
+
return { success: false, error: "Contact not found" };
|
|
855
|
+
}
|
|
856
|
+
const eligibility = checkRestoreEligibility(
|
|
857
|
+
contact.deletedAt,
|
|
858
|
+
retentionDays
|
|
859
|
+
);
|
|
860
|
+
if (!eligibility.canRestore) {
|
|
861
|
+
const errorMsg = `Cannot restore contact. Retention period of ${retentionDays} days has expired. Item was deleted ${eligibility.daysExpired} days ago.`;
|
|
862
|
+
console.error("[restoreContact]", errorMsg);
|
|
863
|
+
return { success: false, error: errorMsg };
|
|
864
|
+
}
|
|
865
|
+
const { errors } = await client.models.Contact.update({
|
|
866
|
+
id,
|
|
867
|
+
deletedAt: null,
|
|
868
|
+
deletedBy: null
|
|
869
|
+
});
|
|
870
|
+
if (errors) {
|
|
871
|
+
console.error("[restoreContact] GraphQL errors:", errors);
|
|
872
|
+
return { success: false, error: "Failed to restore contact" };
|
|
873
|
+
}
|
|
874
|
+
return { success: true };
|
|
875
|
+
} catch (error) {
|
|
876
|
+
console.error("[restoreContact] Error restoring contact:", error);
|
|
877
|
+
throw error;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
async function deleteContact(client, id) {
|
|
881
|
+
try {
|
|
882
|
+
const { errors } = await client.models.Contact.delete({ id });
|
|
883
|
+
if (errors) {
|
|
884
|
+
console.error("[deleteContact] GraphQL errors:", errors);
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
return true;
|
|
888
|
+
} catch (error) {
|
|
889
|
+
console.error("[deleteContact] Error deleting contact:", error);
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
async function mergeContacts(client, input, mergedBy) {
|
|
894
|
+
try {
|
|
895
|
+
const validationResult = mergeContactsSchema.safeParse(input);
|
|
896
|
+
if (!validationResult.success) {
|
|
897
|
+
const errorMessage = validationResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
|
|
898
|
+
throw new ContactValidationError(
|
|
899
|
+
`Validation failed: ${errorMessage}`,
|
|
900
|
+
validationResult.error.issues
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
const { primaryId, duplicateIds } = input;
|
|
904
|
+
const primaryContact = await getContact(client, primaryId);
|
|
905
|
+
if (!primaryContact) {
|
|
906
|
+
return {
|
|
907
|
+
success: false,
|
|
908
|
+
error: `Primary contact not found: ${primaryId}`
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
const duplicateContacts = [];
|
|
912
|
+
for (const duplicateId of duplicateIds) {
|
|
913
|
+
const duplicate = await getContact(client, duplicateId);
|
|
914
|
+
if (!duplicate) {
|
|
915
|
+
return {
|
|
916
|
+
success: false,
|
|
917
|
+
error: `Duplicate contact not found: ${duplicateId}`
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
duplicateContacts.push(duplicate);
|
|
921
|
+
}
|
|
922
|
+
let totalVisits = primaryContact.totalVisits || 0;
|
|
923
|
+
let firstVisitDate = primaryContact.firstVisitDate;
|
|
924
|
+
let lastVisitDate = primaryContact.lastVisitDate;
|
|
925
|
+
const allTags = new Set(primaryContact.tags || []);
|
|
926
|
+
for (const duplicate of duplicateContacts) {
|
|
927
|
+
totalVisits += duplicate.totalVisits || 0;
|
|
928
|
+
if (duplicate.firstVisitDate) {
|
|
929
|
+
if (!firstVisitDate || duplicate.firstVisitDate < firstVisitDate) {
|
|
930
|
+
firstVisitDate = duplicate.firstVisitDate;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (duplicate.lastVisitDate) {
|
|
934
|
+
if (!lastVisitDate || duplicate.lastVisitDate > lastVisitDate) {
|
|
935
|
+
lastVisitDate = duplicate.lastVisitDate;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
if (duplicate.tags) {
|
|
939
|
+
duplicate.tags.forEach((tag) => allTags.add(tag));
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const updateInput = {
|
|
943
|
+
id: primaryId,
|
|
944
|
+
totalVisits,
|
|
945
|
+
firstVisitDate,
|
|
946
|
+
lastVisitDate,
|
|
947
|
+
tags: Array.from(allTags),
|
|
948
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
949
|
+
updatedBy: mergedBy
|
|
950
|
+
};
|
|
951
|
+
const updatedPrimary = await updateContact(client, updateInput);
|
|
952
|
+
if (!updatedPrimary) {
|
|
953
|
+
return {
|
|
954
|
+
success: false,
|
|
955
|
+
error: "Failed to update primary contact with merged data"
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
const deletedIds = [];
|
|
959
|
+
for (const duplicateId of duplicateIds) {
|
|
960
|
+
const deleted = await softDeleteContact(client, duplicateId, mergedBy);
|
|
961
|
+
if (deleted) {
|
|
962
|
+
deletedIds.push(duplicateId);
|
|
963
|
+
} else {
|
|
964
|
+
console.error(`[mergeContacts] Failed to soft-delete duplicate: ${duplicateId}`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
success: true,
|
|
969
|
+
mergedContact: updatedPrimary,
|
|
970
|
+
deletedIds
|
|
971
|
+
};
|
|
972
|
+
} catch (error) {
|
|
973
|
+
if (error instanceof ContactValidationError) {
|
|
974
|
+
console.error("[mergeContacts] Validation error:", error.message);
|
|
975
|
+
throw error;
|
|
976
|
+
}
|
|
977
|
+
console.error("[mergeContacts] Error merging contacts:", error);
|
|
978
|
+
throw error;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
512
981
|
export {
|
|
982
|
+
ContactValidationError,
|
|
983
|
+
ReservationValidationError,
|
|
513
984
|
createAccount,
|
|
514
985
|
createBrand,
|
|
986
|
+
createContact,
|
|
987
|
+
createContactSchema,
|
|
515
988
|
createProductInstance,
|
|
989
|
+
createReservation,
|
|
516
990
|
createUser,
|
|
517
991
|
deleteAccount,
|
|
518
992
|
deleteBrand,
|
|
993
|
+
deleteContact,
|
|
519
994
|
deleteProductInstance,
|
|
995
|
+
deleteReservation,
|
|
520
996
|
deleteUser,
|
|
521
997
|
initializeSystemSettings,
|
|
998
|
+
mergeContacts,
|
|
999
|
+
mergeContactsSchema,
|
|
522
1000
|
restoreAccount,
|
|
523
1001
|
restoreBrand,
|
|
1002
|
+
restoreContact,
|
|
1003
|
+
restoreReservation,
|
|
524
1004
|
restoreUser,
|
|
525
1005
|
softDeleteAccount,
|
|
526
1006
|
softDeleteBrand,
|
|
1007
|
+
softDeleteContact,
|
|
1008
|
+
softDeleteReservation,
|
|
527
1009
|
softDeleteUser,
|
|
528
1010
|
toggleProductInstanceEnabled,
|
|
529
1011
|
updateAccount,
|
|
530
1012
|
updateBrand,
|
|
1013
|
+
updateContact,
|
|
1014
|
+
updateContactSchema,
|
|
531
1015
|
updateProductInstance,
|
|
1016
|
+
updateReservation,
|
|
1017
|
+
updateReservationStatus,
|
|
532
1018
|
updateSystemSettings,
|
|
533
1019
|
updateUser
|
|
534
1020
|
};
|