diff --git a/app/api/drops/active/route.ts b/app/api/drops/active/route.ts index 24f84d0..286c48b 100644 --- a/app/api/drops/active/route.ts +++ b/app/api/drops/active/route.ts @@ -1,11 +1,11 @@ import { NextResponse } from 'next/server' import pool from '@/lib/db' -// GET /api/drops/active - Get the currently active drop (not sold out) +// GET /api/drops/active - Get the earliest unfilled drop (not sold out) export async function GET() { try { const [rows] = await pool.execute( - 'SELECT * FROM drops WHERE fill < size ORDER BY created_at DESC LIMIT 1' + 'SELECT * FROM drops WHERE fill < size ORDER BY created_at ASC LIMIT 1' ) const drops = rows as any[] diff --git a/app/components/Drop.tsx b/app/components/Drop.tsx index c32bcd4..a3b8cdd 100644 --- a/app/components/Drop.tsx +++ b/app/components/Drop.tsx @@ -1,52 +1,178 @@ 'use client' -import { useState } from 'react' +import { useState, useEffect } from 'react' import Image from 'next/image' +interface DropData { + id: number + item: string + size: number + fill: number + unit: string + ppu: number + image_url: string | null + created_at: string +} + export default function Drop() { - const [selectedSize, setSelectedSize] = useState('50g') + const [drop, setDrop] = useState(null) + const [loading, setLoading] = useState(true) + const [selectedSize, setSelectedSize] = useState(50) + + useEffect(() => { + fetchActiveDrop() + }, []) + + const fetchActiveDrop = async () => { + try { + const response = await fetch('/api/drops/active') + if (response.ok) { + const data = await response.json() + setDrop(data) + } + } catch (error) { + console.error('Error fetching active drop:', error) + } finally { + setLoading(false) + } + } + + const getProgressPercentage = (fill: number, size: number) => { + return Math.min((fill / size) * 100, 100) + } + + const formatSize = (size: number, unit: string) => { + if (unit === 'g' && size >= 1000) { + return `${(size / 1000).toFixed(1)}kg` + } + return `${size}${unit}` + } + + const getAvailableSizes = () => { + if (!drop) return [] + const sizes = [50, 100, 250] // Always in grams + + // Calculate remaining inventory in grams + let remainingInGrams = 0 + if (drop.unit === 'kg') { + remainingInGrams = (drop.size - drop.fill) * 1000 + } else { + // For 'g' or any other unit, assume same unit + remainingInGrams = drop.size - drop.fill + } + + // Only show sizes that don't exceed remaining inventory + return sizes.filter((size) => size <= remainingInGrams) + } + + if (loading) { + return ( +
+
+

Loading...

+
+
+ ) + } + + if (!drop) { + return ( +
+
+

Drop Sold Out

+

+ The current collective drop has been fully reserved. +

+

+ Next collective drop coming soon. +

+
+
+ ) + } + + const progressPercentage = getProgressPercentage(drop.fill, drop.size) + const availableSizes = getAvailableSizes() + + // Calculate remaining in the drop's unit + const remaining = drop.size - drop.fill + const hasRemaining = remaining > 0 return (
- Harlequin CBD + {drop.image_url ? ( + {drop.item} + ) : ( +
+ No Image +
+ )}
-

Harlequin – Collective Drop

-
1kg Batch · Indoor · Switzerland
-
2.50 CHF / g · incl. 2.5% VAT
+

{drop.item}

+
+ {formatSize(drop.size, drop.unit)} Batch +
+
+ {drop.ppu.toFixed(2)} CHF / {drop.unit} · incl. 2.5% VAT +
- +
-
620g of 1,000g reserved
- -
- - - +
+ {drop.fill} + {drop.unit} of {drop.size} + {drop.unit} reserved
- + {hasRemaining && availableSizes.length > 0 && ( + <> +
+ {availableSizes.map((size) => ( + + ))} +
+ + + + )} + + {hasRemaining && availableSizes.length === 0 && ( +
+

+ Less than 50{drop.unit} remaining. This drop is almost fully reserved. +

+
+ )} + + {!hasRemaining && ( +
+

This drop is fully reserved

+
+ )}
)