From 5a6d9070066e683afff408918782fdc396ca01a8 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Fri, 19 Sep 2025 08:36:55 +0200 Subject: [PATCH] feat: integrate Jira configuration into Header and layout - Added `JiraConfigProvider` to `layout.tsx` for server-side Jira configuration retrieval. - Updated `Header.tsx` to conditionally display a link to the Jira dashboard with the project key if Jira is configured. - Enhanced user experience by integrating Jira settings into the main application layout. --- components/ui/Header.tsx | 10 ++++++++ src/app/layout.tsx | 12 ++++++--- src/contexts/JiraConfigContext.tsx | 40 ++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/contexts/JiraConfigContext.tsx diff --git a/components/ui/Header.tsx b/components/ui/Header.tsx index e4d9066..21cbe11 100644 --- a/components/ui/Header.tsx +++ b/components/ui/Header.tsx @@ -1,6 +1,7 @@ import { Card, CardContent } from '@/components/ui/Card'; import { TaskStats } from '@/lib/types'; import { useTheme } from '@/contexts/ThemeContext'; +import { useJiraConfig } from '@/contexts/JiraConfigContext'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; @@ -13,6 +14,7 @@ interface HeaderProps { export function Header({ title = "TowerControl", subtitle = "Task Management", stats, syncing = false }: HeaderProps) { const { theme, toggleTheme } = useTheme(); + const { isConfigured: isJiraConfigured, config: jiraConfig } = useJiraConfig(); const pathname = usePathname(); // Fonction pour déterminer si un lien est actif @@ -82,6 +84,14 @@ export function Header({ title = "TowerControl", subtitle = "Task Management", s > Tags + {isJiraConfigured && ( + + Jira ({jiraConfig?.projectKey}) + + )} ) { - // Récupérer uniquement le thème côté serveur pour le SSR (optimisé) - const initialTheme = await userPreferencesService.getTheme(); + // Récupérer les données côté serveur pour le SSR + const [initialTheme, jiraConfig] = await Promise.all([ + userPreferencesService.getTheme(), + userPreferencesService.getJiraConfig() + ]); return ( @@ -33,7 +37,9 @@ export default async function RootLayout({ className={`${geistSans.variable} ${geistMono.variable} antialiased`} > - {children} + + {children} + diff --git a/src/contexts/JiraConfigContext.tsx b/src/contexts/JiraConfigContext.tsx new file mode 100644 index 0000000..e68e3c7 --- /dev/null +++ b/src/contexts/JiraConfigContext.tsx @@ -0,0 +1,40 @@ +'use client'; + +import React, { createContext, useContext } from 'react'; +import { JiraConfig } from '@/lib/types'; + +interface JiraConfigContextType { + config: JiraConfig; + isConfigured: boolean; +} + +const JiraConfigContext = createContext(undefined); + +interface JiraConfigProviderProps { + children: React.ReactNode; + config: JiraConfig; +} + +export function JiraConfigProvider({ children, config }: JiraConfigProviderProps) { + // Une config Jira est considérée comme valide si elle a les champs obligatoires + const isConfigured = Boolean( + config.baseUrl && + config.email && + config.apiToken && + config.enabled + ); + + return ( + + {children} + + ); +} + +export function useJiraConfig() { + const context = useContext(JiraConfigContext); + if (context === undefined) { + throw new Error('useJiraConfig must be used within a JiraConfigProvider'); + } + return context; +}