missions carrousse
This commit is contained in:
parent
e6a62fa090
commit
fa167bac9b
@ -27,6 +27,8 @@ interface Mission {
|
||||
niveau: string;
|
||||
missionType: string;
|
||||
projection: string;
|
||||
participation?: string;
|
||||
services?: string[];
|
||||
createdAt: string;
|
||||
creator: User;
|
||||
missionUsers: MissionUser[];
|
||||
@ -82,11 +84,62 @@ export default function MissionsPage() {
|
||||
});
|
||||
};
|
||||
|
||||
// Function to get mission category
|
||||
const getCategory = (mission: Mission) => {
|
||||
return mission.oddScope && mission.oddScope.length > 0
|
||||
? mission.oddScope[0].replace('odd-', 'ODD ')
|
||||
: "Non catégorisé";
|
||||
// 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 ? `/images/odd/odd-${oddNumber}.png` : "/images/odd/default.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- Apprentissage';
|
||||
case 'b': return 'B- Basique';
|
||||
case 'c': return 'C- Complexe';
|
||||
case 's': return 'S- Spécial';
|
||||
default: return niveau;
|
||||
}
|
||||
};
|
||||
|
||||
// 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) => {
|
||||
if (!participation) return 'Non spécifié';
|
||||
switch(participation) {
|
||||
case 'volontaire': return 'Volontaire';
|
||||
case 'cooptation': return 'Cooptation';
|
||||
default: return participation;
|
||||
}
|
||||
};
|
||||
|
||||
// Function to get mission duration
|
||||
@ -101,74 +154,141 @@ export default function MissionsPage() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full w-full bg-white">
|
||||
<div className="bg-white border-b border-gray-100 py-2 px-4">
|
||||
<div className="bg-white border-b border-gray-100 py-3 px-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-gray-700 text-sm font-medium">Gérez vos missions et opportunités de bénévolat</h1>
|
||||
<h1 className="text-gray-800 text-base font-medium">Gérez vos missions et opportunités de bénévolat</h1>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-3.5 w-3.5 text-gray-500" />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500" />
|
||||
<Input
|
||||
placeholder="Rechercher une mission..."
|
||||
className="h-8 pl-7 pr-2 py-1 text-xs bg-white border-gray-200 rounded-sm w-48"
|
||||
className="h-9 pl-9 pr-3 py-2 text-sm bg-white border-gray-200 rounded-md w-60"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Link href="/missions/new">
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 h-9 rounded-md text-sm">
|
||||
Nouvelle mission
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-auto bg-white p-4">
|
||||
<div className="flex-1 overflow-auto bg-gray-50 p-6">
|
||||
{loading ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-gray-900"></div>
|
||||
<div className="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-blue-600"></div>
|
||||
</div>
|
||||
) : filteredMissions.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{filteredMissions.map((mission) => (
|
||||
<div key={mission.id} className="bg-white shadow-sm border border-gray-200 overflow-hidden h-full rounded-sm">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{filteredMissions.map((mission) => {
|
||||
const oddInfo = getODDInfo(mission);
|
||||
const niveauColor = getNiveauBadgeColor(mission.niveau);
|
||||
|
||||
return (
|
||||
<div key={mission.id} className="bg-white shadow-sm hover:shadow-md transition-shadow duration-200 border border-gray-200 overflow-hidden h-full rounded-lg">
|
||||
<div className="p-0">
|
||||
<div className="flex justify-between items-center px-4 pt-4 pb-2">
|
||||
<span className="bg-gray-100 px-2 py-1 text-xs rounded-sm font-normal text-gray-800">
|
||||
{getCategory(mission)}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 px-2 py-1 border border-gray-200 rounded-sm font-normal">
|
||||
{getDuration(mission.projection)}
|
||||
{/* Card Header with Logo and ODD */}
|
||||
<div className="flex items-start px-5 pt-5 pb-3">
|
||||
<div className="flex-shrink-0 mr-4 w-16 h-16 relative">
|
||||
{mission.logo ? (
|
||||
<img
|
||||
src={mission.logo}
|
||||
alt={mission.name}
|
||||
className="w-full h-full object-cover rounded-md border border-gray-200"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center bg-gray-100 rounded-md border border-gray-200 text-gray-400">
|
||||
{mission.name.slice(0, 2).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
{oddInfo.number && (
|
||||
<div className="flex items-center bg-gray-100 px-2 py-1 rounded-md">
|
||||
<img
|
||||
src={oddInfo.iconPath}
|
||||
alt={oddInfo.label}
|
||||
className="w-5 h-5 mr-1"
|
||||
onError={(e) => {
|
||||
// Fallback if image fails to load
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
<span className="text-xs font-medium text-gray-800">{oddInfo.label}</span>
|
||||
</div>
|
||||
)}
|
||||
<span className={`text-xs font-medium px-2 py-1 rounded-md ${niveauColor}`}>
|
||||
{getNiveauLabel(mission.niveau)}
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="text-base font-medium text-gray-900 line-clamp-2">{mission.name}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-4 pb-4">
|
||||
<h2 className="text-base font-medium mb-2">{mission.name}</h2>
|
||||
<p className="text-sm text-gray-600 mb-4">Type: {mission.missionType}</p>
|
||||
{/* Card Content */}
|
||||
<div className="px-5 pb-4">
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<span className="font-medium mr-2">Type:</span>
|
||||
{getMissionTypeLabel(mission.missionType)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1 mb-4">
|
||||
{mission.missionUsers.slice(0, 3).map(missionUser => (
|
||||
<span key={missionUser.id} className="bg-gray-100 text-gray-700 px-2 py-1 rounded-sm text-xs">
|
||||
{missionUser.role === 'gardien-temps' ? 'Gardien du Temps' :
|
||||
missionUser.role === 'gardien-parole' ? 'Gardien de la Parole' :
|
||||
missionUser.role === 'gardien-memoire' ? 'Gardien de la Mémoire' : 'Volontaire'}
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<span className="font-medium mr-2">Durée:</span>
|
||||
{getDuration(mission.projection)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<span className="font-medium mr-2">Participation:</span>
|
||||
{getParticipationLabel(mission.participation)}
|
||||
</div>
|
||||
|
||||
{mission.services && mission.services.length > 0 && (
|
||||
<div className="mt-3">
|
||||
<span className="text-sm font-medium text-gray-700 block mb-2">Services:</span>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{mission.services.map(service => (
|
||||
<span key={service} className="bg-blue-50 text-blue-700 px-2 py-1 rounded-md text-xs font-medium">
|
||||
{service}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center px-4 py-2 border-t border-gray-100">
|
||||
{/* Card Footer */}
|
||||
<div className="flex justify-between items-center px-5 py-3 border-t border-gray-100 bg-gray-50">
|
||||
<span className="text-xs text-gray-500">
|
||||
Créée le {formatDate(mission.createdAt)}
|
||||
</span>
|
||||
<Link href={`/missions/${mission.id}`}>
|
||||
<Button className="bg-black text-white text-xs px-3 py-1 h-6 rounded-sm">
|
||||
<Button className="bg-black hover:bg-gray-800 text-white text-xs px-3 py-1 h-7 rounded-md">
|
||||
Voir détails
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center p-8">
|
||||
<p className="text-gray-500">Aucune mission trouvée</p>
|
||||
<div className="text-center py-16 px-6 bg-white rounded-lg border border-gray-200 shadow-sm">
|
||||
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Search className="h-8 w-8 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">Aucune mission trouvée</h3>
|
||||
<p className="text-gray-500 mb-6 max-w-md mx-auto">
|
||||
Créez votre première mission pour commencer à organiser vos projets et inviter des participants.
|
||||
</p>
|
||||
<Link href="/missions/new">
|
||||
<Button className="mt-4 bg-blue-600 text-white">
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2">
|
||||
Créer une nouvelle mission
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user