Cette bibliothèque décrit tous les composants UI réutilisables de l'application, leurs props, leurs variantes et leurs cas d'usage.
Bouton principal de l'application avec plusieurs variantes.
interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
icon?: string; // Nom de l'icône Font Awesome
iconPosition?: 'left' | 'right';
disabled?: boolean;
loading?: boolean;
fullWidth?: boolean;
onClick?: () => void;
type?: 'button' | 'submit' | 'reset';
className?: string;
}- primary : Bouton principal (fond vert, texte blanc)
- secondary : Bouton secondaire (fond gris, texte foncé)
- outline : Bouton avec bordure (fond transparent, bordure verte)
- ghost : Bouton sans bordure (fond transparent au hover)
- danger : Bouton de danger (fond rouge, texte blanc)
- sm : Petit (h: 32px, padding: 8px 16px)
- md : Moyen (h: 40px, padding: 10px 20px) - Par défaut
- lg : Grand (h: 48px, padding: 12px 24px)
<Button variant="primary" size="md" icon="fa-save" iconPosition="left">
Enregistrer
</Button>Champ de saisie de texte avec validation.
interface InputProps {
label?: string;
name: string;
type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'date';
value: string;
onChange: (value: string) => void;
placeholder?: string;
required?: boolean;
disabled?: boolean;
error?: string;
helperText?: string;
icon?: string;
iconPosition?: 'left' | 'right';
className?: string;
}<Input
label="Email"
name="email"
type="email"
value={email}
onChange={setEmail}
required
error={errors.email}
helperText="Votre adresse email"
/>Liste déroulante de sélection.
interface SelectProps {
label?: string;
name: string;
value: string;
onChange: (value: string) => void;
options: { value: string; label: string }[];
placeholder?: string;
required?: boolean;
disabled?: boolean;
error?: string;
helperText?: string;
className?: string;
}<Select
label="Rôle"
name="role"
value={role}
onChange={setRole}
options={[
{ value: 'teacher', label: 'Professeur' },
{ value: 'student', label: 'Élève' }
]}
required
/>Zone de texte multi-lignes.
interface TextareaProps {
label?: string;
name: string;
value: string;
onChange: (value: string) => void;
placeholder?: string;
rows?: number;
required?: boolean;
disabled?: boolean;
error?: string;
helperText?: string;
className?: string;
}Case à cocher pour sélections multiples.
interface CheckboxProps {
label: string;
name: string;
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
error?: string;
className?: string;
}Bouton radio pour sélection unique.
interface RadioProps {
label: string;
name: string;
value: string;
checked: boolean;
onChange: (value: string) => void;
disabled?: boolean;
className?: string;
}Fenêtre modale pour afficher du contenu par-dessus la page.
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title?: string;
children: React.ReactNode;
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
showCloseButton?: boolean;
footer?: React.ReactNode;
className?: string;
}- Overlay sombre avec opacité
- Classe CSS
modal-contentsur le contenu (selon mémoire) - Fermeture par clic sur overlay ou bouton X
- Animation d'ouverture/fermeture
<Modal
isOpen={isOpen}
onClose={handleClose}
title="Confirmer la suppression"
size="md"
>
<p>Êtes-vous sûr de vouloir supprimer cet élément ?</p>
<div className="flex gap-4 mt-4">
<Button variant="primary" onClick={handleConfirm}>Confirmer</Button>
<Button variant="secondary" onClick={handleClose}>Annuler</Button>
</div>
</Modal>Tableau avec tri, pagination et filtres.
interface TableProps<T> {
data: T[];
columns: Column<T>[];
onRowClick?: (row: T) => void;
sortable?: boolean;
pagination?: {
page: number;
pageSize: number;
total: number;
onPageChange: (page: number) => void;
};
loading?: boolean;
emptyMessage?: string;
className?: string;
}
interface Column<T> {
key: keyof T | string;
header: string;
render?: (value: any, row: T) => React.ReactNode;
sortable?: boolean;
width?: string;
}<Table
data={students}
columns={[
{ key: 'firstName', header: 'Prénom', sortable: true },
{ key: 'lastName', header: 'Nom', sortable: true },
{ key: 'email', header: 'Email' },
{
key: 'status',
header: 'Statut',
render: (value) => <Badge status={value} />
}
]}
pagination={{
page: 1,
pageSize: 20,
total: 100,
onPageChange: setPage
}}
/>Carte pour afficher du contenu groupé.
interface CardProps {
children: React.ReactNode;
title?: string;
subtitle?: string;
header?: React.ReactNode;
footer?: React.ReactNode;
hover?: boolean;
onClick?: () => void;
className?: string;
}<Card
title="Violon Débutant"
subtitle="Cours collectif - 10 élèves"
hover
onClick={handleClick}
>
<p>Description du cours...</p>
</Card>Badge pour afficher des statuts ou labels.
interface BadgeProps {
children: React.ReactNode;
variant?: 'primary' | 'success' | 'warning' | 'error' | 'info' | 'gray';
size?: 'sm' | 'md';
className?: string;
}<Badge variant="success">Actif</Badge>
<Badge variant="warning">En attente</Badge>
<Badge variant="error">Annulé</Badge>Message d'alerte pour informer l'utilisateur.
interface AlertProps {
children: React.ReactNode;
variant?: 'success' | 'error' | 'warning' | 'info';
title?: string;
icon?: string;
onClose?: () => void;
className?: string;
}<Alert variant="success" title="Succès" onClose={handleClose}>
L'élève a été créé avec succès.
</Alert>Notification toast pour les actions rapides.
interface ToastProps {
message: string;
variant?: 'success' | 'error' | 'warning' | 'info';
icon?: string;
duration?: number;
onClose?: () => void;
}Note : Selon la mémoire, les toasts doivent avoir le background comme la variante 50 de la couleur et l'icône doit correspondre à la couleur de la bordure (ex: green-50 background, green-600 icon).
Menu déroulant pour actions contextuelles.
interface DropdownProps {
trigger: React.ReactNode;
items: DropdownItem[];
align?: 'left' | 'right';
className?: string;
}
interface DropdownItem {
label: string;
icon?: string;
onClick: () => void;
disabled?: boolean;
divider?: boolean;
}Onglets pour organiser le contenu.
interface TabsProps {
tabs: Tab[];
activeTab: string;
onChange: (tabId: string) => void;
className?: string;
}
interface Tab {
id: string;
label: string;
icon?: string;
content: React.ReactNode;
}Composant calendrier réutilisable.
Voir la section "Composants métier" ci-dessous.
Calendrier avec vues jour/semaine/mois.
interface CalendarProps {
view: 'day' | 'week' | 'month';
date: Date;
onDateChange: (date: Date) => void;
events: CalendarEvent[];
onEventClick?: (event: CalendarEvent) => void;
onEventCreate?: (date: Date, time: string) => void;
filters?: {
teacherId?: string;
studentId?: string;
roomId?: string;
};
className?: string;
}
interface CalendarEvent {
id: string;
title: string;
start: Date;
end: Date;
type: 'lesson' | 'course' | 'workshop' | 'rehearsal';
color?: string;
data?: any; // Données supplémentaires
}- Vue jour, semaine, mois
- Drag & drop pour déplacer les événements
- Création d'événement par clic
- Filtres par professeur, élève, salle
- Indicateurs visuels pour les conflits
Carte affichant les informations d'un élève.
interface StudentCardProps {
student: Student;
onClick?: () => void;
showActions?: boolean;
className?: string;
}Carte affichant les informations d'un professeur.
interface TeacherCardProps {
teacher: Teacher;
onClick?: () => void;
showActions?: boolean;
className?: string;
}Carte affichant un instrument avec son icône.
interface InstrumentCardProps {
instrument: Instrument;
onClick?: () => void;
showAvailability?: boolean;
className?: string;
}Formulaire multi-étapes pour l'inscription.
interface EnrollmentFormProps {
studentId?: string;
onSubmit: (enrollment: Enrollment) => void;
onCancel?: () => void;
}- Informations élève
- Sélection des cours
- Plan de paiement
- Documents
- Confirmation
Composant pour uploader des documents.
interface DocumentUploaderProps {
onUpload: (file: File) => Promise<void>;
accept?: string;
maxSize?: number; // En MB
multiple?: boolean;
className?: string;
}Affichage de l'échéancier de paiement.
interface PaymentScheduleProps {
installments: PaymentInstallment[];
onPaymentClick?: (installment: PaymentInstallment) => void;
className?: string;
}Layout standard pour les pages.
interface PageLayoutProps {
title: string;
subtitle?: string;
actions?: React.ReactNode;
children: React.ReactNode;
breadcrumbs?: Breadcrumb[];
}Layout pour les tableaux de bord.
interface DashboardLayoutProps {
children: React.ReactNode;
sidebar?: React.ReactNode;
header?: React.ReactNode;
}Navigation latérale principale.
interface SidebarProps {
items: SidebarItem[];
activeItem?: string;
onItemClick?: (item: SidebarItem) => void;
collapsed?: boolean;
onToggleCollapse?: () => void;
}
interface SidebarItem {
id: string;
label: string;
icon: string;
path: string;
badge?: number;
children?: SidebarItem[];
}En-tête de l'application.
interface HeaderProps {
user: User;
onLogout?: () => void;
onProfileClick?: () => void;
notifications?: Notification[];
}Spinner de chargement.
interface LoadingSpinnerProps {
size?: 'sm' | 'md' | 'lg';
className?: string;
}Composant pour afficher un état de chargement avec spinner et texte.
interface LoadingStateProps {
text?: string;
size?: 'sm' | 'md' | 'lg' | 'xl';
fullScreen?: boolean;
className?: string;
}<LoadingState text="Chargement des données..." size="md" />
<LoadingState text="Chargement..." fullScreen />Affichage quand il n'y a pas de données. Supporte deux variantes : default et card.
interface EmptyStateProps {
icon?: string; // Nom de l'icône Font Awesome (ex: 'fa-user-graduate')
title: string;
description?: string;
action?: {
label: string;
onClick: () => void;
icon?: string;
};
variant?: 'default' | 'card';
children?: ReactNode;
className?: string;
}- default : Affichage centré avec grande icône
- card : Affichage dans une carte avec icône dans conteneur arrondi
<EmptyState
icon="fa-user-graduate"
title="Aucun élève trouvé"
description="Commencez par ajouter un élève"
variant="card"
/>
<EmptyState
icon="fa-inbox"
title="Aucune donnée"
description="Il n'y a pas encore de données"
action={{
label: "Ajouter",
onClick: handleAdd,
icon: "fa-plus"
}}
/>Composant pour standardiser les en-têtes de section avec titre, sous-titre et actions.
interface SectionHeaderProps {
title: string;
subtitle?: string;
actions?: ReactNode;
className?: string;
}<SectionHeader
title="Gestion des élèves"
subtitle="Gérez tous les élèves du conservatoire"
actions={<Button>Ajouter</Button>}
/>Composant pour créer des conteneurs de cartes avec style uniforme.
interface CardContainerProps extends HTMLAttributes<HTMLDivElement> {
children: ReactNode;
padding?: 'none' | 'sm' | 'md' | 'lg';
shadow?: 'none' | 'sm' | 'md' | 'lg';
className?: string;
}<CardContainer padding="md" shadow="md">
<h3>Titre</h3>
<p>Contenu</p>
</CardContainer>Composant wrapper pour appliquer un style uniforme aux barres de défilement.
interface ScrollbarProps extends HTMLAttributes<HTMLDivElement> {
children: ReactNode;
direction?: 'vertical' | 'horizontal' | 'both';
className?: string;
}<Scrollbar direction="vertical" className="max-h-64">
<div>Contenu scrollable</div>
</Scrollbar>
<Scrollbar direction="horizontal">
<table>Table large</table>
</Scrollbar>Barre de recherche avec filtres.
interface SearchBarProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
filters?: Filter[];
onFilterChange?: (filters: Record<string, any>) => void;
className?: string;
}Utiliser React Hook Form pour la gestion des formulaires :
const { register, handleSubmit, formState: { errors } } = useForm();
<form onSubmit={handleSubmit(onSubmit)}>
<Input
label="Email"
{...register('email', { required: true })}
error={errors.email?.message}
/>
<Button type="submit">Envoyer</Button>
</form><Table
data={items}
columns={columns}
pagination={{
page: currentPage,
pageSize: 20,
total: totalItems,
onPageChange: setCurrentPage
}}
/><Modal isOpen={isOpen} onClose={onClose} title="Créer un élève">
<form onSubmit={handleSubmit}>
{/* Champs du formulaire */}
</form>
</Modal>