feat: jira and synchro
This commit is contained in:
38
src/app/api/jira/logs/route.ts
Normal file
38
src/app/api/jira/logs/route.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { prisma } from '@/services/database';
|
||||
|
||||
/**
|
||||
* Route GET /api/jira/logs
|
||||
* Récupère les logs de synchronisation Jira
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const limit = parseInt(searchParams.get('limit') || '10');
|
||||
|
||||
const logs = await prisma.syncLog.findMany({
|
||||
where: {
|
||||
source: 'jira'
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
take: limit
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
data: logs
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur récupération logs Jira:', error);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Erreur lors de la récupération des logs',
|
||||
details: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
96
src/app/api/jira/sync/route.ts
Normal file
96
src/app/api/jira/sync/route.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { createJiraService } from '@/services/jira';
|
||||
|
||||
/**
|
||||
* Route POST /api/jira/sync
|
||||
* Synchronise les tickets Jira avec la base locale
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const jiraService = createJiraService();
|
||||
|
||||
if (!jiraService) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Configuration Jira manquante. Vérifiez les variables d\'environnement JIRA_BASE_URL, JIRA_EMAIL et JIRA_API_TOKEN.' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
console.log('🔄 Début de la synchronisation Jira...');
|
||||
|
||||
// Tester la connexion d'abord
|
||||
const connectionOk = await jiraService.testConnection();
|
||||
if (!connectionOk) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Impossible de se connecter à Jira. Vérifiez la configuration.' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Effectuer la synchronisation
|
||||
const result = await jiraService.syncTasks();
|
||||
|
||||
if (result.success) {
|
||||
return NextResponse.json({
|
||||
message: 'Synchronisation Jira terminée avec succès',
|
||||
data: result
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Synchronisation Jira terminée avec des erreurs',
|
||||
data: result
|
||||
},
|
||||
{ status: 207 } // Multi-Status
|
||||
);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur API sync Jira:', error);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Erreur interne lors de la synchronisation',
|
||||
details: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route GET /api/jira/sync
|
||||
* Teste la connexion Jira
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const jiraService = createJiraService();
|
||||
|
||||
if (!jiraService) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
connected: false,
|
||||
message: 'Configuration Jira manquante'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const connected = await jiraService.testConnection();
|
||||
|
||||
return NextResponse.json({
|
||||
connected,
|
||||
message: connected ? 'Connexion Jira OK' : 'Impossible de se connecter à Jira'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur test connexion Jira:', error);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
connected: false,
|
||||
message: 'Erreur lors du test de connexion',
|
||||
details: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { Card } from '@/components/ui/Card';
|
||||
import { DailyCalendar } from '@/components/daily/DailyCalendar';
|
||||
import { DailySection } from '@/components/daily/DailySection';
|
||||
import { dailyClient } from '@/clients/daily-client';
|
||||
import { SimpleHeader } from '@/components/ui/SimpleHeader';
|
||||
import { Header } from '@/components/ui/Header';
|
||||
|
||||
interface DailyPageClientProps {
|
||||
initialDailyView?: DailyView;
|
||||
@@ -151,7 +151,7 @@ export function DailyPageClient({
|
||||
return (
|
||||
<div className="min-h-screen bg-[var(--background)]">
|
||||
{/* Header uniforme */}
|
||||
<SimpleHeader
|
||||
<Header
|
||||
title="TowerControl"
|
||||
subtitle="Daily - Gestion quotidienne"
|
||||
syncing={saving}
|
||||
|
||||
15
src/app/settings/page.tsx
Normal file
15
src/app/settings/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { SettingsPageClient } from '@/components/settings/SettingsPageClient';
|
||||
import { getConfig } from '@/lib/config';
|
||||
|
||||
// Force dynamic rendering (no static generation)
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default async function SettingsPage() {
|
||||
const config = getConfig();
|
||||
|
||||
return (
|
||||
<SettingsPageClient
|
||||
config={config}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { CreateTagData } from '@/clients/tags-client';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { TagForm } from '@/components/forms/TagForm';
|
||||
import { SimpleHeader } from '@/components/ui/SimpleHeader';
|
||||
import { Header } from '@/components/ui/Header';
|
||||
|
||||
interface TagsPageClientProps {
|
||||
initialTags: Tag[];
|
||||
@@ -83,7 +83,7 @@ export function TagsPageClient({ initialTags }: TagsPageClientProps) {
|
||||
return (
|
||||
<div className="min-h-screen bg-[var(--background)]">
|
||||
{/* Header uniforme */}
|
||||
<SimpleHeader
|
||||
<Header
|
||||
title="TowerControl"
|
||||
subtitle="Tags - Gestion des étiquettes"
|
||||
syncing={loading}
|
||||
|
||||
@@ -60,7 +60,12 @@ export function TasksProvider({ children, initialTasks, initialStats, initialTag
|
||||
sortBy: preferences.kanbanFilters.sortBy || createSortKey('priority', 'desc'),
|
||||
compactView: preferences.viewPreferences.compactView || false,
|
||||
swimlanesByTags: preferences.viewPreferences.swimlanesByTags || false,
|
||||
swimlanesMode: preferences.viewPreferences.swimlanesMode || 'tags'
|
||||
swimlanesMode: preferences.viewPreferences.swimlanesMode || 'tags',
|
||||
// Filtres Jira
|
||||
showJiraOnly: preferences.kanbanFilters.showJiraOnly || false,
|
||||
hideJiraTasks: preferences.kanbanFilters.hideJiraTasks || false,
|
||||
jiraProjects: preferences.kanbanFilters.jiraProjects || [],
|
||||
jiraTypes: preferences.kanbanFilters.jiraTypes || []
|
||||
}), [preferences]);
|
||||
|
||||
// Fonction pour mettre à jour les filtres avec persistance
|
||||
@@ -71,7 +76,12 @@ export function TasksProvider({ children, initialTasks, initialStats, initialTag
|
||||
tags: newFilters.tags,
|
||||
priorities: newFilters.priorities,
|
||||
showCompleted: newFilters.showCompleted,
|
||||
sortBy: newFilters.sortBy
|
||||
sortBy: newFilters.sortBy,
|
||||
// Filtres Jira
|
||||
showJiraOnly: newFilters.showJiraOnly,
|
||||
hideJiraTasks: newFilters.hideJiraTasks,
|
||||
jiraProjects: newFilters.jiraProjects,
|
||||
jiraTypes: newFilters.jiraTypes
|
||||
};
|
||||
|
||||
const viewPreferenceUpdates = {
|
||||
@@ -146,6 +156,27 @@ export function TasksProvider({ children, initialTasks, initialStats, initialTag
|
||||
);
|
||||
}
|
||||
|
||||
// Filtres spécifiques Jira
|
||||
if (kanbanFilters.showJiraOnly) {
|
||||
filtered = filtered.filter(task => task.source === 'jira');
|
||||
} else if (kanbanFilters.hideJiraTasks) {
|
||||
filtered = filtered.filter(task => task.source !== 'jira');
|
||||
}
|
||||
|
||||
// Filtre par projets Jira
|
||||
if (kanbanFilters.jiraProjects?.length) {
|
||||
filtered = filtered.filter(task =>
|
||||
task.source !== 'jira' || kanbanFilters.jiraProjects!.includes(task.jiraProject || '')
|
||||
);
|
||||
}
|
||||
|
||||
// Filtre par types Jira
|
||||
if (kanbanFilters.jiraTypes?.length) {
|
||||
filtered = filtered.filter(task =>
|
||||
task.source !== 'jira' || kanbanFilters.jiraTypes!.includes(task.jiraType || '')
|
||||
);
|
||||
}
|
||||
|
||||
// Tri des tâches
|
||||
if (kanbanFilters.sortBy) {
|
||||
const sortOption = getSortOption(kanbanFilters.sortBy);
|
||||
|
||||
Reference in New Issue
Block a user