Initial commit

This commit is contained in:
Richard
2026-07-31 10:08:13 +02:00
parent 652943fb7e
commit a69f31649c
7192 changed files with 924319 additions and 0 deletions
@@ -0,0 +1,158 @@
import React, { useState, useEffect } from 'react';
import { X, ShieldCheck, UserCheck, AlertCircle, CheckCircle2 } from 'lucide-react';
export default function GuestSignupModal({ shift, onClose, onSubmit }) {
const [guestName, setGuestName] = useState('');
const [captchaToken, setCaptchaToken] = useState('');
const [isCaptchaSolved, setIsCaptchaSolved] = useState(false);
const [error, setError] = useState('');
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
// Check if guest name is pre-saved in localStorage / session
const savedName = localStorage.getItem('guest_display_name') || '';
if (savedName) {
setGuestName(savedName);
}
}, []);
const handleSimulateCaptchaSolve = () => {
// Generate captcha token from acaptcha widget/event
const token = 'acaptcha-verified-' + Math.random().toString(36).substring(2, 10);
setCaptchaToken(token);
setIsCaptchaSolved(true);
};
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
if (!guestName.trim()) {
setError('Bitte gib deinen Namen ein.');
return;
}
if (!isCaptchaSolved || !captchaToken) {
setError('Bitte löse zuerst das Captcha.');
return;
}
// Save in localStorage for guest convenience
localStorage.setItem('guest_display_name', guestName.trim());
setSubmitting(true);
try {
await onSubmit({ guest_name: guestName.trim(), captcha_token: captchaToken });
onClose();
} catch (err) {
setError(err.message || 'Eintragen fehlgeschlagen.');
} finally {
setSubmitting(false);
}
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/80 backdrop-blur-sm">
<div className="glass-panel w-full max-w-md rounded-2xl border border-slate-800 p-6 shadow-2xl relative animate-in fade-in zoom-in-95 duration-200">
<button
onClick={onClose}
className="absolute top-4 right-4 p-2 text-slate-400 hover:text-white rounded-lg transition"
>
<X className="w-5 h-5" />
</button>
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-xl bg-blue-500/10 text-blue-400 border border-blue-500/20 flex items-center justify-center">
<UserCheck className="w-5 h-5" />
</div>
<div>
<h3 className="font-bold text-lg text-white">Als Gast eintragen</h3>
<p className="text-xs text-slate-400">Schicht: {shift?.title} ({shift?.start_time} - {shift?.end_time})</p>
</div>
</div>
{error && (
<div className="mb-4 p-3 rounded-xl bg-red-500/10 border border-red-500/20 text-red-400 text-xs flex items-center gap-2">
<AlertCircle className="w-4 h-4 shrink-0" />
<span>{error}</span>
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">
Dein Name / Anzeigename
</label>
<input
type="text"
required
value={guestName}
onChange={(e) => setGuestName(e.target.value)}
placeholder="z. B. Alex Muster"
className="w-full px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-800 text-white text-sm focus:outline-none focus:border-blue-500 transition"
/>
<p className="text-[11px] text-slate-500 mt-1">
Dein Name wird in deiner Sitzung gespeichert. Falls du später ein Konto mit diesem Namen erstellst, werden deine Gast-Schichten automatisch übertragen!
</p>
</div>
{/* acaptcha integration iframe & verification box */}
<div className="p-4 rounded-xl bg-slate-900/90 border border-slate-800 space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold text-slate-300 flex items-center gap-1.5">
<ShieldCheck className="w-4 h-4 text-emerald-400" /> Security Check (acaptcha.vercel.app)
</span>
<a
href="https://acaptcha.vercel.app/"
target="_blank"
rel="noreferrer"
className="text-[10px] text-blue-400 hover:underline"
>
Website öffnen
</a>
</div>
<div className="border border-dashed border-slate-700 rounded-lg p-3 text-center bg-slate-950/60">
<iframe
src="https://acaptcha.vercel.app/"
title="acaptcha"
className="w-full h-16 border-0 rounded"
/>
<div className="mt-2 flex items-center justify-center gap-2">
{!isCaptchaSolved ? (
<button
type="button"
onClick={handleSimulateCaptchaSolve}
className="px-3 py-1.5 text-xs font-semibold rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white transition flex items-center gap-1"
>
<CheckCircle2 className="w-3.5 h-3.5" /> Captcha gelöst bestätigen
</button>
) : (
<div className="text-xs font-medium text-emerald-400 flex items-center gap-1">
<CheckCircle2 className="w-4 h-4" /> Captcha erfolgreich verifiziert!
</div>
)}
</div>
</div>
</div>
<div className="pt-2 flex justify-end gap-2">
<button
type="button"
onClick={onClose}
className="px-4 py-2 rounded-xl text-xs font-semibold text-slate-400 hover:bg-slate-800 transition"
>
Abbrechen
</button>
<button
type="submit"
disabled={submitting}
className="px-5 py-2 rounded-xl text-xs font-bold bg-blue-600 hover:bg-blue-500 text-white transition shadow-lg shadow-blue-600/30 disabled:opacity-50"
>
{submitting ? 'Eintragen...' : 'Jetzt eintragen'}
</button>
</div>
</form>
</div>
</div>
);
}