Integrate the backend with the frontend:
the backend:
server.ts:
import express from 'express';
import morgan from 'morgan';
import dotenv from 'dotenv';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import compression from 'compression';
import { errorHandler } from './middleware/errorHandler';
import categoryRoutes from './routes/categoryRoutes';
import subcategoryRoutes from './routes/subcategoryRoutes';
import productRoutes from "./routes/productRoutes";
import userRoutes from "./routes/userRoutes";
import counterRoutes from "./routes/counterRoutes";
import addressRoutes from "./routes/addressRoutes";
import couponRoutes from "./routes/couponRoutes";

dotenv.config();

const app = express();

app.use(cors())
app.use(compression());

app.use(express.json());
app.use(cookieParser());
app.use(morgan('dev'));

app.set('trust proxy', 1);

// Middleware to add caching headers
const addCacheHeaders = (maxAge: number) => (req: express.Request, res: express.Response, next: express.NextFunction) => {
    res.set('Cache-Control', `public, s-maxage=${maxAge}, stale-while-revalidate`);
    res.set('CDN-Cache-Control', `public, s-maxage=${maxAge}, stale-while-revalidate`);
    res.set('Vercel-CDN-Cache-Control', `public, s-maxage=${maxAge}, stale-while-revalidate`);
    next();
};

// Routes with caching
app.use('/user', addCacheHeaders(84600), userRoutes); // Cache for 1 minute
app.use('/counter', counterRoutes);
app.use('/categories', addCacheHeaders(84600), categoryRoutes); // Cache for 1 hour
app.use('/subcategories', addCacheHeaders(84600), subcategoryRoutes); // Cache for 1 hour
app.use('/products', productRoutes); // Cache for 10 minutes
app.use('/addresses', addCacheHeaders(84600), addressRoutes); // Cache for 1 hour
app.use('/coupons', addCacheHeaders(84600), couponRoutes); // Cache for 1 hour

app.use(errorHandler);

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

export default app;
cartRoutes.ts:
// routes/cartRoutes.ts
import express from 'express';
import { body } from 'express-validator';
import { validateRequest } from '../middleware/validateRequest';
import { isAuthenticated } from '../middleware/isAuthenticated';
import * as cartController from '../controllers/cartController';

const router = express.Router();

// Create a new cart with items
router.post(
    '/',
    isAuthenticated,
    [
        // Validate cart fields
        body('notes').optional().isString(),
        body('full_name').optional().isString(),
        body('governorate').optional().isString(),
        body('city').optional().isString(),
        body('address').optional().isString(),
        body('closest_point').optional().isString(),
        body('phone_number').optional().isString(),
        body('type').optional().isIn(['Home', 'Work', 'Other']),
        body('coupon_id').optional().isInt(),

        // Validate items
        body('items').isArray({ min: 1 }).withMessage('Items array is required'),
        body('items..product_id').isInt().withMessage('Product ID must be an integer'),
        body('items..size_id').optional().isInt(),
        body('items..color_id').optional().isInt(),
        body('items..quantity').isInt({ gt: 0 }).withMessage('Quantity must be greater than zero'),
    ],
    validateRequest,
    cartController.createCart
);

router.get(
    '/',
    isAuthenticated,
    cartController.getCart
);

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

// Interfaces for TypeScript typing
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;
}

// Function to create a cart with items in a single transaction
export async function createCartWithItems(
    cartData: Partial<Cart>,
    items: Partial<CartItem>[]
): Promise<{ cart: Cart; items: CartItem[] }> {
    try {
        await query('BEGIN');

        // Insert into carts table
        const cartResult = await query<Cart>(
            `INSERT INTO carts (
        user_id, status, notes, full_name, governorate, city, address, closest_point, phone_number, type, coupon_id
      ) VALUES (
        $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11
      ) RETURNING *`,
            [
                cartData.user_id,
                cartData.status || 'Pending',
                cartData.notes,
                cartData.full_name,
                cartData.governorate,
                cartData.city,
                cartData.address,
                cartData.closest_point,
                cartData.phone_number,
                cartData.type,
                cartData.coupon_id,
            ]
        );

        const cart = cartResult.rows[0];
        if (!cart) {
            throw new Error('Failed to create cart');
        }

        // Insert cart items
        const cartItems: CartItem[] = [];
        const insertItemText = `INSERT INTO cart_items (
      cart_id, product_id, size_id, color_id, quantity
    ) VALUES ($1, $2, $3, $4, $5) RETURNING *`;

        for (const item of items) {
            const itemResult = await query<CartItem>(insertItemText, [
                cart.id,
                item.product_id,
                item.size_id,
                item.color_id,
                item.quantity,
            ]);
            cartItems.push(itemResult.rows[0]);
        }

        await query('COMMIT');
        return { cart, items: cartItems };
    } catch (error) {
        await query('ROLLBACK');
        console.error('Error creating cart with items:', error);
        throw error;
    }
}

