Missions Corrections

This commit is contained in:
alma 2026-01-13 23:10:33 +01:00
parent ed2175c080
commit 98f0f8532a
5 changed files with 61 additions and 34 deletions

View File

@ -126,27 +126,27 @@ export async function POST(request: Request) {
} = {};
// Mapper les champs N8N vers notre schéma Prisma
// Vérifier que les valeurs ne sont pas des chaînes vides
if (body.gitRepoUrl !== undefined) {
updateData.giteaRepositoryUrl = body.gitRepoUrl || null;
logger.debug('Updating giteaRepositoryUrl', { hasUrl: !!body.gitRepoUrl });
updateData.giteaRepositoryUrl = (body.gitRepoUrl && body.gitRepoUrl.trim() !== '') ? body.gitRepoUrl : null;
logger.debug('Updating giteaRepositoryUrl', { hasUrl: !!updateData.giteaRepositoryUrl, value: body.gitRepoUrl });
}
if (body.leantimeProjectId !== undefined) {
// N8N peut retourner un number, on le convertit en string
updateData.leantimeProjectId = body.leantimeProjectId
? String(body.leantimeProjectId)
: null;
logger.debug('Updating leantimeProjectId', { hasId: !!updateData.leantimeProjectId });
const projectId = body.leantimeProjectId ? String(body.leantimeProjectId).trim() : '';
updateData.leantimeProjectId = projectId !== '' ? projectId : null;
logger.debug('Updating leantimeProjectId', { hasId: !!updateData.leantimeProjectId, value: body.leantimeProjectId });
}
if (body.documentationCollectionId !== undefined) {
updateData.outlineCollectionId = body.documentationCollectionId || null;
logger.debug('Updating outlineCollectionId', { hasId: !!updateData.outlineCollectionId });
updateData.outlineCollectionId = (body.documentationCollectionId && body.documentationCollectionId.trim() !== '') ? body.documentationCollectionId : null;
logger.debug('Updating outlineCollectionId', { hasId: !!updateData.outlineCollectionId, value: body.documentationCollectionId });
}
if (body.rocketchatChannelId !== undefined) {
updateData.rocketChatChannelId = body.rocketchatChannelId || null;
logger.debug('Updating rocketChatChannelId', { hasId: !!updateData.rocketChatChannelId });
updateData.rocketChatChannelId = (body.rocketchatChannelId && body.rocketchatChannelId.trim() !== '') ? body.rocketchatChannelId : null;
logger.debug('Updating rocketChatChannelId', { hasId: !!updateData.rocketChatChannelId, value: body.rocketchatChannelId });
}
// Vérifier qu'il y a au moins un champ à mettre à jour

View File

