@12-apps/ui 6.1.0 → 6.3.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 (194) hide show
  1. package/components-catalog.md +8 -0
  2. package/dist/{chunk-35VGGLOC.js → chunk-CEKPLUQV.js} +2 -2
  3. package/dist/{chunk-35VGGLOC.js.map → chunk-CEKPLUQV.js.map} +1 -1
  4. package/dist/{chunk-62XQWGXU.js → chunk-MXPXKORR.js} +19 -17
  5. package/dist/chunk-MXPXKORR.js.map +1 -0
  6. package/dist/{chunk-7XLEWBKL.js → chunk-N554AFDB.js} +7 -10
  7. package/dist/chunk-N554AFDB.js.map +1 -0
  8. package/dist/{chunk-JDO7W4D4.js → chunk-R5ZQZSEQ.js} +2 -20
  9. package/dist/{chunk-JDO7W4D4.js.map → chunk-R5ZQZSEQ.js.map} +1 -1
  10. package/dist/{chunk-R4TYXKDD.js → chunk-TPQRIPCN.js} +52 -48
  11. package/dist/chunk-TPQRIPCN.js.map +1 -0
  12. package/dist/copy.js +1 -0
  13. package/dist/copy.js.map +1 -0
  14. package/dist/data-display/CardKit.js +277 -0
  15. package/dist/data-display/CardKit.js.map +1 -0
  16. package/dist/data-display/DataViews.js +664 -616
  17. package/dist/data-display/DataViews.js.map +1 -1
  18. package/dist/data-display/SectionOnboarding.js +3 -3
  19. package/dist/data-display/SectionOnboarding.js.map +1 -1
  20. package/dist/feedback/ConfirmAction.js +3 -5
  21. package/dist/form/CategorySelect.js +1 -1
  22. package/dist/form/CepField.js +1 -1
  23. package/dist/form/total-form.js +8 -4
  24. package/dist/form/total-form.js.map +1 -1
  25. package/dist/layout/ContentToolbar.js +1 -1
  26. package/dist/layout/TableFilter.js +1 -1
  27. package/dist/pt-BR.js +222 -0
  28. package/dist/pt-BR.js.map +1 -0
  29. package/dist/types/components/data-display/CardKit/CardKebab.d.ts +23 -0
  30. package/dist/types/components/data-display/CardKit/CardKebab.d.ts.map +1 -0
  31. package/dist/types/components/data-display/CardKit/CardKit.types.d.ts +55 -0
  32. package/dist/types/components/data-display/CardKit/CardKit.types.d.ts.map +1 -0
  33. package/dist/types/components/data-display/CardKit/card-actions-context.d.ts +51 -0
  34. package/dist/types/components/data-display/CardKit/card-actions-context.d.ts.map +1 -0
  35. package/dist/types/components/data-display/CardKit/index.d.ts +15 -0
  36. package/dist/types/components/data-display/CardKit/index.d.ts.map +1 -0
  37. package/dist/types/components/data-display/CardKit/list-card-parts.d.ts +60 -0
  38. package/dist/types/components/data-display/CardKit/list-card-parts.d.ts.map +1 -0
  39. package/dist/types/components/data-display/CardKit/row-actions-to-menu.d.ts +17 -0
  40. package/dist/types/components/data-display/CardKit/row-actions-to-menu.d.ts.map +1 -0
  41. package/dist/types/components/data-display/CardKit/use-remove-confirm.d.ts +65 -0
  42. package/dist/types/components/data-display/CardKit/use-remove-confirm.d.ts.map +1 -0
  43. package/dist/types/components/data-display/CardKit/use-row-confirm.d.ts +60 -0
  44. package/dist/types/components/data-display/CardKit/use-row-confirm.d.ts.map +1 -0
  45. package/dist/types/components/data-display/DataViews/DataViewsBoard.d.ts.map +1 -1
  46. package/dist/types/components/data-display/DataViews/DeleteViewDialog.d.ts.map +1 -1
  47. package/dist/types/components/data-display/DataViews/ManageViewsDialog.d.ts +4 -1
  48. package/dist/types/components/data-display/DataViews/ManageViewsDialog.d.ts.map +1 -1
  49. package/dist/types/components/data-display/DataViews/SaveViewModal.d.ts +0 -5
  50. package/dist/types/components/data-display/DataViews/SaveViewModal.d.ts.map +1 -1
  51. package/dist/types/components/data-display/DataViews/base-card.d.ts.map +1 -1
  52. package/dist/types/components/data-display/DataViews/base-list-card-gutters.d.ts.map +1 -1
  53. package/dist/types/components/data-display/DataViews/data-views-bar-controls.d.ts.map +1 -1
  54. package/dist/types/components/data-display/DataViews/data-views-category-pill.d.ts.map +1 -1
  55. package/dist/types/components/data-display/DataViews/data-views-columns-tab.d.ts.map +1 -1
  56. package/dist/types/components/data-display/DataViews/data-views-copy-context.d.ts +17 -0
  57. package/dist/types/components/data-display/DataViews/data-views-copy-context.d.ts.map +1 -0
  58. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +205 -0
  59. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -0
  60. package/dist/types/components/data-display/DataViews/data-views-display-panel.d.ts.map +1 -1
  61. package/dist/types/components/data-display/DataViews/data-views-display-tabs.d.ts.map +1 -1
  62. package/dist/types/components/data-display/DataViews/data-views-export.d.ts.map +1 -1
  63. package/dist/types/components/data-display/DataViews/data-views-filter-panel.d.ts.map +1 -1
  64. package/dist/types/components/data-display/DataViews/data-views-grid-helpers.d.ts.map +1 -1
  65. package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts.map +1 -1
  66. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
  67. package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
  68. package/dist/types/components/data-display/DataViews/data-views-range-presets.d.ts +3 -2
  69. package/dist/types/components/data-display/DataViews/data-views-range-presets.d.ts.map +1 -1
  70. package/dist/types/components/data-display/DataViews/data-views-scopes.d.ts.map +1 -1
  71. package/dist/types/components/data-display/DataViews/data-views-search.d.ts.map +1 -1
  72. package/dist/types/components/data-display/DataViews/data-views-select-all-strip.d.ts.map +1 -1
  73. package/dist/types/components/data-display/DataViews/data-views-table-parts.d.ts.map +1 -1
  74. package/dist/types/components/data-display/DataViews/data-views-view-nav.d.ts +1 -1
  75. package/dist/types/components/data-display/DataViews/data-views-view-nav.d.ts.map +1 -1
  76. package/dist/types/components/data-display/DataViews/index.d.ts +2 -0
  77. package/dist/types/components/data-display/DataViews/index.d.ts.map +1 -1
  78. package/dist/types/components/data-display/DataViews/save-view-preview.d.ts +10 -0
  79. package/dist/types/components/data-display/DataViews/save-view-preview.d.ts.map +1 -0
  80. package/dist/types/components/data-display/DataViews/use-saved-views-controller.d.ts.map +1 -1
  81. package/dist/types/components/data-display/SectionOnboarding/SectionOnboarding.types.d.ts +6 -5
  82. package/dist/types/components/data-display/SectionOnboarding/SectionOnboarding.types.d.ts.map +1 -1
  83. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.hooks.d.ts +1 -3
  84. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.hooks.d.ts.map +1 -1
  85. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.parts.d.ts +4 -1
  86. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.parts.d.ts.map +1 -1
  87. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.types.d.ts +9 -1
  88. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.types.d.ts.map +1 -1
  89. package/dist/types/components/feedback/ConfirmAction/ConfirmButton.d.ts +2 -1
  90. package/dist/types/components/feedback/ConfirmAction/ConfirmButton.d.ts.map +1 -1
  91. package/dist/types/components/feedback/ConfirmAction/index.d.ts +2 -2
  92. package/dist/types/components/feedback/ConfirmAction/index.d.ts.map +1 -1
  93. package/dist/types/components/feedback/ConfirmAction/with-confirmation.d.ts +5 -1
  94. package/dist/types/components/feedback/ConfirmAction/with-confirmation.d.ts.map +1 -1
  95. package/dist/types/components/form/CategorySelect/CategoryPanel.d.ts +4 -1
  96. package/dist/types/components/form/CategorySelect/CategoryPanel.d.ts.map +1 -1
  97. package/dist/types/components/form/CategorySelect/CategoryPanelFoot.d.ts +5 -2
  98. package/dist/types/components/form/CategorySelect/CategoryPanelFoot.d.ts.map +1 -1
  99. package/dist/types/components/form/CategorySelect/CategoryPanelHead.d.ts +5 -2
  100. package/dist/types/components/form/CategorySelect/CategoryPanelHead.d.ts.map +1 -1
  101. package/dist/types/components/form/CategorySelect/CategoryPanelList.d.ts +3 -0
  102. package/dist/types/components/form/CategorySelect/CategoryPanelList.d.ts.map +1 -1
  103. package/dist/types/components/form/CategorySelect/CategorySelect.types.d.ts +5 -0
  104. package/dist/types/components/form/CategorySelect/CategorySelect.types.d.ts.map +1 -1
  105. package/dist/types/components/form/CategorySelect/CategoryTrigger.d.ts +4 -1
  106. package/dist/types/components/form/CategorySelect/CategoryTrigger.d.ts.map +1 -1
  107. package/dist/types/components/form/CepField/CepField.d.ts +1 -1
  108. package/dist/types/components/form/CepField/CepField.d.ts.map +1 -1
  109. package/dist/types/components/form/CepField/CepField.types.d.ts +3 -0
  110. package/dist/types/components/form/CepField/CepField.types.d.ts.map +1 -1
  111. package/dist/types/components/form/total-form/Fields/CategoryField.d.ts +4 -1
  112. package/dist/types/components/form/total-form/Fields/CategoryField.d.ts.map +1 -1
  113. package/dist/types/components/form/total-form/Fields/CepField.d.ts +4 -1
  114. package/dist/types/components/form/total-form/Fields/CepField.d.ts.map +1 -1
  115. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts +6 -1
  116. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts.map +1 -1
  117. package/dist/types/components/layout/ContentToolbar/SavedViewsMenu.d.ts.map +1 -1
  118. package/dist/types/copy.d.ts +99 -0
  119. package/dist/types/copy.d.ts.map +1 -0
  120. package/dist/types/pt-BR.d.ts +28 -0
  121. package/dist/types/pt-BR.d.ts.map +1 -0
  122. package/dist/types/utils/export-rows.d.ts +31 -0
  123. package/dist/types/utils/export-rows.d.ts.map +1 -0
  124. package/dist/types/utils/index.d.ts +1 -0
  125. package/dist/types/utils/index.d.ts.map +1 -1
  126. package/dist/utils.js +50 -0
  127. package/dist/utils.js.map +1 -1
  128. package/package.json +14 -2
  129. package/src/components/data-display/CardKit/CardKebab.tsx +43 -0
  130. package/src/components/data-display/CardKit/CardKit.md +87 -0
  131. package/src/components/data-display/CardKit/CardKit.types.ts +60 -0
  132. package/src/components/data-display/CardKit/card-actions-context.tsx +98 -0
  133. package/src/components/data-display/CardKit/index.ts +19 -0
  134. package/src/components/data-display/CardKit/list-card-parts.tsx +163 -0
  135. package/src/components/data-display/CardKit/row-actions-to-menu.ts +29 -0
  136. package/src/components/data-display/CardKit/use-remove-confirm.tsx +111 -0
  137. package/src/components/data-display/CardKit/use-row-confirm.tsx +96 -0
  138. package/src/components/data-display/DataViews/DataViewsBoard.tsx +7 -4
  139. package/src/components/data-display/DataViews/DeleteViewDialog.tsx +6 -4
  140. package/src/components/data-display/DataViews/ManageViewsDialog.tsx +26 -10
  141. package/src/components/data-display/DataViews/SaveViewModal.tsx +56 -90
  142. package/src/components/data-display/DataViews/base-card.tsx +3 -1
  143. package/src/components/data-display/DataViews/base-list-card-gutters.tsx +3 -1
  144. package/src/components/data-display/DataViews/data-views-bar-controls.tsx +8 -5
  145. package/src/components/data-display/DataViews/data-views-category-pill.tsx +6 -3
  146. package/src/components/data-display/DataViews/data-views-columns-tab.tsx +5 -3
  147. package/src/components/data-display/DataViews/data-views-copy-context.tsx +43 -0
  148. package/src/components/data-display/DataViews/data-views-copy.ts +214 -0
  149. package/src/components/data-display/DataViews/data-views-display-panel.tsx +15 -9
  150. package/src/components/data-display/DataViews/data-views-display-tabs.tsx +19 -29
  151. package/src/components/data-display/DataViews/data-views-export.tsx +9 -10
  152. package/src/components/data-display/DataViews/data-views-filter-panel.tsx +9 -5
  153. package/src/components/data-display/DataViews/data-views-grid-helpers.tsx +5 -2
  154. package/src/components/data-display/DataViews/data-views-more-fields.tsx +5 -3
  155. package/src/components/data-display/DataViews/data-views-more-filters.tsx +6 -3
  156. package/src/components/data-display/DataViews/data-views-range-pill.tsx +9 -5
  157. package/src/components/data-display/DataViews/data-views-range-presets.ts +7 -8
  158. package/src/components/data-display/DataViews/data-views-scopes.tsx +3 -1
  159. package/src/components/data-display/DataViews/data-views-search.tsx +7 -4
  160. package/src/components/data-display/DataViews/data-views-select-all-strip.tsx +5 -3
  161. package/src/components/data-display/DataViews/data-views-table-parts.tsx +7 -2
  162. package/src/components/data-display/DataViews/data-views-view-nav.tsx +22 -16
  163. package/src/components/data-display/DataViews/index.ts +20 -0
  164. package/src/components/data-display/DataViews/save-view-preview.tsx +78 -0
  165. package/src/components/data-display/DataViews/use-saved-views-controller.ts +3 -1
  166. package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +3 -3
  167. package/src/components/data-display/SectionOnboarding/SectionOnboarding.types.ts +8 -7
  168. package/src/components/feedback/ConfirmAction/ConfirmAction.hooks.ts +2 -2
  169. package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +12 -5
  170. package/src/components/feedback/ConfirmAction/ConfirmAction.types.ts +9 -1
  171. package/src/components/feedback/ConfirmAction/ConfirmButton.tsx +12 -4
  172. package/src/components/feedback/ConfirmAction/index.ts +2 -2
  173. package/src/components/feedback/ConfirmAction/with-confirmation.tsx +4 -3
  174. package/src/components/form/CategorySelect/CategoryPanel.tsx +18 -6
  175. package/src/components/form/CategorySelect/CategoryPanelFoot.tsx +10 -5
  176. package/src/components/form/CategorySelect/CategoryPanelHead.tsx +8 -3
  177. package/src/components/form/CategorySelect/CategoryPanelList.tsx +17 -10
  178. package/src/components/form/CategorySelect/CategorySelect.tsx +3 -3
  179. package/src/components/form/CategorySelect/CategorySelect.types.ts +5 -0
  180. package/src/components/form/CategorySelect/CategoryTrigger.tsx +5 -1
  181. package/src/components/form/CepField/CepField.tsx +6 -9
  182. package/src/components/form/CepField/CepField.types.ts +3 -0
  183. package/src/components/form/total-form/Fields/CategoryField.tsx +5 -0
  184. package/src/components/form/total-form/Fields/CepField.tsx +5 -0
  185. package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +6 -1
  186. package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +1 -21
  187. package/src/components/layout/TableFilter/TableFilter.tsx +1 -1
  188. package/src/copy.ts +103 -0
  189. package/src/pt-BR.ts +247 -0
  190. package/src/utils/export-rows.ts +95 -0
  191. package/src/utils/index.ts +1 -0
  192. package/dist/chunk-62XQWGXU.js.map +0 -1
  193. package/dist/chunk-7XLEWBKL.js.map +0 -1
  194. package/dist/chunk-R4TYXKDD.js.map +0 -1
