Skip to content

Commit

Permalink
chore: ecommerce example (#46)
Browse files Browse the repository at this point in the history
* starting on the ecommerce example

* add the cart

* fixes

* fixies

* new generic

* show why convrting to a node can help, here the mutation only returns {id:x}

* finishing touches

* fix imports

* examples/next -> examples/spacex

* Revert "examples/next -> examples/spacex"

This reverts commit fe719b7.

---------

Co-authored-by: Max Stoiber <[email protected]>
  • Loading branch information
JoviDeCroock and mxstbr authored Dec 5, 2023
1 parent 1bcb37e commit b7d0c23
Show file tree
Hide file tree
Showing 33 changed files with 1,283 additions and 15 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
36 changes: 36 additions & 0 deletions examples/ecommerce/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
36 changes: 36 additions & 0 deletions examples/ecommerce/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
17 changes: 17 additions & 0 deletions examples/ecommerce/app/api/fuse/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createAPIRouteHandler } from 'fuse/next'

const files = require.context('../../../types', true, /\.ts$/)
files
.keys()
.filter((path: string) => path.includes('types/'))
.forEach(files)

const layer = createAPIRouteHandler<{ userId: string }>({
context: () => ({
// For demo purposes everyone is the same user
userId: '1',
}),
})

export const GET = layer
export const POST = layer
Binary file added examples/ecommerce/app/favicon.ico
Binary file not shown.
14 changes: 14 additions & 0 deletions examples/ecommerce/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
html,
body {
max-width: 100vw;
overflow-x: hidden;
}

body {
color: black;
}

a {
color: inherit;
text-decoration: none;
}
19 changes: 19 additions & 0 deletions examples/ecommerce/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Inter } from 'next/font/google'
import './globals.css'
import { DatalayerProvider } from '@/components/DatalayerProvider'

const inter = Inter({ subsets: ['latin'] })

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang='en'>
<body className={inter.className}>
<DatalayerProvider>{children}</DatalayerProvider>
</body>
</html>
)
}
11 changes: 11 additions & 0 deletions examples/ecommerce/app/page.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.main {
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}

.list {
padding: 0;
margin: 0;
}
41 changes: 41 additions & 0 deletions examples/ecommerce/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react'
import { registerClient, createClient, fetchExchange } from 'fuse/next/server'

import styles from './page.module.css'
import { graphql } from '@/fuse'
import { Category } from '@/components/Category'
import { Cart } from '@/components/Cart'

const { getClient } = registerClient(() =>
createClient({
url:
process.env.NODE_ENV === 'production'
? 'https://spacex-fuse.vercel.app/api/fuse'
: 'http://localhost:3000/api/fuse',
exchanges: [fetchExchange],
}),
)

const HomePageQuery = graphql(`
query HomePage {
myCart {
...Cart_CartFields
}
categories {
...Category_CategoryFields
}
}
`)

export default async function Page() {
const result = await getClient().query(HomePageQuery, {}).toPromise()
return (
<main className={styles.main}>
<h1>Fuse Store</h1>
{result.data?.myCart && <Cart cart={result.data.myCart} />}
{result.data?.categories.map((category, i) => (
<Category key={i} category={category} />
))}
</main>
)
}
17 changes: 17 additions & 0 deletions examples/ecommerce/components/Cart.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.cartTitle {
margin-bottom: 12px;
text-align: center;
}

.cartSection {
margin-bottom: 16px;
}

.grid {
list-style: none;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
padding: 0;
margin: 0;
}
44 changes: 44 additions & 0 deletions examples/ecommerce/components/Cart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { FragmentType, graphql, useFragment } from '@/fuse'
import { Product } from './Product'
import styles from './Cart.module.css'

const CartFields = graphql(`
fragment Cart_CartFields on Cart {
items {
quantity
product {
id
price
...Product_ProductFields
}
}
}
`)

export const Cart = (props: { cart: FragmentType<typeof CartFields> }) => {
const cart = useFragment(CartFields, props.cart)
return (
<section className={styles.cartSection}>
<h2 className={styles.cartTitle}>
Cart - Total Price: $
{cart.items?.reduce(
(acc, item) => acc + item.quantity * item.product.price,
0,
)}
</h2>
<ul className={styles.grid}>
{cart.items?.map(
(cartItem, i) =>
cartItem &&
cartItem.product && (
<Product
key={cartItem.product.id}
product={cartItem.product}
noAddToCart
/>
),
)}
</ul>
</section>
)
}
17 changes: 17 additions & 0 deletions examples/ecommerce/components/Category.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.categoryTitle {
margin-bottom: 12px;
text-align: center;
}

.categorySection {
margin-bottom: 16px;
}

.grid {
list-style: none;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
padding: 0;
margin: 0;
}
29 changes: 29 additions & 0 deletions examples/ecommerce/components/Category.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FragmentType, graphql, useFragment } from '@/fuse'
import { Product } from './Product'
import styles from './Category.module.css'

const CategoryFields = graphql(`
fragment Category_CategoryFields on Category {
name
products {
id
...Product_ProductFields
}
}
`)

export const Category = (props: {
category: FragmentType<typeof CategoryFields>
}) => {
const category = useFragment(CategoryFields, props.category)
return (
<section className={styles.categorySection}>
<h2 className={styles.categoryTitle}>{category.name}</h2>
<ul className={styles.grid}>
{category.products.map((product, i) => (
<Product key={product.id} product={product} />
))}
</ul>
</section>
)
}
32 changes: 32 additions & 0 deletions examples/ecommerce/components/DatalayerProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use client'

import {
Provider,
ssrExchange,
cacheExchange,
fetchExchange,
createClient,
} from 'fuse/next/client'
import React from 'react'

export const DatalayerProvider = (props: any) => {
const [client, ssr] = React.useMemo(() => {
const ssr = ssrExchange()
const client = createClient({
url:
process.env.NODE_ENV === 'production'
? 'https://spacex-fuse.vercel.app/api/fuse'
: 'http://localhost:3000/api/fuse',
exchanges: [cacheExchange, ssr, fetchExchange],
suspense: true,
})

return [client, ssr]
}, [])

return (
<Provider client={client} ssr={ssr}>
{props.children}
</Provider>
)
}
31 changes: 31 additions & 0 deletions examples/ecommerce/components/Product.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.productItem {
border: 1px solid black;
border-radius: 6px;
padding: 16px;
width: 256px;
display: flex;
flex-direction: column;
}

.addToCart {
margin-bottom: 16px;
}

.productImage {
height: 128px;
width: 128px;
margin-left: auto;
margin-right: auto;
margin-bottom: 12px;
}

.productName {
text-align: center;
margin: 0;
margin-bottom: 16px;
}

.productDescription {
margin: 0;
font-size: 14px;
}
Loading

1 comment on commit b7d0c23

@vercel
Copy link

@vercel vercel bot commented on b7d0c23 Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.