"use client" import type React from "react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Checkbox } from "@/components/ui/checkbox" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" import Link from "next/link" import { useState } from "react" // ── Pricing & accommodation constants ───────────────────────── const PRICING_TIERS = [ { label: "Early bird", price: 80, cutoff: "2026-03-31" }, { label: "Regular", price: 120, cutoff: "2026-07-01" }, { label: "Late", price: 150, cutoff: "2099-12-31" }, ] function getCurrentTier() { const now = new Date().toISOString().slice(0, 10) return PRICING_TIERS.find((t) => now < t.cutoff) ?? PRICING_TIERS[PRICING_TIERS.length - 1] } const ACCOMMODATION_PRICES: Record = { "ch-multi": { label: "Bed in shared room (Commons Hub)", price: 279.30 }, "ch-double": { label: "Bed in double room (Commons Hub)", price: 356.30 }, "hh-single": { label: "Single room (Herrnhof)", price: 665 }, "hh-double-separate": { label: "Double room, separate beds (Herrnhof)", price: 420 }, "hh-double-shared": { label: "Double room, shared double bed (Herrnhof)", price: 350 }, "hh-triple": { label: "Triple room, one single + one shared double bed (Herrnhof)", price: 350 }, "hh-daybed": { label: "Daybed or extra bed in living room (Herrnhof)", price: 280 }, } // ── Promo codes ─────────────────────────────────────────────── export const PROMO_CODES: Record = { "earlybird-friends": "Early bird", } export { PRICING_TIERS } // ── Component ───────────────────────────────────────────────── interface RegisterFormProps { tierOverride?: { label: string; price: number; cutoff: string } promoCode?: string banner?: React.ReactNode } export default function RegisterForm({ tierOverride, promoCode, banner }: RegisterFormProps) { const [step, setStep] = useState<"form" | "payment">("form") const [isSubmitting, setIsSubmitting] = useState(false) const [includeAccommodation, setIncludeAccommodation] = useState(true) const [wantFood, setWantFood] = useState(false) const [accommodationVenue, setAccommodationVenue] = useState<"commons-hub" | "herrnhof">("commons-hub") const [accommodationType, setAccommodationType] = useState("ch-multi") const [formData, setFormData] = useState({ name: "", email: "", contact: "", contributions: "", expectations: "", howHeard: "", dietary: [] as string[], dietaryOther: "", crewConsent: "", }) const currentTier = tierOverride ?? getCurrentTier() const baseTicketPrice = currentTier.price const accommodationPrice = ACCOMMODATION_PRICES[accommodationType]?.price ?? 0 const subtotalPrice = baseTicketPrice + (includeAccommodation ? accommodationPrice : 0) const processingFee = Math.round(subtotalPrice * 0.02 * 100) / 100 const totalPrice = subtotalPrice + processingFee const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() // Validate required fields if ( !formData.name || !formData.email || !formData.contact || !formData.contributions || !formData.expectations || !formData.crewConsent ) { alert("Please fill in all required fields") return } setIsSubmitting(true) try { // Submit registration to Google Sheet first const dietaryString = formData.dietary.join(", ") + (formData.dietaryOther ? `, ${formData.dietaryOther}` : "") const response = await fetch("/api/register", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: formData.name, email: formData.email, contact: formData.contact, contributions: formData.contributions, expectations: formData.expectations, howHeard: formData.howHeard, dietary: dietaryString, crewConsent: formData.crewConsent, wantFood, }), }) if (!response.ok) { throw new Error("Failed to record registration") } // Proceed to payment step setStep("payment") } catch (error) { console.error("Registration error:", error) alert("There was an error recording your registration. Please try again.") } finally { setIsSubmitting(false) } } const handleDietaryChange = (value: string, checked: boolean) => { setFormData((prev) => ({ ...prev, dietary: checked ? [...prev.dietary, value] : prev.dietary.filter((item) => item !== value), })) } if (step === "payment") { return (
{/* Header */}
CCG

Complete Your Registration

Choose your payment method

{banner} Event Registration {currentTier.label} pricing — €{currentTier.price}
{/* Ticket */}
CCG 2026 Ticket
{PRICING_TIERS.map((t, i) => ( {i > 0 ? " · " : ""}€{t.price} {t.label}{t.label === currentTier.label ? " (current)" : ""} ))}
CCA members: Bring two newcomers, get a free ticket!
€{baseTicketPrice.toFixed(2)}
{/* Accommodation */}
setIncludeAccommodation(checked as boolean)} className="mt-1" />
{includeAccommodation && ( €{accommodationPrice.toFixed(2)} )}
{includeAccommodation ? (
{/* Venue selection */} { const venue = value as "commons-hub" | "herrnhof" setAccommodationVenue(venue) setAccommodationType(venue === "commons-hub" ? "ch-multi" : "hh-single") }} className="space-y-2" >
{/* Sub-options per venue */} {accommodationVenue === "commons-hub" ? (

30 beds on-site at the main venue. Basic, communal accommodation.

) : (

Historic 19th-century mansion with newly renovated apartments, sauna, and river swimming platform. Prices per person for 7 nights (incl. city tax & VAT).

For whole-apartment quotes, contact{" "} office@commons-hub.at.

)}
) : (

I'll arrange my own accommodation

)}
{includeAccommodation && (

We'll follow up closer to the event to confirm room assignments and accommodation details.

)} {/* Food */}
setWantFood(checked as boolean)} className="mt-1" />

We are exploring co-producing our own meals as a community. More details and costs will be shared soon — checking this box registers your interest so we can plan accordingly. Your dietary preferences from step 1 have been noted.

{/* Processing fee */}
Payment processing fee (2%)
€{processingFee.toFixed(2)}
{/* Total */}
Total Amount
Ticket{includeAccommodation ? " + accommodation" : ""}
€{totalPrice.toFixed(2)}
{/* Payment Methods */}
Payment Options Choose your preferred payment method
{promoCode && }

You'll be redirected to Mollie's secure checkout where you can pay by credit card, SEPA bank transfer, iDEAL, PayPal, or other methods.

All payments are processed securely through Mollie. You'll receive a confirmation email after successful payment.

{/* Footer */}

CCG 2026

Crypto Commons Gathering
August 16-23, 2026

Links

  • Register to Attend
  • Gallery
  • About CCG 2026
  • Directions
  • Financial Transparency

Community

  • Join the CCG26 Telegram
  • Join the Crypto Commons Association Telegram

Partners

  • Commons Hub
  • Crypto Commons Association
  • Sponsorships

This website is under Creative Commons license. Built with solidarity for the commons.

) } return (
{/* Header */}
CCG

Register for CCG 2026

August 16-23, 2026 at the Commons Hub in Austria

{banner} Registration Form Tell us about yourself and what you'd like to bring to CCG
{/* Name */}
setFormData({ ...formData, name: e.target.value })} />
{/* Email */}
setFormData({ ...formData, email: e.target.value })} />

We'll send your registration confirmation and event updates here.

{/* Contact */}
setFormData({ ...formData, contact: e.target.value })} />
{/* Contributions */}