flysoft-react-ui 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/AI_CONTEXT.md +1400 -217
  2. package/AI_INTEGRATION_GUIDE.md +343 -0
  3. package/INTEGRATION_GUIDE.md +60 -0
  4. package/README.md +5 -3
  5. package/dist/components/form-controls/Input.d.ts.map +1 -1
  6. package/dist/components/form-controls/index.d.ts +2 -2
  7. package/dist/components/form-controls/index.d.ts.map +1 -1
  8. package/dist/components/layout/Accordion.d.ts +1 -0
  9. package/dist/components/layout/Accordion.d.ts.map +1 -1
  10. package/dist/components/layout/DataTable.d.ts.map +1 -1
  11. package/dist/components/layout/DropdownMenu.d.ts +2 -1
  12. package/dist/components/layout/DropdownMenu.d.ts.map +1 -1
  13. package/dist/components/layout/DropdownPanel.d.ts +2 -1
  14. package/dist/components/layout/DropdownPanel.d.ts.map +1 -1
  15. package/dist/components/layout/Filter.d.ts +1 -0
  16. package/dist/components/layout/Filter.d.ts.map +1 -1
  17. package/dist/components/layout/Menu.d.ts +2 -1
  18. package/dist/components/layout/Menu.d.ts.map +1 -1
  19. package/dist/components/layout/TabsGroup.d.ts +1 -0
  20. package/dist/components/layout/TabsGroup.d.ts.map +1 -1
  21. package/dist/index.css +1 -1
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +11889 -24
  25. package/dist/index.js.map +1 -1
  26. package/dist/templates/forms/ContactForm.d.ts +1 -0
  27. package/dist/templates/forms/ContactForm.d.ts.map +1 -1
  28. package/dist/templates/forms/LoginForm.d.ts +1 -0
  29. package/dist/templates/forms/LoginForm.d.ts.map +1 -1
  30. package/dist/templates/forms/RegistrationForm.d.ts +1 -0
  31. package/dist/templates/forms/RegistrationForm.d.ts.map +1 -1
  32. package/dist/templates/layouts/DashboardLayout.d.ts +1 -0
  33. package/dist/templates/layouts/DashboardLayout.d.ts.map +1 -1
  34. package/dist/templates/layouts/SidebarLayout.d.ts +1 -0
  35. package/dist/templates/layouts/SidebarLayout.d.ts.map +1 -1
  36. package/dist/templates/patterns/FormPattern.d.ts +1 -0
  37. package/dist/templates/patterns/FormPattern.d.ts.map +1 -1
  38. package/dist/templates/patterns/ListPattern.d.ts +77 -0
  39. package/dist/templates/patterns/ListPattern.d.ts.map +1 -0
  40. package/package.json +6 -3
  41. package/dist/App.d.ts +0 -4
  42. package/dist/App.d.ts.map +0 -1
  43. package/dist/App.js +0 -30
  44. package/dist/components/ThemeSwitcher.js +0 -12
  45. package/dist/components/form-controls/AutocompleteInput.js +0 -680
  46. package/dist/components/form-controls/Button.js +0 -211
  47. package/dist/components/form-controls/Checkbox.js +0 -79
  48. package/dist/components/form-controls/CurrencyInput.js +0 -106
  49. package/dist/components/form-controls/DateInput.js +0 -578
  50. package/dist/components/form-controls/DatePicker.js +0 -144
  51. package/dist/components/form-controls/Input.js +0 -35
  52. package/dist/components/form-controls/LinkButton.js +0 -248
  53. package/dist/components/form-controls/Pagination.js +0 -23
  54. package/dist/components/form-controls/RadioButtonGroup.js +0 -220
  55. package/dist/components/form-controls/SearchSelectInput-OLD.d.ts +0 -68
  56. package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +0 -1
  57. package/dist/components/form-controls/SearchSelectInput-OLD.js +0 -962
  58. package/dist/components/form-controls/SearchSelectInput.js +0 -336
  59. package/dist/components/form-controls/index.js +0 -11
  60. package/dist/components/index.js +0 -7
  61. package/dist/components/layout/Accordion.js +0 -67
  62. package/dist/components/layout/AppLayout.js +0 -230
  63. package/dist/components/layout/Card.js +0 -54
  64. package/dist/components/layout/Collection.js +0 -18
  65. package/dist/components/layout/DataField.js +0 -38
  66. package/dist/components/layout/DataTable.js +0 -164
  67. package/dist/components/layout/DropdownMenu.js +0 -176
  68. package/dist/components/layout/DropdownPanel.js +0 -162
  69. package/dist/components/layout/Filter.js +0 -629
  70. package/dist/components/layout/Menu.js +0 -21
  71. package/dist/components/layout/TabPanel.js +0 -11
  72. package/dist/components/layout/TabsGroup.js +0 -52
  73. package/dist/components/layout/index.js +0 -12
  74. package/dist/components/utils/Avatar.js +0 -77
  75. package/dist/components/utils/Badge.js +0 -151
  76. package/dist/components/utils/Dialog.js +0 -44
  77. package/dist/components/utils/FiltersDialog.js +0 -104
  78. package/dist/components/utils/Loader.js +0 -44
  79. package/dist/components/utils/RoadMap.js +0 -139
  80. package/dist/components/utils/Skeleton.js +0 -10
  81. package/dist/components/utils/Snackbar.js +0 -136
  82. package/dist/components/utils/SnackbarContainer.js +0 -26
  83. package/dist/components/utils/iconUtils.js +0 -40
  84. package/dist/components/utils/index.js +0 -9
  85. package/dist/contexts/AppLayoutContext.js +0 -104
  86. package/dist/contexts/AuthContext.js +0 -224
  87. package/dist/contexts/CrudContext.js +0 -333
  88. package/dist/contexts/SnackbarContext.js +0 -41
  89. package/dist/contexts/ThemeContext.js +0 -197
  90. package/dist/contexts/index.js +0 -13
  91. package/dist/contexts/presets.js +0 -311
  92. package/dist/contexts/types.js +0 -1
  93. package/dist/docs/AccordionDocs.d.ts +0 -4
  94. package/dist/docs/AccordionDocs.d.ts.map +0 -1
  95. package/dist/docs/AccordionDocs.js +0 -21
  96. package/dist/docs/AuthDocs.tsx/AuthDocs.d.ts +0 -13
  97. package/dist/docs/AuthDocs.tsx/AuthDocs.d.ts.map +0 -1
  98. package/dist/docs/AuthDocs.tsx/AuthDocs.js +0 -18
  99. package/dist/docs/AuthDocs.tsx/AuthDocsContent.d.ts +0 -2
  100. package/dist/docs/AuthDocs.tsx/AuthDocsContent.d.ts.map +0 -1
  101. package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +0 -22
  102. package/dist/docs/AuthDocs.tsx/mockAuthService.d.ts +0 -24
  103. package/dist/docs/AuthDocs.tsx/mockAuthService.d.ts.map +0 -1
  104. package/dist/docs/AuthDocs.tsx/mockAuthService.js +0 -78
  105. package/dist/docs/AutocompleteInputDocs.d.ts +0 -4
  106. package/dist/docs/AutocompleteInputDocs.d.ts.map +0 -1
  107. package/dist/docs/AutocompleteInputDocs.js +0 -84
  108. package/dist/docs/AvatarDocs.d.ts +0 -4
  109. package/dist/docs/AvatarDocs.d.ts.map +0 -1
  110. package/dist/docs/AvatarDocs.js +0 -7
  111. package/dist/docs/BadgeDocs.d.ts +0 -4
  112. package/dist/docs/BadgeDocs.d.ts.map +0 -1
  113. package/dist/docs/BadgeDocs.js +0 -9
  114. package/dist/docs/ButtonDocs.d.ts +0 -4
  115. package/dist/docs/ButtonDocs.d.ts.map +0 -1
  116. package/dist/docs/ButtonDocs.js +0 -7
  117. package/dist/docs/CardDocs.d.ts +0 -4
  118. package/dist/docs/CardDocs.d.ts.map +0 -1
  119. package/dist/docs/CardDocs.js +0 -22
  120. package/dist/docs/CheckboxDocs.d.ts +0 -4
  121. package/dist/docs/CheckboxDocs.d.ts.map +0 -1
  122. package/dist/docs/CheckboxDocs.js +0 -7
  123. package/dist/docs/CurrencyInputDocs.d.ts +0 -4
  124. package/dist/docs/CurrencyInputDocs.d.ts.map +0 -1
  125. package/dist/docs/CurrencyInputDocs.js +0 -22
  126. package/dist/docs/DataFieldDocs.d.ts +0 -4
  127. package/dist/docs/DataFieldDocs.d.ts.map +0 -1
  128. package/dist/docs/DataFieldDocs.js +0 -7
  129. package/dist/docs/DataTableDocs.d.ts +0 -4
  130. package/dist/docs/DataTableDocs.d.ts.map +0 -1
  131. package/dist/docs/DataTableDocs.js +0 -244
  132. package/dist/docs/DateInputDocs.d.ts +0 -5
  133. package/dist/docs/DateInputDocs.d.ts.map +0 -1
  134. package/dist/docs/DateInputDocs.js +0 -19
  135. package/dist/docs/DatePickerDocs.d.ts +0 -5
  136. package/dist/docs/DatePickerDocs.d.ts.map +0 -1
  137. package/dist/docs/DatePickerDocs.js +0 -16
  138. package/dist/docs/DialogDocs.d.ts +0 -4
  139. package/dist/docs/DialogDocs.d.ts.map +0 -1
  140. package/dist/docs/DialogDocs.js +0 -13
  141. package/dist/docs/DocAdmin.d.ts +0 -4
  142. package/dist/docs/DocAdmin.d.ts.map +0 -1
  143. package/dist/docs/DocAdmin.js +0 -68
  144. package/dist/docs/DocsMenu.d.ts +0 -2
  145. package/dist/docs/DocsMenu.d.ts.map +0 -1
  146. package/dist/docs/DocsMenu.js +0 -5
  147. package/dist/docs/DocsRouter.d.ts +0 -4
  148. package/dist/docs/DocsRouter.d.ts.map +0 -1
  149. package/dist/docs/DocsRouter.js +0 -39
  150. package/dist/docs/DropdownMenuDocs.d.ts +0 -4
  151. package/dist/docs/DropdownMenuDocs.d.ts.map +0 -1
  152. package/dist/docs/DropdownMenuDocs.js +0 -66
  153. package/dist/docs/DropdownPanelDocs.d.ts +0 -4
  154. package/dist/docs/DropdownPanelDocs.d.ts.map +0 -1
  155. package/dist/docs/DropdownPanelDocs.js +0 -7
  156. package/dist/docs/ExampleFormDocs.d.ts +0 -4
  157. package/dist/docs/ExampleFormDocs.d.ts.map +0 -1
  158. package/dist/docs/ExampleFormDocs.js +0 -153
  159. package/dist/docs/FilterDocs.d.ts +0 -4
  160. package/dist/docs/FilterDocs.d.ts.map +0 -1
  161. package/dist/docs/FilterDocs.js +0 -130
  162. package/dist/docs/InputDocs.d.ts +0 -4
  163. package/dist/docs/InputDocs.d.ts.map +0 -1
  164. package/dist/docs/InputDocs.js +0 -17
  165. package/dist/docs/LinkButtonDocs.d.ts +0 -4
  166. package/dist/docs/LinkButtonDocs.d.ts.map +0 -1
  167. package/dist/docs/LinkButtonDocs.js +0 -7
  168. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +0 -2
  169. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +0 -1
  170. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +0 -47
  171. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.d.ts +0 -2
  172. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.d.ts.map +0 -1
  173. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.js +0 -34
  174. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.d.ts +0 -2
  175. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.d.ts.map +0 -1
  176. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.js +0 -66
  177. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts +0 -2
  178. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts.map +0 -1
  179. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.js +0 -7
  180. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.d.ts +0 -10
  181. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.d.ts.map +0 -1
  182. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.js +0 -39
  183. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts +0 -2
  184. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +0 -1
  185. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +0 -57
  186. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts +0 -9
  187. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts.map +0 -1
  188. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.js +0 -30
  189. package/dist/docs/LoaderDocs.d.ts +0 -4
  190. package/dist/docs/LoaderDocs.d.ts.map +0 -1
  191. package/dist/docs/LoaderDocs.js +0 -33
  192. package/dist/docs/MenuDocs.d.ts +0 -4
  193. package/dist/docs/MenuDocs.d.ts.map +0 -1
  194. package/dist/docs/MenuDocs.js +0 -26
  195. package/dist/docs/PaginationDocs.d.ts +0 -4
  196. package/dist/docs/PaginationDocs.d.ts.map +0 -1
  197. package/dist/docs/PaginationDocs.js +0 -38
  198. package/dist/docs/RadioButtonGroupDocs.d.ts +0 -4
  199. package/dist/docs/RadioButtonGroupDocs.d.ts.map +0 -1
  200. package/dist/docs/RadioButtonGroupDocs.js +0 -46
  201. package/dist/docs/RoadMapDocs.d.ts +0 -4
  202. package/dist/docs/RoadMapDocs.d.ts.map +0 -1
  203. package/dist/docs/RoadMapDocs.js +0 -171
  204. package/dist/docs/SearchSelectInputDocs.d.ts +0 -4
  205. package/dist/docs/SearchSelectInputDocs.d.ts.map +0 -1
  206. package/dist/docs/SearchSelectInputDocs.js +0 -168
  207. package/dist/docs/SkeletonDocs.d.ts +0 -4
  208. package/dist/docs/SkeletonDocs.d.ts.map +0 -1
  209. package/dist/docs/SkeletonDocs.js +0 -7
  210. package/dist/docs/SnackbarDocs.d.ts +0 -4
  211. package/dist/docs/SnackbarDocs.d.ts.map +0 -1
  212. package/dist/docs/SnackbarDocs.js +0 -69
  213. package/dist/docs/TabsGroupDocs.d.ts +0 -4
  214. package/dist/docs/TabsGroupDocs.d.ts.map +0 -1
  215. package/dist/docs/TabsGroupDocs.js +0 -38
  216. package/dist/docs/ThemeSwitcherDocs.d.ts +0 -4
  217. package/dist/docs/ThemeSwitcherDocs.d.ts.map +0 -1
  218. package/dist/docs/ThemeSwitcherDocs.js +0 -11
  219. package/dist/docs/docMockServices/empresaService.d.ts +0 -38
  220. package/dist/docs/docMockServices/empresaService.d.ts.map +0 -1
  221. package/dist/docs/docMockServices/empresaService.js +0 -125
  222. package/dist/docs/docMockServices/index.d.ts +0 -9
  223. package/dist/docs/docMockServices/index.d.ts.map +0 -1
  224. package/dist/docs/docMockServices/index.js +0 -8
  225. package/dist/docs/docMockServices/initialData.d.ts +0 -6
  226. package/dist/docs/docMockServices/initialData.d.ts.map +0 -1
  227. package/dist/docs/docMockServices/initialData.js +0 -132
  228. package/dist/docs/docMockServices/interfaces.d.ts +0 -38
  229. package/dist/docs/docMockServices/interfaces.d.ts.map +0 -1
  230. package/dist/docs/docMockServices/interfaces.js +0 -1
  231. package/dist/docs/docMockServices/personaEmpresaService.d.ts +0 -43
  232. package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +0 -1
  233. package/dist/docs/docMockServices/personaEmpresaService.js +0 -151
  234. package/dist/docs/docMockServices/personaService.d.ts +0 -39
  235. package/dist/docs/docMockServices/personaService.d.ts.map +0 -1
  236. package/dist/docs/docMockServices/personaService.js +0 -190
  237. package/dist/helpers/currencyFormat.js +0 -3
  238. package/dist/helpers/getErrorMessage.js +0 -13
  239. package/dist/helpers/getInitialLetters.js +0 -5
  240. package/dist/helpers/getQueryString.js +0 -13
  241. package/dist/helpers/index.js +0 -9
  242. package/dist/helpers/mappers.js +0 -27
  243. package/dist/helpers/nameValueArrayToObject.js +0 -3
  244. package/dist/helpers/objectToQueryString.js +0 -3
  245. package/dist/helpers/queryStringToObject.js +0 -13
  246. package/dist/helpers/regularExpressions.js +0 -5
  247. package/dist/hooks/index.js +0 -6
  248. package/dist/hooks/useAsyncRequest.js +0 -53
  249. package/dist/hooks/useBreakpoint.js +0 -59
  250. package/dist/hooks/useElementScroll.js +0 -58
  251. package/dist/hooks/useEnum.js +0 -21
  252. package/dist/hooks/useGlobalThemeStyles.js +0 -40
  253. package/dist/hooks/useThemeOverride.js +0 -99
  254. package/dist/interfaces/index.js +0 -1
  255. package/dist/interfaces/name-value.interface.js +0 -1
  256. package/dist/interfaces/pagination.interface.js +0 -1
  257. package/dist/main.d.ts +0 -2
  258. package/dist/main.d.ts.map +0 -1
  259. package/dist/main.js +0 -6
  260. package/dist/services/apiClient.js +0 -216
  261. package/dist/services/index.js +0 -1
  262. package/dist/styles.d.ts +0 -2
  263. package/dist/styles.d.ts.map +0 -1
  264. package/dist/styles.js +0 -3
  265. package/dist/templates/forms/ContactForm.js +0 -58
  266. package/dist/templates/forms/LoginForm.js +0 -36
  267. package/dist/templates/forms/RegistrationForm.js +0 -54
  268. package/dist/templates/layouts/DashboardLayout.js +0 -26
  269. package/dist/templates/layouts/SidebarLayout.js +0 -28
  270. package/dist/templates/patterns/FormPattern.js +0 -68
