├── app ├── favicon.ico ├── opengraph-image.tsx ├── api │ └── revalidate │ │ └── route.ts ├── robots.ts ├── [page] │ ├── layout.tsx │ ├── opengraph-image.tsx │ └── page.tsx ├── search │ ├── children-wrapper.tsx │ ├── [collection] │ │ ├── opengraph-image.tsx │ │ └── page.tsx │ ├── loading.tsx │ ├── layout.tsx │ └── page.tsx ├── page.tsx ├── error.tsx ├── globals.css ├── layout.tsx ├── sitemap.ts └── product │ └── [handle] │ └── page.tsx ├── fonts └── Inter-Bold.ttf ├── postcss.config.mjs ├── lib ├── shopify │ ├── fragments │ │ ├── seo.ts │ │ ├── image.ts │ │ ├── cart.ts │ │ └── product.ts │ ├── queries │ │ ├── menu.ts │ │ ├── cart.ts │ │ ├── page.ts │ │ ├── product.ts │ │ └── collection.ts │ ├── mutations │ │ └── cart.ts │ ├── types.ts │ └── index.ts ├── type-guards.ts ├── constants.ts └── utils.ts ├── .env.example ├── .vscode ├── settings.json └── launch.json ├── next.config.ts ├── components ├── grid │ ├── index.tsx │ ├── tile.tsx │ └── three-items.tsx ├── loading-dots.tsx ├── icons │ └── logo.tsx ├── price.tsx ├── logo-square.tsx ├── cart │ ├── open-cart.tsx │ ├── delete-item-button.tsx │ ├── edit-item-quantity-button.tsx │ ├── actions.ts │ ├── add-to-cart.tsx │ ├── cart-context.tsx │ └── modal.tsx ├── prose.tsx ├── welcome-toast.tsx ├── label.tsx ├── layout │ ├── product-grid-items.tsx │ ├── search │ │ ├── filter │ │ │ ├── index.tsx │ │ │ ├── dropdown.tsx │ │ │ └── item.tsx │ │ └── collections.tsx │ ├── footer-menu.tsx │ ├── navbar │ │ ├── search.tsx │ │ ├── index.tsx │ │ └── mobile-menu.tsx │ └── footer.tsx ├── product │ ├── product-description.tsx │ ├── product-context.tsx │ ├── gallery.tsx │ └── variant-selector.tsx ├── opengraph-image.tsx └── carousel.tsx ├── .gitignore ├── tsconfig.json ├── package.json ├── license.md ├── README.md └── pnpm-lock.yaml /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-commerce/main/app/favicon.ico -------------------------------------------------------------------------------- /fonts/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-commerce/main/fonts/Inter-Bold.ttf -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | export default { 3 | plugins: { 4 | '@tailwindcss/postcss': {}, 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /app/opengraph-image.tsx: -------------------------------------------------------------------------------- 1 | import OpengraphImage from 'components/opengraph-image'; 2 | 3 | export default async function Image() { 4 | return await OpengraphImage(); 5 | } 6 | -------------------------------------------------------------------------------- /lib/shopify/fragments/seo.ts: -------------------------------------------------------------------------------- 1 | const seoFragment = /* GraphQL */ ` 2 | fragment seo on SEO { 3 | description 4 | title 5 | } 6 | `; 7 | 8 | export default seoFragment; 9 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | COMPANY_NAME="Vercel Inc." 2 | SITE_NAME="Next.js Commerce" 3 | SHOPIFY_REVALIDATION_SECRET="" 4 | SHOPIFY_STOREFRONT_ACCESS_TOKEN="" 5 | SHOPIFY_STORE_DOMAIN="[your-shopify-store-subdomain].myshopify.com" 6 | -------------------------------------------------------------------------------- /lib/shopify/fragments/image.ts: -------------------------------------------------------------------------------- 1 | const imageFragment = /* GraphQL */ ` 2 | fragment image on Image { 3 | url 4 | altText 5 | width 6 | height 7 | } 8 | `; 9 | 10 | export default imageFragment; 11 | -------------------------------------------------------------------------------- /lib/shopify/queries/menu.ts: -------------------------------------------------------------------------------- 1 | export const getMenuQuery = /* GraphQL */ ` 2 | query getMenu($handle: String!) { 3 | menu(handle: $handle) { 4 | items { 5 | title 6 | url 7 | } 8 | } 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /app/api/revalidate/route.ts: -------------------------------------------------------------------------------- 1 | import { revalidate } from 'lib/shopify'; 2 | import { NextRequest, NextResponse } from 'next/server'; 3 | 4 | export async function POST(req: NextRequest): Promise { 5 | return revalidate(req); 6 | } 7 | -------------------------------------------------------------------------------- /lib/shopify/queries/cart.ts: -------------------------------------------------------------------------------- 1 | import cartFragment from '../fragments/cart'; 2 | 3 | export const getCartQuery = /* GraphQL */ ` 4 | query getCart($cartId: ID!) { 5 | cart(id: $cartId) { 6 | ...cart 7 | } 8 | } 9 | ${cartFragment} 10 | `; 11 | -------------------------------------------------------------------------------- /app/robots.ts: -------------------------------------------------------------------------------- 1 | import { baseUrl } from 'lib/utils'; 2 | 3 | export default function robots() { 4 | return { 5 | rules: [ 6 | { 7 | userAgent: '*' 8 | } 9 | ], 10 | sitemap: `${baseUrl}/sitemap.xml`, 11 | host: baseUrl 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true, 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll": "explicit", 6 | "source.organizeImports": "explicit", 7 | "source.sortMembers": "explicit" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/[page]/layout.tsx: -------------------------------------------------------------------------------- 1 | import Footer from 'components/layout/footer'; 2 | 3 | export default function Layout({ children }: { children: React.ReactNode }) { 4 | return ( 5 | <> 6 |
7 |
{children}
8 |
9 |