export async function getCartWithItemsByUserId(
    userId: number
): Promise<{ cart: Cart; items: CartItem[] } | null> {
    // Get the most recent cart for the user
    const cartResult = await query<Cart>(
        `SELECT * FROM carts WHERE user_id = $1 ORDER BY created_at DESC LIMIT 1`,
        [userId]
    );

    const cart = cartResult.rows[0];
    if (!cart) {
        return null;
    }

    // Get the cart items
    const itemsResult = await query<CartItem>(
        `SELECT * FROM cart_items WHERE cart_id = $1`,
        [cart.id]
    );

    return { cart, items: itemsResult.rows };
}
controller/cartController.ts:
// controllers/cartController.ts
import { Request, Response, NextFunction } from 'express';
import * as cartModel from '../models/cartModel';

export async function createCart(req: Request, res: Response, next: NextFunction) {
    try {
        const userId: any = req.user?.id || null;

        const {
            status,
            notes,
            full_name,
            governorate,
            city,
            address,
            closest_point,
            phone_number,
            type,
            coupon_id,
            items,
        } = req.body;

        if (!items || !Array.isArray(items) || items.length === 0) {
            return res.status(400).json({ message: 'Items are required to create a cart' });
        }

        // Prepare cart data
        const cartData = {
            user_id: userId,
            status: status || 'Pending',
            notes,
            full_name,
            governorate,
            city,
            address,
            closest_point,
            phone_number,
            type,
            coupon_id,
        };

        // Create cart with items
        const { cart, items: cartItems } = await cartModel.createCartWithItems(cartData, items);

        res.status(201).json({ cart, items: cartItems });
    } catch (error) {
        next(error);
    }
}

export async function getCart(req: Request, res: Response, next: NextFunction) {
    try {
        const userId = req.user?.id;
        if (!userId) {
            return res.status(401).json({ message: 'Unauthorized' });
        }

        const cartWithItems = await cartModel.getCartWithItemsByUserId(userId);
        if (!cartWithItems) {
            return res.status(404).json({ message: 'No cart found for the user' });
        }

        res.json(cartWithItems);
    } catch (error) {
        next(error);
    }
}


The frontend:
Cart.jsx:
'use client'
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import Link from 'next/link';
import CartItem from './CartItem';
import { motion, AnimatePresence } from 'framer-motion';
import Image from "next/image";
import { useRouter } from "next/navigation";
import Loading from "@/components/Loading";
import { useAppDispatch } from "@/lib/hook";
import { setOrder } from "@/lib/features/orderSlice";
import EmptyCart from "@/components/EmptyCart";
import {useSelector} from "react-redux";
import {CircleArrowLeft} from "lucide-react";