@ -384,11 +384,14 @@ export default function MissionTabDetailPage() {
)}
{/* Ressources Métiers */}
{(mission.rocketChatChannelId || mission.outlineCollectionId || mission.giteaRepositoryUrl || mission.leantimeProjectId) && (
{((mission.rocketChatChannelId && mission.rocketChatChannelId.trim() !== '') ||
(mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '') ||
(mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '') ||
(mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '')) && (
<div className="bg-white rounded-lg shadow-sm border border-gray-100 p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">Ressources Métiers</h2>
<div className="space-y-3">
{mission.rocketChatChannelId && (() => {
{mission.rocketChatChannelId && mission.rocketChatChannelId.trim() !== '' && (() => {
// Build RocketChat URL: parole.slm-lab.net/channel/[channelId]
// If it's already a full URL, use it; otherwise build from ID
const rocketChatUrl = mission.rocketChatChannelId.startsWith('http')
@ -413,7 +416,7 @@ export default function MissionTabDetailPage() {
);
})()}
{mission.outlineCollectionId && (() => {
{mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '' && (() => {
// Build Outline URL: chapitre.slm-lab.net/collection/[collectionId]
// If it's already a full URL, use it; otherwise build from ID
const outlineUrl = mission.outlineCollectionId.startsWith('http')
@ -438,7 +441,7 @@ export default function MissionTabDetailPage() {
);
})()}
{mission.giteaRepositoryUrl && (() => {
{mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '' && (() => {
// Use mission name (sanitized) as repo name
const repoName = sanitizeMissionName(mission.name);
@ -469,7 +472,7 @@ export default function MissionTabDetailPage() {
);
})()}
{mission.leantimeProjectId && (() => {
{mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '' && (() => {
// Build Leantime URL: agilite.slm-lab.net/projects/showProject/[projectId]
// If it's already a full URL, use it; otherwise build from ID
const leantimeUrl = mission.leantimeProjectId.startsWith('http')

View File

@ -18,7 +18,8 @@ import {
Loader2,
FileText,
Lock,
CheckCircle
CheckCircle,
ArrowLeft
} from "lucide-react";
import { useToast } from "@/components/ui/use-toast";
import { useParams, useRouter } from "next/navigation";
@ -396,6 +397,18 @@ export default function MissionDetailPage() {
return (
<div className="bg-gray-50 min-h-screen p-6">
{/* Back Button */}
<div className="mb-4">
<Button
variant="ghost"
className="flex items-center text-gray-600 hover:text-gray-900"
onClick={() => router.push('/missions')}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Retour aux missions
</Button>
</div>
{/* Header with Name and Logo */}
<div className="bg-white rounded-lg shadow-sm border border-gray-100 mb-6 p-6">
<div className="flex justify-between items-start">
@ -591,11 +604,14 @@ export default function MissionDetailPage() {
)}
{/* Ressources Métiers */}
{(mission.rocketChatChannelId || mission.outlineCollectionId || mission.giteaRepositoryUrl || mission.leantimeProjectId) && (
{((mission.rocketChatChannelId && mission.rocketChatChannelId.trim() !== '') ||
(mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '') ||
(mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '') ||
(mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '')) && (
<div className="bg-white rounded-lg shadow-sm border border-gray-100 p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">Ressources Métiers</h2>
<div className="space-y-3">
{mission.rocketChatChannelId && (() => {
{mission.rocketChatChannelId && mission.rocketChatChannelId.trim() !== '' && (() => {
// Build RocketChat URL: parole.slm-lab.net/channel/[channelId]
// If it's already a full URL, use it; otherwise build from ID
const rocketChatUrl = mission.rocketChatChannelId.startsWith('http')
@ -620,7 +636,7 @@ export default function MissionDetailPage() {
);
})()}
{mission.outlineCollectionId && (() => {
{mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '' && (() => {
// Build Outline URL: chapitre.slm-lab.net/collection/[collectionId]
// If it's already a full URL, use it; otherwise build from ID
const outlineUrl = mission.outlineCollectionId.startsWith('http')
@ -645,7 +661,7 @@ export default function MissionDetailPage() {
);
})()}
{mission.giteaRepositoryUrl && (() => {
{mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '' && (() => {
// Use mission name (sanitized) as repo name
const repoName = sanitizeMissionName(mission.name);
@ -676,7 +692,7 @@ export default function MissionDetailPage() {
);
})()}
{mission.leantimeProjectId && (() => {
{mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '' && (() => {
// Build Leantime URL: agilite.slm-lab.net/projects/showProject/[projectId]
// If it's already a full URL, use it; otherwise build from ID
const leantimeUrl = mission.leantimeProjectId.startsWith('http')

View File

@ -769,14 +769,15 @@ export default function EquipePage() {
<td className="px-4 py-3 text-sm text-gray-600">{user.email}</td>
<td className="px-4 py-3">
<div className="flex flex-wrap gap-1">
{(user.roles || []).slice(0, 3).map(role => (
<span key={role} className="px-2 py-0.5 text-xs bg-blue-50 text-blue-700 rounded-full">
{role}
</span>
))}
{(user.roles || []).length > 3 && (
<span className="px-2 py-0.5 text-xs bg-gray-100 text-gray-600 rounded-full">
+{user.roles.length - 3}
{Array.isArray(user.roles) && user.roles.length > 0 ? (
user.roles.map(role => (
<span key={role} className="px-2 py-0.5 text-xs bg-blue-50 text-blue-700 rounded-full">
{role}
</span>
))
) : (
<span className="px-2 py-0.5 text-xs bg-gray-100 text-gray-400 rounded-full">
Aucun rôle
</span>
)}
</div>

View File

@ -481,15 +481,21 @@ export function MissionMembersPanel({
{selectedTab === 'users' ? (
filteredUsers.length > 0 ? (
<div className="divide-y divide-gray-200">
{filteredUsers.map(user => (
{filteredUsers.map(user => {
const displayName = (user.firstName && user.lastName)
? `${user.firstName} ${user.lastName}`
: user.firstName || user.lastName || user.username || user.email || 'Utilisateur';
const initials = (user.firstName?.[0] || "") + (user.lastName?.[0] || "") || user.username?.[0]?.toUpperCase() || user.email?.[0]?.toUpperCase() || "?";
return (
<div key={user.id} className="p-3 hover:bg-gray-50 flex items-center justify-between">
<div className="flex items-center">
<div className="h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 font-medium mr-3">
{user.firstName?.[0] || ""}{user.lastName?.[0] || ""}
{initials}
</div>
<div>
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
<div className="text-sm text-gray-500">{user.email}</div>
<div className="font-medium text-gray-900">{displayName}</div>
<div className="text-sm text-gray-500">{user.email || user.username || ''}</div>
{isUserAssigned(user.id) && (
<div className="flex flex-wrap gap-1 mt-1">
{getUserRoles(user.id).map((role) => (
@ -570,7 +576,8 @@ export function MissionMembersPanel({
)}
</div>
</div>
))}
);
})}
</div>
) : (
<div className="p-4 text-center text-gray-500">