@hed-hog/finance 0.0.303 → 0.0.305

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.
@@ -1,131 +1,135 @@
1
- 'use client';
2
-
3
- import {
4
- Card,
5
- CardContent,
6
- CardDescription,
7
- CardHeader,
8
- CardTitle,
9
- } from '@/components/ui/card';
10
- import { cn } from '@/lib/utils';
11
- import type { ReactNode } from 'react';
12
-
13
- type FinancePageSectionProps = {
14
- title?: ReactNode;
15
- description?: ReactNode;
16
- actions?: ReactNode;
17
- children: ReactNode;
18
- className?: string;
19
- contentClassName?: string;
20
- variant?: 'card' | 'flat';
21
- };
22
-
23
- export function FinancePageSection({
24
- title,
25
- description,
26
- actions,
27
- children,
28
- className,
29
- contentClassName,
30
- variant = 'card',
31
- }: FinancePageSectionProps) {
32
- const hasHeader = title || description || actions;
33
-
34
- if (variant === 'flat') {
35
- return (
36
- <section className={cn('space-y-4', className)}>
37
- {hasHeader ? (
38
- <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
39
- <div className="space-y-1">
40
- {title ? (
41
- <h2 className="text-base font-semibold">{title}</h2>
42
- ) : null}
43
- {description ? (
44
- <p className="text-sm text-muted-foreground">{description}</p>
45
- ) : null}
46
- </div>
47
- {actions ? <div className="shrink-0">{actions}</div> : null}
48
- </div>
49
- ) : null}
50
- <div className={cn(contentClassName)}>{children}</div>
51
- </section>
52
- );
53
- }
54
-
55
- return (
56
- <Card
57
- className={cn(
58
- 'overflow-hidden border-border/60 py-0 shadow-sm',
59
- className
60
- )}
61
- >
62
- {hasHeader ? (
63
- <CardHeader className="flex flex-col gap-3 border-b border-border/50 px-5 py-4 sm:flex-row sm:items-start sm:justify-between">
64
- <div className="space-y-1">
65
- {title ? (
66
- <CardTitle className="text-base">{title}</CardTitle>
67
- ) : null}
68
- {description ? (
69
- <CardDescription>{description}</CardDescription>
70
- ) : null}
71
- </div>
72
- {actions ? <div className="shrink-0">{actions}</div> : null}
73
- </CardHeader>
74
- ) : null}
75
- <CardContent className={cn('p-0', contentClassName)}>
76
- {children}
77
- </CardContent>
78
- </Card>
79
- );
80
- }
81
-
82
- type FinanceSheetBodyProps = {
83
- children: ReactNode;
84
- className?: string;
85
- };
86
-
87
- export function FinanceSheetBody({
88
- children,
89
- className,
90
- }: FinanceSheetBodyProps) {
91
- return (
92
- <div
93
- className={cn(
94
- 'flex-1 space-y-5 overflow-y-auto px-4 py-4 sm:px-6 sm:py-5',
95
- className
96
- )}
97
- >
98
- {children}
99
- </div>
100
- );
101
- }
102
-
103
- type FinanceSheetSectionProps = {
104
- title: ReactNode;
105
- description?: ReactNode;
106
- children: ReactNode;
107
- className?: string;
108
- contentClassName?: string;
109
- };
110
-
111
- export function FinanceSheetSection({
112
- title,
113
- description,
114
- children,
115
- className,
116
- contentClassName,
117
- }: FinanceSheetSectionProps) {
118
- return (
119
- <section className={cn('', className)}>
120
- <div className="mb-4 space-y-1">
121
- <h3 className="text-sm font-semibold text-foreground">{title}</h3>
122
- {description ? (
123
- <p className="text-sm leading-relaxed text-muted-foreground">
124
- {description}
125
- </p>
126
- ) : null}
127
- </div>
128
- <div className={cn('space-y-4', contentClassName)}>{children}</div>
129
- </section>
130
- );
131
- }
1
+ 'use client';
2
+
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from '@/components/ui/card';
10
+ import { cn } from '@/lib/utils';
11
+ import type { ReactNode } from 'react';
12
+
13
+ type FinancePageSectionProps = {
14
+ title?: ReactNode;
15
+ description?: ReactNode;
16
+ actions?: ReactNode;
17
+ children: ReactNode;
18
+ className?: string;
19
+ contentClassName?: string;
20
+ variant?: 'card' | 'flat';
21
+ };
22
+
23
+ export function FinancePageSection({
24
+ title,
25
+ description,
26
+ actions,
27
+ children,
28
+ className,
29
+ contentClassName,
30
+ variant = 'card',
31
+ }: FinancePageSectionProps) {
32
+ const hasHeader = title || description || actions;
33
+
34
+ if (variant === 'flat') {
35
+ return (
36
+ <section className={cn('space-y-4', className)}>
37
+ {hasHeader ? (
38
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
39
+ <div className="space-y-1">
40
+ {title ? (
41
+ <h2 className="text-base font-semibold">{title}</h2>
42
+ ) : null}
43
+ {description ? (
44
+ <p className="text-sm text-muted-foreground">{description}</p>
45
+ ) : null}
46
+ </div>
47
+ {actions ? <div className="shrink-0">{actions}</div> : null}
48
+ </div>
49
+ ) : null}
50
+ <div className={cn(contentClassName)}>{children}</div>
51
+ </section>
52
+ );
53
+ }
54
+
55
+ return (
56
+ <Card
57
+ className={cn(
58
+ 'overflow-hidden border-border/60 py-0 shadow-sm',
59
+ className
60
+ )}
61
+ >
62
+ {hasHeader ? (
63
+ <CardHeader className="flex flex-col gap-3 border-b border-border/50 px-5 py-4 sm:flex-row sm:items-start sm:justify-between">
64
+ <div className="space-y-1">
65
+ {title ? (
66
+ <CardTitle className="text-base">{title}</CardTitle>
67
+ ) : null}
68
+ {description ? (
69
+ <CardDescription>{description}</CardDescription>
70
+ ) : null}
71
+ </div>
72
+ {actions ? <div className="shrink-0">{actions}</div> : null}
73
+ </CardHeader>
74
+ ) : null}
75
+ <CardContent className={cn('p-0', contentClassName)}>
76
+ {children}
77
+ </CardContent>
78
+ </Card>
79
+ );
80
+ }
81
+
82
+ type FinanceSheetBodyProps = {
83
+ children: ReactNode;
84
+ className?: string;
85
+ };
86
+
87
+ export function FinanceSheetBody({
88
+ children,
89
+ className,
90
+ }: FinanceSheetBodyProps) {
91
+ return (
92
+ <div
93
+ className={cn(
94
+ 'flex-1 space-y-5 overflow-y-auto px-4 py-4 sm:px-6 sm:py-5',
95
+ className
96
+ )}
97
+ >
98
+ {children}
99
+ </div>
100
+ );
101
+ }
102
+
103
+ type FinanceSheetSectionProps = {
104
+ title?: ReactNode;
105
+ description?: ReactNode;
106
+ children: ReactNode;
107
+ className?: string;
108
+ contentClassName?: string;
109
+ };
110
+
111
+ export function FinanceSheetSection({
112
+ title,
113
+ description,
114
+ children,
115
+ className,
116
+ contentClassName,
117
+ }: FinanceSheetSectionProps) {
118
+ return (
119
+ <section className={cn('', className)}>
120
+ {title || description ? (
121
+ <div className="mb-4 space-y-1">
122
+ {title ? (
123
+ <h3 className="text-sm font-semibold text-foreground">{title}</h3>
124
+ ) : null}
125
+ {description ? (
126
+ <p className="text-sm leading-relaxed text-muted-foreground">
127
+ {description}
128
+ </p>
129
+ ) : null}
130
+ </div>
131
+ ) : null}
132
+ <div className={cn('space-y-4', contentClassName)}>{children}</div>
133
+ </section>
134
+ );
135
+ }
@@ -64,8 +64,12 @@ import {
64
64
  TooltipContent,
65
65
  TooltipTrigger,
66
66
  } from '@/components/ui/tooltip';
