Untitled

 avatar
unknown
plain_text
2 months ago
5.8 kB
6
No Index
//wrangler.toml
name = "tshirt"
compatibility_flags = ["nodejs_compat"]
compatibility_date = "2024-09-23"

[[r2_buckets]]
binding = "tshirtbucket"
bucket_name = "tshirt"
preview_bucket_name = "tshirt"

[[hyperdrive]]
binding = "hyperdrive"
id = "35d40131da5f460f9aa0146b260f8bf4"


----------------------------------------------------------------------------------------------
//src/lib/db.ts
import { Kysely, PostgresDialect } from 'kysely';
import { Pool, Client } from 'pg';
import { cache } from 'react';
import { getCloudflareContext } from '@opennextjs/cloudflare';


export interface Database {
  products: {
    id: string             // UUID
    name: string
    description: string | null
    category_id: string    // UUID
    price: number          // DECIMAL mapped to number
    discount: number | null
    gender: 'men' | 'women' | 'unisex'
    public_id: string
    is_active: boolean
    created_at: Date
  }
  product_variants: {
    id: number             // INT
    product_id: string     // UUID
    size: string
    color: string
    stock: number
  }
  product_images: {
    product_id: string     // UUID
    variant_id: number     // INT
    rank: number
    path: string
  }
}

export const getKyselyDb = cache(() => {
  const { env } = getCloudflareContext();
  const connectionString = env.hyperdrive.connectionString;

  const pool = new Pool({
    connectionString,
    max: 5, // Max connections per Worker request
    maxUses: 1, // Do not reuse connections across requests
  });

  return new Kysely<Database>({
    dialect: new PostgresDialect({ pool }),
  });
});

-----------------------------------------------------------------------------------------------
//src/app/product/[itemid]/page.tsx
import {splitUuidVariant} from '@/utils/helper';
import {getKyselyDb, Database} from '@/lib/db';
import Image from 'next/image';
import { Button } from '@/components/ui/button';

export const fetchCache = 'force-no-store';
export const runtime = 'edge';

export default async function ItemRoute({ params }: { params: { itemid: string } }) {
    const res = splitUuidVariant(params.itemid);

    if (!res) {
        return <div>Invalid item ID</div>;
    }

    const [uuid, variant] = res;
    let product: Database['products'] | null = null;
    let variants: Database['product_variants'][] = [];
    let images: Database['product_images'][] = [];

    try {
        ({ product, variants, images } = await fetchItemDetails(uuid, variant));
    } catch (error) {
        console.error('Error fetching item details:', error);
        return <div>Error loading item details. Please try again later.</div>;
    }
    
    if (!product || variants.length === 0 || images.length === 0) {
        return <div>Item not found</div>;
    }

    // Render product details
    return (
        <div className="max-w-6xl mx-auto p-4 grid grid-cols-1 md:grid-cols-2 gap-8">
            <div className="space-y-4">
                {images.map(img => (
                <Image
                    key={img.product_id + '-' + img.variant_id + '-' + img.rank}
                    src={img.path}
                    alt={product?.name || 'Product image'}
                    width={500}
                    height={500}
                    className="rounded-lg border"
                />
                ))}
            </div>
        

            {/* Product Details */}
            <div className="space-y-4">
                <h1 className="text-3xl font-bold">{product.name}</h1>
                {product.discount ? (
                <div className="flex items-baseline space-x-2">
                    <span className="text-xl font-semibold text-red-600">${(product.price - product.discount).toFixed(2)}</span>
                    <span className="text-gray-400 line-through">${product.price.toFixed(2)}</span>
                </div>
                ) : (
                <div className="text-xl font-semibold">${product.price.toFixed(2)}</div>
                )}

                <p className="text-gray-700">{product.description}</p>

                {/* Variant Selector */}
                {variants.length > 1 && (
                <div className="space-y-2">
                    <h3 className="font-semibold">Select Size & Color</h3>
                    <div className="flex flex-wrap gap-2">
                    {variants.map(v => (
                        <Button key={v.id} variant="outline" size="sm">
                        {v.size} - {v.color}
                        </Button>
                    ))}
                    </div>
                </div>
                )}

                {/* Add to Cart */}
                <Button className="mt-4 w-full">Add to Cart</Button>
            </div>
        </div>
  )
}

const fetchItemDetails = async (product_id: string, variant_id: number): Promise<{
    product: Database['products'] | null;
    variants: Database['product_variants'][];
    images: Database['product_images'][];
}> => {
    const db = getKyselyDb();
    const [productResult, variants, images] = await Promise.all([
        db.selectFrom('products')
        .where('id', '=', product_id)
        .selectAll()
        .executeTakeFirst()??null,

        db.selectFrom('product_variants')
        .where('product_id', '=', product_id)
        .where('id', '=', variant_id)
        .selectAll()
        .execute(),

        db.selectFrom('product_images')
        .where('product_id', '=', product_id)
        .selectAll()
        .execute(),
    ]);

    const product = productResult ?? null;
    return { product, variants, images };
}
Editor is loading...
Leave a Comment