first commit

This commit is contained in:
2026-01-17 14:17:42 +05:30
commit 0f194eb9e7
328 changed files with 73544 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { rankSystem } from '@/lib/ranks'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Get user with current rank
const user = await prisma.user.findUnique({
where: { id: userId },
include: {
currentRank: true
}
})
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
// Calculate current metrics
const metrics = await rankSystem.calculateUserMetrics(userId)
// Get all ranks to find next rank
const allRanks = await prisma.rank.findMany({
where: { isActive: true },
orderBy: { order: 'asc' }
})
// Find next rank
const currentRankOrder = user.currentRank?.order || 0
const nextRank = allRanks.find(rank => rank.order > currentRankOrder)
// Get user's achievements
const achievements = await prisma.rankAchievement.findMany({
where: { userId },
include: {
rank: true
},
orderBy: { achievedAt: 'desc' }
})
const response = {
currentRank: user.currentRank,
nextRank: nextRank || null,
metrics,
achievements: achievements.map(achievement => ({
id: achievement.id,
rank: achievement.rank,
achievedAt: achievement.achievedAt.toISOString()
}))
}
return NextResponse.json(response)
} catch (error) {
console.error('Error fetching achievements:', error)
return NextResponse.json(
{ error: 'Failed to fetch achievements' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,195 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
// Check if category exists
const existingCategory = await prisma.category.findUnique({
where: { id },
include: {
_count: {
select: {
products: true
}
}
}
})
if (!existingCategory) {
return NextResponse.json(
{ error: 'Category not found' },
{ status: 404 }
)
}
// Check if category has products
if (existingCategory._count.products > 0) {
return NextResponse.json(
{
error: 'Cannot delete category with associated products',
productCount: existingCategory._count.products
},
{ status: 400 }
)
}
// Delete the category
await prisma.category.delete({
where: { id }
})
return NextResponse.json({
message: 'Category deleted successfully',
deletedCategory: {
id: existingCategory.id,
name: existingCategory.name
}
})
} catch (error: any) {
console.error('Error deleting category:', error)
return NextResponse.json(
{ error: 'Failed to delete category' },
{ status: 500 }
)
}
}
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const body = await request.json()
const { name, description, image, isActive } = body
// Check if category exists
const existingCategory = await prisma.category.findUnique({
where: { id }
})
if (!existingCategory) {
return NextResponse.json(
{ error: 'Category not found' },
{ status: 404 }
)
}
// If name is being updated, check for uniqueness
if (name && name !== existingCategory.name) {
const nameExists = await prisma.category.findUnique({
where: { name }
})
if (nameExists) {
return NextResponse.json(
{ error: 'Category name already exists' },
{ status: 400 }
)
}
}
// Update the category
const updatedCategory = await prisma.category.update({
where: { id },
data: {
...(name !== undefined && { name }),
...(description !== undefined && { description }),
...(image !== undefined && { image }),
...(isActive !== undefined && { isActive })
},
include: {
_count: {
select: {
products: true
}
}
}
})
return NextResponse.json(updatedCategory)
} catch (error: any) {
console.error('Error updating category:', error)
// Handle Prisma unique constraint errors
if (error.code === 'P2002') {
return NextResponse.json(
{ error: 'Category name already exists' },
{ status: 400 }
)
}
return NextResponse.json(
{ error: 'Failed to update category' },
{ status: 500 }
)
}
}
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const category = await prisma.category.findUnique({
where: { id },
include: {
_count: {
select: {
products: true
}
},
products: {
select: {
id: true,
name: true,
price: true,
isActive: true
},
take: 10 // Limit to first 10 products for preview
}
}
})
if (!category) {
return NextResponse.json(
{ error: 'Category not found' },
{ status: 404 }
)
}
return NextResponse.json(category)
} catch (error: any) {
console.error('Error fetching category:', error)
return NextResponse.json(
{ error: 'Failed to fetch category' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const search = searchParams.get('search')
const categories = await prisma.category.findMany({
where: search ? {
name: {
contains: search,
mode: 'insensitive'
}
} : {},
include: {
_count: {
select: {
products: true
}
}
},
orderBy: {
createdAt: 'desc'
}
})
return NextResponse.json(categories)
} catch (error) {
console.error('Error fetching categories:', error)
return NextResponse.json(
{ error: 'Failed to fetch categories' },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const { name, description, image, isActive } = body
const category = await prisma.category.create({
data: {
name,
description,
image,
isActive
}
})
return NextResponse.json(category)
} catch (error) {
console.error('Error creating category:', error)
return NextResponse.json(
{ error: 'Failed to create category' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,43 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const settings = await prisma.commissionSettings.findMany({
orderBy: { level: 'asc' },
})
return NextResponse.json(settings)
} catch (error) {
console.error('Commission settings API error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { level, percentage, isActive } = await request.json()
const setting = await prisma.commissionSettings.upsert({
where: { level },
update: { percentage, isActive },
create: { level, percentage, isActive },
})
return NextResponse.json(setting)
} catch (error) {
console.error('Create commission setting error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}

View File

@@ -0,0 +1,37 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id: commissionId } = await params
const { status } = await request.json()
// Validate status
if (!['PENDING', 'APPROVED', 'PAID', 'CANCELLED'].includes(status)) {
return NextResponse.json({ error: 'Invalid status' }, { status: 400 })
}
const commission = await prisma.commission.update({
where: { id: commissionId },
data: { status }
})
return NextResponse.json(commission)
} catch (error) {
console.error('Error updating commission:', error)
return NextResponse.json(
{ error: 'Failed to update commission' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,77 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const url = new URL(request.url)
const status = url.searchParams.get('status')
const type = url.searchParams.get('type')
const level = url.searchParams.get('level')
const page = parseInt(url.searchParams.get('page') || '1')
const limit = parseInt(url.searchParams.get('limit') || '50')
const where: any = {}
if (status && status !== 'all') {
where.status = status
}
if (type && type !== 'all') {
where.type = type
}
if (level && level !== 'all') {
where.level = parseInt(level)
}
const commissions = await prisma.commission.findMany({
where,
include: {
user: {
select: {
id: true,
name: true,
email: true
}
},
fromUser: {
select: {
id: true,
name: true,
email: true
}
}
},
orderBy: {
createdAt: 'desc'
},
skip: (page - 1) * limit,
take: limit
})
const total = await prisma.commission.count({ where })
return NextResponse.json({
commissions: commissions || [],
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
})
} catch (error) {
console.error('Error fetching admin commissions:', error)
return NextResponse.json({
commissions: [],
pagination: { page: 1, limit: 50, total: 0, pages: 0 }
})
}
}

View File

@@ -0,0 +1,36 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id: settingId } = await params
const { level, percentage, isActive } = await request.json()
const setting = await prisma.commissionSettings.update({
where: { id: settingId },
data: {
level,
percentage,
isActive
}
})
return NextResponse.json(setting)
} catch (error) {
console.error('Error updating commission setting:', error)
return NextResponse.json(
{ error: 'Failed to update commission setting' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,41 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
let settings = await prisma.commissionSettings.findMany({
orderBy: { level: 'asc' }
})
// If no settings exist, create default ones
if (settings.length === 0) {
const defaultSettings = [
{ level: 1, percentage: 10, isActive: true },
{ level: 2, percentage: 5, isActive: true },
{ level: 3, percentage: 3, isActive: true },
{ level: 4, percentage: 2, isActive: true },
{ level: 5, percentage: 1, isActive: true }
]
await prisma.commissionSettings.createMany({
data: defaultSettings
})
settings = await prisma.commissionSettings.findMany({
orderBy: { level: 'asc' }
})
}
return NextResponse.json({ settings: settings || [] })
} catch (error) {
console.error('Error fetching commission settings:', error)
return NextResponse.json({ settings: [] })
}
}

View File

@@ -0,0 +1,66 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Get total commissions count
const totalCommissions = await prisma.commission.count()
// Get amounts by status
const [pendingResult, approvedResult, paidResult] = await Promise.all([
prisma.commission.aggregate({
where: { status: 'PENDING' },
_sum: { amount: true }
}),
prisma.commission.aggregate({
where: { status: 'APPROVED' },
_sum: { amount: true }
}),
prisma.commission.aggregate({
where: { status: 'PAID' },
_sum: { amount: true }
})
])
// Get this month commissions
const startOfMonth = new Date()
startOfMonth.setDate(1)
startOfMonth.setHours(0, 0, 0, 0)
const thisMonthCommissions = await prisma.commission.count({
where: {
createdAt: { gte: startOfMonth }
}
})
// Calculate average commission
const totalAmount = (pendingResult._sum.amount || 0) + (approvedResult._sum.amount || 0) + (paidResult._sum.amount || 0)
const averageCommission = totalCommissions > 0 ? totalAmount / totalCommissions : 0
return NextResponse.json({
totalCommissions: totalCommissions || 0,
pendingAmount: pendingResult._sum.amount || 0,
approvedAmount: approvedResult._sum.amount || 0,
paidAmount: paidResult._sum.amount || 0,
thisMonthCommissions: thisMonthCommissions || 0,
averageCommission: averageCommission || 0
})
} catch (error) {
console.error('Error fetching commission stats:', error)
return NextResponse.json({
totalCommissions: 0,
pendingAmount: 0,
approvedAmount: 0,
paidAmount: 0,
thisMonthCommissions: 0,
averageCommission: 0
})
}
}

View File

@@ -0,0 +1,34 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const orders = await prisma.order.findMany({
take: 10,
orderBy: { createdAt: 'desc' },
include: {
user: {
select: {
name: true,
email: true
}
}
}
})
return NextResponse.json({ orders })
} catch (error) {
console.error('Error fetching recent orders:', error)
return NextResponse.json(
{ error: 'Failed to fetch recent orders' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,128 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Get current date ranges
const now = new Date()
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1)
const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0)
// Get current totals
const [totalUsers, totalProducts, totalOrders, totalRevenueResult] = await Promise.all([
prisma.user.count(),
prisma.product.count({ where: { isActive: true } }),
prisma.order.count(),
prisma.order.aggregate({
where: { status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] } },
_sum: { total: true }
})
])
// Get last month data for comparison
const [lastMonthUsers, lastMonthProducts, lastMonthOrders, lastMonthRevenueResult] = await Promise.all([
prisma.user.count({
where: {
joinedAt: {
gte: startOfLastMonth,
lte: endOfLastMonth
}
}
}),
prisma.product.count({
where: {
isActive: true,
createdAt: {
gte: startOfLastMonth,
lte: endOfLastMonth
}
}
}),
prisma.order.count({
where: {
createdAt: {
gte: startOfLastMonth,
lte: endOfLastMonth
}
}
}),
prisma.order.aggregate({
where: {
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] },
createdAt: {
gte: startOfLastMonth,
lte: endOfLastMonth
}
},
_sum: { total: true }
})
])
// Get this month data
const [thisMonthUsers, thisMonthProducts, thisMonthOrders, thisMonthRevenueResult] = await Promise.all([
prisma.user.count({
where: {
joinedAt: { gte: startOfMonth }
}
}),
prisma.product.count({
where: {
isActive: true,
createdAt: { gte: startOfMonth }
}
}),
prisma.order.count({
where: {
createdAt: { gte: startOfMonth }
}
}),
prisma.order.aggregate({
where: {
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] },
createdAt: { gte: startOfMonth }
},
_sum: { total: true }
})
])
// Calculate growth percentages
const calculateGrowth = (current: number, previous: number) => {
if (previous === 0) return current > 0 ? 100 : 0
return ((current - previous) / previous) * 100
}
const totalRevenue = totalRevenueResult._sum.total || 0
const lastMonthRevenue = lastMonthRevenueResult._sum.total || 0
const thisMonthRevenue = thisMonthRevenueResult._sum.total || 0
const userGrowth = calculateGrowth(thisMonthUsers, lastMonthUsers)
const productGrowth = calculateGrowth(thisMonthProducts, lastMonthProducts)
const orderGrowth = calculateGrowth(thisMonthOrders, lastMonthOrders)
const revenueGrowth = calculateGrowth(thisMonthRevenue, lastMonthRevenue)
return NextResponse.json({
totalUsers,
totalProducts,
totalOrders,
totalRevenue,
userGrowth,
productGrowth,
orderGrowth,
revenueGrowth
})
} catch (error) {
console.error('Error fetching dashboard stats:', error)
return NextResponse.json(
{ error: 'Failed to fetch dashboard stats' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,59 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Get top products by order count this month
const startOfMonth = new Date()
startOfMonth.setDate(1)
startOfMonth.setHours(0, 0, 0, 0)
const topProducts = await prisma.product.findMany({
take: 5,
include: {
orderItems: {
where: {
order: {
createdAt: { gte: startOfMonth },
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] }
}
},
select: {
quantity: true,
price: true
}
}
}
})
const productsWithStats = topProducts.map(product => {
const orderCount = product.orderItems.reduce((sum, item) => sum + item.quantity, 0)
const totalRevenue = product.orderItems.reduce((sum, item) => sum + (item.price * item.quantity), 0)
return {
id: product.id,
name: product.name,
price: product.price,
orderCount,
totalRevenue
}
}).filter(product => product.orderCount > 0)
.sort((a, b) => b.orderCount - a.orderCount)
.slice(0, 5)
return NextResponse.json({ products: productsWithStats })
} catch (error) {
console.error('Error fetching top products:', error)
return NextResponse.json(
{ error: 'Failed to fetch top products' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,148 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const order = await prisma.order.findUnique({
where: { id },
include: {
user: {
select: {
id: true,
name: true,
email: true,
phone: true
}
},
orderItems: {
include: {
product: {
select: {
id: true,
name: true,
images: true,
sku: true,
price: true
}
}
}
},
shippingAddress: {
select: {
firstName: true,
lastName: true,
company: true,
address1: true,
address2: true,
city: true,
state: true,
zipCode: true,
country: true,
phone: true
}
}
}
})
if (!order) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
return NextResponse.json(order)
} catch (error) {
console.error('Error fetching order:', error)
return NextResponse.json(
{ error: 'Failed to fetch order' },
{ status: 500 }
)
}
}
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const body = await request.json()
const { status } = body
if (!['PENDING', 'PAID', 'SHIPPED', 'DELIVERED', 'CANCELLED'].includes(status)) {
return NextResponse.json(
{ error: 'Invalid status' },
{ status: 400 }
)
}
const order = await prisma.order.update({
where: { id },
data: {
status,
updatedAt: new Date()
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
phone: true
}
},
orderItems: {
include: {
product: {
select: {
id: true,
name: true,
images: true,
sku: true,
price: true
}
}
}
},
shippingAddress: {
select: {
firstName: true,
lastName: true,
company: true,
address1: true,
address2: true,
city: true,
state: true,
zipCode: true,
country: true,
phone: true
}
}
}
})
return NextResponse.json(order)
} catch (error) {
console.error('Error updating order:', error)
return NextResponse.json(
{ error: 'Failed to update order' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,48 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function PATCH(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const { orderIds, status } = body
if (!orderIds || !Array.isArray(orderIds) || orderIds.length === 0) {
return NextResponse.json(
{ error: 'Order IDs are required' },
{ status: 400 }
)
}
if (!['PENDING', 'PAID', 'SHIPPED', 'DELIVERED', 'CANCELLED'].includes(status)) {
return NextResponse.json(
{ error: 'Invalid status' },
{ status: 400 }
)
}
await prisma.order.updateMany({
where: {
id: { in: orderIds }
},
data: {
status,
updatedAt: new Date()
}
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error in bulk order update:', error)
return NextResponse.json(
{ error: 'Failed to perform bulk update' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,72 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const search = searchParams.get('search')
const status = searchParams.get('status')
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '20')
const skip = (page - 1) * limit
const where: any = {}
if (search) {
where.OR = [
{ id: { contains: search, mode: 'insensitive' } },
{ razorpayOrderId: { contains: search, mode: 'insensitive' } },
{ user: { name: { contains: search, mode: 'insensitive' } } },
{ user: { email: { contains: search, mode: 'insensitive' } } }
]
}
if (status) {
where.status = status
}
const orders = await prisma.order.findMany({
where,
include: {
user: {
select: {
id: true,
name: true,
email: true
}
},
orderItems: {
include: {
product: {
select: {
id: true,
name: true,
images: true
}
}
}
}
},
orderBy: {
createdAt: 'desc'
},
skip,
take: limit
})
return NextResponse.json(orders)
} catch (error) {
console.error('Error fetching orders:', error)
return NextResponse.json(
{ error: 'Failed to fetch orders' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,66 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id: payoutId } = await params
const { status, adminNotes } = await request.json()
// Validate status
if (!['APPROVED', 'REJECTED', 'PAID'].includes(status)) {
return NextResponse.json({ error: 'Invalid status' }, { status: 400 })
}
const result = await prisma.$transaction(async (tx) => {
// Get the payout
const payout = await tx.payout.findUnique({
where: { id: payoutId },
include: { user: true }
})
if (!payout) {
throw new Error('Payout not found')
}
// Update payout
const updatedPayout = await tx.payout.update({
where: { id: payoutId },
data: {
status,
adminNotes,
updatedAt: new Date()
}
})
// If rejecting, restore the amount to user's wallet
if (status === 'REJECTED') {
await tx.wallet.update({
where: { userId: payout.userId },
data: {
balance: { increment: payout.amount }
}
})
}
return updatedPayout
})
return NextResponse.json(result)
} catch (error) {
console.error('Error updating payout:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to update payout' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,59 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const url = new URL(request.url)
const status = url.searchParams.get('status')
const page = parseInt(url.searchParams.get('page') || '1')
const limit = parseInt(url.searchParams.get('limit') || '50')
const where: any = {}
if (status && status !== 'all') {
where.status = status
}
const payouts = await prisma.payout.findMany({
where,
include: {
user: {
select: {
id: true,
name: true,
email: true
}
}
},
orderBy: {
createdAt: 'desc'
},
skip: (page - 1) * limit,
take: limit
})
const total = await prisma.payout.count({ where })
return NextResponse.json({
payouts,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
})
} catch (error) {
console.error('Error fetching admin payouts:', error)
return NextResponse.json(
{ error: 'Failed to fetch payouts' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,45 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Get total requests
const totalRequests = await prisma.payout.count()
// Get amounts by status
const [pendingResult, approvedResult, paidResult] = await Promise.all([
prisma.payout.aggregate({
where: { status: 'PENDING' },
_sum: { amount: true }
}),
prisma.payout.aggregate({
where: { status: 'APPROVED' },
_sum: { amount: true }
}),
prisma.payout.aggregate({
where: { status: 'PAID' },
_sum: { amount: true }
})
])
return NextResponse.json({
totalRequests,
pendingAmount: pendingResult._sum.amount || 0,
approvedAmount: approvedResult._sum.amount || 0,
paidAmount: paidResult._sum.amount || 0
})
} catch (error) {
console.error('Error fetching payout stats:', error)
return NextResponse.json(
{ error: 'Failed to fetch payout stats' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,114 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const product = await prisma.product.findUnique({
where: { id },
include: {
category: {
select: {
id: true,
name: true
}
}
}
})
if (!product) {
return NextResponse.json({ error: 'Product not found' }, { status: 404 })
}
return NextResponse.json(product)
} catch (error) {
console.error('Error fetching product:', error)
return NextResponse.json(
{ error: 'Failed to fetch product' },
{ status: 500 }
)
}
}
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const body = await request.json()
const { name, description, price, discount, images, stock, sku, isActive, categoryId, weight } = body
const product = await prisma.product.update({
where: { id },
data: {
name,
description,
price,
discount,
images,
stock,
sku,
isActive,
categoryId,
weight,
slug: name.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-') + '-' + Date.now()
},
include: {
category: true
}
})
return NextResponse.json(product)
} catch (error) {
console.error('Error updating product:', error)
return NextResponse.json(
{ error: 'Failed to update product' },
{ status: 500 }
)
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
await prisma.product.delete({
where: { id }
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting product:', error)
return NextResponse.json(
{ error: 'Failed to delete product' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,61 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function PATCH(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const { productIds, action } = body
if (!productIds || !Array.isArray(productIds) || productIds.length === 0) {
return NextResponse.json(
{ error: 'Product IDs are required' },
{ status: 400 }
)
}
let updateData: any = {}
switch (action) {
case 'activate':
updateData = { isActive: true }
break
case 'deactivate':
updateData = { isActive: false }
break
case 'delete':
await prisma.product.deleteMany({
where: {
id: { in: productIds }
}
})
return NextResponse.json({ success: true })
default:
return NextResponse.json(
{ error: 'Invalid action' },
{ status: 400 }
)
}
await prisma.product.updateMany({
where: {
id: { in: productIds }
},
data: updateData
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error in bulk action:', error)
return NextResponse.json(
{ error: 'Failed to perform bulk action' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,195 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { auth } from '@/auth'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json(
{ success: false, message: 'Unauthorized' },
{ status: 401 }
)
}
const { searchParams } = new URL(request.url)
const selectedColumns = searchParams.get('columns')?.split(',') || []
const categoryId = searchParams.get('categoryId') || ''
const isActive = searchParams.get('isActive') || ''
const limit = parseInt(searchParams.get('limit') || '10000')
// Build where clause based on filters
const whereClause: any = {}
if (categoryId && categoryId !== '') {
whereClause.categoryId = categoryId
}
if (isActive && isActive !== '') {
whereClause.isActive = isActive === 'true'
}
// Fetch products with category information
const products = await prisma.product.findMany({
where: whereClause,
include: {
category: {
select: { name: true }
}
},
take: limit,
orderBy: { createdAt: 'desc' }
})
// Transform data based on selected columns
const transformedData = products.map(product => {
const baseData = {
id: product.id,
name: product.name,
description: product.description || '',
price: product.price,
discount: product.discount,
stock: product.stock,
sku: product.sku,
slug: product.slug,
isActive: product.isActive,
categoryName: product.category?.name || '',
categoryId: product.categoryId || '',
brand: product.brand || '',
origin: product.origin || '',
weight: product.weight || '',
images: product.images.join(', '),
createdAt: product.createdAt.toISOString(),
updatedAt: product.updatedAt.toISOString(),
// Calculated fields
finalPrice: product.price - (product.price * product.discount / 100),
discountAmount: product.price * product.discount / 100,
stockStatus: product.stock > 10 ? 'In Stock' : product.stock > 0 ? 'Low Stock' : 'Out of Stock',
status: product.isActive ? 'Active' : 'Inactive'
}
// Filter by selected columns if specified
if (selectedColumns.length > 0) {
const filteredData: any = {}
selectedColumns.forEach(column => {
if (column in baseData) {
filteredData[column] = (baseData as any)[column]
}
})
return filteredData
}
return baseData
})
return NextResponse.json({
success: true,
data: transformedData,
count: transformedData.length
})
} catch (error) {
console.error('Product export error:', error)
return NextResponse.json(
{ success: false, message: 'Internal server error' },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json(
{ success: false, message: 'Unauthorized' },
{ status: 401 }
)
}
const { columns = [], filters = {} } = await request.json()
const selectedColumns = columns || []
const categoryId = filters.categoryId || ''
const isActive = filters.isActive || ''
const limit = parseInt(filters.limit || '10000')
// Build where clause based on filters
const whereClause: any = {}
if (categoryId && categoryId !== '') {
whereClause.categoryId = categoryId
}
if (isActive && isActive !== '') {
whereClause.isActive = isActive === 'true'
}
// Fetch products with category information
const products = await prisma.product.findMany({
where: whereClause,
include: {
category: {
select: { name: true }
}
},
take: limit,
orderBy: { createdAt: 'desc' }
})
// Transform data based on selected columns
const transformedData = products.map(product => {
const baseData = {
id: product.id,
name: product.name,
description: product.description || '',
price: product.price,
discount: product.discount,
stock: product.stock,
sku: product.sku,
slug: product.slug,
isActive: product.isActive,
categoryName: product.category?.name || '',
categoryId: product.categoryId || '',
brand: product.brand || '',
origin: product.origin || '',
weight: product.weight || '',
images: product.images.join(', '),
createdAt: product.createdAt.toISOString(),
updatedAt: product.updatedAt.toISOString(),
// Calculated fields
finalPrice: product.price - (product.price * product.discount / 100),
discountAmount: product.price * product.discount / 100,
stockStatus: product.stock > 10 ? 'In Stock' : product.stock > 0 ? 'Low Stock' : 'Out of Stock',
status: product.isActive ? 'Active' : 'Inactive'
}
// Filter by selected columns if specified
if (selectedColumns.length > 0) {
const filteredData: any = {}
selectedColumns.forEach((column: string) => {
if (column in baseData) {
filteredData[column] = (baseData as any)[column]
}
})
return filteredData
}
return baseData
})
return NextResponse.json({
success: true,
data: transformedData,
count: transformedData.length
})
} catch (error) {
console.error('Product export error:', error)
return NextResponse.json(
{ success: false, message: 'Internal server error' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,162 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { auth } from '@/auth'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json(
{ success: false, message: 'Unauthorized' },
{ status: 401 }
)
}
const { data } = await request.json()
if (!Array.isArray(data) || data.length === 0) {
return NextResponse.json(
{ success: false, message: 'No products provided' },
{ status: 400 }
)
}
let successCount = 0
let errorCount = 0
const errors: string[] = []
// Process products in batches to avoid overwhelming the database
const batchSize = 50
const batches: any[][] = []
for (let i = 0; i < data.length; i += batchSize) {
batches.push(data.slice(i, i + batchSize))
}
for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
const batch = batches[batchIndex]
try {
const productsToCreate: any[] = []
const productsToUpdate: any[] = []
for (let index = 0; index < batch.length; index++) {
const productData = batch[index]
try {
// Validate required fields
if (!productData.name || !productData.price) {
errors.push(`Row ${batchIndex * batchSize + index + 1}: Missing required fields (name, price)`)
errorCount++
continue
}
// Check if category exists
let categoryId: string | undefined = undefined
if (productData.categoryId) {
const category = await prisma.category.findUnique({
where: { id: productData.categoryId }
})
if (category) {
categoryId = category.id
} else {
errors.push(`Row ${batchIndex * batchSize + index + 1}: Category with ID '${productData.categoryId}' not found`)
errorCount++
continue
}
}
// Generate SKU if not provided
const sku = productData.sku || `PROD-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
// Prepare product data
const productPayload = {
name: productData.name.trim(),
description: productData.description || '',
price: parseFloat(productData.price) || 0,
discount: parseFloat(productData.discount) || 0,
stock: parseInt(productData.stock) || 0,
sku: sku,
slug: productData.slug || productData.name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
isActive: productData.active === 'true' || productData.active === true || productData.active === '1' || true,
...(categoryId && { categoryId }),
brand: productData.brand || '',
origin: productData.origin || '',
weight: productData.weight || '',
images: productData.images ? (typeof productData.images === 'string' ? productData.images.split(',').map((img: string) => img.trim()) : []) : []
}
// Check if product exists (by SKU)
const existingProduct = await prisma.product.findUnique({
where: { sku: productPayload.sku }
})
if (existingProduct) {
// Update existing product - create update data without null categoryId
const updatePayload = { ...productPayload }
if (!categoryId) {
delete updatePayload.categoryId
}
productsToUpdate.push({
where: { id: existingProduct.id },
data: updatePayload
})
} else {
// Create new product
productsToCreate.push(productPayload)
}
} catch (error) {
errors.push(`Row ${batchIndex * batchSize + index + 1}: ${error instanceof Error ? error.message : 'Processing error'}`)
errorCount++
}
}
// Create new products
if (productsToCreate.length > 0) {
try {
await prisma.product.createMany({
data: productsToCreate,
skipDuplicates: true
})
successCount += productsToCreate.length
} catch (error) {
errors.push(`Batch ${batchIndex + 1}: Failed to create products - ${error instanceof Error ? error.message : 'Unknown error'}`)
errorCount += productsToCreate.length
}
}
// Update existing products
for (const updateData of productsToUpdate) {
try {
await prisma.product.update(updateData)
successCount++
} catch (error) {
errors.push(`Failed to update product: ${error instanceof Error ? error.message : 'Unknown error'}`)
errorCount++
}
}
} catch (error) {
errors.push(`Batch ${batchIndex + 1}: ${error instanceof Error ? error.message : 'Batch processing error'}`)
errorCount += batch.length
}
}
return NextResponse.json({
success: errorCount === 0,
message: errorCount === 0 ?
`Successfully imported ${successCount} products` :
`Import completed with errors. ${successCount} successful, ${errorCount} failed.`,
successCount,
errorCount,
errors: errors.slice(0, 50) // Limit to first 50 errors
})
} catch (error) {
console.error('Product import error:', error)
return NextResponse.json(
{ success: false, message: 'Internal server error' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,54 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const { name, description, price, discount, images, stock, sku, isActive, categoryId } = body
// Check if SKU already exists
const existingSku = await prisma.product.findUnique({
where: { sku }
})
if (existingSku) {
return NextResponse.json(
{ error: 'SKU already exists' },
{ status: 400 }
)
}
const product = await prisma.product.create({
data: {
name,
description,
price,
discount,
images,
stock,
sku,
isActive,
categoryId,
slug: name.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-') + '-' + Date.now()
},
include: {
category: true
}
})
return NextResponse.json(product)
} catch (error) {
console.error('Error creating product:', error)
return NextResponse.json(
{ error: 'Failed to create product' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,35 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
interface Props {
params: Promise<{ id: string }>
}
export async function POST(
request: NextRequest,
{ params }: Props
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const review = await prisma.review.update({
where: { id },
data: { isApproved: true }
})
return NextResponse.json({ review })
} catch (error) {
console.error('Error approving review:', error)
return NextResponse.json(
{ error: 'Failed to approve review' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,35 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
interface Props {
params: Promise<{ id: string }>
}
export async function POST(
request: NextRequest,
{ params }: Props
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const review = await prisma.review.update({
where: { id },
data: { isApproved: false }
})
return NextResponse.json({ review })
} catch (error) {
console.error('Error rejecting review:', error)
return NextResponse.json(
{ error: 'Failed to reject review' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,44 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
interface Props {
params: Promise<{ id: string }>
}
export async function DELETE(
request: NextRequest,
{ params }: Props
) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
// Delete related records first
await prisma.reviewHelpfulVote.deleteMany({
where: { reviewId: id }
})
await prisma.reviewReport.deleteMany({
where: { reviewId: id }
})
// Delete the review
await prisma.review.delete({
where: { id }
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting review:', error)
return NextResponse.json(
{ error: 'Failed to delete review' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,91 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '20')
const search = searchParams.get('search') || ''
const approved = searchParams.get('approved')
const skip = (page - 1) * limit
const where: any = {}
if (search) {
where.OR = [
{ title: { contains: search, mode: 'insensitive' } },
{ comment: { contains: search, mode: 'insensitive' } },
{ user: { name: { contains: search, mode: 'insensitive' } } },
{ product: { name: { contains: search, mode: 'insensitive' } } }
]
}
if (approved !== null && approved !== undefined) {
where.isApproved = approved === 'true'
}
const [reviews, total] = await Promise.all([
prisma.review.findMany({
where,
include: {
user: {
select: {
id: true,
name: true,
email: true
}
},
product: {
select: {
id: true,
name: true
}
},
_count: {
select: {
helpfulVotedBy: true,
reportedBy: true
}
}
},
orderBy: {
createdAt: 'desc'
},
skip,
take: limit
}),
prisma.review.count({ where })
])
const reviewsWithCounts = reviews.map(review => ({
...review,
helpfulVotes: review._count.helpfulVotedBy,
reportCount: review._count.reportedBy
}))
return NextResponse.json({
reviews: reviewsWithCounts,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
})
} catch (error) {
console.error('Error fetching admin reviews:', error)
return NextResponse.json(
{ error: 'Failed to fetch reviews' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,62 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const categories = await prisma.category.findMany({
include: {
_count: {
select: {
products: true
}
}
},
orderBy: { name: 'asc' }
})
const categoriesWithCount = categories.map(category => ({
id: category.id,
name: category.name,
description: category.description,
image: category.image,
isActive: category.isActive,
productCount: category._count.products
}))
return NextResponse.json({ categories: categoriesWithCount })
} catch (error) {
console.error('Error fetching categories:', error)
return NextResponse.json({ categories: [] })
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { name, description, image, isActive } = await request.json()
const category = await prisma.category.create({
data: { name, description, image, isActive }
})
return NextResponse.json(category)
} catch (error) {
console.error('Error creating category:', error)
return NextResponse.json(
{ error: 'Failed to create category' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,46 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const settings = await prisma.commissionSettings.findMany({
orderBy: { level: 'asc' }
})
return NextResponse.json({ settings })
} catch (error) {
console.error('Error fetching commission settings:', error)
return NextResponse.json({ settings: [] })
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { level, percentage, isActive } = await request.json()
const setting = await prisma.commissionSettings.create({
data: { level, percentage, isActive }
})
return NextResponse.json(setting)
} catch (error) {
console.error('Error creating commission setting:', error)
return NextResponse.json(
{ error: 'Failed to create commission setting' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,128 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { clearSettingsCache } from '@/lib/settings'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Get or create system settings
let settings = await prisma.systemSettings.findFirst()
if (!settings) {
// Create default settings if none exist
settings = await prisma.systemSettings.create({
data: {
siteName: 'Padmaaja Rasooi',
siteDescription: 'Premium Rice Products & Quality Grains',
supportEmail: 'support@padmaajarasooi.com',
minimumPayout: 100,
enableReferrals: true,
enableCommissions: true,
maintenanceMode: false,
allowRegistration: true
}
})
}
return NextResponse.json({
siteName: settings.siteName,
siteDescription: settings.siteDescription,
supportEmail: settings.supportEmail,
minimumPayout: settings.minimumPayout,
enableReferrals: settings.enableReferrals,
enableCommissions: settings.enableCommissions,
maintenanceMode: settings.maintenanceMode,
allowRegistration: settings.allowRegistration
})
} catch (error) {
console.error('Error fetching system settings:', error)
return NextResponse.json(
{ error: 'Failed to fetch system settings' },
{ status: 500 }
)
}
}
export async function PUT(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const {
siteName,
siteDescription,
supportEmail,
minimumPayout,
enableReferrals,
enableCommissions,
maintenanceMode,
allowRegistration
} = await request.json()
// Get or create system settings
let settings = await prisma.systemSettings.findFirst()
if (!settings) {
// Create new settings
settings = await prisma.systemSettings.create({
data: {
siteName,
siteDescription,
supportEmail,
minimumPayout,
enableReferrals,
enableCommissions,
maintenanceMode,
allowRegistration
}
})
} else {
// Update existing settings
settings = await prisma.systemSettings.update({
where: { id: settings.id },
data: {
siteName,
siteDescription,
supportEmail,
minimumPayout,
enableReferrals,
enableCommissions,
maintenanceMode,
allowRegistration
}
})
}
// Clear the settings cache so new values are fetched
clearSettingsCache()
return NextResponse.json({
success: true,
settings: {
siteName: settings.siteName,
siteDescription: settings.siteDescription,
supportEmail: settings.supportEmail,
minimumPayout: settings.minimumPayout,
enableReferrals: settings.enableReferrals,
enableCommissions: settings.enableCommissions,
maintenanceMode: settings.maintenanceMode,
allowRegistration: settings.allowRegistration
}
})
} catch (error) {
console.error('Error updating system settings:', error)
return NextResponse.json(
{ error: 'Failed to update system settings' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,76 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '10')
const search = searchParams.get('search')
const role = searchParams.get('role')
const skip = (page - 1) * limit
const where: any = {}
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
]
}
if (role) {
where.role = role
}
const [users, total] = await Promise.all([
prisma.user.findMany({
where,
select: {
id: true,
name: true,
email: true,
role: true,
isActive: true,
joinedAt: true,
referralCode: true,
referrer: {
select: {
name: true,
email: true,
},
},
_count: {
select: {
referrals: true,
orders: true,
},
},
},
skip,
take: limit,
orderBy: { joinedAt: 'desc' },
}),
prisma.user.count({ where }),
])
return NextResponse.json({
users,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit),
},
})
} catch (error) {
console.error('Admin users API error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}

View File

@@ -0,0 +1,3 @@
import { handlers } from '@/auth'
export const { GET, POST } = handlers

View File

@@ -0,0 +1,72 @@
import { NextRequest, NextResponse } from "next/server";
import { hash } from 'bcryptjs'
import { prisma } from '@/lib/prisma'
import { signUpSchema } from '@/lib/validations/auth'
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { name, email, password, phone, referralCode, role } = signUpSchema.parse(body)
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email },
})
if (existingUser) {
return NextResponse.json(
{ error: 'User already exists' },
{ status: 400 }
)
}
// Hash password
const hashedPassword = await hash(password, 12)
// Find referrer if referral code is provided
let referrerId = null
if (referralCode) {
const referrer = await prisma.user.findUnique({
where: { referralCode },
})
if (referrer) {
referrerId = referrer.id
}
}
// Create user
const user = await prisma.user.create({
data: {
name,
email,
password: hashedPassword,
phone,
role: role || 'CUSTOMER',
referrerId,
},
})
// Create wallet for members
if (role === 'MEMBER') {
await prisma.wallet.create({
data: {
userId: user.id,
balance: 0,
totalEarnings: 0,
totalWithdrawn: 0,
},
})
}
return NextResponse.json(
{ message: 'User created successfully', userId: user.id },
{ status: 201 }
)
} catch (error) {
console.error('Signup error:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}

105
app/api/categories/route.ts Normal file
View File

@@ -0,0 +1,105 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { categorySchema } from '@/lib/validations/product'
import { DatabaseOptimizer } from '@/lib/database-optimizer'
export async function GET() {
const startTime = Date.now()
try {
// Cache key for categories
const cacheKey = 'categories_navigation_with_counts'
// Try to get from cache first
const cached = await DatabaseOptimizer.getCachedData(cacheKey)
if (cached) {
return NextResponse.json({
...cached,
_performance: {
responseTime: Date.now() - startTime,
cached: true
}
})
}
// Get categories with product counts for better navigation
const categories = await DatabaseOptimizer.executeOptimizedQuery(
'categories_with_product_counts',
async () => {
const cats = await prisma.category.findMany({
where: { isActive: true },
include: {
_count: {
select: {
products: {
where: {
isActive: true,
stock: { gt: 0 }
}
}
}
}
},
orderBy: { name: 'asc' },
})
// Add SEO-friendly data
return cats.map(cat => ({
...cat,
productCount: cat._count.products,
hasProducts: cat._count.products > 0,
_count: undefined // Remove from response
}))
},
1800 // Cache for 30 minutes
)
const responseData = {
categories,
seo: {
totalCategories: categories.length,
categoriesWithProducts: categories.filter(c => c.hasProducts).length
}
}
// Cache the response
await DatabaseOptimizer.setCachedData(cacheKey, responseData, 1800)
return NextResponse.json({
...responseData,
_performance: {
responseTime: Date.now() - startTime,
cached: false
}
})
} catch (error) {
console.error('Categories API error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const data = categorySchema.parse(body)
const category = await prisma.category.create({
data,
})
// Invalidate category caches after creation
DatabaseOptimizer.invalidateCache('categories')
DatabaseOptimizer.invalidateCache('category_stats')
return NextResponse.json(category, { status: 201 })
} catch (error) {
console.error('Create category error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}

View File

@@ -0,0 +1,69 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const url = new URL(request.url)
const page = parseInt(url.searchParams.get('page') || '1')
const limit = parseInt(url.searchParams.get('limit') || '50')
const status = url.searchParams.get('status')
const type = url.searchParams.get('type')
const level = url.searchParams.get('level')
const where: any = { userId: session.user.id }
if (status && status !== 'all') {
where.status = status
}
if (type && type !== 'all') {
where.type = type
}
if (level && level !== 'all') {
where.level = parseInt(level)
}
const commissions = await prisma.commission.findMany({
where,
include: {
fromUser: {
select: {
name: true,
email: true
}
}
},
orderBy: {
createdAt: 'desc'
},
skip: (page - 1) * limit,
take: limit
})
const total = await prisma.commission.count({ where })
return NextResponse.json({
commissions,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
})
} catch (error) {
console.error('Error fetching commissions:', error)
return NextResponse.json(
{ error: 'Failed to fetch commissions' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,95 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Get total earnings
const totalEarningsResult = await prisma.commission.aggregate({
where: {
userId,
status: { in: ['APPROVED', 'PAID'] }
},
_sum: {
amount: true
}
})
// Get pending amount
const pendingAmountResult = await prisma.commission.aggregate({
where: {
userId,
status: 'PENDING'
},
_sum: {
amount: true
}
})
// Get this month earnings
const now = new Date()
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
const thisMonthResult = await prisma.commission.aggregate({
where: {
userId,
status: { in: ['APPROVED', 'PAID'] },
createdAt: {
gte: startOfMonth
}
},
_sum: {
amount: true
}
})
// Get total commissions count
const totalCommissions = await prisma.commission.count({
where: { userId }
})
// Get earnings by level
const byLevel = await prisma.commission.groupBy({
by: ['level'],
where: {
userId,
status: { in: ['APPROVED', 'PAID'] }
},
_sum: {
amount: true
},
_count: {
id: true
},
orderBy: {
level: 'asc'
}
})
return NextResponse.json({
totalEarnings: totalEarningsResult._sum.amount || 0,
pendingAmount: pendingAmountResult._sum.amount || 0,
thisMonthEarnings: thisMonthResult._sum.amount || 0,
totalCommissions,
byLevel: byLevel.map(item => ({
level: item.level,
amount: item._sum.amount || 0,
count: item._count.id
}))
})
} catch (error) {
console.error('Error fetching commission stats:', error)
return NextResponse.json(
{ error: 'Failed to fetch commission stats' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,56 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '10')
const skip = (page - 1) * limit
const [commissions, total] = await Promise.all([
prisma.commission.findMany({
where: { userId: session.user.id },
include: {
fromUser: {
select: {
name: true,
email: true
}
}
},
orderBy: { createdAt: 'desc' },
skip,
take: limit
}),
prisma.commission.count({ where: { userId: session.user.id } })
])
return NextResponse.json({
commissions: commissions.map(commission => ({
id: commission.id,
amount: commission.amount,
level: commission.level,
type: commission.type,
status: commission.status,
createdAt: commission.createdAt,
fromUser: commission.fromUser
})),
total,
pages: Math.ceil(total / limit)
})
} catch (error) {
console.error('Error fetching commissions:', error)
return NextResponse.json(
{ error: 'Failed to fetch commissions' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,171 @@
import { NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
interface TeamMember {
id: string
name: string
email: string
joinedAt: string
totalEarnings: number
directReferrals: number
level: number
children?: TeamMember[]
}
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Build genealogy tree
const genealogyTree = await buildGenealogyTree(userId, 0)
// Calculate stats
const teamSize = await calculateTeamSize(userId)
const totalVolume = await calculateTeamVolume(userId)
const levels = await calculateNetworkLevels(userId)
return NextResponse.json({
user: genealogyTree,
teamSize,
totalVolume,
levels
})
} catch (error) {
console.error('Error fetching genealogy data:', error)
return NextResponse.json(
{ error: 'Failed to fetch genealogy data' },
{ status: 500 }
)
}
}
async function buildGenealogyTree(userId: string, level: number): Promise<TeamMember> {
const user = await prisma.user.findUnique({
where: { id: userId },
include: {
wallet: true,
referrals: {
select: {
id: true,
name: true,
email: true,
joinedAt: true
}
}
}
})
if (!user) {
throw new Error('User not found')
}
// Get direct referrals count
const directReferrals = await prisma.user.count({
where: { referrerId: userId }
})
const teamMember: TeamMember = {
id: user.id,
name: user.name || 'Unknown',
email: user.email,
joinedAt: user.joinedAt.toISOString(),
totalEarnings: user.wallet?.totalEarnings || 0,
directReferrals,
level,
children: []
}
// Recursively build children (limit to 5 levels to prevent infinite recursion)
if (level < 5 && user.referrals.length > 0) {
for (const referral of user.referrals) {
const childTree = await buildGenealogyTree(referral.id, level + 1)
teamMember.children!.push(childTree)
}
}
return teamMember
}
async function calculateTeamSize(userId: string): Promise<number> {
const getAllTeamMembers = async (id: string): Promise<string[]> => {
const directReferrals = await prisma.user.findMany({
where: { referrerId: id },
select: { id: true }
})
let allMembers = directReferrals.map(r => r.id)
for (const referral of directReferrals) {
const subTeam = await getAllTeamMembers(referral.id)
allMembers = [...allMembers, ...subTeam]
}
return allMembers
}
const teamMembers = await getAllTeamMembers(userId)
return teamMembers.length
}
async function calculateTeamVolume(userId: string): Promise<number> {
const getAllTeamMembers = async (id: string): Promise<string[]> => {
const directReferrals = await prisma.user.findMany({
where: { referrerId: id },
select: { id: true }
})
let allMembers = directReferrals.map(r => r.id)
for (const referral of directReferrals) {
const subTeam = await getAllTeamMembers(referral.id)
allMembers = [...allMembers, ...subTeam]
}
return allMembers
}
const teamMembers = await getAllTeamMembers(userId)
if (teamMembers.length === 0) return 0
const volume = await prisma.order.aggregate({
where: {
userId: { in: teamMembers },
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] }
},
_sum: { total: true }
})
return volume._sum.total || 0
}
async function calculateNetworkLevels(userId: string): Promise<number> {
const getMaxLevel = async (id: string, currentLevel: number): Promise<number> => {
const directReferrals = await prisma.user.findMany({
where: { referrerId: id },
select: { id: true }
})
if (directReferrals.length === 0) {
return currentLevel
}
let maxLevel = currentLevel + 1
for (const referral of directReferrals) {
const level = await getMaxLevel(referral.id, currentLevel + 1)
maxLevel = Math.max(maxLevel, level)
}
return maxLevel
}
return await getMaxLevel(userId, 0)
}

View File

@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { amount, bankDetails } = await request.json()
// Check wallet balance
const wallet = await prisma.wallet.findUnique({
where: { userId: session.user.id }
})
if (!wallet || wallet.balance < amount) {
return NextResponse.json({ error: 'Insufficient balance' }, { status: 400 })
}
// Create payout request
const payout = await prisma.payout.create({
data: {
userId: session.user.id,
amount,
bankDetails,
status: 'PENDING'
}
})
// Update wallet balance (deduct the requested amount)
await prisma.wallet.update({
where: { userId: session.user.id },
data: {
balance: { decrement: amount }
}
})
return NextResponse.json(payout)
} catch (error) {
console.error('Error creating payout request:', error)
return NextResponse.json(
{ error: 'Failed to create payout request' },
{ status: 500 }
)
}
}
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const payouts = await prisma.payout.findMany({
where: { userId: session.user.id },
orderBy: { createdAt: 'desc' }
})
return NextResponse.json(payouts)
} catch (error) {
console.error('Error fetching payouts:', error)
return NextResponse.json(
{ error: 'Failed to fetch payouts' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,80 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { CommissionService } from '@/lib/commission'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
// Get user profile
const user = await prisma.user.findUnique({
where: { id },
select: {
id: true,
name: true,
email: true,
phone: true,
address: true,
joinedAt: true,
role: true,
referralCode: true,
isActive: true
}
})
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
// Get team stats
const stats = await CommissionService.getTeamStats(id)
// Get wallet info
const wallet = await prisma.wallet.findUnique({
where: { userId: id }
})
// Get recent commissions
const recentCommissions = await prisma.commission.findMany({
where: { userId: id },
orderBy: { createdAt: 'desc' },
take: 5,
select: {
id: true,
amount: true,
level: true,
type: true,
status: true,
createdAt: true
}
})
const profile = {
...user,
stats: {
...stats,
totalEarnings: wallet?.totalEarnings || 0,
walletBalance: wallet?.balance || 0
},
recentCommissions
}
return NextResponse.json(profile)
} catch (error) {
console.error('Error fetching user profile:', error)
return NextResponse.json(
{ error: 'Failed to fetch profile' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,70 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Get user's direct referrals
const referrals = await prisma.user.findMany({
where: {
referrerId: session.user.id
},
select: {
id: true,
name: true,
email: true,
phone: true,
joinedAt: true,
isActive: true,
orders: {
where: {
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] }
},
select: {
total: true
}
},
// Get commissions earned from this referral
commissionsFrom: {
where: {
userId: session.user.id,
status: { in: ['APPROVED', 'PAID'] }
},
select: {
amount: true
}
}
}
})
const formattedReferrals = referrals.map(referral => ({
id: referral.id,
name: referral.name || 'User',
email: referral.email,
phone: referral.phone,
joinedAt: referral.joinedAt,
isActive: referral.isActive,
totalOrders: referral.orders.length,
totalSpent: referral.orders.reduce((sum, order) => sum + order.total, 0),
commissionEarned: referral.commissionsFrom.reduce((sum, comm) => sum + comm.amount, 0),
status: referral.isActive ? 'ACTIVE' : 'INACTIVE'
}))
return NextResponse.json({
referrals: formattedReferrals
})
} catch (error) {
console.error('Error fetching referrals:', error)
return NextResponse.json(
{ error: 'Failed to fetch referrals' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,89 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Get total referrals
const totalReferrals = await prisma.user.count({
where: { referrerId: userId }
})
// Get active referrals
const activeReferrals = await prisma.user.count({
where: {
referrerId: userId,
isActive: true
}
})
// Get pending referrals
const pendingReferrals = await prisma.user.count({
where: {
referrerId: userId,
isActive: false
}
})
// Get total commission earned from referrals
const totalCommissionResult = await prisma.commission.aggregate({
where: {
userId,
status: { in: ['APPROVED', 'PAID'] }
},
_sum: {
amount: true
}
})
// Get this month's stats
const now = new Date()
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
const thisMonthReferrals = await prisma.user.count({
where: {
referrerId: userId,
joinedAt: {
gte: startOfMonth
}
}
})
const thisMonthCommissionResult = await prisma.commission.aggregate({
where: {
userId,
status: { in: ['APPROVED', 'PAID'] },
createdAt: {
gte: startOfMonth
}
},
_sum: {
amount: true
}
})
return NextResponse.json({
totalReferrals,
activeReferrals,
pendingReferrals,
totalCommissionEarned: totalCommissionResult._sum.amount || 0,
thisMonthReferrals,
thisMonthCommission: thisMonthCommissionResult._sum.amount || 0
})
} catch (error) {
console.error('Error fetching referral stats:', error)
return NextResponse.json(
{ error: 'Failed to fetch referral stats' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,159 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const range = parseInt(searchParams.get('range') || '30')
const type = searchParams.get('type') || 'all'
const userId = session.user.id
const startDate = new Date()
startDate.setDate(startDate.getDate() - range)
// Get commission data
const commissions = await prisma.commission.findMany({
where: {
userId,
createdAt: { gte: startDate }
},
include: {
fromUser: {
select: { name: true, email: true }
}
},
orderBy: { createdAt: 'desc' }
})
const totalCommissions = commissions.reduce((sum, c) => sum + c.amount, 0)
// Calculate commission growth
const previousPeriodStart = new Date(startDate)
previousPeriodStart.setDate(previousPeriodStart.getDate() - range)
const previousCommissions = await prisma.commission.findMany({
where: {
userId,
createdAt: {
gte: previousPeriodStart,
lt: startDate
}
}
})
const previousTotal = previousCommissions.reduce((sum, c) => sum + c.amount, 0)
const growth = previousTotal > 0 ? ((totalCommissions - previousTotal) / previousTotal) * 100 : 0
// Commission by level
const commissionsByLevel = commissions.reduce((acc, c) => {
const existing = acc.find(item => item.level === c.level)
if (existing) {
existing.amount += c.amount
existing.count += 1
} else {
acc.push({ level: c.level, amount: c.amount, count: 1 })
}
return acc
}, [] as { level: number; amount: number; count: number }[])
// Get team data
const directReferrals = await prisma.user.findMany({
where: { referrerId: userId },
select: {
id: true,
joinedAt: true,
orders: {
where: {
createdAt: { gte: startDate },
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] }
}
}
}
})
// Get all team members recursively
const getAllTeamMembers = async (id: string): Promise<string[]> => {
const refs = await prisma.user.findMany({
where: { referrerId: id },
select: { id: true }
})
let allMembers = refs.map(r => r.id)
for (const ref of refs) {
const subTeam = await getAllTeamMembers(ref.id)
allMembers = [...allMembers, ...subTeam]
}
return allMembers
}
const allTeamMemberIds = await getAllTeamMembers(userId)
// Team metrics
const activeMembers = directReferrals.filter(r => r.orders.length > 0).length
const newThisMonth = directReferrals.filter(r =>
new Date(r.joinedAt) >= new Date(new Date().getFullYear(), new Date().getMonth(), 1)
).length
// Sales data
const userOrders = await prisma.order.findMany({
where: {
userId,
createdAt: { gte: startDate },
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] }
}
})
const teamOrders = await prisma.order.findMany({
where: {
userId: { in: allTeamMemberIds },
createdAt: { gte: startDate },
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] }
}
})
const personalVolume = userOrders.reduce((sum, o) => sum + o.total, 0)
const teamVolume = teamOrders.reduce((sum, o) => sum + o.total, 0)
const reportData = {
commissions: {
total: totalCommissions,
thisMonth: commissions.filter(c =>
new Date(c.createdAt) >= new Date(new Date().getFullYear(), new Date().getMonth(), 1)
).reduce((sum, c) => sum + c.amount, 0),
lastMonth: previousTotal,
growth,
byLevel: commissionsByLevel.sort((a, b) => a.level - b.level),
recent: commissions.slice(0, 10)
},
team: {
totalMembers: allTeamMemberIds.length,
activeMembers,
newThisMonth,
byLevel: [{ level: 1, count: directReferrals.length }] // Simplified for now
},
sales: {
totalVolume: personalVolume + teamVolume,
personalVolume,
teamVolume,
ordersCount: userOrders.length + teamOrders.length
}
}
return NextResponse.json(reportData)
} catch (error) {
console.error('Error fetching reports:', error)
return NextResponse.json(
{ error: 'Failed to fetch reports' },
{ status: 500 }
)
}
}

119
app/api/dashboard/route.ts Normal file
View File

@@ -0,0 +1,119 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Get user's wallet
const wallet = await prisma.wallet.findUnique({
where: { userId }
})
// Get commission stats
const [totalEarnings, pendingCommissions, thisMonthEarnings] = await Promise.all([
prisma.commission.aggregate({
where: {
userId,
status: { in: ['APPROVED', 'PAID'] }
},
_sum: { amount: true }
}),
prisma.commission.aggregate({
where: {
userId,
status: 'PENDING'
},
_sum: { amount: true }
}),
prisma.commission.aggregate({
where: {
userId,
status: { in: ['APPROVED', 'PAID'] },
createdAt: {
gte: new Date(new Date().getFullYear(), new Date().getMonth(), 1)
}
},
_sum: { amount: true }
})
])
// Get referral stats
const [totalReferrals, activeReferrals] = await Promise.all([
prisma.user.count({
where: { referrerId: userId }
}),
prisma.user.count({
where: {
referrerId: userId,
isActive: true
}
})
])
// Get order stats
const totalOrders = await prisma.order.count({
where: { userId }
})
// Get recent commissions
const recentCommissions = await prisma.commission.findMany({
where: { userId },
include: {
fromUser: {
select: {
name: true,
email: true
}
}
},
orderBy: { createdAt: 'desc' },
take: 5
})
// Get recent orders
const recentOrders = await prisma.order.findMany({
where: { userId },
include: {
orderItems: {
include: {
product: {
select: {
name: true
}
}
}
}
},
orderBy: { createdAt: 'desc' },
take: 5
})
const stats = {
totalEarnings: totalEarnings._sum.amount || 0,
pendingCommissions: pendingCommissions._sum.amount || 0,
thisMonthEarnings: thisMonthEarnings._sum.amount || 0,
currentBalance: wallet?.balance || 0,
totalReferrals,
activeReferrals,
totalOrders,
recentCommissions,
recentOrders
}
return NextResponse.json({ stats })
} catch (error) {
console.error('Error fetching dashboard stats:', error)
return NextResponse.json(
{ error: 'Failed to fetch dashboard stats' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,48 @@
import { NextResponse } from 'next/server'
import { auth } from '@/auth'
import { CommissionService } from '@/lib/commission'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Get team stats
const teamStats = await CommissionService.getTeamStats(userId)
// Get wallet info
const wallet = await prisma.wallet.findUnique({
where: { userId }
})
// Get pending commissions
const pendingCommissions = await prisma.commission.aggregate({
where: {
userId,
status: 'PENDING'
},
_sum: { amount: true }
})
const stats = {
...teamStats,
totalEarnings: wallet?.totalEarnings || 0,
walletBalance: wallet?.balance || 0,
pendingCommissions: pendingCommissions._sum.amount || 0
}
return NextResponse.json(stats)
} catch (error) {
console.error('Error fetching dashboard stats:', error)
return NextResponse.json(
{ error: 'Failed to fetch stats' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,100 @@
import { NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Get direct referrals
const directReferrals = await prisma.user.findMany({
where: { referrerId: userId },
select: {
id: true,
name: true,
email: true,
role: true,
joinedAt: true,
referrals: {
select: {
id: true
}
}
},
orderBy: { joinedAt: 'desc' }
})
// Get team statistics by levels
const getAllTeamMembers = async (id: string, level: number = 1, maxLevel: number = 5): Promise<string[]> => {
if (level > maxLevel) return []
const refs = await prisma.user.findMany({
where: { referrerId: id },
select: { id: true }
})
let allMembers = refs.map(r => r.id)
if (level < maxLevel) {
for (const ref of refs) {
const subTeam = await getAllTeamMembers(ref.id, level + 1, maxLevel)
allMembers = [...allMembers, ...subTeam]
}
}
return allMembers
}
// Calculate team size per level
const levels = []
for (let level = 1; level <= 5; level++) {
if (level === 1) {
levels.push({ level, count: directReferrals.length })
} else {
// Get members at specific level
const getLevelMembers = async (parentIds: string[], targetLevel: number, currentLevel: number = 1): Promise<string[]> => {
if (currentLevel === targetLevel) return parentIds
const nextLevel = await prisma.user.findMany({
where: { referrerId: { in: parentIds } },
select: { id: true }
})
if (nextLevel.length === 0 || currentLevel >= targetLevel) return []
return getLevelMembers(nextLevel.map(u => u.id), targetLevel, currentLevel + 1)
}
const levelMemberIds = await getLevelMembers([userId], level)
levels.push({ level, count: levelMemberIds.length })
}
}
const allTeamMemberIds = await getAllTeamMembers(userId)
const teamData = {
directReferrals: directReferrals.map(ref => ({
...ref,
referrals: ref.referrals || []
})),
teamStats: {
totalTeamSize: allTeamMemberIds.length,
levels
}
}
return NextResponse.json(teamData)
} catch (error) {
console.error('Error fetching team data:', error)
return NextResponse.json(
{ error: 'Failed to fetch team data' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,71 @@
import { NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = session.user.id
// Get wallet info
const wallet = await prisma.wallet.findUnique({
where: { userId }
})
// Mock transaction history (you can implement proper transaction tracking)
const commissions = await prisma.commission.findMany({
where: { userId },
orderBy: { createdAt: 'desc' },
take: 20,
include: {
fromUser: {
select: { name: true }
}
}
})
const payouts = await prisma.payout.findMany({
where: { userId },
orderBy: { createdAt: 'desc' },
take: 20
})
// Combine and format transactions
const transactions = [
...commissions.map(commission => ({
id: commission.id,
type: 'COMMISSION' as const,
amount: commission.amount,
description: `Level ${commission.level} commission from ${commission.fromUser.name}`,
status: commission.status,
createdAt: commission.createdAt
})),
...payouts.map(payout => ({
id: payout.id,
type: 'PAYOUT' as const,
amount: payout.amount,
description: 'Withdrawal request',
status: payout.status,
createdAt: payout.createdAt
}))
].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
return NextResponse.json({
balance: wallet?.balance || 0,
totalEarnings: wallet?.totalEarnings || 0,
totalWithdrawn: wallet?.totalWithdrawn || 0,
transactions
})
} catch (error) {
console.error('Error fetching wallet data:', error)
return NextResponse.json(
{ error: 'Failed to fetch wallet data' },
{ status: 500 }
)
}
}

105
app/api/forms/[id]/route.ts Normal file
View File

@@ -0,0 +1,105 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { z } from 'zod'
const UpdateFormSchema = z.object({
status: z.enum(['new', 'in_progress', 'resolved', 'closed'])
})
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const formResponse = await prisma.formResponse.findUnique({
where: { id }
})
if (!formResponse) {
return NextResponse.json(
{ success: false, message: 'Form response not found' },
{ status: 404 }
)
}
return NextResponse.json({
success: true,
data: formResponse
})
} catch (error) {
console.error('Error fetching form response:', error)
return NextResponse.json(
{ success: false, message: 'Failed to fetch form response' },
{ status: 500 }
)
}
}
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const body = await request.json()
const validatedData = UpdateFormSchema.parse(body)
const formResponse = await prisma.formResponse.update({
where: { id },
data: {
status: validatedData.status,
updatedAt: new Date()
}
})
return NextResponse.json({
success: true,
data: formResponse
})
} catch (error) {
console.error('Error updating form response:', error)
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
message: 'Validation error',
errors: error.errors
},
{ status: 400 }
)
}
return NextResponse.json(
{ success: false, message: 'Failed to update form response' },
{ status: 500 }
)
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
await prisma.formResponse.delete({
where: { id }
})
return NextResponse.json({
success: true,
message: 'Form response deleted successfully'
})
} catch (error) {
console.error('Error deleting form response:', error)
return NextResponse.json(
{ success: false, message: 'Failed to delete form response' },
{ status: 500 }
)
}
}

168
app/api/forms/route.ts Normal file
View File

@@ -0,0 +1,168 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { z } from 'zod'
import { Prisma } from '@prisma/client'
// CORS headers
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
// Validation schema for form submissions
const FormSubmissionSchema = z.object({
formId: z.string().min(1, 'Form ID is required'), // e.g., "contact", "partnership", "bulk_inquiry"
name: z.string().min(2, 'Name must be at least 2 characters'),
email: z.string().email('Invalid email address'),
phone: z.string().optional(),
subject: z.string().optional(),
message: z.string().min(10, 'Message must be at least 10 characters'),
inquiryType: z.string().optional(),
company: z.string().optional(),
formSource: z.string().optional(), // Optional field to track which form page it came from
// Allow additional fields that will be stored in JSON
}).passthrough()
export async function OPTIONS() {
return new Response(null, {
status: 200,
headers: corsHeaders,
})
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// Validate the form data
const validatedData = FormSubmissionSchema.parse(body)
// Extract formId from the data
const { formId, ...formData } = validatedData
// Get client information
const forwarded = request.headers.get('x-forwarded-for')
const ipAddress = forwarded ? forwarded.split(',')[0] :
request.headers.get('x-real-ip') ||
'unknown'
const userAgent = request.headers.get('user-agent') || 'unknown'
const referrer = request.headers.get('referer') || null
// Prepare metadata
const metadata = {
ipAddress,
userAgent,
referrer,
timestamp: new Date().toISOString()
}
// Save to database with new simplified schema
const formResponse = await prisma.formResponse.create({
data: {
formId,
data: formData as Prisma.JsonObject, // Store all form data as JSON with proper Prisma type
metadata: metadata as Prisma.JsonObject, // Cast metadata with proper Prisma type
status: 'new'
}
})
// Send email notification (optional)
// You can add email sending logic here using nodemailer or your preferred service
return NextResponse.json(
{
success: true,
message: 'Form submitted successfully',
id: formResponse.id
},
{
status: 200,
headers: corsHeaders
}
)
} catch (error) {
console.error('Form submission error:', error)
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
message: 'Validation error',
errors: error.errors
},
{
status: 400,
headers: corsHeaders
}
)
}
return NextResponse.json(
{
success: false,
message: 'Internal server error'
},
{
status: 500,
headers: corsHeaders
}
)
}
}
export async function GET(request: NextRequest) {
try {
// This endpoint could be used for admin to fetch form submissions
// Add authentication/authorization logic here
const { searchParams } = new URL(request.url)
const status = searchParams.get('status')
const formId = searchParams.get('formId') // Changed from formType to formId
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '10')
const where: any = {}
if (status) {
where.status = status.toLowerCase() // Changed to lowercase to match new schema
}
if (formId) {
where.formId = formId
}
const [forms, total] = await Promise.all([
prisma.formResponse.findMany({
where,
orderBy: { createdAt: 'desc' },
skip: (page - 1) * limit,
take: limit,
}),
prisma.formResponse.count({ where })
])
return NextResponse.json({
success: true,
data: forms,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
})
} catch (error) {
console.error('Error fetching forms:', error)
return NextResponse.json(
{
success: false,
message: 'Failed to fetch forms'
},
{ status: 500 }
)
}
}

109
app/api/inquiries/route.ts Normal file
View File

@@ -0,0 +1,109 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { emailService } from '@/lib/email'
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// Validate required fields
const requiredFields = [
'companyName', 'contactPerson', 'designation', 'email', 'phone',
'businessType', 'address', 'quantityRequired', 'deliveryLocation', 'message'
]
for (const field of requiredFields) {
if (!body[field]) {
return NextResponse.json(
{ error: `Missing required field: ${field}` },
{ status: 400 }
)
}
}
// Prepare form data for database
const formData = {
formId: 'b2b_inquiry',
data: {
// Company Information
companyName: body.companyName,
contactPerson: body.contactPerson,
designation: body.designation,
email: body.email,
phone: body.phone,
businessType: body.businessType,
gstNumber: body.gstNumber || null,
address: body.address,
// Product Information (if applicable)
productId: body.productId || null,
productName: body.productName || null,
productCategory: body.productCategory || null,
productPrice: body.productPrice || null,
// Requirements
quantityRequired: body.quantityRequired,
quantityUnit: body.quantityUnit || 'tons',
deliveryLocation: body.deliveryLocation,
expectedDeliveryDate: body.expectedDeliveryDate || null,
// Additional Information
message: body.message,
hearAboutUs: body.hearAboutUs || null,
// Metadata
submissionType: 'b2b_inquiry',
submittedAt: body.submittedAt || new Date().toISOString(),
userAgent: request.headers.get('user-agent') || null,
ipAddress: request.headers.get('x-forwarded-for') ||
request.headers.get('x-real-ip') ||
'unknown'
},
status: 'new',
metadata: {
source: 'website_inquiry_form',
type: 'b2b_bulk_inquiry',
priority: 'high'
}
}
// Save to database
const formResponse = await prisma.formResponse.create({
data: formData
})
// Send email notifications
try {
// Send confirmation email to customer
await emailService.sendB2BInquiryConfirmation({
customerEmail: body.email,
customerName: body.contactPerson,
companyName: body.companyName,
inquiryId: formResponse.id,
productName: body.productName
})
// Send notification to admin
await emailService.sendB2BInquiryAdminNotification({
inquiryData: body,
inquiryId: formResponse.id
})
} catch (emailError) {
console.error('Email notification failed:', emailError)
// Don't fail the API call if email fails
}
return NextResponse.json({
success: true,
message: 'Inquiry submitted successfully',
inquiryId: formResponse.id
})
} catch (error) {
console.error('Error processing B2B inquiry:', error)
return NextResponse.json(
{ error: 'Failed to process inquiry. Please try again.' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,24 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { auth } from '@/auth'
export async function GET() {
try {
// Get system settings
const settings = await prisma.systemSettings.findFirst()
// Check if user is admin
const session = await auth()
const isAdmin = session?.user?.role === 'ADMIN'
return NextResponse.json({
maintenanceMode: (settings?.maintenanceMode || false) && !isAdmin
})
} catch (error) {
console.error('Error checking maintenance mode:', error)
// Return false if there's an error to prevent blocking access
return NextResponse.json({
maintenanceMode: false
})
}
}

View File

@@ -0,0 +1,102 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id: orderId } = await params
const order = await prisma.order.findFirst({
where: {
id: orderId,
userId: session.user.id
},
include: {
orderItems: {
include: {
product: {
select: {
name: true,
images: true,
discount: true
}
}
}
},
user: {
select: {
name: true,
email: true
}
},
shippingAddress: {
select: {
firstName: true,
lastName: true,
address1: true,
address2: true,
city: true,
state: true,
zipCode: true,
phone: true
}
}
}
})
if (!order) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
// Calculate estimated delivery (5-7 business days)
const estimatedDelivery = new Date()
estimatedDelivery.setDate(estimatedDelivery.getDate() + 7)
const response = {
orderId: order.id,
total: order.total,
status: order.status,
createdAt: order.createdAt,
estimatedDelivery: estimatedDelivery.toISOString(),
customer: {
name: order.user.name || 'Customer',
email: order.user.email
},
shippingAddress: order.shippingAddress ? {
name: `${order.shippingAddress.firstName} ${order.shippingAddress.lastName}`,
address: order.shippingAddress.address1,
address2: order.shippingAddress.address2,
city: order.shippingAddress.city,
state: order.shippingAddress.state,
zipCode: order.shippingAddress.zipCode,
phone: order.shippingAddress.phone
} : null,
items: order.orderItems.map(item => ({
id: item.id,
name: item.product.name,
quantity: item.quantity,
price: item.price,
image: item.product.images[0] || null,
discount: item.product.discount || 0
}))
}
return NextResponse.json(response)
} catch (error) {
console.error('Error fetching order confirmation:', error)
return NextResponse.json(
{ error: 'Failed to fetch order details' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,63 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { calculateCommissions } from '@/lib/commission'
export async function POST(request: NextRequest, props: { params: Promise<{ id: string }> }) {
const params = await props.params;
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const orderId = params.id
// Verify order belongs to user
const order = await prisma.order.findFirst({
where: {
id: orderId,
userId: session.user.id
}
})
if (!order) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
if (order.status !== 'PENDING') {
return NextResponse.json({ error: 'Order already processed' }, { status: 400 })
}
// Update order status and calculate commissions in transaction
const result = await prisma.$transaction(async (tx) => {
// Update order status
const updatedOrder = await tx.order.update({
where: { id: orderId },
data: { status: 'PAID' }
})
// Calculate and create commissions
const commissionResult = await calculateCommissions(orderId)
return {
order: updatedOrder,
commissions: commissionResult
}
})
return NextResponse.json({
success: true,
order: result.order,
commissions: result.commissions
})
} catch (error) {
console.error('Error confirming order:', error)
return NextResponse.json(
{ error: 'Failed to confirm order' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,110 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const order = await prisma.order.findFirst({
where: {
id,
userId: session.user.id
},
include: {
orderItems: {
include: {
product: {
select: {
id: true,
name: true,
images: true,
discount: true,
sku: true
}
}
}
},
user: {
select: {
name: true,
email: true
}
}
}
})
if (!order) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
return NextResponse.json(order)
} catch (error) {
console.error('Error fetching order:', error)
return NextResponse.json(
{ error: 'Failed to fetch order' },
{ status: 500 }
)
}
}
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id } = await params
const { status } = await request.json()
// Only allow cancellation of pending orders
if (status === 'CANCELLED') {
const order = await prisma.order.findFirst({
where: {
id,
userId: session.user.id,
status: 'PENDING'
}
})
if (!order) {
return NextResponse.json(
{ error: 'Order not found or cannot be cancelled' },
{ status: 400 }
)
}
const updatedOrder = await prisma.order.update({
where: { id },
data: { status: 'CANCELLED' }
})
return NextResponse.json(updatedOrder)
}
return NextResponse.json(
{ error: 'Invalid status update' },
{ status: 400 }
)
} catch (error) {
console.error('Error updating order:', error)
return NextResponse.json(
{ error: 'Failed to update order' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,90 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { orderId, reason } = await request.json()
const result = await prisma.$transaction(async (tx) => {
// Get order with items
const order = await tx.order.findFirst({
where: {
id: orderId,
userId: session.user.id,
status: { in: ['PENDING', 'PAID'] } // Only allow cancellation for these statuses
},
include: {
orderItems: true
}
})
if (!order) {
throw new Error('Order not found or cannot be cancelled')
}
// Update order status
const updatedOrder = await tx.order.update({
where: { id: orderId },
data: {
status: 'CANCELLED'
}
})
// Restore stock for all items
for (const item of order.orderItems) {
await tx.product.update({
where: { id: item.productId },
data: {
stock: {
increment: item.quantity
}
}
})
}
// If order was paid, reverse commissions
if (order.status === 'PAID') {
await tx.commission.updateMany({
where: { orderId: order.id },
data: { status: 'CANCELLED' }
})
// Reverse wallet earnings (you may want to handle this differently)
const commissions = await tx.commission.findMany({
where: { orderId: order.id }
})
for (const commission of commissions) {
await tx.wallet.update({
where: { userId: commission.userId },
data: {
balance: { decrement: commission.amount },
totalEarnings: { decrement: commission.amount }
}
})
}
}
return updatedOrder
})
return NextResponse.json({
success: true,
order: result
})
} catch (error) {
console.error('Error cancelling order:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to cancel order' },
{ status: 500 }
)
}
}

186
app/api/orders/route.ts Normal file
View File

@@ -0,0 +1,186 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { createRazorpayInstance, getRazorpayKeyId } from '@/lib/razorpay'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json(
{ error: 'Authentication required to place orders' },
{ status: 401 }
)
}
const data = await request.json()
const { items, total, shippingAddress, shippingAddressId } = data
// Validate required fields
if (!items || !Array.isArray(items) || items.length === 0) {
return NextResponse.json(
{ error: 'Order items are required' },
{ status: 400 }
)
}
if (!total || total <= 0) {
return NextResponse.json(
{ error: 'Valid order total is required' },
{ status: 400 }
)
}
// Validate stock availability for all items
for (const item of items) {
const product = await prisma.product.findUnique({
where: { id: item.productId },
select: { stock: true, name: true, isActive: true }
})
if (!product) {
return NextResponse.json(
{ error: `Product not found: ${item.productId}` },
{ status: 400 }
)
}
if (!product.isActive) {
return NextResponse.json(
{ error: `Product is no longer available: ${product.name}` },
{ status: 400 }
)
}
if (product.stock < item.quantity) {
return NextResponse.json(
{ error: `Insufficient stock for ${product.name}. Available: ${product.stock}, Requested: ${item.quantity}` },
{ status: 400 }
)
}
}
const razorpay = createRazorpayInstance()
const razorpayKeyId = getRazorpayKeyId()
console.log('Creating Razorpay order for amount:', total)
console.log('Using Razorpay environment:', process.env.RAZORPAY_ENV || 'test')
console.log('Razorpay Key ID:', razorpayKeyId)
// Create Razorpay order
const razorpayOrder = await razorpay.orders.create({
amount: Math.round(total * 100), // Convert to paise
currency: 'INR',
receipt: `order_${Date.now()}`,
notes: {
userId: session.user.id,
itemCount: items.length,
environment: process.env.RAZORPAY_ENV || 'test'
}
})
console.log('Razorpay order created:', razorpayOrder.id)
// Use transaction to ensure atomicity
const result = await prisma.$transaction(async (tx) => {
// Create order
const order = await tx.order.create({
data: {
userId: session.user.id,
total,
status: 'PENDING',
razorpayOrderId: razorpayOrder.id,
shippingAddressId: shippingAddressId || null,
orderItems: {
create: items.map((item: any) => ({
productId: item.productId,
quantity: item.quantity,
price: item.price
}))
}
},
include: {
orderItems: {
include: {
product: true
}
},
shippingAddress: true
}
})
// Reserve stock (reduce stock for pending orders)
for (const item of items) {
await tx.product.update({
where: { id: item.productId },
data: {
stock: {
decrement: item.quantity
}
}
})
}
return order
})
return NextResponse.json({
id: result.id,
total: result.total,
status: result.status,
createdAt: result.createdAt,
razorpayOrderId: razorpayOrder.id,
razorpayKeyId: razorpayKeyId,
isProduction: process.env.RAZORPAY_ENV === 'production'
})
} catch (error) {
console.error('Error creating order:', error)
return NextResponse.json(
{ error: 'Failed to create order' },
{ status: 500 }
)
}
}
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const orders = await prisma.order.findMany({
where: {
userId: session.user.id
},
include: {
orderItems: {
include: {
product: {
select: {
id: true,
name: true,
images: true,
discount: true
}
}
}
}
},
orderBy: {
createdAt: 'desc'
}
})
return NextResponse.json(orders)
} catch (error) {
console.error('Error fetching orders:', error)
return NextResponse.json(
{ error: 'Failed to fetch orders' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,218 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { getRazorpayConfig } from '@/lib/razorpay'
import crypto from 'crypto'
import { emailService } from '@/lib/email'
import { rankSystem } from '@/lib/ranks'
import { calculateCommissions } from '@/lib/commission'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { razorpay_order_id, razorpay_payment_id, razorpay_signature, orderId } = await request.json()
const config = getRazorpayConfig()
// Verify signature
const hmac = crypto.createHmac('sha256', config.keySecret)
hmac.update(razorpay_order_id + '|' + razorpay_payment_id)
const generated_signature = hmac.digest('hex')
if (generated_signature !== razorpay_signature) {
console.error('Payment signature verification failed')
console.error('Generated:', generated_signature)
console.error('Received:', razorpay_signature)
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 })
}
// Process successful payment in transaction
const result = await prisma.$transaction(async (tx) => {
// Update order status
const order = await tx.order.update({
where: {
id: orderId,
userId: session.user.id
},
data: {
status: 'PAID',
razorpayPaymentId: razorpay_payment_id
},
include: {
orderItems: {
include: {
product: true
}
},
user: {
include: {
referrer: true
}
}
}
})
return order
})
// Calculate commissions after successful payment
if (result.user.referrerId) {
const commissionResult = await calculateCommissions(result.id)
console.log('Commission calculation result:', commissionResult)
}
// Send order confirmation email (don't block on failure)
setImmediate(async () => {
try {
const emailResult = await emailService.sendOrderConfirmation(
result.user.email,
result.user.name || 'Customer',
result
)
if (!emailResult.skipped) {
console.log('Order confirmation email sent successfully')
}
} catch (emailError) {
console.error('Failed to send order confirmation email:', emailError)
}
})
// Update ranks for user and referrers (don't block on failure)
setImmediate(async () => {
try {
await rankSystem.updateUserRank(result.userId)
if (result.user.referrerId) {
await rankSystem.updateUserRank(result.user.referrerId)
}
console.log('User ranks updated successfully')
} catch (rankError) {
console.error('Failed to update ranks:', rankError)
}
})
console.log('Payment verified and order processed successfully')
return NextResponse.json({
success: true,
order: result
})
} catch (error) {
console.error('Error verifying payment:', error)
return NextResponse.json(
{ error: 'Failed to verify payment' },
{ status: 500 }
)
}
}
async function generateCommissions(tx: any, order: any) {
// Check if commission settings exist, if not create default ones
const commissionSettings = await tx.commissionSettings.findMany({
where: { isActive: true },
orderBy: { level: 'asc' }
})
// If no settings exist, create default commission structure
if (commissionSettings.length === 0) {
await tx.commissionSettings.createMany({
data: [
{ level: 1, percentage: 10, isActive: true },
{ level: 2, percentage: 5, isActive: true },
{ level: 3, percentage: 3, isActive: true }
]
})
// Refetch settings
const newSettings = await tx.commissionSettings.findMany({
where: { isActive: true },
orderBy: { level: 'asc' }
})
await processCommissions(tx, order, newSettings)
} else {
await processCommissions(tx, order, commissionSettings)
}
}
async function processCommissions(tx: any, order: any, settings: any[]) {
let currentUser = order.user
let level = 1
for (const setting of settings) {
if (!currentUser.referrerId || level > setting.level) break
const referrer = await tx.user.findUnique({
where: { id: currentUser.referrerId },
include: { referrer: true, currentRank: true }
})
if (!referrer) break
// Apply rank multiplier to commission
let commissionAmount = (order.total * setting.percentage) / 100
if (referrer.currentRank) {
commissionAmount *= referrer.currentRank.commissionMultiplier
}
// Create commission record
const commission = await tx.commission.create({
data: {
userId: referrer.id,
fromUserId: order.userId,
orderId: order.id,
amount: commissionAmount,
level: setting.level,
type: 'REFERRAL',
status: 'APPROVED'
},
include: {
fromUser: {
select: {
name: true,
email: true
}
}
}
})
// Update referrer's wallet
await tx.wallet.upsert({
where: { userId: referrer.id },
update: {
balance: { increment: commissionAmount },
totalEarnings: { increment: commissionAmount }
},
create: {
userId: referrer.id,
balance: commissionAmount,
totalEarnings: commissionAmount,
totalWithdrawn: 0
}
})
// Send commission email (async, don't block transaction)
setImmediate(async () => {
try {
const emailResult = await emailService.sendCommissionAlert(
referrer.email,
referrer.name || 'User',
commission
)
if (!emailResult.skipped) {
console.log('Commission alert email sent successfully')
}
} catch (emailError) {
console.error('Failed to send commission email:', emailError)
}
})
console.log(`Generated commission of ₹${commissionAmount.toFixed(2)} for user ${referrer.id} at level ${setting.level}`)
currentUser = referrer
level++
}
}

View File

@@ -0,0 +1,160 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcryptjs'
import { EmailService } from '@/lib/email'
interface PartTimeRegistrationData {
// Personal Information
firstName: string
lastName: string
email: string
phone: string
age: string
gender: string
// Address Information
address: string
city: string
state: string
zipCode: string
// Professional Information
education: string
experience: string
preferredRole: string
availableHours: string
availableDays: string
// Additional Information
skills: string
motivation: string
previousWorkExperience: string
languagesKnown: string
}
export async function POST(request: NextRequest) {
try {
const data: PartTimeRegistrationData = await request.json()
// Validate required fields
const requiredFields = [
'firstName', 'lastName', 'email', 'phone', 'age', 'gender',
'address', 'city', 'state', 'zipCode',
'education', 'experience', 'preferredRole', 'availableHours', 'availableDays',
'motivation'
]
for (const field of requiredFields) {
if (!data[field as keyof PartTimeRegistrationData]) {
return NextResponse.json(
{ success: false, message: `${field} is required` },
{ status: 400 }
)
}
}
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email: data.email }
})
if (existingUser) {
return NextResponse.json(
{ success: false, message: 'Email already registered' },
{ status: 400 }
)
}
// Generate a temporary password
const tempPassword = Math.random().toString(36).slice(-8)
const hashedPassword = await bcrypt.hash(tempPassword, 10)
// Create user with PART_TIME role
const user = await prisma.user.create({
data: {
name: `${data.firstName} ${data.lastName}`,
email: data.email,
password: hashedPassword,
role: 'PART_TIME',
phone: data.phone,
address: `${data.address}, ${data.city}, ${data.state} ${data.zipCode}`,
isActive: true,
joinedAt: new Date()
}
})
// Create address record
await prisma.address.create({
data: {
userId: user.id,
firstName: data.firstName,
lastName: data.lastName,
address1: data.address,
city: data.city,
state: data.state,
zipCode: data.zipCode,
country: 'India',
phone: data.phone,
isDefault: true,
type: 'HOME'
}
})
// Create form response for admin tracking
await prisma.formResponse.create({
data: {
formId: 'part_time_registration',
data: {
...data,
userId: user.id,
registrationDate: new Date().toISOString()
},
status: 'new',
userId: user.id
}
})
// Send email notifications
const emailService = new EmailService()
// Send welcome email to part-time applicant
await emailService.sendPartTimeWelcomeEmail({
to: user.email,
name: user.name || 'Applicant',
email: user.email,
password: tempPassword,
preferredRole: data.preferredRole
})
// Send admin notification
await emailService.sendPartTimeAdminNotification({
applicantName: user.name || 'Unknown',
applicantEmail: user.email,
preferredRole: data.preferredRole,
phone: data.phone,
availableHours: data.availableHours,
availableDays: data.availableDays,
motivation: data.motivation,
registrationDate: new Date().toLocaleDateString()
})
return NextResponse.json({
success: true,
message: 'Part-time job application submitted successfully',
loginCredentials: {
email: user.email,
password: tempPassword
}
})
} catch (error) {
console.error('Part-time registration error:', error)
return NextResponse.json(
{
success: false,
message: 'Registration failed',
error: error instanceof Error ? error.message : 'Unknown error'
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,222 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcryptjs'
import { EmailService } from '@/lib/email'
const emailService = new EmailService()
interface PartnerApplicationData {
// Personal Information
firstName: string
lastName: string
email: string
phone: string
// Business Information
businessName?: string
businessType: string
experience: string
// Partnership Details
partnershipTier: 'Diamond' | 'Gold' | 'Silver'
expectedCustomers: number
// Address Information
address: string
city: string
state: string
zipCode: string
// Additional Information
motivation: string
marketingPlan: string
}
export async function POST(request: NextRequest) {
try {
const data: PartnerApplicationData = await request.json()
// Validate required fields
if (!data.firstName || !data.lastName || !data.email || !data.phone || !data.partnershipTier) {
return NextResponse.json(
{ success: false, message: 'Missing required fields' },
{ status: 400 }
)
}
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email: data.email }
})
if (existingUser) {
return NextResponse.json(
{ success: false, message: 'User with this email already exists' },
{ status: 400 }
)
}
// Check tier availability
const tierAvailability = await checkTierAvailability(data.partnershipTier)
if (!tierAvailability.available) {
return NextResponse.json(
{
success: false,
message: `${data.partnershipTier} tier is currently full. Please select another tier.`,
availableTiers: tierAvailability.availableTiers
},
{ status: 400 }
)
}
// Generate random password and referral code
const randomPassword = Math.random().toString(36).slice(-8) + Math.random().toString(36).slice(-8)
const hashedPassword = await bcrypt.hash(randomPassword, 12)
const referralCode = generateReferralCode(data.firstName, data.lastName)
// Create user with MEMBER role
const newUser = await prisma.user.create({
data: {
name: `${data.firstName} ${data.lastName}`,
email: data.email,
phone: data.phone,
password: hashedPassword,
role: 'MEMBER',
address: `${data.address}, ${data.city}, ${data.state} ${data.zipCode}`,
isActive: true,
partnerTier: data.partnershipTier,
minReferrals: 3, // Partners need to add minimum 3 members
referralCode: referralCode,
}
})
// Create address record
await prisma.address.create({
data: {
userId: newUser.id,
firstName: data.firstName,
lastName: data.lastName,
company: data.businessName || '',
address1: data.address,
city: data.city,
state: data.state,
zipCode: data.zipCode,
phone: data.phone,
isDefault: true,
type: 'HOME'
}
})
// Store form response for admin review
await prisma.formResponse.create({
data: {
formId: 'partnership_application',
userId: newUser.id,
data: {
...data,
applicationStatus: 'approved', // Auto-approve for now
partnerTier: data.partnershipTier
},
status: 'approved'
}
})
// Create wallet for the partner
await prisma.wallet.create({
data: {
userId: newUser.id,
balance: 0,
totalEarnings: 0,
totalWithdrawn: 0
}
})
try {
// Send admin notification email
await emailService.sendPartnerApplicationAdminNotification(data, newUser)
// Send confirmation email to partner
await emailService.sendPartnerWelcomeEmail(data, newUser, randomPassword)
} catch (emailError) {
console.error('Email sending failed:', emailError)
// Don't fail the request if email fails
}
return NextResponse.json({
success: true,
message: 'Partnership application submitted successfully! Check your email for login details.',
user: {
id: newUser.id,
email: newUser.email,
name: newUser.name,
role: newUser.role,
referralCode: newUser.referralCode
}
})
} catch (error) {
console.error('Partnership application error:', error)
return NextResponse.json(
{ success: false, message: 'Internal server error' },
{ status: 500 }
)
}
}
// Generate referral code
function generateReferralCode(firstName: string, lastName: string): string {
const initials = (firstName.charAt(0) + lastName.charAt(0)).toUpperCase()
const timestamp = Date.now().toString().slice(-6)
return `${initials}${timestamp}`
}
// Check tier availability
async function checkTierAvailability(requestedTier: string) {
// Define tier limits
const tierLimits = {
Diamond: 100, // Maximum 100 Diamond partners
Gold: 500, // Maximum 500 Gold partners
Silver: 1000 // Maximum 1000 Silver partners
}
// Count current partners for each tier
const tierCounts = await prisma.user.groupBy({
by: ['partnerTier'],
where: {
role: 'MEMBER',
partnerTier: {
in: ['Diamond', 'Gold', 'Silver']
}
},
_count: {
_all: true
}
})
// Create a map of current counts
const currentCounts: Record<string, number> = {}
tierCounts.forEach(tier => {
if (tier.partnerTier) {
currentCounts[tier.partnerTier] = tier._count._all
}
})
// Check availability for all tiers
const availability = {
Diamond: (currentCounts.Diamond || 0) < tierLimits.Diamond,
Gold: (currentCounts.Gold || 0) < tierLimits.Gold,
Silver: (currentCounts.Silver || 0) < tierLimits.Silver
}
// Get available tiers
const availableTiers = Object.entries(availability)
.filter(([_, available]) => available)
.map(([tier, _]) => tier)
return {
available: availability[requestedTier as keyof typeof availability] || false,
availableTiers,
currentCounts,
limits: tierLimits
}
}

View File

@@ -0,0 +1,188 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcryptjs'
import { EmailService } from '@/lib/email'
const emailService = new EmailService()
interface AddMemberData {
name: string
email: string
phone: string
address?: string
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json(
{ success: false, message: 'Unauthorized' },
{ status: 401 }
)
}
// Check if user is a partner (MEMBER role)
const partner = await prisma.user.findUnique({
where: { id: session.user.id },
include: {
referrals: true
}
})
if (!partner || partner.role !== 'MEMBER') {
return NextResponse.json(
{ success: false, message: 'Only partners can add members' },
{ status: 403 }
)
}
// Check if partner has minimum required members
if (partner.referrals.length < partner.minReferrals) {
// Partner is still building their minimum required members
console.log(`Partner needs ${partner.minReferrals - partner.referrals.length} more members to meet minimum requirement`)
}
const data: AddMemberData = await request.json()
// Validate required fields
if (!data.name || !data.email || !data.phone) {
return NextResponse.json(
{ success: false, message: 'Name, email, and phone are required' },
{ status: 400 }
)
}
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email: data.email }
})
if (existingUser) {
return NextResponse.json(
{ success: false, message: 'User with this email already exists' },
{ status: 400 }
)
}
// Generate random password
const randomPassword = Math.random().toString(36).slice(-8) + Math.random().toString(36).slice(-8)
const hashedPassword = await bcrypt.hash(randomPassword, 12)
// Create new member user
const newMember = await prisma.user.create({
data: {
name: data.name,
email: data.email,
phone: data.phone,
password: hashedPassword,
role: 'CUSTOMER',
referrerId: partner.id,
address: data.address,
isActive: true,
}
})
// Create wallet for the new member
await prisma.wallet.create({
data: {
userId: newMember.id,
balance: 0,
totalEarnings: 0,
totalWithdrawn: 0
}
})
try {
// Send notification to admin
await emailService.sendMemberAddedAdminNotification(partner, newMember)
// Send welcome email to new member
await emailService.sendMemberWelcomeEmail(data, partner, randomPassword)
} catch (emailError) {
console.error('Email sending failed:', emailError)
// Don't fail the request if email fails
}
return NextResponse.json({
success: true,
message: 'Member added successfully! They will receive login details via email.',
member: {
id: newMember.id,
name: newMember.name,
email: newMember.email,
phone: newMember.phone,
referralCode: newMember.referralCode
},
totalMembers: partner.referrals.length + 1,
minRequired: partner.minReferrals,
isMinimumMet: (partner.referrals.length + 1) >= partner.minReferrals
})
} catch (error) {
console.error('Add member error:', error)
return NextResponse.json(
{ success: false, message: 'Internal server error' },
{ status: 500 }
)
}
}
// Get partner's members
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json(
{ success: false, message: 'Unauthorized' },
{ status: 401 }
)
}
// Get partner with their referrals
const partner = await prisma.user.findUnique({
where: { id: session.user.id },
include: {
referrals: {
select: {
id: true,
name: true,
email: true,
phone: true,
joinedAt: true,
isActive: true,
referralCode: true
},
orderBy: {
joinedAt: 'desc'
}
}
}
})
if (!partner || partner.role !== 'MEMBER') {
return NextResponse.json(
{ success: false, message: 'Only partners can view members' },
{ status: 403 }
)
}
return NextResponse.json({
success: true,
members: partner.referrals,
totalMembers: partner.referrals.length,
minRequired: partner.minReferrals,
isMinimumMet: partner.referrals.length >= partner.minReferrals
})
} catch (error) {
console.error('Get members error:', error)
return NextResponse.json(
{ success: false, message: 'Internal server error' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,62 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
// Define tier limits
const tierLimits = {
Diamond: 500, // Maximum 500 Diamond partners
Gold: 1500, // Maximum 1500 Gold partners
Silver: 3000 // Maximum 3000 Silver partners
}
// Count current partners for each tier
const tierCounts = await prisma.user.groupBy({
by: ['partnerTier'],
where: {
role: 'MEMBER',
partnerTier: {
in: ['Diamond', 'Gold', 'Silver']
}
},
_count: {
_all: true
}
})
// Create a map of current counts
const currentCounts: Record<string, number> = {}
tierCounts.forEach(tier => {
if (tier.partnerTier) {
currentCounts[tier.partnerTier] = tier._count._all
}
})
// Calculate availability and remaining slots for each tier
const tiers = Object.entries(tierLimits).map(([tierName, limit]) => {
const currentCount = currentCounts[tierName] || 0
const remaining = limit - currentCount
return {
name: tierName,
limit,
currentCount,
remaining,
available: remaining > 0,
percentageFull: Math.round((currentCount / limit) * 100)
}
})
return NextResponse.json({
success: true,
tiers
})
} catch (error) {
console.error('Tier availability error:', error)
return NextResponse.json(
{ success: false, message: 'Failed to fetch tier availability' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,58 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { createOrder } from '@/lib/razorpay'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { orderId } = await request.json()
// Get order details
const order = await prisma.order.findUnique({
where: {
id: orderId,
userId: session.user.id,
status: 'PENDING'
},
include: {
orderItems: {
include: {
product: true
}
}
}
})
if (!order) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
// Create Razorpay order
const razorpayOrder = await createOrder(order.total, order.id)
// Update order with Razorpay order ID
await prisma.order.update({
where: { id: order.id },
data: { razorpayOrderId: razorpayOrder.id }
})
return NextResponse.json({
orderId: razorpayOrder.id,
amount: razorpayOrder.amount,
currency: razorpayOrder.currency,
key: process.env.RAZORPAY_KEY_ID,
})
} catch (error) {
console.error('Error creating payment order:', error)
return NextResponse.json(
{ error: 'Failed to create payment order' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,70 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { verifyPayment } from '@/lib/razorpay'
import { CommissionService } from '@/lib/commission'
import { emailService } from '@/lib/email'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const {
razorpay_order_id,
razorpay_payment_id,
razorpay_signature,
orderId
} = await request.json()
// Verify payment signature
const isValid = verifyPayment(
razorpay_order_id,
razorpay_payment_id,
razorpay_signature
)
if (!isValid) {
return NextResponse.json({ error: 'Invalid payment signature' }, { status: 400 })
}
// Update order status
const order = await prisma.order.update({
where: { id: orderId },
data: {
status: 'PAID',
razorpayPaymentId: razorpay_payment_id
},
include: {
orderItems: {
include: {
product: true
}
},
user: true
}
})
// Calculate and distribute commissions
await CommissionService.calculateCommissions(order.id)
// Send order confirmation email
await emailService.sendOrderConfirmation(
order.user.email,
order.user.name || 'Customer',
order
)
return NextResponse.json({ success: true, order })
} catch (error) {
console.error('Error verifying payment:', error)
return NextResponse.json(
{ error: 'Failed to verify payment' },
{ status: 500 }
)
}
}

86
app/api/payouts/route.ts Normal file
View File

@@ -0,0 +1,86 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { getSystemSettings } from '@/lib/settings'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const payouts = await prisma.payout.findMany({
where: { userId: session.user.id },
orderBy: { createdAt: 'desc' }
})
return NextResponse.json(payouts)
} catch (error) {
console.error('Error fetching payouts:', error)
return NextResponse.json(
{ error: 'Failed to fetch payouts' },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { amount, bankDetails } = await request.json()
const settings = await getSystemSettings()
if (amount < settings.minimumPayout) {
return NextResponse.json(
{ error: `Minimum payout amount is ₹${settings.minimumPayout}` },
{ status: 400 }
)
}
const wallet = await prisma.wallet.findUnique({
where: { userId: session.user.id }
})
if (!wallet || wallet.balance < amount) {
return NextResponse.json(
{ error: 'Insufficient balance' },
{ status: 400 }
)
}
const result = await prisma.$transaction(async (tx) => {
const payout = await tx.payout.create({
data: {
userId: session.user.id,
amount,
bankDetails,
status: 'PENDING'
}
})
await tx.wallet.update({
where: { userId: session.user.id },
data: {
balance: { decrement: amount }
}
})
return payout
})
return NextResponse.json(result)
} catch (error) {
console.error('Error creating payout request:', error)
return NextResponse.json(
{ error: 'Failed to create payout request' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,87 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { PricingService } from '@/lib/pricing'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { productId, quantity } = await request.json()
if (!productId || !quantity) {
return NextResponse.json(
{ error: 'Product ID and quantity are required' },
{ status: 400 }
)
}
// Get user role
const userRole = await PricingService.getUserRole(session.user.id)
// Calculate pricing
const pricing = await PricingService.getEffectivePrice(
productId,
quantity,
{
userId: session.user.id,
userRole: userRole || undefined,
quantity
}
)
// Get wholesale discount info
const wholesaleInfo = PricingService.getWholesaleDiscountInfo()
return NextResponse.json({
success: true,
pricing,
wholesaleInfo,
userRole,
isWholesaler: userRole === 'WHOLESALER'
})
} catch (error) {
console.error('Pricing calculation error:', error)
return NextResponse.json(
{
success: false,
error: 'Failed to calculate pricing'
},
{ status: 500 }
)
}
}
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Check if user is wholesaler
const isWholesaler = await PricingService.isWholesaler(session.user.id)
const wholesaleInfo = PricingService.getWholesaleDiscountInfo()
return NextResponse.json({
success: true,
isWholesaler,
wholesaleInfo
})
} catch (error) {
console.error('Wholesale check error:', error)
return NextResponse.json(
{
success: false,
error: 'Failed to check wholesale status'
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,37 @@
import { NextRequest, NextResponse } from "next/server";
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest, props: { params: Promise<{ slug: string }> }) {
const params = await props.params;
try {
const product = await prisma.product.findUnique({
where: {
slug: params.slug,
isActive: true
},
include: {
category: {
select: {
id: true,
name: true
}
}
},
})
if (!product) {
return NextResponse.json(
{ error: 'Product not found' },
{ status: 404 }
)
}
return NextResponse.json(product)
} catch (error) {
console.error('Error fetching product by slug:', error)
return NextResponse.json(
{ error: 'Failed to fetch product' },
{ status: 500 }
)
}
}

214
app/api/products/route.ts Normal file
View File

@@ -0,0 +1,214 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import { productSchema } from '@/lib/validations/product'
import { DatabaseOptimizer } from '@/lib/database-optimizer'
export async function GET(request: NextRequest) {
const startTime = Date.now()
try {
const { searchParams } = new URL(request.url)
const category = searchParams.get('category')
const search = searchParams.get('search')
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '12')
const isAdmin = searchParams.get('admin') === 'true'
const skip = (page - 1) * limit
// Create cache key for this specific query
const cacheKey = `products:${category || 'all'}:${search || 'none'}:${page}:${limit}:${isAdmin}`
// Try to get from cache first
const cached = await DatabaseOptimizer.getCachedData(cacheKey)
if (cached && !isAdmin) { // Don't cache admin requests
return NextResponse.json({
...cached,
_performance: {
responseTime: Date.now() - startTime,
cached: true
}
})
}
const where: any = {}
// Only filter by isActive if not admin request
if (!isAdmin) {
where.isActive = true
// Add stock filter for better SEO (only show available products)
where.stock = { gt: 0 }
}
if (category) {
where.categoryId = category
}
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ description: { contains: search, mode: 'insensitive' } },
{
category: {
name: { contains: search, mode: 'insensitive' }
}
},
]
}
// Optimize queries with parallel execution and selective field inclusion
const [products, total, categoryStats] = await Promise.all([
// Optimized products query with caching
DatabaseOptimizer.executeOptimizedQuery(
`products_list_${JSON.stringify(where)}_${skip}_${limit}`,
() => prisma.product.findMany({
where,
include: {
category: {
select: {
id: true,
name: true,
}
},
reviews: {
select: {
rating: true,
createdAt: true,
},
take: 5, // Limit reviews for performance
orderBy: {
createdAt: 'desc'
}
},
_count: {
select: {
reviews: true,
orderItems: true, // For popularity metrics
}
}
},
skip,
take: limit,
orderBy: [
{ isActive: 'desc' }, // Active products first
{ stock: 'desc' }, // In-stock products next
{ createdAt: 'desc' }, // Then by creation date
],
}),
300 // Cache for 5 minutes
),
// Optimized count query
DatabaseOptimizer.executeOptimizedQuery(
`products_count_${JSON.stringify(where)}`,
() => prisma.product.count({ where }),
600 // Cache for 10 minutes
),
// Optimized category stats
DatabaseOptimizer.executeOptimizedQuery(
`category_stats_${category || 'all'}`,
() => prisma.product.groupBy({
by: ['categoryId'],
where: { isActive: true, stock: { gt: 0 } },
_count: {
categoryId: true,
},
orderBy: {
_count: {
categoryId: 'desc'
}
}
}),
900 // Cache for 15 minutes
)
])
// Calculate advanced metrics for SEO and performance
const productsWithMetrics = products.map(product => ({
...product,
averageRating: product.reviews.length > 0
? Math.round((product.reviews.reduce((acc, review) => acc + review.rating, 0) / product.reviews.length) * 10) / 10
: null,
reviewCount: product._count.reviews,
popularityScore: product._count.orderItems,
hasRecentReviews: product.reviews.some(review =>
new Date(review.createdAt) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
),
isPopular: product._count.orderItems > 10,
inStock: product.stock > 0,
lowStock: product.stock > 0 && product.stock <= 10,
// Remove internal data from response
reviews: undefined,
_count: undefined,
}))
const responseData = {
products: productsWithMetrics,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit),
},
seo: {
totalProducts: total,
categoryDistribution: categoryStats,
hasProducts: total > 0
}
}
// Cache the response for non-admin requests
if (!isAdmin) {
await DatabaseOptimizer.setCachedData(cacheKey, responseData, 300)
}
return NextResponse.json({
...responseData,
_performance: {
responseTime: Date.now() - startTime,
cached: false
}
})
} catch (error) {
console.error('Products API error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user || session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const validatedData = productSchema.parse(body)
// Generate SKU and slug if not provided
const sku = validatedData.sku || `SKU-${Date.now()}`
const slug = validatedData.slug || validatedData.name.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '')
const product = await prisma.product.create({
data: {
...validatedData,
sku,
slug
},
include: {
category: true,
},
})
// Invalidate product caches after creation
DatabaseOptimizer.invalidateCache('products')
DatabaseOptimizer.invalidateCache('category_stats')
return NextResponse.json(product, { status: 201 })
} catch (error) {
console.error('Create product error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}

View File

@@ -0,0 +1,96 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user) {
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
}
const { id: reviewId } = await params
// Check if review exists
const review = await prisma.review.findUnique({
where: { id: reviewId }
})
if (!review) {
return NextResponse.json({ error: 'Review not found' }, { status: 404 })
}
// Check if user already voted
const existingVote = await prisma.reviewHelpfulVote.findUnique({
where: {
reviewId_userId: {
reviewId,
userId: session.user.id
}
}
})
if (existingVote) {
// Remove the vote (toggle)
await prisma.reviewHelpfulVote.delete({
where: { id: existingVote.id }
})
// Update helpful count
const updatedReview = await prisma.review.update({
where: { id: reviewId },
data: {
helpfulVotes: {
decrement: 1
}
},
select: {
helpfulVotes: true
}
})
return NextResponse.json({
message: 'Helpful vote removed',
hasVoted: false,
totalVotes: updatedReview.helpfulVotes
})
} else {
// Add the vote
await prisma.reviewHelpfulVote.create({
data: {
reviewId,
userId: session.user.id
}
})
// Update helpful count
const updatedReview = await prisma.review.update({
where: { id: reviewId },
data: {
helpfulVotes: {
increment: 1
}
},
select: {
helpfulVotes: true
}
})
return NextResponse.json({
message: 'Review marked as helpful',
hasVoted: true,
totalVotes: updatedReview.helpfulVotes
})
}
} catch (error) {
console.error('Error voting on review:', error)
return NextResponse.json(
{ error: 'Failed to vote on review' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,85 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user) {
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
}
const { id: reviewId } = await params
const body = await request.json()
const { reason, comment } = body
// Validate reason
const validReasons = ['SPAM', 'INAPPROPRIATE', 'FAKE', 'OFFENSIVE', 'OTHER']
if (!reason || !validReasons.includes(reason)) {
return NextResponse.json(
{ error: 'Valid reason is required' },
{ status: 400 }
)
}
// Check if review exists
const review = await prisma.review.findUnique({
where: { id: reviewId }
})
if (!review) {
return NextResponse.json({ error: 'Review not found' }, { status: 404 })
}
// Check if user already reported this review
const existingReport = await prisma.reviewReport.findUnique({
where: {
reviewId_userId: {
reviewId,
userId: session.user.id
}
}
})
if (existingReport) {
return NextResponse.json(
{ error: 'You have already reported this review' },
{ status: 400 }
)
}
// Create the report
await prisma.reviewReport.create({
data: {
reviewId,
userId: session.user.id,
reason,
comment
}
})
// Update report count
await prisma.review.update({
where: { id: reviewId },
data: {
reportCount: {
increment: 1
}
}
})
return NextResponse.json({
message: 'Review reported successfully. Thank you for helping us maintain quality.'
})
} catch (error) {
console.error('Error reporting review:', error)
return NextResponse.json(
{ error: 'Failed to report review' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,164 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const review = await prisma.review.findUnique({
where: { id },
include: {
user: {
select: {
id: true,
name: true,
image: true,
}
},
product: {
select: {
id: true,
name: true,
}
},
_count: {
select: {
helpfulVotedBy: true,
reportedBy: true,
}
}
}
})
if (!review) {
return NextResponse.json({ error: 'Review not found' }, { status: 404 })
}
return NextResponse.json(review)
} catch (error) {
console.error('Error fetching review:', error)
return NextResponse.json(
{ error: 'Failed to fetch review' },
{ status: 500 }
)
}
}
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user) {
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
}
const { id } = await params
const body = await request.json()
const { rating, title, comment, images } = body
// Find the review and check ownership
const existingReview = await prisma.review.findUnique({
where: { id }
})
if (!existingReview) {
return NextResponse.json({ error: 'Review not found' }, { status: 404 })
}
if (existingReview.userId !== session.user.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 403 })
}
// Validate rating if provided
if (rating && (rating < 1 || rating > 5)) {
return NextResponse.json(
{ error: 'Rating must be between 1 and 5' },
{ status: 400 }
)
}
const updatedReview = await prisma.review.update({
where: { id },
data: {
...(rating && { rating }),
...(title !== undefined && { title }),
...(comment !== undefined && { comment }),
...(images && { images }),
isApproved: false, // Reset approval status when edited
},
include: {
user: {
select: {
id: true,
name: true,
image: true,
}
},
product: {
select: {
id: true,
name: true,
}
}
}
})
return NextResponse.json({
review: updatedReview,
message: 'Review updated successfully. It will be visible after admin approval.'
})
} catch (error) {
console.error('Error updating review:', error)
return NextResponse.json(
{ error: 'Failed to update review' },
{ status: 500 }
)
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user) {
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
}
const { id } = await params
// Find the review and check ownership or admin access
const existingReview = await prisma.review.findUnique({
where: { id }
})
if (!existingReview) {
return NextResponse.json({ error: 'Review not found' }, { status: 404 })
}
if (existingReview.userId !== session.user.id && session.user.role !== 'ADMIN') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 403 })
}
await prisma.review.delete({
where: { id }
})
return NextResponse.json({ message: 'Review deleted successfully' })
} catch (error) {
console.error('Error deleting review:', error)
return NextResponse.json(
{ error: 'Failed to delete review' },
{ status: 500 }
)
}
}

130
app/api/reviews/route.ts Normal file
View File

@@ -0,0 +1,130 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
// GET /api/reviews - Get reviews with filters
export async function GET(request: NextRequest) {
try {
const session = await auth()
const { searchParams } = new URL(request.url)
const productId = searchParams.get('productId')
const userId = searchParams.get('userId')
const rating = searchParams.get('rating')
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '10')
const skip = (page - 1) * limit
const withStats = searchParams.get('withStats') === 'true'
const where: any = {
isApproved: true, // Only show approved reviews publicly
}
if (productId) where.productId = productId
if (userId) where.userId = userId
if (rating) where.rating = parseInt(rating)
const [reviews, total, aggregateStats] = await Promise.all([
prisma.review.findMany({
where,
include: {
user: {
select: {
id: true,
name: true,
image: true,
// Add user credibility for social proof
_count: {
select: {
reviews: true, // Total reviews by this user
}
}
}
},
product: {
select: {
id: true,
name: true,
}
},
helpfulVotedBy: session?.user?.id ? {
where: {
userId: session.user.id
}
} : false,
_count: {
select: {
helpfulVotedBy: true,
reportedBy: true,
}
}
},
orderBy: [
{ helpfulVotedBy: { _count: 'desc' } }, // Most helpful first
{ rating: 'desc' }, // Higher ratings next
{ createdAt: 'desc' } // Then by recency
],
skip,
take: limit,
}),
prisma.review.count({ where }),
// Get aggregate stats for social proof
withStats ? prisma.review.groupBy({
by: ['rating'],
where: productId ? { productId, isApproved: true } : { isApproved: true },
_count: {
rating: true,
},
}) : null,
])
// Calculate enhanced social proof metrics
const enhancedReviews = reviews.map(review => ({
...review,
// Add social proof indicators
isVerifiedReviewer: review.user._count.reviews >= 3, // Users with 3+ reviews are "verified"
helpfulnessScore: review._count.helpfulVotedBy,
// Add time-based freshness
isFresh: new Date(review.createdAt) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), // Last 30 days
}))
// Calculate rating distribution for social proof
let ratingDistribution = null
if (aggregateStats) {
const totalReviews = aggregateStats.reduce((sum, stat) => sum + stat._count.rating, 0)
ratingDistribution = {
1: 0, 2: 0, 3: 0, 4: 0, 5: 0,
...aggregateStats.reduce((acc, stat) => ({
...acc,
[stat.rating]: Math.round((stat._count.rating / totalReviews) * 100)
}), {})
}
}
return NextResponse.json({
reviews: enhancedReviews,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
// Enhanced social proof data
socialProof: {
totalReviews: total,
averageRating: enhancedReviews.length > 0
? enhancedReviews.reduce((sum, review) => sum + review.rating, 0) / enhancedReviews.length
: 0,
ratingDistribution,
verifiedReviewersCount: enhancedReviews.filter(r => r.isVerifiedReviewer).length,
recentReviewsCount: enhancedReviews.filter(r => r.isFresh).length,
}
})
} catch (error) {
console.error('Error fetching reviews:', error)
return NextResponse.json(
{ error: 'Failed to fetch reviews' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,240 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { DatabaseOptimizer } from '@/lib/database-optimizer'
// GET /api/social-proof - Get aggregated social proof data
export async function GET(request: NextRequest) {
const startTime = Date.now()
try {
const { searchParams } = new URL(request.url)
const productId = searchParams.get('productId')
// Create cache key
const cacheKey = `social_proof_${productId || 'global'}`
// Try to get from cache first
const cached = await DatabaseOptimizer.getCachedData(cacheKey)
if (cached) {
return NextResponse.json({
...cached,
_performance: {
responseTime: Date.now() - startTime,
cached: true
}
})
}
// Get comprehensive social proof metrics with optimization
const [
totalUsers,
totalReviews,
recentReviews,
topRatedProducts,
verifiedReviewers,
recentOrders,
averageRatings,
] = await Promise.all([
// Optimized total active users
DatabaseOptimizer.executeOptimizedQuery(
'total_active_users',
() => prisma.user.count({
where: {
isActive: true,
role: 'CUSTOMER'
}
}),
1800 // Cache for 30 minutes
),
// Optimized total approved reviews
DatabaseOptimizer.executeOptimizedQuery(
`total_reviews_${productId || 'all'}`,
() => prisma.review.count({
where: {
isApproved: true,
...(productId && { productId })
}
}),
600 // Cache for 10 minutes
),
// Optimized recent reviews (last 7 days)
DatabaseOptimizer.executeOptimizedQuery(
`recent_reviews_${productId || 'all'}`,
() => prisma.review.count({
where: {
isApproved: true,
createdAt: {
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
},
...(productId && { productId })
}
}),
300 // Cache for 5 minutes
),
// Optimized top rated products for social proof
DatabaseOptimizer.executeOptimizedQuery(
'top_rated_products',
() => prisma.product.findMany({
where: {
isActive: true,
stock: { gt: 0 },
reviews: {
some: {
isApproved: true,
rating: { gte: 4 }
}
}
},
include: {
reviews: {
where: { isApproved: true },
select: { rating: true }
},
_count: {
select: {
reviews: true,
orderItems: true
}
}
},
take: 5,
orderBy: {
reviews: {
_count: 'desc'
}
}
}),
900 // Cache for 15 minutes
),
// Optimized verified reviewers - get users with multiple reviews
DatabaseOptimizer.executeOptimizedQuery(
'verified_reviewers_count',
async () => {
const users = await prisma.user.findMany({
where: {
isActive: true,
reviews: {
some: {
isApproved: true
}
}
},
include: {
_count: {
select: {
reviews: {
where: {
isApproved: true
}
}
}
}
}
})
return users.filter(user => user._count.reviews >= 3).length
},
1800 // Cache for 30 minutes
),
// Optimized recent orders for activity proof
DatabaseOptimizer.executeOptimizedQuery(
'recent_orders_count',
() => prisma.order.count({
where: {
status: { in: ['PAID', 'SHIPPED', 'DELIVERED'] },
createdAt: {
gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // Last 24 hours
}
}
}),
300 // Cache for 5 minutes
),
// Optimized average ratings by category
DatabaseOptimizer.executeOptimizedQuery(
`average_ratings_${productId || 'all'}`,
() => prisma.review.groupBy({
by: ['productId'],
where: {
isApproved: true,
...(productId && { productId })
},
_avg: {
rating: true
},
_count: {
rating: true
},
having: {
rating: {
_count: {
gte: 3 // At least 3 reviews
}
}
}
}),
600 // Cache for 10 minutes
)
])
// Calculate enhanced social proof metrics
const topRatedWithMetrics = topRatedProducts.map(product => ({
id: product.id,
name: product.name,
averageRating: product.reviews.length > 0
? Math.round((product.reviews.reduce((sum, review) => sum + review.rating, 0) / product.reviews.length) * 10) / 10
: 0,
totalReviews: product._count.reviews,
totalOrders: product._count.orderItems,
popularityScore: product._count.orderItems + (product._count.reviews * 2)
})).filter(product => product.averageRating >= 4.0)
const overallAverageRating = averageRatings.length > 0
? Math.round((averageRatings.reduce((sum, item) => sum + (item._avg.rating || 0), 0) / averageRatings.length) * 10) / 10
: 0
const responseData = {
socialProof: {
totalCustomers: totalUsers,
totalReviews,
recentReviews,
verifiedReviewers,
recentOrders,
overallAverageRating,
topRatedProducts: topRatedWithMetrics,
trustIndicators: {
hasRecentActivity: recentOrders > 0,
hasRecentReviews: recentReviews > 0,
hasVerifiedReviewers: verifiedReviewers > 0,
highRatingProducts: topRatedWithMetrics.length > 0,
},
metrics: {
reviewEngagement: totalReviews > 0 ? Math.round((recentReviews / totalReviews) * 100) : 0,
customerRetention: totalUsers > 0 ? Math.round((verifiedReviewers / totalUsers) * 100) : 0,
productSatisfaction: Math.round(overallAverageRating * 20), // Convert to percentage
}
}
}
// Cache the response
await DatabaseOptimizer.setCachedData(cacheKey, responseData, 600)
return NextResponse.json({
...responseData,
_performance: {
responseTime: Date.now() - startTime,
cached: false
}
})
} catch (error) {
console.error('Error fetching social proof data:', error)
return NextResponse.json(
{ error: 'Failed to fetch social proof data' },
{ status: 500 }
)
}
}

85
app/api/team/route.ts Normal file
View File

@@ -0,0 +1,85 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Get direct referrals
const directReferrals = await prisma.user.findMany({
where: { referrerId: session.user.id },
select: {
id: true,
name: true,
email: true,
role: true,
joinedAt: true,
referrals: {
select: {
id: true,
name: true,
email: true,
role: true,
joinedAt: true,
},
},
},
orderBy: { joinedAt: 'desc' },
})
// Get team statistics
const teamStats = await getTeamStats(session.user.id)
return NextResponse.json({
directReferrals,
teamStats,
})
} catch (error) {
console.error('Team API error:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}
async function getTeamStats(userId: string) {
const levels = []
for (let level = 1; level <= 5; level++) {
const count = await getTeamCountAtLevel(userId, level)
levels.push({ level, count })
}
const totalTeamSize = levels.reduce((sum, level) => sum + level.count, 0)
return {
totalTeamSize,
levels,
}
}
async function getTeamCountAtLevel(userId: string, targetLevel: number, currentLevel = 0, visited = new Set<string>()): Promise<number> {
if (currentLevel === targetLevel || visited.has(userId)) {
return currentLevel === targetLevel ? 1 : 0
}
visited.add(userId)
const directReferrals = await prisma.user.findMany({
where: { referrerId: userId },
select: { id: true },
})
if (currentLevel === targetLevel - 1) {
return directReferrals.length
}
let count = 0
for (const referral of directReferrals) {
count += await getTeamCountAtLevel(referral.id, targetLevel, currentLevel + 1, visited)
}
return count
}

View File

@@ -0,0 +1,194 @@
import { NextRequest, NextResponse } from 'next/server'
import blobStorage from '@/lib/blob-storage'
export async function POST(request: NextRequest) {
try {
const formData = await request.formData()
const files = formData.getAll('files') as File[]
const folder = formData.get('folder') as string || 'general'
const type = formData.get('type') as string || 'general'
if (!files || files.length === 0) {
return NextResponse.json(
{ success: false, message: 'No files provided' },
{ status: 400 }
)
}
// Validate files
for (const file of files) {
if (!file || file.size === 0) {
return NextResponse.json(
{ success: false, message: 'Invalid file provided' },
{ status: 400 }
)
}
}
let uploadResults: any[] = []
try {
// Handle different upload types
switch (type) {
case 'product-images': {
const productName = formData.get('productName') as string
if (!productName) {
return NextResponse.json(
{ success: false, message: 'Product name is required for product images' },
{ status: 400 }
)
}
const urls = await blobStorage.uploadProductImages(files, productName)
uploadResults = urls.map(url => ({ url, type: 'product-image' }))
break
}
case 'category-image': {
if (files.length > 1) {
return NextResponse.json(
{ success: false, message: 'Only one category image allowed' },
{ status: 400 }
)
}
const categoryName = formData.get('categoryName') as string
if (!categoryName) {
return NextResponse.json(
{ success: false, message: 'Category name is required' },
{ status: 400 }
)
}
const url = await blobStorage.uploadCategoryImage(files[0], categoryName)
uploadResults = [{ url, type: 'category-image' }]
break
}
case 'user-avatar': {
if (files.length > 1) {
return NextResponse.json(
{ success: false, message: 'Only one avatar image allowed' },
{ status: 400 }
)
}
const userId = formData.get('userId') as string
if (!userId) {
return NextResponse.json(
{ success: false, message: 'User ID is required' },
{ status: 400 }
)
}
const url = await blobStorage.uploadUserAvatar(files[0], userId)
uploadResults = [{ url, type: 'user-avatar' }]
break
}
default: {
// General file upload
const results = await blobStorage.uploadMultipleFiles(files, { folder })
uploadResults = results.map(result => ({
url: result.url,
downloadUrl: result.downloadUrl,
pathname: result.pathname,
size: result.size,
uploadedAt: result.uploadedAt,
type: 'general'
}))
break
}
}
return NextResponse.json({
success: true,
message: 'Files uploaded successfully',
data: uploadResults
})
} catch (uploadError) {
console.error('Upload error:', uploadError)
return NextResponse.json(
{
success: false,
message: uploadError instanceof Error ? uploadError.message : 'Failed to upload files'
},
{ status: 500 }
)
}
} catch (error) {
console.error('File upload error:', error)
return NextResponse.json(
{
success: false,
message: 'Internal server error'
},
{ status: 500 }
)
}
}
export async function DELETE(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const url = searchParams.get('url')
const urls = searchParams.getAll('urls')
if (!url && (!urls || urls.length === 0)) {
return NextResponse.json(
{ success: false, message: 'No URL(s) provided for deletion' },
{ status: 400 }
)
}
if (url) {
// Delete single file
await blobStorage.deleteFile(url)
} else {
// Delete multiple files
await blobStorage.deleteMultipleFiles(urls)
}
return NextResponse.json({
success: true,
message: 'File(s) deleted successfully'
})
} catch (error) {
console.error('File deletion error:', error)
return NextResponse.json(
{
success: false,
message: error instanceof Error ? error.message : 'Failed to delete file(s)'
},
{ status: 500 }
)
}
}
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const prefix = searchParams.get('prefix')
const limit = parseInt(searchParams.get('limit') || '100')
const cursor = searchParams.get('cursor')
const result = await blobStorage.listFiles({
prefix: prefix || undefined,
limit,
cursor: cursor || undefined
})
return NextResponse.json({
success: true,
data: result
})
} catch (error) {
console.error('File listing error:', error)
return NextResponse.json(
{
success: false,
message: 'Failed to list files'
},
{ status: 500 }
)
}
}

56
app/api/upload/route.ts Normal file
View File

@@ -0,0 +1,56 @@
import { NextRequest, NextResponse } from 'next/server'
import { writeFile, mkdir } from 'fs/promises'
import { join } from 'path'
import { existsSync } from 'fs'
export async function POST(request: NextRequest) {
try {
const data = await request.formData()
const files: File[] = data.getAll('files') as File[]
if (!files || files.length === 0) {
return NextResponse.json({ error: 'No files uploaded' }, { status: 400 })
}
const uploadedUrls: string[] = []
for (const file of files) {
if (!file.type.startsWith('image/')) {
return NextResponse.json({ error: 'Only image files are allowed' }, { status: 400 })
}
const bytes = await file.arrayBuffer()
const buffer = new Uint8Array(bytes)
// Create products directory if it doesn't exist
const productsDir = join(process.cwd(), 'public', 'products')
if (!existsSync(productsDir)) {
await mkdir(productsDir, { recursive: true })
}
// Generate unique filename
const timestamp = Date.now()
const filename = `${timestamp}-${file.name.replace(/[^a-zA-Z0-9.-]/g, '_')}`
const filepath = join(productsDir, filename)
// Write file to public/products directory
await writeFile(filepath, buffer)
// Add to uploaded URLs (relative path for use in app)
uploadedUrls.push(`/products/${filename}`)
}
return NextResponse.json({
success: true,
urls: uploadedUrls,
message: `${uploadedUrls.length} file(s) uploaded successfully`
})
} catch (error) {
console.error('Upload error:', error)
return NextResponse.json(
{ error: 'Failed to upload files' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,157 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
)
}
const { provider, providerAccountId, access_token, refresh_token, expires_at } = await request.json()
if (!provider || !providerAccountId) {
return NextResponse.json(
{ error: 'Missing required fields' },
{ status: 400 }
)
}
// Check if this account is already linked to another user
const existingAccount = await prisma.account.findFirst({
where: {
provider,
providerAccountId,
},
})
if (existingAccount) {
return NextResponse.json(
{ error: 'This account is already linked to another user' },
{ status: 400 }
)
}
// Link the account to the current user
const account = await prisma.account.create({
data: {
userId: session.user.id,
type: 'oauth',
provider,
providerAccountId,
access_token,
refresh_token,
expires_at,
token_type: 'Bearer',
scope: 'email profile openid',
},
})
return NextResponse.json({
success: true,
message: 'Account linked successfully',
account: {
id: account.id,
provider: account.provider,
},
})
} catch (error) {
console.error('Error linking account:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
export async function DELETE(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
)
}
const { searchParams } = new URL(request.url)
const provider = searchParams.get('provider')
if (!provider) {
return NextResponse.json(
{ error: 'Provider is required' },
{ status: 400 }
)
}
// Remove the linked account
const deletedAccount = await prisma.account.deleteMany({
where: {
userId: session.user.id,
provider,
},
})
if (deletedAccount.count === 0) {
return NextResponse.json(
{ error: 'Account not found' },
{ status: 404 }
)
}
return NextResponse.json({
success: true,
message: 'Account unlinked successfully',
})
} catch (error) {
console.error('Error unlinking account:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
export async function GET(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
)
}
// Get all linked accounts for the current user
const accounts = await prisma.account.findMany({
where: {
userId: session.user.id,
},
select: {
id: true,
provider: true,
type: true,
},
})
return NextResponse.json({
accounts,
})
} catch (error) {
console.error('Error fetching linked accounts:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id: addressId } = await params
// Check if address belongs to user
const existingAddress = await prisma.address.findFirst({
where: {
id: addressId,
userId: session.user.id
}
})
if (!existingAddress) {
return NextResponse.json({ error: 'Address not found' }, { status: 404 })
}
// Unset all other default addresses for this user
await prisma.address.updateMany({
where: {
userId: session.user.id,
isDefault: true
},
data: { isDefault: false }
})
// Set this address as default
const address = await prisma.address.update({
where: { id: addressId },
data: { isDefault: true }
})
return NextResponse.json({ address })
} catch (error) {
console.error('Error setting default address:', error)
return NextResponse.json(
{ error: 'Failed to set default address' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,122 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id: addressId } = await params
const data = await request.json()
const {
firstName,
lastName,
company,
address1,
address2,
city,
state,
zipCode,
country,
phone,
isDefault,
type
} = data
// Check if address belongs to user
const existingAddress = await prisma.address.findFirst({
where: {
id: addressId,
userId: session.user.id
}
})
if (!existingAddress) {
return NextResponse.json({ error: 'Address not found' }, { status: 404 })
}
// If setting as default, unset other default addresses
if (isDefault) {
await prisma.address.updateMany({
where: {
userId: session.user.id,
isDefault: true,
id: { not: addressId }
},
data: { isDefault: false }
})
}
const address = await prisma.address.update({
where: { id: addressId },
data: {
firstName,
lastName,
company,
address1,
address2,
city,
state,
zipCode,
country: country || 'India',
phone,
isDefault,
type
}
})
return NextResponse.json({ address })
} catch (error) {
console.error('Error updating address:', error)
return NextResponse.json(
{ error: 'Failed to update address' },
{ status: 500 }
)
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { id: addressId } = await params
// Check if address belongs to user
const existingAddress = await prisma.address.findFirst({
where: {
id: addressId,
userId: session.user.id
}
})
if (!existingAddress) {
return NextResponse.json({ error: 'Address not found' }, { status: 404 })
}
await prisma.address.delete({
where: { id: addressId }
})
return NextResponse.json({ message: 'Address deleted successfully' })
} catch (error) {
console.error('Error deleting address:', error)
return NextResponse.json(
{ error: 'Failed to delete address' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,94 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const addresses = await prisma.address.findMany({
where: { userId: session.user.id },
orderBy: [
{ isDefault: 'desc' },
{ createdAt: 'desc' }
]
})
return NextResponse.json({ addresses })
} catch (error) {
console.error('Error fetching addresses:', error)
return NextResponse.json(
{ error: 'Failed to fetch addresses' },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const data = await request.json()
const {
firstName,
lastName,
company,
address1,
address2,
city,
state,
zipCode,
country,
phone,
isDefault,
type
} = data
// If setting as default, unset other default addresses
if (isDefault) {
await prisma.address.updateMany({
where: {
userId: session.user.id,
isDefault: true
},
data: { isDefault: false }
})
}
const address = await prisma.address.create({
data: {
userId: session.user.id,
firstName,
lastName,
company,
address1,
address2,
city,
state,
zipCode,
country: country || 'India',
phone,
isDefault,
type
}
})
return NextResponse.json({ address })
} catch (error) {
console.error('Error creating address:', error)
return NextResponse.json(
{ error: 'Failed to create address' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,58 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcryptjs'
export async function POST(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { currentPassword, newPassword } = await request.json()
if (!currentPassword || !newPassword) {
return NextResponse.json({ error: 'All fields are required' }, { status: 400 })
}
if (newPassword.length < 6) {
return NextResponse.json({ error: 'Password must be at least 6 characters long' }, { status: 400 })
}
// Get current user with password
const user = await prisma.user.findUnique({
where: { id: session.user.id },
select: { password: true }
})
if (!user || !user.password) {
return NextResponse.json({ error: 'User not found or password not set' }, { status: 404 })
}
// Verify current password
const isCurrentPasswordValid = await bcrypt.compare(currentPassword, user.password)
if (!isCurrentPasswordValid) {
return NextResponse.json({ error: 'Current password is incorrect' }, { status: 400 })
}
// Hash new password
const hashedNewPassword = await bcrypt.hash(newPassword, 12)
// Update password
await prisma.user.update({
where: { id: session.user.id },
data: { password: hashedNewPassword }
})
return NextResponse.json({ message: 'Password updated successfully' })
} catch (error) {
console.error('Error changing password:', error)
return NextResponse.json(
{ error: 'Failed to change password' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,83 @@
import { NextRequest, NextResponse } from 'next/server'
import { auth } from '@/auth'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const user = await prisma.user.findUnique({
where: { id: session.user.id },
select: {
id: true,
name: true,
email: true,
phone: true,
address: true,
image: true,
role: true,
referralCode: true,
isActive: true,
joinedAt: true
}
})
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
return NextResponse.json(user)
} catch (error) {
console.error('Error fetching user profile:', error)
return NextResponse.json(
{ error: 'Failed to fetch user profile' },
{ status: 500 }
)
}
}
export async function PUT(request: NextRequest) {
try {
const session = await auth()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const data = await request.json()
const { name, phone, address } = data
const updatedUser = await prisma.user.update({
where: { id: session.user.id },
data: {
name: name || undefined,
phone: phone || undefined,
address: address || undefined
},
select: {
id: true,
name: true,
email: true,
phone: true,
address: true,
role: true,
referralCode: true,
isActive: true,
joinedAt: true
}
})
return NextResponse.json(updatedUser)
} catch (error) {
console.error('Error updating user profile:', error)
return NextResponse.json(
{ error: 'Failed to update user profile' },
{ status: 500 }
)
}
}

28
app/api/version/route.ts Normal file
View File

@@ -0,0 +1,28 @@
import { NextResponse } from 'next/server'
export async function GET() {
try {
// Get version info from environment or package.json
const versionInfo = {
version: process.env.npm_package_version || '1.0.0',
buildTime: process.env.BUILD_TIME || new Date().toISOString(),
environment: process.env.NODE_ENV || 'development',
commitHash: process.env.VERCEL_GIT_COMMIT_SHA || process.env.GIT_COMMIT_SHA || 'unknown',
lastUpdated: new Date().toISOString()
}
return NextResponse.json(versionInfo, {
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
}
})
} catch (error) {
console.error('Version API error:', error)
return NextResponse.json(
{ error: 'Failed to get version info' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,152 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcryptjs'
import { EmailService } from '@/lib/email'
interface WholesalerRegistrationData {
// Business Information
businessName: string
businessType: string
gstNumber: string
panNumber: string
// Personal Information
firstName: string
lastName: string
email: string
phone: string
// Address Information
address: string
city: string
state: string
zipCode: string
// Business Details
experience: string
expectedOrderVolume: string
productCategories: string
businessDescription: string
}
export async function POST(request: NextRequest) {
try {
const data: WholesalerRegistrationData = await request.json()
// Validate required fields
const requiredFields = ['businessName', 'firstName', 'lastName', 'email', 'phone', 'address', 'city', 'state', 'zipCode']
for (const field of requiredFields) {
if (!data[field as keyof WholesalerRegistrationData]) {
return NextResponse.json(
{ success: false, message: `${field} is required` },
{ status: 400 }
)
}
}
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email: data.email }
})
if (existingUser) {
return NextResponse.json(
{ success: false, message: 'Email already registered' },
{ status: 400 }
)
}
// Generate a temporary password
const tempPassword = Math.random().toString(36).slice(-8)
const hashedPassword = await bcrypt.hash(tempPassword, 10)
// Create user with WHOLESALER role
const user = await prisma.user.create({
data: {
name: `${data.firstName} ${data.lastName}`,
email: data.email,
password: hashedPassword,
role: 'WHOLESALER',
phone: data.phone,
address: data.address,
isActive: true,
joinedAt: new Date()
}
})
// Create address record
await prisma.address.create({
data: {
userId: user.id,
firstName: data.firstName,
lastName: data.lastName,
company: data.businessName,
address1: data.address,
city: data.city,
state: data.state,
zipCode: data.zipCode,
country: 'India',
phone: data.phone,
isDefault: true,
type: 'WORK'
}
})
// Create form response for admin tracking
await prisma.formResponse.create({
data: {
formId: 'wholesaler_registration',
data: {
...data,
userId: user.id,
registrationDate: new Date().toISOString()
},
status: 'new',
userId: user.id
}
})
// Send email notifications
const emailService = new EmailService()
// Send welcome email to wholesaler
await emailService.sendWholesalerWelcomeEmail({
to: user.email,
name: user.name || 'Wholesaler',
email: user.email,
password: tempPassword,
businessName: data.businessName
})
// Send admin notification
await emailService.sendWholesalerAdminNotification({
wholesalerName: user.name || 'Unknown',
wholesalerEmail: user.email,
businessName: data.businessName,
businessType: data.businessType,
phone: data.phone,
expectedVolume: data.expectedOrderVolume,
registrationDate: new Date().toLocaleDateString()
})
return NextResponse.json({
success: true,
message: 'Wholesaler registration successful',
loginCredentials: {
email: user.email,
password: tempPassword
}
})
} catch (error) {
console.error('Wholesaler registration error:', error)
return NextResponse.json(
{
success: false,
message: 'Registration failed',
error: error instanceof Error ? error.message : 'Unknown error'
},
{ status: 500 }
)
}
}