@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,78 @@
|
|
|
1
|
+
import { HttpTypes } from "@medusajs/types"
|
|
2
|
+
import { getPercentageDiff } from "./get-percentage-diff"
|
|
3
|
+
import { convertToLocale } from "./money"
|
|
4
|
+
|
|
5
|
+
export const getPricesForVariant = (variant: any) => {
|
|
6
|
+
if (!variant?.calculated_price?.calculated_amount) {
|
|
7
|
+
return null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
calculated_price_number: variant.calculated_price.calculated_amount,
|
|
12
|
+
calculated_price: convertToLocale({
|
|
13
|
+
amount: variant.calculated_price.calculated_amount,
|
|
14
|
+
currency_code: variant.calculated_price.currency_code,
|
|
15
|
+
}),
|
|
16
|
+
original_price_number: variant.calculated_price.original_amount,
|
|
17
|
+
original_price: convertToLocale({
|
|
18
|
+
amount: variant.calculated_price.original_amount,
|
|
19
|
+
currency_code: variant.calculated_price.currency_code,
|
|
20
|
+
}),
|
|
21
|
+
currency_code: variant.calculated_price.currency_code,
|
|
22
|
+
price_type: variant.calculated_price.calculated_price.price_list_type,
|
|
23
|
+
percentage_diff: getPercentageDiff(
|
|
24
|
+
variant.calculated_price.original_amount,
|
|
25
|
+
variant.calculated_price.calculated_amount
|
|
26
|
+
),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getProductPrice({
|
|
31
|
+
product,
|
|
32
|
+
variantId,
|
|
33
|
+
}: {
|
|
34
|
+
product: HttpTypes.StoreProduct
|
|
35
|
+
variantId?: string
|
|
36
|
+
}) {
|
|
37
|
+
if (!product || !product.id) {
|
|
38
|
+
throw new Error("No product provided")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const cheapestPrice = () => {
|
|
42
|
+
if (!product || !product.variants?.length) {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const cheapestVariant: any = product.variants
|
|
47
|
+
.filter((v: any) => !!v.calculated_price)
|
|
48
|
+
.sort((a: any, b: any) => {
|
|
49
|
+
return (
|
|
50
|
+
a.calculated_price.calculated_amount - b.calculated_price.calculated_amount
|
|
51
|
+
)
|
|
52
|
+
})[0]
|
|
53
|
+
|
|
54
|
+
return getPricesForVariant(cheapestVariant)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const variantPrice = () => {
|
|
58
|
+
if (!product || !variantId) {
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const variant: any = product.variants?.find(
|
|
63
|
+
(v) => v.id === variantId || v.sku === variantId
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if (!variant) {
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return getPricesForVariant(variant)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
product,
|
|
75
|
+
cheapestPrice: cheapestPrice(),
|
|
76
|
+
variantPrice: variantPrice(),
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth redirect_uri must exactly match a URI registered in Google Cloud Console.
|
|
3
|
+
* That is the storefront callback path (often includes country code), NOT the backend API path.
|
|
4
|
+
*
|
|
5
|
+
* Set NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_URL to override (must match Console exactly).
|
|
6
|
+
*/
|
|
7
|
+
export function getGoogleOAuthCallbackUrl(countryCode?: string): string {
|
|
8
|
+
const explicit = process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CALLBACK_URL?.trim()
|
|
9
|
+
if (explicit) {
|
|
10
|
+
return explicit
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const base =
|
|
14
|
+
process.env.NEXT_PUBLIC_BASE_URL ||
|
|
15
|
+
process.env.STOREFRONT_URL ||
|
|
16
|
+
"http://localhost:8000"
|
|
17
|
+
const normalized = base.replace(/\/$/, "")
|
|
18
|
+
const region =
|
|
19
|
+
countryCode?.trim().toLowerCase() ||
|
|
20
|
+
process.env.NEXT_PUBLIC_DEFAULT_REGION?.trim().toLowerCase() ||
|
|
21
|
+
""
|
|
22
|
+
|
|
23
|
+
if (region && /^[a-z]{2,3}$/i.test(region)) {
|
|
24
|
+
return `${normalized}/${region}/auth/customer/google/callback`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return `${normalized}/auth/customer/google/callback`
|
|
28
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const isObject = (input: any) => input instanceof Object
|
|
2
|
+
export const isArray = (input: any) => Array.isArray(input)
|
|
3
|
+
export const isEmpty = (input: any) => {
|
|
4
|
+
return (
|
|
5
|
+
input === null ||
|
|
6
|
+
input === undefined ||
|
|
7
|
+
(isObject(input) && Object.keys(input).length === 0) ||
|
|
8
|
+
(isArray(input) && (input as any[]).length === 0) ||
|
|
9
|
+
(typeof input === "string" && input.trim().length === 0)
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default function medusaError(error: any): never {
|
|
2
|
+
if (error.response) {
|
|
3
|
+
// The request was made and the server responded with a status code
|
|
4
|
+
// that falls out of the range of 2xx
|
|
5
|
+
const u = new URL(error.config.url, error.config.baseURL)
|
|
6
|
+
|
|
7
|
+
// Extracting the error message from the response data
|
|
8
|
+
const message = error.response.data.message || error.response.data
|
|
9
|
+
|
|
10
|
+
throw new Error(message.charAt(0).toUpperCase() + message.slice(1) + ".")
|
|
11
|
+
} else if (error.request) {
|
|
12
|
+
// The request was made but no response was received
|
|
13
|
+
throw new Error("No response received: " + error.request)
|
|
14
|
+
} else {
|
|
15
|
+
// Something happened in setting up the request that triggered an Error
|
|
16
|
+
throw new Error("Error setting up the request: " + error.message)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isEmpty } from "./isEmpty"
|
|
2
|
+
|
|
3
|
+
type ConvertToLocaleParams = {
|
|
4
|
+
amount: number
|
|
5
|
+
currency_code: string
|
|
6
|
+
minimumFractionDigits?: number
|
|
7
|
+
maximumFractionDigits?: number
|
|
8
|
+
locale?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const convertToLocale = ({
|
|
12
|
+
amount,
|
|
13
|
+
currency_code,
|
|
14
|
+
minimumFractionDigits,
|
|
15
|
+
maximumFractionDigits,
|
|
16
|
+
locale = "en-US",
|
|
17
|
+
}: ConvertToLocaleParams) => {
|
|
18
|
+
return currency_code && !isEmpty(currency_code)
|
|
19
|
+
? new Intl.NumberFormat(locale, {
|
|
20
|
+
style: "currency",
|
|
21
|
+
currency: currency_code,
|
|
22
|
+
minimumFractionDigits,
|
|
23
|
+
maximumFractionDigits,
|
|
24
|
+
}).format(amount)
|
|
25
|
+
: amount.toString()
|
|
26
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import Delivered from "@modules/common/icons/delivered"
|
|
2
|
+
import { HttpTypes } from "@medusajs/types"
|
|
3
|
+
import { ReactNode } from "react"
|
|
4
|
+
|
|
5
|
+
export type OrderStatusConfig = {
|
|
6
|
+
label: string
|
|
7
|
+
bgColor: string
|
|
8
|
+
textColor: string
|
|
9
|
+
iconBg: string
|
|
10
|
+
icon: ReactNode | string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const getStatusConfig = (status: string): OrderStatusConfig => {
|
|
14
|
+
const statusLower = status?.toLowerCase() || ""
|
|
15
|
+
|
|
16
|
+
if (statusLower.includes("cancel")) {
|
|
17
|
+
return {
|
|
18
|
+
label: "Cancelled",
|
|
19
|
+
bgColor: "bg-[#FFF1F2]",
|
|
20
|
+
textColor: "text-[#BE123C]",
|
|
21
|
+
iconBg: "bg-[#BE123C]",
|
|
22
|
+
icon: "×",
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
statusLower === "delivered" ||
|
|
28
|
+
statusLower === "complete" ||
|
|
29
|
+
statusLower === "completed"
|
|
30
|
+
) {
|
|
31
|
+
return {
|
|
32
|
+
label: "Delivered",
|
|
33
|
+
bgColor: "bg-[#E6F4F0]",
|
|
34
|
+
textColor: "text-[#008A5D]",
|
|
35
|
+
iconBg: "bg-[#008A5D]",
|
|
36
|
+
icon: <Delivered />,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (statusLower === "shipped" || statusLower === "partially_shipped") {
|
|
41
|
+
return {
|
|
42
|
+
label: "Out For Delivery",
|
|
43
|
+
bgColor: "bg-[#EBF5FF]",
|
|
44
|
+
textColor: "text-[#0068D2]",
|
|
45
|
+
iconBg: "bg-[#0068D2]",
|
|
46
|
+
icon: (
|
|
47
|
+
<svg
|
|
48
|
+
width="20"
|
|
49
|
+
height="20"
|
|
50
|
+
viewBox="0 0 24 24"
|
|
51
|
+
fill="none"
|
|
52
|
+
stroke="white"
|
|
53
|
+
strokeWidth="2.5"
|
|
54
|
+
strokeLinecap="round"
|
|
55
|
+
strokeLinejoin="round"
|
|
56
|
+
>
|
|
57
|
+
<rect x="1" y="3" width="15" height="13" />
|
|
58
|
+
<polygon points="16 8 20 8 23 11 23 16 16 16 16 8" />
|
|
59
|
+
<circle cx="5.5" cy="18.5" r="2.5" />
|
|
60
|
+
<circle cx="18.5" cy="18.5" r="2.5" />
|
|
61
|
+
</svg>
|
|
62
|
+
),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (statusLower === "fulfilled" || statusLower === "partially_fulfilled") {
|
|
67
|
+
return {
|
|
68
|
+
label: "Shipped",
|
|
69
|
+
bgColor: "bg-[#F3E8FF]",
|
|
70
|
+
textColor: "text-[#8B5AB1]",
|
|
71
|
+
iconBg: "bg-[#8B5AB1]",
|
|
72
|
+
icon: (
|
|
73
|
+
<svg
|
|
74
|
+
width="20"
|
|
75
|
+
height="20"
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
fill="none"
|
|
78
|
+
stroke="white"
|
|
79
|
+
strokeWidth="2.5"
|
|
80
|
+
strokeLinecap="round"
|
|
81
|
+
strokeLinejoin="round"
|
|
82
|
+
>
|
|
83
|
+
<path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" />
|
|
84
|
+
<polyline points="3.29 7 12 12 20.71 7" />
|
|
85
|
+
<line x1="12" y1="22" x2="12" y2="12" />
|
|
86
|
+
</svg>
|
|
87
|
+
),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (statusLower === "returned" || statusLower === "partially_returned") {
|
|
92
|
+
return {
|
|
93
|
+
label: statusLower === "returned" ? "Returned" : "Partially Returned",
|
|
94
|
+
bgColor: "bg-[#F0F9FF]",
|
|
95
|
+
textColor: "text-[#0284C7]",
|
|
96
|
+
iconBg: "bg-[#0284C7]",
|
|
97
|
+
icon: (
|
|
98
|
+
<svg
|
|
99
|
+
width="20"
|
|
100
|
+
height="20"
|
|
101
|
+
viewBox="0 0 24 24"
|
|
102
|
+
fill="none"
|
|
103
|
+
stroke="white"
|
|
104
|
+
strokeWidth="2.5"
|
|
105
|
+
strokeLinecap="round"
|
|
106
|
+
strokeLinejoin="round"
|
|
107
|
+
>
|
|
108
|
+
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
|
|
109
|
+
<path d="M21 3v5h-5" />
|
|
110
|
+
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
|
|
111
|
+
<path d="M3 21v-5h5" />
|
|
112
|
+
</svg>
|
|
113
|
+
),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (statusLower === "return_requested") {
|
|
118
|
+
return {
|
|
119
|
+
label: "Return Requested",
|
|
120
|
+
bgColor: "bg-[#FEFCE8]",
|
|
121
|
+
textColor: "text-[#D97706]",
|
|
122
|
+
iconBg: "bg-[#CA8A04]",
|
|
123
|
+
icon: (
|
|
124
|
+
<svg
|
|
125
|
+
width="20"
|
|
126
|
+
height="20"
|
|
127
|
+
viewBox="0 0 24 24"
|
|
128
|
+
fill="none"
|
|
129
|
+
stroke="white"
|
|
130
|
+
strokeWidth="2.5"
|
|
131
|
+
strokeLinecap="round"
|
|
132
|
+
strokeLinejoin="round"
|
|
133
|
+
>
|
|
134
|
+
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
|
|
135
|
+
<polyline points="22,6 12,13 2,6" />
|
|
136
|
+
<path d="M12 13v5" />
|
|
137
|
+
</svg>
|
|
138
|
+
),
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
label: "Order Confirmed",
|
|
144
|
+
bgColor: "bg-[#F0FDF4]",
|
|
145
|
+
textColor: "text-[#16A34A]",
|
|
146
|
+
iconBg: "bg-[#16A34A]",
|
|
147
|
+
icon: (
|
|
148
|
+
<svg
|
|
149
|
+
width="20"
|
|
150
|
+
height="20"
|
|
151
|
+
viewBox="0 0 24 24"
|
|
152
|
+
fill="none"
|
|
153
|
+
stroke="white"
|
|
154
|
+
strokeWidth="3"
|
|
155
|
+
strokeLinecap="round"
|
|
156
|
+
strokeLinejoin="round"
|
|
157
|
+
>
|
|
158
|
+
<polyline points="20 6 9 17 4 12" />
|
|
159
|
+
</svg>
|
|
160
|
+
),
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const getOrderDisplayStatus = (order: HttpTypes.StoreOrder): string => {
|
|
165
|
+
const isCancelled = order.status === "canceled" || order.status === "cancelled"
|
|
166
|
+
|
|
167
|
+
const returns =
|
|
168
|
+
(order as HttpTypes.StoreOrder & { returns?: { status: string }[] }).returns || []
|
|
169
|
+
const isReviewedReturn = returns.some(
|
|
170
|
+
(r) => r.status === "received" || r.status === "partially_received"
|
|
171
|
+
)
|
|
172
|
+
const isRequestedReturn = returns.some((r) => r.status === "requested")
|
|
173
|
+
|
|
174
|
+
if (isCancelled) return "canceled"
|
|
175
|
+
if (isReviewedReturn) return "returned"
|
|
176
|
+
if (isRequestedReturn) return "return_requested"
|
|
177
|
+
|
|
178
|
+
return order.fulfillment_status || order.status || "pending"
|
|
179
|
+
}
|