-
- >
- );
+ return null; // This component will redirect immediately
};
export default AllProducts;
diff --git a/app/api/cart/get/route.js b/app/api/cart/get/route.js
new file mode 100644
index 000000000..005282489
--- /dev/null
+++ b/app/api/cart/get/route.js
@@ -0,0 +1,39 @@
+import connectDb from "@/config/db";
+import User from "@/models/Users";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function GET(request) {
+ try{
+ const {userId} = getAuth(request)
+
+ if (!userId) {
+ return NextResponse.json({
+ success: false,
+ message: "User not authenticated"
+ }, { status: 401 });
+ }
+
+ await connectDb()
+ const user = await User.findById(userId)
+
+ if (!user) {
+ return NextResponse.json({
+ success: false,
+ message: "User not found"
+ }, { status: 404 });
+ }
+
+ const {cartItems} = user
+
+ return NextResponse.json({ success: true, cartItems})
+
+ }catch(error){
+ console.error("Error in cart get route:", error);
+ return NextResponse.json({
+ success: false,
+ message: "Failed to fetch cart items",
+ error: error.message
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/cart/update/route.js b/app/api/cart/update/route.js
new file mode 100644
index 000000000..bcd5709b3
--- /dev/null
+++ b/app/api/cart/update/route.js
@@ -0,0 +1,23 @@
+import connectDb from "@/config/db";
+import User from "@/models/Users";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function POST(request) {
+ try{
+ const { userId} = getAuth(request);
+ const { cartData } = await request.json()
+
+ await connectDb()
+ const user = await User.findById(userId)
+
+ user.cartItems = cartData
+ await user.save()
+
+ return NextResponse.json({ success : true})
+
+
+ }catch(error){
+ return NextResponse.json({ success: false, message : error.message})
+ }
+}
\ No newline at end of file
diff --git a/app/api/check-env/route.js b/app/api/check-env/route.js
new file mode 100644
index 000000000..4ff0d27d2
--- /dev/null
+++ b/app/api/check-env/route.js
@@ -0,0 +1,26 @@
+import { NextResponse } from 'next/server';
+
+export async function GET() {
+ try {
+ const keyId = process.env.RAZORPAY_KEY_ID;
+ const keySecret = process.env.RAZORPAY_KEY_SECRET;
+ const publicKey = process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID;
+
+ return NextResponse.json({
+ success: true,
+ data: {
+ hasKeyId: !!keyId,
+ keyIdLength: keyId?.length,
+ hasKeySecret: !!keySecret,
+ keySecretLength: keySecret?.length,
+ hasPublicKey: !!publicKey,
+ publicKeyLength: publicKey?.length
+ }
+ });
+ } catch (error) {
+ return NextResponse.json({
+ success: false,
+ message: error.message
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/api/design-request/[id]/download/route.js b/app/api/design-request/[id]/download/route.js
new file mode 100644
index 000000000..6f5c76b9d
--- /dev/null
+++ b/app/api/design-request/[id]/download/route.js
@@ -0,0 +1,58 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import DesignRequest from "@/models/DesignRequest";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function GET(request, context) {
+ try {
+ const { userId } = getAuth(request);
+
+ // Extract id from context params
+ const id = context?.params?.id;
+
+ if (!id) {
+ return NextResponse.json({
+ success: false,
+ message: "Invalid request: Missing design request ID"
+ }, { status: 400 });
+ }
+
+ if (!userId) {
+ return NextResponse.json({ success: false, message: "Not authenticated" });
+ }
+
+ const isSeller = await authSeller(userId);
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not Authorized" });
+ }
+
+ await connectDb();
+
+ const designRequest = await DesignRequest.findById(id);
+
+ if (!designRequest) {
+ return NextResponse.json({
+ success: false,
+ message: "Design request not found"
+ }, { status: 404 });
+ }
+
+ // Check if we have a Cloudinary URL
+ if (designRequest.fileUrl) {
+ return NextResponse.redirect(designRequest.fileUrl);
+ }
+
+ return NextResponse.json({
+ success: false,
+ message: "No file URL found"
+ }, { status: 404 });
+
+ } catch (error) {
+ console.error("Error downloading file:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message || "Error downloading file"
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/design-request/[id]/status/route.js b/app/api/design-request/[id]/status/route.js
new file mode 100644
index 000000000..1024c10fe
--- /dev/null
+++ b/app/api/design-request/[id]/status/route.js
@@ -0,0 +1,57 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import DesignRequest from "@/models/DesignRequest";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function PUT(request, { params }) {
+ try {
+ const { userId } = getAuth(request);
+ const { id } = params;
+ const { status, quote, estimatedDelivery, adminNotes } = await request.json();
+
+ if (!userId) {
+ return NextResponse.json({ success: false, message: "Not authenticated" });
+ }
+
+ const isSeller = await authSeller(userId);
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not Authorized" });
+ }
+
+ await connectDb();
+
+ const updateData = { status };
+
+ // Add optional fields if provided
+ if (quote !== undefined) updateData.quote = quote;
+ if (estimatedDelivery !== undefined) updateData.estimatedDelivery = estimatedDelivery;
+ if (adminNotes !== undefined) updateData.adminNotes = adminNotes;
+
+ const updatedRequest = await DesignRequest.findByIdAndUpdate(
+ id,
+ updateData,
+ { new: true }
+ );
+
+ if (!updatedRequest) {
+ return NextResponse.json({
+ success: false,
+ message: "Design request not found"
+ }, { status: 404 });
+ }
+
+ return NextResponse.json({
+ success: true,
+ message: "Status updated successfully",
+ designRequest: updatedRequest
+ });
+
+ } catch (error) {
+ console.error("Error updating design request status:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message || "Error updating status"
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/design-request/create/route.js b/app/api/design-request/create/route.js
new file mode 100644
index 000000000..ac868285d
--- /dev/null
+++ b/app/api/design-request/create/route.js
@@ -0,0 +1,102 @@
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+import connectDb from "@/config/db";
+import DesignRequest from "@/models/DesignRequest";
+import { v2 as cloudinary } from 'cloudinary';
+
+// Configure Cloudinary
+cloudinary.config({
+ cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+ api_key: process.env.CLOUDINARY_API_KEY,
+ api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+
+export async function POST(request) {
+ try {
+ const { userId } = getAuth(request);
+
+ if (!userId) {
+ return NextResponse.json({ success: false, message: 'Unauthorized' });
+ }
+
+ const formData = await request.formData();
+ const file = formData.get('file');
+ const designName = formData.get('designName');
+ const description = formData.get('description');
+ const material = formData.get('material');
+ const color = formData.get('color');
+ const quantity = parseInt(formData.get('quantity'));
+ const specialRequirements = formData.get('specialRequirements');
+
+ // Validate required fields
+ if (!file || !designName) {
+ return NextResponse.json({ success: false, message: 'Design file and name are required' });
+ }
+
+ // Validate file type
+ const allowedTypes = ['.stl', '.3mf', '.gcode'];
+ const fileName = file.name.toLowerCase();
+ const fileExtension = fileName.substring(fileName.lastIndexOf('.'));
+
+ if (!allowedTypes.includes(fileExtension)) {
+ return NextResponse.json({ success: false, message: 'Invalid file type. Please upload .stl, .3mf, or .gcode files' });
+ }
+
+ // Validate file size (10MB max)
+ if (file.size > 10 * 1024 * 1024) {
+ return NextResponse.json({ success: false, message: 'File size should be less than 10MB' });
+ }
+
+ await connectDb();
+
+ // Create unique filename
+ const timestamp = Date.now();
+ const uniqueFileName = `${userId}_${timestamp}_${fileName}`;
+
+ // Convert file to buffer
+ const fileBuffer = Buffer.from(await file.arrayBuffer());
+
+ // Upload to Cloudinary
+ const uploadResult = await new Promise((resolve, reject) => {
+ cloudinary.uploader.upload_stream(
+ {
+ resource_type: 'raw',
+ public_id: `designs/${uniqueFileName}`,
+ format: fileExtension.substring(1), // Remove the dot
+ },
+ (error, result) => {
+ if (error) reject(error);
+ else resolve(result);
+ }
+ ).end(fileBuffer);
+ });
+
+ // Create design request in database
+ const designRequest = await DesignRequest.create({
+ userId,
+ designName,
+ description,
+ material,
+ color,
+ quantity,
+ specialRequirements,
+ fileName: uniqueFileName,
+ fileUrl: uploadResult.secure_url,
+ status: 'PENDING',
+ date: Date.now()
+ });
+
+ return NextResponse.json({
+ success: true,
+ message: 'Design request submitted successfully',
+ designRequest
+ });
+
+ } catch (error) {
+ console.error('Error creating design request:', error);
+ return NextResponse.json({
+ success: false,
+ message: error.message || 'Error submitting design request'
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/api/design-request/list/route.js b/app/api/design-request/list/route.js
new file mode 100644
index 000000000..1e3af6c8a
--- /dev/null
+++ b/app/api/design-request/list/route.js
@@ -0,0 +1,38 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import DesignRequest from "@/models/DesignRequest";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function GET(request) {
+ try {
+ const { userId } = getAuth(request);
+
+ if (!userId) {
+ return NextResponse.json({ success: false, message: "Not authenticated" });
+ }
+
+ const isSeller = await authSeller(userId);
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not Authorized" });
+ }
+
+ await connectDb();
+
+ const designRequests = await DesignRequest.find({})
+ .sort({ date: -1 }) // Sort by date, newest first
+ .lean();
+
+ return NextResponse.json({
+ success: true,
+ designRequests
+ });
+
+ } catch (error) {
+ console.error("Error fetching design requests:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message || "Error fetching design requests"
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/inngest/route.js b/app/api/inngest/route.js
new file mode 100644
index 000000000..df54f59b9
--- /dev/null
+++ b/app/api/inngest/route.js
@@ -0,0 +1,14 @@
+import { serve } from "inngest/next";
+import { createUserOrder, inngest, syncUserCreation, syncUserDeletion, syncUserUpdation } from "@/config/inngest";
+
+// Create an API that serves zero functions
+export const { GET, POST, PUT } = serve({
+ client: inngest,
+ functions: [
+ /* your functions will be passed here later! */
+ syncUserCreation,
+ syncUserUpdation,
+ syncUserDeletion,
+ createUserOrder
+ ],
+});
diff --git a/app/api/order/[orderId]/route.js b/app/api/order/[orderId]/route.js
new file mode 100644
index 000000000..839131ebb
--- /dev/null
+++ b/app/api/order/[orderId]/route.js
@@ -0,0 +1,110 @@
+import { NextResponse } from 'next/server';
+import connectDb from '@/config/db';
+import Order from '@/models/Order';
+import User from '@/models/Users';
+import { getAuth } from '@clerk/nextjs/server';
+import authSeller from "@/lib/authSeller";
+
+export async function DELETE(request, { params }) {
+ try {
+ const { userId } = getAuth(request);
+ const { orderId } = params;
+
+ await connectDb();
+
+ // Find the order and verify it belongs to the user
+ const order = await Order.findOne({ _id: orderId, userId });
+
+ if (!order) {
+ return NextResponse.json({
+ success: false,
+ message: "Order not found or unauthorized"
+ });
+ }
+
+ // Only allow deletion of pending orders
+ if (order.paymentStatus !== 'PENDING') {
+ return NextResponse.json({
+ success: false,
+ message: "Cannot delete completed orders"
+ });
+ }
+
+ // Delete the order
+ await Order.findByIdAndDelete(orderId);
+
+ return NextResponse.json({
+ success: true,
+ message: "Order deleted successfully"
+ });
+ } catch (error) {
+ console.error("Error deleting order:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message
+ });
+ }
+}
+
+export async function GET(request, { params }) {
+ try {
+ const { userId } = getAuth(request);
+ const isSeller = await authSeller(userId);
+
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not authorized" });
+ }
+
+ const { orderId } = params;
+
+ if (!orderId) {
+ return NextResponse.json({ success: false, message: "Order ID is required" });
+ }
+
+ await connectDb();
+
+ // Find order and populate related data
+ const order = await Order.findById(orderId)
+ .populate('address')
+ .populate('items.product');
+
+ if (!order) {
+ return NextResponse.json({ success: false, message: "Order not found" });
+ }
+
+ // Fetch user data separately since userId is a String
+ let userEmail = 'N/A';
+ let userName = 'N/A';
+
+ if (order.userId) {
+ try {
+ const user = await User.findById(order.userId);
+ if (user) {
+ userEmail = user.email || 'N/A';
+ userName = user.name || 'N/A';
+ }
+ } catch (userError) {
+ console.error("Error fetching user:", userError);
+ }
+ }
+
+ // Add user email and name to the order object for easy access
+ const orderWithUserData = {
+ ...order.toObject(),
+ userEmail: userEmail,
+ userName: userName
+ };
+
+ return NextResponse.json({
+ success: true,
+ order: orderWithUserData
+ });
+
+ } catch (error) {
+ console.error("Error fetching order:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/api/order/[orderId]/send-invoice/route.js b/app/api/order/[orderId]/send-invoice/route.js
new file mode 100644
index 000000000..bb3e6d5e4
--- /dev/null
+++ b/app/api/order/[orderId]/send-invoice/route.js
@@ -0,0 +1,67 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import Order from "@/models/Order";
+import User from "@/models/Users";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function POST(request, { params }) {
+ try {
+ const { userId } = getAuth(request);
+ const isSeller = await authSeller(userId);
+
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not authorized" });
+ }
+
+ const { orderId } = params;
+
+ if (!orderId) {
+ return NextResponse.json({ success: false, message: "Order ID is required" });
+ }
+
+ await connectDb();
+
+ // Find order and populate related data
+ const order = await Order.findById(orderId)
+ .populate('address')
+ .populate('items.product');
+
+ if (!order) {
+ return NextResponse.json({ success: false, message: "Order not found" });
+ }
+
+ // Fetch user data separately since userId is a String
+ let userEmail = 'N/A';
+ let userName = 'N/A';
+
+ if (order.userId) {
+ try {
+ const user = await User.findById(order.userId);
+ if (user) {
+ userEmail = user.email || 'N/A';
+ userName = user.name || 'N/A';
+ }
+ } catch (userError) {
+ console.error("Error fetching user:", userError);
+ }
+ }
+
+ // TODO: Implement EmailJS service call here
+ // This is where you'll add your EmailJS service to send GST invoice
+ // You now have access to userEmail and userName for the invoice
+
+ // For now, return success
+ return NextResponse.json({
+ success: true,
+ message: "GST Invoice sent successfully"
+ });
+
+ } catch (error) {
+ console.error("Error sending GST invoice:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message
+ });
+ }
+}
diff --git a/app/api/order/create/route.js b/app/api/order/create/route.js
new file mode 100644
index 000000000..cc61cbdeb
--- /dev/null
+++ b/app/api/order/create/route.js
@@ -0,0 +1,536 @@
+import { inngest } from "@/config/inngest";
+import Product from "@/models/Product";
+import User from "@/models/Users";
+import Address from "@/models/Address";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+import connectDb from "@/config/db";
+import Order from "@/models/Order";
+import mongoose from "mongoose";
+import { generateCustomOrderId } from "@/lib/orderIdGenerator";
+import { isFilamentCategory } from "@/lib/productCategories";
+
+function isValidObjectId(id) {
+ return mongoose.Types.ObjectId.isValid(id);
+}
+
+export async function POST(request) {
+ try {
+ console.log('Starting order creation process...');
+
+ await connectDb();
+ console.log('Database connected successfully');
+
+ // Verify Order model is available
+ console.log('Order model available:', !!Order);
+ console.log('Order model name:', Order.modelName);
+
+ const { userId } = getAuth(request);
+ console.log('User ID from auth:', userId);
+
+ const { address, items, paymentMethod, paymentId, orderId, signature, couponCode, discount } = await request.json();
+ console.log('Request data received:', { address, itemsCount: items?.length, paymentMethod, couponCode, discount });
+
+ if (!address || items.length === 0) {
+ return NextResponse.json({ success: false, message: 'Invalid Data' });
+ }
+
+ // calculate amount using items
+ let subtotal = 0;
+ console.log('Starting subtotal calculation for', items.length, 'items');
+
+ for (const item of items) {
+ // Extract productId if item.product contains an underscore
+ let productId = item.product;
+ if (typeof productId === 'string' && productId.includes('_')) {
+ productId = productId.split('_')[0];
+ }
+ if (!isValidObjectId(productId)) {
+ throw new Error(`Invalid product id: ${productId}`);
+ }
+ const product = await Product.findById(productId);
+ if (!product) {
+ throw new Error(`Product not found for id: ${productId}`);
+ }
+ const itemTotal = product.offerPrice * item.quantity;
+ subtotal += itemTotal;
+ console.log(`Item: ${product.name}, Price: ${product.offerPrice}, Qty: ${item.quantity}, Total: ${itemTotal}, Running subtotal: ${subtotal}`);
+ }
+
+ console.log('Final subtotal calculated:', subtotal);
+
+ // Ensure subtotal is a valid number
+ if (typeof subtotal !== 'number' || isNaN(subtotal)) {
+ throw new Error('Invalid subtotal calculation - subtotal is not a number');
+ }
+
+ // Calculate payment breakdown with category-wise GST (5% for Organic, 18% for others)
+ const discountAmount = discount || 0;
+ const discountedSubtotal = subtotal - discountAmount; // Apply discount to subtotal first
+
+ // Compute category split on gross subtotal
+ let organicGross = 0;
+ let otherGross = 0;
+ for (const item of items) {
+ let productId = item.product;
+ if (typeof productId === 'string' && productId.includes('_')) {
+ productId = productId.split('_')[0];
+ }
+ const product = await Product.findById(productId);
+ if (!product) continue;
+ const lineTotal = (product.offerPrice || 0) * item.quantity;
+ if (isFilamentCategory(product.category)) {
+ organicGross += lineTotal;
+ } else {
+ otherGross += lineTotal;
+ }
+ }
+ const totalGross = organicGross + otherGross || 1; // avoid divide-by-zero
+ const organicPortion = (organicGross / totalGross) * discountedSubtotal;
+ const otherPortion = discountedSubtotal - organicPortion;
+ const gst = Math.floor(organicPortion * 0.05 + otherPortion * 0.18);
+
+ const deliveryCharges = 0; // Free delivery for now
+ const totalAmount = discountedSubtotal + gst + deliveryCharges; // Total = discounted subtotal + GST + delivery
+
+ console.log('Payment breakdown calculation:', {
+ originalSubtotal: subtotal,
+ discountAmount,
+ discountedSubtotal,
+ gst,
+ deliveryCharges,
+ totalAmount
+ });
+
+ // Additional validation for subtotal
+ console.log('Subtotal validation:', {
+ subtotal: subtotal,
+ subtotalType: typeof subtotal,
+ isNaN: isNaN(subtotal),
+ isFinite: isFinite(subtotal)
+ });
+
+ // Validate calculations
+ if (isNaN(subtotal) || subtotal < 0) {
+ throw new Error('Invalid subtotal calculation');
+ }
+ if (isNaN(discountedSubtotal) || discountedSubtotal < 0) {
+ throw new Error('Invalid discounted subtotal calculation');
+ }
+ if (isNaN(totalAmount) || totalAmount < 0) {
+ throw new Error('Invalid total amount calculation');
+ }
+
+ // Ensure all values are numbers
+ if (typeof subtotal !== 'number' || !isFinite(subtotal)) {
+ throw new Error('Subtotal must be a valid number');
+ }
+ if (typeof discountedSubtotal !== 'number' || !isFinite(discountedSubtotal)) {
+ throw new Error('Discounted subtotal must be a valid number');
+ }
+ if (typeof totalAmount !== 'number' || !isFinite(totalAmount)) {
+ throw new Error('Total amount must be a valid number');
+ }
+
+ // Get user details for email
+ const user = await User.findById(userId);
+ if (!user) {
+ return NextResponse.json({ success: false, message: 'User not found' });
+ }
+
+ // Generate custom order ID
+ let customOrderId;
+ try {
+ customOrderId = await generateCustomOrderId();
+ console.log('Generated custom order ID:', customOrderId);
+
+ // Validate the generated ID
+ if (!customOrderId || typeof customOrderId !== 'string') {
+ throw new Error('Generated customOrderId is invalid');
+ }
+ } catch (error) {
+ console.error('Error generating custom order ID:', error);
+ // Fallback to timestamp-based ID if generation fails
+ customOrderId = `ORDER-${Date.now()}`;
+ }
+
+ // Ensure we have a valid customOrderId
+ if (!customOrderId || typeof customOrderId !== 'string') {
+ customOrderId = `ORDER-${Date.now()}`;
+ }
+
+ console.log('Final customOrderId:', customOrderId);
+ console.log('customOrderId type:', typeof customOrderId);
+ console.log('customOrderId length:', customOrderId.length);
+
+ // Create order with appropriate status based on payment method
+ let orderData = {
+ customOrderId,
+ userId,
+ address,
+ items,
+ amount: totalAmount,
+ subtotal: discountedSubtotal, // Store the discounted subtotal
+ gst: gst,
+ deliveryCharges: deliveryCharges,
+ discount: discountAmount,
+ paymentMethod,
+ date: Date.now(),
+ data: { items, address, paymentMethod, couponCode, discount }
+ };
+
+ // Debug logging
+ console.log('Order data being created:', {
+ customOrderId,
+ subtotal: discountedSubtotal,
+ amount: totalAmount,
+ gst,
+ deliveryCharges,
+ discount: discountAmount,
+ userId,
+ address,
+ items: items.length
+ });
+
+ // Validate order data before creation
+ if (!customOrderId || !userId || !address || !items || items.length === 0) {
+ throw new Error('Missing required order data');
+ }
+
+ if (typeof discountedSubtotal !== 'number' || discountedSubtotal < 0) {
+ throw new Error('Invalid subtotal value');
+ }
+
+ if (typeof totalAmount !== 'number' || totalAmount < 0) {
+ throw new Error('Invalid total amount value');
+ }
+
+ // Additional validation for required fields
+ if (!customOrderId || typeof customOrderId !== 'string') {
+ throw new Error('Invalid customOrderId');
+ }
+
+ if (!userId || typeof userId !== 'string') {
+ throw new Error('Invalid userId');
+ }
+
+ if (!address || typeof address !== 'string') {
+ throw new Error('Invalid address');
+ }
+
+ if (!Array.isArray(items) || items.length === 0) {
+ throw new Error('Invalid items array');
+ }
+
+ // Log the final order data for debugging
+ console.log('Final order data validation:', {
+ customOrderId: !!customOrderId,
+ userId: !!userId,
+ address: !!address,
+ itemsCount: items.length,
+ subtotal: discountedSubtotal,
+ amount: totalAmount,
+ gst: gst,
+ deliveryCharges: deliveryCharges,
+ discount: discountAmount
+ });
+
+ if (paymentMethod === 'ONLINE') {
+ // For online payment, create completed order with payment details
+ orderData.paymentStatus = 'COMPLETED';
+ orderData.status = 'Order Placed';
+ orderData.razorpayOrderId = orderId;
+ orderData.razorpayPaymentId = paymentId;
+ } else {
+ // For COD, create completed order
+ orderData.paymentStatus = 'COMPLETED';
+ orderData.status = 'Order Placed';
+ }
+
+ console.log('About to create order with data:', orderData);
+
+ // Final validation before order creation
+ console.log('Final validation check:', {
+ customOrderId: {
+ value: customOrderId,
+ type: typeof customOrderId,
+ isValid: !!customOrderId && typeof customOrderId === 'string'
+ },
+ subtotal: {
+ value: discountedSubtotal,
+ type: typeof discountedSubtotal,
+ isValid: typeof discountedSubtotal === 'number' && isFinite(discountedSubtotal)
+ },
+ amount: {
+ value: totalAmount,
+ type: typeof totalAmount,
+ isValid: typeof totalAmount === 'number' && isFinite(totalAmount)
+ },
+ userId: {
+ value: userId,
+ type: typeof userId,
+ isValid: !!userId && typeof userId === 'string'
+ },
+ address: {
+ value: address,
+ type: typeof address,
+ isValid: !!address && typeof address === 'string'
+ }
+ });
+
+ // Double-check all required fields
+ if (!customOrderId || typeof customOrderId !== 'string') {
+ throw new Error(`Invalid customOrderId: ${customOrderId}`);
+ }
+ if (typeof discountedSubtotal !== 'number' || !isFinite(discountedSubtotal)) {
+ throw new Error(`Invalid subtotal: ${discountedSubtotal}`);
+ }
+ if (typeof totalAmount !== 'number' || !isFinite(totalAmount)) {
+ throw new Error(`Invalid totalAmount: ${totalAmount}`);
+ }
+ if (!userId || typeof userId !== 'string') {
+ throw new Error(`Invalid userId: ${userId}`);
+ }
+ if (!address || typeof address !== 'string') {
+ throw new Error(`Invalid address: ${address}`);
+ }
+
+ let order;
+ try {
+ order = await Order.create(orderData);
+ console.log('Order created successfully:', order._id);
+ } catch (createError) {
+ console.error('Error creating order in database:', createError);
+ if (createError.errors) {
+ console.error('Validation errors:', createError.errors);
+ }
+ throw createError;
+ }
+
+ // Clear user cart items (user variable already defined above)
+ if (user) {
+ // clear user cart for all completed orders
+ user.cartItems = {};
+ await user.save();
+ console.log('User cart cleared successfully');
+ } else {
+ console.log('User not found for cart clearing, continuing...');
+ }
+
+ // Send event to Inngest for background processing
+ await inngest.send({
+ name: "order/created",
+ data: {
+ customOrderId,
+ userId,
+ address,
+ items,
+ amount: totalAmount,
+ subtotal: discountedSubtotal, // Use discountedSubtotal, not subtotal
+ gst: gst,
+ deliveryCharges: deliveryCharges,
+ discount: discountAmount,
+ paymentMethod,
+ date: Date.now()
+ }
+ });
+
+ console.log('Order created successfully and Inngest event sent');
+
+ // Create Shiprocket shipment asynchronously (don't wait for it)
+ try {
+ console.log('=== SHIPROCKET CREATION STARTED ===');
+ console.log('Creating Shiprocket shipment for order:', order._id);
+
+ // Fetch address and user data for Shiprocket
+ console.log('Fetching address with ID:', address);
+ const addressData = await Address.findById(address);
+ console.log('Address data:', addressData);
+
+ console.log('Fetching user with ID:', userId);
+ const userData = await User.findById(userId);
+ console.log('User data:', userData);
+
+ if (!addressData) {
+ console.error('Address not found for order:', order._id, 'Address ID:', address);
+ return;
+ }
+
+ if (!userData) {
+ console.error('User not found for order:', order._id, 'User ID:', userId);
+ return;
+ }
+
+ // Import and call shipment creation (async, non-blocking)
+ const { createShipment } = await import('@/lib/shiprocket');
+
+ // Prepare order items for shipment
+ const orderItems = [];
+ for (const item of items) {
+ let productId = item.product;
+ if (typeof productId === 'string' && productId.includes('_')) {
+ productId = productId.split('_')[0];
+ }
+ const product = await Product.findById(productId);
+ if (product) {
+ orderItems.push({
+ name: product.name,
+ sku: product._id.toString(),
+ units: item.quantity,
+ selling_price: product.offerPrice,
+ discount: product.price - product.offerPrice,
+ tax: Math.floor(product.offerPrice * 0.18),
+ hsn: "8518", // Default HSN for electronics
+ product_type: "standard"
+ });
+ }
+ }
+
+ // Prepare basic shipment data
+ const shipmentData = {
+ order_id: order.customOrderId || order._id.toString(),
+ order_date: new Date(order.date).toISOString().split('T')[0],
+ pickup_location: "warehouse", // Your actual pickup location identifier from Shiprocket
+ billing_customer_name: addressData.fullName || userData.name || "Customer",
+ billing_last_name: "",
+ billing_address: `${addressData.area}, ${addressData.city}, ${addressData.state} ${addressData.pincode}, India`,
+ billing_address_2: "",
+ billing_city: addressData.city,
+ billing_pincode: addressData.pincode,
+ billing_state: addressData.state,
+ billing_country: "India",
+ billing_email: userData.email,
+ billing_phone: addressData.phoneNumber || userData.phone || "8750461279",
+ shipping_is_billing: true,
+ order_items: orderItems,
+ payment_method: paymentMethod === 'COD' ? 'COD' : 'Prepaid',
+ sub_total: subtotal,
+ length: 10,
+ breadth: 10,
+ height: 5,
+ weight: 0.5,
+ shipping_charges: deliveryCharges,
+ total_discount: discount,
+ cod_amount: paymentMethod === 'COD' ? totalAmount : 0
+ };
+
+ // Create shipment (this will run in background)
+ console.log('Calling Shiprocket createShipment API...');
+ createShipment(shipmentData).then(shipmentResponse => {
+ console.log('Shiprocket response:', shipmentResponse);
+
+ // Handle different response structures from Shiprocket
+ let shipment;
+ if (shipmentResponse.shipment_id) {
+ // Direct response format (what we're getting)
+ shipment = shipmentResponse;
+ } else if (shipmentResponse.status === 201 && shipmentResponse.data) {
+ // Wrapped response format
+ shipment = shipmentResponse.data;
+ } else {
+ console.error('Unexpected Shiprocket response format:', shipmentResponse);
+ return;
+ }
+
+ // Update order with shipment details
+ Order.findByIdAndUpdate(order._id, {
+ shiprocketAWB: shipment.awb_code || '',
+ shipmentId: shipment.shipment_id ? shipment.shipment_id.toString() : '',
+ courierName: shipment.courier_name || '',
+ trackingUrl: shipment.awb_code ? `https://www.shiprocket.in/tracking/${shipment.awb_code}` : '',
+ shipmentStatus: shipment.status || 'NEW',
+ pickupScheduledDate: shipment.pickup_scheduled_date ? new Date(shipment.pickup_scheduled_date) : null,
+ expectedDeliveryDate: shipment.expected_delivery_date ? new Date(shipment.expected_delivery_date) : null
+ }).then(() => {
+ console.log('Shiprocket shipment created and order updated:', {
+ orderId: order._id,
+ shiprocketOrderId: shipment.order_id,
+ shipmentId: shipment.shipment_id,
+ status: shipment.status,
+ awb: shipment.awb_code
+ });
+ }).catch(updateError => {
+ console.error('Error updating order with shipment details:', updateError);
+ });
+ }).catch(shipmentError => {
+ console.error('Error creating Shiprocket shipment:', shipmentError);
+ // Don't fail the order if shipment creation fails
+ });
+
+ console.log('Shiprocket shipment creation initiated for order:', order._id);
+ console.log('=== SHIPROCKET CREATION COMPLETED ===');
+ } catch (shipmentError) {
+ console.error('=== SHIPROCKET CREATION FAILED ===');
+ console.error('Error initiating Shiprocket shipment:', shipmentError);
+ // Don't fail the order if shipment creation fails
+ }
+
+ // Debug NextResponse
+ console.log('NextResponse available:', !!NextResponse);
+ console.log('NextResponse type:', typeof NextResponse);
+ console.log('NextResponse.json available:', !!NextResponse?.json);
+
+ // Try to return response
+ try {
+ const response = NextResponse.json({
+ success: true,
+ message: paymentMethod === 'ONLINE' ? "Order Placed Successfully" : "Order Placed",
+ order
+ });
+ console.log('Response created successfully:', !!response);
+ return response;
+ } catch (responseError) {
+ console.error('Error creating response:', responseError);
+ // Fallback response
+ return new Response(JSON.stringify({
+ success: true,
+ message: paymentMethod === 'ONLINE' ? "Order Placed Successfully" : "Order Placed",
+ order
+ }), {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ }
+
+ } catch (error) {
+ console.error("Error creating order:", error);
+
+ // Log detailed error information
+ if (error.errors) {
+ console.error("Validation errors:", error.errors);
+ }
+ if (error.message) {
+ console.error("Error message:", error.message);
+ }
+
+ // Debug NextResponse in error case
+ console.log('NextResponse available in error handler:', !!NextResponse);
+ console.log('NextResponse type in error handler:', typeof NextResponse);
+
+ // Try to return error response
+ try {
+ const errorResponse = NextResponse.json({
+ success: false,
+ message: error.message || 'Failed to create order',
+ details: error.errors || {}
+ });
+ console.log('Error response created successfully:', !!errorResponse);
+ return errorResponse;
+ } catch (responseError) {
+ console.error('Error creating error response:', responseError);
+ // Fallback error response
+ return new Response(JSON.stringify({
+ success: false,
+ message: error.message || 'Failed to create order',
+ details: error.errors || {}
+ }), {
+ status: 500,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/api/order/list/route.js b/app/api/order/list/route.js
new file mode 100644
index 000000000..ba3ef2e52
--- /dev/null
+++ b/app/api/order/list/route.js
@@ -0,0 +1,24 @@
+import connectDb from "@/config/db";
+import Order from "@/models/Order";
+import { getAuth } from "@clerk/nextjs/server";
+import { connect } from "mongoose";
+import { NextResponse } from "next/server";
+import Address from "@/models/Address";
+
+
+export async function GET(request) {
+ try {
+ const{userId} = getAuth(request);
+ await connectDb()
+
+ const orders = await Order.find({userId})
+ .populate('address')
+ .populate('items.product')
+ .select('customOrderId items address amount paymentMethod paymentStatus date')
+
+ return NextResponse.json({success : true, orders })
+
+ }catch(error){
+ return NextResponse.json({success:false, message : error.message})
+ }
+}
\ No newline at end of file
diff --git a/app/api/order/seller-order/route.js b/app/api/order/seller-order/route.js
new file mode 100644
index 000000000..171c62e72
--- /dev/null
+++ b/app/api/order/seller-order/route.js
@@ -0,0 +1,37 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import Order from "@/models/Order";
+import Address from "@/models/Address";
+import Product from "@/models/Product";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function GET(request) {
+ try {
+ const { userId } = getAuth(request);
+ const isSeller = await authSeller(userId);
+
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not authorized" });
+ }
+
+ await connectDb();
+
+ const orders = await Order.find({}).populate('address items.product')
+ .select('customOrderId items address amount subtotal gst deliveryCharges discount paymentMethod paymentStatus status date');
+
+ // Handle backward compatibility for existing orders
+ const ordersWithDefaults = orders.map(order => ({
+ ...order.toObject(),
+ subtotal: order.subtotal || (order.amount ? Math.round(order.amount / 1.18) : 0),
+ gst: order.gst || (order.amount ? Math.round(order.amount - (order.amount / 1.18)) : 0),
+ deliveryCharges: order.deliveryCharges || 0,
+ discount: order.discount || 0
+ }));
+
+ return NextResponse.json({ success : true, orders: ordersWithDefaults})
+
+ } catch (error) {
+ return NextResponse.json({ success : false, message : error.message})
+ }
+}
\ No newline at end of file
diff --git a/app/api/order/send-email/route.js b/app/api/order/send-email/route.js
new file mode 100644
index 000000000..2e38182f3
--- /dev/null
+++ b/app/api/order/send-email/route.js
@@ -0,0 +1,75 @@
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+import connectDb from "@/config/db";
+import Order from "@/models/Order";
+import User from "@/models/Users";
+import Product from "@/models/Product";
+import Address from "@/models/Address";
+
+export async function POST(request) {
+ try {
+ await connectDb();
+ const { userId } = getAuth(request);
+ const { orderId } = await request.json();
+
+ if (!orderId) {
+ return NextResponse.json({ success: false, message: 'Order ID is required' });
+ }
+
+ // Find the order
+ const order = await Order.findById(orderId);
+ if (!order) {
+ return NextResponse.json({ success: false, message: 'Order not found' });
+ }
+
+ // Get user details
+ const user = await User.findById(userId);
+ if (!user) {
+ return NextResponse.json({ success: false, message: 'User not found' });
+ }
+
+ // Get product details
+ const itemsWithDetails = await Promise.all(order.items.map(async (item) => {
+ const product = await Product.findById(item.product);
+ return {
+ name: product?.name || 'Product',
+ quantity: item.quantity,
+ price: product?.offerPrice || 0,
+ color: item.color || null
+ };
+ }));
+
+ // Get address details
+ const addressDetails = await Address.findById(order.address);
+ const formattedAddress = addressDetails ?
+ `${addressDetails.fullName}, ${addressDetails.area}, ${addressDetails.city}, ${addressDetails.state}, ${addressDetails.pincode}`
+ : 'Address not found';
+
+ // Prepare order details for email
+ const orderDetails = {
+ email: user.email,
+ orderNumber: order.customOrderId,
+ totalAmount: order.amount,
+ subtotal: order.subtotal,
+ gst: order.gst,
+ deliveryCharges: order.deliveryCharges,
+ discount: order.discount,
+ items: itemsWithDetails,
+ shippingAddress: formattedAddress,
+ paymentMethod: order.paymentMethod
+ };
+
+ return NextResponse.json({
+ success: true,
+ message: 'Order details retrieved successfully',
+ orderDetails
+ });
+
+ } catch (error) {
+ console.error("Error retrieving order details for email:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message || 'Failed to retrieve order details'
+ });
+ }
+}
diff --git a/app/api/order/track/route.js b/app/api/order/track/route.js
new file mode 100644
index 000000000..a105ae63e
--- /dev/null
+++ b/app/api/order/track/route.js
@@ -0,0 +1,140 @@
+/**
+ * Track Order API
+ * This route provides order tracking information including shipment status
+ */
+
+import Order from '@/models/Order';
+import User from '@/models/Users';
+import { trackShipment } from '@/lib/shiprocket';
+import connectDb from '@/config/db';
+
+export async function GET(request) {
+ try {
+ await connectDb();
+
+ const { searchParams } = new URL(request.url);
+ const orderId = searchParams.get('orderId');
+ const trackingId = searchParams.get('trackingId');
+
+ if (!orderId && !trackingId) {
+ return Response.json({
+ success: false,
+ message: 'Order ID or Tracking ID is required'
+ }, { status: 400 });
+ }
+
+ let order;
+
+ // Find order by ID or tracking ID
+ if (orderId) {
+ order = await Order.findById(orderId).populate('address').populate('items.product');
+ } else if (trackingId) {
+ order = await Order.findOne({
+ $or: [
+ { customOrderId: trackingId },
+ { shiprocketAWB: trackingId }
+ ]
+ }).populate('address').populate('items.product');
+ }
+
+ // Get user information
+ let user = null;
+ if (order && order.userId) {
+ user = await User.findById(order.userId);
+ console.log('User found:', user ? { name: user.name, email: user.email } : 'No user found');
+ }
+
+ console.log('Order address:', order.address ? {
+ fullName: order.address.fullName,
+ area: order.address.area,
+ phoneNumber: order.address.phoneNumber
+ } : 'No address found');
+
+ if (!order) {
+ return Response.json({
+ success: false,
+ message: 'Order not found'
+ }, { status: 404 });
+ }
+
+ // Get real-time tracking data from Shiprocket if available
+ let shiprocketTracking = null;
+ if (order.shiprocketAWB) {
+ try {
+ shiprocketTracking = await trackShipment(order.shiprocketAWB);
+ } catch (error) {
+ console.log('Could not fetch Shiprocket tracking data:', error.message);
+ // Continue without Shiprocket data
+ }
+ }
+
+ // Prepare tracking response
+ const trackingData = {
+ orderId: order._id,
+ customOrderId: order.customOrderId,
+ status: order.status,
+ orderDate: order.date,
+ totalAmount: order.amount,
+ paymentMethod: order.paymentMethod,
+
+ // Customer details
+ customer: {
+ name: user ? user.name : 'N/A',
+ email: user ? user.email : 'N/A',
+ phone: 'N/A' // User model doesn't have phone field
+ },
+
+ // Address details
+ address: order.address ? {
+ name: order.address.fullName,
+ address: order.address.area,
+ city: order.address.city,
+ state: order.address.state,
+ pincode: order.address.pincode,
+ phone: order.address.phoneNumber
+ } : null,
+
+ // Shipment details
+ shipment: {
+ status: order.shipmentStatus || 'PENDING',
+ awb: order.shiprocketAWB || '',
+ courierName: order.courierName || '',
+ trackingUrl: order.trackingUrl || '',
+ shipmentId: order.shipmentId || '',
+ pickupScheduledDate: order.pickupScheduledDate,
+ expectedDeliveryDate: order.expectedDeliveryDate
+ },
+
+ // Real-time tracking from Shiprocket
+ realTimeTracking: shiprocketTracking ? {
+ currentStatus: shiprocketTracking.track_data?.status || 'Unknown',
+ trackingHistory: shiprocketTracking.track_data?.shipment_track || [],
+ lastUpdate: shiprocketTracking.track_data?.shipment_track_activities?.[0] || null
+ } : null,
+
+ // Order items
+ items: order.items.map(item => ({
+ productId: item.product._id,
+ name: item.product.name,
+ quantity: item.quantity,
+ price: item.product.offerPrice,
+ image: item.product.image
+ }))
+ };
+
+ return Response.json({
+ success: true,
+ message: 'Order tracking information retrieved successfully',
+ data: trackingData
+ });
+
+ } catch (error) {
+ console.error('Error tracking order:', error);
+
+ return Response.json({
+ success: false,
+ message: `Failed to track order: ${error.message}`,
+ error: error.message
+ }, { status: 500 });
+ }
+}
diff --git a/app/api/payment/create/route.js b/app/api/payment/create/route.js
new file mode 100644
index 000000000..fea6bd08a
--- /dev/null
+++ b/app/api/payment/create/route.js
@@ -0,0 +1,62 @@
+import { NextResponse } from 'next/server';
+import crypto from 'crypto';
+
+export async function POST(request) {
+ try {
+ const { amount } = await request.json();
+
+ if (!amount) {
+ return NextResponse.json({
+ success: false,
+ message: "Amount is required"
+ });
+ }
+
+ const options = {
+ amount: Math.round(amount * 100), // amount in smallest currency unit (paise for INR)
+ currency: "INR",
+ receipt: "receipt_" + Date.now(),
+ };
+
+ // Check if API keys are configured
+ const keyId = process.env.RAZORPAY_KEY_ID;
+ const keySecret = process.env.RAZORPAY_KEY_SECRET;
+
+ if (!keyId || !keySecret) {
+ throw new Error('Razorpay API keys are not properly configured');
+ }
+
+ const authString = Buffer.from(`${keyId}:${keySecret}`).toString('base64');
+
+ // Create order using Razorpay REST API
+ const response = await fetch('https://api.razorpay.com/v1/orders', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Basic ${authString}`
+ },
+ body: JSON.stringify(options)
+ });
+
+ const order = await response.json();
+
+ if (!response.ok) {
+ throw new Error(`Razorpay API error: ${order.error?.description || 'Unknown error'}`);
+ }
+
+ if (!order.id) {
+ throw new Error('Failed to create Razorpay order: No order ID in response');
+ }
+
+ return NextResponse.json({
+ success: true,
+ order
+ });
+ } catch (error) {
+ console.error("Error creating Razorpay order:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message || "Failed to create payment order"
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/api/payment/verify/route.js b/app/api/payment/verify/route.js
new file mode 100644
index 000000000..fafca5150
--- /dev/null
+++ b/app/api/payment/verify/route.js
@@ -0,0 +1,35 @@
+import crypto from 'crypto';
+import { NextResponse } from 'next/server';
+
+export async function POST(request) {
+ try {
+ const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = await request.json();
+
+ // Verify signature
+ const body = razorpay_order_id + "|" + razorpay_payment_id;
+ const expectedSignature = crypto
+ .createHmac("sha256", process.env.RAZORPAY_KEY_SECRET)
+ .update(body.toString())
+ .digest("hex");
+
+ const isAuthentic = expectedSignature === razorpay_signature;
+
+ if (isAuthentic) {
+ return NextResponse.json({
+ success: true,
+ message: "Payment verified successfully"
+ });
+ } else {
+ return NextResponse.json({
+ success: false,
+ message: "Payment verification failed"
+ });
+ }
+ } catch (error) {
+ console.error("Error verifying payment:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/api/product/[id]/route.js b/app/api/product/[id]/route.js
new file mode 100644
index 000000000..6a6a5e088
--- /dev/null
+++ b/app/api/product/[id]/route.js
@@ -0,0 +1,185 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import Product from "@/models/Product";
+import { getAuth } from "@clerk/nextjs/server";
+import { v2 as cloudinary } from "cloudinary";
+import { NextResponse } from "next/server";
+
+cloudinary.config({
+ cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+ api_key: process.env.CLOUDINARY_API_KEY,
+ api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+
+// Helper function to upload a file to Cloudinary
+const uploadToCloudinary = (buffer) => {
+ return new Promise((resolve, reject) => {
+ const stream = cloudinary.uploader.upload_stream(
+ { resource_type: 'auto' },
+ (error, result) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(result);
+ }
+ }
+ );
+ stream.end(buffer);
+ });
+};
+
+export async function GET(request, { params }) {
+ try {
+ await connectDb();
+ const product = await Product.findById(params.id);
+
+ if (!product) {
+ return NextResponse.json({ success: false, message: "Product not found" }, { status: 404 });
+ }
+
+ return NextResponse.json({ success: true, product });
+ } catch (error) {
+ console.error("Error fetching product:", error);
+ return NextResponse.json({ success: false, message: "Error fetching product" }, { status: 500 });
+ }
+}
+
+export async function PUT(request, { params }) {
+ const { userId } = getAuth(request);
+ if (!userId) {
+ return NextResponse.json({ success: false, message: "Not authenticated" }, { status: 401 });
+ }
+ const isSeller = await authSeller(userId);
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not Authorized" }, { status: 403 });
+ }
+
+ await connectDb();
+ const product = await Product.findById(params.id);
+ if (!product) {
+ return NextResponse.json({ success: false, message: "Product not found" }, { status: 404 });
+ }
+
+ // Ensure the seller owns the product
+ if (product.userId.toString() !== userId) {
+ return NextResponse.json({ success: false, message: "Not authorized to edit this product" }, { status: 403 });
+ }
+
+ try {
+ const formData = await request.formData();
+ const updateData = {};
+
+ const textFields = [
+ 'name', 'description', 'overview', 'additionalInfo', 'idealFor',
+ 'keyFeatures', 'compatibility', 'technicalSpecifications', 'materialType',
+ 'category', 'boxIncludes', 'careInstructions', 'faq1', 'faq2', 'faq3', 'faq4'
+ ];
+ textFields.forEach((field) => {
+ if (formData.has(field)) updateData[field] = formData.get(field);
+ });
+ if (formData.has('price')) updateData.price = Number(formData.get('price'));
+ if (formData.has('offerPrice')) updateData.offerPrice = Number(formData.get('offerPrice'));
+ ['length', 'height', 'depth', 'weight'].forEach((field) => {
+ if (formData.has(field)) {
+ const val = formData.get(field);
+ updateData[field] = val ? Number(val) : undefined;
+ }
+ });
+
+ // Handle colors
+ if (formData.has('colors')) {
+ updateData.colors = JSON.parse(formData.get('colors'));
+ }
+
+ // Merge existing image slots with any newly uploaded files
+ if (formData.has('existingImages')) {
+ const slots = JSON.parse(formData.get('existingImages'));
+ for (let i = 0; i < 4; i++) {
+ const file = formData.get(`imageSlot_${i}`);
+ if (file && typeof file.arrayBuffer === 'function') {
+ const arrayBuffer = await file.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+ const result = await uploadToCloudinary(buffer);
+ slots[i] = result.secure_url;
+ }
+ }
+ updateData.image = slots.filter(Boolean);
+ } else {
+ const newImages = formData.getAll('images');
+ if (newImages.length > 0) {
+ const imageUrls = await Promise.all(
+ newImages.map(async (file) => {
+ const arrayBuffer = await file.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+ const result = await uploadToCloudinary(buffer);
+ return result.secure_url;
+ })
+ );
+ updateData.image = imageUrls;
+ }
+ }
+
+ // Handle color images for selected colors only
+ const colors = updateData.colors || product.colors;
+ const colorImages = {};
+ for (const color of colors) {
+ colorImages[color] = (product.colorImages || {})[color] || null;
+ const colorFile = formData.get(`colorImages[${color}]`);
+ if (colorFile && typeof colorFile.arrayBuffer === 'function') {
+ const arrayBuffer = await colorFile.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+ const result = await uploadToCloudinary(buffer);
+ colorImages[color] = result.secure_url;
+ }
+ }
+ updateData.colorImages = colorImages;
+
+ const updatedProduct = await Product.findByIdAndUpdate(params.id, updateData, { new: true });
+
+ return NextResponse.json({ success: true, message: "Product updated successfully", product: updatedProduct });
+ } catch (error) {
+ console.error("Error updating product:", error);
+ return NextResponse.json({ success: false, message: "Error updating product" }, { status: 500 });
+ }
+}
+
+export async function DELETE(request, { params }) {
+ try {
+ const { userId } = getAuth(request);
+
+ if (!userId) {
+ return NextResponse.json({ success: false, message: "Not authenticated" });
+ }
+
+ const isSeller = await authSeller(userId);
+
+ if (!isSeller) {
+ return NextResponse.json({ success: false, message: "Not Authorized" });
+ }
+
+ await connectDb();
+ const product = await Product.findById(params.id);
+
+ if (!product) {
+ return NextResponse.json({ success: false, message: "Product not found" });
+ }
+
+ // Optional: Verify that the user deleting the product is the one who created it
+ if (product.userId.toString() !== userId) {
+ return NextResponse.json({ success: false, message: "Not authorized to delete this product" });
+ }
+
+ // Delete images from Cloudinary
+ const imagePublicIds = product.image.map(url => url.split('/').pop().split('.')[0]);
+ if (imagePublicIds.length > 0) {
+ await cloudinary.api.delete_resources(imagePublicIds);
+ }
+
+ await Product.findByIdAndDelete(params.id);
+
+ return NextResponse.json({ success: true, message: "Product deleted successfully" });
+ } catch (error) {
+ console.error("Error deleting product:", error);
+ return NextResponse.json({ success: false, message: "Error deleting product" });
+ }
+}
\ No newline at end of file
diff --git a/app/api/product/add/route.js b/app/api/product/add/route.js
new file mode 100644
index 000000000..c920108ab
--- /dev/null
+++ b/app/api/product/add/route.js
@@ -0,0 +1,208 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import Product from "@/models/Product";
+import { getAuth } from "@clerk/nextjs/server";
+import { v2 as cloudinary } from "cloudinary";
+import { NextResponse } from "next/server";
+
+cloudinary.config({
+ cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
+ api_key: process.env.CLOUDINARY_API_KEY,
+ api_secret: process.env.CLOUDINARY_API_SECRET,
+});
+
+export async function POST(request) {
+ try {
+ console.log("Starting product upload process..."); // Debug log
+
+ const { userId } = getAuth(request);
+ console.log("User ID:", userId); // Debug log
+
+ if (!userId) {
+ console.log("No user ID found"); // Debug log
+ return NextResponse.json({ success: false, message: "Not authenticated" });
+ }
+
+ const isSeller = await authSeller(userId);
+ console.log("Is seller:", isSeller); // Debug log
+
+ if (!isSeller) {
+ console.log("User is not a seller"); // Debug log
+ return NextResponse.json({ success: false, message: "Not Authorized" });
+ }
+
+ const formData = await request.formData();
+ console.log("Form data received"); // Debug log
+
+ const name = formData.get('name');
+ const description = formData.get('description');
+ const overview = formData.get('overview');
+ const additionalInfo = formData.get('additionalInfo');
+ const idealFor = formData.get('idealFor');
+ const keyFeatures = formData.get('keyFeatures');
+ const compatibility = formData.get('compatibility');
+ const technicalSpecifications = formData.get('technicalSpecifications');
+ const materialType = formData.get('materialType');
+ const category = formData.get('category');
+ const colorsJson = formData.get('colors');
+ const price = formData.get('price');
+ const offerPrice = formData.get('offerPrice');
+ const length = formData.get('length');
+ const height = formData.get('height');
+ const depth = formData.get('depth');
+ const weight = formData.get('weight');
+ const boxIncludes = formData.get('boxIncludes');
+ const careInstructions = formData.get('careInstructions');
+ const faq1 = formData.get('faq1');
+ const faq2 = formData.get('faq2');
+ const faq3 = formData.get('faq3');
+ const faq4 = formData.get('faq4');
+ const files = formData.getAll('images');
+
+ console.log("Files received:", files.length); // Debug log
+ console.log("Product details:", { name, description, category, colors: colorsJson, price, offerPrice }); // Debug log
+
+ if (!files || files.length === 0) {
+ console.log("No files found in request"); // Debug log
+ return NextResponse.json({ success: false, message: "No files uploaded" });
+ }
+
+ // Parse colors from JSON string
+ let colors = [];
+ try {
+ colors = JSON.parse(colorsJson);
+ } catch (error) {
+ console.log("Error parsing colors JSON"); // Debug log
+ return NextResponse.json({
+ success: false,
+ message: "Invalid colors data"
+ }, { status: 400 });
+ }
+
+ // Validate required fields
+ if (!name || !description || !category || !colors || colors.length === 0 || !price || !offerPrice) {
+ console.log("Missing required fields"); // Debug log
+ return NextResponse.json({
+ success: false,
+ message: "All fields are required and at least one color must be selected"
+ }, { status: 400 });
+ }
+
+ console.log("Uploading files to Cloudinary..."); // Debug log
+ const result = await Promise.all(
+ files.map(async (file) => {
+ try {
+ const arrayBuffer = await file.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+
+ return new Promise((resolve, reject) => {
+ const stream = cloudinary.uploader.upload_stream(
+ { resource_type: 'auto' },
+ (error, result) => {
+ if (error) {
+ console.error("Cloudinary upload error:", error);
+ reject(error);
+ } else {
+ console.log("File uploaded successfully:", result.secure_url); // Debug log
+ resolve(result);
+ }
+ }
+ );
+ stream.end(buffer);
+ });
+ } catch (error) {
+ console.error("Error processing file:", error);
+ throw error;
+ }
+ })
+ );
+
+ const images = result.map(result => result.secure_url);
+ console.log("All files uploaded successfully"); // Debug log
+
+ // Handle color images
+ const colorImages = {};
+ for (const color of colors) {
+ const colorFile = formData.get(`colorImages[${color}]`);
+ if (colorFile) {
+ try {
+ const arrayBuffer = await colorFile.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+ const uploadResult = await new Promise((resolve, reject) => {
+ const stream = cloudinary.uploader.upload_stream(
+ { resource_type: 'auto' },
+ (error, result) => {
+ if (error) reject(error);
+ else resolve(result);
+ }
+ );
+ stream.end(buffer);
+ });
+ colorImages[color] = uploadResult.secure_url;
+ } catch (error) {
+ console.error(`Error uploading color image for ${color}:`, error);
+ colorImages[color] = null;
+ }
+ } else {
+ colorImages[color] = null;
+ }
+ }
+
+ await connectDb();
+ console.log("Database connected"); // Debug log
+
+ console.log("About to create product with colors:", colors); // Debug log - show colors array
+ console.log("Colors type:", typeof colors); // Debug log - show colors type
+ console.log("Colors length:", colors.length); // Debug log - show colors length
+
+ const newProduct = await Product.create({
+ userId,
+ name,
+ description,
+ overview,
+ additionalInfo,
+ idealFor,
+ keyFeatures,
+ compatibility,
+ technicalSpecifications,
+ materialType,
+ category,
+ colors,
+ colorImages,
+ price: Number(price),
+ offerPrice: Number(offerPrice),
+ length: length ? Number(length) : undefined,
+ height: height ? Number(height) : undefined,
+ depth: depth ? Number(depth) : undefined,
+ weight: weight ? Number(weight) : undefined,
+ boxIncludes,
+ careInstructions,
+ faq1,
+ faq2,
+ faq3,
+ faq4,
+ image: images,
+ date: Date.now()
+ });
+
+ console.log("Product created successfully:", newProduct._id); // Debug log
+ console.log("Created product data:", JSON.stringify(newProduct, null, 2)); // Debug log - show full product data
+
+ return NextResponse.json({
+ success: true,
+ message: "Product uploaded successfully",
+ product: newProduct
+ });
+
+ } catch (error) {
+ console.error("Error in product upload:", error);
+ // Log the full error stack
+ console.error("Error stack:", error.stack);
+
+ return NextResponse.json({
+ success: false,
+ message: error.message || "Error uploading product",
+ error: process.env.NODE_ENV === 'development' ? error.stack : undefined
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/product/list/route.js b/app/api/product/list/route.js
new file mode 100644
index 000000000..61e0e5919
--- /dev/null
+++ b/app/api/product/list/route.js
@@ -0,0 +1,13 @@
+import connectDb from "@/config/db"
+import Product from "@/models/Product"
+import { NextResponse } from "next/server"
+export async function GET(request){
+ try{
+
+ await connectDb()
+ const products = await Product.find({})
+ return NextResponse.json({success : true, products })
+ }catch(error){
+ return NextResponse.json({success : false , message : error.message })
+ }
+}
\ No newline at end of file
diff --git a/app/api/product/search/route.js b/app/api/product/search/route.js
new file mode 100644
index 000000000..1e6daa45c
--- /dev/null
+++ b/app/api/product/search/route.js
@@ -0,0 +1,39 @@
+import { NextResponse } from "next/server";
+
+import Product from "@/models/Product";
+import connectDb from "@/config/db";
+
+export async function GET(request) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const query = searchParams.get("query");
+
+ if (!query) {
+ return NextResponse.json(
+ { error: "Search query is required" },
+ { status: 400 }
+ );
+ }
+
+ await connectDb()
+
+ // Search in product name, description, and category
+ const products = await Product.find({
+ $or: [
+ { name: { $regex: query, $options: "i" } },
+ { description: { $regex: query, $options: "i" } },
+ { category: { $regex: query, $options: "i" } },
+ ],
+ })
+ .select("name price images description")
+ .limit(10);
+
+ return NextResponse.json({ products });
+ } catch (error) {
+ console.error("Search error:", error);
+ return NextResponse.json(
+ { error: "Failed to search products" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/api/product/seller-list/route.js b/app/api/product/seller-list/route.js
new file mode 100644
index 000000000..c17ce774b
--- /dev/null
+++ b/app/api/product/seller-list/route.js
@@ -0,0 +1,22 @@
+import connectDb from "@/config/db";
+import authSeller from "@/lib/authSeller";
+import Product from "@/models/Product";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function GET(request) {
+ try{
+ const{userId} = getAuth(request)
+ const isSeller = authSeller(userId)
+
+ if(!isSeller){
+ return NextResponse.json({success : false, message : 'not authorized'})
+ }
+ await connectDb()
+ const products = await Product.find({})
+ return NextResponse.json({ success : true, products})
+
+ }catch(error){
+ return NextResponse.json({success : false, message : error.message})
+ }
+}
\ No newline at end of file
diff --git a/app/api/shiprocket/create-shipment/route.js b/app/api/shiprocket/create-shipment/route.js
new file mode 100644
index 000000000..894a8cb5a
--- /dev/null
+++ b/app/api/shiprocket/create-shipment/route.js
@@ -0,0 +1,171 @@
+/**
+ * Create Shiprocket Shipment
+ * This route creates a shipment in Shiprocket after order placement
+ */
+
+import { createShipment, getShippingRates } from '@/lib/shiprocket';
+import Order from '@/models/Order';
+import Product from '@/models/Product';
+import Address from '@/models/Address';
+import User from '@/models/Users';
+import connectDb from '@/config/db';
+
+export async function POST(request) {
+ try {
+ await connectDb();
+
+ const { orderId, courierId } = await request.json();
+
+ if (!orderId) {
+ return Response.json({
+ success: false,
+ message: 'Order ID is required'
+ }, { status: 400 });
+ }
+
+ // Find the order with populated data
+ const order = await Order.findById(orderId)
+ .populate('address')
+ .populate('items.product');
+
+ if (!order) {
+ return Response.json({
+ success: false,
+ message: 'Order not found'
+ }, { status: 404 });
+ }
+
+ // Get user details
+ const user = await User.findById(order.userId);
+ if (!user) {
+ return Response.json({
+ success: false,
+ message: 'User not found'
+ }, { status: 404 });
+ }
+
+ // Get address details
+ const address = await Address.findById(order.address);
+ if (!address) {
+ return Response.json({
+ success: false,
+ message: 'Address not found'
+ }, { status: 404 });
+ }
+
+ // Prepare shipment data for Shiprocket
+ const shipmentData = {
+ order_id: order.customOrderId || orderId,
+ order_date: new Date(order.date).toISOString().split('T')[0],
+ pickup_location: "warehouse", // Your actual pickup location identifier from Shiprocket
+ billing_customer_name: address.fullName || user.name || "Customer",
+ billing_last_name: "",
+ billing_address: `${address.area}, ${address.city}, ${address.state} ${address.pincode}, India`,
+ billing_address_2: "",
+ billing_city: address.city,
+ billing_pincode: address.pincode,
+ billing_state: address.state,
+ billing_country: "India",
+ billing_email: user.email,
+ billing_phone: address.phoneNumber || user.phone || "8750461279",
+ shipping_is_billing: true,
+ order_items: order.items.map(item => ({
+ name: item.product.name,
+ sku: item.product._id.toString(),
+ units: item.quantity,
+ selling_price: item.product.offerPrice,
+ discount: item.product.price - item.product.offerPrice,
+ tax: Math.floor(item.product.offerPrice * 0.18),
+ hsn: "8518", // Default HSN for electronics
+ product_type: "standard"
+ })),
+ payment_method: order.paymentMethod === 'COD' ? 'COD' : 'Prepaid',
+ sub_total: order.subtotal,
+ length: 10,
+ breadth: 10,
+ height: 5,
+ weight: 0.5,
+ shipping_charges: order.deliveryCharges,
+ total_discount: order.discount,
+ cod_amount: order.paymentMethod === 'COD' ? order.amount : 0
+ };
+
+ console.log('Creating Shiprocket shipment for order:', orderId);
+ console.log('Shipment data:', JSON.stringify(shipmentData, null, 2));
+
+ // Validate required fields
+ const requiredFields = ['order_id', 'pickup_location', 'billing_customer_name', 'billing_address', 'billing_city', 'billing_pincode', 'billing_state', 'billing_country', 'billing_email', 'billing_phone'];
+ const missingFields = requiredFields.filter(field => !shipmentData[field]);
+
+ if (missingFields.length > 0) {
+ console.error('Missing required fields:', missingFields);
+ return Response.json({
+ success: false,
+ message: `Missing required fields: ${missingFields.join(', ')}`
+ }, { status: 400 });
+ }
+
+ // Create shipment in Shiprocket
+ const shipmentResponse = await createShipment(shipmentData);
+
+ console.log('Full Shiprocket response:', JSON.stringify(shipmentResponse, null, 2));
+
+ // Handle different response structures from Shiprocket
+ // Check if response has shipment_id (successful creation) or if it's wrapped in status/data
+ let shipment;
+ if (shipmentResponse.shipment_id) {
+ // Direct response format (what we're getting)
+ shipment = shipmentResponse;
+ } else if (shipmentResponse.status === 201 && shipmentResponse.data) {
+ // Wrapped response format
+ shipment = shipmentResponse.data;
+ } else {
+ console.error('Shiprocket API returned unexpected response:', shipmentResponse);
+ throw new Error(`Shiprocket API error: ${shipmentResponse.message || shipmentResponse.error || JSON.stringify(shipmentResponse)}`);
+ }
+
+ // Update order with Shiprocket details
+ await Order.findByIdAndUpdate(orderId, {
+ shiprocketAWB: shipment.awb_code || '',
+ shipmentId: shipment.shipment_id ? shipment.shipment_id.toString() : '',
+ courierName: shipment.courier_name || '',
+ trackingUrl: shipment.awb_code ? `https://www.shiprocket.in/tracking/${shipment.awb_code}` : '',
+ shipmentStatus: shipment.status || 'NEW',
+ pickupScheduledDate: shipment.pickup_scheduled_date ? new Date(shipment.pickup_scheduled_date) : null,
+ expectedDeliveryDate: shipment.expected_delivery_date ? new Date(shipment.expected_delivery_date) : null
+ });
+
+ console.log('Shipment created successfully:', {
+ orderId,
+ shiprocketOrderId: shipment.order_id,
+ shipmentId: shipment.shipment_id,
+ channelOrderId: shipment.channel_order_id,
+ status: shipment.status,
+ awb: shipment.awb_code
+ });
+
+ return Response.json({
+ success: true,
+ message: 'Shipment created successfully',
+ data: {
+ shiprocketOrderId: shipment.order_id,
+ shipmentId: shipment.shipment_id,
+ channelOrderId: shipment.channel_order_id,
+ status: shipment.status,
+ awb: shipment.awb_code || '',
+ courier: shipment.courier_name || '',
+ trackingUrl: shipment.awb_code ? `https://www.shiprocket.in/tracking/${shipment.awb_code}` : '',
+ pickupDate: shipment.pickup_scheduled_date,
+ expectedDelivery: shipment.expected_delivery_date
+ }
+ });
+
+ } catch (error) {
+ console.error('Error creating shipment:', error);
+ return Response.json({
+ success: false,
+ message: `Failed to create shipment: ${error.message}`,
+ error: error.message
+ }, { status: 500 });
+ }
+}
diff --git a/app/api/shiprocket/manual-test-order/route.js b/app/api/shiprocket/manual-test-order/route.js
new file mode 100644
index 000000000..33d28f4a3
--- /dev/null
+++ b/app/api/shiprocket/manual-test-order/route.js
@@ -0,0 +1,76 @@
+/**
+ * Manual Test Order Creation in Shiprocket
+ * Uses exact data from your real order
+ */
+
+import { createShipment } from '@/lib/shiprocket';
+
+export async function POST() {
+ try {
+ console.log('Creating manual test order in Shiprocket with real order data...');
+
+ // Use the exact data from your order
+ const manualOrderData = {
+ order_id: "MANUAL-TEST-25-26/W/0263",
+ order_date: new Date().toISOString().split('T')[0],
+ pickup_location: "warehouse",
+ billing_customer_name: "King Kong",
+ billing_last_name: "",
+ billing_address: "Bidholi Uk D, dehradoon, Uttarakhand 248007, India",
+ billing_address_2: "",
+ billing_city: "dehradoon",
+ billing_pincode: "248007",
+ billing_state: "Uttarakhand",
+ billing_country: "India",
+ billing_email: "8750461279a@gmail.com",
+ billing_phone: "09104411978",
+ shipping_is_billing: true,
+ order_items: [
+ {
+ name: "Adjustable 3D Printed Phone Stand – Ergonomic and Foldable Mobile Holder with Gears – Stylish Geometric Design – Compatible with All Smartphones (Magenta and White",
+ sku: "686e137f55572d5e2c73ae73",
+ units: 1,
+ selling_price: 999,
+ discount: 0,
+ tax: 180,
+ hsn: "8518",
+ product_type: "standard"
+ }
+ ],
+ payment_method: "COD",
+ sub_total: 999,
+ length: 10,
+ breadth: 10,
+ height: 5,
+ weight: 0.5,
+ shipping_charges: 179,
+ total_discount: 0,
+ cod_amount: 1178
+ };
+
+ console.log('Manual order data:', JSON.stringify(manualOrderData, null, 2));
+
+ // Create order in Shiprocket
+ const response = await createShipment(manualOrderData);
+
+ console.log('Manual Shiprocket response:', JSON.stringify(response, null, 2));
+
+ return Response.json({
+ success: true,
+ message: 'Manual test order created successfully',
+ data: {
+ manualOrderId: manualOrderData.order_id,
+ shiprocketResponse: response
+ }
+ });
+
+ } catch (error) {
+ console.error('Error creating manual test order:', error);
+
+ return Response.json({
+ success: false,
+ message: `Failed to create manual test order: ${error.message}`,
+ error: error.message
+ }, { status: 500 });
+ }
+}
diff --git a/app/api/shiprocket/pickup-locations/route.js b/app/api/shiprocket/pickup-locations/route.js
new file mode 100644
index 000000000..dd6b3019c
--- /dev/null
+++ b/app/api/shiprocket/pickup-locations/route.js
@@ -0,0 +1,35 @@
+/**
+ * Get Pickup Locations from Shiprocket
+ * This route fetches available pickup locations from Shiprocket
+ */
+
+import { getPickupLocations } from '@/lib/shiprocket';
+import connectDb from '@/config/db';
+
+export async function GET() {
+ try {
+ await connectDb();
+
+ console.log('Fetching pickup locations from Shiprocket...');
+
+ // Get pickup locations from Shiprocket
+ const pickupLocations = await getPickupLocations();
+
+ console.log('Pickup locations fetched:', pickupLocations);
+
+ return Response.json({
+ success: true,
+ message: 'Pickup locations fetched successfully',
+ data: pickupLocations
+ });
+
+ } catch (error) {
+ console.error('Error fetching pickup locations:', error);
+
+ return Response.json({
+ success: false,
+ message: `Failed to fetch pickup locations: ${error.message}`,
+ error: error.message
+ }, { status: 500 });
+ }
+}
diff --git a/app/api/shiprocket/test-create-order/route.js b/app/api/shiprocket/test-create-order/route.js
new file mode 100644
index 000000000..4435463bc
--- /dev/null
+++ b/app/api/shiprocket/test-create-order/route.js
@@ -0,0 +1,76 @@
+/**
+ * Test Create Order in Shiprocket
+ * This route manually creates a test order in Shiprocket dashboard
+ */
+
+import { createShipment } from '@/lib/shiprocket';
+
+export async function POST() {
+ try {
+ console.log('Creating test order in Shiprocket...');
+
+ // Test order data
+ const testOrderData = {
+ order_id: "TEST-" + Date.now(),
+ order_date: new Date().toISOString().split('T')[0],
+ pickup_location: "warehouse",
+ billing_customer_name: "Test Customer",
+ billing_last_name: "",
+ billing_address: "123 Test Street, Dehradun, Uttarakhand 248007, India",
+ billing_address_2: "",
+ billing_city: "Dehradun",
+ billing_pincode: "248007",
+ billing_state: "Uttarakhand",
+ billing_country: "India",
+ billing_email: "test@example.com",
+ billing_phone: "9876543210",
+ shipping_is_billing: true,
+ order_items: [
+ {
+ name: "Test Product",
+ sku: "TEST-SKU-001",
+ units: 1,
+ selling_price: 999,
+ discount: 0,
+ tax: 180,
+ hsn: "8518",
+ product_type: "standard"
+ }
+ ],
+ payment_method: "COD",
+ sub_total: 999,
+ length: 10,
+ breadth: 10,
+ height: 5,
+ weight: 0.5,
+ shipping_charges: 50,
+ total_discount: 0,
+ cod_amount: 1049
+ };
+
+ console.log('Test order data:', JSON.stringify(testOrderData, null, 2));
+
+ // Create order in Shiprocket
+ const response = await createShipment(testOrderData);
+
+ console.log('Shiprocket response:', JSON.stringify(response, null, 2));
+
+ return Response.json({
+ success: true,
+ message: 'Test order created successfully',
+ data: {
+ testOrderId: testOrderData.order_id,
+ shiprocketResponse: response
+ }
+ });
+
+ } catch (error) {
+ console.error('Error creating test order:', error);
+
+ return Response.json({
+ success: false,
+ message: `Failed to create test order: ${error.message}`,
+ error: error.message
+ }, { status: 500 });
+ }
+}
diff --git a/app/api/shiprocket/test/route.js b/app/api/shiprocket/test/route.js
new file mode 100644
index 000000000..f2803b44d
--- /dev/null
+++ b/app/api/shiprocket/test/route.js
@@ -0,0 +1,36 @@
+/**
+ * Test Shiprocket API Connection
+ * This route tests the Shiprocket integration
+ */
+
+import { testConnection } from '@/lib/shiprocket';
+
+export async function GET() {
+ try {
+ console.log('Testing Shiprocket connection...');
+
+ const result = await testConnection();
+
+ if (result.success) {
+ return Response.json({
+ success: true,
+ message: result.message,
+ token: result.token,
+ timestamp: new Date().toISOString()
+ });
+ } else {
+ return Response.json({
+ success: false,
+ message: result.message,
+ timestamp: new Date().toISOString()
+ }, { status: 500 });
+ }
+ } catch (error) {
+ console.error('Shiprocket test error:', error);
+ return Response.json({
+ success: false,
+ message: `Test failed: ${error.message}`,
+ timestamp: new Date().toISOString()
+ }, { status: 500 });
+ }
+}
diff --git a/app/api/test-full-order-flow/route.js b/app/api/test-full-order-flow/route.js
new file mode 100644
index 000000000..619abc653
--- /dev/null
+++ b/app/api/test-full-order-flow/route.js
@@ -0,0 +1,157 @@
+/**
+ * Test Full Order Flow
+ * Creates order in database and then in Shiprocket
+ */
+
+import { NextResponse } from "next/server";
+import connectDb from "@/config/db";
+import Order from "@/models/Order";
+import User from "@/models/Users";
+import Address from "@/models/Address";
+import Product from "@/models/Product";
+import { createShipment } from '@/lib/shiprocket';
+
+export async function POST() {
+ try {
+ await connectDb();
+
+ console.log('Testing full order flow...');
+
+ // Find or create test user
+ let testUser = await User.findOne({ email: 'test@example.com' });
+ if (!testUser) {
+ testUser = await User.create({
+ _id: `test-user-${Date.now()}`,
+ name: 'Test Customer',
+ email: 'test@example.com',
+ imageUrl: 'https://example.com/test-avatar.jpg',
+ cartItems: {}
+ });
+ }
+
+ // Find or create test address
+ let testAddress = await Address.findOne({ userId: testUser._id.toString() });
+ if (!testAddress) {
+ testAddress = await Address.create({
+ userId: testUser._id.toString(),
+ fullName: 'Test Customer',
+ phoneNumber: '9876543210',
+ pincode: 248007,
+ area: 'Test Area',
+ city: 'Dehradun',
+ state: 'Uttarakhand'
+ });
+ }
+
+ // Find a test product
+ const testProduct = await Product.findOne();
+ if (!testProduct) {
+ return NextResponse.json({
+ success: false,
+ message: 'No products found in database'
+ }, { status: 400 });
+ }
+
+ // Create test order in database
+ const testOrder = await Order.create({
+ customOrderId: `TEST-DB-${Date.now()}`,
+ userId: testUser._id.toString(),
+ address: testAddress._id,
+ items: [{
+ product: testProduct._id,
+ quantity: 1
+ }],
+ amount: 999,
+ subtotal: 999,
+ deliveryCharges: 50,
+ discount: 0,
+ paymentMethod: 'COD',
+ paymentStatus: 'PENDING',
+ status: 'PENDING'
+ });
+
+ console.log('Test order created in database:', testOrder);
+
+ // Now send to Shiprocket
+ const orderWithPopulated = await Order.findById(testOrder._id)
+ .populate('address')
+ .populate('items.product');
+
+ const shipmentData = {
+ order_id: orderWithPopulated.customOrderId,
+ order_date: new Date(orderWithPopulated.date).toISOString().split('T')[0],
+ pickup_location: "warehouse",
+ billing_customer_name: orderWithPopulated.address.fullName,
+ billing_last_name: "",
+ billing_address: `${orderWithPopulated.address.area}, ${orderWithPopulated.address.city}, ${orderWithPopulated.address.state} ${orderWithPopulated.address.pincode}, India`,
+ billing_address_2: "",
+ billing_city: orderWithPopulated.address.city,
+ billing_pincode: orderWithPopulated.address.pincode,
+ billing_state: orderWithPopulated.address.state,
+ billing_country: "India",
+ billing_email: testUser.email,
+ billing_phone: orderWithPopulated.address.phoneNumber,
+ shipping_is_billing: true,
+ order_items: orderWithPopulated.items.map(item => ({
+ name: item.product.name,
+ sku: item.product._id.toString(),
+ units: item.quantity,
+ selling_price: item.product.offerPrice,
+ discount: item.product.price - item.product.offerPrice,
+ tax: Math.floor(item.product.offerPrice * 0.18),
+ hsn: "8518",
+ product_type: "standard"
+ })),
+ payment_method: orderWithPopulated.paymentMethod === 'COD' ? 'COD' : 'Prepaid',
+ sub_total: orderWithPopulated.subtotal,
+ length: 10,
+ breadth: 10,
+ height: 5,
+ weight: 0.5,
+ shipping_charges: orderWithPopulated.deliveryCharges,
+ total_discount: orderWithPopulated.discount,
+ cod_amount: orderWithPopulated.paymentMethod === 'COD' ? orderWithPopulated.amount : 0
+ };
+
+ console.log('Shipment data:', JSON.stringify(shipmentData, null, 2));
+
+ const shiprocketResponse = await createShipment(shipmentData);
+ console.log('Shiprocket response:', shiprocketResponse);
+
+ // Update order with Shiprocket details
+ let shipment;
+ if (shiprocketResponse.shipment_id) {
+ shipment = shiprocketResponse;
+ } else if (shiprocketResponse.status === 201 && shiprocketResponse.data) {
+ shipment = shiprocketResponse.data;
+ }
+
+ if (shipment) {
+ await Order.findByIdAndUpdate(testOrder._id, {
+ shiprocketAWB: shipment.awb_code || '',
+ shipmentId: shipment.shipment_id ? shipment.shipment_id.toString() : '',
+ courierName: shipment.courier_name || '',
+ trackingUrl: shipment.awb_code ? `https://www.shiprocket.in/tracking/${shipment.awb_code}` : '',
+ shipmentStatus: shipment.status || 'NEW'
+ });
+ }
+
+ return NextResponse.json({
+ success: true,
+ message: 'Full order flow test completed',
+ data: {
+ orderId: testOrder._id,
+ customOrderId: testOrder.customOrderId,
+ shiprocketResponse: shiprocketResponse
+ }
+ });
+
+ } catch (error) {
+ console.error('Error in full order flow test:', error);
+ return NextResponse.json({
+ success: false,
+ message: `Test failed: ${error.message}`,
+ error: error.message
+ }, { status: 500 });
+ }
+}
diff --git a/app/api/test-order-id/route.js b/app/api/test-order-id/route.js
new file mode 100644
index 000000000..fcf54c45a
--- /dev/null
+++ b/app/api/test-order-id/route.js
@@ -0,0 +1,25 @@
+import { generateCustomOrderId } from "@/lib/orderIdGenerator";
+import { NextResponse } from "next/server";
+
+export async function GET() {
+ try {
+ console.log('Testing generateCustomOrderId function...');
+
+ const customOrderId = await generateCustomOrderId();
+
+ console.log('Test result:', customOrderId);
+
+ return NextResponse.json({
+ success: true,
+ customOrderId,
+ type: typeof customOrderId,
+ length: customOrderId.length
+ });
+ } catch (error) {
+ console.error('Test failed:', error);
+ return NextResponse.json({
+ success: false,
+ error: error.message
+ });
+ }
+}
diff --git a/app/api/user/add-address/route.js b/app/api/user/add-address/route.js
new file mode 100644
index 000000000..d027ea1cf
--- /dev/null
+++ b/app/api/user/add-address/route.js
@@ -0,0 +1,44 @@
+import connectDb from "@/config/db";
+import Address from "@/models/Address";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function POST(request) {
+ try {
+ // Fix 1: Pass the request object to getAuth
+ const { userId } = getAuth(request);
+
+ // Fix 2: Check if userId exists
+ if (!userId) {
+ return NextResponse.json({
+ success: false,
+ message: "Unauthorized - User not authenticated"
+ }, { status: 401 });
+ }
+
+ const { address } = await request.json();
+
+ // Fix 3: Validate address data
+ if (!address) {
+ return NextResponse.json({
+ success: false,
+ message: "Address data is required"
+ }, { status: 400 });
+ }
+
+ await connectDb();
+ const newAddress = await Address.create({ ...address, userId });
+
+ return NextResponse.json({
+ success: true,
+ message: "Address added successfully", // Fixed typo: "cart" -> "successfully"
+ newAddress
+ });
+ } catch (error) {
+ console.error("Error adding address:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/user/data/route.js b/app/api/user/data/route.js
new file mode 100644
index 000000000..a34f79055
--- /dev/null
+++ b/app/api/user/data/route.js
@@ -0,0 +1,55 @@
+import connectDb from "@/config/db";
+import User from "@/models/Users";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function GET(request){
+ try{
+ const{userId} = getAuth(request)
+
+ if (!userId) {
+ return NextResponse.json({
+ success: false,
+ message: "User not authenticated"
+ }, { status: 401 });
+ }
+
+ await connectDb()
+ let user = await User.findById(userId)
+
+ // If user doesn't exist in database, create them
+ if(!user){
+ // Get user data from Clerk
+ const { clerkClient } = await import('@clerk/nextjs/server');
+ const clerkUser = await clerkClient.users.getUser(userId);
+
+ if (!clerkUser) {
+ return NextResponse.json({
+ success: false,
+ message: "User not found in Clerk"
+ }, { status: 404 });
+ }
+
+ // Create user in database
+ const userData = {
+ _id: userId,
+ email: clerkUser.emailAddresses[0]?.emailAddress || '',
+ name: `${clerkUser.firstName || ''} ${clerkUser.lastName || ''}`.trim(),
+ imageUrl: clerkUser.imageUrl || '',
+ cartItems: {}
+ };
+
+ user = await User.create(userData);
+ }
+
+ return NextResponse.json({success: true, user})
+
+ }catch(error){
+ console.error("Error in user data route:", error);
+ return NextResponse.json({
+ success: false,
+ message: "Failed to fetch user data",
+ error: error.message
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/api/user/get-address/route.js b/app/api/user/get-address/route.js
new file mode 100644
index 000000000..c25c4847a
--- /dev/null
+++ b/app/api/user/get-address/route.js
@@ -0,0 +1,33 @@
+import connectDb from "@/config/db";
+import Address from "@/models/Address";
+import { getAuth } from "@clerk/nextjs/server";
+import { NextResponse } from "next/server";
+
+export async function GET(request) {
+ try {
+ // Fix 1: Pass the request object to getAuth
+ const { userId } = getAuth(request);
+
+ // Fix 2: Check if userId exists
+ if (!userId) {
+ return NextResponse.json({
+ success: false,
+ message: "Unauthorized - User not authenticated"
+ }, { status: 401 });
+ }
+
+ await connectDb();
+ const addresses = await Address.find({ userId });
+
+ return NextResponse.json({
+ success: true,
+ addresses
+ });
+ } catch (error) {
+ console.error("Error fetching addresses:", error);
+ return NextResponse.json({
+ success: false,
+ message: error.message
+ }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/cancellation/page.jsx b/app/cancellation/page.jsx
new file mode 100644
index 000000000..cd0134251
--- /dev/null
+++ b/app/cancellation/page.jsx
@@ -0,0 +1,40 @@
+'use client'
+import React from "react";
+import Navbar from "@/components/Navbar";
+import Footer from "@/components/Footer";
+
+const CancellationPolicy = () => {
+ return (
+ <>
+
+
+
+
+
Cancellation & Refund
+
+
+
FILAMENT FREAKS is committed to providing its customers with a fair and accommodating cancellation and refund policy. The terms of this policy are as follows:
+
+
Cancellation requests will be considered only if submitted within seven (7) days from the date of placing the order. However, such requests may not be accepted if the order has already been communicated to the vendor/merchant and the shipping process has commenced.
+
In cases where the product is ready for shipment:
+
+
For online payments, a security charge shall be deducted from the refund amount.
+
For Cash on Delivery (COD) orders, the cancellation request will be declined.
+
+
+
For items that are damaged or defective upon delivery, the issue must be reported to our Customer Service team within seven (7) days of receipt. The resolution will be provided only after the merchant has reviewed and confirmed the nature of the defect.
+
If the product received does not match its description or does not meet the customer's expectations, the customer must notify our Customer Service team within seven (7) days of delivery. After reviewing the complaint, an appropriate course of action will be taken by the team.
+
For products covered under a manufacturer's warranty, any complaints should be directed to the respective manufacturer as per their warranty policy.
+
In cases where refunds are approved by FILAMENT FREAKS, the amount will be processed within three (3) to five (5) business days from the date of approval.
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default CancellationPolicy;
\ No newline at end of file
diff --git a/app/cart/page.jsx b/app/cart/page.jsx
index 7a54084b1..bb56cadaf 100644
--- a/app/cart/page.jsx
+++ b/app/cart/page.jsx
@@ -40,13 +40,21 @@ const Cart = () => {
- {Object.keys(cartItems).map((itemId) => {
- const product = products.find(product => product._id === itemId);
+ {Object.keys(cartItems).map((cartKey) => {
+ // Extract productId and color from cartKey (format: productId_color or just productId)
+ const [productId, selectedColor] = cartKey.includes('_') ? cartKey.split('_') : [cartKey, null];
+ const product = products.find(product => product._id === productId);
+ const cartItem = cartItems[cartKey];
- if (!product || cartItems[itemId] <= 0) return null;
+ // Handle both old format (number) and new format (object)
+ const quantity = typeof cartItem === 'number' ? cartItem : cartItem?.quantity || 0;
+ const color = typeof cartItem === 'object' ? cartItem?.color : null;
+ const colorImage = typeof cartItem === 'object' ? cartItem?.colorImage : null;
+
+ if (!product || quantity <= 0) return null;
return (
-
+ You will be automatically redirected to your orders page in a few seconds...
+
)
}
diff --git a/app/page.jsx b/app/page.jsx
index 0773aa1dd..fecfdd9a1 100644
--- a/app/page.jsx
+++ b/app/page.jsx
@@ -7,10 +7,24 @@ import NewsLetter from "@/components/NewsLetter";
import FeaturedProduct from "@/components/FeaturedProduct";
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
+import Image from "next/image";
+import { assets } from "@/assets/assets";
+import SearchModal from "@/components/SearchModal";
const Home = () => {
+
return (
<>
+
+
+
+
+
+
diff --git a/app/policies/cancellation/page.jsx b/app/policies/cancellation/page.jsx
new file mode 100644
index 000000000..cd0134251
--- /dev/null
+++ b/app/policies/cancellation/page.jsx
@@ -0,0 +1,40 @@
+'use client'
+import React from "react";
+import Navbar from "@/components/Navbar";
+import Footer from "@/components/Footer";
+
+const CancellationPolicy = () => {
+ return (
+ <>
+
+
+
+
+
Cancellation & Refund
+
+
+
FILAMENT FREAKS is committed to providing its customers with a fair and accommodating cancellation and refund policy. The terms of this policy are as follows:
+
+
Cancellation requests will be considered only if submitted within seven (7) days from the date of placing the order. However, such requests may not be accepted if the order has already been communicated to the vendor/merchant and the shipping process has commenced.
+
In cases where the product is ready for shipment:
+
+
For online payments, a security charge shall be deducted from the refund amount.
+
For Cash on Delivery (COD) orders, the cancellation request will be declined.
+
+
+
For items that are damaged or defective upon delivery, the issue must be reported to our Customer Service team within seven (7) days of receipt. The resolution will be provided only after the merchant has reviewed and confirmed the nature of the defect.
+
If the product received does not match its description or does not meet the customer's expectations, the customer must notify our Customer Service team within seven (7) days of delivery. After reviewing the complaint, an appropriate course of action will be taken by the team.
+
For products covered under a manufacturer's warranty, any complaints should be directed to the respective manufacturer as per their warranty policy.
+
In cases where refunds are approved by FILAMENT FREAKS, the amount will be processed within three (3) to five (5) business days from the date of approval.
+ For the purpose of these Terms and Conditions, the terms "we," "us," or "our" shall refer to FILAMENT FREAKS,
+ whose registered/operational office is located at 532 SECTOR 19, CHOPASNI HOUSING BOARD, JODHPUR, RAJASTHAN 342008.
+ The terms "you," "your," "user," or "visitor" shall refer to any natural or legal person accessing our website
+ and/or purchasing products or services from us.
+
+
+
+ Your use of this website and/or your purchases from us are governed by the following Terms and Conditions:
+
+
+
+
The content published on this website is subject to change at any time without prior notice.
+
Neither FILAMENT FREAKS nor any third-party entities provide warranties or guarantees regarding the accuracy, timeliness, performance, completeness, or suitability of the content, information, or materials provided on this website for any particular purpose.
+
Users acknowledge that such content may contain inaccuracies or errors, and FILAMENT FREAKS expressly excludes liability for any such inaccuracies to the fullest extent permitted by law.
+
Any use of information, materials, or services from this website or related products is entirely at the user's own risk. FILAMENT FREAKS shall not be held liable, and it is the user's responsibility to ensure that any products, services, or information obtained through this website meet their specific requirements.
+
All content available on this website—including but not limited to design, layout, appearance, graphics, and written content—is either owned by or licensed to FILAMENT FREAKS.
+
Reproduction of any website material is strictly prohibited, except in accordance with the copyright notice that forms part of these Terms and Conditions.
+
All trademarks displayed on this website, which are not owned by or licensed to FILAMENT FREAKS, are acknowledged appropriately.
+
Unauthorized use of this website or its contents may give rise to a claim for damages and/or constitute a criminal offense.
+
This website may include links to external websites for informational purposes and user convenience. These links do not signify any endorsement by FILAMENT FREAKS, and we bear no responsibility for the content of the linked websites.
+
Users may not create a hyperlink to this website from another website or document without obtaining prior written permission from FILAMENT FREAKS.
+
Any disputes arising out of or in connection with the use of this website or any purchases made through it shall be governed by the laws of India.
+
FILAMENT FREAKS shall not be responsible for any loss or damage incurred—directly or indirectly—due to declined authorization for any transaction by your card issuer or acquiring bank.
+
+
+
+ )}
+
+ {activeSection === "cancellation" && (
+
+
Cancellation & Refund
+
+
+ FILAMENT FREAKS is committed to providing its customers with a fair and accommodating cancellation and refund policy.
+ The terms of this policy are as follows:
+
+
+
+
Cancellation requests will be considered only if submitted within seven (7) days from the date of placing the order. However, such requests may not be accepted if the order has already been communicated to the vendor/merchant and the shipping process has commenced.
+
In cases where the product is ready for shipment:
+
+
For online payments, a security charge shall be deducted from the refund amount.
+
For Cash on Delivery (COD) orders, the cancellation request will be declined.
+
+
+
For items that are damaged or defective upon delivery, the issue must be reported to our Customer Service team within seven (7) days of receipt. The resolution will be provided only after the merchant has reviewed and confirmed the nature of the defect.
+
If the product received does not match its description or does not meet the customer's expectations, the customer must notify our Customer Service team within seven (7) days of delivery. After reviewing the complaint, an appropriate course of action will be taken by the team.
+
For products covered under a manufacturer's warranty, any complaints should be directed to the respective manufacturer as per their warranty policy.
+
In cases where refunds are approved by FILAMENT FREAKS, the amount will be processed within three (3) to five (5) business days from the date of approval.
+
+
+
+ )}
+
+ {activeSection === "shipping" && (
+
+
Shipping & Delivery
+
+
+
For international orders, shipments are dispatched and delivered via authorized international courier services and/or international speed post services only.
+
For domestic orders, shipments are dispatched using registered domestic courier partners and/or speed post services exclusively.
+
Delivery is expected within 0 to 7 days, or as per the delivery date agreed upon during order confirmation.
+
Delivery timelines are subject to the operational norms of the designated courier or postal service provider.
+
FILAMENT FREAKS shall not be held liable for any delays in delivery caused by courier agencies or postal authorities.
+
Our responsibility is limited to ensuring the consignment is handed over to the courier or postal service within 0 to 7 days of order and payment, or as per the mutually agreed schedule at the time of order placement.
+
All orders will be delivered to the shipping address provided by the buyer at the time of registration.
+
Order and delivery confirmations will be sent to the buyer via the email address submitted during registration.
+
For any issues or support regarding our services, customers can contact us at 8005833266 or email freaksfilament@gmail.com.
+
+
+
+ )}
+
+ {activeSection === "privacy" && (
+
+
Privacy Policies
+
+
+ FILAMENT FREAKS is committed to safeguarding the privacy of all users interacting with our website and purchasing our 3D printed household appliances.
+ The following policy outlines how we collect, use, share, and protect personal information:
+
+
+
+
+
Information Collection and Use
+
+ Personal information is collected solely for the purpose of order fulfillment, service communication, customer support, and business-related improvements.
+ This may include name, email address, contact number, billing and shipping addresses, and payment-related details (processed securely via trusted payment gateways).
+
+
+ We do not collect or store any sensitive financial details such as CVV numbers or bank credentials.
+ All online transactions are handled via secure, PCI-DSS-compliant payment partners.
+
+
+
+
+
Use of Information
+
The information collected is used to:
+
+
Process and deliver your order.
+
Notify you of order status and shipment tracking.
+
Manage refunds, cancellations, and replacement requests as per our policies.
+
Provide customer support and service-related communications.
+
Send promotional updates only if explicitly permitted by the customer.
+
Maintain internal business records and comply with applicable laws.
+
+
+
+
+
Information Sharing
+
Your personal information may be shared with:
+
+
Reputed courier/logistics partners for timely order delivery.
+
Payment service providers to process online payments.
+
Legal or regulatory authorities if required by law.
+
Internal teams responsible for customer support and service fulfillment.
+
+
+ We do not sell, rent, or disclose your personal data to third parties for marketing or commercial purposes.
+
+
+
+
+
Security Measures
+
+ We implement standard industry security protocols to protect user data. These include encrypted connections,
+ restricted access to data, and periodic security assessments.
+
+
+
+
+
Cookies and Tracking
+
+ Our website may use cookies to enhance functionality and analyze web traffic. These cookies do not collect
+ personally identifiable information unless voluntarily provided by the user.
+
+
+ You may choose to disable cookies through your browser settings, though certain features of the website may
+ become limited as a result.
+
+
+
+
+
User Rights
+
Users have the right to:
+
+
Access or correct the information shared with us.
+
Opt out of promotional communications at any time.
+
Request the deletion of their personal data, subject to compliance and record-keeping requirements.
+
+
+
+
+
Data Retention
+
+ Personal data is retained only as long as necessary for fulfilling the purpose for which it was collected,
+ or as required under legal obligations.
+
+
+
+
+
Third-Party Links
+
+ Our website may include links to third-party websites. We are not responsible for the privacy practices or
+ content on those platforms. Users are advised to review those sites' privacy policies independently.
+
+
+
+
+
Contact Information
+
For any privacy-related concerns, complaints, or requests, customers may contact our support team at:
+
+
📧 Email: freaksfilament@gmail.com
+
📞 Phone: +91-8005833266
+
+
+
+
+
+ )}
+
+
+
+
+
+
+ );
+};
+
+export default Policies;
\ No newline at end of file
diff --git a/app/policies/privacy/page.jsx b/app/policies/privacy/page.jsx
new file mode 100644
index 000000000..e4cdcf106
--- /dev/null
+++ b/app/policies/privacy/page.jsx
@@ -0,0 +1,69 @@
+'use client'
+import React from "react";
+import Navbar from "@/components/Navbar";
+import Footer from "@/components/Footer";
+
+const PrivacyPolicy = () => {
+ return (
+ <>
+
+
+
+
+
Privacy Policy
+
+
+
FILAMENT FREAKS is committed to safeguarding the privacy of all users interacting with our website and purchasing our 3D printed household appliances. The following policy outlines how we collect, use, share, and protect personal information:
+
Information Collection and Use
+
Personal information is collected solely for the purpose of order fulfillment, service communication, customer support, and business-related improvements. This may include name, email address, contact number, billing and shipping addresses, and payment-related details (processed securely via trusted payment gateways).
+
We do not collect or store any sensitive financial details such as CVV numbers or bank credentials. All online transactions are handled via secure, PCI-DSS-compliant payment partners.
+
Use of Information
+
+
Process and deliver your order.
+
Notify you of order status and shipment tracking.
+
Manage refunds, cancellations, and replacement requests as per our policies.
+
Provide customer support and service-related communications.
+
Send promotional updates only if explicitly permitted by the customer.
+
Maintain internal business records and comply with applicable laws.
+
+
Information Sharing
+
Your personal information may be shared with:
+
+
Reputed courier/logistics partners for timely order delivery.
+
Payment service providers to process online payments.
+
Legal or regulatory authorities if required by law.
+
Internal teams responsible for customer support and service fulfillment.
+
+
We do not sell, rent, or disclose your personal data to third parties for marketing or commercial purposes.
+
Security Measures
+
We implement standard industry security protocols to protect user data. These include encrypted connections, restricted access to data, and periodic security assessments.
+
Cookies and Tracking
+
Our website may use cookies to enhance functionality and analyze web traffic. These cookies do not collect personally identifiable information unless voluntarily provided by the user.
+
You may choose to disable cookies through your browser settings, though certain features of the website may become limited as a result.
+
User Rights
+
+
Access or correct the information shared with us.
+
Opt out of promotional communications at any time.
+
Request the deletion of their personal data, subject to compliance and record-keeping requirements.
+
+
Data Retention
+
Personal data is retained only as long as necessary for fulfilling the purpose for which it was collected, or as required under legal obligations.
+
Third-Party Links
+
Our website may include links to third-party websites. We are not responsible for the privacy practices or content on those platforms. Users are advised to review those sites' privacy policies independently.
+
Contact Information
+
For any privacy-related concerns, complaints, or requests, customers may contact our support team at:
+
+
📧 Email: freaksfilament@gmail.com
+
📞 Phone: +91-8005833266
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default PrivacyPolicy;
\ No newline at end of file
diff --git a/app/policies/shipping/page.jsx b/app/policies/shipping/page.jsx
new file mode 100644
index 000000000..70146b14a
--- /dev/null
+++ b/app/policies/shipping/page.jsx
@@ -0,0 +1,35 @@
+'use client'
+import React from "react";
+import Navbar from "@/components/Navbar";
+import Footer from "@/components/Footer";
+
+const ShippingAndDelivery = () => {
+ return (
+ <>
+
+
+
+
+
Shipping & Delivery
+
+
+
For international orders, shipments are dispatched and delivered via authorized international courier services and/or international speed post services only.
+
For domestic orders, shipments are dispatched using registered domestic courier partners and/or speed post services exclusively.
+
Delivery is expected within 0 to 7 days, or as per the delivery date agreed upon during order confirmation.
+
Delivery timelines are subject to the operational norms of the designated courier or postal service provider.
+
FILAMENT FREAKS shall not be held liable for any delays in delivery caused by courier agencies or postal authorities.
+
Our responsibility is limited to ensuring the consignment is handed over to the courier or postal service within 0 to 7 days of order and payment, or as per the mutually agreed schedule at the time of order placement.
+
All orders will be delivered to the shipping address provided by the buyer at the time of registration.
+
Order and delivery confirmations will be sent to the buyer via the email address submitted during registration.
+
For any issues or support regarding our services, customers can contact us at 8005833266 or email freaksfilament@gmail.com.
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ShippingAndDelivery;
\ No newline at end of file
diff --git a/app/policies/terms/page.jsx b/app/policies/terms/page.jsx
new file mode 100644
index 000000000..523d2ef0e
--- /dev/null
+++ b/app/policies/terms/page.jsx
@@ -0,0 +1,42 @@
+'use client'
+import React from "react";
+import Navbar from "@/components/Navbar";
+import Footer from "@/components/Footer";
+
+const TermsAndConditions = () => {
+ return (
+ <>
+
+
+
+
+
Terms & Conditions
+
+
+
For the purpose of these Terms and Conditions, the terms "we," "us," or "our" shall refer to FILAMENT FREAKS, whose registered/operational office is located at 532 SECTOR 19, CHOPASNI HOUSING BOARD, JODHPUR, RAJASTHAN 342008. The terms "you," "your," "user," or "visitor" shall refer to any natural or legal person accessing our website and/or purchasing products or services from us.
+
Your use of this website and/or your purchases from us are governed by the following Terms and Conditions:
+
+
The content published on this website is subject to change at any time without prior notice.
+
Neither FILAMENT FREAKS nor any third-party entities provide warranties or guarantees regarding the accuracy, timeliness, performance, completeness, or suitability of the content, information, or materials provided on this website for any particular purpose.
+
Users acknowledge that such content may contain inaccuracies or errors, and FILAMENT FREAKS expressly excludes liability for any such inaccuracies to the fullest extent permitted by law.
+
Any use of information, materials, or services from this website or related products is entirely at the user's own risk. FILAMENT FREAKS shall not be held liable, and it is the user's responsibility to ensure that any products, services, or information obtained through this website meet their specific requirements.
+
All content available on this website—including but not limited to design, layout, appearance, graphics, and written content—is either owned by or licensed to FILAMENT FREAKS.
+
Reproduction of any website material is strictly prohibited, except in accordance with the copyright notice that forms part of these Terms and Conditions.
+
All trademarks displayed on this website, which are not owned by or licensed to FILAMENT FREAKS, are acknowledged appropriately.
+
Unauthorized use of this website or its contents may give rise to a claim for damages and/or constitute a criminal offense.
+
This website may include links to external websites for informational purposes and user convenience. These links do not signify any endorsement by FILAMENT FREAKS, and we bear no responsibility for the content of the linked websites.
+
Users may not create a hyperlink to this website from another website or document without obtaining prior written permission from FILAMENT FREAKS.
+
Any disputes arising out of or in connection with the use of this website or any purchases made through it shall be governed by the laws of India.
+
FILAMENT FREAKS shall not be responsible for any loss or damage incurred—directly or indirectly—due to declined authorization for any transaction by your card issuer or acquiring bank.
diff --git a/components/Footer.jsx b/components/Footer.jsx
index 8d795f8d5..8edf245f6 100644
--- a/components/Footer.jsx
+++ b/components/Footer.jsx
@@ -1,6 +1,7 @@
import React from "react";
import { assets } from "@/assets/assets";
import Image from "next/image";
+import Link from "next/link";
const Footer = () => {
return (
@@ -8,11 +9,10 @@ const Footer = () => {
-
- Lorem Ipsum is simply dummy text of the printing and typesetting
- industry. Lorem Ipsum has been the industry's standard dummy text
- ever since the 1500s, when an unknown printer took a galley of type
- and scrambled it to make a type specimen book.
+
+ Filament Freaks is a 3D printing brand in India specializing in custom 3D printed products,
+ personalized designs, decorative lamps, word art, prototypes, and premium PLA+ filaments for
+ creators, businesses, and makers.
+ );
+};
+
+export default LoadingOverlay;
\ No newline at end of file
diff --git a/components/LoadingOverlayWrapper.jsx b/components/LoadingOverlayWrapper.jsx
new file mode 100644
index 000000000..247bea911
--- /dev/null
+++ b/components/LoadingOverlayWrapper.jsx
@@ -0,0 +1,12 @@
+"use client";
+import React from 'react';
+import { useAppContext } from '@/context/AppContext';
+import LoadingOverlay from './LoadingOverlay';
+
+const LoadingOverlayWrapper = () => {
+ const { isLoading } = useAppContext();
+
+ return ;
+};
+
+export default LoadingOverlayWrapper;
\ No newline at end of file
diff --git a/components/Navbar.jsx b/components/Navbar.jsx
index ed1f4b58e..3a4aeb196 100644
--- a/components/Navbar.jsx
+++ b/components/Navbar.jsx
@@ -1,57 +1,175 @@
-"use client"
-import React from "react";
-import { assets} from "@/assets/assets";
-import Link from "next/link"
+"use client";
+import React, { useState, useEffect, useRef } from "react";
+import { assets, BagIcon, BoxIcon, CartIcon, HomeIcon } from "@/assets/assets";
+import Link from "next/link";
import { useAppContext } from "@/context/AppContext";
import Image from "next/image";
+import { useClerk, UserButton, useUser } from "@clerk/nextjs";
+import { SHOP_CATEGORIES } from "@/lib/productCategories";
const Navbar = () => {
+ const { isSeller, router, user } = useAppContext();
+ const { openSignIn } = useClerk();
+ const [isShopDropdownOpen, setIsShopDropdownOpen] = useState(false);
+ const dropdownRef = useRef(null);
- const { isSeller, router } = useAppContext();
+ // Close dropdown when clicking outside
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
+ setIsShopDropdownOpen(false);
+ }
+ };
+
+ if (isShopDropdownOpen) {
+ document.addEventListener('mousedown', handleClickOutside);
+ }
+
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [isShopDropdownOpen]);
return (
-
+ <>
+
+ >
);
};
-export default Navbar;
\ No newline at end of file
+export default Navbar;
diff --git a/components/NewsLetter.jsx b/components/NewsLetter.jsx
index 1e3f667b1..1e197ddb3 100644
--- a/components/NewsLetter.jsx
+++ b/components/NewsLetter.jsx
@@ -4,11 +4,10 @@ const NewsLetter = () => {
return (
- Subscribe now & get 20% off
+ Stay Updated with Latest 3D Printing Trends
- Lorem Ipsum is simply dummy text of the printing and typesetting
- industry.
+ Subscribe to our newsletter and be the first to know about new products, special offers, and 3D printing tips.
{
type="text"
placeholder="Enter your email id"
/>
-
+
Subscribe