@axzydev/axzy_ui_system 1.4.8 → 1.5.0

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 (223) hide show
  1. package/README.md +2 -11
  2. package/dist/index.cjs +1826 -1351
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +1 -1
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.cts +115 -14
  7. package/dist/index.d.ts +115 -14
  8. package/dist/index.js +1481 -1010
  9. package/dist/index.js.map +1 -1
  10. package/dist/layered.css +1 -1
  11. package/package.json +3 -16
  12. package/src/App.tsx +90 -73
  13. package/src/components/atoms/badget/badget.tsx +23 -5
  14. package/src/components/atoms/chip/chip.tsx +3 -1
  15. package/src/components/atoms/image/image.tsx +3 -1
  16. package/src/components/atoms/input/input.tsx +8 -5
  17. package/src/components/atoms/loader/loader.tsx +3 -1
  18. package/src/components/atoms/slide/slide.props.ts +12 -0
  19. package/src/components/atoms/slide/slide.tsx +3 -0
  20. package/src/components/molecules/alert/alert.tsx +3 -1
  21. package/src/components/molecules/breadcrumbs/breadcrumbs.tsx +3 -1
  22. package/src/components/molecules/calendar/calendar.tsx +9 -6
  23. package/src/components/molecules/card/card.tsx +5 -2
  24. package/src/components/molecules/chip-input/chip-input.tsx +9 -7
  25. package/src/components/molecules/date-picker/datePicker.tsx +9 -9
  26. package/src/components/molecules/dropdown-menu/dropdown-menu.props.ts +1 -1
  27. package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +6 -3
  28. package/src/components/molecules/field/field.tsx +3 -1
  29. package/src/components/molecules/form-header/form-header.tsx +3 -1
  30. package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +2 -2
  31. package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +20 -15
  32. package/src/components/molecules/masked-input/masked-input.tsx +3 -1
  33. package/src/components/molecules/multi-select/multi-select.tsx +12 -9
  34. package/src/components/molecules/pagination/pagination.tsx +11 -5
  35. package/src/components/molecules/search-select/search-select.tsx +11 -9
  36. package/src/components/molecules/select/select.tsx +6 -4
  37. package/src/components/molecules/stepper/stepper.tsx +6 -4
  38. package/src/components/molecules/table/table.tsx +30 -28
  39. package/src/components/molecules/time-picker/timePicker.tsx +8 -6
  40. package/src/components/molecules/wysiwyg/wysiwyg.tsx +23 -12
  41. package/src/components/organisms/confirm-dialog/confirm-dialog.props.ts +4 -4
  42. package/src/components/organisms/confirm-dialog/confirm-dialog.tsx +16 -8
  43. package/src/components/organisms/data-table/dataTable.tsx +31 -25
  44. package/src/components/organisms/dialog/dialog.tsx +3 -0
  45. package/src/components/organisms/drawer/drawer.tsx +3 -0
  46. package/src/components/organisms/dropfile/dropfile.tsx +19 -17
  47. package/src/components/organisms/form-builder/fieldRenderer.tsx +4 -2
  48. package/src/components/organisms/navbar/navbar.tsx +4 -2
  49. package/src/components/organisms/page-header/page-header.tsx +3 -1
  50. package/src/components/organisms/searchTable/components/PaginationControls.tsx +8 -6
  51. package/src/components/organisms/searchTable/components/PaginationInfo.tsx +3 -1
  52. package/src/components/organisms/searchTable/components/SearchAndSortBar.tsx +7 -5
  53. package/src/components/organisms/searchTable/components/SearchInput.tsx +4 -2
  54. package/src/components/organisms/searchTable/components/SortButton.tsx +13 -11
  55. package/src/components/organisms/searchTable/components/TableEmptyState.tsx +4 -2
  56. package/src/components/organisms/searchTable/components/TableRow.tsx +6 -4
  57. package/src/components/organisms/searchTable/searchTable.tsx +4 -2
  58. package/src/components/organisms/sidebar/sidebar.props.ts +1 -1
  59. package/src/components/organisms/sidebar/sidebar.tsx +9 -4
  60. package/src/components/organisms/toast/toast.tsx +3 -1
  61. package/src/components/organisms/topbar/topbar.tsx +4 -2
  62. package/src/components/templates/page/page.props.ts +4 -4
  63. package/src/components/templates/page/page.tsx +6 -4
  64. package/src/components/theme-provider/themeProvider.tsx +28 -17
  65. package/src/dev.css +77 -2
  66. package/src/global.d.ts +10 -0
  67. package/src/i18n/context.ts +9 -0
  68. package/src/i18n/index.ts +22 -0
  69. package/src/i18n/locales/en.ts +163 -0
  70. package/src/i18n/locales/es.ts +165 -0
  71. package/src/i18n/provider.tsx +44 -0
  72. package/src/i18n/translator.ts +82 -0
  73. package/src/i18n/types.ts +54 -0
  74. package/src/i18n/useITTranslation.ts +20 -0
  75. package/src/index.css +0 -1
  76. package/src/index.ts +7 -1
  77. package/src/sandbox/LandingShowcase.tsx +224 -580
  78. package/src/sandbox/NotFoundShowcase.tsx +51 -36
  79. package/src/sandbox/TopbarSearch.tsx +6 -4
  80. package/src/sandbox/controls.tsx +180 -0
  81. package/src/sandbox/i18n.ts +1929 -0
  82. package/src/sandbox/icons.tsx +25 -0
  83. package/src/sandbox/locale.tsx +72 -0
  84. package/src/sandbox/ui.tsx +377 -0
  85. package/src/sandbox/useHashRoute.ts +46 -10
  86. package/src/showcases/DataShowcases.tsx +103 -87
  87. package/src/showcases/FeedbackShowcases.tsx +37 -30
  88. package/src/showcases/FormShowcases.tsx +302 -214
  89. package/src/showcases/GettingStartedShowcase.tsx +151 -145
  90. package/src/showcases/HelpShowcase.tsx +126 -128
  91. package/src/showcases/HomeShowcase.tsx +678 -482
  92. package/src/showcases/LayoutPrimitivesShowcases.tsx +129 -114
  93. package/src/showcases/NavigationShowcases.tsx +45 -37
  94. package/src/showcases/PageShowcases.tsx +171 -152
  95. package/src/showcases/ProductoDetalle.tsx +272 -129
  96. package/src/showcases/ShowcaseLayout.tsx +270 -216
  97. package/src/showcases/SizesShowcase.tsx +123 -64
  98. package/src/showcases/StructureShowcases.tsx +157 -131
  99. package/src/showcases/portfolioUi.tsx +153 -0
  100. package/src/showcases/techIcons.ts +3 -0
  101. package/src/utils/table.utils.ts +10 -2
  102. package/src/components/atoms/avatar/avatar.doc.mdx +0 -48
  103. package/src/components/atoms/avatar/avatar.stories.tsx +0 -64
  104. package/src/components/atoms/badget/badget.doc.mdx +0 -46
  105. package/src/components/atoms/badget/badget.stories.tsx +0 -75
  106. package/src/components/atoms/button/button.doc.mdx +0 -59
  107. package/src/components/atoms/button/button.stories.tsx +0 -173
  108. package/src/components/atoms/checkbox/checkbox.doc.mdx +0 -54
  109. package/src/components/atoms/checkbox/checkbox.stories.tsx +0 -53
  110. package/src/components/atoms/chip/chip.doc.mdx +0 -47
  111. package/src/components/atoms/chip/chip.stories.tsx +0 -64
  112. package/src/components/atoms/divider/divider.doc.mdx +0 -42
  113. package/src/components/atoms/divider/divider.stories.tsx +0 -33
  114. package/src/components/atoms/flex/flex.doc.mdx +0 -59
  115. package/src/components/atoms/flex/flex.stories.tsx +0 -70
  116. package/src/components/atoms/grid/grid.doc.mdx +0 -54
  117. package/src/components/atoms/grid/grid.stories.tsx +0 -71
  118. package/src/components/atoms/image/image.doc.mdx +0 -44
  119. package/src/components/atoms/image/image.stories.tsx +0 -48
  120. package/src/components/atoms/input/input.doc.mdx +0 -70
  121. package/src/components/atoms/input/input.stories.tsx +0 -113
  122. package/src/components/atoms/input-number/input-number.doc.mdx +0 -74
  123. package/src/components/atoms/input-number/input-number.stories.tsx +0 -111
  124. package/src/components/atoms/loader/loader.doc.mdx +0 -43
  125. package/src/components/atoms/loader/loader.stories.tsx +0 -104
  126. package/src/components/atoms/popover/popover.doc.mdx +0 -53
  127. package/src/components/atoms/popover/popover.stories.tsx +0 -24
  128. package/src/components/atoms/progress/progress.doc.mdx +0 -48
  129. package/src/components/atoms/progress/progress.stories.tsx +0 -39
  130. package/src/components/atoms/radio/radio.doc.mdx +0 -62
  131. package/src/components/atoms/radio/radio.stories.tsx +0 -48
  132. package/src/components/atoms/segmented-control/segmented-control.doc.mdx +0 -60
  133. package/src/components/atoms/segmented-control/segmented-control.stories.tsx +0 -62
  134. package/src/components/atoms/skeleton/skeleton.doc.mdx +0 -43
  135. package/src/components/atoms/skeleton/skeleton.stories.tsx +0 -29
  136. package/src/components/atoms/slide/slide.doc.mdx +0 -46
  137. package/src/components/atoms/slide/slide.stories.tsx +0 -120
  138. package/src/components/atoms/slider/slider.doc.mdx +0 -49
  139. package/src/components/atoms/slider/slider.stories.tsx +0 -29
  140. package/src/components/atoms/stack/stack.doc.mdx +0 -126
  141. package/src/components/atoms/stack/stack.stories.tsx +0 -283
  142. package/src/components/atoms/text/text.doc.mdx +0 -41
  143. package/src/components/atoms/text/text.stories.tsx +0 -66
  144. package/src/components/atoms/textarea/textarea.doc.mdx +0 -64
  145. package/src/components/atoms/textarea/textarea.stories.tsx +0 -26
  146. package/src/components/atoms/tooltip/tooltip.doc.mdx +0 -47
  147. package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -19
  148. package/src/components/isolation/isolation.stories.tsx +0 -202
  149. package/src/components/molecules/accordion/accordion.doc.mdx +0 -47
  150. package/src/components/molecules/accordion/accordion.stories.tsx +0 -68
  151. package/src/components/molecules/alert/alert.doc.mdx +0 -48
  152. package/src/components/molecules/alert/alert.stories.tsx +0 -40
  153. package/src/components/molecules/breadcrumbs/breadcrumbs.doc.mdx +0 -57
  154. package/src/components/molecules/breadcrumbs/breadcrumbs.stories.tsx +0 -20
  155. package/src/components/molecules/calendar/calendar.doc.mdx +0 -76
  156. package/src/components/molecules/calendar/calendar.stories.tsx +0 -90
  157. package/src/components/molecules/card/card.doc.mdx +0 -54
  158. package/src/components/molecules/card/card.stories.tsx +0 -57
  159. package/src/components/molecules/chip-input/chip-input.doc.mdx +0 -44
  160. package/src/components/molecules/chip-input/chip-input.stories.tsx +0 -52
  161. package/src/components/molecules/date-picker/date-picker.doc.mdx +0 -65
  162. package/src/components/molecules/date-picker/date-picker.stories.tsx +0 -88
  163. package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +0 -46
  164. package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +0 -41
  165. package/src/components/molecules/empty-state/empty-state.doc.mdx +0 -50
  166. package/src/components/molecules/empty-state/empty-state.stories.tsx +0 -19
  167. package/src/components/molecules/field/field.doc.mdx +0 -43
  168. package/src/components/molecules/field/field.stories.tsx +0 -42
  169. package/src/components/molecules/form-header/form-header.doc.mdx +0 -38
  170. package/src/components/molecules/form-header/form-header.stories.tsx +0 -52
  171. package/src/components/molecules/masked-input/masked-input.doc.mdx +0 -78
  172. package/src/components/molecules/masked-input/masked-input.stories.tsx +0 -126
  173. package/src/components/molecules/multi-select/multi-select.doc.mdx +0 -55
  174. package/src/components/molecules/multi-select/multi-select.stories.tsx +0 -59
  175. package/src/components/molecules/pagination/pagination.doc.mdx +0 -62
  176. package/src/components/molecules/pagination/pagination.stories.tsx +0 -110
  177. package/src/components/molecules/search-select/search-select.doc.mdx +0 -77
  178. package/src/components/molecules/search-select/search-select.stories.tsx +0 -146
  179. package/src/components/molecules/select/select.doc.mdx +0 -75
  180. package/src/components/molecules/select/select.stories.tsx +0 -85
  181. package/src/components/molecules/stat-card/stat-card.doc.mdx +0 -48
  182. package/src/components/molecules/stat-card/stat-card.stories.tsx +0 -40
  183. package/src/components/molecules/stepper/stepper.doc.mdx +0 -54
  184. package/src/components/molecules/stepper/stepper.stories.tsx +0 -154
  185. package/src/components/molecules/table/table.doc.mdx +0 -140
  186. package/src/components/molecules/table/table.stories.tsx +0 -402
  187. package/src/components/molecules/tabs/tabs.doc.mdx +0 -52
  188. package/src/components/molecules/tabs/tabs.stories.tsx +0 -31
  189. package/src/components/molecules/time-picker/timePicker.doc.mdx +0 -68
  190. package/src/components/molecules/time-picker/timePicker.stories.tsx +0 -130
  191. package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +0 -95
  192. package/src/components/organisms/confirm-dialog/confirm-dialog.doc.mdx +0 -60
  193. package/src/components/organisms/confirm-dialog/confirm-dialog.stories.tsx +0 -32
  194. package/src/components/organisms/data-table/ITDataTable.doc.mdx +0 -132
  195. package/src/components/organisms/data-table/ITDataTable.stories.tsx +0 -353
  196. package/src/components/organisms/dialog/dialog.doc.mdx +0 -54
  197. package/src/components/organisms/dialog/dialog.stories.tsx +0 -126
  198. package/src/components/organisms/drawer/drawer.doc.mdx +0 -59
  199. package/src/components/organisms/drawer/drawer.stories.tsx +0 -40
  200. package/src/components/organisms/dropfile/dropfile.doc.mdx +0 -74
  201. package/src/components/organisms/dropfile/dropfile.stories.tsx +0 -93
  202. package/src/components/organisms/form-builder/formBuilder.doc.mdx +0 -251
  203. package/src/components/organisms/form-builder/formBuilder.stories.tsx +0 -914
  204. package/src/components/organisms/navbar/navbar.doc.mdx +0 -77
  205. package/src/components/organisms/navbar/navbar.stories.tsx +0 -83
  206. package/src/components/organisms/page-header/page-header.doc.mdx +0 -54
  207. package/src/components/organisms/page-header/page-header.stories.tsx +0 -74
  208. package/src/components/organisms/searchTable/searchTable.doc.mdx +0 -79
  209. package/src/components/organisms/searchTable/searchTable.stories.tsx +0 -73
  210. package/src/components/organisms/sidebar/sidebar.doc.mdx +0 -176
  211. package/src/components/organisms/sidebar/sidebar.stories.tsx +0 -234
  212. package/src/components/organisms/toast/toast.doc.mdx +0 -50
  213. package/src/components/organisms/toast/toast.stories.tsx +0 -137
  214. package/src/components/organisms/topbar/topbar.doc.mdx +0 -72
  215. package/src/components/organisms/topbar/topbar.stories.tsx +0 -79
  216. package/src/components/organisms/triple-filter/tripleFilter.doc.mdx +0 -62
  217. package/src/components/organisms/triple-filter/tripleFilter.stories.tsx +0 -31
  218. package/src/components/templates/layout/layout.doc.mdx +0 -53
  219. package/src/components/templates/layout/layout.stories.tsx +0 -113
  220. package/src/components/templates/page/page.doc.mdx +0 -86
  221. package/src/components/templates/page/page.stories.tsx +0 -97
  222. package/src/components/theme-provider/themeProvider.doc.mdx +0 -85
  223. package/src/components/theme-provider/themeProvider.stories.tsx +0 -58
