├── dist ├── favicon.ico ├── static │ ├── favicon.ico │ ├── favicon-192.png │ ├── favicon-512.png │ ├── apple-touch-icon.png │ ├── manifest.webmanifest │ ├── refractor-KYq3km5Z.js │ ├── create-manifest.json │ ├── favicon.svg │ ├── ViteDevServerStopped-DHTTIZvn.js │ ├── resources-DUHp_Zll.js │ ├── resources4-CSctsSbN.js │ ├── index-o8Z_cZLs.js │ ├── 9958038f.create-schema.json │ ├── f4be14c6.create-tools.json │ ├── index2-D9Rare3g.js │ ├── resources-yGHF8Toy.js │ ├── resources3-L9TjxJAN.js │ ├── stegaEncodeSourceMap-B9OfWko4.js │ ├── resources2-Cts1kShQ.js │ ├── browser-D2aw37IN.js │ ├── resources5-Bb_1eNso.js │ └── index3-B5cTfUTo.js ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg ├── next.svg └── index.html ├── production.tar.gz ├── src ├── app │ ├── favicon.ico │ ├── globals.css │ ├── page.tsx │ ├── studio │ │ └── [[...tool]] │ │ │ └── page.tsx │ ├── layout.tsx │ ├── auth │ │ ├── sign-up │ │ │ └── page.tsx │ │ └── sign-in │ │ │ └── page.tsx │ ├── search │ │ └── page.tsx │ ├── category │ │ └── [slug] │ │ │ └── page.tsx │ └── product │ │ └── [id] │ │ └── page.tsx ├── lib │ ├── prisma.ts │ └── utils.ts ├── sanity │ ├── structure.ts │ ├── lib │ │ ├── image.ts │ │ ├── live.ts │ │ └── client.ts │ ├── schemaTypes │ │ ├── index.ts │ │ └── schema │ │ │ ├── product-category.ts │ │ │ ├── promotion-codes.ts │ │ │ ├── promotion-campaign.ts │ │ │ └── product.ts │ └── env.ts ├── components │ ├── product │ │ ├── ProductGrid.tsx │ │ ├── ProductItem.tsx │ │ └── AddToCartButton.tsx │ ├── layout │ │ ├── HeaderSearchBar.tsx │ │ ├── SalesCampaignBanner.tsx │ │ ├── HeaderCategorySelector.tsx │ │ └── Header.tsx │ └── auth │ │ ├── SignIn.tsx │ │ └── SignUp.tsx ├── stores │ └── cart-store.ts └── actions │ ├── auth.ts │ └── cart-action.ts ├── postcss.config.mjs ├── public ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── prisma ├── migrations │ ├── migration_lock.toml │ └── 20250324041958_init │ │ └── migration.sql └── schema.prisma ├── next.config.ts ├── .sanity └── runtime │ ├── app.js │ └── index.html ├── sanity.cli.ts ├── eslint.config.mjs ├── .gitignore ├── tsconfig.json ├── sanity.config.ts ├── package.json ├── middleware.ts ├── README.md └── sanity.types.ts /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/engripaye/ecommerce-store/HEAD/dist/favicon.ico -------------------------------------------------------------------------------- /production.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/engripaye/ecommerce-store/HEAD/production.tar.gz -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/engripaye/ecommerce-store/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /dist/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/engripaye/ecommerce-store/HEAD/dist/static/favicon.ico -------------------------------------------------------------------------------- /dist/static/favicon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/engripaye/ecommerce-store/HEAD/dist/static/favicon-192.png -------------------------------------------------------------------------------- /dist/static/favicon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/engripaye/ecommerce-store/HEAD/dist/static/favicon-512.png -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /dist/static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/engripaye/ecommerce-store/HEAD/dist/static/apple-touch-icon.png -------------------------------------------------------------------------------- /dist/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lib/prisma.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | const prisma = new PrismaClient(); 4 | 5 | export default PrismaClient; -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | export const formatPrice = (price: number) => { 2 | return new Intl.NumberFormat("en-US", { 3 | style: 'currency', 4 | currency: 'USD', 5 | }).format(price); 6 | } -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | images: { 6 | domains: ["cdn.sanity.io"], 7 | }, 8 | }; 9 | 10 | export default nextConfig; 11 | -------------------------------------------------------------------------------- /src/sanity/structure.ts: -------------------------------------------------------------------------------- 1 | import type {StructureResolver} from 'sanity/structure' 2 | 3 | // https://www.sanity.io/docs/structure-builder-cheat-sheet 4 | export const structure: StructureResolver = (S) => 5 | S.list() 6 | .title('Content') 7 | .items(S.documentTypeListItems()) 8 | -------------------------------------------------------------------------------- /dist/static/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "icons": [ 3 | { 4 | "src": "/static/favicon-192.png", 5 | "type": "image/png", 6 | "sizes": "192x192" 7 | }, 8 | { 9 | "src": "/static/favicon-512.png", 10 | "type": "image/png", 11 | "sizes": "512x512" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.sanity/runtime/app.js: -------------------------------------------------------------------------------- 1 | 2 | // This file is auto-generated on 'sanity dev' 3 | // Modifications to this file is automatically discarded 4 | import {renderStudio} from "sanity" 5 | import studioConfig from "..\\..\\sanity.config.ts" 6 | 7 | renderStudio( 8 | document.getElementById("sanity"), 9 | studioConfig, 10 | {reactStrictMode: false, basePath: "/"} 11 | ) 12 | -------------------------------------------------------------------------------- /dist/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sanity.cli.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This configuration file lets you run `$ sanity [command]` in this folder 3 | * Go to https://www.sanity.io/docs/cli to learn more. 4 | **/ 5 | import { defineCliConfig } from 'sanity/cli' 6 | 7 | const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID 8 | const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET 9 | 10 | export default defineCliConfig({ api: { projectId, dataset } }) 11 | -------------------------------------------------------------------------------- /src/sanity/lib/image.ts: -------------------------------------------------------------------------------- 1 | import createImageUrlBuilder from '@sanity/image-url' 2 | import { SanityImageSource } from "@sanity/image-url/lib/types/types"; 3 | 4 | import { dataset, projectId } from '../env' 5 | 6 | // https://www.sanity.io/docs/image-url 7 | const builder = createImageUrlBuilder({ projectId, dataset }) 8 | 9 | export const urlFor = (source: SanityImageSource) => { 10 | return builder.image(source) 11 | } 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/sanity/schemaTypes/index.ts: -------------------------------------------------------------------------------- 1 | import { type SchemaTypeDefinition } from 'sanity' 2 | import { promotionCode } from './schema/promotion-codes' 3 | import { promotionCampaign } from './schema/promotion-campaign' 4 | import { productCategory } from './schema/product-category' 5 | import { product } from './schema/product' 6 | 7 | export const schema: { types: SchemaTypeDefinition[] } = { 8 | types: [ 9 | promotionCode, 10 | promotionCampaign, 11 | productCategory, 12 | product 13 | ], 14 | } 15 | -------------------------------------------------------------------------------- /src/sanity/lib/live.ts: -------------------------------------------------------------------------------- 1 | // Querying with "sanityFetch" will keep content automatically updated 2 | // Before using it, import and render "" in your layout, see 3 | // https://github.com/sanity-io/next-sanity#live-content-api for more information. 4 | import { defineLive } from "next-sanity"; 5 | import { client } from './client' 6 | 7 | const token = process.env.SANITY_API_READ_TOKEN; 8 | if(!token){ 9 | throw new Error('SANITY_API_TOKEN is not set'); 10 | } 11 | 12 | export const { sanityFetch, SanityLive } = defineLive({ 13 | client, 14 | serverToken: token, 15 | browserToken: token, 16 | }); -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | -------------------------------------------------------------------------------- /dist/static/refractor-KYq3km5Z.js: -------------------------------------------------------------------------------- 1 | import{a as d,R as u,j as i}from"./sanity-jqEJABLI.js";function f(c){const e=d.c(13),{language:g,value:a}=c,t=typeof g=="string"?g:void 0;let l;e[0]!==t?(l=t?u.hasLanguage(t):!1,e[0]=t,e[1]=l):l=e[1];const r=l;let s;e[2]!==t||e[3]!==r||e[4]!==a?(s=!(t&&r)&&i.jsx("code",{children:a}),e[2]=t,e[3]=r,e[4]=a,e[5]=s):s=e[5];let n;e[6]!==t||e[7]!==r||e[8]!==a?(n=t&&r&&i.jsx(u,{inline:!0,language:t,value:String(a)}),e[6]=t,e[7]=r,e[8]=a,e[9]=n):n=e[9];let o;return e[10]!==s||e[11]!==n?(o=i.jsxs(i.Fragment,{children:[s,n]}),e[10]=s,e[11]=n,e[12]=o):o=e[12],o}f.displayName="LazyRefractor";export{f as default}; 2 | -------------------------------------------------------------------------------- /src/sanity/env.ts: -------------------------------------------------------------------------------- 1 | export const apiVersion = 2 | process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2025-03-24' 3 | 4 | export const dataset = assertValue( 5 | process.env.NEXT_PUBLIC_SANITY_DATASET, 6 | 'Missing environment variable: NEXT_PUBLIC_SANITY_DATASET' 7 | ) 8 | 9 | export const projectId = assertValue( 10 | process.env.NEXT_PUBLIC_SANITY_PROJECT_ID, 11 | 'Missing environment variable: NEXT_PUBLIC_SANITY_PROJECT_ID' 12 | ) 13 | 14 | function assertValue(v: T | undefined, errorMessage: string): T { 15 | if (v === undefined) { 16 | throw new Error(errorMessage) 17 | } 18 | 19 | return v 20 | } 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/components/product/ProductGrid.tsx: -------------------------------------------------------------------------------- 1 | import { Product } from '@/sanity.types'; 2 | import React from 'react' 3 | import ProductItem from './ProductItem'; 4 | 5 | type ProductGridProps = { 6 | products: Product[]; 7 | } 8 | 9 | const ProductGrid = ({ products } : ProductGridProps ) => { 10 | return ( 11 |
12 | {products.map((product) => ( 13 | 14 | 18 | ) 19 | )} 20 | 21 |
22 | ) 23 | } 24 | 25 | export default ProductGrid -------------------------------------------------------------------------------- /src/sanity/schemaTypes/schema/product-category.ts: -------------------------------------------------------------------------------- 1 | import { defineField, defineType } from "sanity"; 2 | 3 | 4 | 5 | export const productCategory = defineType({ 6 | name: 'productCategory', 7 | title: 'Product Category', 8 | type: 'document', 9 | fields: [ 10 | defineField({ 11 | name: 'title', 12 | title: 'Title', 13 | type: 'string', 14 | }), 15 | defineField({ 16 | name: 'description', 17 | title: 'Description', 18 | type: 'text', 19 | }), 20 | defineField({ 21 | name: 'slug', 22 | title: 'Slug', 23 | type: 'slug', 24 | }), 25 | ] 26 | }) -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { getCurrentSession } from "@/actions/auth"; 2 | import SalesCampaignBanner from "@/components/layout/SalesCampaignBanner"; 3 | import ProductGrid from "@/components/product/ProductGrid"; 4 | import { getAllProducts } from "@/sanity/lib/client"; 5 | 6 | 7 | const Home = async () => { 8 | const { user } = await getCurrentSession(); 9 | const products = await getAllProducts(); 10 | 11 | return ( 12 |
13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | {/* Product Grid */} 21 | 22 | 23 | 24 |
25 |
26 | ); 27 | }; 28 | 29 | export default Home; 30 | 31 | -------------------------------------------------------------------------------- /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 | "@/*": ["./src/*", "./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /prisma/migrations/20250324041958_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Session" ( 3 | "id" TEXT NOT NULL, 4 | "userId" INTEGER NOT NULL, 5 | "expiresAt" TIMESTAMP(3) NOT NULL, 6 | 7 | CONSTRAINT "Session_pkey" PRIMARY KEY ("id") 8 | ); 9 | 10 | -- CreateTable 11 | CREATE TABLE "User" ( 12 | "id" SERIAL NOT NULL, 13 | "email" TEXT NOT NULL, 14 | "passwordHash" TEXT NOT NULL, 15 | 16 | CONSTRAINT "User_pkey" PRIMARY KEY ("id") 17 | ); 18 | 19 | -- CreateIndex 20 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); 21 | 22 | -- AddForeignKey 23 | ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 24 | -------------------------------------------------------------------------------- /src/app/studio/[[...tool]]/page.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This route is responsible for the built-in authoring environment using Sanity Studio. 3 | * All routes under your studio path is handled by this file using Next.js' catch-all routes: 4 | * https://nextjs.org/docs/routing/dynamic-routes#catch-all-routes 5 | * 6 | * You can learn more about the next-sanity package here: 7 | * https://github.com/sanity-io/next-sanity 8 | */ 9 | 10 | import { NextStudio } from 'next-sanity/studio' 11 | import config from '../../../../sanity.config' 12 | 13 | export const dynamic = 'force-static' 14 | 15 | export { metadata, viewport } from 'next-sanity/studio' 16 | 17 | export default function StudioPage() { 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /src/sanity/schemaTypes/schema/promotion-codes.ts: -------------------------------------------------------------------------------- 1 | import { defineField, defineType } from "sanity"; 2 | 3 | 4 | 5 | export const promotionCode = defineType({ 6 | name: 'promotionCode', 7 | title: 'Promotion Code', 8 | type: 'document', 9 | fields: [ 10 | defineField({ 11 | name: 'code', 12 | title: 'Code', 13 | type: 'string', 14 | }), 15 | defineField({ 16 | name: 'discountPerecentage', 17 | title: 'Discount Percentage(%)', 18 | type: 'number', 19 | }), 20 | defineField({ 21 | name: 'expirationDate', 22 | title: 'Expiration Date', 23 | type: 'date', 24 | }), 25 | ] 26 | }) -------------------------------------------------------------------------------- /src/sanity/schemaTypes/schema/promotion-campaign.ts: -------------------------------------------------------------------------------- 1 | import { defineField, defineType } from "sanity"; 2 | 3 | 4 | 5 | export const promotionCampaign = defineType({ 6 | name: 'promotionCampaign', 7 | title: 'Promotion Campaign', 8 | type: 'document', 9 | fields: [ 10 | defineField({ 11 | name: 'title', 12 | title: 'Title', 13 | type: 'string', 14 | }), 15 | defineField({ 16 | name: 'description', 17 | title: 'Description', 18 | type: 'text', 19 | }), 20 | defineField({ 21 | name: 'code', 22 | title: 'Code', 23 | type: 'reference', 24 | to: [{ type: 'promotionCode' }], 25 | }), 26 | ] 27 | }) -------------------------------------------------------------------------------- /src/sanity/schemaTypes/schema/product.ts: -------------------------------------------------------------------------------- 1 | import { defineField, defineType } from "sanity"; 2 | 3 | 4 | 5 | export const product = defineType({ 6 | name: 'product', 7 | title: 'Product', 8 | type: 'document', 9 | fields: [ 10 | defineField({ 11 | name: 'title', 12 | title: 'Title', 13 | type: 'string', 14 | }), 15 | defineField({ 16 | name: 'description', 17 | title: 'Description', 18 | type: 'text', 19 | }), 20 | defineField({ 21 | name: 'price', 22 | title: 'Price', 23 | type: 'number', 24 | }), 25 | defineField({ 26 | name: 'image', 27 | title: 'Image', 28 | type: 'image', 29 | }), 30 | defineField({ 31 | name: 'category', 32 | title: 'Category', 33 | type: 'reference', 34 | to: [{ type: 'productCategory' }], 35 | }) 36 | ] 37 | }) -------------------------------------------------------------------------------- /dist/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sanity.config.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | /** 4 | * This configuration is used to for the Sanity Studio that’s mounted on the `\src\app\studio\[[...tool]]\page.tsx` route 5 | */ 6 | 7 | import {visionTool} from '@sanity/vision' 8 | import {defineConfig} from 'sanity' 9 | import {structureTool} from 'sanity/structure' 10 | 11 | // Go to https://www.sanity.io/docs/api-versioning to learn how API versioning works 12 | import {apiVersion, dataset, projectId} from './src/sanity/env' 13 | import {schema} from './src/sanity/schemaTypes' 14 | import {structure} from './src/sanity/structure' 15 | 16 | export default defineConfig({ 17 | basePath: '/studio', 18 | projectId, 19 | dataset, 20 | // Add and edit the content schema in the './sanity/schemaTypes' folder 21 | schema, 22 | plugins: [ 23 | structureTool({structure}), 24 | // Vision is for querying with GROQ from inside the Studio 25 | // https://www.sanity.io/docs/the-vision-plugin 26 | visionTool({defaultApiVersion: apiVersion}), 27 | ], 28 | }) 29 | -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | } 4 | 5 | datasource db { 6 | provider = "postgresql" 7 | url = env("DATABASE_URL") 8 | } 9 | 10 | model Session { 11 | id String @id 12 | userId Int 13 | expiresAt DateTime 14 | user User @relation(fields: [userId], references: [id]) 15 | } 16 | 17 | model User { 18 | id Int @id @default(autoincrement()) 19 | email String @unique 20 | passwordHash String 21 | sessions Session[] 22 | cart Cart? 23 | } 24 | 25 | model CartLineItem { 26 | id String @id 27 | sanityProductId String 28 | quantity Int 29 | 30 | title String 31 | price Float 32 | Image String 33 | 34 | cartId String 35 | cart Cart @relation(references: [id], fields: [cartId], onDelete: Cascade) 36 | 37 | } 38 | 39 | model Cart { 40 | id String @id 41 | 42 | userId Int? @unique 43 | user User? @relation(references: [id], fields: [userId], onDelete: Cascade) 44 | 45 | items CartLineItem[] 46 | } -------------------------------------------------------------------------------- /dist/static/create-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "createdAt": "2025-03-24T07:34:56.720Z", 4 | "workspaces": [ 5 | { 6 | "name": "default", 7 | "title": "Default", 8 | "basePath": "/studio", 9 | "projectId": "nt990nqg", 10 | "dataset": "production", 11 | "icon": "D", 12 | "schema": "9958038f.create-schema.json", 13 | "tools": "f4be14c6.create-tools.json" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /src/components/layout/HeaderSearchBar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from 'react'; 3 | import Form from 'next/form'; 4 | 5 | const HeaderSearchBar = () => { 6 | 7 | 8 | 9 | return ( 10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 |
18 | 24 |
25 |
26 | ) 27 | } 28 | 29 | export default HeaderSearchBar -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce-store", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@oslojs/crypto": "^1.0.1", 13 | "@oslojs/encoding": "^1.1.0", 14 | "@prisma/client": "^6.5.0", 15 | "@sanity/image-url": "^1.1.0", 16 | "@sanity/vision": "^3.81.0", 17 | "lucide-react": "^0.483.0", 18 | "next": "15.2.3", 19 | "next-sanity": "^9.9.5", 20 | "react": "^19.0.0", 21 | "react-dom": "^19.0.0", 22 | "sanity": "^3.81.0", 23 | "styled-components": "^6.1.16", 24 | "zod": "^3.24.2", 25 | "zustand": "^5.0.3" 26 | }, 27 | "devDependencies": { 28 | "@eslint/eslintrc": "^3", 29 | "@tailwindcss/postcss": "^4", 30 | "@types/node": "^20", 31 | "@types/react": "^19", 32 | "@types/react-dom": "^19", 33 | "eslint": "^9", 34 | "eslint-config-next": "15.2.3", 35 | "prisma": "^6.5.0", 36 | "tailwindcss": "^4", 37 | "typescript": "^5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dist/static/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import "./globals.css"; 3 | import { Inter } from "next/font/google"; 4 | const inter = Inter({ subsets: ["latin"] }); 5 | import Header from '@/components/layout/Header'; 6 | import React from "react"; 7 | import { getCurrentSession } from "@/actions/auth"; 8 | import { SanityLive } from "@/sanity/lib/live"; 9 | import HeaderCategorySelector from "@/components/layout/HeaderCategorySelector"; 10 | 11 | 12 | export const metadata: Metadata = { 13 | title: "Create Next App", 14 | description: "Generated by create next app", 15 | }; 16 | 17 | const RootLayout = async ({ 18 | children, 19 | }: Readonly<{ 20 | children: React.ReactNode; 21 | }>) => { 22 | const { user } = await getCurrentSession(); 23 | 24 | 25 | return ( 26 | 27 | 28 |
} 31 | /> 32 | {children} 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | 40 | export default RootLayout; 41 | 42 | -------------------------------------------------------------------------------- /dist/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/auth/sign-up/page.tsx: -------------------------------------------------------------------------------- 1 | import { getCurrentSession, loginUser, registerUser } from '@/actions/auth'; 2 | import SignUp from '@/components/auth/SignUp'; 3 | import { redirect } from 'next/navigation'; 4 | import React from 'react'; 5 | import zod from 'zod'; 6 | 7 | const SignUpSchema = zod.object({ 8 | email: zod.string().email(), 9 | password: zod.string().min(5), 10 | }); 11 | 12 | const SignUpPage = async () => { 13 | const { user } = await getCurrentSession(); 14 | 15 | if (user) { 16 | return redirect('/'); 17 | } 18 | 19 | const action = async (prevState: any, formData: FormData) => { 20 | "use server"; 21 | const parsed = SignUpSchema.safeParse(Object.fromEntries(formData)); 22 | if (!parsed.success) { 23 | return { 24 | message: "Invalid form data", 25 | }; 26 | } 27 | 28 | const { email, password } = parsed.data; 29 | const { user, error } = await registerUser(email, password); 30 | if (error) { 31 | return { message: error }; 32 | } 33 | 34 | if (user) { 35 | await loginUser(email, password); 36 | return { success: true }; // Indicate successful signup 37 | } 38 | 39 | return { message: "Something went wrong" }; 40 | }; 41 | 42 | return ; 43 | }; 44 | 45 | export default SignUpPage; 46 | -------------------------------------------------------------------------------- /src/app/auth/sign-in/page.tsx: -------------------------------------------------------------------------------- 1 | import { getCurrentSession, loginUser, registerUser } from '@/actions/auth'; 2 | import SignIn from '@/components/auth/SignIn'; 3 | import SignUp from '@/components/auth/SignUp'; 4 | import { redirect } from 'next/navigation'; 5 | import React from 'react'; 6 | import zod from 'zod'; 7 | 8 | const SignInSchema = zod.object({ 9 | email: zod.string().email(), 10 | password: zod.string().min(5), 11 | }); 12 | 13 | const SignInPage = async () => { 14 | const { user } = await getCurrentSession(); 15 | 16 | if (user) { 17 | return redirect('/'); 18 | } 19 | 20 | const action = async (prevState: any, formData: FormData) => { 21 | "use server"; 22 | const parsed = SignInSchema.safeParse(Object.fromEntries(formData)); 23 | if (!parsed.success) { 24 | return { 25 | message: "Invalid form data", 26 | }; 27 | } 28 | 29 | const { email, password } = parsed.data; 30 | const { user, error } = await loginUser(email, password); 31 | if (error) { 32 | return { message: error }; 33 | } 34 | 35 | if (user) { 36 | 37 | return { success: true }; // Indicate successful signup 38 | } 39 | 40 | return { message: "Something went wrong" }; 41 | }; 42 | 43 | return ; 44 | }; 45 | 46 | export default SignInPage; 47 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | // middleware.ts 2 | import { NextResponse } from "next/server"; 3 | 4 | import type { NextRequest } from "next/server"; 5 | 6 | export async function middleware(request: NextRequest): Promise { 7 | if (request.method === "GET") { 8 | const response = NextResponse.next(); 9 | const token = request.cookies.get("session")?.value ?? null; 10 | if (token !== null) { 11 | // Only extend cookie expiration on GET requests since we can be sure 12 | // a new session wasn't set when handling the request. 13 | response.cookies.set("session", token, { 14 | path: "/", 15 | maxAge: 60 * 60 * 24 * 30, 16 | sameSite: "lax", 17 | httpOnly: true, 18 | secure: process.env.NODE_ENV === "production" 19 | }); 20 | } 21 | return response; 22 | } 23 | 24 | 25 | 26 | // CSRF PROTECTION 27 | const originHeader = request.headers.get("Origin"); 28 | // NOTE: You may need to use `X-Forwarded-Host` instead 29 | const hostHeader = request.headers.get("Host"); 30 | if (originHeader === null || hostHeader === null) { 31 | return new NextResponse(null, { 32 | status: 403 33 | }); 34 | } 35 | let origin: URL; 36 | try { 37 | origin = new URL(originHeader); 38 | } catch { 39 | return new NextResponse(null, { 40 | status: 403 41 | }); 42 | } 43 | if (origin.host !== hostHeader) { 44 | return new NextResponse(null, { 45 | status: 403 46 | }); 47 | } 48 | return NextResponse.next(); 49 | } -------------------------------------------------------------------------------- /dist/static/ViteDevServerStopped-DHTTIZvn.js: -------------------------------------------------------------------------------- 1 | import{a,j as o,C as l,b as m,S as d,T as h,r as p,H as u}from"./sanity-jqEJABLI.js";const S="Dev server stopped",f="The development server has stopped. You may need to restart it to continue working.";class x extends Error{constructor(){super(S),this.name="ViteDevServerStoppedError",this.ViteDevServerStoppedError=!0}}const _=e=>!1,E=()=>{const e=a.c(5),[r,s]=p.useState(!1);let t;e[0]===Symbol.for("react.memo_cache_sentinel")?(t=()=>s(!0),e[0]=t):t=e[0];const v=t;let n,c;e[1]===Symbol.for("react.memo_cache_sentinel")?(n=()=>{},c=[v],e[1]=n,e[2]=c):(n=e[1],c=e[2]),p.useEffect(n,c);let i;return e[3]!==r?(i={devServerStopped:r},e[3]=r,e[4]=i):i=e[4],i},D=()=>{const{devServerStopped:e}=E();if(e)throw new x;return null},b=()=>{const e=a.c(1);let r;return e[0]===Symbol.for("react.memo_cache_sentinel")?(r=_()?o.jsx(D,{}):null,e[0]=r):r=e[0],r},R=()=>{const e=a.c(3);let r;e[0]===Symbol.for("react.memo_cache_sentinel")?(r=[4,5,6,7],e[0]=r):r=e[0];let s;e[1]===Symbol.for("react.memo_cache_sentinel")?(s=o.jsx(u,{children:S}),e[1]=s):s=e[1];let t;return e[2]===Symbol.for("react.memo_cache_sentinel")?(t=o.jsx(l,{height:"fill",overflow:"auto",paddingY:r,paddingX:4,sizing:"border",tone:"critical",children:o.jsx(m,{width:3,children:o.jsxs(d,{space:4,children:[s,o.jsx(l,{border:!0,radius:2,overflow:"auto",padding:4,tone:"inherit",children:o.jsx(d,{space:4,children:o.jsx(h,{size:2,children:f})})})]})})}),e[2]=t):t=e[2],t};export{b as DetectViteDevServerStopped,R as DevServerStoppedErrorScreen,x as ViteDevServerStoppedError}; 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dist/static/resources-DUHp_Zll.js: -------------------------------------------------------------------------------- 1 | import{d as e}from"./sanity-jqEJABLI.js";const t=e("vision",{"action.copy-url-to-clipboard":"Copy to clipboard","action.listen-cancel":"Stop","action.listen-execute":"Listen","action.query-cancel":"Cancel","action.query-execute":"Fetch","label.new":"New","params.error.params-invalid-json":"Parameters are not valid JSON","params.label":"Params","query.error.column":"Column","query.error.line":"Line","query.label":"Query","query.url":"Query URL","result.end-to-end-time-label":"End-to-end","result.execution-time-label":"Execution","result.label":"Result","result.save-result-as-csv.not-csv-encodable":"Result cannot be encoded as CSV","result.save-result-as-format":"Save result as ","result.timing-not-applicable":"n/a","settings.api-version-label":"API version","settings.custom-api-version-label":"Custom API version","settings.dataset-label":"Dataset","settings.error.invalid-api-version":"Invalid API version","settings.other-api-version-label":"Other","settings.perspective-label":"Perspective","settings.perspective.preview-drafts-renamed-to-drafts.description":'The "previewDrafts" perspective has been renamed to "drafts" and is now deprecated. This change is effective for all versions with perspective support (>= v2021-03-25).',"settings.perspectives.action.docs-link":"Read docs","settings.perspectives.default":"No perspective (API default)","settings.perspectives.description":'Perspectives allow your query to run against different "views" of the content in your dataset',"settings.perspectives.new-default.description":'The default perspective will change from "raw" to "published" in an upcoming API version. Please consult docs for more details.',"settings.perspectives.pinned-release-label":"Pinned release","settings.perspectives.title":"Perspectives"});export{t as default}; 2 | -------------------------------------------------------------------------------- /src/components/layout/SalesCampaignBanner.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useRouter } from 'next/navigation'; 3 | import React from 'react' 4 | 5 | const SalesCampaignBanner = () => { 6 | 7 | const router = useRouter(); 8 | 9 | return ( 10 |
11 |
12 |
13 |
14 | 15 | 🔥 16 | 17 |
18 | FLASH SALE ENDS IN: 19 |
20 |
21 | 23:59:59 22 |
23 |
24 | 25 |
26 | 27 | 28 | UP TO 95% OFF! 29 | 30 |
31 | 32 | 40 | 41 |
42 | 43 |
44 | 45 |
46 | ) 47 | } 48 | 49 | export default SalesCampaignBanner -------------------------------------------------------------------------------- /src/components/layout/HeaderCategorySelector.tsx: -------------------------------------------------------------------------------- 1 | import { getAllCategories } from '@/sanity/lib/client'; 2 | import Link from 'next/link'; 3 | import React from 'react' 4 | 5 | const HeaderCategorySelector = async () => { 6 | 7 | const categories = await getAllCategories(); 8 | return ( 9 |
10 | 28 | 29 |
30 |
31 |
32 | {categories.map((category) => ( 33 | 39 | {category.title} 40 | 41 | ))} 42 | 43 | 44 |
45 |
46 |
47 |
48 | ) 49 | } 50 | 51 | export default HeaderCategorySelector 52 | -------------------------------------------------------------------------------- /dist/static/resources4-CSctsSbN.js: -------------------------------------------------------------------------------- 1 | import{d as e}from"./sanity-jqEJABLI.js";const i=e("create",{"start-in-create-link.label":"Start in Create","start-in-create-banner.title":"Get started with Sanity Create","start-in-create-banner.title-badge":"Early access","start-in-create-banner.subtitle":"A free-form, AI-powered editor that syncs directly with your Studio documents.","start-in-create-dialog.cta.learn-more":"Learn more.","start-in-create-dialog.error-toast.unresolved-url":"Unable to determine Sanity Create URL.","linking-in-progress-dialog.header":"Linking to Sanity Create","linking-in-progress-dialog.lede":"Head back to Sanity Create. Your document will sync automatically (usually takes a few seconds).","linking-in-progress-dialog.details":"Once linked, your edits will appear here in real-time.","linking-in-progress-dialog.troubleshooting.button.title":"Linking delayed?","linking-in-progress-dialog.troubleshooting.content":"Check if your Sanity Create window is still open, or if the process seems stuck. If problems persist, try refreshing the Studio and try again.","studio-create-link-banner.text":"This document is linked to Sanity Create","create-link-info.tooltip":"Learn more","create-link-info-popover.eyebrow-title":"Sanity Create","create-link-info-popover.eyebrow-badge":"Early access","create-link-info-popover.header":"Idea-first authoring","create-link-info-popover.text":"Write naturally in an AI-powered editor. Your content automatically maps to Studio fields as you type.","edit-in-create-button.text":"Edit with Sanity Create","unlink-from-create-button.text":"Unlink","unlink-from-create-dialog.header":"Switch editing to Studio?","unlink-from-create-dialog.first-paragraph":"You’re unlinking “{{title}}” from Sanity Create so it can be edited here.","unlink-from-create-dialog.second-paragraph":"You’ll keep your content in both places. Any new changes in Sanity Create will stop syncing to this Studio.","unlink-from-create-dialog.cancel.text":"Cancel","unlink-from-create-dialog.document.untitled.text":"Untitled","unlink-from-create-dialog.unlink.text":"Unlink now"});export{i as default}; 2 | -------------------------------------------------------------------------------- /src/sanity/lib/client.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from 'next-sanity' 2 | 3 | import { apiVersion, dataset, projectId } from '../env' 4 | import { sanityFetch } from './live' 5 | import { Product, ProductCategory } from '@/sanity.types' 6 | 7 | export const client = createClient({ 8 | projectId, 9 | dataset, 10 | apiVersion, 11 | useCdn: true, // Set to false if statically generating pages, using ISR or tag-based revalidation 12 | }) 13 | 14 | export const getAllProducts = async () => { 15 | const query = `*[_type == "product"]` 16 | const products = await sanityFetch({query: query}) 17 | return products.data as Product[]; 18 | } 19 | 20 | export const getAllCategories = async () => { 21 | const query = `*[_type == "productCategory"]` 22 | const categories = await sanityFetch({query: query}) 23 | return categories.data as ProductCategory[]; 24 | } 25 | 26 | export const getCategoryBySlug = async (slug: string) => { 27 | const query = `*[_type == "productCategory" && slug.current == $slug][0]` 28 | const category = await sanityFetch({query: query, params: {slug}}); 29 | return category.data as ProductCategory; 30 | 31 | } 32 | 33 | export const getProductsByCategorySlug = async (slug: string) => { 34 | const query = `*[_type == "product" && references(*[_type == "productCategory" && slug.current == $slug][0]._id)]` 35 | const products = await sanityFetch({query: query, params: {slug}}); 36 | return products.data as Product[]; 37 | 38 | } 39 | 40 | export const getProductById = async (id: string) => { 41 | const query = `*[_type == "product" && _id == $id][0]`; 42 | const product = await sanityFetch({ query: query, params: { id } }); 43 | return product.data as Product; 44 | } 45 | 46 | export const searchProducts = async (searchQuery: string) => { 47 | const query = `*[_type == "product" && ( 48 | title match "*" + $searchQuery + "*" || 49 | description match "*" + $searchQuery + "*" || 50 | category->title match "*" + $searchQuery + "*" || 51 | category->slug.current match "*" + $searchQuery + "*" 52 | )]`; 53 | 54 | const products = await sanityFetch({ query: query, params: { searchQuery } }); 55 | return products.data as Product[]; 56 | } 57 | -------------------------------------------------------------------------------- /dist/static/index-o8Z_cZLs.js: -------------------------------------------------------------------------------- 1 | import{a as I,r as E,aT as v,aU as G,j as u,ao as R,p as V,ab as g,ae as w,aV as A,aq as L,aH as W,aW as q,o as K,aX as X}from"./sanity-jqEJABLI.js";const _=V(K)` 2 | position: relative; 3 | `;function z(d){const e=I.c(3),{children:a}=d,{collapsed:s}=q();let t;return e[0]!==a||e[1]!==s?(t=u.jsx(_,{hidden:s,height:"fill",overflow:"auto",children:a}),e[0]=a,e[1]=s,e[2]=t):t=e[2],t}function D(d){const e=I.c(11),{actionHandlers:a,index:s,menuItems:t,menuItemGroups:o,title:n}=d,{features:c}=g();if(!(t!=null&&t.length)&&!n)return null;let l;e[0]!==a||e[1]!==o||e[2]!==t?(l=u.jsx(X,{menuItems:t,menuItemGroups:o,actionHandlers:a}),e[0]=a,e[1]=o,e[2]=t,e[3]=l):l=e[3];let r;e[4]!==c.backButton||e[5]!==s?(r=c.backButton&&s>0&&u.jsx(w,{as:L,"data-as":"a",icon:A,mode:"bleed",tooltipProps:{content:"Back"}}),e[4]=c.backButton,e[5]=s,e[6]=r):r=e[6];let i;return e[7]!==l||e[8]!==r||e[9]!==n?(i=u.jsx(W,{actions:l,backButton:r,title:n}),e[7]=l,e[8]=r,e[9]=n,e[10]=i):i=e[10],i}function M(d){const e=I.c(37);let a,s,t,o;e[0]!==d?({index:a,pane:s,paneKey:t,...o}=d,e[0]=d,e[1]=a,e[2]=s,e[3]=t,e[4]=o):(a=e[1],s=e[2],t=e[3],o=e[4]);let n,c,l,r,i;if(e[5]!==s){const{child:S,component:U,menuItems:y,menuItemGroups:T,type:F,...$}=s;c=S,n=U,r=y,l=T,i=$,e[5]=s,e[6]=n,e[7]=c,e[8]=l,e[9]=r,e[10]=i}else n=e[6],c=e[7],l=e[8],r=e[9],i=e[10];const[k,C]=E.useState(null),{title:b}=v(s),B=b===void 0?"":b;let p,m;e[11]!==i||e[12]!==o?({key:m,...p}={...o,...i},e[11]=i,e[12]=o,e[13]=p,e[14]=m):(p=e[13],m=e[14]);const H=k==null?void 0:k.actionHandlers;let x;e[15]!==a||e[16]!==l||e[17]!==r||e[18]!==H||e[19]!==B?(x=u.jsx(D,{actionHandlers:H,index:a,menuItems:r,menuItemGroups:l,title:B}),e[15]=a,e[16]=l,e[17]=r,e[18]=H,e[19]=B,e[20]=x):x=e[20];let f;e[21]!==n||e[22]!==c||e[23]!==p||e[24]!==m||e[25]!==t?(f=G.isValidElementType(n)&&u.jsx(n,{...p,ref:C,child:c,paneKey:t},m),e[21]=n,e[22]=c,e[23]=p,e[24]=m,e[25]=t,e[26]=f):f=e[26];let P;e[27]!==n?(P=E.isValidElement(n)&&n,e[27]=n,e[28]=P):P=e[28];let h;e[29]!==f||e[30]!==P?(h=u.jsxs(z,{children:[f,P]}),e[29]=f,e[30]=P,e[31]=h):h=e[31];let j;return e[32]!==t||e[33]!==o.isSelected||e[34]!==x||e[35]!==h?(j=u.jsxs(R,{id:t,minWidth:320,selected:o.isSelected,children:[x,h]}),e[32]=t,e[33]=o.isSelected,e[34]=x,e[35]=h,e[36]=j):j=e[36],j}export{M as default}; 4 | -------------------------------------------------------------------------------- /dist/static/9958038f.create-schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "fields": [ 4 | { 5 | "name": "code", 6 | "type": "string" 7 | }, 8 | { 9 | "name": "discountPerecentage", 10 | "type": "number", 11 | "title": "Discount Percentage(%)" 12 | }, 13 | { 14 | "name": "expirationDate", 15 | "type": "date" 16 | } 17 | ], 18 | "name": "promotionCode", 19 | "type": "document" 20 | }, 21 | { 22 | "fields": [ 23 | { 24 | "name": "title", 25 | "type": "string" 26 | }, 27 | { 28 | "name": "description", 29 | "type": "text" 30 | }, 31 | { 32 | "to": [ 33 | { 34 | "type": "promotionCode" 35 | } 36 | ], 37 | "name": "code", 38 | "type": "reference" 39 | } 40 | ], 41 | "name": "promotionCampaign", 42 | "type": "document" 43 | }, 44 | { 45 | "fields": [ 46 | { 47 | "name": "title", 48 | "type": "string" 49 | }, 50 | { 51 | "name": "description", 52 | "type": "text" 53 | }, 54 | { 55 | "validation": [ 56 | { 57 | "rules": [ 58 | { 59 | "flag": "custom" 60 | } 61 | ], 62 | "level": "error" 63 | } 64 | ], 65 | "name": "slug", 66 | "type": "slug" 67 | } 68 | ], 69 | "name": "productCategory", 70 | "type": "document" 71 | }, 72 | { 73 | "fields": [ 74 | { 75 | "name": "title", 76 | "type": "string" 77 | }, 78 | { 79 | "name": "description", 80 | "type": "text" 81 | }, 82 | { 83 | "name": "price", 84 | "type": "number" 85 | }, 86 | { 87 | "name": "image", 88 | "type": "image" 89 | }, 90 | { 91 | "to": [ 92 | { 93 | "type": "productCategory" 94 | } 95 | ], 96 | "name": "category", 97 | "type": "reference" 98 | } 99 | ], 100 | "name": "product", 101 | "type": "document" 102 | } 103 | ] -------------------------------------------------------------------------------- /src/components/product/ProductItem.tsx: -------------------------------------------------------------------------------- 1 | import { Product } from '@/sanity.types'; 2 | import { urlFor } from '@/sanity/lib/image'; 3 | import React from 'react' 4 | import Image from 'next/image'; 5 | import Link from 'next/link'; 6 | 7 | type ProductItemProps = { 8 | 9 | product: Product; 10 | } 11 | 12 | const ProductItem = ({product} : ProductItemProps) => { 13 | return ( 14 |
15 |
16 | HOT! 17 | 18 |
19 | 20 |
21 | {product.image && ( 22 | {product.title 28 | )} 29 | 30 |
31 | 32 |
33 | 34 |

{product.title}

35 |
36 |
37 | ${(product.price || 0).toFixed(2)} 38 | ${((product.price || 0) * 5).toFixed(2)} 39 |
40 |
41 | 🔥 {100 + Math.abs(product._id.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0) % 500)}+ sold in the last 24h 42 |
43 | 46 | GRAB IT NOW! 47 |
⚡ Limited time offer!
48 | 49 |
50 | 51 | 52 |
53 | 54 |
55 | ) 56 | } 57 | 58 | export default ProductItem -------------------------------------------------------------------------------- /dist/static/f4be14c6.create-tools.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Structure", 4 | "name": "structure", 5 | "type": "sanity/structure", 6 | "icon": "" 7 | }, 8 | { 9 | "title": "Vision", 10 | "name": "vision", 11 | "type": "sanity/vision", 12 | "icon": "" 13 | }, 14 | { 15 | "title": "Schedules", 16 | "name": "schedules", 17 | "type": "sanity/scheduled-publishing", 18 | "icon": "" 19 | }, 20 | { 21 | "title": "Releases", 22 | "name": "releases", 23 | "type": null, 24 | "icon": "R" 25 | } 26 | ] -------------------------------------------------------------------------------- /src/components/product/AddToCartButton.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { formatPrice } from '@/lib/utils' 3 | import { Product } from '@/sanity.types' 4 | import { Loader2 } from 'lucide-react' 5 | import { resolve } from 'path'; 6 | import React, { useState } from 'react' 7 | 8 | type AddToCartButtonProps = { 9 | product: Product 10 | } 11 | 12 | const AddToCartButton = ({ product }: AddToCartButtonProps) => { 13 | const [isLoading, setLoading] = useState(false); 14 | 15 | const handleAddToCart = async () => { 16 | setLoading(true); 17 | 18 | // Add the item to the cart 19 | await new Promise(resolve => setTimeout(resolve, 1000)); 20 | 21 | 22 | setLoading(false); 23 | 24 | } 25 | 26 | if(!product.price) { 27 | return null; 28 | } 29 | 30 | return ( 31 | 32 | 74 | ) 75 | } 76 | 77 | export default AddToCartButton -------------------------------------------------------------------------------- /src/app/search/page.tsx: -------------------------------------------------------------------------------- 1 | import SalesCampaignBanner from '@/components/layout/SalesCampaignBanner'; 2 | import ProductGrid from '@/components/product/ProductGrid'; 3 | import { getCategoryBySlug, getProductsByCategorySlug, searchProducts } from '@/sanity/lib/client'; 4 | import React from 'react' 5 | 6 | 7 | type SearchPageProps = { 8 | searchParams: Promise<{ query: string }> 9 | 10 | } 11 | const SearchPage = async ({ searchParams } : SearchPageProps ) => { 12 | 13 | const { query } = await searchParams; 14 | 15 | const products = await searchProducts(query); 16 | 17 | return ( 18 |
19 | 20 | 21 |
22 |
23 |

24 | Search Result for &qout;{query}" - UP TO 90% OFF! 🔥 25 |

26 |

27 | ⚡ Flash Sales Ending Soon! ⏰ Limited Time Only 28 |

29 |

30 | Discover amazing deals matching your search 31 | 32 |

33 |
34 | 35 |
36 | 37 |
38 |
39 |
40 |
41 | 🚚 42 | Free Shipping 43 | 44 |
45 |
46 | 47 | Top Rated 48 | 49 |
50 |
51 | 💰 52 | Best Prices 53 | 54 |
55 | 56 |
57 | 58 |
59 | 60 |
61 | 62 |
63 |
64 |

🎉 {products.length} Amazing Deals Available Now!

65 | 66 |
67 | 68 | 69 |
70 |
71 | ) 72 | } 73 | 74 | export default SearchPage -------------------------------------------------------------------------------- /src/app/category/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import SalesCampaignBanner from '@/components/layout/SalesCampaignBanner'; 2 | import ProductGrid from '@/components/product/ProductGrid'; 3 | import { getCategoryBySlug, getProductsByCategorySlug } from '@/sanity/lib/client'; 4 | import React from 'react' 5 | 6 | 7 | type CategoryPageProps = { 8 | params: Promise<{ slug: string }> 9 | 10 | } 11 | const CategoryPage = async ({ params } : CategoryPageProps ) => { 12 | 13 | const { slug } = await params; 14 | 15 | const [category, products] = await Promise.all([ 16 | getCategoryBySlug(slug), 17 | getProductsByCategorySlug(slug) 18 | ]); 19 | return ( 20 |
21 | 22 | 23 |
24 |
25 |

{category.title} - UP TO 90% OFF!🔥

26 |

27 | ⚡ Flash Sales Ending Soon! ⏰ Limited Time Only 28 | 29 |

30 |

31 | {category.description} 32 | 33 |

34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 |
42 | 🚚 43 | Free Shipping 44 | 45 |
46 |
47 | 48 | Top Rated 49 | 50 |
51 |
52 | 💰 53 | Best Prices 54 | 55 |
56 | 57 |
58 | 59 |
60 | 61 |
62 | 63 |
64 |
65 |

🎉 {products.length} Amazing Deals Available Now!

66 | 67 |
68 | 69 | 70 |
71 |
72 | ) 73 | } 74 | 75 | export default CategoryPage -------------------------------------------------------------------------------- /dist/static/index2-D9Rare3g.js: -------------------------------------------------------------------------------- 1 | import{a as L,aT as k,j as x,ao as A,p as S,aY as $,ab as H,aW as W,ae as C,aV as D,aq as G,aH as E,ap as F,aZ as R,a_ as X,aI as q,o as K,a$ as M,aX as U}from"./sanity-jqEJABLI.js";const V=S.hr` 2 | background-color: var(--card-border-color); 3 | height: 1px; 4 | margin: 0; 5 | border: none; 6 | `;function Y(y){const t=L.c(20),{childItemId:o,items:e,isActive:d,layout:u,showIcons:i,title:r}=y,{collapsed:j}=F();let c;t[0]!==e?(c=e==null?void 0:e.filter(Z),t[0]=e,t[1]=c):c=t[1];const n=R(c);let s;t[2]!==e?(s=a=>{var b;return((b=e==null?void 0:e.find((B,g)=>g===a))==null?void 0:b.type)==="divider"},t[2]=e,t[3]=s):s=t[3];const l=s;let v;t[4]!==i?(v=a=>{var B;const b=(B=a.displayOptions)==null?void 0:B.showIcon;return typeof b<"u"?b!==!1:i!==!1},t[4]=i,t[5]=v):v=t[5];const I=v;let p;t[6]!==o||t[7]!==n||t[8]!==d||t[9]!==u||t[10]!==I?(p=(a,b)=>{const{virtualIndex:B}=b;if(a.type==="divider")return x.jsx(K,{marginBottom:1,children:x.jsx(V,{})},`divider-${B}`);const g=!d&&o===a.id,w=d&&o===a.id,T=a._id&&a.schemaType?{_id:a._id,_type:a.schemaType.name,title:a.title}:void 0;return x.jsx(M,{icon:I(a)?a.icon:!1,id:a.id,layout:u,marginBottom:1,pressed:g,schemaType:a.schemaType,selected:w,title:n(a).title,value:T},a.id)},t[6]=o,t[7]=n,t[8]=d,t[9]=u,t[10]=I,t[11]=p):p=t[11];const m=p,f=j?"hidden":"auto";let h;t[12]!==l||t[13]!==e||t[14]!==m||t[15]!==r?(h=e&&e.length>0&&x.jsx(X,{activeItemDataAttr:"data-hovered",ariaLabel:r,canReceiveFocus:!0,getItemDisabled:l,itemHeight:51,items:e,onlyShowSelectionWhenActive:!0,paddingBottom:1,paddingX:3,renderItem:m,wrapAround:!1}),t[12]=l,t[13]=e,t[14]=m,t[15]=r,t[16]=h):h=t[16];let P;return t[17]!==f||t[18]!==h?(P=x.jsx(q,{overflow:f,children:h}),t[17]=f,t[18]=h,t[19]=P):P=t[19],P}function Z(y){return y.type!=="divider"}const z=y=>{const t=L.c(11),{index:o,menuItems:e,menuItemGroups:d,title:u}=y,{features:i}=H(),{collapsed:r,isLast:j}=W(),c=j&&!r?-1:0;let n;t[0]!==d||t[1]!==e?(n=x.jsx(U,{menuItems:e,menuItemGroups:d}),t[0]=d,t[1]=e,t[2]=n):n=t[2];let s;t[3]!==i.backButton||t[4]!==o?(s=i.backButton&&o>0&&x.jsx(C,{as:G,"data-as":"a",icon:D,mode:"bleed",tooltipProps:{content:"Back"}}),t[3]=i.backButton,t[4]=o,t[5]=s):s=t[5];let l;return t[6]!==n||t[7]!==s||t[8]!==c||t[9]!==u?(l=x.jsx(E,{actions:n,backButton:s,tabIndex:c,title:u}),t[6]=n,t[7]=s,t[8]=c,t[9]=u,t[10]=l):l=t[10],l};function N(y){const t=L.c(21),{childItemId:o,index:e,isActive:d,isSelected:u,pane:i,paneKey:r}=y,{defaultLayout:j,displayOptions:c,items:n,menuItems:s,menuItemGroups:l}=i,v=(c==null?void 0:c.showIcons)!==!1,{title:I}=k(i);let p;t[0]!==i.source?(p=$,t[0]=i.source,t[1]=p):p=t[1];let m;t[2]!==e||t[3]!==l||t[4]!==s||t[5]!==I?(m=x.jsx(z,{index:e,menuItems:s,menuItemGroups:l,title:I}),t[2]=e,t[3]=l,t[4]=s,t[5]=I,t[6]=m):m=t[6];let f;t[7]!==o||t[8]!==j||t[9]!==d||t[10]!==n||t[11]!==r||t[12]!==v||t[13]!==I?(f=x.jsx(Y,{childItemId:o,isActive:d,items:n,layout:j,showIcons:v,title:I},r),t[7]=o,t[8]=j,t[9]=d,t[10]=n,t[11]=r,t[12]=v,t[13]=I,t[14]=f):f=t[14];let h;return t[15]!==u||t[16]!==r||t[17]!==p||t[18]!==m||t[19]!==f?(h=x.jsxs(A,{currentMaxWidth:350,"data-testid":"structure-tool-list-pane","data-ui":"ListPane",id:r,maxWidth:640,minWidth:320,selected:u,children:[p,m,f]}),t[15]=u,t[16]=r,t[17]=p,t[18]=m,t[19]=f,t[20]=h):h=t[20],h}export{N as default}; 7 | -------------------------------------------------------------------------------- /dist/static/resources-yGHF8Toy.js: -------------------------------------------------------------------------------- 1 | import{d as e}from"./sanity-jqEJABLI.js";const o=e("comments",{"close-pane-button-text":"Close comments","close-pane-button-text-aria-label":"Close comments","compose.add-comment-input-placeholder":"Add comment to {{field}}","compose.add-comment-input-placeholder-upsell":"Upgrade to add comment","compose.create-comment-placeholder":"Create a new comment","compose.mention-user-aria-label":"Mention user","compose.mention-user-tooltip":"Mention user","compose.reply-placeholder":"Reply","compose.reply-placeholder-upsell":"Upgrade to reply","compose.send-comment-aria-label":"Send comment","compose.send-comment-tooltip":"Send comment","copy-link-error-message":"Unable to copy link to clipboard","delete-comment.body":"Once deleted, a comment cannot be recovered.","delete-comment.confirm":"Delete comment","delete-comment.title":"Delete this comment?","delete-dialog.error":"An error occurred while deleting the comment. Please try again.","delete-thread.body":"This comment and its replies will be deleted, and once deleted cannot be recovered.","delete-thread.confirm":"Delete thread","delete-thread.title":"Delete this comment thread?","discard.button-confirm":"Discard","discard.header":"Discard comment?","discard.text":"Do you want to discard the comment?","feature-feedback.link":"Share your feedback","feature-feedback.title":"Help improve ","feature-name":"Comments","field-button.aria-label-add":"Add comment","field-button.aria-label-open":"Open comments","field-button.content_one":"View comment","field-button.content_other":"View comments","field-button.title":"Add comment","inline-add-comment-button.disabled-overlap-title":"Comments cannot overlap","inline-add-comment-button.title":"Add comment","list-item.breadcrumb-button-go-to-field-aria-label":"Go to {{field}} field","list-item.context-menu-add-reaction":"Add reaction","list-item.context-menu-add-reaction-aria-label":"Add reaction","list-item.context-menu-add-reaction-upsell":"Upgrade to add reaction","list-item.copy-link":"Copy link to comment","list-item.delete-comment":"Delete comment","list-item.edit-comment":"Edit comment","list-item.edit-comment-upsell":"Upgrade to edit comment","list-item.go-to-field-button.aria-label":"Go to field","list-item.layout-context":"on {{title}}","list-item.layout-edited":"edited","list-item.layout-failed-sent":"Failed to send.","list-item.layout-posting":"Posting...","list-item.layout-retry":"Retry","list-item.missing-referenced-value-tooltip-content":"The commented text has been deleted","list-item.open-menu-aria-label":"Open comment actions menu","list-item.re-open-resolved":"Re-open","list-item.re-open-resolved-aria-label":"Re-open","list-item.resolved-tooltip-aria-label":"Mark comment as resolved","list-item.resolved-tooltip-content":"Mark as resolved","list-status.empty-state-open-text":"Open comments on this document will be shown here.","list-status.empty-state-open-title":"No open comments yet","list-status.empty-state-resolved-text":"Resolved comments on this document will be shown here.","list-status.empty-state-resolved-title":"No resolved comments yet","list-status.error":"Something went wrong","list-status.loading":"Loading comments","mentions.no-users-found":"No users found","mentions.unauthorized-user":"Unauthorized","mentions.user-list-aria-label":"List of users to mention","onboarding.body":"You can add comments to any field in a document. They'll show up here, grouped by field.","onboarding.dismiss":"Got it","onboarding.header":"Document fields now have comments","reactions.add-reaction-tooltip":"Add reaction","reactions.react-with-aria-label":"React with {{reactionName}}","reactions.user-list.unknown-user-fallback-name":"Unknown user","reactions.user-list.you":"you","reactions.user-list.you_leading":"You","reactions.users-reacted-with-reaction":" reacted with ","status-filter.status-open":"Open","status-filter.status-open-full":"Open comments","status-filter.status-resolved":"Resolved","status-filter.status-resolved-full":"Resolved comments","status-filter.status-resolved-full-upsell":"Upgrade to see resolved comments"});export{o as default}; 2 | -------------------------------------------------------------------------------- /dist/static/resources3-L9TjxJAN.js: -------------------------------------------------------------------------------- 1 | import{d as t}from"./sanity-jqEJABLI.js";const s=t("tasks",{"actions.create.text":"Create new task","actions.open.text":"Tasks","buttons.create.text":"Create Task","buttons.discard.text":"Discard","buttons.draft.text":"Draft","buttons.new.text":"New task","buttons.new.upsell-tooltip":"Upgrade to create tasks","buttons.next.tooltip":"Go to next task","buttons.previous.tooltip":"Go to previous task","dialog.remove-task.body":"Once deleted, a task cannot be recovered.","dialog.remove-task.buttons.cancel.text":"Cancel","dialog.remove-task.buttons.confirm.text":"Delete","dialog.remove-task.title":"Delete this task?","document.footer.open-tasks.placeholder_one":"Open task","document.footer.open-tasks.placeholder_other":"Open tasks","document.footer.open-tasks.text_one":"{{count}} open task","document.footer.open-tasks.text_other":"{{count}} open tasks","empty-state.list.assigned.heading":"You haven't been assigned any tasks","empty-state.list.assigned.text":"Once you're assigned tasks they'll show up here","empty-state.list.create-new":"Create new task","empty-state.list.document.heading":"This document doesn't have any tasks yet","empty-state.list.document.text":"Once a document has connected tasks, they will be shown here.","empty-state.list.no-active-document.heading":"Open a document to see its task","empty-state.list.no-active-document.text":"Tasks on your active document will be shown here.","empty-state.list.subscribed.heading":"You haven't subscribed to any tasks","empty-state.list.subscribed.text":"When you create, modify, or comment on a task you will be subscribed automatically","empty-state.status.list.closed.assigned.heading":"No completed tasks","empty-state.status.list.closed.assigned.text":"Your tasks marked done will show up here","empty-state.status.list.closed.document.heading":"No completed tasks","empty-state.status.list.closed.subscribed.heading":"No completed tasks","empty-state.status.list.closed.subscribed.text":"Tasks you subscribe to marked done will show up here","empty-state.status.list.open.assigned.heading":"You're all caught up","empty-state.status.list.open.assigned.text":"New tasks assigned to you will show up here","empty-state.status.list.open.document.heading":"No tasks on this document","empty-state.status.list.open.subscribed.heading":"No subscribed tasks","empty-state.status.list.open.subscribed.text":"Tasks you subscribe to will show up here","form.input.assignee.no-user-assigned.text":"Unassigned","form.input.assignee.no-user-assigned.tooltip":"Set assignee","form.input.assignee.search.no-users.text":"No users found","form.input.assignee.search.placeholder":"Select assignee","form.input.assignee.unauthorized.text":"Unauthorized","form.input.assignee.user-assigned.tooltip":"Change assignee","form.input.assignee.user-not-found.text":"User not found","form.input.create-more.text":"Create more","form.input.date.buttons.empty.tooltip":"Set due date","form.input.date.buttons.remove.text":"Remove","form.input.date.buttons.tooltip":"Change due date","form.input.description.placeholder":"Add description","form.input.status.button.tooltip":"Change status","form.input.target.buttons.remove.text":"Remove target content","form.input.target.error.schema-not-found":"Schema not found","form.input.target.search.placeholder":"Select target document","form.input.title.placeholder":"Task title","form.status.error.title-required":"Title is required","form.status.success":"Task created","list.empty.text":"No tasks","list.feedback.text":"Help us improve, share feedback on Tasks ","menuitem.copylink.text":"Copy link to task","menuitem.delete.text":"Delete task","menuitem.duplicate.text":"Duplicate task","menuitem.duplicate.upsell-tooltip":"Upgrade to duplicate tasks","panel.activity.created-fragment":"created this task","panel.activity.title":"Activity","panel.activity.unknown-user":"Unknown user","panel.close.tooltip":"Close sidebar","panel.comment.placeholder":"Add a comment...","panel.comment.placeholder.upsell":"Upgrade to comment on tasks","panel.create.title":"Create","panel.drafts.title":"Drafts","panel.navigation.tooltip":"Open tasks","panel.title":"Tasks","tab.assigned.label":"Assigned","tab.document.label":"Active Document","tab.subscribed.label":"Subscribed","toolbar.tooltip":"Tasks"});export{s as default}; 2 | -------------------------------------------------------------------------------- /src/components/auth/SignIn.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from 'react'; 3 | import { useRouter } from "next/navigation"; 4 | import { useActionState } from 'react'; 5 | import { Loader2 } from 'lucide-react'; 6 | 7 | const initialState = { 8 | message: '', 9 | }; 10 | 11 | type SignInProps = { 12 | action: (prevState: any, formData: FormData) => Promise<{ success?: boolean; message?: string }>; 13 | }; 14 | 15 | const SignIn = ({ action }: SignInProps) => { 16 | const router = useRouter(); 17 | const [state, formAction, isPending] = useActionState(action, initialState); 18 | 19 | // Redirect user to home page when sign-up is successful 20 | React.useEffect(() => { 21 | if (state?.success) { 22 | router.push("/"); 23 | } 24 | }, [state, router]); 25 | 26 | return ( 27 |
28 |

29 | Welcome Back! 30 |

31 |

32 | 🔥 MEMBER EXCLUSIVE 🔥 33 |

34 |

35 | Sign in to access your exclusive member deals. 36 |

37 | 38 |
39 | {/* Email */} 40 |
41 | 44 | 53 |
54 | 55 | {/* Password */} 56 |
57 | 60 | 69 |
70 | 71 | {/* Copywriting */} 72 |
73 |

⚡ Members save an extra 15% on all orders!

74 |

🛍️ Plus get free shipping on orders over $15.00

75 |
76 | 77 | {/* Submit button */} 78 | 91 | 92 | {/* Error Message */} 93 | {state?.message && state.message.length > 0 && ( 94 |

95 | {state.message} 96 | 97 |

98 | )} 99 | 100 |
101 |
102 | ); 103 | }; 104 | 105 | export default SignIn; 106 | -------------------------------------------------------------------------------- /src/components/auth/SignUp.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from 'react'; 3 | import { useRouter } from "next/navigation"; 4 | import { useActionState } from 'react'; 5 | import { Loader2 } from 'lucide-react'; 6 | 7 | const initialState = { 8 | message: '', 9 | }; 10 | 11 | type SignUpProps = { 12 | action: (prevState: any, formData: FormData) => Promise<{ success?: boolean; message?: string }>; 13 | }; 14 | 15 | const SignUp = ({ action }: SignUpProps) => { 16 | const router = useRouter(); 17 | const [state, formAction, isPending] = useActionState(action, initialState); 18 | 19 | // Redirect user to home page when sign-up is successful 20 | React.useEffect(() => { 21 | if (state?.success) { 22 | router.push("/"); 23 | } 24 | }, [state, router]); 25 | 26 | return ( 27 |
28 |

29 | Join the DEAL Revolution! 30 |

31 |

32 | 🔥 LIMITED TIME OFFER 🔥 33 |

34 |

35 | Sign up now and get 90% OFF your first order! 36 |

37 | 38 |
39 | {/* Email */} 40 |
41 | 44 | 53 |
54 | 55 | {/* Password */} 56 |
57 | 60 | 69 |
70 | 71 | {/* Copywriting */} 72 |
73 |

⚡ Only 127 welcome bonus packages remaining

74 |

🕰️ Offer expires in: 13:45

75 |
76 | 77 | {/* Submit button */} 78 | 91 | 92 | {/* Error Message */} 93 | {state?.message && state.message.length > 0 && ( 94 |

95 | {state.message} 96 | 97 |

98 | )} 99 | 100 | 101 |
102 |
103 | ); 104 | }; 105 | 106 | export default SignUp; 107 | -------------------------------------------------------------------------------- /src/stores/cart-store.ts: -------------------------------------------------------------------------------- 1 | import { getOrCreateCart, syncCartWithUser, updateCartItem } from '@/actions/cart-action'; 2 | import { create } from 'zustand'; 3 | import { persist } from 'zustand/middleware'; 4 | export type CartItem = { 5 | id: string; 6 | title: string; 7 | price: number; 8 | quantity: number; 9 | image: string; 10 | } 11 | 12 | type CartStore = { 13 | items: CartItem[]; 14 | isLoaded: boolean; 15 | cartId: string | null; 16 | setStore: (store: Partial) => void; 17 | addItem: (item: CartItem) => Promise; 18 | removeItem: (id: string) => Promise; 19 | updateQuantity: (id: string, quantity: number) => Promise; 20 | clearCart: () => void; 21 | open: () => void; 22 | close: () => void; 23 | setLoaded: (loaded: boolean) => void; 24 | syncWithUser: () => Promise; 25 | getTotalItems: () => number; 26 | getTotalPrice: () => number; 27 | 28 | } 29 | 30 | export const useCartStore = create() ( 31 | persist( 32 | (set, get) => ({ 33 | items: [], 34 | isOpen: false, 35 | isLoaded: false, 36 | cartId: null, 37 | 38 | setStore: (strore) => set(strore), 39 | 40 | addItem: async (item) => { 41 | const { cartId } = get(); 42 | if (!cartId) { 43 | return; 44 | } 45 | 46 | const updatedCart = await updateCartItem(cartId, item.id, { 47 | title: item.title, 48 | price: item.price, 49 | image: item.image, 50 | quantity: item.quantity, 51 | }); 52 | 53 | set((state) => { 54 | const existingItem = state.items.find((i) => i.id === item.id); 55 | if(existingItem) { 56 | return { 57 | ...state, 58 | cartId: updatedCart.id, 59 | items: state.items.map((i) => i.id === item.id ? { ...i, quantity: i.quantity + item.quantity} : i) 60 | } 61 | } 62 | return { 63 | ...state, 64 | cartId: updatedCart.id, 65 | items: [...state.items, { ...item }], 66 | }; 67 | }); 68 | }, 69 | 70 | removeItem: async (id) => { 71 | const { cartId } = get(); 72 | if (!cartId) { 73 | return; 74 | } 75 | 76 | const updatedCart = await updateCartItem(cartId, id, { 77 | quantity: 0, 78 | }); 79 | 80 | set((state) => { 81 | return { 82 | ...state, 83 | cartId: updatedCart.id, 84 | items: state.items.filter((item) => item.id !== id) 85 | }; 86 | }); 87 | }, 88 | 89 | updateQuantity: async (id, quantity) => { 90 | const { cartId } = get(); 91 | if (!cartId) { 92 | return; 93 | } 94 | 95 | const updatedCart = await updateCartItem(cartId, id, { 96 | quantity: quantity, 97 | }); 98 | 99 | set((state) => ({ 100 | ...state, 101 | cartId: updatedCart.id, 102 | items: state.items.map((item) => item.id === id ? { ...item, quantity } : item) 103 | })); 104 | }, 105 | 106 | syncWithUser: async () => { 107 | const { cartId } = get(); 108 | if(!cartId) { 109 | const cart = await getOrCreateCart(); 110 | set((state) => ({ 111 | ...state, 112 | cartId: cart.id, 113 | items: cart.items 114 | })); 115 | } 116 | const syncedCart = await syncCartWithUser(cartId); 117 | if(syncedCart) { 118 | set((state) => ({ 119 | ...state, 120 | cartId: syncedCart.id, 121 | items: syncedCart.items, 122 | })) 123 | } 124 | }, 125 | 126 | clearCart: () => { 127 | set((state) => ({ ...state, items: [] })); 128 | }, 129 | 130 | open: () => { 131 | set((state) => ({ ...state, isOpen: true })); 132 | }, 133 | close: () => { 134 | set((state) => ({ ...state, isOpen: false })); 135 | }, 136 | 137 | setLoaded: (loaded) => { 138 | set((state) => ({ ...state, isLoaded: loaded })); 139 | }, 140 | 141 | getTotalItems: () => { 142 | const { items } = get(); 143 | return items.reduce((total, item) => total + item.quantity, 0); 144 | }, 145 | getTotalPrice: () => { 146 | const { items } = get(); 147 | return items.reduce((total, item) => total + item.price * item.quantity, 0); 148 | }, 149 | }), 150 | 151 | 152 | 153 | 154 | 155 | { 156 | name: 'cart-storage', 157 | skipHydration: true, 158 | 159 | } 160 | ) 161 | ); -------------------------------------------------------------------------------- /sanity.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * --------------------------------------------------------------------------------- 3 | * This file has been generated by Sanity TypeGen. 4 | * Command: `sanity typegen generate` 5 | * 6 | * Any modifications made directly to this file will be overwritten the next time 7 | * the TypeScript definitions are generated. Please make changes to the Sanity 8 | * schema definitions and/or GROQ queries if you need to update these types. 9 | * 10 | * For more information on how to use Sanity TypeGen, visit the official documentation: 11 | * https://www.sanity.io/docs/sanity-typegen 12 | * --------------------------------------------------------------------------------- 13 | */ 14 | 15 | // Source: schema.json 16 | export type SanityImagePaletteSwatch = { 17 | _type: "sanity.imagePaletteSwatch"; 18 | background?: string; 19 | foreground?: string; 20 | population?: number; 21 | title?: string; 22 | }; 23 | 24 | export type SanityImagePalette = { 25 | _type: "sanity.imagePalette"; 26 | darkMuted?: SanityImagePaletteSwatch; 27 | lightVibrant?: SanityImagePaletteSwatch; 28 | darkVibrant?: SanityImagePaletteSwatch; 29 | vibrant?: SanityImagePaletteSwatch; 30 | dominant?: SanityImagePaletteSwatch; 31 | lightMuted?: SanityImagePaletteSwatch; 32 | muted?: SanityImagePaletteSwatch; 33 | }; 34 | 35 | export type SanityImageDimensions = { 36 | _type: "sanity.imageDimensions"; 37 | height?: number; 38 | width?: number; 39 | aspectRatio?: number; 40 | }; 41 | 42 | export type SanityFileAsset = { 43 | _id: string; 44 | _type: "sanity.fileAsset"; 45 | _createdAt: string; 46 | _updatedAt: string; 47 | _rev: string; 48 | originalFilename?: string; 49 | label?: string; 50 | title?: string; 51 | description?: string; 52 | altText?: string; 53 | sha1hash?: string; 54 | extension?: string; 55 | mimeType?: string; 56 | size?: number; 57 | assetId?: string; 58 | uploadId?: string; 59 | path?: string; 60 | url?: string; 61 | source?: SanityAssetSourceData; 62 | }; 63 | 64 | export type Geopoint = { 65 | _type: "geopoint"; 66 | lat?: number; 67 | lng?: number; 68 | alt?: number; 69 | }; 70 | 71 | export type Product = { 72 | _id: string; 73 | _type: "product"; 74 | _createdAt: string; 75 | _updatedAt: string; 76 | _rev: string; 77 | title?: string; 78 | description?: string; 79 | price?: number; 80 | image?: { 81 | asset?: { 82 | _ref: string; 83 | _type: "reference"; 84 | _weak?: boolean; 85 | [internalGroqTypeReferenceTo]?: "sanity.imageAsset"; 86 | }; 87 | hotspot?: SanityImageHotspot; 88 | crop?: SanityImageCrop; 89 | _type: "image"; 90 | }; 91 | category?: { 92 | _ref: string; 93 | _type: "reference"; 94 | _weak?: boolean; 95 | [internalGroqTypeReferenceTo]?: "productCategory"; 96 | }; 97 | }; 98 | 99 | export type SanityImageCrop = { 100 | _type: "sanity.imageCrop"; 101 | top?: number; 102 | bottom?: number; 103 | left?: number; 104 | right?: number; 105 | }; 106 | 107 | export type SanityImageHotspot = { 108 | _type: "sanity.imageHotspot"; 109 | x?: number; 110 | y?: number; 111 | height?: number; 112 | width?: number; 113 | }; 114 | 115 | export type SanityImageAsset = { 116 | _id: string; 117 | _type: "sanity.imageAsset"; 118 | _createdAt: string; 119 | _updatedAt: string; 120 | _rev: string; 121 | originalFilename?: string; 122 | label?: string; 123 | title?: string; 124 | description?: string; 125 | altText?: string; 126 | sha1hash?: string; 127 | extension?: string; 128 | mimeType?: string; 129 | size?: number; 130 | assetId?: string; 131 | uploadId?: string; 132 | path?: string; 133 | url?: string; 134 | metadata?: SanityImageMetadata; 135 | source?: SanityAssetSourceData; 136 | }; 137 | 138 | export type SanityAssetSourceData = { 139 | _type: "sanity.assetSourceData"; 140 | name?: string; 141 | id?: string; 142 | url?: string; 143 | }; 144 | 145 | export type SanityImageMetadata = { 146 | _type: "sanity.imageMetadata"; 147 | location?: Geopoint; 148 | dimensions?: SanityImageDimensions; 149 | palette?: SanityImagePalette; 150 | lqip?: string; 151 | blurHash?: string; 152 | hasAlpha?: boolean; 153 | isOpaque?: boolean; 154 | }; 155 | 156 | export type ProductCategory = { 157 | _id: string; 158 | _type: "productCategory"; 159 | _createdAt: string; 160 | _updatedAt: string; 161 | _rev: string; 162 | title?: string; 163 | description?: string; 164 | slug?: Slug; 165 | }; 166 | 167 | export type Slug = { 168 | _type: "slug"; 169 | current?: string; 170 | source?: string; 171 | }; 172 | 173 | export type PromotionCampaign = { 174 | _id: string; 175 | _type: "promotionCampaign"; 176 | _createdAt: string; 177 | _updatedAt: string; 178 | _rev: string; 179 | title?: string; 180 | description?: string; 181 | code?: { 182 | _ref: string; 183 | _type: "reference"; 184 | _weak?: boolean; 185 | [internalGroqTypeReferenceTo]?: "promotionCode"; 186 | }; 187 | }; 188 | 189 | export type PromotionCode = { 190 | _id: string; 191 | _type: "promotionCode"; 192 | _createdAt: string; 193 | _updatedAt: string; 194 | _rev: string; 195 | code?: string; 196 | discountPerecentage?: number; 197 | expirationDate?: string; 198 | }; 199 | 200 | export type AllSanitySchemaTypes = SanityImagePaletteSwatch | SanityImagePalette | SanityImageDimensions | SanityFileAsset | Geopoint | Product | SanityImageCrop | SanityImageHotspot | SanityImageAsset | SanityAssetSourceData | SanityImageMetadata | ProductCategory | Slug | PromotionCampaign | PromotionCode; 201 | export declare const internalGroqTypeReferenceTo: unique symbol; 202 | -------------------------------------------------------------------------------- /src/components/layout/Header.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { User } from '@prisma/client'; 3 | import React, { useEffect, useState } from 'react' 4 | import Link from 'next/link'; 5 | import { text } from 'stream/consumers'; 6 | import { logoutUser } from '@/actions/auth'; 7 | import { useRouter } from 'next/navigation'; 8 | import HeaderSearchBar from './HeaderSearchBar'; 9 | 10 | const AnnouncementBar = () => { 11 | return ( 12 | 13 |
14 |
15 | 16 | FREE SHIPPING ON ORDERS OVER $15.00 ★ FREE RETURNS 17 | 18 |
19 | 20 |
21 | ) 22 | } 23 | 24 | type HeaderProps = { 25 | user: Omit | null; 26 | categorySelector: React.ReactNode 27 | 28 | } 29 | const Header = ({ user, categorySelector } : HeaderProps) => { 30 | const router = useRouter(); 31 | const [isOpen, setIsOpen] = useState(true); 32 | const [prevScrollY, setPrevScrollY] = useState(0); 33 | 34 | useEffect(() => { 35 | const handleScroll = () => { 36 | const currentScrollY = window.scrollY; 37 | const scrolledUp = currentScrollY < prevScrollY; 38 | 39 | if (scrolledUp) { 40 | setIsOpen(true); 41 | } else if (currentScrollY > 100){ 42 | setIsOpen(false); 43 | } 44 | setPrevScrollY(currentScrollY); 45 | } 46 | setPrevScrollY(window.scrollY); 47 | 48 | window.addEventListener('scroll', handleScroll); 49 | 50 | return () => {window.removeEventListener('scroll', handleScroll); 51 | } 52 | }, [prevScrollY]) 53 | return ( 54 |
55 |
57 | 58 | 59 |
60 |
61 |
62 | 67 | 68 | 73 | 74 |
75 | 76 | 77 | 78 | DEAL 79 | 80 | 81 | 82 | 83 |
84 | 85 | 86 | { user ? ( 87 |
88 | {user.email} 89 | { 92 | e.preventDefault(); 93 | await logoutUser(); 94 | router.refresh(); 95 | }}> 96 | Sign Out 97 | 98 |
99 | ): ( 100 | 101 | Sign In 102 | Sign Up 103 | 104 | 105 | )} 106 | 107 | 108 | 109 | 117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 | ) 125 | } 126 | 127 | export default Header -------------------------------------------------------------------------------- /src/actions/auth.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from "@oslojs/encoding"; 3 | import { sha256 } from "@oslojs/crypto/sha2"; 4 | import type { User, Session } from "@prisma/client"; 5 | import { cookies } from "next/headers"; 6 | import { cache } from "react"; 7 | 8 | import { PrismaClient } from '@prisma/client'; 9 | 10 | const prisma = new PrismaClient(); 11 | 12 | export async function generateSessionToken(): Promise { 13 | const bytes = new Uint8Array(20); 14 | crypto.getRandomValues(bytes); 15 | const token = encodeBase32LowerCaseNoPadding(bytes); 16 | return token; 17 | } 18 | 19 | export async function createSession(token: string, userId: number): Promise { 20 | const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); 21 | const session: Session = { 22 | id: sessionId, 23 | userId, 24 | expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30) 25 | }; 26 | await prisma.session.create({ 27 | data: session 28 | }); 29 | return session; 30 | } 31 | 32 | export async function validateSessionToken(token: string): Promise { 33 | const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); 34 | const result = await prisma.session.findUnique({ 35 | where: { 36 | id: sessionId 37 | }, 38 | include: { 39 | user: true 40 | } 41 | }); 42 | if (result === null) { 43 | return { session: null, user: null }; 44 | } 45 | const { user, ...session } = result; 46 | if (Date.now() >= session.expiresAt.getTime()) { 47 | await prisma.session.delete({ where: { id: sessionId } }); 48 | return { session: null, user: null }; 49 | } 50 | if (Date.now() >= session.expiresAt.getTime() - 1000 * 60 * 60 * 24 * 15) { 51 | session.expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30); 52 | await prisma.session.update({ 53 | where: { 54 | id: session.id 55 | }, 56 | data: { 57 | expiresAt: session.expiresAt 58 | } 59 | }); 60 | } 61 | 62 | const safeUser = { 63 | ...user, 64 | passwordHash: undefined, 65 | } 66 | 67 | return { session, user: safeUser }; 68 | } 69 | 70 | export async function invalidateSession(sessionId: string): Promise { 71 | await prisma.session.delete({ where: { id: sessionId } }); 72 | } 73 | 74 | export type SessionValidationResult = 75 | | { session: Session; user: Omit } 76 | | { session: null; user: null }; 77 | 78 | 79 | 80 | 81 | 82 | /* Cookies */ 83 | export async function setSessionTokenCookie(token: string, expiresAt: Date): Promise { 84 | const cookieStore = await cookies(); 85 | cookieStore.set("session", token, { 86 | httpOnly: true, 87 | sameSite: "lax", 88 | secure: process.env.NODE_ENV === "production", 89 | expires: expiresAt, 90 | path: "/" 91 | }); 92 | } 93 | 94 | export async function deleteSessionTokenCookie(): Promise { 95 | const cookieStore = await cookies(); 96 | cookieStore.set("session", "", { 97 | httpOnly: true, 98 | sameSite: "lax", 99 | secure: process.env.NODE_ENV === "production", 100 | maxAge: 0, 101 | path: "/" 102 | }); 103 | } 104 | 105 | export const getCurrentSession = cache(async (): Promise => { 106 | const cookieStore = await cookies(); 107 | const token = cookieStore.get("session")?.value ?? null; 108 | if (token === null) { 109 | return { session: null, user: null }; 110 | } 111 | const result = await validateSessionToken(token); 112 | return result; 113 | }); 114 | 115 | 116 | /* User register, login, logout */ 117 | export const hashPassword = async (password: string) => { 118 | return encodeHexLowerCase(sha256(new TextEncoder().encode(password))); 119 | } 120 | 121 | export const verifyPassword = async (password: string, hash: string) => { 122 | const passwordHash = await hashPassword(password); 123 | return passwordHash === hash; 124 | } 125 | 126 | export const registerUser = async (email: string, password: string) => { 127 | const passwordHash = await hashPassword(password); 128 | 129 | try { 130 | console.log("🔍 Attempting to create user:", email); 131 | 132 | const user = await prisma.user.create({ 133 | data: { 134 | email, 135 | passwordHash, 136 | } 137 | }); 138 | 139 | console.log("✅ User created successfully:", user); 140 | 141 | const safeUser = { 142 | ...user, 143 | passwordHash: undefined, 144 | }; 145 | 146 | return { 147 | user: safeUser, 148 | error: null, 149 | }; 150 | } catch (e: any) { 151 | console.error("❌ Prisma Error:", e); 152 | 153 | return { 154 | user: null, 155 | error: e.message || "Failed to register user", // Show the real error 156 | }; 157 | } 158 | }; 159 | 160 | export const loginUser = async (email: string, password: string) => { 161 | const user = await prisma.user.findUnique({ 162 | where: { 163 | email: email, 164 | } 165 | }); 166 | 167 | if(!user) { 168 | return { 169 | user: null, 170 | error: "User not found", 171 | } 172 | } 173 | 174 | const passwordValid = await verifyPassword(password, user.passwordHash); 175 | if(!passwordValid) { 176 | return { 177 | user: null, 178 | error: "Invalid password", 179 | } 180 | } 181 | 182 | const token = await generateSessionToken(); 183 | const session = await createSession(token, user.id); 184 | await setSessionTokenCookie(token, session.expiresAt); 185 | 186 | const safeUser = { 187 | ...user, 188 | passwordHash: undefined, 189 | }; 190 | 191 | return { 192 | user: safeUser, 193 | error: null, 194 | } 195 | } 196 | 197 | export const logoutUser = async () => { 198 | const session = await getCurrentSession(); 199 | if(session.session?.id) { 200 | await invalidateSession(session.session.id); 201 | } 202 | await deleteSessionTokenCookie(); 203 | } -------------------------------------------------------------------------------- /dist/static/stegaEncodeSourceMap-B9OfWko4.js: -------------------------------------------------------------------------------- 1 | import{c as H}from"./sanity-jqEJABLI.js";const D=/_key\s*==\s*['"](.*)['"]/;function K(t){return typeof t=="string"?D.test(t.trim()):typeof t=="object"&&"_key"in t}function L(t){if(!Array.isArray(t))throw new Error("Path is not an array");return t.reduce((e,r,i)=>{const n=typeof r;if(n==="number")return`${e}[${r}]`;if(n==="string")return`${e}${i===0?"":"."}${r}`;if(K(r)&&r._key)return`${e}[_key=="${r._key}"]`;if(Array.isArray(r)){const[s,o]=r;return`${e}[${s}:${o}]`}throw new Error(`Unsupported path segment \`${JSON.stringify(r)}\``)},"")}const I={"\f":"\\f","\n":"\\n","\r":"\\r"," ":"\\t","'":"\\'","\\":"\\\\"},A={"\\f":"\f","\\n":` 2 | `,"\\r":"\r","\\t":" ","\\'":"'","\\\\":"\\"};function X(t){return`$${t.map(e=>typeof e=="string"?`['${e.replace(/[\f\n\r\t'\\]/g,r=>I[r])}']`:typeof e=="number"?`[${e}]`:e._key!==""?`[?(@._key=='${e._key.replace(/['\\]/g,r=>I[r])}')]`:`[${e._index}]`).join("")}`}function j(t){const e=[],r=/\['(.*?)'\]|\[(\d+)\]|\[\?\(@\._key=='(.*?)'\)\]/g;let i;for(;(i=r.exec(t))!==null;){if(i[1]!==void 0){const n=i[1].replace(/\\(\\|f|n|r|t|')/g,s=>A[s]);e.push(n);continue}if(i[2]!==void 0){e.push(parseInt(i[2],10));continue}if(i[3]!==void 0){const n=i[3].replace(/\\(\\')/g,s=>A[s]);e.push({_key:n,_index:-1});continue}}return e}function C(t){return t.map(e=>{if(typeof e=="string"||typeof e=="number")return e;if(e._key!=="")return{_key:e._key};if(e._index!==-1)return e._index;throw new Error(`invalid segment:${JSON.stringify(e)}`)})}function z(t){return t.map(e=>{if(typeof e=="string"||typeof e=="number")return e;if(e._index!==-1)return e._index;throw new Error(`invalid segment:${JSON.stringify(e)}`)})}function B(t,e){if(!(e!=null&&e.mappings))return;const r=X(z(t));if(e.mappings[r]!==void 0)return{mapping:e.mappings[r],matchedPath:r,pathSuffix:""};const i=Object.entries(e.mappings).filter(([p])=>r.startsWith(p)).sort(([p],[d])=>d.length-p.length);if(i.length==0)return;const[n,s]=i[0],o=r.substring(n.length);return{mapping:s,matchedPath:n,pathSuffix:o}}function Q(t){return t!==null&&Array.isArray(t)}function x(t){return typeof t=="object"&&t!==null}function $(t,e,r=[]){if(Q(t))return t.map((i,n)=>{if(x(i)){const s=i._key;if(typeof s=="string")return $(i,e,r.concat({_key:s,_index:n}))}return $(i,e,r.concat(n))});if(x(t)){if(t._type==="block"||t._type==="span"){const i={...t};return t._type==="block"?i.children=$(t.children,e,r.concat("children")):t._type==="span"&&(i.text=$(t.text,e,r.concat("text"))),i}return Object.fromEntries(Object.entries(t).map(([i,n])=>[i,$(n,e,r.concat(i))]))}return e(t,r)}function Y(t,e,r){return $(t,(i,n)=>{if(typeof i!="string")return i;const s=B(n,e);if(!s)return i;const{mapping:o,matchedPath:p}=s;if(o.type!=="value"||o.source.type!=="documentValue")return i;const d=e.documents[o.source.document],u=e.paths[o.source.path],h=j(p),y=j(u).concat(n.slice(h.length));return r({sourcePath:y,sourceDocument:d,resultPath:n,value:i})})}const Z="drafts",v="versions",E=".",N=`${Z}${E}`,g=`${v}${E}`;function W(t){return t.startsWith(N)}function m(t){return t.startsWith(g)}function tt(t){return!W(t)&&!m(t)}function et(t){if(!m(t))return;const[e,r,...i]=t.split(E);return r}function nt(t){return m(t)?t.split(E).slice(2).join(E):W(t)?t.slice(N.length):t}function rt(t){const{baseUrl:e,workspace:r="default",tool:i="default",id:n,type:s,path:o,projectId:p,dataset:d}=t;if(!e)throw new Error("baseUrl is required");if(!o)throw new Error("path is required");if(!n)throw new Error("id is required");if(e!=="/"&&e.endsWith("/"))throw new Error("baseUrl must not end with a slash");const u=r==="default"?void 0:r,h=i==="default"?void 0:i,y=nt(n),k=Array.isArray(o)?L(C(o)):o,f=new URLSearchParams({baseUrl:e,id:y,type:s,path:k});if(u&&f.set("workspace",u),h&&f.set("tool",h),p&&f.set("projectId",p),d&&f.set("dataset",d),tt(n))f.set("perspective","published");else if(m(n)){const P=et(n);f.set("perspective",P)}const l=[e==="/"?"":e];u&&l.push(u);const b=["mode=presentation",`id=${y}`,`type=${s}`,`path=${encodeURIComponent(k)}`];return h&&b.push(`tool=${h}`),l.push("intent","edit",`${b.join(";")}?${f}`),l.join("/")}function it(t){let e=typeof t=="string"?t:t.baseUrl;return e!=="/"&&(e=e.replace(/\/$/,"")),typeof t=="string"?{baseUrl:e}:{...t,baseUrl:e}}const T=({sourcePath:t,resultPath:e,value:r})=>{if(st(r)||at(r))return!1;const i=t.at(-1);return!(t.at(-2)==="slug"&&i==="current"||typeof i=="string"&&(i.startsWith("_")||i.endsWith("Id"))||t.some(n=>n==="meta"||n==="metadata"||n==="openGraph"||n==="seo")||U(t)||U(e)||typeof i=="string"&&ot.has(i))},ot=new Set(["color","colour","currency","email","format","gid","hex","href","hsl","hsla","icon","id","index","key","language","layout","link","linkAction","locale","lqip","page","path","ref","rgb","rgba","route","secret","slug","status","tag","template","theme","type","textTheme","unit","url","username","variant","website"]);function st(t){return/^\d{4}-\d{2}-\d{2}/.test(t)?!!Date.parse(t):!1}function at(t){try{new URL(t,t.startsWith("/")?"https://acme.com":void 0)}catch{return!1}return!0}function U(t){return t.some(e=>typeof e=="string"&&e.match(/type/i)!==null)}const S=20;function ct(t,e,r){var d,u,h,y,k,f,l,b,P;const{filter:i,logger:n,enabled:s}=r;if(!s){const a="config.enabled must be true, don't call this function otherwise";throw(d=n==null?void 0:n.error)==null||d.call(n,`[@sanity/client]: ${a}`,{result:t,resultSourceMap:e,config:r}),new TypeError(a)}if(!e)return(u=n==null?void 0:n.error)==null||u.call(n,"[@sanity/client]: Missing Content Source Map from response body",{result:t,resultSourceMap:e,config:r}),t;if(!r.studioUrl){const a="config.studioUrl must be defined";throw(h=n==null?void 0:n.error)==null||h.call(n,`[@sanity/client]: ${a}`,{result:t,resultSourceMap:e,config:r}),new TypeError(a)}const o={encoded:[],skipped:[]},p=Y(t,e,({sourcePath:a,sourceDocument:_,resultPath:w,value:c})=>{if((typeof i=="function"?i({sourcePath:a,resultPath:w,filterDefault:T,sourceDocument:_,value:c}):T({sourcePath:a,resultPath:w,value:c}))===!1)return n&&o.skipped.push({path:O(a),value:`${c.slice(0,S)}${c.length>S?"...":""}`,length:c.length}),c;n&&o.encoded.push({path:O(a),value:`${c.slice(0,S)}${c.length>S?"...":""}`,length:c.length});const{baseUrl:R,workspace:V,tool:M}=it(typeof r.studioUrl=="function"?r.studioUrl(_):r.studioUrl);if(!R)return c;const{_id:q,_type:J,_projectId:F,_dataset:G}=_;return H(c,{origin:"sanity.io",href:rt({baseUrl:R,workspace:V,tool:M,id:q,type:J,path:a,...!r.omitCrossDatasetReferenceData&&{dataset:G,projectId:F}})},!1)});if(n){const a=o.skipped.length,_=o.encoded.length;if((a||_)&&((y=(n==null?void 0:n.groupCollapsed)||n.log)==null||y("[@sanity/client]: Encoding source map into result"),(k=n.log)==null||k.call(n,`[@sanity/client]: Paths encoded: ${o.encoded.length}, skipped: ${o.skipped.length}`)),o.encoded.length>0&&((f=n==null?void 0:n.log)==null||f.call(n,"[@sanity/client]: Table of encoded paths"),(l=(n==null?void 0:n.table)||n.log)==null||l(o.encoded)),o.skipped.length>0){const w=new Set;for(const{path:c}of o.skipped)w.add(c.replace(D,"0").replace(/\[\d+\]/g,"[]"));(b=n==null?void 0:n.log)==null||b.call(n,"[@sanity/client]: List of skipped paths",[...w.values()])}(a||_)&&((P=n==null?void 0:n.groupEnd)==null||P.call(n))}return p}function O(t){return L(C(t))}var ft=Object.freeze({__proto__:null,stegaEncodeSourceMap:ct});export{Y as encodeIntoResult,ct as stegaEncodeSourceMap,ft as stegaEncodeSourceMap$1}; 3 | -------------------------------------------------------------------------------- /src/actions/cart-action.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | 4 | import { getCurrentSession } from "@/actions/auth"; 5 | import prisma from "@/lib/prisma"; 6 | import { dataset } from "@/sanity/env"; 7 | import { revalidatePath } from "next/cache"; 8 | import { title } from "process"; 9 | 10 | 11 | 12 | export const createCart = async () => { 13 | const { user } = await getCurrentSession(); 14 | 15 | const cart = await prisma.cart.create({ 16 | data: { 17 | id: crypto.randomUUID(), 18 | user: user? { connect : { id: user.id } } : undefined, 19 | items: { 20 | create: [], 21 | }, 22 | }, 23 | include: { 24 | items: true, 25 | } 26 | }); 27 | return cart; 28 | } 29 | 30 | export const getOrCreateCart = async (cardId?: string | null) => { 31 | const { user } = await getCurrentSession(); 32 | 33 | if (user) { 34 | const userCart = await prisma.cart.findUnique({ 35 | where: { 36 | userId: user.id, 37 | }, 38 | include: { 39 | items: true, 40 | } 41 | }); 42 | if (userCart) { 43 | return userCart; 44 | 45 | } 46 | } 47 | 48 | if(!cardId){ 49 | return createCart(); 50 | } 51 | 52 | const cart = await prisma.cart.findUnique({ 53 | 54 | where: { 55 | id: cardId, 56 | }, 57 | include: { 58 | items: true, 59 | } 60 | }); 61 | if (!cart) { 62 | return createCart(); 63 | } 64 | return cart; 65 | } 66 | 67 | export const updateCartItem = async ( 68 | cartId: string, 69 | sanityProductId: string, 70 | data: { 71 | title?: string; 72 | price?: number; 73 | image?: string; 74 | quantity?: number; 75 | 76 | } 77 | ) => { 78 | const cart = await getOrCreateCart(); 79 | 80 | const existingItem = cart.items.find( 81 | (item) => sanityProductId === item.sanityProductId 82 | ); 83 | 84 | if(existingItem) { 85 | // update quantity 86 | if (data.quantity === 0) { 87 | await prisma.cartLineItem.delete({ 88 | where: { 89 | id: existingItem.id, 90 | }, 91 | 92 | }); 93 | }else if (data.quantity && data.quantity > 0) { 94 | await prisma.cartLineItem.update({ 95 | where: { 96 | id: existingItem.id, 97 | }, 98 | data: { 99 | quantity: data.quantity, 100 | }, 101 | }); 102 | } 103 | 104 | } else if (data.quantity && data.quantity > 0) { 105 | 106 | await prisma.cartLineItem.create({ 107 | data: { 108 | id: crypto.randomUUID(), 109 | cartId: cart.id, 110 | sanityProducId: sanityProductId, 111 | quantity: data.quantity, 112 | title: data.title || '', 113 | price: data.price || 0, 114 | image: data.image || '', 115 | } 116 | }); 117 | } 118 | 119 | revalidatePath("/"); 120 | return getOrCreateCart(cartId); 121 | 122 | 123 | } 124 | 125 | export const syncCartWithUser = async (cartId: string | null) => { 126 | const { user } = await getCurrentSession(); 127 | if (!user) { 128 | return null; 129 | } 130 | 131 | const existingUserCart = await prisma.cart.findUnique({ 132 | where: { 133 | userId: user.id, 134 | }, 135 | include: { 136 | items: true, 137 | } 138 | }); 139 | 140 | const existingAnonymousCart = cartId? await prisma.cart.findUnique({ 141 | where: { 142 | id: cartId, 143 | }, 144 | include: { 145 | items: true, 146 | } 147 | }) : null; 148 | 149 | if(!cartId && existingUserCart) { 150 | return existingUserCart; 151 | } 152 | 153 | if(!cartId){ 154 | return createCart(); 155 | } 156 | 157 | if (!existingAnonymousCart && !existingUserCart) { 158 | return createCart(); 159 | } 160 | 161 | if (existingUserCart && existingUserCart.id == cartId) { 162 | return existingUserCart; 163 | } 164 | 165 | if (!existingUserCart) { 166 | const newCart = await prisma.cart.update({ 167 | where:{ 168 | id: cartId, 169 | }, data: { 170 | user: { 171 | connect: { 172 | id: user.id, 173 | } 174 | }, 175 | }, 176 | include:{ 177 | items: true, 178 | } 179 | }) 180 | 181 | return newCart; 182 | } 183 | 184 | if (existingAnonymousCart){ 185 | return existingUserCart; 186 | } 187 | for(const item of existingAnonymousCart?.items) { 188 | const existingItem = existingUserCart.items.find((item) => item.sanityProductId === item.sanityProductId); 189 | 190 | if(existingItem) { 191 | // add two cart quantities together 192 | await prisma.cartLineItem.update({ 193 | where:{ 194 | id: existingItem.id, 195 | }, 196 | data:{ 197 | quantity: existingItem.quantity + item.quantity, 198 | } 199 | }) 200 | } 201 | else { 202 | // add non-existing items to the source cart 203 | await prisma.cartLineItem.create({ 204 | data: { 205 | id: crypto.randomUUID(), 206 | cartId: existingUserCart.id, 207 | sanityProductId: item.sanityProductId, 208 | quantity: item.quantity, 209 | title: item.title, 210 | price: item.price, 211 | image: item.image, 212 | } 213 | }) 214 | } 215 | } 216 | 217 | await prisma.cart.delete({ 218 | where: { 219 | id: cartId, 220 | } 221 | }); 222 | 223 | revalidatePath("/"); 224 | return getOrCreateCart(existingUserCart.id); 225 | } 226 | 227 | -------------------------------------------------------------------------------- /src/app/product/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import SalesCampaignBanner from '@/components/layout/SalesCampaignBanner'; 2 | import AddToCartButton from '@/components/product/AddToCartButton'; 3 | import { formatPrice } from '@/lib/utils'; 4 | import { getProductById } from '@/sanity/lib/client'; 5 | import { urlFor } from '@/sanity/lib/image'; 6 | import { ChevronRight, Home } from 'lucide-react'; 7 | import Image from 'next/image'; 8 | import Link from 'next/link'; 9 | import React from 'react' 10 | 11 | const ProductPage = async ( { params } : {params: Promise<{id: string }>} ) => { 12 | const {id} = await params; 13 | 14 | const product = await getProductById(id); 15 | 16 | if(!product.price) { 17 | return
Product not found
; 18 | } 19 | 20 | const originalPrice = product.price * 5; 21 | 22 | return ( 23 |
24 | 25 | 26 | {/* BreadCrumb Navigation */} 27 |
28 |
29 |
30 | 34 | 35 | < Home className='w-4 h-4'/> 36 | Home 37 | 38 | 39 | 40 | 41 | 42 | {product.title} 43 | 44 | 45 |
46 |
47 | 48 |
49 | 50 | {/* Product Sale Banner*/} 51 | 52 |
53 | 54 |
55 |

56 | 🔥 FLASH SALE - 80% OFF 🔥 57 |

58 |
59 |

60 | ⚡ Only {Math.floor(Math.random() * 10) + 1} items left at this price! 61 |

62 | 63 |
64 | ⏰ Offer ends soon! 65 |
66 | 67 |
68 | 69 |
70 | 71 |
72 | 73 | {/* Guarantee Items*/} 74 | 75 |
76 |
77 |
78 |
79 | 🚚 80 | Free Express Shipping 81 |
82 | 83 |
84 | 85 | Satisfaction Guaranteed 86 |
87 | 88 |
89 | 🔒 90 | Secure Checkout 91 |
92 | 93 |
94 | 95 |
96 |
97 | 98 | {/* Product Details */} 99 |
100 |
101 | 102 | {/* Product Image */} 103 | {product.image && ( 104 |
105 |
106 | {product.title 113 | 114 |
115 |
116 | )} 117 | 118 | {/* Product Information*/} 119 | 120 |
121 | 122 |

123 | {product.title} 124 | 125 |

126 | 127 |

128 | {product.description} 129 |

130 | 131 | {/*price section*/} 132 | 133 |
134 |
135 |
136 | US 137 | 138 | {formatPrice(product.price).replace('$', '')} 139 | 140 |
141 |
142 | 143 | {formatPrice(originalPrice)} 144 | 145 |
146 | 147 | -80% 148 | 149 | 150 | MEGA SAVINGS 151 | 152 |
153 |
154 | 155 |
156 | 157 |
158 | 💰 159 | 160 | You save {formatPrice(originalPrice - product.price)} 161 | 162 |
163 | 164 |
165 | 166 | {Math.floor(Math.random() * 50) + 20} people bought in the last hour 167 | 168 |
169 |
170 |
171 |
172 | ⚡q 173 | Limited Time Offer! 174 |
175 |
176 | Order now before price changes! 177 |
178 |
179 | 180 | 181 |
182 |
183 | 184 | In stock - Ships within 24hours 185 | 186 |
187 |
188 | ↩️ 189 | 30-day money-back guarantee 190 | 191 |
192 |
193 | 🛡️ 194 | Secure payment processing 195 | 196 |
197 | 198 |
199 |
200 |
201 |
202 | 203 |
204 | ) 205 | } 206 | 207 | export default ProductPage -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | Sanity Studio 265 |
-------------------------------------------------------------------------------- /.sanity/runtime/index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | Sanity Studio 270 |
-------------------------------------------------------------------------------- /dist/static/resources2-Cts1kShQ.js: -------------------------------------------------------------------------------- 1 | const e={"action.add-document":"Add document","action.archive":"Archive release","action.archive.tooltip":"Unschedule this release to archive it","action.archived":"Archived","action.create-revert-release":"Stage in new release","action.delete-release":"Delete release","action.edit":"Edit release","action.open":"Active","action.schedule":"Schedule release...","action.unpublish":"Unpublish","action.unpublish-doc-actions":"Unpublish when releasing","action.unschedule":"Unschedule release","action.publish-all-documents":"Run release","action.review":"Review changes","action.revert":"Revert release","actions.summary":"Summary","action.immediate-revert-release":"Revert now","action.unarchive":"Unarchive release","activity.event.add-document":"added a document version","activity.event.archive":"archived the {{releaseTitle}} release","activity.event.create":"created the {{releaseTitle}} release targeting ","activity.event.discard-document":"discarded a document version","activity.event.edit":"set release time to ","activity.event.edit-time-asap":"immediately","activity.event.edit-time-undecided":"never","activity.event.publish":"published the {{releaseTitle}} release","activity.event.schedule":"marked as scheduled","activity.event.unarchive":"unarchived the {{releaseTitle}} release","activity.event.unschedule":"marked as unscheduled","activity.panel.loading":"Loading release activity","activity.panel.error":"An error occurred getting the release activity","activity.panel.title":"Activity","archive-dialog.confirm-archive-header":"Are you sure you want to archive this release?","archive-dialog.confirm-archive-title":"Are you sure you want to archive the '{{title}}' release?","archive-dialog.confirm-archive-description_one":"This will archive 1 document version.","archive-dialog.confirm-archive-description_other":"This will archive {{count}} document versions.","archive-dialog.confirm-archive-button":"Yes, archive release","archive-info.title":"This release is archived","archive-info.description":"Your plan supports a {{retentionDays}}-day retention period. After this period this release will be removed.","changes-published-docs.title":"Changes to published documents",created:"Created ","dashboard.details.published-asap":"Published","dashboard.details.published-on":"Published on {{date}}","dashboard.details.pin-release":"Pin release","dashboard.details.unpin-release":"Unpin release","dashboard.details.activity":"Activity","delete-dialog.confirm-delete.header":"Are you sure you want to delete this release?","delete-dialog.confirm-delete-description_one":"This will delete 1 document version.","delete-dialog.confirm-delete-description_other":"This will delete {{count}} document versions.","delete-dialog.confirm-delete-button":"Yes, delete release","diff.no-changes":"No changes","diff.list-empty":"Changes list is empty, see document","discard-version-dialog.description":"This will also permanently remove the changes made to this document within this release.","discard-version-dialog.header":"Are you sure you want to remove this document from the release?","discard-version-dialog.title":"Yes, discard version","document-validation.error_other":"{{count}} validation errors","document-validation.error_one":"{{count}} validation error","deleted-release":"The '{{title}}' release has been deleted","error-details-title":"Error details","failed-edit-title":"Failed to save changes","failed-publish-title":"Failed to publish","failed-schedule-title":"Failed to schedule","footer.status.archived":"Archived","footer.status.created":"Created","footer.status.edited":"Edited","footer.status.published":"Published","footer.status.unarchived":"Unarchived","loading-release":"Loading release","loading-release-documents":"Loading documents","loading-release-documents.error.title":"Something went wrong","loading-release-documents.error.description":"We're unable to load the documents for this release. Please try again later.","menu.label":"Release menu","menu.tooltip":"Actions","menu.group.when-releasing":"When releasing","no-archived-release":"No archived releases","no-releases":"No Releases","not-found":"Release not found: {{releaseId}}","overview.action.documentation":"Documentation","overview.calendar.tooltip":"View calendar","overview.description":"Releases are collections of document changes which can be managed, scheduled, and rolled back together.","overview.search-releases-placeholder":"Search releases","overview.title":"Releases","permissions.error.discard-version":"You do not have permission to discard this version","permissions.error.unpublish":"You do not have permission to unpublish this document","permission-missing-title":"Limited access","permission-missing-description":"Your role currently limits what you can see in this release. You may not publish nor schedule this release.","permissions.error.archive":"You do not have permission to archive this release","permissions.error.delete":"You do not have permission to delete this release","permissions.error.unarchive":"You do not have permission to unarchive this release","publish-action.validation.no-documents":"There are no documents to publish","publish-dialog.confirm-publish.title":"Are you sure you want to publish the release and all document versions?","publish-dialog.confirm-publish-description_one":"The '{{title}}' release and its document will be published.","publish-dialog.confirm-publish-description_other":"The '{{title}}' release and its {{releaseDocumentsLength}} documents will be published.","publish-dialog.validation.no-permission":"You do not have permission to publish","publish-dialog.validation.loading":"Validating documents...","publish-dialog.validation.error":"Some documents have validation errors","publish-info.title":"This release is published","release-placeholder.title":"Untitled","review.description":"Add documents to this release to review changes","review.edited":"Edited ","revert-dialog.confirm-revert-description_one":"This will revert {{releaseDocumentsLength}} document version.","revert-dialog.confirm-revert-description_other":"This will revert {{releaseDocumentsLength}} document versions.","revert-dialog.confirm-revert.title":"Are you sure you want to revert the '{{title}}' release?","revert-dialog.confirm-revert.stage-revert-checkbox-label":"Stage revert actions in a new release","revert-dialog.confirm-revert.warning-card":"Changes were made to documents in this release after they were published. Reverting will overwrite these changes.","revert-release.title":'Reverting "{{title}}"',"revert-release.description":'Revert changes to document versions in "{{title}}".',"schedule-button.tooltip":"Are you sure you want to unschedule the release?","schedule-action.validation.no-documents":"There are no documents to schedule","schedule-button-tooltip.validation.no-permission":"You do not have permission to schedule","schedule-button-tooltip.validation.loading":"Validating documents...","schedule-button-tooltip.validation.error":"Some documents have validation errors","schedule-button-tooltip.already-scheduled":"This release is already scheduled","schedule-dialog.confirm-title":"Schedule the release","schedule-dialog.confirm-description_one":"The '{{title}}' release and its document will be published on the selected date.","schedule-dialog.confirm-description_other":"The {{title}} release and its {{count}} document versions will be scheduled.","schedule-dialog.confirm-button":"Yes, schedule","schedule-dialog.select-publish-date-label":"Schedule on","unschedule-dialog.confirm-title":"Are you sure you want to unschedule the release?","unschedule-dialog.confirm-description":"The release will no longer be published on the scheduled date","schedule-dialog.publish-date-in-past-warning":"Schedule this release for a future time and date.","search-documents-placeholder":"Search documents","summary.created":"Created ","summary.published":"Published ","summary.not-published":"Not published","summary.no-documents":"No documents","summary.document-count_one":"{{count}} document","summary.document-count_other":"{{count}} documents","table-body.action.add":"Add","table-body.action.change":"Change","table-body.action.unpublish":"Unpublish","table-header.archivedAt":"Archived","table-header.contributors":"Contributors","table-header.type":"Type","table-header.title":"Release","table-header.action":"Action","table-header.documents":"Documents","table-header.edited":"Edited","table-header.publishedAt":"Published","table-header.time":"Time","toast.archive.error":"Failed to archive '{{title}}': {{error}}","toast.create-version.error":"Failed to add document to release: {{error}}","toast.delete.error":"Failed to delete '{{title}}': {{error}}","toast.delete.success":"The '{{title}}' release was successfully deleted","toast.publish.error":"Failed to publish '{{title}}': {{error}}","toast.schedule.error":"Failed to schedule '{{title}}': {{error}}","toast.schedule.success":"The '{{title}}' release was scheduled.","toast.unschedule.error":"Failed to unscheduled '{{title}}': {{error}}","toast.unarchive.error":"Failed to unarchive '{{title}}': {{error}}","type-picker.tooltip.scheduled":"The release is scheduled, unschedule it to change type","toast.revert.error":"Failed to revert release: {{error}}","toast.immediate-revert.success":"The '{{title}}' release was successfully reverted","toast.revert-stage.success":"Revert release for '{{title}}' was successfully created. ","toast.revert-stage.success-link":"View revert release","unpublish.already-unpublished":"This document is already unpublished.","unpublish.no-published-version":"There is no published version of this document.","unpublish-dialog.header":"Are you sure you want to unpublish this document when releasing?","unpublish-dialog.action.cancel":"Cancel","unpublish-dialog.action.unpublish":"Yes, unpublish when releasing","unpublish-dialog.description.to-draft":"This will unpublish the document as part of the release, and create a draft if no draft exists at the time of release.","unpublish-dialog.description.lost-changes":"Any changes made to this document version will be lost."};export{e as default}; 2 | -------------------------------------------------------------------------------- /dist/static/browser-D2aw37IN.js: -------------------------------------------------------------------------------- 1 | import{g as Ue}from"./sanity-jqEJABLI.js";function Ge(G,K){for(var y=0;yT[_]})}}}return Object.freeze(Object.defineProperty(G,Symbol.toStringTag,{value:"Module"}))}var Q={exports:{}};/** @license 2 | * eventsource.js 3 | * Available under MIT License (MIT) 4 | * https://github.com/Yaffle/EventSource/ 5 | */var Ve=Q.exports,Ae;function $e(){return Ae||(Ae=1,function(G,K){(function(y){var T=y.setTimeout,_=y.clearTimeout,h=y.XMLHttpRequest,ce=y.XDomainRequest,le=y.ActiveXObject,Z=y.EventSource,I=y.document,Fe=y.Promise,J=y.fetch,ve=y.Response,Y=y.TextDecoder,he=y.TextEncoder,te=y.AbortController;if(typeof window<"u"&&typeof I<"u"&&!("readyState"in I)&&I.body==null&&(I.readyState="loading",window.addEventListener("load",function(e){I.readyState="complete"},!1)),h==null&&le!=null&&(h=function(){return new le("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function r(){}return r.prototype=e,new r}),Date.now||(Date.now=function(){return new Date().getTime()}),te==null){var Oe=J;J=function(e,r){var n=r.signal;return Oe(e,{headers:r.headers,credentials:r.credentials,cache:r.cache}).then(function(t){var o=t.body.getReader();return n._reader=o,n._aborted&&n._reader.cancel(),{status:t.status,statusText:t.statusText,headers:t.headers,body:{getReader:function(){return o}}}})},te=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function pe(){this.bitsNeeded=0,this.codePoint=0}pe.prototype.decode=function(e){function r(d,u,i){if(i===1)return d>=128>>u&&d<=2048>>u&&d<=57344>>u&&d<=65536>>u&&d<>6>15?3:u>31?2:1;if(d===6*2)return u>15?3:2;if(d===6*3)return 3;throw new Error}for(var t=65533,o="",a=this.bitsNeeded,s=this.codePoint,c=0;c191||!r(s<<6|f&63,a-6,n(a,s)))&&(a=0,s=t,o+=String.fromCharCode(s)),a===0?(f>=0&&f<=127?(a=0,s=f):f>=192&&f<=223?(a=6*1,s=f&31):f>=224&&f<=239?(a=6*2,s=f&15):f>=240&&f<=247?(a=6*3,s=f&7):(a=0,s=t),a!==0&&!r(s,a,n(a,s))&&(a=0,s=t)):(a-=6,s=s<<6|f&63),a===0&&(s<=65535?o+=String.fromCharCode(s):(o+=String.fromCharCode(55296+(s-65535-1>>10)),o+=String.fromCharCode(56320+(s-65535-1&1023))))}return this.bitsNeeded=a,this.codePoint=s,o};var Ne=function(){try{return new Y().decode(new he().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(Y==null||he==null||!Ne())&&(Y=pe);var R=function(){};function j(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=R,this.onload=R,this.onerror=R,this.onreadystatechange=R,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=R}j.prototype.open=function(e,r){this._abort(!0);var n=this,t=this._xhr,o=1,a=0;this._abort=function(i){n._sendTimeout!==0&&(_(n._sendTimeout),n._sendTimeout=0),(o===1||o===2||o===3)&&(o=4,t.onload=R,t.onerror=R,t.onabort=R,t.onprogress=R,t.onreadystatechange=R,t.abort(),a!==0&&(_(a),a=0),i||(n.readyState=4,n.onabort(null),n.onreadystatechange())),o=0};var s=function(){if(o===1){var i=0,l="",D=void 0;if("contentType"in t)i=200,l="OK",D=t.contentType;else try{i=t.status,l=t.statusText,D=t.getResponseHeader("Content-Type")}catch{i=0,l="",D=void 0}i!==0&&(o=2,n.readyState=2,n.status=i,n.statusText=l,n._contentType=D,n.onreadystatechange())}},c=function(){if(s(),o===2||o===3){o=3;var i="";try{i=t.responseText}catch{}n.readyState=3,n.responseText=i,n.onprogress()}},f=function(i,l){if((l==null||l.preventDefault==null)&&(l={preventDefault:R}),c(),o===1||o===2||o===3){if(o=4,a!==0&&(_(a),a=0),n.readyState=4,i==="load")n.onload(l);else if(i==="error")n.onerror(l);else if(i==="abort")n.onabort(l);else throw new TypeError;n.onreadystatechange()}},d=function(i){t!=null&&(t.readyState===4?(!("onload"in t)||!("onerror"in t)||!("onabort"in t))&&f(t.responseText===""?"error":"load",i):t.readyState===3?"onprogress"in t||c():t.readyState===2&&s())},u=function(){a=T(function(){u()},500),t.readyState===3&&c()};"onload"in t&&(t.onload=function(i){f("load",i)}),"onerror"in t&&(t.onerror=function(i){f("error",i)}),"onabort"in t&&(t.onabort=function(i){f("abort",i)}),"onprogress"in t&&(t.onprogress=c),"onreadystatechange"in t&&(t.onreadystatechange=function(i){d(i)}),("contentType"in t||!("ontimeout"in h.prototype))&&(r+=(r.indexOf("?")===-1?"?":"&")+"padding=true"),t.open(e,r,!0),"readyState"in t&&(a=T(function(){u()},0))},j.prototype.abort=function(){this._abort(!1)},j.prototype.getResponseHeader=function(e){return this._contentType},j.prototype.setRequestHeader=function(e,r){var n=this._xhr;"setRequestHeader"in n&&n.setRequestHeader(e,r)},j.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},j.prototype.send=function(){if((!("ontimeout"in h.prototype)||!("sendAsBinary"in h.prototype)&&!("mozAnon"in h.prototype))&&I!=null&&I.readyState!=null&&I.readyState!=="complete"){var e=this;e._sendTimeout=T(function(){e._sendTimeout=0,e.send()},4);return}var r=this._xhr;"withCredentials"in r&&(r.withCredentials=this.withCredentials);try{r.send(void 0)}catch(n){throw n}};function ye(e){return e.replace(/[A-Z]/g,function(r){return String.fromCharCode(r.charCodeAt(0)+32)})}function ge(e){for(var r=Object.create(null),n=e.split(`\r 6 | `),t=0;t"u"?typeof window<"u"?window:typeof self<"u"?self:Ve:globalThis)}(Q,Q.exports)),Q.exports}var ue,xe;function We(){return xe||(xe=1,ue=$e().EventSourcePolyfill),ue}var De=We();const ze=Ue(De),Ke=Ge({__proto__:null,default:ze},[De]);export{Ke as b}; 8 | -------------------------------------------------------------------------------- /dist/static/resources5-Bb_1eNso.js: -------------------------------------------------------------------------------- 1 | import{d as e}from"./sanity-jqEJABLI.js";const n=e("structure",{"action.copy-document-url.label":"Copy Document URL","action.delete.disabled.not-ready":"Operation not ready","action.delete.disabled.nothing-to-delete":"This document doesn't yet exist or is already deleted","action.delete.label":"Delete","action.delete.running.label":"Deleting…","action.discard-changes.confirm-dialog.confirm-discard-changes":"Are you sure you want to discard all changes since last published?","action.discard-changes.disabled.no-change":"This document has no unpublished changes","action.discard-changes.disabled.not-published":"This document is not published","action.discard-changes.disabled.not-ready":"Operation not ready","action.discard-changes.label":"Discard changes","action.duplicate.disabled.not-ready":"Operation not ready","action.duplicate.disabled.nothing-to-duplicate":"This document doesn't yet exist so there's nothing to duplicate","action.duplicate.label":"Duplicate","action.duplicate.running.label":"Duplicating…","action.publish.already-published.no-time-ago.tooltip":"Already published","action.publish.already-published.tooltip":"Published {{timeSincePublished}}","action.publish.disabled.not-ready":"Operation not ready","action.publish.draft.label":"Publish","action.publish.label":"Publish","action.publish.live-edit.label":"Publish","action.publish.live-edit.publish-disabled":"Cannot publish since Live Edit is enabled for this document type","action.publish.live-edit.tooltip":"Live Edit is enabled for this content type and publishing happens automatically as you make changes","action.publish.no-changes.tooltip":"No unpublished changes","action.publish.published.label":"Published","action.publish.running.label":"Publishing…","action.publish.validation-issues.tooltip":"There are validation errors that need to be fixed before this document can be published","action.publish.waiting":"Waiting for tasks to finish before publishing","action.restore.confirm.message":"Are you sure you want to restore this document?","action.restore.disabled.cannot-restore-initial":"You can't restore to the initial revision","action.restore.label":"Revert to revision","action.restore.tooltip":"Restore to this revision","action.unpublish.disabled.not-published":"This document is not published","action.unpublish.disabled.not-ready":"Operation not ready","action.unpublish.label":"Unpublish","action.unpublish.live-edit.disabled":"This document has live edit enabled and cannot be unpublished","banners.archived-release.description":"This document version belongs to the archived {{title}} release","banners.deleted-document-banner.restore-button.text":"Restore most recent revision","banners.deleted-document-banner.text":"This document has been deleted.","banners.deprecated-document-type-banner.text":"This document type has been deprecated.","banners.live-edit-draft-banner.discard.tooltip":"Discard draft","banners.live-edit-draft-banner.publish.tooltip":"Publish to continue editing","banners.live-edit-draft-banner.text":"The type {{schemaType}} has liveEdit enabled, but a draft version of this document exists. Publish or discard the draft in order to continue live editing it.","banners.permission-check-banner.missing-permission_create_one":"Your role does not have permission to publish this document.","banners.permission-check-banner.missing-permission_create_other":"Your roles do not have permission to publish this document.","banners.permission-check-banner.missing-permission_update_one":"Your role does not have permission to edit this document.","banners.permission-check-banner.missing-permission_update_other":"Your roles do not have permission to edit this document.","banners.permission-check-banner.request-permission-button.sent":"Editor request sent","banners.permission-check-banner.request-permission-button.text":"Ask to edit","banners.published-release.description":"You are viewing a read-only document that was published as part of {{title}}. It can't be edited","banners.reference-changed-banner.reason-changed.reload-button.text":"Reload reference","banners.reference-changed-banner.reason-changed.text":"This reference has changed since you opened it.","banners.reference-changed-banner.reason-removed.close-button.text":"Close reference","banners.reference-changed-banner.reason-removed.text":"This reference has been removed since you opened it.","banners.release.action.add-to-release":"Add to release","banners.release.action.open-to-edit":"Open release to edit","banners.release.error.description":"An error occurred when adding document to the release: {{message}}","banners.release.error.title":"Error adding document to release","banners.release.navigate-to-edit-description":"The document only exists in the","banners.release.navigate-to-edit-description-end_one":"release","banners.release.navigate-to-edit-description-end_other":"releases","banners.release.navigate-to-edit-description-multiple_one":"This document is part of the release and {{count}} more release.","banners.release.navigate-to-edit-description-multiple_other":"This document is part of the release and {{count}} more releases","banners.release.navigate-to-edit-description-single":"This document is part of the release","banners.release.not-in-release":"Not in the {{title}} release.","banners.release.waiting.description":"Please hold tight while the document is added to the release. It should not take longer than a few seconds.","banners.release.waiting.title":"Adding document to release…","banners.unpublished-release-banner.text":"This document will be unpublished as part of the {{title}} release","browser-document-title.new-document":"New {{schemaType}}","browser-document-title.untitled-document":"Untitled","buttons.action-menu-button.aria-label":"Open document actions","buttons.action-menu-button.tooltip":"Document actions","buttons.split-pane-button.aria-label":"Split pane right","buttons.split-pane-button.tooltip":"Split pane right","buttons.split-pane-close-button.title":"Close split pane","buttons.split-pane-close-group-button.title":"Close pane group","changes.from.label":"From","changes.tab.history":"History","changes.tab.review-changes":"Review changes","changes.to.label":"To","compare-version.error.invalidModeParam":'"{{input}}" is not a supported document comparison mode.',"compare-version.error.invalidNextDocumentParam":"The next document parameter is invalid.","compare-version.error.invalidParams.title":"Unable to compare documents","compare-version.error.invalidPreviousDocumentParam":"The previous document parameter is invalid.","compare-versions.menu-item.title":"Compare versions","compare-versions.status.draft":"Draft","compare-versions.status.published":"Published","compare-versions.title":"Compare versions","confirm-delete-dialog.cancel-button.text":"Cancel","confirm-delete-dialog.cdr-summary.document-count_one":"1 document","confirm-delete-dialog.cdr-summary.document-count_other":"{{count}} documents","confirm-delete-dialog.cdr-summary.subtitle_one":"Dataset: {{datasets}}","confirm-delete-dialog.cdr-summary.subtitle_other":"Datasets: {{datasets}}","confirm-delete-dialog.cdr-summary.subtitle_unavailable_one":"Unavailable dataset","confirm-delete-dialog.cdr-summary.subtitle_unavailable_other":"Unavailable datasets","confirm-delete-dialog.cdr-summary.title_one":"{{documentCount}} in another dataset","confirm-delete-dialog.cdr-summary.title_other":"{{documentCount}} in {{count}} datasets","confirm-delete-dialog.cdr-table.copy-id-button.tooltip":"Copy ID to clipboard","confirm-delete-dialog.cdr-table.dataset.label":"Dataset","confirm-delete-dialog.cdr-table.document-id.label":"Document ID","confirm-delete-dialog.cdr-table.id-copied-toast.title-failed":"Failed to copy document ID","confirm-delete-dialog.cdr-table.project-id.label":"Project ID","confirm-delete-dialog.confirm-anyway-button.text_delete":"Delete anyway","confirm-delete-dialog.confirm-anyway-button.text_unpublish":"Unpublish anyway","confirm-delete-dialog.confirm-button.text_delete":"Delete now","confirm-delete-dialog.confirm-button.text_unpublish":"Unpublish now","confirm-delete-dialog.confirmation.text_delete":"Are you sure you want to delete “”?","confirm-delete-dialog.confirmation.text_unpublish":"Are you sure you want to unpublish “”?","confirm-delete-dialog.error.message.text":"An error occurred while loading referencing documents.","confirm-delete-dialog.error.retry-button.text":"Retry","confirm-delete-dialog.error.title.text":"Error","confirm-delete-dialog.header.text_delete":"Delete document?","confirm-delete-dialog.header.text_unpublish":"Unpublish document?","confirm-delete-dialog.loading.text":"Looking for referring documents…","confirm-delete-dialog.other-reference-count.title_one":"1 other reference not show","confirm-delete-dialog.other-reference-count.title_other":"{{count}} other references not shown","confirm-delete-dialog.other-reference-count.tooltip":"We can't display metadata for these references due to a missing access token for the related datasets.","confirm-delete-dialog.preview-item.preview-unavailable.subtitle":"ID: {{documentId}}","confirm-delete-dialog.preview-item.preview-unavailable.title":"Preview unavailable","confirm-delete-dialog.referential-integrity-disclaimer.text_delete":"If you delete this document, documents that refer to it will no longer be able to access it.","confirm-delete-dialog.referential-integrity-disclaimer.text_unpublish":"If you unpublish this document, documents that refer to it will no longer be able to access it.","confirm-delete-dialog.referring-document-count.text_one":"1 document refers to “”","confirm-delete-dialog.referring-document-count.text_other":"{{count}} documents refer to “”","confirm-delete-dialog.referring-documents-descriptor.text_delete":"You may not be able to delete “” because the following documents refer to it:","confirm-delete-dialog.referring-documents-descriptor.text_unpublish":"You may not be able to unpublish “” because the following documents refer to it:","confirm-dialog.cancel-button.fallback-text":"Cancel","confirm-dialog.confirm-button.fallback-text":"Confirm","default-definition.content-title":"Content","doc-title.error.text":"Error: {{errorMessage}}","doc-title.fallback.text":"Untitled","doc-title.unknown-schema-type.text":"Unknown schema type: {{schemaType}}","document-inspector.close-button.tooltip":"Close","document-inspector.dialog.title":"Inspecting ","document-inspector.dialog.title-no-value":"No value","document-inspector.menu-item.title":"Inspect","document-inspector.search.placeholder":"Search","document-inspector.view-mode.parsed":"Parsed","document-inspector.view-mode.raw-json":"Raw JSON","document-view.form-view.form-hidden":"This form is hidden","document-view.form-view.form-title-fallback":"Untitled","document-view.form-view.loading":"Loading document…","document-view.form-view.sync-lock-toast.description":"Please hold tight while the document is synced. This usually happens right after the document has been published, and it should not take more than a few seconds","document-view.form-view.sync-lock-toast.title":"Syncing document…","events.inspect.release":"Inspect {{releaseTitle}} document","events.open.draft":"Open draft document","events.open.release":"Open {{releaseTitle}} release","insufficient-permissions-message-tooltip.loading-text":"Loading…","menu-item-groups.actions-group":"Actions","menu-item-groups.layout-group":"Layout","menu-item-groups.sorting-group":"Sort","menu-items.layout.compact-view":"Compact view","menu-items.layout.detailed-view":"Detailed view","menu-items.sort-by.created":"Sort by Created","menu-items.sort-by.last-edited":"Sort by Last Edited","no-document-types-screen.link-text":"Learn how to add a document type →","no-document-types-screen.subtitle":"Please define at least one document type in your schema.","no-document-types-screen.title":"No document types","pane-header.back-button.text":"Back","pane-header.context-menu-button.tooltip":"Show menu","pane-header.create-menu.label":"Create","pane-header.create-new-button.tooltip":"Create new document","pane-header.disabled-created-button.aria-label":"Insufficient permissions","pane-item.draft-status.has-draft.tooltip":"Edited ","pane-item.draft-status.no-draft.tooltip":"No unpublished edits","pane-item.missing-schema-type.subtitle":"Document: {{documentId}}","pane-item.missing-schema-type.title":"No schema found for type {{documentType}}","pane-item.published-status.has-published.tooltip":"Published ","pane-item.published-status.no-published.tooltip":"No unpublished edits","panes.document-header-title.error.text":"Error: {{error}}","panes.document-header-title.new.text":"New {{schemaType}}","panes.document-header-title.untitled.text":"Untitled","panes.document-list-pane.error.text":"Error: {{error}}","panes.document-list-pane.error.title":"Could not fetch list items","panes.document-list-pane.max-items.text":"Displaying a maximum of {{limit}} documents","panes.document-list-pane.no-documents-of-type.text":"No documents of this type","panes.document-list-pane.no-documents.text":"No results found","panes.document-list-pane.no-matching-documents.text":"No matching documents","panes.document-list-pane.search-input.aria-label":"Search list","panes.document-list-pane.search-input.placeholder":"Search list","panes.document-operation-results.error.summary.title":"Details","panes.document-operation-results.operation-error":"An error occurred during {{context}}","panes.document-operation-results.operation-error_delete":"An error occurred while attempting to delete this document. This usually means that there are other documents that refers to it.","panes.document-operation-results.operation-error_unpublish":"An error occurred while attempting to unpublish this document. This usually means that there are other documents that refers to it.","panes.document-operation-results.operation-success":"Successfully performed {{op}} on document","panes.document-operation-results.operation-success_copy-url":"Document URL copied to clipboard","panes.document-operation-results.operation-success_createVersion":"{{title}} was added to the release","panes.document-operation-results.operation-success_delete":"The document was successfully deleted","panes.document-operation-results.operation-success_discardChanges":"All changes since last publish has now been discarded. The discarded draft can still be recovered from history","panes.document-operation-results.operation-success_duplicate":"The document was successfully duplicated","panes.document-operation-results.operation-success_publish":"{{title}} was published","panes.document-operation-results.operation-success_restore":"{{title}} was restored","panes.document-operation-results.operation-success_unpublish":"{{title}} was unpublished. A draft has been created from the latest published revision.","panes.document-operation-results.operation-undefined-title":"Untitled","panes.document-pane.document-not-found.loading":"Loading document…","panes.document-pane.document-not-found.text":"The document type is not defined, and a document with the {{id}} identifier could not be found.","panes.document-pane.document-not-found.title":"The document was not found","panes.document-pane.document-unknown-type.text":"This document has the schema type {{documentType}}, which is not defined as a type in the local content studio schema.","panes.document-pane.document-unknown-type.title":"Unknown document type: {{documentType}}","panes.document-pane.document-unknown-type.without-schema.text":"This document does not exist, and no schema type was specified for it.","panes.resolving.default-message":"Loading…","panes.resolving.slow-resolve-message":"Still loading…","panes.unknown-pane-type.missing-type.text":"Structure item is missing required type property.","panes.unknown-pane-type.title":"Unknown pane type","panes.unknown-pane-type.unknown-type.text":"Structure item of type {{type}} is not a known entity.","production-preview.menu-item.title":"Open preview","request-permission-dialog.confirm-button.text":"Send request","request-permission-dialog.description.text":"Your request will be sent to the project administrator(s). If you'd like, you can also include a note","request-permission-dialog.header.text":"Ask for edit access","request-permission-dialog.note-input.description.text":"If you'd like, you can add a note","request-permission-dialog.note-input.placeholder.text":"Add note...","request-permission-dialog.warning.denied.text":"Your request to access this project has been declined.","request-permission-dialog.warning.limit-reached.text":"You've reached the limit for role requests across all projects. Please wait before submitting more requests or contact an administrator for assistance.","status-bar.document-status-pulse.status.saved.text":"Saved","status-bar.document-status-pulse.status.syncing.text":"Saving...","status-bar.publish-status-button.last-published-time.aria-label":"Last published {{relativeTime}}","status-bar.publish-status-button.last-published-time.tooltip":"Last published ","status-bar.publish-status-button.last-updated-time.aria-label":"Last updated {{relativeTime}}","status-bar.publish-status-button.last-updated-time.tooltip":"Last updated ","status-bar.review-changes-button.aria-label":"Review changes","status-bar.review-changes-button.status.saved.text":"Saved!","status-bar.review-changes-button.status.syncing.text":"Saving...","status-bar.review-changes-button.tooltip.changes-saved":"Changes saved","status-bar.review-changes-button.tooltip.text":"Review changes","structure-error.docs-link.text":"View documentation","structure-error.error.label":"Error","structure-error.header.text":"Encountered an error while reading structure","structure-error.reload-button.text":"Reload","structure-error.structure-path.label":"Structure path","timeline-item.menu-button.aria-label":"Open action menu","timeline-item.menu-button.tooltip":"Actions","timeline-item.menu.action-collapse":"Collapse","timeline-item.menu.action-expand":"Expand"});export{n as default}; 2 | -------------------------------------------------------------------------------- /dist/static/index3-B5cTfUTo.js: -------------------------------------------------------------------------------- 1 | const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["static/index-o8Z_cZLs.js","static/sanity-jqEJABLI.js","static/index2-D9Rare3g.js"])))=>i.map(i=>d[i]); 2 | import{a0 as Oe,a1 as Me,a as $,a2 as Ae,r as S,j as p,a3 as Ve,a4 as Be,a5 as he,k as re,a6 as se,a7 as de,S as pe,T as N,f as Pe,o as ne,a8 as Ee,C as ae,b as $e,p as Re,a9 as fe,aa as ye,ab as oe,ac as We,ad as xe,H as He,ae as Ke,af as Fe,ag as Z,ah as me,m as Ge,i as Ue,ai as ke,w as Je,aj as qe,ak as ge,al as Ye,am as q,an as Qe,ao as Xe,ap as Ze,aq as et,ar as tt,F as we,as as F,at as nt,au as Te,av as Ce,aw as at,l as rt,ax as st,ay as ot,az as it,aA as lt,aB as ct,aC as dt,aD as ut,aE as ee,aF as pt,aG as Ie,aH as mt,aI as ht,U as _e,aJ as ft,aK as yt,aL as xt,aM as gt,aN as It,aO as Pt,aP as ve,aQ as wt,aR as _t,aS as vt}from"./sanity-jqEJABLI.js";function bt(){return Oe(function(t,e){var r,n=!1;t.subscribe(Me(e,function(o){var a=r;r=o,n&&e.next([a,o]),n=!0}))})}const jt=[];function St(t){const e=$.c(60),{children:r,flatIndex:n,index:o,params:a,payload:i,siblingIndex:s}=t,{navigate:l,navigateIntent:u,resolvePathFromState:c}=fe(),d=ye(),{panes:x,expand:I}=Ze();let f;f=(d==null?void 0:d.panes)||jt;const m=f;let P;P=x==null?void 0:x[x.length-2];const g=P,h=o-1;let y;e[0]!==h||e[1]!==m||e[2]!==d||e[3]!==s?(y=v=>{const j=m[h]||[],E=j[s],M=v(j,E),X=[...m.slice(0,h),M,...m.slice(h+1)];return{...d||{},panes:X}},e[0]=h,e[1]=m,e[2]=d,e[3]=s,e[4]=y):y=e[4];const w=y;let _;e[5]!==w||e[6]!==l?(_=v=>{const j=w(v);return setTimeout(()=>l(j),0),j},e[5]=w,e[6]=l,e[7]=_):_=e[7];const b=_;let R;e[8]!==w||e[9]!==c||e[10]!==s?(R=v=>{const j=w((E,M)=>[...E.slice(0,s),{...M,params:v},...E.slice(s+1)]);return c(j)},e[8]=w,e[9]=c,e[10]=s,e[11]=R):R=e[11];const U=R;let T;e[12]!==b||e[13]!==s?(T=v=>{b((j,E)=>[...j.slice(0,s),{...E,payload:v},...j.slice(s+1)])},e[12]=b,e[13]=s,e[14]=T):T=e[14];const L=T;let C;e[15]!==b||e[16]!==s?(C=v=>{b((j,E)=>[...j.slice(0,s),{...E,params:v},...j.slice(s+1)])},e[15]=b,e[16]=s,e[17]=C):C=e[17];const k=C;let z;e[18]!==h||e[19]!==l||e[20]!==m?(z=v=>{const{id:j,parentRefPath:E,type:M,template:X,version:Ne}=v;l({panes:[...m.slice(0,h+1),[{id:j,params:{template:X.id,parentRefPath:it(E),type:M,version:Ne},payload:X.params}]]})},e[18]=h,e[19]=l,e[20]=m,e[21]=z):z=e[21];const O=z;let J;const D=m[h]?m[h].length>1:!1,A=m[h]?m[h].length:0,le=n?et:void 0;let V,B;e[22]!==b?(V=v=>{const j=v===void 0?{}:v;b(()=>[{id:j.id||"",payload:j.payload,params:j.params||{}}])},B=()=>{b(Et)},e[22]=b,e[23]=V,e[24]=B):(V=e[23],B=e[24]);let W;e[25]!==I||e[26]!==h||e[27]!==g||e[28]!==l||e[29]!==m?(W=v=>{(v===void 0||v)&&g&&I(g.element),l({panes:[...m.slice(0,h)]})},e[25]=I,e[26]=h,e[27]=g,e[28]=l,e[29]=m,e[30]=W):W=e[30];let H;e[31]!==b||e[32]!==s?(H=v=>{b((j,E)=>{const M={...E,payload:(v==null?void 0:v.payload)||E.payload,params:(v==null?void 0:v.params)||E.params};return[...j.slice(0,s),M,...j.slice(s)]})},e[31]=b,e[32]=s,e[33]=H):H=e[33];let K;e[34]!==a||e[35]!==k?(K=v=>{const j=Ce(a,"view");return k(v?{...j,view:v}:j)},e[34]=a,e[35]=k,e[36]=K):K=e[36];let Y;e[37]!==U||e[38]!==n||e[39]!==h||e[40]!==O||e[41]!==u||e[42]!==a||e[43]!==i||e[44]!==m||e[45]!==k||e[46]!==L||e[47]!==s||e[48]!==A||e[49]!==le||e[50]!==V||e[51]!==B||e[52]!==W||e[53]!==H||e[54]!==K||e[55]!==D?(Y={index:n,groupIndex:h,siblingIndex:s,payload:i,params:a,hasGroupSiblings:D,groupLength:A,routerPanesState:m,ChildLink:dt,BackLink:le,ReferenceChildLink:ct,handleEditReference:O,ParameterizedLink:lt,replaceCurrent:V,closeCurrent:B,closeCurrentAndAfter:W,duplicateCurrent:H,setView:K,setParams:k,setPayload:L,createPathWithParams:U,navigateIntent:u},e[37]=U,e[38]=n,e[39]=h,e[40]=O,e[41]=u,e[42]=a,e[43]=i,e[44]=m,e[45]=k,e[46]=L,e[47]=s,e[48]=A,e[49]=le,e[50]=V,e[51]=B,e[52]=W,e[53]=H,e[54]=K,e[55]=D,e[56]=Y):Y=e[56],J=Y;const ce=J;let Q;return e[57]!==r||e[58]!==ce?(Q=p.jsx(tt.Provider,{value:ce,children:r}),e[57]=r,e[58]=ce,e[59]=Q):Q=e[59],Q}function Et(t,e){return t.length>1?t.filter(r=>r!==e):t}class G extends Error{constructor({message:e,context:r,helpId:n,cause:o}){super(e),this.name="PaneResolutionError",this.context=r,this.helpId=n,this.cause=o}}const be=new WeakMap;function ie(t){const e=be.get(t);if(e)return e;const r=It();return be.set(t,r),r}const $t=t=>!!t&&typeof(t==null?void 0:t.then)=="function",Rt=t=>xe(t)?typeof t.serialize=="function":!1,kt=t=>(e,r,n)=>{try{return t(e,r,n)}catch(o){throw o instanceof G?o:new G({message:typeof(o==null?void 0:o.message)=="string"?o.message:"",context:r,cause:o})}},Tt=t=>(...e)=>t(...e).pipe(xt(1),gt());function De(t){const e=kt(Tt(t((r,n,o)=>{if(!r)throw new G({message:"Pane returned no child",context:n,helpId:"structure-item-returned-no-child"});return $t(r)||pt(r)?ut(r).pipe(Ie(a=>e(a,n,o))):Rt(r)?e(r.serialize(n),n,o):typeof r=="function"?e(r(n.id,n),n,o):ee(r)})));return e}const je=new WeakMap;function Le(t,e){const r=je.get(t)||new Map;if(r){const a=r.get(e);if(a)return a}const n=t[e];if(typeof n!="function")throw new Error(`Expected property \`${e}\` to be a function but got ${typeof n} instead.`);const o=n.bind(t);return r.set(e,o),je.set(t,r),o}async function Ct(t){const e=new Map,r=De(i=>(s,l,u)=>{const c=s&&`${ie(s)}-${l.path.join("__")}`,d=c&&e.get(c);if(d)return d;const x=i(s,l,u);return c&&e.set(c,x),x}),n=[[{id:`__edit__${t.params.id}`,params:{...Ce(t.params,["id"]),type:t.params.type},payload:t.payload}]];async function o({currentId:i,flatIndex:s,intent:l,params:u,parent:c,path:d,payload:x,unresolvedPane:I,levelIndex:f,structureContext:m}){var w;if(!I)return[];const{id:P,type:g,...h}=u,y=await Te(r(I,{id:i,splitIndex:0,parent:c,path:d,index:s,params:{},payload:void 0,structureContext:m},s));return y.type==="document"&&y.id===P?[{panes:[...d.slice(0,d.length-1).map(_=>[{id:_}]),[{id:P,params:h,payload:x}]],depthIndex:d.length,levelIndex:f}]:(w=y.canHandleIntent)!=null&&w.call(y,l,u,{pane:y,index:s})||y.type==="documentList"&&y.schemaTypeName===g&&y.options.filter==="_type == $type"?[{panes:[...d.map(_=>[{id:_}]),[{id:u.id,params:h,payload:x}]],depthIndex:d.length,levelIndex:f}]:y.type==="list"&&y.child&&y.items?(await Promise.all(y.items.map((_,b)=>_.type==="divider"?Promise.resolve([]):o({currentId:_._id||_.id,flatIndex:s+1,intent:l,params:u,parent:y,path:[...d,_.id],payload:x,unresolvedPane:typeof y.child=="function"?Le(y,"child"):y.child,levelIndex:b,structureContext:m})))).flat():[]}const a=(await o({currentId:"root",flatIndex:0,levelIndex:0,intent:t.intent,params:t.params,parent:null,path:[],payload:t.payload,unresolvedPane:t.rootPaneNode,structureContext:t.structureContext})).sort((i,s)=>i.depthIndex===s.depthIndex?i.levelIndex-s.levelIndex:i.depthIndex-s.depthIndex)[0];return a?a.panes:n}const Dt=(t,e)=>{const r=t.replace(/^__edit__/,""),{params:n,payload:o,structureContext:{resolveDocumentNode:a}}=e,{type:i,template:s}=n;if(!i)throw new Error(`Document type for document with ID ${r} was not provided in the router params.`);let l=a({schemaType:i,documentId:r}).id("editor");return s&&(l=l.initialValueTemplate(s,o)),l.serialize()};function Lt(t){var e,r;return`contextHash(${JSON.stringify({id:t.id,parentId:parent&&ie(parent),path:t.path,index:t.index,splitIndex:t.splitIndex,serializeOptionsIndex:(e=t.serializeOptions)==null?void 0:e.index,serializeOptionsPath:(r=t.serializeOptions)==null?void 0:r.path})})`}const Se=t=>{const e={type:t.type,id:t.routerPaneSibling.id,params:t.routerPaneSibling.params||{},payload:t.routerPaneSibling.payload||null,flatIndex:t.flatIndex,groupIndex:t.groupIndex,siblingIndex:t.siblingIndex,path:t.path,paneNode:t.type==="resolvedMeta"?ie(t.paneNode):null};return`metaHash(${JSON.stringify(e)})`};function te({unresolvedPane:t,flattenedRouterPanes:e,parent:r,path:n,resolvePane:o,structureContext:a}){const[i,...s]=e,l=s[0],u={id:i.routerPaneSibling.id,splitIndex:i.siblingIndex,parent:r,path:[...n,i.routerPaneSibling.id],index:i.flatIndex,params:i.routerPaneSibling.params||{},payload:i.routerPaneSibling.payload,structureContext:a};try{return o(t,u,i.flatIndex).pipe(Ie(c=>{const d={type:"resolvedMeta",...i,paneNode:c,path:u.path},x=s.map((f,m)=>({type:"loading",path:[...u.path,...s.slice(m).map((P,g)=>`[${f.flatIndex+g}]`)],paneNode:null,...f}));if(!s.length)return ee([d]);let I;return l!=null&&l.routerPaneSibling.id.startsWith("__edit__")?I=te({unresolvedPane:Dt,flattenedRouterPanes:s,parent:r,path:u.path,resolvePane:o,structureContext:a}):i.groupIndex===(l==null?void 0:l.groupIndex)?I=te({unresolvedPane:t,flattenedRouterPanes:s,parent:r,path:n,resolvePane:o,structureContext:a}):I=te({unresolvedPane:typeof c.child=="function"?Le(c,"child"):c.child,flattenedRouterPanes:s,parent:c,path:u.path,resolvePane:o,structureContext:a}),vt(ee([d,...x]),I.pipe(F(f=>[d,...f])))}))}catch(c){if(c instanceof G&&(c.context&&console.warn(`Pane resolution error at index ${c.context.index}${c.context.splitIndex>0?` for split pane index ${c.context.splitIndex}`:""}: ${c.message}${c.helpId?` - see ${Ee(c.helpId)}`:""}`,c),c.helpId==="structure-item-returned-no-child"))return ee([]);throw c}}function zt({routerPanesStream:t,rootPaneNode:e,initialCacheState:r={cacheKeysByFlatIndex:[],flattenedRouterPanes:[],resolvedPaneCache:new Map,resolvePane:()=>wt},structureContext:n}){return t.pipe(F(o=>[[{id:"root"}],...o]),F(o=>o.flatMap((a,i)=>a.map((s,l)=>({routerPaneSibling:s,groupIndex:i,siblingIndex:l}))).map((a,i)=>({...a,flatIndex:i}))),Pt([]),bt(),F(([o,a])=>{for(let i=0;i{const{cacheKeysByFlatIndex:i,resolvedPaneCache:s}=o,{flattenedRouterPanes:l,diffIndex:u}=a,c=i.slice(0,u+1),d=i.slice(u+1),x=new Set(c.flatMap(f=>Array.from(f))),I=d.flatMap(f=>Array.from(f)).filter(f=>!x.has(f));for(const f of I)s.delete(f);return{flattenedRouterPanes:l,cacheKeysByFlatIndex:i,resolvedPaneCache:s,resolvePane:De(f=>(m,P,g)=>{const h=m&&`${ie(m)}-${Lt(P)}`,y=h&&s.get(h);if(y)return y;const w=f(m,P,g);if(!h)return w;const _=i[g]||new Set;return _.add(h),i[g]=_,s.set(h,w),w})}},r),Ie(({flattenedRouterPanes:o,resolvePane:a})=>te({unresolvedPane:e,flattenedRouterPanes:o,parent:null,path:[],resolvePane:a,structureContext:n}))).pipe(ve((o,a)=>a.map((i,s)=>{const l=o[s];return!l||i.type!=="loading"?i:l.routerPaneSibling.id===i.routerPaneSibling.id?l:i}),[]),_t((o,a)=>{if(o.length!==a.length)return!1;for(let i=0;i{e.next(a)},s=[a,e],t[2]=a,t[3]=e,t[4]=i,t[5]=s):(i=t[4],s=t[5]),S.useEffect(i,s),o}function Ot(t){return(t==null?void 0:t.panes)||[]}function Mt(){return new at(1)}function At(){const t=$.c(6),[e,r]=S.useState();if(e)throw e;const{structureContext:n,rootPaneNode:o}=oe();let a;t[0]===Symbol.for("react.memo_cache_sentinel")?(a={paneDataItems:[],resolvedPanes:[],routerPanes:[]},t[0]=a):a=t[0];const[i,s]=S.useState(a),l=Nt();let u,c;return t[1]!==o||t[2]!==l||t[3]!==n?(u=()=>{const d=zt({rootPaneNode:o,routerPanesStream:l,structureContext:n}).pipe(F(Vt)).subscribe({next:x=>s(x),error:x=>r(x)});return()=>d.unsubscribe()},c=[o,l,n],t[1]=o,t[2]=l,t[3]=n,t[4]=u,t[5]=c):(u=t[4],c=t[5]),S.useEffect(u,c),i}function Vt(t){const e=t.reduce(Wt,[]),r=e.length,n=t.map(o=>{const{groupIndex:a,flatIndex:i,siblingIndex:s,routerPaneSibling:l,path:u}=o,c=l.id,d=e[a+1];return{active:a===r-2,childItemId:(d==null?void 0:d[0].id)??null,index:i,itemId:l.id,groupIndex:a,key:`${o.type==="loading"?"unknown":o.paneNode.id}-${c}-${s}`,pane:o.type==="loading"?q:o.paneNode,params:l.params||{},path:u.join(";"),payload:l.payload,selected:i===t.length-1,siblingIndex:s}});return{paneDataItems:n,routerPanes:e,resolvedPanes:n.map(Bt)}}function Bt(t){return t.pane}function Wt(t,e){const r=t[e.groupIndex]||[];return r[e.siblingIndex]=e.routerPaneSibling,t[e.groupIndex]=r,t}async function Ht(t,e,r){if(e&&r)return{id:e,type:r};if(!e&&r)return{id:nt(),type:r};if(e&&!r){const n=await Te(t.resolveTypeForDocument(e));return{id:e,type:n}}throw new G({message:"Neither document `id` or `type` was provided when trying to resolve intent."})}const Kt={},Ft=S.memo(function(){const t=$.c(7),{navigate:e}=fe(),r=ye(Gt),{rootPaneNode:n,structureContext:o}=oe(),a=We(),[i,s]=S.useState(null);if(i)throw i;let l,u;return t[0]!==a||t[1]!==r||t[2]!==e||t[3]!==n||t[4]!==o?(l=()=>{if(r){const{intent:c,params:d,payload:x}=r;let I;return I=!1,async function(){const{id:f,type:m}=await Ht(a,typeof d.id=="string"?d.id:void 0,typeof d.type=="string"?d.type:void 0);if(I)return;const P=await Ct({intent:c,params:{...d,id:f,type:m},payload:x,rootPaneNode:n,structureContext:o});I||e({panes:P},{replace:!0})}().catch(s),()=>{I=!0}}},u=[a,r,e,n,o],t[0]=a,t[1]=r,t[2]=e,t[3]=n,t[4]=o,t[5]=l,t[6]=u):(l=t[5],u=t[6]),S.useEffect(l,u),null});function Gt(t){const e=typeof t.intent=="string"?t.intent:void 0;return e?{intent:e,params:xe(t.params)?t.params:Kt,payload:t.payload}:void 0}const Ut=Re.span` 3 | &:not(:last-child)::after { 4 | content: ' ➝ '; 5 | opacity: 0.5; 6 | } 7 | `;function Jt(t){return t.replace(/\(\.\.\.\)\./g,`(...) 8 | .`).replace(/__WEBPACK_IMPORTED_MODULE_\d+_+/g,"").replace(/___default\./g,".").replace(new RegExp(` \\(https?:\\/\\/${window.location.host}`,"g")," (")}function qt(t){const e=$.c(37),{error:r}=t;if(!(r instanceof G))throw r;const{cause:n}=r,{t:o}=re(se),a=(n==null?void 0:n.stack)||r.stack,i=a&&!(n instanceof de)&&!r.message.includes("Module build failed:");let s;e[0]!==n?(s=n instanceof de?n.path:[],e[0]=n,e[1]=s):s=e[1];const l=s,u=n instanceof de&&n.helpId||r.helpId,c=Qt;let d;e[2]!==o?(d=o("structure-error.header.text"),e[2]=o,e[3]=d):d=e[3];let x;e[4]!==d?(x=p.jsx(He,{as:"h2",children:d}),e[4]=d,e[5]=x):x=e[5];let I;e[6]!==l||e[7]!==o?(I=l.length>0&&p.jsxs(pe,{space:2,children:[p.jsx(N,{size:1,weight:"medium",children:o("structure-error.structure-path.label")}),p.jsx(Pe,{children:l.slice(1).map(Yt)})]}),e[6]=l,e[7]=o,e[8]=I):I=e[8];let f;e[9]!==o?(f=o("structure-error.error.label"),e[9]=o,e[10]=f):f=e[10];let m;e[11]!==f?(m=p.jsx(N,{size:1,weight:"medium",children:f}),e[11]=f,e[12]=m):m=e[12];let P;e[13]!==r.message||e[14]!==i||e[15]!==a?(P=i?Jt(a):r.message,e[13]=r.message,e[14]=i,e[15]=a,e[16]=P):P=e[16];let g;e[17]!==P?(g=p.jsx(Pe,{children:P}),e[17]=P,e[18]=g):g=e[18];let h;e[19]!==m||e[20]!==g?(h=p.jsxs(pe,{marginTop:4,space:2,children:[m,g]}),e[19]=m,e[20]=g,e[21]=h):h=e[21];let y;e[22]!==u||e[23]!==o?(y=u&&p.jsx(ne,{marginTop:4,children:p.jsx(N,{children:p.jsx("a",{href:Ee(u),rel:"noopener noreferrer",target:"_blank",children:o("structure-error.docs-link.text")})})}),e[22]=u,e[23]=o,e[24]=y):y=e[24];let w;e[25]!==o?(w=o("structure-error.reload-button.text"),e[25]=o,e[26]=w):w=e[26];let _;e[27]!==w?(_=p.jsx(ne,{marginTop:4,children:p.jsx(Ke,{text:w,icon:Fe,tone:"primary",onClick:c})}),e[27]=w,e[28]=_):_=e[28];let b;e[29]!==y||e[30]!==_||e[31]!==I||e[32]!==h?(b=p.jsxs(ae,{marginTop:4,padding:4,radius:2,overflow:"auto",shadow:1,tone:"inherit",children:[I,h,y,_]}),e[29]=y,e[30]=_,e[31]=I,e[32]=h,e[33]=b):b=e[33];let R;return e[34]!==b||e[35]!==x?(R=p.jsx(ae,{height:"fill",overflow:"auto",padding:4,sizing:"border",tone:"critical",children:p.jsxs($e,{children:[x,b]})}),e[34]=b,e[35]=x,e[36]=R):R=e[36],R}function Yt(t,e){return p.jsx(Ut,{children:t},`${t}-${e}`)}function Qt(){window.location.reload()}function Xt(t){const e=$.c(14),{isSelected:r,pane:n,paneKey:o}=t;let a;e[0]!==n?(a=xe(n)&&n.type||null,e[0]=n,e[1]=a):a=e[1];const i=a,{t:s}=re(se);let l;e[2]!==s?(l=s("panes.unknown-pane-type.title"),e[2]=s,e[3]=l):l=e[3];let u;e[4]!==l?(u=p.jsx(mt,{title:l}),e[4]=l,e[5]=u):u=e[5];let c;e[6]!==s||e[7]!==i?(c=p.jsx(ht,{children:p.jsx(ne,{padding:4,children:typeof i=="string"?p.jsx(N,{as:"p",muted:!0,children:p.jsx(_e,{t:s,i18nKey:"panes.unknown-pane-type.unknown-type.text",values:{type:i}})}):p.jsx(N,{as:"p",muted:!0,children:p.jsx(_e,{t:s,i18nKey:"panes.unknown-pane-type.missing-type.text"})})})}),e[6]=s,e[7]=i,e[8]=c):c=e[8];let d;return e[9]!==r||e[10]!==o||e[11]!==u||e[12]!==c?(d=p.jsxs(Xe,{id:o,selected:r,children:[u,c]}),e[9]=r,e[10]=o,e[11]=u,e[12]=c,e[13]=d):d=e[13],d}const Zt={component:S.lazy(()=>Z(()=>import("./index-o8Z_cZLs.js"),__vite__mapDeps([0,1]))),document:S.lazy(()=>Z(()=>import("./sanity-jqEJABLI.js").then(t=>t.b0),[]).then(function(t){return t.pane})),documentList:S.lazy(()=>Z(()=>import("./sanity-jqEJABLI.js").then(t=>t.b0),[]).then(function(t){return t.pane$1})),list:S.lazy(()=>Z(()=>import("./index2-D9Rare3g.js"),__vite__mapDeps([2,1])))},en=S.memo(function(t){const e=$.c(23),{active:r,childItemId:n,groupIndex:o,index:a,itemId:i,pane:s,paneKey:l,params:u,payload:c,path:d,selected:x,siblingIndex:I}=t,f=Zt[s.type]||Xt;let m;e[0]!==l||e[1]!==d||e[2]!==x?(m=p.jsx(ge,{paneKey:l,path:d,selected:x}),e[0]=l,e[1]=d,e[2]=x,e[3]=m):m=e[3];const P=n||"";let g;e[4]!==f||e[5]!==r||e[6]!==a||e[7]!==i||e[8]!==s||e[9]!==l||e[10]!==x||e[11]!==P?(g=p.jsx(f,{childItemId:P,index:a,itemId:i,isActive:r,isSelected:x,paneKey:l,pane:s}),e[4]=f,e[5]=r,e[6]=a,e[7]=i,e[8]=s,e[9]=l,e[10]=x,e[11]=P,e[12]=g):g=e[12];let h;e[13]!==m||e[14]!==g?(h=p.jsx(S.Suspense,{fallback:m,children:g}),e[13]=m,e[14]=g,e[15]=h):h=e[15];let y;return e[16]!==o||e[17]!==a||e[18]!==u||e[19]!==c||e[20]!==I||e[21]!==h?(y=p.jsx(St,{flatIndex:a,index:o,params:u,payload:c,siblingIndex:I,children:h}),e[16]=o,e[17]=a,e[18]=u,e[19]=c,e[20]=I,e[21]=h,e[22]=y):y=e[22],y},({params:t={},payload:e=null,...r},{params:n={},payload:o=null,...a})=>{if(!me(t,n)||!me(e,o))return!1;const i=new Set([...Object.keys(r),...Object.keys(a)]);for(const s of i)if(r[s]!==a[s])return!1;return!0});function tn(){const t=$.c(17),{t:e}=re(se);let r;t[0]===Symbol.for("react.memo_cache_sentinel")?(r=p.jsx(ne,{children:p.jsx(N,{size:1,children:p.jsx(ft,{})})}),t[0]=r):r=t[0];let n;t[1]!==e?(n=e("no-document-types-screen.title"),t[1]=e,t[2]=n):n=t[2];let o;t[3]!==n?(o=p.jsx(N,{as:"h1",size:1,weight:"medium",children:n}),t[3]=n,t[4]=o):o=t[4];let a;t[5]!==e?(a=e("no-document-types-screen.subtitle"),t[5]=e,t[6]=a):a=t[6];let i;t[7]!==a?(i=p.jsx(N,{as:"p",muted:!0,size:1,children:a}),t[7]=a,t[8]=i):i=t[8];let s;t[9]!==e?(s=e("no-document-types-screen.link-text"),t[9]=e,t[10]=s):s=t[10];let l;t[11]!==s?(l=p.jsx(N,{as:"p",muted:!0,size:1,children:p.jsx("a",{href:"https://www.sanity.io/docs/create-a-schema-and-configure-sanity-studio",target:"_blank",rel:"noreferrer",children:s})}),t[11]=s,t[12]=l):l=t[12];let u;return t[13]!==o||t[14]!==i||t[15]!==l?(u=p.jsx(ae,{height:"fill",children:p.jsx(we,{align:"center",height:"fill",justify:"center",padding:4,sizing:"border",children:p.jsx($e,{width:0,children:p.jsx(ae,{padding:4,radius:2,shadow:1,tone:"caution",children:p.jsxs(we,{children:[r,p.jsxs(pe,{flex:1,marginLeft:3,space:3,children:[o,i,l]})]})})})})}),t[13]=o,t[14]=i,t[15]=l,t[16]=u):u=t[16],u}const nn=t=>{const e=$.c(7),{documentId:r,documentType:n}=t,{selectedReleaseId:o}=rt(),a=st(r,n,"default",o),i=ke(),{t:s}=re(se),l=!(a!=null&&a.published)&&!(a!=null&&a.draft),u=(a==null?void 0:a.version)||(a==null?void 0:a.draft)||(a==null?void 0:a.published),c=i.get(n),{value:d,isLoading:x}=ot({enabled:!0,schemaType:c,value:u}),I=l?s("browser-document-title.new-document",{schemaType:(c==null?void 0:c.title)||(c==null?void 0:c.name)}):(d==null?void 0:d.title)||s("browser-document-title.untitled-document"),f=a.ready&&!x,m=ze(I);let P;e[0]!==m||e[1]!==f?(P=()=>{f&&(document.title=m)},e[0]=m,e[1]=f,e[2]=P):P=e[2];let g;return e[3]!==I||e[4]!==m||e[5]!==f?(g=[I,f,m],e[3]=I,e[4]=m,e[5]=f,e[6]=g):g=e[6],S.useEffect(P,g),null},ue=t=>{const e=$.c(5),{title:r}=t,n=ze(r);let o;e[0]!==n?(o=()=>{document.title=n},e[0]=n,e[1]=o):o=e[1];let a;return e[2]!==n||e[3]!==r?(a=[n,r],e[2]=n,e[3]=r,e[4]=a):a=e[4],S.useEffect(o,a),null},an=t=>{const e=$.c(8),{resolvedPanes:r}=t;if(!(r!=null&&r.length))return null;const n=r[r.length-1];if(on(n)){let i;return e[0]===Symbol.for("react.memo_cache_sentinel")?(i=p.jsx(ue,{}),e[0]=i):i=e[0],i}if(sn(n)){if(n!=null&&n.title){let s;return e[1]!==n.title?(s=p.jsx(ue,{title:n.title}),e[1]=n.title,e[2]=s):s=e[2],s}let i;return e[3]!==n.options.id||e[4]!==n.options.type?(i=p.jsx(nn,{documentId:n.options.id,documentType:n.options.type}),e[3]=n.options.id,e[4]=n.options.type,e[5]=i):i=e[5],i}const o=n==null?void 0:n.title;let a;return e[6]!==o?(a=p.jsx(ue,{title:o}),e[6]=o,e[7]=a):a=e[7],a};function ze(t){const e=$.c(3),r=oe().structureContext.title;let n;return e[0]!==t||e[1]!==r?(n=[t,r].filter(rn),e[0]=t,e[1]=r,e[2]=n):n=e[2],n.join(" | ")}function rn(t){return t}function sn(t){return t!==q&&t.type==="document"}function on(t){return t===q}const ln=Re(yt)` 9 | min-height: 100%; 10 | min-width: 320px; 11 | `,cn=Ge("mod+s"),dn=S.memo(function(t){var J;const e=$.c(31),{onPaneChange:r}=t,{push:n}=Ue(),o=ke(),{layoutCollapsed:a,setLayoutCollapsed:i}=oe(),{paneDataItems:s,resolvedPanes:l}=At(),u=ye(un),{sanity:c}=Je(),{media:d}=c,[x,I]=S.useState(null);let f;e[0]!==i?(f=()=>i(!0),e[0]=i,e[1]=f):f=e[1];const m=f;let P;e[2]!==i?(P=()=>i(!1),e[2]=i,e[3]=P):P=e[3];const g=P;let h,y;e[4]!==r||e[5]!==l?(h=()=>{l.length&&r(l)},y=[r,l],e[4]=r,e[5]=l,e[6]=h,e[7]=y):(h=e[6],y=e[7]),S.useEffect(h,y);let w,_;if(e[8]!==n?(w=()=>{const D=A=>{cn(A)&&(A.preventDefault(),n({closable:!0,id:"auto-save-message",status:"info",title:"Your work is automatically saved!",duration:4e3}))};return window.addEventListener("keydown",D),()=>window.removeEventListener("keydown",D)},_=[n],e[8]=n,e[9]=w,e[10]=_):(w=e[9],_=e[10]),S.useEffect(w,_),!((J=o._original)!=null&&J.types.some(qe))){let D;return e[11]===Symbol.for("react.memo_cache_sentinel")?(D=p.jsx(tn,{}),e[11]=D):D=e[11],D}const b=x||null,R=a?void 0:"fill",U=d[1];let T;e[12]!==s?(T=s.map(pn),e[12]=s,e[13]=T):T=e[13];let L;e[14]!==u||e[15]!==s.length?(L=s.length<=1&&u&&p.jsx(ge,{paneKey:"intent-resolver"}),e[14]=u,e[15]=s.length,e[16]=L):L=e[16];let C;e[17]!==m||e[18]!==g||e[19]!==d[1]||e[20]!==T||e[21]!==L||e[22]!==R?(C=p.jsxs(ln,{flex:1,height:R,minWidth:U,onCollapse:m,onExpand:g,children:[T,L]}),e[17]=m,e[18]=g,e[19]=d[1],e[20]=T,e[21]=L,e[22]=R,e[23]=C):C=e[23];let k;e[24]!==l?(k=p.jsx(an,{resolvedPanes:l}),e[24]=l,e[25]=k):k=e[25];let z;e[26]===Symbol.for("react.memo_cache_sentinel")?(z=p.jsx("div",{"data-portal":"",ref:I}),e[26]=z):z=e[26];let O;return e[27]!==C||e[28]!==k||e[29]!==b?(O=p.jsxs(Ye,{element:b,children:[C,k,z]}),e[27]=C,e[28]=k,e[29]=b,e[30]=O):O=e[30],O});function un(t){return typeof t.intent=="string"}function pn(t){const{active:e,childItemId:r,groupIndex:n,itemId:o,key:a,pane:i,index:s,params:l,path:u,payload:c,siblingIndex:d,selected:x}=t;return p.jsx(S.Fragment,{children:i===q?p.jsx(ge,{paneKey:a,path:u,selected:x}):p.jsx(en,{active:e,groupIndex:n,index:s,pane:i,childItemId:r,itemId:o,paneKey:a,params:l,payload:c,path:u,selected:x,siblingIndex:d})},`${i===q?"loading":i.type}-${s}`)}function yn(t){const e=$.c(14),{tool:r}=t,{options:n}=r,{unstable_sources:o}=Ae(),[a]=o;let i;e[0]!==n?(i=n||{},e[0]=n,e[1]=i):i=e[1];const{source:s,defaultDocumentNode:l,structure:u}=i;let c;e[2]===Symbol.for("react.memo_cache_sentinel")?(c=[],e[2]=c):c=e[2],S.useEffect(mn,c);let d;e[3]===Symbol.for("react.memo_cache_sentinel")?(d={error:null},e[3]=d):d=e[3];const[x,I]=S.useState(d),{error:f}=x;if(f){let w;return e[4]!==f?(w=p.jsx(qt,{error:f}),e[4]=f,e[5]=w):w=e[5],w}const m=s||a.name;let P,g;e[6]===Symbol.for("react.memo_cache_sentinel")?(P=p.jsx(dn,{onPaneChange:he}),g=p.jsx(Ft,{}),e[6]=P,e[7]=g):(P=e[6],g=e[7]);let h;e[8]!==l||e[9]!==u?(h=p.jsxs(Qe,{defaultDocumentNode:l,structure:u,children:[P,g]}),e[8]=l,e[9]=u,e[10]=h):h=e[10];let y;return e[11]!==m||e[12]!==h?(y=p.jsx(Ve,{onCatch:I,children:p.jsx(Be,{name:m,children:h})}),e[11]=m,e[12]=h,e[13]=y):y=e[13],y}function mn(){return he([]),hn}function hn(){return he([])}export{yn as default}; 12 | --------------------------------------------------------------------------------