crypto-commons-gather.ing-w.../app/register/page.tsx

767 lines
33 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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"
export default function RegisterPage() {
const [step, setStep] = useState<"form" | "payment">("form")
const [isSubmitting, setIsSubmitting] = useState(false)
const [includeAccommodation, setIncludeAccommodation] = useState(true)
const [accommodationVenue, setAccommodationVenue] = useState<"commons-hub" | "herrnhof">("commons-hub")
const [accommodationType, setAccommodationType] = useState("ch-multi")
const [formData, setFormData] = useState({
name: "",
contact: "",
contributions: "",
expectations: "",
howHeard: "",
dietary: [] as string[],
dietaryOther: "",
crewConsent: "",
})
const baseTicketPrice = 80 // Early bird price €80
const accommodationPrices: Record<string, { label: string; price: number }> = {
"ch-multi": { label: "Bed in shared room (Commons Hub)", price: 274.40 },
"ch-double": { label: "Bed in double room (Commons Hub)", price: 351.40 },
"hh-single": { label: "Single — double bed (Herrnhof)", price: 95 },
"hh-double-separate": { label: "Double — double + single bed (Herrnhof)", price: 60 },
"hh-double-shared": { label: "Double — shared double bed (Herrnhof)", price: 50 },
"hh-double-couch": { label: "Double — couch + single in living room (Herrnhof)", price: 40 },
"hh-triple": { label: "Triple — shared double + single bed (Herrnhof)", price: 50 },
}
const accommodationPrice = accommodationPrices[accommodationType]?.price ?? 0
const totalPrice =
baseTicketPrice +
(includeAccommodation ? accommodationPrice : 0)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
// Validate required fields
if (
!formData.name ||
!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,
contact: formData.contact,
contributions: formData.contributions,
expectations: formData.expectations,
howHeard: formData.howHeard,
dietary: dietaryString,
crewConsent: formData.crewConsent,
}),
})
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 (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="border-b">
<div className="container mx-auto px-4 py-4">
<Link href="/" className="text-2xl font-bold text-primary">
CCG
</Link>
</div>
</header>
<main className="container mx-auto px-4 py-12 max-w-5xl">
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold mb-4">Complete Your Registration</h1>
<p className="text-xl text-muted-foreground">Choose your payment method</p>
</div>
<Card className="mb-8 border-primary/40">
<CardHeader>
<CardTitle>Event Registration</CardTitle>
<CardDescription>
Early bird pricing available until March 31, 2026
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{/* Ticket */}
<div className="flex items-start justify-between py-4 border-b border-border">
<div>
<div className="font-medium">CCG 2026 Ticket</div>
<div className="text-sm text-muted-foreground">
80 Early bird (until Mar 31) · 120 Regular (Apr-Jun) · 150 Late (after Jul 1)
</div>
<div className="text-xs text-muted-foreground mt-1">
CCA members: Bring two newcomers, get a free ticket!
</div>
</div>
<span className="text-lg font-semibold whitespace-nowrap ml-4">{baseTicketPrice.toFixed(2)}</span>
</div>
{/* Accommodation */}
<div className="py-4 border-b border-border">
<div className="flex items-start gap-3">
<Checkbox
id="include-accommodation"
checked={includeAccommodation}
onCheckedChange={(checked) => setIncludeAccommodation(checked as boolean)}
className="mt-1"
/>
<div className="flex-1">
<div className="flex justify-between items-start">
<Label htmlFor="include-accommodation" className="font-medium cursor-pointer">
Accommodation (7 nights, Aug 1623)
</Label>
{includeAccommodation && (
<span className="text-lg font-semibold whitespace-nowrap ml-4">{accommodationPrice.toFixed(2)}</span>
)}
</div>
{includeAccommodation ? (
<div className="mt-3 space-y-4">
{/* Venue selection */}
<RadioGroup
value={accommodationVenue}
onValueChange={(value: string) => {
const venue = value as "commons-hub" | "herrnhof"
setAccommodationVenue(venue)
setAccommodationType(venue === "commons-hub" ? "ch-multi" : "hh-single")
}}
className="space-y-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="commons-hub" id="venue-ch" />
<Label htmlFor="venue-ch" className="font-medium cursor-pointer text-sm">
Commons Hub
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="herrnhof" id="venue-hh" />
<Label htmlFor="venue-hh" className="font-medium cursor-pointer text-sm">
Herrnhof Villa
</Label>
</div>
</RadioGroup>
{/* Sub-options per venue */}
{accommodationVenue === "commons-hub" ? (
<div className="pl-6 border-l-2 border-primary/20">
<p className="text-xs text-muted-foreground mb-2">
30 beds on-site at the main venue. Basic, communal accommodation.
</p>
<RadioGroup
value={accommodationType}
onValueChange={setAccommodationType}
className="space-y-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="ch-multi" id="ch-multi" />
<Label htmlFor="ch-multi" className="font-normal cursor-pointer text-sm">
Bed in shared room 274.40 (39.20/night)
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="ch-double" id="ch-double" />
<Label htmlFor="ch-double" className="font-normal cursor-pointer text-sm">
Bed in double room 351.40 (50.20/night)
</Label>
</div>
</RadioGroup>
</div>
) : (
<div className="pl-6 border-l-2 border-primary/20">
<p className="text-xs text-muted-foreground mb-2">
Historic 19th-century mansion with newly renovated apartments, sauna,
and river swimming platform. Prices per person for 7 nights (incl. city tax &amp; VAT).
</p>
<RadioGroup
value={accommodationType}
onValueChange={setAccommodationType}
className="space-y-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="hh-single" id="hh-single" />
<Label htmlFor="hh-single" className="font-normal cursor-pointer text-sm">
Single double bed 95
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="hh-double-separate" id="hh-double-separate" />
<Label htmlFor="hh-double-separate" className="font-normal cursor-pointer text-sm">
Double double + single bed 60/person
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="hh-double-shared" id="hh-double-shared" />
<Label htmlFor="hh-double-shared" className="font-normal cursor-pointer text-sm">
Double shared double bed 50/person
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="hh-double-couch" id="hh-double-couch" />
<Label htmlFor="hh-double-couch" className="font-normal cursor-pointer text-sm">
Double couch + single in living room 40/person
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="hh-triple" id="hh-triple" />
<Label htmlFor="hh-triple" className="font-normal cursor-pointer text-sm">
Triple shared double + single bed 50/person
</Label>
</div>
</RadioGroup>
<p className="text-xs text-muted-foreground mt-2">
For whole-apartment quotes, contact{" "}
<a href="mailto:office@commons-hub.at" className="text-primary hover:underline">office@commons-hub.at</a>.
</p>
</div>
)}
</div>
) : (
<p className="text-sm text-muted-foreground mt-1">
I&apos;ll arrange my own accommodation
</p>
)}
</div>
</div>
</div>
{/* Food note */}
<div className="py-4 border-b border-border">
<p className="text-sm text-muted-foreground">
<span className="font-medium text-foreground">Food:</span>{" "}
We&apos;ll follow up via email with food options and pricing closer to the event.
Your dietary preferences from step 1 have been noted.
</p>
</div>
{includeAccommodation && (
<p className="text-xs text-muted-foreground bg-muted/50 rounded-lg p-3">
We&apos;ll follow up closer to the event to confirm room assignments and accommodation details.
</p>
)}
{/* Total */}
<div className="flex justify-between items-center py-4 bg-primary/10 -mx-6 px-6 mt-4">
<div>
<div className="font-bold text-lg">Total Amount</div>
<div className="text-sm text-muted-foreground">
Ticket{includeAccommodation ? " + accommodation" : ""}
</div>
</div>
<span className="text-2xl font-bold text-primary">{totalPrice.toFixed(2)}</span>
</div>
</div>
</CardContent>
</Card>
{/* Payment Methods */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Payment Options</CardTitle>
<CardDescription>Choose your preferred payment method</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<form action="/api/create-checkout-session" method="POST">
<input type="hidden" name="registrationData" value={JSON.stringify(formData)} />
<input type="hidden" name="includeAccommodation" value={includeAccommodation ? "true" : "false"} />
<input type="hidden" name="accommodationType" value={accommodationType} />
<div className="space-y-4">
<p className="text-sm text-muted-foreground">
You'll be redirected to Mollie's secure checkout where you can pay by credit card,
SEPA bank transfer, iDEAL, PayPal, or other methods.
</p>
<div className="flex gap-3 pt-4">
<Button type="button" variant="outline" onClick={() => setStep("form")} className="flex-1">
Back to Form
</Button>
<Button type="submit" className="flex-1">
Proceed to Payment
</Button>
</div>
</div>
</form>
</CardContent>
</Card>
<div className="text-center text-sm text-muted-foreground">
<p>
All payments are processed securely through Mollie. You'll receive a confirmation email after successful
payment.
</p>
</div>
</div>
</main>
{/* Footer */}
<footer className="py-12 px-4 border-t border-border">
<div className="container mx-auto max-w-6xl">
<div className="grid sm:grid-cols-2 md:grid-cols-4 gap-8 mb-8">
<div>
<h3 className="font-bold mb-4">CCG 2026</h3>
<p className="text-sm text-muted-foreground">
Crypto Commons Gathering
<br />
August 16-23, 2026
</p>
</div>
<div>
<h3 className="font-semibold mb-4 text-sm">Links</h3>
<ul className="space-y-2 text-sm">
<li>
<Link href="/register" className="text-muted-foreground hover:text-foreground transition-colors">
Register to Attend
</Link>
</li>
<li>
<Link href="/gallery" className="text-muted-foreground hover:text-foreground transition-colors">
Gallery
</Link>
</li>
<li>
<Link href="/about" className="text-muted-foreground hover:text-foreground transition-colors">
About CCG 2026
</Link>
</li>
<li>
<Link href="/directions" className="text-muted-foreground hover:text-foreground transition-colors">
Directions
</Link>
</li>
<li>
<Link
href="/transparency"
className="text-muted-foreground hover:text-foreground transition-colors"
>
Financial Transparency
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4 text-sm">Community</h3>
<ul className="space-y-2 text-sm">
<li>
<Link
href="https://t.me/+n5V_wDVKWrk1ZTBh"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Join the CCG26 Telegram
</Link>
</li>
<li>
<Link
href="https://t.me/+gZjhNaDswIc0ZDg0"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Join the Crypto Commons Association Telegram
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4 text-sm">Partners</h3>
<ul className="space-y-2 text-sm">
<li>
<Link
href="https://www.commons-hub.at/"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Commons Hub
</Link>
</li>
<li>
<Link
href="https://crypto-commons.org"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Crypto Commons Association
</Link>
</li>
<li>
<Link
href="/sponsorships"
className="text-muted-foreground hover:text-foreground transition-colors"
>
Sponsorships
</Link>
</li>
</ul>
</div>
</div>
<div className="pt-8 border-t border-border text-center text-sm text-muted-foreground">
<p>This website is under Creative Commons license. Built with solidarity for the commons.</p>
</div>
</div>
</footer>
</div>
)
}
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="border-b">
<div className="container mx-auto px-4 py-4">
<Link href="/" className="text-2xl font-bold text-primary">
CCG
</Link>
</div>
</header>
<main className="container mx-auto px-4 py-12 max-w-3xl">
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold mb-4">Register for CCG 2026</h1>
<p className="text-xl text-muted-foreground">August 16-23, 2026 at the Commons Hub in Austria</p>
</div>
<Card>
<CardHeader>
<CardTitle>Registration Form</CardTitle>
<CardDescription>Tell us about yourself and what you'd like to bring to CCG</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
{/* Name */}
<div className="space-y-2">
<Label htmlFor="name">What's your name? *</Label>
<Input
id="name"
required
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
/>
</div>
{/* Contact */}
<div className="space-y-2">
<Label htmlFor="contact">How can we contact you besides via email? *</Label>
<Input
id="contact"
required
placeholder="Telegram, Signal, phone, etc."
value={formData.contact}
onChange={(e) => setFormData({ ...formData, contact: e.target.value })}
/>
</div>
{/* Contributions */}
<div className="space-y-2">
<Label htmlFor="contributions">What inputs do you want to contribute to CCG? *</Label>
<Textarea
id="contributions"
required
placeholder="This could be a talk, workshop, research inquiry, prototype, game, performance, etc."
className="min-h-[120px]"
value={formData.contributions}
onChange={(e) => setFormData({ ...formData, contributions: e.target.value })}
/>
<p className="text-sm text-muted-foreground">
This event is organized as an unconference, meaning that each participant is invited to co-create the
program.
</p>
</div>
{/* Expectations */}
<div className="space-y-2">
<Label htmlFor="expectations">What do you expect to gain from participating? *</Label>
<Textarea
id="expectations"
required
placeholder="What are you looking for in particular?"
className="min-h-[100px]"
value={formData.expectations}
onChange={(e) => setFormData({ ...formData, expectations: e.target.value })}
/>
</div>
{/* How heard */}
<div className="space-y-2">
<Label htmlFor="howHeard">How did you hear about CCG?</Label>
<Input
id="howHeard"
placeholder="First timers: Did anyone recommend it to you? (they might be rewarded!)"
value={formData.howHeard}
onChange={(e) => setFormData({ ...formData, howHeard: e.target.value })}
/>
</div>
{/* Dietary Requirements */}
<div className="space-y-3">
<Label>Dietary Requirements</Label>
<p className="text-sm text-muted-foreground">
Food will involve catering as well as self-prepared meals. Do you have any special dietary
requirements?
</p>
<div className="space-y-2">
{["vegetarian", "vegan", "gluten free", "lactose free"].map((diet) => (
<div key={diet} className="flex items-center space-x-2">
<Checkbox
id={diet}
checked={formData.dietary.includes(diet)}
onCheckedChange={(checked) => handleDietaryChange(diet, checked as boolean)}
/>
<Label htmlFor={diet} className="font-normal cursor-pointer">
{diet.charAt(0).toUpperCase() + diet.slice(1)}
</Label>
</div>
))}
<div className="flex items-start space-x-2 mt-2">
<Checkbox
id="other"
checked={!!formData.dietaryOther}
onCheckedChange={(checked) => {
if (!checked) setFormData({ ...formData, dietaryOther: "" })
}}
/>
<div className="flex-1">
<Label htmlFor="other" className="font-normal cursor-pointer">
Other:
</Label>
<Input
id="dietaryOther"
className="mt-1"
placeholder="Please specify..."
value={formData.dietaryOther}
onChange={(e) => setFormData({ ...formData, dietaryOther: e.target.value })}
/>
</div>
</div>
</div>
</div>
{/* Commons Crews Consent */}
<div className="space-y-3">
<Label>Commons Crews Participation *</Label>
<div className="bg-muted p-4 rounded-lg space-y-2 text-sm">
<p>
Part of the magic of CCG is that we consider the event a 'temporary commons' - we co-produce its
program as well as its leisure activities collectively.
</p>
<p>
This also involves joint care for the space and its maintenance needs, for emerging social and
emotional dynamics and for documentation of the sessions.
</p>
<p className="font-medium">
Besides active involvement in shaping the content of the event, you will be expected to contribute
to one or more 'commons crews' (kitchen, cleaning, documentation, facilitation, fire/water and
atmosphere).
</p>
</div>
<RadioGroup
required
value={formData.crewConsent}
onValueChange={(value) => setFormData({ ...formData, crewConsent: value })}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="cant-wait" id="cant-wait" />
<Label htmlFor="cant-wait" className="font-normal cursor-pointer">
Can't wait
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="yes-please" id="yes-please" />
<Label htmlFor="yes-please" className="font-normal cursor-pointer">
Yes please gimme work!
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="love-it" id="love-it" />
<Label htmlFor="love-it" className="font-normal cursor-pointer">
I love getting my hands dirty :D
</Label>
</div>
</RadioGroup>
</div>
<div className="pt-4">
<Button type="submit" className="w-full" size="lg" disabled={isSubmitting}>
{isSubmitting ? "Recording registration..." : "Continue to Payment"}
</Button>
</div>
<div className="text-sm text-muted-foreground text-center">
<p>
Questions? Contact us on{" "}
<a href="https://t.me/+Bc48A-mqvmY3MmE0" className="text-primary hover:underline">
Telegram
</a>
</p>
</div>
</form>
</CardContent>
</Card>
</main>
{/* Footer */}
<footer className="py-12 px-4 border-t border-border">
<div className="container mx-auto max-w-6xl">
<div className="grid sm:grid-cols-2 md:grid-cols-4 gap-8 mb-8">
<div>
<h3 className="font-bold mb-4">CCG 2026</h3>
<p className="text-sm text-muted-foreground">
Crypto Commons Gathering
<br />
August 16-23, 2026
</p>
</div>
<div>
<h3 className="font-semibold mb-4 text-sm">Links</h3>
<ul className="space-y-2 text-sm">
<li>
<Link href="/register" className="text-muted-foreground hover:text-foreground transition-colors">
Register to Attend
</Link>
</li>
<li>
<Link href="/gallery" className="text-muted-foreground hover:text-foreground transition-colors">
Gallery
</Link>
</li>
<li>
<Link href="/about" className="text-muted-foreground hover:text-foreground transition-colors">
About CCG 2026
</Link>
</li>
<li>
<Link href="/directions" className="text-muted-foreground hover:text-foreground transition-colors">
Directions
</Link>
</li>
<li>
<Link href="/transparency" className="text-muted-foreground hover:text-foreground transition-colors">
Financial Transparency
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4 text-sm">Community</h3>
<ul className="space-y-2 text-sm">
<li>
<Link
href="https://t.me/+n5V_wDVKWrk1ZTBh"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Join the CCG26 Telegram
</Link>
</li>
<li>
<Link
href="https://t.me/+gZjhNaDswIc0ZDg0"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Join the Crypto Commons Association Telegram
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4 text-sm">Partners</h3>
<ul className="space-y-2 text-sm">
<li>
<Link
href="https://www.commons-hub.at/"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Commons Hub
</Link>
</li>
<li>
<Link
href="https://crypto-commons.org"
className="text-muted-foreground hover:text-foreground transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Crypto Commons Association
</Link>
</li>
<li>
<Link href="/sponsorships" className="text-muted-foreground hover:text-foreground transition-colors">
Sponsorships
</Link>
</li>
</ul>
</div>
</div>
<div className="pt-8 border-t border-border text-center text-sm text-muted-foreground">
<p>This website is under Creative Commons license. Built with solidarity for the commons.</p>
</div>
</div>
</footer>
</div>
)
}