@@ -6,6 +6,7 @@ import ITText from "@/components/atoms/text/text";
6
6
  import ITButton from "@/components/atoms/button/button";
7
7
  import ITDialog from "@/components/organisms/dialog/dialog";
8
8
  import { ITDropfileProps, FileTypeEnum, UploadStatus } from "./dropfile.props";
9
+ import { useITTranslation } from "@/i18n";
9
10
  export { FileTypeEnum, UploadStatus } from "./dropfile.props";
10
11
 
11
12
  /**
@@ -50,9 +51,10 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
50
51
  onStatusChange,
51
52
  initialPreviewUrl,
52
53
  view = "drop",
53
- buttonLabel = "Subir archivo",
54
+ buttonLabel,
54
55
  modalTitle,
55
56
  }) => {
57
+ const { t } = useITTranslation();
56
58
  const [isModalOpen, setIsModalOpen] = useState(false);
57
59
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
58
60
  const [fileType, setFileType] = useState<string | null>(null);
@@ -172,22 +174,22 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
172
174
  const StatusBadge = ({ status }: { status: UploadStatus }) => {
173
175
  const config = {
174
176
  [UploadStatus.PENDING]: {
175
- label: "Pendiente",
177
+ label: t("dropfile.status.pending"),
176
178
  color: "bg-warning-100 text-warning-800 border-warning-200",
177
179
  dotColor: "bg-warning-400",
178
180
  },
179
181
  [UploadStatus.UPLOADING]: {
180
- label: "Subiendo...",
182
+ label: t("dropfile.status.uploading"),
181
183
  color: "bg-primary-100 text-primary-800 border-primary-200",
182
184
  dotColor: "bg-primary-400 animate-pulse",
183
185
  },
184
186
  [UploadStatus.UPLOADED]: {
185
- label: "Subido",
187
+ label: t("dropfile.status.uploaded"),
186
188
  color: "bg-success-100 text-success-800 border-success-200",
187
189
  dotColor: "bg-success-400",
188
190
  },
189
191
  [UploadStatus.ERROR]: {
190
- label: "Error",
192
+ label: t("dropfile.status.error"),
191
193
  color: "bg-danger-100 text-danger-800 border-danger-200",
192
194
  dotColor: "bg-danger-400",
193
195
  },
@@ -223,7 +225,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
223
225
  setImagePreview(previewUrl);
224
226
  }
225
227
  } else {
226
- alert(`Tipo de archivo no permitido.`);
228
+ alert(t("dropfile.fileTypeNotAllowed"));
227
229
  setSelectedFile(null);
228
230
  setFileType(null);
229
231
  setImagePreview(null);
@@ -318,7 +320,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
318
320
  const headerRow = (
319
321
  <div className="flex items-center justify-between mb-2">
320
322
  <label className="block text-sm font-semibold text-secondary-700">
321
- <ITText as="span">Subir archivo </ITText><ITText as="span" className="text-secondary-400 font-normal text-xs">({getFileExtensions()})</ITText>
323
+ <ITText as="span">{t("dropfile.upload")} </ITText><ITText as="span" className="text-secondary-400 font-normal text-xs">({getFileExtensions()})</ITText>
322
324
  </label>
323
325
 
324
326
  {showStatusBadge && selectedFile && (
@@ -355,7 +357,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
355
357
 
356
358
  <div className="text-center space-y-1">
357
359
  <ITText as="p" className={`text-sm font-medium transition-colors duration-300 ${isDragActive ? 'text-primary-700' : 'text-secondary-700'}`}>
358
- {isDragActive ? "¡Suelta aquí!" : "Haz clic o arrastra"}
360
+ {isDragActive ? t("dropfile.dropHere") : t("dropfile.clickOrDrag")}
359
361
  </ITText>
360
362
  </div>
361
363
  </div>
@@ -375,8 +377,8 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
375
377
  )}
376
378
  </div>
377
379
  <div className="min-w-0">
378
- <ITText as="p" className="text-xs font-medium text-secondary-900 truncate" title={selectedFile?.name || "Imagen cargada"}>
379
- {selectedFile?.name || "Imagen cargada"}
380
+ <ITText as="p" className="text-xs font-medium text-secondary-900 truncate" title={selectedFile?.name || t("dropfile.imageLoaded")}>
381
+ {selectedFile?.name || t("dropfile.imageLoaded")}
380
382
  </ITText>
381
383
  <ITText as="p" className="text-[10px] text-secondary-500">
382
384
  {selectedFile ? (selectedFile.size / 1024 / 1024).toFixed(2) + " MB" : ""}
@@ -389,7 +391,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
389
391
  {((selectedFile && fileType?.startsWith('image/')) || (!selectedFile && imagePreview)) ? (
390
392
  <img
391
393
  src={imagePreview}
392
- alt="Vista previa"
394
+ alt={t("dropfile.previewAlt")}
393
395
  className="w-full h-full object-contain max-h-[200px]"
394
396
  />
395
397
  ) : (
@@ -397,7 +399,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
397
399
  <svg className="w-10 h-10 mb-2 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
398
400
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
399
401
  </svg>
400
- <ITText as="span" className="text-xs">Sin vista previa</ITText>
402
+ <ITText as="span" className="text-xs">{t("dropfile.noPreview")}</ITText>
401
403
  </div>
402
404
  )}
403
405
  </div>
@@ -412,7 +414,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
412
414
  size="sm"
413
415
  onClick={handleCancel}
414
416
  >
415
- <ITText>Cancelar</ITText>
417
+ <ITText>{t("common.cancel")}</ITText>
416
418
  </ITButton>
417
419
  <ITButton
418
420
  type="button"
@@ -422,7 +424,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
422
424
  onClick={handleConfirm}
423
425
  >
424
426
  <div className="flex items-center gap-2">
425
- <ITText>Confirmar</ITText>
427
+ <ITText>{t("common.confirm")}</ITText>
426
428
  <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
427
429
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
428
430
  </svg>
@@ -440,7 +442,7 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
440
442
  <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
441
443
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
442
444
  </svg>
443
- <ITText>Eliminar</ITText>
445
+ <ITText>{t("common.delete")}</ITText>
444
446
  </ITButton>
445
447
  )}
446
448
  </div>
@@ -479,13 +481,13 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
479
481
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
480
482
  </svg>
481
483
  <ITText className="truncate">
482
- {selectedFile?.name || (hasFile ? "Archivo cargado" : buttonLabel)}
484
+ {selectedFile?.name || (hasFile ? t("dropfile.fileLoaded") : (buttonLabel ?? t("dropfile.upload")))}
483
485
  </ITText>
484
486
  {showStatusBadge && hasFile && <StatusBadge status={uploadStatus} />}
485
487
  </div>
486
488
  </ITButton>
487
489
 
488
- <ITDialog isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} title={modalTitle ?? buttonLabel}>
490
+ <ITDialog isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} title={modalTitle ?? buttonLabel ?? t("dropfile.upload")}>
489
491
  <div className="w-full transition-all duration-300">
490
492
  {headerRow}
491
493
  {dropZoneOrPreview}
@@ -11,6 +11,7 @@ import { useFieldRules } from "./useFormBuilder";
11
11
  import { getColSpanClass, getGridColsClass } from "@/utils/styles";
12
12
  import ITText from "@/components/atoms/text/text";
13
13
  import { FaSpinner } from "react-icons/fa";
14
+ import { useITTranslation } from "@/i18n";
14
15
 
15
16
  /**
16
17
  * Resolves `FieldConfigV2["options"]` to a stable `{ value, label }[]` array,
@@ -123,6 +124,7 @@ const ITFieldRenderer = ({
123
124
  dependentValues = {},
124
125
  }: ITFieldRendererProps) => {
125
126
  const context = useITFormBuilderContext();
127
+ const { t } = useITTranslation();
126
128
  const { isVisible, isRequired, isDisabled, dynamicProps } = useFieldRules(
127
129
  config,
128
130
  dependentValues,
@@ -220,7 +222,7 @@ const ITFieldRenderer = ({
220
222
  >
221
223
  <FaSpinner className="animate-spin text-primary-500" />
222
224
  <ITText as="span" className="text-sm text-slate-500">
223
- Cargando opciones...
225
+ {t("formBuilder.loadingOptions")}
224
226
  </ITText>
225
227
  </div>
226
228
  );
@@ -366,7 +368,7 @@ const ITFieldRenderer = ({
366
368
  return (
367
369
  <div className="p-4 border-2 border-dashed border-secondary-200 rounded-xl">
368
370
  <ITText as="p" className="text-sm text-secondary-500 text-center">
369
- Array Field: {label}
371
+ {t("formBuilder.arrayField", { label: label ?? "" })}
370
372
  </ITText>
371
373
  </div>
372
374
  );
@@ -4,6 +4,7 @@ import { FaChevronDown, FaChevronRight, FaUserCircle } from "react-icons/fa";
4
4
  import { ITNavbarProps, ITNavigationItem } from "./navbar.props";
5
5
  import ITText from "@/components/atoms/text/text";
6
6
  import { theme } from "@/theme/theme";
7
+ import { useITTranslation } from "@/i18n";
7
8
 
8
9
  /**
9
10
  * Full sidebar navigation shell with collapsible submenus, user menu dropdown, and themed styling.
@@ -44,6 +45,7 @@ export default function ITNavbar({
44
45
  showSidebarOnMobile = false,
45
46
  sidebarItems,
46
47
  }: ITNavbarProps) {
48
+ const { t } = useITTranslation();
47
49
  const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
48
50
  const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
49
51
 
@@ -106,7 +108,7 @@ export default function ITNavbar({
106
108
  <img
107
109
  className="w-8 h-8 rounded-full"
108
110
  src={userMenu.userImage}
109
- alt="user photo"
111
+ alt={t("common.userAvatar")}
110
112
  />
111
113
  ) : (
112
114
  <FaUserCircle className="w-8 h-8 text-secondary-500" />
@@ -290,7 +292,7 @@ export default function ITNavbar({
290
292
  <img
291
293
  className="w-10 h-10 rounded-full border-2 transition-colors"
292
294
  src={userMenu.userImage}
293
- alt="user photo"
295
+ alt={t("common.userAvatar")}
294
296
  style={{ borderColor: sidebar.borderColor }}
295
297
  />
296
298
  ) : (
@@ -3,6 +3,7 @@ import { ITPageHeaderProps } from "./page-header.props";
3
3
  import ITBreadcrumbs from "@/components/molecules/breadcrumbs/breadcrumbs";
4
4
  import ITText from "@/components/atoms/text/text";
5
5
  import { useITFlatAppearance } from "@/theme/theme-context";
6
+ import { useITTranslation } from "@/i18n";
6
7
 
7
8
  /**
8
9
  * Page title bar with breadcrumbs, description, back button, and action buttons.
@@ -29,6 +30,7 @@ export default function ITPageHeader({
29
30
  iconColor,
30
31
  className,
31
32
  }: ITPageHeaderProps) {
33
+ const { t } = useITTranslation();
32
34
  const showTopRow = breadcrumbs?.length || backAction;
33
35
  // Flat appearance: compact title, no decorative icon tile.
34
36
  const flat = useITFlatAppearance();
@@ -43,7 +45,7 @@ export default function ITPageHeader({
43
45
  <button
44
46
  onClick={backAction}
45
47
  className="flex items-center justify-center w-9 h-9 rounded-full border border-slate-200 dark:border-slate-700 text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:border-slate-300 dark:hover:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-800/60 transition-all duration-200 flex-shrink-0"
46
- aria-label="Volver"
48
+ aria-label={t("pageHeader.back")}
47
49
  >
48
50
  <FaChevronLeft size={14} />
49
51
  </button>
@@ -4,6 +4,7 @@ import PaginationInfo from "./PaginationInfo";
4
4
  import ITSelect from "@/components/molecules/select/select";
5
5
  import ITButton from "@/components/atoms/button/button";
6
6
  import ITText from "@/components/atoms/text/text";
7
+ import { useITTranslation } from "@/i18n";
7
8
 
8
9
  interface PaginationControlsProps {
9
10
  pageIndex: number;
@@ -30,13 +31,14 @@ export default function PaginationControls({
30
31
  currentCount,
31
32
  totalCount,
32
33
  }: PaginationControlsProps) {
34
+ const { t } = useITTranslation();
33
35
  return (
34
36
  <div className="flex flex-col sm:flex-row justify-between items-center gap-4">
35
37
  <div className="flex items-center space-x-4">
36
38
  <PaginationInfo currentCount={currentCount} totalCount={totalCount} />
37
39
 
38
40
  <div className="flex items-center space-x-2">
39
- <ITText as="span" className="text-sm text-secondary-700">Mostrar:</ITText>
41
+ <ITText as="span" className="text-sm text-secondary-700">{t("searchTable.showLabel")}</ITText>
40
42
  <ITSelect
41
43
  name="itemsPerPage"
42
44
  options={itemsPerPageOptions.map((option) => ({
@@ -59,14 +61,14 @@ export default function PaginationControls({
59
61
  onClick={() => onPageChange(pageIndex - 1)}
60
62
  disabled={!hasPreviousPage}
61
63
  className="min-w-[32px]"
62
- ariaLabel="Página anterior"
63
- title="Ir a la página anterior"
64
+ ariaLabel={t("searchTable.prev")}
65
+ title={t("searchTable.prev")}
64
66
  >
65
67
  <FaArrowLeft aria-hidden="true" />
66
68
  </ITButton>
67
69
 
68
70
  <ITText as="span" className="px-4 py-2 text-sm text-secondary-700" aria-live="polite">
69
- Página {pageIndex} de {totalPages}
71
+ {t("searchTable.pageOf", { current: pageIndex, total: totalPages })}
70
72
  </ITText>
71
73
 
72
74
  <ITButton
@@ -75,8 +77,8 @@ export default function PaginationControls({
75
77
  onClick={() => onPageChange(pageIndex + 1)}
76
78
  disabled={!hasNextPage}
77
79
  className="min-w-[32px]"
78
- ariaLabel="Página siguiente"
79
- title="Ir a la página siguiente"
80
+ ariaLabel={t("searchTable.next")}
81
+ title={t("searchTable.next")}
80
82
  >
81
83
  <FaArrowRight aria-hidden="true" />
82
84
  </ITButton>
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import ITText from "@/components/atoms/text/text";
3
+ import { useITTranslation } from "@/i18n";
3
4
 
4
5
  interface PaginationInfoProps {
5
6
  currentCount: number;
@@ -12,9 +13,10 @@ export default function PaginationInfo({
12
13
  totalCount,
13
14
  className = "",
14
15
  }: PaginationInfoProps) {
16
+ const { t } = useITTranslation();
15
17
  return (
16
18
  <ITText as="span" className={`text-sm text-secondary-700 ${className}`}>
17
- Mostrando {currentCount} de {totalCount} resultados
19
+ {t("searchTable.showingCount", { from: currentCount, total: totalCount })}
18
20
  </ITText>
19
21
  );
20
22
  }
@@ -3,6 +3,7 @@ import SearchInput from "./SearchInput";
3
3
  import SortButton from "./SortButton";
4
4
  import ITButton from "@/components/atoms/button/button";
5
5
  import ITText from "@/components/atoms/text/text";
6
+ import { useITTranslation } from "@/i18n";
6
7
 
7
8
  interface SearchAndSortBarProps {
8
9
  searchTerm: string;
@@ -22,13 +23,14 @@ export default function SearchAndSortBar({
22
23
  onClearSearch,
23
24
  onGlobalSort,
24
25
  sortConfig,
25
- searchInputPlaceholder = "Buscar en todos los campos...",
26
+ searchInputPlaceholder,
26
27
  }: SearchAndSortBarProps) {
28
+ const { t } = useITTranslation();
27
29
  return (
28
30
  <div className="bg-secondary-50 px-6 py-4 border-b border-secondary-200">
29
31
  <div className="flex items-center gap-4">
30
32
  <SearchInput
31
- placeholder={searchInputPlaceholder}
33
+ placeholder={searchInputPlaceholder ?? t("searchTable.searchPlaceholder")}
32
34
  value={searchTerm}
33
35
  onChange={onSearchChange}
34
36
  />
@@ -41,10 +43,10 @@ export default function SearchAndSortBar({
41
43
  variant="outlined"
42
44
  color="danger"
43
45
  onClick={onClearSearch}
44
- ariaLabel="Limpiar búsqueda"
45
- title="Limpiar búsqueda"
46
+ ariaLabel={t("searchTable.clearSearch")}
47
+ title={t("searchTable.clearSearch")}
46
48
  >
47
- <ITText as="span">Limpiar</ITText>
49
+ <ITText as="span">{t("common.clear")}</ITText>
48
50
  </ITButton>
49
51
  )}
50
52
  </div>
@@ -1,6 +1,7 @@
1
1
  import ITInput from "@/components/atoms/input/input";
2
2
  import React from "react";
3
3
  import { FaSearch } from "react-icons/fa";
4
+ import { useITTranslation } from "@/i18n";
4
5
 
5
6
  interface SearchInputProps {
6
7
  placeholder?: string;
@@ -10,11 +11,12 @@ interface SearchInputProps {
10
11
  }
11
12
 
12
13
  export default function SearchInput({
13
- placeholder = "Buscar en todos los campos...",
14
+ placeholder,
14
15
  value,
15
16
  onChange,
16
17
  className = "",
17
18
  }: SearchInputProps) {
19
+ const { t } = useITTranslation();
18
20
  return (
19
21
  <div className={`relative flex-1 ${className}`}>
20
22
  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
@@ -22,7 +24,7 @@ export default function SearchInput({
22
24
  </div>
23
25
  <ITInput
24
26
  name="global-search"
25
- placeholder={placeholder}
27
+ placeholder={placeholder ?? t("searchTable.searchPlaceholder")}
26
28
  value={value}
27
29
  onChange={(e) => onChange(e.target.value)}
28
30
  onBlur={() => {}}
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
3
3
  import ITText from "@/components/atoms/text/text";
4
+ import { useITTranslation } from "@/i18n";
4
5
 
5
6
  interface SortButtonProps {
6
7
  sortConfig?: {
@@ -16,18 +17,19 @@ export default function SortButton({
16
17
  onClick,
17
18
  className = "",
18
19
  }: SortButtonProps) {
20
+ const { t } = useITTranslation();
21
+ const directionLabel = sortConfig
22
+ ? sortConfig.direction === "asc"
23
+ ? t("searchTable.sortDescending")
24
+ : t("searchTable.sortAscending")
25
+ : t("searchTable.sortAscending");
26
+
19
27
  return (
20
28
  <button
21
29
  onClick={onClick}
22
30
  className={`p-3 rounded-lg bg-white border border-secondary-300 hover:bg-secondary-50 transition-colors duration-200 flex items-center gap-2 min-w-[120px] ${className}`}
23
- aria-label={`Ordenar tabla ${
24
- sortConfig
25
- ? sortConfig.direction === "asc"
26
- ? "descendente"
27
- : "ascendente"
28
- : "ascendente"
29
- }`}
30
- title="Ordenar tabla"
31
+ aria-label={t("searchTable.sortAria", { direction: directionLabel })}
32
+ title={t("searchTable.sortTable")}
31
33
  >
32
34
  {sortConfig ? (
33
35
  sortConfig.direction === "asc" ? (
@@ -41,9 +43,9 @@ export default function SortButton({
41
43
  <ITText as="span" className="text-sm font-medium text-secondary-700">
42
44
  {sortConfig
43
45
  ? sortConfig.direction === "asc"
44
- ? "Asc ↑"
45
- : "Desc ↓"
46
- : "Ordenar"}
46
+ ? t("searchTable.sortAsc")
47
+ : t("searchTable.sortDesc")
48
+ : t("searchTable.sort")}
47
49
  </ITText>
48
50
  </button>
49
51
  );
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import ITText from "@/components/atoms/text/text";
3
+ import { useITTranslation } from "@/i18n";
3
4
 
4
5
  interface TableEmptyStateProps {
5
6
  colSpan: number;
@@ -10,12 +11,13 @@ export default function TableEmptyState({
10
11
  colSpan,
11
12
  searchTerm,
12
13
  }: TableEmptyStateProps) {
14
+ const { t } = useITTranslation();
13
15
  return (
14
16
  <tr>
15
17
  <td colSpan={colSpan} className="px-6 py-4 text-center">
16
18
  {searchTerm
17
- ? <ITText as="span">No se encontraron resultados para tu búsqueda</ITText>
18
- : <ITText as="span">No se encontraron resultados</ITText>}
19
+ ? <ITText as="span">{t("searchTable.emptySearch")}</ITText>
20
+ : <ITText as="span">{t("searchTable.empty")}</ITText>}
19
21
  </td>
20
22
  </tr>
21
23
  );
@@ -6,6 +6,7 @@ import { FaCheck, FaTimes } from "react-icons/fa";
6
6
  import { SearchColumn } from "../searchTable.props";
7
7
  import EditableCell from "./EditableCell";
8
8
  import * as yup from "yup";
9
+ import { useITTranslation, toIntlLocale } from "@/i18n";
9
10
 
10
11
  interface TableRowProps<T> {
11
12
  row: T;
@@ -31,6 +32,7 @@ export default function TableRow<T>({
31
32
  isEditing = false,
32
33
  onEdit,
33
34
  }: TableRowProps<T>) {
35
+ const { t, locale } = useITTranslation();
34
36
  const {
35
37
  editedRow,
36
38
  errors,
@@ -55,18 +57,18 @@ export default function TableRow<T>({
55
57
  switch (col.type) {
56
58
  case "number":
57
59
  return typeof value === "number" && col.currencyMX
58
- ? formatCurrencyMX(value)
60
+ ? formatCurrencyMX(value, toIntlLocale(locale))
59
61
  : (value as React.ReactNode);
60
62
 
61
63
  case "boolean":
62
64
  return value ? (
63
65
  <FaCheck
64
66
  className="text-green-500"
65
- aria-label="Verdadero"
66
- title="Verdadero"
67
+ aria-label={t("common.true")}
68
+ title={t("common.true")}
67
69
  />
68
70
  ) : (
69
- <FaTimes className="text-danger-500" aria-label="Falso" title="Falso" />
71
+ <FaTimes className="text-danger-500" aria-label={t("common.false")} title={t("common.false")} />
70
72
  );
71
73
 
72
74
  case "actions":
@@ -11,6 +11,7 @@ import { getNestedValue } from "@/utils/table.utils";
11
11
  import TableEmptyState from "./components/TableEmptyState";
12
12
  import PaginationControls from "./components/PaginationControls";
13
13
  import ITText from "@/components/atoms/text/text";
14
+ import { useITTranslation } from "@/i18n";
14
15
 
15
16
  interface CustomITSearchTableProps<T> extends ITSearchTableProps<T> {
16
17
  editingRow?: number | null;
@@ -53,7 +54,7 @@ export default function ITSearchTable<T extends Record<string, unknown>>({
53
54
  data,
54
55
  containerClassName,
55
56
  searchTermInitial = "",
56
- searchInputPlaceholder = "Buscar en todos los campos...",
57
+ searchInputPlaceholder,
57
58
  variant = "default",
58
59
  size = "md",
59
60
  itemsPerPageOptions = [10, 20, 50, 100],
@@ -76,6 +77,7 @@ export default function ITSearchTable<T extends Record<string, unknown>>({
76
77
  onSave,
77
78
  onCancel,
78
79
  }: CustomITSearchTableProps<T>) {
80
+ const { t } = useITTranslation();
79
81
  const [itemsPerPage, setItemsPerPage] = useState(defaultItemsPerPage);
80
82
 
81
83
  const { searchTerm, handleSearchChange, handleClearSearch } = useDebouncedSearch({
@@ -145,7 +147,7 @@ return (
145
147
  onClearSearch={handleClearSearchWithClear}
146
148
  onGlobalSort={handleGlobalSort}
147
149
  sortConfig={sortConfig}
148
- searchInputPlaceholder={searchInputPlaceholder}
150
+ searchInputPlaceholder={searchInputPlaceholder ?? t("searchTable.searchPlaceholder")}
149
151
  />
150
152
  </div>
151
153
 
@@ -75,7 +75,7 @@ export interface ITSidebarNotification {
75
75
  count: number;
76
76
  /** Icon rendered left of the label (e.g. a bell). Omit for a count-only pill. */
