@connectedxm/admin 6.2.0 → 6.3.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/DEVELOPER_DOCUMENTATION.md +141 -77
- package/dist/index.cjs +206 -0
- package/dist/index.d.cts +144 -1
- package/dist/index.d.ts +144 -1
- package/dist/index.js +190 -0
- package/package.json +1 -1
|
@@ -95,6 +95,7 @@ src/
|
|
|
95
95
|
**Purpose**: Centralized TypeScript interfaces and enums that define all API response types, domain models, and constants used throughout the SDK.
|
|
96
96
|
|
|
97
97
|
**Key Contents**:
|
|
98
|
+
|
|
98
99
|
- `ConnectedXMResponse<T>` - Wrapper interface for all API responses
|
|
99
100
|
- Domain models: `Account`, `Event`, `Group`, `Channel`, etc.
|
|
100
101
|
- Enums: `EventType`, `AccountAccess`, `PassTypeVisibility`, etc.
|
|
@@ -116,16 +117,17 @@ export interface ConnectedXMResponse<TData> {
|
|
|
116
117
|
|
|
117
118
|
```typescript
|
|
118
119
|
// Response type for a single account
|
|
119
|
-
ConnectedXMResponse<Account
|
|
120
|
+
ConnectedXMResponse<Account>;
|
|
120
121
|
|
|
121
122
|
// Response type for a list of accounts
|
|
122
|
-
ConnectedXMResponse<Account[]
|
|
123
|
+
ConnectedXMResponse<Account[]>;
|
|
123
124
|
|
|
124
125
|
// Response type with cursor for pagination
|
|
125
|
-
ConnectedXMResponse<Account[]
|
|
126
|
+
ConnectedXMResponse<Account[]>; // cursor field included
|
|
126
127
|
```
|
|
127
128
|
|
|
128
129
|
**Conventions**:
|
|
130
|
+
|
|
129
131
|
- All domain models are PascalCase (e.g., `Account`, `EventSession`)
|
|
130
132
|
- Enums use PascalCase for the enum name and SCREAMING_SNAKE_CASE for values
|
|
131
133
|
- Nested interfaces follow the pattern `ParentChild` (e.g., `EventSession`, `AccountAddress`)
|
|
@@ -135,6 +137,7 @@ ConnectedXMResponse<Account[]> // cursor field included
|
|
|
135
137
|
**Purpose**: Input parameter interfaces for all mutations. These define the shape of data sent to the API when creating or updating resources.
|
|
136
138
|
|
|
137
139
|
**Pattern**: All mutation inputs follow naming conventions:
|
|
140
|
+
|
|
138
141
|
- `*CreateInputs` - For creating new resources (e.g., `AccountCreateInputs`)
|
|
139
142
|
- `*UpdateInputs` - For updating existing resources (e.g., `AccountUpdateInputs`)
|
|
140
143
|
- `*TranslationUpdateInputs` - For updating translated fields (e.g., `EventTranslationUpdateInputs`)
|
|
@@ -160,6 +163,7 @@ export interface AccountUpdateInputs {
|
|
|
160
163
|
```
|
|
161
164
|
|
|
162
165
|
**Key Patterns**:
|
|
166
|
+
|
|
163
167
|
- Create inputs typically have required fields (e.g., `email: string`)
|
|
164
168
|
- Update inputs make most fields optional (e.g., `firstName?: string | null`)
|
|
165
169
|
- Fields can be `null` to explicitly clear values
|
|
@@ -178,9 +182,10 @@ export interface AccountUpdateInputs {
|
|
|
178
182
|
|
|
179
183
|
```typescript
|
|
180
184
|
export interface AdminApiParams {
|
|
181
|
-
apiUrl:
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
apiUrl:
|
|
186
|
+
| "https://admin-api.connected.dev"
|
|
187
|
+
| "https://staging-admin-api.connected.dev"
|
|
188
|
+
| "http://localhost:4001";
|
|
184
189
|
organizationId: string;
|
|
185
190
|
getToken?: () => Promise<string | undefined> | string | undefined;
|
|
186
191
|
apiKey?: string;
|
|
@@ -189,6 +194,7 @@ export interface AdminApiParams {
|
|
|
189
194
|
```
|
|
190
195
|
|
|
191
196
|
**Parameters**:
|
|
197
|
+
|
|
192
198
|
- `apiUrl`: The base URL for the API (production, staging, or local)
|
|
193
199
|
- `organizationId`: Required organization identifier
|
|
194
200
|
- `getToken`: Optional function to retrieve authentication token
|
|
@@ -279,11 +285,11 @@ onMutationError?: (
|
|
|
279
285
|
getToken={getToken}
|
|
280
286
|
onNotAuthorized={(error) => {
|
|
281
287
|
// Handle 401 errors
|
|
282
|
-
router.push(
|
|
288
|
+
router.push("/login");
|
|
283
289
|
}}
|
|
284
290
|
onModuleForbidden={(error) => {
|
|
285
291
|
// Handle 403/460/461 errors
|
|
286
|
-
showError(
|
|
292
|
+
showError("You do not have permission");
|
|
287
293
|
}}
|
|
288
294
|
>
|
|
289
295
|
{children}
|
|
@@ -314,6 +320,7 @@ const { data: account, isLoading } = useGetAccount(accountId);
|
|
|
314
320
|
```
|
|
315
321
|
|
|
316
322
|
**Characteristics**:
|
|
323
|
+
|
|
317
324
|
- Returns a single resource
|
|
318
325
|
- Automatically handles 404 errors
|
|
319
326
|
- 60-second stale time
|
|
@@ -330,18 +337,18 @@ const { data: account, isLoading } = useGetAccount(accountId);
|
|
|
330
337
|
**Example**:
|
|
331
338
|
|
|
332
339
|
```typescript
|
|
333
|
-
const {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
});
|
|
340
|
+
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetAccounts(
|
|
341
|
+
verified,
|
|
342
|
+
online,
|
|
343
|
+
{
|
|
344
|
+
pageSize: 25,
|
|
345
|
+
search: "john",
|
|
346
|
+
}
|
|
347
|
+
);
|
|
342
348
|
```
|
|
343
349
|
|
|
344
350
|
**Characteristics**:
|
|
351
|
+
|
|
345
352
|
- Uses `pageParam` (number) for pagination
|
|
346
353
|
- Default page size: 25
|
|
347
354
|
- Automatically determines next page based on response length
|
|
@@ -370,16 +377,13 @@ const getNextPageParam = (lastPage, allPages) => {
|
|
|
370
377
|
**Example**:
|
|
371
378
|
|
|
372
379
|
```typescript
|
|
373
|
-
const {
|
|
374
|
-
|
|
375
|
-
fetchNextPage,
|
|
376
|
-
hasNextPage
|
|
377
|
-
} = useGetSomeCursorBasedList({
|
|
378
|
-
pageSize: 50
|
|
380
|
+
const { data, fetchNextPage, hasNextPage } = useGetSomeCursorBasedList({
|
|
381
|
+
pageSize: 50,
|
|
379
382
|
});
|
|
380
383
|
```
|
|
381
384
|
|
|
382
385
|
**Characteristics**:
|
|
386
|
+
|
|
383
387
|
- Uses `cursor` (string | number | null) for pagination
|
|
384
388
|
- Initial cursor is `null`
|
|
385
389
|
- Next cursor comes from `response.cursor`
|
|
@@ -416,6 +420,7 @@ export const ACCOUNT_QUERY_KEY = (accountId: string) => [
|
|
|
416
420
|
```
|
|
417
421
|
|
|
418
422
|
**Conventions**:
|
|
423
|
+
|
|
419
424
|
- Named: `*_QUERY_KEY`
|
|
420
425
|
- Returns an array (React Query key format)
|
|
421
426
|
- Can accept parameters for filtering/variants
|
|
@@ -440,6 +445,7 @@ export const SET_ACCOUNT_QUERY_DATA = (
|
|
|
440
445
|
```
|
|
441
446
|
|
|
442
447
|
**Conventions**:
|
|
448
|
+
|
|
443
449
|
- Named: `SET_*_QUERY_DATA`
|
|
444
450
|
- Takes QueryClient, key parameters, and response data
|
|
445
451
|
- Used in mutations to optimistically update cache
|
|
@@ -464,6 +470,7 @@ export const GetAccount = async ({
|
|
|
464
470
|
```
|
|
465
471
|
|
|
466
472
|
**Conventions**:
|
|
473
|
+
|
|
467
474
|
- Named: `Get*` (PascalCase)
|
|
468
475
|
- Accepts `adminApiParams` (and other params)
|
|
469
476
|
- Returns `Promise<ConnectedXMResponse<T>>`
|
|
@@ -495,6 +502,7 @@ export const useGetAccount = (
|
|
|
495
502
|
```
|
|
496
503
|
|
|
497
504
|
**Conventions**:
|
|
505
|
+
|
|
498
506
|
- Named: `useGet*` (camelCase with "use" prefix)
|
|
499
507
|
- Wraps the query function with `useConnectedSingleQuery` or similar
|
|
500
508
|
- Accepts options that extend base query options
|
|
@@ -507,55 +515,62 @@ Query keys follow a hierarchical structure that enables efficient cache manageme
|
|
|
507
515
|
#### Structure Pattern
|
|
508
516
|
|
|
509
517
|
```typescript
|
|
510
|
-
["RESOURCE_TYPE", ...filters, ...identifiers]
|
|
518
|
+
["RESOURCE_TYPE", ...filters, ...identifiers];
|
|
511
519
|
```
|
|
512
520
|
|
|
513
521
|
#### Examples
|
|
514
522
|
|
|
515
523
|
**Base List Query**:
|
|
524
|
+
|
|
516
525
|
```typescript
|
|
517
|
-
ACCOUNTS_QUERY_KEY()
|
|
526
|
+
ACCOUNTS_QUERY_KEY();
|
|
518
527
|
// Returns: ["ACCOUNTS"]
|
|
519
528
|
```
|
|
520
529
|
|
|
521
530
|
**Filtered List Query**:
|
|
531
|
+
|
|
522
532
|
```typescript
|
|
523
|
-
ACCOUNTS_QUERY_KEY(true, false) // verified=true, online=false
|
|
533
|
+
ACCOUNTS_QUERY_KEY(true, false); // verified=true, online=false
|
|
524
534
|
// Returns: ["ACCOUNTS", "VERIFIED", "OFFLINE"]
|
|
525
535
|
```
|
|
526
536
|
|
|
527
537
|
**Single Resource Query**:
|
|
538
|
+
|
|
528
539
|
```typescript
|
|
529
|
-
ACCOUNT_QUERY_KEY("account-123")
|
|
540
|
+
ACCOUNT_QUERY_KEY("account-123");
|
|
530
541
|
// Returns: ["ACCOUNTS", "account-123"]
|
|
531
542
|
// Note: Spreads parent key for hierarchy
|
|
532
543
|
```
|
|
533
544
|
|
|
534
545
|
**Nested Resource Query**:
|
|
546
|
+
|
|
535
547
|
```typescript
|
|
536
|
-
EVENT_SESSION_QUERY_KEY("event-123", "session-456")
|
|
548
|
+
EVENT_SESSION_QUERY_KEY("event-123", "session-456");
|
|
537
549
|
// Returns: ["EVENTS", "event-123", "SESSIONS", "session-456"]
|
|
538
550
|
```
|
|
539
551
|
|
|
540
552
|
#### Key Design Principles
|
|
541
553
|
|
|
542
554
|
1. **Hierarchical Inheritance**: Child keys include parent keys
|
|
555
|
+
|
|
543
556
|
```typescript
|
|
544
557
|
export const ACCOUNT_QUERY_KEY = (accountId: string) => [
|
|
545
|
-
...ACCOUNTS_QUERY_KEY(),
|
|
558
|
+
...ACCOUNTS_QUERY_KEY(), // Includes parent
|
|
546
559
|
accountId,
|
|
547
560
|
];
|
|
548
561
|
```
|
|
549
562
|
|
|
550
563
|
2. **Filter Representation**: Filters are included as string constants
|
|
564
|
+
|
|
551
565
|
```typescript
|
|
552
566
|
if (typeof verified !== "undefined")
|
|
553
567
|
keys.push(verified ? "VERIFIED" : "UNVERIFIED");
|
|
554
568
|
```
|
|
555
569
|
|
|
556
570
|
3. **Identifier Last**: Resource IDs come after filters
|
|
571
|
+
|
|
557
572
|
```typescript
|
|
558
|
-
["EVENTS", "PAST", "FEATURED", "event-123"]
|
|
573
|
+
["EVENTS", "PAST", "FEATURED", "event-123"];
|
|
559
574
|
```
|
|
560
575
|
|
|
561
576
|
4. **Consistent Naming**: Use SCREAMING_SNAKE_CASE for constants
|
|
@@ -590,6 +605,7 @@ export interface CreateAccountParams extends MutationParams {
|
|
|
590
605
|
```
|
|
591
606
|
|
|
592
607
|
**Conventions**:
|
|
608
|
+
|
|
593
609
|
- Extends `MutationParams` (includes `adminApiParams` and `queryClient`)
|
|
594
610
|
- Named: `*Params` (e.g., `CreateAccountParams`, `UpdateAccountParams`)
|
|
595
611
|
- Includes domain-specific parameters
|
|
@@ -613,17 +629,18 @@ export const CreateAccount = async ({
|
|
|
613
629
|
`/accounts`,
|
|
614
630
|
account
|
|
615
631
|
);
|
|
616
|
-
|
|
632
|
+
|
|
617
633
|
if (queryClient && data.status === "ok") {
|
|
618
634
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
619
635
|
SET_ACCOUNT_QUERY_DATA(queryClient, [data?.data.id], data);
|
|
620
636
|
}
|
|
621
|
-
|
|
637
|
+
|
|
622
638
|
return data;
|
|
623
639
|
};
|
|
624
640
|
```
|
|
625
641
|
|
|
626
642
|
**Conventions**:
|
|
643
|
+
|
|
627
644
|
- Named: `Create*`, `Update*`, `Delete*` (PascalCase)
|
|
628
645
|
- Accepts params including `adminApiParams` and `queryClient`
|
|
629
646
|
- Returns `Promise<ConnectedXMResponse<T>>`
|
|
@@ -655,6 +672,7 @@ export const useCreateAccount = (
|
|
|
655
672
|
```
|
|
656
673
|
|
|
657
674
|
**Conventions**:
|
|
675
|
+
|
|
658
676
|
- Named: `useCreate*`, `useUpdate*`, `useDelete*`
|
|
659
677
|
- Wraps mutation function with `useConnectedMutation`
|
|
660
678
|
- Options exclude `queryClient` and `adminApiParams` (injected automatically)
|
|
@@ -675,16 +693,19 @@ if (queryClient && data.status === "ok") {
|
|
|
675
693
|
```
|
|
676
694
|
|
|
677
695
|
**When to Use**:
|
|
696
|
+
|
|
678
697
|
- After creating new resources (adds to list)
|
|
679
698
|
- After deleting resources (removes from list)
|
|
680
699
|
- When you want fresh data from the server
|
|
681
700
|
|
|
682
701
|
**Benefits**:
|
|
702
|
+
|
|
683
703
|
- Ensures data consistency
|
|
684
704
|
- Handles edge cases automatically
|
|
685
705
|
- Simple to implement
|
|
686
706
|
|
|
687
707
|
**Drawbacks**:
|
|
708
|
+
|
|
688
709
|
- Causes network request
|
|
689
710
|
- May cause loading states
|
|
690
711
|
|
|
@@ -696,29 +717,33 @@ Directly update the cache with known data.
|
|
|
696
717
|
if (queryClient && data.status === "ok") {
|
|
697
718
|
// Invalidate list to show new item
|
|
698
719
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
699
|
-
|
|
720
|
+
|
|
700
721
|
// Optimistically update single item cache
|
|
701
722
|
SET_ACCOUNT_QUERY_DATA(queryClient, [data?.data.id], data);
|
|
702
723
|
}
|
|
703
724
|
```
|
|
704
725
|
|
|
705
726
|
**When to Use**:
|
|
727
|
+
|
|
706
728
|
- After updating existing resources
|
|
707
729
|
- When you have the complete updated data
|
|
708
730
|
- To provide instant UI feedback
|
|
709
731
|
|
|
710
732
|
**Benefits**:
|
|
733
|
+
|
|
711
734
|
- Instant UI updates
|
|
712
735
|
- Better user experience
|
|
713
736
|
- Reduces unnecessary requests
|
|
714
737
|
|
|
715
738
|
**Drawbacks**:
|
|
739
|
+
|
|
716
740
|
- Must ensure data consistency
|
|
717
741
|
- More complex implementation
|
|
718
742
|
|
|
719
743
|
#### Common Patterns
|
|
720
744
|
|
|
721
745
|
**Create Pattern**:
|
|
746
|
+
|
|
722
747
|
```typescript
|
|
723
748
|
// 1. Invalidate list (to show new item)
|
|
724
749
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
@@ -728,6 +753,7 @@ SET_ACCOUNT_QUERY_DATA(queryClient, [data?.data.id], data);
|
|
|
728
753
|
```
|
|
729
754
|
|
|
730
755
|
**Update Pattern**:
|
|
756
|
+
|
|
731
757
|
```typescript
|
|
732
758
|
// 1. Invalidate list (in case item appears in filtered views)
|
|
733
759
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
@@ -737,6 +763,7 @@ SET_ACCOUNT_QUERY_DATA(queryClient, [accountId], data);
|
|
|
737
763
|
```
|
|
738
764
|
|
|
739
765
|
**Delete Pattern**:
|
|
766
|
+
|
|
740
767
|
```typescript
|
|
741
768
|
// 1. Invalidate list (to remove item)
|
|
742
769
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
@@ -746,6 +773,7 @@ queryClient.removeQueries({ queryKey: ACCOUNT_QUERY_KEY(accountId) });
|
|
|
746
773
|
```
|
|
747
774
|
|
|
748
775
|
**Complex Invalidation**:
|
|
776
|
+
|
|
749
777
|
```typescript
|
|
750
778
|
// Invalidate multiple related queries
|
|
751
779
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
@@ -822,6 +850,7 @@ queries/
|
|
|
822
850
|
#### Domain Organization
|
|
823
851
|
|
|
824
852
|
Each domain folder contains:
|
|
853
|
+
|
|
825
854
|
- **Multiple query files**: One per endpoint/resource
|
|
826
855
|
- **index.ts**: Barrel export file that re-exports all queries from the domain
|
|
827
856
|
|
|
@@ -838,11 +867,13 @@ export * from "./useGetAccountEvents";
|
|
|
838
867
|
#### Base Utilities
|
|
839
868
|
|
|
840
869
|
The root `queries/` directory contains reusable query hooks:
|
|
870
|
+
|
|
841
871
|
- `useConnectedSingleQuery.ts` - Wrapper for single resource queries
|
|
842
872
|
- `useConnectedInfiniteQuery.ts` - Wrapper for paginated list queries
|
|
843
873
|
- `useConnectedCursorQuery.ts` - Wrapper for cursor-based queries
|
|
844
874
|
|
|
845
875
|
These provide:
|
|
876
|
+
|
|
846
877
|
- Consistent error handling
|
|
847
878
|
- Automatic retry logic
|
|
848
879
|
- Standardized query options
|
|
@@ -877,6 +908,7 @@ mutations/
|
|
|
877
908
|
#### Domain Organization
|
|
878
909
|
|
|
879
910
|
Similar to queries:
|
|
911
|
+
|
|
880
912
|
- **Multiple mutation files**: One per operation (Create, Update, Delete, etc.)
|
|
881
913
|
- **index.ts**: Barrel export file
|
|
882
914
|
- **translations/**: Subfolder for translation-specific mutations (when applicable)
|
|
@@ -884,6 +916,7 @@ Similar to queries:
|
|
|
884
916
|
#### Base Utility
|
|
885
917
|
|
|
886
918
|
`useConnectedMutation.ts` provides:
|
|
919
|
+
|
|
887
920
|
- Automatic `adminApiParams` injection
|
|
888
921
|
- Error handling integration
|
|
889
922
|
- QueryClient access
|
|
@@ -896,19 +929,23 @@ Utility functions used across queries and mutations.
|
|
|
896
929
|
#### Available Utilities
|
|
897
930
|
|
|
898
931
|
**Cache Management**:
|
|
932
|
+
|
|
899
933
|
- `CacheIndividualQueries.ts` - Caches individual items from list responses
|
|
900
934
|
- Used in queries to populate single-item caches from list responses
|
|
901
935
|
|
|
902
936
|
**Data Transformation**:
|
|
937
|
+
|
|
903
938
|
- `TransformPrice.ts` - Formats price values
|
|
904
939
|
- `GetImageVariant.ts` - Generates image URLs with variants
|
|
905
940
|
- `CalculateDuration.ts` - Calculates time durations
|
|
906
941
|
|
|
907
942
|
**Query Helpers**:
|
|
943
|
+
|
|
908
944
|
- `MergeInfinitePages.ts` - Flattens infinite query pages into single array
|
|
909
945
|
- `AppendInfiniteQuery.ts` - Appends new page to infinite query cache
|
|
910
946
|
|
|
911
947
|
**Type Utilities**:
|
|
948
|
+
|
|
912
949
|
- `IsUUID.ts` - Validates UUID format
|
|
913
950
|
- `GetErrorMessage.ts` - Extracts error messages from responses
|
|
914
951
|
|
|
@@ -933,46 +970,56 @@ The SDK implements standardized error handling across all queries and mutations.
|
|
|
933
970
|
All query hooks handle these status codes consistently:
|
|
934
971
|
|
|
935
972
|
**401 - Unauthorized**:
|
|
973
|
+
|
|
936
974
|
```typescript
|
|
937
975
|
if (error.response?.status === 401) {
|
|
938
976
|
if (onNotAuthorized) onNotAuthorized(error, queryKeys, shouldRedirect);
|
|
939
977
|
return false; // Don't retry
|
|
940
978
|
}
|
|
941
979
|
```
|
|
980
|
+
|
|
942
981
|
- Triggers `onNotAuthorized` callback
|
|
943
982
|
- Typically indicates expired token
|
|
944
983
|
- No automatic retry
|
|
945
984
|
|
|
946
985
|
**403/460/461 - Forbidden**:
|
|
986
|
+
|
|
947
987
|
```typescript
|
|
948
|
-
if (
|
|
949
|
-
|
|
950
|
-
|
|
988
|
+
if (
|
|
989
|
+
error.response?.status === 403 ||
|
|
990
|
+
error.response?.status === 460 ||
|
|
991
|
+
error.response?.status === 461
|
|
992
|
+
) {
|
|
951
993
|
if (onModuleForbidden) onModuleForbidden(error, queryKeys, shouldRedirect);
|
|
952
994
|
return false; // Don't retry
|
|
953
995
|
}
|
|
954
996
|
```
|
|
997
|
+
|
|
955
998
|
- Triggers `onModuleForbidden` callback
|
|
956
999
|
- Indicates user lacks permission
|
|
957
1000
|
- No automatic retry
|
|
958
1001
|
|
|
959
1002
|
**404 - Not Found**:
|
|
1003
|
+
|
|
960
1004
|
```typescript
|
|
961
1005
|
if (error.response?.status === 404) {
|
|
962
1006
|
if (onNotFound) onNotFound(error, queryKeys, shouldRedirect);
|
|
963
1007
|
return false; // Don't retry
|
|
964
1008
|
}
|
|
965
1009
|
```
|
|
1010
|
+
|
|
966
1011
|
- Triggers `onNotFound` callback
|
|
967
1012
|
- Resource doesn't exist
|
|
968
1013
|
- No automatic retry
|
|
969
1014
|
|
|
970
1015
|
**Other Errors**:
|
|
1016
|
+
|
|
971
1017
|
```typescript
|
|
972
1018
|
// Default retry logic
|
|
973
1019
|
if (failureCount < 3) return true;
|
|
974
1020
|
return false;
|
|
975
1021
|
```
|
|
1022
|
+
|
|
976
1023
|
- Retries up to 3 times
|
|
977
1024
|
- For network errors, timeouts, etc.
|
|
978
1025
|
|
|
@@ -985,6 +1032,7 @@ return false;
|
|
|
985
1032
|
#### Error Callbacks
|
|
986
1033
|
|
|
987
1034
|
All error callbacks receive:
|
|
1035
|
+
|
|
988
1036
|
1. **Error object**: Axios error with response data
|
|
989
1037
|
2. **Query key**: The React Query key that failed
|
|
990
1038
|
3. **Should redirect flag**: Whether redirect should occur
|
|
@@ -1118,6 +1166,7 @@ Caches individual items from a list response into their respective single-item q
|
|
|
1118
1166
|
**Purpose**: When fetching a list, also populate individual item caches for instant access.
|
|
1119
1167
|
|
|
1120
1168
|
**Signature**:
|
|
1169
|
+
|
|
1121
1170
|
```typescript
|
|
1122
1171
|
export const CacheIndividualQueries = <TData extends ItemWithId>(
|
|
1123
1172
|
page: ConnectedXMResponse<TData[]>,
|
|
@@ -1128,22 +1177,21 @@ export const CacheIndividualQueries = <TData extends ItemWithId>(
|
|
|
1128
1177
|
```
|
|
1129
1178
|
|
|
1130
1179
|
**Usage Example**:
|
|
1180
|
+
|
|
1131
1181
|
```typescript
|
|
1132
|
-
const { data } = await adminApi.get(
|
|
1133
|
-
CacheIndividualQueries(
|
|
1134
|
-
data,
|
|
1135
|
-
queryClient,
|
|
1136
|
-
(id) => ACCOUNT_QUERY_KEY(id)
|
|
1137
|
-
);
|
|
1182
|
+
const { data } = await adminApi.get("/accounts");
|
|
1183
|
+
CacheIndividualQueries(data, queryClient, (id) => ACCOUNT_QUERY_KEY(id));
|
|
1138
1184
|
```
|
|
1139
1185
|
|
|
1140
1186
|
**Features**:
|
|
1187
|
+
|
|
1141
1188
|
- Caches by `id`
|
|
1142
1189
|
- Also caches by `slug`, `username`, `name`, `code`, `alternateId` if available
|
|
1143
1190
|
- Sets cache timestamp to 1 minute ago (allows refetch if needed)
|
|
1144
1191
|
- Optional `itemMap` for data transformation
|
|
1145
1192
|
|
|
1146
1193
|
**When to Use**:
|
|
1194
|
+
|
|
1147
1195
|
- In list queries to populate individual caches
|
|
1148
1196
|
- After fetching paginated lists
|
|
1149
1197
|
- To enable instant navigation to detail pages
|
|
@@ -1155,11 +1203,13 @@ Direct cache update helpers for each resource type.
|
|
|
1155
1203
|
**Purpose**: Update cache with known data (optimistic updates).
|
|
1156
1204
|
|
|
1157
1205
|
**Pattern**:
|
|
1206
|
+
|
|
1158
1207
|
```typescript
|
|
1159
1208
|
SET_ACCOUNT_QUERY_DATA(queryClient, [accountId], response);
|
|
1160
1209
|
```
|
|
1161
1210
|
|
|
1162
1211
|
**Usage**:
|
|
1212
|
+
|
|
1163
1213
|
- After mutations to update cache immediately
|
|
1164
1214
|
- For optimistic UI updates
|
|
1165
1215
|
- When you have complete data
|
|
@@ -1171,13 +1221,15 @@ SET_ACCOUNT_QUERY_DATA(queryClient, [accountId], response);
|
|
|
1171
1221
|
Flattens infinite query pages into a single array.
|
|
1172
1222
|
|
|
1173
1223
|
**Signature**:
|
|
1224
|
+
|
|
1174
1225
|
```typescript
|
|
1175
1226
|
export function MergeInfinitePages<TData>(
|
|
1176
1227
|
data: InfiniteData<ConnectedXMResponse<TData[]>>
|
|
1177
|
-
): TData[]
|
|
1228
|
+
): TData[];
|
|
1178
1229
|
```
|
|
1179
1230
|
|
|
1180
1231
|
**Usage Example**:
|
|
1232
|
+
|
|
1181
1233
|
```typescript
|
|
1182
1234
|
const { data } = useGetAccounts();
|
|
1183
1235
|
const allAccounts = MergeInfinitePages(data);
|
|
@@ -1185,6 +1237,7 @@ const allAccounts = MergeInfinitePages(data);
|
|
|
1185
1237
|
```
|
|
1186
1238
|
|
|
1187
1239
|
**When to Use**:
|
|
1240
|
+
|
|
1188
1241
|
- Displaying all items from infinite query
|
|
1189
1242
|
- Filtering/searching across all pages
|
|
1190
1243
|
- Calculating totals across pages
|
|
@@ -1305,6 +1358,7 @@ export const useGet[Resource] = (
|
|
|
1305
1358
|
#### Step 6: Export from Index
|
|
1306
1359
|
|
|
1307
1360
|
Add to `src/queries/[domain]/index.ts`:
|
|
1361
|
+
|
|
1308
1362
|
```typescript
|
|
1309
1363
|
export * from "./useGet[Resource]";
|
|
1310
1364
|
```
|
|
@@ -1418,12 +1472,12 @@ export const Create[Resource] = async ({
|
|
|
1418
1472
|
`/[endpoint]`,
|
|
1419
1473
|
[resource]
|
|
1420
1474
|
);
|
|
1421
|
-
|
|
1475
|
+
|
|
1422
1476
|
if (queryClient && data.status === "ok") {
|
|
1423
1477
|
queryClient.invalidateQueries({ queryKey: [RESOURCES]_QUERY_KEY() });
|
|
1424
1478
|
SET_[RESOURCE]_QUERY_DATA(queryClient, [data?.data.id], data);
|
|
1425
1479
|
}
|
|
1426
|
-
|
|
1480
|
+
|
|
1427
1481
|
return data;
|
|
1428
1482
|
};
|
|
1429
1483
|
```
|
|
@@ -1454,6 +1508,7 @@ export const useCreate[Resource] = (
|
|
|
1454
1508
|
#### Step 5: Export from Index
|
|
1455
1509
|
|
|
1456
1510
|
Add to `src/mutations/[domain]/index.ts`:
|
|
1511
|
+
|
|
1457
1512
|
```typescript
|
|
1458
1513
|
export * from "./useCreate[Resource]";
|
|
1459
1514
|
```
|
|
@@ -1533,19 +1588,19 @@ export const useCreateGroup = (
|
|
|
1533
1588
|
|
|
1534
1589
|
```typescript
|
|
1535
1590
|
// Simple list
|
|
1536
|
-
ACCOUNTS_QUERY_KEY()
|
|
1591
|
+
ACCOUNTS_QUERY_KEY();
|
|
1537
1592
|
// ["ACCOUNTS"]
|
|
1538
1593
|
|
|
1539
1594
|
// Filtered list
|
|
1540
|
-
ACCOUNTS_QUERY_KEY(true, false)
|
|
1595
|
+
ACCOUNTS_QUERY_KEY(true, false);
|
|
1541
1596
|
// ["ACCOUNTS", "VERIFIED", "OFFLINE"]
|
|
1542
1597
|
|
|
1543
1598
|
// Single item (inherits parent)
|
|
1544
|
-
ACCOUNT_QUERY_KEY("123")
|
|
1599
|
+
ACCOUNT_QUERY_KEY("123");
|
|
1545
1600
|
// ["ACCOUNTS", "123"]
|
|
1546
1601
|
|
|
1547
1602
|
// Nested resource
|
|
1548
|
-
EVENT_SESSION_QUERY_KEY("event-123", "session-456")
|
|
1603
|
+
EVENT_SESSION_QUERY_KEY("event-123", "session-456");
|
|
1549
1604
|
// ["EVENTS", "event-123", "SESSIONS", "session-456"]
|
|
1550
1605
|
```
|
|
1551
1606
|
|
|
@@ -1553,14 +1608,14 @@ EVENT_SESSION_QUERY_KEY("event-123", "session-456")
|
|
|
1553
1608
|
|
|
1554
1609
|
```typescript
|
|
1555
1610
|
// ❌ Don't use IDs in base list keys
|
|
1556
|
-
ACCOUNTS_QUERY_KEY("account-123") // Wrong!
|
|
1611
|
+
ACCOUNTS_QUERY_KEY("account-123"); // Wrong!
|
|
1557
1612
|
|
|
1558
1613
|
// ❌ Don't forget to inherit parent
|
|
1559
|
-
ACCOUNT_QUERY_KEY("123")
|
|
1614
|
+
ACCOUNT_QUERY_KEY("123");
|
|
1560
1615
|
// Should be: [...ACCOUNTS_QUERY_KEY(), "123"]
|
|
1561
1616
|
|
|
1562
1617
|
// ❌ Don't use inconsistent naming
|
|
1563
|
-
accounts_query_key() // Should be ACCOUNTS_QUERY_KEY
|
|
1618
|
+
accounts_query_key(); // Should be ACCOUNTS_QUERY_KEY
|
|
1564
1619
|
```
|
|
1565
1620
|
|
|
1566
1621
|
### 8.4 Cache Management
|
|
@@ -1568,11 +1623,13 @@ accounts_query_key() // Should be ACCOUNTS_QUERY_KEY
|
|
|
1568
1623
|
#### When to Invalidate
|
|
1569
1624
|
|
|
1570
1625
|
**Always Invalidate**:
|
|
1626
|
+
|
|
1571
1627
|
- After creating new resources
|
|
1572
1628
|
- After deleting resources
|
|
1573
1629
|
- When data might be stale
|
|
1574
1630
|
|
|
1575
1631
|
**Example**:
|
|
1632
|
+
|
|
1576
1633
|
```typescript
|
|
1577
1634
|
// After create
|
|
1578
1635
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
@@ -1581,11 +1638,13 @@ queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
|
1581
1638
|
#### When to Update Directly
|
|
1582
1639
|
|
|
1583
1640
|
**Update Directly**:
|
|
1641
|
+
|
|
1584
1642
|
- After updating existing resources (you have the new data)
|
|
1585
1643
|
- For optimistic updates
|
|
1586
1644
|
- When you want instant UI feedback
|
|
1587
1645
|
|
|
1588
1646
|
**Example**:
|
|
1647
|
+
|
|
1589
1648
|
```typescript
|
|
1590
1649
|
// After update
|
|
1591
1650
|
SET_ACCOUNT_QUERY_DATA(queryClient, [accountId], updatedData);
|
|
@@ -1594,18 +1653,21 @@ SET_ACCOUNT_QUERY_DATA(queryClient, [accountId], updatedData);
|
|
|
1594
1653
|
#### Best Practices
|
|
1595
1654
|
|
|
1596
1655
|
1. **Combine Both**: Invalidate lists, update individual items
|
|
1656
|
+
|
|
1597
1657
|
```typescript
|
|
1598
1658
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
1599
1659
|
SET_ACCOUNT_QUERY_DATA(queryClient, [accountId], data);
|
|
1600
1660
|
```
|
|
1601
1661
|
|
|
1602
1662
|
2. **Invalidate Related Queries**: If an account update affects events, invalidate both
|
|
1663
|
+
|
|
1603
1664
|
```typescript
|
|
1604
1665
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
1605
1666
|
queryClient.invalidateQueries({ queryKey: EVENTS_QUERY_KEY() });
|
|
1606
1667
|
```
|
|
1607
1668
|
|
|
1608
1669
|
3. **Use Hierarchical Keys**: Invalidating parent invalidates children
|
|
1670
|
+
|
|
1609
1671
|
```typescript
|
|
1610
1672
|
// This invalidates all account queries
|
|
1611
1673
|
queryClient.invalidateQueries({ queryKey: ACCOUNTS_QUERY_KEY() });
|
|
@@ -1627,10 +1689,10 @@ SET_ACCOUNT_QUERY_DATA(queryClient, [accountId], updatedData);
|
|
|
1627
1689
|
#### Mocking AdminAPI
|
|
1628
1690
|
|
|
1629
1691
|
```typescript
|
|
1630
|
-
import { vi } from
|
|
1631
|
-
import { GetAdminAPI } from
|
|
1692
|
+
import { vi } from "vitest";
|
|
1693
|
+
import { GetAdminAPI } from "@src/AdminAPI";
|
|
1632
1694
|
|
|
1633
|
-
vi.mock(
|
|
1695
|
+
vi.mock("@src/AdminAPI", () => ({
|
|
1634
1696
|
GetAdminAPI: vi.fn(),
|
|
1635
1697
|
}));
|
|
1636
1698
|
```
|
|
@@ -1638,22 +1700,22 @@ vi.mock('@src/AdminAPI', () => ({
|
|
|
1638
1700
|
#### Testing Query Hooks
|
|
1639
1701
|
|
|
1640
1702
|
```typescript
|
|
1641
|
-
import { renderHook, waitFor } from
|
|
1642
|
-
import { QueryClient, QueryClientProvider } from
|
|
1643
|
-
import { useGetAccount } from
|
|
1703
|
+
import { renderHook, waitFor } from "@testing-library/react";
|
|
1704
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
1705
|
+
import { useGetAccount } from "@src/queries";
|
|
1644
1706
|
|
|
1645
|
-
test(
|
|
1707
|
+
test("fetches account data", async () => {
|
|
1646
1708
|
const queryClient = new QueryClient();
|
|
1647
1709
|
const wrapper = ({ children }) => (
|
|
1648
|
-
<QueryClientProvider client={queryClient}>
|
|
1649
|
-
{children}
|
|
1650
|
-
</QueryClientProvider>
|
|
1710
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
1651
1711
|
);
|
|
1652
1712
|
|
|
1653
|
-
const { result } = renderHook(() => useGetAccount(
|
|
1713
|
+
const { result } = renderHook(() => useGetAccount("account-123"), {
|
|
1714
|
+
wrapper,
|
|
1715
|
+
});
|
|
1654
1716
|
|
|
1655
1717
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
1656
|
-
expect(result.current.data?.data.id).toBe(
|
|
1718
|
+
expect(result.current.data?.data.id).toBe("account-123");
|
|
1657
1719
|
});
|
|
1658
1720
|
```
|
|
1659
1721
|
|
|
@@ -1662,14 +1724,14 @@ test('fetches account data', async () => {
|
|
|
1662
1724
|
#### Testing Mutation Hooks
|
|
1663
1725
|
|
|
1664
1726
|
```typescript
|
|
1665
|
-
import { renderHook, waitFor } from
|
|
1666
|
-
import { useCreateAccount } from
|
|
1727
|
+
import { renderHook, waitFor } from "@testing-library/react";
|
|
1728
|
+
import { useCreateAccount } from "@src/mutations";
|
|
1667
1729
|
|
|
1668
|
-
test(
|
|
1730
|
+
test("creates account and updates cache", async () => {
|
|
1669
1731
|
const { result } = renderHook(() => useCreateAccount());
|
|
1670
1732
|
|
|
1671
1733
|
result.current.mutate({
|
|
1672
|
-
account: { email:
|
|
1734
|
+
account: { email: "test@example.com" },
|
|
1673
1735
|
});
|
|
1674
1736
|
|
|
1675
1737
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
@@ -1682,17 +1744,17 @@ test('creates account and updates cache', async () => {
|
|
|
1682
1744
|
|
|
1683
1745
|
```typescript
|
|
1684
1746
|
const mockAccountResponse = {
|
|
1685
|
-
status:
|
|
1686
|
-
message:
|
|
1747
|
+
status: "ok",
|
|
1748
|
+
message: "Success",
|
|
1687
1749
|
data: {
|
|
1688
|
-
id:
|
|
1689
|
-
email:
|
|
1750
|
+
id: "account-123",
|
|
1751
|
+
email: "test@example.com",
|
|
1690
1752
|
// ... other fields
|
|
1691
|
-
}
|
|
1753
|
+
},
|
|
1692
1754
|
};
|
|
1693
1755
|
|
|
1694
1756
|
(GetAdminAPI as any).mockResolvedValue({
|
|
1695
|
-
get: vi.fn().mockResolvedValue({ data: mockAccountResponse })
|
|
1757
|
+
get: vi.fn().mockResolvedValue({ data: mockAccountResponse }),
|
|
1696
1758
|
});
|
|
1697
1759
|
```
|
|
1698
1760
|
|
|
@@ -1713,6 +1775,7 @@ The SDK uses `tsup` for building:
|
|
|
1713
1775
|
```
|
|
1714
1776
|
|
|
1715
1777
|
**Output**:
|
|
1778
|
+
|
|
1716
1779
|
- CommonJS: `dist/index.cjs`
|
|
1717
1780
|
- ES Modules: `dist/index.js`
|
|
1718
1781
|
- Type Definitions: `dist/index.d.ts`
|
|
@@ -1769,18 +1832,21 @@ dist/
|
|
|
1769
1832
|
### Common Patterns Quick Reference
|
|
1770
1833
|
|
|
1771
1834
|
**Query Pattern**:
|
|
1835
|
+
|
|
1772
1836
|
```typescript
|
|
1773
1837
|
QUERY_KEY → SETTER → QUERY_FUNCTION → REACT_HOOK
|
|
1774
1838
|
```
|
|
1775
1839
|
|
|
1776
1840
|
**Mutation Pattern**:
|
|
1841
|
+
|
|
1777
1842
|
```typescript
|
|
1778
1843
|
PARAMS_INTERFACE → MUTATION_FUNCTION → REACT_HOOK
|
|
1779
1844
|
```
|
|
1780
1845
|
|
|
1781
1846
|
**Cache Update Pattern**:
|
|
1847
|
+
|
|
1782
1848
|
```typescript
|
|
1783
|
-
invalidateQueries() + SET_*_QUERY_DATA()
|
|
1849
|
+
invalidateQueries() + SET_ * _QUERY_DATA();
|
|
1784
1850
|
```
|
|
1785
1851
|
|
|
1786
1852
|
---
|
|
@@ -1788,5 +1854,3 @@ invalidateQueries() + SET_*_QUERY_DATA()
|
|
|
1788
1854
|
## Questions or Issues?
|
|
1789
1855
|
|
|
1790
1856
|
For questions about this SDK or to report issues, please contact the ConnectedXM development team or refer to the main repository documentation.
|
|
1791
|
-
|
|
1792
|
-
|