@lasterp/shared 1.0.0-alpha.8 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/index.d.ts +157 -84
- package/dist/node/index.js +109 -3
- package/dist/rn/index.d.ts +157 -84
- package/dist/rn/index.js +109 -3
- package/package.json +2 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -7
package/dist/node/index.d.ts
CHANGED
|
@@ -1,28 +1,101 @@
|
|
|
1
1
|
interface Item {
|
|
2
|
-
|
|
2
|
+
item_code: string;
|
|
3
3
|
area: string;
|
|
4
4
|
model: string;
|
|
5
5
|
region: string;
|
|
6
6
|
grade: string;
|
|
7
|
-
|
|
7
|
+
grade_issuer: string;
|
|
8
8
|
color: string;
|
|
9
9
|
storage: string;
|
|
10
10
|
memory: string;
|
|
11
11
|
connectivity: string;
|
|
12
|
-
|
|
12
|
+
carrier_compatibility: string;
|
|
13
13
|
}
|
|
14
14
|
interface ItemVariant extends Item {
|
|
15
|
-
|
|
15
|
+
item_variant: string;
|
|
16
16
|
}
|
|
17
17
|
interface Model {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
model_number: string;
|
|
19
|
+
sim_card_type: string;
|
|
20
20
|
connectivity: string;
|
|
21
21
|
}
|
|
22
22
|
interface Colour {
|
|
23
23
|
color: string;
|
|
24
24
|
hex: string;
|
|
25
|
-
|
|
25
|
+
color_system: string;
|
|
26
|
+
}
|
|
27
|
+
interface Globals {
|
|
28
|
+
header: Header;
|
|
29
|
+
footer: Footer;
|
|
30
|
+
}
|
|
31
|
+
interface Brand {
|
|
32
|
+
brandImage?: string;
|
|
33
|
+
}
|
|
34
|
+
interface NavbarItem {
|
|
35
|
+
label: string;
|
|
36
|
+
enableDropdown: boolean;
|
|
37
|
+
enableLink: boolean;
|
|
38
|
+
link?: string;
|
|
39
|
+
dropdownDescription?: string;
|
|
40
|
+
dropdownCTALabel?: string;
|
|
41
|
+
dropdownCTALink?: string;
|
|
42
|
+
groups?: NavbarSubItemGroup[];
|
|
43
|
+
}
|
|
44
|
+
interface NavbarSubItemGroup {
|
|
45
|
+
title: string;
|
|
46
|
+
items: NavbarSubItem[];
|
|
47
|
+
}
|
|
48
|
+
interface NavbarSubItem {
|
|
49
|
+
label: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
image?: string;
|
|
52
|
+
link?: string;
|
|
53
|
+
}
|
|
54
|
+
interface Topbar {
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
layout: string;
|
|
57
|
+
items: TopbarItem[];
|
|
58
|
+
}
|
|
59
|
+
interface TopbarItem {
|
|
60
|
+
icon?: string;
|
|
61
|
+
label: string;
|
|
62
|
+
link?: string;
|
|
63
|
+
}
|
|
64
|
+
interface Header {
|
|
65
|
+
headerType?: string;
|
|
66
|
+
brand: Brand;
|
|
67
|
+
tabs: NavbarItem[];
|
|
68
|
+
topbar: Topbar;
|
|
69
|
+
}
|
|
70
|
+
interface FooterItemGroup {
|
|
71
|
+
title: string;
|
|
72
|
+
items: FooterItem[];
|
|
73
|
+
}
|
|
74
|
+
interface FooterItem {
|
|
75
|
+
label: string;
|
|
76
|
+
link?: string;
|
|
77
|
+
}
|
|
78
|
+
interface Footer {
|
|
79
|
+
footerType?: string;
|
|
80
|
+
groups: FooterItemGroup[];
|
|
81
|
+
copyright?: string;
|
|
82
|
+
address?: string;
|
|
83
|
+
country?: string;
|
|
84
|
+
phone?: string;
|
|
85
|
+
email?: string;
|
|
86
|
+
}
|
|
87
|
+
interface Hero {
|
|
88
|
+
type: string;
|
|
89
|
+
data: Record<string, unknown>;
|
|
90
|
+
}
|
|
91
|
+
interface Block {
|
|
92
|
+
type: string;
|
|
93
|
+
data: Record<string, unknown>;
|
|
94
|
+
}
|
|
95
|
+
interface Page {
|
|
96
|
+
slug: string;
|
|
97
|
+
hero?: Hero;
|
|
98
|
+
blocks: Block[];
|
|
26
99
|
}
|
|
27
100
|
import { FrappeApp, FrappeAuth, FrappeCall } from "frappe-js-sdk";
|
|
28
101
|
import { FrappeDB } from "frappe-js-sdk/lib/db";
|
|
@@ -41,6 +114,15 @@ interface FrappeConfig {
|
|
|
41
114
|
enableSocket?: boolean;
|
|
42
115
|
socketPort?: string;
|
|
43
116
|
}
|
|
117
|
+
interface FrappeError {
|
|
118
|
+
httpStatus: number;
|
|
119
|
+
httpStatusText: string;
|
|
120
|
+
message: string;
|
|
121
|
+
exception: string;
|
|
122
|
+
exc_type?: string;
|
|
123
|
+
traceback?: string;
|
|
124
|
+
_server_messages?: string;
|
|
125
|
+
}
|
|
44
126
|
interface TokenParams {
|
|
45
127
|
/** Whether to use token for API calls */
|
|
46
128
|
useToken: boolean;
|
|
@@ -119,11 +201,19 @@ declare const useFrappeGetCall: <T = any>(method: string, params?: Record<string
|
|
|
119
201
|
retry?: boolean | number;
|
|
120
202
|
}, type?: "GET" | "POST") => {
|
|
121
203
|
data: T | undefined;
|
|
122
|
-
error:
|
|
204
|
+
error: FrappeError | null;
|
|
123
205
|
isLoading: boolean;
|
|
124
206
|
isFetching: boolean;
|
|
125
207
|
refetch: () => void;
|
|
126
208
|
};
|
|
209
|
+
interface FrappeMutationResult<T = any> {
|
|
210
|
+
call: (params: Record<string, any>) => Promise<T>;
|
|
211
|
+
result: T | null;
|
|
212
|
+
loading: boolean;
|
|
213
|
+
error: FrappeError | null;
|
|
214
|
+
isCompleted: boolean;
|
|
215
|
+
reset: () => void;
|
|
216
|
+
}
|
|
127
217
|
/**
|
|
128
218
|
*
|
|
129
219
|
* @param method - name of the method to call (POST request) (will be dotted path e.g. "frappe.client.set_value")
|
|
@@ -138,7 +228,7 @@ declare const useFrappeGetCall: <T = any>(method: string, params?: Record<string
|
|
|
138
228
|
* }
|
|
139
229
|
*
|
|
140
230
|
*/
|
|
141
|
-
declare const useFrappePostCall:
|
|
231
|
+
declare const useFrappePostCall: <T = any>(method: string) => FrappeMutationResult<T>;
|
|
142
232
|
/**
|
|
143
233
|
*
|
|
144
234
|
* @param method - name of the method to call (PUT request) (will be dotted path e.g. "frappe.client.set_value")
|
|
@@ -152,7 +242,7 @@ declare const useFrappePostCall: unknown;
|
|
|
152
242
|
* const message = await call({ doctype: "User", docname: "test@example.com", fieldname: "full_name", value: "John Doe" })
|
|
153
243
|
* }
|
|
154
244
|
*/
|
|
155
|
-
declare const useFrappePutCall:
|
|
245
|
+
declare const useFrappePutCall: <T = any>(method: string) => FrappeMutationResult<T>;
|
|
156
246
|
/**
|
|
157
247
|
*
|
|
158
248
|
* @param method - name of the method to call (DELETE request) (will be dotted path e.g. "frappe.client.delete")
|
|
@@ -166,7 +256,7 @@ declare const useFrappePutCall: unknown;
|
|
|
166
256
|
* const message = await call({ doctype: "User", docname: "test@example.com" })
|
|
167
257
|
* }
|
|
168
258
|
*/
|
|
169
|
-
declare const useFrappeDeleteCall:
|
|
259
|
+
declare const useFrappeDeleteCall: <T = any>(method: string) => FrappeMutationResult<T>;
|
|
170
260
|
import { Filter } from "frappe-js-sdk/lib/db/types";
|
|
171
261
|
/**
|
|
172
262
|
* Hook to fetch number of documents from the database
|
|
@@ -188,7 +278,7 @@ declare const useFrappeGetDocCount: <T = any>(doctype: string, filters?: Filter<
|
|
|
188
278
|
retry?: boolean | number;
|
|
189
279
|
}) => {
|
|
190
280
|
data: number | undefined;
|
|
191
|
-
error:
|
|
281
|
+
error: FrappeError | null;
|
|
192
282
|
isLoading: boolean;
|
|
193
283
|
isFetching: boolean;
|
|
194
284
|
refetch: () => void;
|
|
@@ -209,7 +299,7 @@ import { FrappeDoc } from "frappe-js-sdk/lib/db/types";
|
|
|
209
299
|
declare const useFrappeCreateDoc: <T = any>() => {
|
|
210
300
|
createDoc: (doctype: string, doc: T) => Promise<FrappeDoc<T>>;
|
|
211
301
|
loading: boolean;
|
|
212
|
-
error:
|
|
302
|
+
error: FrappeError | null;
|
|
213
303
|
isCompleted: boolean;
|
|
214
304
|
reset: () => void;
|
|
215
305
|
};
|
|
@@ -230,7 +320,7 @@ declare const useFrappeDeleteDoc: <T = any>() => {
|
|
|
230
320
|
message: string;
|
|
231
321
|
}>;
|
|
232
322
|
loading: boolean;
|
|
233
|
-
error:
|
|
323
|
+
error: FrappeError | null;
|
|
234
324
|
isCompleted: boolean;
|
|
235
325
|
reset: () => void;
|
|
236
326
|
};
|
|
@@ -382,7 +472,7 @@ declare const useFrappeGetDoc: <T = any>(doctype: string, name?: string, options
|
|
|
382
472
|
retry?: boolean | number;
|
|
383
473
|
}) => {
|
|
384
474
|
data: FrappeDoc2<T> | undefined;
|
|
385
|
-
error:
|
|
475
|
+
error: FrappeError | null;
|
|
386
476
|
isLoading: boolean;
|
|
387
477
|
isFetching: boolean;
|
|
388
478
|
refetch: () => void;
|
|
@@ -421,7 +511,7 @@ declare const useFrappeGetDocList: <
|
|
|
421
511
|
retry?: boolean | number;
|
|
422
512
|
}) => {
|
|
423
513
|
data: T[] | undefined;
|
|
424
|
-
error:
|
|
514
|
+
error: FrappeError | null;
|
|
425
515
|
isLoading: boolean;
|
|
426
516
|
isFetching: boolean;
|
|
427
517
|
refetch: () => void;
|
|
@@ -471,7 +561,7 @@ declare const useSearch: (doctype: string, text: string, filters?: Filter2[], li
|
|
|
471
561
|
data: {
|
|
472
562
|
message: SearchResult[];
|
|
473
563
|
} | undefined;
|
|
474
|
-
error:
|
|
564
|
+
error: FrappeError | null;
|
|
475
565
|
isLoading: boolean;
|
|
476
566
|
isFetching: boolean;
|
|
477
567
|
refetch: () => void;
|
|
@@ -492,83 +582,66 @@ import { FrappeDoc as FrappeDoc4 } from "frappe-js-sdk/lib/db/types";
|
|
|
492
582
|
declare const useFrappeUpdateDoc: <T = any>() => {
|
|
493
583
|
updateDoc: (doctype: string, docname: string | null, doc: Partial<T>) => Promise<FrappeDoc4<T>>;
|
|
494
584
|
loading: boolean;
|
|
495
|
-
error:
|
|
585
|
+
error: FrappeError | null;
|
|
496
586
|
isCompleted: boolean;
|
|
497
587
|
reset: () => void;
|
|
498
588
|
};
|
|
499
|
-
interface
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
enableLink: boolean;
|
|
510
|
-
link?: string;
|
|
511
|
-
dropdownDescription?: string;
|
|
512
|
-
dropdownCTALabel?: string;
|
|
513
|
-
dropdownCTALink?: string;
|
|
514
|
-
groups?: NavbarSubItemGroup[];
|
|
515
|
-
}
|
|
516
|
-
interface NavbarSubItemGroup {
|
|
517
|
-
title: string;
|
|
518
|
-
items: NavbarSubItem[];
|
|
519
|
-
}
|
|
520
|
-
interface NavbarSubItem {
|
|
521
|
-
label: string;
|
|
522
|
-
description?: string;
|
|
523
|
-
image?: string;
|
|
524
|
-
link?: string;
|
|
525
|
-
}
|
|
526
|
-
interface Topbar {
|
|
527
|
-
enabled: boolean;
|
|
528
|
-
layout: string;
|
|
529
|
-
items: TopbarItem[];
|
|
589
|
+
interface VariantSelectorProps<T extends {
|
|
590
|
+
id: string;
|
|
591
|
+
specs: Record<string, string>;
|
|
592
|
+
}> {
|
|
593
|
+
variants: T[];
|
|
594
|
+
attributes: {
|
|
595
|
+
key: string;
|
|
596
|
+
values: string[];
|
|
597
|
+
}[];
|
|
598
|
+
defaultId?: string;
|
|
530
599
|
}
|
|
531
|
-
interface
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
interface Header {
|
|
537
|
-
headerType?: string;
|
|
538
|
-
brand: Brand;
|
|
539
|
-
tabs: NavbarItem[];
|
|
540
|
-
topbar: Topbar;
|
|
600
|
+
interface VariantSelectorOption {
|
|
601
|
+
key: string;
|
|
602
|
+
value: string;
|
|
603
|
+
state: "selected" | "available" | "unavailable";
|
|
541
604
|
}
|
|
542
|
-
interface
|
|
543
|
-
|
|
544
|
-
|
|
605
|
+
interface VariantSelectorResult {
|
|
606
|
+
variantId?: string;
|
|
607
|
+
onOptionSelect: (key: string, value: string) => void;
|
|
608
|
+
getOptions: (key: string) => VariantSelectorOption[];
|
|
545
609
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
610
|
+
declare const useVariantSelector: <T extends {
|
|
611
|
+
id: string;
|
|
612
|
+
specs: Record<string, string>;
|
|
613
|
+
}>(props: VariantSelectorProps<T>) => VariantSelectorResult;
|
|
614
|
+
interface Category {
|
|
615
|
+
name: string;
|
|
549
616
|
}
|
|
550
|
-
interface
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
country?: string;
|
|
556
|
-
phone?: string;
|
|
557
|
-
email?: string;
|
|
617
|
+
interface Product {
|
|
618
|
+
name: string;
|
|
619
|
+
image: string;
|
|
620
|
+
item_code: string;
|
|
621
|
+
description: string;
|
|
558
622
|
}
|
|
559
|
-
interface
|
|
560
|
-
|
|
561
|
-
|
|
623
|
+
interface ProductVariant extends ItemVariant {
|
|
624
|
+
name: string;
|
|
625
|
+
currency: string;
|
|
626
|
+
rate: number;
|
|
627
|
+
qty: number;
|
|
628
|
+
images: string[];
|
|
629
|
+
specs: Record<string, string>;
|
|
562
630
|
}
|
|
563
|
-
interface
|
|
564
|
-
|
|
565
|
-
|
|
631
|
+
interface ShopContext {
|
|
632
|
+
categories: Category[];
|
|
633
|
+
grades: string[];
|
|
634
|
+
selected_category?: Category;
|
|
635
|
+
selected_grade?: string;
|
|
636
|
+
products: Product[];
|
|
566
637
|
}
|
|
567
|
-
interface
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
638
|
+
interface ProductContext {
|
|
639
|
+
product: Product;
|
|
640
|
+
specs: Record<string, string[]>;
|
|
641
|
+
variants: ProductVariant[];
|
|
642
|
+
models: Record<string, Model>;
|
|
643
|
+
colors: Record<string, Colour>;
|
|
571
644
|
}
|
|
572
645
|
import { camelize, decamelize, camelizeKeys, decamelizeKeys } from "humps";
|
|
573
646
|
declare function equalsIgnoreCase(str1: string, str2: string): boolean;
|
|
574
|
-
export { useSearch, useFrappeUpdateDoc, useFrappePutCall, useFrappePrefetchDoc, useFrappePostCall, useFrappeGetDocList, useFrappeGetDocCount, useFrappeGetDoc, useFrappeGetCall, useFrappeFileUpload, useFrappeEventListener, useFrappeDocumentEventListener, useFrappeDocTypeEventListener, useFrappeDeleteDoc, useFrappeDeleteCall, useFrappeCreateDoc, useFrappeAuth, equalsIgnoreCase, decamelizeKeys, decamelize, camelizeKeys, camelize, ViewerEventData, UseFrappeFileUploadReturnType, TopbarItem, Topbar, TokenParams, SearchResult, Page, NavbarSubItemGroup, NavbarSubItem, NavbarItem, Model, ItemVariant, Item, Hero, Header, Globals, FrappeProviderProps, FrappeProvider, FrappeFileUploadResponse, FrappeContext, FrappeConfig, FooterItemGroup, FooterItem, Footer, DocumentUpdateEventData, DocTypeListUpdateEventData, Colour, Brand, Block };
|
|
647
|
+
export { useVariantSelector, useSearch, useFrappeUpdateDoc, useFrappePutCall, useFrappePrefetchDoc, useFrappePostCall, useFrappeGetDocList, useFrappeGetDocCount, useFrappeGetDoc, useFrappeGetCall, useFrappeFileUpload, useFrappeEventListener, useFrappeDocumentEventListener, useFrappeDocTypeEventListener, useFrappeDeleteDoc, useFrappeDeleteCall, useFrappeCreateDoc, useFrappeAuth, equalsIgnoreCase, decamelizeKeys, decamelize, camelizeKeys, camelize, ViewerEventData, VariantSelectorResult, VariantSelectorProps, VariantSelectorOption, UseFrappeFileUploadReturnType, TopbarItem, Topbar, TokenParams, ShopContext, SearchResult, ProductVariant, ProductContext, Product, Page, NavbarSubItemGroup, NavbarSubItem, NavbarItem, Model, ItemVariant, Item, Hero, Header, Globals, FrappeProviderProps, FrappeProvider, FrappeMutationResult, FrappeFileUploadResponse, FrappeError, FrappeContext, FrappeConfig, FooterItemGroup, FooterItem, Footer, DocumentUpdateEventData, DocTypeListUpdateEventData, Colour, Category, Brand, Block };
|
package/dist/node/index.js
CHANGED
|
@@ -46,7 +46,7 @@ class SocketIO {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// src/frappe/provider.tsx
|
|
49
|
-
import {
|
|
49
|
+
import { jsx } from "react/jsx-runtime";
|
|
50
50
|
|
|
51
51
|
var FrappeContext = createContext(null);
|
|
52
52
|
var FrappeProvider = ({
|
|
@@ -71,10 +71,10 @@ var FrappeProvider = ({
|
|
|
71
71
|
socket: enableSocket ? new SocketIO(url, siteName, socketPort, tokenParams).socket : undefined
|
|
72
72
|
};
|
|
73
73
|
}, [url, tokenParams, enableSocket, socketPort, siteName, customHeaders]);
|
|
74
|
-
return /* @__PURE__ */
|
|
74
|
+
return /* @__PURE__ */ jsx(FrappeContext.Provider, {
|
|
75
75
|
value: frappeConfig,
|
|
76
76
|
children
|
|
77
|
-
}
|
|
77
|
+
});
|
|
78
78
|
};
|
|
79
79
|
// src/frappe/hooks/auth.ts
|
|
80
80
|
import { useCallback, useContext, useState, useEffect } from "react";
|
|
@@ -465,12 +465,118 @@ var useFrappeUpdateDoc = () => {
|
|
|
465
465
|
reset
|
|
466
466
|
};
|
|
467
467
|
};
|
|
468
|
+
// src/hooks/use-variant-selector/hook.ts
|
|
469
|
+
import { useCallback as useCallback4, useMemo as useMemo2, useState as useState5 } from "react";
|
|
470
|
+
|
|
471
|
+
// src/hooks/use-variant-selector/utils.ts
|
|
472
|
+
function findVariant(variants, specs, caseInsensitive) {
|
|
473
|
+
return variants.find((variant) => {
|
|
474
|
+
return Object.entries(specs).every(([key, value]) => {
|
|
475
|
+
const vv = variant.specs[key];
|
|
476
|
+
if (!vv)
|
|
477
|
+
return false;
|
|
478
|
+
if (caseInsensitive) {
|
|
479
|
+
return vv.toLowerCase() === value.toLowerCase();
|
|
480
|
+
}
|
|
481
|
+
return vv === value;
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
function findVariants(variants, specs, caseInsensitive) {
|
|
486
|
+
return variants.filter((variant) => {
|
|
487
|
+
return Object.entries(specs).every(([key, value]) => {
|
|
488
|
+
const vv = variant.specs[key];
|
|
489
|
+
if (!vv)
|
|
490
|
+
return false;
|
|
491
|
+
if (caseInsensitive) {
|
|
492
|
+
return vv.toLowerCase() === value.toLowerCase();
|
|
493
|
+
}
|
|
494
|
+
return vv === value;
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/hooks/use-variant-selector/hook.ts
|
|
500
|
+
var useVariantSelector = (props) => {
|
|
501
|
+
const { variants, attributes, defaultId } = props;
|
|
502
|
+
const [selectedSpecs, setSelectedSpecs] = useState5(() => {
|
|
503
|
+
if (defaultId) {
|
|
504
|
+
const variant = variants.find((v) => v.id === defaultId);
|
|
505
|
+
return variant?.specs || {};
|
|
506
|
+
}
|
|
507
|
+
return {};
|
|
508
|
+
});
|
|
509
|
+
const variantId = useMemo2(() => {
|
|
510
|
+
const complete = attributes.every((attr) => selectedSpecs[attr.key]);
|
|
511
|
+
if (!complete)
|
|
512
|
+
return;
|
|
513
|
+
const variant = findVariant(variants, selectedSpecs);
|
|
514
|
+
return variant?.id;
|
|
515
|
+
}, [variants, selectedSpecs, attributes]);
|
|
516
|
+
const options = useMemo2(() => {
|
|
517
|
+
const result = {};
|
|
518
|
+
attributes.forEach((attr, attrIndex) => {
|
|
519
|
+
const constraints = {};
|
|
520
|
+
for (let i = 0;i < attrIndex; i++) {
|
|
521
|
+
const key = attributes[i]?.key;
|
|
522
|
+
if (key && selectedSpecs[key])
|
|
523
|
+
constraints[key] = selectedSpecs[key];
|
|
524
|
+
}
|
|
525
|
+
const matchingVariants = findVariants(variants, constraints);
|
|
526
|
+
const availableValues = new Set(matchingVariants.map((v) => v.specs[attr.key]));
|
|
527
|
+
result[attr.key] = attr.values.map((value) => {
|
|
528
|
+
const isSelected = selectedSpecs[attr.key] === value;
|
|
529
|
+
const isAvailable = availableValues.has(value);
|
|
530
|
+
return {
|
|
531
|
+
key: attr.key,
|
|
532
|
+
value,
|
|
533
|
+
state: isSelected ? "selected" : isAvailable ? "available" : "unavailable"
|
|
534
|
+
};
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
return result;
|
|
538
|
+
}, [variants, attributes, selectedSpecs]);
|
|
539
|
+
const onOptionSelect = useCallback4((key, value) => {
|
|
540
|
+
setSelectedSpecs((prev) => {
|
|
541
|
+
const newSpecs = { ...prev, [key]: value };
|
|
542
|
+
const attrIndex = attributes.findIndex((a) => a.key === key);
|
|
543
|
+
for (let i = attrIndex + 1;i < attributes.length; i++) {
|
|
544
|
+
const downstreamAttr = attributes[i];
|
|
545
|
+
if (!downstreamAttr)
|
|
546
|
+
continue;
|
|
547
|
+
const constraints = {};
|
|
548
|
+
for (let j = 0;j < i; j++) {
|
|
549
|
+
const k = attributes[j]?.key;
|
|
550
|
+
if (k && newSpecs[k])
|
|
551
|
+
constraints[k] = newSpecs[k];
|
|
552
|
+
}
|
|
553
|
+
const matching = findVariants(variants, constraints);
|
|
554
|
+
const availableValues = new Set(matching.map((v) => v.specs[downstreamAttr.key]));
|
|
555
|
+
if (!availableValues.has(newSpecs[downstreamAttr.key])) {
|
|
556
|
+
const firstAvailable = downstreamAttr.values.find((v) => availableValues.has(v));
|
|
557
|
+
if (firstAvailable) {
|
|
558
|
+
newSpecs[downstreamAttr.key] = firstAvailable;
|
|
559
|
+
} else {
|
|
560
|
+
delete newSpecs[downstreamAttr.key];
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return newSpecs;
|
|
565
|
+
});
|
|
566
|
+
}, [attributes, variants]);
|
|
567
|
+
return {
|
|
568
|
+
variantId,
|
|
569
|
+
onOptionSelect,
|
|
570
|
+
getOptions: (key) => options[key] || []
|
|
571
|
+
};
|
|
572
|
+
};
|
|
468
573
|
// src/utils/char.ts
|
|
469
574
|
import { camelize, decamelize, camelizeKeys, decamelizeKeys } from "humps";
|
|
470
575
|
function equalsIgnoreCase(str1, str2) {
|
|
471
576
|
return str1.localeCompare(str2, undefined, { sensitivity: "accent" }) === 0;
|
|
472
577
|
}
|
|
473
578
|
export {
|
|
579
|
+
useVariantSelector,
|
|
474
580
|
useSearch,
|
|
475
581
|
useFrappeUpdateDoc,
|
|
476
582
|
useFrappePutCall,
|
package/dist/rn/index.d.ts
CHANGED
|
@@ -1,28 +1,101 @@
|
|
|
1
1
|
interface Item {
|
|
2
|
-
|
|
2
|
+
item_code: string;
|
|
3
3
|
area: string;
|
|
4
4
|
model: string;
|
|
5
5
|
region: string;
|
|
6
6
|
grade: string;
|
|
7
|
-
|
|
7
|
+
grade_issuer: string;
|
|
8
8
|
color: string;
|
|
9
9
|
storage: string;
|
|
10
10
|
memory: string;
|
|
11
11
|
connectivity: string;
|
|
12
|
-
|
|
12
|
+
carrier_compatibility: string;
|
|
13
13
|
}
|
|
14
14
|
interface ItemVariant extends Item {
|
|
15
|
-
|
|
15
|
+
item_variant: string;
|
|
16
16
|
}
|
|
17
17
|
interface Model {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
model_number: string;
|
|
19
|
+
sim_card_type: string;
|
|
20
20
|
connectivity: string;
|
|
21
21
|
}
|
|
22
22
|
interface Colour {
|
|
23
23
|
color: string;
|
|
24
24
|
hex: string;
|
|
25
|
-
|
|
25
|
+
color_system: string;
|
|
26
|
+
}
|
|
27
|
+
interface Globals {
|
|
28
|
+
header: Header;
|
|
29
|
+
footer: Footer;
|
|
30
|
+
}
|
|
31
|
+
interface Brand {
|
|
32
|
+
brandImage?: string;
|
|
33
|
+
}
|
|
34
|
+
interface NavbarItem {
|
|
35
|
+
label: string;
|
|
36
|
+
enableDropdown: boolean;
|
|
37
|
+
enableLink: boolean;
|
|
38
|
+
link?: string;
|
|
39
|
+
dropdownDescription?: string;
|
|
40
|
+
dropdownCTALabel?: string;
|
|
41
|
+
dropdownCTALink?: string;
|
|
42
|
+
groups?: NavbarSubItemGroup[];
|
|
43
|
+
}
|
|
44
|
+
interface NavbarSubItemGroup {
|
|
45
|
+
title: string;
|
|
46
|
+
items: NavbarSubItem[];
|
|
47
|
+
}
|
|
48
|
+
interface NavbarSubItem {
|
|
49
|
+
label: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
image?: string;
|
|
52
|
+
link?: string;
|
|
53
|
+
}
|
|
54
|
+
interface Topbar {
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
layout: string;
|
|
57
|
+
items: TopbarItem[];
|
|
58
|
+
}
|
|
59
|
+
interface TopbarItem {
|
|
60
|
+
icon?: string;
|
|
61
|
+
label: string;
|
|
62
|
+
link?: string;
|
|
63
|
+
}
|
|
64
|
+
interface Header {
|
|
65
|
+
headerType?: string;
|
|
66
|
+
brand: Brand;
|
|
67
|
+
tabs: NavbarItem[];
|
|
68
|
+
topbar: Topbar;
|
|
69
|
+
}
|
|
70
|
+
interface FooterItemGroup {
|
|
71
|
+
title: string;
|
|
72
|
+
items: FooterItem[];
|
|
73
|
+
}
|
|
74
|
+
interface FooterItem {
|
|
75
|
+
label: string;
|
|
76
|
+
link?: string;
|
|
77
|
+
}
|
|
78
|
+
interface Footer {
|
|
79
|
+
footerType?: string;
|
|
80
|
+
groups: FooterItemGroup[];
|
|
81
|
+
copyright?: string;
|
|
82
|
+
address?: string;
|
|
83
|
+
country?: string;
|
|
84
|
+
phone?: string;
|
|
85
|
+
email?: string;
|
|
86
|
+
}
|
|
87
|
+
interface Hero {
|
|
88
|
+
type: string;
|
|
89
|
+
data: Record<string, unknown>;
|
|
90
|
+
}
|
|
91
|
+
interface Block {
|
|
92
|
+
type: string;
|
|
93
|
+
data: Record<string, unknown>;
|
|
94
|
+
}
|
|
95
|
+
interface Page {
|
|
96
|
+
slug: string;
|
|
97
|
+
hero?: Hero;
|
|
98
|
+
blocks: Block[];
|
|
26
99
|
}
|
|
27
100
|
import { FrappeApp, FrappeAuth, FrappeCall } from "frappe-js-sdk";
|
|
28
101
|
import { FrappeDB } from "frappe-js-sdk/lib/db";
|
|
@@ -41,6 +114,15 @@ interface FrappeConfig {
|
|
|
41
114
|
enableSocket?: boolean;
|
|
42
115
|
socketPort?: string;
|
|
43
116
|
}
|
|
117
|
+
interface FrappeError {
|
|
118
|
+
httpStatus: number;
|
|
119
|
+
httpStatusText: string;
|
|
120
|
+
message: string;
|
|
121
|
+
exception: string;
|
|
122
|
+
exc_type?: string;
|
|
123
|
+
traceback?: string;
|
|
124
|
+
_server_messages?: string;
|
|
125
|
+
}
|
|
44
126
|
interface TokenParams {
|
|
45
127
|
/** Whether to use token for API calls */
|
|
46
128
|
useToken: boolean;
|
|
@@ -119,11 +201,19 @@ declare const useFrappeGetCall: <T = any>(method: string, params?: Record<string
|
|
|
119
201
|
retry?: boolean | number;
|
|
120
202
|
}, type?: "GET" | "POST") => {
|
|
121
203
|
data: T | undefined;
|
|
122
|
-
error:
|
|
204
|
+
error: FrappeError | null;
|
|
123
205
|
isLoading: boolean;
|
|
124
206
|
isFetching: boolean;
|
|
125
207
|
refetch: () => void;
|
|
126
208
|
};
|
|
209
|
+
interface FrappeMutationResult<T = any> {
|
|
210
|
+
call: (params: Record<string, any>) => Promise<T>;
|
|
211
|
+
result: T | null;
|
|
212
|
+
loading: boolean;
|
|
213
|
+
error: FrappeError | null;
|
|
214
|
+
isCompleted: boolean;
|
|
215
|
+
reset: () => void;
|
|
216
|
+
}
|
|
127
217
|
/**
|
|
128
218
|
*
|
|
129
219
|
* @param method - name of the method to call (POST request) (will be dotted path e.g. "frappe.client.set_value")
|
|
@@ -138,7 +228,7 @@ declare const useFrappeGetCall: <T = any>(method: string, params?: Record<string
|
|
|
138
228
|
* }
|
|
139
229
|
*
|
|
140
230
|
*/
|
|
141
|
-
declare const useFrappePostCall:
|
|
231
|
+
declare const useFrappePostCall: <T = any>(method: string) => FrappeMutationResult<T>;
|
|
142
232
|
/**
|
|
143
233
|
*
|
|
144
234
|
* @param method - name of the method to call (PUT request) (will be dotted path e.g. "frappe.client.set_value")
|
|
@@ -152,7 +242,7 @@ declare const useFrappePostCall: unknown;
|
|
|
152
242
|
* const message = await call({ doctype: "User", docname: "test@example.com", fieldname: "full_name", value: "John Doe" })
|
|
153
243
|
* }
|
|
154
244
|
*/
|
|
155
|
-
declare const useFrappePutCall:
|
|
245
|
+
declare const useFrappePutCall: <T = any>(method: string) => FrappeMutationResult<T>;
|
|
156
246
|
/**
|
|
157
247
|
*
|
|
158
248
|
* @param method - name of the method to call (DELETE request) (will be dotted path e.g. "frappe.client.delete")
|
|
@@ -166,7 +256,7 @@ declare const useFrappePutCall: unknown;
|
|
|
166
256
|
* const message = await call({ doctype: "User", docname: "test@example.com" })
|
|
167
257
|
* }
|
|
168
258
|
*/
|
|
169
|
-
declare const useFrappeDeleteCall:
|
|
259
|
+
declare const useFrappeDeleteCall: <T = any>(method: string) => FrappeMutationResult<T>;
|
|
170
260
|
import { Filter } from "frappe-js-sdk/lib/db/types";
|
|
171
261
|
/**
|
|
172
262
|
* Hook to fetch number of documents from the database
|
|
@@ -188,7 +278,7 @@ declare const useFrappeGetDocCount: <T = any>(doctype: string, filters?: Filter<
|
|
|
188
278
|
retry?: boolean | number;
|
|
189
279
|
}) => {
|
|
190
280
|
data: number | undefined;
|
|
191
|
-
error:
|
|
281
|
+
error: FrappeError | null;
|
|
192
282
|
isLoading: boolean;
|
|
193
283
|
isFetching: boolean;
|
|
194
284
|
refetch: () => void;
|
|
@@ -209,7 +299,7 @@ import { FrappeDoc } from "frappe-js-sdk/lib/db/types";
|
|
|
209
299
|
declare const useFrappeCreateDoc: <T = any>() => {
|
|
210
300
|
createDoc: (doctype: string, doc: T) => Promise<FrappeDoc<T>>;
|
|
211
301
|
loading: boolean;
|
|
212
|
-
error:
|
|
302
|
+
error: FrappeError | null;
|
|
213
303
|
isCompleted: boolean;
|
|
214
304
|
reset: () => void;
|
|
215
305
|
};
|
|
@@ -230,7 +320,7 @@ declare const useFrappeDeleteDoc: <T = any>() => {
|
|
|
230
320
|
message: string;
|
|
231
321
|
}>;
|
|
232
322
|
loading: boolean;
|
|
233
|
-
error:
|
|
323
|
+
error: FrappeError | null;
|
|
234
324
|
isCompleted: boolean;
|
|
235
325
|
reset: () => void;
|
|
236
326
|
};
|
|
@@ -382,7 +472,7 @@ declare const useFrappeGetDoc: <T = any>(doctype: string, name?: string, options
|
|
|
382
472
|
retry?: boolean | number;
|
|
383
473
|
}) => {
|
|
384
474
|
data: FrappeDoc2<T> | undefined;
|
|
385
|
-
error:
|
|
475
|
+
error: FrappeError | null;
|
|
386
476
|
isLoading: boolean;
|
|
387
477
|
isFetching: boolean;
|
|
388
478
|
refetch: () => void;
|
|
@@ -421,7 +511,7 @@ declare const useFrappeGetDocList: <
|
|
|
421
511
|
retry?: boolean | number;
|
|
422
512
|
}) => {
|
|
423
513
|
data: T[] | undefined;
|
|
424
|
-
error:
|
|
514
|
+
error: FrappeError | null;
|
|
425
515
|
isLoading: boolean;
|
|
426
516
|
isFetching: boolean;
|
|
427
517
|
refetch: () => void;
|
|
@@ -471,7 +561,7 @@ declare const useSearch: (doctype: string, text: string, filters?: Filter2[], li
|
|
|
471
561
|
data: {
|
|
472
562
|
message: SearchResult[];
|
|
473
563
|
} | undefined;
|
|
474
|
-
error:
|
|
564
|
+
error: FrappeError | null;
|
|
475
565
|
isLoading: boolean;
|
|
476
566
|
isFetching: boolean;
|
|
477
567
|
refetch: () => void;
|
|
@@ -492,83 +582,66 @@ import { FrappeDoc as FrappeDoc4 } from "frappe-js-sdk/lib/db/types";
|
|
|
492
582
|
declare const useFrappeUpdateDoc: <T = any>() => {
|
|
493
583
|
updateDoc: (doctype: string, docname: string | null, doc: Partial<T>) => Promise<FrappeDoc4<T>>;
|
|
494
584
|
loading: boolean;
|
|
495
|
-
error:
|
|
585
|
+
error: FrappeError | null;
|
|
496
586
|
isCompleted: boolean;
|
|
497
587
|
reset: () => void;
|
|
498
588
|
};
|
|
499
|
-
interface
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
enableLink: boolean;
|
|
510
|
-
link?: string;
|
|
511
|
-
dropdownDescription?: string;
|
|
512
|
-
dropdownCTALabel?: string;
|
|
513
|
-
dropdownCTALink?: string;
|
|
514
|
-
groups?: NavbarSubItemGroup[];
|
|
515
|
-
}
|
|
516
|
-
interface NavbarSubItemGroup {
|
|
517
|
-
title: string;
|
|
518
|
-
items: NavbarSubItem[];
|
|
519
|
-
}
|
|
520
|
-
interface NavbarSubItem {
|
|
521
|
-
label: string;
|
|
522
|
-
description?: string;
|
|
523
|
-
image?: string;
|
|
524
|
-
link?: string;
|
|
525
|
-
}
|
|
526
|
-
interface Topbar {
|
|
527
|
-
enabled: boolean;
|
|
528
|
-
layout: string;
|
|
529
|
-
items: TopbarItem[];
|
|
589
|
+
interface VariantSelectorProps<T extends {
|
|
590
|
+
id: string;
|
|
591
|
+
specs: Record<string, string>;
|
|
592
|
+
}> {
|
|
593
|
+
variants: T[];
|
|
594
|
+
attributes: {
|
|
595
|
+
key: string;
|
|
596
|
+
values: string[];
|
|
597
|
+
}[];
|
|
598
|
+
defaultId?: string;
|
|
530
599
|
}
|
|
531
|
-
interface
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
interface Header {
|
|
537
|
-
headerType?: string;
|
|
538
|
-
brand: Brand;
|
|
539
|
-
tabs: NavbarItem[];
|
|
540
|
-
topbar: Topbar;
|
|
600
|
+
interface VariantSelectorOption {
|
|
601
|
+
key: string;
|
|
602
|
+
value: string;
|
|
603
|
+
state: "selected" | "available" | "unavailable";
|
|
541
604
|
}
|
|
542
|
-
interface
|
|
543
|
-
|
|
544
|
-
|
|
605
|
+
interface VariantSelectorResult {
|
|
606
|
+
variantId?: string;
|
|
607
|
+
onOptionSelect: (key: string, value: string) => void;
|
|
608
|
+
getOptions: (key: string) => VariantSelectorOption[];
|
|
545
609
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
610
|
+
declare const useVariantSelector: <T extends {
|
|
611
|
+
id: string;
|
|
612
|
+
specs: Record<string, string>;
|
|
613
|
+
}>(props: VariantSelectorProps<T>) => VariantSelectorResult;
|
|
614
|
+
interface Category {
|
|
615
|
+
name: string;
|
|
549
616
|
}
|
|
550
|
-
interface
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
country?: string;
|
|
556
|
-
phone?: string;
|
|
557
|
-
email?: string;
|
|
617
|
+
interface Product {
|
|
618
|
+
name: string;
|
|
619
|
+
image: string;
|
|
620
|
+
item_code: string;
|
|
621
|
+
description: string;
|
|
558
622
|
}
|
|
559
|
-
interface
|
|
560
|
-
|
|
561
|
-
|
|
623
|
+
interface ProductVariant extends ItemVariant {
|
|
624
|
+
name: string;
|
|
625
|
+
currency: string;
|
|
626
|
+
rate: number;
|
|
627
|
+
qty: number;
|
|
628
|
+
images: string[];
|
|
629
|
+
specs: Record<string, string>;
|
|
562
630
|
}
|
|
563
|
-
interface
|
|
564
|
-
|
|
565
|
-
|
|
631
|
+
interface ShopContext {
|
|
632
|
+
categories: Category[];
|
|
633
|
+
grades: string[];
|
|
634
|
+
selected_category?: Category;
|
|
635
|
+
selected_grade?: string;
|
|
636
|
+
products: Product[];
|
|
566
637
|
}
|
|
567
|
-
interface
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
638
|
+
interface ProductContext {
|
|
639
|
+
product: Product;
|
|
640
|
+
specs: Record<string, string[]>;
|
|
641
|
+
variants: ProductVariant[];
|
|
642
|
+
models: Record<string, Model>;
|
|
643
|
+
colors: Record<string, Colour>;
|
|
571
644
|
}
|
|
572
645
|
import { camelize, decamelize, camelizeKeys, decamelizeKeys } from "humps";
|
|
573
646
|
declare function equalsIgnoreCase(str1: string, str2: string): boolean;
|
|
574
|
-
export { useSearch, useFrappeUpdateDoc, useFrappePutCall, useFrappePrefetchDoc, useFrappePostCall, useFrappeGetDocList, useFrappeGetDocCount, useFrappeGetDoc, useFrappeGetCall, useFrappeFileUpload, useFrappeEventListener, useFrappeDocumentEventListener, useFrappeDocTypeEventListener, useFrappeDeleteDoc, useFrappeDeleteCall, useFrappeCreateDoc, useFrappeAuth, equalsIgnoreCase, decamelizeKeys, decamelize, camelizeKeys, camelize, ViewerEventData, UseFrappeFileUploadReturnType, TopbarItem, Topbar, TokenParams, SearchResult, Page, NavbarSubItemGroup, NavbarSubItem, NavbarItem, Model, ItemVariant, Item, Hero, Header, Globals, FrappeProviderProps, FrappeProvider, FrappeFileUploadResponse, FrappeContext, FrappeConfig, FooterItemGroup, FooterItem, Footer, DocumentUpdateEventData, DocTypeListUpdateEventData, Colour, Brand, Block };
|
|
647
|
+
export { useVariantSelector, useSearch, useFrappeUpdateDoc, useFrappePutCall, useFrappePrefetchDoc, useFrappePostCall, useFrappeGetDocList, useFrappeGetDocCount, useFrappeGetDoc, useFrappeGetCall, useFrappeFileUpload, useFrappeEventListener, useFrappeDocumentEventListener, useFrappeDocTypeEventListener, useFrappeDeleteDoc, useFrappeDeleteCall, useFrappeCreateDoc, useFrappeAuth, equalsIgnoreCase, decamelizeKeys, decamelize, camelizeKeys, camelize, ViewerEventData, VariantSelectorResult, VariantSelectorProps, VariantSelectorOption, UseFrappeFileUploadReturnType, TopbarItem, Topbar, TokenParams, ShopContext, SearchResult, ProductVariant, ProductContext, Product, Page, NavbarSubItemGroup, NavbarSubItem, NavbarItem, Model, ItemVariant, Item, Hero, Header, Globals, FrappeProviderProps, FrappeProvider, FrappeMutationResult, FrappeFileUploadResponse, FrappeError, FrappeContext, FrappeConfig, FooterItemGroup, FooterItem, Footer, DocumentUpdateEventData, DocTypeListUpdateEventData, Colour, Category, Brand, Block };
|
package/dist/rn/index.js
CHANGED
|
@@ -46,7 +46,7 @@ class SocketIO {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// src/frappe/provider.tsx
|
|
49
|
-
import {
|
|
49
|
+
import { jsx } from "react/jsx-runtime";
|
|
50
50
|
|
|
51
51
|
var FrappeContext = createContext(null);
|
|
52
52
|
var FrappeProvider = ({
|
|
@@ -71,10 +71,10 @@ var FrappeProvider = ({
|
|
|
71
71
|
socket: enableSocket ? new SocketIO(url, siteName, socketPort, tokenParams).socket : undefined
|
|
72
72
|
};
|
|
73
73
|
}, [url, tokenParams, enableSocket, socketPort, siteName, customHeaders]);
|
|
74
|
-
return /* @__PURE__ */
|
|
74
|
+
return /* @__PURE__ */ jsx(FrappeContext.Provider, {
|
|
75
75
|
value: frappeConfig,
|
|
76
76
|
children
|
|
77
|
-
}
|
|
77
|
+
});
|
|
78
78
|
};
|
|
79
79
|
// src/frappe/hooks/auth.ts
|
|
80
80
|
import { useCallback, useContext, useState, useEffect } from "react";
|
|
@@ -465,12 +465,118 @@ var useFrappeUpdateDoc = () => {
|
|
|
465
465
|
reset
|
|
466
466
|
};
|
|
467
467
|
};
|
|
468
|
+
// src/hooks/use-variant-selector/hook.ts
|
|
469
|
+
import { useCallback as useCallback4, useMemo as useMemo2, useState as useState5 } from "react";
|
|
470
|
+
|
|
471
|
+
// src/hooks/use-variant-selector/utils.ts
|
|
472
|
+
function findVariant(variants, specs, caseInsensitive) {
|
|
473
|
+
return variants.find((variant) => {
|
|
474
|
+
return Object.entries(specs).every(([key, value]) => {
|
|
475
|
+
const vv = variant.specs[key];
|
|
476
|
+
if (!vv)
|
|
477
|
+
return false;
|
|
478
|
+
if (caseInsensitive) {
|
|
479
|
+
return vv.toLowerCase() === value.toLowerCase();
|
|
480
|
+
}
|
|
481
|
+
return vv === value;
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
function findVariants(variants, specs, caseInsensitive) {
|
|
486
|
+
return variants.filter((variant) => {
|
|
487
|
+
return Object.entries(specs).every(([key, value]) => {
|
|
488
|
+
const vv = variant.specs[key];
|
|
489
|
+
if (!vv)
|
|
490
|
+
return false;
|
|
491
|
+
if (caseInsensitive) {
|
|
492
|
+
return vv.toLowerCase() === value.toLowerCase();
|
|
493
|
+
}
|
|
494
|
+
return vv === value;
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/hooks/use-variant-selector/hook.ts
|
|
500
|
+
var useVariantSelector = (props) => {
|
|
501
|
+
const { variants, attributes, defaultId } = props;
|
|
502
|
+
const [selectedSpecs, setSelectedSpecs] = useState5(() => {
|
|
503
|
+
if (defaultId) {
|
|
504
|
+
const variant = variants.find((v) => v.id === defaultId);
|
|
505
|
+
return variant?.specs || {};
|
|
506
|
+
}
|
|
507
|
+
return {};
|
|
508
|
+
});
|
|
509
|
+
const variantId = useMemo2(() => {
|
|
510
|
+
const complete = attributes.every((attr) => selectedSpecs[attr.key]);
|
|
511
|
+
if (!complete)
|
|
512
|
+
return;
|
|
513
|
+
const variant = findVariant(variants, selectedSpecs);
|
|
514
|
+
return variant?.id;
|
|
515
|
+
}, [variants, selectedSpecs, attributes]);
|
|
516
|
+
const options = useMemo2(() => {
|
|
517
|
+
const result = {};
|
|
518
|
+
attributes.forEach((attr, attrIndex) => {
|
|
519
|
+
const constraints = {};
|
|
520
|
+
for (let i = 0;i < attrIndex; i++) {
|
|
521
|
+
const key = attributes[i]?.key;
|
|
522
|
+
if (key && selectedSpecs[key])
|
|
523
|
+
constraints[key] = selectedSpecs[key];
|
|
524
|
+
}
|
|
525
|
+
const matchingVariants = findVariants(variants, constraints);
|
|
526
|
+
const availableValues = new Set(matchingVariants.map((v) => v.specs[attr.key]));
|
|
527
|
+
result[attr.key] = attr.values.map((value) => {
|
|
528
|
+
const isSelected = selectedSpecs[attr.key] === value;
|
|
529
|
+
const isAvailable = availableValues.has(value);
|
|
530
|
+
return {
|
|
531
|
+
key: attr.key,
|
|
532
|
+
value,
|
|
533
|
+
state: isSelected ? "selected" : isAvailable ? "available" : "unavailable"
|
|
534
|
+
};
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
return result;
|
|
538
|
+
}, [variants, attributes, selectedSpecs]);
|
|
539
|
+
const onOptionSelect = useCallback4((key, value) => {
|
|
540
|
+
setSelectedSpecs((prev) => {
|
|
541
|
+
const newSpecs = { ...prev, [key]: value };
|
|
542
|
+
const attrIndex = attributes.findIndex((a) => a.key === key);
|
|
543
|
+
for (let i = attrIndex + 1;i < attributes.length; i++) {
|
|
544
|
+
const downstreamAttr = attributes[i];
|
|
545
|
+
if (!downstreamAttr)
|
|
546
|
+
continue;
|
|
547
|
+
const constraints = {};
|
|
548
|
+
for (let j = 0;j < i; j++) {
|
|
549
|
+
const k = attributes[j]?.key;
|
|
550
|
+
if (k && newSpecs[k])
|
|
551
|
+
constraints[k] = newSpecs[k];
|
|
552
|
+
}
|
|
553
|
+
const matching = findVariants(variants, constraints);
|
|
554
|
+
const availableValues = new Set(matching.map((v) => v.specs[downstreamAttr.key]));
|
|
555
|
+
if (!availableValues.has(newSpecs[downstreamAttr.key])) {
|
|
556
|
+
const firstAvailable = downstreamAttr.values.find((v) => availableValues.has(v));
|
|
557
|
+
if (firstAvailable) {
|
|
558
|
+
newSpecs[downstreamAttr.key] = firstAvailable;
|
|
559
|
+
} else {
|
|
560
|
+
delete newSpecs[downstreamAttr.key];
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return newSpecs;
|
|
565
|
+
});
|
|
566
|
+
}, [attributes, variants]);
|
|
567
|
+
return {
|
|
568
|
+
variantId,
|
|
569
|
+
onOptionSelect,
|
|
570
|
+
getOptions: (key) => options[key] || []
|
|
571
|
+
};
|
|
572
|
+
};
|
|
468
573
|
// src/utils/char.ts
|
|
469
574
|
import { camelize, decamelize, camelizeKeys, decamelizeKeys } from "humps";
|
|
470
575
|
function equalsIgnoreCase(str1, str2) {
|
|
471
576
|
return str1.localeCompare(str2, undefined, { sensitivity: "accent" }) === 0;
|
|
472
577
|
}
|
|
473
578
|
export {
|
|
579
|
+
useVariantSelector,
|
|
474
580
|
useSearch,
|
|
475
581
|
useFrappeUpdateDoc,
|
|
476
582
|
useFrappePutCall,
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lasterp/shared",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
4
4
|
"description": "Shared repo for webapp and native app",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
8
8
|
],
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "bunup",
|
|
10
|
+
"build": "NODE_ENV=production bunup",
|
|
11
11
|
"dev": "bunup --watch",
|
|
12
12
|
"type-check": "tsc --noEmit"
|
|
13
13
|
},
|
package/dist/index.d.ts
DELETED