@pradip1995/segment-powerhouse-shop 0.1.1
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 +40 -0
- package/src/index.ts +2 -0
- package/src/manifest.ts +10 -0
- package/src/product-card.tsx +132 -0
- package/src/segment.tsx +1 -0
- package/src/shop.css +670 -0
- package/src/shop.tsx +537 -0
- package/src/types.ts +41 -0
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pradip1995/segment-powerhouse-shop",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"./src/shop.css"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": "./src/index.ts",
|
|
16
|
+
"./manifest": "./src/manifest.ts"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@medusajs/types": "^2.0.0",
|
|
20
|
+
"@pradip1995/plugin-sdk": "^0.2.0",
|
|
21
|
+
"next": ">=15",
|
|
22
|
+
"react": ">=19",
|
|
23
|
+
"react-dom": ">=19"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@pradip1995/commerce-core": "^4.0.0",
|
|
27
|
+
"@pradip1995/segment-primitives": "^0.4.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@medusajs/types": "^2.0.0",
|
|
31
|
+
"@pradip1995/plugin-sdk": "^0.2.0",
|
|
32
|
+
"@types/react": "^19",
|
|
33
|
+
"react": "19.0.3",
|
|
34
|
+
"typescript": "^5.7.2"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"lint": "tsc --noEmit"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/index.ts
ADDED
package/src/manifest.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, type MouseEvent } from "react"
|
|
4
|
+
import { useRouter } from "next/navigation"
|
|
5
|
+
import { addToCart } from "@pradip1995/commerce-core/client/actions/cart"
|
|
6
|
+
import LocalizedLink from "@pradip1995/segment-primitives/localized-link"
|
|
7
|
+
import { formatPrice } from "@pradip1995/segment-primitives/format-price"
|
|
8
|
+
import { isNextRedirect } from "@pradip1995/segment-primitives/is-next-redirect"
|
|
9
|
+
import type { HttpTypes } from "@medusajs/types"
|
|
10
|
+
|
|
11
|
+
function stripHtml(value?: string | null) {
|
|
12
|
+
if (!value) return ""
|
|
13
|
+
return value.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function snippet(value?: string | null, length = 140) {
|
|
17
|
+
const text = stripHtml(value)
|
|
18
|
+
if (text.length <= length) return text
|
|
19
|
+
return `${text.slice(0, length).trim()}...`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function ShopProductCard({
|
|
23
|
+
product,
|
|
24
|
+
countryCode,
|
|
25
|
+
}: {
|
|
26
|
+
product: HttpTypes.StoreProduct
|
|
27
|
+
countryCode: string
|
|
28
|
+
}) {
|
|
29
|
+
const router = useRouter()
|
|
30
|
+
const [adding, setAdding] = useState(false)
|
|
31
|
+
const variants = product.variants ?? []
|
|
32
|
+
const variant = variants[0]
|
|
33
|
+
const price = variant?.calculated_price?.calculated_amount
|
|
34
|
+
const originalPrice = variant?.calculated_price?.original_amount
|
|
35
|
+
const currency = variant?.calculated_price?.currency_code || "inr"
|
|
36
|
+
const onSale = originalPrice != null && price != null && originalPrice > price
|
|
37
|
+
const saved = onSale && originalPrice != null && price != null ? originalPrice - price : null
|
|
38
|
+
const inventory = variant?.inventory_quantity
|
|
39
|
+
const inStock =
|
|
40
|
+
variant?.manage_inventory === false ||
|
|
41
|
+
variant?.allow_backorder === true ||
|
|
42
|
+
(inventory ?? 0) > 0
|
|
43
|
+
const lowStock = inventory != null && inventory > 0 && inventory <= 5
|
|
44
|
+
const href = `/products/${product.handle}`
|
|
45
|
+
const hasOptions = variants.length > 1
|
|
46
|
+
const copy = snippet(product.description)
|
|
47
|
+
|
|
48
|
+
const handleAdd = async (event: MouseEvent) => {
|
|
49
|
+
event.preventDefault()
|
|
50
|
+
event.stopPropagation()
|
|
51
|
+
if (!variant?.id || adding || !inStock || hasOptions) return
|
|
52
|
+
setAdding(true)
|
|
53
|
+
try {
|
|
54
|
+
await addToCart({
|
|
55
|
+
variantId: variant.id,
|
|
56
|
+
quantity: 1,
|
|
57
|
+
countryCode,
|
|
58
|
+
})
|
|
59
|
+
window.dispatchEvent(new CustomEvent("cart:item-added", { detail: { message: "Added to cart" } }))
|
|
60
|
+
router.refresh()
|
|
61
|
+
} catch (err) {
|
|
62
|
+
if (isNextRedirect(err)) throw err
|
|
63
|
+
const message = err instanceof Error ? err.message : "Could not add to cart"
|
|
64
|
+
window.dispatchEvent(new CustomEvent("cart:item-added", { detail: { message, variant: "error" } }))
|
|
65
|
+
} finally {
|
|
66
|
+
setAdding(false)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<article className={`powerhouse-shop__card${onSale ? " is-sale" : ""}`}>
|
|
72
|
+
<LocalizedLink href={href} countryCode={countryCode} className="powerhouse-shop__media" tabIndex={-1}>
|
|
73
|
+
{product.thumbnail?.trim() ? (
|
|
74
|
+
<img src={product.thumbnail.trim()} alt={product.title || ""} />
|
|
75
|
+
) : (
|
|
76
|
+
<div className="powerhouse-shop__placeholder" />
|
|
77
|
+
)}
|
|
78
|
+
{onSale && saved != null ? (
|
|
79
|
+
<span className="powerhouse-shop__badge">Save {formatPrice(saved, currency)}</span>
|
|
80
|
+
) : null}
|
|
81
|
+
{!inStock ? <span className="powerhouse-shop__sold">Sold out</span> : null}
|
|
82
|
+
</LocalizedLink>
|
|
83
|
+
|
|
84
|
+
<div className="powerhouse-shop__info">
|
|
85
|
+
<h2 className="powerhouse-shop__name">
|
|
86
|
+
<LocalizedLink href={href} countryCode={countryCode}>
|
|
87
|
+
{product.title}
|
|
88
|
+
</LocalizedLink>
|
|
89
|
+
</h2>
|
|
90
|
+
|
|
91
|
+
{lowStock ? (
|
|
92
|
+
<p className="powerhouse-shop__stock">Only {inventory} left!</p>
|
|
93
|
+
) : inStock ? (
|
|
94
|
+
<p className="powerhouse-shop__stock is-ok">in stock</p>
|
|
95
|
+
) : (
|
|
96
|
+
<p className="powerhouse-shop__stock is-out">Out of stock</p>
|
|
97
|
+
)}
|
|
98
|
+
|
|
99
|
+
{copy ? <p className="powerhouse-shop__excerpt">{copy}</p> : null}
|
|
100
|
+
|
|
101
|
+
{price != null ? (
|
|
102
|
+
<div className="powerhouse-shop__price">
|
|
103
|
+
{onSale && originalPrice != null ? (
|
|
104
|
+
<span className="powerhouse-shop__compare">{formatPrice(originalPrice, currency)}</span>
|
|
105
|
+
) : null}
|
|
106
|
+
<span className="powerhouse-shop__current">{formatPrice(price, currency)}</span>
|
|
107
|
+
</div>
|
|
108
|
+
) : null}
|
|
109
|
+
|
|
110
|
+
<div className="powerhouse-shop__actions">
|
|
111
|
+
<LocalizedLink href={href} countryCode={countryCode} className="powerhouse-shop__quickshop">
|
|
112
|
+
Quick shop
|
|
113
|
+
</LocalizedLink>
|
|
114
|
+
{hasOptions ? (
|
|
115
|
+
<LocalizedLink href={href} countryCode={countryCode} className="powerhouse-shop__atc">
|
|
116
|
+
Choose options
|
|
117
|
+
</LocalizedLink>
|
|
118
|
+
) : (
|
|
119
|
+
<button
|
|
120
|
+
type="button"
|
|
121
|
+
className="powerhouse-shop__atc"
|
|
122
|
+
onClick={handleAdd}
|
|
123
|
+
disabled={adding || !variant?.id || !inStock}
|
|
124
|
+
>
|
|
125
|
+
{adding ? "Adding…" : !inStock ? "Sold out" : "Add to cart"}
|
|
126
|
+
</button>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</article>
|
|
131
|
+
)
|
|
132
|
+
}
|
package/src/segment.tsx
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./shop"
|