diff --git a/admin/prisma/schema.prisma b/admin/prisma/schema.prisma
index ce93b4f..abb5f64 100644
--- a/admin/prisma/schema.prisma
+++ b/admin/prisma/schema.prisma
@@ -30,11 +30,11 @@ model User {
businesses Business?
comments Comment[]
conversations ConversationParticipant[]
+ favorites Favorite[]
sentMessages Message[]
reports MessageReport[]
ratings Rating[]
country Country? @relation(fields: [countryId], references: [id])
- favorites Favorite[]
}
model Business {
@@ -68,8 +68,6 @@ model Business {
websiteUrl String?
isSuspended Boolean @default(false)
suspensionReason String?
- metaTitle String?
- metaDescription String?
plan Plan @default(STARTER)
city String?
countryId String?
@@ -78,14 +76,17 @@ model Business {
coverZoom Float? @default(1.0)
suggestedCategory String?
categoryId String?
+ metaDescription String?
+ metaTitle String?
categoryRef BusinessCategory? @relation(fields: [categoryId], references: [id])
country Country? @relation(fields: [countryId], references: [id])
owner User @relation(fields: [ownerId], references: [id])
comments Comment[]
conversations Conversation[]
+ favorites Favorite[]
+ homeSlides HomeSlide[]
offers Offer[]
ratings Rating[]
- favorites Favorite[]
}
model BusinessCategory {
@@ -161,6 +162,21 @@ model BlogPost {
status ContentStatus @default(PUBLISHED)
}
+model HomeSlide {
+ id String @id @default(uuid())
+ type HomeSlideType @default(CUSTOM)
+ businessId String?
+ title String?
+ subtitle String?
+ imageUrl String?
+ linkUrl String?
+ order Int @default(0)
+ isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ business Business? @relation(fields: [businessId], references: [id])
+}
+
model Interview {
id String @id @default(uuid())
title String
@@ -301,6 +317,23 @@ model SiteSetting {
verifyEmailTemplate String @default("
{siteName}
Bonjour {name},
Merci de vous être inscrit sur {siteName}. Pour finaliser la création de votre compte, merci de vérifier votre adresse email en cliquant sur le bouton ci-dessous :
+ {selectedPlan.type === 'POST_EVENT' || selectedPlan.type === 'POST_NEWS'
+ ? "Votre paiement a été validé. Vous pouvez maintenant remplir le formulaire de votre publication."
+ : `Votre service ${selectedPlan.name} a été activé avec succès.`
+ }
+
+ );
+};
+
+export default OneShotPricingSection;
diff --git a/components/dashboard/CreateEventModal.tsx b/components/dashboard/CreateEventModal.tsx
new file mode 100644
index 0000000..8c73bb0
--- /dev/null
+++ b/components/dashboard/CreateEventModal.tsx
@@ -0,0 +1,229 @@
+"use client";
+
+import React, { useState, useTransition } from 'react';
+import { X, Calendar, MapPin, Link as LinkIcon, Image as ImageIcon, AlertTriangle, Loader2 } from 'lucide-react';
+import { createEvent } from '@/app/actions/events';
+import { uploadImage } from '@/app/actions/upload';
+import { toast } from 'react-hot-toast';
+
+interface CreateEventModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+export default function CreateEventModal({ isOpen, onClose }: CreateEventModalProps) {
+ const [isPending, startTransition] = useTransition();
+ const [imagePreview, setImagePreview] = useState(null);
+ const [uploading, setUploading] = useState(false);
+
+ if (!isOpen) return null;
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ const formData = new FormData(e.currentTarget);
+
+ const data = {
+ title: formData.get('title') as string,
+ description: formData.get('description') as string,
+ date: formData.get('date') as string,
+ location: formData.get('location') as string,
+ thumbnailUrl: imagePreview || "https://picsum.photos/800/400?random=event",
+ link: formData.get('link') as string || undefined,
+ };
+
+ if (!data.title || !data.description || !data.date || !data.location) {
+ toast.error("Veuillez remplir tous les champs obligatoires");
+ return;
+ }
+
+ startTransition(async () => {
+ const result = await createEvent(data);
+ if (result.success) {
+ toast.success("Événement soumis avec succès !");
+ onClose();
+ } else {
+ toast.error(result.error || "Une erreur est survenue");
+ }
+ });
+ };
+
+ const handleImageChange = async (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (!file) return;
+
+ setUploading(true);
+ const formData = new FormData();
+ formData.append('file', file);
+
+ const result = await uploadImage(formData);
+ if (result.success && result.url) {
+ setImagePreview(result.url);
+ } else {
+ toast.error(result.error || "Erreur lors de l'upload");
+ }
+ setUploading(false);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
Publier un événement
+
Partagez votre actualité avec la communauté
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/dashboard/CreateNewsModal.tsx b/components/dashboard/CreateNewsModal.tsx
new file mode 100644
index 0000000..d1eef9b
--- /dev/null
+++ b/components/dashboard/CreateNewsModal.tsx
@@ -0,0 +1,216 @@
+"use client";
+
+import React, { useState, useTransition } from 'react';
+import { X, Newspaper, User, Image as ImageIcon, AlertTriangle, Loader2, AlignLeft } from 'lucide-react';
+import { createNews } from '@/app/actions/news';
+import { uploadImage } from '@/app/actions/upload';
+import { toast } from 'react-hot-toast';
+
+interface CreateNewsModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+export default function CreateNewsModal({ isOpen, onClose }: CreateNewsModalProps) {
+ const [isPending, startTransition] = useTransition();
+ const [imagePreview, setImagePreview] = useState(null);
+ const [uploading, setUploading] = useState(false);
+
+ if (!isOpen) return null;
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ const formData = new FormData(e.currentTarget);
+
+ const data = {
+ title: formData.get('title') as string,
+ excerpt: formData.get('excerpt') as string,
+ content: formData.get('content') as string,
+ author: formData.get('author') as string,
+ imageUrl: imagePreview || "https://picsum.photos/800/400?random=news",
+ };
+
+ if (!data.title || !data.excerpt || !data.content || !data.author) {
+ toast.error("Veuillez remplir tous les champs obligatoires");
+ return;
+ }
+
+ startTransition(async () => {
+ const result = await createNews(data);
+ if (result.success) {
+ toast.success("Actualité soumise avec succès !");
+ onClose();
+ } else {
+ toast.error(result.error || "Une erreur est survenue");
+ }
+ });
+ };
+
+ const handleImageChange = async (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (!file) return;
+
+ setUploading(true);
+ const formData = new FormData();
+ formData.append('file', file);
+
+ const result = await uploadImage(formData);
+ if (result.success && result.url) {
+ setImagePreview(result.url);
+ } else {
+ toast.error(result.error || "Erreur lors de l'upload");
+ }
+ setUploading(false);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
Publier une actualité
+
Mettez en avant vos succès et nouveautés
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/lib/prisma.ts b/lib/prisma.ts
index 46f5636..757c49c 100644
--- a/lib/prisma.ts
+++ b/lib/prisma.ts
@@ -10,11 +10,11 @@ function makePrisma() {
return new PrismaClient({ adapter })
}
-const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
+const globalForPrisma = globalThis as unknown as { prisma_v2: PrismaClient }
-// Sync triggered for websiteUrl and privacy fields
-export const prisma = globalForPrisma.prisma || makePrisma()
+// Updated: 2026-05-10T17:21 (Forced reload for OneShotPlan model)
+export const prisma = globalForPrisma.prisma_v2 || makePrisma()
-if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
+if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma_v2 = prisma
export default prisma
diff --git a/package.json b/package.json
index 6dc4a8f..51fc51c 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"all": "concurrently \"npm run dev\" \"npm run dev:admin\"",
"build": "prisma generate && next build && npm run build:admin",
"build:admin": "npm install --prefix admin && prisma generate && npm run build --prefix admin",
+ "generate": "npx prisma generate && cd admin && npx prisma generate && cd ..",
"start": "npx prisma migrate deploy && next start",
"start:admin": "npm run start --prefix admin",
"db:migrate": "prisma migrate dev",
@@ -61,4 +62,4 @@
"postcss": "^8.5.10",
"@hono/node-server": "^1.19.13"
}
-}
+}
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 183f8b5..abb5f64 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -317,6 +317,23 @@ model SiteSetting {
verifyEmailTemplate String @default("
{siteName}
Bonjour {name},
Merci de vous être inscrit sur {siteName}. Pour finaliser la création de votre compte, merci de vérifier votre adresse email en cliquant sur le bouton ci-dessous :