'use client' import { useState, useEffect } from 'react' import Image from 'next/image' import AuthModal from './AuthModal' interface DropData { id: number item: string size: number fill: number unit: string ppu: number image_url: string | null created_at: string } interface User { id: number username: string email: string } export default function Drop() { const [drop, setDrop] = useState(null) const [loading, setLoading] = useState(true) const [selectedSize, setSelectedSize] = useState(50) const [showConfirmModal, setShowConfirmModal] = useState(false) const [showAuthModal, setShowAuthModal] = useState(false) const [processing, setProcessing] = useState(false) const [user, setUser] = useState(null) const [checkingAuth, setCheckingAuth] = useState(true) useEffect(() => { fetchActiveDrop() checkAuth() }, []) const checkAuth = async () => { try { const response = await fetch('/api/auth/session', { credentials: 'include', }) if (response.ok) { const data = await response.json() setUser(data.user) } } catch (error) { console.error('Error checking auth:', error) } finally { setCheckingAuth(false) } } 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) } const handleJoinDrop = () => { // Check if user is logged in if (!user) { setShowAuthModal(true) return } setShowConfirmModal(true) } const handleLogin = (loggedInUser: User) => { setUser(loggedInUser) setShowAuthModal(false) // After login, show the confirmation modal setShowConfirmModal(true) } const handleConfirmPurchase = async () => { if (!drop) return setProcessing(true) try { // Create NOWPayments invoice and sale record const response = await fetch('/api/payments/create-invoice', { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', // Important for cookies body: JSON.stringify({ drop_id: drop.id, size: selectedSize, // Size in grams }), }) if (!response.ok) { const error = await response.json() if (response.status === 401) { // User not authenticated - show login modal setShowConfirmModal(false) setShowAuthModal(true) setProcessing(false) return } alert(`Error: ${error.error || 'Failed to create payment invoice'}`) setProcessing(false) return } const data = await response.json() // Close modal setShowConfirmModal(false) // Redirect to NOWPayments invoice if (data.invoice_url) { window.location.href = data.invoice_url } else { alert('Payment invoice created but no redirect URL received') await fetchActiveDrop() } } catch (error) { console.error('Error creating payment invoice:', error) alert('Failed to create payment invoice. Please try again.') setProcessing(false) } } const handleCancelPurchase = () => { setShowConfirmModal(false) } const calculatePrice = () => { if (!drop) return 0 if (drop.unit === 'kg') { return (selectedSize / 1000) * drop.ppu } return selectedSize * drop.ppu } 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 (
{drop.image_url ? ( {drop.item} ) : (
No Image
)}

{drop.item}

{formatSize(drop.size, drop.unit)} Batch
{drop.ppu.toFixed(2)} CHF / {drop.unit} ยท incl. 2.5% VAT
{drop.unit === 'kg' ? drop.fill.toFixed(2) : Math.round(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

)}
{/* Confirmation Modal */} {showConfirmModal && drop && (
e.stopPropagation()} >

Confirm Purchase

Item: {drop.item}

Quantity: {selectedSize}g

Price per {drop.unit}: {drop.ppu.toFixed(2)} CHF

Total: {calculatePrice().toFixed(2)} CHF

incl. 2.5% VAT

)} {/* Auth Modal */} setShowAuthModal(false)} onLogin={handleLogin} />
) }