Merge version_2 into main #1
368
src/app/order/page.tsx
Normal file
368
src/app/order/page.tsx
Normal file
@@ -0,0 +1,368 @@
|
||||
"use client";
|
||||
|
||||
import ButtonHoverBubble from "@/components/button/ButtonHoverBubble";
|
||||
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
|
||||
import FooterCard from "@/components/sections/footer/FooterCard";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { CheckCircle, XCircle } from "lucide-react";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import ReactLenis from "lenis/react";
|
||||
|
||||
interface Wilaya {
|
||||
code: string;
|
||||
name: string;
|
||||
communes: string[];
|
||||
}
|
||||
|
||||
const AlgerianWilayas: Wilaya[] = [
|
||||
{ "code": "01", "name": "Adrar", "communes": ["Adrar", "Tamest", "Reggane", "Bourg Haouas"] },
|
||||
{ "code": "02", "name": "Chlef", "communes": ["Chlef", "El Karimia", "Ouled Fares", "Ténès"] },
|
||||
{ "code": "03", "name": "Laghouat", "communes": ["Laghouat", "Aflou", "Ksar El Hirane", "Brida"] },
|
||||
{ "code": "04", "name": "Oum El Bouaghi", "communes": ["Oum El Bouaghi", "Ain Beida", "Ain M'lila", "Meskhiana"] },
|
||||
{ "code": "05", "name": "Batna", "communes": ["Batna", "Barika", "Arris", "Menaa"] },
|
||||
{ "code": "06", "name": "Béjaïa", "communes": ["Béjaïa", "Akbou", "Amizour", "Kherrata"] },
|
||||
{ "code": "07", "name": "Biskra", "communes": ["Biskra", "Ouled Djellal", "Tolga", "Sidi Okba"] },
|
||||
{ "code": "08", "name": "Béchar", "communes": ["Béchar", "Abadla", "Taghit", "Béni Ounif"] },
|
||||
{ "code": "09", "name": "Blida", "communes": ["Blida", "Boufarik", "Meftah", "Larbaa"] },
|
||||
{ "code": "10", "name": "Bouira", "communes": ["Bouira", "Lakhdaria", "Sour El Ghozlane", "Ain Bessem"] },
|
||||
{ "code": "11", "name": "Tamanrasset", "communes": ["Tamanrasset", "In Salah", "In Guezzam", "Idlès"] },
|
||||
{ "code": "12", "name": "Tébessa", "communes": ["Tébessa", "Bir El Ater", "Ouenza", "El Ma Labiodh"] },
|
||||
{ "code": "13", "name": "Tlemcen", "communes": ["Tlemcen", "Ghazaouet", "Maghnia", "Remchi"] },
|
||||
{ "code": "14", "name": "Tiaret", "communes": ["Tiaret", "Frenda", "Mahdia", "Rahouia"] },
|
||||
{ "code": "15", "name": "Tizi Ouzou", "communes": ["Tizi Ouzou", "Larbaâ Nath Irathen", "Azazga", "Bouzeguene"] },
|
||||
{ "code": "16", "name": "Alger", "communes": ["Alger Centre", "Bab El Oued", "Bir Mourad Raïs", "Hussein Dey"] },
|
||||
{ "code": "17", "name": "Djelfa", "communes": ["Djelfa", "Messaad", "Ain Oussara", "Hassi Bahbah"] },
|
||||
{ "code": "18", "name": "Jijel", "communes": ["Jijel", "Taher", "El Milia", "Chekfa"] },
|
||||
{ "code": "19", "name": "Sétif", "communes": ["Sétif", "El Eulma", "Ain Oulmene", "Bougaa"] },
|
||||
{ "code": "20", "name": "Saïda", "communes": ["Saïda", "Ain El Hadjar", "Sidi Boubkeur", "Youb"] },
|
||||
{ "code": "21", "name": "Skikda", "communes": ["Skikda", "Collo", "Azzaba", "El Harrouch"] },
|
||||
{ "code": "22", "name": "Sidi Bel Abbès", "communes": ["Sidi Bel Abbès", "Sfisef", "Telagh", "Ras El Ma"] },
|
||||
{ "code": "23", "name": "Annaba", "communes": ["Annaba", "El Bouni", "Sidi Amar", "Berrahal"] },
|
||||
{ "code": "24", "name": "Guelma", "communes": ["Guelma", "Hammam Debagh", "Ain Makhlouf", "Oued Zenati"] },
|
||||
{ "code": "25", "name": "Constantine", "communes": ["Constantine", "El Khroub", "Hamma Bouziane", "Didouche Mourad"] },
|
||||
{ "code": "26", "name": "Médéa", "communes": ["Médéa", "Berrouaghia", "Ksar Boukhari", "Chahbounia"] },
|
||||
{ "code": "27", "name": "Mostaganem", "communes": ["Mostaganem", "Ain Tedles", "Hassi Mameche", "Bouguirat"] },
|
||||
{ "code": "28", "name": "M'Sila", "communes": ["M'Sila", "Bou Saada", "Magra", "Sidi Aïssa"] },
|
||||
{ "code": "29", "name": "Mascara", "communes": ["Mascara", "Mohammadia", "Sig", "Bou Hanifia"] },
|
||||
{ "code": "30", "name": "Ouargla", "communes": ["Ouargla", "Touggourt", "Hassi Messaoud", "N'Goussa"] },
|
||||
{ "code": "31", "name": "Oran", "communes": ["Oran", "Es Sénia", "Bir El Djir", "Arzew"] },
|
||||
{ "code": "32", "name": "El Bayadh", "communes": ["El Bayadh", "Bougtoub", "Brezina", "Chellala"] },
|
||||
{ "code": "33", "name": "Illizi", "communes": ["Illizi", "Djanet", "Bordj Omar Driss", "Debdeb"] },
|
||||
{ "code": "34", "name": "Bordj Bou Arréridj", "communes": ["Bordj Bou Arréridj", "Ras El Oued", "Ain Taghrout", "El Hamadia"] },
|
||||
{ "code": "35", "name": "Boumerdès", "communes": ["Boumerdès", "Dellys", "Boudouaou", "Khemis El Khechna"] },
|
||||
{ "code": "36", "name": "El Tarf", "communes": ["El Tarf", "El Kala", "Ben M'hidi", "Drean"] },
|
||||
{ "code": "37", "name": "Tindouf", "communes": ["Tindouf", "Oum El Assel"] },
|
||||
{ "code": "38", "name": "Tissemsilt", "communes": ["Tissemsilt", "Théniet El Had", "Lardjem", "Bordj Bounaama"] },
|
||||
{ "code": "39", "name": "El Oued", "communes": ["El Oued", "Guemar", "Robbah", "Hassani Abdelkrim"] },
|
||||
{ "code": "40", "name": "Khenchela", "communes": ["Khenchela", "Chechar", "Kais", "Djellal"] },
|
||||
{ "code": "41", "name": "Souk Ahras", "communes": ["Souk Ahras", "Sedrata", "Taoura", "Merahna"] },
|
||||
{ "code": "42", "name": "Tipaza", "communes": ["Tipaza", "Cherchell", "Hadjout", "Kolea"] },
|
||||
{ "code": "43", "name": "Mila", "communes": ["Mila", "Chelghoum Laid", "Grarem Gouga", "Rouached"] },
|
||||
{ "code": "44", "name": "Aïn Defla", "communes": ["Aïn Defla", "Khemis Miliana", "El Attaf", "Miliana"] },
|
||||
{ "code": "45", "name": "Naâma", "communes": ["Naâma", "Mécheria", "Ain Sefra", "Sfissifa"] },
|
||||
{ "code": "46", "name": "Aïn Témouchent", "communes": ["Aïn Témouchent", "Hammam Bou Hadjar", "Beni Saf", "El Amria"] },
|
||||
{ "code": "47", "name": "Ghardaïa", "communes": ["Ghardaïa", "El Guerrara", "Bounoura", "Daya Ben Dahoua"] },
|
||||
{ "code": "48", "name": "Relizane", "communes": ["Relizane", "Oued Rhiou", "Mazouna", "Zemmoura"] },
|
||||
{ "code": "49", "name": "El M'ghair", "communes": ["El M'ghair", "Djamaa", "M'ghair", "Sidi Khelil"] },
|
||||
{ "code": "50", "name": "El Menia", "communes": ["El Menia", "Hassi Gara", "Hassi Fehal"] },
|
||||
{ "code": "51", "name": "Ouled Djellal", "communes": ["Ouled Djellal", "Sidi Khaled", "Besbes", "Ras El Mia"] },
|
||||
{ "code": "52", "name": "Bordj Baji Mokhtar", "communes": ["Bordj Baji Mokhtar", "Timiaouine"] },
|
||||
{ "code": "53", "name": "Béni Abbès", "communes": ["Béni Abbès", "Kerzaz", "Tabelbala", "Ouled Khoudir"] },
|
||||
{ "code": "54", "name": "Timimoun", "communes": ["Timimoun", "Aougrout", "Charouin", "Deladla"] },
|
||||
{ "code": "55", "name": "Touggourt", "communes": ["Touggourt", "Temacine", "Blidet Amor", "M'Rara"] },
|
||||
{ "code": "56", "name": "Djanet", "communes": ["Djanet", "Bordj El Houasse"] },
|
||||
{ "code": "57", "name": "In Salah", "communes": ["In Salah", "Foggaret Ezzoua", "Ain Ghar"] },
|
||||
{ "code": "58", "name": "In Guezzam", "communes": ["In Guezzam", "Tin Zaouatine"] }
|
||||
];
|
||||
|
||||
interface OrderFormData {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
phoneNumber: string;
|
||||
wilaya: string;
|
||||
commune: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
const OrderFormPage = () => {
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [phoneNumber, setPhoneNumber] = useState("");
|
||||
const [wilaya, setWilaya] = useState("");
|
||||
const [commune, setCommune] = useState(""); const [quantity, setQuantity] = useState(1);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [submissionStatus, setSubmissionStatus] = useState<"idle" | "success" | "error">("idle");
|
||||
|
||||
const availableCommunes = wilaya
|
||||
? AlgerianWilayas.find((w) => w.name === wilaya)?.communes || []
|
||||
: [];
|
||||
|
||||
useEffect(() => {
|
||||
// Reset commune when wilaya changes
|
||||
setCommune("");
|
||||
}, [wilaya]);
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
if (!firstName) newErrors.firstName = "First Name is required.";
|
||||
if (!lastName) newErrors.lastName = "Last Name is required.";
|
||||
// Algerian phone number validation: starts with 05, 06, or 07, followed by 8 digits
|
||||
if (!phoneNumber) {
|
||||
newErrors.phoneNumber = "Phone Number is required.";
|
||||
} else if (!/^(05|06|07)\d{8}$/.test(phoneNumber)) {
|
||||
newErrors.phoneNumber = "Invalid Algerian phone number (e.g., 05XXXXXXXX).";
|
||||
}
|
||||
if (!wilaya) newErrors.wilaya = "Wilaya is required.";
|
||||
if (!commune) newErrors.commune = "Commune is required.";
|
||||
if (quantity < 1) newErrors.quantity = "Quantity must be at least 1.";
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSubmissionStatus("idle");
|
||||
|
||||
if (validateForm()) {
|
||||
const formData: OrderFormData = {
|
||||
firstName,
|
||||
lastName,
|
||||
phoneNumber,
|
||||
wilaya,
|
||||
commune,
|
||||
quantity,
|
||||
};
|
||||
console.log("Form data submitted:", formData);
|
||||
|
||||
// Simulate backend saving and success animation
|
||||
setTimeout(() => {
|
||||
try {
|
||||
localStorage.setItem("latestOrder", JSON.stringify(formData));
|
||||
setSubmissionStatus("success");
|
||||
// Reset form
|
||||
setFirstName("");
|
||||
setLastName("");
|
||||
setPhoneNumber("");
|
||||
setWilaya("");
|
||||
setCommune("");
|
||||
setQuantity(1);
|
||||
setErrors({});
|
||||
} catch (error) {
|
||||
console.error("Error saving to local storage:", error);
|
||||
setSubmissionStatus("error");
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
setSubmissionStatus("error"); // Indicate form validation failed
|
||||
}
|
||||
};
|
||||
|
||||
const navItems = [
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Features", id: "/#features" },
|
||||
{ name: "Products", id: "/#products" },
|
||||
{ name: "Testimonials", id: "/#testimonials" },
|
||||
{ name: "FAQs", id: "/#faqs" },
|
||||
{ name: "Order Now", id: "/order" },
|
||||
];
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="text-shift"
|
||||
defaultTextAnimation="background-highlight"
|
||||
borderRadius="soft"
|
||||
contentWidth="mediumLarge"
|
||||
sizing="mediumLargeSizeMediumTitles"
|
||||
background="aurora"
|
||||
cardStyle="gradient-bordered"
|
||||
primaryButtonStyle="primary-glow"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="extrabold"
|
||||
>
|
||||
<ReactLenis root>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple navItems={navItems} brandName="Product Platform" />
|
||||
</div>
|
||||
|
||||
<div className="relative isolate py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-xl text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
||||
Place Your Order
|
||||
</h2>
|
||||
<p className="mt-2 text-lg leading-8 text-foreground/70">
|
||||
Fill out the form below to complete your purchase.
|
||||
</p>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} className="mx-auto mt-16 max-w-xl sm:mt-20">
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-6 sm:grid-cols-2">
|
||||
<div>
|
||||
<label htmlFor="first-name" className="block text-sm font-semibold leading-6 text-foreground">
|
||||
First name
|
||||
</label>
|
||||
<div className="mt-2.5">
|
||||
<input
|
||||
type="text"
|
||||
name="first-name"
|
||||
id="first-name"
|
||||
autoComplete="given-name"
|
||||
className="block w-full rounded-md border-0 bg-background/50 px-3.5 py-2 text-foreground shadow-sm ring-1 ring-inset ring-foreground/20 placeholder:text-foreground/50 focus:ring-2 focus:ring-inset focus:ring-primary-cta sm:text-sm sm:leading-6"
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
/>
|
||||
{errors.firstName && <p className="mt-1 text-xs text-red-500">{errors.firstName}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="last-name" className="block text-sm font-semibold leading-6 text-foreground">
|
||||
Last name
|
||||
</label>
|
||||
<div className="mt-2.5">
|
||||
<input
|
||||
type="text"
|
||||
name="last-name"
|
||||
id="last-name"
|
||||
autoComplete="family-name"
|
||||
className="block w-full rounded-md border-0 bg-background/50 px-3.5 py-2 text-foreground shadow-sm ring-1 ring-inset ring-foreground/20 placeholder:text-foreground/50 focus:ring-2 focus:ring-inset focus:ring-primary-cta sm:text-sm sm:leading-6"
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
/>
|
||||
{errors.lastName && <p className="mt-1 text-xs text-red-500">{errors.lastName}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="phone-number" className="block text-sm font-semibold leading-6 text-foreground">
|
||||
Phone number (Algeria)
|
||||
</label>
|
||||
<div className="mt-2.5">
|
||||
<input
|
||||
type="tel"
|
||||
name="phone-number"
|
||||
id="phone-number"
|
||||
autoComplete="tel"
|
||||
className="block w-full rounded-md border-0 bg-background/50 px-3.5 py-2 text-foreground shadow-sm ring-1 ring-inset ring-foreground/20 placeholder:text-foreground/50 focus:ring-2 focus:ring-inset focus:ring-primary-cta sm:text-sm sm:leading-6"
|
||||
placeholder="e.g., 05XXXXXXXX, 06XXXXXXXX, 07XXXXXXXX"
|
||||
value={phoneNumber}
|
||||
onChange={(e) => setPhoneNumber(e.target.value)}
|
||||
/>
|
||||
{errors.phoneNumber && <p className="mt-1 text-xs text-red-500">{errors.phoneNumber}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="wilaya" className="block text-sm font-semibold leading-6 text-foreground">
|
||||
Wilaya
|
||||
</label>
|
||||
<div className="mt-2.5">
|
||||
<select
|
||||
id="wilaya"
|
||||
name="wilaya"
|
||||
className="block w-full rounded-md border-0 bg-background/50 px-3.5 py-2 text-foreground shadow-sm ring-1 ring-inset ring-foreground/20 focus:ring-2 focus:ring-inset focus:ring-primary-cta sm:text-sm sm:leading-6"
|
||||
value={wilaya}
|
||||
onChange={(e) => setWilaya(e.target.value)}
|
||||
>
|
||||
<option value="">Select a Wilaya</option>
|
||||
{AlgerianWilayas.map((w) => (
|
||||
<option key={w.code} value={w.name}>
|
||||
{w.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.wilaya && <p className="mt-1 text-xs text-red-500">{errors.wilaya}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="commune" className="block text-sm font-semibold leading-6 text-foreground">
|
||||
Commune
|
||||
</label>
|
||||
<div className="mt-2.5">
|
||||
<select
|
||||
id="commune"
|
||||
name="commune"
|
||||
className="block w-full rounded-md border-0 bg-background/50 px-3.5 py-2 text-foreground shadow-sm ring-1 ring-inset ring-foreground/20 focus:ring-2 focus:ring-inset focus:ring-primary-cta sm:text-sm sm:leading-6"
|
||||
value={commune}
|
||||
onChange={(e) => setCommune(e.target.value)}
|
||||
disabled={!wilaya}
|
||||
>
|
||||
<option value="">Select a Commune</option>
|
||||
{availableCommunes.map((c) => (
|
||||
<option key={c} value={c}>
|
||||
{c}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.commune && <p className="mt-1 text-xs text-red-500">{errors.commune}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="quantity" className="block text-sm font-semibold leading-6 text-foreground">
|
||||
Quantity
|
||||
</label>
|
||||
<div className="mt-2.5">
|
||||
<input
|
||||
type="number"
|
||||
name="quantity"
|
||||
id="quantity"
|
||||
min="1"
|
||||
className="block w-full rounded-md border-0 bg-background/50 px-3.5 py-2 text-foreground shadow-sm ring-1 ring-inset ring-foreground/20 placeholder:text-foreground/50 focus:ring-2 focus:ring-inset focus:ring-primary-cta sm:text-sm sm:leading-6"
|
||||
value={quantity}
|
||||
onChange={(e) => setQuantity(Math.max(1, parseInt(e.target.value) || 1))}
|
||||
/>
|
||||
{errors.quantity && <p className="mt-1 text-xs text-red-500">{errors.quantity}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-10">
|
||||
<ButtonHoverBubble
|
||||
text="Submit Order"
|
||||
type="submit"
|
||||
className="w-full justify-center"
|
||||
bgClassName="bg-primary-cta text-primary-cta-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{submissionStatus === "success" && (
|
||||
<div className="mt-4 flex items-center justify-center gap-2 rounded-md bg-green-500/10 p-3 text-green-600 animate-in fade-in duration-500">
|
||||
<CheckCircle className="h-5 w-5" />
|
||||
<p className="text-sm font-medium">Order placed successfully!</p>
|
||||
</div>
|
||||
)}
|
||||
{submissionStatus === "error" && Object.keys(errors).length > 0 && (
|
||||
<div className="mt-4 flex items-center justify-center gap-2 rounded-md bg-red-500/10 p-3 text-red-600 animate-in fade-in duration-500">
|
||||
<XCircle className="h-5 w-5" />
|
||||
<p className="text-sm font-medium">Please correct the errors above.</p>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sticky 'Order Now' button for mobile */}
|
||||
<div className="fixed bottom-4 left-0 right-0 z-50 px-4 md:hidden">
|
||||
<ButtonHoverBubble
|
||||
text="Order Now"
|
||||
href="/order"
|
||||
className="w-full justify-center"
|
||||
bgClassName="bg-primary-cta text-primary-cta-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterCard
|
||||
logoText="Product Platform"
|
||||
copyrightText="© 2024 Product Platform. All rights reserved."
|
||||
socialLinks={[
|
||||
{ icon: XCircle, href: "#", ariaLabel: "Twitter" },
|
||||
{ icon: CheckCircle, href: "#", ariaLabel: "Facebook" },
|
||||
{ icon: XCircle, href: "#", ariaLabel: "Instagram" },
|
||||
]} // Temporarily replaced with XCircle/CheckCircle as Facebook/Instagram/Twitter not imported in this specific file
|
||||
/>
|
||||
</div>
|
||||
</ReactLenis>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const metadata = {
|
||||
title: "Place Your Order - Product Platform", description: "Complete your order with our secure and easy-to-use order form. Select your wilaya and commune for quick delivery."};
|
||||
|
||||
export default OrderFormPage;
|
||||
Reference in New Issue
Block a user