feat: implement dynamic categories, admin category CRUD, fix routing and cleanup

This commit is contained in:
AyrisAI
2026-07-13 13:51:48 +03:00
parent 2f2dafcfb9
commit 5b44c78396
54 changed files with 3619 additions and 473 deletions
+26
View File
@@ -0,0 +1,26 @@
import { NextRequest, NextResponse } from 'next/server'
import { mockDb } from '@/lib/mockDb'
export async function POST(req: NextRequest) {
try {
const body = await req.json()
const { listingId, actionType } = body
if (!listingId || !actionType) {
return NextResponse.json({ error: 'listingId and actionType are required' }, { status: 400 })
}
const validActions = ['views', 'whatsapp', 'phone', 'menu']
if (!validActions.includes(actionType)) {
return NextResponse.json({ error: 'Invalid actionType' }, { status: 400 })
}
// Call stateful DB increment
const record = await mockDb.incrementAnalytics(listingId, actionType)
return NextResponse.json({ success: true, record })
} catch (err: any) {
console.error('Error logging event analytics:', err)
return NextResponse.json({ error: err.message || 'Internal Server Error' }, { status: 500 })
}
}
+61
View File
@@ -0,0 +1,61 @@
import { NextRequest, NextResponse } from 'next/server'
import { mockDb } from '@/lib/mockDb'
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url)
const neighborhoodSlug = searchParams.get('neighborhood')
if (!neighborhoodSlug) {
return NextResponse.json({ error: 'Neighborhood is required' }, { status: 400 })
}
// Get neighborhood
const neighborhood = await mockDb.getNeighborhoodBySlug(neighborhoodSlug)
if (!neighborhood) {
return NextResponse.json({ listings: [] }, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
}
})
}
// Get listings in that neighborhood that are local approved
const listings = await mockDb.getListings({
neighborhoodId: neighborhood.id,
isLocalApproved: true
})
// Format response matching widget needs
const formattedListings = listings.map(l => ({
id: l.id,
name: l.nameTr,
nameEn: l.nameEn,
nameRu: l.nameRu,
slug: l.slug,
categorySlug: l.category?.slug || 'isletme',
categoryName: l.category?.nameTr || '',
neighborhoodName: l.neighborhood?.nameTr || '',
rating: l.rating,
priceSymbols: '₺'.repeat(l.priceRange),
coverImage: l.images && l.images.length > 0 ? l.images[0].url : 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=800&auto=format&fit=crop&q=80'
}))
return NextResponse.json({ listings: formattedListings }, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
}
})
}
// Handle preflight OPTIONS request
export async function OPTIONS() {
return new NextResponse(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
}
})
}