Missions Corrections

This commit is contained in:
alma 2026-01-13 23:20:18 +01:00
parent 98f0f8532a
commit 5e3289f0e1
3 changed files with 219 additions and 26 deletions

View File

@ -54,7 +54,13 @@ export async function POST(request: Request) {
logger.debug('Received mission-created data', {
hasMissionId: !!body.missionId,
hasName: !!body.name,
hasCreatorId: !!body.creatorId
hasCreatorId: !!body.creatorId,
gitRepoUrl: body.gitRepoUrl,
leantimeProjectId: body.leantimeProjectId,
documentationCollectionId: body.documentationCollectionId,
rocketchatChannelId: body.rocketchatChannelId,
gitRepoUrlType: typeof body.gitRepoUrl,
rocketchatChannelIdType: typeof body.rocketchatChannelId,
});
// Validation des champs requis
@ -125,28 +131,71 @@ export async function POST(request: Request) {
rocketChatChannelId?: string | null;
} = {};
// Helper function to check if a value is valid
const isValidValue = (value: any, type?: 'gitea' | 'outline' | 'rocketchat' | 'leantime'): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
if (trimmed === '' || trimmed === '0' || trimmed === 'null' || trimmed === 'undefined') return false;
// Additional validation based on type
if (type === 'gitea') {
// Reject Outline collection paths (they start with /collection/)
if (trimmed.startsWith('/collection/')) return false;
// Reject if it doesn't look like a URL or repo path
if (!trimmed.includes('http') && !trimmed.includes('/') && trimmed.length < 3) return false;
}
if (type === 'outline') {
// Outline IDs should be UUIDs or paths
if (trimmed.startsWith('/collection/')) return true; // This is valid for Outline
}
if (type === 'rocketchat') {
// RocketChat channel IDs should be alphanumeric strings
if (trimmed.length < 3) return false;
}
return true;
}
if (typeof value === 'number') return value !== 0;
return true;
};
// Mapper les champs N8N vers notre schéma Prisma
// Vérifier que les valeurs ne sont pas des chaînes vides
// Vérifier que les valeurs ne sont pas des chaînes vides, "0", "null", "undefined", etc.
// ET vérifier qu'elles correspondent au bon type (pas de mélange Gitea/Outline)
if (body.gitRepoUrl !== undefined) {
updateData.giteaRepositoryUrl = (body.gitRepoUrl && body.gitRepoUrl.trim() !== '') ? body.gitRepoUrl : null;
logger.debug('Updating giteaRepositoryUrl', { hasUrl: !!updateData.giteaRepositoryUrl, value: body.gitRepoUrl });
const isValid = isValidValue(body.gitRepoUrl, 'gitea');
updateData.giteaRepositoryUrl = isValid ? body.gitRepoUrl : null;
logger.debug('Updating giteaRepositoryUrl', {
hasUrl: !!updateData.giteaRepositoryUrl,
value: body.gitRepoUrl,
isValid,
isOutlinePath: typeof body.gitRepoUrl === 'string' && body.gitRepoUrl.trim().startsWith('/collection/')
});
}
if (body.leantimeProjectId !== undefined) {
// N8N peut retourner un number, on le convertit en string
const projectId = body.leantimeProjectId ? String(body.leantimeProjectId).trim() : '';
updateData.leantimeProjectId = projectId !== '' ? projectId : null;
logger.debug('Updating leantimeProjectId', { hasId: !!updateData.leantimeProjectId, value: body.leantimeProjectId });
const isValid = isValidValue(projectId, 'leantime');
updateData.leantimeProjectId = isValid ? projectId : null;
logger.debug('Updating leantimeProjectId', { hasId: !!updateData.leantimeProjectId, value: body.leantimeProjectId, isValid });
}
if (body.documentationCollectionId !== undefined) {
updateData.outlineCollectionId = (body.documentationCollectionId && body.documentationCollectionId.trim() !== '') ? body.documentationCollectionId : null;
logger.debug('Updating outlineCollectionId', { hasId: !!updateData.outlineCollectionId, value: body.documentationCollectionId });
const isValid = isValidValue(body.documentationCollectionId, 'outline');
updateData.outlineCollectionId = isValid ? body.documentationCollectionId : null;
logger.debug('Updating outlineCollectionId', { hasId: !!updateData.outlineCollectionId, value: body.documentationCollectionId, isValid });
}
if (body.rocketchatChannelId !== undefined) {
updateData.rocketChatChannelId = (body.rocketchatChannelId && body.rocketchatChannelId.trim() !== '') ? body.rocketchatChannelId : null;
logger.debug('Updating rocketChatChannelId', { hasId: !!updateData.rocketChatChannelId, value: body.rocketchatChannelId });
const isValid = isValidValue(body.rocketchatChannelId, 'rocketchat');
updateData.rocketChatChannelId = isValid ? body.rocketchatChannelId : null;
logger.debug('Updating rocketChatChannelId', {
hasId: !!updateData.rocketChatChannelId,
value: body.rocketchatChannelId,
isValid,
valueType: typeof body.rocketchatChannelId
});
}
// Vérifier qu'il y a au moins un champ à mettre à jour

