Skip to content

Commit f4f79d6

Browse files
pbennett1-godaddywcole1-godaddyampcode-comgithub-advanced-security[bot]
authored
Storefront product grid and product details (#1215)
* Add storefront product grid with image support - Add ProductGrid and ProductCard components for displaying SKU groups - Add Badge and Card UI components - Update GraphQL queries to fetch SKU groups with mediaObjects - Display product images from mediaObjects (IMAGE type) - Add formatCurrency utility function - Create example store page in Next.js app - Add providers setup for React Query Amp-Thread-ID: https://ampcode.com/threads/T-65ac7bb2-a629-4360-a457-dddb0eb691c8 Co-authored-by: Amp <[email protected]> * load styles from provider for exported components * product details page with option tracking * add storeId and clientId to gd provider context * add cart and router link support * comment out image dupe testing * update providers and skus query * add storefront schema * refactor gql for namespacing * fix input usage for storefront skus * load in attributes * add orders schemas and graphql * Potential fix for code scanning alert no. 1: Incomplete URL scheme check Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Incomplete URL scheme check Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * add lucide to example nextjs app * replace sku with productId * add return URL logic back * use skuGroup for product-details * remove nuqs dep * Small style updates to product grid and details * add changeset --------- Co-authored-by: Wes Cole <[email protected]> Co-authored-by: Amp <[email protected]> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent aed677c commit f4f79d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+28894
-10633
lines changed

.changeset/cute-bees-cross.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@godaddy/react": patch
3+
---
4+
5+
Add product grid and product details components using public storefront apis

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ dist
4242
/blob-report/
4343
/playwright/.cache/
4444

45-
schema.graphql
45+
*.graphql
4646

4747
# macOS
4848
.DS_Store

examples/nextjs/app/checkout.tsx

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client';
22

33
import type { CheckoutFormSchema, CheckoutSession } from '@godaddy/react';
4-
import { Checkout, GoDaddyProvider } from '@godaddy/react';
5-
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
4+
import { Checkout } from '@godaddy/react';
65
import { z } from 'zod';
76

87
/* Override the checkout form schema to make shippingPhone required */
@@ -12,26 +11,23 @@ const customSchema: CheckoutFormSchema = {
1211

1312
export function CheckoutPage({ session }: { session: CheckoutSession }) {
1413
return (
15-
<GoDaddyProvider apiHost={process.env.NEXT_PUBLIC_GODADDY_API_HOST}>
16-
<Checkout
17-
session={session}
18-
checkoutFormSchema={customSchema}
19-
squareConfig={{
20-
appId: process.env.NEXT_PUBLIC_SQUARE_APP_ID || '',
21-
locationId: process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID || '',
22-
}}
23-
stripeConfig={{
24-
publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || '',
25-
}}
26-
godaddyPaymentsConfig={{
27-
businessId: process.env.NEXT_PUBLIC_GODADDY_BUSINESS_ID || '',
28-
appId: process.env.NEXT_PUBLIC_GODADDY_APP_ID || '',
29-
}}
30-
paypalConfig={{
31-
clientId: process.env.NEXT_PUBLIC_PAYPAL_CLIENT_ID || '',
32-
}}
33-
/>
34-
<ReactQueryDevtools initialIsOpen={false} />
35-
</GoDaddyProvider>
14+
<Checkout
15+
session={session}
16+
checkoutFormSchema={customSchema}
17+
squareConfig={{
18+
appId: process.env.NEXT_PUBLIC_SQUARE_APP_ID || '',
19+
locationId: process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID || '',
20+
}}
21+
stripeConfig={{
22+
publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || '',
23+
}}
24+
godaddyPaymentsConfig={{
25+
businessId: process.env.NEXT_PUBLIC_GODADDY_BUSINESS_ID || '',
26+
appId: process.env.NEXT_PUBLIC_GODADDY_APP_ID || '',
27+
}}
28+
paypalConfig={{
29+
clientId: process.env.NEXT_PUBLIC_PAYPAL_CLIENT_ID || '',
30+
}}
31+
/>
3632
);
3733
}

examples/nextjs/app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export default async function Home() {
9090
},
9191
{
9292
auth: {
93-
clientId: process.env.GODADDY_CLIENT_ID || '',
93+
clientId: process.env.NEXT_PUBLIC_GODADDY_CLIENT_ID || '',
9494
clientSecret: process.env.GODADDY_CLIENT_SECRET || '',
9595
},
9696
}

examples/nextjs/app/providers.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,37 @@
11
'use client';
22

3-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3+
import { GoDaddyProvider } from '@godaddy/react';
4+
import { QueryClient } from '@tanstack/react-query';
5+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
6+
import Link from 'next/link';
47
import { useState } from 'react';
58

69
export function Providers({ children }: { children: React.ReactNode }) {
7-
const [queryClient] = useState(() => new QueryClient());
10+
const [queryClient] = useState(
11+
() =>
12+
new QueryClient({
13+
defaultOptions: {
14+
queries: {
15+
retry: false,
16+
refetchOnWindowFocus: false,
17+
},
18+
},
19+
})
20+
);
821

922
return (
10-
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
23+
<GoDaddyProvider
24+
queryClient={queryClient}
25+
apiHost={process.env.NEXT_PUBLIC_GODADDY_API_HOST}
26+
storeId={process.env.NEXT_PUBLIC_GODADDY_STORE_ID}
27+
clientId={process.env.NEXT_PUBLIC_GODADDY_CLIENT_ID}
28+
Link={Link}
29+
appearance={{
30+
variables: { primary: '#ff0000', 'primary-foreground': '#FFFFFF' },
31+
}}
32+
>
33+
{children}
34+
<ReactQueryDevtools client={queryClient} initialIsOpen={false} />
35+
</GoDaddyProvider>
1136
);
1237
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use client';
2+
3+
import { CartLineItems, CartTotals } from '@godaddy/react';
4+
5+
export default function CartPage() {
6+
const items = [
7+
{
8+
id: 'LineItem_2y0l7o6Oi4BW6fpSiKPX1hhBccU',
9+
name: 'Box of cookies',
10+
image:
11+
'https://isteam.dev-wsimg.com/ip/2f2e05ec-de6f-4a89-90f2-038c749655b0/cookies.webp',
12+
quantity: 2,
13+
originalPrice: 10.99,
14+
price: 10.99,
15+
notes: [],
16+
},
17+
{
18+
id: 'LineItem_2y0l9FykA04qp2pC6y3YZ0TbZFD',
19+
name: 'Cupcakes',
20+
image:
21+
'https://isteam.dev-wsimg.com/ip/2f2e05ec-de6f-4a89-90f2-038c749655b0/cupcakes.webp/:/rs=w:600,h:600',
22+
quantity: 1,
23+
originalPrice: 5.99,
24+
price: 5.99,
25+
notes: [],
26+
},
27+
];
28+
29+
const totals = {
30+
subtotal: 27.97,
31+
discount: 0,
32+
shipping: 0,
33+
currency: 'USD',
34+
itemCount: 3,
35+
total: 27.97,
36+
tip: 0,
37+
taxes: 0,
38+
enableDiscounts: false,
39+
enableTaxes: true,
40+
isTaxLoading: false,
41+
};
42+
43+
return (
44+
<div className='container px-4 py-8'>
45+
<h1 className='text-2xl font-bold mb-6'>Cart</h1>
46+
<div className='grid grid-cols-1 gap-8'>
47+
<div>
48+
<div className='bg-white rounded-lg shadow p-6'>
49+
<h2 className='text-lg font-semibold mb-4'>Your Items</h2>
50+
<CartLineItems items={items} />
51+
</div>
52+
</div>
53+
<div>
54+
<div className='bg-white rounded-lg shadow p-6 sticky top-4'>
55+
<h2 className='text-lg font-semibold mb-4'>Order Summary</h2>
56+
<CartTotals {...totals} />
57+
</div>
58+
</div>
59+
</div>
60+
</div>
61+
);
62+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Cart from './cart';
2+
3+
export default function StorePage() {
4+
return (
5+
<div className='p-4'>
6+
<Cart />
7+
</div>
8+
);
9+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use client';
2+
3+
import { Cart } from '@godaddy/react';
4+
import { ShoppingCart } from 'lucide-react';
5+
import { createContext, useContext, useState } from 'react';
6+
7+
interface CartContextType {
8+
openCart: () => void;
9+
closeCart: () => void;
10+
}
11+
12+
const CartContext = createContext<CartContextType | undefined>(undefined);
13+
14+
export function useCart() {
15+
const context = useContext(CartContext);
16+
if (!context) {
17+
throw new Error('useCart must be used within CartProvider');
18+
}
19+
return context;
20+
}
21+
22+
export default function StoreLayout({
23+
children,
24+
}: {
25+
children: React.ReactNode;
26+
}) {
27+
const [isCartOpen, setIsCartOpen] = useState(false);
28+
29+
const openCart = () => setIsCartOpen(true);
30+
const closeCart = () => setIsCartOpen(false);
31+
32+
return (
33+
<CartContext.Provider value={{ openCart, closeCart }}>
34+
<section className='relative max-w-6xl mx-auto'>
35+
{/* Cart toggle button */}
36+
<button
37+
onClick={openCart}
38+
className='fixed top-4 right-4 z-40 bg-primary text-primary-foreground p-3 rounded-full shadow-lg hover:bg-primary/90 transition-colors'
39+
aria-label='Open shopping cart'
40+
>
41+
<ShoppingCart className='h-6 w-6' />
42+
</button>
43+
44+
{children}
45+
46+
<Cart open={isCartOpen} onOpenChange={setIsCartOpen} />
47+
</section>
48+
</CartContext.Provider>
49+
);
50+
}
51+
52+
export { CartContext };

examples/nextjs/app/store/page.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Products from './products';
2+
3+
export default function StorePage() {
4+
return (
5+
<div className='p-4'>
6+
<Products />
7+
</div>
8+
);
9+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client';
2+
3+
import { use } from 'react';
4+
import Product from '@/app/store/product/[productId]/product';
5+
6+
export default function ProductPage({
7+
params,
8+
}: {
9+
params: Promise<{ productId: string }>;
10+
}) {
11+
const { productId } = use(params);
12+
13+
return (
14+
<div className='p-4'>
15+
<Product productId={productId} />
16+
</div>
17+
);
18+
}

0 commit comments

Comments
 (0)