const Cart = () => {
    const [cartItems, setCartItems] = useState([]);
    const [subTotal, setSubTotal] = useState(0);
    const [delivery] = useState(5000);
    const [discount, setDiscount] = useState(0);
    const [coupon, setCoupon] = useState('');
    const [isLoading, setIsLoading] = useState(true);
    const [showConfirmation, setShowConfirmation] = useState(false);
    const [confirmationMessage, setConfirmationMessage] = useState('');
    const [couponMessage, setCouponMessage] = useState('');
    const [appliedCoupon, setAppliedCoupon] = useState('');
    const dispatch = useAppDispatch();
    const order = useSelector(state => state.order);
    const router = useRouter();

    const fetchCartItems = useCallback(async () => {
        const storedCart = JSON.parse(localStorage.getItem('cart')) || [];
        if (storedCart.length === 0) {
            setIsLoading(false);
            return;
        }

        const itemsWithDetails = await Promise.all(
            storedCart.map(async (item) => {
                try {
                    const response = await fetch(`https://api.naayiq.com/products/${item.product_id}`);
                    const data = await response.json();
                    const product = data.product;
                    const selectedSize = product.sizes.find(size => size.id === item.size_id);
                    const selectedColor = product.colors.find(color => color.id === item.color_id);

                    return {
                        ...item,
                        title: product.name,
                        image: `https://storage.naayiq.com/resources/${selectedSize?.images[0]}`,
                        price: parseInt(selectedSize?.price || product.price),
                        color: selectedColor?.name,
                        size: selectedSize?.name,
                    };
                } catch (error) {
                    console.error('Error fetching product details:', error);
                    return item;
                }
            })
        );
        setCartItems(itemsWithDetails);
        setIsLoading(false);
    }, []);

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

    useEffect(() => {
        const total = cartItems.reduce((sum, item) => sum + item.price * item.qty, 0);
        setSubTotal(total);
    }, [cartItems]);

    const handleUpdateQuantity = useCallback((id, newQty) => {
        setCartItems(prevItems => {
            const updatedItems = prevItems.map(item =>
                item.product_id === id ? { ...item, qty: newQty } : item
            );
            localStorage.setItem('cart', JSON.stringify(updatedItems));
            return updatedItems;
        });
    }, []);

    const handleRemoveItem = useCallback((id) => {
        setShowConfirmation(true);
        setConfirmationMessage(`Are you sure you want to remove this item from your cart?`);
        setConfirmAction(() => () => {
            setCartItems(prevItems => {
                const updatedItems = prevItems.filter(item => item.product_id !== id);
                localStorage.setItem('cart', JSON.stringify(updatedItems));
                return updatedItems;
            });
            setShowConfirmation(false);
        });
    }, []);

    const handleApplyCoupon = useCallback(() => {
        if (appliedCoupon) {
            setShowConfirmation(true);
            setConfirmationMessage(`Are you sure you want to remove the applied coupon?`);
            setConfirmAction(() => () => {
                setAppliedCoupon('');
                setDiscount(0);
                setCoupon('');
                setCouponMessage('');
                setShowConfirmation(false);
            });
        } else {
            if (coupon === "FREE") {
                setDiscount(5000);
                setAppliedCoupon(coupon);
                setCouponMessage('Applied!');
            } else {
                setCouponMessage("Invalid coupon code");
                setTimeout(() => setCouponMessage(''), 3000);
            }
        }
    }, [appliedCoupon, coupon]);

    const [confirmAction, setConfirmAction] = useState(() => {});

    const totalPrice = useMemo(() => subTotal + delivery - discount, [subTotal, delivery, discount]);

    if (isLoading) {
        return <Loading />;
    }


    return (
        cartItems.length === 0 ? <EmptyCart />:
        <>
            <header className="flex items-center mb-6">
                <CircleArrowLeft size={52} strokeWidth={0.7} onClick={router.back} className="p-2 relative z-20" />
                <h1 className="text-3xl z-10 text-[#181717] left-0 right-0 absolute font-sans text-center font-medium">
                    Cart
                </h1>
            </header>
            <motion.div
                initial={{opacity: 0}}
                animate={{opacity: 1}}
                exit={{opacity: 0}}
                className="p-4 overflow-x-hideen font-serif container mx-auto relative"
            >
                <AnimatePresence>
                    {showConfirmation && (
                        <motion.div
                            initial={{opacity: 0}}
                            animate={{opacity: 1}}
                            exit={{opacity: 0}}
                            className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
                        >
                            <motion.div
                                initial={{ scale: 0.9 }}
                                animate={{ scale: 1 }}
                                exit={{ scale: 0.9 }}
                                className="bg-white p-4 rounded-lg shadow-lg"
                            >
                                <p className="mb-4">{confirmationMessage}</p>
                                <div className="flex justify-end">
                                    <button
                                        onClick={() => setShowConfirmation(false)}
                                        className="bg-gray-300 text-black px-4 py-2 rounded mr-2"
                                    >
                                        Cancel
                                    </button>
                                    <button
                                        onClick={confirmAction}
                                        className="bg-red-500 text-white px-4 py-2 rounded"
                                    >
                                        Confirm
                                    </button>
                                </div>
                            </motion.div>
                        </motion.div>
                    )}
                </AnimatePresence>

                <AnimatePresence>
                    {cartItems.map(item => (
                        <motion.div
                            key={item.product_id}
                            initial={{ opacity: 0, y: 20 }}
                            animate={{ opacity: 1, y: 0 }}
                            exit={{ opacity: 0, y: -20 }}
                            transition={{ duration: 0.3 }}
                        >
                            <CartItem
                                id={item.product_id}
                                title={item.title}
                                color={item.color}
                                image={item.image}
                                size={item.size}
                                qty={item.qty}
                                price={item.price}
                                onUpdateQuantity={handleUpdateQuantity}
                                onRemove={handleRemoveItem}
                            />
                        </motion.div>
                    ))}
                </AnimatePresence>

                <motion.div
                    initial={{ opacity: 0, y: 20 }}
                    animate={{ opacity: 1, y: 0 }}
                    transition={{ delay: 0.3 }}
                    className="mt-8 bg-white rounded-lg shadow p-4"
                >
                    <h2 className="text-xl font-sans font-medium mb-4">Price Details</h2>
                    <div className="mb-2">
                        <span>Discounted Coupon</span>
                        <div className="flex justify-between items-center">
                            <input
                                type="text"
                                value={coupon}
                                onChange={(e) => setCoupon(e.target.value)}
                                className="border rounded w-full p-2 mr-2"
                                placeholder="Enter coupon"
                                disabled={appliedCoupon !== ''}
                            />
                            <motion.button
                                whileHover={{ scale: 1.05 }}
                                whileTap={{ scale: 0.95 }}
                                onClick={handleApplyCoupon}
                                className={`${appliedCoupon ? 'bg-red-500' : 'bg-[#3B5345]'} text-white px-8 py-2 rounded`}
                            >
                                {appliedCoupon ? 'Remove' : 'Apply'}
                            </motion.button>
                        </div>
                        <AnimatePresence>
                            {couponMessage && (
                                <motion.div
                                    initial={{ opacity: 0, y: -10 }}
                                    animate={{ opacity: 1, y: 0 }}
                                    exit={{ opacity: 0, y: -10 }}
                                    className={`text-sm mt-1 ${couponMessage === 'Applied!' ? 'text-green-500' : 'text-red-500'}`}
                                >
                                    {couponMessage}
                                </motion.div>
                            )}
                        </AnimatePresence>
                    </div>
                    <div className="flex justify-between mb-2">
                        <span>Sub-total</span>
                        <span>{subTotal} IQD</span>
                    </div>
                    <div className="flex justify-between mb-2">
                        <span>Delivery</span>
                        <span>{delivery} IQD</span>
                    </div>
                    <div className="flex justify-between mb-2">
                        <span>Discount</span>
                        <span>{discount > 0 ? "-" : ""}{discount} IQD</span>
                    </div>
                    <div className="flex justify-between font-bold mt-4">
                        <span>Total Price</span>
                        <span>{totalPrice} IQD</span>
                    </div>
                </motion.div>
                <Link
                    onClick={() => dispatch(setOrder({...order, info: {delivery, discount, subTotal}}))}
                    href="/cart/order"
                    className="w-full bg-[#3B5345] text-white py-3 rounded-lg font-medium text-lg mt-6 block text-center"
                >
                    Proceed
                </Link>
            </motion.div>
        </>
    );
};

