@hartl-services/medusa-storefront 0.1.0 → 0.2.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/README.md +11 -0
- package/dist/browser/actions.d.ts +4 -0
- package/dist/browser/cart-mutations.d.ts +2 -1
- package/dist/browser/cart-mutations.js +2 -2
- package/dist/browser/index.d.ts +5 -1
- package/dist/browser/query-keys.d.ts +1 -0
- package/dist/browser/query-keys.js +2 -0
- package/dist/browser/query-options.d.ts +4 -0
- package/dist/browser/query-options.js +6 -0
- package/dist/core/catalog/category-filter.d.ts +16 -0
- package/dist/core/catalog/category-filter.js +33 -0
- package/dist/core/form-values.d.ts +49 -6
- package/dist/core/form-values.js +65 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/texts-de.d.ts +2 -0
- package/dist/core/texts-de.js +2 -0
- package/dist/react/use-account.d.ts +10 -0
- package/dist/react/use-account.js +10 -0
- package/dist/react/use-auth.d.ts +8 -1
- package/dist/react/use-auth.js +32 -0
- package/dist/react/use-cart.d.ts +26 -1
- package/dist/react/use-cart.js +50 -3
- package/dist/react/use-checkout-address-form.d.ts +18 -18
- package/dist/react/use-checkout.d.ts +12 -12
- package/dist/registry/account.json +1 -1
- package/dist/registry/affiliate.json +1 -1
- package/dist/registry/cart.json +1 -1
- package/dist/registry/checkout.json +1 -1
- package/dist/registry/form.json +1 -1
- package/dist/registry/registry.json +6 -6
- package/dist/registry/setup.json +2 -2
- package/dist/server/catalog.d.ts +9 -1
- package/dist/server/catalog.js +17 -1
- package/dist/server/customer.d.ts +17 -1
- package/dist/server/customer.js +89 -3
- package/dist/server/index.d.ts +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,17 @@ The copied files belong to the shop: style them, rename them, change the
|
|
|
73
73
|
wording. Keep the commerce logic in the package — a fix there reaches the shop
|
|
74
74
|
with the next version; a fix only in a copied file does not.
|
|
75
75
|
|
|
76
|
+
## Optional actions
|
|
77
|
+
|
|
78
|
+
Some server functions are optional in `createStorefrontBrowser`'s `actions`,
|
|
79
|
+
so a shop wired before they existed keeps working. Add the `"use server"`
|
|
80
|
+
wrapper (as in the `setup` item's `lib/storefront/actions.ts`) to use them:
|
|
81
|
+
|
|
82
|
+
| Action | Since | Used by |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `registerCustomerWithProfile` | 0.2.0 | `usePasswordAuth().registerWithProfile` (name, phone, default address) |
|
|
85
|
+
| `getOrder` | 0.2.0 | `useAccountOrderDetail` (order lines and totals in the account) |
|
|
86
|
+
|
|
76
87
|
## Wiring rules
|
|
77
88
|
|
|
78
89
|
- Create `createStorefrontServer` and `createStorefrontBrowser` once each, at
|
|
@@ -15,6 +15,8 @@ export interface StorefrontActions {
|
|
|
15
15
|
loginCustomer: Server["customer"]["loginCustomer"];
|
|
16
16
|
checkCustomerVerification: Server["customer"]["checkCustomerVerification"];
|
|
17
17
|
registerCustomer: Server["customer"]["registerCustomer"];
|
|
18
|
+
/** Optional (since 0.2.0): registration with name, phone and address. */
|
|
19
|
+
registerCustomerWithProfile?: Server["customer"]["registerCustomerWithProfile"];
|
|
18
20
|
requestLoginCode: Server["customer"]["requestLoginCode"];
|
|
19
21
|
loginWithCode: Server["customer"]["loginWithCode"];
|
|
20
22
|
requestCustomerPasswordReset: Server["customer"]["requestCustomerPasswordReset"];
|
|
@@ -44,6 +46,8 @@ export interface StorefrontActions {
|
|
|
44
46
|
revokeAffiliateCouponCode: Server["affiliate"]["revokeAffiliateCouponCode"];
|
|
45
47
|
createConsignmentReport: Server["affiliate"]["createConsignmentReport"];
|
|
46
48
|
listOrders: Server["orders"]["listOrders"];
|
|
49
|
+
/** Optional (since 0.2.0): one order with lines and totals (account order detail). */
|
|
50
|
+
getOrder?: Server["orders"]["getOrder"];
|
|
47
51
|
listDocuments: Server["documents"]["listDocuments"];
|
|
48
52
|
}
|
|
49
53
|
export {};
|
|
@@ -25,8 +25,9 @@ export declare function createCartMutations({ actions, createClient, referral, r
|
|
|
25
25
|
}>;
|
|
26
26
|
completeCart(cartId: string): Promise<HttpTypes.StoreCompleteCartResponse>;
|
|
27
27
|
/** Adds one unit of a variant, creating the cart first when there is none. */
|
|
28
|
-
addProductToCart({ activeCart, regionId, variantId }: {
|
|
28
|
+
addProductToCart({ activeCart, quantity, regionId, variantId }: {
|
|
29
29
|
activeCart: Pick<HttpTypes.StoreCart, "id"> | null;
|
|
30
|
+
quantity?: number;
|
|
30
31
|
regionId: string;
|
|
31
32
|
variantId: string;
|
|
32
33
|
}): Promise<Pick<HttpTypes.StoreCart, "id">>;
|
|
@@ -125,10 +125,10 @@ export function createCartMutations({ actions, createClient, referral, reportChe
|
|
|
125
125
|
return result;
|
|
126
126
|
},
|
|
127
127
|
/** Adds one unit of a variant, creating the cart first when there is none. */
|
|
128
|
-
async addProductToCart({ activeCart, regionId, variantId }) {
|
|
128
|
+
async addProductToCart({ activeCart, quantity = 1, regionId, variantId }) {
|
|
129
129
|
const cart = activeCart ?? (await createCart({ region_id: regionId }));
|
|
130
130
|
await addCartLineItem(cart.id, {
|
|
131
|
-
quantity
|
|
131
|
+
quantity,
|
|
132
132
|
variant_id: variantId,
|
|
133
133
|
});
|
|
134
134
|
return cart;
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -79,8 +79,9 @@ export declare function createStorefrontBrowser(config: StorefrontBrowserConfig)
|
|
|
79
79
|
ready: true;
|
|
80
80
|
}>;
|
|
81
81
|
completeCart(cartId: string): Promise<HttpTypes.StoreCompleteCartResponse>;
|
|
82
|
-
addProductToCart({ activeCart, regionId, variantId }: {
|
|
82
|
+
addProductToCart({ activeCart, quantity, regionId, variantId }: {
|
|
83
83
|
activeCart: Pick<HttpTypes.StoreCart, "id"> | null;
|
|
84
|
+
quantity?: number;
|
|
84
85
|
regionId: string;
|
|
85
86
|
variantId: string;
|
|
86
87
|
}): Promise<Pick<HttpTypes.StoreCart, "id">>;
|
|
@@ -253,6 +254,9 @@ export declare function createStorefrontBrowser(config: StorefrontBrowserConfig)
|
|
|
253
254
|
limit: number;
|
|
254
255
|
offset: number;
|
|
255
256
|
}], HttpTypes.StoreOrderListResponse, Error>;
|
|
257
|
+
orderDetail: (orderId: string) => import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<HttpTypes.StoreOrder | null, Error, HttpTypes.StoreOrder | null, readonly ["medusa", "orders", "detail", string]>, "queryFn"> & {
|
|
258
|
+
queryFn?: import("@tanstack/react-query").QueryFunction<HttpTypes.StoreOrder | null, readonly ["medusa", "orders", "detail", string], never> | undefined;
|
|
259
|
+
} & import("@tanstack/react-query").QueryKeyWithDataTag<readonly ["medusa", "orders", "detail", string], HttpTypes.StoreOrder | null, Error>;
|
|
256
260
|
customerPasswordPolicy: () => import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<{
|
|
257
261
|
min_length: number;
|
|
258
262
|
require_mixed_case: boolean;
|
|
@@ -11,6 +11,7 @@ export declare const medusaQueryKeys: {
|
|
|
11
11
|
limit: number;
|
|
12
12
|
offset: number;
|
|
13
13
|
}];
|
|
14
|
+
orderDetail: (orderId: string) => readonly ["medusa", "orders", "detail", string];
|
|
14
15
|
ordersAll: () => readonly ["medusa", "orders"];
|
|
15
16
|
paymentProviders: (regionId: string) => readonly ["medusa", "payment-providers", string];
|
|
16
17
|
pluginFeatures: () => readonly ["medusa", "plugin-features"];
|
|
@@ -4,6 +4,8 @@ export const medusaQueryKeys = {
|
|
|
4
4
|
customerPasswordPolicy: () => ["medusa", "customer-password-policy"],
|
|
5
5
|
documents: () => ["medusa", "documents"],
|
|
6
6
|
orders: (page) => ["medusa", "orders", page],
|
|
7
|
+
// Under ordersAll, so everything that resets the orders resets a detail too.
|
|
8
|
+
orderDetail: (orderId) => ["medusa", "orders", "detail", orderId],
|
|
7
9
|
ordersAll: () => ["medusa", "orders"],
|
|
8
10
|
paymentProviders: (regionId) => ["medusa", "payment-providers", regionId],
|
|
9
11
|
pluginFeatures: () => ["medusa", "plugin-features"],
|
|
@@ -111,6 +111,10 @@ export declare function createQueryOptions({ actions, affiliateApiPaths, cart, r
|
|
|
111
111
|
limit: number;
|
|
112
112
|
offset: number;
|
|
113
113
|
}], import("@medusajs/types").StoreOrderListResponse, Error>;
|
|
114
|
+
/** One order with its lines and totals; needs the optional `getOrder` action. */
|
|
115
|
+
orderDetail: (orderId: string) => import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<import("@medusajs/types").StoreOrder | null, Error, import("@medusajs/types").StoreOrder | null, readonly ["medusa", "orders", "detail", string]>, "queryFn"> & {
|
|
116
|
+
queryFn?: import("@tanstack/react-query").QueryFunction<import("@medusajs/types").StoreOrder | null, readonly ["medusa", "orders", "detail", string], never> | undefined;
|
|
117
|
+
} & import("@tanstack/react-query").QueryKeyWithDataTag<readonly ["medusa", "orders", "detail", string], import("@medusajs/types").StoreOrder | null, Error>;
|
|
114
118
|
customerPasswordPolicy: () => import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<{
|
|
115
119
|
min_length: number;
|
|
116
120
|
require_mixed_case: boolean;
|
|
@@ -78,6 +78,12 @@ export function createQueryOptions({ actions, affiliateApiPaths, cart, referral,
|
|
|
78
78
|
queryKey: medusaQueryKeys.orders({ limit, offset }),
|
|
79
79
|
queryFn: () => actions.listOrders({ limit, offset }),
|
|
80
80
|
}),
|
|
81
|
+
/** One order with its lines and totals; needs the optional `getOrder` action. */
|
|
82
|
+
orderDetail: (orderId) => queryOptions({
|
|
83
|
+
queryKey: medusaQueryKeys.orderDetail(orderId),
|
|
84
|
+
queryFn: async () => (actions.getOrder ? await actions.getOrder(orderId) : null),
|
|
85
|
+
enabled: Boolean(actions.getOrder) && Boolean(orderId),
|
|
86
|
+
}),
|
|
81
87
|
customerPasswordPolicy: () => queryOptions({
|
|
82
88
|
queryKey: medusaQueryKeys.customerPasswordPolicy(),
|
|
83
89
|
queryFn: () => actions.fetchCustomerPasswordPolicy(),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HttpTypes } from "@medusajs/types";
|
|
2
|
+
/** A category with the descendants Medusa returns for `include_descendants_tree`. */
|
|
3
|
+
export type CategoryTreeNode = Pick<HttpTypes.StoreProductCategory, "id" | "handle" | "name"> & {
|
|
4
|
+
category_children?: CategoryTreeNode[] | null;
|
|
5
|
+
};
|
|
6
|
+
/** The category and all its descendants: choosing a parent filter includes its subcategories. */
|
|
7
|
+
export declare function collectCategoryIds(category: CategoryTreeNode): string[];
|
|
8
|
+
/** The node with this handle anywhere in the trees, or null. */
|
|
9
|
+
export declare function findCategoryByHandle(trees: CategoryTreeNode[], handle: string): CategoryTreeNode | null;
|
|
10
|
+
/**
|
|
11
|
+
* Keeps the products that match every selected filter: a product matches one
|
|
12
|
+
* filter when it belongs to the chosen category or one of its descendants.
|
|
13
|
+
* Medusa's `category_id` is an OR list, so two filters at once (e.g. an
|
|
14
|
+
* application area AND an ingredient group) are intersected here.
|
|
15
|
+
*/
|
|
16
|
+
export declare function filterProductsByCategories<T extends Pick<HttpTypes.StoreProduct, "categories">>(products: T[], selected: CategoryTreeNode[]): T[];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** The category and all its descendants: choosing a parent filter includes its subcategories. */
|
|
2
|
+
export function collectCategoryIds(category) {
|
|
3
|
+
return [category.id, ...(category.category_children ?? []).flatMap(collectCategoryIds)];
|
|
4
|
+
}
|
|
5
|
+
/** The node with this handle anywhere in the trees, or null. */
|
|
6
|
+
export function findCategoryByHandle(trees, handle) {
|
|
7
|
+
for (const node of trees) {
|
|
8
|
+
if (node.handle === handle) {
|
|
9
|
+
return node;
|
|
10
|
+
}
|
|
11
|
+
const match = findCategoryByHandle(node.category_children ?? [], handle);
|
|
12
|
+
if (match) {
|
|
13
|
+
return match;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Keeps the products that match every selected filter: a product matches one
|
|
20
|
+
* filter when it belongs to the chosen category or one of its descendants.
|
|
21
|
+
* Medusa's `category_id` is an OR list, so two filters at once (e.g. an
|
|
22
|
+
* application area AND an ingredient group) are intersected here.
|
|
23
|
+
*/
|
|
24
|
+
export function filterProductsByCategories(products, selected) {
|
|
25
|
+
if (selected.length === 0) {
|
|
26
|
+
return products;
|
|
27
|
+
}
|
|
28
|
+
const filters = selected.map((category) => new Set(collectCategoryIds(category)));
|
|
29
|
+
return products.filter((product) => {
|
|
30
|
+
const productCategoryIds = (product.categories ?? []).map((category) => category.id);
|
|
31
|
+
return filters.every((filter) => productCategoryIds.some((id) => filter.has(id)));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -10,6 +10,47 @@ export declare function createCustomerRegistrationSchema(t: Translate, policy: C
|
|
|
10
10
|
email: z.ZodString;
|
|
11
11
|
password: z.ZodString;
|
|
12
12
|
}, z.core.$strip>;
|
|
13
|
+
/** The address a registration may carry (stored as the customer's default address). */
|
|
14
|
+
export declare const registrationAddressSchema: z.ZodObject<{
|
|
15
|
+
address_1: z.ZodString;
|
|
16
|
+
city: z.ZodString;
|
|
17
|
+
country_code: z.ZodString;
|
|
18
|
+
postal_code: z.ZodString;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
/** What the server takes for a registration with profile (and optional address). */
|
|
21
|
+
export declare const customerRegistrationProfileSchema: z.ZodObject<{
|
|
22
|
+
email: z.ZodString;
|
|
23
|
+
password: z.ZodString;
|
|
24
|
+
address: z.ZodOptional<z.ZodObject<{
|
|
25
|
+
address_1: z.ZodString;
|
|
26
|
+
city: z.ZodString;
|
|
27
|
+
country_code: z.ZodString;
|
|
28
|
+
postal_code: z.ZodString;
|
|
29
|
+
}, z.core.$strip>>;
|
|
30
|
+
first_name: z.ZodString;
|
|
31
|
+
last_name: z.ZodString;
|
|
32
|
+
phone: z.ZodString;
|
|
33
|
+
}, z.core.$strip>;
|
|
34
|
+
/**
|
|
35
|
+
* The registration form with profile, address, a repeated password and the
|
|
36
|
+
* accepted terms. Address fields are all-or-nothing: none or a complete
|
|
37
|
+
* address.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createCustomerRegistrationFormSchema(t: Translate, policy: CustomerPasswordPolicy | null): z.ZodObject<{
|
|
40
|
+
accept_terms: z.ZodBoolean;
|
|
41
|
+
address_1: z.ZodString;
|
|
42
|
+
city: z.ZodString;
|
|
43
|
+
confirmPassword: z.ZodString;
|
|
44
|
+
country_code: z.ZodString;
|
|
45
|
+
email: z.ZodString;
|
|
46
|
+
first_name: z.ZodString;
|
|
47
|
+
last_name: z.ZodString;
|
|
48
|
+
password: z.ZodString;
|
|
49
|
+
phone: z.ZodString;
|
|
50
|
+
postal_code: z.ZodString;
|
|
51
|
+
}, z.core.$strip>;
|
|
52
|
+
/** The registration form's values as the server takes them. */
|
|
53
|
+
export declare function toRegistrationProfile(value: CustomerRegistrationFormValues): CustomerRegistrationProfileValues;
|
|
13
54
|
export declare function createPasswordResetSchema(t: Translate, policy: CustomerPasswordPolicy | null): z.ZodObject<{
|
|
14
55
|
confirmPassword: z.ZodString;
|
|
15
56
|
password: z.ZodString;
|
|
@@ -85,31 +126,33 @@ export declare const createCheckoutAddressSchema: (t: Translate, merchantCountry
|
|
|
85
126
|
has_different_billing_address: z.ZodBoolean;
|
|
86
127
|
shipping_address: z.ZodObject<{
|
|
87
128
|
address_1: z.ZodString;
|
|
88
|
-
address_2: z.ZodString;
|
|
89
129
|
city: z.ZodString;
|
|
90
|
-
company: z.ZodString;
|
|
91
130
|
country_code: z.ZodString;
|
|
131
|
+
postal_code: z.ZodString;
|
|
92
132
|
first_name: z.ZodString;
|
|
93
133
|
last_name: z.ZodString;
|
|
94
134
|
phone: z.ZodString;
|
|
95
|
-
|
|
135
|
+
address_2: z.ZodString;
|
|
136
|
+
company: z.ZodString;
|
|
96
137
|
province: z.ZodString;
|
|
97
138
|
}, z.core.$strip>;
|
|
98
139
|
billing_address: z.ZodObject<{
|
|
99
140
|
address_1: z.ZodString;
|
|
100
|
-
address_2: z.ZodString;
|
|
101
141
|
city: z.ZodString;
|
|
102
|
-
company: z.ZodString;
|
|
103
142
|
country_code: z.ZodString;
|
|
143
|
+
postal_code: z.ZodString;
|
|
104
144
|
first_name: z.ZodString;
|
|
105
145
|
last_name: z.ZodString;
|
|
106
146
|
phone: z.ZodString;
|
|
107
|
-
|
|
147
|
+
address_2: z.ZodString;
|
|
148
|
+
company: z.ZodString;
|
|
108
149
|
province: z.ZodString;
|
|
109
150
|
}, z.core.$strip>;
|
|
110
151
|
vat_number: z.ZodString;
|
|
111
152
|
}, z.core.$strip>;
|
|
112
153
|
export type CustomerAuthValues = z.infer<typeof customerAuthSchema>;
|
|
154
|
+
export type CustomerRegistrationProfileValues = z.infer<typeof customerRegistrationProfileSchema>;
|
|
155
|
+
export type CustomerRegistrationFormValues = z.infer<ReturnType<typeof createCustomerRegistrationFormSchema>>;
|
|
113
156
|
export type LoginCodeRequestValues = z.infer<ReturnType<typeof createLoginCodeRequestSchema>>;
|
|
114
157
|
export type PasswordResetValues = z.infer<ReturnType<typeof createPasswordResetSchema>>;
|
|
115
158
|
export type VerificationResendValues = z.infer<ReturnType<typeof createVerificationResendSchema>>;
|
package/dist/core/form-values.js
CHANGED
|
@@ -27,6 +27,71 @@ export function createCustomerRegistrationSchema(t, policy) {
|
|
|
27
27
|
password: createNewPasswordSchema(t, policy),
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
+
/** The address a registration may carry (stored as the customer's default address). */
|
|
31
|
+
export const registrationAddressSchema = z.object({
|
|
32
|
+
address_1: z.string().trim().min(1),
|
|
33
|
+
city: z.string().trim().min(1),
|
|
34
|
+
country_code: z.string().trim().min(1),
|
|
35
|
+
postal_code: z.string().trim().min(1),
|
|
36
|
+
});
|
|
37
|
+
/** What the server takes for a registration with profile (and optional address). */
|
|
38
|
+
export const customerRegistrationProfileSchema = customerAuthSchema.extend({
|
|
39
|
+
address: registrationAddressSchema.optional(),
|
|
40
|
+
first_name: z.string().trim().min(1),
|
|
41
|
+
last_name: z.string().trim().min(1),
|
|
42
|
+
phone: z.string().trim(),
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* The registration form with profile, address, a repeated password and the
|
|
46
|
+
* accepted terms. Address fields are all-or-nothing: none or a complete
|
|
47
|
+
* address.
|
|
48
|
+
*/
|
|
49
|
+
export function createCustomerRegistrationFormSchema(t, policy) {
|
|
50
|
+
return z
|
|
51
|
+
.object({
|
|
52
|
+
accept_terms: z.boolean().refine((accepted) => accepted, t("registration.terms_required")),
|
|
53
|
+
address_1: z.string().trim(),
|
|
54
|
+
city: z.string().trim(),
|
|
55
|
+
confirmPassword: z.string(),
|
|
56
|
+
country_code: z.string().trim(),
|
|
57
|
+
email: z.string().trim().min(1, t("form.email_required")).email(t("form.email_invalid")),
|
|
58
|
+
first_name: z.string().trim().min(1, t("address.first_name_required")),
|
|
59
|
+
last_name: z.string().trim().min(1, t("address.last_name_required")),
|
|
60
|
+
password: createNewPasswordSchema(t, policy),
|
|
61
|
+
phone: z.string().trim(),
|
|
62
|
+
postal_code: z.string().trim(),
|
|
63
|
+
})
|
|
64
|
+
.superRefine((value, context) => {
|
|
65
|
+
if (value.password !== value.confirmPassword) {
|
|
66
|
+
context.addIssue({ code: "custom", message: t("form.passwords_mismatch"), path: ["confirmPassword"] });
|
|
67
|
+
}
|
|
68
|
+
const addressFields = [
|
|
69
|
+
["address_1", "address.address_1_required"],
|
|
70
|
+
["postal_code", "address.postal_code_required"],
|
|
71
|
+
["city", "address.city_required"],
|
|
72
|
+
["country_code", "address.country_required"],
|
|
73
|
+
];
|
|
74
|
+
if (addressFields.some(([field]) => value[field])) {
|
|
75
|
+
for (const [field, code] of addressFields) {
|
|
76
|
+
if (!value[field]) {
|
|
77
|
+
context.addIssue({ code: "custom", message: t(code), path: [field] });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** The registration form's values as the server takes them. */
|
|
84
|
+
export function toRegistrationProfile(value) {
|
|
85
|
+
const hasAddress = Boolean(value.address_1 && value.postal_code && value.city && value.country_code);
|
|
86
|
+
return {
|
|
87
|
+
email: value.email.trim(),
|
|
88
|
+
first_name: value.first_name.trim(),
|
|
89
|
+
last_name: value.last_name.trim(),
|
|
90
|
+
password: value.password,
|
|
91
|
+
phone: value.phone.trim(),
|
|
92
|
+
...(hasAddress ? { address: { address_1: value.address_1.trim(), city: value.city.trim(), country_code: value.country_code.trim().toLowerCase(), postal_code: value.postal_code.trim() } } : {}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
30
95
|
export function createPasswordResetSchema(t, policy) {
|
|
31
96
|
return z
|
|
32
97
|
.object({
|
package/dist/core/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from "./cart/cart-tax-breakdown.js";
|
|
|
17
17
|
export * from "./cart/coupon-promotions.js";
|
|
18
18
|
export * from "./cart/payment-session.js";
|
|
19
19
|
export * from "./cart/refunded-cart.js";
|
|
20
|
+
export * from "./catalog/category-filter.js";
|
|
20
21
|
export * from "./catalog/inventory.js";
|
|
21
22
|
export * from "./catalog/medusa-product-image.js";
|
|
22
23
|
export * from "./catalog/product-price.js";
|
package/dist/core/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./cart/cart-tax-breakdown.js";
|
|
|
18
18
|
export * from "./cart/coupon-promotions.js";
|
|
19
19
|
export * from "./cart/payment-session.js";
|
|
20
20
|
export * from "./cart/refunded-cart.js";
|
|
21
|
+
export * from "./catalog/category-filter.js";
|
|
21
22
|
export * from "./catalog/inventory.js";
|
|
22
23
|
export * from "./catalog/medusa-product-image.js";
|
|
23
24
|
export * from "./catalog/product-price.js";
|
package/dist/core/texts-de.d.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
export declare const textsDe: {
|
|
7
7
|
readonly "form.password_required": "Bitte gib ein Passwort ein.";
|
|
8
8
|
readonly "form.passwords_mismatch": "Die Passwörter stimmen nicht überein.";
|
|
9
|
+
readonly "registration.terms_required": "Bitte akzeptiere die Nutzungsbedingungen und Datenschutzrichtlinien.";
|
|
10
|
+
readonly "registration.address_not_saved": "Dein Konto wurde angelegt, die Adresse konnte aber nicht gespeichert werden. Du kannst sie in deinem Konto nachtragen.";
|
|
9
11
|
readonly "form.email_required": "Bitte gib deine E-Mail-Adresse ein.";
|
|
10
12
|
readonly "form.email_invalid": "Bitte gib eine gültige E-Mail-Adresse ein.";
|
|
11
13
|
readonly "form.login_code_invalid": "Bitte gib den {length}-stelligen Code ein.";
|
package/dist/core/texts-de.js
CHANGED
|
@@ -8,6 +8,8 @@ export const textsDe = {
|
|
|
8
8
|
// Forms
|
|
9
9
|
"form.password_required": "Bitte gib ein Passwort ein.",
|
|
10
10
|
"form.passwords_mismatch": "Die Passwörter stimmen nicht überein.",
|
|
11
|
+
"registration.terms_required": "Bitte akzeptiere die Nutzungsbedingungen und Datenschutzrichtlinien.",
|
|
12
|
+
"registration.address_not_saved": "Dein Konto wurde angelegt, die Adresse konnte aber nicht gespeichert werden. Du kannst sie in deinem Konto nachtragen.",
|
|
11
13
|
"form.email_required": "Bitte gib deine E-Mail-Adresse ein.",
|
|
12
14
|
"form.email_invalid": "Bitte gib eine gültige E-Mail-Adresse ein.",
|
|
13
15
|
"form.login_code_invalid": "Bitte gib den {length}-stelligen Code ein.",
|
|
@@ -76,6 +76,16 @@ export declare function useAccountOrders(): {
|
|
|
76
76
|
isPending: boolean;
|
|
77
77
|
orders: HttpTypes.StoreOrder[];
|
|
78
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* One order of the account with its lines and totals, loaded when it is
|
|
81
|
+
* opened (the list carries only the total). Needs the shop's optional
|
|
82
|
+
* `getOrder` action; `null` loads nothing.
|
|
83
|
+
*/
|
|
84
|
+
export declare function useAccountOrderDetail(orderId: string | null): {
|
|
85
|
+
isError: boolean;
|
|
86
|
+
isPending: boolean;
|
|
87
|
+
order: HttpTypes.StoreOrder | null;
|
|
88
|
+
};
|
|
79
89
|
/** The account's address book page: add, edit (with the UID check), delete and default addresses. */
|
|
80
90
|
export declare function useAccountAddresses(): {
|
|
81
91
|
addressBook: {
|
|
@@ -145,6 +145,16 @@ export function useAccountOrders() {
|
|
|
145
145
|
orders: ordersResponse?.orders ?? [],
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* One order of the account with its lines and totals, loaded when it is
|
|
150
|
+
* opened (the list carries only the total). Needs the shop's optional
|
|
151
|
+
* `getOrder` action; `null` loads nothing.
|
|
152
|
+
*/
|
|
153
|
+
export function useAccountOrderDetail(orderId) {
|
|
154
|
+
const { queries } = useStorefront();
|
|
155
|
+
const { data: order, isError, isFetching } = useQuery({ ...queries.orderDetail(orderId ?? ""), enabled: Boolean(orderId) && queries.orderDetail(orderId ?? "").enabled !== false });
|
|
156
|
+
return { isError, isPending: Boolean(orderId) && isFetching && !order, order: order ?? null };
|
|
157
|
+
}
|
|
148
158
|
/** The account's address book page: add, edit (with the UID check), delete and default addresses. */
|
|
149
159
|
export function useAccountAddresses() {
|
|
150
160
|
const { actions, t, unwrapMedusaAction } = useStorefront();
|
package/dist/react/use-auth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StorefrontBrowser } from "../browser/index.js";
|
|
2
|
-
import type { CustomerAuthValues, PasswordResetValues, VerificationResendValues } from "../core/form-values.js";
|
|
2
|
+
import type { CustomerAuthValues, CustomerRegistrationProfileValues, PasswordResetValues, VerificationResendValues } from "../core/form-values.js";
|
|
3
3
|
/**
|
|
4
4
|
* Start of the backend's 400 for a registration with an address that already
|
|
5
5
|
* has an account; the form then links to the password reset.
|
|
@@ -48,6 +48,13 @@ export declare function usePasswordAuth({ getValues, onSuccess }: PasswordAuthOp
|
|
|
48
48
|
cancelVerification: () => void;
|
|
49
49
|
login: (value: CustomerAuthValues) => Promise<boolean>;
|
|
50
50
|
register: (value: CustomerAuthValues) => Promise<boolean>;
|
|
51
|
+
/** The registration did not store its address; the account exists (add it in the account). */
|
|
52
|
+
isAddressNotSaved: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Registration with name, phone and address; needs the shop's optional
|
|
55
|
+
* `registerCustomerWithProfile` action. True once signed in.
|
|
56
|
+
*/
|
|
57
|
+
registerWithProfile(value: CustomerRegistrationProfileValues): Promise<boolean>;
|
|
51
58
|
/** "I confirmed it": signs in again with the kept form values, also after a registration (the account exists by now). */
|
|
52
59
|
continueAfterVerification(): Promise<void>;
|
|
53
60
|
};
|
package/dist/react/use-auth.js
CHANGED
|
@@ -73,6 +73,8 @@ export function usePasswordAuth({ getValues, onSuccess }) {
|
|
|
73
73
|
// Set while the backend waits for the address to be confirmed; replaces the form with the next steps.
|
|
74
74
|
const [verification, setVerification] = useState(null);
|
|
75
75
|
const [isContinuing, setIsContinuing] = useState(false);
|
|
76
|
+
// A registration with profile succeeded but its address could not be stored.
|
|
77
|
+
const [isAddressNotSaved, setIsAddressNotSaved] = useState(false);
|
|
76
78
|
// The background check and the "continue" button must not sign in twice.
|
|
77
79
|
const confirmationSignIn = useRef("idle");
|
|
78
80
|
async function completeSignIn(sessionToken) {
|
|
@@ -130,6 +132,36 @@ export function usePasswordAuth({ getValues, onSuccess }) {
|
|
|
130
132
|
cancelVerification: () => setVerification(null),
|
|
131
133
|
login: (value) => authenticate(actions.loginCustomer, value),
|
|
132
134
|
register: (value) => authenticate(actions.registerCustomer, value),
|
|
135
|
+
/** The registration did not store its address; the account exists (add it in the account). */
|
|
136
|
+
isAddressNotSaved,
|
|
137
|
+
/**
|
|
138
|
+
* Registration with name, phone and address; needs the shop's optional
|
|
139
|
+
* `registerCustomerWithProfile` action. True once signed in.
|
|
140
|
+
*/
|
|
141
|
+
async registerWithProfile(value) {
|
|
142
|
+
const action = actions.registerCustomerWithProfile;
|
|
143
|
+
setErrorMessage(null);
|
|
144
|
+
setIsAddressNotSaved(false);
|
|
145
|
+
if (!action) {
|
|
146
|
+
setErrorMessage(t("auth.registration_unsupported_step"));
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const result = unwrapMedusaAction(await action(value));
|
|
151
|
+
if (result.state === "verification_required") {
|
|
152
|
+
setVerification((current) => ({ email: value.email, stillUnverified: current !== null }));
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
setIsAddressNotSaved(!result.addressSaved);
|
|
156
|
+
await completeSignIn(result.sessionToken);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
setVerification(null);
|
|
161
|
+
setErrorMessage(t.message(getErrorMessage(error, t("auth.sign_in_failed"))));
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
133
165
|
/** "I confirmed it": signs in again with the kept form values, also after a registration (the account exists by now). */
|
|
134
166
|
async continueAfterVerification() {
|
|
135
167
|
setIsContinuing(true);
|
package/dist/react/use-cart.d.ts
CHANGED
|
@@ -7,11 +7,36 @@ export declare function useActiveCart(): import("@tanstack/react-query").UseQuer
|
|
|
7
7
|
* opening its cart popover).
|
|
8
8
|
*/
|
|
9
9
|
export declare function useAddToCart(countryCode: string): {
|
|
10
|
-
addToCart: (variant: HttpTypes.StoreProductVariant) => Promise<void>;
|
|
10
|
+
addToCart: (variant: HttpTypes.StoreProductVariant, quantity?: number) => Promise<void>;
|
|
11
11
|
errorMessage: string | null;
|
|
12
12
|
isReady: boolean;
|
|
13
13
|
isSubmitting: boolean;
|
|
14
14
|
};
|
|
15
|
+
/** A line of an earlier order to put into the cart again. */
|
|
16
|
+
export interface ReorderLine {
|
|
17
|
+
/** Order line id; reported back for the lines that could not be added. */
|
|
18
|
+
id: string;
|
|
19
|
+
quantity: number;
|
|
20
|
+
title: string;
|
|
21
|
+
/** Null once the variant was deleted. */
|
|
22
|
+
variant_id: string | null | undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Puts lines of an earlier order into the active cart again ("Erneut
|
|
26
|
+
* bestellen", or one line). Medusa has no reorder endpoint, so the lines are
|
|
27
|
+
* added one after another into the same cart (the first add may create it);
|
|
28
|
+
* a deleted, unavailable or refused line is reported in `failedLines` while
|
|
29
|
+
* the others are still added.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useReorder(countryCode: string): {
|
|
32
|
+
failedLines: ReorderLine[];
|
|
33
|
+
isReady: boolean;
|
|
34
|
+
isSubmitting: boolean;
|
|
35
|
+
reorder: (lines: ReorderLine[]) => Promise<{
|
|
36
|
+
added: number;
|
|
37
|
+
failed: ReorderLine[];
|
|
38
|
+
} | undefined>;
|
|
39
|
+
};
|
|
15
40
|
/**
|
|
16
41
|
* Runs one line-item change at a time (quantity, removal) through the "cart
|
|
17
42
|
* price changed" boundary; `pendingItemId` marks the line that is busy.
|
package/dist/react/use-cart.js
CHANGED
|
@@ -23,16 +23,17 @@ export function useAddToCart(countryCode) {
|
|
|
23
23
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
24
24
|
const { data: activeCart, isPending: isCartPending } = useQuery(queries.activeCart());
|
|
25
25
|
const { data: region } = useQuery(queries.region(countryCode));
|
|
26
|
-
|
|
26
|
+
/** Adds `quantity` (a whole number of at least 1, default 1) of the variant. */
|
|
27
|
+
const addToCart = async (variant, quantity = 1) => {
|
|
27
28
|
// activeCart is undefined while the query is still pending - that must
|
|
28
29
|
// never be mistaken for "no cart yet" (which would create a second cart).
|
|
29
|
-
if (!region?.id || activeCart === undefined) {
|
|
30
|
+
if (!region?.id || activeCart === undefined || !Number.isInteger(quantity) || quantity < 1) {
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
setErrorMessage(null);
|
|
33
34
|
setIsSubmitting(true);
|
|
34
35
|
try {
|
|
35
|
-
await storefront.cart.addProductToCart({ activeCart, regionId: region.id, variantId: variant.id });
|
|
36
|
+
await storefront.cart.addProductToCart({ activeCart, quantity, regionId: region.id, variantId: variant.id });
|
|
36
37
|
await storefront.refreshCartAfterMutation(queryClient);
|
|
37
38
|
callbacks.onAddedToCart?.(variant);
|
|
38
39
|
}
|
|
@@ -45,6 +46,52 @@ export function useAddToCart(countryCode) {
|
|
|
45
46
|
};
|
|
46
47
|
return { addToCart, errorMessage, isReady: Boolean(region?.id) && !isCartPending, isSubmitting };
|
|
47
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Puts lines of an earlier order into the active cart again ("Erneut
|
|
51
|
+
* bestellen", or one line). Medusa has no reorder endpoint, so the lines are
|
|
52
|
+
* added one after another into the same cart (the first add may create it);
|
|
53
|
+
* a deleted, unavailable or refused line is reported in `failedLines` while
|
|
54
|
+
* the others are still added.
|
|
55
|
+
*/
|
|
56
|
+
export function useReorder(countryCode) {
|
|
57
|
+
const storefront = useStorefront();
|
|
58
|
+
const { queries } = storefront;
|
|
59
|
+
const queryClient = useQueryClient();
|
|
60
|
+
const [failedLines, setFailedLines] = useState([]);
|
|
61
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
62
|
+
const { data: activeCart, isPending: isCartPending } = useQuery(queries.activeCart());
|
|
63
|
+
const { data: region } = useQuery(queries.region(countryCode));
|
|
64
|
+
const reorder = async (lines) => {
|
|
65
|
+
if (!region?.id || activeCart === undefined) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setFailedLines([]);
|
|
69
|
+
setIsSubmitting(true);
|
|
70
|
+
const failed = [];
|
|
71
|
+
let cart = activeCart;
|
|
72
|
+
try {
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
if (!line.variant_id || !Number.isInteger(line.quantity) || line.quantity < 1) {
|
|
75
|
+
failed.push(line);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
cart = await storefront.cart.addProductToCart({ activeCart: cart, quantity: line.quantity, regionId: region.id, variantId: line.variant_id });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
failed.push(line);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
await storefront.refreshCartAfterMutation(queryClient);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
setFailedLines(failed);
|
|
89
|
+
setIsSubmitting(false);
|
|
90
|
+
}
|
|
91
|
+
return { added: lines.length - failed.length, failed };
|
|
92
|
+
};
|
|
93
|
+
return { failedLines, isReady: Boolean(region?.id) && !isCartPending, isSubmitting, reorder };
|
|
94
|
+
}
|
|
48
95
|
/**
|
|
49
96
|
* Runs one line-item change at a time (quantity, removal) through the "cart
|
|
50
97
|
* price changed" boundary; `pendingItemId` marks the line that is busy.
|
|
@@ -34,26 +34,26 @@ export declare function useCheckoutAddressForm({ countryCode, countryOptions, in
|
|
|
34
34
|
has_different_billing_address: boolean;
|
|
35
35
|
shipping_address: {
|
|
36
36
|
address_1: string;
|
|
37
|
-
address_2: string;
|
|
38
37
|
city: string;
|
|
39
|
-
company: string;
|
|
40
38
|
country_code: string;
|
|
39
|
+
postal_code: string;
|
|
41
40
|
first_name: string;
|
|
42
41
|
last_name: string;
|
|
43
42
|
phone: string;
|
|
44
|
-
|
|
43
|
+
address_2: string;
|
|
44
|
+
company: string;
|
|
45
45
|
province: string;
|
|
46
46
|
};
|
|
47
47
|
billing_address: {
|
|
48
48
|
address_1: string;
|
|
49
|
-
address_2: string;
|
|
50
49
|
city: string;
|
|
51
|
-
company: string;
|
|
52
50
|
country_code: string;
|
|
51
|
+
postal_code: string;
|
|
53
52
|
first_name: string;
|
|
54
53
|
last_name: string;
|
|
55
54
|
phone: string;
|
|
56
|
-
|
|
55
|
+
address_2: string;
|
|
56
|
+
company: string;
|
|
57
57
|
province: string;
|
|
58
58
|
};
|
|
59
59
|
vat_number: string;
|
|
@@ -68,26 +68,26 @@ export declare function useCheckoutAddressForm({ countryCode, countryOptions, in
|
|
|
68
68
|
has_different_billing_address: boolean;
|
|
69
69
|
shipping_address: {
|
|
70
70
|
address_1: string;
|
|
71
|
-
address_2: string;
|
|
72
71
|
city: string;
|
|
73
|
-
company: string;
|
|
74
72
|
country_code: string;
|
|
73
|
+
postal_code: string;
|
|
75
74
|
first_name: string;
|
|
76
75
|
last_name: string;
|
|
77
76
|
phone: string;
|
|
78
|
-
|
|
77
|
+
address_2: string;
|
|
78
|
+
company: string;
|
|
79
79
|
province: string;
|
|
80
80
|
};
|
|
81
81
|
billing_address: {
|
|
82
82
|
address_1: string;
|
|
83
|
-
address_2: string;
|
|
84
83
|
city: string;
|
|
85
|
-
company: string;
|
|
86
84
|
country_code: string;
|
|
85
|
+
postal_code: string;
|
|
87
86
|
first_name: string;
|
|
88
87
|
last_name: string;
|
|
89
88
|
phone: string;
|
|
90
|
-
|
|
89
|
+
address_2: string;
|
|
90
|
+
company: string;
|
|
91
91
|
province: string;
|
|
92
92
|
};
|
|
93
93
|
vat_number: string;
|
|
@@ -104,26 +104,26 @@ export declare function useCheckoutAddressForm({ countryCode, countryOptions, in
|
|
|
104
104
|
has_different_billing_address: import("zod").ZodBoolean;
|
|
105
105
|
shipping_address: import("zod").ZodObject<{
|
|
106
106
|
address_1: import("zod").ZodString;
|
|
107
|
-
address_2: import("zod").ZodString;
|
|
108
107
|
city: import("zod").ZodString;
|
|
109
|
-
company: import("zod").ZodString;
|
|
110
108
|
country_code: import("zod").ZodString;
|
|
109
|
+
postal_code: import("zod").ZodString;
|
|
111
110
|
first_name: import("zod").ZodString;
|
|
112
111
|
last_name: import("zod").ZodString;
|
|
113
112
|
phone: import("zod").ZodString;
|
|
114
|
-
|
|
113
|
+
address_2: import("zod").ZodString;
|
|
114
|
+
company: import("zod").ZodString;
|
|
115
115
|
province: import("zod").ZodString;
|
|
116
116
|
}, import("zod/v4/core").$strip>;
|
|
117
117
|
billing_address: import("zod").ZodObject<{
|
|
118
118
|
address_1: import("zod").ZodString;
|
|
119
|
-
address_2: import("zod").ZodString;
|
|
120
119
|
city: import("zod").ZodString;
|
|
121
|
-
company: import("zod").ZodString;
|
|
122
120
|
country_code: import("zod").ZodString;
|
|
121
|
+
postal_code: import("zod").ZodString;
|
|
123
122
|
first_name: import("zod").ZodString;
|
|
124
123
|
last_name: import("zod").ZodString;
|
|
125
124
|
phone: import("zod").ZodString;
|
|
126
|
-
|
|
125
|
+
address_2: import("zod").ZodString;
|
|
126
|
+
company: import("zod").ZodString;
|
|
127
127
|
province: import("zod").ZodString;
|
|
128
128
|
}, import("zod/v4/core").$strip>;
|
|
129
129
|
vat_number: import("zod").ZodString;
|
|
@@ -28,26 +28,26 @@ export declare function useCheckout(countryCode: string): {
|
|
|
28
28
|
has_different_billing_address: boolean;
|
|
29
29
|
shipping_address: {
|
|
30
30
|
address_1: string;
|
|
31
|
-
address_2: string;
|
|
32
31
|
city: string;
|
|
33
|
-
company: string;
|
|
34
32
|
country_code: string;
|
|
33
|
+
postal_code: string;
|
|
35
34
|
first_name: string;
|
|
36
35
|
last_name: string;
|
|
37
36
|
phone: string;
|
|
38
|
-
|
|
37
|
+
address_2: string;
|
|
38
|
+
company: string;
|
|
39
39
|
province: string;
|
|
40
40
|
};
|
|
41
41
|
billing_address: {
|
|
42
42
|
address_1: string;
|
|
43
|
-
address_2: string;
|
|
44
43
|
city: string;
|
|
45
|
-
company: string;
|
|
46
44
|
country_code: string;
|
|
45
|
+
postal_code: string;
|
|
47
46
|
first_name: string;
|
|
48
47
|
last_name: string;
|
|
49
48
|
phone: string;
|
|
50
|
-
|
|
49
|
+
address_2: string;
|
|
50
|
+
company: string;
|
|
51
51
|
province: string;
|
|
52
52
|
};
|
|
53
53
|
vat_number: string;
|
|
@@ -59,26 +59,26 @@ export declare function useCheckout(countryCode: string): {
|
|
|
59
59
|
has_different_billing_address: boolean;
|
|
60
60
|
shipping_address: {
|
|
61
61
|
address_1: string;
|
|
62
|
-
address_2: string;
|
|
63
62
|
city: string;
|
|
64
|
-
company: string;
|
|
65
63
|
country_code: string;
|
|
64
|
+
postal_code: string;
|
|
66
65
|
first_name: string;
|
|
67
66
|
last_name: string;
|
|
68
67
|
phone: string;
|
|
69
|
-
|
|
68
|
+
address_2: string;
|
|
69
|
+
company: string;
|
|
70
70
|
province: string;
|
|
71
71
|
};
|
|
72
72
|
billing_address: {
|
|
73
73
|
address_1: string;
|
|
74
|
-
address_2: string;
|
|
75
74
|
city: string;
|
|
76
|
-
company: string;
|
|
77
75
|
country_code: string;
|
|
76
|
+
postal_code: string;
|
|
78
77
|
first_name: string;
|
|
79
78
|
last_name: string;
|
|
80
79
|
phone: string;
|
|
81
|
-
|
|
80
|
+
address_2: string;
|
|
81
|
+
company: string;
|
|
82
82
|
province: string;
|
|
83
83
|
};
|
|
84
84
|
vat_number: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "Customer account",
|
|
5
5
|
"description": "Sign-in with password or e-mail code, password reset, account verification, address book with UID check, profile, orders, the account workspace layout and the document download route.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hartl-services/medusa-storefront@^0.
|
|
7
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
8
8
|
"@hartl-services/medusa-contracts@^1.1.0",
|
|
9
9
|
"@medusajs/types@^2.19.0",
|
|
10
10
|
"lucide-react"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "Partner portal",
|
|
5
5
|
"description": "Partner portal: gated registration with UID check, links, coupon codes, commissions with date filter, consignment reports, and its Route Handlers.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hartl-services/medusa-storefront@^0.
|
|
7
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
8
8
|
"@hartl-services/medusa-contracts@^1.1.0",
|
|
9
9
|
"@medusajs/types@^2.19.0",
|
|
10
10
|
"lucide-react",
|
package/dist/registry/cart.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "Cart",
|
|
5
5
|
"description": "Cart widget with popover, line items, totals, cart page, add-to-cart button, variant picker and product price.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hartl-services/medusa-storefront@^0.
|
|
7
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
8
8
|
"@medusajs/types@^2.19.0",
|
|
9
9
|
"lucide-react"
|
|
10
10
|
],
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "Checkout",
|
|
5
5
|
"description": "Checkout page (address, login prompt, shipping and Stripe payment, coupon, UID status) and the order confirmation page.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hartl-services/medusa-storefront@^0.
|
|
7
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
8
8
|
"@medusajs/types@^2.19.0",
|
|
9
9
|
"@stripe/react-stripe-js@^5.6.1",
|
|
10
10
|
"@stripe/stripe-js@^8.11.0",
|
package/dist/registry/form.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "Storefront form kit",
|
|
5
5
|
"description": "The shop's TanStack form hook (useAppForm) with unstyled field components; the package's hooks hand their formOptions to it.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hartl-services/medusa-storefront@^0.
|
|
7
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
8
8
|
"@tanstack/react-form@^1.33.5",
|
|
9
9
|
"input-otp",
|
|
10
10
|
"lucide-react"
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"title": "Storefront setup",
|
|
8
8
|
"description": "Wires the storefront package into a Next shop: server and browser instances, the Server Action wrappers, texts, error reporting and the provider root for the [countryCode] layout.",
|
|
9
9
|
"dependencies": [
|
|
10
|
-
"@hartl-services/medusa-storefront@^0.
|
|
10
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
11
11
|
"@hartl-services/medusa-contracts@^1.1.0",
|
|
12
12
|
"@medusajs/framework@^2.19.0",
|
|
13
13
|
"@medusajs/js-sdk@^2.19.0",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"title": "Storefront form kit",
|
|
74
74
|
"description": "The shop's TanStack form hook (useAppForm) with unstyled field components; the package's hooks hand their formOptions to it.",
|
|
75
75
|
"dependencies": [
|
|
76
|
-
"@hartl-services/medusa-storefront@^0.
|
|
76
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
77
77
|
"@tanstack/react-form@^1.33.5",
|
|
78
78
|
"input-otp",
|
|
79
79
|
"lucide-react"
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
"title": "Cart",
|
|
198
198
|
"description": "Cart widget with popover, line items, totals, cart page, add-to-cart button, variant picker and product price.",
|
|
199
199
|
"dependencies": [
|
|
200
|
-
"@hartl-services/medusa-storefront@^0.
|
|
200
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
201
201
|
"@medusajs/types@^2.19.0",
|
|
202
202
|
"lucide-react"
|
|
203
203
|
],
|
|
@@ -306,7 +306,7 @@
|
|
|
306
306
|
"title": "Customer account",
|
|
307
307
|
"description": "Sign-in with password or e-mail code, password reset, account verification, address book with UID check, profile, orders, the account workspace layout and the document download route.",
|
|
308
308
|
"dependencies": [
|
|
309
|
-
"@hartl-services/medusa-storefront@^0.
|
|
309
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
310
310
|
"@hartl-services/medusa-contracts@^1.1.0",
|
|
311
311
|
"@medusajs/types@^2.19.0",
|
|
312
312
|
"lucide-react"
|
|
@@ -516,7 +516,7 @@
|
|
|
516
516
|
"title": "Checkout",
|
|
517
517
|
"description": "Checkout page (address, login prompt, shipping and Stripe payment, coupon, UID status) and the order confirmation page.",
|
|
518
518
|
"dependencies": [
|
|
519
|
-
"@hartl-services/medusa-storefront@^0.
|
|
519
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
520
520
|
"@medusajs/types@^2.19.0",
|
|
521
521
|
"@stripe/react-stripe-js@^5.6.1",
|
|
522
522
|
"@stripe/stripe-js@^8.11.0",
|
|
@@ -661,7 +661,7 @@
|
|
|
661
661
|
"title": "Partner portal",
|
|
662
662
|
"description": "Partner portal: gated registration with UID check, links, coupon codes, commissions with date filter, consignment reports, and its Route Handlers.",
|
|
663
663
|
"dependencies": [
|
|
664
|
-
"@hartl-services/medusa-storefront@^0.
|
|
664
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
665
665
|
"@hartl-services/medusa-contracts@^1.1.0",
|
|
666
666
|
"@medusajs/types@^2.19.0",
|
|
667
667
|
"lucide-react",
|
package/dist/registry/setup.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "Storefront setup",
|
|
5
5
|
"description": "Wires the storefront package into a Next shop: server and browser instances, the Server Action wrappers, texts, error reporting and the provider root for the [countryCode] layout.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hartl-services/medusa-storefront@^0.
|
|
7
|
+
"@hartl-services/medusa-storefront@^0.2.0",
|
|
8
8
|
"@hartl-services/medusa-contracts@^1.1.0",
|
|
9
9
|
"@medusajs/framework@^2.19.0",
|
|
10
10
|
"@medusajs/js-sdk@^2.19.0",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"path": "registry/items/setup/lib/storefront/actions.ts",
|
|
35
35
|
"target": "@lib/storefront/actions.ts",
|
|
36
36
|
"type": "registry:lib",
|
|
37
|
-
"content": "\"use server\"\n\n// One Server Action per storefront server function: Next only accepts \"use server\"\n// functions defined in the app. The browser side gets them through lib/storefront/client.ts.\n\nimport { storefront } from \"@/lib/storefront/server\"\n\nexport async function getActiveCart(...args: Parameters<typeof storefront.cart.getActiveCart>) {\n return await storefront.cart.getActiveCart(...args)\n}\n\nexport async function applyCustomerDefaultsToActiveCart(...args: Parameters<typeof storefront.cart.applyCustomerDefaultsToActiveCart>) {\n return await storefront.cart.applyCustomerDefaultsToActiveCart(...args)\n}\n\nexport async function syncCartId(...args: Parameters<typeof storefront.cart.syncCartId>) {\n return await storefront.cart.syncCartId(...args)\n}\n\nexport async function clearCartId(...args: Parameters<typeof storefront.cart.clearCartId>) {\n return await storefront.cart.clearCartId(...args)\n}\n\nexport async function discardActiveCart(...args: Parameters<typeof storefront.cart.discardActiveCart>) {\n return await storefront.cart.discardActiveCart(...args)\n}\n\nexport async function retrieveCustomer(...args: Parameters<typeof storefront.customer.retrieveCustomer>) {\n return await storefront.customer.retrieveCustomer(...args)\n}\n\nexport async function loginCustomer(...args: Parameters<typeof storefront.customer.loginCustomer>) {\n return await storefront.customer.loginCustomer(...args)\n}\n\nexport async function checkCustomerVerification(...args: Parameters<typeof storefront.customer.checkCustomerVerification>) {\n return await storefront.customer.checkCustomerVerification(...args)\n}\n\nexport async function registerCustomer(...args: Parameters<typeof storefront.customer.registerCustomer>) {\n return await storefront.customer.registerCustomer(...args)\n}\n\nexport async function requestLoginCode(...args: Parameters<typeof storefront.customer.requestLoginCode>) {\n return await storefront.customer.requestLoginCode(...args)\n}\n\nexport async function loginWithCode(...args: Parameters<typeof storefront.customer.loginWithCode>) {\n return await storefront.customer.loginWithCode(...args)\n}\n\nexport async function requestCustomerPasswordReset(...args: Parameters<typeof storefront.customer.requestCustomerPasswordReset>) {\n return await storefront.customer.requestCustomerPasswordReset(...args)\n}\n\nexport async function resetCustomerPassword(...args: Parameters<typeof storefront.customer.resetCustomerPassword>) {\n return await storefront.customer.resetCustomerPassword(...args)\n}\n\nexport async function confirmCustomerEmail(...args: Parameters<typeof storefront.customer.confirmCustomerEmail>) {\n return await storefront.customer.confirmCustomerEmail(...args)\n}\n\nexport async function resendCustomerVerification(...args: Parameters<typeof storefront.customer.resendCustomerVerification>) {\n return await storefront.customer.resendCustomerVerification(...args)\n}\n\nexport async function fetchCustomerPasswordPolicy(...args: Parameters<typeof storefront.customer.fetchCustomerPasswordPolicy>) {\n return await storefront.customer.fetchCustomerPasswordPolicy(...args)\n}\n\nexport async function updateCustomer(...args: Parameters<typeof storefront.customer.updateCustomer>) {\n return await storefront.customer.updateCustomer(...args)\n}\n\nexport async function createCustomerAddress(...args: Parameters<typeof storefront.customer.createCustomerAddress>) {\n return await storefront.customer.createCustomerAddress(...args)\n}\n\nexport async function saveFirstCheckoutAddresses(...args: Parameters<typeof storefront.customer.saveFirstCheckoutAddresses>) {\n return await storefront.customer.saveFirstCheckoutAddresses(...args)\n}\n\nexport async function updateCustomerAddress(...args: Parameters<typeof storefront.customer.updateCustomerAddress>) {\n return await storefront.customer.updateCustomerAddress(...args)\n}\n\nexport async function deleteCustomerAddress(...args: Parameters<typeof storefront.customer.deleteCustomerAddress>) {\n return await storefront.customer.deleteCustomerAddress(...args)\n}\n\nexport async function logoutCustomer(...args: Parameters<typeof storefront.customer.logoutCustomer>) {\n return await storefront.customer.logoutCustomer(...args)\n}\n\nexport async function listShippingOptions(...args: Parameters<typeof storefront.checkout.listShippingOptions>) {\n return await storefront.checkout.listShippingOptions(...args)\n}\n\nexport async function listPaymentProviders(...args: Parameters<typeof storefront.checkout.listPaymentProviders>) {\n return await storefront.checkout.listPaymentProviders(...args)\n}\n\nexport async function listRegions(...args: Parameters<typeof storefront.region.listRegions>) {\n return await storefront.region.listRegions(...args)\n}\n\nexport async function fetchPluginFeatures(...args: Parameters<typeof storefront.pluginFeatures.fetchPluginFeatures>) {\n return await storefront.pluginFeatures.fetchPluginFeatures(...args)\n}\n\nexport async function checkCartTaxIdentification(...args: Parameters<typeof storefront.taxCompliance.checkCartTaxIdentification>) {\n return await storefront.taxCompliance.checkCartTaxIdentification(...args)\n}\n\nexport async function listCustomerTaxIdentifications(...args: Parameters<typeof storefront.taxCompliance.listCustomerTaxIdentifications>) {\n return await storefront.taxCompliance.listCustomerTaxIdentifications(...args)\n}\n\nexport async function verifyCustomerAddressTaxIdentification(...args: Parameters<typeof storefront.taxCompliance.verifyCustomerAddressTaxIdentification>) {\n return await storefront.taxCompliance.verifyCustomerAddressTaxIdentification(...args)\n}\n\nexport async function submitAffiliateApplication(...args: Parameters<typeof storefront.affiliate.submitAffiliateApplication>) {\n return await storefront.affiliate.submitAffiliateApplication(...args)\n}\n\nexport async function applyAffiliateOwnOrderPromotion(...args: Parameters<typeof storefront.affiliate.applyAffiliateOwnOrderPromotion>) {\n return await storefront.affiliate.applyAffiliateOwnOrderPromotion(...args)\n}\n\nexport async function createAffiliateSlug(...args: Parameters<typeof storefront.affiliate.createAffiliateSlug>) {\n return await storefront.affiliate.createAffiliateSlug(...args)\n}\n\nexport async function updateAffiliateSlug(...args: Parameters<typeof storefront.affiliate.updateAffiliateSlug>) {\n return await storefront.affiliate.updateAffiliateSlug(...args)\n}\n\nexport async function createAffiliateCouponCode(...args: Parameters<typeof storefront.affiliate.createAffiliateCouponCode>) {\n return await storefront.affiliate.createAffiliateCouponCode(...args)\n}\n\nexport async function rotateAffiliateCouponCode(...args: Parameters<typeof storefront.affiliate.rotateAffiliateCouponCode>) {\n return await storefront.affiliate.rotateAffiliateCouponCode(...args)\n}\n\nexport async function revokeAffiliateCouponCode(...args: Parameters<typeof storefront.affiliate.revokeAffiliateCouponCode>) {\n return await storefront.affiliate.revokeAffiliateCouponCode(...args)\n}\n\nexport async function createConsignmentReport(...args: Parameters<typeof storefront.affiliate.createConsignmentReport>) {\n return await storefront.affiliate.createConsignmentReport(...args)\n}\n\nexport async function listOrders(...args: Parameters<typeof storefront.orders.listOrders>) {\n return await storefront.orders.listOrders(...args)\n}\n\nexport async function listDocuments(...args: Parameters<typeof storefront.documents.listDocuments>) {\n return await storefront.documents.listDocuments(...args)\n}\n"
|
|
37
|
+
"content": "\"use server\"\n\n// One Server Action per storefront server function: Next only accepts \"use server\"\n// functions defined in the app. The browser side gets them through lib/storefront/client.ts.\n\nimport { storefront } from \"@/lib/storefront/server\"\n\nexport async function getActiveCart(...args: Parameters<typeof storefront.cart.getActiveCart>) {\n return await storefront.cart.getActiveCart(...args)\n}\n\nexport async function applyCustomerDefaultsToActiveCart(...args: Parameters<typeof storefront.cart.applyCustomerDefaultsToActiveCart>) {\n return await storefront.cart.applyCustomerDefaultsToActiveCart(...args)\n}\n\nexport async function syncCartId(...args: Parameters<typeof storefront.cart.syncCartId>) {\n return await storefront.cart.syncCartId(...args)\n}\n\nexport async function clearCartId(...args: Parameters<typeof storefront.cart.clearCartId>) {\n return await storefront.cart.clearCartId(...args)\n}\n\nexport async function discardActiveCart(...args: Parameters<typeof storefront.cart.discardActiveCart>) {\n return await storefront.cart.discardActiveCart(...args)\n}\n\nexport async function retrieveCustomer(...args: Parameters<typeof storefront.customer.retrieveCustomer>) {\n return await storefront.customer.retrieveCustomer(...args)\n}\n\nexport async function loginCustomer(...args: Parameters<typeof storefront.customer.loginCustomer>) {\n return await storefront.customer.loginCustomer(...args)\n}\n\nexport async function checkCustomerVerification(...args: Parameters<typeof storefront.customer.checkCustomerVerification>) {\n return await storefront.customer.checkCustomerVerification(...args)\n}\n\nexport async function registerCustomer(...args: Parameters<typeof storefront.customer.registerCustomer>) {\n return await storefront.customer.registerCustomer(...args)\n}\n\nexport async function registerCustomerWithProfile(...args: Parameters<typeof storefront.customer.registerCustomerWithProfile>) {\n return await storefront.customer.registerCustomerWithProfile(...args)\n}\n\nexport async function requestLoginCode(...args: Parameters<typeof storefront.customer.requestLoginCode>) {\n return await storefront.customer.requestLoginCode(...args)\n}\n\nexport async function loginWithCode(...args: Parameters<typeof storefront.customer.loginWithCode>) {\n return await storefront.customer.loginWithCode(...args)\n}\n\nexport async function requestCustomerPasswordReset(...args: Parameters<typeof storefront.customer.requestCustomerPasswordReset>) {\n return await storefront.customer.requestCustomerPasswordReset(...args)\n}\n\nexport async function resetCustomerPassword(...args: Parameters<typeof storefront.customer.resetCustomerPassword>) {\n return await storefront.customer.resetCustomerPassword(...args)\n}\n\nexport async function confirmCustomerEmail(...args: Parameters<typeof storefront.customer.confirmCustomerEmail>) {\n return await storefront.customer.confirmCustomerEmail(...args)\n}\n\nexport async function resendCustomerVerification(...args: Parameters<typeof storefront.customer.resendCustomerVerification>) {\n return await storefront.customer.resendCustomerVerification(...args)\n}\n\nexport async function fetchCustomerPasswordPolicy(...args: Parameters<typeof storefront.customer.fetchCustomerPasswordPolicy>) {\n return await storefront.customer.fetchCustomerPasswordPolicy(...args)\n}\n\nexport async function updateCustomer(...args: Parameters<typeof storefront.customer.updateCustomer>) {\n return await storefront.customer.updateCustomer(...args)\n}\n\nexport async function createCustomerAddress(...args: Parameters<typeof storefront.customer.createCustomerAddress>) {\n return await storefront.customer.createCustomerAddress(...args)\n}\n\nexport async function saveFirstCheckoutAddresses(...args: Parameters<typeof storefront.customer.saveFirstCheckoutAddresses>) {\n return await storefront.customer.saveFirstCheckoutAddresses(...args)\n}\n\nexport async function updateCustomerAddress(...args: Parameters<typeof storefront.customer.updateCustomerAddress>) {\n return await storefront.customer.updateCustomerAddress(...args)\n}\n\nexport async function deleteCustomerAddress(...args: Parameters<typeof storefront.customer.deleteCustomerAddress>) {\n return await storefront.customer.deleteCustomerAddress(...args)\n}\n\nexport async function logoutCustomer(...args: Parameters<typeof storefront.customer.logoutCustomer>) {\n return await storefront.customer.logoutCustomer(...args)\n}\n\nexport async function listShippingOptions(...args: Parameters<typeof storefront.checkout.listShippingOptions>) {\n return await storefront.checkout.listShippingOptions(...args)\n}\n\nexport async function listPaymentProviders(...args: Parameters<typeof storefront.checkout.listPaymentProviders>) {\n return await storefront.checkout.listPaymentProviders(...args)\n}\n\nexport async function listRegions(...args: Parameters<typeof storefront.region.listRegions>) {\n return await storefront.region.listRegions(...args)\n}\n\nexport async function fetchPluginFeatures(...args: Parameters<typeof storefront.pluginFeatures.fetchPluginFeatures>) {\n return await storefront.pluginFeatures.fetchPluginFeatures(...args)\n}\n\nexport async function checkCartTaxIdentification(...args: Parameters<typeof storefront.taxCompliance.checkCartTaxIdentification>) {\n return await storefront.taxCompliance.checkCartTaxIdentification(...args)\n}\n\nexport async function listCustomerTaxIdentifications(...args: Parameters<typeof storefront.taxCompliance.listCustomerTaxIdentifications>) {\n return await storefront.taxCompliance.listCustomerTaxIdentifications(...args)\n}\n\nexport async function verifyCustomerAddressTaxIdentification(...args: Parameters<typeof storefront.taxCompliance.verifyCustomerAddressTaxIdentification>) {\n return await storefront.taxCompliance.verifyCustomerAddressTaxIdentification(...args)\n}\n\nexport async function submitAffiliateApplication(...args: Parameters<typeof storefront.affiliate.submitAffiliateApplication>) {\n return await storefront.affiliate.submitAffiliateApplication(...args)\n}\n\nexport async function applyAffiliateOwnOrderPromotion(...args: Parameters<typeof storefront.affiliate.applyAffiliateOwnOrderPromotion>) {\n return await storefront.affiliate.applyAffiliateOwnOrderPromotion(...args)\n}\n\nexport async function createAffiliateSlug(...args: Parameters<typeof storefront.affiliate.createAffiliateSlug>) {\n return await storefront.affiliate.createAffiliateSlug(...args)\n}\n\nexport async function updateAffiliateSlug(...args: Parameters<typeof storefront.affiliate.updateAffiliateSlug>) {\n return await storefront.affiliate.updateAffiliateSlug(...args)\n}\n\nexport async function createAffiliateCouponCode(...args: Parameters<typeof storefront.affiliate.createAffiliateCouponCode>) {\n return await storefront.affiliate.createAffiliateCouponCode(...args)\n}\n\nexport async function rotateAffiliateCouponCode(...args: Parameters<typeof storefront.affiliate.rotateAffiliateCouponCode>) {\n return await storefront.affiliate.rotateAffiliateCouponCode(...args)\n}\n\nexport async function revokeAffiliateCouponCode(...args: Parameters<typeof storefront.affiliate.revokeAffiliateCouponCode>) {\n return await storefront.affiliate.revokeAffiliateCouponCode(...args)\n}\n\nexport async function createConsignmentReport(...args: Parameters<typeof storefront.affiliate.createConsignmentReport>) {\n return await storefront.affiliate.createConsignmentReport(...args)\n}\n\nexport async function listOrders(...args: Parameters<typeof storefront.orders.listOrders>) {\n return await storefront.orders.listOrders(...args)\n}\n\nexport async function getOrder(...args: Parameters<typeof storefront.orders.getOrder>) {\n return await storefront.orders.getOrder(...args)\n}\n\nexport async function listDocuments(...args: Parameters<typeof storefront.documents.listDocuments>) {\n return await storefront.documents.listDocuments(...args)\n}\n"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"path": "registry/items/setup/lib/storefront/client.ts",
|
package/dist/server/catalog.d.ts
CHANGED
|
@@ -6,14 +6,22 @@ export interface CommerceProductListInput {
|
|
|
6
6
|
collectionId?: string;
|
|
7
7
|
countryCode: string;
|
|
8
8
|
handles?: string[];
|
|
9
|
+
/** Medusa's product search (title, description, handle, ...). */
|
|
10
|
+
q?: string;
|
|
9
11
|
}
|
|
10
12
|
/** Medusa's half of a catalog: products priced for a country, categories and collections. */
|
|
11
13
|
export declare function createCatalog(context: ServerContext, region: ServerRegion): {
|
|
12
|
-
listCommerceProducts({ categoryId, collectionId, countryCode, handles }: CommerceProductListInput): Promise<HttpTypes.StoreProduct[] | null>;
|
|
14
|
+
listCommerceProducts({ categoryId, collectionId, countryCode, handles, q }: CommerceProductListInput): Promise<HttpTypes.StoreProduct[] | null>;
|
|
13
15
|
commerceProductByHandle({ countryCode, handle }: {
|
|
14
16
|
countryCode: string;
|
|
15
17
|
handle: string;
|
|
16
18
|
}): Promise<HttpTypes.StoreProduct | null>;
|
|
19
|
+
/**
|
|
20
|
+
* The top-level categories with all descendants (e.g. "Anwendungsbereiche"
|
|
21
|
+
* and "Inhaltsstoffe" with their entries) - the filter trees of a product
|
|
22
|
+
* list; filter with `filterProductsByCategories` from /core.
|
|
23
|
+
*/
|
|
24
|
+
listCategoryTrees(): Promise<HttpTypes.StoreProductCategory[]>;
|
|
17
25
|
categoryByPath(categoryPath: string[]): Promise<HttpTypes.StoreProductCategory | null>;
|
|
18
26
|
collectionByHandle(handle: string): Promise<HttpTypes.StoreCollection | null>;
|
|
19
27
|
};
|
package/dist/server/catalog.js
CHANGED
|
@@ -14,7 +14,7 @@ export function createCatalog(context, region) {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
return {
|
|
17
|
-
async listCommerceProducts({ categoryId, collectionId, countryCode, handles }) {
|
|
17
|
+
async listCommerceProducts({ categoryId, collectionId, countryCode, handles, q }) {
|
|
18
18
|
const pricingContext = await getPricingContext(countryCode);
|
|
19
19
|
if (!pricingContext) {
|
|
20
20
|
return null;
|
|
@@ -25,6 +25,7 @@ export function createCatalog(context, region) {
|
|
|
25
25
|
...(categoryId ? { category_id: [categoryId] } : {}),
|
|
26
26
|
...(collectionId ? { collection_id: [collectionId] } : {}),
|
|
27
27
|
...(handles?.length ? { handle: handles } : {}),
|
|
28
|
+
...(q?.trim() ? { q: q.trim() } : {}),
|
|
28
29
|
fields: PRODUCT_FIELDS,
|
|
29
30
|
limit: catalogFetchLimit,
|
|
30
31
|
});
|
|
@@ -44,6 +45,21 @@ export function createCatalog(context, region) {
|
|
|
44
45
|
});
|
|
45
46
|
return products[0] ?? null;
|
|
46
47
|
},
|
|
48
|
+
/**
|
|
49
|
+
* The top-level categories with all descendants (e.g. "Anwendungsbereiche"
|
|
50
|
+
* and "Inhaltsstoffe" with their entries) - the filter trees of a product
|
|
51
|
+
* list; filter with `filterProductsByCategories` from /core.
|
|
52
|
+
*/
|
|
53
|
+
async listCategoryTrees() {
|
|
54
|
+
const sdk = context.createMedusaClient();
|
|
55
|
+
const { product_categories: productCategories } = await sdk.store.category.list({
|
|
56
|
+
fields: "id,name,handle,description,metadata,rank,*category_children",
|
|
57
|
+
include_descendants_tree: true,
|
|
58
|
+
limit: catalogFetchLimit,
|
|
59
|
+
parent_category_id: "null",
|
|
60
|
+
});
|
|
61
|
+
return productCategories;
|
|
62
|
+
},
|
|
47
63
|
async categoryByPath(categoryPath) {
|
|
48
64
|
const sdk = context.createMedusaClient();
|
|
49
65
|
const { product_categories: productCategories } = await sdk.store.category.list({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LoginWithCodeRequest, RequestLoginCodeRequest } from "@hartl-services/medusa-contracts/types/customer-auth-store";
|
|
2
|
-
import type { CheckoutAddressValues, CustomerAddressValues, CustomerAuthValues, CustomerProfileValues, VerificationResendValues } from "../core/form-values.js";
|
|
2
|
+
import type { CheckoutAddressValues, CustomerAddressValues, CustomerAuthValues, CustomerProfileValues, CustomerRegistrationProfileValues, VerificationResendValues } from "../core/form-values.js";
|
|
3
3
|
import type { ServerContext } from "./context.js";
|
|
4
4
|
import type { RunMedusaAction } from "./run-medusa-action.js";
|
|
5
5
|
export interface UpdateCustomerAddressInput {
|
|
@@ -31,6 +31,22 @@ export declare function createCustomer(context: ServerContext, runMedusaAction:
|
|
|
31
31
|
state: "success";
|
|
32
32
|
sessionToken: string;
|
|
33
33
|
}>>;
|
|
34
|
+
/**
|
|
35
|
+
* Registration with name, phone and an optional address (the default
|
|
36
|
+
* address). The account counts as registered even if only the address
|
|
37
|
+
* fails (`addressSaved: false`, to add it in the account later) - a retry
|
|
38
|
+
* would otherwise end in "e-mail exists". While the e-mail is unconfirmed
|
|
39
|
+
* the address waits in the customer's metadata until the first login.
|
|
40
|
+
*/
|
|
41
|
+
registerCustomerWithProfile(data: CustomerRegistrationProfileValues): Promise<import("../core/action-result.js").MedusaActionResult<{
|
|
42
|
+
state: "verification_required";
|
|
43
|
+
addressSaved?: undefined;
|
|
44
|
+
sessionToken?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
addressSaved: boolean;
|
|
47
|
+
state: "success";
|
|
48
|
+
sessionToken: string;
|
|
49
|
+
}>>;
|
|
34
50
|
requestLoginCode(data: RequestLoginCodeRequest): Promise<import("../core/action-result.js").MedusaActionResult<void>>;
|
|
35
51
|
loginWithCode(data: LoginWithCodeRequest): Promise<import("../core/action-result.js").MedusaActionResult<{
|
|
36
52
|
state: "success";
|
package/dist/server/customer.js
CHANGED
|
@@ -6,7 +6,7 @@ import { completeCustomerSignIn as completeCustomerSignInWith, loginWithEmailpas
|
|
|
6
6
|
import { toCustomerAddressesFromCheckout } from "../core/checkout/checkout-address-draft.js";
|
|
7
7
|
import { StorefrontError } from "../core/errors.js";
|
|
8
8
|
import { CART_OWNER_FIELDS, CUSTOMER_FIELDS } from "../core/fields.js";
|
|
9
|
-
import { createCheckoutAddressSchema, createVerificationResendSchema, customerAddressSchema, customerAuthSchema, customerProfileSchema } from "../core/form-values.js";
|
|
9
|
+
import { createCheckoutAddressSchema, createVerificationResendSchema, customerAddressSchema, customerAuthSchema, customerProfileSchema, customerRegistrationProfileSchema, registrationAddressSchema } from "../core/form-values.js";
|
|
10
10
|
function cartCustomerHasAccount(cart) {
|
|
11
11
|
const customer = Reflect.get(cart, "customer");
|
|
12
12
|
return typeof customer === "object" && customer !== null && Reflect.get(customer, "has_account") === true;
|
|
@@ -14,6 +14,12 @@ function cartCustomerHasAccount(cart) {
|
|
|
14
14
|
function isVerificationRequired(result) {
|
|
15
15
|
return typeof result === "object" && result !== null && Reflect.get(result, "verification_required") === true && typeof Reflect.get(result, "token") === "string";
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Metadata key of the address a registration could not store yet: Medusa takes
|
|
19
|
+
* no address with the customer, and while the e-mail is unconfirmed there is
|
|
20
|
+
* no token to add one, so it waits here until the first login.
|
|
21
|
+
*/
|
|
22
|
+
const PENDING_REGISTRATION_ADDRESS_KEY = "pending_registration_address";
|
|
17
23
|
function getAuthenticationToken(result) {
|
|
18
24
|
return typeof result === "string" ? result : null;
|
|
19
25
|
}
|
|
@@ -71,12 +77,55 @@ export function createCustomer(context, runMedusaAction) {
|
|
|
71
77
|
return undefined;
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Stores the registration address as the customer's default address. True
|
|
82
|
+
* when stored (or nothing to store); never throws - a failing address must
|
|
83
|
+
* not turn a registration or login into a failure.
|
|
84
|
+
*/
|
|
85
|
+
async function storeRegistrationAddress(token, profile, address) {
|
|
86
|
+
const parsed = registrationAddressSchema.safeParse(address);
|
|
87
|
+
if (!parsed.success) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const sdk = createAuthenticatedMedusaClient(token);
|
|
92
|
+
await sdk.store.customer.createAddress({
|
|
93
|
+
...parsed.data,
|
|
94
|
+
first_name: profile.first_name ?? undefined,
|
|
95
|
+
is_default_billing: true,
|
|
96
|
+
is_default_shipping: true,
|
|
97
|
+
last_name: profile.last_name ?? undefined,
|
|
98
|
+
phone: profile.phone || undefined,
|
|
99
|
+
});
|
|
100
|
+
await sdk.store.customer.update({ metadata: { [PENDING_REGISTRATION_ADDRESS_KEY]: null } });
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
context.reportError(error, { area: "customer-auth", context: { step: "registration_address" } });
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** After a login: adds a registration address that waited for the e-mail confirmation. */
|
|
109
|
+
async function applyPendingRegistrationAddress(token) {
|
|
110
|
+
try {
|
|
111
|
+
const { customer } = await createAuthenticatedMedusaClient(token).store.customer.retrieve({ fields: "first_name,last_name,phone,metadata,*addresses" });
|
|
112
|
+
const pending = customer.metadata?.[PENDING_REGISTRATION_ADDRESS_KEY];
|
|
113
|
+
if (pending && (customer.addresses?.length ?? 0) === 0) {
|
|
114
|
+
await storeRegistrationAddress(token, customer, pending);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
context.reportError(error, { area: "customer-auth", context: { step: "pending_registration_address" } });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function completeCustomerSignIn(token, email) {
|
|
122
|
+
const result = await completeCustomerSignInWith(token, email, {
|
|
76
123
|
createAuthenticatedClient: createAuthenticatedMedusaClient,
|
|
77
124
|
storeToken: session.setMedusaAuthToken,
|
|
78
125
|
transferCart: transferActiveCartToCustomer,
|
|
79
126
|
});
|
|
127
|
+
await applyPendingRegistrationAddress(result.sessionToken);
|
|
128
|
+
return result;
|
|
80
129
|
}
|
|
81
130
|
async function signInCustomer(data, { requestVerificationMail }) {
|
|
82
131
|
const parsed = customerAuthSchema.parse(data);
|
|
@@ -144,6 +193,43 @@ export function createCustomer(context, runMedusaAction) {
|
|
|
144
193
|
return { state: "success", sessionToken: token };
|
|
145
194
|
}, { endSessionOnUnauthorized: false });
|
|
146
195
|
},
|
|
196
|
+
/**
|
|
197
|
+
* Registration with name, phone and an optional address (the default
|
|
198
|
+
* address). The account counts as registered even if only the address
|
|
199
|
+
* fails (`addressSaved: false`, to add it in the account later) - a retry
|
|
200
|
+
* would otherwise end in "e-mail exists". While the e-mail is unconfirmed
|
|
201
|
+
* the address waits in the customer's metadata until the first login.
|
|
202
|
+
*/
|
|
203
|
+
async registerCustomerWithProfile(data) {
|
|
204
|
+
const parsed = customerRegistrationProfileSchema.parse(data);
|
|
205
|
+
const credentials = { email: parsed.email, password: parsed.password };
|
|
206
|
+
return runMedusaAction(async () => {
|
|
207
|
+
const registrationSdk = createMedusaClient();
|
|
208
|
+
const registrationToken = await registrationSdk.auth.register("customer", "emailpass", credentials);
|
|
209
|
+
const customerSdk = createAuthenticatedMedusaClient(registrationToken);
|
|
210
|
+
const { customer } = await customerSdk.store.customer.create({
|
|
211
|
+
email: parsed.email,
|
|
212
|
+
first_name: parsed.first_name,
|
|
213
|
+
last_name: parsed.last_name,
|
|
214
|
+
...(parsed.phone ? { phone: parsed.phone } : {}),
|
|
215
|
+
...(parsed.address ? { metadata: { [PENDING_REGISTRATION_ADDRESS_KEY]: parsed.address } } : {}),
|
|
216
|
+
});
|
|
217
|
+
const loginSdk = createMedusaClient();
|
|
218
|
+
const result = await loginSdk.auth.login("customer", "emailpass", credentials);
|
|
219
|
+
if (isVerificationRequired(result)) {
|
|
220
|
+
await requestCustomerVerification(parsed.email, result.token);
|
|
221
|
+
return { state: "verification_required" };
|
|
222
|
+
}
|
|
223
|
+
const token = getAuthenticationToken(result);
|
|
224
|
+
if (!token) {
|
|
225
|
+
throw new StorefrontError("auth.registration_unsupported_step");
|
|
226
|
+
}
|
|
227
|
+
await transferActiveCartToCustomer(createAuthenticatedMedusaClient(token), { id: customer.id, email: parsed.email });
|
|
228
|
+
await session.setMedusaAuthToken(token);
|
|
229
|
+
const addressSaved = parsed.address ? await storeRegistrationAddress(token, parsed, parsed.address) : true;
|
|
230
|
+
return { addressSaved, state: "success", sessionToken: token };
|
|
231
|
+
}, { endSessionOnUnauthorized: false });
|
|
232
|
+
},
|
|
147
233
|
// The backend answers 202 for every address (no account enumeration) and
|
|
148
234
|
// sends at most one code per address and minute.
|
|
149
235
|
async requestLoginCode(data) {
|
package/dist/server/index.d.ts
CHANGED
|
@@ -61,6 +61,15 @@ export declare function createStorefrontServer(config: StorefrontServerConfig):
|
|
|
61
61
|
state: "success";
|
|
62
62
|
sessionToken: string;
|
|
63
63
|
}>>;
|
|
64
|
+
registerCustomerWithProfile(data: import("../core/form-values.js").CustomerRegistrationProfileValues): Promise<import("../core/action-result.js").MedusaActionResult<{
|
|
65
|
+
state: "verification_required";
|
|
66
|
+
addressSaved?: undefined;
|
|
67
|
+
sessionToken?: undefined;
|
|
68
|
+
} | {
|
|
69
|
+
addressSaved: boolean;
|
|
70
|
+
state: "success";
|
|
71
|
+
sessionToken: string;
|
|
72
|
+
}>>;
|
|
64
73
|
requestLoginCode(data: import("@hartl-services/medusa-contracts/types/customer-auth-store").RequestLoginCodeRequest): Promise<import("../core/action-result.js").MedusaActionResult<void>>;
|
|
65
74
|
loginWithCode(data: import("@hartl-services/medusa-contracts/types/customer-auth-store").LoginWithCodeRequest): Promise<import("../core/action-result.js").MedusaActionResult<{
|
|
66
75
|
state: "success";
|
|
@@ -236,11 +245,12 @@ export declare function createStorefrontServer(config: StorefrontServerConfig):
|
|
|
236
245
|
}): Promise<Response>;
|
|
237
246
|
};
|
|
238
247
|
catalog: {
|
|
239
|
-
listCommerceProducts({ categoryId, collectionId, countryCode, handles }: import("./catalog.js").CommerceProductListInput): Promise<import("@medusajs/types").StoreProduct[] | null>;
|
|
248
|
+
listCommerceProducts({ categoryId, collectionId, countryCode, handles, q }: import("./catalog.js").CommerceProductListInput): Promise<import("@medusajs/types").StoreProduct[] | null>;
|
|
240
249
|
commerceProductByHandle({ countryCode, handle }: {
|
|
241
250
|
countryCode: string;
|
|
242
251
|
handle: string;
|
|
243
252
|
}): Promise<import("@medusajs/types").StoreProduct | null>;
|
|
253
|
+
listCategoryTrees(): Promise<import("@medusajs/types").StoreProductCategory[]>;
|
|
244
254
|
categoryByPath(categoryPath: string[]): Promise<import("@medusajs/types").StoreProductCategory | null>;
|
|
245
255
|
collectionByHandle(handle: string): Promise<import("@medusajs/types").StoreCollection | null>;
|
|
246
256
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hartl-services/medusa-storefront",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Headless Medusa storefront logic, Next.js server/browser adapters and React hooks for shops built from the @hartl-services/medusa-* plugins.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|