@@ -0,0 +1,96 @@
1
+ 'use client';
2
+
3
+ import { useState, type JSX } from 'react';
4
+
5
+ import type { ConfirmActionCopy } from '../../../copy';
6
+ import { ConfirmActionDialog, useConfirmAction } from '../../feedback/ConfirmAction';
7
+
8
+ /**
9
+ * How the popup should read for the rows about to be acted on.
10
+ *
11
+ * Built from the SELECTION rather than from a single row, because the same
12
+ * guard covers a bulk action: "Delete 4 categories?" and "Delete the category
13
+ * Drinks?" are the same question asked of a different-sized selection — and an
14
+ * operator who multi-selected by mistake is exactly who this popup is for.
15
+ */
16
+ export interface RowConfirmCopy {
17
+ title: string;
18
+ description: string;
19
+ /** Echoed into the popup; omit for a multi-row selection the title covers. */
20
+ entityName?: string;
21
+ /** The VERB. Required, never defaulted — see {@link useRowConfirm}. */
22
+ confirmText: string;
23
+ /** Override where the word "cancel" would mean the ACTION itself. */
24
+ cancelText?: string;
25
+ /** Require typing this before the confirm enables — for the unrecoverable few. */
26
+ typeToConfirm?: string;
27
+ }
28
+
29
+ interface RowConfirmOptions<T> {
30
+ /**
31
+ * The destructive write, over the whole selection. Called ONLY after the
32
+ * operator confirms; throw (or reject) to keep the popup open carrying the
33
+ * message.
34
+ */
35
+ write: (rows: T[]) => Promise<unknown>;
36
+ /** The wording, given the selection. */
37
+ describe: (rows: T[]) => RowConfirmCopy;
38
+ dataTestId: string;
39
+ /** Shown when `write` fails without a message of its own. */
40
+ errorText: string;
41
+ /** The dialog's own words. REQUIRED — this package ships no default copy. */
42
+ copy: ConfirmActionCopy;
43
+ }
44
+
45
+ export interface RowConfirm<T> {
46
+ /** Hand this a grid action's `rows`: it opens the popup and writes nothing. */
47
+ request: (rows: T[]) => void;
48
+ /** Render next to the grid. */
49
+ dialog: JSX.Element | null;
50
+ }
51
+
52
+ /**
53
+ * Confirmation for a grid's `RowAction` — the per-row kebab entry and the
54
+ * multi-select actions menu both, since they run the same handler.
55
+ *
56
+ * The SELECTION is held while the popup is open, so the write sees exactly the
57
+ * rows the operator was looking at when they were asked. Re-reading the grid's
58
+ * live selection at confirm time is the bug this shape avoids: the list can
59
+ * refresh underneath an open dialog.
60
+ *
61
+ * `confirmText` is required and comes from `describe`, with no default. A verb
62
+ * defaulted in the origin's language reads as finished to the next adopter
63
+ * right up until an operator sees it — and `ConfirmOptions` already refuses to
64
+ * default one for the same reason.
65
+ */
66
+ export function useRowConfirm<T>({
67
+ write,
68
+ describe,
69
+ dataTestId,
70
+ errorText,
71
+ copy,
72
+ }: RowConfirmOptions<T>): RowConfirm<T> {
73
+ const [target, setTarget] = useState<T[] | null>(null);
74
+
75
+ const state = useConfirmAction(async () => {
76
+ if (!target) return;
77
+ await write(target);
78
+ setTarget(null);
79
+ }, errorText);
80
+
81
+ const request = (rows: T[]): void => {
82
+ // An empty selection has nothing to confirm — asking would be a popup about
83
+ // nothing, and confirming it would write nothing.
84
+ if (rows.length === 0) return;
85
+ setTarget(rows);
86
+ state.request();
87
+ };
88
+
89
+ return {
90
+ request,
91
+ dialog: target ? (
92
+ <ConfirmActionDialog state={state}
93
+ copy={copy} {...describe(target)} dataTestId={dataTestId} />
94
+ ) : null,
95
+ };
96
+ }
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { useDataViewsCopy } from "./data-views-copy-context";
3
4
  import { Box } from "../../../mui/Box";