export default Cart;
CartCheckout.jsx:
'use client'

import React, { useState } from 'react';
import { CircleArrowLeft, FileText } from 'lucide-react';
import Link from 'next/link';
import {useRouter} from "next/navigation";
import {useSelector} from "react-redux";

const CartCheckout = ({ subTotal, delivery, discount, onBack }) => {
    const [note, setNote] = useState('');
    const { shippingAddress } = useSelector(state => state.order);
    const router = useRouter()
    return (
        <>
            <header className="flex items-center mb-12">
                <CircleArrowLeft size={52} strokeWidth={0.7} onClick={router.back} className="p-2 relative z-20" />
                <h1 className="text-2xl ssm:text-3xl absolute right-0 left-0 z-10 text-center font-medium font-sans">Checkout</h1>
            </header>
            <div
                className="font-serif overflow-x-hideen container mx-auto max-w-md">
                <section className="mb-6">
                    <h2 className="text-xl font-sans font-medium mb-2">Shipping Address</h2>
                    {shippingAddress ? ( <>
                        <div className="border border-gray-200 rounded-lg p-4">
                            <p>{shippingAddress.governorate}, {shippingAddress.city}</p>
                            <p>{shippingAddress.address}</p>
                            <p>{shippingAddress.phone_number}</p>
                        </div>
                            <Link
                                href="/cart/choose-address"
                                className="w-full border mt-2 border-[#37474F] rounded-lg p-3 flex items-center justify-center text-[#3B5345] bg-[rgba(59,83,69,0.05)]"
                            >
                                <span className="text-base font-semibold">Choose another address</span>
                            </Link>
                        </>
                    ) : (
                        <Link
                            href="/cart/choose-address"
                            prefetch={false}
                            className="w-full border border-[#37474F] rounded-lg p-3 flex items-center justify-center text-[#3B5345] bg-[rgba(59,83,69,0.05)]"
                        >
                            <span className="text-base font-semibold">Choose address</span>
                        </Link>
                    )}
                </section>

                <section
                    className="mb-6"
                >
                    <h2 className="text-xl font-sans font-medium mb-2">Note</h2>
                    <p className="text-sm text-gray-600 mb-2">Write Any Additional Note</p>
                    <div className="relative">
                        <FileText className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"/>
                        <input
                            type="text"
                            value={note}
                            onChange={(e) => setNote(e.target.value)}
                            placeholder="I am not available on Sunday"
                            className="w-full border border-[rgba(105,92,92,0.3)] rounded-lg p-4 pl-12 text-sm"
                        />
                    </div>
                </section>

                <section
                    className="mb-6"
                >
                    <h2 className="text-xl font-sans font-medium mb-2">Payment Method</h2>
                    <div className="flex items-center">
                        <div
                            className="w-8 h-8 rounded-full border border-[#3B5345] flex items-center justify-center mr-3">
                            <div className="w-5 h-5 rounded-full bg-[#97C86C]"></div>
                        </div>
                        <span>Cash On Delivery</span>
                    </div>
                </section>

                <section
                    className="-mx-8 mb-6 bg-[#F6F3F1]/30 rounded-lg p-8"
                >
                    <h2 className="text-xl font-sans font-medium mb-4">Price Details</h2>
                    <div className="h-[1px] w-full mb-6 bg-[#695C5C]/30"></div>
                    <div className="space-y-3">
                        <div className="flex justify-between">
                            <span>Sub-total</span>
                            <span>{subTotal} IQD</span>
                        </div>
                        <div className="flex justify-between">
                            <span>Delivery</span>
                            <span>{delivery} IQD</span>
                        </div>
                        <div className="flex justify-between">
                            <span>Discount</span>
                            <span>{discount} IQD</span>
                        </div>
                        <div className="flex justify-between font-bold">
                            <span>Total Price</span>
                            <span>{subTotal + delivery - discount} IQD</span>
                        </div>
                    </div>
                </section>
                <Link
                    href={"/cart/order/confirm"}
                    className="w-full inline-block bg-[#3B5345] text-white py-3 px-4 rounded-lg font-medium text-lg"
                >
                    <p className="w-full text-center">Submit Order</p>
                </Link>
            </div>
        </>
    );
};

