From 8354631fc0af5184ace31dd379934e94c59039b6 Mon Sep 17 00:00:00 2001
From: "ALMAZROUEI Shamma (2021) WKIS203"
 <shamma.almazrouei.2021@live.rhul.ac.uk>
Date: Tue, 11 Mar 2025 17:23:48 +0530
Subject: [PATCH] Build the contact page

---
 golden-crust-bakery/package-lock.json     |  43 +++
 golden-crust-bakery/package.json          |   2 +
 golden-crust-bakery/src/main.jsx          |  10 +-
 golden-crust-bakery/src/pages/Contact.jsx | 381 ++++++++++++++++++++++
 4 files changed, 431 insertions(+), 5 deletions(-)
 create mode 100644 golden-crust-bakery/src/pages/Contact.jsx

diff --git a/golden-crust-bakery/package-lock.json b/golden-crust-bakery/package-lock.json
index 5b8061a..9d36fc4 100644
--- a/golden-crust-bakery/package-lock.json
+++ b/golden-crust-bakery/package-lock.json
@@ -19,8 +19,10 @@
         "clsx": "^2.1.1",
         "lucide-react": "^0.479.0",
         "next-themes": "^0.4.5",
+        "pigeon-maps": "^0.22.1",
         "react": "^19.0.0",
         "react-dom": "^19.0.0",
+        "react-leaflet": "^5.0.0-rc.2",
         "react-router-dom": "^7.3.0",
         "sonner": "^2.0.1",
         "tailwind-merge": "^3.0.2",
@@ -1891,6 +1893,17 @@
       "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
       "license": "MIT"
     },
+    "node_modules/@react-leaflet/core": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz",
+      "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==",
+      "license": "Hippocratic-2.1",
+      "peerDependencies": {
+        "leaflet": "^1.9.0",
+        "react": "^19.0.0",
+        "react-dom": "^19.0.0"
+      }
+    },
     "node_modules/@rollup/rollup-android-arm-eabi": {
       "version": "4.35.0",
       "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz",
@@ -3480,6 +3493,13 @@
         "json-buffer": "3.0.1"
       }
     },
+    "node_modules/leaflet": {
+      "version": "1.9.4",
+      "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
+      "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
+      "license": "BSD-2-Clause",
+      "peer": true
+    },
     "node_modules/levn": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -3838,6 +3858,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/pigeon-maps": {
+      "version": "0.22.1",
+      "resolved": "https://registry.npmjs.org/pigeon-maps/-/pigeon-maps-0.22.1.tgz",
+      "integrity": "sha512-mVWxgpIyhAekITeJvDGlFAo/Ytm6Fg7prpDiAuQ0Z6cJ/LwpnS8F0sF+0TDqyJu7C/DDHupkstFTNoIRqhdP5A==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": "*"
+      }
+    },
     "node_modules/pirates": {
       "version": "4.0.6",
       "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
@@ -4051,6 +4080,20 @@
         "react": "^19.0.0"
       }
     },
