This commit is contained in:
Sebastian 2026-02-24 21:53:49 +01:00
parent d07a5bd9a8
commit 8c620abbe6
5 changed files with 147 additions and 97 deletions

View file

@ -20,7 +20,6 @@ public class ParticipantRegistrationRequest
public string? PhoneNumber { get; set; }
[EmailAddress]
public string? Email { get; set; }
[Required]

View file

@ -15,6 +15,7 @@ interface EventContent {
rulesAndGdpr: string;
additionalInfo: string;
registrationEnabled: boolean;
volunteerRegistrationEnabled?: boolean;
visitorOnly: boolean;
volunteerAreas: string;
}
@ -128,6 +129,19 @@ export default function AdminDashboard() {
Endast Besöks-registrering (Ingen Dator)
</label>
</div>
<div className="flex items-center">
<input
type="checkbox"
id="volunteerRegistrationEnabled"
name="volunteerRegistrationEnabled"
className="h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded cursor-pointer"
checked={content.volunteerRegistrationEnabled ?? true}
onChange={(e) => setContent(prev => prev ? { ...prev, volunteerRegistrationEnabled: e.target.checked } : null)}
/>
<label htmlFor="volunteerRegistrationEnabled" className="ml-3 text-sm font-semibold text-gray-900 cursor-pointer">
Aktivera Funktionärs-anmälan
</label>
</div>
</div>
<div className="col-span-2">

View file

@ -15,6 +15,7 @@ interface EventContent {
rulesAndGdpr: string;
additionalInfo: string;
registrationEnabled: boolean;
volunteerRegistrationEnabled?: boolean;
visitorOnly: boolean;
volunteerAreas: string;
}
@ -73,98 +74,111 @@ export default function LandingPage() {
{/* Info Section */}
<main className="max-w-4xl mx-auto py-12 px-6 space-y-20">
{(content.eventDate ||
content.eventTime ||
content.locationName ||
content.locationAddress ||
content.additionalInfo) && (
<section className="space-y-6">
<h2 className="text-3xl font-bold border-b-2 border-blue-500 pb-2 inline-block">
Information
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-6">
{(content.eventDate || content.eventTime) && (
<div className="space-y-2">
<h3 className="font-semibold text-lg text-blue-600">
Tid/Datum
</h3>
{content.eventDate && <p>{content.eventDate}</p>}
{content.eventTime && <p>{content.eventTime}</p>}
</div>
)}
{(content.locationName || content.locationAddress) && (
<div className="space-y-2">
<h3 className="font-semibold text-lg text-blue-600">Plats</h3>
{content.locationName && <p>{content.locationName}</p>}
{content.locationAddress && <p>{content.locationAddress}</p>}
</div>
)}
</div>
{content.additionalInfo && (
<div className="mt-8 text-lg text-gray-700 whitespace-pre-wrap leading-relaxed">
{content.additionalInfo}
</div>
)}
</section>
)}
{content.whatToBring && (
<section className="space-y-6">
<h2 className="text-3xl font-bold border-b-2 border-blue-500 pb-2 inline-block">
Ta Med(bokad LAN-plats)
</h2>
<ul className="list-disc list-inside space-y-3 text-lg text-gray-700">
{content.whatToBring
.split("\n")
.filter((line) => line.trim())
.map((line, i) => (
<li key={i}>{line}</li>
))}
</ul>
</section>
)}
{content.rulesAndGdpr && (
<section className="space-y-6">
<h2 className="text-3xl font-bold border-b-2 border-blue-500 pb-2 inline-block">
Regler för LAN & GDPR
</h2>
<p className="text-lg text-gray-700 leading-relaxed whitespace-pre-wrap">
{content.rulesAndGdpr}
{!content.registrationEnabled ? (
<section className="flex flex-col items-center justify-center py-20 space-y-6">
<h2 className="text-4xl font-extrabold text-gray-900">Anmälan är stängd</h2>
<p className="text-xl text-gray-600 text-center max-w-lg">
Vi tar tyvärr inte emot fler anmälningar just nu. Håll utkik för framtida evenemang!
</p>
</section>
)}
) : (
<>
{(content.eventDate ||
content.eventTime ||
content.locationName ||
content.locationAddress ||
content.additionalInfo) && (
<section className="space-y-6">
<h2 className="text-3xl font-bold border-b-2 border-blue-500 pb-2 inline-block">
Information
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-6">
{(content.eventDate || content.eventTime) && (
<div className="space-y-2">
<h3 className="font-semibold text-lg text-blue-600">
Tid/Datum
</h3>
{content.eventDate && <p>{content.eventDate}</p>}
{content.eventTime && <p>{content.eventTime}</p>}
</div>
)}
{(content.locationName || content.locationAddress) && (
<div className="space-y-2">
<h3 className="font-semibold text-lg text-blue-600">Plats</h3>
{content.locationName && <p>{content.locationName}</p>}
{content.locationAddress && <p>{content.locationAddress}</p>}
</div>
)}
</div>
{content.additionalInfo && (
<div className="mt-8 text-lg text-gray-700 whitespace-pre-wrap leading-relaxed">
{content.additionalInfo}
</div>
)}
</section>
)}
{/* Call to Action */}
{content.registrationEnabled && (
<section className="flex flex-col items-center justify-center space-y-8 pt-12 border-t border-gray-100">
<div className="text-center space-y-2">
<h2 className="text-4xl font-bold">Vill du delta?</h2>
<p className="text-gray-600 text-lg">
Begränsade platser, Säkra din plats idag!
</p>
</div>
<Link
href="/register"
className="bg-blue-600 hover:bg-blue-700 text-white text-xl font-bold py-4 px-12 rounded-full shadow-lg transform transition hover:scale-105 active:scale-95"
>
Anmäl dig nu
</Link>
</section>
{content.whatToBring && (
<section className="space-y-6">
<h2 className="text-3xl font-bold border-b-2 border-blue-500 pb-2 inline-block">
Ta Med(bokad LAN-plats)
</h2>
<ul className="list-disc list-inside space-y-3 text-lg text-gray-700">
{content.whatToBring
.split("\n")
.filter((line) => line.trim())
.map((line, i) => (
<li key={i}>{line}</li>
))}
</ul>
</section>
)}
{content.rulesAndGdpr && (
<section className="space-y-6">
<h2 className="text-3xl font-bold border-b-2 border-blue-500 pb-2 inline-block">
Regler för LAN & GDPR
</h2>
<p className="text-lg text-gray-700 leading-relaxed whitespace-pre-wrap">
{content.rulesAndGdpr}
</p>
</section>
)}
{/* Call to Action */}
{content.registrationEnabled && (
<section className="flex flex-col items-center justify-center space-y-8 pt-12 border-t border-gray-100">
<div className="text-center space-y-2">
<h2 className="text-4xl font-bold">Vill du delta?</h2>
<p className="text-gray-600 text-lg">
Begränsade platser, Säkra din plats idag!
</p>
</div>
<Link
href="/register"
className="bg-blue-600 hover:bg-blue-700 text-white text-xl font-bold py-4 px-12 rounded-full shadow-lg transform transition hover:scale-105 active:scale-95"
>
Anmäl dig nu
</Link>
</section>
)}
</>
)}
</main>
{/* Footer */}
<footer className="bg-gray-50 py-12 text-center text-gray-500 border-t border-gray-100 relative">
<div className="mb-8">
<p className="text-gray-600 mb-4">Vill du hjälpa till?</p>
<Link
href="/volunteer"
className="inline-block bg-gray-800 hover:bg-black text-white font-bold py-2 px-6 rounded-md transition"
>
Bli Funktionär
</Link>
</div>
{(content.volunteerRegistrationEnabled ?? true) && (
<div className="mb-8">
<p className="text-gray-600 mb-4">Vill du hjälpa till?</p>
<Link
href="/volunteer"
className="inline-block bg-gray-800 hover:bg-black text-white font-bold py-2 px-6 rounded-md transition"
>
Bli Funktionär
</Link>
</div>
)}
<p>© 2026 Vbytes Gaming. Alla rättigheter förbehållna.</p>
</footer>
</div>

View file

@ -169,10 +169,6 @@ export default function RegisterPage() {
"Vårdnadshavarens e-postadress är ogiltig.";
}
if (formData.email.trim() && !isValidEmail(formData.email.trim())) {
nextFieldErrors.email = "Din e-postadress är ogiltig.";
}
if (!formData.hasApprovedGdpr) {
nextFieldErrors.hasApprovedGdpr = "Du måste godkänna GDPR för att anmäla dig.";
}
@ -240,7 +236,9 @@ export default function RegisterPage() {
if (response.ok) {
setMessage({
type: "success",
text: "Registreringen är klar! Du är nu anmäld till LAN:et.",
text: isMember
? "Registreringen är klar! Du är nu anmäld till LAN:et."
: "Din anmälan är mottagen, men det återstår ett viktigt steg för att du ska få delta.",
});
setShowBecomeMemberCta(!isMember);
@ -300,19 +298,22 @@ export default function RegisterPage() {
{message.type === "success" && (
<div className="mb-8 space-y-6">
<div className="p-6 rounded-lg text-center bg-green-50 text-green-800 border border-green-200">
<div className="text-4xl mb-4">🎉</div>
<h2 className="text-xl font-bold mb-2">Klart!</h2>
<div className="text-4xl mb-4">{showBecomeMemberCta ? "⏳" : "🎉"}</div>
<h2 className="text-xl font-bold mb-2">
{showBecomeMemberCta ? "Nästan Klart!" : "Klart!"}
</h2>
<p>{message.text}</p>
</div>
{showBecomeMemberCta && (
<div className="bg-gray-50 p-6 rounded-lg border border-gray-200">
<div className="bg-gray-50 p-6 rounded-lg border border-orange-200">
<h3 className="text-lg font-semibold text-gray-900 mb-2">
Du är inte medlem än
</h3>
<p className="text-gray-600 mb-4">
Vi hittade dig inte i vårt medlemsregister. Genom att bli
medlem i vBytes hjälper du oss att anordna fler och bättre
Vi hittade dig inte i vårt medlemsregister. För att kunna
delta LAN:et måste du vara medlem i vBytes. Medlemskapet är
gratis och hjälper oss dessutom att anordna fler och bättre
LAN!
</p>
<a
@ -470,7 +471,7 @@ export default function RegisterPage() {
E-post
</label>
<input
type="email"
type="text"
name="email"
id="email"
className={`mt-1 block w-full rounded-md shadow-sm sm:text-sm p-2 border text-gray-900 ${fieldErrors.email ? "border-red-500 focus:border-red-500 focus:ring-red-500" : "border-gray-300 focus:border-blue-500 focus:ring-blue-500"}`}
@ -659,7 +660,7 @@ export default function RegisterPage() {
</div>
</div>
{message.text && (
{message.text && message.type !== "success" && (
<div
className={`p-4 rounded-md ${
message.type === "success"

View file

@ -6,6 +6,7 @@ import { useRouter } from "next/navigation";
interface EventContent {
volunteerAreas: string;
volunteerRegistrationEnabled?: boolean;
}
const isValidEmail = (value: string) =>
@ -42,6 +43,27 @@ export default function VolunteerPage() {
.then((data) => setContent(data))
.catch((err) => console.error("Failed to fetch content", err));
}, []);
if (content && content.volunteerRegistrationEnabled === false) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
<div className="max-w-md w-full bg-white p-8 rounded-xl shadow-md text-center">
<h1 className="text-2xl font-bold text-gray-900 mb-4">
Anmälan är stängd
</h1>
<p className="text-gray-600 mb-6">
Vi tar tyvärr inte emot fler funktionärsanmälningar just nu.
</p>
<button
onClick={() => router.push("/")}
className="text-blue-600 hover:underline"
>
tillbaka till startsidan
</button>
</div>
</div>
);
}
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target;