In admin panel, create orders page where I can the carts...
cart = order.
it should include the images of the products and the price of its sizes.. they can update the status of the product o pending/onway/ordered or they just can remove the order...
They should be able to see all the details of the order in a nice way...
Give me the full code of the OrdersPage
Using nextjs 14 app router.
cartRoutes.ts:
// routes/cartRoutes.ts
import express from 'express';
import { body } from 'express-validator';
import { validateRequest } from '../middleware/validateRequest';
import {deleteCart, getAllCarts, updateCart} from "../controllers/cartControllers";

const router = express.Router();


// Admin routes
router.get('/', getAllCarts);

router.put(
    '/carts/:id',
    [
        body('status').isString().withMessage('Status is required'),
    ],
    validateRequest,
    updateCart
);

router.delete('/carts/:id', deleteCart);

export default router;

cartController.ts:
import { Request, Response, NextFunction } from 'express';
import {deleteCartById, getAllCartsWithItems, updateCartStatus} from "../models/cartModel";


export async function getAllCarts(req: Request, res: Response, next: NextFunction) {
    try {
        const carts = await getAllCartsWithItems();
        res.json(carts);
    } catch (error) {
        next(error);
    }
}

export async function updateCart(req: Request, res: Response, next: NextFunction) {
    try {
        const cartId = parseInt(req.params.id, 10);
        const { status } = req.body;

        const updatedCart = await updateCartStatus(cartId, status);

        if (!updatedCart) {
            return res.status(404).json({ message: 'Cart not found' });
        }

        res.json({ message: 'Cart status updated successfully', cart: updatedCart });
    } catch (error) {
        next(error);
    }
}

export async function deleteCart(req: Request, res: Response, next: NextFunction) {
    try {
        const cartId = parseInt(req.params.id, 10);

        const deleted = await deleteCartById(cartId);

        if (!deleted) {
            return res.status(404).json({ message: 'Cart not found' });
        }

        res.json({ message: 'Cart deleted successfully' });
    } catch (error) {
        next(error);
    }
}

cartModel.ts:
// models/cartModel.ts
import { query } from '../config/database';

export interface Cart {
    id: number;
    user_id?: number | null;
    status: string;
    notes?: string;
    full_name?: string;
    governorate?: string;
    city?: string;
    address?: string;
    closest_point?: string;
    phone_number?: string;
    type?: string;
    created_at: Date;
    coupon_id?: number | null;
}

export interface CartItem {
    id: number;
    cart_id: number;
    product_id: number;
    size_id?: number | null;
    color_id?: number | null;
    quantity: number;
}

export interface CartWithItems extends Cart {
    items: CartItem[];
}

export async function getAllCartsWithItems(): Promise<CartWithItems[]> {
    try {
        const cartResult = await query<Cart>('SELECT * FROM carts ORDER BY created_at DESC');

        const carts = cartResult.rows;

        const cartsWithItems: CartWithItems[] = [];

        for (const cart of carts) {
            const itemsResult = await query<CartItem>(
                'SELECT * FROM cart_items WHERE cart_id = $1',
                [cart.id]
            );

            cartsWithItems.push({
                ...cart,
                items: itemsResult.rows,
            });
        }

        return cartsWithItems;
    } catch (error) {
        console.error('Error fetching carts with items:', error);
        throw error;
    }
}

export async function updateCartStatus(cartId: number, status: string): Promise<Cart | null> {
    try {
        const result = await query<Cart>(
            'UPDATE carts SET status = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 RETURNING *',
            [status, cartId]
        );

        return result.rows[0] || null;
    } catch (error) {
        console.error('Error updating cart status:', error);
        throw error;
    }
}

export async function deleteCartById(cartId: number): Promise<boolean> {
    try {
        await query('BEGIN');

        // Delete cart items
        await query('DELETE FROM cart_items WHERE cart_id = $1', [cartId]);

        // Delete the cart
        const result = await query('DELETE FROM carts WHERE id = $1 RETURNING *', [cartId]);

        await query('COMMIT');

        return result.rows.length > 0;
    } catch (error) {
        await query('ROLLBACK');
        console.error('Error deleting cart:', error);
        throw error;
    }
}


