refact & cleanup : Sidebar, Button, card, EmptyState, Loader, ServiceObject, StatusBadge, UptameObject, BackupWidget, SecurityWidget, ServicesWidget, SupportCtaWidget, UptimeWidget, DashboardLayout

This commit is contained in:
LathanDevers
2026-07-29 22:50:04 +02:00
parent 438d739b1a
commit ec900efe3a
42 changed files with 3357 additions and 3966 deletions
+37
View File
@@ -0,0 +1,37 @@
import React from 'react';
import { cn } from '@/utils/utils';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
}
export const Button: React.FC<ButtonProps> = ({
children, variant = 'primary', size = 'md', isLoading, className, disabled, ...props
}) => {
const baseStyles = "inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2";
const variants = {
primary: "bg-blue-900 text-white hover:bg-blue-800 focus:ring-blue-900 shadow-sm",
outline: "border border-slate-200 text-slate-900 hover:border-blue-900 hover:bg-blue-50 focus:ring-blue-900",
ghost: "text-slate-600 hover:text-slate-900 hover:bg-slate-100 focus:ring-slate-500",
};
const sizes = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "w-full py-3 text-base",
};
return (
<button
disabled={disabled || isLoading}
className={cn(baseStyles, variants[variant], sizes[size], (disabled || isLoading) && "opacity-50 cursor-not-allowed", className)}
{...props}
>
{isLoading && <span className="mr-2 w-4 h-4 rounded-full border-2 border-current border-b-transparent animate-spin" />}
{children}
</button>
);
};
+31
View File
@@ -0,0 +1,31 @@
import React from 'react';
import { cn } from '@/utils/utils';
export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("bg-white rounded-xl shadow-sm border border-slate-200 flex flex-col overflow-hidden", className)}
{...props}
/>
)
);
Card.displayName = "Card";
export const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("px-6 py-5 border-b border-slate-100 flex flex-col space-y-1.5", className)}
{...props}
/>
)
);
CardHeader.displayName = "CardHeader";
export const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 flex-1", className)} {...props} />
)
);
CardContent.displayName = "CardContent";
+18
View File
@@ -0,0 +1,18 @@
import React from 'react';
import { cn } from '@utils/utils';
interface EmptyStateProps {
message: string;
className?: string;
}
export const EmptyState: React.FC<EmptyStateProps> = ({ message, className }) => {
return (
<div className={cn(
"flex-1 flex justify-center items-center py-8 text-sm text-slate-500 italic",
className
)}>
{message}
</div>
);
};
+14
View File
@@ -0,0 +1,14 @@
import React from 'react';
import { cn } from '@utils/utils';
interface LoaderProps {
className?: string;
}
export const Loader: React.FC<LoaderProps> = ({ className }) => {
return (
<div className={cn("flex-1 flex justify-center items-center py-8", className)}>
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-slate-900"></div>
</div>
);
};
+23
View File
@@ -0,0 +1,23 @@
import React from 'react';
import type { ManagedContract } from '@/types/ManagedContract';
import { StatusBadge } from '@/components/ui/StatusBadge';
interface ServiceObjectProps {
contract: ManagedContract;
}
export const ServiceObject: React.FC<ServiceObjectProps> = ({ contract }) => {
return (
<div className="p-4 bg-slate-50 rounded-lg border border-slate-100 flex justify-between items-center">
<div>
<h3 className="text-sm font-medium text-slate-900">{contract.service_name}</h3>
{contract.description && (
<p className="text-xs text-slate-500 mt-1">{contract.description}</p>
)}
</div>
{/* Fonctionne instantanément avec 'Actif', 'En déploiement', 'Suspendu' */}
<StatusBadge status={contract.status} />
</div>
);
};
+66
View File
@@ -0,0 +1,66 @@
// Fichier : src/components/ui/StatusBadge.tsx
import React from 'react';
import { cn } from '@/utils/utils';
import type { AegisStatus } from '@/types/Status';
interface StatusBadgeProps {
status: AegisStatus;
className?: string;
}
// Configuration visuelle unique pour chaque statut
const STATUS_CONFIG: Record<AegisStatus, { bg: string; dot: string }> = {
// Statuts Infrastructure & Contrats Positifs
'Opérationnel': {
bg: 'bg-emerald-50 text-emerald-700 border-emerald-200/60',
dot: 'bg-emerald-500',
},
'Actif': {
bg: 'bg-emerald-50 text-emerald-700 border-emerald-200/60',
dot: 'bg-emerald-500',
},
// Statuts Intermédiaires / En cours
'Dégradé': {
bg: 'bg-amber-50 text-amber-700 border-amber-200/60',
dot: 'bg-amber-500',
},
'En déploiement': {
bg: 'bg-blue-50 text-blue-700 border-blue-200/60',
dot: 'bg-blue-500',
},
// Statuts Alertes / Inactifs
'Hors Ligne': {
bg: 'bg-red-50 text-red-700 border-red-200/60',
dot: 'bg-red-500',
},
'Suspendu': {
bg: 'bg-slate-100 text-slate-700 border-slate-200',
dot: 'bg-slate-400',
},
};
export const StatusBadge: React.FC<StatusBadgeProps> = ({ status, className }) => {
// Récupération de la configuration ou fallback de sécurité
const config = STATUS_CONFIG[status] || {
bg: 'bg-slate-50 text-slate-600 border-slate-200',
dot: 'bg-slate-400',
};
return (
<div
className={cn(
"px-2.5 py-1 rounded-full text-xs font-semibold inline-flex items-center border transition-colors",
config.bg,
className
)}
>
<span className={cn("w-1.5 h-1.5 rounded-full mr-2 shrink-0", config.dot)} />
{status}
</div>
);
};
export default StatusBadge;
+32
View File
@@ -0,0 +1,32 @@
import React from 'react';
import type { InfrastructureNode } from '@/types/InfrastructureNode';
import { StatusBadge } from '@ui/StatusBadge';
interface UptimeObjectProps {
node: InfrastructureNode;
}
export const UptimeObject: React.FC<UptimeObjectProps> = ({ node }) => {
return (
// La "key" a été retirée d'ici, elle sera gérée par le parent
<div className="py-4 flex items-center justify-between first:pt-0 last:pb-0">
{/* Infos Serveur */}
<div>
<p className="font-medium text-slate-900">{node.label}</p>
<p className="text-xs text-slate-500 mt-0.5">{node.type}</p>
</div>
{/* Métriques & Badge */}
<div className="flex items-center space-x-6">
<div className="text-right">
<p className="text-xs text-slate-500">SLA</p>
<p className="text-sm font-semibold text-slate-900">{node.uptime_sla}</p>
</div>
<StatusBadge status={node.status} />
</div>
</div>
);
};