@springmicro/cart 0.5.0 → 0.5.2

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.
@@ -1,106 +1,106 @@
1
- .card {
2
- display: flex;
3
- gap: 0.5rem;
4
- flex-direction: column;
5
- border-radius: 8px;
6
- padding: 1rem;
7
- box-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0);
8
- transition: all 0.3s 0.1s;
9
- cursor: pointer;
10
- }
11
-
12
- .card:hover {
13
- box-shadow: 0px 2px 5px 2px #dfdfdfff;
14
- transition: all 0.3s 0s;
15
- }
16
-
17
- .card-section {
18
- display: flex;
19
- gap: 8px;
20
- flex-direction: column;
21
- align-items: center;
22
- }
23
-
24
- .image-container {
25
- width: 100%;
26
- background-color: #ccc;
27
- border-radius: 4px;
28
- aspect-ratio: 1.5;
29
- }
30
-
31
- .button-section {
32
- display: flex;
33
- justify-content: space-around;
34
- flex-direction: row;
35
- flex-wrap: wrap;
36
- margin: 4px 2rem 0px;
37
- gap: 0.5rem;
38
- }
39
-
40
- .addtocartbutton {
41
- color: white;
42
- background-color: rgb(39, 138, 230);
43
- padding: 4px 16px 6px;
44
- border-radius: 8px;
45
- outline: none;
46
- transition: all 0.3s;
47
- }
48
-
49
- .addtocartbutton.red {
50
- background-color: rgb(230, 39, 39);
51
- }
52
-
53
- .addtocartbutton.disabled {
54
- background-color: rgb(170, 170, 170);
55
- cursor: not-allowed;
56
- }
57
- .addtocartbutton.disabled:hover {
58
- background-color: rgb(170, 170, 170);
59
- }
60
-
61
- .addtocartbutton:hover {
62
- background-color: rgb(36, 124, 207);
63
- }
64
-
65
- .hover-button > .hover-section {
66
- display: none;
67
- position: absolute;
68
- width: 325px;
69
- margin-left: calc(-113.75px);
70
- padding: 1rem;
71
- color: black;
72
- }
73
-
74
- .hover-button:hover > .hover-section {
75
- display: flex;
76
- justify-content: center;
77
- }
78
-
79
- .hover-data {
80
- border-radius: 8px;
81
- padding: 1rem;
82
- box-shadow: 0px 2px 5px 2px rgb(173, 173, 173);
83
- background-color: white;
84
- }
85
-
86
- .hover-data > div {
87
- display: flex;
88
- flex-direction: column;
89
- background-color: rgb(203, 203, 203);
90
- gap: 1px;
91
- }
92
-
93
- .tieredpricingbutton {
94
- display: flex;
95
- flex-direction: column;
96
- justify-content: flex-start;
97
- align-items: flex-start;
98
- text-align: left;
99
- padding: 4px 0.5rem 8px;
100
- background-color: white;
101
- transition: all 0.3s;
102
- }
103
-
104
- .tieredpricingbutton:hover {
105
- background-color: rgb(233, 233, 233);
106
- }
1
+ .card {
2
+ display: flex;
3
+ gap: 0.5rem;
4
+ flex-direction: column;
5
+ border-radius: 8px;
6
+ padding: 1rem;
7
+ box-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0);
8
+ transition: all 0.3s 0.1s;
9
+ cursor: pointer;
10
+ }
11
+
12
+ .card:hover {
13
+ box-shadow: 0px 2px 5px 2px #dfdfdfff;
14
+ transition: all 0.3s 0s;
15
+ }
16
+
17
+ .card-section {
18
+ display: flex;
19
+ gap: 8px;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ }
23
+
24
+ .image-container {
25
+ width: 100%;
26
+ background-color: #ccc;
27
+ border-radius: 4px;
28
+ aspect-ratio: 1.5;
29
+ }
30
+
31
+ .button-section {
32
+ display: flex;
33
+ justify-content: space-around;
34
+ flex-direction: row;
35
+ flex-wrap: wrap;
36
+ margin: 4px 2rem 0px;
37
+ gap: 0.5rem;
38
+ }
39
+
40
+ .addtocartbutton {
41
+ color: white;
42
+ background-color: rgb(39, 138, 230);
43
+ padding: 4px 16px 6px;
44
+ border-radius: 8px;
45
+ outline: none;
46
+ transition: all 0.3s;
47
+ }
48
+
49
+ .addtocartbutton.red {
50
+ background-color: rgb(230, 39, 39);
51
+ }
52
+
53
+ .addtocartbutton.disabled {
54
+ background-color: rgb(170, 170, 170);
55
+ cursor: not-allowed;
56
+ }
57
+ .addtocartbutton.disabled:hover {
58
+ background-color: rgb(170, 170, 170);
59
+ }
60
+
61
+ .addtocartbutton:hover {
62
+ background-color: rgb(36, 124, 207);
63
+ }
64
+
65
+ .hover-button > .hover-section {
66
+ display: none;
67
+ position: absolute;
68
+ width: 325px;
69
+ margin-left: calc(-113.75px);
70
+ padding: 1rem;
71
+ color: black;
72
+ }
73
+
74
+ .hover-button:hover > .hover-section {
75
+ display: flex;
76
+ justify-content: center;
77
+ }
78
+
79
+ .hover-data {
80
+ border-radius: 8px;
81
+ padding: 1rem;
82
+ box-shadow: 0px 2px 5px 2px rgb(173, 173, 173);
83
+ background-color: white;
84
+ }
85
+
86
+ .hover-data > div {
87
+ display: flex;
88
+ flex-direction: column;
89
+ background-color: rgb(203, 203, 203);
90
+ gap: 1px;
91
+ }
92
+
93
+ .tieredpricingbutton {
94
+ display: flex;
95
+ flex-direction: column;
96
+ justify-content: flex-start;
97
+ align-items: flex-start;
98
+ text-align: left;
99
+ padding: 4px 0.5rem 8px;
100
+ background-color: white;
101
+ transition: all 0.3s;
102
+ }
103
+
104
+ .tieredpricingbutton:hover {
105
+ background-color: rgb(233, 233, 233);
106
+ }
@@ -1,165 +1,165 @@
1
- import "./ProductCard.css";
2
- import { useStore } from "@nanostores/react";
3
- import type React from "react";
4
- import { Product, ProductPricing } from "./types";
5
- import { addToCart, cartStore, removeFromCart } from "./utils/storage";
6
-
7
- /**
8
- * A card meant for displaying product data, allowing for purchases with managed functions.
9
- *
10
- * By default it displays a card component from the ecommerce-example repo but you can use a different component by providing it in the properties.
11
- *
12
- * A new component should be declared using this format: ```function ExampleComponent(props: @type {ProductCardComponentProps} { return <></>; })```
13
- * Pass this new component as ```component={ExampleComponent}```
14
- */
15
-
16
- export type ProductCardComponentProps = {
17
- product: Product;
18
- foundInCart: {
19
- product: boolean;
20
- price: boolean;
21
- };
22
- pricing;
23
- addToCart: (price_index?: number) => void;
24
- removeFromCart: (price?: boolean) => void;
25
- };
26
-
27
- export default function ProductCard({
28
- product,
29
- component,
30
- priceTierName,
31
- }: {
32
- product: Product;
33
- component?: React.FC<ProductCardComponentProps>;
34
- priceTierName?: string; // Might have incomplete code for cases where the tier name doesn't match.
35
- }) {
36
- const cart = JSON.parse(useStore(cartStore));
37
-
38
- const productInCartIndex: number = (cart.items as any[]).findIndex(
39
- (p) => p.product_id === product.id
40
- );
41
- const productFoundInCart = !!~productInCartIndex;
42
-
43
- const pricing: ProductPricing[] = JSON.parse(product.pricing);
44
-
45
- const pi = pricing.findIndex((price) => price.tier_label === priceTierName); // TODO add fail-case for missing price
46
-
47
- const priceInCartIndex: number = (cart.items as any[]).findIndex(
48
- (p) => p.price_id === pricing[pi].id
49
- );
50
- const priceFoundInCart = !!~priceInCartIndex;
51
-
52
- function addToCartSafe(priceIndex: number = pi) {
53
- // Removes the ability to add improper info from the addToCart
54
- addToCart({
55
- product_id: product.id,
56
- name: product.name,
57
- price_id: pricing[priceIndex].id,
58
- image: undefined,
59
- quantity: undefined,
60
- });
61
- }
62
-
63
- const props: ProductCardComponentProps = {
64
- product,
65
- foundInCart: {
66
- product: productFoundInCart,
67
- price: priceFoundInCart,
68
- },
69
- pricing,
70
- addToCart: addToCartSafe,
71
- removeFromCart: (price?: boolean) => {
72
- removeFromCart(price ? priceInCartIndex : productInCartIndex);
73
- },
74
- };
75
-
76
- if (component !== undefined) {
77
- return component!(props);
78
- }
79
-
80
- return <DefaultProductCard {...props} />;
81
- }
82
-
83
- function DefaultProductCard({
84
- product,
85
- foundInCart,
86
- pricing,
87
- addToCart,
88
- removeFromCart,
89
- }: ProductCardComponentProps) {
90
- return (
91
- <div
92
- className="card"
93
- onClick={(e) =>
94
- // @ts-expect-error it works
95
- !e.target.closest("button") &&
96
- (window.location.href = `/product/${product.id}`)
97
- }
98
- >
99
- <div className="card-section">
100
- <b>{product.name}</b>
101
- <div className="image-container"></div>
102
- </div>
103
- {product.description && (
104
- <p style={{ margin: "0px 1rem", flexGrow: 1 }}>{product.description}</p>
105
- )}
106
- <div className="button-section">
107
- {foundInCart.product ? (
108
- <button
109
- className="addtocartbutton"
110
- onClick={() => {
111
- removeFromCart();
112
- }}
113
- >
114
- Remove from cart
115
- </button>
116
- ) : pricing.length === 1 ? (
117
- <button
118
- className="addtocartbutton"
119
- onClick={() => {
120
- addToCart();
121
- }}
122
- >
123
- Add {formatPricing(pricing[0])}
124
- </button>
125
- ) : pricing.length === 0 ? (
126
- <button className="addtocartbutton disabled">
127
- Currently unavailable
128
- </button>
129
- ) : (
130
- <button className="addtocartbutton hover-button">
131
- Add to cart ▼
132
- <div className="hover-section">
133
- <div className="hover-data">
134
- <div>
135
- {pricing.map((p, i) => (
136
- <button
137
- className="tieredpricingbutton"
138
- onClick={() => {
139
- addToCart(i);
140
- }}
141
- >
142
- Tier {i + 1}:
143
- <br />
144
- {formatPricing(p)}
145
- </button>
146
- ))}
147
- </div>
148
- </div>
149
- </div>
150
- </button>
151
- )}
152
- </div>
153
- </div>
154
- );
155
- }
156
-
157
- function formatPricing({ unit_amount, recurring }: any) {
158
- if (recurring) {
159
- const { interval, interval_count } = recurring;
160
- return `$${(unit_amount / 100).toFixed(2)}/${
161
- interval_count === 1 ? interval : `${interval_count} ${interval}s`
162
- }`;
163
- }
164
- return `$${(unit_amount / 100).toFixed(2)}`;
165
- }
1
+ import "./ProductCard.css";
2
+ import { useStore } from "@nanostores/react";
3
+ import type React from "react";
4
+ import { Product, ProductPricing } from "./types";
5
+ import { addToCart, cartStore, removeFromCart } from "./utils/storage";
6
+
7
+ /**
8
+ * A card meant for displaying product data, allowing for purchases with managed functions.
9
+ *
10
+ * By default it displays a card component from the ecommerce-example repo but you can use a different component by providing it in the properties.
11
+ *
12
+ * A new component should be declared using this format: ```function ExampleComponent(props: @type {ProductCardComponentProps} { return <></>; })```
13
+ * Pass this new component as ```component={ExampleComponent}```
14
+ */
15
+
16
+ export type ProductCardComponentProps = {
17
+ product: Product;
18
+ foundInCart: {
19
+ product: boolean;
20
+ price: boolean;
21
+ };
22
+ pricing;
23
+ addToCart: (price_index?: number) => void;
24
+ removeFromCart: (price?: boolean) => void;
25
+ };
26
+
27
+ export default function ProductCard({
28
+ product,
29
+ component,
30
+ priceTierName,
31
+ }: {
32
+ product: Product;
33
+ component?: React.FC<ProductCardComponentProps>;
34
+ priceTierName?: string; // Might have incomplete code for cases where the tier name doesn't match.
35
+ }) {
36
+ const cart = JSON.parse(useStore(cartStore));
37
+
38
+ const productInCartIndex: number = (cart.items as any[]).findIndex(
39
+ (p) => p.product_id === product.id
40
+ );
41
+ const productFoundInCart = !!~productInCartIndex;
42
+
43
+ const pricing: ProductPricing[] = JSON.parse(product.pricing);
44
+
45
+ const pi = pricing.findIndex((price) => price.tier_label === priceTierName); // TODO add fail-case for missing price
46
+
47
+ const priceInCartIndex: number = (cart.items as any[]).findIndex(
48
+ (p) => p.price_id === pricing[pi].id
49
+ );
50
+ const priceFoundInCart = !!~priceInCartIndex;
51
+
52
+ function addToCartSafe(priceIndex: number = pi) {
53
+ // Removes the ability to add improper info from the addToCart
54
+ addToCart({
55
+ product_id: product.id,
56
+ name: product.name,
57
+ price_id: pricing[priceIndex].id,
58
+ image: undefined,
59
+ quantity: undefined,
60
+ });
61
+ }
62
+
63
+ const props: ProductCardComponentProps = {
64
+ product,
65
+ foundInCart: {
66
+ product: productFoundInCart,
67
+ price: priceFoundInCart,
68
+ },
69
+ pricing,
70
+ addToCart: addToCartSafe,
71
+ removeFromCart: (price?: boolean) => {
72
+ removeFromCart(price ? priceInCartIndex : productInCartIndex);
73
+ },
74
+ };
75
+
76
+ if (component !== undefined) {
77
+ return component!(props);
78
+ }
79
+
80
+ return <DefaultProductCard {...props} />;
81
+ }
82
+
83
+ function DefaultProductCard({
84
+ product,
85
+ foundInCart,
86
+ pricing,
87
+ addToCart,
88
+ removeFromCart,
89
+ }: ProductCardComponentProps) {
90
+ return (
91
+ <div
92
+ className="card"
93
+ onClick={(e) =>
94
+ // @ts-expect-error it works
95
+ !e.target.closest("button") &&
96
+ (window.location.href = `/product/${product.id}`)
97
+ }
98
+ >
99
+ <div className="card-section">
100
+ <b>{product.name}</b>
101
+ <div className="image-container"></div>
102
+ </div>
103
+ {product.description && (
104
+ <p style={{ margin: "0px 1rem", flexGrow: 1 }}>{product.description}</p>
105
+ )}
106
+ <div className="button-section">
107
+ {foundInCart.product ? (
108
+ <button
109
+ className="addtocartbutton"
110
+ onClick={() => {
111
+ removeFromCart();
112
+ }}
113
+ >
114
+ Remove from cart
115
+ </button>
116
+ ) : pricing.length === 1 ? (
117
+ <button
118
+ className="addtocartbutton"
119
+ onClick={() => {
120
+ addToCart();
121
+ }}
122
+ >
123
+ Add {formatPricing(pricing[0])}
124
+ </button>
125
+ ) : pricing.length === 0 ? (
126
+ <button className="addtocartbutton disabled">
127
+ Currently unavailable
128
+ </button>
129
+ ) : (
130
+ <button className="addtocartbutton hover-button">
131
+ Add to cart ▼
132
+ <div className="hover-section">
133
+ <div className="hover-data">
134
+ <div>
135
+ {pricing.map((p, i) => (
136
+ <button
137
+ className="tieredpricingbutton"
138
+ onClick={() => {
139
+ addToCart(i);
140
+ }}
141
+ >
142
+ Tier {i + 1}:
143
+ <br />
144
+ {formatPricing(p)}
145
+ </button>
146
+ ))}
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </button>
151
+ )}
152
+ </div>
153
+ </div>
154
+ );
155
+ }
156
+
157
+ function formatPricing({ unit_amount, recurring }: any) {
158
+ if (recurring) {
159
+ const { interval, interval_count } = recurring;
160
+ return `$${(unit_amount / 100).toFixed(2)}/${
161
+ interval_count === 1 ? interval : `${interval_count} ${interval}s`
162
+ }`;
163
+ }
164
+ return `$${(unit_amount / 100).toFixed(2)}`;
165
+ }