├── ecommerce ├── public │ ├── assets │ │ ├── Checkout.svg │ │ └── squirrel.jpg │ ├── vercel.svg │ └── next.svg ├── .eslintrc.json ├── app │ ├── favicon.ico │ ├── custom-components │ │ └── page.tsx │ ├── stores │ │ ├── page.tsx │ │ └── [slug] │ │ │ └── page.tsx │ ├── help-center │ │ └── page.tsx │ ├── page.tsx │ ├── symbols │ │ └── page.tsx │ ├── figma-imports │ │ └── page.tsx │ ├── page │ │ └── [page] │ │ │ └── page.tsx │ ├── layout.tsx │ ├── [...page] │ │ └── page.tsx │ ├── blog │ │ └── [slug] │ │ │ └── page.tsx │ ├── category │ │ └── [category] │ │ │ └── page.tsx │ ├── product │ │ └── [handle] │ │ │ └── page.tsx │ └── globals.css ├── postcss.config.js ├── .env.example ├── config │ ├── emporix.ts │ ├── algolia.tsx │ ├── swell.ts │ └── shopify.ts ├── src │ ├── components │ │ ├── testing │ │ │ └── BlogCategory.tsx │ │ ├── Blocks │ │ │ ├── CloudinaryImage.tsx │ │ │ └── BynderImage.tsx │ │ ├── Counter │ │ │ ├── styles.module.css │ │ │ └── Counter.tsx │ │ ├── Hero │ │ │ ├── TextHero.tsx │ │ │ ├── HeroWithChildren.tsx │ │ │ ├── ImageHero.tsx │ │ │ └── SplitHero.tsx │ │ ├── PLP │ │ │ ├── Pagination.tsx │ │ │ ├── CategoryFilter.tsx │ │ │ ├── ColorFilter.tsx │ │ │ └── SizeFilter.tsx │ │ ├── ui │ │ │ ├── label.tsx │ │ │ ├── input.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── productBox.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── tabs.tsx │ │ │ ├── card.tsx │ │ │ ├── accordion.tsx │ │ │ └── button.tsx │ │ ├── CommercetoolsProduct │ │ │ └── CommercetoolsProduct.tsx │ │ ├── builder.tsx │ │ ├── Card │ │ │ ├── IconCard.tsx │ │ │ └── ProductCard.tsx │ │ ├── Accordion │ │ │ └── accordion.tsx │ │ ├── SwellProduct │ │ │ └── SwellProduct.tsx │ │ ├── QueryProvider.tsx │ │ ├── builderLiveDataPreview.tsx │ │ ├── CustomText │ │ │ └── index.tsx │ │ ├── ShopifyProduct │ │ │ └── ShopifyProduct.tsx │ │ ├── Layout │ │ │ ├── SideNav.tsx │ │ │ ├── AuthSlider.tsx │ │ │ ├── CartSlider.tsx │ │ │ └── Footer.tsx │ │ ├── Collection │ │ │ └── Collection.tsx │ │ └── AlgoliaSearchBox │ │ │ └── AlgoliaSearchBox.tsx │ └── mappings │ │ ├── Header.mapper.tsx │ │ ├── Collection.mapper.tsx │ │ ├── TextHero.mapper.tsx │ │ ├── HeroWithChildren.mapper.tsx │ │ ├── IconCard.mapper.tsx │ │ ├── ProductCard.mapper.tsx │ │ ├── Hero.mapper.tsx │ │ └── SplitHero.mapper.tsx ├── components.json ├── .env ├── tsconfig.json ├── .gitignore ├── lib │ ├── swell │ │ └── api.ts │ ├── utils.ts │ └── shopify │ │ └── api.ts ├── README.md ├── package.json └── next.config.mjs ├── marketing ├── app │ ├── blog │ │ └── [slug] │ │ │ └── page.tsx │ ├── globals.css │ ├── favicon.ico │ ├── layout.tsx │ ├── page.tsx │ └── [...page] │ │ └── page.tsx ├── .eslintrc.json ├── postcss.config.js ├── next.config.mjs ├── builder-registry.ts ├── components │ ├── Counter │ │ ├── styles.module.css │ │ └── Counter.tsx │ ├── Hero │ │ ├── TextHero.tsx │ │ ├── HeroWithChildren.tsx │ │ ├── ImageHero.tsx │ │ └── SplitHero.tsx │ ├── ui │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── checkbox.tsx │ │ ├── radio-group.tsx │ │ ├── button.tsx │ │ ├── tabs.tsx │ │ └── accordion.tsx │ ├── builder.tsx │ ├── Card │ │ ├── ProductCard.tsx │ │ └── IconCard.tsx │ └── Layout │ │ ├── SideNav.tsx │ │ ├── AuthSlider.tsx │ │ ├── CartSlider.tsx │ │ └── Footer.tsx ├── tailwind.config.ts ├── .gitignore ├── public │ ├── vercel.svg │ └── next.svg ├── tsconfig.json ├── package.json └── README.md └── vcp-playground ├── app ├── favicon.ico ├── layout.tsx ├── figma-imports │ └── page.tsx ├── globals.css └── page.tsx ├── postcss.config.js ├── public ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── src ├── mappings │ ├── Header.mapper.tsx │ ├── ProductCard.mapper.tsx │ ├── Collection.mapper.tsx │ ├── TextHero.mapper.tsx │ ├── HeroWithChildren.mapper.tsx │ ├── IconCard.mapper.tsx │ ├── Hero.mapper.tsx │ └── SplitHero.mapper.tsx └── components │ ├── Counter │ ├── styles.module.css │ └── Counter.tsx │ ├── Hero │ ├── TextHero.tsx │ ├── HeroWithChildren.tsx │ ├── ImageHero.tsx │ └── SplitHero.tsx │ ├── ui │ ├── label.tsx │ ├── input.tsx │ ├── checkbox.tsx │ ├── productBox.tsx │ ├── radio-group.tsx │ ├── tabs.tsx │ ├── card.tsx │ ├── accordion.tsx │ └── button.tsx │ ├── builder.tsx │ ├── Card │ ├── ProductCard.tsx │ └── IconCard.tsx │ ├── QueryProvider.tsx │ ├── Layout │ ├── SideNav.tsx │ ├── AuthSlider.tsx │ ├── CartSlider.tsx │ └── Footer.tsx │ └── Collection │ └── Collection.tsx ├── eslint.config.mjs ├── .gitignore ├── tsconfig.json ├── README.md ├── package.json ├── lib └── utils.ts └── next.config.mjs /ecommerce/public/assets/Checkout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /marketing/app/blog/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ecommerce/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /marketing/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /marketing/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | -------------------------------------------------------------------------------- /ecommerce/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuilderIO/unified-demo/main/ecommerce/app/favicon.ico -------------------------------------------------------------------------------- /marketing/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuilderIO/unified-demo/main/marketing/app/favicon.ico -------------------------------------------------------------------------------- /vcp-playground/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuilderIO/unified-demo/main/vcp-playground/app/favicon.ico -------------------------------------------------------------------------------- /ecommerce/public/assets/squirrel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuilderIO/unified-demo/main/ecommerce/public/assets/squirrel.jpg -------------------------------------------------------------------------------- /ecommerce/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /marketing/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /vcp-playground/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /vcp-playground/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /marketing/next.config.mjs: -------------------------------------------------------------------------------- 1 | import BuilderDevTools from "@builder.io/dev-tools/next"; 2 | 3 | /** @type {import('next').NextConfig} */ 4 | const nextConfig = BuilderDevTools()({}); 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /ecommerce/.env.example: -------------------------------------------------------------------------------- 1 | # Created by Vercel CLI 2 | VERCEL="1" 3 | VERCEL_ENV="development" 4 | VERCEL_URL="http://localhost:3000" 5 | 6 | # Public API key for Builder.io 7 | NEXT_PUBLIC_BUILDER_API_KEY=your_builder_api_key 8 | -------------------------------------------------------------------------------- /ecommerce/config/emporix.ts: -------------------------------------------------------------------------------- 1 | const emporixConfig = { 2 | apiUrl: "https://api.emporix.io", 3 | clientId: "XXXXXXXXXXX", // Hardcoded client ID 4 | tenant: "XXXXXXXXXXX", // Hardcoded tenant name 5 | }; 6 | 7 | export default emporixConfig; -------------------------------------------------------------------------------- /ecommerce/src/components/testing/BlogCategory.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | const BlogCategory = (props: any) => { 4 | return ( 5 |
6 | {props?.category} 7 |
8 | ); 9 | }; 10 | 11 | 12 | 13 | export default BlogCategory; 14 | 15 | 16 | -------------------------------------------------------------------------------- /ecommerce/config/algolia.tsx: -------------------------------------------------------------------------------- 1 | import algoliasearch from "algoliasearch"; 2 | 3 | const ALGOLIA_APP_ID: string = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID ?? ""; 4 | const ALGOLIA_API_KEY: string = process.env.NEXT_PUBLIC_ALGOLIA_API_KEY ?? ""; 5 | 6 | export const client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_API_KEY); -------------------------------------------------------------------------------- /vcp-playground/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /marketing/builder-registry.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { builder, Builder } from "@builder.io/react"; 3 | import Counter from "./components/Counter/Counter"; 4 | 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | Builder.registerComponent(Counter, { 8 | name: "Counter", 9 | inputs: [ 10 | { 11 | name: "initialCount", 12 | type: "number", 13 | }, 14 | ], 15 | }); 16 | -------------------------------------------------------------------------------- /vcp-playground/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ecommerce/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/src/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /ecommerce/src/components/Blocks/CloudinaryImage.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | const CloudinaryImage = (props: any) => { 4 | if (!props.cloudinaryOptions) { 5 | return 'Choose an Image'; 6 | } 7 | return ( 8 | 13 | ); 14 | }; 15 | 16 | export default CloudinaryImage; 17 | -------------------------------------------------------------------------------- /ecommerce/src/mappings/Header.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import { Header } from "@/src/components/Layout/Header"; 3 | 4 | // ❖ Header 5 | interface FigmaHeaderProps extends BaseFigmaProps { 6 | } 7 | 8 | figmaMapping({ 9 | componentKey: "bf08eba01818598edd0d7ca65a48372fa4395184", 10 | mapper(figma: FigmaHeaderProps) { 11 | return
; 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /vcp-playground/src/mappings/Header.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import { Header } from "@/src/components/Layout/Header"; 3 | 4 | // ❖ Header 5 | interface FigmaHeaderProps extends BaseFigmaProps { 6 | } 7 | 8 | figmaMapping({ 9 | componentKey: "bf08eba01818598edd0d7ca65a48372fa4395184", 10 | mapper(figma: FigmaHeaderProps) { 11 | return
; 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /vcp-playground/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /vcp-playground/src/mappings/ProductCard.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import ProductCard from "@/src/components/Card/ProductCard"; 3 | 4 | // ❖ ProductCard 5 | interface FigmaProductCardProps extends BaseFigmaProps { 6 | } 7 | 8 | figmaMapping({ 9 | componentKey: "d2f114400698059ab75be459a13d26a99d85f47b", 10 | mapper(figma: FigmaProductCardProps) { 11 | 12 | return ( 13 | 14 | ); 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /ecommerce/src/mappings/Collection.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import { Collection } from "@/src/components/Collection/Collection"; 3 | 4 | // ❖ Collection 5 | interface FigmaCollectionProps extends BaseFigmaProps { 6 | Collection: string; 7 | } 8 | 9 | figmaMapping({ 10 | componentKey: "cdd285abc6752847b63424a7d063a968c5e70330", 11 | mapper(figma: FigmaCollectionProps) { 12 | return ; 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /marketing/components/Counter/styles.module.css: -------------------------------------------------------------------------------- 1 | .counter { 2 | margin: 32px auto; 3 | display: flex; 4 | width: 100%; 5 | max-width: 190px; 6 | } 7 | 8 | .btn { 9 | width: 42px; 10 | font-size: 32px; 11 | font-weight: bold; 12 | background-color: #1c6bd1; 13 | color: white; 14 | border: none; 15 | border-radius: 4px; 16 | cursor: pointer; 17 | line-height: 1.4; 18 | } 19 | 20 | .btn:hover { 21 | opacity: 0.8; 22 | } 23 | 24 | .count { 25 | flex: 1; 26 | font-size: 42px; 27 | text-align: center; 28 | } 29 | -------------------------------------------------------------------------------- /vcp-playground/src/mappings/Collection.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import { Collection } from "@/src/components/Collection/Collection"; 3 | 4 | // ❖ Collection 5 | interface FigmaCollectionProps extends BaseFigmaProps { 6 | Collection: string; 7 | } 8 | 9 | figmaMapping({ 10 | componentKey: "cdd285abc6752847b63424a7d063a968c5e70330", 11 | mapper(figma: FigmaCollectionProps) { 12 | return ; 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /ecommerce/src/components/Counter/styles.module.css: -------------------------------------------------------------------------------- 1 | .counter { 2 | margin: 32px auto; 3 | display: flex; 4 | width: 100%; 5 | max-width: 190px; 6 | } 7 | 8 | .btn { 9 | width: 42px; 10 | font-size: 32px; 11 | font-weight: bold; 12 | background-color: #1c6bd1; 13 | color: white; 14 | border: none; 15 | border-radius: 4px; 16 | cursor: pointer; 17 | line-height: 1.4; 18 | } 19 | 20 | .btn:hover { 21 | opacity: 0.8; 22 | } 23 | 24 | .count { 25 | flex: 1; 26 | font-size: 42px; 27 | text-align: center; 28 | } 29 | -------------------------------------------------------------------------------- /vcp-playground/src/components/Counter/styles.module.css: -------------------------------------------------------------------------------- 1 | .counter { 2 | margin: 32px auto; 3 | display: flex; 4 | width: 100%; 5 | max-width: 190px; 6 | } 7 | 8 | .btn { 9 | width: 42px; 10 | font-size: 32px; 11 | font-weight: bold; 12 | background-color: #1c6bd1; 13 | color: white; 14 | border: none; 15 | border-radius: 4px; 16 | cursor: pointer; 17 | line-height: 1.4; 18 | } 19 | 20 | .btn:hover { 21 | opacity: 0.8; 22 | } 23 | 24 | .count { 25 | flex: 1; 26 | font-size: 42px; 27 | text-align: center; 28 | } 29 | -------------------------------------------------------------------------------- /ecommerce/config/swell.ts: -------------------------------------------------------------------------------- 1 | if (!process.env.NEXT_PUBLIC_SWELL_STORE_ID) { 2 | throw new Error( 3 | "Missing required environment variable: NEXT_PUBLIC_STORE_ID" 4 | ); 5 | } 6 | if (!process.env.NEXT_PUBLIC_SWELL_PUBLIC_KEY) { 7 | throw new Error( 8 | "Missing required environment variable: NEXT_PUBLIC_SWELL_PUBLIC_KEY" 9 | ); 10 | } 11 | 12 | const swellConfig = { 13 | storeId: process.env.NEXT_PUBLIC_SWELL_STORE_ID || "", 14 | publicKey: process.env.NEXT_PUBLIC_SWELL_PUBLIC_KEY || "", 15 | }; 16 | 17 | export default swellConfig; 18 | -------------------------------------------------------------------------------- /ecommerce/src/mappings/TextHero.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import TextHero from "@/src/components/Hero/TextHero"; 3 | 4 | // ❖ TextHero 5 | interface FigmaTextHeroProps extends BaseFigmaProps { 6 | Title?: string; 7 | } 8 | 9 | figmaMapping({ 10 | componentKey: "9fe4060f538441a94effbb4fc38caf7989e7b66d", 11 | mapper(figma: FigmaTextHeroProps) { 12 | return ( 13 | 14 | ); 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /vcp-playground/src/mappings/TextHero.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import TextHero from "@/src/components/Hero/TextHero"; 3 | 4 | // ❖ TextHero 5 | interface FigmaTextHeroProps extends BaseFigmaProps { 6 | Title?: string; 7 | } 8 | 9 | figmaMapping({ 10 | componentKey: "9fe4060f538441a94effbb4fc38caf7989e7b66d", 11 | mapper(figma: FigmaTextHeroProps) { 12 | return ( 13 | 14 | ); 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /ecommerce/.env: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_BUILDER_API_KEY=a87584e551b6472fa0f0a2eb10f2c0ff #main Ecomm APIkey 2 | # NEXT_PUBLIC_BUILDER_API_KEY=63f2ce2bdfab4e1fa0343b7d8a43d1d9 #Nestle Ecomm API key 3 | 4 | NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=unified-demo-builderio.myshopify.com 5 | NEXT_PUBLIC_SHOPIFY_STOREFRONT_API_TOKEN=c7cb35afad78a2b1be48a1898d800dad 6 | 7 | NEXT_PUBLIC_SWELL_STORE_ID=builder-store 8 | NEXT_PUBLIC_SWELL_PUBLIC_KEY=pk_test_Y6L084xFtHln0rmvBjCAWwJtZglcc1Um 9 | 10 | NEXT_PUBLIC_ALGOLIA_APP_ID=Q072QNXOR4 11 | NEXT_PUBLIC_ALGOLIA_API_KEY=64fc283015a83bc7a7c15968b152db32 12 | -------------------------------------------------------------------------------- /marketing/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "Create Next App", 9 | description: "Generated by create next app", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /marketing/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /ecommerce/app/custom-components/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "@/src/components/builder"; 3 | 4 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 5 | 6 | interface CustomComponentsPage { 7 | params: { 8 | page: string[]; 9 | }; 10 | } 11 | 12 | export default async function Page(props: CustomComponentsPage) { 13 | const builderModelName = "custom-component-showcase"; 14 | 15 | return ( 16 | <> 17 | {/* Render the Builder page */} 18 | 19 | 20 | ); 21 | } -------------------------------------------------------------------------------- /marketing/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | .env.development 32 | .env.production 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | -------------------------------------------------------------------------------- /ecommerce/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /marketing/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ecommerce/config/shopify.ts: -------------------------------------------------------------------------------- 1 | import { Config } from 'shopify-buy' 2 | 3 | if (!process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN) { 4 | throw new Error('Missing required environment variable SHOPIFY_STORE_DOMAIN') 5 | } 6 | if (!process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_API_TOKEN) { 7 | throw new Error( 8 | 'Missing required environment variable SHOPIFY_STOREFRONT_API_TOKEN' 9 | ) 10 | } 11 | const shopifyConfig: Config = { 12 | domain: process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN || '', 13 | storefrontAccessToken: process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_API_TOKEN || '', 14 | apiVersion: '2024-01' 15 | } 16 | 17 | export default shopifyConfig; -------------------------------------------------------------------------------- /vcp-playground/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /ecommerce/app/stores/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { builder } from "@builder.io/sdk"; 3 | import { RenderBuilderContent } from "@/src/components/builder"; 4 | 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | export default async function Page(props: { params: { page: any[] } }) { 8 | const content = await builder 9 | .get("store-page", { 10 | userAttributes: { 11 | urlPath: "/" + (props?.params?.page?.join("/") || ""), 12 | }, 13 | }) 14 | .toPromise(); 15 | 16 | return ( 17 | <> 18 | {/* Render the Builder page */} 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /ecommerce/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /marketing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /ecommerce/app/help-center/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { builder } from "@builder.io/sdk"; 3 | import { RenderBuilderContent } from "@/src/components/builder"; 4 | 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | export default async function Page(props: { params: { page: any[] } }) { 8 | const content = await builder 9 | .get("help-center", { 10 | userAttributes: { 11 | urlPath: "/" + (props?.params?.page?.join("/") || ""), 12 | }, 13 | }) 14 | .toPromise(); 15 | 16 | return ( 17 | <> 18 | {/* Render the Builder page */} 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /ecommerce/src/components/Hero/TextHero.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | 6 | interface TextHeroProps { 7 | title: string; 8 | subTitle?: string; 9 | } 10 | 11 | const TextHero: React.FC = ({ title, subTitle }) => { 12 | return ( 13 |
14 |

