feat: jira config in database

This commit is contained in:
Julien Froidefond
2025-09-17 18:04:13 +02:00
parent 83e48d5972
commit 0223611b3f
10 changed files with 383 additions and 50 deletions

View File

@@ -1,46 +1,100 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/Button';
import { Badge } from '@/components/ui/Badge';
import { AppConfig } from '@/lib/config';
import { useJiraConfig } from '@/hooks/useJiraConfig';
interface JiraConfigFormProps {
config: AppConfig;
}
export function JiraConfigForm({ config }: JiraConfigFormProps) {
export function JiraConfigForm() {
const { config, isLoading: configLoading, saveConfig, deleteConfig } = useJiraConfig();
const [formData, setFormData] = useState({
baseUrl: '',
email: '',
apiToken: ''
});
const [isLoading, setIsLoading] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null);
// Charger les données existantes dans le formulaire
useEffect(() => {
if (config) {
setFormData({
baseUrl: config.baseUrl || '',
email: config.email || '',
apiToken: config.apiToken || ''
});
}
}, [config]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setIsSubmitting(true);
setMessage(null);
try {
// Note: Dans un vrai environnement, ces variables seraient configurées côté serveur
// Pour cette démo, on affiche juste un message informatif
setMessage({
type: 'success',
text: 'Configuration sauvegardée. Redémarrez l&apos;application pour appliquer les changements.'
});
} catch {
const result = await saveConfig(formData);
if (result.success) {
setMessage({
type: 'success',
text: result.message
});
} else {
setMessage({
type: 'error',
text: result.message
});
}
} catch (error) {
setMessage({
type: 'error',
text: 'Erreur lors de la sauvegarde de la configuration'
text: error instanceof Error ? error.message : 'Erreur lors de la sauvegarde de la configuration'
});
} finally {
setIsLoading(false);
setIsSubmitting(false);
}
};
const isJiraConfigured = config.integrations.jira.enabled;
const handleDelete = async () => {
if (!confirm('Êtes-vous sûr de vouloir supprimer la configuration Jira ?')) {
return;
}
setIsSubmitting(true);
setMessage(null);
try {
const result = await deleteConfig();
if (result.success) {
setFormData({
baseUrl: '',
email: '',
apiToken: ''
});
setMessage({
type: 'success',
text: result.message
});
} else {
setMessage({
type: 'error',
text: result.message
});
}
} catch (error) {
setMessage({
type: 'error',
text: error instanceof Error ? error.message : 'Erreur lors de la suppression de la configuration'
});
} finally {
setIsSubmitting(false);
}
};
const isJiraConfigured = config?.enabled && (config?.baseUrl || config?.email);
const isLoading = configLoading || isSubmitting;
return (
<div className="space-y-6">
@@ -67,19 +121,19 @@ export function JiraConfigForm({ config }: JiraConfigFormProps) {
<div>
<span className="text-[var(--muted-foreground)]">URL de base:</span>{' '}
<code className="bg-[var(--background)] px-2 py-1 rounded text-xs">
{config.integrations.jira.baseUrl || 'Non définie'}
{config?.baseUrl || 'Non définie'}
</code>
</div>
<div>
<span className="text-[var(--muted-foreground)]">Email:</span>{' '}
<code className="bg-[var(--background)] px-2 py-1 rounded text-xs">
{config.integrations.jira.email || 'Non défini'}
{config?.email || 'Non défini'}
</code>
</div>
<div>
<span className="text-[var(--muted-foreground)]">Token API:</span>{' '}
<code className="bg-[var(--background)] px-2 py-1 rounded text-xs">
{config.integrations.jira.apiToken ? '••••••••' : 'Non défini'}
{config?.apiToken ? '••••••••' : 'Non défini'}
</code>
</div>
</div>
@@ -147,13 +201,27 @@ export function JiraConfigForm({ config }: JiraConfigFormProps) {
</p>
</div>
<Button
type="submit"
disabled={isLoading}
className="w-full"
>
{isLoading ? 'Sauvegarde...' : 'Sauvegarder la configuration'}
</Button>
<div className="flex gap-3">
<Button
type="submit"
disabled={isLoading}
className="flex-1"
>
{isLoading ? 'Sauvegarde...' : 'Sauvegarder la configuration'}
</Button>
{isJiraConfigured && (
<Button
type="button"
variant="secondary"
onClick={handleDelete}
disabled={isLoading}
className="px-6"
>
Supprimer
</Button>
)}
</div>
</form>
{message && (

View File

@@ -6,13 +6,10 @@ import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import { JiraConfigForm } from '@/components/settings/JiraConfigForm';
import { JiraSync } from '@/components/jira/JiraSync';
import { JiraLogs } from '@/components/jira/JiraLogs';
import { AppConfig } from '@/lib/config';
import { useJiraConfig } from '@/hooks/useJiraConfig';
interface SettingsPageClientProps {
config: AppConfig;
}
export function SettingsPageClient({ config }: SettingsPageClientProps) {
export function SettingsPageClient() {
const { config: jiraConfig } = useJiraConfig();
const [activeTab, setActiveTab] = useState<'general' | 'integrations' | 'advanced'>('general');
const tabs = [
@@ -97,14 +94,14 @@ export function SettingsPageClient({ config }: SettingsPageClientProps) {
</p>
</CardHeader>
<CardContent>
<JiraConfigForm config={config} />
<JiraConfigForm />
</CardContent>
</Card>
</div>
{/* Colonne 2: Actions et Logs */}
<div className="space-y-4">
{config.integrations.jira.enabled && (
{jiraConfig?.enabled && (
<>
<JiraSync />
<JiraLogs />