// @ts-nocheck import { mutation, query } from "./_generated/server" import { v } from "convex/values" const orderStatus = v.union( v.literal("pending"), v.literal("paid"), v.literal("fulfilled"), v.literal("cancelled"), v.literal("refunded") ) const orderItem = v.object({ productId: v.optional(v.id("products")), stripeProductId: v.optional(v.string()), stripePriceId: v.string(), productName: v.string(), image: v.optional(v.string()), price: v.number(), quantity: v.number(), }) const shippingAddress = v.object({ name: v.optional(v.string()), address: v.optional(v.string()), city: v.optional(v.string()), state: v.optional(v.string()), zipCode: v.optional(v.string()), country: v.optional(v.string()), }) async function attachItems(ctx: any, order: any) { const items = await ctx.db .query("orderItems") .withIndex("by_orderId", (q: any) => q.eq("orderId", order._id)) .collect() return { id: order._id, customerEmail: order.customerEmail, customerName: order.customerName, totalAmount: order.totalAmount, currency: order.currency, status: order.status, stripeSessionId: order.stripeSessionId ?? null, paymentIntentId: order.stripePaymentIntentId ?? null, createdAt: new Date(order.createdAt).toISOString(), updatedAt: new Date(order.updatedAt).toISOString(), shippingAddress: order.shippingAddress, items: items.map((item: any) => ({ productId: item.productId ?? null, productName: item.productName, price: item.price, quantity: item.quantity, priceId: item.stripePriceId, image: item.image, })), } } export const listAdmin = query({ args: { status: v.optional(orderStatus), search: v.optional(v.string()), }, handler: async (ctx, args) => { let orders = await ctx.db .query("orders") .withIndex("by_createdAt") .collect() orders = orders.sort((a, b) => b.createdAt - a.createdAt) if (args.status) { orders = orders.filter((order) => order.status === args.status) } const search = args.search?.trim().toLowerCase() if (search) { orders = orders.filter((order) => { return ( order.customerEmail.toLowerCase().includes(search) || order.customerName?.toLowerCase().includes(search) || order._id.toLowerCase().includes(search) ) }) } return await Promise.all(orders.map((order) => attachItems(ctx, order))) }, }) export const getMetrics = query({ args: {}, handler: async (ctx) => { const orders = await ctx.db.query("orders").collect() const paidOrders = orders.filter( (order) => order.status === "paid" || order.status === "fulfilled" ) const revenue = paidOrders.reduce( (sum, order) => sum + order.totalAmount, 0 ) return { totalOrders: orders.length, totalRevenue: revenue, pendingOrders: orders.filter((order) => order.status === "pending") .length, completedOrders: orders.filter((order) => order.status === "fulfilled") .length, paidOrders: orders.filter((order) => order.status === "paid").length, refundedOrders: orders.filter((order) => order.status === "refunded") .length, } }, }) export const upsertStripeOrder = mutation({ args: { customerEmail: v.string(), customerName: v.optional(v.string()), status: orderStatus, totalAmount: v.number(), currency: v.string(), stripeSessionId: v.optional(v.string()), stripePaymentIntentId: v.optional(v.string()), shippingAddress: v.optional(shippingAddress), items: v.array(orderItem), }, handler: async (ctx, args) => { const now = Date.now() let existing = args.stripeSessionId ? await ctx.db .query("orders") .withIndex("by_stripeSessionId", (q) => q.eq("stripeSessionId", args.stripeSessionId) ) .unique() : null let orderId if (existing) { orderId = existing._id await ctx.db.patch(orderId, { customerEmail: args.customerEmail, customerName: args.customerName, status: args.status, totalAmount: args.totalAmount, currency: args.currency, stripePaymentIntentId: args.stripePaymentIntentId, shippingAddress: args.shippingAddress, updatedAt: now, }) const existingItems = await ctx.db .query("orderItems") .withIndex("by_orderId", (q) => q.eq("orderId", orderId)) .collect() for (const item of existingItems) { await ctx.db.delete(item._id) } } else { orderId = await ctx.db.insert("orders", { customerEmail: args.customerEmail, customerName: args.customerName, status: args.status, totalAmount: args.totalAmount, currency: args.currency, stripeSessionId: args.stripeSessionId, stripePaymentIntentId: args.stripePaymentIntentId, shippingAddress: args.shippingAddress, createdAt: now, updatedAt: now, }) } for (const item of args.items) { await ctx.db.insert("orderItems", { orderId, productId: item.productId, stripeProductId: item.stripeProductId, stripePriceId: item.stripePriceId, productName: item.productName, image: item.image, price: item.price, quantity: item.quantity, createdAt: now, }) } return await attachItems(ctx, (await ctx.db.get(orderId))!) }, }) export const updateStatus = mutation({ args: { id: v.id("orders"), status: orderStatus, }, handler: async (ctx, args) => { await ctx.db.patch(args.id, { status: args.status, updatedAt: Date.now(), }) return await attachItems(ctx, (await ctx.db.get(args.id))!) }, })