67
+ import { useFormDraft } from '@/hooks/use-form-draft';
68
+ import { formatDateTime } from '@/lib/format-date';
67
69
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
68
70
  import { zodResolver } from '@hookform/resolvers/zod';
71
+ import { formatDistanceToNow } from 'date-fns';
72
+ import { enUS, ptBR } from 'date-fns/locale';
69
73
  import {
70
74
  AlertTriangle,
71
75
  FileText,
@@ -80,7 +84,7 @@ import { useTranslations } from 'next-intl';
80
84
  import Link from 'next/link';
81
85
  import { usePathname, useRouter, useSearchParams } from 'next/navigation';
82
86
  import { useEffect, useMemo, useRef, useState } from 'react';
83
- import { useFieldArray, useForm } from 'react-hook-form';
87
+ import { useFieldArray, useForm, useWatch } from 'react-hook-form';
84
88
  import { z } from 'zod';
85
89
  import { formatarData } from '../../_lib/formatters';
86
90
  import {
@@ -246,6 +250,21 @@ const getNewTitleFormSchema = (t: ReturnType<typeof useTranslations>) =>
246
250
 
247
251
  type NewTitleFormValues = z.infer<ReturnType<typeof getNewTitleFormSchema>>;
248
252
 
253
+ type PayableInstallmentDraftPayload = {
254
+ mode: 'create' | 'edit';
255
+ titleId: string | null;
256
+ values: NewTitleFormValues;
257
+ uploadedFileId: number | null;
258
+ uploadedFileName: string;
259
+ isInstallmentsEdited: boolean;
260
+ autoRedistributeInstallments: boolean;
261
+ };
262
+
263
+ const PAYABLE_NEW_TITLE_DRAFT_STORAGE_KEY =
264
+ 'finance-accounts-payable-installments-new-draft';
265
+ const PAYABLE_EDIT_TITLE_DRAFT_STORAGE_KEY =
266
+ 'finance-accounts-payable-installments-edit-draft';
267
+
249
268
  const getSettleTitleFormSchema = (t: ReturnType<typeof useTranslations>) =>
250
269
  z.object({
251
270
  installmentId: z.string().min(1, t('validation.installmentRequired')),
@@ -276,7 +295,8 @@ function NovoTituloSheet({
276
295
  open?: boolean;
277
296
  onOpenChange?: (open: boolean) => void;
278
297
  }) {
279
- const { request, showToastHandler } = useApp();
298
+ const { request, showToastHandler, currentLocaleCode, getSettingValue } =
299
+ useApp();
280
300
  const [internalOpen, setInternalOpen] = useState(false);
281
301
  const [uploadedFileId, setUploadedFileId] = useState<number | null>(null);
282
302
  const [uploadedFileName, setUploadedFileName] = useState('');
@@ -345,13 +365,141 @@ function NovoTituloSheet({
345
365
  name: 'installments',
346
366
  });
347
367
 
368
+ const watchedFormValues = useWatch({
369
+ control: form.control,
370
+ });
371
+
372
+ const {
373
+ clearDraft,
374
+ loadDraft,
375
+ hasDraft,
376
+ savedAt: draftSavedAt,
377
+ } = useFormDraft<PayableInstallmentDraftPayload>({
378
+ storageKey: PAYABLE_NEW_TITLE_DRAFT_STORAGE_KEY,
379
+ value: {
380
+ mode: 'create',
381
+ titleId: null,
382
+ values: {
383
+ documento: watchedFormValues.documento ?? '',
384
+ fornecedorId: watchedFormValues.fornecedorId ?? '',
385
+ competencia: watchedFormValues.competencia ?? '',
386
+ vencimento: watchedFormValues.vencimento ?? '',
387
+ valor: watchedFormValues.valor ?? 0,
388
+ installmentsCount: watchedFormValues.installmentsCount ?? 1,
389
+ installments: watchedFormValues.installments?.map((installment) => ({
390
+ dueDate: installment?.dueDate ?? '',
391
+ amount: Number(installment?.amount ?? 0),
392
+ })) ?? [{ dueDate: '', amount: 0 }],
393
+ categoriaId: watchedFormValues.categoriaId ?? '',
394
+ centroCustoId: watchedFormValues.centroCustoId ?? '',
395
+ metodo: watchedFormValues.metodo ?? '',
396
+ descricao: watchedFormValues.descricao ?? '',
397
+ },
398
+ uploadedFileId,
399
+ uploadedFileName,
400
+ isInstallmentsEdited,
401
+ autoRedistributeInstallments,
402
+ },
403
+ hasData: Boolean(
404
+ (watchedFormValues.documento ?? '').trim() ||
405
+ (watchedFormValues.fornecedorId ?? '').trim() ||
406
+ (watchedFormValues.competencia ?? '').trim() ||
407
+ (watchedFormValues.vencimento ?? '').trim() ||
408
+ (watchedFormValues.categoriaId ?? '').trim() ||
409
+ (watchedFormValues.centroCustoId ?? '').trim() ||
410
+ (watchedFormValues.metodo ?? '').trim() ||
411
+ (watchedFormValues.descricao ?? '').trim() ||
412
+ (watchedFormValues.valor ?? 0) > 0 ||
413
+ (watchedFormValues.installmentsCount ?? 1) !== 1 ||
414
+ (watchedFormValues.installments ?? []).some(
415
+ (installment) =>
416
+ (installment?.dueDate ?? '').trim() ||
417
+ Number(installment?.amount ?? 0) > 0
418
+ ) ||
419
+ uploadedFileId != null ||
420
+ uploadedFileName.trim()
421
+ ),
422
+ enabled: isOpen,
423
+ });
424
+
425
+ const draftStatusContent = useMemo(() => {
426
+ if (!hasDraft || !draftSavedAt) {
427
+ return null;
428
+ }
429
+
430
+ const savedDate = new Date(draftSavedAt);
431
+ if (Number.isNaN(savedDate.getTime())) {
432
+ return null;
433
+ }
434
+
435
+ const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
436
+ const relativeLabel = formatDistanceToNow(savedDate, {
437
+ addSuffix: true,
438
+ locale,
439
+ });
440
+ const absoluteLabel = formatDateTime(
441
+ savedDate,
442
+ getSettingValue,
443
+ currentLocaleCode
444
+ );
445
+
446
+ return currentLocaleCode.startsWith('pt')
447
+ ? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
448
+ : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
449
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
450
+
348
451
  const watchedInstallmentsCount = form.watch('installmentsCount');
349
452
  const watchedTotalValue = form.watch('valor');
350
453
  const watchedDueDate = form.watch('vencimento');
351
454
  const watchedInstallments = form.watch('installments');
352
455
 
353
456
  useEffect(() => {
354
- if (isInstallmentsEdited) {
457
+ if (!isOpen) {
458
+ return;
459
+ }
460
+
461
+ const storedDraft = loadDraft();
462
+
463
+ if (storedDraft?.payload.mode === 'create') {
464
+ form.reset(storedDraft.payload.values);
465
+ setUploadedFileId(storedDraft.payload.uploadedFileId ?? null);
466
+ setUploadedFileName(storedDraft.payload.uploadedFileName ?? '');
467
+ setExtractionConfidence(null);
468
+ setExtractionWarnings([]);
469
+ setUploadProgress(storedDraft.payload.uploadedFileId ? 100 : 0);
470
+ setIsInstallmentsEdited(
471
+ storedDraft.payload.isInstallmentsEdited ?? false
472
+ );
473
+ setAutoRedistributeInstallments(
474
+ storedDraft.payload.autoRedistributeInstallments ?? true
475
+ );
476
+ return;
477
+ }
478
+
479
+ form.reset({
480
+ documento: '',
481
+ fornecedorId: '',
482
+ competencia: '',
483
+ vencimento: '',
484
+ valor: 0,
485
+ installmentsCount: 1,
486
+ installments: [{ dueDate: '', amount: 0 }],
487
+ categoriaId: '',
488
+ centroCustoId: '',
489
+ metodo: '',
490
+ descricao: '',
491
+ });
492
+ setUploadedFileId(null);
493
+ setUploadedFileName('');
494
+ setExtractionConfidence(null);
495
+ setExtractionWarnings([]);
496
+ setUploadProgress(0);
497
+ setIsInstallmentsEdited(false);
498
+ setAutoRedistributeInstallments(true);
499
+ }, [form, isOpen, loadDraft]);
500
+
501
+ useEffect(() => {
502
+ if (isInstallmentsEdited || !isOpen) {
355
503
  return;
356
504
  }
357
505
 
@@ -364,6 +512,7 @@ function NovoTituloSheet({
364
512
  );
365
513
  }, [
366
514
  isInstallmentsEdited,
515
+ isOpen,
367
516
  replaceInstallments,
368
517
  watchedDueDate,
369
518
  watchedInstallmentsCount,
@@ -464,6 +613,7 @@ function NovoTituloSheet({
464
613
  },
465
614
  });
466
615
 
616
+ clearDraft();
467
617
  await onCreated();
468
618
  form.reset();
469
619
  setUploadedFileId(null);
@@ -480,14 +630,6 @@ function NovoTituloSheet({
480
630
  };
481
631
 
482
632
  const handleCancel = () => {
483
- form.reset();
484
- setUploadedFileId(null);
485
- setUploadedFileName('');
486
- setExtractionConfidence(null);
487
- setExtractionWarnings([]);
488
- setUploadProgress(0);
489
- setIsInstallmentsEdited(false);
490
- setAutoRedistributeInstallments(true);
491
633
  handleOpenChange(false);
492
634
  };
493
635
 
@@ -1158,6 +1300,11 @@ function NovoTituloSheet({
1158
1300
  </FinanceSheetBody>
1159
1301
 
1160
1302
  <div className="border-t border-border/50 px-4 py-4 sm:px-6">
1303
+ {draftStatusContent ? (
1304
+ <p className="mb-2 text-xs text-muted-foreground">
1305
+ {draftStatusContent}
1306
+ </p>
1307
+ ) : null}
1161
1308
  <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
1162
1309
  <Button type="button" variant="outline" onClick={handleCancel}>
1163
1310
  {t('common.cancel')}
@@ -1209,7 +1356,8 @@ function EditarTituloSheet({
1209
1356
  onCategoriesUpdated?: () => Promise<any> | void;
1210
1357
  onCostCentersUpdated?: () => Promise<any> | void;
1211
1358
  }) {
1212
- const { request, showToastHandler } = useApp();
1359
+ const { request, showToastHandler, currentLocaleCode, getSettingValue } =
1360
+ useApp();
1213
1361
  const [uploadedFileId, setUploadedFileId] = useState<number | null>(null);
1214
1362
  const [uploadedFileName, setUploadedFileName] = useState('');
1215
1363
  const [isUploadingFile, setIsUploadingFile] = useState(false);
@@ -1270,6 +1418,89 @@ function EditarTituloSheet({
1270
1418
  name: 'installments',
1271
1419
  });
1272
1420
 
1421
+ const watchedFormValues = useWatch({
1422
+ control: form.control,
1423
+ });
1424
+
1425
+ const {
1426
+ clearDraft,
1427
+ loadDraft,
1428
+ hasDraft,
1429
+ savedAt: draftSavedAt,
1430
+ } = useFormDraft<PayableInstallmentDraftPayload>({
1431
+ storageKey: PAYABLE_EDIT_TITLE_DRAFT_STORAGE_KEY,
1432
+ value: {
1433
+ mode: 'edit',
1434
+ titleId: titulo?.id ? String(titulo.id) : null,
1435
+ values: {
1436
+ documento: watchedFormValues.documento ?? '',
1437
+ fornecedorId: watchedFormValues.fornecedorId ?? '',
1438
+ competencia: watchedFormValues.competencia ?? '',
1439
+ vencimento: watchedFormValues.vencimento ?? '',
1440
+ valor: watchedFormValues.valor ?? 0,
1441
+ installmentsCount: watchedFormValues.installmentsCount ?? 1,
1442
+ installments: watchedFormValues.installments?.map((installment) => ({
1443
+ dueDate: installment?.dueDate ?? '',
1444
+ amount: Number(installment?.amount ?? 0),
1445
+ })) ?? [{ dueDate: '', amount: 0 }],
1446
+ categoriaId: watchedFormValues.categoriaId ?? '',
1447
+ centroCustoId: watchedFormValues.centroCustoId ?? '',
1448
+ metodo: watchedFormValues.metodo ?? '',
1449
+ descricao: watchedFormValues.descricao ?? '',
1450
+ },
1451
+ uploadedFileId,
1452
+ uploadedFileName,
1453
+ isInstallmentsEdited,
1454
+ autoRedistributeInstallments,
1455
+ },
1456
+ hasData: Boolean(
1457
+ titulo &&
1458
+ ((watchedFormValues.documento ?? '').trim() ||
1459
+ (watchedFormValues.fornecedorId ?? '').trim() ||
1460
+ (watchedFormValues.competencia ?? '').trim() ||
1461
+ (watchedFormValues.vencimento ?? '').trim() ||
1462
+ (watchedFormValues.categoriaId ?? '').trim() ||
1463
+ (watchedFormValues.centroCustoId ?? '').trim() ||
1464
+ (watchedFormValues.metodo ?? '').trim() ||
1465
+ (watchedFormValues.descricao ?? '').trim() ||
1466
+ (watchedFormValues.valor ?? 0) > 0 ||
1467
+ (watchedFormValues.installments ?? []).some(
1468
+ (installment) =>
1469
+ (installment?.dueDate ?? '').trim() ||
1470
+ Number(installment?.amount ?? 0) > 0
1471
+ ) ||
1472
+ uploadedFileId != null ||
1473
+ uploadedFileName.trim())
1474
+ ),
1475
+ enabled: open,
1476
+ });
1477
+
1478
+ const draftStatusContent = useMemo(() => {
1479
+ if (!hasDraft || !draftSavedAt) {
1480
+ return null;
1481
+ }
1482
+
1483
+ const savedDate = new Date(draftSavedAt);
1484
+ if (Number.isNaN(savedDate.getTime())) {
1485
+ return null;
1486
+ }
1487
+
1488
+ const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
1489
+ const relativeLabel = formatDistanceToNow(savedDate, {
1490
+ addSuffix: true,
1491
+ locale,
1492
+ });
1493
+ const absoluteLabel = formatDateTime(
1494
+ savedDate,
1495
+ getSettingValue,
1496
+ currentLocaleCode
1497
+ );
1498
+
1499
+ return currentLocaleCode.startsWith('pt')
1500
+ ? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
1501
+ : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
1502
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
1503
+
1273
1504
  const watchedInstallmentsCount = form.watch('installmentsCount');
1274
1505
  const watchedTotalValue = form.watch('valor');
1275
1506
  const watchedDueDate = form.watch('vencimento');
@@ -1297,6 +1528,25 @@ function EditarTituloSheet({
1297
1528
  return;
1298
1529
  }
1299
1530
 
1531
+ const storedDraft = loadDraft();
1532
+ const shouldRestoreDraft =
1533
+ storedDraft?.payload.mode === 'edit' &&
1534
+ String(storedDraft.payload.titleId ?? '') === String(titulo.id ?? '');
1535
+
1536
+ if (shouldRestoreDraft) {
1537
+ form.reset(storedDraft.payload.values);
1538
+ setUploadedFileId(storedDraft.payload.uploadedFileId ?? null);
1539
+ setUploadedFileName(storedDraft.payload.uploadedFileName ?? '');
1540
+ setExtractionConfidence(null);
1541
+ setExtractionWarnings([]);
1542
+ setUploadProgress(storedDraft.payload.uploadedFileId ? 100 : 0);
1543
+ setIsInstallmentsEdited(storedDraft.payload.isInstallmentsEdited ?? true);
1544
+ setAutoRedistributeInstallments(
1545
+ storedDraft.payload.autoRedistributeInstallments ?? true
1546
+ );
1547
+ return;
1548
+ }
1549
+
1300
1550
  const installments = Array.isArray(titulo.parcelas) ? titulo.parcelas : [];
1301
1551
  const normalizedInstallments =
1302
1552
  installments.length > 0
@@ -1359,9 +1609,9 @@ function EditarTituloSheet({
1359
1609
  setExtractionConfidence(null);
1360
1610
  setExtractionWarnings([]);
1361
1611
  setUploadProgress(0);
1362
-
1612
+ setAutoRedistributeInstallments(true);
1363
1613
  setIsInstallmentsEdited(true);
1364
- }, [form, open, titulo]);
1614
+ }, [form, loadDraft, open, titulo]);
1365
1615
 
1366
1616
  useEffect(() => {
1367
1617
  if (isInstallmentsEdited || !open) {
@@ -1483,6 +1733,7 @@ function EditarTituloSheet({
1483
1733
  },
1484
1734
  });
1485
1735
 
1736
+ clearDraft();
1486
1737
  await onUpdated();
1487
1738
  showToastHandler?.('success', t('messages.updateSuccess'));
1488
1739
  onOpenChange(false);
@@ -2160,6 +2411,11 @@ function EditarTituloSheet({
2160
2411
  </FinanceSheetBody>
2161
2412
 
2162
2413
  <div className="border-t border-border/50 px-4 py-4 sm:px-6">
2414
+ {draftStatusContent ? (
2415
+ <p className="mb-2 text-xs text-muted-foreground">
2416
+ {draftStatusContent}
2417
+ </p>
2418
+ ) : null}
2163
2419
  <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
2164
2420
  <Button type="button" variant="outline" onClick={handleCancel}>
2165
2421
  {t('common.cancel')}