Skip to content

Commit 0c8c25c

Browse files
committed
examples: Add Shopping Cart example
1 parent d1aad0d commit 0c8c25c

File tree

10 files changed

+258
-25
lines changed

10 files changed

+258
-25
lines changed

dist/assets/index-BoApkmJG.js renamed to dist/assets/index-BGLEp_jH.js

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assets/index-CO8DVATt.css renamed to dist/assets/index-BL40SdZj.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>REACT USER ANALYTICS</title>
8-
<script type="module" crossorigin src="./assets/index-BoApkmJG.js"></script>
9-
<link rel="stylesheet" crossorigin href="./assets/index-CO8DVATt.css">
8+
<script type="module" crossorigin src="./assets/index-BGLEp_jH.js"></script>
9+
<link rel="stylesheet" crossorigin href="./assets/index-BL40SdZj.css">
1010
</head>
1111
<body>
1212
<div id="root"></div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { default as React } from 'react';
2+
import { UserInteractionResource } from '../../../../../../../../../../../../src/index';
3+
export interface ShoppingCartProps {
4+
onEventLog: (e: React.MouseEvent<HTMLElement, MouseEvent>, interactionResource: UserInteractionResource) => void;
5+
}
6+
declare const ShoppingCart: React.FC;
7+
export default ShoppingCart;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export declare const ShoppingCartInteractionLogger: () => any;
2+
export default ShoppingCartInteractionLogger;

playground/app/routes/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ButtonInteractionLogger from '@examples/ButtonWithTracking';
77
import InputInteractionLogger from '@examples/InputWithTracking';
88
import NewsletterSignupInteractionLogger from '@examples/NewsletterSignupWithTracking';
99
import EventRSVPInteractionLogger from '@examples/EventRSVPWithTracking';
10+
import ShoppingCartInteractionLogger from '@examples/ShoppingCartWithTracking';
1011

1112
export const router = createBrowserRouter(
1213
[
@@ -45,6 +46,10 @@ export const router = createBrowserRouter(
4546
path: 'event-rsvp',
4647
element: <EventRSVPInteractionLogger />,
4748
},
49+
{
50+
path: 'shopping-cart',
51+
element: <ShoppingCartInteractionLogger />,
52+
}
4853
],
4954
},
5055
]

