feat: implement home page, business directory, and administrative management features with updated schema and API routes
Some checks failed
Build and Push App / build (push) Failing after 51s
Some checks failed
Build and Push App / build (push) Failing after 51s
This commit is contained in:
@@ -18,10 +18,10 @@ model User {
|
||||
updatedAt DateTime @updatedAt
|
||||
bio String?
|
||||
location String?
|
||||
countryId String?
|
||||
phone String?
|
||||
isSuspended Boolean @default(false)
|
||||
suspensionReason String?
|
||||
countryId String?
|
||||
businesses Business?
|
||||
comments Comment[]
|
||||
conversations ConversationParticipant[]
|
||||
@@ -35,7 +35,10 @@ model Business {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
category String
|
||||
suggestedCategory String?
|
||||
location String
|
||||
countryId String?
|
||||
city String?
|
||||
description String
|
||||
logoUrl String
|
||||
coverUrl String?
|
||||
@@ -66,26 +69,37 @@ model Business {
|
||||
isSuspended Boolean @default(false)
|
||||
suspensionReason String?
|
||||
plan Plan @default(STARTER)
|
||||
city String?
|
||||
countryId String?
|
||||
country Country? @relation(fields: [countryId], references: [id])
|
||||
owner User @relation(fields: [ownerId], references: [id])
|
||||
comments Comment[]
|
||||
conversations Conversation[]
|
||||
offers Offer[]
|
||||
ratings Rating[]
|
||||
country Country? @relation(fields: [countryId], references: [id])
|
||||
categoryRef BusinessCategory? @relation(fields: [categoryId], references: [id])
|
||||
categoryId String?
|
||||
}
|
||||
|
||||
model BusinessCategory {
|
||||
id String @id @default(uuid())
|
||||
name String @unique
|
||||
slug String @unique
|
||||
icon String? // Lucide icon name
|
||||
isActive Boolean @default(true)
|
||||
businesses Business[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Country {
|
||||
id String @id @default(uuid())
|
||||
name String @unique
|
||||
code String @unique
|
||||
flag String?
|
||||
code String @unique // ISO alpha-2
|
||||
flag String? // Emoji or URL
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
businesses Business[]
|
||||
users User[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model PricingPlan {
|
||||
@@ -93,7 +107,9 @@ model PricingPlan {
|
||||
tier Plan @unique
|
||||
name String
|
||||
priceXOF String
|
||||
yearlyPriceXOF String?
|
||||
priceEUR String
|
||||
yearlyPriceEUR String?
|
||||
description String
|
||||
features String[]
|
||||
offerLimit Int @default(1)
|
||||
@@ -172,34 +188,40 @@ model AnalyticsEvent {
|
||||
label String?
|
||||
value Float?
|
||||
metadata Json?
|
||||
createdAt DateTime @default(now())
|
||||
browser String?
|
||||
city String?
|
||||
country String?
|
||||
device String?
|
||||
ip String?
|
||||
country String?
|
||||
city String?
|
||||
browser String?
|
||||
os String?
|
||||
device String?
|
||||
referrer String?
|
||||
userId String?
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model Rating {
|
||||
id String @id @default(uuid())
|
||||
id String @id @default(uuid())
|
||||
value Int
|
||||
userId String
|
||||
businessId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
comment String?
|
||||
reply String?
|
||||
replyAt DateTime?
|
||||
status RatingStatus @default(PENDING)
|
||||
business Business @relation(fields: [businessId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
businessId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
business Business @relation(fields: [businessId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([userId, businessId])
|
||||
}
|
||||
|
||||
enum RatingStatus {
|
||||
PENDING
|
||||
APPROVED
|
||||
REJECTED
|
||||
}
|
||||
|
||||
model Conversation {
|
||||
id String @id @default(uuid())
|
||||
businessId String
|
||||
@@ -244,6 +266,34 @@ model MessageReport {
|
||||
reporter User @relation(fields: [reporterId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
enum ReportStatus {
|
||||
PENDING
|
||||
RESOLVED
|
||||
DISMISSED
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
VISITOR
|
||||
ENTREPRENEUR
|
||||
ADMIN
|
||||
}
|
||||
|
||||
enum Plan {
|
||||
STARTER
|
||||
BOOSTER
|
||||
EMPIRE
|
||||
}
|
||||
|
||||
enum OfferType {
|
||||
PRODUCT
|
||||
SERVICE
|
||||
}
|
||||
|
||||
enum InterviewType {
|
||||
VIDEO
|
||||
ARTICLE
|
||||
}
|
||||
|
||||
model SiteSetting {
|
||||
id String @id @default("singleton")
|
||||
siteName String @default("Afrohub")
|
||||
@@ -267,39 +317,6 @@ model SiteSetting {
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
enum Plan {
|
||||
STARTER
|
||||
BOOSTER
|
||||
EMPIRE
|
||||
}
|
||||
|
||||
enum RatingStatus {
|
||||
PENDING
|
||||
APPROVED
|
||||
REJECTED
|
||||
}
|
||||
|
||||
enum ReportStatus {
|
||||
PENDING
|
||||
RESOLVED
|
||||
DISMISSED
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
VISITOR
|
||||
ENTREPRENEUR
|
||||
ADMIN
|
||||
}
|
||||
|
||||
enum OfferType {
|
||||
PRODUCT
|
||||
SERVICE
|
||||
}
|
||||
|
||||
enum InterviewType {
|
||||
VIDEO
|
||||
ARTICLE
|
||||
}
|
||||
model LegalDocument {
|
||||
id String @id @default(uuid())
|
||||
type String @unique // 'CGU' or 'CGV'
|
||||
|
||||
52
prisma/seed-categories.ts
Normal file
52
prisma/seed-categories.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import pg from 'pg';
|
||||
import { config } from 'dotenv';
|
||||
|
||||
config();
|
||||
config({ path: '.env.local', override: true });
|
||||
|
||||
const connectionString = process.env.DATABASE_URL!;
|
||||
const pool = new pg.Pool({ connectionString });
|
||||
const adapter = new PrismaPg(pool);
|
||||
const prisma = new PrismaClient({ adapter });
|
||||
|
||||
const CATEGORIES = [
|
||||
{ name: "Technologie & IT", slug: "technologie-it" },
|
||||
{ name: "Agriculture & Agrobusiness", slug: "agriculture-agrobusiness" },
|
||||
{ name: "Mode & Textile", slug: "mode-textile" },
|
||||
{ name: "Cosmétique & Beauté", slug: "cosmetique-beaute" },
|
||||
{ name: "Services aux entreprises", slug: "services-entreprises" },
|
||||
{ name: "Restauration & Alimentation", slug: "restauration-alimentation" },
|
||||
{ name: "Construction & BTP", slug: "construction-btp" },
|
||||
{ name: "Éducation & Formation", slug: "education-formation" },
|
||||
{ name: "Santé & Bien-être", slug: "sante-bien-etre" },
|
||||
{ name: "Artisanat & Déco", slug: "artisanat-deco" },
|
||||
{ name: "Tourisme & Loisirs", slug: "tourisme-loisirs" },
|
||||
{ name: "Finance & Assurance", slug: "finance-assurance" }
|
||||
];
|
||||
|
||||
async function main() {
|
||||
console.log('Seeding categories...');
|
||||
for (const cat of CATEGORIES) {
|
||||
await prisma.businessCategory.upsert({
|
||||
where: { name: cat.name },
|
||||
update: {},
|
||||
create: {
|
||||
name: cat.name,
|
||||
slug: cat.slug,
|
||||
isActive: true
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log('Categories seeded!');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
126
prisma/seed-events.ts
Normal file
126
prisma/seed-events.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import pg from 'pg';
|
||||
import { config } from 'dotenv';
|
||||
|
||||
config();
|
||||
config({ path: '.env.local', override: true });
|
||||
|
||||
const connectionString = process.env.DATABASE_URL!;
|
||||
const pool = new pg.Pool({ connectionString });
|
||||
const adapter = new PrismaPg(pool);
|
||||
const prisma = new PrismaClient({ adapter });
|
||||
|
||||
const EVENTS = [
|
||||
{
|
||||
title: "Forum de l'Entrepreneuriat Africain 2026",
|
||||
slug: "forum-entrepreneuriat-africain-2026",
|
||||
description: "Le plus grand rassemblement d'entrepreneurs africains pour discuter des opportunités de croissance et de financement.",
|
||||
date: new Date("2026-02-15T09:00:00Z"),
|
||||
location: "Abidjan, Côte d'Ivoire",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1540575861501-7ad05823c28b?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Entrepreneuriat", "Networking", "Abidjan"],
|
||||
},
|
||||
{
|
||||
title: "Tech Summit Dakar 2026",
|
||||
slug: "tech-summit-dakar-2026",
|
||||
description: "Découvrez les dernières innovations technologiques et rencontrez les startups les plus prometteuses du Sénégal.",
|
||||
date: new Date("2026-03-10T10:00:00Z"),
|
||||
location: "Dakar, Sénégal",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1505373877841-8d25f7d46678?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Tech", "Innovation", "Dakar"],
|
||||
},
|
||||
{
|
||||
title: "Salon de l'Agrobusiness Africain",
|
||||
slug: "salon-agrobusiness-africain-2026",
|
||||
description: "Exploration des nouvelles techniques agricoles et des opportunités de transformation locale.",
|
||||
date: new Date("2026-05-20T08:30:00Z"),
|
||||
location: "Nairobi, Kenya",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1523348837708-15d4a09cfac2?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Agriculture", "Agrobusiness", "Nairobi"],
|
||||
},
|
||||
{
|
||||
title: "Conférence sur l'Économie Numérique",
|
||||
slug: "conference-economie-numerique-2026",
|
||||
description: "Vers une accélération de la digitalisation des services en Afrique centrale.",
|
||||
date: new Date("2026-06-15T09:00:00Z"),
|
||||
location: "Douala, Cameroun",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Digital", "Économie", "Douala"],
|
||||
},
|
||||
{
|
||||
title: "Afro-Invest 2026",
|
||||
slug: "afro-invest-2026",
|
||||
description: "Rencontre entre investisseurs internationaux et porteurs de projets africains.",
|
||||
date: new Date("2026-08-05T14:00:00Z"),
|
||||
location: "Lagos, Nigeria",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Investissement", "Finance", "Lagos"],
|
||||
},
|
||||
{
|
||||
title: "Sommet du Tourisme Durable",
|
||||
slug: "sommet-tourisme-durable-2026",
|
||||
description: "Promotion des destinations africaines à travers un tourisme respectueux de l'environnement.",
|
||||
date: new Date("2026-09-22T09:00:00Z"),
|
||||
location: "Cape Town, Afrique du Sud",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Tourisme", "Durable", "Cape Town"],
|
||||
},
|
||||
{
|
||||
title: "Salon de la Mode et du Design Africain",
|
||||
slug: "salon-mode-design-africain-2026",
|
||||
description: "Célébration des créateurs locaux et de l'artisanat de luxe.",
|
||||
date: new Date("2026-10-12T11:00:00Z"),
|
||||
location: "Accra, Ghana",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1445205170230-053b83016050?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Mode", "Design", "Accra"],
|
||||
},
|
||||
{
|
||||
title: "Forum de l'Énergie Renouvelable",
|
||||
slug: "forum-energie-renouvelable-2026",
|
||||
description: "Solutions énergétiques pour l'Afrique de demain.",
|
||||
date: new Date("2026-11-08T09:30:00Z"),
|
||||
location: "Casablanca, Maroc",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1473341304170-971dccb5ac1e?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Énergie", "Solaire", "Casablanca"],
|
||||
},
|
||||
{
|
||||
title: "Afro-Education Summit",
|
||||
slug: "afro-education-summit-2026",
|
||||
description: "L'avenir de l'éducation et de la formation professionnelle en Afrique.",
|
||||
date: new Date("2026-12-05T09:00:00Z"),
|
||||
location: "Kigali, Rwanda",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1524178232363-1fb2b075b655?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Éducation", "Kigali"],
|
||||
},
|
||||
{
|
||||
title: "Gala de fin d'année des Entrepreneurs",
|
||||
slug: "gala-entrepreneurs-2026",
|
||||
description: "Soirée de prestige pour célébrer les réussites entrepreneuriales de l'année.",
|
||||
date: new Date("2026-12-20T20:00:00Z"),
|
||||
location: "Abidjan, Côte d'Ivoire",
|
||||
thumbnailUrl: "https://images.unsplash.com/photo-1519671482749-fd09be7ccebf?auto=format&fit=crop&q=80&w=1200",
|
||||
tags: ["Gala", "Célébration", "Abidjan"],
|
||||
},
|
||||
];
|
||||
|
||||
async function main() {
|
||||
console.log('Seeding events...');
|
||||
for (const event of EVENTS) {
|
||||
await prisma.event.upsert({
|
||||
where: { slug: event.slug },
|
||||
update: event,
|
||||
create: event,
|
||||
});
|
||||
}
|
||||
console.log('Events seeded!');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
Reference in New Issue
Block a user