From 49949f1404be54c7428e9dab0ee1c0909515b37e Mon Sep 17 00:00:00 2001 From: mickael Date: Tue, 20 Jan 2026 11:05:50 +0300 Subject: [PATCH] add components --- src/components/Breadcrumbs.jsx | 64 + src/components/CallToAction.jsx | 17 + src/components/ChartCard.tsx | 338 +++ src/components/DataTable.jsx | 155 ++ src/components/DropdownMenu.tsx | 159 ++ src/components/FormModal.jsx | 146 ++ src/components/HeroImage.jsx | 31 + src/components/KPIBar.tsx | 154 ++ src/components/KanbanBoard.jsx | 158 ++ src/components/KpiCard.jsx | 42 + src/components/NotificationCenter.tsx | 108 + src/components/PageTransition.jsx | 18 + src/components/Pagination.jsx | 57 + src/components/PeriodFilter.jsx | 258 ++ src/components/PremiumHeader.jsx | 146 ++ src/components/PrimaryButton.jsx | 27 + src/components/PrimaryButton_v2.tsx | 43 + src/components/PriorityBanner.jsx | 38 + src/components/SecondaryBanner.jsx | 38 + src/components/SegmentedControl.jsx | 35 + src/components/SmartForm.jsx | 145 ++ src/components/StatusBadget.jsx | 95 + src/components/StatusBadgetCommande.jsx | 29 + src/components/StatusBadgetLettre.tsx | 107 + src/components/Tabs.jsx | 43 + src/components/Timeline.jsx | 115 + src/components/UserAvatar.jsx | 24 + src/components/UserMenu.jsx | 63 + src/components/WelcomeMessage.jsx | 17 + src/components/chart/Chart.tsx | 260 ++ src/components/common/AdvancedFilters.tsx | 284 +++ src/components/common/ColumnSelector.tsx | 135 + src/components/common/CompanySelector.tsx | 279 ++ src/components/common/ExportDropdown.tsx | 353 +++ src/components/common/InfoTooltip.jsx | 42 + src/components/common/PeriodSelector.jsx | 245 ++ src/components/common/ProductLogo.jsx | 19 + .../document-entry/DocumentHeader.jsx | 207 ++ .../document-entry/DocumentLinesTable.jsx | 255 ++ .../document-entry/StickyTotals.tsx | 110 + src/components/filter/ItemsFilter.tsx | 246 ++ src/components/forms/ArticleFormModal.tsx | 443 ++++ src/components/forms/ClientFormModal.jsx | 407 +++ .../forms/ProductFamilyFormModal.jsx | 90 + src/components/forms/ProspectFormModal.jsx | 405 +++ .../indicators/SignatureWorkflow.tsx | 122 + src/components/layout/AppLayout.jsx | 190 ++ src/components/layout/AuthLayout.tsx | 105 + src/components/layout/GlobalMessageBanner.jsx | 102 + src/components/layout/Sidebar.jsx | 126 + src/components/layout/TextField.tsx | 269 ++ src/components/layout/Topbar.jsx | 132 + src/components/loading.tsx | 55 + src/components/logo.jsx | 32 + src/components/modal/ModalArticle.tsx | 609 +++++ src/components/modal/ModalAvoir.tsx | 484 ++++ src/components/modal/ModalBL.tsx | 452 ++++ src/components/modal/ModalClient.tsx | 913 +++++++ src/components/modal/ModalCommande.tsx | 909 +++++++ .../modal/ModalCommandetoFacture.tsx | 119 + src/components/modal/ModalCommercial.tsx | 700 ++++++ src/components/modal/ModalContact.tsx | 560 +++++ src/components/modal/ModalFacture.tsx | 491 ++++ src/components/modal/ModalFamille.tsx | 282 +++ src/components/modal/ModalFournisseur.tsx | 2234 +++++++++++++++++ src/components/modal/ModalGateway.tsx | 431 ++++ src/components/modal/ModalLoading.tsx | 43 + src/components/modal/ModalPDFPreview.tsx | 151 ++ src/components/modal/ModalPaymentPanel.tsx | 639 +++++ src/components/modal/ModalQuote.tsx | 649 +++++ .../modal/ModalSendSignatureRequest.tsx | 309 +++ src/components/modal/ModalStatus.tsx | 406 +++ src/components/modal/ModalStock.tsx | 247 ++ .../modal/ModalValidationWarningCard.tsx | 243 ++ src/components/modal/ModalWorkflowToBL.tsx | 121 + src/components/modal/PDFPreview.tsx | 425 ++++ .../molecules/ArticleAutocomplete.tsx | 429 ++++ .../molecules/ClientAutocomplete.tsx | 262 ++ .../page/client/ClientContactsList.tsx | 207 ++ .../page/client/ClientDetailsGrid.tsx | 324 +++ .../page/client/ClientInfoVerifier.tsx | 269 ++ src/components/page/devis/DevisContent.tsx | 460 ++++ src/components/page/devis/DevisHeader.tsx | 532 ++++ src/components/page/devis/TransformOption.tsx | 41 + .../page/facture/FactureContent.tsx | 480 ++++ src/components/page/facture/FactureHeader.tsx | 566 +++++ .../page/facture/ValiderFacture.tsx | 66 + src/components/panels/PDFPreviewPanel.tsx | 810 ++++++ src/components/ribbons/ActionButton.tsx | 40 + src/components/ribbons/QuoteActionRibbon.jsx | 107 + src/components/signature/SignatureChart.jsx | 80 + .../signature/SignatureCreditAlert.jsx | 63 + src/components/signature/SignatureKPIs.jsx | 93 + .../signature/SignatureReminderModal.jsx | 79 + src/components/system/SystemStatusDrawer.jsx | 169 ++ src/components/ui/AuthInput.tsx | 88 + src/components/ui/DataTable.tsx | 263 ++ src/components/ui/FormDynamique.tsx | 280 +++ src/components/ui/FormModal.tsx | 314 +++ src/components/ui/InputField.tsx | 394 +++ src/components/ui/InputValidator.tsx | 705 ++++++ src/components/ui/InternationalPhoneInput.tsx | 552 ++++ src/components/ui/ManualLineInput.tsx | 157 ++ src/components/ui/PDFActionButtons.tsx | 62 + src/components/ui/Progress.tsx | 30 + src/components/ui/Section.tsx | 44 + src/components/ui/StatusBadge.tsx | 123 + src/components/ui/ToggleSwitch.tsx | 61 + src/components/ui/button.jsx | 54 + src/components/ui/buttonTsx.tsx | 69 + src/components/ui/checkbox.tsx | 29 + src/components/ui/dropdown-menu.tsx | 240 ++ src/components/ui/progress.jsx | 18 + src/components/ui/switch.jsx | 22 + src/components/ui/switchTsx.tsx | 27 + src/components/ui/toast.jsx | 101 + src/components/ui/toaster.jsx | 34 + src/components/ui/tooltip.jsx | 25 + src/components/ui/ui.tsx | 246 ++ src/components/ui/use-toast.js | 135 + 120 files changed, 28150 insertions(+) create mode 100644 src/components/Breadcrumbs.jsx create mode 100644 src/components/CallToAction.jsx create mode 100644 src/components/ChartCard.tsx create mode 100644 src/components/DataTable.jsx create mode 100644 src/components/DropdownMenu.tsx create mode 100644 src/components/FormModal.jsx create mode 100644 src/components/HeroImage.jsx create mode 100644 src/components/KPIBar.tsx create mode 100644 src/components/KanbanBoard.jsx create mode 100644 src/components/KpiCard.jsx create mode 100644 src/components/NotificationCenter.tsx create mode 100644 src/components/PageTransition.jsx create mode 100644 src/components/Pagination.jsx create mode 100644 src/components/PeriodFilter.jsx create mode 100644 src/components/PremiumHeader.jsx create mode 100644 src/components/PrimaryButton.jsx create mode 100644 src/components/PrimaryButton_v2.tsx create mode 100644 src/components/PriorityBanner.jsx create mode 100644 src/components/SecondaryBanner.jsx create mode 100644 src/components/SegmentedControl.jsx create mode 100644 src/components/SmartForm.jsx create mode 100644 src/components/StatusBadget.jsx create mode 100644 src/components/StatusBadgetCommande.jsx create mode 100644 src/components/StatusBadgetLettre.tsx create mode 100644 src/components/Tabs.jsx create mode 100644 src/components/Timeline.jsx create mode 100644 src/components/UserAvatar.jsx create mode 100644 src/components/UserMenu.jsx create mode 100644 src/components/WelcomeMessage.jsx create mode 100644 src/components/chart/Chart.tsx create mode 100644 src/components/common/AdvancedFilters.tsx create mode 100644 src/components/common/ColumnSelector.tsx create mode 100644 src/components/common/CompanySelector.tsx create mode 100644 src/components/common/ExportDropdown.tsx create mode 100644 src/components/common/InfoTooltip.jsx create mode 100644 src/components/common/PeriodSelector.jsx create mode 100644 src/components/common/ProductLogo.jsx create mode 100644 src/components/document-entry/DocumentHeader.jsx create mode 100644 src/components/document-entry/DocumentLinesTable.jsx create mode 100644 src/components/document-entry/StickyTotals.tsx create mode 100644 src/components/filter/ItemsFilter.tsx create mode 100644 src/components/forms/ArticleFormModal.tsx create mode 100644 src/components/forms/ClientFormModal.jsx create mode 100644 src/components/forms/ProductFamilyFormModal.jsx create mode 100644 src/components/forms/ProspectFormModal.jsx create mode 100644 src/components/indicators/SignatureWorkflow.tsx create mode 100644 src/components/layout/AppLayout.jsx create mode 100644 src/components/layout/AuthLayout.tsx create mode 100644 src/components/layout/GlobalMessageBanner.jsx create mode 100644 src/components/layout/Sidebar.jsx create mode 100644 src/components/layout/TextField.tsx create mode 100644 src/components/layout/Topbar.jsx create mode 100644 src/components/loading.tsx create mode 100644 src/components/logo.jsx create mode 100644 src/components/modal/ModalArticle.tsx create mode 100644 src/components/modal/ModalAvoir.tsx create mode 100644 src/components/modal/ModalBL.tsx create mode 100644 src/components/modal/ModalClient.tsx create mode 100644 src/components/modal/ModalCommande.tsx create mode 100644 src/components/modal/ModalCommandetoFacture.tsx create mode 100644 src/components/modal/ModalCommercial.tsx create mode 100644 src/components/modal/ModalContact.tsx create mode 100644 src/components/modal/ModalFacture.tsx create mode 100644 src/components/modal/ModalFamille.tsx create mode 100644 src/components/modal/ModalFournisseur.tsx create mode 100644 src/components/modal/ModalGateway.tsx create mode 100644 src/components/modal/ModalLoading.tsx create mode 100644 src/components/modal/ModalPDFPreview.tsx create mode 100644 src/components/modal/ModalPaymentPanel.tsx create mode 100644 src/components/modal/ModalQuote.tsx create mode 100644 src/components/modal/ModalSendSignatureRequest.tsx create mode 100644 src/components/modal/ModalStatus.tsx create mode 100644 src/components/modal/ModalStock.tsx create mode 100644 src/components/modal/ModalValidationWarningCard.tsx create mode 100644 src/components/modal/ModalWorkflowToBL.tsx create mode 100644 src/components/modal/PDFPreview.tsx create mode 100644 src/components/molecules/ArticleAutocomplete.tsx create mode 100644 src/components/molecules/ClientAutocomplete.tsx create mode 100644 src/components/page/client/ClientContactsList.tsx create mode 100644 src/components/page/client/ClientDetailsGrid.tsx create mode 100644 src/components/page/client/ClientInfoVerifier.tsx create mode 100644 src/components/page/devis/DevisContent.tsx create mode 100644 src/components/page/devis/DevisHeader.tsx create mode 100644 src/components/page/devis/TransformOption.tsx create mode 100644 src/components/page/facture/FactureContent.tsx create mode 100644 src/components/page/facture/FactureHeader.tsx create mode 100644 src/components/page/facture/ValiderFacture.tsx create mode 100644 src/components/panels/PDFPreviewPanel.tsx create mode 100644 src/components/ribbons/ActionButton.tsx create mode 100644 src/components/ribbons/QuoteActionRibbon.jsx create mode 100644 src/components/signature/SignatureChart.jsx create mode 100644 src/components/signature/SignatureCreditAlert.jsx create mode 100644 src/components/signature/SignatureKPIs.jsx create mode 100644 src/components/signature/SignatureReminderModal.jsx create mode 100644 src/components/system/SystemStatusDrawer.jsx create mode 100644 src/components/ui/AuthInput.tsx create mode 100644 src/components/ui/DataTable.tsx create mode 100644 src/components/ui/FormDynamique.tsx create mode 100644 src/components/ui/FormModal.tsx create mode 100644 src/components/ui/InputField.tsx create mode 100644 src/components/ui/InputValidator.tsx create mode 100644 src/components/ui/InternationalPhoneInput.tsx create mode 100644 src/components/ui/ManualLineInput.tsx create mode 100644 src/components/ui/PDFActionButtons.tsx create mode 100644 src/components/ui/Progress.tsx create mode 100644 src/components/ui/Section.tsx create mode 100644 src/components/ui/StatusBadge.tsx create mode 100644 src/components/ui/ToggleSwitch.tsx create mode 100644 src/components/ui/button.jsx create mode 100644 src/components/ui/buttonTsx.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/progress.jsx create mode 100644 src/components/ui/switch.jsx create mode 100644 src/components/ui/switchTsx.tsx create mode 100644 src/components/ui/toast.jsx create mode 100644 src/components/ui/toaster.jsx create mode 100644 src/components/ui/tooltip.jsx create mode 100644 src/components/ui/ui.tsx create mode 100644 src/components/ui/use-toast.js diff --git a/src/components/Breadcrumbs.jsx b/src/components/Breadcrumbs.jsx new file mode 100644 index 0000000..8045b01 --- /dev/null +++ b/src/components/Breadcrumbs.jsx @@ -0,0 +1,64 @@ + +import React from 'react'; +import { useLocation, Link } from 'react-router-dom'; +import { ChevronRight, Home } from 'lucide-react'; + +const routeNameMap = { + 'crm': 'CRM', + 'sales': 'Ventes', + 'purchases': 'Achats', + 'support': 'SAV', + 'admin': 'Administration', + 'prospects': 'Prospects', + 'clients': 'Clients', + 'suppliers': 'Fournisseurs', + 'opportunities': 'Opportunités', + 'quotes': 'Devis', + 'orders': 'Commandes', + 'delivery-notes': 'Bons de livraison', + 'invoices': 'Factures', + 'credit-notes': 'Avoirs', + 'tickets': 'Tickets', + 'dashboard': 'Tableau de bord', + 'users': 'Utilisateurs', + 'settings': 'Paramètres', + 'profile': 'Profil', + 'preferences': 'Préférences' +}; + +const Breadcrumbs = () => { + const location = useLocation(); + const pathnames = location.pathname.split('/').filter((x) => x); + + if (pathnames.length === 0) return null; + + return ( + + ); +}; + +export default Breadcrumbs; diff --git a/src/components/CallToAction.jsx b/src/components/CallToAction.jsx new file mode 100644 index 0000000..d2203da --- /dev/null +++ b/src/components/CallToAction.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const CallToAction = () => { + return ( + + Let's turn your ideas into reality + + ); +}; + +export default CallToAction; diff --git a/src/components/ChartCard.tsx b/src/components/ChartCard.tsx new file mode 100644 index 0000000..fad52ee --- /dev/null +++ b/src/components/ChartCard.tsx @@ -0,0 +1,338 @@ +import React from 'react'; +import { + LineChart, Line, BarChart, Bar, PieChart, Pie, Cell, AreaChart, Area, + ResponsiveContainer, XAxis, YAxis, CartesianGrid, Tooltip, Legend, + RadialBarChart, RadialBar, LabelList +} from 'recharts'; +import { motion } from 'framer-motion'; +import { Maximize2 } from 'lucide-react'; + +const COLORS = ['#007E45', '#10b981', '#059669', '#047857', '#065f46', '#064e3b', '#022c22']; + +// ============================================ +// FORMATTERS +// ============================================ + +const formatToK = (value: number): string => { + if (value >= 1000000) { + return `${(value / 1000000).toFixed(1)}M€`; + } + if (value >= 1000) { + return `${(value / 1000).toFixed(0)}K€`; + } + return `${value}€`; +}; + +const formatToKShort = (value: number): string => { + if (value >= 1000000) { + return `${(value / 1000000).toFixed(1)}M`; + } + if (value >= 1000) { + return `${(value / 1000).toFixed(0)}K`; + } + return `${value}`; +}; + +const formatCurrency = (value: number): string => { + return new Intl.NumberFormat('fr-FR', { + style: 'currency', + currency: 'EUR', + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }).format(value); +}; + +// ============================================ +// CUSTOM TOOLTIP +// ============================================ + +interface TooltipProps { + active?: boolean; + payload?: any[]; + label?: string; +} + +const CustomTooltip: React.FC = ({ active, payload, label }) => { + if (active && payload && payload.length) { + return ( +
+

{label}

+ {payload.map((entry, index) => ( +
+
+ {entry.name}: + + {formatCurrency(entry.value)} + +
+ ))} +
+ ); + } + return null; +}; + +// ============================================ +// CUSTOM LABEL POUR LES VALEURS SUR LE GRAPHIQUE +// ============================================ + +const CustomLabel = (props: any) => { + const { x, y, value, index } = props; + return ( + + {formatToK(value)} + + ); +}; + +// ============================================ +// TYPES (exportés pour réutilisation) +// ============================================ + +interface ChartCardProps { + title: string; + type?: string; + data: any[]; + height?: number; + className?: string; + showLabels?: boolean; // Afficher les valeurs sur le graphique + showTotal?: boolean; // Afficher le total sous le titre + showYAxisLabel?: boolean; // Afficher le label de l'axe Y + yAxisLabel?: string; // Label de l'axe Y (ex: "Montant en K€") +} + +// ============================================ +// COMPOSANT PRINCIPAL +// ============================================ + +const ChartCard: React.FC = ({ + title, + type = 'line', + data, + height = 300, + className, + showLabels = true, + showTotal = true, + showYAxisLabel = true, + yAxisLabel = "Montant en K€" +}) => { + // Guard clause + if (!data || data.length === 0) { + return ( +
+