+    "node_modules/react-leaflet": {
+      "version": "5.0.0-rc.2",
+      "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0-rc.2.tgz",
+      "integrity": "sha512-1xQGYG9mEIW+nfkQhqgHImwUuB1UDlnzYFSzv6PrBFDBeYrFmv0BbpwpNAFdJg/UQ2yz5UZSL7ZwlUxjwb8MZw==",
+      "license": "Hippocratic-2.1",
+      "dependencies": {
+        "@react-leaflet/core": "^3.0.0-rc.2"
+      },
+      "peerDependencies": {
+        "leaflet": "^1.9.0",
+        "react": "^19.0.0",
+        "react-dom": "^19.0.0"
+      }
+    },
     "node_modules/react-refresh": {
       "version": "0.14.2",
       "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
diff --git a/golden-crust-bakery/package.json b/golden-crust-bakery/package.json
index e9b80ea..d63182e 100644
--- a/golden-crust-bakery/package.json
+++ b/golden-crust-bakery/package.json
@@ -21,8 +21,10 @@
     "clsx": "^2.1.1",
     "lucide-react": "^0.479.0",
     "next-themes": "^0.4.5",
+    "pigeon-maps": "^0.22.1",
     "react": "^19.0.0",
     "react-dom": "^19.0.0",
+    "react-leaflet": "^5.0.0-rc.2",
     "react-router-dom": "^7.3.0",
     "sonner": "^2.0.1",
     "tailwind-merge": "^3.0.2",
diff --git a/golden-crust-bakery/src/main.jsx b/golden-crust-bakery/src/main.jsx
index 4a44fcb..42b3ab9 100644
--- a/golden-crust-bakery/src/main.jsx
+++ b/golden-crust-bakery/src/main.jsx
@@ -10,7 +10,7 @@ import './index.css';
 import App from './App.jsx';
 import Landing from './pages/Landing';
 import About from './pages/About';
-// import Contact from './pages/Contact';
+import Contact from './pages/Contact';
 import Login from './pages/Login';
 import Register from './pages/Register';
 
@@ -27,10 +27,10 @@ const router = createBrowserRouter([
         path: '/about',
         element: <About />,
       },
-      // {
-      //   path: '/contact',
-      //   element: <Contact />,
-      // },
+      {
+        path: '/contact',
+        element: <Contact />,
+      },
       {
         path: '/login',
         element: <Login />,
diff --git a/golden-crust-bakery/src/pages/Contact.jsx b/golden-crust-bakery/src/pages/Contact.jsx
new file mode 100644
index 0000000..a7d34b5
--- /dev/null
+++ b/golden-crust-bakery/src/pages/Contact.jsx
@@ -0,0 +1,381 @@
+import { useState } from 'react';
+import { toast } from 'sonner';
+import { Clock, Mail, MapPin, Phone, Send } from 'lucide-react';
+
+import { Map, Marker } from 'pigeon-maps';
+
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Textarea } from '@/components/ui/textarea';
+import {
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from '@/components/ui/select';
+import {
+  Accordion,
+  AccordionContent,
+  AccordionItem,
+  AccordionTrigger,
+} from '@/components/ui/accordion';
+
+export default function Contact() {
+  const [formData, setFormData] = useState({
+    name: '',
+    email: '',
+    phone: '',
+    subject: '',
+    message: '',
+  });
+
+  const [errors, setErrors] = useState({});
+  const [isSubmitting, setIsSubmitting] = useState(false);
+
+  const handleChange = (e) => {
+    const { name, value } = e.target;
+    setFormData((prev) => ({
+      ...prev,
+      [name]: value,
+    }));
+
+    // Clear error when field is being edited
+    if (errors[name]) {
+      setErrors((prev) => ({
+        ...prev,
+        [name]: '',
+      }));
+    }
+  };
+
+  const handleSelectChange = (value) => {
+    setFormData((prev) => ({
+      ...prev,
+      subject: value,
+    }));
+
+    if (errors.subject) {
+      setErrors((prev) => ({
+        ...prev,
+        subject: '',
+      }));
+    }
+  };
+
+  const validateForm = () => {
+    const newErrors = {};
+
+    if (!formData.name.trim()) newErrors.name = 'Name is required';
+    if (!formData.email.trim()) {
+      newErrors.email = 'Email is required';
+    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
+      newErrors.email = 'Email is invalid';
+    }
+    if (!formData.subject) newErrors.subject = 'Subject is required';
+    if (!formData.message.trim()) newErrors.message = 'Message is required';
+
+    setErrors(newErrors);
+    return Object.keys(newErrors).length === 0;
+  };
+
+  const handleSubmit = (e) => {
+    e.preventDefault();
+
+    if (!validateForm()) return;
+
+    setIsSubmitting(true);
+
+    setTimeout(() => {
+      setIsSubmitting(false);
+
+      toast({
+        title: 'Message Sent!',
+        description: "Thank you for contacting us. We'll get back to you soon.",
+      });
+
+      setFormData({
+        name: '',
+        email: '',
+        phone: '',
+        subject: '',
+        message: '',
+      });
+    }, 1500);
+  };
+
+  return (
+    <div className='container mx-auto px-4 py-8 md:py-12'>
+      <div className='text-center mb-12'>
+        <h1 className='text-4xl font-bold text-gray-800 mb-4'>Contact Us</h1>
+        <p className='text-gray-600 max-w-2xl mx-auto'>
+          Have questions or special requests? We're here to help! Get in touch
+          with our team.
+        </p>
+      </div>
+
+      <div className='grid grid-cols-1 lg:grid-cols-2 gap-12'>
+        <div>
+          <div className='bg-white rounded-xl border shadow-sm overflow-hidden mb-8'>
+            <div className='p-6'>
+              <h2 className='text-2xl font-bold text-gray-800 mb-6'>
+                Send Us a Message
+              </h2>
+
+              <form onSubmit={handleSubmit} className='space-y-6'>
+                <div className='space-y-2'>
+                  <Label htmlFor='name'>Full Name</Label>
+                  <Input
+                    id='name'
+                    name='name'
+                    placeholder='Peter Parker'
+                    value={formData.name}
+                    onChange={handleChange}
+                    className={`rounded-lg ${
+                      errors.name ? 'border-red-500' : 'border-gray-200'
+                    }`}
+                  />
+                  {errors.name && (
+                    <p className='text-red-500 text-sm'>{errors.name}</p>
+                  )}
+                </div>
+
+                <div className='space-y-2'>
+                  <Label htmlFor='email'>Email Address</Label>
+                  <Input
+                    id='email'
+                    name='email'
+                    type='email'
+                    placeholder='yourname@email.com'
+                    value={formData.email}
+                    onChange={handleChange}
+                    className={`rounded-lg ${
+                      errors.email ? 'border-red-500' : 'border-gray-200'
+                    }`}
+                  />
+                  {errors.email && (
+                    <p className='text-red-500 text-sm'>{errors.email}</p>
+                  )}
+                </div>
+
+                <div className='space-y-2'>
+                  <Label htmlFor='phone'>Phone Number (Optional)</Label>
+                  <Input
+                    id='phone'
+                    name='phone'
+                    placeholder='(+44) 8765432109'
+                    value={formData.phone}
+                    onChange={handleChange}
+                    className='rounded-lg border-gray-200'
+                  />
+                </div>
+
+                <div className='space-y-2'>
+                  <Label htmlFor='subject'>Subject</Label>
+                  <Select
+                    value={formData.subject}
+                    onValueChange={handleSelectChange}
+                  >
+                    <SelectTrigger
+                      className={`rounded-lg ${
+                        errors.subject ? 'border-red-500' : 'border-gray-200'
+                      }`}
+                    >
+                      <SelectValue placeholder='Select a subject' />
+                    </SelectTrigger>
+                    <SelectContent>
+                      <SelectItem value='general'>General Inquiry</SelectItem>
+                      <SelectItem value='order'>Order Question</SelectItem>
+                      <SelectItem value='custom'>
+                        Custom Cake Request
+                      </SelectItem>
+                      <SelectItem value='feedback'>Feedback</SelectItem>
+                      <SelectItem value='other'>Other</SelectItem>
+                    </SelectContent>
+                  </Select>
+                  {errors.subject && (
+                    <p className='text-red-500 text-sm'>{errors.subject}</p>
+                  )}
+                </div>
+
+                <div className='space-y-2'>
+                  <Label htmlFor='message'>Your Message</Label>
+                  <Textarea
+                    id='message'
+                    name='message'
+                    placeholder='Your message here...'
+                    value={formData.message}
+                    onChange={handleChange}
+                    className={`rounded-lg min-h-[150px] ${
+                      errors.message ? 'border-red-500' : 'border-gray-200'
+                    }`}
+                  />
+                  {errors.message && (
+                    <p className='text-red-500 text-sm'>{errors.message}</p>
+                  )}
+                </div>
+
+                <Button
+                  type='submit'
+                  disabled={isSubmitting}
+                  className='w-full bg-pink-500 hover:bg-pink-600 text-white rounded-full py-6'
+                >
+                  {isSubmitting ? (
+                    'Sending...'
+                  ) : (
+                    <>
+                      <Send className='mr-2 h-4 w-4' /> Send Message
+                    </>
+                  )}
+                </Button>
+              </form>
+            </div>
+          </div>
+        </div>
+
+        <div>
+          <div className='bg-white rounded-xl shadow-sm border overflow-hidden mb-8'>
+            <div className='p-6'>
+              <h2 className='text-2xl font-bold text-gray-800 mb-6'>
+                Contact Information
+              </h2>
+
+              <div className='space-y-6'>
+                <div className='flex items-start'>
+                  <div className='mr-4'>
+                    <div className='w-10 h-10 bg-pink-100 rounded-full flex items-center justify-center'>
+                      <MapPin className='h-5 w-5 text-pink-600' />
+                    </div>
+                  </div>
+                  <div>
+                    <h3 className='font-semibold text-gray-800 mb-1'>
+                      Our Location
+                    </h3>
+                    <p className='text-gray-600'>
+                      Golden Crust Bakery, Bakery Street, London, UK
+                    </p>
+                  </div>
+                </div>
+
+                <div className='flex items-start'>
+                  <div className='mr-4'>
+                    <div className='w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center'>
+                      <Phone className='h-5 w-5 text-blue-600' />
+                    </div>
+                  </div>
+                  <div>
+                    <h3 className='font-semibold text-gray-800 mb-1'>
+                      Phone Number
+                    </h3>
+                    <p className='text-gray-600'>(+44) 8765432109</p>
+                  </div>
+                </div>
+
+                <div className='flex items-start'>
+                  <div className='mr-4'>
+                    <div className='w-10 h-10 bg-yellow-100 rounded-full flex items-center justify-center'>
+                      <Mail className='h-5 w-5 text-yellow-600' />
+                    </div>
+                  </div>
+                  <div>
+                    <h3 className='font-semibold text-gray-800 mb-1'>
+                      Email Address
+                    </h3>
+                    <p className='text-gray-600'>info@goldencrust.com</p>
+                  </div>
+                </div>
+
+                <div className='flex items-start'>
+                  <div className='mr-4'>
+                    <div className='w-10 h-10 bg-green-100 rounded-full flex items-center justify-center'>
+                      <Clock className='h-5 w-5 text-green-600' />
+                    </div>
+                  </div>
+                  <div>
+                    <h3 className='font-semibold text-gray-800 mb-1'>
+                      Business Hours
+                    </h3>
+                    <p className='text-gray-600'>
+                      Everyday - 11:00 AM - 7:00 PM
+                    </p>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div className='bg-white rounded-xl shadow-sm border overflow-hidden'>
+            <div className='p-6'>
+              <h2 className='text-2xl font-bold text-gray-800 mb-6'>
+                Frequently Asked Questions
+              </h2>
+
+              <div className='space-y-4'>
+                <Accordion type='single' collapsible>
+                  <AccordionItem value='item-1'>
+                    <AccordionTrigger>
+                      How far in advance should I order a cake?
+                    </AccordionTrigger>
+                    <AccordionContent>
+                      We recommend placing your order at least 3-5 days in
+                      advance for standard cakes, and 2-3 weeks for custom or
+                      wedding cakes.
+                    </AccordionContent>
+                  </AccordionItem>
+
+                  <AccordionItem value='item-2'>
+                    <AccordionTrigger>Do you offer delivery?</AccordionTrigger>
+                    <AccordionContent>
+                      Yes, we offer delivery within a 15-mile radius of our
+                      bakery for a small fee. For larger orders or wedding
+                      cakes, delivery is available at special rates.
+                    </AccordionContent>
+                  </AccordionItem>
+
+                  <AccordionItem value='item-3'>
+                    <AccordionTrigger>
+                      Can you accommodate dietary restrictions?
+                    </AccordionTrigger>
+                    <AccordionContent>
+                      We offer gluten-free and vegan options for many of our
+                      cakes. Please contact us directly to discuss your specific
+                      dietary needs.
+                    </AccordionContent>
+                  </AccordionItem>
+
+                  <AccordionItem value='item-4'>
+                    <AccordionTrigger>
+                      How do I place a custom cake order?
+                    </AccordionTrigger>
+                    <AccordionContent>
+                      You can contact us through this form, call us, or visit
+                      our bakery in person to discuss your custom cake
+                      requirements.
+                    </AccordionContent>
+                  </AccordionItem>
+                </Accordion>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Map Section */}
+      <div className='mt-12 bg-white rounded-xl shadow-md overflow-hidden'>
+        <div className='p-6'>
+          <h2 className='text-2xl font-bold text-gray-800 mb-6 text-center'>
+            Find Us
+          </h2>
+          <div className='relative h-[400px] bg-gray-200 rounded-lg'>
+            <div className='absolute inset-0 flex items-center justify-center rounded-lg overflow-hidden'>
+              <Map defaultCenter={[51.5072, 0.1276]} defaultZoom={16}>
+                <Marker width={50} anchor={[51.5072, 0.1276]} />
+              </Map>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}
-- 
GitLab