77
77
  icon?: React.ReactNode;
78
- /** Accessible label and tooltip for the notification button. @default "Notificaciones" */
78
+ /** Accessible label and tooltip for the notification button. @default i18n `sidebar.notifications` ("Notificaciones") */
79
79
  label?: string;
80
80
  /** Click handler for the notification button. */
81
81
  onClick?: () => void;
@@ -11,6 +11,7 @@ import {
11
11
  import ITBadget from "@/components/atoms/badget/badget";
12
12
  import ITText from "@/components/atoms/text/text";
13
13
  import { useITFlatAppearance } from "@/theme/theme-context";
14
+ import { useITTranslation } from "@/i18n";
14
15
 
15
16
  /** Narrows a submenu entry to a titled group. */
16
17
  function isSubItemGroup(
@@ -73,6 +74,7 @@ export default function ITSidebar({
73
74
  }: ITSidebarProps) {
74
75
  // Flat appearance: plain surface, no glow/accent bar, narrower panel.
75
76
  const flat = useITFlatAppearance();
77
+ const { t } = useITTranslation();
76
78
  const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
77
79
  const [isHovering, setIsHovering] = useState(false);
78
80
  const sidebarRef = useRef<HTMLDivElement>(null);
@@ -251,7 +253,7 @@ export default function ITSidebar({
251
253
  const {
252
254
  count,
253
255
  icon,
254
- label = "Notificaciones",
256
+ label = t("sidebar.notifications"),
255
257
  onClick,
256
258
  badgeProps,
257
259
  } = notification;
@@ -371,11 +373,14 @@ export default function ITSidebar({
371
373
  )}
372
374
 
373
375
  {/* Navigation Items */}
374
- <nav aria-label="Componentes de la librería" className={`flex-1 py-4 custom-scrollbar ${fixedRail ? "overflow-visible" : "overflow-y-auto overflow-x-hidden"} ${flat && isSidebarCollapsed ? "px-3" : "px-4"}`}>
376
+ <nav aria-label={t("sidebar.navAria")} className={`flex-1 py-4 custom-scrollbar ${fixedRail ? "overflow-visible" : "overflow-y-auto overflow-x-hidden"} ${flat && isSidebarCollapsed ? "px-3" : "px-4"}`}>
375
377
  <ul className="space-y-1">
376
378
  {navigationItems.map((item) => {
377
379
  const hasSubmenu = !!item.subitems && item.subitems.length > 0;
378
380
  const submenuId = `it-sidebar-submenu-${item.id}`;
381
+ // El submenú solo existe en el DOM cuando la barra está expandida:
382
+ // `aria-controls` a un id inexistente es un valor ARIA inválido.
383
+ const hasRenderedSubmenu = hasSubmenu && !isSidebarCollapsed;
379
384
  const { className: badgeClassName, ...badgeRest } = item.badgeProps ?? {};
380
385
  // Flat + expanded: a parent whose subitem is active stays plain; the
381
386
  // subitem carries the highlight. Collapsed, the parent icon shows it.
@@ -387,7 +392,7 @@ export default function ITSidebar({
387
392
  tabIndex={0}
388
393
  aria-label={item.label}
389
394
  aria-expanded={hasSubmenu ? expandedItems.has(item.id) : undefined}
390
- aria-controls={hasSubmenu ? submenuId : undefined}
395
+ aria-controls={hasRenderedSubmenu ? submenuId : undefined}
391
396
  className={`flex items-center cursor-pointer
392
397
  transition-all duration-300 ease-[cubic-bezier(0.2,0,0,1)]
393
398
  rounded-xl relative overflow-visible
@@ -525,7 +530,7 @@ export default function ITSidebar({
525
530
  ))}
526
531
  </div>
527
532
  ) : (
528
- <ITText as="div" className="px-5 py-3 text-sm italic" style={{ color: "var(--it-sidebar-label-color, var(--color-secondary-400))" }}>No hay submenú</ITText>
533
+ <ITText as="div" className="px-5 py-3 text-sm italic" style={{ color: "var(--it-sidebar-label-color, var(--color-secondary-400))" }}>{t("sidebar.noSubmenu")}</ITText>
529
534
  )}
530
535
  </div>
531
536
  )}
@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
5
5
  import { ITToastProps } from "./toast.props";
6
6
  import { FaTimesCircle, FaCheckCircle, FaExclamationTriangle, FaInfoCircle, FaTimes } from "react-icons/fa";
7
7
  import ITText from "@/components/atoms/text/text";
8
+ import { useITTranslation } from "@/i18n";
8
9
 
9
10
  /**
10
11
  * ITToast — floating notification with automatic dismiss and severity-based styling.
@@ -34,6 +35,7 @@ export default function ITToast({
34
35
  position = "top-right",
35
36
  onClose,
36
37
  }: ITToastProps) {
38
+ const { t } = useITTranslation();
37
39
  const [isVisible, setIsVisible] = useState(true);
38
40
 
39
41
  useEffect(() => {
@@ -98,7 +100,7 @@ export default function ITToast({
98
100
  <button
99
101
  onClick={handleClose}
100
102
  className="p-1.5 rounded-full hover:bg-black/15 transition-colors focus:outline-none focus:ring-2 focus:ring-white/50"
101
- aria-label="Close notification"
103
+ aria-label={t("common.closeNotification")}
102
104
  >
103
105
  <FaTimes className="w-4 h-4" />
104
106
  </button>
@@ -4,6 +4,7 @@ import { useRef, useState } from "react";
4
4
  import { ITTopBarProps } from "./topbar.props";
5
5
  import ITText from "@/components/atoms/text/text";
6
6
  import { useITFlatAppearance } from "@/theme/theme-context";
7
+ import { useITTranslation } from "@/i18n";
7
8
 
8
9
  /**
9
10
  * ITTopBar — sticky top navigation bar with logo, nav items, and user avatar dropdown.
@@ -73,6 +74,7 @@ export default function ITTopBar({
73
74
  }: ITTopBarProps) {
74
75
  // Flat appearance: plain bar (no shadow/blur), initials avatar, name before it.
75
76
  const flat = useITFlatAppearance();
77
+ const { t } = useITTranslation();
76
78
  const initials = (userMenu?.userName ?? "")
77
79
  .trim()
78
80
  .split(/\s+/)
@@ -102,7 +104,7 @@ export default function ITTopBar({
102
104
  {showMobileMenuButton && (
103
105
  <button
104
106
  type="button"
105
- aria-label="Menú"
107
+ aria-label={t("common.menu")}
106
108
  className={`${showDesktopMenuButton ? "" : "lg:hidden"} p-2.5 -ml-2.5 rounded-xl transition-colors duration-200`}
107
109
  style={{
108
110
  color: "var(--it-topbar-icon, #64748b)",
@@ -200,7 +202,7 @@ export default function ITTopBar({
200
202
  <img
201
203
  className="w-9 h-9 rounded-full object-cover ring-2 ring-white shadow-sm"
202
204
  src={userMenu.userImage}
203
- alt="Current user"
205
+ alt={t("common.userAvatar")}
204
206
  />
205
207
  ) : flat && initials ? (
206
208
  <div