{title}

+
+ Pas de données +
+
+ ); + } + + // Calculer le total pour l'afficher + const total = data.reduce((sum, item) => sum + (item.value || 0), 0) / 1000 + + return ( + +
+
+

{title}

+ {showTotal && ( +

+ Total: {total.toFixed(0)} K € +

+ )} +
+ +
+ + {/* Label de l'axe Y */} + {showYAxisLabel && (type === 'line' || type === 'area' || type === 'bar') && ( +

{yAxisLabel}

+ )} + +
+ + {type === 'line' && ( + + + + + } /> + + {showLabels && ( + + )} + + + )} + + {type === 'area' && ( + + + + + + + + + + + } /> + + {showLabels && ( + + )} + + + )} + + {type === 'bar' && ( + + + + + } /> + + {data.map((entry, index) => ( + + ))} + {showLabels && ( + + )} + + + )} + + {type === 'donut' && ( + + + {data.map((entry, index) => ( + + ))} + + } /> + + + )} + + {type === 'radial' && ( + + + + + )} + +
+
+ ); +}; + +export default ChartCard; diff --git a/src/components/DataTable.jsx b/src/components/DataTable.jsx new file mode 100644 index 0000000..6c2d345 --- /dev/null +++ b/src/components/DataTable.jsx @@ -0,0 +1,155 @@ + +import React, { useState } from 'react'; +import { ChevronDown, ChevronUp, Search } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import Pagination from '@/components/Pagination'; +import CircularProgress from '@mui/material/CircularProgress'; + + + +const DataTable = ({ columns, data, onRowClick, actions, status = false, searchLabel = "Rechercher..." }) => { + const [sortColumn, setSortColumn] = useState(null); + const [sortDirection, setSortDirection] = useState('asc'); + const [searchTerm, setSearchTerm] = useState(''); + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 10; + + const handleSort = (column) => { + if (sortColumn === column) { + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + } else { + setSortColumn(column); + setSortDirection('asc'); + } + }; + + + const filteredData = data.filter(row => + Object.values(row).some(value => + String(value).toLowerCase().includes(searchTerm.toLowerCase()) + ) + ); + + const sortedData = [...filteredData].sort((a, b) => { + if (!sortColumn) return 0; + const aVal = a[sortColumn]; + const bVal = b[sortColumn]; + if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1; + if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1; + return 0; + }); + + const paginatedData = sortedData.slice( + (currentPage - 1) * itemsPerPage, + currentPage * itemsPerPage + ); + + const totalPages = Math.ceil(filteredData.length / itemsPerPage); + + return ( +
+
+
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2.5 bg-white dark:bg-gray-950 border border-gray-200 dark:border-gray-800 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-[#007E45] text-gray-900 dark:text-white" + /> +
+
+ +
+
+ + + + {columns.map((column) => ( + + ))} + {actions && } + + + + {status ? ( + // LOADING + + + + ) : paginatedData.length === 0 ? ( + // AUCUNE DONNÉE + + + + ) : ( + // DONNÉES + paginatedData.map((row, index) => ( + onRowClick?.(row)} + className="hover:bg-gray-50 dark:hover:bg-gray-900 transition-colors cursor-pointer" + > + {columns.map((column) => ( + + ))} + + {actions && ( + + )} + + )) + )} + + +
column.sortable && handleSort(column.key)} + className={cn( + "px-6 py-3 text-left text-xs font-bold text-black dark:text-gray-400 uppercase tracking-wider", + column.sortable && "cursor-pointer hover:text-gray-900 dark:hover:text-white" + )} + > +
+ {column.label} + {column.sortable && sortColumn === column.key && ( + sortDirection === 'asc' ? : + )} +
+
Actions
+
+ +
+
+ Empty +
+ {column.render + ? column.render(row[column.key], row) + : row[column.key]} + +
e.stopPropagation()} + > + {actions(row)} +
+
+
+
+ + {totalPages > 1 && ( + + )} +
+ ); +}; + +export default DataTable; diff --git a/src/components/DropdownMenu.tsx b/src/components/DropdownMenu.tsx new file mode 100644 index 0000000..959792f --- /dev/null +++ b/src/components/DropdownMenu.tsx @@ -0,0 +1,159 @@ +import { useState, useEffect, useRef } from 'react'; +import { Edit, Download, FileSignature, MoreVertical, Trash2, CircleDot, Copy } from 'lucide-react'; + +export const DropdownMenuTable = ({ + row, + onEdit, + onStatus, + onESign, + onDownload, + onDelete, + onDulipcate +}: { + row: any; + onEdit?: () => void; + onStatus?: () => void; + onESign?: () => void; + onDownload?: () => void; + onDelete?: () => void; + onDulipcate?: () => void; +}) => { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + + // Fermer le menu quand on clique à l'extérieur + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + if (isOpen) { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isOpen]); + + return ( +
+ + + {isOpen && ( +
+ + + { + onEdit && ( + + ) + } + + { + onDulipcate && ( + + ) + } + + { + onStatus && ( + + ) + } + + + { + onESign && ( + + ) + } + + { + onDownload && ( + + ) + } + + + { + onDelete && ( + <> +
+ + + + ) + } + + + +
+ )} +
+ ); +}; diff --git a/src/components/FormModal.jsx b/src/components/FormModal.jsx new file mode 100644 index 0000000..e639f32 --- /dev/null +++ b/src/components/FormModal.jsx @@ -0,0 +1,146 @@ + +import React from 'react'; +import { X } from 'lucide-react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { cn } from '@/lib/utils'; + +export const FormSection = ({ title, description, children, className }) => ( +
+

{title}

+ {description &&

{description}

} +
+ {children} +
+
+); + + +export const FormField = ({ label, children, required, error, fullWidth, className }) => ( +
+ + {children} + {error &&

{error}

} +
+); + +export const Input = React.forwardRef(({ className, error, ...props }, ref) => ( + +)); +Input.displayName = "Input"; + +export const Select = React.forwardRef(({ className, error, children, ...props }, ref) => ( + +)); +Select.displayName = "Select"; + +export const Textarea = React.forwardRef(({ className, error, ...props }, ref) => ( +