@htlkg/data 0.0.20 → 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/hooks/index.d.ts +88 -2
- package/dist/hooks/index.js +63 -3
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +441 -3
- package/dist/index.js.map +1 -1
- package/dist/mutations/index.d.ts +338 -2
- package/dist/mutations/index.js +286 -0
- package/dist/mutations/index.js.map +1 -1
- package/dist/queries/index.d.ts +110 -2
- package/dist/queries/index.js +110 -1
- package/dist/queries/index.js.map +1 -1
- package/package.json +13 -12
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useContacts.test.ts +159 -0
- package/src/hooks/useContacts.ts +176 -0
- package/src/mutations/contacts.test.ts +604 -0
- package/src/mutations/contacts.ts +554 -0
- package/src/mutations/index.ts +18 -0
- package/src/queries/contacts.test.ts +505 -0
- package/src/queries/contacts.ts +237 -0
- package/src/queries/index.ts +10 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { C as CreateAuditFields, a as UpdateWithSoftDeleteFields } from '../common-DSxswsZ3.js';
|
|
2
2
|
export { S as SoftDeleteFields, U as UpdateAuditFields } from '../common-DSxswsZ3.js';
|
|
3
|
-
import { Brand, Account, User, SystemSettings } from '@htlkg/core/types';
|
|
3
|
+
import { Brand, Account, User, SystemSettings, Contact } from '@htlkg/core/types';
|
|
4
4
|
export { C as CreateProductInstanceInput, U as UpdateProductInstanceInput, c as createProductInstance, d as deleteProductInstance, t as toggleProductInstanceEnabled, u as updateProductInstance } from '../productInstances-BpQv1oLS.js';
|
|
5
5
|
export { C as CreateReservationInput, b as ReservationStatus, R as ReservationValidationError, U as UpdateReservationInput, c as createReservation, d as deleteReservation, r as restoreReservation, s as softDeleteReservation, u as updateReservation, a as updateReservationStatus } from '../reservations-CdDfkcZ_.js';
|
|
6
|
+
import { z } from 'zod';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Brand Mutation Functions
|
|
@@ -377,4 +378,339 @@ declare function updateSystemSettings<TClient = any>(client: TClient, input: Upd
|
|
|
377
378
|
*/
|
|
378
379
|
declare function initializeSystemSettings<TClient = any>(client: TClient, initializedBy: string): Promise<SystemSettings | null>;
|
|
379
380
|
|
|
380
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Contact Mutation Functions
|
|
383
|
+
*
|
|
384
|
+
* Provides type-safe mutation functions for creating, updating, and deleting contacts.
|
|
385
|
+
* Includes Zod validation for input data before mutations.
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Zod schema for creating a contact
|
|
390
|
+
*/
|
|
391
|
+
declare const createContactSchema: z.ZodObject<{
|
|
392
|
+
brandId: z.ZodString;
|
|
393
|
+
email: z.ZodString;
|
|
394
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
395
|
+
firstName: z.ZodString;
|
|
396
|
+
lastName: z.ZodString;
|
|
397
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
398
|
+
gdprConsent: z.ZodBoolean;
|
|
399
|
+
gdprConsentDate: z.ZodOptional<z.ZodString>;
|
|
400
|
+
marketingOptIn: z.ZodOptional<z.ZodBoolean>;
|
|
401
|
+
preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
402
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
403
|
+
totalVisits: z.ZodOptional<z.ZodNumber>;
|
|
404
|
+
lastVisitDate: z.ZodOptional<z.ZodString>;
|
|
405
|
+
firstVisitDate: z.ZodOptional<z.ZodString>;
|
|
406
|
+
legacyId: z.ZodOptional<z.ZodString>;
|
|
407
|
+
createdAt: z.ZodOptional<z.ZodString>;
|
|
408
|
+
createdBy: z.ZodOptional<z.ZodString>;
|
|
409
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
410
|
+
updatedBy: z.ZodOptional<z.ZodString>;
|
|
411
|
+
}, "strip", z.ZodTypeAny, {
|
|
412
|
+
brandId: string;
|
|
413
|
+
email: string;
|
|
414
|
+
lastName: string;
|
|
415
|
+
firstName: string;
|
|
416
|
+
gdprConsent: boolean;
|
|
417
|
+
updatedAt?: string | undefined;
|
|
418
|
+
updatedBy?: string | undefined;
|
|
419
|
+
phone?: string | undefined;
|
|
420
|
+
locale?: string | undefined;
|
|
421
|
+
gdprConsentDate?: string | undefined;
|
|
422
|
+
marketingOptIn?: boolean | undefined;
|
|
423
|
+
preferences?: Record<string, any> | undefined;
|
|
424
|
+
tags?: string[] | undefined;
|
|
425
|
+
totalVisits?: number | undefined;
|
|
426
|
+
lastVisitDate?: string | undefined;
|
|
427
|
+
firstVisitDate?: string | undefined;
|
|
428
|
+
legacyId?: string | undefined;
|
|
429
|
+
createdAt?: string | undefined;
|
|
430
|
+
createdBy?: string | undefined;
|
|
431
|
+
}, {
|
|
432
|
+
brandId: string;
|
|
433
|
+
email: string;
|
|
434
|
+
lastName: string;
|
|
435
|
+
firstName: string;
|
|
436
|
+
gdprConsent: boolean;
|
|
437
|
+
updatedAt?: string | undefined;
|
|
438
|
+
updatedBy?: string | undefined;
|
|
439
|
+
phone?: string | undefined;
|
|
440
|
+
locale?: string | undefined;
|
|
441
|
+
gdprConsentDate?: string | undefined;
|
|
442
|
+
marketingOptIn?: boolean | undefined;
|
|
443
|
+
preferences?: Record<string, any> | undefined;
|
|
444
|
+
tags?: string[] | undefined;
|
|
445
|
+
totalVisits?: number | undefined;
|
|
446
|
+
lastVisitDate?: string | undefined;
|
|
447
|
+
firstVisitDate?: string | undefined;
|
|
448
|
+
legacyId?: string | undefined;
|
|
449
|
+
createdAt?: string | undefined;
|
|
450
|
+
createdBy?: string | undefined;
|
|
451
|
+
}>;
|
|
452
|
+
/**
|
|
453
|
+
* Zod schema for updating a contact
|
|
454
|
+
*/
|
|
455
|
+
declare const updateContactSchema: z.ZodObject<{
|
|
456
|
+
id: z.ZodString;
|
|
457
|
+
brandId: z.ZodOptional<z.ZodString>;
|
|
458
|
+
email: z.ZodOptional<z.ZodString>;
|
|
459
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
460
|
+
firstName: z.ZodOptional<z.ZodString>;
|
|
461
|
+
lastName: z.ZodOptional<z.ZodString>;
|
|
462
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
463
|
+
gdprConsent: z.ZodOptional<z.ZodBoolean>;
|
|
464
|
+
gdprConsentDate: z.ZodOptional<z.ZodString>;
|
|
465
|
+
marketingOptIn: z.ZodOptional<z.ZodBoolean>;
|
|
466
|
+
preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
467
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
468
|
+
totalVisits: z.ZodOptional<z.ZodNumber>;
|
|
469
|
+
lastVisitDate: z.ZodOptional<z.ZodString>;
|
|
470
|
+
firstVisitDate: z.ZodOptional<z.ZodString>;
|
|
471
|
+
legacyId: z.ZodOptional<z.ZodString>;
|
|
472
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
473
|
+
updatedBy: z.ZodOptional<z.ZodString>;
|
|
474
|
+
deletedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
475
|
+
deletedBy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
476
|
+
}, "strip", z.ZodTypeAny, {
|
|
477
|
+
id: string;
|
|
478
|
+
updatedAt?: string | undefined;
|
|
479
|
+
updatedBy?: string | undefined;
|
|
480
|
+
brandId?: string | undefined;
|
|
481
|
+
deletedAt?: string | null | undefined;
|
|
482
|
+
deletedBy?: string | null | undefined;
|
|
483
|
+
email?: string | undefined;
|
|
484
|
+
phone?: string | undefined;
|
|
485
|
+
lastName?: string | undefined;
|
|
486
|
+
firstName?: string | undefined;
|
|
487
|
+
locale?: string | undefined;
|
|
488
|
+
gdprConsent?: boolean | undefined;
|
|
489
|
+
gdprConsentDate?: string | undefined;
|
|
490
|
+
marketingOptIn?: boolean | undefined;
|
|
491
|
+
preferences?: Record<string, any> | undefined;
|
|
492
|
+
tags?: string[] | undefined;
|
|
493
|
+
totalVisits?: number | undefined;
|
|
494
|
+
lastVisitDate?: string | undefined;
|
|
495
|
+
firstVisitDate?: string | undefined;
|
|
496
|
+
legacyId?: string | undefined;
|
|
497
|
+
}, {
|
|
498
|
+
id: string;
|
|
499
|
+
updatedAt?: string | undefined;
|
|
500
|
+
updatedBy?: string | undefined;
|
|
501
|
+
brandId?: string | undefined;
|
|
502
|
+
deletedAt?: string | null | undefined;
|
|
503
|
+
deletedBy?: string | null | undefined;
|
|
504
|
+
email?: string | undefined;
|
|
505
|
+
phone?: string | undefined;
|
|
506
|
+
lastName?: string | undefined;
|
|
507
|
+
firstName?: string | undefined;
|
|
508
|
+
locale?: string | undefined;
|
|
509
|
+
gdprConsent?: boolean | undefined;
|
|
510
|
+
gdprConsentDate?: string | undefined;
|
|
511
|
+
marketingOptIn?: boolean | undefined;
|
|
512
|
+
preferences?: Record<string, any> | undefined;
|
|
513
|
+
tags?: string[] | undefined;
|
|
514
|
+
totalVisits?: number | undefined;
|
|
515
|
+
lastVisitDate?: string | undefined;
|
|
516
|
+
firstVisitDate?: string | undefined;
|
|
517
|
+
legacyId?: string | undefined;
|
|
518
|
+
}>;
|
|
519
|
+
/**
|
|
520
|
+
* Zod schema for merging contacts
|
|
521
|
+
*/
|
|
522
|
+
declare const mergeContactsSchema: z.ZodObject<{
|
|
523
|
+
primaryId: z.ZodString;
|
|
524
|
+
duplicateIds: z.ZodArray<z.ZodString, "many">;
|
|
525
|
+
}, "strip", z.ZodTypeAny, {
|
|
526
|
+
primaryId: string;
|
|
527
|
+
duplicateIds: string[];
|
|
528
|
+
}, {
|
|
529
|
+
primaryId: string;
|
|
530
|
+
duplicateIds: string[];
|
|
531
|
+
}>;
|
|
532
|
+
/**
|
|
533
|
+
* Validation error class for contact operations
|
|
534
|
+
*/
|
|
535
|
+
declare class ContactValidationError extends Error {
|
|
536
|
+
readonly issues: z.ZodIssue[];
|
|
537
|
+
constructor(message: string, issues?: z.ZodIssue[]);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Input type for creating a contact
|
|
541
|
+
*/
|
|
542
|
+
interface CreateContactInput extends CreateAuditFields {
|
|
543
|
+
brandId: string;
|
|
544
|
+
email: string;
|
|
545
|
+
phone?: string;
|
|
546
|
+
firstName: string;
|
|
547
|
+
lastName: string;
|
|
548
|
+
locale?: string;
|
|
549
|
+
gdprConsent: boolean;
|
|
550
|
+
gdprConsentDate?: string;
|
|
551
|
+
marketingOptIn?: boolean;
|
|
552
|
+
preferences?: Record<string, any>;
|
|
553
|
+
tags?: string[];
|
|
554
|
+
totalVisits?: number;
|
|
555
|
+
lastVisitDate?: string;
|
|
556
|
+
firstVisitDate?: string;
|
|
557
|
+
legacyId?: string;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Input type for updating a contact
|
|
561
|
+
*/
|
|
562
|
+
interface UpdateContactInput extends UpdateWithSoftDeleteFields {
|
|
563
|
+
id: string;
|
|
564
|
+
brandId?: string;
|
|
565
|
+
email?: string;
|
|
566
|
+
phone?: string;
|
|
567
|
+
firstName?: string;
|
|
568
|
+
lastName?: string;
|
|
569
|
+
locale?: string;
|
|
570
|
+
gdprConsent?: boolean;
|
|
571
|
+
gdprConsentDate?: string;
|
|
572
|
+
marketingOptIn?: boolean;
|
|
573
|
+
preferences?: Record<string, any>;
|
|
574
|
+
tags?: string[];
|
|
575
|
+
totalVisits?: number;
|
|
576
|
+
lastVisitDate?: string;
|
|
577
|
+
firstVisitDate?: string;
|
|
578
|
+
legacyId?: string;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Input type for merging contacts
|
|
582
|
+
*/
|
|
583
|
+
interface MergeContactsInput {
|
|
584
|
+
primaryId: string;
|
|
585
|
+
duplicateIds: string[];
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Result type for merge operation
|
|
589
|
+
*/
|
|
590
|
+
interface MergeContactsResult {
|
|
591
|
+
success: boolean;
|
|
592
|
+
mergedContact?: Contact;
|
|
593
|
+
deletedIds?: string[];
|
|
594
|
+
error?: string;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Create a new contact with Zod validation
|
|
598
|
+
*
|
|
599
|
+
* @throws {ContactValidationError} if input validation fails
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```typescript
|
|
603
|
+
* import { createContact } from '@htlkg/data/mutations';
|
|
604
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
605
|
+
*
|
|
606
|
+
* const client = generateClient<Schema>();
|
|
607
|
+
* const contact = await createContact(client, {
|
|
608
|
+
* brandId: 'brand-123',
|
|
609
|
+
* email: 'guest@example.com',
|
|
610
|
+
* firstName: 'John',
|
|
611
|
+
* lastName: 'Doe',
|
|
612
|
+
* gdprConsent: true,
|
|
613
|
+
* gdprConsentDate: new Date().toISOString()
|
|
614
|
+
* });
|
|
615
|
+
* ```
|
|
616
|
+
*/
|
|
617
|
+
declare function createContact<TClient = any>(client: TClient, input: CreateContactInput): Promise<Contact | null>;
|
|
618
|
+
/**
|
|
619
|
+
* Update an existing contact with Zod validation
|
|
620
|
+
*
|
|
621
|
+
* @throws {ContactValidationError} if input validation fails
|
|
622
|
+
*
|
|
623
|
+
* @example
|
|
624
|
+
* ```typescript
|
|
625
|
+
* import { updateContact } from '@htlkg/data/mutations';
|
|
626
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
627
|
+
*
|
|
628
|
+
* const client = generateClient<Schema>();
|
|
629
|
+
* const contact = await updateContact(client, {
|
|
630
|
+
* id: 'contact-123',
|
|
631
|
+
* firstName: 'Jane',
|
|
632
|
+
* marketingOptIn: true
|
|
633
|
+
* });
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
declare function updateContact<TClient = any>(client: TClient, input: UpdateContactInput): Promise<Contact | null>;
|
|
637
|
+
/**
|
|
638
|
+
* Soft delete a contact (sets deletedAt/deletedBy instead of removing)
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* ```typescript
|
|
642
|
+
* import { softDeleteContact } from '@htlkg/data/mutations';
|
|
643
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
644
|
+
*
|
|
645
|
+
* const client = generateClient<Schema>();
|
|
646
|
+
* await softDeleteContact(client, 'contact-123', 'admin@example.com');
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
declare function softDeleteContact<TClient = any>(client: TClient, id: string, deletedBy: string): Promise<boolean>;
|
|
650
|
+
/**
|
|
651
|
+
* Restore a soft-deleted contact
|
|
652
|
+
* Checks retention period before allowing restoration
|
|
653
|
+
*
|
|
654
|
+
* @example
|
|
655
|
+
* ```typescript
|
|
656
|
+
* import { restoreContact } from '@htlkg/data/mutations';
|
|
657
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
658
|
+
*
|
|
659
|
+
* const client = generateClient<Schema>();
|
|
660
|
+
* await restoreContact(client, 'contact-123');
|
|
661
|
+
* // Or with custom retention days:
|
|
662
|
+
* await restoreContact(client, 'contact-123', 60);
|
|
663
|
+
* ```
|
|
664
|
+
*/
|
|
665
|
+
declare function restoreContact<TClient = any>(client: TClient, id: string, retentionDays?: number): Promise<{
|
|
666
|
+
success: boolean;
|
|
667
|
+
error?: string;
|
|
668
|
+
}>;
|
|
669
|
+
/**
|
|
670
|
+
* Hard delete a contact (permanently removes from database)
|
|
671
|
+
* Use with caution - prefer softDeleteContact for recoverable deletion
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```typescript
|
|
675
|
+
* import { deleteContact } from '@htlkg/data/mutations';
|
|
676
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
677
|
+
*
|
|
678
|
+
* const client = generateClient<Schema>();
|
|
679
|
+
* await deleteContact(client, 'contact-123');
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
declare function deleteContact<TClient = any>(client: TClient, id: string): Promise<boolean>;
|
|
683
|
+
/**
|
|
684
|
+
* Merge duplicate contacts into a primary contact
|
|
685
|
+
*
|
|
686
|
+
* This function:
|
|
687
|
+
* 1. Validates all contact IDs exist
|
|
688
|
+
* 2. Aggregates data from duplicates into the primary contact
|
|
689
|
+
* 3. Soft deletes the duplicate contacts
|
|
690
|
+
* 4. Returns the updated primary contact
|
|
691
|
+
*
|
|
692
|
+
* Merge strategy:
|
|
693
|
+
* - totalVisits: Sum of all contacts
|
|
694
|
+
* - firstVisitDate: Earliest date across all contacts
|
|
695
|
+
* - lastVisitDate: Latest date across all contacts
|
|
696
|
+
* - tags: Merged unique tags from all contacts
|
|
697
|
+
* - preferences: Primary contact preferences take precedence
|
|
698
|
+
* - Other fields: Primary contact values are preserved
|
|
699
|
+
*
|
|
700
|
+
* @throws {ContactValidationError} if input validation fails
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* ```typescript
|
|
704
|
+
* import { mergeContacts } from '@htlkg/data/mutations';
|
|
705
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
706
|
+
*
|
|
707
|
+
* const client = generateClient<Schema>();
|
|
708
|
+
* const result = await mergeContacts(client, {
|
|
709
|
+
* primaryId: 'contact-primary',
|
|
710
|
+
* duplicateIds: ['contact-dup-1', 'contact-dup-2']
|
|
711
|
+
* }, 'admin@example.com');
|
|
712
|
+
* ```
|
|
713
|
+
*/
|
|
714
|
+
declare function mergeContacts<TClient = any>(client: TClient, input: MergeContactsInput, mergedBy: string): Promise<MergeContactsResult>;
|
|
715
|
+
|
|
716
|
+
export { ContactValidationError, type CreateAccountInput, CreateAuditFields, type CreateBrandInput, type CreateContactInput, type CreateUserInput, type MergeContactsInput, type MergeContactsResult, type UpdateAccountInput, type UpdateBrandInput, type UpdateContactInput, type UpdateSystemSettingsInput, type UpdateUserInput, UpdateWithSoftDeleteFields, createAccount, createBrand, createContact, createContactSchema, createUser, deleteAccount, deleteBrand, deleteContact, deleteUser, initializeSystemSettings, mergeContacts, mergeContactsSchema, restoreAccount, restoreBrand, restoreContact, restoreUser, softDeleteAccount, softDeleteBrand, softDeleteContact, softDeleteUser, updateAccount, updateBrand, updateContact, updateContactSchema, updateSystemSettings, updateUser };
|
package/dist/mutations/index.js
CHANGED
|
@@ -702,30 +702,316 @@ async function updateReservationStatus(client, id, newStatus) {
|
|
|
702
702
|
throw error;
|
|
703
703
|
}
|
|
704
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
|
+
}
|
|
705
981
|
export {
|
|
982
|
+
ContactValidationError,
|
|
706
983
|
ReservationValidationError,
|
|
707
984
|
createAccount,
|
|
708
985
|
createBrand,
|
|
986
|
+
createContact,
|
|
987
|
+
createContactSchema,
|
|
709
988
|
createProductInstance,
|
|
710
989
|
createReservation,
|
|
711
990
|
createUser,
|
|
712
991
|
deleteAccount,
|
|
713
992
|
deleteBrand,
|
|
993
|
+
deleteContact,
|
|
714
994
|
deleteProductInstance,
|
|
715
995
|
deleteReservation,
|
|
716
996
|
deleteUser,
|
|
717
997
|
initializeSystemSettings,
|
|
998
|
+
mergeContacts,
|
|
999
|
+
mergeContactsSchema,
|
|
718
1000
|
restoreAccount,
|
|
719
1001
|
restoreBrand,
|
|
1002
|
+
restoreContact,
|
|
720
1003
|
restoreReservation,
|
|
721
1004
|
restoreUser,
|
|
722
1005
|
softDeleteAccount,
|
|
723
1006
|
softDeleteBrand,
|
|
1007
|
+
softDeleteContact,
|
|
724
1008
|
softDeleteReservation,
|
|
725
1009
|
softDeleteUser,
|
|
726
1010
|
toggleProductInstanceEnabled,
|
|
727
1011
|
updateAccount,
|
|
728
1012
|
updateBrand,
|
|
1013
|
+
updateContact,
|
|
1014
|
+
updateContactSchema,
|
|
729
1015
|
updateProductInstance,
|
|
730
1016
|
updateReservation,
|
|
731
1017
|
updateReservationStatus,
|