View File

@ -384,14 +384,50 @@ export default function MissionTabDetailPage() {
)}
{/* Ressources Métiers */}
{((mission.rocketChatChannelId && mission.rocketChatChannelId.trim() !== '') ||
(mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '') ||
(mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '') ||
(mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '')) && (
{(() => {
// Helper function to check if a value is valid (not null, not undefined, not empty string, not "0", not "null", not "undefined")
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
if (typeof value === 'number') return value !== 0;
return true;
};
const hasRocketChat = isValid(mission.rocketChatChannelId);
const hasOutline = isValid(mission.outlineCollectionId);
const hasGitea = isValid(mission.giteaRepositoryUrl);
const hasLeantime = isValid(mission.leantimeProjectId);
console.log('Ressources Métiers check (mission-tab):', {
hasRocketChat,
hasOutline,
hasGitea,
hasLeantime,
rocketChatValue: mission.rocketChatChannelId,
outlineValue: mission.outlineCollectionId,
giteaValue: mission.giteaRepositoryUrl,
leantimeValue: mission.leantimeProjectId,
});
return hasRocketChat || hasOutline || hasGitea || hasLeantime;
})() && (
<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.trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
return true;
};
return isValid(mission.rocketChatChannelId);
})() && (() => {
// 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')
@ -416,7 +452,17 @@ export default function MissionTabDetailPage() {
);
})()}
{mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
return true;
};
return isValid(mission.outlineCollectionId);
})() && (() => {
// 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')
@ -441,7 +487,17 @@ export default function MissionTabDetailPage() {
);
})()}
{mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
return true;
};
return isValid(mission.giteaRepositoryUrl);
})() && (() => {
// Use mission name (sanitized) as repo name
const repoName = sanitizeMissionName(mission.name);
@ -472,7 +528,18 @@ export default function MissionTabDetailPage() {
);
})()}
{mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
if (typeof value === 'number') return value !== 0;
return true;
};
return isValid(mission.leantimeProjectId);
})() && (() => {
// 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

@ -94,6 +94,16 @@ export default function MissionDetailPage() {
}
const data = await response.json();
console.log("Mission details:", data);
console.log("Integration IDs:", {
rocketChatChannelId: data.rocketChatChannelId,
outlineCollectionId: data.outlineCollectionId,
giteaRepositoryUrl: data.giteaRepositoryUrl,
leantimeProjectId: data.leantimeProjectId,
rocketChatType: typeof data.rocketChatChannelId,
outlineType: typeof data.outlineCollectionId,
giteaType: typeof data.giteaRepositoryUrl,
leantimeType: typeof data.leantimeProjectId,
});
setMission(data);
setEditedPlan(data.actionPlan || "");
} catch (error) {
@ -604,14 +614,50 @@ export default function MissionDetailPage() {
)}
{/* Ressources Métiers */}
{((mission.rocketChatChannelId && mission.rocketChatChannelId.trim() !== '') ||
(mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '') ||
(mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '') ||
(mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '')) && (
{(() => {
// Helper function to check if a value is valid (not null, not undefined, not empty string, not "0", not "null", not "undefined")
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
if (typeof value === 'number') return value !== 0;
return true;
};
const hasRocketChat = isValid(mission.rocketChatChannelId);
const hasOutline = isValid(mission.outlineCollectionId);
const hasGitea = isValid(mission.giteaRepositoryUrl);
const hasLeantime = isValid(mission.leantimeProjectId);
console.log('Ressources Métiers check:', {
hasRocketChat,
hasOutline,
hasGitea,
hasLeantime,
rocketChatValue: mission.rocketChatChannelId,
outlineValue: mission.outlineCollectionId,
giteaValue: mission.giteaRepositoryUrl,
leantimeValue: mission.leantimeProjectId,
});
return hasRocketChat || hasOutline || hasGitea || hasLeantime;
})() && (
<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.trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
return true;
};
return isValid(mission.rocketChatChannelId);
})() && (() => {
// 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')
@ -636,7 +682,17 @@ export default function MissionDetailPage() {
);
})()}
{mission.outlineCollectionId && mission.outlineCollectionId.trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
return true;
};
return isValid(mission.outlineCollectionId);
})() && (() => {
// 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')
@ -661,7 +717,17 @@ export default function MissionDetailPage() {
);
})()}
{mission.giteaRepositoryUrl && mission.giteaRepositoryUrl.trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
return true;
};
return isValid(mission.giteaRepositoryUrl);
})() && (() => {
// Use mission name (sanitized) as repo name
const repoName = sanitizeMissionName(mission.name);
@ -692,7 +758,18 @@ export default function MissionDetailPage() {
);
})()}
{mission.leantimeProjectId && mission.leantimeProjectId.toString().trim() !== '' && (() => {
{(() => {
const isValid = (value: any): boolean => {
if (value === null || value === undefined) return false;
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed !== '' && trimmed !== '0' && trimmed !== 'null' && trimmed !== 'undefined';
}
if (typeof value === 'number') return value !== 0;
return true;
};
return isValid(mission.leantimeProjectId);
})() && (() => {
// 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')