@hook-sdk/template 0.13.0 → 0.14.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.
- package/dist/index.cjs +117 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +117 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -200,9 +200,15 @@ interface UseLoginFormResult {
|
|
|
200
200
|
email: string;
|
|
201
201
|
setEmail: (v: string) => void;
|
|
202
202
|
emailError: string | null;
|
|
203
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
204
|
+
markEmailTouched: () => void;
|
|
203
205
|
password: string;
|
|
204
206
|
setPassword: (v: string) => void;
|
|
205
207
|
passwordError: string | null;
|
|
208
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
209
|
+
markPasswordTouched: () => void;
|
|
210
|
+
/** True after the user has attempted submit at least once. */
|
|
211
|
+
formSubmitAttempted: boolean;
|
|
206
212
|
/**
|
|
207
213
|
* Submete o form. Retorna true se o login deu OK (cookies setados), false
|
|
208
214
|
* se validação falhou, credenciais inválidas, rate-limit ou erro de rede.
|
|
@@ -225,12 +231,20 @@ interface UseSignupFormResult {
|
|
|
225
231
|
name: string;
|
|
226
232
|
setName: (v: string) => void;
|
|
227
233
|
nameError: string | null;
|
|
234
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
235
|
+
markNameTouched: () => void;
|
|
228
236
|
email: string;
|
|
229
237
|
setEmail: (v: string) => void;
|
|
230
238
|
emailError: string | null;
|
|
239
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
240
|
+
markEmailTouched: () => void;
|
|
231
241
|
password: string;
|
|
232
242
|
setPassword: (v: string) => void;
|
|
233
243
|
passwordError: string | null;
|
|
244
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
245
|
+
markPasswordTouched: () => void;
|
|
246
|
+
/** True after the user has attempted submit at least once. */
|
|
247
|
+
formSubmitAttempted: boolean;
|
|
234
248
|
/**
|
|
235
249
|
* Submete o form. Retorna true se o signup deu OK (backend respondeu 2xx),
|
|
236
250
|
* false se validação falhou, houve erro de rede/servidor, ou email já em uso.
|
|
@@ -254,6 +268,10 @@ interface UseForgotFormResult {
|
|
|
254
268
|
email: string;
|
|
255
269
|
setEmail: (v: string) => void;
|
|
256
270
|
emailError: string | null;
|
|
271
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
272
|
+
markEmailTouched: () => void;
|
|
273
|
+
/** True after the user has attempted submit at least once. */
|
|
274
|
+
formSubmitAttempted: boolean;
|
|
257
275
|
/**
|
|
258
276
|
* Submete o form. Retorna true se o backend aceitou a requisição (email
|
|
259
277
|
* de reset foi enfileirado — sem leak de "existe esse email" por design),
|
|
@@ -274,9 +292,15 @@ interface UseResetFormResult {
|
|
|
274
292
|
password: string;
|
|
275
293
|
setPassword: (v: string) => void;
|
|
276
294
|
passwordError: string | null;
|
|
295
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
296
|
+
markPasswordTouched: () => void;
|
|
277
297
|
confirm: string;
|
|
278
298
|
setConfirm: (v: string) => void;
|
|
279
299
|
confirmError: string | null;
|
|
300
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
301
|
+
markConfirmTouched: () => void;
|
|
302
|
+
/** True after the user has attempted submit at least once. */
|
|
303
|
+
formSubmitAttempted: boolean;
|
|
280
304
|
submit: () => Promise<void>;
|
|
281
305
|
submitting: boolean;
|
|
282
306
|
canSubmit: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -200,9 +200,15 @@ interface UseLoginFormResult {
|
|
|
200
200
|
email: string;
|
|
201
201
|
setEmail: (v: string) => void;
|
|
202
202
|
emailError: string | null;
|
|
203
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
204
|
+
markEmailTouched: () => void;
|
|
203
205
|
password: string;
|
|
204
206
|
setPassword: (v: string) => void;
|
|
205
207
|
passwordError: string | null;
|
|
208
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
209
|
+
markPasswordTouched: () => void;
|
|
210
|
+
/** True after the user has attempted submit at least once. */
|
|
211
|
+
formSubmitAttempted: boolean;
|
|
206
212
|
/**
|
|
207
213
|
* Submete o form. Retorna true se o login deu OK (cookies setados), false
|
|
208
214
|
* se validação falhou, credenciais inválidas, rate-limit ou erro de rede.
|
|
@@ -225,12 +231,20 @@ interface UseSignupFormResult {
|
|
|
225
231
|
name: string;
|
|
226
232
|
setName: (v: string) => void;
|
|
227
233
|
nameError: string | null;
|
|
234
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
235
|
+
markNameTouched: () => void;
|
|
228
236
|
email: string;
|
|
229
237
|
setEmail: (v: string) => void;
|
|
230
238
|
emailError: string | null;
|
|
239
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
240
|
+
markEmailTouched: () => void;
|
|
231
241
|
password: string;
|
|
232
242
|
setPassword: (v: string) => void;
|
|
233
243
|
passwordError: string | null;
|
|
244
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
245
|
+
markPasswordTouched: () => void;
|
|
246
|
+
/** True after the user has attempted submit at least once. */
|
|
247
|
+
formSubmitAttempted: boolean;
|
|
234
248
|
/**
|
|
235
249
|
* Submete o form. Retorna true se o signup deu OK (backend respondeu 2xx),
|
|
236
250
|
* false se validação falhou, houve erro de rede/servidor, ou email já em uso.
|
|
@@ -254,6 +268,10 @@ interface UseForgotFormResult {
|
|
|
254
268
|
email: string;
|
|
255
269
|
setEmail: (v: string) => void;
|
|
256
270
|
emailError: string | null;
|
|
271
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
272
|
+
markEmailTouched: () => void;
|
|
273
|
+
/** True after the user has attempted submit at least once. */
|
|
274
|
+
formSubmitAttempted: boolean;
|
|
257
275
|
/**
|
|
258
276
|
* Submete o form. Retorna true se o backend aceitou a requisição (email
|
|
259
277
|
* de reset foi enfileirado — sem leak de "existe esse email" por design),
|
|
@@ -274,9 +292,15 @@ interface UseResetFormResult {
|
|
|
274
292
|
password: string;
|
|
275
293
|
setPassword: (v: string) => void;
|
|
276
294
|
passwordError: string | null;
|
|
295
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
296
|
+
markPasswordTouched: () => void;
|
|
277
297
|
confirm: string;
|
|
278
298
|
setConfirm: (v: string) => void;
|
|
279
299
|
confirmError: string | null;
|
|
300
|
+
/** Wave 5 #42: call on input blur so the error becomes visible. */
|
|
301
|
+
markConfirmTouched: () => void;
|
|
302
|
+
/** True after the user has attempted submit at least once. */
|
|
303
|
+
formSubmitAttempted: boolean;
|
|
280
304
|
submit: () => Promise<void>;
|
|
281
305
|
submitting: boolean;
|
|
282
306
|
canSubmit: boolean;
|
package/dist/index.js
CHANGED
|
@@ -1849,14 +1849,18 @@ import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef3
|
|
|
1849
1849
|
import { useHook as useHook4 } from "@hook-sdk/sdk";
|
|
1850
1850
|
import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1851
1851
|
var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
|
|
1852
|
+
var MAX_CYCLES = 3;
|
|
1853
|
+
var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
|
|
1852
1854
|
function PaymentReturnHandler({ children }) {
|
|
1853
1855
|
const { subscription } = useHook4();
|
|
1854
1856
|
const subRef = useRef3(subscription);
|
|
1855
1857
|
subRef.current = subscription;
|
|
1856
1858
|
const runIdRef = useRef3(0);
|
|
1859
|
+
const cyclesRef = useRef3(0);
|
|
1857
1860
|
const [state, setState] = useState5("idle");
|
|
1858
1861
|
const runPoll = useCallback3(() => {
|
|
1859
1862
|
const runId = ++runIdRef.current;
|
|
1863
|
+
cyclesRef.current += 1;
|
|
1860
1864
|
setState("confirming");
|
|
1861
1865
|
let attempts = 0;
|
|
1862
1866
|
const tick = async () => {
|
|
@@ -1872,12 +1876,17 @@ function PaymentReturnHandler({ children }) {
|
|
|
1872
1876
|
const cleanUrl = new URL(window.location.href);
|
|
1873
1877
|
cleanUrl.searchParams.delete("paymentReturn");
|
|
1874
1878
|
window.history.replaceState({}, "", cleanUrl.toString());
|
|
1879
|
+
cyclesRef.current = 0;
|
|
1875
1880
|
setState("idle");
|
|
1876
1881
|
return;
|
|
1877
1882
|
}
|
|
1878
1883
|
const delay = BACKOFF_MS[attempts - 1];
|
|
1879
1884
|
if (delay === void 0) {
|
|
1880
|
-
|
|
1885
|
+
if (cyclesRef.current >= MAX_CYCLES) {
|
|
1886
|
+
setState("timeout");
|
|
1887
|
+
} else {
|
|
1888
|
+
setState("waiting");
|
|
1889
|
+
}
|
|
1881
1890
|
return;
|
|
1882
1891
|
}
|
|
1883
1892
|
setTimeout(tick, delay);
|
|
@@ -1888,11 +1897,18 @@ function PaymentReturnHandler({ children }) {
|
|
|
1888
1897
|
if (typeof window === "undefined") return;
|
|
1889
1898
|
const url = new URL(window.location.href);
|
|
1890
1899
|
if (url.searchParams.get("paymentReturn") !== "1") return;
|
|
1900
|
+
cyclesRef.current = 0;
|
|
1891
1901
|
runPoll();
|
|
1892
1902
|
return () => {
|
|
1893
1903
|
runIdRef.current++;
|
|
1894
1904
|
};
|
|
1895
1905
|
}, [runPoll]);
|
|
1906
|
+
const goHome = useCallback3(() => {
|
|
1907
|
+
const cleanUrl = new URL(window.location.href);
|
|
1908
|
+
cleanUrl.searchParams.delete("paymentReturn");
|
|
1909
|
+
cleanUrl.pathname = "/app/home";
|
|
1910
|
+
window.location.href = cleanUrl.toString();
|
|
1911
|
+
}, []);
|
|
1896
1912
|
if (state === "confirming") {
|
|
1897
1913
|
return /* @__PURE__ */ jsx18("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
|
|
1898
1914
|
}
|
|
@@ -1902,6 +1918,45 @@ function PaymentReturnHandler({ children }) {
|
|
|
1902
1918
|
/* @__PURE__ */ jsx18("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
|
|
1903
1919
|
] }) });
|
|
1904
1920
|
}
|
|
1921
|
+
if (state === "timeout") {
|
|
1922
|
+
return /* @__PURE__ */ jsx18("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
|
|
1923
|
+
/* @__PURE__ */ jsx18("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
|
|
1924
|
+
/* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
1925
|
+
/* @__PURE__ */ jsx18(
|
|
1926
|
+
"button",
|
|
1927
|
+
{
|
|
1928
|
+
type: "button",
|
|
1929
|
+
onClick: () => {
|
|
1930
|
+
cyclesRef.current = 0;
|
|
1931
|
+
runPoll();
|
|
1932
|
+
},
|
|
1933
|
+
style: buttonStyle,
|
|
1934
|
+
"data-testid": "payment-timeout-retry",
|
|
1935
|
+
children: "Tentar de novo"
|
|
1936
|
+
}
|
|
1937
|
+
),
|
|
1938
|
+
/* @__PURE__ */ jsx18(
|
|
1939
|
+
"button",
|
|
1940
|
+
{
|
|
1941
|
+
type: "button",
|
|
1942
|
+
onClick: goHome,
|
|
1943
|
+
style: secondaryButtonStyle,
|
|
1944
|
+
"data-testid": "payment-timeout-home",
|
|
1945
|
+
children: "Voltar pro app"
|
|
1946
|
+
}
|
|
1947
|
+
),
|
|
1948
|
+
/* @__PURE__ */ jsx18(
|
|
1949
|
+
"a",
|
|
1950
|
+
{
|
|
1951
|
+
href: SUPPORT_MAILTO,
|
|
1952
|
+
style: linkStyle,
|
|
1953
|
+
"data-testid": "payment-timeout-support",
|
|
1954
|
+
children: "Falar com suporte"
|
|
1955
|
+
}
|
|
1956
|
+
)
|
|
1957
|
+
] })
|
|
1958
|
+
] }) });
|
|
1959
|
+
}
|
|
1905
1960
|
return /* @__PURE__ */ jsx18(Fragment5, { children });
|
|
1906
1961
|
}
|
|
1907
1962
|
var overlayStyle2 = {
|
|
@@ -1926,6 +1981,19 @@ var buttonStyle = {
|
|
|
1926
1981
|
fontWeight: 600,
|
|
1927
1982
|
cursor: "pointer"
|
|
1928
1983
|
};
|
|
1984
|
+
var secondaryButtonStyle = {
|
|
1985
|
+
...buttonStyle,
|
|
1986
|
+
background: "transparent",
|
|
1987
|
+
color: "white",
|
|
1988
|
+
border: "1px solid rgba(255,255,255,0.5)"
|
|
1989
|
+
};
|
|
1990
|
+
var linkStyle = {
|
|
1991
|
+
color: "white",
|
|
1992
|
+
textDecoration: "underline",
|
|
1993
|
+
fontSize: "0.9rem",
|
|
1994
|
+
marginTop: 4,
|
|
1995
|
+
textAlign: "center"
|
|
1996
|
+
};
|
|
1929
1997
|
|
|
1930
1998
|
// src/AppRoot.tsx
|
|
1931
1999
|
import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
@@ -2262,18 +2330,24 @@ function useLoginForm() {
|
|
|
2262
2330
|
const [password, setPassword] = useState7("");
|
|
2263
2331
|
const [submitting, setSubmitting] = useState7(false);
|
|
2264
2332
|
const [error, setError] = useState7(null);
|
|
2265
|
-
const
|
|
2333
|
+
const [touchedEmail, setTouchedEmail] = useState7(false);
|
|
2334
|
+
const [touchedPassword, setTouchedPassword] = useState7(false);
|
|
2335
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState7(false);
|
|
2336
|
+
const validateEmail = useMemo4(() => {
|
|
2266
2337
|
if (email.length === 0) return null;
|
|
2267
2338
|
if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2268
2339
|
return null;
|
|
2269
2340
|
}, [email]);
|
|
2270
|
-
const
|
|
2341
|
+
const validatePassword = useMemo4(() => {
|
|
2271
2342
|
if (password.length === 0) return null;
|
|
2272
2343
|
if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
|
|
2273
2344
|
return null;
|
|
2274
2345
|
}, [password]);
|
|
2275
|
-
const
|
|
2346
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2347
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2348
|
+
const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
|
|
2276
2349
|
const submit = useCallback5(async () => {
|
|
2350
|
+
setFormSubmitAttempted(true);
|
|
2277
2351
|
if (!canSubmit) return false;
|
|
2278
2352
|
setSubmitting(true);
|
|
2279
2353
|
setError(null);
|
|
@@ -2291,9 +2365,12 @@ function useLoginForm() {
|
|
|
2291
2365
|
email,
|
|
2292
2366
|
setEmail,
|
|
2293
2367
|
emailError,
|
|
2368
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
2294
2369
|
password,
|
|
2295
2370
|
setPassword,
|
|
2296
2371
|
passwordError,
|
|
2372
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
2373
|
+
formSubmitAttempted,
|
|
2297
2374
|
submit,
|
|
2298
2375
|
submitting,
|
|
2299
2376
|
canSubmit,
|
|
@@ -2314,23 +2391,31 @@ function useSignupForm() {
|
|
|
2314
2391
|
const [password, setPassword] = useState8("");
|
|
2315
2392
|
const [submitting, setSubmitting] = useState8(false);
|
|
2316
2393
|
const [error, setError] = useState8(null);
|
|
2317
|
-
const
|
|
2394
|
+
const [touchedName, setTouchedName] = useState8(false);
|
|
2395
|
+
const [touchedEmail, setTouchedEmail] = useState8(false);
|
|
2396
|
+
const [touchedPassword, setTouchedPassword] = useState8(false);
|
|
2397
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState8(false);
|
|
2398
|
+
const validateName = useMemo5(() => {
|
|
2318
2399
|
if (name.length === 0) return null;
|
|
2319
2400
|
if (name.trim().length < 2) return "Nome muito curto.";
|
|
2320
2401
|
return null;
|
|
2321
2402
|
}, [name]);
|
|
2322
|
-
const
|
|
2403
|
+
const validateEmail = useMemo5(() => {
|
|
2323
2404
|
if (email.length === 0) return null;
|
|
2324
2405
|
if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2325
2406
|
return null;
|
|
2326
2407
|
}, [email]);
|
|
2327
|
-
const
|
|
2408
|
+
const validatePassword = useMemo5(() => {
|
|
2328
2409
|
if (password.length === 0) return null;
|
|
2329
2410
|
if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
|
|
2330
2411
|
return null;
|
|
2331
2412
|
}, [password]);
|
|
2332
|
-
const
|
|
2413
|
+
const nameError = touchedName || formSubmitAttempted ? validateName : null;
|
|
2414
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2415
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2416
|
+
const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
|
|
2333
2417
|
const submit = useCallback6(async () => {
|
|
2418
|
+
setFormSubmitAttempted(true);
|
|
2334
2419
|
if (!canSubmit) return false;
|
|
2335
2420
|
setSubmitting(true);
|
|
2336
2421
|
setError(null);
|
|
@@ -2348,12 +2433,16 @@ function useSignupForm() {
|
|
|
2348
2433
|
name,
|
|
2349
2434
|
setName,
|
|
2350
2435
|
nameError,
|
|
2436
|
+
markNameTouched: () => setTouchedName(true),
|
|
2351
2437
|
email,
|
|
2352
2438
|
setEmail,
|
|
2353
2439
|
emailError,
|
|
2440
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
2354
2441
|
password,
|
|
2355
2442
|
setPassword,
|
|
2356
2443
|
passwordError,
|
|
2444
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
2445
|
+
formSubmitAttempted,
|
|
2357
2446
|
submit,
|
|
2358
2447
|
submitting,
|
|
2359
2448
|
canSubmit,
|
|
@@ -2372,13 +2461,17 @@ function useForgotForm() {
|
|
|
2372
2461
|
const [submitting, setSubmitting] = useState9(false);
|
|
2373
2462
|
const [sent, setSent] = useState9(false);
|
|
2374
2463
|
const [error, setError] = useState9(null);
|
|
2375
|
-
const
|
|
2464
|
+
const [touchedEmail, setTouchedEmail] = useState9(false);
|
|
2465
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
|
|
2466
|
+
const validateEmail = useMemo6(() => {
|
|
2376
2467
|
if (email.length === 0) return null;
|
|
2377
2468
|
if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
|
|
2378
2469
|
return null;
|
|
2379
2470
|
}, [email]);
|
|
2380
|
-
const
|
|
2471
|
+
const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
|
|
2472
|
+
const canSubmit = email.length > 0 && validateEmail === null && !submitting;
|
|
2381
2473
|
const submit = useCallback7(async () => {
|
|
2474
|
+
setFormSubmitAttempted(true);
|
|
2382
2475
|
if (!canSubmit) return false;
|
|
2383
2476
|
setSubmitting(true);
|
|
2384
2477
|
setError(null);
|
|
@@ -2397,6 +2490,8 @@ function useForgotForm() {
|
|
|
2397
2490
|
email,
|
|
2398
2491
|
setEmail,
|
|
2399
2492
|
emailError,
|
|
2493
|
+
markEmailTouched: () => setTouchedEmail(true),
|
|
2494
|
+
formSubmitAttempted,
|
|
2400
2495
|
submit,
|
|
2401
2496
|
submitting,
|
|
2402
2497
|
canSubmit,
|
|
@@ -2417,24 +2512,30 @@ function useResetForm() {
|
|
|
2417
2512
|
const [submitting, setSubmitting] = useState10(false);
|
|
2418
2513
|
const [done, setDone] = useState10(false);
|
|
2419
2514
|
const [error, setError] = useState10(null);
|
|
2515
|
+
const [touchedPassword, setTouchedPassword] = useState10(false);
|
|
2516
|
+
const [touchedConfirm, setTouchedConfirm] = useState10(false);
|
|
2517
|
+
const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
|
|
2420
2518
|
useEffect8(() => {
|
|
2421
2519
|
if (typeof window === "undefined") return;
|
|
2422
2520
|
const params = new URLSearchParams(window.location.search);
|
|
2423
2521
|
const t = params.get("token");
|
|
2424
2522
|
setToken(t && t.length > 0 ? t : null);
|
|
2425
2523
|
}, []);
|
|
2426
|
-
const
|
|
2524
|
+
const validatePassword = useMemo7(() => {
|
|
2427
2525
|
if (password.length === 0) return null;
|
|
2428
2526
|
if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
|
|
2429
2527
|
return null;
|
|
2430
2528
|
}, [password]);
|
|
2431
|
-
const
|
|
2529
|
+
const validateConfirm = useMemo7(() => {
|
|
2432
2530
|
if (confirm.length === 0) return null;
|
|
2433
2531
|
if (confirm !== password) return "Senhas n\xE3o coincidem.";
|
|
2434
2532
|
return null;
|
|
2435
2533
|
}, [confirm, password]);
|
|
2436
|
-
const
|
|
2534
|
+
const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
|
|
2535
|
+
const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
|
|
2536
|
+
const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
|
|
2437
2537
|
const submit = useCallback8(async () => {
|
|
2538
|
+
setFormSubmitAttempted(true);
|
|
2438
2539
|
if (!canSubmit || token === null) return;
|
|
2439
2540
|
setSubmitting(true);
|
|
2440
2541
|
setError(null);
|
|
@@ -2458,9 +2559,12 @@ function useResetForm() {
|
|
|
2458
2559
|
password,
|
|
2459
2560
|
setPassword,
|
|
2460
2561
|
passwordError,
|
|
2562
|
+
markPasswordTouched: () => setTouchedPassword(true),
|
|
2461
2563
|
confirm,
|
|
2462
2564
|
setConfirm,
|
|
2463
2565
|
confirmError,
|
|
2566
|
+
markConfirmTouched: () => setTouchedConfirm(true),
|
|
2567
|
+
formSubmitAttempted,
|
|
2464
2568
|
submit,
|
|
2465
2569
|
submitting,
|
|
2466
2570
|
canSubmit,
|