feat: add maxSyncPeriod configuration to TFS settings

- Introduced maxSyncPeriod option in TfsConfigForm for user-defined synchronization duration.
- Updated TfsService to filter pull requests based on the configured maxSyncPeriod.
- Enhanced TfsPullRequest type to include 'rejected' status for better PR management.
- Set default maxSyncPeriod to '90d' in user preferences and TFS configuration.
This commit is contained in:
Julien Froidefond
2025-10-03 09:06:24 +02:00
parent 7900ba3b73
commit c84ee86ed4
4 changed files with 92 additions and 15 deletions

View File

@@ -15,6 +15,7 @@ export function TfsConfigForm() {
personalAccessToken: '',
repositories: [],
ignoredRepositories: [],
maxSyncPeriod: '90d',
});
const [isPending, startTransition] = useTransition();
const [message, setMessage] = useState<{
@@ -94,13 +95,14 @@ export function TfsConfigForm() {
startTransition(async () => {
setMessage(null);
// Réinitialiser la config
const resetConfig = {
const resetConfig: TfsConfig = {
enabled: false,
organizationUrl: '',
projectName: '',
personalAccessToken: '',
repositories: [],
ignoredRepositories: [],
maxSyncPeriod: '90d',
};
const result = await saveTfsConfig(resetConfig);
@@ -275,6 +277,14 @@ export function TfsConfigForm() {
<span className="text-xs">Aucun</span>
)}
</div>
<div>
<span className="text-[var(--muted-foreground)]">
Période max de sync:
</span>{' '}
<code className="bg-[var(--background)] px-2 py-1 rounded text-xs">
{config?.maxSyncPeriod || '90d'}
</code>
</div>
</div>
</div>
)}
@@ -475,6 +485,29 @@ export function TfsConfigForm() {
</div>
)}
</div>
<div>
<label className="block text-sm font-medium mb-2">
Période maximale de synchronisation
</label>
<select
value={config.maxSyncPeriod || '90d'}
onChange={(e) => updateConfig('maxSyncPeriod', e.target.value)}
className="w-full px-3 py-2 border border-[var(--border)] rounded bg-[var(--background)] text-[var(--foreground)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] focus:border-transparent"
>
<option value="7d">7 jours</option>
<option value="30d">30 jours</option>
<option value="90d">90 jours (recommandé)</option>
<option value="180d">180 jours</option>
<option value="1y">1 an</option>
<option value="2y">2 ans</option>
<option value="3y">3 ans</option>
</select>
<p className="text-xs text-[var(--muted-foreground)] mt-1">
Définit la période maximale pour récupérer les Pull Requests.
Les PRs plus anciennes seront ignorées lors de la synchronisation.
</p>
</div>
</>
)}

View File

