From c9f6c9b97b76c0358dc629cf8e73414bd536be58 Mon Sep 17 00:00:00 2001
From: "ALMAZROUEI Shamma (2021) WKIS203"
 <shamma.almazrouei.2021@live.rhul.ac.uk>
Date: Wed, 12 Mar 2025 17:43:08 +0530
Subject: [PATCH] Build orders page

---
 golden-crust-bakery/package-lock.json         |  29 ++
 golden-crust-bakery/package.json              |   1 +
 .../src/components/ui/alert-dialog.jsx        |  97 ++++++
 golden-crust-bakery/src/main.jsx              |   5 +
 golden-crust-bakery/src/pages/Orders.jsx      | 276 ++++++++++++++++++
 5 files changed, 408 insertions(+)
 create mode 100644 golden-crust-bakery/src/components/ui/alert-dialog.jsx
 create mode 100644 golden-crust-bakery/src/pages/Orders.jsx

diff --git a/golden-crust-bakery/package-lock.json b/golden-crust-bakery/package-lock.json
index 9d36fc4..87b1ca4 100644
--- a/golden-crust-bakery/package-lock.json
+++ b/golden-crust-bakery/package-lock.json
@@ -9,6 +9,7 @@
       "version": "0.0.0",
       "dependencies": {
         "@radix-ui/react-accordion": "^1.2.3",
+        "@radix-ui/react-alert-dialog": "^1.1.6",
         "@radix-ui/react-checkbox": "^1.1.4",
         "@radix-ui/react-dialog": "^1.1.6",
         "@radix-ui/react-dropdown-menu": "^2.1.6",
@@ -1185,6 +1186,34 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-alert-dialog": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.6.tgz",
+      "integrity": "sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/primitive": "1.1.1",
+        "@radix-ui/react-compose-refs": "1.1.1",
+        "@radix-ui/react-context": "1.1.1",
+        "@radix-ui/react-dialog": "1.1.6",
+        "@radix-ui/react-primitive": "2.0.2",
+        "@radix-ui/react-slot": "1.1.2"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-arrow": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz",
diff --git a/golden-crust-bakery/package.json b/golden-crust-bakery/package.json
index d63182e..45050f1 100644
--- a/golden-crust-bakery/package.json
+++ b/golden-crust-bakery/package.json
@@ -11,6 +11,7 @@
   },
   "dependencies": {
     "@radix-ui/react-accordion": "^1.2.3",
+    "@radix-ui/react-alert-dialog": "^1.1.6",
     "@radix-ui/react-checkbox": "^1.1.4",
     "@radix-ui/react-dialog": "^1.1.6",
     "@radix-ui/react-dropdown-menu": "^2.1.6",
diff --git a/golden-crust-bakery/src/components/ui/alert-dialog.jsx b/golden-crust-bakery/src/components/ui/alert-dialog.jsx
new file mode 100644
index 0000000..a4174f3
--- /dev/null
+++ b/golden-crust-bakery/src/components/ui/alert-dialog.jsx
@@ -0,0 +1,97 @@
+import * as React from "react"
+import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
+
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
+
+const AlertDialog = AlertDialogPrimitive.Root
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger
+
+const AlertDialogPortal = AlertDialogPrimitive.Portal
+
+const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
+  <AlertDialogPrimitive.Overlay
+    className={cn(
+      "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
+      className
+    )}
+    {...props}
+    ref={ref} />
+))
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
+
+const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (
+  <AlertDialogPortal>
+    <AlertDialogOverlay />
+    <AlertDialogPrimitive.Content
+      ref={ref}
+      className={cn(
+        "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
+        className
+      )}
+      {...props} />
+  </AlertDialogPortal>
+))
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
+
+const AlertDialogHeader = ({
+  className,
+  ...props
+}) => (
+  <div
+    className={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
+    {...props} />
+)
+AlertDialogHeader.displayName = "AlertDialogHeader"
+
+const AlertDialogFooter = ({
+  className,
+  ...props
+}) => (
+  <div
+    className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
+    {...props} />
+)
+AlertDialogFooter.displayName = "AlertDialogFooter"
+
+const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (
+  <AlertDialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold", className)} {...props} />
+))
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
+
+const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (
+  <AlertDialogPrimitive.Description
+    ref={ref}
+    className={cn("text-sm text-muted-foreground", className)}
+    {...props} />
+))
+AlertDialogDescription.displayName =
+  AlertDialogPrimitive.Description.displayName
+
+const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (
+  <AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
+))
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
+
+const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (
+  <AlertDialogPrimitive.Cancel
+    ref={ref}
+    className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
+    {...props} />
+))
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
+
+export {
+  AlertDialog,
+  AlertDialogPortal,
+  AlertDialogOverlay,
+  AlertDialogTrigger,
+  AlertDialogContent,
+  AlertDialogHeader,
+  AlertDialogFooter,
+  AlertDialogTitle,
+  AlertDialogDescription,
+  AlertDialogAction,
+  AlertDialogCancel,
+}
diff --git a/golden-crust-bakery/src/main.jsx b/golden-crust-bakery/src/main.jsx
index f7c36cc..aa26fb2 100644
--- a/golden-crust-bakery/src/main.jsx
+++ b/golden-crust-bakery/src/main.jsx
@@ -17,6 +17,7 @@ import Menu from './pages/Menu';
 import MenuItem from './pages/MenuItem';
 import ErrorPage from './pages/Error';
 import Cart from './pages/Cart';