playground/components/pages/HomePage/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ const HomePage = () => {
2929
<li>
3030
<Link to="/examples/pages/event-rsvp">📰 Event RSVP Example</Link>
3131
</li>
32+
<li>
33+
<Link to="/examples/pages/shopping-cart">🛒 Shopping Cart Example</Link>
34+
</li>
3235
</ul>
3336
<p>
3437
<a href="docs/index.html">View API documentation</a>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.product-card {
2+
margin-bottom: 20px;
3+
}
4+
5+
.quantity-controls {
6+
display: flex;
7+
align-items: center;
8+
gap: 16px; /* increased from 10px to 16px for more breathing room */
9+
margin: 10px 0;
10+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import React, { useState } from 'react';
2+
import './index.css';
3+
4+
import Header from '@components/elements/Header';
5+
import Card from '@components/elements/Card';
6+
import Button from '@components/elements/Button';
7+
8+
import withTracking, { UserInteractionResource } from 'react-user-analytics/index';
9+
10+
const ButtonWithTracking = withTracking(Button);
11+
12+
interface Product {
13+
id: number;
14+
name: string;
15+
description: string;
16+
quantity: number;
17+
}
18+
19+
const initialProducts: Product[] = [
20+
{
21+
id: 1,
22+
name: "Infinity Mug",
23+
description: "Holds more coffee than the Time Stone holds secrets. Snap your drowsiness away.",
24+
quantity: 0
25+
},
26+
{
27+
id: 2,
28+
name: "Bat-Signal Night Light",
29+
description: "Lights up your room. Sadly doesn't summon billionaires with abs.",
30+
quantity: 0
31+
},
32+
{
33+
id: 3,
34+
name: "Los Pollos Hermanos Apron",
35+
description: "Cook like Gus. Just... stick to fried chicken. No RVs involved.",
36+
quantity: 0
37+
},
38+
{
39+
id: 4,
40+
name: "World's Best Boss Mug",
41+
description: "Just like Michael Scott's. Perfect for asserting dominance in ironic fashion.",
42+
quantity: 0
43+
},
44+
{
45+
id: 5,
46+
name: "Pearson Hardman Legal Pad",
47+
description: "For jotting down genius moves — even if you didn't go to Harvard.",
48+
quantity: 0
49+
}
50+
];
51+
52+
export interface ShoppingCartProps {
53+
onEventLog: (
54+
e: React.MouseEvent<HTMLElement, MouseEvent>,
55+
interactionResource: UserInteractionResource,
56+
) => void;
57+
}
58+
59+
const ShoppingCart: React.FC = ({onEventLog}: ShoppingCartProps) => {
60+
const [products, setProducts] = useState<Product[]>(initialProducts);
61+
62+
const handleAdd = (id: number) => {
63+
setProducts(prev =>
64+
prev.map(product =>
65+
product.id === id ? { ...product, quantity: product.quantity + 1 } : product
66+
)
67+
);
68+
};
69+
70+
const handleRemove = (id: number) => {
71+
setProducts(prev =>
72+
prev.map(product =>
73+
product.id === id && product.quantity > 0
74+
? { ...product, quantity: product.quantity - 1 }
75+
: product
76+
)
77+
);
78+
};
79+
80+
const totalItems = products.reduce((sum, p) => sum + p.quantity, 0);
81+
82+
return (
83+
<>
84+
<Header
85+
title={'Shopping Cart'}
86+
subTitle={
87+
'A sample Shopping cart example page that captures user interaction.'
88+
}
89+
></Header>
90+
<div className="split-layout">
91+
<div className="split-left">
92+
<h2 className="split-title">Add products</h2>
93+
{products.map(product => (
94+
<Card key={product.id} className="product-card">
95+
<h3>{product.name}</h3>
96+
<p>{product.description}</p>
97+
<div className="quantity-controls">
98+
<ButtonWithTracking
99+
label="−"
100+
onClick={() => handleRemove(product.id)}
101+
trackers={[
102+
{
103+
action: 'onClick',
104+
track: onEventLog,
105+
data: {
106+
event: 'PRODUCT_REMOVE',
107+
productId: product.id,
108+
},
109+
},
110+
]}
111+
/>
112+
<span className="quantity">{product.quantity}</span>
113+
<ButtonWithTracking
114+
label="+"
115+
onClick={() => handleAdd(product.id)}
116+
trackers={[
117+
{
118+
action: 'onClick',
119+
track: onEventLog,
120+
data: {
121+
event: 'PRODUCT_ADD',
122+
productId: product.id,
123+
},
124+
},
125+
]}
126+
/>
127+
</div>
128+
</Card>
129+
))}
130+
</div>
131+
<div className="split-right">
132+
<h2 className="split-title">Your Cart</h2>
133+
134+
<p>Total items added: <strong>{totalItems}</strong></p>
135+
<ButtonWithTracking
136+
label="Proceed to Buy"
137+
disabled={totalItems === 0}
138+
trackers={[
139+
{
140+
action: 'onClick',
141+
track: onEventLog,
142+
data: {
143+
event: 'CHECKOUT',
144+
totalItems: totalItems,
145+
},
146+
},
147+
]}
148+
/>
149+
</div>
150+
</div>
151+
</>
152+
);
153+
};
154+
155+
export default ShoppingCart;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React, { useState } from 'react';
2+
3+
import ReactJsonView from '@microlink/react-json-view';
4+
5+
import ShoppingCart from '@components/templates/ShoppingCart';
6+
import SplitLayout from '@components/Layout/SplitLayout';
7+
8+
import withTracking,
9+
{
10+
DataContext,
11+
UserInteraction,
12+
UserInteractionResource
13+
} from 'react-user-analytics/index';
14+
15+
const data = {
16+
context: 'Shopping page',
17+
app: {
18+
version: '1',
19+
},
20+
} as UserInteraction.DataContext;
21+
22+
export const ShoppingCartInteractionLogger = () => {
23+
const [eventData, setEventData] = useState<UserInteractionResource[]>([]);
24+
25+
const handleLogEvent = (
26+
e: React.MouseEvent<HTMLElement, MouseEvent>,
27+
interactionResource: UserInteractionResource,
28+
) => {
29+
setEventData((prev) => [...prev, interactionResource]);
30+
}
31+
32+
return (
33+
<SplitLayout>
34+
35+
<DataContext.Provider value={data}>
36+
<ShoppingCart onEventLog={handleLogEvent} />
37+
</DataContext.Provider>
38+
39+
<ReactJsonView
40+
src={
41+
eventData.length
42+
? eventData
43+
: { note: 'Waiting for events...' }
44+
}
45+
collapsed={false}
46+
/>
47+
</SplitLayout>
48+
);
49+
}
50+
51+
export default ShoppingCartInteractionLogger;

0 commit comments

Comments
 (0)