{title}

15 |
16 |
17 | ); 18 | }; 19 | 20 | export default TextHero; -------------------------------------------------------------------------------- /ecommerce/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | /app/testing/ 4 | /components/testing/ 5 | 6 | # dependencies 7 | /node_modules 8 | /.pnp 9 | .pnp.js 10 | .yarn/install-state.gz 11 | 12 | # testing 13 | /coverage 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | 26 | # debug 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # local env files 32 | .env*.local 33 | .env.development 34 | .env.production 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | 43 | /app/testing 44 | /components/testing 45 | -------------------------------------------------------------------------------- /vcp-playground/src/components/Hero/TextHero.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | 6 | interface TextHeroProps { 7 | title: string; 8 | subTitle?: string; 9 | } 10 | 11 | const TextHero: React.FC = ({ title, subTitle }) => { 12 | return ( 13 |
14 |

{title}

15 |
16 |
17 | ); 18 | }; 19 | 20 | export default TextHero; -------------------------------------------------------------------------------- /ecommerce/src/mappings/HeroWithChildren.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import HeroWithChildren from "@/src/components/Hero/HeroWithChildren"; 3 | 4 | // ❖ HeroWithChildren 5 | interface FigmaHeroWithChildren extends BaseFigmaProps { 6 | Header?: string; 7 | } 8 | 9 | figmaMapping({ 10 | componentKey: "df1998bf6ffbd4f7a7f4005da8a0ea47b14a85a4", 11 | mapper(figma: FigmaHeroWithChildren) { 12 | return ( 13 | 18 | ); 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /vcp-playground/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import "./globals.css"; 3 | import QueryProvider from "@/src/components/QueryProvider"; 4 | 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | export default async function RootLayout({ 8 | children, 9 | }: Readonly<{ 10 | children: React.ReactNode; 11 | }>) { 12 | 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
{children}
22 |
23 |
24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /vcp-playground/src/mappings/HeroWithChildren.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import HeroWithChildren from "@/src/components/Hero/HeroWithChildren"; 3 | 4 | // ❖ HeroWithChildren 5 | interface FigmaHeroWithChildren extends BaseFigmaProps { 6 | Header?: string; 7 | } 8 | 9 | figmaMapping({ 10 | componentKey: "df1998bf6ffbd4f7a7f4005da8a0ea47b14a85a4", 11 | mapper(figma: FigmaHeroWithChildren) { 12 | return ( 13 | 18 | ); 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /vcp-playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /marketing/components/Counter/Counter.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React, { useState } from "react"; 3 | import styles from "./styles.module.css"; 4 | 5 | interface CounterProps { 6 | initialCount?: number; 7 | } 8 | 9 | const Counter = ({ initialCount = 99 }: CounterProps) => { 10 | const [count, setCount] = useState(initialCount); 11 | 12 | const updateCount = (amount: number) => () => setCount(count + amount); 13 | 14 | return ( 15 |
16 | 19 | {count} 20 | 23 |
24 | ); 25 | }; 26 | 27 | export default Counter; 28 | -------------------------------------------------------------------------------- /marketing/components/Hero/TextHero.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | 6 | interface TextHeroProps { 7 | title: string; 8 | subTitle: string; 9 | } 10 | 11 | const TextHero: React.FC = ({ title, subTitle }) => { 12 | return ( 13 |
14 |

{title}

15 |
16 | {/*
{subTitle}
*/} 17 | 18 |
19 | ); 20 | }; 21 | 22 | export default TextHero; -------------------------------------------------------------------------------- /marketing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ce-nextjs-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@builder.io/dev-tools": "^1.0.2", 13 | "@builder.io/react": "^3.2.7", 14 | "@builder.io/sdk": "^2.2.2", 15 | "next": "14.1.4", 16 | "react": "^18", 17 | "react-dom": "^18" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^20", 21 | "@types/react": "^18", 22 | "@types/react-dom": "^18", 23 | "autoprefixer": "^10.0.1", 24 | "eslint": "^8", 25 | "eslint-config-next": "14.1.4", 26 | "postcss": "^8", 27 | "tailwindcss": "^3.3.0", 28 | "typescript": "^5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ecommerce/src/components/Counter/Counter.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React, { useState } from "react"; 3 | import styles from "./styles.module.css"; 4 | 5 | interface CounterProps { 6 | initialCount?: number; 7 | } 8 | 9 | const Counter = ({ initialCount = 99 }: CounterProps) => { 10 | const [count, setCount] = useState(initialCount); 11 | 12 | const updateCount = (amount: number) => () => setCount(count + amount); 13 | 14 | return ( 15 |
16 | 19 | {count} 20 | 23 |
24 | ); 25 | }; 26 | 27 | export default Counter; 28 | -------------------------------------------------------------------------------- /vcp-playground/src/components/Counter/Counter.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React, { useState } from "react"; 3 | import styles from "./styles.module.css"; 4 | 5 | interface CounterProps { 6 | initialCount?: number; 7 | } 8 | 9 | const Counter = ({ initialCount = 99 }: CounterProps) => { 10 | const [count, setCount] = useState(initialCount); 11 | 12 | const updateCount = (amount: number) => () => setCount(count + amount); 13 | 14 | return ( 15 |
16 | 19 | {count} 20 | 23 |
24 | ); 25 | }; 26 | 27 | export default Counter; 28 | -------------------------------------------------------------------------------- /ecommerce/src/components/PLP/Pagination.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | 6 | export const Pagination: React.FC = () => { 7 | return ( 8 |
9 |
10 | 1 2 3 11 |
12 | Next page 19 |
20 | ); 21 | }; -------------------------------------------------------------------------------- /marketing/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /ecommerce/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /ecommerce/src/mappings/IconCard.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import IconCard from "@/src/components/Card/IconCard"; 3 | 4 | // ❖ IconCard 5 | interface FigmaIconCardProps extends BaseFigmaProps { 6 | Icon?: string; 7 | Title?: string; 8 | Description?: string; 9 | Alignment: "center" | "left" | "right"; 10 | } 11 | 12 | figmaMapping({ 13 | componentKey: "41d8f0857eeaf8760328fdfc07620326f1f761e4", 14 | mapper(figma: FigmaIconCardProps) { 15 | return ( 16 | 23 | ); 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /vcp-playground/src/mappings/IconCard.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import IconCard from "@/src/components/Card/IconCard"; 3 | 4 | // ❖ IconCard 5 | interface FigmaIconCardProps extends BaseFigmaProps { 6 | Icon?: string; 7 | Title?: string; 8 | Description?: string; 9 | Alignment: "center" | "left" | "right"; 10 | } 11 | 12 | figmaMapping({ 13 | componentKey: "41d8f0857eeaf8760328fdfc07620326f1f761e4", 14 | mapper(figma: FigmaIconCardProps) { 15 | return ( 16 | 23 | ); 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /ecommerce/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from '@builder.io/sdk'; 2 | import { RenderBuilderContent } from '@/src/components/builder'; 3 | 4 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 5 | 6 | interface PageProps { 7 | params: { 8 | page: string[]; 9 | }; 10 | } 11 | 12 | export default async function Homepage(props: PageProps) { 13 | const builderModelName = 'homepage'; 14 | 15 | const content = await builder 16 | // Get the page content from Builder with the specified options 17 | .get(builderModelName, { 18 | userAttributes: { 19 | loggedIn: true, 20 | }, 21 | }) 22 | // Convert the result to a promise 23 | .toPromise(); 24 | 25 | return ( 26 | <> 27 | {/* Render the Builder page */} 28 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /marketing/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /ecommerce/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /ecommerce/app/symbols/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "@/src/components/builder"; 3 | 4 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 5 | 6 | interface PageProps { 7 | params: { 8 | page: string[]; 9 | }; 10 | } 11 | 12 | export default async function Page(props: PageProps) { 13 | const builderModelName = "symbol"; 14 | 15 | const content = await builder 16 | // Get the page content from Builder with the specified options 17 | .get(builderModelName, { 18 | userAttributes: { 19 | // Use the page path specified in the URL to fetch the content 20 | urlPath: "/" + (props?.params?.page?.join("/") || ""), 21 | }, 22 | }) 23 | // Convert the result to a promise 24 | .toPromise(); 25 | 26 | return ( 27 | <> 28 | {/* Render the Builder page */} 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /ecommerce/src/mappings/ProductCard.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import ProductCard from "@/src/components/Card/ProductCard"; 3 | 4 | // ❖ ProductCard 5 | interface FigmaProductCardProps extends BaseFigmaProps { 6 | exampleProduct?: { 7 | "@type": "@builder.io/core:Reference"; 8 | id: string; 9 | model: string; 10 | } 11 | } 12 | 13 | figmaMapping({ 14 | componentKey: "d2f114400698059ab75be459a13d26a99d85f47b", 15 | mapper(figma: FigmaProductCardProps) { 16 | const exampleProduct = figma.exampleProduct ?? { 17 | "@type": "@builder.io/core:Reference", 18 | id: "50b344f9116e4820a020e382058146e0_088c35a5a6914ac68b99a4ea12abba6a", 19 | model: "product-data", 20 | }; 21 | 22 | return ( 23 | 29 | ); 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /marketing/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "../components/builder"; 3 | 4 | // Builder Public API Key set in .env file 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | interface PageProps { 8 | params: { 9 | page: string[]; 10 | }; 11 | } 12 | 13 | export default async function Homepage(props: PageProps) { 14 | const builderModelName = "homepage"; 15 | 16 | const content = await builder 17 | // Get the page content from Builder with the specified options 18 | .get(builderModelName, { 19 | userAttributes: { 20 | // Use the page path specified in the URL to fetch the content 21 | urlPath: "/", 22 | }, 23 | }) 24 | // Convert the result to a promise 25 | .toPromise(); 26 | 27 | return ( 28 | <> 29 | {/* Render the Builder page */} 30 |
Hello
31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /ecommerce/app/figma-imports/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "@/src/components/builder"; 3 | 4 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 5 | 6 | interface PageProps { 7 | params: { 8 | page: string[]; 9 | }; 10 | } 11 | 12 | export default async function Page(props: PageProps) { 13 | const builderModelName = "figma-imports"; 14 | 15 | const content = await builder 16 | // Get the page content from Builder with the specified options 17 | .get(builderModelName, { 18 | userAttributes: { 19 | // Use the page path specified in the URL to fetch the content 20 | urlPath: "/" + (props?.params?.page?.join("/") || ""), 21 | }, 22 | }) 23 | // Convert the result to a promise 24 | .toPromise(); 25 | 26 | return ( 27 | <> 28 | {/* Render the Builder page */} 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /marketing/app/[...page]/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "../../components/builder"; 3 | 4 | // Builder Public API Key set in .env file 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | interface PageProps { 8 | params: { 9 | page: string[]; 10 | }; 11 | } 12 | 13 | export default async function Page(props: PageProps) { 14 | const builderModelName = "page"; 15 | 16 | const content = await builder 17 | // Get the page content from Builder with the specified options 18 | .get(builderModelName, { 19 | userAttributes: { 20 | // Use the page path specified in the URL to fetch the content 21 | urlPath: "/" + (props?.params?.page?.join("/") || ""), 22 | }, 23 | }) 24 | // Convert the result to a promise 25 | .toPromise(); 26 | 27 | return ( 28 | <> 29 | {/* Render the Builder page */} 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /vcp-playground/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vcp-playground/app/figma-imports/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "@/src/components/builder"; 3 | 4 | // Builder Public API Key set in .env file 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | interface PageProps { 8 | params: Promise<{ 9 | page: string[]; 10 | }>; 11 | } 12 | 13 | export default async function Page(props: PageProps) { 14 | const builderModelName = "figma-imports"; 15 | 16 | const content = await builder 17 | // Get the page content from Builder with the specified options 18 | .get(builderModelName, { 19 | userAttributes: { 20 | // Use the page path specified in the URL to fetch the content 21 | urlPath: "/" + ((await props?.params)?.page?.join("/") || ""), 22 | }, 23 | }) 24 | // Convert the result to a promise 25 | .toPromise(); 26 | 27 | return ( 28 | <> 29 | {/* Render the Builder page */} 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /ecommerce/src/components/CommercetoolsProduct/CommercetoolsProduct.tsx: -------------------------------------------------------------------------------- 1 | import ProductBox from "../ui/productBox"; 2 | 3 | const CommercetoolsProduct = ({ commercetoolsProduct }: { commercetoolsProduct: any }) => { 4 | const product = commercetoolsProduct?.data?.masterData?.current 5 | const productData = { 6 | data: { 7 | images: product?.masterVariant?.images?.map( 8 | (image: { url: string; altText?: string }) => ({ 9 | image: image.url, 10 | altText: image.altText || "Product image", 11 | }) 12 | ), 13 | productName: product?.name["en-US"], 14 | price: product?.masterVariant?.prices?.[0]?.value?.centAmount, 15 | }, 16 | }; 17 | return ( 18 |
19 | 24 |
25 | ); 26 | }; 27 | 28 | export default CommercetoolsProduct; 29 | -------------------------------------------------------------------------------- /ecommerce/src/components/Hero/HeroWithChildren.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React, {ComponentProps } from "react"; 5 | import { BuilderBlocks } from "@builder.io/react"; 6 | 7 | interface HeroWithChildrenProps { 8 | childBlocks: any, 9 | header: string, 10 | makeFullBleed: boolean 11 | } 12 | 13 | const HeroWithChildren: React.FC = (props:any) => { 14 | return ( 15 |
16 |

17 | {props.header} 18 |

19 | 25 |
26 | ); 27 | }; 28 | 29 | export default HeroWithChildren; -------------------------------------------------------------------------------- /marketing/components/Hero/HeroWithChildren.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React, {ComponentProps } from "react"; 5 | import { BuilderBlocks } from "@builder.io/react"; 6 | 7 | interface HeroWithChildrenProps { 8 | childBlocks: [], 9 | header: string, 10 | makeFullBleed: boolean 11 | } 12 | 13 | const HeroWithChildren: React.FC = (props:any) => { 14 | return ( 15 |
16 |

17 | {props.header} 18 |

19 | 25 |
26 | ); 27 | }; 28 | 29 | export default HeroWithChildren; -------------------------------------------------------------------------------- /vcp-playground/src/components/Hero/HeroWithChildren.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React, {ComponentProps } from "react"; 5 | import { BuilderBlocks } from "@builder.io/react"; 6 | 7 | interface HeroWithChildrenProps { 8 | childBlocks: any, 9 | header: string, 10 | makeFullBleed: boolean 11 | } 12 | 13 | const HeroWithChildren: React.FC = (props:any) => { 14 | return ( 15 |
16 |

17 | {props.header} 18 |

19 | 25 |
26 | ); 27 | }; 28 | 29 | export default HeroWithChildren; -------------------------------------------------------------------------------- /ecommerce/src/mappings/Hero.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import ImageHero from "@/src/components/Hero/ImageHero"; 3 | 4 | // ❖ Hero 5 | interface FigmaHeroProps extends BaseFigmaProps { 6 | buttonText?: string; 7 | Title?: string; 8 | } 9 | 10 | figmaMapping({ 11 | componentKey: "4bd6da0f53b73a462b070b55dd055ce6a4cb3eca", 12 | mapper(figma: FigmaHeroProps) { 13 | return ( 14 | 31 | ); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /vcp-playground/src/mappings/Hero.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import ImageHero from "@/src/components/Hero/ImageHero"; 3 | 4 | // ❖ Hero 5 | interface FigmaHeroProps extends BaseFigmaProps { 6 | buttonText?: string; 7 | Title?: string; 8 | } 9 | 10 | figmaMapping({ 11 | componentKey: "4bd6da0f53b73a462b070b55dd055ce6a4cb3eca", 12 | mapper(figma: FigmaHeroProps) { 13 | return ( 14 | 31 | ); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /marketing/components/builder.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { ComponentProps } from "react"; 3 | import { BuilderComponent, Builder, builder, useIsPreviewing } from "@builder.io/react"; 4 | import DefaultErrorPage from "next/error"; 5 | import "../builder-registry"; 6 | 7 | type BuilderPageProps = ComponentProps; 8 | 9 | // Builder Public API Key set in .env file 10 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 11 | 12 | export function RenderBuilderContent(props: BuilderPageProps) { 13 | // Call the useIsPreviewing hook to determine if 14 | // the page is being previewed in Builder 15 | const isPreviewing = useIsPreviewing();///(Builder.isPreviewing || Builder.isEditing) 16 | // If "content" has a value or the page is being previewed in Builder, 17 | // render the BuilderComponent with the specified content and model props. 18 | if (props.content || isPreviewing) { 19 | return ; 20 | } 21 | // If the "content" is falsy and the page is 22 | // not being previewed in Builder, render the 23 | // DefaultErrorPage with a 404. 24 | return ; 25 | } 26 | -------------------------------------------------------------------------------- /ecommerce/app/page/[page]/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "@/src/components/builder"; 3 | 4 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 5 | 6 | interface PageProps { 7 | params: { 8 | page: string[]; 9 | }; 10 | } 11 | 12 | // export const revalidate = 500; 13 | 14 | export default async function Page(props: PageProps) { 15 | const builderModelName = "freeform-page"; 16 | let locale = "en-US"; 17 | 18 | const content = await builder 19 | // Get the page content from Builder with the specified options 20 | .get(builderModelName, { 21 | userAttributes: { 22 | // Use the page path specified in the URL to fetch the content 23 | urlPath: "/page/" + (props?.params?.page || ""), 24 | locale, 25 | }, 26 | locale 27 | }) 28 | // Convert the result to a promise 29 | .toPromise(); 30 | 31 | return ( 32 | <> 33 | {/* Render the Builder page */} 34 | 35 | 36 | ); 37 | } -------------------------------------------------------------------------------- /ecommerce/src/mappings/SplitHero.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import SplitHero from "@/src/components/Hero/SplitHero"; 3 | 4 | // ❖ SplitHero 5 | interface FigmaSplitHeroProps extends BaseFigmaProps { 6 | ButtonText?: string; 7 | Subtitle?: string; 8 | imageUrl?: string; 9 | Title?: string; 10 | } 11 | 12 | figmaMapping({ 13 | componentKey: "1143d39901c24e64dd2a65188ff027054138033f", 14 | mapper(figma: FigmaSplitHeroProps) { 15 | return ( 16 | 33 | ); 34 | } 35 | }) -------------------------------------------------------------------------------- /vcp-playground/src/mappings/SplitHero.mapper.tsx: -------------------------------------------------------------------------------- 1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma"; 2 | import SplitHero from "@/src/components/Hero/SplitHero"; 3 | 4 | // ❖ SplitHero 5 | interface FigmaSplitHeroProps extends BaseFigmaProps { 6 | ButtonText?: string; 7 | Subtitle?: string; 8 | imageUrl?: string; 9 | Title?: string; 10 | } 11 | 12 | figmaMapping({ 13 | componentKey: "1143d39901c24e64dd2a65188ff027054138033f", 14 | mapper(figma: FigmaSplitHeroProps) { 15 | return ( 16 | 33 | ); 34 | } 35 | }) -------------------------------------------------------------------------------- /marketing/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import * as React from "react" 3 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 4 | import { Check } from "lucide-react" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Checkbox = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 23 | 24 | 25 | 26 | )) 27 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 28 | 29 | export { Checkbox } 30 | -------------------------------------------------------------------------------- /ecommerce/src/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import * as React from "react" 3 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 4 | import { Check } from "lucide-react" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Checkbox = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 23 | 24 | 25 | 26 | )) 27 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 28 | 29 | export { Checkbox } 30 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import * as React from "react" 3 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 4 | import { Check } from "lucide-react" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Checkbox = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 23 | 24 | 25 | 26 | )) 27 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 28 | 29 | export { Checkbox } 30 | -------------------------------------------------------------------------------- /vcp-playground/src/components/builder.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { ComponentProps } from "react"; 3 | import { BuilderComponent, Builder, builder, useIsPreviewing } from "@builder.io/react"; 4 | import DefaultErrorPage from "next/error"; 5 | import "../builder-registry"; 6 | 7 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 8 | 9 | type BuilderPageProps = ComponentProps; 10 | 11 | export function RenderBuilderContent(props: BuilderPageProps) { 12 | // Call the useIsPreviewing hook to determine if 13 | // the page is being previewed in Builder 14 | const isPreviewing = useIsPreviewing();///(Builder.isPreviewing || Builder.isEditing) 15 | // If "content" has a value or the page is being previewed in Builder, 16 | // render the BuilderComponent with the specified content and model props. 17 | // If the "content" is falsy and the page is 18 | if (props.content || isPreviewing) { 19 | return ( 20 | <> 21 | 22 | 23 | ); 24 | } 25 | // If the "content" is falsy and the page is 26 | // not being previewed in Builder, render the 27 | // DefaultErrorPage with a 404. 28 | return ; 29 | } -------------------------------------------------------------------------------- /marketing/components/Card/ProductCard.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | // "use client" 5 | import React from "react"; 6 | 7 | interface ProductCardProps { 8 | product: any, 9 | classes: string; 10 | } 11 | 12 | const ProductCard: React.FC = ({ product, classes }) => { 13 | // product.data if its in a visual editor repeat, otherwise for a straight 14 | // data binding, use product.value.data 15 | let productData = product?.data || product?.value?.data; 16 | return ( 17 | 27 | ); 28 | }; 29 | 30 | export default ProductCard; -------------------------------------------------------------------------------- /vcp-playground/src/components/Card/ProductCard.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import ProductBox from "../ui/productBox"; 3 | 4 | interface ProductCardProps { 5 | classes?: string; 6 | dataSource?: string; 7 | product?: any; 8 | } 9 | 10 | const ProductCard: React.FC = ({ 11 | classes, 12 | dataSource, 13 | product, 14 | }) => { 15 | dataSource = dataSource || "Builder"; 16 | const [ productData, setProduct ] = useState(product); 17 | 18 | useEffect(() => { 19 | async function fetchContent() { 20 | await fetch("https://cdn.builder.io/api/v3/content/product-data?apiKey=a87584e551b6472fa0f0a2eb10f2c0ff&query.id=088c35a5a6914ac68b99a4ea12abba6a").then 21 | (async (res) => { 22 | const data = await res.json(); 23 | setProduct(data.results[0]); 24 | }); 25 | ; 26 | } 27 | if (!product) fetchContent(); 28 | }, [product]); 29 | 30 | 31 | return ( 32 |
35 | 36 |
37 | ); 38 | }; 39 | 40 | export default ProductCard; 41 | -------------------------------------------------------------------------------- /ecommerce/lib/swell/api.ts: -------------------------------------------------------------------------------- 1 | import swell from "swell-js"; 2 | 3 | swell.init( 4 | process.env.NEXT_PUBLIC_SWELL_STORE_ID as string, 5 | process.env.NEXT_PUBLIC_SWELL_PUBLIC_KEY as string 6 | ); 7 | 8 | interface Product { 9 | id?: string; 10 | name?: string; 11 | slug?: string; 12 | price?: number; 13 | images?: any[]; 14 | } 15 | 16 | export async function getAllProducts(limit?: number): Promise { 17 | const response = await swell.products.list({ limit }); 18 | return response.results.filter((product: Product) => product.id); 19 | } 20 | 21 | export async function getAllProductPaths(limit?: number): Promise { 22 | const response = await swell.products.list({ limit }); 23 | return response.results 24 | .map((product) => product.slug) 25 | .filter((slug): slug is string => slug !== undefined); 26 | } 27 | 28 | export async function getProduct(options: { 29 | id?: string; 30 | handle?: string; 31 | }): Promise { 32 | const product = options.handle 33 | ? await swell.products.get(options.handle) 34 | : options.id 35 | ? await swell.products.get(options.id) 36 | : null; 37 | 38 | if (!product) throw new Error("A product ID or handle is required"); 39 | return product; 40 | } 41 | -------------------------------------------------------------------------------- /ecommerce/src/components/builder.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { ComponentProps } from "react"; 3 | import { 4 | BuilderComponent, 5 | Builder, 6 | builder, 7 | useIsPreviewing, 8 | } from "@builder.io/react"; 9 | import DefaultErrorPage from "next/error"; 10 | import "../builder-registry"; 11 | 12 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 13 | 14 | type BuilderPageProps = ComponentProps; 15 | 16 | export function RenderBuilderContent(props: BuilderPageProps) { 17 | // Call the useIsPreviewing hook to determine if 18 | // the page is being previewed in Builder 19 | const isPreviewing = useIsPreviewing(); ///(Builder.isPreviewing || Builder.isEditing) 20 | // If "content" has a value or the page is being previewed in Builder, 21 | // render the BuilderComponent with the specified content and model props. 22 | // If the "content" is falsy and the page is 23 | if (props.content || isPreviewing) { 24 | return ( 25 | <> 26 | 27 | 28 | ); 29 | } 30 | // If the "content" is falsy and the page is 31 | // not being previewed in Builder, render the 32 | // DefaultErrorPage with a 404. 33 | return ; 34 | } 35 | -------------------------------------------------------------------------------- /ecommerce/src/components/PLP/CategoryFilter.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | import { Checkbox } from '@/src/components/ui/checkbox'; 6 | 7 | interface CategoryFilterProps { 8 | selectedCategories: string[]; 9 | setSelectedCategories: React.Dispatch>; 10 | } 11 | 12 | export const CategoryFilter: React.FC = ({ 13 | selectedCategories, 14 | setSelectedCategories, 15 | }) => { 16 | const categories = ['Jackets', 'Pea Coats', 'Leather', 'Vests']; 17 | 18 | const handleCategoryChange = (category: string) => { 19 | setSelectedCategories((prev) => 20 | prev.includes(category) 21 | ? prev.filter((c) => c !== category) 22 | : [...prev, category] 23 | ); 24 | }; 25 | 26 | return ( 27 | <> 28 | {categories.map((category) => ( 29 |
30 | handleCategoryChange(category)} 34 | /> 35 | 36 |
37 | ))} 38 | 39 | ); 40 | }; -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/productBox.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | interface ProductBoxProps { 4 | productData: any; 5 | dataSource: string; 6 | } 7 | 8 | const ProductBox: React.FC = ({ productData, dataSource }) => { 9 | let product = productData?.data || productData?.value?.data; 10 | return ( 11 | 12 |
13 | {product?.images?.[0]?.altText} 20 |
21 |
22 |
23 |
24 | {product?.productName} 25 |
26 |

${product?.price}

27 |
28 |

29 | {product?.colors?.[0]?.label} 30 |

31 |
32 |
33 | ); 34 | }; 35 | 36 | export default ProductBox; 37 | -------------------------------------------------------------------------------- /marketing/components/Card/IconCard.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from "react"; 5 | 6 | interface IconCardProps { 7 | icon: string; 8 | title: string; 9 | description: string; 10 | altText: string; 11 | coloredBackground?: boolean; 12 | alignment?: 'left' | 'center' | 'right'; 13 | } 14 | 15 | const IconCard: React.FC = ({ icon, title, description, altText, coloredBackground, alignment }) => { 16 | const textAlignment = `text-${alignment}`; 17 | //kinda hacky, items-left isnt valid but it reverts to left alignment so it... kinda works? 18 | const iconAlignment = alignment === 'right' ? 'items-end' : `items-${alignment}` 19 | 20 | return ( 21 |
22 |
23 |
24 | {altText} 25 |
26 |

{title}

27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default IconCard; -------------------------------------------------------------------------------- /ecommerce/src/components/Card/IconCard.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from "react"; 5 | 6 | interface IconCardProps { 7 | icon: string; 8 | title: string; 9 | description: string; 10 | altText: string; 11 | coloredBackground?: boolean; 12 | alignment?: 'left' | 'center' | 'right'; 13 | } 14 | 15 | const IconCard: React.FC = ({ icon, title, description, altText, coloredBackground, alignment }) => { 16 | const textAlignment = `text-${alignment}`; 17 | //kinda hacky, items-left isnt valid but it reverts to left alignment so it... kinda works? 18 | const iconAlignment = alignment === 'right' ? 'items-end' : `items-${alignment}` 19 | 20 | return ( 21 |
22 |
23 |
24 | {altText} 25 |
26 |

{title}

27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default IconCard; -------------------------------------------------------------------------------- /vcp-playground/src/components/Card/IconCard.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from "react"; 5 | 6 | interface IconCardProps { 7 | icon: string; 8 | title: string; 9 | description: string; 10 | altText: string; 11 | coloredBackground?: boolean; 12 | alignment?: 'left' | 'center' | 'right'; 13 | } 14 | 15 | const IconCard: React.FC = ({ icon, title, description, altText, coloredBackground, alignment }) => { 16 | const textAlignment = `text-${alignment}`; 17 | //kinda hacky, items-left isnt valid but it reverts to left alignment so it... kinda works? 18 | const iconAlignment = alignment === 'right' ? 'items-end' : `items-${alignment}` 19 | 20 | return ( 21 |
22 |
23 |
24 | {altText} 25 |
26 |

{title}

27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default IconCard; -------------------------------------------------------------------------------- /ecommerce/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /marketing/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vcp-playground/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ecommerce/README.md: -------------------------------------------------------------------------------- 1 | # Ecommerce 2 | 3 | Welcome to our ecommerce project! This README provides all the necessary information to get started, including links to our Jira board and Figma designs, as well as instructions for running the project locally. 4 | 5 | ## Links 6 | 7 | - **Jira Board**: Pick up tickets to build from our [Jira Board](https://builder-io.atlassian.net/jira/core/projects/CE/board). 8 | - **Figma Designs**: View our designs on [Figma](https://www.figma.com/design/uLnxlr3A1qA7g5PHVGoETT/Logan's-Playground?node-id=1-9&t=KQZ9egfmxQ5dU1Cv-0). 9 | 10 | ## Running the Project Locally 11 | 12 | To run the project locally, follow these steps: 13 | 14 | 1. **Clone the repository**: 15 | ```bash 16 | git clone https://github.com/BuilderIO/unified-demo 17 | cd ecommerce 18 | 19 | ## Getting Started 20 | 21 | First, run the development server: 22 | 23 | ```bash 24 | npm run install 25 | npm run dev 26 | # or 27 | yarn 28 | yarn dev 29 | # or 30 | pnpm install 31 | pnpm dev 32 | # or 33 | bun dev 34 | ``` 35 | ## Additional Information 36 | 37 | - Refer to the `.env.example` file to understand the required environment variables format. 38 | - Ensure that your local environment is configured correctly to match the hostname-based environment variable setup in `middleware.ts`. 39 | 40 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 41 | 42 | 43 | -------------------------------------------------------------------------------- /ecommerce/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { Header } from "@/src/components/Layout/Header"; 3 | import "./globals.css"; 4 | import Footer from "@/src/components/Layout/Footer"; 5 | import { RenderBuilderContent } from "@/src/components/builder"; 6 | import QueryProvider from "@/src/components/QueryProvider"; 7 | 8 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 9 | 10 | export default async function RootLayout({ 11 | children, 12 | }: Readonly<{ 13 | children: React.ReactNode; 14 | }>) { 15 | await import('isolated-vm'); 16 | const locale="en-US" 17 | 18 | const headerContent = await builder 19 | .get("header-links", { fields: "data" , options: { locale }}) 20 | .toPromise(); 21 | 22 | const bannerContent = await builder.get("banner", { 23 | userAttributes: {loggedIn: true}, 24 | options: { 25 | locale 26 | } 27 | }).toPromise(); 28 | 29 | return ( 30 | 31 | 32 | 33 |
34 | {bannerContent && } 35 |
36 |
{children}
37 |
38 |
39 |
40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /ecommerce/src/components/ui/productBox.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | interface ProductBoxProps { 4 | productData: any; 5 | dataSource: string; 6 | } 7 | 8 | const ProductBox: React.FC = ({ productData, dataSource }) => { 9 | let product = productData?.data || productData?.value?.data; 10 | 11 | if (!product?.images?.[0]?.image) { 12 | console.error("[ProductBox] No image found to display"); 13 | } 14 | 15 | return ( 16 | 17 |
18 | {product?.images?.[0]?.altText} 25 |
26 |
27 |
28 |
29 | {product?.productName} 30 |
31 |

${product?.price}

32 |
33 |

34 | {product?.colors?.[0]?.label} 35 |

36 |
37 |
38 | ); 39 | }; 40 | 41 | export default ProductBox; 42 | -------------------------------------------------------------------------------- /ecommerce/src/components/Accordion/accordion.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | interface AccordionItemProps { 4 | title: string; 5 | content: string; 6 | } 7 | 8 | export const AccordionItem: React.FC = ({ 9 | title, 10 | content, 11 | }) => { 12 | const [isOpen, setIsOpen] = useState(false); 13 | 14 | const accordionToggle = () => { 15 | setIsOpen(!isOpen); 16 | }; 17 | 18 | return ( 19 |
20 |
27 |
36 | {title} 37 |
38 | 39 | {isOpen &&
{content}
} 40 |
41 |
42 | ); 43 | }; 44 | 45 | interface AccordionProps { 46 | items: AccordionItemProps[]; 47 | } 48 | 49 | const Accordion: React.FC = ({ items }) => { 50 | return ( 51 |
52 | {items.map((item, index) => ( 53 | 58 | ))} 59 |
60 | ); 61 | }; 62 | 63 | export default Accordion; 64 | -------------------------------------------------------------------------------- /marketing/README.md: -------------------------------------------------------------------------------- 1 | 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). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | 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. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /vcp-playground/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | 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. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /ecommerce/app/[...page]/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from '@builder.io/sdk'; 2 | import { RenderBuilderContent } from '@/src/components/builder'; 3 | 4 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 5 | 6 | interface PageProps { 7 | params: { 8 | page: string[]; 9 | }; 10 | } 11 | 12 | export default async function Page(props: PageProps) { 13 | const builderModelName = 'page'; 14 | let locale = 'en-US'; 15 | 16 | const content = await builder 17 | // Get the page content from Builder with the specified options 18 | .get(builderModelName, { 19 | userAttributes: { 20 | // Use the page path specified in the URL to fetch the content 21 | urlPath: '/' + (props?.params?.page?.join('/') || ''), 22 | // When content is localized through Targeting, the locale should be passed to the userAttributes 23 | // As of Gen1 SDK v6.0.3 or Gen2 SDK v2.0.31, locale in either UserAttributes OR base options are checked for all locale features, and only one is needed 24 | // locale: locale, 25 | loggedIn: true, 26 | }, 27 | // When content is localized in-page, the locale should be passed directly to the options 28 | locale: locale, 29 | }) 30 | // Convert the result to a promise 31 | .toPromise(); 32 | 33 | return ( 34 | <> 35 | {/* Render the Builder page */} 36 | 43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /ecommerce/app/blog/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderLiveDataPreview } from "@/src/components/builderLiveDataPreview"; 3 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 4 | 5 | interface BlogPageProps { 6 | params: { 7 | slug: string[]; 8 | }; 9 | } 10 | 11 | export default async function BlogPage(props: BlogPageProps) { 12 | const builderBlogModelName = "blog-article-data"; 13 | const builderBlogTemplateModelName = "blog-template"; 14 | 15 | const blogData = await builder 16 | // Get the page content from Builder with the specified options 17 | .get(builderBlogModelName, { 18 | query: { 19 | data: { 20 | slug: props?.params?.slug, 21 | }, 22 | }, 23 | locale: "en-US", 24 | }) 25 | // Convert the result to a promise 26 | .toPromise(); 27 | 28 | const blogTemplate = await builder 29 | // Get the page content from Builder with the specified options 30 | .get(builderBlogTemplateModelName, { 31 | userAttributes: { 32 | category: blogData?.data?.category, 33 | urlPath: `/blog/${props?.params?.slug}`, 34 | }, 35 | locale: "en-US", 36 | }) 37 | // Convert the result to a promise 38 | .toPromise(); 39 | 40 | return ( 41 | <> 42 | {/* Render the Builder page */} 43 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /ecommerce/app/stores/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import { RenderBuilderContent } from "@/src/components/builder"; 3 | import React from "react"; 4 | 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | interface PageProps { 8 | params: { 9 | slug: string; 10 | }; 11 | } 12 | 13 | export default async function StorePage(props: PageProps) { 14 | const { slug: storeSlug } = props.params; 15 | 16 | const storeData = await builder 17 | .get("store-data", { 18 | query: { "data.slug": storeSlug }, 19 | prerender: false, 20 | }) 21 | .toPromise(); 22 | 23 | const storeLocations = await builder 24 | .get("store-locations", { 25 | userAttributes: { 26 | urlPath: `/${storeSlug}`, 27 | }, 28 | prerender: false, 29 | }) 30 | .toPromise(); 31 | 32 | if (!storeData) { 33 | return
Store not found
; 34 | } 35 | 36 | return ( 37 | <> 38 |
39 | {storeData && ( 40 | 41 | )} 42 |
43 | {storeLocations && ( 44 | 55 | )} 56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /ecommerce/src/components/SwellProduct/SwellProduct.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { getProduct } from "@/lib/swell/api"; 3 | import ProductBox from "../ui/productBox"; 4 | 5 | const SwellProduct = ({ 6 | swellProductHandle, 7 | }: { 8 | swellProductHandle: string; 9 | }) => { 10 | const [product, setProduct] = useState(null); 11 | const [loading, setLoading] = useState(false); 12 | 13 | useEffect(() => { 14 | const fetchProduct = async () => { 15 | setLoading(true); 16 | try { 17 | const fetchedProduct = await getProduct({ 18 | handle: swellProductHandle, 19 | }); 20 | setProduct(fetchedProduct); 21 | } catch (error) { 22 | console.error("fetching product error", error); 23 | } finally { 24 | setLoading(false); 25 | } 26 | }; 27 | 28 | if (swellProductHandle) { 29 | fetchProduct(); 30 | } 31 | }, [swellProductHandle]); 32 | 33 | if (loading) return
Loading Swell product...
; 34 | if (!product) return
No product found.
; 35 | 36 | const productData = { 37 | data: { 38 | images: product.images.map( 39 | (image: { file: { url: string }; altText: string }) => ({ 40 | image: image.file.url, 41 | altText: image.altText || "Product image", 42 | }) 43 | ), 44 | productName: product.name, 45 | price: Math.round(product.price), 46 | }, 47 | }; 48 | 49 | return ( 50 |
51 | 52 |
53 | ); 54 | }; 55 | 56 | export default SwellProduct; 57 | -------------------------------------------------------------------------------- /marketing/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" 5 | import { Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const RadioGroup = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => { 13 | return ( 14 | 19 | ) 20 | }) 21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName 22 | 23 | const RadioGroupItem = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => { 27 | return ( 28 | 36 | 37 | 38 | 39 | 40 | ) 41 | }) 42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName 43 | 44 | export { RadioGroup, RadioGroupItem } 45 | -------------------------------------------------------------------------------- /ecommerce/src/components/QueryProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 3 | 4 | function makeQueryClient() { 5 | return new QueryClient({ 6 | defaultOptions: { 7 | queries: { 8 | // With SSR, we usually want to set some default staleTime 9 | // above 0 to avoid refetching immediately on the client 10 | staleTime: 60 * 1000, 11 | }, 12 | }, 13 | }); 14 | } 15 | 16 | let browserQueryClient: QueryClient | undefined = undefined; 17 | 18 | function getQueryClient() { 19 | if (typeof window === "undefined") { 20 | // Server: always make a new query client 21 | return makeQueryClient(); 22 | } else { 23 | // Browser: make a new query client if we don't already have one 24 | // This is very important, so we don't re-make a new client if React 25 | // suspends during the initial render. This may not be needed if we 26 | // have a suspense boundary BELOW the creation of the query client 27 | if (!browserQueryClient) browserQueryClient = makeQueryClient(); 28 | return browserQueryClient; 29 | } 30 | } 31 | 32 | export default function QueryProvider({ 33 | children, 34 | }: React.PropsWithChildren<{}>) { 35 | // NOTE: Avoid useState when initializing the query client if you don't 36 | // have a suspense boundary between this and the code that may 37 | // suspend because React will throw away the client on the initial 38 | // render if it suspends and there is no boundary 39 | const queryClient = getQueryClient(); 40 | 41 | return ( 42 | {children} 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /ecommerce/src/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" 5 | import { Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const RadioGroup = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => { 13 | return ( 14 | 19 | ) 20 | }) 21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName 22 | 23 | const RadioGroupItem = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => { 27 | return ( 28 | 36 | 37 | 38 | 39 | 40 | ) 41 | }) 42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName 43 | 44 | export { RadioGroup, RadioGroupItem } 45 | -------------------------------------------------------------------------------- /vcp-playground/src/components/QueryProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 3 | 4 | function makeQueryClient() { 5 | return new QueryClient({ 6 | defaultOptions: { 7 | queries: { 8 | // With SSR, we usually want to set some default staleTime 9 | // above 0 to avoid refetching immediately on the client 10 | staleTime: 60 * 1000, 11 | }, 12 | }, 13 | }); 14 | } 15 | 16 | let browserQueryClient: QueryClient | undefined = undefined; 17 | 18 | function getQueryClient() { 19 | if (typeof window === "undefined") { 20 | // Server: always make a new query client 21 | return makeQueryClient(); 22 | } else { 23 | // Browser: make a new query client if we don't already have one 24 | // This is very important, so we don't re-make a new client if React 25 | // suspends during the initial render. This may not be needed if we 26 | // have a suspense boundary BELOW the creation of the query client 27 | if (!browserQueryClient) browserQueryClient = makeQueryClient(); 28 | return browserQueryClient; 29 | } 30 | } 31 | 32 | export default function QueryProvider({ 33 | children, 34 | }: React.PropsWithChildren<{}>) { 35 | // NOTE: Avoid useState when initializing the query client if you don't 36 | // have a suspense boundary between this and the code that may 37 | // suspend because React will throw away the client on the initial 38 | // render if it suspends and there is no boundary 39 | const queryClient = getQueryClient(); 40 | 41 | return ( 42 | {children} 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" 5 | import { Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const RadioGroup = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => { 13 | return ( 14 | 19 | ) 20 | }) 21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName 22 | 23 | const RadioGroupItem = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => { 27 | return ( 28 | 36 | 37 | 38 | 39 | 40 | ) 41 | }) 42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName 43 | 44 | export { RadioGroup, RadioGroupItem } 45 | -------------------------------------------------------------------------------- /ecommerce/src/components/PLP/ColorFilter.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | 6 | interface ColorFilterProps { 7 | selectedColors: string[]; 8 | setSelectedColors: React.Dispatch>; 9 | } 10 | 11 | const colors = [ 12 | { name: 'Soft Blue', bgColor: 'bg-[#5b6b7b]' }, 13 | { name: 'Rose', bgColor: 'bg-[#7b5b5d]' }, 14 | { name: 'Moss Green', bgColor: 'bg-[#637b5b]' }, 15 | { name: 'Soft Grey', bgColor: 'bg-[#9b9b9b]' }, 16 | { name: 'Powder', bgColor: 'bg-slate-300' }, 17 | { name: 'Navy', bgColor: 'bg-blue-800' }, 18 | ]; 19 | 20 | export const ColorFilter: React.FC = ({ 21 | selectedColors, 22 | setSelectedColors, 23 | }) => { 24 | const handleColorChange = (colorName: string) => { 25 | setSelectedColors((prev) => 26 | prev.includes(colorName) 27 | ? prev.filter((c) => c !== colorName) 28 | : [...prev, colorName] 29 | ); 30 | }; 31 | 32 | return ( 33 | <> 34 |
35 | {colors.map((color) => ( 36 |
handleColorChange(color.name)} 40 | > 41 |
45 |
{color.name}
46 |
47 | ))} 48 |
49 | 50 | ); 51 | }; -------------------------------------------------------------------------------- /ecommerce/src/components/builderLiveDataPreview.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { builder, useIsPreviewing, BuilderContent } from "@builder.io/react"; 3 | import { RenderBuilderContent } from "./builder"; 4 | import DefaultErrorPage from "next/error"; 5 | import "../builder-registry"; 6 | 7 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 8 | 9 | type BuilderLiveDataPreviewProps = { 10 | templateModelName: string; 11 | templateModelData: object; 12 | dataModelName: string; 13 | dataModelData: object; 14 | } 15 | 16 | export function RenderBuilderLiveDataPreview({ dataModelData, dataModelName, templateModelData, templateModelName}: BuilderLiveDataPreviewProps) { 17 | // Call the useIsPreviewing hook to determine if 18 | // the page is being previewed in Builder 19 | const isPreviewing = useIsPreviewing();///(Builder.isPreviewing || Builder.isEditing) 20 | // If "content" has a value or the page is being previewed in Builder, 21 | // render the BuilderComponent with the specified content and model props. 22 | 23 | if (dataModelData || templateModelData || isPreviewing) { 24 | return ( 25 | <> 26 | 27 | {(data, loading, content) => { 28 | if (loading) return
Loading...
; 29 | return ( 30 | 31 | ); 32 | }} 33 |
34 | 35 | ); 36 | } 37 | // If the "content" is falsy and the page is 38 | // not being previewed in Builder, render the 39 | // DefaultErrorPage with a 404. 40 | return ; 41 | } 42 | -------------------------------------------------------------------------------- /ecommerce/src/components/CustomText/index.tsx: -------------------------------------------------------------------------------- 1 | // ---- Component Definition components/CustomText.jsx 2 | 3 | import React from "react"; 4 | 5 | function replaceAnchorsInHtmlString(htmlString: any, links: any[]) { 6 | // Create a DOM parser to parse the string into DOM elements 7 | const parser = new DOMParser(); 8 | const doc = parser.parseFromString(htmlString, "text/html"); 9 | 10 | // Find all elements in the parsed document 11 | doc.querySelectorAll("a").forEach((anchor, i) => { 12 | const linkMatch = links.find((link) => anchor.href.endsWith(link.key)); 13 | if (linkMatch) { 14 | // Build your custom anchor element 15 | const newAnchor = document.createElement("a"); 16 | 17 | // Example logic: set new attributes based on existing anchor 18 | // Replace with your own custom model format 19 | newAnchor.href = `/products/${linkMatch?.product?.value?.data?.handle}`; // copy href 20 | newAnchor.target = linkMatch?.target; // open in new tab 21 | newAnchor.rel = linkMatch?.rel; // set rel attribute 22 | newAnchor.innerText = linkMatch?.label; // set link's text value 23 | 24 | // Replace the old anchor with the new one 25 | anchor.replaceWith(newAnchor); 26 | } 27 | }); 28 | 29 | // Serialize the DOM back into a string 30 | return doc.body.innerHTML; 31 | } 32 | 33 | // A Custom Text-rendering replacement for Builder's built-in Text component, 34 | // Does a key-based replace on links, replacing 35 | const CustomText = (props: any) => { 36 | // TODO: Should probably memo-ize this! 37 | const updatedHtmlString = replaceAnchorsInHtmlString(props.text, props.links); 38 | 39 | return
; 40 | }; 41 | 42 | export default CustomText; 43 | -------------------------------------------------------------------------------- /ecommerce/src/components/ShopifyProduct/ShopifyProduct.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { getProduct } from "@/lib/shopify/api"; 3 | import shopifyConfig from "@/config/shopify"; 4 | import ProductBox from "../ui/productBox"; 5 | 6 | const ShopifyProduct = ({ shopifyProductHandle }: { shopifyProductHandle: string }) => { 7 | const [product, setProduct] = useState(null); 8 | const [loading, setLoading] = useState(false); 9 | 10 | useEffect(() => { 11 | const fetchProduct = async () => { 12 | setLoading(true); 13 | try { 14 | const fetchedProduct = await getProduct(shopifyConfig, { 15 | handle: shopifyProductHandle, 16 | }); 17 | setProduct(fetchedProduct); 18 | } catch (error) { 19 | console.error("Error fetching product:", error); 20 | } finally { 21 | setLoading(false); 22 | } 23 | }; 24 | 25 | if (shopifyProductHandle) { 26 | fetchProduct(); 27 | } 28 | }, [shopifyProductHandle]); 29 | 30 | if (loading) return
Loading Shopify product...
; 31 | if (!product) return
No product found.
; 32 | 33 | const productData = { 34 | data: { 35 | images: product.images.map( 36 | (image: { src: string; altText: string }) => ({ 37 | image: image.src, 38 | altText: image.altText || "Product image", 39 | }) 40 | ), 41 | productName: product.title, 42 | price: Math.round(product.variants[0].priceV2.amount), 43 | }, 44 | }; 45 | 46 | return ( 47 |
48 | 52 |
53 | ); 54 | }; 55 | 56 | export default ShopifyProduct; 57 | -------------------------------------------------------------------------------- /ecommerce/app/category/[category]/page.tsx: -------------------------------------------------------------------------------- 1 | import { builder } from "@builder.io/sdk"; 2 | import CategoryLanding from "@/src/components/PLP/CategoryLanding"; 3 | import { capitalizeWord } from "@/lib/utils"; 4 | 5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 6 | 7 | interface CategoryPageProps { 8 | params: { 9 | category: string; 10 | }; 11 | } 12 | 13 | export default async function CategoryPage(props: CategoryPageProps) { 14 | const plpTileModel = "plp-tile"; 15 | const plpProductDataModel = "product-data"; 16 | 17 | const plpTileContent = await builder 18 | // Get the page content from Builder with the specified options 19 | .getAll(plpTileModel, { 20 | userAttributes: { 21 | category: props?.params?.category.toLowerCase(), 22 | }, 23 | locale: "en-US", 24 | }); 25 | 26 | const productDetailsContent = await builder 27 | // Get the page content from Builder with the specified options 28 | .getAll(plpProductDataModel, { 29 | query: { 30 | data: { 31 | category: props?.params?.category.toLowerCase(), 32 | }, 33 | }, 34 | locale: "en-US", 35 | }); 36 | // console.log("DATA", productDetailsContent); 37 | return ( 38 | <> 39 | {/* Render the Builder page */} 40 |
41 |
{capitalizeWord(props?.params?.category)}
42 |
43 |
44 | {props?.params?.category.toUpperCase()} 45 |
46 | 50 | 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /ecommerce/src/components/Layout/SideNav.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { Button } from "../ui/button" 4 | import { Input } from "../ui/input" 5 | import { Label } from "../ui/label" 6 | import { RxHamburgerMenu } from "react-icons/rx"; 7 | import { 8 | Sheet, 9 | SheetClose, 10 | SheetContent, 11 | SheetDescription, 12 | SheetFooter, 13 | SheetHeader, 14 | SheetTitle, 15 | SheetTrigger, 16 | } from "../ui/sheet" 17 | 18 | 19 | export function SideNav() { 20 | return ( 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | Edit profile 31 | 32 | Make changes to your profile here. Click save when you&rsquore done. 33 | 34 | 35 |
36 |
37 | 40 | 41 |
42 |
43 | 46 | 47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 |
55 |
56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /marketing/components/Layout/SideNav.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { Button } from "../ui/button" 4 | import { Input } from "../ui/input" 5 | import { Label } from "../ui/label" 6 | import { RxHamburgerMenu } from "react-icons/rx"; 7 | import { 8 | Sheet, 9 | SheetClose, 10 | SheetContent, 11 | SheetDescription, 12 | SheetFooter, 13 | SheetHeader, 14 | SheetTitle, 15 | SheetTrigger, 16 | } from "../ui/sheet" 17 | 18 | 19 | export function SideNav() { 20 | return ( 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | Edit profile 31 | 32 | Make changes to your profile here. Click save when you&rsquore done. 33 | 34 | 35 |
36 |
37 | 40 | 41 |
42 |
43 | 46 | 47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 |
55 |
56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /vcp-playground/src/components/Layout/SideNav.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { Button } from "../ui/button" 4 | import { Input } from "../ui/input" 5 | import { Label } from "../ui/label" 6 | import { RxHamburgerMenu } from "react-icons/rx"; 7 | import { 8 | Sheet, 9 | SheetClose, 10 | SheetContent, 11 | SheetDescription, 12 | SheetFooter, 13 | SheetHeader, 14 | SheetTitle, 15 | SheetTrigger, 16 | } from "../ui/sheet" 17 | 18 | 19 | export function SideNav() { 20 | return ( 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | Edit profile 31 | 32 | Make changes to your profile here. Click save when you&rsquore done. 33 | 34 | 35 |
36 |
37 | 40 | 41 |
42 |
43 | 46 | 47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 |
55 |
56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /ecommerce/app/product/[handle]/page.tsx: -------------------------------------------------------------------------------- 1 | "use server"; 2 | import ProductDetails from "@/src/components/PDP/ProductDetails"; 3 | import { builder } from "@builder.io/sdk"; 4 | import { RenderBuilderContent } from "@/src/components/builder"; 5 | 6 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!); 7 | 8 | interface ProductPageProps { 9 | params: { 10 | handle: string; 11 | }; 12 | } 13 | 14 | export default async function ProductPage(props: ProductPageProps) { 15 | const builderProductDataModel = "product-data"; 16 | const builderProductDetailsModel = "product-details-bottom"; 17 | 18 | const productData = await builder 19 | // Get the page content from Builder with the specified options 20 | .get(builderProductDataModel, { 21 | query: { 22 | data: { 23 | handle: props?.params?.handle, 24 | }, 25 | }, 26 | locale: "en-US", 27 | }) 28 | // Convert the result to a promise 29 | .toPromise(); 30 | 31 | const productDetailsContent = await builder 32 | // Get the page content from Builder with the specified options 33 | .get(builderProductDetailsModel, { 34 | userAttributes: { 35 | // Use the page path specified in the URL to fetch the content 36 | product: props?.params?.handle, 37 | category: productData?.data?.category, 38 | options: { enrich: true }, 39 | }, 40 | locale: "en-US", 41 | }) 42 | // Convert the result to a promise 43 | .toPromise(); 44 | 45 | return ( 46 | <> 47 | {/* Render the Builder page */} 48 | 49 | {productDetailsContent ? ( 50 | 55 | ) : null} 56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /marketing/components/Layout/AuthSlider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { Button } from "../ui/button" 3 | import { Input } from "../ui/input" 4 | import { Label } from "../ui/label" 5 | import { 6 | Sheet, 7 | SheetClose, 8 | SheetContent, 9 | SheetDescription, 10 | SheetFooter, 11 | SheetHeader, 12 | SheetTitle, 13 | SheetTrigger, 14 | } from "../ui/sheet" 15 | 16 | type AuthSliderProps = { 17 | variant: 'white' | 'black'; 18 | }; 19 | 20 | export const AuthSlider: React.FC = ({ variant }) => { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | Edit profile 29 | 30 | Make changes to your profile here. Click save when you&rsquore done. 31 | 32 | 33 |
34 |
35 | 38 | 39 |
40 |
41 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 |
53 |
54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /ecommerce/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: #ffffff; 8 | --foreground: #000000; 9 | --tertiary: #C8E2EE; 10 | 11 | --card: #ffffff; 12 | --card-foreground: #000000; 13 | 14 | --popover: #ffffff; 15 | --popover-foreground: #000000; 16 | 17 | --primary: #000000; 18 | --primary-foreground: #ffffff; 19 | 20 | --secondary: #ffffff; 21 | --secondary-foreground: #000000; 22 | 23 | --muted: #18B4F4; 24 | --muted-foreground: #18B4F4; 25 | 26 | --accent: #F35959; 27 | --accent-foreground: #000000; 28 | 29 | --energetic: #A97FF2; 30 | --energetic-foreground: #000000; 31 | 32 | --destructive: #C8E2EE; 33 | --destructive-foreground: #000000; 34 | 35 | --border: #e2e8f0; 36 | --input: #e2e8f0; 37 | --ring: #020817; 38 | 39 | --radius: 0.5rem; 40 | } 41 | 42 | .dark { 43 | --background: #000000; 44 | --foreground: #ffffff; 45 | 46 | --card: #000000; 47 | --card-foreground: #ffffff; 48 | 49 | --popover: #000000; 50 | --popover-foreground: #ffffff; 51 | 52 | --primary: #ffffff; 53 | --primary-foreground: #000000; 54 | 55 | --secondary: #000000; 56 | --secondary-foreground: #ffffff; 57 | 58 | --muted: #18B4F4; 59 | --muted-foreground: #94a3b8; 60 | 61 | --accent: #F35959; 62 | --accent-foreground: #ffffff; 63 | 64 | --destructive: #C8E2EE; 65 | --destructive-foreground: #ffffff; 66 | 67 | --border: #000000; 68 | --input: #000000; 69 | --ring: #cbd5e1; 70 | } 71 | } 72 | 73 | @layer base { 74 | * { 75 | @apply border-border; 76 | } 77 | body { 78 | @apply bg-background text-foreground; 79 | } 80 | } 81 | 82 | body { 83 | font-family: "Poppins", sans-serif; 84 | } -------------------------------------------------------------------------------- /vcp-playground/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: #ffffff; 8 | --foreground: #000000; 9 | --tertiary: #C8E2EE; 10 | 11 | --card: #ffffff; 12 | --card-foreground: #000000; 13 | 14 | --popover: #ffffff; 15 | --popover-foreground: #000000; 16 | 17 | --primary: #000000; 18 | --primary-foreground: #ffffff; 19 | 20 | --secondary: #ffffff; 21 | --secondary-foreground: #000000; 22 | 23 | --muted: #18B4F4; 24 | --muted-foreground: #18B4F4; 25 | 26 | --accent: #F35959; 27 | --accent-foreground: #000000; 28 | 29 | --energetic: #A97FF2; 30 | --energetic-foreground: #000000; 31 | 32 | --destructive: #C8E2EE; 33 | --destructive-foreground: #000000; 34 | 35 | --border: #e2e8f0; 36 | --input: #e2e8f0; 37 | --ring: #020817; 38 | 39 | --radius: 0.5rem; 40 | } 41 | 42 | .dark { 43 | --background: #000000; 44 | --foreground: #ffffff; 45 | 46 | --card: #000000; 47 | --card-foreground: #ffffff; 48 | 49 | --popover: #000000; 50 | --popover-foreground: #ffffff; 51 | 52 | --primary: #ffffff; 53 | --primary-foreground: #000000; 54 | 55 | --secondary: #000000; 56 | --secondary-foreground: #ffffff; 57 | 58 | --muted: #18B4F4; 59 | --muted-foreground: #94a3b8; 60 | 61 | --accent: #F35959; 62 | --accent-foreground: #ffffff; 63 | 64 | --destructive: #C8E2EE; 65 | --destructive-foreground: #ffffff; 66 | 67 | --border: #000000; 68 | --input: #000000; 69 | --ring: #cbd5e1; 70 | } 71 | } 72 | 73 | @layer base { 74 | * { 75 | @apply border-border; 76 | } 77 | body { 78 | @apply bg-background text-foreground; 79 | } 80 | } 81 | 82 | body { 83 | font-family: "Poppins", sans-serif; 84 | } -------------------------------------------------------------------------------- /vcp-playground/src/components/Layout/AuthSlider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { Button } from "../ui/button" 3 | import { Input } from "../ui/input" 4 | import { Label } from "../ui/label" 5 | import { 6 | Sheet, 7 | SheetClose, 8 | SheetContent, 9 | SheetDescription, 10 | SheetFooter, 11 | SheetHeader, 12 | SheetTitle, 13 | SheetTrigger, 14 | } from "../ui/sheet" 15 | 16 | type AuthSliderProps = { 17 | variant: 'white' | 'black'; 18 | }; 19 | 20 | export const AuthSlider: React.FC = ({ variant }) => { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | Edit profile 29 | 30 | Make changes to your profile here. Click save when you&rsquore done. 31 | 32 | 33 |
34 |
35 | 38 | 39 |
40 |
41 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 |
53 |
54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /ecommerce/src/components/PLP/SizeFilter.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | 6 | interface SizeFilterProps { 7 | selectedSizes: string[]; 8 | setSelectedSizes: React.Dispatch>; 9 | } 10 | 11 | const sizes = Array.from({ length: 18 }, (_, i) => i.toString()); 12 | const sizeLabels = ['X SMALL', 'SMALL', 'MEDIUM', 'LARGE', 'X LARGE', 'XX LARGE']; 13 | 14 | export const SizeFilter: React.FC = ({ 15 | selectedSizes, 16 | setSelectedSizes, 17 | }) => { 18 | const handleSizeChange = (size: string) => { 19 | setSelectedSizes((prev) => 20 | prev.includes(size) 21 | ? prev.filter((s) => s !== size) 22 | : [...prev, size] 23 | ); 24 | }; 25 | 26 | return ( 27 | <> 28 |
29 | {sizes.map((size) => ( 30 |
handleSizeChange(size)} 36 | > 37 | {size} 38 |
39 | ))} 40 |
41 |
42 | {sizeLabels.map((label) => ( 43 |
handleSizeChange(label)} 49 | > 50 | {label} 51 |
52 | ))} 53 |
54 | 55 | ); 56 | }; -------------------------------------------------------------------------------- /ecommerce/src/components/Layout/AuthSlider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "../ui/button"; 3 | import { Input } from "../ui/input"; 4 | import { Label } from "../ui/label"; 5 | import { 6 | Sheet, 7 | SheetClose, 8 | SheetContent, 9 | SheetDescription, 10 | SheetFooter, 11 | SheetHeader, 12 | SheetTitle, 13 | SheetTrigger, 14 | } from "../ui/sheet"; 15 | 16 | type AuthSliderProps = { 17 | variant: "white" | "black"; 18 | }; 19 | 20 | export const AuthSlider: React.FC = ({ variant }) => { 21 | return ( 22 | 23 | 24 | 30 | 31 | 32 | 33 | Edit profile 34 | 35 | Make changes to your profile here. Click save when you done. 36 | 37 | 38 |
39 |
40 | 43 | 44 |
45 |
46 | 49 | 50 |
51 |
52 | 53 | 54 | 55 | 56 | 57 |
58 |
59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /marketing/components/Layout/CartSlider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { IoCartOutline } from 'react-icons/io5' 3 | import { Button } from "../ui/button" 4 | import { Input } from "../ui/input" 5 | import { Label } from "../ui/label" 6 | import { 7 | Sheet, 8 | SheetClose, 9 | SheetContent, 10 | SheetDescription, 11 | SheetFooter, 12 | SheetHeader, 13 | SheetTitle, 14 | SheetTrigger, 15 | } from "../ui/sheet" 16 | 17 | type CartSliderProps = { 18 | variant: 'white' | 'black'; 19 | }; 20 | 21 | export const CartSlider: React.FC = ({ variant }) => { 22 | return ( 23 | 24 | 25 | 28 | 29 | 30 | 31 | Edit profile 32 | 33 | Make changes to your profile here. Click save when you&rsquore done. 34 | 35 | 36 |
37 |
38 | 41 | 42 |
43 |
44 | 47 | 48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 |
56 |
57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /ecommerce/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ce-nextjs-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@builder.io/dev-tools": "^1.18.44", 13 | "@builder.io/react": "^9.0.0", 14 | "@builder.io/react-hydration-overlay": "^0.3.0", 15 | "@builder.io/sdk": "^6.1.3", 16 | "@builder.io/widgets": "^2.1.0", 17 | "@radix-ui/react-accordion": "^1.2.0", 18 | "@radix-ui/react-checkbox": "^1.1.1", 19 | "@radix-ui/react-dialog": "^1.1.1", 20 | "@radix-ui/react-icons": "^1.3.0", 21 | "@radix-ui/react-label": "^2.1.0", 22 | "@radix-ui/react-navigation-menu": "^1.1.4", 23 | "@radix-ui/react-radio-group": "^1.2.0", 24 | "@radix-ui/react-slot": "^1.1.0", 25 | "@radix-ui/react-tabs": "^1.0.4", 26 | "@tanstack/react-query": "^5.51.11", 27 | "algoliasearch": "^4.24.0", 28 | "autoprefixer": "^10.4.19", 29 | "axios": "^1.7.7", 30 | "class-variance-authority": "^0.7.0", 31 | "clsx": "^2.1.1", 32 | "dotenv": "^16.4.5", 33 | "lucide-react": "^0.395.0", 34 | "next": "^14.2.5", 35 | "react": "^18", 36 | "react-dom": "^18", 37 | "react-icons": "^5.2.1", 38 | "react-instantsearch-dom": "^6.40.4", 39 | "react-query": "^3.39.3", 40 | "shopify-buy": "^2.22.0", 41 | "swell-js": "^4.2.4", 42 | "swell-node": "^5.6.0", 43 | "tailwind-merge": "^2.3.0", 44 | "tailwindcss-animate": "^1.0.7", 45 | "vaul": "^0.9.1", 46 | "webpack": "^5.93.0" 47 | }, 48 | "devDependencies": { 49 | "@types/axios": "^0.9.36", 50 | "@types/node": "20.14.7", 51 | "@types/react": "^18", 52 | "@types/react-dom": "^18", 53 | "@types/react-instantsearch-dom": "^6.12.8", 54 | "@types/shopify-buy": "^2.17.4", 55 | "eslint": "^8", 56 | "eslint-config-next": "14.1.4", 57 | "tailwindcss": "^3.4.4", 58 | "typescript": "^5" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ecommerce/src/components/Layout/CartSlider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { IoCartOutline } from 'react-icons/io5' 3 | import { Button } from "../ui/button" 4 | import { Input } from "../ui/input" 5 | import { Label } from "../ui/label" 6 | import { 7 | Sheet, 8 | SheetClose, 9 | SheetContent, 10 | SheetDescription, 11 | SheetFooter, 12 | SheetHeader, 13 | SheetTitle, 14 | SheetTrigger, 15 | } from "../ui/sheet" 16 | 17 | type CartSliderProps = { 18 | variant: 'white' | 'black'; 19 | }; 20 | 21 | export const CartSlider: React.FC = ({ variant }) => { 22 | return ( 23 | 24 | 25 | 28 | 29 | 30 | 31 | Edit profile 32 | 33 | Make changes to your profile here. Click save when you&rsquore done. 34 | 35 | 36 |
37 |
38 | 41 | 42 |
43 |
44 | 47 | 48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 |
56 |
57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /vcp-playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ce-nextjs-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@builder.io/dev-tools": "^1.1.42", 13 | "@builder.io/react": "^7.0.1", 14 | "@builder.io/react-hydration-overlay": "^0.0.8", 15 | "@builder.io/sdk": "^2.2.9", 16 | "@builder.io/widgets": "^1.2.24", 17 | "@radix-ui/react-accordion": "^1.2.0", 18 | "@radix-ui/react-checkbox": "^1.1.1", 19 | "@radix-ui/react-dialog": "^1.1.1", 20 | "@radix-ui/react-icons": "^1.3.0", 21 | "@radix-ui/react-label": "^2.1.0", 22 | "@radix-ui/react-navigation-menu": "^1.1.4", 23 | "@radix-ui/react-radio-group": "^1.2.0", 24 | "@radix-ui/react-slot": "^1.1.0", 25 | "@radix-ui/react-tabs": "^1.0.4", 26 | "@tanstack/react-query": "^5.51.11", 27 | "algoliasearch": "^4.24.0", 28 | "autoprefixer": "^10.4.19", 29 | "axios": "^1.7.7", 30 | "class-variance-authority": "^0.7.0", 31 | "clsx": "^2.1.1", 32 | "dotenv": "^16.4.5", 33 | "lucide-react": "^0.395.0", 34 | "next": "^14.2.5", 35 | "react": "^18", 36 | "react-dom": "^18", 37 | "react-icons": "^5.2.1", 38 | "react-instantsearch-dom": "^6.40.4", 39 | "react-query": "^3.39.3", 40 | "shopify-buy": "^2.22.0", 41 | "swell-js": "^4.2.4", 42 | "swell-node": "^5.6.0", 43 | "tailwind-merge": "^2.3.0", 44 | "tailwindcss-animate": "^1.0.7", 45 | "vaul": "^0.9.1", 46 | "webpack": "^5.93.0" 47 | }, 48 | "devDependencies": { 49 | "@types/axios": "^0.9.36", 50 | "@types/node": "20.14.7", 51 | "@types/react": "^18", 52 | "@types/react-dom": "^18", 53 | "@types/react-instantsearch-dom": "^6.12.8", 54 | "@types/shopify-buy": "^2.17.4", 55 | "eslint": "^8", 56 | "eslint-config-next": "14.1.4", 57 | "tailwindcss": "^3.4.4", 58 | "typescript": "^5" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /vcp-playground/src/components/Layout/CartSlider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { IoCartOutline } from 'react-icons/io5' 3 | import { Button } from "../ui/button" 4 | import { Input } from "../ui/input" 5 | import { Label } from "../ui/label" 6 | import { 7 | Sheet, 8 | SheetClose, 9 | SheetContent, 10 | SheetDescription, 11 | SheetFooter, 12 | SheetHeader, 13 | SheetTitle, 14 | SheetTrigger, 15 | } from "../ui/sheet" 16 | 17 | type CartSliderProps = { 18 | variant: 'white' | 'black'; 19 | }; 20 | 21 | export const CartSlider: React.FC = ({ variant }) => { 22 | return ( 23 | 24 | 25 | 28 | 29 | 30 | 31 | Edit profile 32 | 33 | Make changes to your profile here. Click save when you&rsquore done. 34 | 35 | 36 |
37 |
38 | 41 | 42 |
43 |
44 | 47 | 48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 |
56 |
57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /ecommerce/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | 8 | export function rgbaToHex(rgbaString:string) { 9 | // Define a regular expression to match and capture r, g, b, and a values 10 | const rgbaRegex = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*([01]?\.?\d*)\s*)?\)$/; 11 | 12 | // Use the regex to match the rgbaString 13 | const match = rgbaString.match(rgbaRegex); 14 | 15 | if (match) { 16 | // Extract and parse the r, g, b, and a values 17 | const r = parseInt(match[1], 10); 18 | const g = parseInt(match[2], 10); 19 | const b = parseInt(match[3], 10); 20 | const a = match[4] !== undefined ? parseFloat(match[4]) : 1; 21 | 22 | // Convert r, g, and b to two-digit hex values 23 | const rHex = r.toString(16).padStart(2, '0'); 24 | const gHex = g.toString(16).padStart(2, '0'); 25 | const bHex = b.toString(16).padStart(2, '0'); 26 | 27 | // Convert a to a two-digit hex value, scaled to 255 28 | const aHex = Math.round(a * 255).toString(16).padStart(2, '0'); 29 | 30 | // Combine the hex values into a single string 31 | const hex = `#${rHex}${gHex}${bHex}${aHex !== 'ff' ? aHex : ''}`; 32 | 33 | return hex; 34 | } 35 | 36 | // If the input does not match, throw an error or return null 37 | throw new Error("Invalid RGBA string"); 38 | } 39 | 40 | export function capitalizeWord(word:string) { 41 | return word.charAt(0).toUpperCase()+word.slice(1) 42 | } 43 | 44 | export function getBuilderApiKey(hostlist:any) { 45 | const host = typeof hostlist==="string" ? 'localhost' : hostlist.get('host'); 46 | const hostPrefix = host.split(/[-:]/)[0]; 47 | 48 | switch (hostPrefix) { 49 | case 'tim': return '50b344f9116e4820a020e382058146e0'; 50 | case 'localhost': return 'a87584e551b6472fa0f0a2eb10f2c0ff'; 51 | default: return 'a87584e551b6472fa0f0a2eb10f2c0ff' 52 | } 53 | } -------------------------------------------------------------------------------- /vcp-playground/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | 8 | export function rgbaToHex(rgbaString:string) { 9 | // Define a regular expression to match and capture r, g, b, and a values 10 | const rgbaRegex = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*([01]?\.?\d*)\s*)?\)$/; 11 | 12 | // Use the regex to match the rgbaString 13 | const match = rgbaString.match(rgbaRegex); 14 | 15 | if (match) { 16 | // Extract and parse the r, g, b, and a values 17 | const r = parseInt(match[1], 10); 18 | const g = parseInt(match[2], 10); 19 | const b = parseInt(match[3], 10); 20 | const a = match[4] !== undefined ? parseFloat(match[4]) : 1; 21 | 22 | // Convert r, g, and b to two-digit hex values 23 | const rHex = r.toString(16).padStart(2, '0'); 24 | const gHex = g.toString(16).padStart(2, '0'); 25 | const bHex = b.toString(16).padStart(2, '0'); 26 | 27 | // Convert a to a two-digit hex value, scaled to 255 28 | const aHex = Math.round(a * 255).toString(16).padStart(2, '0'); 29 | 30 | // Combine the hex values into a single string 31 | const hex = `#${rHex}${gHex}${bHex}${aHex !== 'ff' ? aHex : ''}`; 32 | 33 | return hex; 34 | } 35 | 36 | // If the input does not match, throw an error or return null 37 | throw new Error("Invalid RGBA string"); 38 | } 39 | 40 | export function capitalizeWord(word:string) { 41 | return word.charAt(0).toUpperCase()+word.slice(1) 42 | } 43 | 44 | export function getBuilderApiKey(hostlist:any) { 45 | const host = typeof hostlist==="string" ? 'localhost' : hostlist.get('host'); 46 | const hostPrefix = host.split(/[-:]/)[0]; 47 | 48 | switch (hostPrefix) { 49 | case 'tim': return '50b344f9116e4820a020e382058146e0'; 50 | case 'localhost': return 'a87584e551b6472fa0f0a2eb10f2c0ff'; 51 | default: return 'a87584e551b6472fa0f0a2eb10f2c0ff' 52 | } 53 | } -------------------------------------------------------------------------------- /vcp-playground/next.config.mjs: -------------------------------------------------------------------------------- 1 | import BuilderDevTools from "@builder.io/dev-tools/next"; 2 | import { withHydrationOverlay } from "@builder.io/react-hydration-overlay/next"; 3 | 4 | /** @type {import('next').NextConfig} */ 5 | const nextConfig = { 6 | images: { 7 | dangerouslyAllowSVG: true, 8 | remotePatterns: [ 9 | { 10 | protocol: "https", 11 | hostname: "cdn.builder.io", 12 | port: "", 13 | pathname: "/**", 14 | }, 15 | { 16 | protocol: "https", 17 | hostname: "img.shopstyle-cdn.com", 18 | port: "", 19 | pathname: "/**", 20 | }, 21 | { 22 | protocol: "https", 23 | hostname: "cdn.shopify.com", 24 | port: "", 25 | pathname: "/**", 26 | }, 27 | { 28 | protocol: "https", 29 | hostname: "cdn.swell.store", 30 | port: "", 31 | pathname: "/**", 32 | }, 33 | { 34 | protocol: "https", 35 | hostname: "algolia", 36 | port: "", 37 | pathname: "/**", 38 | }, 39 | { 40 | protocol: "https", 41 | hostname: "burst.shopifycdn.com", 42 | port: "", 43 | pathname: "/**", 44 | }, 45 | { 46 | protocol: "https", 47 | hostname: "shopifycdn.com", 48 | port: "", 49 | pathname: "/**", 50 | }, 51 | { 52 | protocol: "https", 53 | hostname: "storage.googleapis.com", 54 | port: "", 55 | pathname: "/**", 56 | }, 57 | ], 58 | }, 59 | }; 60 | // export default nextConfig; 61 | const configWithOverlay = BuilderDevTools()( 62 | BuilderDevTools()( 63 | withHydrationOverlay({ 64 | /** 65 | * Optional: `appRootSelector` is the selector for the root element of your app. By default, it is `#__next` which works 66 | * for Next.js apps with pages directory. If you are using the app directory, you should change this to `main`. 67 | */ 68 | appRootSelector: "main", 69 | })(nextConfig) 70 | ) 71 | ); 72 | 73 | export default configWithOverlay; 74 | -------------------------------------------------------------------------------- /marketing/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import * as React from "react" 3 | import { Slot } from "@radix-ui/react-slot" 4 | import { cva, type VariantProps } from "class-variance-authority" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const buttonVariants = cva( 9 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 10 | { 11 | variants: { 12 | variant: { 13 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-10 px-4 py-2", 25 | sm: "h-9 rounded-md px-3", 26 | lg: "h-11 rounded-md px-8", 27 | icon: "h-10 w-10", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /ecommerce/src/components/Layout/Footer.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | "use client" 5 | import React, { useState, useEffect } from "react"; 6 | import { CartSlider } from "./CartSlider"; 7 | import { AuthSlider } from "./AuthSlider"; 8 | 9 | type NavItemProps = { 10 | text: string; 11 | isHighlighted?: boolean; 12 | }; 13 | 14 | const NavItem: React.FC = ({ text, isHighlighted }) => ( 15 |
{text}
16 | ); 17 | 18 | const Footer: React.FC = () => { 19 | const [ isMounted, setIsMounted ] = useState(false); 20 | useEffect(() => setIsMounted(true)); 21 | 22 | const navItems = [ 23 | "WOMEN", 24 | "MEN", 25 | "COLLECTIONS", 26 | "SHOP ALL", 27 | { text: "SALE", isHighlighted: true }, 28 | ]; 29 | 30 | return ( 31 |
32 |
33 |
34 | 43 |
44 | {isMounted && } 45 | {isMounted && } 46 |
47 |
48 |

49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam posuere erat a ante 50 | vestibulum, in volutpat ligula elementum. 51 |

52 |
53 |
54 | ); 55 | }; 56 | 57 | export default Footer; -------------------------------------------------------------------------------- /vcp-playground/src/components/Layout/Footer.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | "use client" 5 | import React, { useState, useEffect } from "react"; 6 | import { CartSlider } from "./CartSlider"; 7 | import { AuthSlider } from "./AuthSlider"; 8 | 9 | type NavItemProps = { 10 | text: string; 11 | isHighlighted?: boolean; 12 | }; 13 | 14 | const NavItem: React.FC = ({ text, isHighlighted }) => ( 15 |
{text}
16 | ); 17 | 18 | const Footer: React.FC = () => { 19 | const [ isMounted, setIsMounted ] = useState(false); 20 | useEffect(() => setIsMounted(true)); 21 | 22 | const navItems = [ 23 | "WOMEN", 24 | "MEN", 25 | "COLLECTIONS", 26 | "SHOP ALL", 27 | { text: "SALE", isHighlighted: true }, 28 | ]; 29 | 30 | return ( 31 |
32 |
33 |
34 | 43 |
44 | {isMounted && } 45 | {isMounted && } 46 |
47 |
48 |

49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam posuere erat a ante 50 | vestibulum, in volutpat ligula elementum. 51 |

52 |
53 |
54 | ); 55 | }; 56 | 57 | export default Footer; -------------------------------------------------------------------------------- /marketing/components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TabsPrimitive from "@radix-ui/react-tabs" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Tabs = TabsPrimitive.Root 9 | 10 | const TabsList = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef 13 | >(({ className, ...props }, ref) => ( 14 | 22 | )) 23 | TabsList.displayName = TabsPrimitive.List.displayName 24 | 25 | const TabsTrigger = React.forwardRef< 26 | React.ElementRef, 27 | React.ComponentPropsWithoutRef 28 | >(({ className, ...props }, ref) => ( 29 | 37 | )) 38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName 39 | 40 | const TabsContent = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 | 52 | )) 53 | TabsContent.displayName = TabsPrimitive.Content.displayName 54 | 55 | export { Tabs, TabsList, TabsTrigger, TabsContent } 56 | -------------------------------------------------------------------------------- /ecommerce/src/components/Blocks/BynderImage.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | const BynderImage = (props: BynderImageProps) => { 4 | let description: string = 5 | props.bynderAsset?.assets?.[0]?.description ?? "Bynder Asset"; 6 | let asset = 7 | props.bynderAsset?.additionalInfo?.selectedFile ?? 8 | props.bynderAsset?.assets?.[0]?.files?.webImage; 9 | 10 | // console.log({ description, asset, props }); 11 | if (!props.bynderAsset || !asset) { 12 | return "Choose a Bynder Asset"; 13 | } 14 | 15 | return ( 16 | // eslint-disable-next-line @next/next/no-img-element 17 | {description} 29 | ); 30 | }; 31 | 32 | export default BynderImage; 33 | 34 | // types copied from Bynder's package or from the Bynder plugin 35 | 36 | interface File { 37 | url: string; 38 | width?: number; 39 | height?: number; 40 | fileSize?: number; 41 | isFakeOriginal?: boolean; 42 | } 43 | type AdditionalInfo = { 44 | selectedFile?: File; 45 | }; 46 | type BynderImageProps = { 47 | bynderAsset: { assets?: BaseAsset[]; additionalInfo?: AdditionalInfo }; 48 | imageFit: "cover" | "contain" | "fill" | "none"; 49 | }; 50 | 51 | interface BaseAsset extends Record { 52 | __typename: string; 53 | id: string; 54 | name: string; 55 | description: string | null; 56 | databaseId: string; 57 | createdAt: string; 58 | originalUrl: string | null; 59 | publishedAt: string; 60 | tags: string[]; 61 | type: string; 62 | updatedAt: string; 63 | url: string; 64 | extensions: string[]; 65 | metaproperties: { 66 | nodes: any[]; 67 | }; 68 | textMetaproperties: any[]; // You might want to define a more specific type here 69 | derivatives: { 70 | thumbnail: string; 71 | webImage: string; 72 | }; 73 | files: { 74 | webImage: File; 75 | thumbnail: File; 76 | mini: File; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /ecommerce/src/components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TabsPrimitive from "@radix-ui/react-tabs" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Tabs = TabsPrimitive.Root 9 | 10 | const TabsList = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef 13 | >(({ className, ...props }, ref) => ( 14 | 22 | )) 23 | TabsList.displayName = TabsPrimitive.List.displayName 24 | 25 | const TabsTrigger = React.forwardRef< 26 | React.ElementRef, 27 | React.ComponentPropsWithoutRef 28 | >(({ className, ...props }, ref) => ( 29 | 37 | )) 38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName 39 | 40 | const TabsContent = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 | 52 | )) 53 | TabsContent.displayName = TabsPrimitive.Content.displayName 54 | 55 | export { Tabs, TabsList, TabsTrigger, TabsContent } 56 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TabsPrimitive from "@radix-ui/react-tabs" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Tabs = TabsPrimitive.Root 9 | 10 | const TabsList = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef 13 | >(({ className, ...props }, ref) => ( 14 | 22 | )) 23 | TabsList.displayName = TabsPrimitive.List.displayName 24 | 25 | const TabsTrigger = React.forwardRef< 26 | React.ElementRef, 27 | React.ComponentPropsWithoutRef 28 | >(({ className, ...props }, ref) => ( 29 | 37 | )) 38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName 39 | 40 | const TabsContent = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 | 52 | )) 53 | TabsContent.displayName = TabsPrimitive.Content.displayName 54 | 55 | export { Tabs, TabsList, TabsTrigger, TabsContent } 56 | -------------------------------------------------------------------------------- /ecommerce/src/components/Collection/Collection.tsx: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import ProductCard from "../Card/ProductCard"; 3 | 4 | export function Collection(props: { 5 | // Faked for now 6 | collection: string; 7 | }) { 8 | const useShopStyle = !(!props.collection || props.collection === "all"); 9 | 10 | const products = useQuery({ 11 | queryKey: ["products", props.collection], 12 | queryFn: async () => { 13 | if (useShopStyle) { 14 | const defaultParams = { 15 | abbreviatedCategoryHistogram: "true", 16 | limit: "20", 17 | cat: props.collection, 18 | view: "web", 19 | useElasticsearch: "true", 20 | sorts: "Popular", 21 | pid: "shopstyle", 22 | }; 23 | 24 | const url = "https://api.shopstyle.com/api/v2/products"; 25 | const params = new URLSearchParams(defaultParams); 26 | return (await fetch(`${url}?${params}`).then((res) => res.json())) 27 | .products; 28 | } 29 | return ( 30 | await fetch( 31 | "https://cdn.builder.io/api/v3/content/product-data?apiKey=f5348105e75441b59830f1e489577801&includeRefs=true&fields=data&limit=10" 32 | ).then((res) => res.json()) 33 | ).results; 34 | }, 35 | }); 36 | // Scrolling flex row of product cards 37 | return ( 38 |
39 | {products.data?.map((product, index) => ( 40 | 58 | ))} 59 |
60 | ); 61 | } -------------------------------------------------------------------------------- /vcp-playground/src/components/Collection/Collection.tsx: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import ProductCard from "../Card/ProductCard"; 3 | 4 | export function Collection(props: { 5 | // Faked for now 6 | collection: string; 7 | }) { 8 | const useShopStyle = !(!props.collection || props.collection === "all"); 9 | 10 | const products = useQuery({ 11 | queryKey: ["products", props.collection], 12 | queryFn: async () => { 13 | if (useShopStyle) { 14 | const defaultParams = { 15 | abbreviatedCategoryHistogram: "true", 16 | limit: "20", 17 | cat: props.collection, 18 | view: "web", 19 | useElasticsearch: "true", 20 | sorts: "Popular", 21 | pid: "shopstyle", 22 | }; 23 | 24 | const url = "https://api.shopstyle.com/api/v2/products"; 25 | const params = new URLSearchParams(defaultParams); 26 | return (await fetch(`${url}?${params}`).then((res) => res.json())) 27 | .products; 28 | } 29 | return ( 30 | await fetch( 31 | "https://cdn.builder.io/api/v3/content/product-data?apiKey=f5348105e75441b59830f1e489577801&includeRefs=true&fields=data&limit=10" 32 | ).then((res) => res.json()) 33 | ).results; 34 | }, 35 | }); 36 | // Scrolling flex row of product cards 37 | return ( 38 |
39 | {products.data?.map((product, index) => ( 40 | 58 | ))} 59 |
60 | ); 61 | } -------------------------------------------------------------------------------- /ecommerce/src/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )) 45 | CardTitle.displayName = "CardTitle" 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )) 57 | CardDescription.displayName = "CardDescription" 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )) 65 | CardContent.displayName = "CardContent" 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )) 77 | CardFooter.displayName = "CardFooter" 78 | 79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 80 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )) 45 | CardTitle.displayName = "CardTitle" 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )) 57 | CardDescription.displayName = "CardDescription" 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )) 65 | CardContent.displayName = "CardContent" 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )) 77 | CardFooter.displayName = "CardFooter" 78 | 79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 80 | -------------------------------------------------------------------------------- /ecommerce/src/components/ui/accordion.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AccordionPrimitive from "@radix-ui/react-accordion" 5 | import { ChevronDown } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Accordion = AccordionPrimitive.Root 10 | 11 | const AccordionItem = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 20 | )) 21 | AccordionItem.displayName = "AccordionItem" 22 | 23 | const AccordionTrigger = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, children, ...props }, ref) => ( 27 | 28 | svg]:rotate-180", 32 | className 33 | )} 34 | {...props} 35 | > 36 | {children} 37 | 38 | 39 | 40 | )) 41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName 42 | 43 | const AccordionContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, children, ...props }, ref) => ( 47 | 52 |
{children}
53 |
54 | )) 55 | 56 | AccordionContent.displayName = AccordionPrimitive.Content.displayName 57 | 58 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } 59 | -------------------------------------------------------------------------------- /marketing/components/Hero/ImageHero.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | import Link from 'next/link'; 6 | import { Button } from '../ui/button'; 7 | 8 | interface ImageHeroProps { 9 | title: string; 10 | subTitle: string; 11 | buttonText: string; 12 | buttonLink: string; 13 | backgroundImage: string; 14 | alignment: 'left' | 'center' | 'right'; 15 | makeFullBleed: boolean; 16 | } 17 | 18 | const ImageHero: React.FC = ({ 19 | title, 20 | subTitle, 21 | buttonText, 22 | buttonLink, 23 | backgroundImage, 24 | alignment, 25 | makeFullBleed 26 | }) => { 27 | const alignmentClasses = { 28 | left: 'md:items-start md:text-left', 29 | center: 'md:items-center md:text-center', 30 | right: 'md:items-end md:text-right', 31 | }; 32 | 33 | return ( 34 |
35 | 41 |
42 |
43 |
44 |

45 | {title} 46 |

47 |

48 |

49 |
50 | 53 |
54 |
55 |
56 | ); 57 | }; 58 | 59 | export default ImageHero; -------------------------------------------------------------------------------- /marketing/components/ui/accordion.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AccordionPrimitive from "@radix-ui/react-accordion" 5 | import { ChevronDown } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Accordion = AccordionPrimitive.Root 10 | 11 | const AccordionItem = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 20 | )) 21 | AccordionItem.displayName = "AccordionItem" 22 | 23 | const AccordionTrigger = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, children, ...props }, ref) => ( 27 | 28 | svg]:rotate-180", 32 | className 33 | )} 34 | {...props} 35 | > 36 | {children} 37 | 38 | 39 | 40 | )) 41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName 42 | 43 | const AccordionContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, children, ...props }, ref) => ( 47 | 52 |
{children}
53 |
54 | )) 55 | 56 | AccordionContent.displayName = AccordionPrimitive.Content.displayName 57 | 58 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } 59 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/accordion.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AccordionPrimitive from "@radix-ui/react-accordion" 5 | import { ChevronDown } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Accordion = AccordionPrimitive.Root 10 | 11 | const AccordionItem = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 20 | )) 21 | AccordionItem.displayName = "AccordionItem" 22 | 23 | const AccordionTrigger = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, children, ...props }, ref) => ( 27 | 28 | svg]:rotate-180", 32 | className 33 | )} 34 | {...props} 35 | > 36 | {children} 37 | 38 | 39 | 40 | )) 41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName 42 | 43 | const AccordionContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, children, ...props }, ref) => ( 47 | 52 |
{children}
53 |
54 | )) 55 | 56 | AccordionContent.displayName = AccordionPrimitive.Content.displayName 57 | 58 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } 59 | -------------------------------------------------------------------------------- /ecommerce/src/components/Hero/ImageHero.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | import Link from 'next/link'; 6 | import { Button } from '../ui/button'; 7 | 8 | interface ImageHeroProps { 9 | title: string; 10 | subTitle?: string; 11 | buttonText: string; 12 | buttonLink: string; 13 | backgroundImage: string; 14 | alignment: 'left' | 'center' | 'right'; 15 | makeFullBleed: boolean; 16 | } 17 | 18 | const ImageHero: React.FC = ({ 19 | title, 20 | subTitle, 21 | buttonText, 22 | buttonLink, 23 | backgroundImage, 24 | alignment, 25 | makeFullBleed 26 | }) => { 27 | const alignmentClasses = { 28 | left: 'md:items-start md:text-left', 29 | center: 'md:items-center md:text-center', 30 | right: 'md:items-end md:text-right', 31 | }; 32 | 33 | return ( 34 |
35 | 41 |
42 |
43 |
44 |

45 | {title} 46 |

47 |
48 |
49 |
50 | 53 |
54 |
55 |
56 | ); 57 | }; 58 | 59 | export default ImageHero; -------------------------------------------------------------------------------- /vcp-playground/src/components/Hero/ImageHero.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | import Link from 'next/link'; 6 | import { Button } from '../ui/button'; 7 | 8 | interface ImageHeroProps { 9 | title: string; 10 | subTitle?: string; 11 | buttonText: string; 12 | buttonLink: string; 13 | backgroundImage: string; 14 | alignment: 'left' | 'center' | 'right'; 15 | makeFullBleed: boolean; 16 | } 17 | 18 | const ImageHero: React.FC = ({ 19 | title, 20 | subTitle, 21 | buttonText, 22 | buttonLink, 23 | backgroundImage, 24 | alignment, 25 | makeFullBleed 26 | }) => { 27 | const alignmentClasses = { 28 | left: 'md:items-start md:text-left', 29 | center: 'md:items-center md:text-center', 30 | right: 'md:items-end md:text-right', 31 | }; 32 | 33 | return ( 34 |
35 | 41 |
42 |
43 |
44 |

45 | {title} 46 |

47 |

48 |

49 |
50 | 53 |
54 |
55 |
56 | ); 57 | }; 58 | 59 | export default ImageHero; -------------------------------------------------------------------------------- /ecommerce/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import * as React from "react"; 3 | import { Slot } from "@radix-ui/react-slot"; 4 | import { cva, type VariantProps } from "class-variance-authority"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const buttonVariants = cva( 9 | "inline-flex items-center justify-center whitespace-nowrap font-semibold text-sm uppercase tracking-[3.15px] ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 10 | { 11 | variants: { 12 | variant: { 13 | default: 14 | "bg-primary text-primary-foreground border border-transparent hover:bg-secondary hover:text-secondary-foreground hover:border-border", 15 | destructive: 16 | "bg-destructive text-destructive-foreground hover:text-primary-foreground", 17 | outline: 18 | "border border-input bg-background hover:bg-background hover:text-accent-foreground", 19 | secondary: 20 | "bg-secondary text-secondary-foreground hover:bg-primary hover:text-primary-foreground", 21 | tertiary: 22 | "bg-tertiary text-secondary-foreground hover:bg-secondary hover:text-secondary-foreground", 23 | ghost: "hover:bg-accent hover:text-accent-foreground", 24 | link: "text-primary underline-offset-4 hover:underline", 25 | }, 26 | size: { 27 | default: "px-5 py-2.5", 28 | sm: "h-9 px-3", 29 | lg: "h-11 px-8", 30 | icon: "h-10 w-10", 31 | }, 32 | }, 33 | defaultVariants: { 34 | variant: "default", 35 | size: "default", 36 | }, 37 | } 38 | ); 39 | 40 | export interface ButtonProps 41 | extends React.ButtonHTMLAttributes, 42 | VariantProps { 43 | asChild?: boolean; 44 | } 45 | 46 | const Button = React.forwardRef( 47 | ({ className, variant, size, asChild = false, ...props }, ref) => { 48 | const Comp = asChild ? Slot : "button"; 49 | return ( 50 | 55 | ); 56 | } 57 | ); 58 | Button.displayName = "Button"; 59 | 60 | export { Button, buttonVariants }; 61 | -------------------------------------------------------------------------------- /marketing/components/Layout/Footer.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | "use client"; 5 | import React from "react"; 6 | import { Button } from "../ui/button"; 7 | import { AuthSlider } from "./AuthSlider"; 8 | import { CartSlider } from "./CartSlider"; 9 | 10 | type NavItemProps = { 11 | text: string; 12 | isHighlighted?: boolean; 13 | }; 14 | 15 | const NavItem: React.FC = ({ text, isHighlighted }) => ( 16 |
{text}
17 | ); 18 | 19 | type SignInButtonProps = { 20 | text: string; 21 | }; 22 | 23 | const SignInButton: React.FC = ({ text }) => ( 24 | 27 | ); 28 | 29 | const Footer: React.FC = () => { 30 | const navItems = [ 31 | "WOMEN", 32 | "MEN", 33 | "COLLECTIONS", 34 | "SHOP ALL", 35 | { text: "SALE", isHighlighted: true }, 36 | ]; 37 | 38 | return ( 39 |
40 |
41 |
42 | 55 |
56 | 57 | 58 |
59 |
60 |

61 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam 62 | posuere erat a ante vestibulum, in volutpat ligula elementum. 63 |

64 |
65 |
66 | ); 67 | }; 68 | 69 | export default Footer; 70 | -------------------------------------------------------------------------------- /vcp-playground/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import * as React from "react"; 3 | import { Slot } from "@radix-ui/react-slot"; 4 | import { cva, type VariantProps } from "class-variance-authority"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const buttonVariants = cva( 9 | "inline-flex items-center justify-center whitespace-nowrap font-semibold text-sm uppercase tracking-[3.15px] ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 10 | { 11 | variants: { 12 | variant: { 13 | default: 14 | "bg-primary text-primary-foreground border border-transparent hover:bg-secondary hover:text-secondary-foreground hover:border-border", 15 | destructive: 16 | "bg-destructive text-destructive-foreground hover:text-primary-foreground", 17 | outline: 18 | "border border-input bg-background hover:bg-background hover:text-accent-foreground", 19 | secondary: 20 | "bg-secondary text-secondary-foreground hover:bg-primary hover:text-primary-foreground", 21 | tertiary: 22 | "bg-tertiary text-secondary-foreground hover:bg-secondary hover:text-secondary-foreground", 23 | ghost: "hover:bg-accent hover:text-accent-foreground", 24 | link: "text-primary underline-offset-4 hover:underline", 25 | }, 26 | size: { 27 | default: "px-5 py-2.5", 28 | sm: "h-9 px-3", 29 | lg: "h-11 px-8", 30 | icon: "h-10 w-10", 31 | }, 32 | }, 33 | defaultVariants: { 34 | variant: "default", 35 | size: "default", 36 | }, 37 | } 38 | ); 39 | 40 | export interface ButtonProps 41 | extends React.ButtonHTMLAttributes, 42 | VariantProps { 43 | asChild?: boolean; 44 | } 45 | 46 | const Button = React.forwardRef( 47 | ({ className, variant, size, asChild = false, ...props }, ref) => { 48 | const Comp = asChild ? Slot : "button"; 49 | return ( 50 | 55 | ); 56 | } 57 | ); 58 | Button.displayName = "Button"; 59 | 60 | export { Button, buttonVariants }; 61 | -------------------------------------------------------------------------------- /ecommerce/lib/shopify/api.ts: -------------------------------------------------------------------------------- 1 | import { buildClient } from 'shopify-buy' 2 | 3 | 4 | const fastClone = (obj: any) => JSON.parse(JSON.stringify(obj)) 5 | 6 | export function getAllProducts(config: ShopifyBuy.Config, limit?: number) { 7 | const client = buildClient(config) 8 | return client.product.fetchAll(limit) 9 | } 10 | 11 | 12 | export async function getAllProductPaths( 13 | config: ShopifyBuy.Config, 14 | limit?: number 15 | ): Promise { 16 | const client = buildClient(config) 17 | // interface need update 18 | const products: any[] = await client.product.fetchAll(limit) 19 | return products.map((val) => val.handle) 20 | } 21 | 22 | export async function getProduct( 23 | config: ShopifyBuy.Config, 24 | options: { id?: string; handle?: string } 25 | ) { 26 | const client = buildClient(config) 27 | if (options.handle) { 28 | return fastClone(await client.product.fetchByHandle(options.handle)) 29 | } 30 | if (!options.id) { 31 | throw new Error('A product ID or handle is required') 32 | } 33 | return fastClone(await client.product.fetch(options.id)) 34 | } 35 | 36 | export function getAllCollections(config: ShopifyBuy.Config, limit?: number) { 37 | const client = buildClient(config) 38 | return client.collection.fetchAll(limit) 39 | } 40 | 41 | export async function getAllCollectionPaths( 42 | config: ShopifyBuy.Config, 43 | limit?: number 44 | ): Promise { 45 | const client = buildClient(config) 46 | // interface need update 47 | const collections: any[] = await client.collection.fetchAll(limit) 48 | return collections.map((val) => val.handle) 49 | } 50 | 51 | export async function getCollection( 52 | config: ShopifyBuy.Config, 53 | options: { id?: string; handle?: string } 54 | ) { 55 | const client = buildClient(config) 56 | if (options.handle) { 57 | return fastClone(await client.collection.fetchByHandle(options.handle)) 58 | } 59 | if (!options.id) { 60 | throw new Error('A collection ID or handle is required') 61 | } 62 | return fastClone(await client.collection.fetch(options.id)) 63 | } 64 | 65 | export async function searchProducts( 66 | config: ShopifyBuy.Config, 67 | searchString: string 68 | ) { 69 | const client = buildClient(config) 70 | return client.product.fetchQuery({ 71 | query: searchString ? `title:*${searchString}*` : '', 72 | // sortBy: 'title', 73 | }) 74 | } -------------------------------------------------------------------------------- /ecommerce/next.config.mjs: -------------------------------------------------------------------------------- 1 | import BuilderDevTools from "@builder.io/dev-tools/next"; 2 | import { withHydrationOverlay } from "@builder.io/react-hydration-overlay/next"; 3 | 4 | /** @type {import('next').NextConfig} */ 5 | const nextConfig = { 6 | images: { 7 | dangerouslyAllowSVG: true, 8 | remotePatterns: [ 9 | { 10 | protocol: "https", 11 | hostname: "cdn.builder.io", 12 | port: "", 13 | pathname: "/**", 14 | }, 15 | { 16 | protocol: "https", 17 | hostname: "img.shopstyle-cdn.com", 18 | port: "", 19 | pathname: "/**", 20 | }, 21 | { 22 | protocol: "https", 23 | hostname: "cdn.shopify.com", 24 | port: "", 25 | pathname: "/**", 26 | }, 27 | { 28 | protocol: "https", 29 | hostname: "cdn.swell.store", 30 | port: "", 31 | pathname: "/**", 32 | }, 33 | { 34 | protocol: "https", 35 | hostname: "res.cloudinary.com", 36 | port: "", 37 | pathname: "/**", 38 | }, 39 | { 40 | protocol: "http", 41 | hostname: "res.cloudinary.com", 42 | port: "", 43 | pathname: "/**", 44 | }, 45 | { 46 | protocol: "https", 47 | hostname: "algolia", 48 | port: "", 49 | pathname: "/**", 50 | }, 51 | { 52 | protocol: "https", 53 | hostname: "burst.shopifycdn.com", 54 | port: "", 55 | pathname: "/**", 56 | }, 57 | { 58 | protocol: "https", 59 | hostname: "shopifycdn.com", 60 | port: "", 61 | pathname: "/**", 62 | }, 63 | { 64 | protocol: "https", 65 | hostname: "storage.googleapis.com", 66 | port: "", 67 | pathname: "/**", 68 | }, 69 | ], 70 | }, 71 | experimental: { 72 | serverComponentsExternalPackages: ["isolated-vm"], 73 | }, 74 | }; 75 | // export default nextConfig; 76 | const configWithOverlay = BuilderDevTools()( 77 | BuilderDevTools()( 78 | withHydrationOverlay({ 79 | /** 80 | * Optional: `appRootSelector` is the selector for the root element of your app. By default, it is `#__next` which works 81 | * for Next.js apps with pages directory. If you are using the app directory, you should change this to `main`. 82 | */ 83 | appRootSelector: "main", 84 | })(nextConfig) 85 | ) 86 | ); 87 | 88 | export default configWithOverlay; 89 | -------------------------------------------------------------------------------- /ecommerce/src/components/AlgoliaSearchBox/AlgoliaSearchBox.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | InstantSearch, 4 | SearchBox, 5 | RefinementList, 6 | connectHits, 7 | connectStateResults, 8 | } from "react-instantsearch-dom"; 9 | import { client } from "../../../config/algolia"; 10 | import ProductBox from "../ui/productBox"; 11 | 12 | interface HitData { 13 | previewUrl: string | undefined; 14 | objectID: string; 15 | data: { 16 | images?: { image: string }[]; 17 | productName: string; 18 | category: string; 19 | price: number; 20 | description: string; 21 | sizes?: { label: string }[]; 22 | }; 23 | } 24 | 25 | const EditHits = ({ hits }: { hits: HitData[] }) => { 26 | return ( 27 |
28 | {hits.slice(0, 5).map((hit: any) => ( 29 | 30 | ))} 31 |
32 | ); 33 | }; 34 | 35 | const ConnectedHits = connectHits(EditHits); 36 | 37 | const ConditionalHits = connectStateResults(({ searchState }) => { 38 | const query = searchState && searchState.query; 39 | return query && query.trim() ? : null; 40 | }); 41 | 42 | const AlgoliaSearchBox = () => { 43 | return ( 44 |
45 | 46 |
47 |
48 | 53 |
54 |

55 | Search by name, category, color, or size 56 |

57 |
58 | 59 |
60 | 61 | 62 | 63 |
64 | 65 | 66 |
67 |
68 | ); 69 | }; 70 | 71 | function Hit({ hit }: { hit: HitData }) { 72 | if (!hit || Object.keys(hit).length === 0) { 73 | return null; 74 | } 75 | 76 | return ; 77 | } 78 | 79 | export default AlgoliaSearchBox; 80 | -------------------------------------------------------------------------------- /marketing/components/Hero/SplitHero.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by Builder.io. 3 | */ 4 | import React from 'react'; 5 | import Link from "next/link" 6 | import { Button } from '../ui/button'; 7 | 8 | interface SplitHeroProps { 9 | imageAlignment: 'left' | 'right'; 10 | title: string; 11 | subTitle: any; 12 | image: string; 13 | altText: string; 14 | hasCTA: boolean; 15 | buttonText?: string; 16 | buttonLink?: any; 17 | makeFullBleed: boolean; 18 | } 19 | 20 | const SplitHero: React.FC = ({ imageAlignment, title, subTitle, image, altText, hasCTA, buttonText, buttonLink, makeFullBleed }) => { 21 | const textContent = ( 22 |
23 |
24 |
25 |
26 |

{title}

27 | 28 |
29 | {hasCTA && 30 | 33 | } 34 |
35 |
36 |
37 | ); 38 | 39 | const imageContent = ( 40 |
41 |
42 | {altText} 48 |
49 |
50 | ); 51 | 52 | return ( 53 |
54 |
55 | {imageAlignment === 'left' ? ( 56 | <> 57 | {imageContent} 58 | {textContent} 59 | 60 | ) : ( 61 | <> 62 | {textContent} 63 | {imageContent} 64 | 65 | )} 66 |
67 |
68 | ); 69 | }; 70 | 71 | export default SplitHero; -------------------------------------------------------------------------------- /ecommerce/src/components/Card/ProductCard.tsx: -------------------------------------------------------------------------------- 1 | import ShopifyProduct from "@/src/components/ShopifyProduct/ShopifyProduct"; 2 | import CommercetoolsProduct from "../CommercetoolsProduct/CommercetoolsProduct"; 3 | import ProductBox from "../ui/productBox"; 4 | import SwellProduct from "../SwellProduct/SwellProduct"; 5 | import EmporixProduct from "../EmporixProduct/EmporixProduct"; 6 | 7 | interface ProductCardProps { 8 | product: any; 9 | classes?: string; 10 | dataSource?: string; 11 | shopifyProductHandle?: string; 12 | swellProductHandle?: string; 13 | commercetoolsProduct?: any; 14 | emporixProductHandle?: string; 15 | } 16 | 17 | const ProductCard: React.FC = ({ 18 | product, 19 | classes, 20 | dataSource, 21 | shopifyProductHandle, 22 | commercetoolsProduct, 23 | swellProductHandle, 24 | emporixProductHandle, 25 | }) => { 26 | if (dataSource === "Builder" && !product) { 27 | return

Please select a product

; 28 | } 29 | 30 | if (dataSource === "Shopify" && !shopifyProductHandle) { 31 | return

Product handle is required for Shopify products.

; 32 | } 33 | 34 | if (dataSource === "Swell" && !swellProductHandle) { 35 | return

Product handle is required for Swell products.

; 36 | } 37 | 38 | if (dataSource === "Commercetools" && !commercetoolsProduct) { 39 | return

Please select a Commercetools product

; 40 | } 41 | 42 | if (dataSource === "Emporix" && !emporixProductHandle) { 43 | return

Product handle is required for Emporix products.

; 44 | } 45 | 46 | return ( 47 |
50 | {dataSource === "Shopify" && ( 51 | 56 | )} 57 | {dataSource === "Swell" && ( 58 | 63 | )} 64 | {dataSource === "Commercetools" && ( 65 | 66 | )} 67 | {dataSource === "Emporix" && ( 68 | 73 | )} 74 | {(dataSource === "Builder" || dataSource === "Shopstyle") && ( 75 | 76 | )} 77 |
78 | ); 79 | }; 80 | 81 | export default ProductCard; 82 | -------------------------------------------------------------------------------- /ecommerce/src/components/Hero/SplitHero.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @next/next/no-img-element */ 2 | /** 3 | * This code was generated by Builder.io. 4 | */ 5 | import React from 'react'; 6 | import Link from "next/link" 7 | import { Button } from '../ui/button'; 8 | 9 | interface SplitHeroProps { 10 | imageAlignment: 'left' | 'right'; 11 | textAlignment: 'left' | 'center' | 'right'; 12 | splitWidth: '1/2' | '1/3'; 13 | title: string; 14 | subTitle: any; 15 | image: string; 16 | altText: string; 17 | hasCTA: boolean; 18 | buttonText?: string; 19 | buttonLink?: any; 20 | makeFullBleed: boolean; 21 | } 22 | 23 | const SplitHero: React.FC = ({ imageAlignment, splitWidth, textAlignment, title, subTitle, image, altText, hasCTA, buttonText, buttonLink, makeFullBleed }) => { 24 | const textWidth = imageAlignment === 'right' ? splitWidth === "1/2" ? "md:w-1/2" : "md:w-2/3" : splitWidth === "1/2" ? "md:w-1/2" : "md:w-2/3"; 25 | const imageWidth = imageAlignment === 'right' ? splitWidth === "1/2" ? "md:w-1/2" : "md:w-1/3" : splitWidth === "1/2" ? "md:w-1/2" : "md:w-1/3"; 26 | 27 | const textContent = ( 28 |
29 |
30 |
31 |
32 |

{title}

33 | 34 |
35 | {hasCTA && 36 | 39 | } 40 |
41 |
42 |
43 | ); 44 | 45 | const imageContent = ( 46 |
47 |
48 | {altText} 54 |
55 |
56 | ); 57 | 58 | return ( 59 |
60 |
61 | {imageAlignment === 'left' ? ( 62 | <> 63 | {imageContent} 64 | {textContent} 65 | 66 | ) : ( 67 | <> 68 | {textContent} 69 | {imageContent} 70 | 71 | )} 72 |
73 |
74 | ); 75 | }; 76 | 77 | export default SplitHero; -------------------------------------------------------------------------------- /vcp-playground/src/components/Hero/SplitHero.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @next/next/no-img-element */ 2 | /** 3 | * This code was generated by Builder.io. 4 | */ 5 | import React from 'react'; 6 | import Link from "next/link" 7 | import { Button } from '../ui/button'; 8 | 9 | interface SplitHeroProps { 10 | imageAlignment: 'left' | 'right'; 11 | textAlignment: 'left' | 'center' | 'right'; 12 | splitWidth: '1/2' | '1/3'; 13 | title: string; 14 | subTitle: any; 15 | image: string; 16 | altText: string; 17 | hasCTA: boolean; 18 | buttonText?: string; 19 | buttonLink?: any; 20 | makeFullBleed: boolean; 21 | } 22 | 23 | const SplitHero: React.FC = ({ imageAlignment, splitWidth, textAlignment, title, subTitle, image, altText, hasCTA, buttonText, buttonLink, makeFullBleed }) => { 24 | const textWidth = imageAlignment === 'right' ? splitWidth === "1/2" ? "md:w-1/2" : "md:w-2/3" : splitWidth === "1/2" ? "md:w-1/2" : "md:w-2/3"; 25 | const imageWidth = imageAlignment === 'right' ? splitWidth === "1/2" ? "md:w-1/2" : "md:w-1/3" : splitWidth === "1/2" ? "md:w-1/2" : "md:w-1/3"; 26 | 27 | const textContent = ( 28 |
29 |
30 |
31 |
32 |

{title}

33 | 34 |
35 | {hasCTA && 36 | 39 | } 40 |
41 |
42 |
43 | ); 44 | 45 | const imageContent = ( 46 |
47 |
48 | {altText} 54 |
55 |
56 | ); 57 | 58 | return ( 59 |
60 |
61 | {imageAlignment === 'left' ? ( 62 | <> 63 | {imageContent} 64 | {textContent} 65 | 66 | ) : ( 67 | <> 68 | {textContent} 69 | {imageContent} 70 | 71 | )} 72 |
73 |
74 | ); 75 | }; 76 | 77 | export default SplitHero; -------------------------------------------------------------------------------- /vcp-playground/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import styles from "./page.module.css"; 3 | 4 | export default function Home() { 5 | return ( 6 |
94 | ); 95 | } 96 | --------------------------------------------------------------------------------