+import Orders from './pages/Orders';
 
 const router = createBrowserRouter([
   {
@@ -48,6 +49,10 @@ const router = createBrowserRouter([
         path: '/cart',
         element: <Cart />,
       },
+      {
+        path: '/orders',
+        element: <Orders />,
+      },
       {
         path: '/menu',
         element: <Menu />,
diff --git a/golden-crust-bakery/src/pages/Orders.jsx b/golden-crust-bakery/src/pages/Orders.jsx
new file mode 100644
index 0000000..17d5886
--- /dev/null
+++ b/golden-crust-bakery/src/pages/Orders.jsx
@@ -0,0 +1,276 @@
+import { useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { toast } from 'sonner';
+import { Home, Package, ShoppingBag, X } from 'lucide-react';
+
+import { useOrderStore } from '@/lib/orderStore';
+import { useAuthStore } from '@/lib/authStore';
+
+import { Button } from '@/components/ui/button';
+import {
+  Accordion,
+  AccordionContent,
+  AccordionItem,
+  AccordionTrigger,
+} from '@/components/ui/accordion';
+import {
+  AlertDialog,
+  AlertDialogAction,
+  AlertDialogCancel,
+  AlertDialogContent,
+  AlertDialogDescription,
+  AlertDialogFooter,
+  AlertDialogHeader,
+  AlertDialogTitle,
+  AlertDialogTrigger,
+} from '@/components/ui/alert-dialog';
+
+export default function Orders() {
+  const navigate = useNavigate();
+  const { orders, cancelOrder, initializeOrders } = useOrderStore();
+  const { isAuthenticated } = useAuthStore();
+
+  useEffect(() => {
+    initializeOrders();
+
+    // Redirect to login if not authenticated
+    if (!isAuthenticated) {
+      toast({
+        title: 'Authentication Required',
+        description: 'Please sign in to view your orders.',
+        variant: 'destructive',
+      });
+      navigate('/login');
+    }
+  }, [initializeOrders, isAuthenticated, navigate]);
+
+  // If not authenticated, don't render the orders content
+  if (!isAuthenticated) {
+    return null;
+  }
+
+  const handleCancelOrder = (orderId) => {
+    cancelOrder(orderId);
+    toast({
+      title: 'Order Cancelled',
+      description: 'Your order has been cancelled successfully.',
+    });
+  };
+
+  const getStatusColor = (status) => {
+    switch (status) {
+      case 'processing':
+        return 'bg-yellow-100 text-yellow-800';
+      case 'shipped':
+        return 'bg-blue-100 text-blue-800';
+      case 'delivered':
+        return 'bg-green-100 text-green-800';
+      case 'cancelled':
+        return 'bg-red-100 text-red-800';
+      default:
+        return 'bg-gray-100 text-gray-800';
+    }
+  };
+
+  return (
+    <div className='container mx-auto px-4 py-8 md:py-12'>
+      <div className='text-center mb-8'>
+        <h1 className='text-4xl font-bold text-gray-800 mb-4'>My Orders</h1>
+        <p className='text-gray-600'>
+          {orders.length > 0
+            ? `You have ${orders.length} order${orders.length !== 1 ? 's' : ''}`
+            : 'You have no orders yet'}
+        </p>
+      </div>
+
+      {orders.length > 0 ? (
+        <div className='max-w-4xl mx-auto'>
+          <Accordion type='single' collapsible className='space-y-4'>
+            {orders.map((order) => (
+              <AccordionItem
+                key={order.id}
+                value={order.id}
+                className='bg-white rounded-xl shadow-md overflow-hidden border-none'
+              >
+                <AccordionTrigger className='px-6 py-4 hover:no-underline hover:bg-gray-50'>
+                  <div className='flex flex-col md:flex-row w-full items-start md:items-center justify-between text-left'>
+                    <div>
+                      <p className='font-semibold text-gray-900'>
+                        Order #{order.id.split('-')[1]}
+                      </p>
+                      <p className='text-sm text-gray-500'>
+                        {new Date(order.date).toLocaleDateString()}
+                      </p>
+                    </div>
+                    <div className='flex items-center mt-2 md:mt-0'>
+                      <div
+                        className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(
+                          order.status
+                        )} mr-4`}
+                      >
+                        {order.status.charAt(0).toUpperCase() +
+                          order.status.slice(1)}
+                      </div>
+                      <p className='font-medium text-pink-600'>
+                        ${order.total.toFixed(2)}
+                      </p>
+                    </div>
+                  </div>
+                </AccordionTrigger>
+                <AccordionContent className='px-6 pb-6'>
+                  <div className='border-t border-gray-200 pt-4'>
+                    <div className='space-y-4'>
+                      <div>
+                        <h3 className='text-sm font-medium text-gray-800 mb-2'>
+                          Items Ordered:
+                        </h3>
+                        <ul className='divide-y divide-gray-200'>
+                          {order.items.map((item) => (
+                            <li
+                              key={item.id}
+                              className='py-3 flex justify-between'
+                            >
+                              <div>
+                                <p className='text-sm font-medium text-gray-900'>
+                                  {item.name}
+                                </p>
+                                <p className='text-sm text-gray-500'>
+                                  Qty: {item.quantity}
+                                </p>
+                              </div>
+                              <p className='text-sm font-medium text-gray-900'>
+                                ${(item.price * item.quantity).toFixed(2)}
+                              </p>
+                            </li>
+                          ))}
+                        </ul>
+                      </div>
+
+                      <div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
+                        <div>
+                          <h3 className='text-sm font-medium text-gray-800 mb-2'>
+                            Delivery Information:
+                          </h3>
+                          <p className='text-sm text-gray-600'>
+                            {order.customer.name}
+                          </p>
+                          <p className='text-sm text-gray-600'>
+                            {order.customer.address}
+                          </p>
+                          <p className='text-sm text-gray-600'>
+                            {order.customer.city}, {order.customer.zipCode}
+                          </p>
+                        </div>
+
+                        <div>
+                          <h3 className='text-sm font-medium text-gray-800 mb-2'>
+                            Order Summary:
+                          </h3>
+                          <div className='space-y-1'>
+                            <div className='flex justify-between'>
+                              <p className='text-sm text-gray-600'>Subtotal</p>
+                              <p className='text-sm text-gray-900'>
+                                ${order.subtotal.toFixed(2)}
+                              </p>
+                            </div>
+                            <div className='flex justify-between'>
+                              <p className='text-sm text-gray-600'>Tax</p>
+                              <p className='text-sm text-gray-900'>
+                                ${order.tax.toFixed(2)}
+                              </p>
+                            </div>
+                            <div className='flex justify-between'>
+                              <p className='text-sm text-gray-600'>
+                                Delivery Fee
+                              </p>
+                              <p className='text-sm text-gray-900'>
+                                ${order.deliveryFee.toFixed(2)}
+                              </p>
+                            </div>
+                            <div className='flex justify-between border-t border-gray-200 pt-1 mt-1'>
+                              <p className='text-sm font-medium text-gray-900'>
+                                Total
+                              </p>
+                              <p className='text-sm font-medium text-pink-600'>
+                                ${order.total.toFixed(2)}
+                              </p>
+                            </div>
+                          </div>
+                        </div>
+                      </div>
+
+                      <div className='flex justify-end mt-4'>
+                        {order.status !== 'cancelled' && (
+                          <AlertDialog>
+                            <AlertDialogTrigger asChild>
+                              <Button
+                                variant='outline'
+                                className='border-red-300 text-red-600 hover:bg-red-50 rounded-full'
+                              >
+                                <X className='mr-2 h-4 w-4' /> Cancel Order
+                              </Button>
+                            </AlertDialogTrigger>
+                            <AlertDialogContent>
+                              <AlertDialogHeader>
+                                <AlertDialogTitle>
+                                  Cancel Order
+                                </AlertDialogTitle>
+                                <AlertDialogDescription>
+                                  Are you sure you want to cancel this order?
+                                  This action cannot be undone.
+                                </AlertDialogDescription>
+                              </AlertDialogHeader>
+                              <AlertDialogFooter>
+                                <AlertDialogCancel>
+                                  No, Keep Order
+                                </AlertDialogCancel>
+                                <AlertDialogAction
+                                  onClick={() => handleCancelOrder(order.id)}
+                                  className='bg-red-500 hover:bg-red-600 text-white'
+                                >
+                                  Yes, Cancel Order
+                                </AlertDialogAction>
+                              </AlertDialogFooter>
+                            </AlertDialogContent>
+                          </AlertDialog>
+                        )}
+                      </div>
+                    </div>
+                  </div>
+                </AccordionContent>
+              </AccordionItem>
+            ))}
+          </Accordion>
+        </div>
+      ) : (
+        <div className='text-center py-12'>
+          <div className='mx-auto w-24 h-24 bg-pink-100 rounded-full flex items-center justify-center mb-6'>
+            <Package className='h-12 w-12 text-pink-500' />
+          </div>
+          <h2 className='text-2xl font-semibold text-gray-800 mb-4'>
+            No orders yet
+          </h2>
+          <p className='text-gray-600 mb-8'>
+            You haven't placed any orders with us yet.
+          </p>
+          <Button
+            onClick={() => navigate('/menu')}
+            className='bg-pink-500 hover:bg-pink-600 text-white rounded-full px-8 py-6 text-lg'
+          >
+            <ShoppingBag className='mr-2 h-5 w-5' /> Start Shopping
+          </Button>
+        </div>
+      )}
+
+      <div className='text-center mt-8'>
+        <Button
+          variant='outline'
+          onClick={() => navigate('/')}
+          className='border-pink-300 text-pink-600 hover:bg-pink-50 rounded-full'
+        >
+          <Home className='mr-2 h-4 w-4' /> Return to Home
+        </Button>
+      </div>
+    </div>
+  );
+}
-- 
GitLab