From 31b46232b006709132555151d572a3cbb975a628 Mon Sep 17 00:00:00 2001 From: Gunnar Torfi Date: Sat, 15 Mar 2025 11:15:46 +0000 Subject: [PATCH] chore: working on migrating to rapyd from shopify --- app/[page]/opengraph-image.tsx | 43 +- app/[page]/page.tsx | 35 +- app/api/revalidate/route.ts | 23 +- app/components/checkout-button.tsx | 2 +- app/globals.css | 120 ++++ app/layout.tsx | 45 +- app/page.tsx | 14 +- app/product/[handle]/page.tsx | 85 +-- app/search/[collection]/opengraph-image.tsx | 48 +- app/search/[collection]/page.tsx | 40 +- app/search/page.tsx | 25 +- app/sitemap.ts | 57 +- components.json | 21 + components/carousel.tsx | 24 +- components/cart/actions.ts | 156 +++--- components/cart/add-to-cart.tsx | 36 +- components/cart/cart-context.tsx | 406 +++++++------- components/cart/delete-item-button.tsx | 44 +- components/cart/edit-item-quantity-button.tsx | 75 +-- components/cart/modal.tsx | 518 ++++++++++-------- components/grid/three-items.tsx | 31 +- components/layout/footer-menu.tsx | 56 +- components/layout/footer.tsx | 78 +-- components/layout/navbar/index.tsx | 64 ++- components/layout/navbar/mobile-menu.tsx | 73 ++- components/layout/product-grid-items.tsx | 23 +- components/layout/search/collections.tsx | 71 +-- components/product/gallery.tsx | 37 +- components/product/main-product-card.tsx | 66 +++ components/product/product-description.tsx | 16 +- components/product/variant-selector.tsx | 171 +++--- components/ui/button.tsx | 59 ++ lib/shopify/fragments/cart.ts | 53 -- lib/shopify/fragments/image.ts | 10 - lib/shopify/fragments/product.ts | 64 --- lib/shopify/fragments/seo.ts | 8 - lib/shopify/index.ts | 501 ----------------- lib/shopify/mutations/cart.ts | 45 -- lib/shopify/queries/cart.ts | 10 - lib/shopify/queries/collection.ts | 56 -- lib/shopify/queries/menu.ts | 10 - lib/shopify/queries/page.ts | 41 -- lib/shopify/queries/product.ts | 32 -- lib/shopify/types.ts | 272 --------- lib/store/menu.ts | 34 ++ lib/store/pages.ts | 48 ++ lib/store/products.ts | 163 ++++++ lib/store/types.ts | 78 +++ lib/utils.ts | 66 ++- lib/utils/image.ts | 8 + package.json | 5 + pnpm-lock.yaml | 76 +++ public/images/products/kinaskak-back.png | Bin 0 -> 1967044 bytes public/images/products/kinaskak.png | Bin 0 -> 1967044 bytes 54 files changed, 1913 insertions(+), 2229 deletions(-) create mode 100644 components.json create mode 100644 components/product/main-product-card.tsx create mode 100644 components/ui/button.tsx delete mode 100644 lib/shopify/fragments/cart.ts delete mode 100644 lib/shopify/fragments/image.ts delete mode 100644 lib/shopify/fragments/product.ts delete mode 100644 lib/shopify/fragments/seo.ts delete mode 100644 lib/shopify/index.ts delete mode 100644 lib/shopify/mutations/cart.ts delete mode 100644 lib/shopify/queries/cart.ts delete mode 100644 lib/shopify/queries/collection.ts delete mode 100644 lib/shopify/queries/menu.ts delete mode 100644 lib/shopify/queries/page.ts delete mode 100644 lib/shopify/queries/product.ts delete mode 100644 lib/shopify/types.ts create mode 100644 lib/store/menu.ts create mode 100644 lib/store/pages.ts create mode 100644 lib/store/products.ts create mode 100644 lib/store/types.ts create mode 100644 lib/utils/image.ts create mode 100644 public/images/products/kinaskak-back.png create mode 100644 public/images/products/kinaskak.png diff --git a/app/[page]/opengraph-image.tsx b/app/[page]/opengraph-image.tsx index 031e73fc8..df436ac86 100644 --- a/app/[page]/opengraph-image.tsx +++ b/app/[page]/opengraph-image.tsx @@ -1,9 +1,44 @@ -import OpengraphImage from 'components/opengraph-image'; -import { getPage } from 'lib/shopify'; +import { getPage } from "lib/store/pages"; +import { ImageResponse } from "next/og"; + +export const runtime = "edge"; +export const contentType = "image/png"; +export const size = { + width: 1200, + height: 630, +}; export default async function Image({ params }: { params: { page: string } }) { const page = await getPage(params.page); - const title = page.seo?.title || page.title; + const title = page?.seo?.title || page?.title || "Page Not Found"; - return await OpengraphImage({ title }); + return new ImageResponse( + ( +
+
+ {title} +
+
+ ), + { + ...size, + } + ); } diff --git a/app/[page]/page.tsx b/app/[page]/page.tsx index aa0c15603..8508146ae 100644 --- a/app/[page]/page.tsx +++ b/app/[page]/page.tsx @@ -1,13 +1,14 @@ -import type { Metadata } from 'next'; +import type { Metadata } from "next"; -import Prose from 'components/prose'; -import { getPage } from 'lib/shopify'; -import { notFound } from 'next/navigation'; +import Prose from "components/prose"; +import { getPage } from "lib/store/pages"; +import { notFound } from "next/navigation"; -export async function generateMetadata(props: { - params: Promise<{ page: string }>; +export async function generateMetadata({ + params, +}: { + params: { page: string }; }): Promise { - const params = await props.params; const page = await getPage(params.page); if (!page) return notFound(); @@ -18,13 +19,12 @@ export async function generateMetadata(props: { openGraph: { publishedTime: page.createdAt, modifiedTime: page.updatedAt, - type: 'article' - } + type: "article", + }, }; } -export default async function Page(props: { params: Promise<{ page: string }> }) { - const params = await props.params; +export default async function Page({ params }: { params: { page: string } }) { const page = await getPage(params.page); if (!page) return notFound(); @@ -34,11 +34,14 @@ export default async function Page(props: { params: Promise<{ page: string }> })

{page.title}

- {`This document was last updated on ${new Intl.DateTimeFormat(undefined, { - year: 'numeric', - month: 'long', - day: 'numeric' - }).format(new Date(page.updatedAt))}.`} + {`This document was last updated on ${new Intl.DateTimeFormat( + undefined, + { + year: "numeric", + month: "long", + day: "numeric", + } + ).format(new Date(page.updatedAt))}.`}

); diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts index 4ecc0b45d..aa5e33ba0 100644 --- a/app/api/revalidate/route.ts +++ b/app/api/revalidate/route.ts @@ -1,6 +1,23 @@ -import { revalidate } from 'lib/shopify'; -import { NextRequest, NextResponse } from 'next/server'; +import { revalidateTag } from "next/cache"; +import { NextRequest, NextResponse } from "next/server"; + +export const runtime = "edge"; export async function POST(req: NextRequest): Promise { - return revalidate(req); + try { + const body = await req.json(); + const tag = body.tag; + + if (!tag) { + return NextResponse.json( + { message: "Missing tag param" }, + { status: 400 } + ); + } + + revalidateTag(tag); + return NextResponse.json({ revalidated: true, now: Date.now() }); + } catch (error) { + return NextResponse.json({ message: "Invalid JSON" }, { status: 400 }); + } } diff --git a/app/components/checkout-button.tsx b/app/components/checkout-button.tsx index 40602ab3e..7a177f4ac 100644 --- a/app/components/checkout-button.tsx +++ b/app/components/checkout-button.tsx @@ -30,7 +30,7 @@ export const CheckoutButton = ({ description, }); - if (!result.success) { + if (!result.success || !result.data?.redirect_url) { toast.error("Failed to create checkout session"); return; } diff --git a/app/globals.css b/app/globals.css index 78b24be67..c3b3a1813 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,8 +1,12 @@ @import 'tailwindcss'; +@custom-variant dark (&:is(.dark *)); + @plugin "@tailwindcss/container-queries"; @plugin "@tailwindcss/typography"; +@plugin 'tailwindcss-animate'; + @layer base { *, ::after, @@ -30,3 +34,119 @@ input, button { @apply focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400 focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-50 dark:focus-visible:ring-neutral-600 dark:focus-visible:ring-offset-neutral-900; } + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/app/layout.tsx b/app/layout.tsx index 5e3355ce9..325f1a8c6 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,46 +1,19 @@ -import { CartProvider } from 'components/cart/cart-context'; -import { Navbar } from 'components/layout/navbar'; -import { WelcomeToast } from 'components/welcome-toast'; -import { GeistSans } from 'geist/font/sans'; -import { getCart } from 'lib/shopify'; -import { ReactNode } from 'react'; -import { Toaster } from 'sonner'; -import './globals.css'; -import { baseUrl } from 'lib/utils'; +import { CartProvider } from "components/cart/cart-context"; +import { Inter } from "next/font/google"; +import { ReactNode } from "react"; +import "./globals.css"; -const { SITE_NAME } = process.env; - -export const metadata = { - metadataBase: new URL(baseUrl), - title: { - default: SITE_NAME!, - template: `%s | ${SITE_NAME}` - }, - robots: { - follow: true, - index: true - } -}; +const inter = Inter({ subsets: ["latin"] }); export default async function RootLayout({ - children + children, }: { children: ReactNode; }) { - // Don't await the fetch, pass the Promise to the context provider - const cart = getCart(); - return ( - - - - -
- {children} - - -
-
+ + + {children} ); diff --git a/app/page.tsx b/app/page.tsx index 7c4a7d74f..d5a7dbeb8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,20 +1,18 @@ -import { Carousel } from 'components/carousel'; -import { ThreeItemGrid } from 'components/grid/three-items'; -import Footer from 'components/layout/footer'; +import Footer from "components/layout/footer"; +import { MainProductCard } from "components/product/main-product-card"; export const metadata = { description: - 'High-performance ecommerce store built with Next.js, Vercel, and Shopify.', + "High-performance ecommerce store built with Next.js, Vercel, and Shopify.", openGraph: { - type: 'website' - } + type: "website", + }, }; export default function HomePage() { return ( <> - - +