diff --git a/app/api/centrale/all/route.ts b/app/api/centrale/all/route.ts
deleted file mode 100644
index 9f65c6a7..00000000
--- a/app/api/centrale/all/route.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { NextResponse } from 'next/server';
-import { getServerSession } from 'next-auth';
-import { authOptions } from "@/app/api/auth/options";
-import { prisma } from '@/lib/prisma';
-import { getPublicUrl } from '@/lib/s3';
-import { S3_CONFIG } from '@/lib/s3';
-
-// Helper function to check authentication
-async function checkAuth(request: Request) {
- const session = await getServerSession(authOptions);
- if (!session?.user?.id) {
- console.error('Unauthorized access attempt:', {
- url: request.url,
- method: request.method,
- headers: Object.fromEntries(request.headers)
- });
- return { authorized: false, userId: null };
- }
- return { authorized: true, userId: session.user.id };
-}
-
-// GET endpoint to list all missions (not filtered by user)
-export async function GET(request: Request) {
- try {
- const { authorized, userId } = await checkAuth(request);
- if (!authorized || !userId) {
- return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
- }
-
- const { searchParams } = new URL(request.url);
- const limit = Number(searchParams.get('limit') || '100'); // Default to 100 for "all"
- const offset = Number(searchParams.get('offset') || '0');
- const search = searchParams.get('search');
-
- // Build query conditions
- const where: any = {};
-
- // Add search filter if provided
- if (search) {
- where.OR = [
- { name: { contains: search, mode: 'insensitive' } },
- { intention: { contains: search, mode: 'insensitive' } }
- ];
- }
-
- // Get all missions with basic info (no user filtering)
- const missions = await prisma.mission.findMany({
- where,
- skip: offset,
- take: limit,
- orderBy: { createdAt: 'desc' },
- select: {
- id: true,
- name: true,
- logo: true,
- oddScope: true,
- niveau: true,
- missionType: true,
- projection: true,
- participation: true,
- services: true,
- intention: true,
- createdAt: true,
- creator: {
- select: {
- id: true,
- email: true
- }
- },
- missionUsers: {
- select: {
- id: true,
- role: true,
- user: {
- select: {
- id: true,
- email: true
- }
- }
- }
- }
- }
- });
-
- // Get total count
- const totalCount = await prisma.mission.count({ where });
-
- // Transform logo paths to public URLs
- const missionsWithPublicUrls = missions.map(mission => ({
- ...mission,
- logo: mission.logo ? `/api/missions/image/${mission.logo}` : null
- }));
-
- return NextResponse.json({
- missions: missionsWithPublicUrls,
- pagination: {
- total: totalCount,
- offset,
- limit
- }
- });
- } catch (error) {
- console.error('Error listing all missions:', error);
- return NextResponse.json({
- error: 'Internal server error',
- details: error instanceof Error ? error.message : String(error)
- }, { status: 500 });
- }
-}
\ No newline at end of file
diff --git a/app/api/centrale/route.ts b/app/api/centrale/route.ts
deleted file mode 100644
index 66c100e2..00000000
--- a/app/api/centrale/route.ts
+++ /dev/null
@@ -1,257 +0,0 @@
-import { NextResponse } from 'next/server';
-import { getServerSession } from 'next-auth';
-import { authOptions } from "@/app/api/auth/options";
-import { prisma } from '@/lib/prisma';
-import { getPublicUrl } from '@/lib/s3';
-import { S3_CONFIG } from '@/lib/s3';
-import { IntegrationService } from '@/lib/services/integration-service';
-
-// Helper function to check authentication
-async function checkAuth(request: Request) {
- const session = await getServerSession(authOptions);
- if (!session?.user?.id) {
- console.error('Unauthorized access attempt:', {
- url: request.url,
- method: request.method,
- headers: Object.fromEntries(request.headers)
- });
- return { authorized: false, userId: null };
- }
- return { authorized: true, userId: session.user.id };
-}
-
-// GET endpoint to list missions with filters
-export async function GET(request: Request) {
- try {
- const { authorized, userId } = await checkAuth(request);
- if (!authorized || !userId) {
- return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
- }
-
- const { searchParams } = new URL(request.url);
- const limit = Number(searchParams.get('limit') || '10');
- const offset = Number(searchParams.get('offset') || '0');
- const search = searchParams.get('search');
-
- // Build query conditions
- const where: any = {};
-
- // Add search filter if provided
- if (search) {
- where.OR = [
- { name: { contains: search, mode: 'insensitive' } },
- { intention: { contains: search, mode: 'insensitive' } }
- ];
- }
-
- // Get missions with basic info
- const missions = await (prisma as any).mission.findMany({
- where,
- skip: offset,
- take: limit,
- orderBy: { createdAt: 'desc' },
- select: {
- id: true,
- name: true,
- logo: true,
- oddScope: true,
- niveau: true,
- missionType: true,
- projection: true,
- participation: true,
- services: true,
- intention: true,
- createdAt: true,
- creator: {
- select: {
- id: true,
- email: true
- }
- },
- missionUsers: {
- select: {
- id: true,
- role: true,
- user: {
- select: {
- id: true,
- email: true
- }
- }
- }
- }
- }
- });
-
- // Get total count
- const totalCount = await (prisma as any).mission.count({ where });
-
- // Transform logo paths to public URLs
- const missionsWithPublicUrls = missions.map((mission: any) => ({
- ...mission,
- logo: mission.logo ? `/api/missions/image/${mission.logo}` : null
- }));
-
- return NextResponse.json({
- missions: missionsWithPublicUrls,
- pagination: {
- total: totalCount,
- offset,
- limit
- }
- });
- } catch (error) {
- console.error('Error listing missions:', error);
- return NextResponse.json({
- error: 'Internal server error',
- details: error instanceof Error ? error.message : String(error)
- }, { status: 500 });
- }
-}
-
-// POST endpoint to create a new mission
-export async function POST(request: Request) {
- try {
- const { authorized, userId } = await checkAuth(request);
- if (!authorized || !userId) {
- return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
- }
-
- // Parse the request body
- const body = await request.json();
- const {
- name,
- logo,
- oddScope,
- niveau,
- intention,
- missionType,
- donneurDOrdre,
- projection,
- services,
- participation,
- profils,
- guardians,
- volunteers
- } = body;
-
- // Validate required fields
- if (!name || !niveau || !intention || !missionType || !donneurDOrdre || !projection) {
- return NextResponse.json({
- error: 'Missing required fields',
- required: {
- name: true,
- niveau: true,
- intention: true,
- missionType: true,
- donneurDOrdre: true,
- projection: true
- },
- received: {
- name: !!name,
- niveau: !!niveau,
- intention: !!intention,
- missionType: !!missionType,
- donneurDOrdre: !!donneurDOrdre,
- projection: !!projection
- }
- }, { status: 400 });
- }
-
- // Wrap the mission creation and integration in a transaction
- const result = await prisma.$transaction(async (tx: any) => {
- // Create the mission
- const mission = await tx.mission.create({
- data: {
- name,
- logo,
- oddScope: oddScope || [],
- niveau,
- intention,
- missionType,
- donneurDOrdre,
- projection,
- services: services || [],
- participation,
- profils: profils || [],
- creatorId: userId
- }
- });
-
- // Add guardians if provided
- if (guardians) {
- const guardianRoles = ['gardien-temps', 'gardien-parole', 'gardien-memoire'];
- const guardianEntries = Object.entries(guardians)
- .filter(([role, userId]) => guardianRoles.includes(role) && userId)
- .map(([role, userId]) => ({
- role,
- userId: userId as string,
- missionId: mission.id
- }));
-
- if (guardianEntries.length > 0) {
- await tx.missionUser.createMany({
- data: guardianEntries
- });
- }
- }
-
- // Add volunteers if provided
- if (volunteers && volunteers.length > 0) {
- const volunteerEntries = volunteers.map((userId: string) => ({
- role: 'volontaire',
- userId,
- missionId: mission.id
- }));
-
- await tx.missionUser.createMany({
- data: volunteerEntries
- });
- }
-
- return mission;
- });
-
- try {
- // Initialize external integrations after transaction completes
- const integrationService = new IntegrationService();
- const integrationResult = await integrationService.setupIntegrationsForMission(result.id);
-
- if (!integrationResult.success) {
- // If integration failed, the mission was already deleted in the integration service
- return NextResponse.json({
- error: 'Failed to set up external services',
- details: integrationResult.error
- }, { status: 500 });
- }
-
- return NextResponse.json({
- success: true,
- mission: {
- id: result.id,
- name: result.name,
- createdAt: result.createdAt
- },
- integrations: {
- status: 'success',
- data: integrationResult.data
- }
- });
- } catch (integrationError) {
- // If there's any unhandled error, delete the mission and report failure
- console.error('Integration error:', integrationError);
- await (prisma as any).mission.delete({ where: { id: result.id } });
-
- return NextResponse.json({
- error: 'Failed to set up external services',
- details: integrationError instanceof Error ? integrationError.message : String(integrationError)
- }, { status: 500 });
- }
- } catch (error) {
- console.error('Error creating mission:', error);
- return NextResponse.json({
- error: 'Internal server error',
- details: error instanceof Error ? error.message : String(error)
- }, { status: 500 });
- }
-}
\ No newline at end of file
diff --git a/app/centrale/[missionId]/edit/page.tsx b/app/centrale/[missionId]/edit/page.tsx
deleted file mode 100644
index 8dd05227..00000000
--- a/app/centrale/[missionId]/edit/page.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-"use client";
-
-import { useState, useEffect } from "react";
-import { Button } from "@/components/ui/button";
-import { useToast } from "@/components/ui/use-toast";
-import { useParams, useRouter } from "next/navigation";
-
-export default function EditMissionPage() {
- const [loading, setLoading] = useState(true);
- const { toast } = useToast();
- const params = useParams();
- const router = useRouter();
- const missionId = params.missionId as string;
-
- useEffect(() => {
- toast({
- title: "Fonctionnalité en développement",
- description: "L'édition de mission sera bientôt disponible.",
- variant: "default",
- });
-
- setLoading(false);
- }, [toast]);
-
- return (
-
-
-
Modifier la mission
-
- {loading ? (
-
- ) : (
-
-
- La fonctionnalité d'édition de mission est en cours de développement et sera disponible prochainement.
-
-
-
-
-
-
-
-
- )}
-
-
- );
-}
\ No newline at end of file
diff --git a/app/centrale/[missionId]/page.tsx b/app/centrale/[missionId]/page.tsx
deleted file mode 100644
index 79f907e9..00000000
--- a/app/centrale/[missionId]/page.tsx
+++ /dev/null
@@ -1,422 +0,0 @@
-"use client";
-
-import { useState, useEffect } from "react";
-import { Button } from "@/components/ui/button";
-import { FileIcon, Calendar, Eye, MapPin, Users, Clock, ThumbsUp, Languages, BarChart, Edit, Trash2 } from "lucide-react";
-import { useToast } from "@/components/ui/use-toast";
-import { useParams, useRouter } from "next/navigation";
-
-// Define types for mission details
-interface User {
- id: string;
- email: string;
-}
-
-interface Attachment {
- id: string;
- filename: string;
- filePath: string;
- fileType: string;
- fileSize: number;
- publicUrl: string;
- createdAt: string;
-}
-
-interface Mission {
- id: string;
- name: string;
- logo?: string | null;
- logoUrl?: string | null;
- oddScope: string[];
- niveau: string;
- missionType: string;
- projection: string;
- intention?: string;
- donneurDOrdre?: string;
- participation?: string;
- services?: string[];
- profils?: string[];
- attachments?: Attachment[];
- createdAt: string;
- creator: User;
- missionUsers: any[];
-}
-
-export default function MissionDetailPage() {
- const [mission, setMission] = useState(null);
- const [loading, setLoading] = useState(true);
- const [deleting, setDeleting] = useState(false);
- const { toast } = useToast();
- const params = useParams();
- const router = useRouter();
- const missionId = params.missionId as string;
-
- // Fetch mission details
- useEffect(() => {
- const fetchMissionDetails = async () => {
- try {
- setLoading(true);
- const response = await fetch(`/api/missions/${missionId}`);
- if (!response.ok) {
- throw new Error('Failed to fetch mission details');
- }
- const data = await response.json();
- console.log("Mission details:", data);
- setMission(data);
- } catch (error) {
- console.error('Error fetching mission details:', error);
- toast({
- title: "Erreur",
- description: "Impossible de charger les détails de la mission",
- variant: "destructive",
- });
- } finally {
- setLoading(false);
- }
- };
-
- if (missionId) {
- fetchMissionDetails();
- }
- }, [missionId, toast]);
-
- // Helper function to format date
- const formatDate = (dateString: string) => {
- const date = new Date(dateString);
- return date.toLocaleDateString('fr-FR', {
- day: '2-digit',
- month: 'long',
- year: 'numeric'
- });
- };
-
- // Helper functions to get labels
- const getMissionTypeLabel = (type: string) => {
- switch(type) {
- case 'remote': return 'À distance';
- case 'onsite': return 'Sur site';
- case 'hybrid': return 'Hybride';
- default: return type;
- }
- };
-
- const getDurationLabel = (projection: string) => {
- switch(projection) {
- case 'short': return '< 1 mois';
- case 'medium': return '1-3 mois';
- case 'long': return '> 3 mois';
- default: return projection;
- }
- };
-
- const getNiveauLabel = (niveau: string) => {
- switch(niveau) {
- case 'a': return 'Apprentissage';
- case 'b': return 'Basique';
- case 'c': return 'Complexe';
- case 's': return 'Spécial';
- default: return niveau;
- }
- };
-
- // Function to get odd info
- const getODDInfo = (oddScope: string[]) => {
- const oddCode = oddScope && oddScope.length > 0
- ? oddScope[0]
- : null;
-
- // Extract number from odd code (e.g., "odd-3" -> "3")
- const oddNumber = oddCode ? oddCode.replace('odd-', '') : null;
-
- return {
- number: oddNumber,
- label: oddNumber ? `ODD ${oddNumber}` : "Non catégorisé",
- iconPath: oddNumber ? `/F SDG Icons 2019 WEB/F-WEB-Goal-${oddNumber.padStart(2, '0')}.png` : ""
- };
- };
-
- // Handle edit mission
- const handleEditMission = () => {
- router.push(`/missions/${missionId}/edit`);
- };
-
- // Handle delete mission
- const handleDeleteMission = async () => {
- if (!confirm("Êtes-vous sûr de vouloir supprimer cette mission ? Cette action est irréversible.")) {
- return;
- }
-
- try {
- setDeleting(true);
- const response = await fetch(`/api/missions/${missionId}`, {
- method: 'DELETE',
- });
-
- if (!response.ok) {
- throw new Error('Failed to delete mission');
- }
-
- toast({
- title: "Mission supprimée",
- description: "La mission a été supprimée avec succès",
- });
-
- // Redirect back to missions list
- router.push('/missions');
- } catch (error) {
- console.error('Error deleting mission:', error);
- toast({
- title: "Erreur",
- description: "Impossible de supprimer la mission",
- variant: "destructive",
- });
- } finally {
- setDeleting(false);
- }
- };
-
- // Loading state
- if (loading) {
- return (
-
- );
- }
-
- // Error state if mission not found
- if (!mission) {
- return (
-
-
-
Mission non trouvée
-
Cette mission n'existe pas ou a été supprimée.
-
-
-
- );
- }
-
- const oddInfo = getODDInfo(mission.oddScope);
-
- return (
-
- {/* Header */}
-
-
-
-
{mission.name}
-
-
-
- {formatDate(mission.createdAt)}
-
-
-
- {Math.floor(Math.random() * 100) + 1} Views
-
-
-
-
- {/* Display logo instead of Participate button */}
-
- {mission.logoUrl ? (
-

{
- console.log("Logo failed to load:", mission.logoUrl);
- // Show placeholder on error
- (e.currentTarget as HTMLImageElement).style.display = 'none';
- const parent = e.currentTarget.parentElement;
- if (parent) {
- parent.classList.add('bg-gray-100');
- parent.classList.add('flex');
- parent.classList.add('items-center');
- parent.classList.add('justify-center');
- parent.innerHTML = `
${mission.name.slice(0, 2).toUpperCase()}`;
- }
- }}
- />
- ) : (
-
- {mission.name.slice(0, 2).toUpperCase()}
-
- )}
-
-
-
-
- {/* Info Grid */}
-
-
-
-
-
-
-
Type de mission
-
{getMissionTypeLabel(mission.missionType)}
-
-
-
-
-
-
-
-
-
Donneur d'ordre
-
{mission.donneurDOrdre || "Non spécifié"}
-
-
-
-
-
-
-
-
-
Durée
-
{getDurationLabel(mission.projection)}
-
-
-
-
-
-
-
-
-
Niveau
-
{getNiveauLabel(mission.niveau)}
-
-
-
-
-
-
-
-
-
Participation
-
{mission.participation || "Non spécifié"}
-
-
-
- {oddInfo.number && (
-
-
-

-
-
-
Objectif
-
Développement durable
-
-
- )}
-
-
- {/* Project Description */}
-
-
Description de la mission
-
- {mission.intention || "Aucune description disponible pour cette mission."}
-
-
-
- {/* Attachments Section */}
- {mission.attachments && mission.attachments.length > 0 && (
-
- )}
-
- {/* Skills Required Section */}
- {mission.profils && mission.profils.length > 0 && (
-
-
Profils recherchés
-
- {mission.profils.map((profil, index) => (
-
- {profil}
-
- ))}
-
-
- )}
-
- {/* Services Section */}
- {mission.services && mission.services.length > 0 && (
-
-
Services
-
- {mission.services.map((service, index) => (
-
- {service}
-
- ))}
-
-
- )}
-
- {/* Action Buttons */}
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/app/centrale/layout.tsx b/app/centrale/layout.tsx
deleted file mode 100644
index aac9d26b..00000000
--- a/app/centrale/layout.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-"use client";
-
-import React from "react";
-import Link from "next/link";
-import { usePathname } from "next/navigation";
-
-export default function MissionsLayout({
- children,
-}: {
- children: React.ReactNode;
-}) {
- const pathname = usePathname();
-
- return (
-
-
- {/* Sidebar with light pink background */}
-
- {/* Title section */}
-
-
CAP
-
Centre d'Administration et de Pilotage
-
-
- {/* Navigation links */}
-
-
-
- {/* Main content - white background */}
-
- {children}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/app/centrale/new/page.tsx b/app/centrale/new/page.tsx
deleted file mode 100644
index 28340e08..00000000
--- a/app/centrale/new/page.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import { MissionsAdminPanel } from "@/components/missions/missions-admin-panel";
-import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
-import Link from "next/link";
-
-export default function NewMissionPage() {
- return (
-
-
-
-
-
-
- Missions
-
-
-
-
- Poster une Mission
-
-
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/app/centrale/page.tsx b/app/centrale/page.tsx
deleted file mode 100644
index c4a2e185..00000000
--- a/app/centrale/page.tsx
+++ /dev/null
@@ -1,311 +0,0 @@
-"use client";
-
-import { useState, useEffect } from "react";
-import { Search } from "lucide-react";
-import { Input } from "@/components/ui/input";
-import Link from "next/link";
-import { Button } from "@/components/ui/button";
-import { useToast } from "@/components/ui/use-toast";
-import { getPublicUrl } from "@/lib/s3";
-
-// Define Mission interface
-interface User {
- id: string;
- email: string;
-}
-
-interface MissionUser {
- id: string;
- role: string;
- user: User;
-}
-
-interface Mission {
- id: string;
- name: string;
- logo?: string;
- oddScope: string[];
- niveau: string;
- missionType: string;
- projection: string;
- participation?: string;
- services?: string[];
- createdAt: string;
- creator: User;
- missionUsers: MissionUser[];
- intention?: string;
-}
-
-export default function MissionsPage() {
- const [searchTerm, setSearchTerm] = useState("");
- const [missions, setMissions] = useState([]);
- const [loading, setLoading] = useState(true);
- const { toast } = useToast();
-
- // Fetch missions from API
- useEffect(() => {
- const fetchMissions = async () => {
- try {
- setLoading(true);
- const response = await fetch('/api/missions');
- if (!response.ok) {
- throw new Error('Failed to fetch missions');
- }
- const data = await response.json();
- // Debug log to check mission data structure including intention
- console.log("Mission data with intention:", data.missions);
- setMissions(data.missions || []);
- } catch (error) {
- console.error('Error fetching missions:', error);
- toast({
- title: "Erreur",
- description: "Impossible de charger les missions",
- variant: "destructive",
- });
- } finally {
- setLoading(false);
- }
- };
-
- fetchMissions();
- }, []);
-
- // Filter missions based on search term
- const filteredMissions = missions.filter(mission =>
- mission.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
- mission.niveau.toLowerCase().includes(searchTerm.toLowerCase()) ||
- mission.missionType.toLowerCase().includes(searchTerm.toLowerCase()) ||
- mission.oddScope.some(scope => scope.toLowerCase().includes(searchTerm.toLowerCase()))
- );
-
- // Function to format date
- const formatDate = (dateString: string) => {
- const date = new Date(dateString);
- return date.toLocaleDateString('fr-FR', {
- day: '2-digit',
- month: '2-digit',
- year: 'numeric'
- });
- };
-
- // Function to get mission category and icon
- const getODDInfo = (mission: Mission) => {
- const oddCode = mission.oddScope && mission.oddScope.length > 0
- ? mission.oddScope[0]
- : null;
-
- // Extract number from odd code (e.g., "odd-3" -> "3")
- const oddNumber = oddCode ? oddCode.replace('odd-', '') : null;
-
- return {
- number: oddNumber,
- label: oddNumber ? `ODD ${oddNumber}` : "Non catégorisé",
- iconPath: oddNumber ? `/F SDG Icons 2019 WEB/F-WEB-Goal-${oddNumber.padStart(2, '0')}.png` : ""
- };
- };
-
- // Function to get appropriate badge color based on niveau
- const getNiveauBadgeColor = (niveau: string) => {
- switch(niveau) {
- case 'a': return 'bg-green-100 text-green-800';
- case 'b': return 'bg-blue-100 text-blue-800';
- case 'c': return 'bg-purple-100 text-purple-800';
- case 's': return 'bg-amber-100 text-amber-800';
- default: return 'bg-gray-100 text-gray-800';
- }
- };
-
- // Function to get full niveau label
- const getNiveauLabel = (niveau: string) => {
- switch(niveau) {
- case 'a': return 'A';
- case 'b': return 'B';
- case 'c': return 'C';
- case 's': return 'S';
- default: return niveau.toUpperCase();
- }
- };
-
- // Function to get mission type label
- const getMissionTypeLabel = (type: string) => {
- switch(type) {
- case 'remote': return 'À distance';
- case 'onsite': return 'Sur site';
- case 'hybrid': return 'Hybride';
- default: return type;
- }
- };
-
- // Function to get participation label
- const getParticipationLabel = (participation: string | null | undefined) => {
- console.log("Participation value:", participation); // Debug log
- if (!participation) return 'Non spécifié';
- switch(participation) {
- case 'volontaire': return 'Volontaire';
- case 'cooptation': return 'Cooptation';
- default: return participation;
- }
- };
-
- // Function to get mission duration
- const getDuration = (projection: string) => {
- switch(projection) {
- case 'short': return '< 1 mois';
- case 'medium': return '1-3 mois';
- case 'long': return '> 3 mois';
- default: return projection;
- }
- };
-
- return (
-
-
-
-
Gérez vos missions et opportunités de bénévolat
-
-
- setSearchTerm(e.target.value)}
- />
-
-
-
-
-
- {loading ? (
-
- ) : filteredMissions.length > 0 ? (
-
- {/* @ts-ignore */}
- {(() => {
- // Debug: Log all mission logos to see what URLs are being used
- console.log("All mission logos:", filteredMissions.map(m => ({
- id: m.id,
- name: m.name,
- logo: m.logo
- })));
- return filteredMissions.map((mission) => {
- const oddInfo = getODDInfo(mission);
- const niveauColor = getNiveauBadgeColor(mission.niveau);
-
- return (
-
- {/* Card Header with Name and Level */}
-
-
{mission.name}
-
- {/* ODD scope icon moved next to level badge */}
- {oddInfo.number && (
-
-

{
- // Fallback if image fails to load
- (e.target as HTMLImageElement).style.display = 'none';
- }}
- />
-
- )}
-
- {getNiveauLabel(mission.niveau)}
-
-
-
-
- {/* Centered Logo */}
-
-
- {mission.logo ? (
-

{
- console.log("Logo failed to load:", mission.logo);
- console.log("Full URL attempted:", mission.logo);
- // If the image fails to load, show the fallback
- (e.currentTarget as HTMLImageElement).style.display = 'none';
- // Show the fallback div
- const fallbackDiv = e.currentTarget.parentElement?.querySelector('.logo-fallback');
- if (fallbackDiv) {
- (fallbackDiv as HTMLElement).style.display = 'flex';
- }
- }}
- />
- ) : null}
-
- {mission.name.slice(0, 2).toUpperCase()}
-
-
-
-
- {/* Card Content - Services and Description */}
-
- {/* Services section */}
- {mission.services && mission.services.length > 0 && (
-
-
Services:
-
- {mission.services.map(service => (
-
- {service}
-
- ))}
-
-
- )}
-
- {/* Description text (can be added from mission data) */}
-
- {mission.intention ?
- (mission.intention.substring(0, 100) + (mission.intention.length > 100 ? '...' : '')) :
- 'Pas de description disponible.'}
-
-
-
- {/* Card Footer */}
-
-
- Créée le {formatDate(mission.createdAt)}
-
-
-
-
-
-
-
- );
- });
- })()}
-
- ) : (
-
-
-
-
-
Aucune mission trouvée
-
- Créez votre première mission pour commencer à organiser vos projets et inviter des participants.
-
-
-
-
-
- )}
-
-
- );
-}
\ No newline at end of file