@12-apps/ui 6.2.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 (166) hide show
  1. package/dist/{chunk-35VGGLOC.js → chunk-CEKPLUQV.js} +2 -2
  2. package/dist/{chunk-35VGGLOC.js.map → chunk-CEKPLUQV.js.map} +1 -1
  3. package/dist/{chunk-62XQWGXU.js → chunk-MXPXKORR.js} +19 -17
  4. package/dist/chunk-MXPXKORR.js.map +1 -0
  5. package/dist/{chunk-7XLEWBKL.js → chunk-N554AFDB.js} +7 -10
  6. package/dist/chunk-N554AFDB.js.map +1 -0
  7. package/dist/{chunk-JDO7W4D4.js → chunk-R5ZQZSEQ.js} +2 -20
  8. package/dist/{chunk-JDO7W4D4.js.map → chunk-R5ZQZSEQ.js.map} +1 -1
  9. package/dist/{chunk-R4TYXKDD.js → chunk-TPQRIPCN.js} +52 -48
  10. package/dist/chunk-TPQRIPCN.js.map +1 -0
  11. package/dist/copy.js +1 -0
  12. package/dist/copy.js.map +1 -0
  13. package/dist/data-display/CardKit.js +15 -4
  14. package/dist/data-display/CardKit.js.map +1 -1
  15. package/dist/data-display/DataViews.js +664 -616
  16. package/dist/data-display/DataViews.js.map +1 -1
  17. package/dist/data-display/SectionOnboarding.js +3 -3
  18. package/dist/data-display/SectionOnboarding.js.map +1 -1
  19. package/dist/feedback/ConfirmAction.js +3 -5
  20. package/dist/form/CategorySelect.js +1 -1
  21. package/dist/form/CepField.js +1 -1
  22. package/dist/form/total-form.js +8 -4
  23. package/dist/form/total-form.js.map +1 -1
  24. package/dist/layout/ContentToolbar.js +1 -1
  25. package/dist/layout/TableFilter.js +1 -1
  26. package/dist/pt-BR.js +222 -0
  27. package/dist/pt-BR.js.map +1 -0
  28. package/dist/types/components/data-display/CardKit/use-remove-confirm.d.ts +4 -1
  29. package/dist/types/components/data-display/CardKit/use-remove-confirm.d.ts.map +1 -1
  30. package/dist/types/components/data-display/CardKit/use-row-confirm.d.ts +5 -2
  31. package/dist/types/components/data-display/CardKit/use-row-confirm.d.ts.map +1 -1
  32. package/dist/types/components/data-display/DataViews/DataViewsBoard.d.ts.map +1 -1
  33. package/dist/types/components/data-display/DataViews/DeleteViewDialog.d.ts.map +1 -1
  34. package/dist/types/components/data-display/DataViews/ManageViewsDialog.d.ts +4 -1
  35. package/dist/types/components/data-display/DataViews/ManageViewsDialog.d.ts.map +1 -1
  36. package/dist/types/components/data-display/DataViews/SaveViewModal.d.ts +0 -5
  37. package/dist/types/components/data-display/DataViews/SaveViewModal.d.ts.map +1 -1
  38. package/dist/types/components/data-display/DataViews/base-card.d.ts.map +1 -1
  39. package/dist/types/components/data-display/DataViews/base-list-card-gutters.d.ts.map +1 -1
  40. package/dist/types/components/data-display/DataViews/data-views-bar-controls.d.ts.map +1 -1
  41. package/dist/types/components/data-display/DataViews/data-views-category-pill.d.ts.map +1 -1
  42. package/dist/types/components/data-display/DataViews/data-views-columns-tab.d.ts.map +1 -1
  43. package/dist/types/components/data-display/DataViews/data-views-copy-context.d.ts +17 -0
  44. package/dist/types/components/data-display/DataViews/data-views-copy-context.d.ts.map +1 -0
  45. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +205 -0
  46. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -0
  47. package/dist/types/components/data-display/DataViews/data-views-display-panel.d.ts.map +1 -1
  48. package/dist/types/components/data-display/DataViews/data-views-display-tabs.d.ts.map +1 -1
  49. package/dist/types/components/data-display/DataViews/data-views-export.d.ts.map +1 -1
  50. package/dist/types/components/data-display/DataViews/data-views-filter-panel.d.ts.map +1 -1
  51. package/dist/types/components/data-display/DataViews/data-views-grid-helpers.d.ts.map +1 -1
  52. package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts.map +1 -1
  53. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
  54. package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
  55. package/dist/types/components/data-display/DataViews/data-views-range-presets.d.ts +3 -2
  56. package/dist/types/components/data-display/DataViews/data-views-range-presets.d.ts.map +1 -1
  57. package/dist/types/components/data-display/DataViews/data-views-scopes.d.ts.map +1 -1
  58. package/dist/types/components/data-display/DataViews/data-views-search.d.ts.map +1 -1
  59. package/dist/types/components/data-display/DataViews/data-views-select-all-strip.d.ts.map +1 -1
  60. package/dist/types/components/data-display/DataViews/data-views-table-parts.d.ts.map +1 -1
  61. package/dist/types/components/data-display/DataViews/data-views-view-nav.d.ts +1 -1
  62. package/dist/types/components/data-display/DataViews/data-views-view-nav.d.ts.map +1 -1
  63. package/dist/types/components/data-display/DataViews/index.d.ts +2 -0
  64. package/dist/types/components/data-display/DataViews/index.d.ts.map +1 -1
  65. package/dist/types/components/data-display/DataViews/save-view-preview.d.ts +10 -0
  66. package/dist/types/components/data-display/DataViews/save-view-preview.d.ts.map +1 -0
  67. package/dist/types/components/data-display/DataViews/use-saved-views-controller.d.ts.map +1 -1
  68. package/dist/types/components/data-display/SectionOnboarding/SectionOnboarding.types.d.ts +6 -5
  69. package/dist/types/components/data-display/SectionOnboarding/SectionOnboarding.types.d.ts.map +1 -1
  70. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.hooks.d.ts +1 -3
  71. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.hooks.d.ts.map +1 -1
  72. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.parts.d.ts +4 -1
  73. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.parts.d.ts.map +1 -1
  74. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.types.d.ts +9 -1
  75. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.types.d.ts.map +1 -1
  76. package/dist/types/components/feedback/ConfirmAction/ConfirmButton.d.ts +2 -1
  77. package/dist/types/components/feedback/ConfirmAction/ConfirmButton.d.ts.map +1 -1
  78. package/dist/types/components/feedback/ConfirmAction/index.d.ts +2 -2
  79. package/dist/types/components/feedback/ConfirmAction/index.d.ts.map +1 -1
  80. package/dist/types/components/feedback/ConfirmAction/with-confirmation.d.ts +5 -1
  81. package/dist/types/components/feedback/ConfirmAction/with-confirmation.d.ts.map +1 -1
  82. package/dist/types/components/form/CategorySelect/CategoryPanel.d.ts +4 -1
  83. package/dist/types/components/form/CategorySelect/CategoryPanel.d.ts.map +1 -1
  84. package/dist/types/components/form/CategorySelect/CategoryPanelFoot.d.ts +5 -2
  85. package/dist/types/components/form/CategorySelect/CategoryPanelFoot.d.ts.map +1 -1
  86. package/dist/types/components/form/CategorySelect/CategoryPanelHead.d.ts +5 -2
  87. package/dist/types/components/form/CategorySelect/CategoryPanelHead.d.ts.map +1 -1
  88. package/dist/types/components/form/CategorySelect/CategoryPanelList.d.ts +3 -0
  89. package/dist/types/components/form/CategorySelect/CategoryPanelList.d.ts.map +1 -1
  90. package/dist/types/components/form/CategorySelect/CategorySelect.types.d.ts +5 -0
  91. package/dist/types/components/form/CategorySelect/CategorySelect.types.d.ts.map +1 -1
  92. package/dist/types/components/form/CategorySelect/CategoryTrigger.d.ts +4 -1
  93. package/dist/types/components/form/CategorySelect/CategoryTrigger.d.ts.map +1 -1
  94. package/dist/types/components/form/CepField/CepField.d.ts +1 -1
  95. package/dist/types/components/form/CepField/CepField.d.ts.map +1 -1
  96. package/dist/types/components/form/CepField/CepField.types.d.ts +3 -0
  97. package/dist/types/components/form/CepField/CepField.types.d.ts.map +1 -1
  98. package/dist/types/components/form/total-form/Fields/CategoryField.d.ts +4 -1
  99. package/dist/types/components/form/total-form/Fields/CategoryField.d.ts.map +1 -1
  100. package/dist/types/components/form/total-form/Fields/CepField.d.ts +4 -1
  101. package/dist/types/components/form/total-form/Fields/CepField.d.ts.map +1 -1
  102. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts +6 -1
  103. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts.map +1 -1
  104. package/dist/types/components/layout/ContentToolbar/SavedViewsMenu.d.ts.map +1 -1
  105. package/dist/types/copy.d.ts +99 -0
  106. package/dist/types/copy.d.ts.map +1 -0
  107. package/dist/types/pt-BR.d.ts +28 -0
  108. package/dist/types/pt-BR.d.ts.map +1 -0
  109. package/package.json +10 -2
  110. package/src/components/data-display/CardKit/use-remove-confirm.tsx +5 -0
  111. package/src/components/data-display/CardKit/use-row-confirm.tsx +7 -2
  112. package/src/components/data-display/DataViews/DataViewsBoard.tsx +7 -4
  113. package/src/components/data-display/DataViews/DeleteViewDialog.tsx +6 -4
  114. package/src/components/data-display/DataViews/ManageViewsDialog.tsx +26 -10
  115. package/src/components/data-display/DataViews/SaveViewModal.tsx +56 -90
  116. package/src/components/data-display/DataViews/base-card.tsx +3 -1
  117. package/src/components/data-display/DataViews/base-list-card-gutters.tsx +3 -1
  118. package/src/components/data-display/DataViews/data-views-bar-controls.tsx +8 -5
  119. package/src/components/data-display/DataViews/data-views-category-pill.tsx +6 -3
  120. package/src/components/data-display/DataViews/data-views-columns-tab.tsx +5 -3
  121. package/src/components/data-display/DataViews/data-views-copy-context.tsx +43 -0
  122. package/src/components/data-display/DataViews/data-views-copy.ts +214 -0
  123. package/src/components/data-display/DataViews/data-views-display-panel.tsx +15 -9
  124. package/src/components/data-display/DataViews/data-views-display-tabs.tsx +19 -29
  125. package/src/components/data-display/DataViews/data-views-export.tsx +9 -10
  126. package/src/components/data-display/DataViews/data-views-filter-panel.tsx +9 -5
  127. package/src/components/data-display/DataViews/data-views-grid-helpers.tsx +5 -2
  128. package/src/components/data-display/DataViews/data-views-more-fields.tsx +5 -3
  129. package/src/components/data-display/DataViews/data-views-more-filters.tsx +6 -3
  130. package/src/components/data-display/DataViews/data-views-range-pill.tsx +9 -5
  131. package/src/components/data-display/DataViews/data-views-range-presets.ts +7 -8
  132. package/src/components/data-display/DataViews/data-views-scopes.tsx +3 -1
  133. package/src/components/data-display/DataViews/data-views-search.tsx +7 -4
  134. package/src/components/data-display/DataViews/data-views-select-all-strip.tsx +5 -3
  135. package/src/components/data-display/DataViews/data-views-table-parts.tsx +7 -2
  136. package/src/components/data-display/DataViews/data-views-view-nav.tsx +22 -16
  137. package/src/components/data-display/DataViews/index.ts +20 -0
  138. package/src/components/data-display/DataViews/save-view-preview.tsx +78 -0
  139. package/src/components/data-display/DataViews/use-saved-views-controller.ts +3 -1
  140. package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +3 -3
  141. package/src/components/data-display/SectionOnboarding/SectionOnboarding.types.ts +8 -7
  142. package/src/components/feedback/ConfirmAction/ConfirmAction.hooks.ts +2 -2
  143. package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +12 -5
  144. package/src/components/feedback/ConfirmAction/ConfirmAction.types.ts +9 -1
  145. package/src/components/feedback/ConfirmAction/ConfirmButton.tsx +12 -4
  146. package/src/components/feedback/ConfirmAction/index.ts +2 -2
  147. package/src/components/feedback/ConfirmAction/with-confirmation.tsx +4 -3
  148. package/src/components/form/CategorySelect/CategoryPanel.tsx +18 -6
  149. package/src/components/form/CategorySelect/CategoryPanelFoot.tsx +10 -5
  150. package/src/components/form/CategorySelect/CategoryPanelHead.tsx +8 -3
  151. package/src/components/form/CategorySelect/CategoryPanelList.tsx +17 -10
  152. package/src/components/form/CategorySelect/CategorySelect.tsx +3 -3
  153. package/src/components/form/CategorySelect/CategorySelect.types.ts +5 -0
  154. package/src/components/form/CategorySelect/CategoryTrigger.tsx +5 -1
  155. package/src/components/form/CepField/CepField.tsx +6 -9
  156. package/src/components/form/CepField/CepField.types.ts +3 -0
  157. package/src/components/form/total-form/Fields/CategoryField.tsx +5 -0
  158. package/src/components/form/total-form/Fields/CepField.tsx +5 -0
  159. package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +6 -1
  160. package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +1 -21
  161. package/src/components/layout/TableFilter/TableFilter.tsx +1 -1
  162. package/src/copy.ts +103 -0
  163. package/src/pt-BR.ts +247 -0
  164. package/dist/chunk-62XQWGXU.js.map +0 -1
  165. package/dist/chunk-7XLEWBKL.js.map +0 -1
  166. package/dist/chunk-R4TYXKDD.js.map +0 -1
