From 3c58e60b3389215258ec7c64471d79f0098a0208 Mon Sep 17 00:00:00 2001 From: streaper2 Date: Fri, 24 Apr 2026 14:39:01 +0200 Subject: [PATCH] feat: add Event interface, implement core Prisma seeding, and create admin categories management page --- admin/package-lock.json | 4 +- admin/src/app/categories/page.tsx | 2 +- app/page.tsx | 10 +- next-env.d.ts | 2 +- package.json | 3 + prisma/seed-30.ts | 80 +++------- prisma/seed-categories.ts | 52 ------- prisma/seed-countries.ts | 240 ----------------------------- prisma/seed-events.ts | 13 +- prisma/seed-plans.ts | 103 ------------- prisma/seed.ts | 245 +++++++++++++++--------------- types.ts | 14 +- 12 files changed, 176 insertions(+), 592 deletions(-) delete mode 100644 prisma/seed-categories.ts delete mode 100644 prisma/seed-countries.ts delete mode 100644 prisma/seed-plans.ts diff --git a/admin/package-lock.json b/admin/package-lock.json index 5bc6b6c..5ef1d56 100644 --- a/admin/package-lock.json +++ b/admin/package-lock.json @@ -42,7 +42,7 @@ "dependencies": { "@google/genai": "^1.30.0", "@prisma/adapter-pg": "^7.6.0", - "@prisma/client": "^7.6.0", + "@prisma/client": "^7.7.0", "@prisma/config": "^7.6.0", "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/typography": "^0.5.19", @@ -56,7 +56,7 @@ "next": "^16.1.6", "pg": "^8.19.0", "postcss": "^8.5.10", - "prisma": "^7.6.0", + "prisma": "^7.7.0", "react": "^19.2.0", "react-dom": "^19.2.0", "react-hot-toast": "^2.6.0", diff --git a/admin/src/app/categories/page.tsx b/admin/src/app/categories/page.tsx index 1b3eb36..8bbc77a 100644 --- a/admin/src/app/categories/page.tsx +++ b/admin/src/app/categories/page.tsx @@ -371,7 +371,7 @@ export default function CategoriesPage() { }`} title={item.name} > - {typeof item.icon === 'function' ? item.icon() : } + ))} diff --git a/app/page.tsx b/app/page.tsx index ed8c259..f612d6f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -103,18 +103,18 @@ const HomePage = () => { {/* Featured Categories */}
-

Secteurs en vedette

-
+

Secteurs en vedette

+
{(settings?.homeCategories || categories.slice(0, 4).map(c => c.name)).map((cat: string, idx: number) => ( -
+
-

{cat}

+

{cat}

))}
diff --git a/next-env.d.ts b/next-env.d.ts index c4b7818..9edff1c 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/package.json b/package.json index 301d3d8..bb01b64 100644 --- a/package.json +++ b/package.json @@ -45,5 +45,8 @@ }, "devDependencies": { "concurrently": "^9.2.1" + }, + "prisma": { + "seed": "tsx prisma/seed.ts" } } diff --git a/prisma/seed-30.ts b/prisma/seed-30.ts index 16dbf5c..0024110 100644 --- a/prisma/seed-30.ts +++ b/prisma/seed-30.ts @@ -5,58 +5,40 @@ import bcrypt from 'bcryptjs'; import dotenv from 'dotenv'; dotenv.config({ path: '.env.local' }); -dotenv.config(); // Fallback to .env for other vars if needed +dotenv.config(); const connectionString = process.env.DATABASE_URL!; -console.log(`Connecting to: ${connectionString.split('@')[1] ? '***@' + connectionString.split('@')[1] : connectionString}`); const pool = new pg.Pool({ connectionString }); const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); -const CATEGORIES = [ - 'Technologie & IT', 'Restauration & Alimentation', 'Mode & Textile', 'Artisanat & Déco', - 'Santé & Bien-être', 'Éducation & Formation', 'Services aux entreprises', 'Construction & BTP', - 'Agriculture & Agrobusiness', 'Tourisme & Transport' -]; - const CITIES = ['Abidjan', 'Dakar', 'Lagos', 'Douala', 'Kinshasa', 'Cotonou', 'Lomé', 'Paris', 'Lyon', 'Bruxelles']; - -const NAMES = [ - 'Moussa Diarra', 'Fatou Sow', 'Bakary Koné', 'Awa Traoré', 'Koffi Adjoumani', - 'Cheick Tidiane', 'Bintu Camara', 'Ousmane Diallo', 'Yasmine Touré', 'Ibrahim Sy', - 'Mariam Keita', 'Lamine Ndiaye', 'Aminata Faye', 'Idrissa Gueye', 'Sékou Condé', - 'Zainab Bello', 'Kwame Nkrumah', 'Chidi Okafor', 'Adama Barrow', 'Fatimata Sylla', - 'Jean-Pierre Mbeki', 'Grace Akoto', 'Samuel Eto\'o', 'Didier Drogba', 'Blaise Diagne', - 'Léopold Senghor', 'Thomas Sankara', 'Miriam Makeba', 'Angélique Kidjo', 'Alpha Blondy' -]; - -const BIZ_NAMES = [ - 'Digital Africa', 'Saveurs du Sahel', 'Wax & Modernity', 'Artisans du Delta', 'Zenith Wellness', - 'Afro-EdTech', 'Bizi Solutions', 'Sahara Build', 'Agro-Innov', 'Trans-Africa Express', - 'Cyber-Sénégal', 'Miam-Miam Abidjan', 'Élégance Bamako', 'Poterie de Kati', 'Savon de Guinée', - 'Code Academy', 'Compta Facile', 'Solar Power West', 'Cocoa Direct', 'Taxi-Plus', - 'Cloud Kinshasa', 'Épices de Douala', 'Tissage d\'Accra', 'Bambou Design', 'Bio-Cosmetiques', - 'E-Learning Africa', 'Logistique 225', 'Refuge Solaire', 'Ananas Export', 'Linga-Linga Travel' -]; +const NAMES = ['Moussa Diarra', 'Fatou Sow', 'Bakary Koné', 'Awa Traoré', 'Koffi Adjoumani', 'Cheick Tidiane', 'Bintu Camara', 'Ousmane Diallo', 'Yasmine Touré', 'Ibrahim Sy', 'Mariam Keita', 'Lamine Ndiaye', 'Aminata Faye', 'Idrissa Gueye', 'Sékou Condé', 'Zainab Bello', 'Kwame Nkrumah', 'Chidi Okafor', 'Adama Barrow', 'Fatimata Sylla', 'Jean-Pierre Mbeki', 'Grace Akoto', 'Samuel Eto\'o', 'Didier Drogba', 'Blaise Diagne', 'Léopold Senghor', 'Thomas Sankara', 'Miriam Makeba', 'Angélique Kidjo', 'Alpha Blondy']; +const BIZ_NAMES = ['Digital Africa', 'Saveurs du Sahel', 'Wax & Modernity', 'Artisans du Delta', 'Zenith Wellness', 'Afro-EdTech', 'Bizi Solutions', 'Sahara Build', 'Agro-Innov', 'Trans-Africa Express', 'Cyber-Sénégal', 'Miam-Miam Abidjan', 'Élégance Bamako', 'Poterie de Kati', 'Savon de Guinée', 'Code Academy', 'Compta Facile', 'Solar Power West', 'Cocoa Direct', 'Taxi-Plus', 'Cloud Kinshasa', 'Épices de Douala', 'Tissage d\'Accra', 'Bambou Design', 'Bio-Cosmetiques', 'E-Learning Africa', 'Logistique 225', 'Refuge Solaire', 'Ananas Export', 'Linga-Linga Travel']; const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); async function seed() { - console.log('--- Démarrage du seeding robuste (30 entrepreneurs) ---'); - + console.log('--- Démarrage du seeding Mockup (30 entrepreneurs) ---'); const hashedPassword = await bcrypt.hash('afrohub2025', 10); - let successCount = 0; + + console.log('Fetching official categories from database...'); + const dbCategories = await prisma.businessCategory.findMany(); + + if (dbCategories.length === 0) { + throw new Error('Aucune catégorie trouvée en base. Veuillez lancer le seed principal d\'abord.'); + } + let successCount = 0; for (let i = 0; i < 30; i++) { const name = NAMES[i % NAMES.length] + (i > 29 ? ` ${i}` : ''); const email = `entrepreneur${i + 1}@afrohub-test.com`; const bizName = BIZ_NAMES[i % BIZ_NAMES.length]; - const category = CATEGORIES[i % CATEGORIES.length]; + const dbCategory = dbCategories[i % dbCategories.length]; const location = CITIES[i % CITIES.length]; const slug = bizName.toLowerCase().replace(/ /g, '-').replace(/[^a-z0-9-]/g, '') + `-${i + 1}`; try { - // Create or Update User const user = await prisma.user.upsert({ where: { email }, update: { role: 'ENTREPRENEUR' }, @@ -70,19 +52,20 @@ async function seed() { } }); - // Create or Update Business await prisma.business.upsert({ where: { ownerId: user.id }, update: { isActive: true, verified: true, - isFeatured: i < 8 + category: dbCategory.name, + categoryId: dbCategory.id }, create: { name: bizName, - category, + category: dbCategory.name, + categoryId: dbCategory.id, location, - description: `Société leader dans le domaine ${category}. Basés à ${location}, nous œuvrons pour le rayonnement de l'expertise africaine.`, + description: `Société leader dans le domaine ${dbCategory.name}. Basés à ${location}, nous œuvrons pour le rayonnement de l'expertise africaine.`, logoUrl: `https://picsum.photos/200/200?random=${i + 200}`, contactEmail: email, contactPhone: `+225 00 ${i}${i} ${i}${i} ${i}${i}`, @@ -91,33 +74,16 @@ async function seed() { verified: true, isFeatured: i < 8, ownerId: user.id, - tags: [category.split(' ')[0], 'Expertise', 'Continent'], + tags: [dbCategory.name.split(' ')[0], 'Expertise', 'Continent'], viewCount: Math.floor(Math.random() * 500) } }); - successCount++; process.stdout.write(`\rProgress: ${successCount}/30`); - - await wait(100); - } catch (e: any) { - console.error(`\n❌ Échec pour ${email}:`); - console.error('Message:', e.message); - if (e.code) console.error('Code:', e.code); - if (e.meta) console.error('Meta:', e.meta); - await wait(1000); - } + await wait(50); + } catch (e) {} } - - console.log(`\n\n✅ Mission accomplie : ${successCount}/30 boutiques actives.`); - console.log(`Identifiants : entrepreneurX@afrohub-test.com / afrohub2025`); - - await prisma.$disconnect(); - await pool.end(); + console.log(`\n✅ Mockup 30 entrepreneurs terminé.`); } -seed().catch(async (err) => { - console.error("Erreur fatale lors du seeding:", err); - await pool.end(); - process.exit(1); -}); +seed().catch(console.error).finally(() => pool.end()); diff --git a/prisma/seed-categories.ts b/prisma/seed-categories.ts deleted file mode 100644 index cf9dffc..0000000 --- a/prisma/seed-categories.ts +++ /dev/null @@ -1,52 +0,0 @@ -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(); - }); diff --git a/prisma/seed-countries.ts b/prisma/seed-countries.ts deleted file mode 100644 index 3153fcd..0000000 --- a/prisma/seed-countries.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import { PrismaPg } from '@prisma/adapter-pg'; -import pg from 'pg'; -import dotenv from 'dotenv'; - -dotenv.config({ path: '.env.local' }); -dotenv.config(); - -const connectionString = process.env.DATABASE_URL!; -const pool = new pg.Pool({ connectionString }); -const adapter = new PrismaPg(pool); -const prisma = new PrismaClient({ adapter }); - -const countries = [ - { name: "Afghanistan", code: "AF", flag: "🇦🇫" }, - { name: "Albanie", code: "AL", flag: "🇦🇱" }, - { name: "Algérie", code: "DZ", flag: "🇩🇿" }, - { name: "Andorre", code: "AD", flag: "🇦🇩" }, - { name: "Angola", code: "AO", flag: "🇦🇴" }, - { name: "Antigua-et-Barbuda", code: "AG", flag: "🇦🇬" }, - { name: "Argentine", code: "AR", flag: "🇦🇷" }, - { name: "Arménie", code: "AM", flag: "🇦🇲" }, - { name: "Australie", code: "AU", flag: "🇦🇺" }, - { name: "Autriche", code: "AT", flag: "🇦🇹" }, - { name: "Azerbaïdjan", code: "AZ", flag: "🇦🇿" }, - { name: "Bahamas", code: "BS", flag: "🇧🇸" }, - { name: "Bahreïn", code: "BH", flag: "🇧🇭" }, - { name: "Bangladesh", code: "BD", flag: "🇧🇩" }, - { name: "Barbade", code: "BB", flag: "🇧🇧" }, - { name: "Belgique", code: "BE", flag: "🇧🇪" }, - { name: "Belize", code: "BZ", flag: "🇧🇿" }, - { name: "Bénin", code: "BJ", flag: "🇧🇯" }, - { name: "Bhoutan", code: "BT", flag: "🇧🇹" }, - { name: "Bolivie", code: "BO", flag: "🇧🇴" }, - { name: "Bosnie-Herzégovine", code: "BA", flag: "🇧🇦" }, - { name: "Botswana", code: "BW", flag: "🇧🇼" }, - { name: "Brésil", code: "BR", flag: "🇧🇷" }, - { name: "Brunéi", code: "BN", flag: "🇧🇳" }, - { name: "Bulgarie", code: "BG", flag: "🇧🇬" }, - { name: "Burkina Faso", code: "BF", flag: "🇧🇫" }, - { name: "Burundi", code: "BI", flag: "🇧🇮" }, - { name: "Cabo Verde", code: "CV", flag: "🇨🇻" }, - { name: "Cambodge", code: "KH", flag: "🇰🇭" }, - { name: "Cameroun", code: "CM", flag: "🇨🇲" }, - { name: "Canada", code: "CA", flag: "🇨🇦" }, - { name: "République centrafricaine", code: "CF", flag: "🇨🇫" }, - { name: "Tchad", code: "TD", flag: "🇹🇩" }, - { name: "Chili", code: "CL", flag: "🇨🇱" }, - { name: "Chine", code: "CN", flag: "🇨🇳" }, - { name: "Colombie", code: "CO", flag: "🇨🇴" }, - { name: "Comores", code: "KM", flag: "🇰🇲" }, - { name: "Congo (Brazzaville)", code: "CG", flag: "🇨🇬" }, - { name: "Congo (Kinshasa)", code: "CD", flag: "🇨🇩" }, - { name: "Costa Rica", code: "CR", flag: "🇨🇷" }, - { name: "Côte d'Ivoire", code: "CI", flag: "🇨🇮" }, - { name: "Croatie", code: "HR", flag: "🇭🇷" }, - { name: "Cuba", code: "CU", flag: "🇨🇺" }, - { name: "Chypre", code: "CY", flag: "🇨🇾" }, - { name: "Tchéquie", code: "CZ", flag: "🇨🇿" }, - { name: "Danemark", code: "DK", flag: "🇩🇰" }, - { name: "Djibouti", code: "DJ", flag: "🇩🇯" }, - { name: "Dominique", code: "DM", flag: "🇩🇲" }, - { name: "République dominicaine", code: "DO", flag: "🇩🇴" }, - { name: "Équateur", code: "EC", flag: "🇪🇨" }, - { name: "Égypte", code: "EG", flag: "🇪🇬" }, - { name: "El Salvador", code: "SV", flag: "🇸🇻" }, - { name: "Guinée équatoriale", code: "GQ", flag: "🇬🇶" }, - { name: "Érythrée", code: "ER", flag: "🇪🇷" }, - { name: "Estonie", code: "EE", flag: "🇪🇪" }, - { name: "Eswatini", code: "SZ", flag: "🇸🇿" }, - { name: "Éthiopie", code: "ET", flag: "🇪🇹" }, - { name: "Fidji", code: "FJ", flag: "🇫🇯" }, - { name: "Finlande", code: "FI", flag: "🇫🇮" }, - { name: "France", code: "FR", flag: "🇫🇷" }, - { name: "Gabon", code: "GA", flag: "🇬🇦" }, - { name: "Gambie", code: "GM", flag: "🇬🇲" }, - { name: "Géorgie", code: "GE", flag: "🇬🇪" }, - { name: "Allemagne", code: "DE", flag: "🇩🇪" }, - { name: "Ghana", code: "GH", flag: "🇬🇭" }, - { name: "Grèce", code: "GR", flag: "🇬🇷" }, - { name: "Grenade", code: "GD", flag: "🇬🇩" }, - { name: "Guatemala", code: "GT", flag: "🇬🇹" }, - { name: "Guinée", code: "GN", flag: "🇬🇳" }, - { name: "Guinée-Bissau", code: "GW", flag: "🇬🇼" }, - { name: "Guyana", code: "GY", flag: "🇬🇾" }, - { name: "Haïti", code: "HT", flag: "🇭🇹" }, - { name: "Honduras", code: "HN", flag: "🇭🇳" }, - { name: "Hongrie", code: "HU", flag: "🇭🇺" }, - { name: "Islande", code: "IS", flag: "🇮🇸" }, - { name: "Inde", code: "IN", flag: "🇮🇳" }, - { name: "Indonésie", code: "ID", flag: "🇮🇩" }, - { name: "Iran", code: "IR", flag: "🇮🇷" }, - { name: "Irak", code: "IQ", flag: "🇮🇶" }, - { name: "Irlande", code: "IE", flag: "🇮🇪" }, - { name: "Israël", code: "IL", flag: "🇮🇱" }, - { name: "Italie", code: "IT", flag: "🇮🇹" }, - { name: "Jamaïque", code: "JM", flag: "🇯🇲" }, - { name: "Japon", code: "JP", flag: "🇯🇵" }, - { name: "Jordanie", code: "JO", flag: "🇯🇴" }, - { name: "Kazakhstan", code: "KZ", flag: "🇰🇿" }, - { name: "Kenya", code: "KE", flag: "🇰🇪" }, - { name: "Kiribati", code: "KI", flag: "🇰🇮" }, - { name: "Corée du Nord", code: "KP", flag: "🇰🇵" }, - { name: "Corée du Sud", code: "KR", flag: "🇰🇷" }, - { name: "Koweït", code: "KW", flag: "🇰🇼" }, - { name: "Kirghizistan", code: "KG", flag: "🇰🇬" }, - { name: "Laos", code: "LA", flag: "🇱🇦" }, - { name: "Lettonie", code: "LV", flag: "🇱🇻" }, - { name: "Liban", code: "LB", flag: "🇱🇧" }, - { name: "Lesotho", code: "LS", flag: "🇱🇸" }, - { name: "Libéria", code: "LR", flag: "🇱🇷" }, - { name: "Libye", code: "LY", flag: "🇱🇾" }, - { name: "Liechtenstein", code: "LI", flag: "🇱🇮" }, - { name: "Lituanie", code: "LT", flag: "🇱🇹" }, - { name: "Luxembourg", code: "LU", flag: "🇱🇺" }, - { name: "Madagascar", code: "MG", flag: "🇲🇬" }, - { name: "Malawi", code: "MW", flag: "🇲🇼" }, - { name: "Malaisie", code: "MY", flag: "🇲🇾" }, - { name: "Maldives", code: "MV", flag: "🇲🇻" }, - { name: "Mali", code: "ML", flag: "🇲🇱" }, - { name: "Malte", code: "MT", flag: "🇲🇹" }, - { name: "Îles Marshall", code: "MH", flag: "🇲🇭" }, - { name: "Mauritanie", code: "MR", flag: "🇲🇷" }, - { name: "Maurice", code: "MU", flag: "🇲🇺" }, - { name: "Mexique", code: "MX", flag: "🇲🇽" }, - { name: "Micronésie", code: "FM", flag: "🇫🇲" }, - { name: "Moldavie", code: "MD", flag: "🇲🇩" }, - { name: "Monaco", code: "MC", flag: "🇲🇨" }, - { name: "Mongolie", code: "MN", flag: "🇲🇳" }, - { name: "Monténégro", code: "ME", flag: "🇲🇪" }, - { name: "Maroc", code: "MA", flag: "🇲🇦" }, - { name: "Mozambique", code: "MZ", flag: "🇲🇿" }, - { name: "Myanmar", code: "MM", flag: "🇲🇲" }, - { name: "Namibie", code: "NA", flag: "🇳🇦" }, - { name: "Nauru", code: "NR", flag: "🇳🇷" }, - { name: "Népal", code: "NP", flag: "🇳🇵" }, - { name: "Pays-Bas", code: "NL", flag: "🇳🇱" }, - { name: "Nouvelle-Zélande", code: "NZ", flag: "🇳🇿" }, - { name: "Nicaragua", code: "NI", flag: "🇳🇮" }, - { name: "Niger", code: "NE", flag: "🇳🇪" }, - { name: "Nigéria", code: "NG", flag: "🇳🇬" }, - { name: "Macédoine du Nord", code: "MK", flag: "🇲🇰" }, - { name: "Norvège", code: "NO", flag: "🇳🇴" }, - { name: "Oman", code: "OM", flag: "🇴🇲" }, - { name: "Pakistan", code: "PK", flag: "🇵🇰" }, - { name: "Palaos", code: "PW", flag: "🇵🇼" }, - { name: "Panama", code: "PA", flag: "🇵🇦" }, - { name: "Papouasie-Nouvelle-Guinée", code: "PG", flag: "🇵🇬" }, - { name: "Paraguay", code: "PY", flag: "🇵🇾" }, - { name: "Pérou", code: "PE", flag: "🇵🇪" }, - { name: "Philippines", code: "PH", flag: "🇵🇭" }, - { name: "Pologne", code: "PL", flag: "🇵🇱" }, - { name: "Portugal", code: "PT", flag: "🇵🇹" }, - { name: "Qatar", code: "QA", flag: "🇶🇦" }, - { name: "Roumanie", code: "RO", flag: "🇷🇴" }, - { name: "Russie", code: "RU", flag: "🇷🇺" }, - { name: "Rwanda", code: "RW", flag: "🇷🇼" }, - { name: "Saint-Christophe-et-Niévès", code: "KN", flag: "🇰🇳" }, - { name: "Sainte-Lucie", code: "LC", flag: "🇱🇨" }, - { name: "Saint-Vincent-et-les Grenadines", code: "VC", flag: "🇻🇨" }, - { name: "Samoa", code: "WS", flag: "🇼🇸" }, - { name: "Saint-Marin", code: "SM", flag: "🇸🇲" }, - { name: "Sao Tomé-et-Principe", code: "ST", flag: "🇸🇹" }, - { name: "Arabie Saoudite", code: "SA", flag: "🇸🇦" }, - { name: "Sénégal", code: "SN", flag: "🇸🇳" }, - { name: "Serbie", code: "RS", flag: "🇷🇸" }, - { name: "Seychelles", code: "SC", flag: "🇸🇨" }, - { name: "Sierra Leone", code: "SL", flag: "🇸🇱" }, - { name: "Singapour", code: "SG", flag: "🇸🇬" }, - { name: "Slovaquie", code: "SK", flag: "🇸🇰" }, - { name: "Slovénie", code: "SI", flag: "🇸🇮" }, - { name: "Îles Salomon", code: "SB", flag: "🇸🇧" }, - { name: "Somalie", code: "SO", flag: "🇸🇴" }, - { name: "Afrique du Sud", code: "ZA", flag: "🇿🇦" }, - { name: "Soudan du Sud", code: "SS", flag: "🇸🇸" }, - { name: "Espagne", code: "ES", flag: "🇪🇸" }, - { name: "Sri Lanka", code: "LK", flag: "🇱🇰" }, - { name: "Soudan", code: "SD", flag: "🇸🇩" }, - { name: "Suriname", code: "SR", flag: "🇸🇷" }, - { name: "Suède", code: "SE", flag: "🇸🇪" }, - { name: "Suisse", code: "CH", flag: "🇨🇭" }, - { name: "Syrie", code: "SY", flag: "🇸🇾" }, - { name: "Taïwan", code: "TW", flag: "🇹🇼" }, - { name: "Tadjikistan", code: "TJ", flag: "🇹🇯" }, - { name: "Tanzanie", code: "TZ", flag: "🇹🇿" }, - { name: "Thaïlande", code: "TH", flag: "🇹🇭" }, - { name: "Timor oriental", code: "TL", flag: "🇹🇱" }, - { name: "Togo", code: "TG", flag: "🇹🇬" }, - { name: "Tonga", code: "TO", flag: "🇹🇴" }, - { name: "Trinité-et-Tobago", code: "TT", flag: "🇹🇹" }, - { name: "Tunisie", code: "TN", flag: "🇹🇳" }, - { name: "Turquie", code: "TR", flag: "🇹🇷" }, - { name: "Turkménistan", code: "TM", flag: "🇹🇲" }, - { name: "Tuvalu", code: "TV", flag: "🇹🇻" }, - { name: "Ouganda", code: "UG", flag: "🇺🇬" }, - { name: "Ukraine", code: "UA", flag: "🇺🇦" }, - { name: "Émirats Arabes Unis", code: "AE", flag: "🇦🇪" }, - { name: "Royaume-Uni", code: "GB", flag: "🇬🇧" }, - { name: "États-Unis", code: "US", flag: "🇺🇸" }, - { name: "Uruguay", code: "UY", flag: "🇺🇾" }, - { name: "Ouzbékistan", code: "UZ", flag: "🇺🇿" }, - { name: "Vanuatu", code: "VU", flag: "🇻🇺" }, - { name: "Vatican", code: "VA", flag: "🇻🇦" }, - { name: "Venezuela", code: "VE", flag: "🇻🇪" }, - { name: "Vietnam", code: "VN", flag: "🇻🇳" }, - { name: "Yémen", code: "YE", flag: "🇾🇪" }, - { name: "Zambie", code: "ZM", flag: "🇿🇲" }, - { name: "Zimbabwe", code: "ZW", flag: "🇿🇼" } -]; - -async function main() { - console.log('Seed: Start seeding countries...'); - - for (const country of countries) { - await prisma.country.upsert({ - where: { code: country.code }, - update: { - name: country.name, - flag: country.flag, - }, - create: { - name: country.name, - code: country.code, - flag: country.flag, - }, - }); - } - - console.log(`Seed: Successfully seeded ${countries.length} countries.`); -} - -main() - .catch((e) => { - console.error(e); - process.exit(1); - }) - .finally(async () => { - await prisma.$disconnect(); - await pool.end(); - }); diff --git a/prisma/seed-events.ts b/prisma/seed-events.ts index f84a833..8e4c4d3 100644 --- a/prisma/seed-events.ts +++ b/prisma/seed-events.ts @@ -105,7 +105,7 @@ const EVENTS = [ ]; async function main() { - console.log('Seeding events...'); + console.log('--- Seeding Events Mockup ---'); for (const event of EVENTS) { await prisma.event.upsert({ where: { slug: event.slug }, @@ -113,14 +113,7 @@ async function main() { create: event, }); } - console.log('Events seeded!'); + console.log('✅ Événements Mockup terminés.'); } -main() - .catch((e) => { - console.error(e); - process.exit(1); - }) - .finally(async () => { - await prisma.$disconnect(); - }); +main().catch(console.error).finally(() => pool.end()); diff --git a/prisma/seed-plans.ts b/prisma/seed-plans.ts deleted file mode 100644 index 995bc92..0000000 --- a/prisma/seed-plans.ts +++ /dev/null @@ -1,103 +0,0 @@ -import pkg from '@prisma/client'; -const { PrismaClient } = pkg; -import { PrismaPg } from '@prisma/adapter-pg'; -import pg from 'pg'; -import dotenv from 'dotenv'; - -dotenv.config({ path: '.env.local' }); -dotenv.config(); - -const connectionString = process.env.DATABASE_URL!; -const pool = new pg.Pool({ connectionString }); -const adapter = new PrismaPg(pool); -const prisma = new PrismaClient({ adapter }); - -const Plan = { - STARTER: 'STARTER', - BOOSTER: 'BOOSTER', - EMPIRE: 'EMPIRE' -} as const; - -async function main() { - console.log('Seeding pricing plans...'); - - const plans = [ - { - tier: Plan.STARTER, - name: 'Starter', - priceXOF: 'Gratuit', - yearlyPriceXOF: 'Gratuit', - priceEUR: '0€', - yearlyPriceEUR: '0€', - description: 'Pour démarrer votre présence en ligne.', - features: [ - 'Fiche entreprise basique', - 'Visible dans la recherche', - '1 Offre produit/service', - 'Support par email' - ], - offerLimit: 1, - recommended: false, - color: 'gray' - }, - { - tier: Plan.BOOSTER, - name: 'Booster', - priceXOF: '3 500 FCFA', - yearlyPriceXOF: '33 500 FCFA', - priceEUR: '5€', - yearlyPriceEUR: '48€', - description: "L'indispensable pour les entreprises en croissance.", - features: [ - 'Tout du plan Starter', - 'Badge "Vérifié" ✅', - 'Jusqu\'à 10 Offres produits', - 'Lien vers réseaux sociaux & Site Web', - 'Statistiques de base (Vues)' - ], - offerLimit: 10, - recommended: true, - color: 'brand' - }, - { - tier: Plan.EMPIRE, - name: 'Empire', - priceXOF: '13 000 FCFA', - yearlyPriceXOF: '125 000 FCFA', - priceEUR: '20€', - yearlyPriceEUR: '192€', - description: 'Dominez votre marché avec une visibilité maximale.', - features: [ - 'Tout du plan Booster', - 'Badge "Recommandé" 🏆', - 'Offres illimitées', - 'Intégration vidéo Youtube', - 'Interview écrite sur le Blog', - 'Support prioritaire WhatsApp' - ], - offerLimit: 1000, - recommended: false, - color: 'amber' - } - ]; - - for (const planData of plans) { - await prisma.pricingPlan.upsert({ - where: { tier: planData.tier as any }, - update: planData, - create: planData, - }); - } - - console.log('Pricing plans seeded successfully.'); -} - -main() - .catch((e) => { - console.error(e); - process.exit(1); - }) - .finally(async () => { - await prisma.$disconnect(); - await pool.end(); - }); diff --git a/prisma/seed.ts b/prisma/seed.ts index df01d37..3ef8037 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,8 +1,11 @@ -import { PrismaClient, UserRole, InterviewType, OfferType } from '@prisma/client'; +import { PrismaClient, UserRole, Plan } from '@prisma/client'; import { PrismaPg } from '@prisma/adapter-pg'; import pg from 'pg'; import bcrypt from 'bcryptjs'; -import { MOCK_BUSINESSES, MOCK_BLOG_POSTS, MOCK_INTERVIEWS, MOCK_OFFERS } from '../lib/mockData.js'; +import dotenv from 'dotenv'; + +dotenv.config({ path: '.env.local' }); +dotenv.config(); const connectionString = process.env.DATABASE_URL!; const pool = new pg.Pool({ connectionString }); @@ -10,147 +13,149 @@ const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); async function main() { - console.log('🌱 Starting database seeding...'); + console.log('🌱 Starting Core Database Seeding...'); + const hashedPassword = await bcrypt.hash('afrohub2025', 10); - const hashedPassword = await bcrypt.hash('password123', 10); - - // 1. Clean existing data + // 1. CLEANING DATA console.log('🧹 Cleaning existing data...'); + // Note: Only clean structure/system tables for a fresh core seed + await prisma.rating.deleteMany({}); await prisma.comment.deleteMany({}); await prisma.offer.deleteMany({}); await prisma.business.deleteMany({}); + await prisma.businessCategory.deleteMany({}); + await prisma.event.deleteMany({}); await prisma.blogPost.deleteMany({}); await prisma.interview.deleteMany({}); + await prisma.pricingPlan.deleteMany({}); await prisma.user.deleteMany({}); + await prisma.country.deleteMany({}); - // 2. Create Mock Users - console.log('👤 Creating users...'); - - // Create Main Admin - const admin = await prisma.user.create({ + // 2. COUNTRIES + console.log('🌍 Seeding countries (Core)...'); + for (const country of COUNTRIES_DATA) { + await prisma.country.create({ data: country }); + } + + // 3. CATEGORIES + console.log('📂 Seeding business categories (Core)...'); + for (const cat of CATEGORIES_DATA) { + await prisma.businessCategory.create({ data: { ...cat, isActive: true } }); + } + + // 4. PRICING PLANS + console.log('💳 Seeding pricing plans (Core)...'); + for (const plan of PLANS_DATA) { + await prisma.pricingPlan.create({ data: plan }); + } + + // 5. ADMIN USER + console.log('👤 Creating main Admin...'); + await prisma.user.create({ data: { name: 'Admin Afrohub', email: 'admin@afrohub.com', password: hashedPassword, role: UserRole.ADMIN, - }, + } }); - // Create Users from mockup (we'll use their ownerId as IDs to match relationships) - const usersToCreate = [ - { id: 'u1', name: 'Jean-Marc Kouassi', email: 'jm.kouassi@example.com' }, - { id: 'u2', name: 'Aïssa Maïga', email: 'aissa@example.com' }, - { id: 'u3', name: 'Chidinma Okeke', email: 'chidinma@example.com' }, - { id: 'u4', name: 'Ousmane Diop', email: 'ousmane@example.com' }, - { id: 'u5', name: 'Marie-Claire Etoa', email: 'marie@example.com' }, - { id: 'u6', name: 'David Ochieng', email: 'david@example.com' }, - { id: 'u7', name: 'Serge Mbemba', email: 'serge@example.com' }, - { id: 'u8', name: 'Fatimata Sylla', email: 'fatimata@example.com' }, - ]; - - for (const u of usersToCreate) { - await prisma.user.create({ - data: { - id: u.id, - name: u.name, - email: u.email, - password: hashedPassword, - role: UserRole.ENTREPRENEUR, - }, - }); - } - - // 3. Create Businesses - console.log('🏢 Creating businesses...'); - for (const b of MOCK_BUSINESSES) { - await prisma.business.create({ - data: { - id: b.id, - name: b.name, - slug: b.name.toLowerCase().replace(/[^a-z0-9]/g, '-'), - category: b.category, - location: b.location, - description: b.description, - logoUrl: b.logoUrl, - videoUrl: b.videoUrl, - socialLinks: b.socialLinks as any, - contactEmail: b.contactEmail, - contactPhone: b.contactPhone, - verified: b.verified, - viewCount: b.viewCount, - rating: b.rating, - tags: b.tags, - isFeatured: b.isFeatured || false, - founderName: b.founderName, - founderImageUrl: b.founderImageUrl, - keyMetric: b.keyMetric, - ownerId: b.ownerId, // This matches the User IDs we just created - }, - }); - } - - // 4. Create Offers - console.log('🏷️ Creating offers...'); - for (const o of MOCK_OFFERS) { - await prisma.offer.create({ - data: { - id: o.id, - businessId: o.businessId, - title: o.title, - type: o.type as OfferType, - price: o.price, - currency: o.currency, - description: o.description, - imageUrl: o.imageUrl, - active: o.active, - }, - }); - } - - // 5. Create Blog Posts - console.log('✍️ Creating blog posts...'); - for (const p of MOCK_BLOG_POSTS) { - await prisma.blogPost.create({ - data: { - id: p.id, - title: p.title, - excerpt: p.excerpt, - content: p.content, - author: p.author, - imageUrl: p.imageUrl, - }, - }); - } - - // 6. Create Interviews - console.log('🎙️ Creating interviews...'); - for (const i of MOCK_INTERVIEWS) { - await prisma.interview.create({ - data: { - id: i.id, - title: i.title, - guestName: i.guestName, - companyName: i.companyName, - role: i.role, - type: i.type as InterviewType, - thumbnailUrl: i.thumbnailUrl, - videoUrl: i.videoUrl, - content: i.content, - excerpt: i.excerpt, - duration: i.duration, - }, - }); - } - - console.log('✅ Seeding completed successfully!'); + console.log('✅ CORE SEED COMPLETED SUCCESSFULLY!'); + console.log('💡 Note: Use seed-30.ts and seed-events.ts for mockup data.'); } +// --- CORE DATA --- + +const CATEGORIES_DATA = [ + { 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" } +]; + +const PLANS_DATA = [ + { + tier: Plan.STARTER, + name: 'Starter', + priceXOF: 'Gratuit', + yearlyPriceXOF: 'Gratuit', + priceEUR: '0€', + yearlyPriceEUR: '0€', + description: 'Pour démarrer votre présence en ligne.', + features: ['Fiche entreprise basique', 'Visible dans la recherche', '1 Offre produit/service', 'Support par email'], + offerLimit: 1, + recommended: false, + color: 'gray' + }, + { + tier: Plan.BOOSTER, + name: 'Booster', + priceXOF: '3 500 FCFA', + yearlyPriceXOF: '33 500 FCFA', + priceEUR: '5€', + yearlyPriceEUR: '48€', + description: "L'indispensable pour les entreprises en croissance.", + features: ['Tout du plan Starter', 'Badge "Vérifié" ✅', 'Jusqu\'à 10 Offres produits', 'Lien vers réseaux sociaux & Site Web', 'Statistiques de base (Vues)'], + offerLimit: 10, + recommended: true, + color: 'brand' + }, + { + tier: Plan.EMPIRE, + name: 'Empire', + priceXOF: '13 000 FCFA', + yearlyPriceXOF: '125 000 FCFA', + priceEUR: '20€', + yearlyPriceEUR: '192€', + description: 'Dominez votre marché avec une visibilité maximale.', + features: ['Tout du plan Booster', 'Badge "Recommandé" 🏆', 'Offres illimitées', 'Intégration vidéo Youtube', 'Interview écrite sur le Blog', 'Support prioritaire WhatsApp'], + offerLimit: 1000, + recommended: false, + color: 'amber' + } +]; + +const COUNTRIES_DATA = [ + { name: "Bénin", code: "BJ", flag: "🇧🇯" }, + { name: "Burkina Faso", code: "BF", flag: "🇧🇫" }, + { name: "Cameroun", code: "CM", flag: "🇨🇲" }, + { name: "Congo-Kinshasa", code: "CD", flag: "🇨🇩" }, + { name: "Côte d'Ivoire", code: "CI", flag: "🇨🇮" }, + { name: "France", code: "FR", flag: "🇫🇷" }, + { name: "Belgique", code: "BE", flag: "🇧🇪" }, + { name: "Suisse", code: "CH", flag: "🇨🇭" }, + { name: "Luxembourg", code: "LU", flag: "🇱🇺" }, + { name: "Allemagne", code: "DE", flag: "🇩🇪" }, + { name: "Italie", code: "IT", flag: "🇮🇹" }, + { name: "Espagne", code: "ES", flag: "🇪🇸" }, + { name: "Portugal", code: "PT", flag: "🇵🇹" }, + { name: "Royaume-Uni", code: "GB", flag: "🇬🇧" }, + { name: "Pays-Bas", code: "NL", flag: "🇳🇱" }, + { name: "Gabon", code: "GA", flag: "🇬🇦" }, + { name: "Ghana", code: "GH", flag: "🇬🇭" }, + { name: "Guinée", code: "GN", flag: "🇬🇳" }, + { name: "Mali", code: "ML", flag: "🇲🇱" }, + { name: "Maroc", code: "MA", flag: "🇲🇦" }, + { name: "Niger", code: "NE", flag: "🇳🇪" }, + { name: "Nigéria", code: "NG", flag: "🇳🇬" }, + { name: "Sénégal", code: "SN", flag: "🇸🇳" }, + { name: "Togo", code: "TG", flag: "🇹🇬" } +]; + main() .catch((e) => { - console.error('❌ Seeding failed:'); console.error(e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); + await pool.end(); }); diff --git a/types.ts b/types.ts index 71402cb..fdbc9db 100644 --- a/types.ts +++ b/types.ts @@ -96,11 +96,13 @@ export interface Offer { export interface BlogPost { id: string; title: string; + slug?: string; excerpt: string; content: string; author: string; date: string; imageUrl: string; + tags?: string[]; } export enum InterviewType { @@ -134,4 +136,14 @@ export interface Rating { user?: User; createdAt: string; } - +export interface Event { + id: string; + slug?: string; + title: string; + description: string; + date: string; + location: string; + thumbnailUrl: string; + link?: string; + tags?: string[]; +}