@@ -221,7 +221,7 @@ export interface TfsPullRequest {
pullRequestId: number;
title: string;
description?: string;
status: 'active' | 'completed' | 'abandoned';
status: 'active' | 'completed' | 'abandoned' | 'rejected';
createdBy: {
displayName: string;
uniqueName: string; // email

View File

@@ -53,6 +53,7 @@ const DEFAULT_PREFERENCES: UserPreferences = {
personalAccessToken: '',
repositories: [],
ignoredRepositories: [],
maxSyncPeriod: '90d', // Par défaut 90 jours
},
tfsAutoSync: false,
tfsSyncInterval: 'daily',

View File

@@ -16,6 +16,7 @@ export interface TfsConfig {
personalAccessToken?: string;
repositories?: string[]; // Liste des repos à surveiller
ignoredRepositories?: string[]; // Liste des repos à ignorer
maxSyncPeriod?: '7d' | '30d' | '90d' | '180d' | '1y' | '2y' | '3y'; // Période maximale de synchronisation
}
export interface TfsSyncAction {
@@ -375,10 +376,10 @@ export class TfsService {
/**
* Filtre les PRs par statut pertinent
* - Garde toutes les PRs actives créées dans les 90 derniers jours
* - Garde toutes les PRs actives créées dans la période configurée
* - Garde les PRs completed récentes (moins de 30 jours)
* - Exclut les PRs abandoned
* - Exclut les PRs trop anciennes
* - Exclut les PRs abandoned et rejected
* - Exclut les PRs trop anciennes selon la configuration
* - Exclut les PRs automatiques (Renovate, etc.)
*/
private filterByRelevantStatus(
@@ -387,6 +388,11 @@ export class TfsService {
const now = new Date();
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
// Calculer la période maximale selon la configuration
const maxSyncPeriod = this.config.maxSyncPeriod || '90d';
const maxSyncPeriodMs = this.getMaxSyncPeriodMs(maxSyncPeriod);
const maxSyncDate = new Date(now.getTime() - maxSyncPeriodMs);
return pullRequests.filter((pr) => {
// Exclure les PRs automatiques (Renovate, Dependabot, etc.)
if (this.isAutomaticPR(pr)) {
@@ -396,20 +402,20 @@ export class TfsService {
return false;
}
// Filtrer d'abord par âge - exclure les PRs trop anciennes
// const createdDate = parseDate(pr.creationDate);
// if (createdDate < ninetyDaysAgo) {
// console.log(
// `🗺 PR ${pr.pullRequestId} (${pr.title}): Trop ancienne (${formatDateForDisplay(createdDate)}) - EXCLUE`
// );
// return false;
// }
// Filtrer par âge selon la configuration
const createdDate = parseDate(pr.creationDate);
if (createdDate < maxSyncDate) {
console.log(
`🗺 PR ${pr.pullRequestId} (${pr.title}): Trop ancienne (${formatDateForDisplay(createdDate)}, limite: ${maxSyncPeriod}) - EXCLUE`
);
return false;
}
switch (pr.status.toLowerCase()) {
case 'active':
// PRs actives récentes
// PRs actives dans la période configurée
console.log(
`✅ PR ${pr.pullRequestId} (${pr.title}): Active récente - INCLUSE`
`✅ PR ${pr.pullRequestId} (${pr.title}): Active récente (${maxSyncPeriod}) - INCLUSE`
);
return true;
@@ -426,15 +432,52 @@ export class TfsService {
case 'abandoned':
// PRs abandonnées ne sont pas pertinentes
console.log(
`❌ PR ${pr.pullRequestId} (${pr.title}): Abandonnée - EXCLUE`
);
return false;
case 'rejected':
// PRs rejetées ne sont pas pertinentes
console.log(
`❌ PR ${pr.pullRequestId} (${pr.title}): Rejetée - EXCLUE`
);
return false;
default:
// Statut inconnu, on l'inclut par précaution
console.log(
`⚠️ PR ${pr.pullRequestId} (${pr.title}): Statut inconnu "${pr.status}" - INCLUSE par précaution`
);
return true;
}
});
}
/**
* Convertit une période de synchronisation en millisecondes
*/
private getMaxSyncPeriodMs(period: string): number {
switch (period) {
case '7d':
return 7 * 24 * 60 * 60 * 1000; // 7 jours
case '30d':
return 30 * 24 * 60 * 60 * 1000; // 30 jours
case '90d':
return 90 * 24 * 60 * 60 * 1000; // 90 jours
case '180d':
return 180 * 24 * 60 * 60 * 1000; // 180 jours
case '1y':
return 365 * 24 * 60 * 60 * 1000; // 1 an
case '2y':
return 2 * 365 * 24 * 60 * 60 * 1000; // 2 ans
case '3y':
return 3 * 365 * 24 * 60 * 60 * 1000; // 3 ans
default:
return 90 * 24 * 60 * 60 * 1000; // Par défaut 90 jours
}
}
/**
* Détermine si une PR est automatique (bot, renovate, dependabot, etc.)
*/