Maj taxonomies
Some checks failed
Build and Push App / build (push) Failing after 11m28s

maj url photo
+ page 404
+ gestion programmation d'article
This commit is contained in:
2026-05-03 21:22:12 +02:00
parent 0e72867d4c
commit 5f421b418e
41 changed files with 1946 additions and 735 deletions

View File

@@ -3,6 +3,7 @@
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
import { generateSlug } from "@/lib/utils";
import { ContentStatus } from "@prisma/client";
export async function createBlogPost(data: {
title: string;
@@ -14,6 +15,8 @@ export async function createBlogPost(data: {
tags?: string[];
metaTitle?: string;
metaDescription?: string;
publishedAt?: Date;
status?: ContentStatus;
}) {
try {
const slug = data.slug || generateSlug(data.title);
@@ -23,10 +26,12 @@ export async function createBlogPost(data: {
...data,
slug,
tags: data.tags || [],
publishedAt: data.publishedAt || new Date(),
status: data.status || ContentStatus.PUBLISHED,
},
});
revalidatePath("/blog");
return { success: true, data: post };
return { success: true, id: post.id };
} catch (error) {
console.error("Failed to create blog post:", error);
return { success: false, error: "Erreur lors de la création de l'article" };
@@ -43,6 +48,8 @@ export async function updateBlogPost(id: string, data: {
tags?: string[];
metaTitle?: string;
metaDescription?: string;
publishedAt?: Date;
status?: ContentStatus;
}) {
try {
const slug = data.slug || generateSlug(data.title);
@@ -56,7 +63,7 @@ export async function updateBlogPost(id: string, data: {
},
});
revalidatePath("/blog");
return { success: true, data: post };
return { success: true, id: post.id };
} catch (error) {
console.error("Failed to update blog post:", error);
return { success: false, error: "Erreur lors de la mise à jour" };

View File

@@ -3,6 +3,7 @@
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
import { generateSlug } from "@/lib/utils";
import { ContentStatus } from "@prisma/client";
export async function createEvent(data: {
title: string;
@@ -15,6 +16,8 @@ export async function createEvent(data: {
tags?: string[];
metaTitle?: string;
metaDescription?: string;
publishedAt?: Date;
status?: ContentStatus;
}) {
try {
const slug = data.slug || generateSlug(data.title);
@@ -24,6 +27,8 @@ export async function createEvent(data: {
...data,
slug,
tags: data.tags || [],
publishedAt: data.publishedAt || new Date(),
status: data.status || ContentStatus.PUBLISHED,
},
});
revalidatePath("/blog");
@@ -46,6 +51,8 @@ export async function updateEvent(id: string, data: {
tags?: string[];
metaTitle?: string;
metaDescription?: string;
publishedAt?: Date;
status?: ContentStatus;
}) {
try {
const slug = data.slug || generateSlug(data.title);

View File

@@ -2,7 +2,7 @@
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
import { InterviewType } from "@prisma/client";
import { InterviewType, ContentStatus } from "@prisma/client";
import { generateSlug } from "@/lib/utils";
export async function createInterview(data: {
@@ -20,6 +20,8 @@ export async function createInterview(data: {
tags?: string[];
metaTitle?: string;
metaDescription?: string;
publishedAt?: Date;
status?: ContentStatus;
}) {
try {
const slug = data.slug || generateSlug(data.title);
@@ -29,6 +31,8 @@ export async function createInterview(data: {
...data,
slug,
tags: data.tags || [],
publishedAt: data.publishedAt || new Date(),
status: data.status || ContentStatus.PUBLISHED,
},
});
revalidatePath("/blog");
@@ -54,6 +58,8 @@ export async function updateInterview(id: string, data: {
tags?: string[];
metaTitle?: string;
metaDescription?: string;
publishedAt?: Date;
status?: ContentStatus;
}) {
try {
const slug = data.slug || generateSlug(data.title);

View File

@@ -0,0 +1,138 @@
'use server';
import { prisma } from '@/lib/prisma';
import { revalidatePath } from 'next/cache';
export async function getAllTags() {
try {
const [blogPosts, interviews, events] = await Promise.all([
prisma.blogPost.findMany({ select: { tags: true } }),
prisma.interview.findMany({ select: { tags: true } }),
prisma.event.findMany({ select: { tags: true } }),
]);
const allTags = new Set<string>();
blogPosts.forEach(p => p.tags.forEach(t => allTags.add(t)));
interviews.forEach(i => i.tags.forEach(t => allTags.add(t)));
events.forEach(e => e.tags.forEach(t => allTags.add(t)));
return Array.from(allTags).sort();
} catch (error) {
console.error('Error fetching tags:', error);
return [];
}
}
export async function deleteTagGlobally(tagName: string) {
try {
// This is expensive as we need to update all records
// In a real app, a Tag model would be better
const [blogPosts, interviews, events] = await Promise.all([
prisma.blogPost.findMany({ where: { tags: { has: tagName } } }),
prisma.interview.findMany({ where: { tags: { has: tagName } } }),
prisma.event.findMany({ where: { tags: { has: tagName } } }),
]);
await Promise.all([
...blogPosts.map(p => prisma.blogPost.update({
where: { id: p.id },
data: { tags: { set: p.tags.filter(t => t !== tagName) } }
})),
...interviews.map(i => prisma.interview.update({
where: { id: i.id },
data: { tags: { set: i.tags.filter(t => t !== tagName) } }
})),
...events.map(e => prisma.event.update({
where: { id: e.id },
data: { tags: { set: e.tags.filter(t => t !== tagName) } }
}))
]);
revalidatePath('/taxonomies');
return { success: true };
} catch (error) {
console.error('Error deleting tag globally:', error);
return { success: false, error: 'Erreur lors de la suppression du tag' };
}
}
export async function renameTagGlobally(oldName: string, newName: string) {
try {
const cleanNewName = newName.trim().toLowerCase();
if (!cleanNewName) return { success: false, error: 'Nouveau nom invalide' };
const [blogPosts, interviews, events] = await Promise.all([
prisma.blogPost.findMany({ where: { tags: { has: oldName } } }),
prisma.interview.findMany({ where: { tags: { has: oldName } } }),
prisma.event.findMany({ where: { tags: { has: oldName } } }),
]);
await Promise.all([
...blogPosts.map(p => prisma.blogPost.update({
where: { id: p.id },
data: { tags: { set: p.tags.map(t => t === oldName ? cleanNewName : t) } }
})),
...interviews.map(i => prisma.interview.update({
where: { id: i.id },
data: { tags: { set: i.tags.map(t => t === oldName ? cleanNewName : t) } }
})),
...events.map(e => prisma.event.update({
where: { id: e.id },
data: { tags: { set: e.tags.map(t => t === oldName ? cleanNewName : t) } }
}))
]);
revalidatePath('/taxonomies');
return { success: true };
} catch (error) {
console.error('Error renaming tag globally:', error);
return { success: false, error: 'Erreur lors du renommage du tag' };
}
}
export async function getTagUsage(tagName: string) {
try {
const [blogPosts, interviews, events] = await Promise.all([
prisma.blogPost.findMany({
where: { tags: { has: tagName } },
select: { id: true, title: true, status: true, author: true }
}),
prisma.interview.findMany({
where: { tags: { has: tagName } },
select: { id: true, title: true, status: true, guestName: true }
}),
prisma.event.findMany({
where: { tags: { has: tagName } },
select: { id: true, title: true, status: true, location: true }
}),
]);
return {
success: true,
data: {
blogPosts: blogPosts.map(p => ({
id: p.id,
title: p.title,
status: p.status,
type: 'blog'
})),
interviews: interviews.map(i => ({
id: i.id,
title: i.title,
status: i.status,
type: 'interview'
})),
events: events.map(e => ({
id: e.id,
title: e.title,
status: e.status,
type: 'event'
})),
}
};
} catch (error) {
console.error('Error fetching tag usage:', error);
return { success: false, error: 'Erreur lors de la récupération de l\'usage' };
}
}

View File

@@ -0,0 +1,42 @@
'use server';
import { writeFile, mkdir } from 'fs/promises';
import { join } from 'path';
export async function uploadImage(formData: FormData) {
try {
const file = formData.get('file') as File;
if (!file) {
return { success: false, error: 'Aucun fichier fourni' };
}
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);
// Create a unique filename
const fileExtension = file.name.split('.').pop();
const fileName = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}.${fileExtension}`;
// Path for the ROOT public/uploads (where the main site will look)
const rootPublicDir = join(process.cwd(), '..', 'public', 'uploads');
// Path for the ADMIN public/uploads (where the admin app can see it for preview)
const adminPublicDir = join(process.cwd(), 'public', 'uploads');
// Ensure both directories exist
await mkdir(rootPublicDir, { recursive: true });
await mkdir(adminPublicDir, { recursive: true });
// Save to both
await writeFile(join(rootPublicDir, fileName), buffer);
await writeFile(join(adminPublicDir, fileName), buffer);
// Return the relative URL
return {
success: true,
url: `/uploads/${fileName}`
};
} catch (error) {
console.error('Error during image upload:', error);
return { success: false, error: 'Erreur lors de l\'upload de l\'image' };
}
}