@pradip1995/commerce-core 1.0.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 +15 -0
- package/package.json +70 -0
- package/src/analytics/ga4-ecommerce.ts +96 -0
- package/src/config.ts +36 -0
- package/src/constants.tsx +84 -0
- package/src/context/modal-context.tsx +40 -0
- package/src/context/wishlist-context.tsx +96 -0
- package/src/data/cart/abandoned.ts +111 -0
- package/src/data/cart/buyNow.ts +184 -0
- package/src/data/cart/checkout.ts +487 -0
- package/src/data/cart/index.ts +7 -0
- package/src/data/cart/mutations.ts +189 -0
- package/src/data/cart/promotions.ts +121 -0
- package/src/data/cart/region.ts +66 -0
- package/src/data/cart/retrieve.ts +162 -0
- package/src/data/categories.ts +90 -0
- package/src/data/collections.ts +109 -0
- package/src/data/contact.ts +143 -0
- package/src/data/cookies.ts +170 -0
- package/src/data/customer-registration.ts +365 -0
- package/src/data/customer.ts +638 -0
- package/src/data/dynamic-config.ts +420 -0
- package/src/data/fulfillment.ts +95 -0
- package/src/data/guest.ts +357 -0
- package/src/data/locale-actions.ts +74 -0
- package/src/data/locales.ts +28 -0
- package/src/data/newsletter.ts +41 -0
- package/src/data/notifications.ts +22 -0
- package/src/data/onboarding.ts +9 -0
- package/src/data/orders.ts +500 -0
- package/src/data/payment-details.ts +68 -0
- package/src/data/payment.ts +32 -0
- package/src/data/products.ts +424 -0
- package/src/data/regions.ts +64 -0
- package/src/data/returns.ts +305 -0
- package/src/data/reviews.ts +279 -0
- package/src/data/swaps.ts +154 -0
- package/src/data/variants.ts +38 -0
- package/src/data/wishlist.ts +292 -0
- package/src/domain/cart/abandoned-carts.ts +49 -0
- package/src/domain/cart/buy-now.ts +15 -0
- package/src/domain/cart/checkout.ts +25 -0
- package/src/domain/cart/index.ts +8 -0
- package/src/domain/cart/metadata.ts +21 -0
- package/src/domain/cart/payment.ts +21 -0
- package/src/domain/cart/phone.ts +17 -0
- package/src/domain/cart/reorder.ts +19 -0
- package/src/domain/cart/validation.ts +43 -0
- package/src/domain/product/pricing.ts +49 -0
- package/src/domain/product/variant-selection.ts +193 -0
- package/src/firebase.ts +48 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/use-add-to-cart.ts +63 -0
- package/src/hooks/use-cart.ts +132 -0
- package/src/hooks/use-checkout.ts +62 -0
- package/src/hooks/use-in-view.tsx +29 -0
- package/src/hooks/use-product-actions.ts +190 -0
- package/src/hooks/use-product-reviews.ts +18 -0
- package/src/hooks/use-product-variant.ts +142 -0
- package/src/hooks/use-server-action.ts +30 -0
- package/src/hooks/use-toggle-state.tsx +46 -0
- package/src/hooks/use-wishlist.ts +3 -0
- package/src/theme/inline-vars.ts +12 -0
- package/src/types/account.ts +21 -0
- package/src/types/cart.ts +13 -0
- package/src/types/home.ts +52 -0
- package/src/types/layout.ts +29 -0
- package/src/types/product-card.ts +17 -0
- package/src/util/compare-addresses.ts +28 -0
- package/src/util/env.ts +3 -0
- package/src/util/get-locale-header.ts +8 -0
- package/src/util/get-percentage-diff.ts +6 -0
- package/src/util/get-product-price.ts +78 -0
- package/src/util/google-oauth.ts +28 -0
- package/src/util/isEmpty.ts +11 -0
- package/src/util/medusa-error.ts +18 -0
- package/src/util/money.ts +26 -0
- package/src/util/order-status.tsx +179 -0
- package/src/util/product.ts +431 -0
- package/src/util/repeat.ts +5 -0
- package/src/util/returns.ts +71 -0
- package/src/util/sort-products.ts +48 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useCallback, useMemo, useState } from "react"
|
|
4
|
+
import { useParams } from "next/navigation"
|
|
5
|
+
import { HttpTypes } from "@medusajs/types"
|
|
6
|
+
import { addToCart, buyNow, updateLineItem, deleteLineItem } from "@core/data/cart"
|
|
7
|
+
import { trackAddToCart } from "@core/analytics/ga4-ecommerce"
|
|
8
|
+
import {
|
|
9
|
+
type ProductOptions,
|
|
10
|
+
isVariantInStock,
|
|
11
|
+
getInventoryLimit,
|
|
12
|
+
getVariantCartItem,
|
|
13
|
+
} from "@core/domain/product/variant-selection"
|
|
14
|
+
import { type VariantPrice } from "@core/domain/product/pricing"
|
|
15
|
+
|
|
16
|
+
type UseProductActionsOptions = {
|
|
17
|
+
product: HttpTypes.StoreProduct
|
|
18
|
+
region?: HttpTypes.StoreRegion
|
|
19
|
+
cart?: HttpTypes.StoreCart | null
|
|
20
|
+
selectedVariant?: HttpTypes.StoreProductVariant
|
|
21
|
+
options: ProductOptions
|
|
22
|
+
isValidVariant: boolean
|
|
23
|
+
displayPrice: VariantPrice | null
|
|
24
|
+
validateOptions: () => boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useProductActions({
|
|
28
|
+
product,
|
|
29
|
+
region,
|
|
30
|
+
cart,
|
|
31
|
+
selectedVariant,
|
|
32
|
+
options,
|
|
33
|
+
isValidVariant,
|
|
34
|
+
displayPrice,
|
|
35
|
+
validateOptions,
|
|
36
|
+
}: UseProductActionsOptions) {
|
|
37
|
+
const { countryCode } = useParams() as { countryCode: string }
|
|
38
|
+
const [isAdding, setIsAdding] = useState(false)
|
|
39
|
+
const [isBuyingNow, setIsBuyingNow] = useState(false)
|
|
40
|
+
const [quantity, setQuantity] = useState(1)
|
|
41
|
+
|
|
42
|
+
const variantInCart = useMemo(
|
|
43
|
+
() => getVariantCartItem(cart, selectedVariant?.id),
|
|
44
|
+
[cart, selectedVariant?.id]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const quantityInCart = useMemo(() => variantInCart?.quantity || 0, [variantInCart])
|
|
48
|
+
|
|
49
|
+
const inStock = useMemo(
|
|
50
|
+
() => isVariantInStock(selectedVariant, product, quantity, quantityInCart),
|
|
51
|
+
[selectedVariant, product, quantity, quantityInCart]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const inventoryLimit = useMemo(
|
|
55
|
+
() => getInventoryLimit(selectedVariant, quantityInCart),
|
|
56
|
+
[selectedVariant, quantityInCart]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const trackCartAdd = useCallback(() => {
|
|
60
|
+
if (!selectedVariant?.id) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const currency = region?.currency_code?.toUpperCase() ?? "USD"
|
|
65
|
+
const unitPrice = displayPrice?.calculated_price_number ?? 0
|
|
66
|
+
const value = (unitPrice * quantity) / 100
|
|
67
|
+
|
|
68
|
+
trackAddToCart({
|
|
69
|
+
currency,
|
|
70
|
+
value,
|
|
71
|
+
items: [
|
|
72
|
+
{
|
|
73
|
+
item_id: selectedVariant.id,
|
|
74
|
+
item_name: product.title ?? "",
|
|
75
|
+
price: unitPrice / 100,
|
|
76
|
+
quantity,
|
|
77
|
+
item_variant: selectedVariant.title ?? undefined,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
})
|
|
81
|
+
}, [selectedVariant, region, displayPrice, quantity, product.title])
|
|
82
|
+
|
|
83
|
+
const handleAddToCart = useCallback(async () => {
|
|
84
|
+
if (!validateOptions()) {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!selectedVariant?.id) {
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
setIsAdding(true)
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
await addToCart({
|
|
96
|
+
variantId: selectedVariant.id,
|
|
97
|
+
quantity,
|
|
98
|
+
countryCode,
|
|
99
|
+
})
|
|
100
|
+
trackCartAdd()
|
|
101
|
+
} finally {
|
|
102
|
+
setIsAdding(false)
|
|
103
|
+
}
|
|
104
|
+
}, [validateOptions, selectedVariant, quantity, countryCode, trackCartAdd])
|
|
105
|
+
|
|
106
|
+
const handleBuyNow = useCallback(async () => {
|
|
107
|
+
if (!validateOptions()) {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!selectedVariant?.id) {
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setIsBuyingNow(true)
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
await buyNow({
|
|
119
|
+
variantId: selectedVariant.id,
|
|
120
|
+
quantity,
|
|
121
|
+
countryCode,
|
|
122
|
+
})
|
|
123
|
+
} catch (e) {
|
|
124
|
+
console.error(e)
|
|
125
|
+
} finally {
|
|
126
|
+
setIsBuyingNow(false)
|
|
127
|
+
}
|
|
128
|
+
}, [validateOptions, selectedVariant, quantity, countryCode])
|
|
129
|
+
|
|
130
|
+
const handleIncreaseQuantity = useCallback(async () => {
|
|
131
|
+
if (!variantInCart || isAdding) {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setIsAdding(true)
|
|
136
|
+
try {
|
|
137
|
+
await updateLineItem({
|
|
138
|
+
lineId: variantInCart.id,
|
|
139
|
+
quantity: variantInCart.quantity + 1,
|
|
140
|
+
})
|
|
141
|
+
} catch (e) {
|
|
142
|
+
console.error(e)
|
|
143
|
+
} finally {
|
|
144
|
+
setIsAdding(false)
|
|
145
|
+
}
|
|
146
|
+
}, [variantInCart, isAdding])
|
|
147
|
+
|
|
148
|
+
const handleDecreaseQuantity = useCallback(async () => {
|
|
149
|
+
if (!variantInCart || isAdding) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
setIsAdding(true)
|
|
154
|
+
try {
|
|
155
|
+
if (variantInCart.quantity > 1) {
|
|
156
|
+
await updateLineItem({
|
|
157
|
+
lineId: variantInCart.id,
|
|
158
|
+
quantity: variantInCart.quantity - 1,
|
|
159
|
+
})
|
|
160
|
+
} else {
|
|
161
|
+
await deleteLineItem(variantInCart.id)
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.error(e)
|
|
165
|
+
} finally {
|
|
166
|
+
setIsAdding(false)
|
|
167
|
+
}
|
|
168
|
+
}, [variantInCart, isAdding])
|
|
169
|
+
|
|
170
|
+
const canAddToCart = useMemo(() => {
|
|
171
|
+
const allOptionsSelected = product.options?.every((opt) => options[opt.id])
|
|
172
|
+
return inStock && isValidVariant && (!product.options?.length || allOptionsSelected)
|
|
173
|
+
}, [inStock, isValidVariant, product.options, options])
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
quantity,
|
|
177
|
+
setQuantity,
|
|
178
|
+
isAdding,
|
|
179
|
+
isBuyingNow,
|
|
180
|
+
variantInCart,
|
|
181
|
+
quantityInCart,
|
|
182
|
+
inStock,
|
|
183
|
+
inventoryLimit,
|
|
184
|
+
handleAddToCart,
|
|
185
|
+
handleBuyNow,
|
|
186
|
+
handleIncreaseQuantity,
|
|
187
|
+
handleDecreaseQuantity,
|
|
188
|
+
canAddToCart,
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react"
|
|
4
|
+
import { fetchReviewsForProduct } from "@core/data/reviews"
|
|
5
|
+
|
|
6
|
+
export function useProductReviews(productId: string | undefined) {
|
|
7
|
+
const [reviews, setReviews] = useState<unknown[]>([])
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!productId) return
|
|
11
|
+
|
|
12
|
+
fetchReviewsForProduct(productId)
|
|
13
|
+
.then(setReviews)
|
|
14
|
+
.catch(() => setReviews([]))
|
|
15
|
+
}, [productId])
|
|
16
|
+
|
|
17
|
+
return reviews
|
|
18
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState, useCallback } from "react"
|
|
4
|
+
import { usePathname, useSearchParams } from "next/navigation"
|
|
5
|
+
import { HttpTypes } from "@medusajs/types"
|
|
6
|
+
import {
|
|
7
|
+
type ProductOptions,
|
|
8
|
+
getInitialOptions,
|
|
9
|
+
findVariantByOptions,
|
|
10
|
+
isValidVariantSelection,
|
|
11
|
+
findColorOption,
|
|
12
|
+
findSizeOption,
|
|
13
|
+
getUniqueColorVariants,
|
|
14
|
+
getOtherOptions,
|
|
15
|
+
getMissingOptionIds,
|
|
16
|
+
} from "@core/domain/product/variant-selection"
|
|
17
|
+
import { useProductContext } from "@modules/products/context/product-context"
|
|
18
|
+
|
|
19
|
+
type UseProductVariantOptions = {
|
|
20
|
+
product: HttpTypes.StoreProduct
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useProductVariant({ product }: UseProductVariantOptions) {
|
|
24
|
+
const pathname = usePathname()
|
|
25
|
+
const searchParams = useSearchParams()
|
|
26
|
+
const { setSelectedVariantId, setSelectedOptions } = useProductContext()
|
|
27
|
+
|
|
28
|
+
const [options, setOptions] = useState<ProductOptions>(() =>
|
|
29
|
+
getInitialOptions(product, searchParams.get("v_id"))
|
|
30
|
+
)
|
|
31
|
+
const [validationErrors, setValidationErrors] = useState<Record<string, boolean>>({})
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const variantIdFromUrl = searchParams.get("v_id")
|
|
35
|
+
if (variantIdFromUrl && product.variants) {
|
|
36
|
+
const variantFromUrl = product.variants.find((v) => v.id === variantIdFromUrl)
|
|
37
|
+
if (variantFromUrl) {
|
|
38
|
+
setOptions(getInitialOptions(product, variantIdFromUrl))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}, [product, searchParams])
|
|
42
|
+
|
|
43
|
+
const selectedVariant = useMemo(
|
|
44
|
+
() => findVariantByOptions(product, options),
|
|
45
|
+
[product, options]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const isValidVariant = useMemo(
|
|
49
|
+
() => isValidVariantSelection(product, options),
|
|
50
|
+
[product, options]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
setSelectedOptions(options)
|
|
55
|
+
}, [options, setSelectedOptions])
|
|
56
|
+
|
|
57
|
+
const setOptionValue = useCallback((optionId: string, value: string) => {
|
|
58
|
+
setOptions((prev) => ({
|
|
59
|
+
...prev,
|
|
60
|
+
[optionId]: value,
|
|
61
|
+
}))
|
|
62
|
+
setValidationErrors((prev) => ({
|
|
63
|
+
...prev,
|
|
64
|
+
[optionId]: false,
|
|
65
|
+
}))
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
const setColorValue = useCallback((colorOptionId: string, value: string) => {
|
|
69
|
+
setOptions((prev) => ({
|
|
70
|
+
...prev,
|
|
71
|
+
[colorOptionId]: value,
|
|
72
|
+
}))
|
|
73
|
+
setValidationErrors((prev) => ({
|
|
74
|
+
...prev,
|
|
75
|
+
[colorOptionId]: false,
|
|
76
|
+
}))
|
|
77
|
+
}, [])
|
|
78
|
+
|
|
79
|
+
const validateOptions = useCallback(() => {
|
|
80
|
+
const missing = getMissingOptionIds(product, options)
|
|
81
|
+
if (Object.keys(missing).length > 0) {
|
|
82
|
+
setValidationErrors(missing)
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
return true
|
|
86
|
+
}, [product, options])
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const params = new URLSearchParams(searchParams.toString())
|
|
90
|
+
const value = isValidVariant ? selectedVariant?.id : undefined
|
|
91
|
+
|
|
92
|
+
setSelectedVariantId(value)
|
|
93
|
+
|
|
94
|
+
if (params.get("v_id") === value) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (value) {
|
|
99
|
+
params.set("v_id", value)
|
|
100
|
+
} else {
|
|
101
|
+
params.delete("v_id")
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const newUrl = pathname + "?" + params.toString()
|
|
105
|
+
window.history.replaceState(null, "", newUrl)
|
|
106
|
+
}, [selectedVariant, isValidVariant, pathname, searchParams, setSelectedVariantId])
|
|
107
|
+
|
|
108
|
+
const colorOption = useMemo(() => findColorOption(product), [product])
|
|
109
|
+
const sizeOption = useMemo(() => findSizeOption(product), [product])
|
|
110
|
+
const otherOptions = useMemo(() => getOtherOptions(product), [product])
|
|
111
|
+
|
|
112
|
+
const colorVariants = useMemo(() => {
|
|
113
|
+
if (!colorOption) {
|
|
114
|
+
return []
|
|
115
|
+
}
|
|
116
|
+
return getUniqueColorVariants(product, colorOption)
|
|
117
|
+
}, [product, colorOption])
|
|
118
|
+
|
|
119
|
+
const selectedColorValue = useMemo(() => {
|
|
120
|
+
if (!colorOption) {
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
return options[colorOption.id]
|
|
124
|
+
}, [options, colorOption])
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
options,
|
|
128
|
+
setOptions,
|
|
129
|
+
setOptionValue,
|
|
130
|
+
setColorValue,
|
|
131
|
+
selectedVariant,
|
|
132
|
+
isValidVariant,
|
|
133
|
+
validationErrors,
|
|
134
|
+
setValidationErrors,
|
|
135
|
+
validateOptions,
|
|
136
|
+
colorOption,
|
|
137
|
+
sizeOption,
|
|
138
|
+
otherOptions,
|
|
139
|
+
colorVariants,
|
|
140
|
+
selectedColorValue,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState, useTransition } from "react"
|
|
4
|
+
|
|
5
|
+
type ServerAction<TInput, TOutput> = (input: TInput) => Promise<TOutput> | TOutput
|
|
6
|
+
|
|
7
|
+
export function useServerAction<TInput, TOutput>(
|
|
8
|
+
action: ServerAction<TInput, TOutput>
|
|
9
|
+
) {
|
|
10
|
+
const [isPending, startTransition] = useTransition()
|
|
11
|
+
const [error, setError] = useState<Error | null>(null)
|
|
12
|
+
const [result, setResult] = useState<TOutput | null>(null)
|
|
13
|
+
|
|
14
|
+
const execute = useCallback(
|
|
15
|
+
(input: TInput) => {
|
|
16
|
+
setError(null)
|
|
17
|
+
startTransition(async () => {
|
|
18
|
+
try {
|
|
19
|
+
const output = await action(input)
|
|
20
|
+
setResult(output)
|
|
21
|
+
} catch (err) {
|
|
22
|
+
setError(err instanceof Error ? err : new Error(String(err)))
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
},
|
|
26
|
+
[action]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return { execute, isPending, error, result }
|
|
30
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useState } from "react"
|
|
2
|
+
|
|
3
|
+
export type StateType = [boolean, () => void, () => void, () => void] & {
|
|
4
|
+
state: boolean
|
|
5
|
+
open: () => void
|
|
6
|
+
close: () => void
|
|
7
|
+
toggle: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param initialState - boolean
|
|
13
|
+
* @returns An array like object with `state`, `open`, `close`, and `toggle` properties
|
|
14
|
+
* to allow both object and array destructuring
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* const [showModal, openModal, closeModal, toggleModal] = useToggleState()
|
|
18
|
+
* // or
|
|
19
|
+
* const { state, open, close, toggle } = useToggleState()
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const useToggleState = (initialState = false) => {
|
|
24
|
+
const [state, setState] = useState<boolean>(initialState)
|
|
25
|
+
|
|
26
|
+
const close = () => {
|
|
27
|
+
setState(false)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const open = () => {
|
|
31
|
+
setState(true)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const toggle = () => {
|
|
35
|
+
setState((state) => !state)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const hookData = [state, open, close, toggle] as StateType
|
|
39
|
+
hookData.state = state
|
|
40
|
+
hookData.open = open
|
|
41
|
+
hookData.close = close
|
|
42
|
+
hookData.toggle = toggle
|
|
43
|
+
return hookData
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default useToggleState
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** CSS variable references for inline styles (maps to active theme.css). */
|
|
2
|
+
export const themeColors = {
|
|
3
|
+
brandAccent: "var(--color-brand-accent)",
|
|
4
|
+
brandAccentHover: "var(--color-brand-accent-hover)",
|
|
5
|
+
brandAccentMuted: "var(--color-brand-accent-muted)",
|
|
6
|
+
brandAccentBorder: "var(--color-brand-accent-border)",
|
|
7
|
+
border: "var(--color-border)",
|
|
8
|
+
surface: "var(--color-surface)",
|
|
9
|
+
pageBg: "var(--color-page-bg)",
|
|
10
|
+
textHeading: "var(--color-text-heading)",
|
|
11
|
+
textInverse: "var(--color-text-inverse)",
|
|
12
|
+
} as const
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AccountPageData = {
|
|
2
|
+
countryCode: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export enum LOGIN_VIEW {
|
|
6
|
+
SIGN_IN = "sign-in",
|
|
7
|
+
REGISTER = "register",
|
|
8
|
+
FORGOT_PASSWORD = "forgot-password",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type LoginSlotProps = {
|
|
12
|
+
setCurrentView: (view: LOGIN_VIEW) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type RegisterSlotProps = {
|
|
16
|
+
setCurrentView: (view: LOGIN_VIEW) => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ForgotPasswordSlotProps = {
|
|
20
|
+
setCurrentView: (view: LOGIN_VIEW) => void
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HttpTypes } from "@medusajs/types"
|
|
2
|
+
|
|
3
|
+
export type AbandonedCartsData = {
|
|
4
|
+
buyNowCarts: HttpTypes.StoreCart[]
|
|
5
|
+
reorderCarts: HttpTypes.StoreCart[]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type CartPageData = {
|
|
9
|
+
cart: HttpTypes.StoreCart | null
|
|
10
|
+
customer: HttpTypes.StoreCustomer | null
|
|
11
|
+
countryCode: string
|
|
12
|
+
abandonedCarts: AbandonedCartsData
|
|
13
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HttpTypes } from "@medusajs/types"
|
|
2
|
+
|
|
3
|
+
export type BannerData = {
|
|
4
|
+
image: string
|
|
5
|
+
title: string
|
|
6
|
+
subtitle: string
|
|
7
|
+
description: string
|
|
8
|
+
buttonName: string
|
|
9
|
+
buttonLink: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type HeroBlockData = {
|
|
13
|
+
homeBanner: BannerData
|
|
14
|
+
appBanner: BannerData
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type WhyChooseUsBlockData = {
|
|
18
|
+
title: string
|
|
19
|
+
features: Array<{ name: string; icon: string }>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type TestimonialsBlockData = {
|
|
23
|
+
initialData: {
|
|
24
|
+
title: string
|
|
25
|
+
testimonials: Array<{
|
|
26
|
+
id: string
|
|
27
|
+
text: string
|
|
28
|
+
name: string
|
|
29
|
+
rating: number
|
|
30
|
+
avatar?: string
|
|
31
|
+
}>
|
|
32
|
+
} | null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type HomePageData = {
|
|
36
|
+
hero: HeroBlockData
|
|
37
|
+
shopByAge: { collections: HttpTypes.StoreCollection[] }
|
|
38
|
+
shopByCategory: { categories: HttpTypes.StoreProductCategory[] }
|
|
39
|
+
whyChooseUs: WhyChooseUsBlockData
|
|
40
|
+
newArrivals: {
|
|
41
|
+
products: HttpTypes.StoreProduct[]
|
|
42
|
+
region: HttpTypes.StoreRegion
|
|
43
|
+
ratings: unknown[]
|
|
44
|
+
}
|
|
45
|
+
lovedByMoms: {
|
|
46
|
+
products: HttpTypes.StoreProduct[]
|
|
47
|
+
region: HttpTypes.StoreRegion
|
|
48
|
+
ratings: unknown[]
|
|
49
|
+
}
|
|
50
|
+
testimonials: TestimonialsBlockData
|
|
51
|
+
features: Record<string, never>
|
|
52
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HttpTypes, StoreRegion } from "@medusajs/types"
|
|
2
|
+
import { StoreCartShippingOption } from "@medusajs/types"
|
|
3
|
+
|
|
4
|
+
export type NavSlotData = {
|
|
5
|
+
regions: StoreRegion[]
|
|
6
|
+
currentLocale: string
|
|
7
|
+
customer: HttpTypes.StoreCustomer | null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type PromoBarSlotData = {
|
|
11
|
+
text: string
|
|
12
|
+
code: string
|
|
13
|
+
value: string
|
|
14
|
+
active: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type FooterSlotData = {
|
|
18
|
+
categories: HttpTypes.StoreProductCategory[]
|
|
19
|
+
socialLinks: Array<{ name: string; url: string; icon: string | null }>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type MainLayoutData = {
|
|
23
|
+
customer: HttpTypes.StoreCustomer | null
|
|
24
|
+
cart: HttpTypes.StoreCart | null
|
|
25
|
+
shippingOptions: StoreCartShippingOption[]
|
|
26
|
+
nav: NavSlotData
|
|
27
|
+
promoBar: PromoBarSlotData
|
|
28
|
+
footer: FooterSlotData
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HttpTypes } from "@medusajs/types"
|
|
2
|
+
|
|
3
|
+
export type ProductCardRating = {
|
|
4
|
+
product_id: string
|
|
5
|
+
average_rating: number
|
|
6
|
+
total_reviews: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Shared props contract for theme ProductCard slots. */
|
|
10
|
+
export type ProductCardProps = {
|
|
11
|
+
product: HttpTypes.StoreProduct
|
|
12
|
+
region: HttpTypes.StoreRegion
|
|
13
|
+
rating?: ProductCardRating
|
|
14
|
+
isFeatured?: boolean
|
|
15
|
+
wishlistProductIds?: string[] | null
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { isEqual, pick } from "lodash"
|
|
2
|
+
|
|
3
|
+
export default function compareAddresses(address1: any, address2: any) {
|
|
4
|
+
return isEqual(
|
|
5
|
+
pick(address1, [
|
|
6
|
+
"first_name",
|
|
7
|
+
"last_name",
|
|
8
|
+
"address_1",
|
|
9
|
+
"company",
|
|
10
|
+
"postal_code",
|
|
11
|
+
"city",
|
|
12
|
+
"country_code",
|
|
13
|
+
"province",
|
|
14
|
+
"phone",
|
|
15
|
+
]),
|
|
16
|
+
pick(address2, [
|
|
17
|
+
"first_name",
|
|
18
|
+
"last_name",
|
|
19
|
+
"address_1",
|
|
20
|
+
"company",
|
|
21
|
+
"postal_code",
|
|
22
|
+
"city",
|
|
23
|
+
"country_code",
|
|
24
|
+
"province",
|
|
25
|
+
"phone",
|
|
26
|
+
])
|
|
27
|
+
)
|
|
28
|
+
}
|
package/src/util/env.ts
ADDED