package/AI_CONTEXT.md CHANGED
@@ -1,217 +1,1400 @@
1
- # Flysoft React UI - AI Context & Documentation
2
-
3
- This document serves as the source of truth for AI models (Gemini, Claude, GPT, etc.) when generating code that consumes the `flysoft-react-ui` library.
4
-
5
- ## Library Philosophy
6
- `flysoft-react-ui` is a React component library built with TypeScript. It emphasizes a consistent look and feel, ease of use, and "premium" aesthetics out of the box.
7
-
8
- ## 🚨 Critical Rules for AI
9
- 1. **Top-Level Imports Only**: Always import components from the root package `flysoft-react-ui`.
10
- - **CORRECT**: `import { Button, Card } from 'flysoft-react-ui';`
11
- - **INCORRECT**: `import { Button } from 'flysoft-react-ui/components/Button';`
12
- 2. **TypeScript First**: Use the exported types (e.g., `ButtonProps`, `DataTableColumn`) to ensure type safety.
13
- 3. **No Direct Style Imports**: Do not import CSS files from the library (like `flysoft-react-ui/dist/index.css`) in components. This is handled at the app root.
14
-
15
- ## Component Categorization
16
-
17
- ### 1. Layouts & Structure
18
- - **AppLayout**: The main wrapper for applications.
19
- - **Props**: `navbar` (NavbarInterface), `leftDrawer` (LeftDrawerInterface), `children`.
20
- - **Card**: Generic container with shadow and rounded corners.
21
- - **Accordion**: Collapsible content sections.
22
- - **Collection**: Renders a list of items using a render prop or component.
23
- - **TabsGroup / TabPanel**: Tabbed interfaces.
24
- - **Menu**: Menu component.
25
- - **DropdownMenu / DropdownPanel**: Components for building drop-down menus.
26
- - **DashboardLayout**: Specialized layout for dashboard views with statistics.
27
- - **SidebarLayout**: Layout with a persistent sidebar.
28
-
29
- ### 2. Form Controls
30
- - **Button / LinkButton**: Standard buttons. Supports `variant` ('primary' | 'secondary' | 'danger' | 'ghost') and `icon`.
31
- - **Input**: Text inputs.
32
- - **AutocompleteInput**: Searchable dropdown.
33
- - **SearchSelectInput**: Specialized input for selecting from search results.
34
- - **DatePicker / DateInput**: Date picking and input.
35
- - **Checkbox**: Boolean selection.
36
- - **RadioButtonGroup**: Single selection from a group.
37
- - **ThemeSwitcher**: Component to toggle theme (light/dark).
38
- - **FormPattern**: Template pattern for building forms.
39
-
40
- ### 3. Data Display
41
- - **DataTable<T>**: High-performance table.
42
- - **Props**: `columns` (array of definitions), `rows` (data), `isLoading`.
43
- - **DataField**: Displays a Label + Value pair, useful for detail views.
44
- - **Pagination**: Pagination controls.
45
-
46
- ### 4. Utilities & Feedback
47
- - **Dialog**: Modal windows for confirmation or complex forms.
48
- - **Filter / FiltersDialog**: Components for filtering lists/tables.
49
- - **Snackbar**: Toast notifications. Requires `SnackbarContainer` at the app root.
50
- - **Loader**: Visual loading indicator.
51
- - **Badge**: Status indicators.
52
- - **Avatar**: User profile images.
53
- - **RoadMap**: Progress or stage visualization.
54
-
55
- ### 5. Ready-to-use Templates
56
- - **LoginForm**
57
- - **RegistrationForm**
58
- - **ContactForm**
59
-
60
- ## Common Patterns
61
-
62
- ### Basic Page Usage (Simple Card)
63
- Use this pattern for simple pages that do not require the full `AppLayout` wrapper or when inserting into an existing structure.
64
- ```tsx
65
- import { Card, Button } from 'flysoft-react-ui';
66
-
67
- export default function SimplePage() {
68
- return (
69
- <div className="p-4">
70
- <Card>
71
- <h2 className="text-xl font-bold mb-4">Content Title</h2>
72
- <p className="mb-4">This is the content within a basic card.</p>
73
- <Button variant="primary" onClick={() => console.log('Clicked')}>
74
- Action
75
- </Button>
76
- </Card>
77
- </div>
78
- );
79
- }
80
- ```
81
-
82
- ### Full Page Layout
83
- ```tsx
84
- import { AppLayout } from 'flysoft-react-ui';
85
-
86
- export default function Page() {
87
- return (
88
- <AppLayout
89
- navbar={{ title: "My Page" }}
90
- >
91
- {/* Children content goes here */}
92
- </AppLayout>
93
- );
94
- }
95
- ```
96
-
97
- ### Data Table Usage
98
- ```tsx
99
- import { DataTable, DataTableColumn } from 'flysoft-react-ui';
100
-
101
- interface User { id: number; name: string; }
102
-
103
- const columns: DataTableColumn<User>[] = [
104
- { header: 'ID', accessorKey: 'id' },
105
- { header: 'Name', accessorKey: 'name' },
106
- ];
107
-
108
- <DataTable columns={columns} rows={users} />
109
- ```
110
-
111
- ## Contexts & State Management
112
-
113
- ### 1. AuthContext
114
- Manages user authentication state.
115
- - **Provider**: `AuthProvider`
116
- - **Hook**: `useContext(AuthContext)`
117
- - **Key Features**: `user`, `login`, `logout`, `isAuthenticated`.
118
- ```tsx
119
- import { AuthProvider, AuthContext } from 'flysoft-react-ui';
120
-
121
- // Wrap app
122
- <AuthProvider
123
- getToken={async (user, pass) => ({ token: '...' })}
124
- getUserData={async (token) => ({ id: 1, name: 'User' })}
125
- >
126
- <App />
127
- </AuthProvider>
128
-
129
- // Use
130
- const { login, user } = useContext(AuthContext);
131
- ```
132
-
133
- ### 2. CrudContext<T>
134
- Powerful context for managing standard CRUD operations with pagination, filtering, and loading states.
135
- - **Provider**: `CrudProvider<T>`
136
- - **Hook**: `useCrud<T>()`
137
- - **Key Features**: Auto-fetching, `list`, `item`, `pagination` node, `isLoading`, CRUD actions (`createItem`, `updateItem`, `deleteItem`).
138
-
139
- **Usage:**
140
- ```tsx
141
- import { CrudProvider, useCrud, DataTable } from 'flysoft-react-ui';
142
-
143
- function UserList() {
144
- const { list, isLoading, pagination } = useCrud<User>();
145
- return (
146
- <div>
147
- <DataTable rows={list} isLoading={isLoading} ... />
148
- {pagination}
149
- </div>
150
- );
151
- }
152
-
153
- // Wrapper
154
- <CrudProvider
155
- getPromise={userApi.getAll}
156
- postPromise={userApi.create}
157
- putPromise={userApi.update}
158
- deletePromise={userApi.delete}
159
- >
160
- <UserList />
161
- </CrudProvider>
162
- ```
163
-
164
- ### 3. AppLayoutContext
165
- Controls the global layout state (navbar, visual settings). usually handled internally by `AppLayout` but accessible if needed.
166
- - **Provider**: `AppLayoutProvider`
167
- - **Hook**: `useAppLayout()`
168
-
169
- ### 4. ThemeContext
170
- Manages the visual theme of the application.
171
- - **Provider**: `ThemeProvider`
172
- - **Hook**: `useTheme()`
173
- - **Features**: Switch between `light`, `dark`, `blue`, `green` themes or custom themes.
174
-
175
- ```tsx
176
- ## Hooks & Services
177
-
178
- ### 1. apiClient (Service)
179
- A singleton wrapper around Axios with built-in token management and simplified methods.
180
- - **Methods**: `get`, `post`, `put`, `del`, `getFile`, `downloadFile`, `uploadFile`.
181
- - **Configuration**: Use `setApiClientTokenProvider(() => token)` to handle auth globally.
182
-
183
- ```tsx
184
- import { apiClient } from 'flysoft-react-ui';
185
-
186
- // GET request
187
- const users = await apiClient.get<User[]>({ url: '/api/users' });
188
-
189
- // POST request with body
190
- const result = await apiClient.post({ url: '/api/users', body: newUser });
191
-
192
- // Download file (handles blob and filename automatically)
193
- await apiClient.downloadFile({ url: '/api/report/pdf' });
194
- ```
195
-
196
- ### 2. useAsyncRequest (Hook)
197
- Simplifies handling async operations (loading states, success/error messages via Snackbar).
198
- - **Props**: `successMessage`, `errorMessage`, `onSuccess`, `onError`.
199
- - **Returns**: `execute(promiseFn)`, `isLoading`.
200
-
201
- ```tsx
202
- import { useAsyncRequest, apiClient } from 'flysoft-react-ui';
203
-
204
- function UserForm() {
205
- const { execute, isLoading } = useAsyncRequest({
206
- successMessage: "User created successfully!",
207
- errorMessage: "Failed to create user."
208
- });
209
-
210
- const handleSubmit = async (data) => {
211
- await execute(async () => {
212
- await apiClient.post({ url: '/api/users', body: data });
213
- });
214
- };
215
-
216
- return <Button loading={isLoading} onClick={handleSubmit}>Save</Button>;
217
- }
1
+ # Flysoft React UI - AI Context & Documentation
2
+
3
+ This document serves as the source of truth for AI models (Gemini, Claude, GPT, etc.) when generating code that consumes the `flysoft-react-ui` library.
4
+
5
+ ## Library Philosophy
6
+
7
+ `flysoft-react-ui` is a React component library built with TypeScript. It emphasizes a consistent look and feel, ease of use, and "premium" aesthetics out of the box. All components use CSS variables for theming and FontAwesome 5 (light/outlined style) for icons.
8
+
9
+ ## Critical Rules for AI
10
+
11
+ 1. **Top-Level Imports Only**: Always import from `'flysoft-react-ui'`.
12
+ - CORRECT: `import { Button, Card } from 'flysoft-react-ui';`
13
+ - INCORRECT: `import { Button } from 'flysoft-react-ui/components/Button';`
14
+ 2. **TypeScript First**: Use the exported types (e.g., `ButtonProps`, `DataTableColumn<T>`) to ensure type safety.
15
+ 3. **Style Import at App Root Only**: Add `import 'flysoft-react-ui/styles';` once at the app root. Never import CSS in individual components.
16
+ 4. **Do Not Use Docs Internals**: Never import or reference anything from `docs/*` or `src/docs/*`.
17
+ 5. **FontAwesome 5 Only**: Use `fa-*` icon classes. Components normalize to light style (`fal`) automatically. Never use other icon libraries.
18
+ 6. **Theme CSS Variables**: Use `var(--color-*)`, `var(--shadow-*)`, `var(--radius-*)`, `var(--font-*)` for custom styling. Never hardcode colors.
19
+
20
+ ---
21
+
22
+ ## Form Controls
23
+
24
+ ### Button
25
+
26
+ Customizable button with variants, colors, icons, and ripple effect.
27
+
28
+ ```typescript
29
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
30
+ variant?: "primary" | "outline" | "ghost"; // default: "primary"
31
+ size?: "sm" | "md" | "lg"; // default: "md"
32
+ color?: "primary" | "secondary" | "success" | "warning" | "danger" | "info"; // default: "primary"
33
+ bg?: string; // Custom background color (hex, rgb, rgba, hsl, or color name)
34
+ textColor?: string; // Custom text color
35
+ icon?: string; // FontAwesome icon class (e.g. "fa-save")
36
+ iconPosition?: "left" | "right"; // default: "left"
37
+ loading?: boolean; // Shows spinner, disables button. default: false
38
+ children?: React.ReactNode;
39
+ }
40
+ ```
41
+
42
+ ```tsx
43
+ <Button variant="primary" icon="fa-save" loading={isLoading} onClick={handleSave}>
44
+ Guardar
45
+ </Button>
46
+ <Button variant="outline" color="danger" icon="fa-trash">Eliminar</Button>
47
+ <Button variant="ghost" size="sm">Cancelar</Button>
48
+ <Button bg="#8b5cf6" textColor="#fff">Custom Color</Button>
49
+ ```
50
+
51
+ ### LinkButton
52
+
53
+ Anchor-styled button that uses React Router `<Link>` for internal routes and `<a>` for external URLs.
54
+
55
+ ```typescript
56
+ interface LinkButtonProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
57
+ to: string; // Route or URL (required)
58
+ target?: string;
59
+ variant?: "primary" | "outline" | "ghost"; // default: "primary"
60
+ size?: "sm" | "md" | "lg"; // default: "md"
61
+ color?: "primary" | "secondary" | "success" | "warning" | "danger" | "info";
62
+ bg?: string;
63
+ textColor?: string;
64
+ icon?: string;
65
+ iconPosition?: "left" | "right"; // default: "left"
66
+ children?: React.ReactNode;
67
+ }
68
+ ```
69
+
70
+ ```tsx
71
+ <LinkButton to="/users" icon="fa-users">Ver Usuarios</LinkButton>
72
+ <LinkButton to="https://example.com" target="_blank">Sitio Externo</LinkButton>
73
+ ```
74
+
75
+ ### Input
76
+
77
+ Text input with labels, icons, error states, and ref forwarding.
78
+
79
+ ```typescript
80
+ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
81
+ label?: string; // Label text above input
82
+ error?: string; // Error message below input
83
+ icon?: string; // FontAwesome icon class
84
+ iconPosition?: "left" | "right"; // default: "left"
85
+ size?: "sm" | "md" | "lg"; // default: "md"
86
+ children?: React.ReactNode;
87
+ onIconClick?: (event: React.MouseEvent<HTMLElement>) => void; // Makes icon clickable
88
+ readOnly?: boolean; // Read-only without disabled appearance
89
+ }
90
+ ```
91
+
92
+ ```tsx
93
+ <Input label="Email" type="email" icon="fa-envelope" placeholder="usuario@email.com" />
94
+ <Input label="Búsqueda" icon="fa-search" iconPosition="right" onIconClick={handleSearch} />
95
+ <Input label="Nombre" error="Campo requerido" />
96
+ ```
97
+
98
+ ### AutocompleteInput
99
+
100
+ Searchable dropdown with single and multiple selection support.
101
+
102
+ ```typescript
103
+ interface AutocompleteOption {
104
+ label: string;
105
+ value: string;
106
+ description?: string | number;
107
+ icon?: string;
108
+ }
109
+
110
+ interface AutocompleteInputProps<T = AutocompleteOption, K = string>
111
+ extends Omit<InputProps, "onChange" | "value" | "ref"> {
112
+ options: T[]; // Options array (required)
113
+ value?: string | string[]; // String for single, array for multiple
114
+ onChange?: ((value: string | string[]) => void) | React.ChangeEventHandler<HTMLInputElement>;
115
+ onSelectOption?: (option: T, value: K) => void;
116
+ noResultsText?: string; // default: "Sin resultados"
117
+ getOptionLabel?: (item: T) => string;
118
+ getOptionValue?: (item: T) => K;
119
+ getOptionDescription?: (item: T) => string | number | undefined;
120
+ renderOption?: (item: T) => React.ReactNode;
121
+ readOnly?: boolean;
122
+ multiple?: boolean; // Multi-select with checkboxes. default: false
123
+ }
124
+ ```
125
+
126
+ ```tsx
127
+ // Single selection
128
+ <AutocompleteInput
129
+ label="País"
130
+ options={[{ label: "Argentina", value: "AR" }, { label: "Brasil", value: "BR" }]}
131
+ value={selectedCountry}
132
+ onChange={setSelectedCountry}
133
+ />
134
+
135
+ // Multiple selection
136
+ <AutocompleteInput
137
+ label="Categorías"
138
+ options={categories}
139
+ multiple
140
+ value={selectedCategories}
141
+ onChange={setSelectedCategories}
142
+ />
143
+
144
+ // Custom objects
145
+ <AutocompleteInput<User, number>
146
+ label="Usuario"
147
+ options={users}
148
+ getOptionLabel={(u) => u.fullName}
149
+ getOptionValue={(u) => u.id}
150
+ getOptionDescription={(u) => u.email}
151
+ />
152
+ ```
153
+
154
+ ### SearchSelectInput
155
+
156
+ Opens a dialog modal for selecting from async search results. Ideal for large datasets.
157
+
158
+ ```typescript
159
+ interface SearchSelectOption {
160
+ label: string;
161
+ value?: string;
162
+ description?: string | number;
163
+ icon?: string;
164
+ }
165
+
166
+ interface SearchSelectInputProps<T = SearchSelectOption, K = string>
167
+ extends Omit<InputProps, "onChange" | "value" | "ref"> {
168
+ value?: T | K | string;
169
+ onChange?: ((value: T | K) => void) | React.ChangeEventHandler<HTMLInputElement>;
170
+ onSearchPromiseFn: (text: string) => Promise<Array<T> | PaginationInterface<T>>; // required
171
+ onSingleSearchPromiseFn: (value: K) => Promise<T | undefined>; // required
172
+ onSelectOption?: (option: T, value: K) => void;
173
+ dialogTitle?: string; // default: "Seleccione una opción"
174
+ icon?: string; // default: "fa-search"
175
+ iconPosition?: "left" | "right"; // default: "right"
176
+ noResultsText?: string; // default: "Sin resultados"
177
+ getOptionLabel?: (item: T) => string;
178
+ getOptionValue?: (item: T) => K;
179
+ getOptionDescription?: (item: T) => string | number | undefined;
180
+ renderOption?: (item: T) => React.ReactNode;
181
+ readOnly?: boolean;
182
+ }
183
+ ```
184
+
185
+ ```tsx
186
+ <SearchSelectInput<Product, number>
187
+ label="Producto"
188
+ onSearchPromiseFn={(text) => apiClient.get({ url: `/api/products?q=${text}` })}
189
+ onSingleSearchPromiseFn={(id) => apiClient.get({ url: `/api/products/${id}` })}
190
+ getOptionLabel={(p) => p.name}
191
+ getOptionValue={(p) => p.id}
192
+ getOptionDescription={(p) => `$${p.price}`}
193
+ onChange={(value) => setProductId(value)}
194
+ />
195
+ ```
196
+
197
+ ### DatePicker
198
+
199
+ Standalone calendar component for date selection.
200
+
201
+ ```typescript
202
+ interface DatePickerProps {
203
+ value?: Dayjs | null;
204
+ onChange?: (date: Dayjs) => void;
205
+ initialViewDate?: Dayjs; // Initial month/year when value is null
206
+ startWeekOn?: "monday" | "sunday"; // default: "sunday"
207
+ className?: string;
208
+ }
209
+ ```
210
+
211
+ ### DateInput
212
+
213
+ Input field with integrated DatePicker dropdown. Accepts manual text and Dayjs objects.
214
+
215
+ ```typescript
216
+ type DateInputFormat = "dd/mm/yyyy" | "mm/dd/yyyy";
217
+
218
+ interface DateInputProps extends Omit<InputProps, "type" | "value" | "onChange" | "ref"> {
219
+ value?: Dayjs | null | string;
220
+ onChange?: ((date: Dayjs | null) => void) | React.ChangeEventHandler<HTMLInputElement>;
221
+ format?: DateInputFormat; // default: "dd/mm/yyyy"
222
+ datePickerProps?: Omit<DatePickerProps, "value" | "onChange">;
223
+ readOnly?: boolean;
224
+ }
225
+ ```
226
+
227
+ ```tsx
228
+ <DateInput label="Fecha de nacimiento" value={birthDate} onChange={setBirthDate} />
229
+ <DateInput label="Start Date" format="mm/dd/yyyy" />
230
+ ```
231
+
232
+ ### Checkbox
233
+
234
+ Boolean checkbox with label and error support. Ref forwarding supported.
235
+
236
+ ```typescript
237
+ interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
238
+ label?: string;
239
+ labelPosition?: "left" | "right"; // default: "right"
240
+ error?: string;
241
+ size?: "sm" | "md" | "lg"; // default: "md"
242
+ readOnly?: boolean;
243
+ }
244
+ ```
245
+
246
+ ```tsx
247
+ <Checkbox label="Acepto los términos" checked={accepted} onChange={handleChange} />
248
+ <Checkbox label="Activo" size="lg" readOnly />
249
+ ```
250
+
251
+ ### RadioButtonGroup
252
+
253
+ Single selection from a group of radio options.
254
+
255
+ ```typescript
256
+ interface RadioOption {
257
+ label: string;
258
+ value: string | number;
259
+ disabled?: boolean;
260
+ }
261
+
262
+ interface RadioButtonGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "children"> {
263
+ options: RadioOption[]; // required
264
+ value?: string | number;
265
+ onChange?: ((value: string | number) => void) | React.ChangeEventHandler<HTMLInputElement>;
266
+ labelPosition?: "left" | "right"; // default: "right"
267
+ size?: "sm" | "md" | "lg"; // default: "md"
268
+ error?: string;
269
+ direction?: "vertical" | "horizontal"; // default: "vertical"
270
+ gap?: "sm" | "md" | "lg"; // default: "md"
271
+ name?: string;
272
+ disabled?: boolean;
273
+ onBlur?: (() => void) | React.FocusEventHandler<HTMLInputElement>;
274
+ readOnly?: boolean;
275
+ }
276
+ ```
277
+
278
+ ```tsx
279
+ <RadioButtonGroup
280
+ options={[
281
+ { label: "Masculino", value: "M" },
282
+ { label: "Femenino", value: "F" },
283
+ { label: "Otro", value: "O" },
284
+ ]}
285
+ value={gender}
286
+ onChange={setGender}
287
+ direction="horizontal"
288
+ />
289
+ ```
290
+
291
+ ### CurrencyInput
292
+
293
+ Numeric input with currency formatting (Argentine locale: 1.234,56). Ref forwarding supported.
294
+
295
+ ```typescript
296
+ interface CurrencyInputProps extends Omit<InputProps, "value" | "onChange" | "type"> {
297
+ value?: number | null;
298
+ onChange?: (value: any) => void; // Receives parsed numeric value
299
+ }
300
+ ```
301
+
302
+ ```tsx
303
+ <CurrencyInput label="Monto" value={amount} onChange={setAmount} icon="fa-dollar-sign" />
304
+ ```
305
+
306
+ ### Pagination
307
+
308
+ URL-based pagination controls using react-router-dom's `useSearchParams`.
309
+
310
+ ```typescript
311
+ interface PaginationProps {
312
+ fieldName?: string; // URL param name. default: "pagina"
313
+ page?: number; // default: 1
314
+ pages?: number; // default: 1
315
+ total?: number; // default: 0
316
+ isLoading?: boolean; // default: false
317
+ }
318
+ ```
319
+
320
+ ```tsx
321
+ <Pagination page={currentPage} pages={totalPages} total={totalItems} />
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Layout Components
327
+
328
+ ### Card
329
+
330
+ Generic container with header, content, footer, and variants.
331
+
332
+ ```typescript
333
+ interface CardProps {
334
+ title?: string | React.ReactNode;
335
+ subtitle?: string | React.ReactNode;
336
+ children?: React.ReactNode;
337
+ className?: string;
338
+ headerActions?: React.ReactNode;
339
+ footer?: React.ReactNode;
340
+ variant?: "default" | "elevated" | "outlined"; // default: "default"
341
+ alwaysDisplayHeaderActions?: boolean; // default: false (shows on hover on lg+)
342
+ headerClassName?: string;
343
+ contentClassName?: string;
344
+ footerClassName?: string;
345
+ compact?: boolean; // Reduced padding. default: false
346
+ }
347
+ ```
348
+
349
+ ```tsx
350
+ <Card title="Usuarios" headerActions={<Button size="sm" icon="fa-plus">Nuevo</Button>}>
351
+ <p>Contenido</p>
352
+ </Card>
353
+ <Card variant="elevated" compact footer={<Button variant="primary">Guardar</Button>}>
354
+ <Input label="Nombre" />
355
+ </Card>
356
+ ```
357
+
358
+ ### AppLayout
359
+
360
+ Main application layout with responsive navbar and sidebar drawer.
361
+
362
+ ```typescript
363
+ interface AppLayoutProps {
364
+ navbar?: NavbarInterface;
365
+ leftDrawer?: LeftDrawerInterface;
366
+ contentFooter?: React.ReactNode;
367
+ children: React.ReactNode; // required
368
+ className?: string;
369
+ }
370
+
371
+ interface NavbarInterface {
372
+ navBarLeftNode?: React.ReactNode;
373
+ navBarRightNode?: React.ReactNode;
374
+ fullWidthNavbar?: boolean; // Fixed full-width (true) or relative (false)
375
+ height?: string; // default: "64px"
376
+ className?: string;
377
+ }
378
+
379
+ interface LeftDrawerInterface {
380
+ headerNode?: React.ReactNode;
381
+ contentNode?: React.ReactNode;
382
+ footerNode?: React.ReactNode;
383
+ className?: string;
384
+ width?: string; // default: "256px"
385
+ }
386
+ ```
387
+
388
+ ```tsx
389
+ <AppLayout
390
+ navbar={{
391
+ navBarLeftNode: <h1>Mi App</h1>,
392
+ navBarRightNode: <Avatar text="Admin" />,
393
+ fullWidthNavbar: true,
394
+ }}
395
+ leftDrawer={{
396
+ headerNode: <h2>Menú</h2>,
397
+ contentNode: <nav>...</nav>,
398
+ }}
399
+ >
400
+ <main>Contenido</main>
401
+ </AppLayout>
402
+ ```
403
+
404
+ **Behaviors**: Navbar auto-hides/shows on scroll. Mobile drawer with overlay. Responsive breakpoints.
405
+
406
+ ### Collection
407
+
408
+ Flex container for rendering lists of items.
409
+
410
+ ```typescript
411
+ interface CollectionProps {
412
+ children: React.ReactNode; // required
413
+ gap?: string; // CSS gap value. default: "1rem"
414
+ direction?: "column" | "row"; // default: "column"
415
+ wrap?: boolean; // default: false
416
+ className?: string;
417
+ }
418
+ ```
419
+
420
+ ### DataField
421
+
422
+ Label + value pair display for detail views.
423
+
424
+ ```typescript
425
+ interface DataFieldProps {
426
+ label?: string;
427
+ value?: string | number | React.ReactNode;
428
+ inline?: boolean; // Horizontal layout. default: false
429
+ align?: "left" | "right" | "center"; // default: "left"
430
+ title?: string; // HTML title tooltip
431
+ link?: string; // Opens URL in new tab
432
+ className?: string;
433
+ labelClassName?: string;
434
+ }
435
+ ```
436
+
437
+ ```tsx
438
+ <DataField label="Nombre" value="Juan Pérez" />
439
+ <DataField label="Email" value="juan@email.com" link="mailto:juan@email.com" inline />
440
+ ```
441
+
442
+ ### TabsGroup / TabPanel
443
+
444
+ Tabbed interfaces with optional URL persistence.
445
+
446
+ ```typescript
447
+ interface Tab {
448
+ id: string | number;
449
+ label: string;
450
+ }
451
+
452
+ interface TabsGroupProps {
453
+ children?: React.ReactNode;
454
+ tabs: Tab[]; // required
455
+ paramName?: string; // URL search param for persistence
456
+ headerNode?: React.ReactNode; // Right-aligned header content
457
+ onChangeTab?: (selectedTab: string) => void;
458
+ }
459
+
460
+ interface TabPanelProps {
461
+ children?: React.ReactNode;
462
+ tabId: string | number; // Must match a Tab.id (required)
463
+ }
464
+ ```
465
+
466
+ ```tsx
467
+ <TabsGroup tabs={[{ id: "info", label: "Información" }, { id: "history", label: "Historial" }]}>
468
+ <TabPanel tabId="info">
469
+ <p>Información del usuario</p>
470
+ </TabPanel>
471
+ <TabPanel tabId="history">
472
+ <p>Historial de actividad</p>
473
+ </TabPanel>
474
+ </TabsGroup>
475
+ ```
476
+
477
+ ### DataTable\<T\>
478
+
479
+ High-performance data table with sorting, formatting, actions, and skeleton loading.
480
+
481
+ ```typescript
482
+ interface DataTableColumn<T> {
483
+ align?: "left" | "right" | "center"; // Auto-set for date/currency/numeric
484
+ width?: string;
485
+ header?: string | React.ReactNode;
486
+ footer?: string | React.ReactNode;
487
+ value?: string | number | ((row: T) => string | React.ReactNode);
488
+ tooltip?: (row: T) => string | React.ReactNode;
489
+ type?: "text" | "numeric" | "currency" | "date";
490
+ actions?: (row: T) => Array<React.ReactNode>;
491
+ headerActions?: () => Array<React.ReactNode>;
492
+ }
493
+
494
+ interface DataTableProps<T> {
495
+ columns: DataTableColumn<T>[]; // required
496
+ rows: T[]; // required
497
+ className?: string;
498
+ maxRows?: number; // Enables sticky header with scroll
499
+ locale?: string; // default: "es-AR"
500
+ isLoading?: boolean; // Shows skeleton rows. default: false
501
+ loadingRows?: number; // default: 5
502
+ rowClassName?: (row: T) => string;
503
+ headerClassName?: string;
504
+ footerClassName?: string;
505
+ headerCellClassName?: string;
506
+ footerCellClassName?: string;
507
+ cellClassName?: string | ((row: T, column: DataTableColumn<T>) => string);
508
+ compact?: boolean; // default: false
509
+ }
510
+ ```
511
+
512
+ ```tsx
513
+ interface User { id: number; name: string; salary: number; createdAt: string; }
514
+
515
+ const columns: DataTableColumn<User>[] = [
516
+ { header: "ID", value: "id", width: "60px" },
517
+ { header: "Nombre", value: (row) => row.name },
518
+ { header: "Salario", value: "salary", type: "currency" },
519
+ { header: "Fecha", value: "createdAt", type: "date" },
520
+ {
521
+ header: "Acciones",
522
+ actions: (row) => [
523
+ <Button key="edit" variant="ghost" size="sm" icon="fa-edit" onClick={() => edit(row)}>Editar</Button>,
524
+ <Button key="del" variant="ghost" size="sm" icon="fa-trash" color="danger" onClick={() => del(row)}>Eliminar</Button>,
525
+ ],
526
+ },
527
+ ];
528
+
529
+ <DataTable<User> columns={columns} rows={users} isLoading={loading} maxRows={10} />
530
+ ```
531
+
532
+ **Type formatting**: `currency` → thousands separator, no symbol. `numeric` → locale formatting. `date` → DD/MM/YYYY.
533
+
534
+ ### Accordion
535
+
536
+ Collapsible content section with smooth animation.
537
+
538
+ ```typescript
539
+ interface AccordionProps {
540
+ title: string | React.ReactNode; // required
541
+ children: React.ReactNode; // required
542
+ icon?: string; // FontAwesome icon
543
+ rightNode?: React.ReactNode;
544
+ defaultOpen?: boolean; // default: false
545
+ className?: string;
546
+ variant?: "default" | "elevated" | "outlined"; // default: "default"
547
+ onToggle?: (isOpen: boolean) => void;
548
+ }
549
+ ```
550
+
551
+ ```tsx
552
+ <Accordion title="Detalles" icon="fa-info-circle" defaultOpen>
553
+ <p>Contenido colapsable</p>
554
+ </Accordion>
555
+ ```
556
+
557
+ ### Menu
558
+
559
+ Simple menu list for displaying options.
560
+
561
+ ```typescript
562
+ interface MenuProps<T = { label: string }> {
563
+ options: T[]; // required
564
+ onOptionSelected: (item: T) => void; // required
565
+ getOptionLabel?: (item: T) => string;
566
+ renderOption?: (item: T) => React.ReactNode;
567
+ className?: string;
568
+ style?: React.CSSProperties;
569
+ itemClassName?: string;
570
+ }
571
+ ```
572
+
573
+ ### DropdownMenu
574
+
575
+ Portal-based dropdown menu triggered by a button. Auto-positions above/below.
576
+
577
+ ```typescript
578
+ interface DropdownMenuProps<T = { label: string }> {
579
+ options: T[]; // required
580
+ onOptionSelected: (item: T) => void; // required
581
+ renderNode?: React.ReactNode; // Custom trigger (default: ellipsis icon button)
582
+ getOptionLabel?: (item: T) => string;
583
+ renderOption?: (item: T) => React.ReactNode;
584
+ replaceOnSingleOption?: boolean; // Show single option inline. default: false
585
+ openOnHover?: boolean; // default: false
586
+ }
587
+ ```
588
+
589
+ ```tsx
590
+ <DropdownMenu
591
+ options={[{ label: "Editar" }, { label: "Eliminar" }]}
592
+ onOptionSelected={(item) => handleAction(item.label)}
593
+ renderNode={<Button variant="ghost" icon="fa-cog" size="sm" />}
594
+ />
595
+ ```
596
+
597
+ ### DropdownPanel
598
+
599
+ Portal-based dropdown that renders arbitrary content (not a list).
600
+
601
+ ```typescript
602
+ interface DropdownPanelProps {
603
+ renderNode?: React.ReactNode; // Custom trigger (default: ellipsis icon button)
604
+ children: React.ReactNode; // required
605
+ openOnHover?: boolean; // default: false
606
+ }
607
+ ```
608
+
609
+ ### Filter
610
+
611
+ Versatile filtering component with multiple filter types and optional URL persistence.
612
+
613
+ ```typescript
614
+ // Discriminated union by filterType
615
+ type FilterProps =
616
+ | TextFilterProps // filterType?: "text" (default)
617
+ | NumberFilterProps // filterType: "number" (+ min?, max?)
618
+ | DateFilterProps // filterType: "date"
619
+ | AutocompleteFilterProps // filterType: "autocomplete" (+ options, multiple?)
620
+ | SearchFilterProps // filterType: "search"
621
+ | SearchSelectFilterProps // filterType: "searchSelect" (+ onSearchPromiseFn, onSingleSearchPromiseFn)
622
+
623
+ // Common props for all filter types:
624
+ interface BaseFilterProps {
625
+ paramName?: string; // URL search param for persistence
626
+ label?: string;
627
+ staticOptions?: Array<{ text: string; value: string }>;
628
+ inputWidth?: string;
629
+ value?: string; // Controlled value
630
+ onChange?: (value: string | undefined) => void;
631
+ hideEmpty?: boolean; // default: false
632
+ disabled?: boolean; // default: false
633
+ }
634
+ ```
635
+
636
+ ```tsx
637
+ <Filter filterType="text" paramName="nombre" label="Nombre" />
638
+ <Filter filterType="number" paramName="edad" label="Edad" min={0} max={120} />
639
+ <Filter filterType="date" paramName="fecha" label="Fecha" />
640
+ <Filter
641
+ filterType="autocomplete"
642
+ paramName="estado"
643
+ label="Estado"
644
+ options={[{ label: "Activo", value: "1" }, { label: "Inactivo", value: "0" }]}
645
+ />
646
+ <Filter
647
+ filterType="searchSelect"
648
+ paramName="cliente"
649
+ label="Cliente"
650
+ onSearchPromiseFn={(text) => apiClient.get({ url: `/api/clients?q=${text}` })}
651
+ onSingleSearchPromiseFn={(id) => apiClient.get({ url: `/api/clients/${id}` })}
652
+ />
653
+ ```
654
+
655
+ ---
656
+
657
+ ## Utility Components
658
+
659
+ ### Badge
660
+
661
+ Status/category label with variants and custom colors.
662
+
663
+ ```typescript
664
+ interface BadgeProps {
665
+ children: React.ReactNode; // required
666
+ variant?: "primary" | "secondary" | "success" | "warning" | "danger" | "info"; // default: "primary"
667
+ size?: "sm" | "md" | "lg"; // default: "md"
668
+ rounded?: boolean; // Full border radius. default: false
669
+ className?: string;
670
+ icon?: string;
671
+ iconPosition?: "left" | "right"; // default: "left"
672
+ iconLabel?: string; // aria-label for icon
673
+ bg?: string; // Custom background color
674
+ textColor?: string; // Custom text color
675
+ onClick?: (event: React.MouseEvent<HTMLElement>) => void;
676
+ }
677
+ ```
678
+
679
+ ```tsx
680
+ <Badge variant="success" icon="fa-check">Activo</Badge>
681
+ <Badge variant="danger" rounded>3</Badge>
682
+ <Badge bg="#8b5cf6" textColor="#fff">Custom</Badge>
683
+ ```
684
+
685
+ ### Avatar
686
+
687
+ User profile display with initials fallback when image fails.
688
+
689
+ ```typescript
690
+ interface AvatarProps {
691
+ text: string; // Name for initials extraction (required)
692
+ image?: string; // Image URL
693
+ bgColor?: string; // default: "#4b5563"
694
+ textColor?: string; // default: "#ffffff"
695
+ size?: "sm" | "md" | "lg"; // default: "md" (sm=32px, md=40px, lg=48px)
696
+ className?: string;
697
+ }
698
+ ```
699
+
700
+ ```tsx
701
+ <Avatar text="Juan Pérez" image="/avatars/juan.jpg" />
702
+ <Avatar text="Admin User" bgColor="#3b82f6" size="lg" />
703
+ ```
704
+
705
+ ### RoadMap
706
+
707
+ Progress/stage visualization with connected circles and gradient lines.
708
+
709
+ ```typescript
710
+ interface RoadMapStage {
711
+ name: string; // required
712
+ description?: string;
713
+ icon?: string;
714
+ disabled?: boolean; // Grayed out at 50% opacity
715
+ variant?: "primary" | "secondary" | "success" | "warning" | "danger" | "info";
716
+ bg?: string; // Custom color (overrides variant)
717
+ }
718
+
719
+ interface RoadMapProps {
720
+ stages: RoadMapStage[]; // required
721
+ className?: string;
722
+ }
723
+ ```
724
+
725
+ ```tsx
726
+ <RoadMap stages={[
727
+ { name: "Creado", icon: "fa-plus", variant: "info" },
728
+ { name: "En Proceso", icon: "fa-cog", variant: "warning" },
729
+ { name: "Completado", icon: "fa-check", variant: "success" },
730
+ { name: "Archivado", icon: "fa-archive", disabled: true },
731
+ ]} />
732
+ ```
733
+
734
+ ### Dialog
735
+
736
+ Modal window with overlay, escape-to-close, and scroll lock.
737
+
738
+ ```typescript
739
+ interface DialogProps {
740
+ isOpen: boolean; // required
741
+ title: React.ReactNode; // required
742
+ children: React.ReactNode; // required
743
+ footer?: React.ReactNode;
744
+ onClose?: () => void;
745
+ closeOnOverlayClick?: boolean; // default: false
746
+ compact?: boolean; // default: false
747
+ }
748
+ ```
749
+
750
+ ```tsx
751
+ <Dialog isOpen={showDialog} title="Confirmar" onClose={() => setShowDialog(false)}
752
+ footer={
753
+ <>
754
+ <Button variant="ghost" onClick={() => setShowDialog(false)}>Cancelar</Button>
755
+ <Button variant="primary" color="danger" onClick={handleDelete}>Eliminar</Button>
756
+ </>
757
+ }
758
+ >
759
+ <p>¿Está seguro que desea eliminar este registro?</p>
760
+ </Dialog>
761
+ ```
762
+
763
+ ### Loader
764
+
765
+ Loading indicator with progress bar. Can wrap content with overlay.
766
+
767
+ ```typescript
768
+ interface LoaderProps {
769
+ isLoading?: boolean; // default: false
770
+ text?: string; // Text below progress bar
771
+ children?: React.ReactNode;
772
+ keepContentWhileLoading?: boolean; // Show content faded at 50% opacity
773
+ contentLoadingNode?: React.ReactNode; // Custom loading content
774
+ overlayClassName?: string; // default: "bg-black/50 backdrop-blur-sm"
775
+ }
776
+ ```
777
+
778
+ ```tsx
779
+ <Loader isLoading={loading} text="Cargando datos...">
780
+ <DataTable ... />
781
+ </Loader>
782
+ <Loader isLoading={loading} keepContentWhileLoading>
783
+ <Card>...</Card>
784
+ </Loader>
785
+ ```
786
+
787
+ ### FiltersDialog
788
+
789
+ Dialog that groups multiple Filter components. Syncs values from/to URL search params.
790
+
791
+ ```typescript
792
+ interface FilterConfig {
793
+ filterType: "text" | "number" | "date" | "autocomplete";
794
+ paramName: string; // required
795
+ label?: string;
796
+ staticOptions?: Array<{ text: string; value: string }>;
797
+ inputWidth?: string;
798
+ min?: number; // For number filters
799
+ max?: number; // For number filters
800
+ options?: any[]; // For autocomplete
801
+ getOptionLabel?: (item: any) => string;
802
+ getOptionValue?: (item: any) => any;
803
+ renderOption?: (item: any) => React.ReactNode;
804
+ noResultsText?: string;
805
+ }
806
+
807
+ interface FiltersDialogProps {
808
+ filters: FilterConfig[]; // required
809
+ }
810
+ ```
811
+
812
+ ```tsx
813
+ <FiltersDialog filters={[
814
+ { filterType: "text", paramName: "nombre", label: "Nombre" },
815
+ { filterType: "number", paramName: "edad", label: "Edad", min: 0, max: 120 },
816
+ { filterType: "date", paramName: "fecha", label: "Fecha" },
817
+ { filterType: "autocomplete", paramName: "estado", label: "Estado",
818
+ options: [{ label: "Activo", value: "1" }, { label: "Inactivo", value: "0" }] },
819
+ ]} />
820
+ ```
821
+
822
+ ### Snackbar / SnackbarContainer
823
+
824
+ Toast notification system. SnackbarContainer must be at the app root.
825
+
826
+ ```typescript
827
+ interface SnackbarContainerProps {
828
+ position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | "top-center" | "bottom-center"; // default: "top-right"
829
+ maxSnackbars?: number; // default: 5
830
+ }
831
+
832
+ // Usage via hook (not direct Snackbar component):
833
+ const { showSnackbar } = useSnackbar();
834
+ showSnackbar("Operación exitosa", "success");
835
+ showSnackbar("Error al guardar", "danger", { duration: 5000, icon: "fa-exclamation" });
836
+ ```
837
+
838
+ **Variants**: `"primary"` | `"secondary"` | `"success"` | `"warning"` | `"danger"` | `"info"`
839
+ **Default icons**: success=fa-check-circle, danger=fa-times-circle, warning=fa-exclamation-triangle, info/primary/secondary=fa-info-circle
840
+
841
+ ### Skeleton
842
+
843
+ Loading placeholder with pulse animation. Fully customizable via className.
844
+
845
+ ```typescript
846
+ interface SkeletonProps {
847
+ className?: string; // Tailwind classes to control width, height, shape
848
+ }
849
+ ```
850
+
851
+ ```tsx
852
+ <Skeleton className="h-4 w-3/4" /> {/* Text line */}
853
+ <Skeleton className="h-10 w-full" /> {/* Input placeholder */}
854
+ <Skeleton className="h-32 w-32 rounded-full" /> {/* Avatar placeholder */}
855
+ ```
856
+
857
+ ### ThemeSwitcher
858
+
859
+ Self-contained theme toggle. No props. Displays available themes with switch buttons and current theme info.
860
+
861
+ ```tsx
862
+ <ThemeSwitcher />
863
+ ```
864
+
865
+ ---
866
+
867
+ ## Contexts & State Management
868
+
869
+ ### ThemeProvider / useTheme
870
+
871
+ Manages application theme with CSS variable injection, presets, and localStorage persistence.
872
+
873
+ ```typescript
874
+ // Provider props
875
+ interface ThemeProviderProps {
876
+ children: ReactNode;
877
+ initialTheme?: string | Theme; // default: "light"
878
+ storageKey?: string; // localStorage key. default: "flysoft-theme"
879
+ forceInitialTheme?: boolean; // Ignore localStorage. default: false
880
+ onThemeChange?: (theme: Theme) => void;
881
+ }
882
+
883
+ // Hook return
884
+ interface ThemeContextType {
885
+ theme: Theme; // Current theme object
886
+ setTheme: (theme: Theme | string) => void; // Switch theme by name or object
887
+ updateTheme: (updates: Partial<Theme> | ((prev: Theme) => Theme)) => void;
888
+ currentThemeName: string;
889
+ availableThemes: string[]; // ["light", "dark", "blue", "green"]
890
+ resetToDefault: () => void;
891
+ isDark: boolean;
892
+ }
893
+ ```
894
+
895
+ ```tsx
896
+ // App root
897
+ <ThemeProvider initialTheme="light">
898
+ <App />
899
+ </ThemeProvider>
900
+
901
+ // In components
902
+ const { theme, setTheme, isDark } = useTheme();
903
+ <Button onClick={() => setTheme(isDark ? "light" : "dark")}>Toggle Theme</Button>
904
+ ```
905
+
906
+ **Preset themes**: `lightTheme`, `darkTheme`, `blueTheme`, `greenTheme` (importable).
907
+
908
+ ### AuthProvider / AuthContext
909
+
910
+ Manages authentication with automatic token validation and refresh.
911
+
912
+ ```typescript
913
+ interface AuthProviderProps {
914
+ children: React.ReactNode;
915
+ getToken: (username: string, password: string) => Promise<AuthTokenInterface>; // required
916
+ getUserData: (auth: AuthTokenInterface) => Promise<AuthContextUserInterface>; // required
917
+ refreshToken?: (auth: AuthTokenInterface) => Promise<AuthTokenInterface>;
918
+ removeToken?: (auth: AuthTokenInterface) => Promise<void>;
919
+ showLog?: boolean; // default: false
920
+ }
921
+
922
+ interface AuthContextType {
923
+ user: AuthContextUserInterface | null;
924
+ login: (username: string, password: string) => Promise<void>;
925
+ logout: () => void;
926
+ isAuthenticated: boolean;
927
+ isLoading: boolean;
928
+ }
929
+
930
+ interface AuthContextUserInterface {
931
+ id?: number | string;
932
+ name?: string;
933
+ aditionalData?: any;
934
+ token?: AuthTokenInterface;
935
+ }
936
+
937
+ interface AuthTokenInterface {
938
+ accessToken?: string;
939
+ expires?: string; // ISO 8601
940
+ tokenType?: string;
941
+ refreshToken?: string;
942
+ aditionalData?: any;
943
+ }
944
+ ```
945
+
946
+ ```tsx
947
+ <AuthProvider
948
+ getToken={async (user, pass) => {
949
+ const res = await apiClient.post({ url: "/auth/login", body: { user, pass } });
950
+ return res.token;
951
+ }}
952
+ getUserData={async (auth) => {
953
+ return await apiClient.get({ url: "/auth/me" });
954
+ }}
955
+ refreshToken={async (auth) => {
956
+ return await apiClient.post({ url: "/auth/refresh", body: { token: auth.refreshToken } });
957
+ }}
958
+ >
959
+ <App />
960
+ </AuthProvider>
961
+
962
+ // In components
963
+ const { user, login, logout, isAuthenticated } = useContext(AuthContext);
964
+ ```
965
+
966
+ **Behaviors**: Validates token on mount. Checks expiration every 60s. Auto-refreshes if `refreshToken` provided. Stores in localStorage as `"auth"`.
967
+
968
+ ### CrudProvider / useCrud\<T\>
969
+
970
+ Generic CRUD context with automatic pagination, URL parameter sync, and snackbar notifications.
971
+
972
+ ```typescript
973
+ interface CrudProviderProps<T> {
974
+ children: ReactNode;
975
+ getPromise?: Function | { execute: Function; successMessage?: string; errorMessage?: string | ((error: any) => string) };
976
+ getItemPromise?: Function | { execute: Function; successMessage?: string; errorMessage?: string | ((error: any) => string) };
977
+ postPromise?: Function | { execute: Function; successMessage?: string; errorMessage?: string | ((error: any) => string) };
978
+ putPromise?: Function | { execute: Function; successMessage?: string; errorMessage?: string | ((error: any) => string) };
979
+ deletePromise?: Function | { execute: Function; successMessage?: string; errorMessage?: string | ((error: any) => string) };
980
+ urlParams?: Array<string>; // URL params to watch. default: []
981
+ limit?: number; // Items per page. default: 15
982
+ pageParam?: string; // URL page param. default: "pagina"
983
+ singleItemId?: string | number;
984
+ extraData?: Record<string, any>;
985
+ }
986
+
987
+ interface CrudContextType<T> {
988
+ list: Array<T> | undefined;
989
+ item: T | undefined;
990
+ page: number;
991
+ pages: number;
992
+ total: number;
993
+ limit: number;
994
+ isLoading: boolean;
995
+ pagination: ReactNode; // Pre-built Pagination component
996
+ params: Record<string, any>;
997
+ extraData?: Record<string, any>;
998
+ setExtraData: Dispatch<SetStateAction<Record<string, any> | undefined>>;
999
+ fetchItems: { execute: (params?: Record<string, any>) => Promise<void>; isLoading: boolean };
1000
+ fetchItem: { execute: (params?: Record<string, any> | string | number) => Promise<T | undefined>; isLoading: boolean };
1001
+ createItem: { execute: (item: T) => Promise<T | undefined | null>; isLoading: boolean };
1002
+ updateItem: { execute: (item: T) => Promise<T | undefined | null>; isLoading: boolean };
1003
+ deleteItem: { execute: (item: T) => Promise<void>; isLoading: boolean };
1004
+ }
1005
+ ```
1006
+
1007
+ ```tsx
1008
+ <CrudProvider<User>
1009
+ getPromise={(params) => apiClient.get({ url: "/api/users", params })}
1010
+ getItemPromise={(id) => apiClient.get({ url: `/api/users/${id}` })}
1011
+ postPromise={{ execute: (item) => apiClient.post({ url: "/api/users", body: item }), successMessage: "Usuario creado" }}
1012
+ putPromise={{ execute: (item) => apiClient.put({ url: `/api/users/${item.id}`, body: item }), successMessage: "Usuario actualizado" }}
1013
+ deletePromise={{ execute: (item) => apiClient.del({ url: `/api/users/${item.id}` }), successMessage: "Usuario eliminado" }}
1014
+ urlParams={["nombre", "estado"]}
1015
+ limit={20}
1016
+ >
1017
+ <UserList />
1018
+ </CrudProvider>
1019
+
1020
+ // In child components
1021
+ const { list, isLoading, pagination, createItem, deleteItem } = useCrud<User>();
1022
+ ```
1023
+
1024
+ **Behaviors**: Auto-fetches when URL params change. Resets pagination on filter change. Shows snackbar on success/error.
1025
+
1026
+ ### SnackbarProvider / useSnackbar
1027
+
1028
+ Manages toast notifications.
1029
+
1030
+ ```typescript
1031
+ interface SnackbarActionsType {
1032
+ showSnackbar: (
1033
+ message: string,
1034
+ variant?: SnackbarVariant,
1035
+ options?: { duration?: number; icon?: string; iconLabel?: string }
1036
+ ) => void;
1037
+ removeSnackbar: (id: string) => void;
1038
+ }
1039
+ ```
1040
+
1041
+ ```tsx
1042
+ // App root
1043
+ <SnackbarProvider>
1044
+ <SnackbarContainer position="bottom-right" maxSnackbars={3} />
1045
+ <App />
1046
+ </SnackbarProvider>
1047
+
1048
+ // In components
1049
+ const { showSnackbar } = useSnackbar();
1050
+ showSnackbar("Guardado exitosamente", "success");
1051
+ showSnackbar("Error de conexión", "danger", { duration: 5000 });
1052
+ ```
1053
+
1054
+ ### AppLayoutProvider / useAppLayout
1055
+
1056
+ Combines ThemeProvider + SnackbarProvider + AppLayout into a single provider.
1057
+
1058
+ ```typescript
1059
+ interface AppLayoutProviderProps {
1060
+ children: ReactNode;
1061
+ initialTheme?: string | Theme;
1062
+ storageKey?: string;
1063
+ forceInitialTheme?: boolean;
1064
+ initialNavbar?: NavbarInterface;
1065
+ initialLeftDrawer?: LeftDrawerInterface;
1066
+ initialContentFooter?: ReactNode;
1067
+ className?: string;
1068
+ }
1069
+
1070
+ interface AppLayoutContextType extends ThemeContextType {
1071
+ navbar: NavbarInterface | undefined;
1072
+ leftDrawer: LeftDrawerInterface | undefined;
1073
+ contentFooter: ReactNode | undefined;
1074
+ className: string;
1075
+ setNavbar: Dispatch<SetStateAction<NavbarInterface | undefined>>;
1076
+ setLeftDrawer: Dispatch<SetStateAction<LeftDrawerInterface | undefined>>;
1077
+ setContentFooter: (node: ReactNode | undefined) => void;
1078
+ setClassName: (className: string) => void;
1079
+ setNavBarLeftNode: (node: ReactNode | undefined) => void;
1080
+ setNavbarRightNode: (node: ReactNode | undefined) => void;
1081
+ }
1082
+ ```
1083
+
1084
+ ```tsx
1085
+ <AppLayoutProvider
1086
+ initialTheme="light"
1087
+ initialNavbar={{ navBarLeftNode: <h1>Mi App</h1>, fullWidthNavbar: true }}
1088
+ initialLeftDrawer={{ contentNode: <nav>...</nav> }}
1089
+ >
1090
+ <Routes />
1091
+ </AppLayoutProvider>
1092
+
1093
+ // In pages - dynamically update layout
1094
+ const { setNavBarLeftNode, setNavbarRightNode } = useAppLayout();
1095
+ useEffect(() => {
1096
+ setNavBarLeftNode(<h1>Dashboard</h1>);
1097
+ }, []);
1098
+ ```
1099
+
1100
+ ---
1101
+
1102
+ ## Hooks
1103
+
1104
+ ### useThemeOverride
1105
+
1106
+ Applies granular CSS variable overrides without changing the entire theme.
1107
+
1108
+ ```typescript
1109
+ function useThemeOverride(options?: {
1110
+ scope?: "global" | "local"; // default: "global"
1111
+ element?: HTMLElement | null;
1112
+ prefix?: string; // default: "flysoft"
1113
+ }): {
1114
+ applyOverride: (overrides: Record<string, string | number>) => void;
1115
+ revertOverride: (keys: string[]) => void;
1116
+ revertAllOverrides: () => void;
1117
+ getCSSVariable: (key: string) => string | null;
1118
+ isOverrideApplied: (key: string) => boolean;
1119
+ appliedOverridesCount: number;
1120
+ }
1121
+ ```
1122
+
1123
+ ### useTemporaryOverride
1124
+
1125
+ Applies CSS variable overrides that auto-revert after a duration.
1126
+
1127
+ ```typescript
1128
+ function useTemporaryOverride(
1129
+ overrides: Record<string, string | number>,
1130
+ duration?: number, // default: 3000
1131
+ options?: { scope?: "global" | "local"; element?: HTMLElement | null; prefix?: string }
1132
+ ): { applyTemporaryOverride: () => Function }
1133
+ ```
1134
+
1135
+ ### useBreakpoint
1136
+
1137
+ Returns current viewport breakpoint and device type.
1138
+
1139
+ ```typescript
1140
+ type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
1141
+
1142
+ function useBreakpoint(): {
1143
+ breakpoint: Breakpoint;
1144
+ windowSize: { width: number; height: number };
1145
+ isMobile: boolean; // xs or sm
1146
+ isTablet: boolean; // md
1147
+ isDesktop: boolean; // lg, xl, or 2xl
1148
+ }
1149
+ ```
1150
+
1151
+ ### useElementScroll
1152
+
1153
+ Tracks scroll position and direction with requestAnimationFrame optimization.
1154
+
1155
+ ```typescript
1156
+ function useElementScroll(elementRef: React.RefObject<HTMLElement | null>): {
1157
+ scrollY: number;
1158
+ scrollDirection: "up" | "down" | null;
1159
+ }
1160
+ ```
1161
+
1162
+ ### useAsyncRequest
1163
+
1164
+ Manages async operations with loading state and snackbar notifications.
1165
+
1166
+ ```typescript
1167
+ interface AsyncRequestOptions {
1168
+ successMessage?: string;
1169
+ errorMessage?: string | ((error: any) => string);
1170
+ successVariant?: SnackbarVariant; // default: "success"
1171
+ errorVariant?: SnackbarVariant; // default: "danger"
1172
+ onSuccess?: (data: any) => void;
1173
+ onError?: (error: any) => void;
1174
+ onFinally?: () => void;
1175
+ }
1176
+
1177
+ function useAsyncRequest(options?: AsyncRequestOptions): {
1178
+ isLoading: boolean;
1179
+ execute: <T>(requestFn: () => Promise<T>) => Promise<T | undefined>;
1180
+ setLoading: (loading: boolean) => void;
1181
+ }
1182
+ ```
1183
+
1184
+ ```tsx
1185
+ const { execute, isLoading } = useAsyncRequest({
1186
+ successMessage: "Guardado exitosamente",
1187
+ errorMessage: (err) => getErrorMessage(err),
1188
+ });
1189
+ await execute(() => apiClient.post({ url: "/api/data", body: formData }));
1190
+ ```
1191
+
1192
+ ### useEnum
1193
+
1194
+ Converts TypeScript enums to arrays for form select options.
1195
+
1196
+ ```typescript
1197
+ function useEnum(baseEnum: any): {
1198
+ getArray: () => Array<NameValueInterface<number>>;
1199
+ getInstance: (id: number) => NameValueInterface<number> | undefined;
1200
+ }
1201
+ ```
1202
+
1203
+ ### useGlobalThemeStyles
1204
+
1205
+ Applies theme colors to `<body>` and `<html>` for full-page theming. No return value.
1206
+
1207
+ ```tsx
1208
+ function useGlobalThemeStyles(): void;
1209
+ ```
1210
+
1211
+ ---
1212
+
1213
+ ## Services
1214
+
1215
+ ### apiClient
1216
+
1217
+ Singleton HTTP client (Axios-based) with automatic Bearer token injection.
1218
+
1219
+ ```typescript
1220
+ // Main methods
1221
+ apiClient.get<T>(options: { url: string; params?: Record<string, unknown>; headers?: Record<string, string> }): Promise<T>;
1222
+ apiClient.post<T>(options: { url: string; body?: unknown; headers?: Record<string, string> }): Promise<T>;
1223
+ apiClient.put<T>(options: { url: string; body?: unknown; headers?: Record<string, string> }): Promise<T>;
1224
+ apiClient.del<T>(options: { url: string; headers?: Record<string, string> }): Promise<T>;
1225
+
1226
+ // File operations
1227
+ apiClient.getFile(options): Promise<{ data: Blob; headers: any }>;
1228
+ apiClient.getFileAsUrl(options): Promise<string>;
1229
+ apiClient.openFile(options): Promise<void>;
1230
+ apiClient.downloadFile(options): Promise<void>;
1231
+ apiClient.uploadFile<T>(options: { url: string; files: FileList | File[]; headers?: { paramName?: string } }): Promise<T>;
1232
+
1233
+ // Token management
1234
+ setApiClientTokenProvider(provider?: () => string | undefined): void;
1235
+ clearApiClientTokenProvider(): void;
1236
+
1237
+ // Create isolated instances
1238
+ createApiClient(config?: { baseURL?: string; timeout?: number; headers?: Record<string, string> }): ApiClientService;
1239
+ ```
1240
+
1241
+ ```tsx
1242
+ // Setup token globally
1243
+ setApiClientTokenProvider(() => user?.token?.accessToken);
1244
+
1245
+ // API calls
1246
+ const users = await apiClient.get<User[]>({ url: "/api/users", params: { page: 1 } });
1247
+ await apiClient.post({ url: "/api/users", body: { name: "Juan" } });
1248
+ await apiClient.downloadFile({ url: "/api/reports/pdf" });
1249
+ await apiClient.uploadFile({ url: "/api/upload", files: fileInput.files });
1250
+ ```
1251
+
1252
+ ---
1253
+
1254
+ ## Helpers
1255
+
1256
+ | Function | Signature | Description |
1257
+ |----------|-----------|-------------|
1258
+ | `currencyFormat` | `(value: number) => string` | Formats as `"1.234,56"` (es-AR locale) |
1259
+ | `getErrorMessage` | `(error: any) => string` | Extracts message from AxiosError. Default: `"Ha ocurrido un error"` |
1260
+ | `getInitialLetters` | `(text: string) => string` | `"Juan Pérez"` → `"JP"` |
1261
+ | `getQueryString` | `(params: URLSearchParams, newParams: any) => string` | Merges params, returns `"?key=value"` |
1262
+ | `objectToQueryString` | `(source: any) => string` | Object to `"a=1&b=2"` (no leading `?`) |
1263
+ | `queryStringToObject` | `(params: string) => Record<string, string>` | `"a=1&b=2"` → `{a: "1", b: "2"}` |
1264
+ | `nameValueArrayToObject` | `<T>(arr: NameValueInterface<T>[]) => Record<string, T>` | Array of {name, value} to object |
1265
+ | `promiseMapper` | `<T, K>(promise, mapper) => Promise<K \| K[] \| PaginationInterface<K>>` | Maps promise results (arrays, pagination, single) |
1266
+ | `RegularExpressions` | Object | `.email`, `.dateString`, `.password(config)` regex patterns |
1267
+
1268
+ ## Interfaces
1269
+
1270
+ ```typescript
1271
+ interface NameValueInterface<T> {
1272
+ name: string;
1273
+ value: T;
1274
+ extras?: any;
1275
+ }
1276
+
1277
+ interface PaginationInterface<T> {
1278
+ list: Array<T>;
1279
+ limit: number;
1280
+ page: number;
1281
+ pages: number;
1282
+ total: number;
1283
+ }
1284
+ ```
1285
+
1286
+ ---
1287
+
1288
+ ## Templates
1289
+
1290
+ ### LoginForm
1291
+
1292
+ ```typescript
1293
+ interface LoginFormProps {
1294
+ onSubmit?: (data: { email: string; password: string }) => void;
1295
+ loading?: boolean;
1296
+ error?: string;
1297
+ className?: string;
1298
+ }
1299
+ ```
1300
+
1301
+ ### RegistrationForm
1302
+
1303
+ ```typescript
1304
+ interface RegistrationFormProps {
1305
+ onSubmit?: (data: { firstName: string; lastName: string; email: string; password: string; confirmPassword: string }) => void;
1306
+ loading?: boolean;
1307
+ error?: string;
1308
+ className?: string;
1309
+ }
1310
+ ```
1311
+
1312
+ ### ContactForm
1313
+
1314
+ ```typescript
1315
+ interface ContactFormProps {
1316
+ onSubmit?: (data: { name: string; email: string; subject: string; message: string }) => void;
1317
+ loading?: boolean;
1318
+ success?: boolean;
1319
+ error?: string;
1320
+ className?: string;
1321
+ }
1322
+ ```
1323
+
1324
+ ### DashboardLayout
1325
+
1326
+ ```typescript
1327
+ interface DashboardStat {
1328
+ title: string;
1329
+ value: string | number;
1330
+ change?: string; // e.g. "+12%"
1331
+ changeType?: "positive" | "negative" | "neutral";
1332
+ icon?: string;
1333
+ }
1334
+
1335
+ interface DashboardLayoutProps {
1336
+ title: string; // required
1337
+ subtitle?: string;
1338
+ stats?: DashboardStat[];
1339
+ actions?: React.ReactNode;
1340
+ children: React.ReactNode; // required
1341
+ className?: string;
1342
+ }
1343
+ ```
1344
+
1345
+ ### SidebarLayout
1346
+
1347
+ ```typescript
1348
+ interface MenuItem {
1349
+ label: string;
1350
+ icon: string;
1351
+ href: string;
1352
+ badge?: string | number;
1353
+ children?: MenuItem[];
1354
+ }
1355
+
1356
+ interface User {
1357
+ name: string;
1358
+ email?: string;
1359
+ avatar?: string;
1360
+ }
1361
+
1362
+ interface SidebarLayoutProps {
1363
+ title: string; // required
1364
+ menuItems: MenuItem[]; // required
1365
+ user: User; // required
1366
+ children: React.ReactNode; // required
1367
+ className?: string;
1368
+ onLogout?: () => void;
1369
+ }
1370
+ ```
1371
+
1372
+ ### FormPattern
1373
+
1374
+ ```typescript
1375
+ interface FormField {
1376
+ name: string;
1377
+ label: string;
1378
+ type?: string; // default: "text"
1379
+ placeholder?: string;
1380
+ icon?: string;
1381
+ required?: boolean;
1382
+ validation?: (value: string) => string | undefined;
1383
+ multiline?: boolean;
1384
+ rows?: number; // default: 4
1385
+ }
1386
+
1387
+ interface FormPatternProps {
1388
+ title: string; // required
1389
+ subtitle?: string;
1390
+ fields: FormField[]; // required
1391
+ onSubmit: (data: Record<string, string>) => void; // required
1392
+ submitText?: string; // default: "Enviar"
1393
+ submitIcon?: string; // default: "fa-paper-plane"
1394
+ loading?: boolean;
1395
+ error?: string;
1396
+ success?: boolean;
1397
+ className?: string;
1398
+ gridCols?: 1 | 2; // default: 1
1399
+ }
1400
+ ```