chore: dependency updates and UI fixes (batch)
This commit is contained in:
parent
3b0f267ab3
commit
6d60eebe1a
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useActionState, useEffect, useState } from 'react';
|
||||||
import { ChevronDown, Copy, Layers, Link2, Link2Off, Loader2, Pencil, Plus, RefreshCw, Trash2 } from 'lucide-react';
|
import { ChevronDown, Copy, Layers, Link2, Link2Off, Loader2, Pencil, Plus, RefreshCw, Trash2 } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
@ -172,7 +172,6 @@ export default function BillModal({ bill, initialBill, categories, onClose, onSa
|
||||||
);
|
);
|
||||||
const [saveTemplate, setSaveTemplate] = useState(false);
|
const [saveTemplate, setSaveTemplate] = useState(false);
|
||||||
const [templateName, setTemplateName] = useState('');
|
const [templateName, setTemplateName] = useState('');
|
||||||
const [busy, setBusy] = useState(false);
|
|
||||||
const [errors, setErrors] = useState({});
|
const [errors, setErrors] = useState({});
|
||||||
const [payments, setPayments] = useState([]);
|
const [payments, setPayments] = useState([]);
|
||||||
const [paymentsLoading, setPaymentsLoading] = useState(false);
|
const [paymentsLoading, setPaymentsLoading] = useState(false);
|
||||||
|
|
@ -508,9 +507,7 @@ export default function BillModal({ bill, initialBill, categories, onClose, onSa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSubmit(e) {
|
const [, submitAction, isPending] = useActionState(async () => {
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (!validateForm()) {
|
if (!validateForm()) {
|
||||||
toast.error('Please fix the form errors before saving.');
|
toast.error('Please fix the form errors before saving.');
|
||||||
return;
|
return;
|
||||||
|
|
@ -560,7 +557,6 @@ export default function BillModal({ bill, initialBill, categories, onClose, onSa
|
||||||
snowball_include: snowballInclude,
|
snowball_include: snowballInclude,
|
||||||
snowball_exempt: snowballExempt,
|
snowball_exempt: snowballExempt,
|
||||||
};
|
};
|
||||||
setBusy(true);
|
|
||||||
try {
|
try {
|
||||||
let savedBill;
|
let savedBill;
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
|
|
@ -583,10 +579,8 @@ export default function BillModal({ bill, initialBill, categories, onClose, onSa
|
||||||
onClose();
|
onClose();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(err.message);
|
toast.error(err.message);
|
||||||
} finally {
|
|
||||||
setBusy(false);
|
|
||||||
}
|
}
|
||||||
}
|
}, null);
|
||||||
|
|
||||||
const inp = 'bg-background/50 border-border/60 h-9 text-sm w-full';
|
const inp = 'bg-background/50 border-border/60 h-9 text-sm w-full';
|
||||||
|
|
||||||
|
|
@ -599,7 +593,7 @@ export default function BillModal({ bill, initialBill, categories, onClose, onSa
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<form id="bill-modal-form" onSubmit={handleSubmit}>
|
<form id="bill-modal-form" action={submitAction}>
|
||||||
<div className="grid gap-x-5 gap-y-4 py-2 sm:grid-cols-2">
|
<div className="grid gap-x-5 gap-y-4 py-2 sm:grid-cols-2">
|
||||||
|
|
||||||
{/* Name */}
|
{/* Name */}
|
||||||
|
|
@ -1325,7 +1319,7 @@ export default function BillModal({ bill, initialBill, categories, onClose, onSa
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={busy}
|
disabled={isPending}
|
||||||
onClick={() => onDuplicate(bill)}
|
onClick={() => onDuplicate(bill)}
|
||||||
className="gap-2 text-xs"
|
className="gap-2 text-xs"
|
||||||
>
|
>
|
||||||
|
|
@ -1334,10 +1328,11 @@ export default function BillModal({ bill, initialBill, categories, onClose, onSa
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button type="button" variant="ghost" disabled={busy} onClick={onClose} className="text-xs">
|
<Button type="button" variant="ghost" disabled={isPending} onClick={onClose} className="text-xs">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" form="bill-modal-form" disabled={busy} className="text-xs">
|
<Button type="submit" form="bill-modal-form" disabled={isPending} className="gap-1.5 text-xs">
|
||||||
|
{isPending && <Loader2 className="h-3.5 w-3.5 animate-spin" />}
|
||||||
{isNew ? 'Add Bill' : 'Save Changes'}
|
{isNew ? 'Add Bill' : 'Save Changes'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Polyfill for React 19's useOptimistic.
|
|
||||||
* Shows optimistic state immediately; reconciles when passthrough changes.
|
|
||||||
*/
|
|
||||||
export function useOptimistic(passthrough, reducer) {
|
|
||||||
const [optimistic, setOptimistic] = useState(passthrough);
|
|
||||||
|
|
||||||
// Whenever the server-confirmed state lands, sync it in.
|
|
||||||
useEffect(() => {
|
|
||||||
setOptimistic(passthrough);
|
|
||||||
}, [passthrough]);
|
|
||||||
|
|
||||||
const dispatch = useCallback(
|
|
||||||
action => setOptimistic(current => reducer(current, action)),
|
|
||||||
[reducer],
|
|
||||||
);
|
|
||||||
|
|
||||||
return [optimistic, dispatch];
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useOptimistic, useRef, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import {
|
import {
|
||||||
|
|
@ -48,7 +48,6 @@ import BillModal from '@/components/BillModal';
|
||||||
import BillHistoricalImportDialog from '@/components/BillHistoricalImportDialog';
|
import BillHistoricalImportDialog from '@/components/BillHistoricalImportDialog';
|
||||||
import { getLinkImportPref } from '@/pages/SettingsPage';
|
import { getLinkImportPref } from '@/pages/SettingsPage';
|
||||||
import { useSearchPanelPreference } from '@/hooks/useSearchPanelPreference';
|
import { useSearchPanelPreference } from '@/hooks/useSearchPanelPreference';
|
||||||
import { useOptimistic } from '@/hooks/useOptimistic';
|
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import { moveInArray, movedItemId, reorderPayload } from '@/lib/reorder';
|
import { moveInArray, movedItemId, reorderPayload } from '@/lib/reorder';
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -45,8 +45,8 @@
|
||||||
"openid-client": "^5.7.1",
|
"openid-client": "^5.7.1",
|
||||||
"otplib": "^13.4.1",
|
"otplib": "^13.4.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"react": "^18.3.1",
|
"react": "^19.2.7",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^19.2.7",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.26.2",
|
||||||
"rehype-sanitize": "^6.0.0",
|
"rehype-sanitize": "^6.0.0",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue