Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added example with salesforce commerce cloud #41376

Merged
merged 15 commits into from Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/with-sfcc/.example.env
@@ -0,0 +1,5 @@
SFDC_CLIENT_ID=
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
SFDC_SECRET=
SFDC_ORGANIZATIONID=
SFDC_SHORTCODE=
SFDC_SITEID=
38 changes: 38 additions & 0 deletions examples/with-sfcc/.gitignore
@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# 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
.env.development.local
.env.test.local
.env.production.local
.env
Nutlope marked this conversation as resolved.
Show resolved Hide resolved

# vercel
.vercel

# typescript
*.tsbuildinfo
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
27 changes: 27 additions & 0 deletions examples/with-sfcc/README.md
@@ -0,0 +1,27 @@
# Example Next.js app with Salesforce Commerce Cloud

This example shows how to create a headless ecommerce application using Next.js, [Salesforce commerce cloud](https://www.salesforce.com/products/commerce-cloud/overview/), and [Tailwind](https://tailwindcss.com).

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or view the demo [here](https://salesforce-cloud-commerce.vercel.app/)

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-sfcc&project-name=with-sfcc&repository-name=with-sfcc&env=SFDC_CLIENT_ID,SFDC_SECRET,SFDC_ORGANIZATIONID,SFDC_SHORTCODE,SFDC_SITEID&envDescription=API%20Keys%20from%20SFCC%20needed%20to%20run%20this%20application.)

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

## How to use

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/with-sfcc) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example::

```bash
npx create-next-app --example with-sfcc nextjs-sfcc-app
# or
yarn create next-app --example with-sfcc nextjs-sfcc-app
# or
pnpm create next-app --example with-sfcc nextjs-sfcc-app
```
Nutlope marked this conversation as resolved.
Show resolved Hide resolved

## References

- SDK: https://github.com/SalesforceCommerceCloud/commerce-sdk
41 changes: 41 additions & 0 deletions examples/with-sfcc/components/Header.js
@@ -0,0 +1,41 @@
import Image from 'next/future/image'

export default function Header({ scrollHandler }) {
return (
<header className="relative">
<div className="absolute inset-x-0 bottom-0 h-1/2 bg-gray-100" />
<div className="mx-auto">
<div className="relative shadow-xl sm:overflow-hidden">
<div className="absolute inset-0">
<Image
priority
fill
className="h-full w-full object-cover"
src="/hero.jpg"
alt="Coffee grinder"
/>
<div className="absolute inset-0 bg-orange-100 mix-blend-multiply" />
</div>
<div className="relative px-4 py-16 sm:px-6 sm:py-24 lg:py-32 lg:px-8">
<p className="relative left-0 right-0 mx-auto mt-5 max-w-xl text-center text-xl font-semibold uppercase tracking-wide text-orange-600">
The Coffee House
</p>
<h1 className="mt-1 text-center font-bold uppercase text-gray-900 sm:text-5xl sm:tracking-tight lg:text-7xl">
<span className="block text-white">Life is better with</span>
<span className="block text-orange-500">coffee</span>
</h1>

<div className="mx-auto mt-10 max-w-xs sm:flex sm:max-w-none sm:justify-center">
<button
className="flex items-center justify-center rounded-md border border-transparent bg-white px-4 py-3 text-base font-medium text-orange-600 shadow-sm hover:bg-orange-100 sm:px-8"
onClick={scrollHandler}
>
Shop coffees
</button>
</div>
</div>
</div>
</div>
</header>
)
}
37 changes: 37 additions & 0 deletions examples/with-sfcc/components/ProductCard.js
@@ -0,0 +1,37 @@
import Image from 'next/future/image'
import Link from 'next/link'
import { useState } from 'react'

export default function ProductCard({ product }) {
const [isLoading, setLoading] = useState(true)
function cn(...classes) {
return classes.filter(Boolean).join(' ')
}
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
return (
<Link href={`/products/${product.id}`}>
<a className="group">
<div className="aspect-w-1 aspect-h-1 w-full overflow-hidden rounded-lg bg-gray-200 xl:aspect-w-7 xl:aspect-h-8">
<Image
alt=""
src={product.imageGroups[0].images[0].link}
fill
className={cn(
'object-cover duration-700 ease-in-out group-hover:opacity-75 ',
isLoading
? 'scale-110 blur-2xl grayscale'
: 'scale-100 blur-0 grayscale-0'
)}
onLoadingComplete={() => setLoading(false)}
/>
</div>
<div className="mt-4 flex items-center justify-between text-base font-medium text-gray-900">
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
<p className="mt-1 text-sm italic text-gray-500">
{product.shortDescription}
</p>
</a>
</Link>
)
}
17 changes: 17 additions & 0 deletions examples/with-sfcc/components/layout.js
@@ -0,0 +1,17 @@
export default function Layout({ children }) {
return (
<>
<main>{children}</main>
<footer className="center mt-5 flex justify-center space-x-4 bg-[#E7E8EF] p-4 text-xs">
<p>Powered by Next.js, Salesforce Commerce Cloud, and Vercel </p>
<span>|</span>
<a
href="https://github.com/vercel/next.js/tree/canary/examples/with-sfcc"
className="font-medium text-orange-600"
>
Source code
</a>
</footer>
</>
)
}
6 changes: 6 additions & 0 deletions examples/with-sfcc/next.config.js
@@ -0,0 +1,6 @@
module.exports = {
reactStrictMode: true,
images: {
domains: ['zzte-003.sandbox.us02.dx.commercecloud.salesforce.com'],
},
}
27 changes: 27 additions & 0 deletions examples/with-sfcc/package.json
@@ -0,0 +1,27 @@
{
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"commerce-sdk": "^2.8.0",
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2"
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
},
"devDependencies": {
"@tailwindcss/aspect-ratio": "^0.4.0",
"@types/node": "17.0.4",
"@types/react": "17.0.38",
"autoprefixer": "^10.4.0",
"eslint": "8.13.0",
"eslint-config-next": "12.1.5",
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
"postcss": "^8.4.5",
"prettier": "^2.5.1",
"prettier-plugin-tailwindcss": "^0.1.1",
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
"tailwindcss": "^3.0.7",
"typescript": "4.5.4"
}
}
18 changes: 18 additions & 0 deletions examples/with-sfcc/pages/_app.js
@@ -0,0 +1,18 @@
import '../styles/globals.css'
import Layout from '../components/layout'
import Head from 'next/head'

function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<title>The Coffee House</title>
</Head>
<Layout>
<Component {...pageProps} />
</Layout>
</>
)
}

export default MyApp
26 changes: 26 additions & 0 deletions examples/with-sfcc/pages/_document.js
@@ -0,0 +1,26 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
render() {
return (
<Html lang="en">
<Head>
<meta
name="description"
content="The premiere coffee delivery service."
/>
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

export default MyDocument
50 changes: 50 additions & 0 deletions examples/with-sfcc/pages/index.js
@@ -0,0 +1,50 @@
import { useRef } from 'react'
import Header from '../components/Header'
import ProductCard from '../components/ProductCard'
import getProducts from '../sfcc.js'

export default function Gallery({ data }) {
let coffeeRef = useRef()

const scrollHandler = (e) => {
e.preventDefault()
coffeeRef.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}

return (
<>
<Header scrollHandler={scrollHandler} />
<div className="mx-auto max-w-2xl px-4 sm:px-6 lg:max-w-7xl lg:px-8">
<div className="sm:py-15 mx-auto max-w-7xl py-16 px-4 sm:px-6 lg:px-8">
<div className="text-center">
<p
className="mt-1 text-4xl font-bold uppercase text-gray-900 sm:text-5xl sm:tracking-tight lg:text-5xl"
ref={(element) => (coffeeRef = element)}
>
Crafted by us, for you
</p>
</div>
</div>
<div className="grid grid-cols-1 gap-y-10 gap-x-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
{data &&
data.map((product) => (
<ProductCard product={product} key={product.id} />
))}
</div>
</div>
</>
)
}

export async function getStaticProps() {
const searchResults = await getProducts('coffee')

return {
props: {
data: searchResults,
},
}
}
60 changes: 60 additions & 0 deletions examples/with-sfcc/pages/products/[slug].js
@@ -0,0 +1,60 @@
import Image from 'next/future/image'
import getProducts from '../../sfcc.js'

export default function Product({ product }) {
return (
<div className="flex h-screen flex-col justify-between">
<div className="mx-auto mt-16 max-w-2xl px-4 sm:px-6 lg:max-w-7xl lg:px-8">
<div className="mx-auto flex flex-col sm:flex-row">
<Image
alt="coffee"
className="rounded-lg"
src={product.imageGroups[0].images[0].link}
width={560}
height={640}
/>
<div className="mt-10 flex flex-col sm:mt-0 sm:ml-10">
<h1 className="mt-1 text-4xl font-bold uppercase text-gray-900 sm:text-5xl sm:tracking-tight lg:text-5xl">
{product.name}
</h1>
<h1 className="mt-3 text-4xl font-bold text-gray-500 sm:text-3xl sm:tracking-tight lg:text-3xl">
${product.price}
</h1>
<div className="mt-10 mb-5 border-t border-gray-200 pt-10 font-bold">
Description
</div>
<p className="max-w-xl">{product.longDescription}</p>
</div>
</div>
</div>
</div>
)
}

export async function getStaticProps({ params }) {
const searchResults = await getProducts(params.slug)
console.log('search Results are: ', searchResults)

const coffeeProduct = searchResults[0]
return {
props: {
product: coffeeProduct,
},
}
}

export async function getStaticPaths() {
const searchResults = await getProducts('coffee')

let coffeeProducts = searchResults

let fullPaths = []
for (let product of coffeeProducts) {
fullPaths.push({ params: { slug: product.id } })
}

return {
paths: fullPaths,
fallback: 'blocking',
}
}
6 changes: 6 additions & 0 deletions examples/with-sfcc/postcss.config.js
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
4 changes: 4 additions & 0 deletions examples/with-sfcc/prettier.config.js
@@ -0,0 +1,4 @@
module.exports = {
Nutlope marked this conversation as resolved.
Show resolved Hide resolved
singleQuote: true,
semi: false,
}
Binary file added examples/with-sfcc/public/favicon.ico
Binary file not shown.
Binary file added examples/with-sfcc/public/hero.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions examples/with-sfcc/public/vercel.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.