feat: implement dynamic categories, admin category CRUD, fix routing and cleanup
This commit is contained in:
@@ -126,6 +126,13 @@ model Listing {
|
||||
featuredUntil DateTime?
|
||||
collections Collection[] @relation("CollectionListings")
|
||||
instagramFeed InstagramFeedCache?
|
||||
|
||||
// Phase 3 Fields
|
||||
hasWidgetInstalled Boolean @default(false)
|
||||
widgetInstalledAt DateTime?
|
||||
widgetSiteUrl String?
|
||||
analytics ListingAnalyticsDaily[]
|
||||
events Event[]
|
||||
}
|
||||
|
||||
model Gallery {
|
||||
@@ -210,3 +217,49 @@ model InstagramFeedCache {
|
||||
|
||||
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model ListingAnalyticsDaily {
|
||||
id String @id @default(cuid())
|
||||
listingId String
|
||||
date DateTime
|
||||
views Int @default(0)
|
||||
whatsappClicks Int @default(0)
|
||||
phoneClicks Int @default(0)
|
||||
menuClicks Int @default(0)
|
||||
|
||||
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([listingId, date])
|
||||
}
|
||||
|
||||
model Event {
|
||||
id String @id @default(cuid())
|
||||
slug String @unique
|
||||
listingId String?
|
||||
titleTr String
|
||||
titleEn String
|
||||
titleRu String
|
||||
descriptionTr String @db.Text
|
||||
descriptionEn String @db.Text
|
||||
descriptionRu String @db.Text
|
||||
startDate DateTime
|
||||
endDate DateTime?
|
||||
coverImage String? // Openinary
|
||||
isSponsored Boolean @default(false)
|
||||
|
||||
listing Listing? @relation(fields: [listingId], references: [id])
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
}
|
||||
|
||||
model GeneratedItinerary {
|
||||
id String @id @default(cuid())
|
||||
paramsHash String @unique
|
||||
params Json // budget, groupType, interests, days
|
||||
content String @db.Text
|
||||
listingIds String[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { mockDb } from '../lib/mockDb'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Bütün mock verileri PostgreSQL veritabanına aktarılıyor...')
|
||||
|
||||
// Force mockDb to use in-memory data for seeding
|
||||
process.env.USE_MOCK = 'true'
|
||||
|
||||
// 1. Kategoriler
|
||||
console.log('Kategoriler ekleniyor...')
|
||||
const categories = await mockDb.getCategories()
|
||||
for (const cat of categories) {
|
||||
await prisma.category.upsert({
|
||||
where: { id: cat.id },
|
||||
update: {},
|
||||
create: {
|
||||
id: cat.id,
|
||||
slug: cat.slug,
|
||||
nameTr: cat.nameTr,
|
||||
nameEn: cat.nameEn,
|
||||
nameRu: cat.nameRu,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 2. Mahalleler
|
||||
console.log('Mahalleler ekleniyor...')
|
||||
const neighborhoods = await mockDb.getNeighborhoods()
|
||||
for (const neigh of neighborhoods) {
|
||||
await prisma.neighborhood.upsert({
|
||||
where: { id: neigh.id },
|
||||
update: {},
|
||||
create: {
|
||||
id: neigh.id,
|
||||
slug: neigh.slug,
|
||||
nameTr: neigh.nameTr,
|
||||
nameEn: neigh.nameEn,
|
||||
nameRu: neigh.nameRu,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 3. Mekanlar (Listings)
|
||||
console.log('Mekanlar ekleniyor...')
|
||||
const listings = await mockDb.getListings() // Default is 100 limit, mockDb returns all for seed if not paginated
|
||||
// Let's ensure we get all
|
||||
const allListings = await mockDb.getListings({ limit: 1000 })
|
||||
for (const listing of allListings) {
|
||||
await prisma.listing.upsert({
|
||||
where: { id: listing.id },
|
||||
update: {},
|
||||
create: {
|
||||
id: listing.id,
|
||||
slug: listing.slug,
|
||||
categoryId: listing.categoryId,
|
||||
neighborhoodId: listing.neighborhoodId,
|
||||
city: listing.city,
|
||||
nameTr: listing.nameTr,
|
||||
nameEn: listing.nameEn,
|
||||
nameRu: listing.nameRu,
|
||||
descriptionTr: listing.descriptionTr,
|
||||
descriptionEn: listing.descriptionEn,
|
||||
descriptionRu: listing.descriptionRu,
|
||||
address: listing.address,
|
||||
phone: listing.phone,
|
||||
whatsapp: listing.whatsapp,
|
||||
website: listing.website,
|
||||
instagram: listing.instagram,
|
||||
priceRange: listing.priceRange,
|
||||
rating: listing.rating,
|
||||
isLocalApproved: listing.isLocalApproved,
|
||||
latitude: listing.latitude,
|
||||
longitude: listing.longitude,
|
||||
openingHours: listing.openingHours ? JSON.parse(JSON.stringify(listing.openingHours)) : undefined,
|
||||
createdAt: listing.createdAt,
|
||||
updatedAt: listing.updatedAt,
|
||||
menuUrl: listing.menuUrl,
|
||||
isFeatured: listing.isFeatured,
|
||||
hasWidgetInstalled: listing.hasWidgetInstalled,
|
||||
widgetSiteUrl: listing.widgetSiteUrl,
|
||||
images: {
|
||||
create: listing.images?.map(img => ({
|
||||
id: img.id,
|
||||
url: img.url,
|
||||
createdAt: img.createdAt
|
||||
})) || []
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 4. Etkinlikler
|
||||
console.log('Etkinlikler ekleniyor...')
|
||||
const events = await mockDb.getEvents(true) // Get all including past
|
||||
for (const event of events) {
|
||||
await prisma.event.upsert({
|
||||
where: { id: event.id },
|
||||
update: {},
|
||||
create: {
|
||||
id: event.id,
|
||||
slug: event.slug,
|
||||
listingId: event.listingId,
|
||||
titleTr: event.titleTr,
|
||||
titleEn: event.titleEn,
|
||||
titleRu: event.titleRu,
|
||||
descriptionTr: event.descriptionTr,
|
||||
descriptionEn: event.descriptionEn,
|
||||
descriptionRu: event.descriptionRu,
|
||||
startDate: event.startDate,
|
||||
endDate: event.endDate,
|
||||
coverImage: event.coverImage,
|
||||
isSponsored: event.isSponsored,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 5. Koleksiyonlar
|
||||
console.log('Koleksiyonlar ekleniyor...')
|
||||
const collections = await mockDb.getCollections()
|
||||
for (const col of collections) {
|
||||
await prisma.collection.upsert({
|
||||
where: { id: col.id },
|
||||
update: {},
|
||||
create: {
|
||||
id: col.id,
|
||||
slug: col.slug,
|
||||
titleTr: col.titleTr,
|
||||
titleEn: col.titleEn,
|
||||
titleRu: col.titleRu,
|
||||
descriptionTr: col.descriptionTr,
|
||||
descriptionEn: col.descriptionEn,
|
||||
descriptionRu: col.descriptionRu,
|
||||
coverImage: col.coverImage,
|
||||
listings: {
|
||||
connect: col.listingIds?.map(id => ({ id })) || []
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
console.log('✅ Veritabanı başarıyla seed edildi!')
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
Reference in New Issue
Block a user