@pradip1995/segment-powerhouse-deals 0.1.2 → 0.1.4
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/package.json +2 -1
- package/src/deal-card.tsx +58 -19
- package/src/deals.css +56 -1
- package/src/segment.tsx +2 -0
- package/src/wishlist-button.tsx +93 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pradip1995/segment-powerhouse-deals",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@pradip1995/commerce-core": "^4.0.0",
|
|
27
27
|
"@pradip1995/medusa-connector": "^0.2.0",
|
|
28
|
+
"@pradip1995/segment-login-popup": "^0.1.2",
|
|
28
29
|
"@pradip1995/segment-primitives": "^0.4.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
package/src/deal-card.tsx
CHANGED
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from "react"
|
|
4
4
|
import { useRouter } from "next/navigation"
|
|
5
|
-
import { addToCart } from "@pradip1995/commerce-core/client/actions/cart"
|
|
5
|
+
import { addToCart, buyNow } from "@pradip1995/commerce-core/client/actions/cart"
|
|
6
6
|
import LocalizedLink from "@pradip1995/segment-primitives/localized-link"
|
|
7
7
|
import { formatPrice } from "@pradip1995/segment-primitives/format-price"
|
|
8
8
|
import { isNextRedirect } from "@pradip1995/segment-primitives/is-next-redirect"
|
|
9
9
|
import type { HttpTypes } from "@medusajs/types"
|
|
10
|
+
import WishlistButton from "./wishlist-button"
|
|
10
11
|
|
|
11
12
|
type Props = {
|
|
12
13
|
product: HttpTypes.StoreProduct
|
|
13
14
|
region?: HttpTypes.StoreRegion
|
|
14
15
|
countryCode: string
|
|
16
|
+
wishlistProductIds?: string[]
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
export default function DealCard({ product, region, countryCode }: Props) {
|
|
19
|
+
export default function DealCard({ product, region, countryCode, wishlistProductIds }: Props) {
|
|
18
20
|
const router = useRouter()
|
|
19
21
|
const [adding, setAdding] = useState(false)
|
|
22
|
+
const [buying, setBuying] = useState(false)
|
|
20
23
|
const variant = product.variants?.[0]
|
|
21
24
|
const price = variant?.calculated_price?.calculated_amount
|
|
22
25
|
const originalPrice = variant?.calculated_price?.original_amount
|
|
@@ -30,17 +33,19 @@ export default function DealCard({ product, region, countryCode }: Props) {
|
|
|
30
33
|
(inventory ?? 0) > 0
|
|
31
34
|
const href = `/products/${product.handle}`
|
|
32
35
|
const hasOptions = (product.variants?.length ?? 0) > 1
|
|
36
|
+
const busy = adding || buying
|
|
37
|
+
const code = countryCode.toLowerCase()
|
|
33
38
|
|
|
34
39
|
const handleAdd = async (event: React.MouseEvent) => {
|
|
35
40
|
event.preventDefault()
|
|
36
41
|
event.stopPropagation()
|
|
37
|
-
if (!variant?.id ||
|
|
42
|
+
if (!variant?.id || busy || !inStock || hasOptions) return
|
|
38
43
|
setAdding(true)
|
|
39
44
|
try {
|
|
40
45
|
await addToCart({
|
|
41
46
|
variantId: variant.id,
|
|
42
47
|
quantity: 1,
|
|
43
|
-
countryCode:
|
|
48
|
+
countryCode: code,
|
|
44
49
|
})
|
|
45
50
|
window.dispatchEvent(new CustomEvent("cart:item-added", { detail: { message: "Added to cart" } }))
|
|
46
51
|
router.refresh()
|
|
@@ -53,19 +58,46 @@ export default function DealCard({ product, region, countryCode }: Props) {
|
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
const handleBuyNow = async (event: React.MouseEvent) => {
|
|
62
|
+
event.preventDefault()
|
|
63
|
+
event.stopPropagation()
|
|
64
|
+
if (!variant?.id || busy || !inStock) return
|
|
65
|
+
setBuying(true)
|
|
66
|
+
try {
|
|
67
|
+
await buyNow({
|
|
68
|
+
variantId: variant.id,
|
|
69
|
+
quantity: 1,
|
|
70
|
+
countryCode: code,
|
|
71
|
+
})
|
|
72
|
+
router.refresh()
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (isNextRedirect(err)) throw err
|
|
75
|
+
const message = err instanceof Error ? err.message : "Buy now failed"
|
|
76
|
+
window.dispatchEvent(new CustomEvent("cart:item-added", { detail: { message, variant: "error" } }))
|
|
77
|
+
setBuying(false)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
56
81
|
return (
|
|
57
82
|
<article className={`powerhouse-deals__card${onSale ? " is-sale" : ""}`}>
|
|
58
|
-
<
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
<div className="powerhouse-deals__media-wrap">
|
|
84
|
+
<LocalizedLink href={href} countryCode={countryCode} className="powerhouse-deals__card-media">
|
|
85
|
+
{product.thumbnail?.trim() ? (
|
|
86
|
+
<img src={product.thumbnail.trim()} alt={product.title || ""} className="powerhouse-deals__card-image" />
|
|
87
|
+
) : (
|
|
88
|
+
<div className="powerhouse-deals__card-placeholder" />
|
|
89
|
+
)}
|
|
90
|
+
{onSale && saved != null ? (
|
|
91
|
+
<span className="powerhouse-deals__badge">Save {formatPrice(saved, currency)}</span>
|
|
92
|
+
) : null}
|
|
93
|
+
{!inStock ? <span className="powerhouse-deals__sold">Sold out</span> : null}
|
|
94
|
+
</LocalizedLink>
|
|
95
|
+
<WishlistButton
|
|
96
|
+
productId={product.id}
|
|
97
|
+
countryCode={countryCode}
|
|
98
|
+
wishlistProductIds={wishlistProductIds}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
69
101
|
|
|
70
102
|
<div className="powerhouse-deals__card-info">
|
|
71
103
|
{price != null ? (
|
|
@@ -92,9 +124,16 @@ export default function DealCard({ product, region, countryCode }: Props) {
|
|
|
92
124
|
)}
|
|
93
125
|
|
|
94
126
|
<div className="powerhouse-deals__actions">
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
127
|
+
{inStock ? (
|
|
128
|
+
<button
|
|
129
|
+
type="button"
|
|
130
|
+
className="powerhouse-deals__quickshop"
|
|
131
|
+
onClick={handleBuyNow}
|
|
132
|
+
disabled={busy || !variant?.id}
|
|
133
|
+
>
|
|
134
|
+
{buying ? "Processing…" : "Buy now"}
|
|
135
|
+
</button>
|
|
136
|
+
) : null}
|
|
98
137
|
{hasOptions ? (
|
|
99
138
|
<LocalizedLink href={href} countryCode={countryCode} className="powerhouse-deals__atc">
|
|
100
139
|
Choose options
|
|
@@ -104,7 +143,7 @@ export default function DealCard({ product, region, countryCode }: Props) {
|
|
|
104
143
|
type="button"
|
|
105
144
|
className="powerhouse-deals__atc"
|
|
106
145
|
onClick={handleAdd}
|
|
107
|
-
disabled={
|
|
146
|
+
disabled={busy || !variant?.id || !inStock}
|
|
108
147
|
>
|
|
109
148
|
{adding ? "Adding…" : !inStock ? "Sold out" : "Add to cart"}
|
|
110
149
|
</button>
|
package/src/deals.css
CHANGED
|
@@ -133,6 +133,10 @@
|
|
|
133
133
|
background: #fff;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
.powerhouse-deals__media-wrap {
|
|
137
|
+
position: relative;
|
|
138
|
+
}
|
|
139
|
+
|
|
136
140
|
.powerhouse-deals__card-media {
|
|
137
141
|
position: relative;
|
|
138
142
|
display: block;
|
|
@@ -141,6 +145,51 @@
|
|
|
141
145
|
background: #f3f3f3;
|
|
142
146
|
}
|
|
143
147
|
|
|
148
|
+
.powerhouse-deals__wishlist {
|
|
149
|
+
position: absolute;
|
|
150
|
+
top: 0.5rem;
|
|
151
|
+
right: 0.5rem;
|
|
152
|
+
z-index: 2;
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: center;
|
|
156
|
+
width: 2.25rem;
|
|
157
|
+
height: 2.25rem;
|
|
158
|
+
padding: 0;
|
|
159
|
+
border: 1px solid rgba(29, 29, 29, 0.12);
|
|
160
|
+
background: rgba(255, 255, 255, 0.94);
|
|
161
|
+
color: #1d1d1d;
|
|
162
|
+
cursor: pointer;
|
|
163
|
+
transition: color 0.2s ease, border-color 0.2s ease;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.powerhouse-deals__wishlist:hover {
|
|
167
|
+
border-color: #1d1d1d;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.powerhouse-deals__wishlist.is-active {
|
|
171
|
+
color: #c1272d;
|
|
172
|
+
border-color: #c1272d;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.powerhouse-deals__wishlist.is-loading {
|
|
176
|
+
opacity: 0.55;
|
|
177
|
+
cursor: wait;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.powerhouse-deals__wishlist-icon {
|
|
181
|
+
width: 1.05rem;
|
|
182
|
+
height: 1.05rem;
|
|
183
|
+
fill: none;
|
|
184
|
+
stroke: currentColor;
|
|
185
|
+
stroke-width: 1.75;
|
|
186
|
+
stroke-linejoin: round;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.powerhouse-deals__wishlist.is-active .powerhouse-deals__wishlist-icon {
|
|
190
|
+
fill: currentColor;
|
|
191
|
+
}
|
|
192
|
+
|
|
144
193
|
.powerhouse-deals__card-image,
|
|
145
194
|
.powerhouse-deals__card-placeholder {
|
|
146
195
|
width: 100%;
|
|
@@ -275,9 +324,15 @@
|
|
|
275
324
|
background: #f1eeae;
|
|
276
325
|
border: 1px solid #f2f682;
|
|
277
326
|
color: #1d1d1d;
|
|
327
|
+
cursor: pointer;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.powerhouse-deals__quickshop:disabled {
|
|
331
|
+
opacity: 0.55;
|
|
332
|
+
cursor: not-allowed;
|
|
278
333
|
}
|
|
279
334
|
|
|
280
|
-
.powerhouse-deals__quickshop:hover {
|
|
335
|
+
.powerhouse-deals__quickshop:hover:not(:disabled) {
|
|
281
336
|
background: #f4f1be;
|
|
282
337
|
}
|
|
283
338
|
|
package/src/segment.tsx
CHANGED
|
@@ -24,6 +24,7 @@ export default async function PowerhouseDeals({
|
|
|
24
24
|
promoLink = DEFAULT_PROMO_LINK,
|
|
25
25
|
promoImage = DEFAULT_PROMO_IMAGE,
|
|
26
26
|
emptyMessage = DEFAULT_EMPTY,
|
|
27
|
+
wishlistProductIds,
|
|
27
28
|
}: PowerhouseDealsProps) {
|
|
28
29
|
const countryCode =
|
|
29
30
|
countryCodeProp?.toLowerCase() ||
|
|
@@ -95,6 +96,7 @@ export default async function PowerhouseDeals({
|
|
|
95
96
|
product={product}
|
|
96
97
|
region={displayRegion ?? undefined}
|
|
97
98
|
countryCode={countryCode}
|
|
99
|
+
wishlistProductIds={wishlistProductIds}
|
|
98
100
|
/>
|
|
99
101
|
))
|
|
100
102
|
)}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, type MouseEvent } from "react"
|
|
4
|
+
import { useRouter } from "next/navigation"
|
|
5
|
+
import {
|
|
6
|
+
addToWishlist,
|
|
7
|
+
getWishlistProductIds,
|
|
8
|
+
removeFromWishlist,
|
|
9
|
+
} from "@pradip1995/commerce-core/client/actions/wishlist"
|
|
10
|
+
import LoginRequiredModal from "@pradip1995/segment-login-popup/required"
|
|
11
|
+
|
|
12
|
+
type WishlistButtonProps = {
|
|
13
|
+
productId?: string | null
|
|
14
|
+
countryCode: string
|
|
15
|
+
wishlistProductIds?: string[]
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function WishlistButton({
|
|
20
|
+
productId,
|
|
21
|
+
countryCode,
|
|
22
|
+
wishlistProductIds,
|
|
23
|
+
className = "powerhouse-deals__wishlist",
|
|
24
|
+
}: WishlistButtonProps) {
|
|
25
|
+
const router = useRouter()
|
|
26
|
+
const [wishlisted, setWishlisted] = useState(false)
|
|
27
|
+
const [loading, setLoading] = useState(false)
|
|
28
|
+
const [loginRequired, setLoginRequired] = useState(false)
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!productId || wishlistProductIds === undefined) return
|
|
32
|
+
setWishlisted(wishlistProductIds.includes(productId))
|
|
33
|
+
}, [productId, wishlistProductIds])
|
|
34
|
+
|
|
35
|
+
async function handleToggle(event: MouseEvent) {
|
|
36
|
+
event.preventDefault()
|
|
37
|
+
event.stopPropagation()
|
|
38
|
+
if (!productId || loading) return
|
|
39
|
+
|
|
40
|
+
setLoading(true)
|
|
41
|
+
try {
|
|
42
|
+
let currentlyWishlisted = wishlisted
|
|
43
|
+
if (wishlistProductIds === undefined) {
|
|
44
|
+
try {
|
|
45
|
+
const ids = await getWishlistProductIds()
|
|
46
|
+
currentlyWishlisted = ids.includes(productId)
|
|
47
|
+
setWishlisted(currentlyWishlisted)
|
|
48
|
+
} catch {
|
|
49
|
+
currentlyWishlisted = false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const result = currentlyWishlisted
|
|
54
|
+
? await removeFromWishlist(productId)
|
|
55
|
+
: await addToWishlist(productId)
|
|
56
|
+
|
|
57
|
+
if (result.success) {
|
|
58
|
+
setWishlisted(!currentlyWishlisted)
|
|
59
|
+
router.refresh()
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (result.error?.toLowerCase().includes("login")) {
|
|
64
|
+
setLoginRequired(true)
|
|
65
|
+
}
|
|
66
|
+
} finally {
|
|
67
|
+
setLoading(false)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
className={`${className}${wishlisted ? " is-active" : ""}${loading ? " is-loading" : ""}`}
|
|
76
|
+
onClick={handleToggle}
|
|
77
|
+
disabled={loading || !productId}
|
|
78
|
+
aria-label={wishlisted ? "Remove from wishlist" : "Add to wishlist"}
|
|
79
|
+
aria-pressed={wishlisted}
|
|
80
|
+
>
|
|
81
|
+
<svg viewBox="0 0 24 24" aria-hidden className={`${className}-icon`}>
|
|
82
|
+
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
|
|
83
|
+
</svg>
|
|
84
|
+
</button>
|
|
85
|
+
<LoginRequiredModal
|
|
86
|
+
isOpen={loginRequired}
|
|
87
|
+
onClose={() => setLoginRequired(false)}
|
|
88
|
+
countryCode={countryCode}
|
|
89
|
+
message="Please log in to save items to your wishlist."
|
|
90
|
+
/>
|
|
91
|
+
</>
|
|
92
|
+
)
|
|
93
|
+
}
|