@@ -0,0 +1,78 @@
1
+ "use client";
2
+
3
+ /**
4
+ * What a saved view actually GUARDS, listed before it is saved.
5
+ *
6
+ * Split out of `SaveViewModal.tsx` when that file crossed the size gate: this
7
+ * is the one self-contained piece of it — a read-only summary of the view
8
+ * state, with no form and no dialog around it.
9
+ */
10
+ import { useDataViewsCopy } from "./data-views-copy-context";
11
+ import { Box } from "../../../mui/Box";
12
+ import { Stack } from "../../../mui/Stack";
13
+ import { Text } from "../../typography/Text";
14
+
15
+ export interface PreviewBoxProps {
16
+ preview: { filters: string[]; columns: string[]; sort: string[] };
17
+ testIdPrefix: string;
18
+ }
19
+
20
+ /** One "label — value" line of the summary. */
21
+ function SummaryRow({
22
+ label,
23
+ items,
24
+ testId,
25
+ }: {
26
+ label: string;
27
+ items: string[];
28
+ testId: string;
29
+ }): React.JSX.Element | null {
30
+ if (items.length === 0) return null;
31
+ return (
32
+ <Box sx={{ display: "flex", gap: 1.5 }} data-testid={testId}>
33
+ <Text variant="caption" as="span">
34
+ <Box component="span" sx={{ width: 116, flexShrink: 0, color: "text.secondary" }}>
35
+ {label}
36
+ </Box>
37
+ </Text>
38
+ <Text variant="caption" as="span">
39
+ <Box component="span" sx={{ minWidth: 0, flex: 1 }}>
40
+ {items.join(", ")}
41
+ </Box>
42
+ </Text>
43
+ </Box>
44
+ );
45
+ }
46
+
47
+ export function PreviewBox({ preview, testIdPrefix }: PreviewBoxProps): React.JSX.Element {
48
+ const copy = useDataViewsCopy();
49
+ const empty =
50
+ preview.filters.length === 0 && preview.columns.length === 0 && preview.sort.length === 0;
51
+ return (
52
+ <Box sx={{ p: 1.5, borderRadius: 1.5, bgcolor: "action.hover", border: (theme) => `1px solid ${theme.palette.divider}` }}>
53
+ <Text variant="caption" as="p">
54
+ <Box
55
+ component="span"
56
+ sx={{ display: "block", mb: 1, textTransform: "uppercase", letterSpacing: 0.6, fontWeight: 600, color: "text.disabled" }}
57
+ >
58
+ {copy.saveView.previewHeading}
59
+ </Box>
60
+ </Text>
61
+ {empty ? (
62
+ // Saving is still allowed — an operator may be naming the default on
63
+ // purpose — but it should not be a surprise afterwards.
64
+ <Text variant="caption" as="p">
65
+ <Box component="span" sx={{ color: "warning.main", lineHeight: 1.6 }} data-testid={`${testIdPrefix}-preview-empty`}>
66
+ {copy.saveView.previewUnchanged}
67
+ </Box>
68
+ </Text>
69
+ ) : (
70
+ <Stack spacing={0.75}>
71
+ <SummaryRow label={copy.saveView.previewFilters} items={preview.filters} testId={`${testIdPrefix}-preview-filters`} />
72
+ <SummaryRow label={copy.saveView.previewHiddenColumns} items={preview.columns} testId={`${testIdPrefix}-preview-columns`} />
73
+ <SummaryRow label={copy.saveView.sortRowLabel} items={preview.sort} testId={`${testIdPrefix}-preview-sort`} />
74
+ </Stack>
75
+ )}
76
+ </Box>
77
+ );
78
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useRef, useState } from "react";
4
4
 