4
5
  import { Text } from "../../typography/Text";
5
6
 
@@ -134,6 +135,7 @@ function BoardColumnHeader({
134
135
  sum: string | null;
135
136
  testId: string;
136
137
  }): React.JSX.Element {
138
+ const copy = useDataViewsCopy();
137
139
  return (
138
140
  <Box sx={{ display: "flex", flexDirection: "column", gap: 0.25 }}>
139
141
  <Box sx={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 1 }}>
@@ -146,10 +148,10 @@ function BoardColumnHeader({
146
148
  <Box
147
149
  component="span"
148
150
  data-testid={`${testId}-count`}
149
- aria-label={`${label}: ${count} nesta página`}
151
+ aria-label={copy.board.countOnPage(label, count)}
150
152
  sx={{ color: "text.secondary", whiteSpace: "nowrap" }}
151
153
  >
152
- {count} nesta página
154
+ {copy.board.onThisPage(count)}
153
155
  </Box>
154
156
  </Text>
155
157
  </Box>
@@ -158,7 +160,7 @@ function BoardColumnHeader({
158
160
  <Box
159
161
  component="span"
160
162
  data-testid={`${testId}-sum`}
161
- aria-label={`Soma desta página: ${sum}`}
163
+ aria-label={copy.board.pageSum(sum)}
162
164
  sx={{ color: "text.secondary" }}
163
165
  >
164
166
  {sum}
@@ -283,6 +285,7 @@ export function DataViewsBoard<T extends Record<string, unknown>>({
283
285
  cardScale,
284
286
  dataTestId,
285
287
  }: DataViewsBoardProps<T>): React.JSX.Element {
288
+ const copy = useDataViewsCopy();
286
289
  const columns = groupRows(rows, board);
287
290
  const width = Math.max(MIN_COLUMN_WIDTH, Math.round(BASE_COLUMN_WIDTH * cardScale));
288
291
  const testId = dataTestId ? `${dataTestId}-board` : "data-views-board";
@@ -290,7 +293,7 @@ export function DataViewsBoard<T extends Record<string, unknown>>({
290
293
  <Box sx={{ mt: 1.5 }}>
291
294
  <Text variant="caption" as="p">
292
295
  <Box component="span" data-testid={`${testId}-scale-note`} sx={{ color: "text.secondary" }}>
293
- As contagens e somas abaixo referem-se apenas aos itens desta página.
296
+ {copy.board.pageScopeNote}
294
297
  </Box>
295
298
  </Text>
296
299
  <Box
@@ -12,6 +12,7 @@
12
12
  * records are untouched, and warns separately when the view is SHARED, because
13
13
  * that deletion takes it from everyone rather than from one person.
14
14
  */
15
+ import { useDataViewsCopy } from "./data-views-copy-context";
15
16
  import { Dialog, DialogContent } from "../../feedback/Dialog";
16
17
  import { Button } from "../../form/Button";
17
18
  import { Stack } from "../../../mui/Stack";
@@ -33,12 +34,13 @@ export function DeleteViewDialog({
33
34
  entityLabel?: string;
34
35
  testIdPrefix?: string;
35
36
  }): React.JSX.Element | null {
37
+ const copy = useDataViewsCopy();
36
38
  if (!view) return null;
37
39
  return (
38
40
  <Dialog
39
41
  open
40
42
  onClose={onClose}
41
- title="Excluir visão"
43
+ title={copy.deleteView.title}
42
44
  size="sm"
43
45
  showCloseButton
44
46
  dataTestId={`${testIdPrefix}-delete-modal`}
@@ -51,12 +53,12 @@ export function DeleteViewDialog({
51
53
 
52
54
  {view.shared && (
53
55
  <Text variant="body" color="warning" as="p" data-testid={`${testIdPrefix}-delete-shared`}>
54
- Esta visão está compartilhada — a equipe perderá o acesso a ela.
56
+ {copy.deleteView.sharedWarning}
55
57
  </Text>
56
58
  )}
57
59
 
58
60
  <Text variant="body" color="secondary" as="p">
59
- {entityLabel} não são afetados; apenas o recorte salvo é removido.
61
+ {copy.deleteView.rowsUnaffected(entityLabel)}
60
62
  </Text>
61
63
 
62
64
  <Stack direction="row" spacing={1.5} sx={{ justifyContent: "flex-end", pt: 0.5 }}>
@@ -73,7 +75,7 @@ export function DeleteViewDialog({
73
75
  onClick={() => onConfirm(view)}
74
76
  dataTestId={`${testIdPrefix}-delete-confirm`}
75
77
  >
76
- Excluir visão
78
+ {copy.deleteView.confirm}
77
79
  </Button>
78
80
  </Stack>
79
81
  </Stack>
@@ -1,6 +1,10 @@
1
1
  "use client";
2
2
 
3
- import { ConfirmButton } from "../../feedback/ConfirmAction";
3
+ import { useMemo } from "react";
4
+
5
+ import type { ConfirmActionCopy } from "../../../copy";
6
+ import { useDataViewsCopy } from "./data-views-copy-context";
7
+ import { createConfirmButton } from "../../feedback/ConfirmAction";
4
8
  import { Dialog, DialogContent } from "../../feedback/Dialog";
5
9
  import { Button } from "../../form/Button";
6
10
  import { Box } from "../../../mui/Box";
@@ -10,6 +14,8 @@ import { Text } from "../../typography/Text";
10
14
  import type { SavedViewSummary } from "./data-views-types";
11
15
 
12
16
  interface ManageViewsDialogProps {
17
+ /** The confirm dialog's words. REQUIRED — this package ships no default copy. */
18
+ confirmCopy: ConfirmActionCopy;
13
19
  open: boolean;
14
20
  onClose: () => void;
15
21
  views: SavedViewSummary[];
@@ -19,11 +25,12 @@ interface ManageViewsDialogProps {
19
25
  }
20
26
 
21
27
  function tags(view: SavedViewSummary): string[] {
28
+ const copy = useDataViewsCopy();
22
29
  const list: string[] = [];
23
- if (view.isDefault) list.push("Padrão");
30
+ if (view.isDefault) list.push(copy.manageViews.defaultTag);
24
31
  if (view.pinned) list.push("Fixada");
25
32
  if (view.shared) list.push("Compartilhada");
26
- if (!view.isOwner) list.push("De outro usuário");
33
+ if (!view.isOwner) list.push(copy.manageViews.otherUserTag);
27
34
  return list;
28
35
  }
29
36
 
@@ -37,12 +44,19 @@ function ManageViewRow({
37
44
  onEdit,
38
45
  onDelete,
39
46
  testIdPrefix,
47
+ confirmCopy,
40
48
  }: {
41
49
  view: SavedViewSummary;
42
50
  onEdit: (view: SavedViewSummary) => void;
43
51
  onDelete: (view: SavedViewSummary) => void;
44
52
  testIdPrefix: string;
53
+ confirmCopy: ConfirmActionCopy;
45
54
  }): React.JSX.Element {
55
+ const ConfirmButton = useMemo(
56
+ () => createConfirmButton(confirmCopy, confirmCopy.defaultError),
57
+ [confirmCopy],
58
+ );
59
+ const copy = useDataViewsCopy();
46
60
  return (
47
61
  <Stack
48
62
  direction="row"
@@ -83,15 +97,15 @@ function ManageViewRow({
83
97
  color="danger"
84
98
  onClick={() => onDelete(view)}
85
99
  confirm={{
86
- title: 'Excluir a visão salva?',
100
+ title: copy.manageViews.deleteTitle,
87
101
  entityName: view.name,
88
102
  description:
89
- 'Os filtros e colunas guardados nela são perdidos. Não é possível restaurar.',
90
- confirmText: 'Excluir',
103
+ copy.manageViews.deleteBody,
104
+ confirmText: copy.manageViews.deleteConfirm,
91
105
  }}
92
106
  dataTestId={`${testIdPrefix}-manage-delete-${view.id}`}
93
107
  >
94
- Excluir
108
+ {copy.manageViews.deleteConfirm}
95
109
  </ConfirmButton>
96
110
  </Stack>
97
111
  )}
@@ -106,12 +120,14 @@ export function ManageViewsDialog({
106
120
  onEdit,
107
121
  onDelete,
108
122
  testIdPrefix = "views",
123
+ confirmCopy,
109
124
  }: ManageViewsDialogProps): React.JSX.Element {
125
+ const copy = useDataViewsCopy();
110
126
  return (
111
127
  <Dialog
112
128
  open={open}
113
129
  onClose={onClose}
114
- title="Gerenciar visões"
130
+ title={copy.manageViews.title}
115
131
  size="md"
116
132
  showCloseButton
117
133
  dataTestId={`${testIdPrefix}-manage-dialog`}
@@ -120,11 +136,11 @@ export function ManageViewsDialog({
120
136
  <Stack spacing={1.5} data-testid={`${testIdPrefix}-manage-list`}>
121
137
  {views.length === 0 ? (
122
138
  <Text variant="body" as="p">
123
- Nenhuma visão salva.
139
+ {copy.manageViews.empty}
124
140
  </Text>
125
141
  ) : (
126
142
  views.map((view) => (
127
- <ManageViewRow
143
+ <ManageViewRow confirmCopy={confirmCopy}
128
144
  key={view.id}
129
145
  view={view}
130
146
  onEdit={onEdit}
@@ -5,6 +5,7 @@ import PushPinOutlinedIcon from "@mui/icons-material/PushPinOutlined";
5
5
  import StarOutlineRoundedIcon from "@mui/icons-material/StarOutlineRounded";
6
6
  import { useState } from "react";
7
7
 
8
+ import { useDataViewsCopy } from "./data-views-copy-context";
8
9
  import { Dialog, DialogContent } from "../../feedback/Dialog";
9
10
  import { Button } from "../../form/Button";
10
11
  import { Input } from "../../form/Input";
@@ -14,6 +15,7 @@ import { Stack } from "../../../mui/Stack";
14
15
  import { Text } from "../../typography/Text";
15
16
 
16
17
  import { describeViewState } from "./data-views-preview";
18
+ import { PreviewBox } from "./save-view-preview";
17
19
  import type {
18
20
  DataViewColumn,
19
21
  DataViewState,
@@ -119,28 +121,29 @@ function FlagsSwitches({
119
121
  setIsDefault,
120
122
  testIdPrefix,
121
123
  }: FlagsSwitchesProps): React.JSX.Element {
124
+ const copy = useDataViewsCopy();
122
125
  return (
123
126
  <Stack spacing={0}>
124
127
  <FlagRow
125
128
  icon={<PushPinOutlinedIcon fontSize="small" />}
126
- title="Fixar na barra lateral"
127
- description="Aparece como atalho no menu, abaixo desta tela."
129
+ title={copy.saveView.pinnedTitle}
130
+ description={copy.saveView.pinnedDescription}
128
131
  checked={pinned}
129
132
  onChange={setPinned}
130
133
  testId={`${testIdPrefix}-save-pinned`}
131
134
  />
132
135
  <FlagRow
133
136
  icon={<GroupOutlinedIcon fontSize="small" />}
134
- title="Compartilhar com a equipe"
135
- description="Qualquer pessoa da loja poderá abrir e usar esta visão."
137
+ title={copy.saveView.sharedTitle}
138
+ description={copy.saveView.sharedDescription}
136
139
  checked={shared}
137
140
  onChange={setShared}
138
141
  testId={`${testIdPrefix}-save-shared`}
139
142
  />
140
143
  <FlagRow
141
144
  icon={<StarOutlineRoundedIcon fontSize="small" />}
142
- title="Definir como padrão"
143
- description="Esta tela abre nesta visão em vez da Visão principal."
145
+ title={copy.saveView.setDefaultTitle}
146
+ description={copy.saveView.setDefaultDescription}
144
147
  checked={isDefault}
145
148
  onChange={setIsDefault}
146
149
  testId={`${testIdPrefix}-save-default`}
@@ -149,70 +152,6 @@ function FlagsSwitches({
149
152
  );
150
153
  }
151
154
 
152
- interface PreviewBoxProps {
153
- preview: { filters: string[]; columns: string[]; sort: string[] };
154
- testIdPrefix: string;
155
- }
156
-
157
- /** One "label — value" line of the summary. */
158
- function SummaryRow({
159
- label,
160
- items,
161
- testId,
162
- }: {
163
- label: string;
164
- items: string[];
165
- testId: string;
166
- }): React.JSX.Element | null {
167
- if (items.length === 0) return null;
168
- return (
169
- <Box sx={{ display: "flex", gap: 1.5 }} data-testid={testId}>
170
- <Text variant="caption" as="span">
171
- <Box component="span" sx={{ width: 116, flexShrink: 0, color: "text.secondary" }}>
172
- {label}
173
- </Box>
174
- </Text>
175
- <Text variant="caption" as="span">
176
- <Box component="span" sx={{ minWidth: 0, flex: 1 }}>
177
- {items.join(", ")}
178
- </Box>
179
- </Text>
180
- </Box>
181
- );
182
- }
183
-
184
- function PreviewBox({ preview, testIdPrefix }: PreviewBoxProps): React.JSX.Element {
185
- const empty =
186
- preview.filters.length === 0 && preview.columns.length === 0 && preview.sort.length === 0;
187
- return (
188
- <Box sx={{ p: 1.5, borderRadius: 1.5, bgcolor: "action.hover", border: (theme) => `1px solid ${theme.palette.divider}` }}>
189
- <Text variant="caption" as="p">
190
- <Box
191
- component="span"
192
- sx={{ display: "block", mb: 1, textTransform: "uppercase", letterSpacing: 0.6, fontWeight: 600, color: "text.disabled" }}
193
- >
194
- O que esta visão guarda
195
- </Box>
196
- </Text>
197
- {empty ? (
198
- // Saving is still allowed — an operator may be naming the default on
199
- // purpose — but it should not be a surprise afterwards.
200
- <Text variant="caption" as="p">
201
- <Box component="span" sx={{ color: "warning.main", lineHeight: 1.6 }} data-testid={`${testIdPrefix}-preview-empty`}>
202
- Nada foi alterado — esta visão ficará igual à Visão principal.
203
- </Box>
204
- </Text>
205
- ) : (
206
- <Stack spacing={0.75}>
207
- <SummaryRow label="Filtros" items={preview.filters} testId={`${testIdPrefix}-preview-filters`} />
208
- <SummaryRow label="Colunas ocultas" items={preview.columns} testId={`${testIdPrefix}-preview-columns`} />
209
- <SummaryRow label="Ordenação" items={preview.sort} testId={`${testIdPrefix}-preview-sort`} />
210
- </Stack>
211
- )}
212
- </Box>
213
- );
214
- }
215
-
216
155
  interface SaveViewFormState {
217
156
  name: string;
218
157
  description: string;
@@ -248,6 +187,7 @@ function useSaveViewForm(
248
187
  editing: SavedViewSummary | null | undefined,
249
188
  onSave: (payload: SaveViewPayload) => Promise<void> | void,
250
189
  ): SaveViewFormState {
190
+ const copy = useDataViewsCopy();
251
191
  const initial = seedForm(editing);
252
192
  const [name, setName] = useState(initial.name);
253
193
  const [description, setDescription] = useState(initial.description);
@@ -267,7 +207,7 @@ function useSaveViewForm(
267
207
  try {
268
208
  await onSave({ name: name.trim(), description: description.trim(), shared, pinned, isDefault });
269
209
  } catch (cause) {
270
- setError(cause instanceof Error ? cause.message : "Não foi possível salvar a visão.");
210
+ setError(cause instanceof Error ? cause.message : copy.saveView.saveFailed);
271
211
  } finally {
272
212
  setSaving(false);
273
213
  }
@@ -295,6 +235,38 @@ function useSaveViewForm(
295
235
  * share / default flags, and previews the filters, visible columns, and sort the
296
236
  * view will store. Used for both create and edit.
297
237
  */
238
+ /**
239
+ * The dialog's two buttons.
240
+ *
241
+ * Cancel first, primary last: the confirming action sits where the eye lands
242
+ * at the end of the dialog, not before the way out.
243
+ */
244
+ function FormFooter({
245
+ editing,
246
+ canSave,
247
+ onSubmit,
248
+ onClose,
249
+ testIdPrefix,
250
+ }: {
251
+ editing: SaveViewModalProps<never>["editing"];
252
+ canSave: boolean;
253
+ onSubmit: () => void;
254
+ onClose: () => void;
255
+ testIdPrefix: string;
256
+ }): React.JSX.Element {
257
+ const copy = useDataViewsCopy();
258
+ return (
259
+ <Stack direction="row" spacing={1.5} sx={{ justifyContent: "flex-end" }}>
260
+ <Button variant="text" color="neutral" onClick={onClose} dataTestId={`${testIdPrefix}-save-cancel`}>
261
+ {copy.saveView.cancel}
262
+ </Button>
263
+ <Button onClick={onSubmit} disabled={!canSave} dataTestId={`${testIdPrefix}-save-submit`}>
264
+ {editing ? copy.saveView.submitEditing : copy.saveView.submitCreating}
265
+ </Button>
266
+ </Stack>
267
+ );
268
+ }
269
+
298
270
  export function SaveViewModal<T extends Record<string, unknown>>({
299
271
  open,
300
272
  onClose,
@@ -305,6 +277,7 @@ export function SaveViewModal<T extends Record<string, unknown>>({
305
277
  onSave,
306
278
  testIdPrefix = "view",
307
279
  }: SaveViewModalProps<T>): React.JSX.Element {
280
+ const copy = useDataViewsCopy();
308
281
  const form = useSaveViewForm(editing, onSave);
309
282
  const preview = describeViewState(currentState, fields, columns);
310
283
 
@@ -312,7 +285,7 @@ export function SaveViewModal<T extends Record<string, unknown>>({
312
285
  <Dialog
313
286
  open={open}
314
287
  onClose={onClose}
315
- title={editing ? "Editar visão" : "Salvar visão"}
288
+ title={editing ? copy.saveView.titleEditing : copy.saveView.titleCreating}
316
289
  size="sm"
317
290
  showCloseButton
318
291
  dataTestId={`${testIdPrefix}-save-modal`}
@@ -322,8 +295,8 @@ export function SaveViewModal<T extends Record<string, unknown>>({
322
295
  <Input
323
296
  size="sm"
324
297
  fullWidth
325
- label="Nome"
326
- placeholder="Ex.: Recusados no PIX desta semana"
298
+ label={copy.saveView.nameLabel}
299
+ placeholder={copy.saveView.namePlaceholder}
327
300
  value={form.name}
328
301
  onChange={(event) => form.setName(event.target.value)}
329
302
  data-testid={`${testIdPrefix}-save-name`}
@@ -331,8 +304,8 @@ export function SaveViewModal<T extends Record<string, unknown>>({
331
304
  <Input
332
305
  size="sm"
333
306
  fullWidth
334
- label="Descrição — opcional"
335
- placeholder="Para que serve esta visão"
307
+ label={copy.saveView.descriptionLabel}
308
+ placeholder={copy.saveView.descriptionPlaceholder}
336
309
  value={form.description}
337
310
  onChange={(event) => form.setDescription(event.target.value)}
338
311
  data-testid={`${testIdPrefix}-save-description`}
@@ -356,20 +329,13 @@ export function SaveViewModal<T extends Record<string, unknown>>({
356
329
  </Text>
357
330
  )}
358
331
 
359
- {/* Cancel first, primary last: the confirming action sits where the
360
- eye lands at the end of the dialog, not before the way out. */}
361
- <Stack direction="row" spacing={1.5} sx={{ justifyContent: "flex-end" }}>
362
- <Button variant="text" color="neutral" onClick={onClose} dataTestId={`${testIdPrefix}-save-cancel`}>
363
- Cancelar
364
- </Button>
365
- <Button
366
- onClick={() => void form.submit()}
367
- disabled={!form.canSave}
368
- dataTestId={`${testIdPrefix}-save-submit`}
369
- >
370
- {editing ? "Salvar alterações" : "Salvar visão"}
371
- </Button>
372
- </Stack>
332
+ <FormFooter
333
+ editing={editing}
334
+ canSave={form.canSave}
335
+ onSubmit={() => void form.submit()}
336
+ onClose={onClose}
337
+ testIdPrefix={testIdPrefix}
338
+ />
373
339
  </Stack>
374
340
  </DialogContent>
375
341
  </Dialog>
@@ -22,6 +22,7 @@ import {
22
22
  type CardSurfaceProps,
23
23
  } from "./card-surface";
24
24
  import { CARD_ASPECT_RATIOS, type CardAspectRatio } from "./data-views-types";
25
+ import { useDataViewsCopy } from "./data-views-copy-context";
25
26
 
26
27
  export interface BaseCardProps extends CardSurfaceProps {
27
28
  /** Extra body content below the caption (chips, meta). Optional. */
@@ -127,6 +128,7 @@ function CardOverlays({
127
128
  checkboxTestId,
128
129
  menu,
129
130
  }: Pick<BaseCardProps, "selected" | "onToggleSelect" | "checkboxTestId" | "menu">): React.JSX.Element {
131
+ const copy = useDataViewsCopy();
130
132
  return (
131
133
  <>
132
134
  {onToggleSelect && (
@@ -138,7 +140,7 @@ function CardOverlays({
138
140
  onClick={(event) => event.stopPropagation()}
139
141
  size="small"
140
142
  data-testid={checkboxTestId}
141
- aria-label="Selecionar"
143
+ aria-label={copy.selection.selectRow}
142
144
  />
143
145
  </Box>
144
146
  )}
@@ -7,6 +7,7 @@ import { Checkbox } from "../../form/Checkbox";
7
7
  import { Box } from "../../../mui/Box";
8
8
 
9
9
  import { DragHandle, useDragItem } from "./data-views-drag";
10
+ import { useDataViewsCopy } from "./data-views-copy-context";
10
11
 
11
12
  /**
12
13
  * THE THREE GUTTERS AT THE HEAD OF A ROW — the disclosure chevron, the drag grip
@@ -72,6 +73,7 @@ export function SelectSlot({
72
73
  onToggleSelect?: (event?: React.MouseEvent) => void;
73
74
  testId?: string;
74
75
  }): React.JSX.Element | null {
76
+ const copy = useDataViewsCopy();
75
77
  if (!selectable) return reserve ? <Box data-slot="select" /> : null;
76
78
  return (
77
79
  // z-index 1: above the stretched link, or the anchor swallows the click.
@@ -88,7 +90,7 @@ export function SelectSlot({
88
90
  }}
89
91
  size="small"
90
92
  data-testid={testId}
91
- aria-label="Selecionar"
93
+ aria-label={copy.selection.selectRow}
92
94
  />
93
95
  </Box>
94
96
  );
@@ -15,6 +15,7 @@ import FilterAltOffRoundedIcon from "@mui/icons-material/FilterAltOffRounded";
15
15
  import { Tooltip } from "@mui/material";
16
16
 
17
17
  import { Box } from "../../../mui/Box";
18
+ import { useDataViewsCopy } from "./data-views-copy-context";
18
19
 
19
20
  /**
20
21
  * "Limpar" — one gesture back to the unfiltered list.
@@ -38,6 +39,7 @@ export function ClearAllControl({
38
39
  compact: boolean;
39
40
  testIdPrefix: string;
40
41
  }): React.JSX.Element {
42
+ const copy = useDataViewsCopy();
41
43
  // No border, no fill. Every other control on this row is an outlined pill
42
44
  // because it OPENS something; this one is an escape hatch, and giving it the
43
45
  // same weight made it read as a sixth filter. Quiet until hovered.
@@ -47,7 +49,7 @@ export function ClearAllControl({
47
49
  type="button"
48
50
  onClick={onClearAll}
49
51
  data-testid={`${testIdPrefix}-clear-all`}
50
- aria-label="Limpar filtros"
52
+ aria-label={copy.filters.clearAll}
51
53
  sx={{
52
54
  display: "inline-flex",
53
55
  alignItems: "center",
@@ -66,10 +68,10 @@ export function ClearAllControl({
66
68
  }}
67
69
  >
68
70
  <FilterAltOffRoundedIcon sx={{ fontSize: 16 }} />
69
- {!compact && <Box component="span">Limpar</Box>}
71
+ {!compact && <Box component="span">{copy.filters.clear}</Box>}
70
72
  </Box>
71
73
  );
72
- return compact ? <Tooltip title="Limpar filtros">{button}</Tooltip> : button;
74
+ return compact ? <Tooltip title={copy.filters.clearAll}>{button}</Tooltip> : button;
73
75
  }
74
76
 
75
77
  /**
@@ -91,13 +93,14 @@ export function CloseSearchControl({
91
93
  onClose: () => void;
92
94
  testIdPrefix: string;
93
95
  }): React.JSX.Element {
96
+ const copy = useDataViewsCopy();
94
97
  return (
95
- <Tooltip title="Fechar busca">
98
+ <Tooltip title={copy.search.close}>
96
99
  <Box
97
100
  component="button"
98
101
  type="button"
99
102
  onClick={onClose}
100
- aria-label="Fechar busca"
103
+ aria-label={copy.search.close}
101
104
  data-testid={`${testIdPrefix}-search-close`}
102
105
  sx={{
103
106
  display: "inline-flex",
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { useDataViewsCopy } from "./data-views-copy-context";
3
4
  import { CategorySelect } from "../../form/CategorySelect";
4
5
  import { MultiSelectDropdown } from "../../layout/ContentToolbar";
5
6
 
@@ -65,10 +66,12 @@ export function PillControl<T extends Record<string, unknown>>({
65
66
  testIdPrefix,
66
67
  layout = "pill",
67
68
  }: PillControlProps<T>): React.JSX.Element {
69
+ const copy = useDataViewsCopy();
68
70
  const testId = `${testIdPrefix}-filter-${fieldId}`;
69
71
  if (pill.control === "category") {
70
72
  return (
71
73
  <CategorySelect
74
+ copy={copy.categorySelect}
72
75
  placeholder={pill.label}
73
76
  label={layout === "stacked" ? pill.label : undefined}
74
77
  fullWidth={layout === "stacked"}
@@ -86,10 +89,10 @@ export function PillControl<T extends Record<string, unknown>>({
86
89
  selected={new Set(selected)}
87
90
  onToggle={(value, checked) => onTogglePill(fieldId, value, checked)}
88
91
  onClear={() => onClearField(fieldId)}
89
- allLabel="Todas"
92
+ allLabel={copy.filters.allOption}
90
93
  searchable={pill.searchEnabled ? true : undefined}
91
- searchPlaceholder="Buscar…"
92
- noResultsLabel="Nenhum resultado"
94
+ searchPlaceholder={copy.filters.optionSearchPlaceholder}
95
+ noResultsLabel={copy.filters.optionsEmpty}
93
96
  layout={layout}
94
97
  onOpenChange={onOpenChange}
95
98
  data-testid={testId}