feat: integrate Jira ticket linking in TaskCard

- Added functionality to generate Jira ticket URLs based on user preferences, enhancing task interactivity.
- Updated UserPreferences to include Jira configuration, ensuring seamless integration with Jira settings.
- Refactored TaskCard to conditionally render Jira links, improving user experience when interacting with Jira tasks.
This commit is contained in:
Julien Froidefond
2025-09-17 14:50:32 +02:00
parent b0dc1bec11
commit 21500269e1
3 changed files with 68 additions and 8 deletions

View File

@@ -6,6 +6,7 @@ import { Card } from '@/components/ui/Card';
import { Badge } from '@/components/ui/Badge'; import { Badge } from '@/components/ui/Badge';
import { TagDisplay } from '@/components/ui/TagDisplay'; import { TagDisplay } from '@/components/ui/TagDisplay';
import { useTasksContext } from '@/contexts/TasksContext'; import { useTasksContext } from '@/contexts/TasksContext';
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
import { useDraggable } from '@dnd-kit/core'; import { useDraggable } from '@dnd-kit/core';
import { getPriorityConfig, getPriorityColorHex } from '@/lib/status-config'; import { getPriorityConfig, getPriorityColorHex } from '@/lib/status-config';
@@ -23,6 +24,14 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
const [showTooltip, setShowTooltip] = useState(false); const [showTooltip, setShowTooltip] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null); const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const { tags: availableTags } = useTasksContext(); const { tags: availableTags } = useTasksContext();
const { preferences } = useUserPreferences();
// Helper pour construire l'URL Jira
const getJiraTicketUrl = (jiraKey: string): string => {
const baseUrl = preferences.jiraConfig.baseUrl;
if (!baseUrl || !jiraKey) return '';
return `${baseUrl}/browse/${jiraKey}`;
};
// Configuration du draggable // Configuration du draggable
const { const {
@@ -374,9 +383,29 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{task.source !== 'manual' && task.source && ( {task.source !== 'manual' && task.source && (
<Badge variant="outline" size="sm"> task.source === 'jira' && task.jiraKey ? (
{task.source === 'jira' && task.jiraKey ? task.jiraKey : task.source} preferences.jiraConfig.baseUrl ? (
<a
href={getJiraTicketUrl(task.jiraKey)}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
className="hover:scale-105 transition-transform"
>
<Badge variant="outline" size="sm" className="hover:bg-blue-500/10 hover:border-blue-400/50 cursor-pointer">
{task.jiraKey}
</Badge> </Badge>
</a>
) : (
<Badge variant="outline" size="sm">
{task.jiraKey}
</Badge>
)
) : (
<Badge variant="outline" size="sm">
{task.source}
</Badge>
)
)} )}
{task.jiraProject && ( {task.jiraProject && (

View File

@@ -78,10 +78,17 @@ export interface ColumnVisibility {
[key: string]: TaskStatus[] | undefined; [key: string]: TaskStatus[] | undefined;
} }
export interface JiraConfig {
baseUrl?: string;
email?: string;
enabled: boolean;
}
export interface UserPreferences { export interface UserPreferences {
kanbanFilters: KanbanFilters; kanbanFilters: KanbanFilters;
viewPreferences: ViewPreferences; viewPreferences: ViewPreferences;
columnVisibility: ColumnVisibility; columnVisibility: ColumnVisibility;
jiraConfig: JiraConfig;
} }
// Interface pour les logs de synchronisation // Interface pour les logs de synchronisation

View File

@@ -1,5 +1,6 @@
import { TaskStatus, KanbanFilters, ViewPreferences, ColumnVisibility, UserPreferences } from '@/lib/types'; import { TaskStatus, KanbanFilters, ViewPreferences, ColumnVisibility, UserPreferences, JiraConfig } from '@/lib/types';
import { prisma } from './database'; import { prisma } from './database';
import { getConfig } from '@/lib/config';
// Valeurs par défaut // Valeurs par défaut
const DEFAULT_PREFERENCES: UserPreferences = { const DEFAULT_PREFERENCES: UserPreferences = {
@@ -21,6 +22,9 @@ const DEFAULT_PREFERENCES: UserPreferences = {
}, },
columnVisibility: { columnVisibility: {
hiddenStatuses: [] hiddenStatuses: []
},
jiraConfig: {
enabled: false
} }
}; };
@@ -161,31 +165,51 @@ class UserPreferencesService {
} }
} }
/**
* Récupère la configuration Jira depuis les variables d'environnement
*/
async getJiraConfig(): Promise<JiraConfig> {
try {
const config = getConfig();
return {
baseUrl: config.integrations.jira.baseUrl,
email: config.integrations.jira.email,
enabled: config.integrations.jira.enabled
};
} catch (error) {
console.warn('Erreur lors de la récupération de la config Jira:', error);
return DEFAULT_PREFERENCES.jiraConfig;
}
}
/** /**
* Récupère toutes les préférences utilisateur * Récupère toutes les préférences utilisateur
*/ */
async getAllPreferences(): Promise<UserPreferences> { async getAllPreferences(): Promise<UserPreferences> {
const [kanbanFilters, viewPreferences, columnVisibility] = await Promise.all([ const [kanbanFilters, viewPreferences, columnVisibility, jiraConfig] = await Promise.all([
this.getKanbanFilters(), this.getKanbanFilters(),
this.getViewPreferences(), this.getViewPreferences(),
this.getColumnVisibility() this.getColumnVisibility(),
this.getJiraConfig()
]); ]);
return { return {
kanbanFilters, kanbanFilters,
viewPreferences, viewPreferences,
columnVisibility columnVisibility,
jiraConfig
}; };
} }
/** /**
* Sauvegarde toutes les préférences utilisateur * Sauvegarde toutes les préférences utilisateur (jiraConfig ignorée car elle vient des env vars)
*/ */
async saveAllPreferences(preferences: UserPreferences): Promise<void> { async saveAllPreferences(preferences: UserPreferences): Promise<void> {
await Promise.all([ await Promise.all([
this.saveKanbanFilters(preferences.kanbanFilters), this.saveKanbanFilters(preferences.kanbanFilters),
this.saveViewPreferences(preferences.viewPreferences), this.saveViewPreferences(preferences.viewPreferences),
this.saveColumnVisibility(preferences.columnVisibility) this.saveColumnVisibility(preferences.columnVisibility)
// jiraConfig n'est pas sauvegardée car elle vient des variables d'environnement
]); ]);
} }