export const getProducts = async (req: Request, res: Response) => {
    try {
        const page = parseInt(req.query.page as string) || 1;
        const limit = 10;
        const offset = (page - 1) * limit;
        const search = (req.query.search as string || '').toLowerCase();
        const categoryId = req.query.c as string;
        const subcategoryId = req.query.sc as string;
        const brandId = req.query.b as string;


        const conditions = [];
        const queryParams: any[] = [];

        if (search) {
            const searchWords = search.split(/\s+/).filter(Boolean);
            const searchConditions = searchWords.map((word) => {
                queryParams.push(`${word}%`, `%${word}%`);
                return `(LOWER(p.name) LIKE $${queryParams.length - 1} OR LOWER(p.name) LIKE $${queryParams.length})`;
            });
            conditions.push(`(${searchConditions.join(' OR ')})`);
        }

        if (categoryId) {
            queryParams.push(categoryId);
            conditions.push(`(c.id = $${queryParams.length} OR EXISTS (
                SELECT 1 FROM subcategories sub 
                WHERE sub.category_id = $${queryParams.length} 
                AND (sub.id = sc.id OR sub.id = p.brand_id)
            ))`);
        }

        if (subcategoryId) {
            queryParams.push(subcategoryId);
            conditions.push(`(sc.id = $${queryParams.length} OR p.brand_id = $${queryParams.length})`);
        }

        if (brandId) {
            queryParams.push(brandId);
            conditions.push(`p.brand_id = $${queryParams.length}`);
        }

        const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';

        // Add search, limit, and offset to queryParams
        queryParams.push(search, limit, offset);

        const productQuery = `
            WITH filtered_products AS (
                SELECT p.*, 
                       sc.id AS subcategory_id, sc.name AS subcategory_name,
                       c.id AS main_category_id, c.name AS main_category_name,
                       COUNT(*) OVER() AS total_count
                FROM products p
                LEFT JOIN product_categories pc ON p.id = pc.product_id
                LEFT JOIN subcategories sc ON pc.category_id = sc.id
                LEFT JOIN categories c ON sc.category_id = c.id
                ${whereClause}
                ORDER BY 
                    CASE 
                        WHEN $${queryParams.length - 2} != '' THEN 
                            CASE 
                                WHEN LOWER(p.name) LIKE LOWER($${queryParams.length - 2} || '%') THEN 1
                                WHEN LOWER(p.name) LIKE '%' || LOWER($${queryParams.length - 2}) || '%' THEN 2
                                ELSE 3
                            END
                        ELSE 0
                    END,
                    p.created_at DESC
            )
            SELECT 
                fp.*,
                (SELECT jsonb_agg(jsonb_build_object(
                    'id', c.id, 'name', c.name, 'price', c.price, 'qty', c.qty,
                    'images', (SELECT jsonb_agg(i.url) FROM images i WHERE i.color_id = c.id),
                    'sizes', (SELECT jsonb_agg(jsonb_build_object(
                        'id', s.id, 'name', s.name, 'price', s.price, 'qty', s.qty,
                        'images', (SELECT jsonb_agg(i.url) FROM images i WHERE i.size_id = s.id)
                    )) FROM sizes s WHERE s.color_id = c.id)
                )) FROM colors c WHERE c.product_id = fp.id) AS colors,
                (SELECT jsonb_agg(jsonb_build_object(
                    'id', s.id, 'name', s.name, 'price', s.price, 'qty', s.qty, 'color_id', s.color_id,
                    'images', (SELECT jsonb_agg(i.url) FROM images i WHERE i.size_id = s.id)
                )) FROM sizes s WHERE s.product_id = fp.id AND s.color_id IS NULL) AS sizes,
                (SELECT jsonb_agg(i.url) FROM images i WHERE i.product_id = fp.id AND i.color_id IS NULL AND i.size_id IS NULL) AS images
            FROM filtered_products fp
            LIMIT $${queryParams.length - 1}::integer OFFSET $${queryParams.length}::integer
        `;

        const productResult = await query(productQuery, queryParams);

        const totalProducts = parseInt(productResult.rows[0]?.total_count || '0');
        const totalPages = Math.ceil(totalProducts / limit);

        const products = productResult.rows.map(product => ({
            id: product.id,
            name: product.name,
            description: product.description,
            brand_id: product.brand_id,
            has_color: product.has_color,
            has_size: product.has_size,
            product_type: product.product_type,
            price: product.price,
            qty: product.qty,
            created_at: product.created_at,
            updated_at: product.updated_at,
            categories: [
                {
                    id: product.subcategory_id,
                    name: product.subcategory_name,
                    main_category_id: product.main_category_id,
                    main_category_name: product.main_category_name
                }
            ],
            colors: product.colors || [],
            sizes: product.sizes || [],
            images: [
                ...(product.images || []),
                ...(product.colors || []).flatMap((c: { images: any; }) => c.images || []),
                ...(product.sizes || []).flatMap((s: { images: any; }) => s.images || [])
            ]
        }));

        const result = {
            products,
            pagination: {
                currentPage: page,
                totalPages,
                totalProducts,
                limit
            }
        };

        res.json(result);
    } catch (error) {
        console.error('Error fetching products:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
};

The api to access products: https://api2.naayiq.com/products
The api to access the cart: https://api2.naayiq.com/cart

users/page.jsx:
"use client";
import React, { useState, useEffect, useCallback } from "react";
import DefaultLayout from "@/components/Layouts/DefaultLayout";
import { PlusCircle, Trash2, User, Info, Search } from "lucide-react";
import CreateUserForm from "@/components/CreateUserForm";
import { toast } from "react-toastify";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

const API_BASE_URL = "https://api2.naayiq.com";

interface User {
  userId: number;
  name: string;
  email: string;
  phone_number: string;
  role: "admin" | "superadmin" | "user";
  is_active: boolean;
}

const UsersTreePage = () => {
  const [users, setUsers] = useState<User[]>([]);
  const [expandedUsers, setExpandedUsers] = useState<number[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [searchTerm, setSearchTerm] = useState("");
  const [currentPage, setCurrentPage] = useState(1);
  const [usersPerPage] = useState(10);
  const [selectedUser, setSelectedUser] = useState<User | null>(null);
  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
  const [isCreateFormVisible, setIsCreateFormVisible] = useState(false);

  const handleCreateUser = () => {
    setIsCreateFormVisible(true);
  };

  const handleCreateUserSubmit = async (userData: any) => {
    try {
      const response = await fetch(`${API_BASE_URL}/user/users`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(userData),
        credentials: "include",
      });
      const j = await response.json();
      if (!response.ok) {
        toast.error(j.message);
      } else {
        toast.success(j.message);
      }
      await fetchUsers();
      setIsCreateFormVisible(false);
    } catch (error) {
      console.error("Error creating user:", error);

      setError("Failed to create user. Please try again.");
    }
  };

  const fetchUsers = useCallback(async () => {
    try {
      const response = await fetch(`${API_BASE_URL}/user/users`, {
        credentials: "include",
      });
      if (!response.ok) throw new Error("Failed to fetch users");
      const data = await response.json();
      setUsers(data);
    } catch (error) {
      console.error("Error fetching users:", error);
      setError("Failed to fetch users. Please try again.");
    }
  }, []);

  useEffect(() => {
    fetchUsers();
  }, [fetchUsers]);

  const toggleUserExpand = (userId: number) => {
    setExpandedUsers((prev) =>
      prev.includes(userId)
        ? prev.filter((id) => id !== userId)
        : [...prev, userId],
    );
  };

  const handleToggleActive = async (user: User) => {
    try {
      const response = await fetch(
        `${API_BASE_URL}/user/users/${user.userId}`,
        {
          method: "PUT",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ is_active: !user.is_active }),
          credentials: "include",
        },
      );
      if (!response.ok) throw new Error("Failed to update user");
      await fetchUsers();
    } catch (error) {
      console.error("Error updating user:", error);
      setError("Failed to update user. Please try again.");
    }
  };

  const handleDeleteUser = async (userId: number) => {
    try {
      const response = await fetch(`${API_BASE_URL}/user/users/${userId}`, {
        method: "DELETE",
        credentials: "include",
      });
      if (!response.ok) throw new Error("Failed to delete user");
      else toast.success("User deleted successfully!");
      await fetchUsers();
      setIsDeleteDialogOpen(false);
    } catch (error) {
      console.error("Error deleting user:", error);
      setError("Failed to delete user. Please try again.");
    }
  };

  const handleViewDetails = (userId: number) => {
    // Implement navigation to user details page
    console.log("View details for user", userId);
  };

  const filteredUsers = users.filter(
    (user) =>
      user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
      user.email.toLowerCase().includes(searchTerm.toLowerCase()),
  );

  const indexOfLastUser = currentPage * usersPerPage;
  const indexOfFirstUser = indexOfLastUser - usersPerPage;
  const currentUsers = filteredUsers.slice(indexOfFirstUser, indexOfLastUser);

  const paginate = (pageNumber: number) => setCurrentPage(pageNumber);

  return (
    <DefaultLayout>
      <div className="container mx-auto p-6">
        <h1 className="mb-6 text-3xl font-bold text-gray-800">Users</h1>

        <div className="mb-4 flex items-center justify-between">
          <button
            onClick={handleCreateUser}
            className="flex items-center rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
          >
            <PlusCircle size={20} className="mr-2" />
            Create New User
          </button>
          <div className="flex items-center">
            <Search size={20} className="mr-2 text-gray-400" />
            <input
              type="text"
              placeholder="Search users..."
              value={searchTerm}
              onChange={(e) => setSearchTerm(e.target.value)}
              className="rounded-md border border-gray-300 px-4 py-2"
            />
          </div>
        </div>

        <table className="min-w-full divide-y divide-gray-200">
          <thead className="bg-gray-50">
            <tr>
              <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
                Name
              </th>
              <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
                Email
              </th>
              <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
                Phone Number
              </th>
              <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
                Role
              </th>
              <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
                Active
              </th>
              <th className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
                Actions
              </th>
            </tr>
          </thead>
          <tbody className="divide-y divide-gray-200 bg-white">
            {currentUsers.map((user) => (
              <tr key={user.userId}>
                <td className="whitespace-nowrap px-6 py-4">{user.name}</td>
                <td className="whitespace-nowrap px-6 py-4">{user.email}</td>
                <td className="whitespace-nowrap px-6 py-4">
                  {user.phone_number}
                </td>
                <td className="whitespace-nowrap px-6 py-4">
                  <span
                    className={`rounded-full px-2 py-1 text-xs ${
                      user.role === "admin"
                        ? "bg-purple-100 text-purple-800"
                        : user.role === "superadmin"
                          ? "bg-red-100 text-red-800"
                          : "bg-green-100 text-green-800"
                    }`}
                  >
                    {user.role}
                  </span>
                </td>
                <td className="whitespace-nowrap px-6 py-4">
                  <input
                    type="checkbox"
                    checked={user.is_active}
                    onChange={() => handleToggleActive(user)}
                    className="form-checkbox h-5 w-5 text-blue-600"
                  />
                </td>
                <td className="whitespace-nowrap px-6 py-4">
                  <div className="flex space-x-2">
                    <button
                      onClick={() => {
                        setSelectedUser(user);
                        setIsDeleteDialogOpen(true);
                      }}
                      className="rounded bg-red p-1 text-white hover:bg-danger"
                    >
                      <Trash2 size={16} />
                    </button>
                  </div>
                </td>
              </tr>
            ))}
          </tbody>
        </table>

        {/* Pagination */}
        <div className="mt-4 flex justify-center">
          {Array.from({
            length: Math.ceil(filteredUsers.length / usersPerPage),
          }).map((_, index) => (
            <button
              key={index}
              onClick={() => paginate(index + 1)}
              className={`mx-1 rounded px-3 py-1 ${
                currentPage === index + 1
                  ? "bg-blue-500 text-white"
                  : "bg-gray-200"
              }`}
            >
              {index + 1}
            </button>
          ))}
        </div>

        {/* Create User Form Modal */}
        {isCreateFormVisible && (
          <div className="fixed inset-0 z-10 overflow-y-auto">
            <div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
              <div
                className="fixed inset-0 transition-opacity"
                aria-hidden="true"
              >
                <div className="absolute inset-0 bg-gray-500 opacity-75"></div>
              </div>
              <span
                className="hidden sm:inline-block sm:h-screen sm:align-middle"
                aria-hidden="true"
              >
                &#8203;
              </span>
              <div className="inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle">
                <div className="bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
                  <h3 className="mb-4 text-lg font-medium leading-6 text-gray-900">
                    Create New User
                  </h3>
                  <CreateUserForm
                    onSubmit={handleCreateUserSubmit}
                    onCancel={() => setIsCreateFormVisible(false)}
                  />
                </div>
              </div>
            </div>
          </div>
        )}

        {/* Delete Confirmation Modal */}
        {isDeleteDialogOpen && (
          <div className="fixed inset-0 z-10 overflow-y-auto">
            <div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
              <div
                className="fixed inset-0 transition-opacity"
                aria-hidden="true"
              >
                <div className="absolute inset-0 bg-gray-500 opacity-75"></div>
              </div>
              <span
                className="hidden sm:inline-block sm:h-screen sm:align-middle"
                aria-hidden="true"
              >
                &#8203;
              </span>
              <div className="inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle">
                <div className="bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
                  <div className="sm:flex sm:items-start">
                    <div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
                      <h3 className="text-lg font-medium leading-6 text-gray-900">
                        Delete User
                      </h3>
                      <div className="mt-2">
                        <p className="text-sm text-gray-500">
                          Are you sure you want to delete this user? This action
                          cannot be undone.
                        </p>
                      </div>
                    </div>
                  </div>
                </div>
                <div className="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
                  <button
                    type="button"
                    className="focus:ring-red-500 inline-flex w-full justify-center rounded-md border border-transparent bg-red px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-danger focus:outline-none focus:ring-2 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
                    onClick={() =>
                      selectedUser && handleDeleteUser(selectedUser.userId)
                    }
                  >
                    Delete
                  </button>
                  <button
                    type="button"
                    className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm"
                    onClick={() => setIsDeleteDialogOpen(false)}
                  >
                    Cancel
                  </button>
                </div>
              </div>
            </div>
          </div>
        )}
      </div>
      <ToastContainer />
    </DefaultLayout>
  );
};

export default UsersTreePage;