export default CartCheckout;
redux:
import { createSlice } from '@reduxjs/toolkit';

const addressesSlice = createSlice({
    name: 'addresses',
    initialState: [],
    reducers: {
        setAddresses: (state, action) => {
            return action.payload;
        },
        addAddress: (state, action) => {
            state.push(action.payload);
        },
        updateAddress: (state, action) => {
            const { index, address } = action.payload;
            state[index] = address;
        },
        removeAddress: (state, action) => {
            state.splice(action.payload, 1);
        },
    },
});

export const { setAddresses, addAddress, updateAddress, removeAddress } = addressesSlice.actions;
export default addressesSlice.reducer;
import { createSlice } from '@reduxjs/toolkit';

const orderSlice = createSlice({
    name: 'order',
    initialState: {
        items: [],
        info: {},
        shippingAddress: null,
        note: '',
        paymentMethod: 'Cash On Delivery'
    },
    reducers: {
        setOrder: (state, action) => {
            return { ...state, ...action.payload };
        },
        addOrderItem: (state, action) => {
            state.items.push(action.payload);
        },
        setShippingAddress: (state, action) => {
            state.shippingAddress = action.payload;
        },
        setNote: (state, action) => {
            state.note = action.payload;
        },
        setPaymentMethod: (state, action) => {
            state.paymentMethod = action.payload;
        }
    },
});

export const { setOrder, addOrderItem, setShippingAddress, setNote, setPaymentMethod } = orderSlice.actions;
export default orderSlice.reducer;

the api to use: "https://api.naayiq.com/cart"