5
+ import { useDataViewsCopy } from "./data-views-copy-context";
5
6
  import { useViewMutations, type ViewMutations } from "./data-views-view-mutations";
6
7
  import {
7
8
  coerceViewState,
@@ -99,6 +100,7 @@ export function useSavedViewsController(
99
100
  initialView: SavedViewSummary | undefined,
100
101
  initialState: DataViewState | undefined,
101
102
  ): SavedViewsController {
103
+ const copy = useDataViewsCopy();
102
104
  const { applied, currentRef, activeViewName, applyView, selectMain } =
103
105
  useAppliedView(router, columnIds, views, initialView, initialState);
104
106
  const [saveOpen, setSaveOpen] = useState(false);
@@ -122,7 +124,7 @@ export function useSavedViewsController(
122
124
  } else {
123
125
  // A rejected pin/share/default/delete must be visible (FUT-100) — the
124
126
  // menus close on click, so an inline Alert is the only surviving signal.
125
- setMutationError(error ?? "Não foi possível salvar a alteração da visão.");
127
+ setMutationError(error ?? copy.nav.saveFailed);
126
128
  }
127
129
  };
128
130
 
@@ -100,8 +100,8 @@ const ConfiguredView: React.FC<SectionOnboardingProps> = ({
100
100
  title,
101
101
  configuredTitle,
102
102
  configuredSummary,
103
- editLabel = 'Editar',
104
- collapseLabel = 'Ocultar',
103
+ editLabel,
104
+ collapseLabel,
105
105
  defaultExpanded = false,
106
106
  children,
107
107
  className,
@@ -167,7 +167,7 @@ const HeroView: React.FC<SectionOnboardingProps & { onStart: () => void }> = ({
167
167
  activeStepId,
168
168
  completedStepIds,
169
169
  primaryAction,
170
- startLabel = 'Começar',
170
+ startLabel,
171
171
  secondaryAction,
172
172
  helpLink,
173
173
  children,
@@ -64,7 +64,14 @@ export interface SectionOnboardingProps {
64
64
  * and `children` exist. Clicking it advances the hero to the in-progress view
65
65
  * (steps + form) without the parent managing state. @default 'Começar'
66
66
  */
67
- startLabel?: string;
67
+ /** The button that begins the section. REQUIRED since FUT-760. */
68
+ startLabel: string;
69
+
70
+ /** Label of the reveal toggle in the configured state. */
71
+ editLabel: string;
72
+
73
+ /** Label of that toggle while the form is revealed. */
74
+ collapseLabel: string;
68
75
 
69
76
  /** Optional secondary CTA of the hero — e.g. "Ver como funciona". */
70
77
  secondaryAction?: SectionOnboardingAction;
@@ -81,12 +88,6 @@ export interface SectionOnboardingProps {
81
88
  */
82
89
  configuredSummary?: ReactNode;
83
90
 
84
- /** Label of the reveal toggle in the configured state. @default 'Editar' */
85
- editLabel?: string;
86
-
87
- /** Label of the reveal toggle while expanded. @default 'Ocultar' */
88
- collapseLabel?: string;
89
-
90
91
  /** Start the configured state with the form already revealed. @default false */
91
92
  defaultExpanded?: boolean;
92
93
 
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  import type { ConfirmHandler, UseConfirmActionResult } from './ConfirmAction.types';
4
4
 
5
5
  /** Used when the guarded action rejects and the caller supplied no wording. */
6
- export const DEFAULT_ERROR_TEXT = 'Não foi possível concluir a ação. Tente novamente.';
6
+
7
7
 
8
8
  /**
9
9
  * What the popup says after a failed attempt.
@@ -29,7 +29,7 @@ function failureMessage(cause: unknown, errorText: string): string {
29
29
  */
30
30
  export function useConfirmAction(
31
31
  onConfirm: ConfirmHandler,
32
- errorText: string = DEFAULT_ERROR_TEXT,
32
+ errorText: string,
33
33
  ): UseConfirmActionResult {
34
34
  const [open, setOpen] = useState(false);
35
35
  const [pending, setPending] = useState(false);
@@ -1,3 +1,4 @@
1
+ import type { ConfirmActionCopy } from "../../../copy";
1
2
  import { Alert, Stack, TextField, Typography } from '@mui/material';
2
3
  import React, { type ReactNode } from 'react';
3
4
 
@@ -10,7 +11,6 @@ import type {
10
11
  UseConfirmActionResult,
11
12
  } from './ConfirmAction.types';
12
13
 
13
- const DEFAULT_CANCEL_TEXT = 'Cancelar';
14
14
  const DEFAULT_TEST_ID = 'confirm-action';
15
15
 
16
16
  /**
@@ -22,8 +22,12 @@ const DEFAULT_TEST_ID = 'confirm-action';
22
22
  * mention it. A body with neither a name nor a description would leave the
23
23
  * operator with only the title to decide on, so a neutral fallback covers that.
24
24
  */
25
- function confirmBody(description: ReactNode, entityName?: string): ReactNode {
26
- if (!entityName) return description ?? "Esta ação não pode ser desfeita.";
25
+ function confirmBody(
26
+ description: ReactNode,
27
+ copy: ConfirmActionCopy,
28
+ entityName?: string,
29
+ ): ReactNode {
30
+ if (!entityName) return description ?? copy.irreversible;
27
31
  return (
28
32
  <>
29
33
  <Typography component="span" sx={{ fontWeight: 600, display: "block" }}>
@@ -86,6 +90,8 @@ function TypeToConfirmField({
86
90
  }
87
91
 
88
92
  export interface ConfirmActionDialogProps extends ConfirmOptions {
93
+ /** The fallback body when no entity is named. REQUIRED — no default copy. */
94
+ copy: ConfirmActionCopy;
89
95
  state: UseConfirmActionResult;
90
96
  }
91
97
 
@@ -101,12 +107,13 @@ export function ConfirmActionDialog({
101
107
  description,
102
108
  entityName,
103
109
  confirmText,
104
- cancelText = DEFAULT_CANCEL_TEXT,
110
+ cancelText,
105
111
  typeToConfirm,
106
112
  typeToConfirmLabel,
107
113
  tone = 'destructive',
108
114
  initialFocus,
109
115
  dataTestId = DEFAULT_TEST_ID,
116
+ copy,
110
117
  }: ConfirmActionDialogProps): React.ReactElement {
111
118
  const [typed, setTyped] = React.useState('');
112
119
 
@@ -123,7 +130,7 @@ export function ConfirmActionDialog({
123
130
  open={state.open}
124
131
  variant={tone === 'destructive' ? 'destructive' : 'default'}
125
132
  title={title}
126
- description={confirmBody(description, entityName)}
133
+ description={confirmBody(description, copy, entityName)}
127
134
  confirmText={confirmText}
128
135
  cancelText={cancelText}
129
136
  loading={state.pending}
@@ -1,3 +1,4 @@
1
+ import type { ConfirmActionCopy } from "../../../copy";
1
2
  import type { ComponentType, ReactNode } from 'react';
2
3
 
3
4
  /**
@@ -43,7 +44,6 @@ export interface ConfirmOptions {
43
44
  /** Defaults to `cancel` for the destructive tone, `confirm` otherwise. */
44
45
  initialFocus?: ConfirmInitialFocus;
45
46
  /** Shown inside the dialog when the action rejects; the dialog stays open. */
46
- errorText?: string;
47
47
  /** Root test id; the dialog's parts derive theirs from it. */
48
48
  dataTestId?: string;
49
49
  }
@@ -82,6 +82,14 @@ export interface UseConfirmActionResult {
82
82
  * the trigger — a button, a menu item, an icon.
83
83
  */
84
84
  export interface ConfirmActionProps extends ConfirmOptions {
85
+ /**
86
+ * What the operator is told when the action fails and the cause carries no
87
+ * message. REQUIRED since FUT-760 — it used to default to this package's own
88
+ * pt-BR, so a host that passed nothing shipped one product's Portuguese.
89
+ */
90
+ errorText: string;
91
+ /** The fallback body when no entity is named. REQUIRED — see `errorText`. */
92
+ copy: ConfirmActionCopy;
85
93
  onConfirm: ConfirmHandler;
86
94
  children: (request: () => void, state: { pending: boolean }) => ReactNode;
87
95
  }
@@ -1,3 +1,4 @@
1
+ import type { ConfirmActionCopy } from '../../../copy';
1
2
  import { Button } from '../../form/Button';
2
3
  import type { ButtonProps } from '../../form/Button/Button.types';
3
4
 
@@ -12,7 +13,14 @@ import type { ConfirmableComponent } from './ConfirmAction.types';
12
13
  * The baked-in defaults are only the wording and tone every destructive
13
14
  * confirmation shares, and each instance can still override them.
14
15
  */
15
- export const ConfirmButton: ConfirmableComponent<ButtonProps> = withConfirmation<ButtonProps>(
16
- Button,
17
- { cancelText: 'Cancelar', tone: 'destructive' },
18
- );
16
+ export function createConfirmButton(
17
+ copy: ConfirmActionCopy,
18
+ errorText: string,
19
+ ): ConfirmableComponent<ButtonProps> {
20
+ return withConfirmation<ButtonProps>(Button, {
21
+ cancelText: copy.cancel,
22
+ tone: 'destructive',
23
+ errorText,
24
+ copy,
25
+ });
26
+ }
@@ -1,6 +1,6 @@
1
1
  export { ConfirmAction } from './ConfirmAction';
2
- export { ConfirmButton } from './ConfirmButton';
3
- export { DEFAULT_ERROR_TEXT, useConfirmAction } from './ConfirmAction.hooks';
2
+ export { createConfirmButton } from './ConfirmButton';
3
+ export { useConfirmAction } from './ConfirmAction.hooks';
4
4
  export { ConfirmActionDialog } from './ConfirmAction.parts';
5
5
  export { withConfirmation } from './with-confirmation';
6
6
  export type {
@@ -1,3 +1,4 @@
1
+ import type { ConfirmActionCopy } from '../../../copy';
1
2
  import React from 'react';
2
3
  import type { ComponentType } from 'react';
3
4
 
@@ -81,12 +82,12 @@ function useGuardedHandler(onClick: ConfirmableProps['onClick']): ConfirmHandler
81
82
  */
82
83
  export function withConfirmation<P extends ConfirmableProps>(
83
84
  Wrapped: ComponentType<P>,
84
- defaults?: Partial<ConfirmOptions>,
85
+ defaults: Partial<ConfirmOptions> & { errorText: string; copy: ConfirmActionCopy },
85
86
  ): ConfirmableComponent<P> {
86
87
  function WithConfirmation(props: P & WithConfirmationProps): React.ReactElement {
87
88
  const { confirm, onClick } = props;
88
89
  const options = resolveOptions(defaults, confirm);
89
- const state = useConfirmAction(useGuardedHandler(onClick), options?.errorText);
90
+ const state = useConfirmAction(useGuardedHandler(onClick), defaults.errorText);
90
91
 
91
92
  // `confirm` is ours and must not reach the DOM — an unknown attribute on a
92
93
  // <button> is a React warning at every render. The cast is the standard HOC
@@ -99,7 +100,7 @@ export function withConfirmation<P extends ConfirmableProps>(
99
100
  return (
100
101
  <>
101
102
  <Wrapped {...forwarded} onClick={state.request} />
102
- <ConfirmActionDialog state={state} {...options} />
103
+ <ConfirmActionDialog state={state} {...options} copy={defaults.copy} />
103
104
  </>
104
105
  );
105
106
  }
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import type { CategorySelectCopy } from "../../../copy";
3
4
  import { Box } from '@mui/material';
4
5
 
5
6
  import { removeChip } from './category-tree';
@@ -12,6 +13,8 @@ import type { CategoryGroup, CategorySelectionChip } from './CategorySelect.type
12
13
  import type { CategorySelectState } from './useCategorySelect';
13
14
 
14
15
  export interface CategoryPanelProps {
16
+ /** The words this panel renders. REQUIRED — no default copy. */
17
+ copy: CategorySelectCopy;
15
18
  state: CategorySelectState;
16
19
  chips: CategorySelectionChip[];
17
20
  rowIds: string[];
@@ -55,6 +58,7 @@ export function CategoryPanel({
55
58
  searchInputRef,
56
59
  listRef,
57
60
  dataTestId,
61
+ copy,
58
62
  }: CategoryPanelProps): React.JSX.Element {
59
63
  return (
60
64
  <Box
@@ -65,8 +69,9 @@ export function CategoryPanel({
65
69
  sx={(theme) => ({ ...panelSx(theme), ...(sheet ? sheetSx(theme) : {}) })}
66
70
  >
67
71
  <CategoryPanelHead
72
+ copy={copy}
68
73
  query={state.query}
69
- placeholder={single ? 'Buscar categoria…' : 'Buscar categoria ou subcategoria'}
74
+ placeholder={single ? copy.search.placeholderSingle : copy.search.placeholderMulti}
70
75
  sheet={sheet}
71
76
  quickActions={
72
77
  single ? undefined : <CategoryQuickActions state={state} dataTestId={dataTestId} />
@@ -76,9 +81,9 @@ export function CategoryPanel({
76
81
  dataTestId={dataTestId}
77
82
  />
78
83
  {!single && !loading && (
79
- <PinnedTray state={state} chips={chips} dataTestId={dataTestId} />
84
+ <PinnedTray state={state} chips={chips} dataTestId={dataTestId} copy={copy} />
80
85
  )}
81
- <CategoryPanelList
86
+ <CategoryPanelList copy={copy}
82
87
  groups={state.visibleGroups}
83
88
  query={state.query}
84
89
  draft={state.draft}
@@ -103,6 +108,7 @@ export function CategoryPanel({
103
108
  changed={changed}
104
109
  single={single}
105
110
  sheet={sheet}
111
+ copy={copy}
106
112
  onApply={onApply}
107
113
  onCancel={onCancel}
108
114
  dataTestId={dataTestId}
@@ -116,9 +122,11 @@ function PinnedTray({
116
122
  state,
117
123
  chips,
118
124
  dataTestId,
119
- }: Pick<CategoryPanelProps, 'state' | 'chips' | 'dataTestId'>): React.JSX.Element {
125
+ copy,
126
+ }: Pick<CategoryPanelProps, 'state' | 'chips' | 'dataTestId' | 'copy'>): React.JSX.Element {
120
127
  return (
121
128
  <CategoryPinnedTray
129
+ copy={copy}
122
130
  chips={chips}
123
131
  onRemove={(chipId) => state.setDraft(removeChip(state.allGroups, chipId, state.draft))}
124
132
  dataTestId={dataTestId}
@@ -135,18 +143,22 @@ function PanelFooter({
135
143
  onApply,
136
144
  onCancel,
137
145
  dataTestId,
146
+ copy,
138
147
  }: Pick<
139
148
  CategoryPanelProps,
140
- 'state' | 'changed' | 'single' | 'sheet' | 'onApply' | 'onCancel' | 'dataTestId'
149
+ 'state' | 'changed' | 'single' | 'sheet' | 'onApply' | 'onCancel' | 'dataTestId' | 'copy'
141
150
  >): React.JSX.Element {
142
151
  if (single) {
143
- return <CategorySingleFoot sheet={sheet} onCancel={onCancel} dataTestId={dataTestId} />;
152
+ return (
153
+ <CategorySingleFoot sheet={sheet} onCancel={onCancel} dataTestId={dataTestId} copy={copy} />
154
+ );
144
155
  }
145
156
  return (
146
157
  <CategoryMultiFoot
147
158
  count={state.draft.size}
148
159
  changed={changed}
149
160
  sheet={sheet}
161
+ copy={copy}
150
162
  onClear={() => state.setDraft(new Set())}
151
163
  onApply={onApply}
152
164
  dataTestId={dataTestId}
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { Box, Button } from '@mui/material';
4
4
 
5
+ import type { CategorySelectCopy } from '../../../copy';
5
6
  import { footerSx, METRICS } from './CategorySelect.styles';
6
7
 
7
8
  const buttonSx = (sheet: boolean) =>
@@ -23,6 +24,7 @@ interface MultiFootProps {
23
24
  onClear: () => void;
24
25
  onApply: () => void;
25
26
  dataTestId: string;
27
+ copy: CategorySelectCopy;
26
28
  }
27
29
 
28
30
  /**
@@ -37,11 +39,12 @@ export function CategoryMultiFoot({
37
39
  onClear,
38
40
  onApply,
39
41
  dataTestId,
42
+ copy,
40
43
  }: MultiFootProps): React.JSX.Element {
41
44
  return (
42
45
  <Box sx={footerSx} data-testid={`${dataTestId}-footer`}>
43
46
  <Box component="span" sx={{ fontSize: 12, color: 'text.secondary' }}>
44
- <strong>{count}</strong> selecionada{count === 1 ? '' : 's'}
47
+ {copy.footer.selectedCount(count)}
45
48
  </Box>
46
49
  <Box sx={{ display: 'flex', gap: '6px' }}>
47
50
  <Button
@@ -51,7 +54,7 @@ export function CategoryMultiFoot({
51
54
  onClick={onClear}
52
55
  data-testid={`${dataTestId}-clear`}
53
56
  >
54
- Limpar
57
+ {copy.footer.clear}
55
58
  </Button>
56
59
  <Button
57
60
  variant="contained"
@@ -59,7 +62,7 @@ export function CategoryMultiFoot({
59
62
  onClick={onApply}
60
63
  data-testid={`${dataTestId}-apply`}
61
64
  >
62
- {changed ? 'Aplicar' : 'Fechar'}
65
+ {changed ? copy.footer.apply : copy.footer.close}
63
66
  </Button>
64
67
  </Box>
65
68
  </Box>
@@ -71,15 +74,17 @@ export function CategorySingleFoot({
71
74
  sheet,
72
75
  onCancel,
73
76
  dataTestId,
77
+ copy,
74
78
  }: {
75
79
  sheet: boolean;
76
80
  onCancel: () => void;
77
81
  dataTestId: string;
82
+ copy: CategorySelectCopy;
78
83
  }): React.JSX.Element {
79
84
  return (
80
85
  <Box sx={footerSx} data-testid={`${dataTestId}-footer`}>
81
86
  <Box component="span" sx={{ fontSize: 12, color: 'text.secondary' }}>
82
- Escolha uma categoria
87
+ {copy.footer.singleHint}
83
88
  </Box>
84
89
  <Button
85
90
  variant="outlined"
@@ -87,7 +92,7 @@ export function CategorySingleFoot({
87
92
  onClick={onCancel}
88
93
  data-testid={`${dataTestId}-cancel`}
89
94
  >
90
- Cancelar
95
+ {copy.footer.cancel}
91
96
  </Button>
92
97
  </Box>
93
98
  );
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import type { CategorySelectCopy } from '../../../copy';
3
4
  import { Box, InputBase } from '@mui/material';
4
5
 
5
6
  import { SearchGlyph } from './CategoryIcons';
@@ -21,6 +22,7 @@ interface PanelHeadProps {
21
22
  onQueryChange: (next: string) => void;
22
23
  searchInputRef: React.RefObject<HTMLInputElement | null>;
23
24
  dataTestId: string;
25
+ copy: CategorySelectCopy;
24
26
  }
25
27
 
26
28
  const searchFieldSx = {
@@ -72,6 +74,7 @@ export function CategoryPanelHead({
72
74
  onQueryChange,
73
75
  searchInputRef,
74
76
  dataTestId,
77
+ copy,
75
78
  }: PanelHeadProps): React.JSX.Element {
76
79
  return (
77
80
  <Box sx={(theme) => panelHeadSx(theme, sheet)}>
@@ -99,7 +102,7 @@ export function CategoryPanelHead({
99
102
  <Box
100
103
  component="button"
101
104
  type="button"
102
- aria-label="Limpar busca"
105
+ aria-label={copy.search.clear}
103
106
  data-testid={`${dataTestId}-search-clear`}
104
107
  sx={clearButtonSx}
105
108
  onClick={() => onQueryChange('')}
@@ -129,6 +132,7 @@ interface PinnedTrayProps {
129
132
  chips: CategorySelectionChip[];
130
133
  onRemove: (chipId: string) => void;
131
134
  dataTestId: string;
135
+ copy: CategorySelectCopy;
132
136
  }
133
137
 
134
138
  /**
@@ -142,18 +146,19 @@ export function CategoryPinnedTray({
142
146
  chips,
143
147
  onRemove,
144
148
  dataTestId,
149
+ copy,
145
150
  }: PinnedTrayProps): React.JSX.Element | null {
146
151
  if (chips.length === 0) return null;
147
152
  return (
148
153
  <Box sx={pinnedSx} data-testid={`${dataTestId}-pinned`}>
149
- <Box sx={pinnedLabelSx}>Selecionadas · {chips.length}</Box>
154
+ <Box sx={pinnedLabelSx}>{copy.search.pinnedLabel(chips.length)}</Box>
150
155
  <Box sx={chipWrapSx}>
151
156
  {chips.map((chip) => (
152
157
  <Box component="span" key={chip.id} sx={chipSx}>
153
158
  {chip.label}
154
159
  <button
155
160
  type="button"
156
- aria-label={`Remover ${chip.label}`}
161
+ aria-label={copy.search.removeChip(chip.label)}
157
162
  data-testid={`${dataTestId}-unpin-${chip.id}`}
158
163
  onClick={() => onRemove(chip.id)}
159
164
  >
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import type { CategorySelectCopy } from "../../../copy";
3
4
  import { Box, Button, Skeleton } from '@mui/material';
4
5
 
5
6
  import { categoryCheckState } from './category-tree';
@@ -18,6 +19,8 @@ const SENTENCE_CASE = { textTransform: 'none' } as const;
18
19
  const SKELETON_ROWS = 6;
19
20
 
20
21
  interface PanelListProps {
22
+ /** The words this panel renders. REQUIRED — no default copy. */
23
+ copy: CategorySelectCopy;
21
24
  groups: CategoryGroup[];
22
25
  query: string;
23
26
  draft: ReadonlySet<string>;
@@ -52,17 +55,19 @@ function LoadingRows(): React.JSX.Element {
52
55
  function EmptyCatalogue({
53
56
  onCreateCategory,
54
57
  dataTestId,
58
+ copy,
55
59
  }: {
56
60
  onCreateCategory?: (name?: string) => void;
57
61
  dataTestId: string;
62
+ copy: CategorySelectCopy;
58
63
  }): React.JSX.Element {
59
64
  return (
60
65
  <Box sx={emptySx} data-testid={`${dataTestId}-empty-catalogue`}>
61
- <strong>Nenhuma categoria ainda</strong>
62
- <p>Categorias organizam o cardápio e os filtros da loja.</p>
66
+ <strong>{copy.emptyTitle}</strong>
67
+ <p>{copy.purpose}</p>
63
68
  {onCreateCategory && (
64
69
  <Button variant="contained" size="small" sx={SENTENCE_CASE} onClick={() => onCreateCategory()}>
65
- Criar categoria
70
+ {copy.createCategory}
66
71
  </Button>
67
72
  )}
68
73
  </Box>
@@ -75,19 +80,21 @@ function NoResults({
75
80
  onClearQuery,
76
81
  onCreateCategory,
77
82
  dataTestId,
83
+ copy,
78
84
  }: {
79
85
  query: string;
80
86
  onClearQuery: () => void;
81
87
  onCreateCategory?: (name?: string) => void;
82
88
  dataTestId: string;
89
+ copy: CategorySelectCopy;
83
90
  }): React.JSX.Element {
84
91
  return (
85
92
  <Box sx={emptySx} data-testid={`${dataTestId}-no-results`}>
86
- <strong>Nada encontrado para “{query}”</strong>
87
- <p>Tente outro termo ou verifique a grafia.</p>
93
+ <strong>{copy.noResults.title(query)}</strong>
94
+ <p>{copy.noResults.hint}</p>
88
95
  <Box sx={{ display: 'flex', gap: '6px', justifyContent: 'center' }}>
89
96
  <Button variant="outlined" size="small" sx={SENTENCE_CASE} onClick={onClearQuery}>
90
- Limpar busca
97
+ {copy.noResults.clearSearch}
91
98
  </Button>
92
99
  {onCreateCategory && (
93
100
  <Button
@@ -97,7 +104,7 @@ function NoResults({
97
104
  data-testid={`${dataTestId}-create`}
98
105
  onClick={() => onCreateCategory(query.trim())}
99
106
  >
100
- Criar “{query.trim()}”
107
+ {copy.noResults.create(query.trim())}
101
108
  </Button>
102
109
  )}
103
110
  </Box>
@@ -161,13 +168,13 @@ function CategoryGroupRows({
161
168
 
162
169
  /** The scrolling body of the panel, including its three non-list states. */
163
170
  export function CategoryPanelList(props: PanelListProps): React.JSX.Element {
164
- const { groups, loading, query, listRef, sheet, dataTestId } = props;
171
+ const { groups, loading, query, listRef, sheet, dataTestId, copy } = props;
165
172
 
166
173
  const body = (): React.JSX.Element => {
167
174
  if (loading) return <LoadingRows />;
168
175
  if (groups.length === 0 && query.trim().length > 0) {
169
176
  return (
170
- <NoResults
177
+ <NoResults copy={copy}
171
178
  query={query}
172
179
  onClearQuery={props.onClearQuery}
173
180
  onCreateCategory={props.onCreateCategory}
@@ -177,7 +184,7 @@ export function CategoryPanelList(props: PanelListProps): React.JSX.Element {
177
184
  }
178
185
  if (groups.length === 0) {
179
186
  return (
180
- <EmptyCatalogue
187
+ <EmptyCatalogue copy={copy}
181
188
  onCreateCategory={props.onCreateCategory}
182
189
  dataTestId={dataTestId}
183
190
  />
@@ -102,7 +102,7 @@ function usePanelFocus(
102
102
  * is the only way a 290px-tall list stays usable one-handed.
103
103
  */
104
104
  export function CategorySelect(props: CategorySelectProps): React.JSX.Element {
105
- const { options, label, error, onCreateCategory } = props;
105
+ const { options, label, error, onCreateCategory, copy } = props;
106
106
  const {
107
107
  single,
108
108
  placeholder,
@@ -135,7 +135,7 @@ export function CategorySelect(props: CategorySelectProps): React.JSX.Element {
135
135
  return (
136
136
  <FormControl fullWidth={fullWidth} error={Boolean(error)}>
137
137
  {label && <FormLabel error={Boolean(error)}>{label}</FormLabel>}
138
- <CategoryTrigger
138
+ <CategoryTrigger copy={copy}
139
139
  placeholder={placeholder}
140
140
  selectionLabel={
141
141
  props.mode === 'single' ? singleLabel(state.allGroups, props.value) : undefined
@@ -155,7 +155,7 @@ export function CategorySelect(props: CategorySelectProps): React.JSX.Element {
155
155
  anchorEl={state.triggerRef.current}
156
156
  onClose={actions.cancel}
157
157
  >
158
- <CategoryPanel
158
+ <CategoryPanel copy={copy}
159
159
  state={state}
160
160
  chips={chips}
161
161
  rowIds={rowIds}