@12-apps/discounts 1.19.0 → 1.19.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/discounts",
3
- "version": "1.19.0",
3
+ "version": "1.19.2",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/e2e/**"
@@ -44,7 +44,7 @@
44
44
  "@12-apps/app-shell": "^5.14.1",
45
45
  "@12-apps/forms-core": "^1.23.0",
46
46
  "@12-apps/shared-helpers": "^2.2.0",
47
- "@12-apps/ui": "^6.96.0",
47
+ "@12-apps/ui": "^6.96.2",
48
48
  "@mui/icons-material": "^6.5.0",
49
49
  "zod": "^4.3.5"
50
50
  },
@@ -142,6 +142,20 @@ function checkCombo(
142
142
  values: DiscountFormValues,
143
143
  slots: readonly ComboRequirement[],
144
144
  copy: DiscountsWebCopy,
145
+ ): Record<string, string> {
146
+ const errors = checkComboKind(values, slots, copy);
147
+ // Recorded apart from `errors.kind`, which holds only ONE message: a bad
148
+ // quantity wins it, and the product line under the builder must still show.
149
+ if (values.kind === "FREE_UNITS" && slots.every((slot) => slot.menuItemIds.length === 0)) {
150
+ errors.freeUnitsTargets = copy.form.freeUnitsTargetRequired;
151
+ }
152
+ return errors;
153
+ }
154
+
155
+ function checkComboKind(
156
+ values: DiscountFormValues,
157
+ slots: readonly ComboRequirement[],
158
+ copy: DiscountsWebCopy,
145
159
  ): Record<string, string> {
146
160
  if (!isComboKind(values.kind)) return {};
147
161
  // "Leve 3, pague 2" is one group of products, so it is refused in those words
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import type { JSX } from "react";
3
+ import { useEffect, type JSX } from "react";
4
4
 
5
5
  import { useFormContext } from "@12-apps/ui/form/total-form";
6
6
 
@@ -87,6 +87,23 @@ export function DiscountTargetPicker({
87
87
  const { values, errors, setFieldValue } = useFormContext();
88
88
  const kind = values.kind ?? "PERCENTAGE";
89
89
  const scope = values.scope ?? "ORDER";
90
+ const staleTargets = errors.freeUnitsTargets !== undefined;
91
+
92
+ const picked = selection.comboRequirements.some((slot) => slot.menuItemIds.length > 0);
93
+ const productRefusal = errors.kind === copy.form.freeUnitsTargetRequired;
94
+
95
+ // `setFieldValue` clears only the key it sets, and neither the kind toggle
96
+ // nor a pick sets `freeUnitsTargets`: the product refusal would outlive a
97
+ // switch away, or the product the owner then picks, and keep the error toast
98
+ // up. Setting its own key drops it; the value is never read or sent. A pick
99
+ // also drops the same refusal from `errors.kind` (re-setting the kind it
100
+ // already has), but never a quantity refusal held there (FUT-4470).
101
+ useEffect(() => {
102
+ const answered = kind !== "FREE_UNITS" || picked;
103
+ if (!answered) return;
104
+ if (staleTargets) setFieldValue("freeUnitsTargets", "");
105
+ if (kind === "FREE_UNITS" && productRefusal) setFieldValue("kind", kind);
106
+ }, [kind, picked, staleTargets, productRefusal, setFieldValue]);
90
107
 
91
108
  if (kind === "FREE_UNITS") {
92
109
  return (
@@ -96,6 +113,7 @@ export function DiscountTargetPicker({
96
113
  copy={copy}
97
114
  freeUnits={values.freeUnits ?? ""}
98
115
  error={errors.freeUnits}
116
+ targetRefused={staleTargets}
99
117
  onChange={selection.onComboRequirementsChange}
100
118
  onFreeUnitsChange={(next) => setFieldValue("freeUnits", next)}
101
119
  />
@@ -116,6 +116,7 @@ export function FreeUnitsBuilder({
116
116
  copy,
117
117
  freeUnits,
118
118
  error,
119
+ targetRefused,
119
120
  onChange,
120
121
  onFreeUnitsChange,
121
122
  }: {
@@ -130,6 +131,8 @@ export function FreeUnitsBuilder({
130
131
  * this itself, but it would also sit outside the sentence it belongs to.
131
132
  */
132
133
  error: string | undefined;
134
+ /** Criar/Salvar refused the product list; the message waits for it. */
135
+ targetRefused: boolean;
133
136
  onChange: (next: ComboRequirement[]) => void;
134
137
  onFreeUnitsChange: (next: string) => void;
135
138
  }): JSX.Element | null {
@@ -138,8 +141,6 @@ export function FreeUnitsBuilder({
138
141
 
139
142
  const slot = onlySlot(slots);
140
143
  const summary = summaryOf(slot.quantity, Number(freeUnits), copy);
141
- const empty = slot.menuItemIds.length === 0;
142
-
143
144
  return (
144
145
  <FormControl fullWidth>
145
146
  <FormLabel>{copy.combo.freeUnitsTitle}</FormLabel>
@@ -172,7 +173,7 @@ export function FreeUnitsBuilder({
172
173
  {error ?? copy.form.wholeNumberOnly}
173
174
  </FormMessage>
174
175
  )}
175
- {empty ? (
176
+ {slot.menuItemIds.length === 0 && targetRefused ? (
176
177
  <FormMessage error dataTestId="free-units-message">
177
178
  {copy.form.freeUnitsTargetRequired}
178
179
  </FormMessage>