Zastosowanie wszystkich poprawek UI i logiki kalkulatora
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
'use client';
|
||||
|
||||
import { useCartStore } from '@/lib/cart-store';
|
||||
import { useActionState, useEffect, startTransition } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { submitOrderForm } from './actions';
|
||||
import { ArrowLeft, CheckCircle2 } from 'lucide-react';
|
||||
import { buttonClasses } from '@/components/ui/Button';
|
||||
import { getRecaptchaToken } from '@/lib/getRecaptchaToken';
|
||||
|
||||
|
||||
const initialState = { success: false, message: '' };
|
||||
|
||||
export default function ZamowieniePage() {
|
||||
const { items, getTotal, clearCart } = useCartStore();
|
||||
const [state, formAction, pending] = useActionState(submitOrderForm, initialState);
|
||||
const total = getTotal();
|
||||
|
||||
useEffect(() => {
|
||||
if (state.success) clearCart();
|
||||
}, [state.success, clearCart]);
|
||||
|
||||
if (items.length === 0 && !state.success) {
|
||||
return (
|
||||
<main className="min-h-screen bg-slate-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<p className="text-text-muted mb-4">Koszyk jest pusty.</p>
|
||||
<Link href="/sklep" className={buttonClasses({ variant: 'primary', size: 'md' })}>
|
||||
Wróć do sklepu
|
||||
</Link>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
if (state.success) {
|
||||
return (
|
||||
<main className="min-h-screen bg-slate-50 flex items-center justify-center py-16">
|
||||
<div className="max-w-md mx-auto px-4 w-full">
|
||||
<div className="bg-green-50 text-green-800 p-8 rounded-2xl border border-green-200 flex flex-col items-center text-center">
|
||||
<CheckCircle2 className="w-16 h-16 text-green-500 mb-4" />
|
||||
<h3 className="text-2xl font-bold font-heading mb-2">Zamówienie przyjęte!</h3>
|
||||
<p className="text-green-700">{state.message}</p>
|
||||
<Link href="/sklep" className="mt-6 px-6 py-2 bg-green-600 text-white rounded-lg font-semibold hover:bg-green-700 transition-colors">
|
||||
Wróć do sklepu
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="bg-slate-50 min-h-screen py-8">
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<Link href="/koszyk" className="inline-flex items-center gap-2 text-sm text-text-muted hover:text-accent mb-6">
|
||||
<ArrowLeft className="size-4" /> Wróć do koszyka
|
||||
</Link>
|
||||
|
||||
<h1 className="text-2xl font-bold text-primary font-heading mb-8">Złóż zamówienie</h1>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
{/* Podsumowanie produktów */}
|
||||
<div className="bg-white rounded-xl border border-border p-6">
|
||||
<h2 className="text-base font-bold text-primary font-heading mb-4">Zamawiane produkty</h2>
|
||||
<div className="flex flex-col gap-2">
|
||||
{items.map((item) => (
|
||||
<div key={item.id} className="flex justify-between text-sm">
|
||||
<span className="text-text-muted">{item.name} × {item.quantity}</span>
|
||||
<span className="font-semibold text-primary">{(item.price * item.quantity).toLocaleString('pl-PL')} zł</span>
|
||||
</div>
|
||||
))}
|
||||
<div className="border-t border-border pt-2 mt-2 flex justify-between font-bold text-primary">
|
||||
<span>Łącznie (urządzenia)</span>
|
||||
<span>{total.toLocaleString('pl-PL')} zł</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Formularz */}
|
||||
<form onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const token = await getRecaptchaToken('submit');
|
||||
if (token) {
|
||||
formData.append('recaptchaToken', token);
|
||||
}
|
||||
startTransition(() => {
|
||||
formAction(formData);
|
||||
});
|
||||
}} className="bg-white rounded-xl border border-border p-6 flex flex-col gap-4">
|
||||
<h2 className="text-base font-bold text-primary font-heading">Twoje dane</h2>
|
||||
|
||||
{/* Hidden: produkty jako JSON */}
|
||||
<input type="hidden" name="products" value={JSON.stringify(items)} />
|
||||
<input type="hidden" name="total" value={total} />
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-xs font-medium text-primary mb-1 block">Imię i nazwisko *</label>
|
||||
<input name="name" required className="w-full border border-border rounded-lg px-3 py-2.5 text-sm focus:border-accent outline-none" placeholder="Jan Kowalski" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs font-medium text-primary mb-1 block">Telefon *</label>
|
||||
<input name="phone" required type="tel" className="w-full border border-border rounded-lg px-3 py-2.5 text-sm focus:border-accent outline-none" placeholder="+48 600 000 000" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-xs font-medium text-primary mb-1 block">Email *</label>
|
||||
<input name="email" required type="email" className="w-full border border-border rounded-lg px-3 py-2.5 text-sm focus:border-accent outline-none" placeholder="[email protected]" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-xs font-medium text-primary mb-1 block">Adres dostawy / montażu</label>
|
||||
<input name="address" className="w-full border border-border rounded-lg px-3 py-2.5 text-sm focus:border-accent outline-none" placeholder="ul. Przykładowa 1, 50-000 Wrocław" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-xs font-medium text-primary mb-1 block">Uwagi do zamówienia</label>
|
||||
<textarea name="notes" rows={3} className="w-full border border-border rounded-lg px-3 py-2.5 text-sm focus:border-accent outline-none resize-none" placeholder="Np. preferowany termin montażu, piętro, specjalne wymagania..." />
|
||||
</div>
|
||||
|
||||
<label className="flex items-start gap-3 cursor-pointer">
|
||||
<input type="checkbox" name="consent" required className="mt-0.5 w-5 h-5 accent-accent shrink-0 cursor-pointer" />
|
||||
<span className="text-xs text-text-muted">Wyrażam zgodę na przetwarzanie danych osobowych w celu realizacji zamówienia zgodnie z Polityką Prywatności. *</span>
|
||||
</label>
|
||||
|
||||
{state.message && !state.success && (
|
||||
<p className="text-sm text-red-500">{state.message}</p>
|
||||
)}
|
||||
|
||||
<button type="submit" disabled={pending} className="btn btn-primary text-white font-bold w-full flex items-center justify-center gap-2">
|
||||
{pending ? 'Wysyłanie...' : 'Wyślij zamówienie'}
|
||||
</button>
|
||||
|
||||
<p className="text-xs text-text-muted text-center">
|
||||
Po złożeniu zamówienia skontaktujemy się z Tobą telefonicznie w ciągu 24 godzin, aby potwierdzić szczegóły i ustalić termin montażu.
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user