polish(ui): loading feedback on sync/refresh buttons system-wide (#1156)
The "Synka bank nu" row in the transactions Importera split button fired syncAll() with zero visual feedback. SplitButton now takes busy/busyLabel per option: the primary face and the menu row swap to a spinning Loader2, show the busy label and go inert until the action resolves. useBankSync holds isBusy across the whole syncAll loop so the spinner does not flicker between per-connection syncs. Sweep of the rest of the system for async buttons missing the same feedback (convention: disabled + Loader2 animate-spin + label swap): - bokslut DigitalInlamning "Uppdatera status" (Bolagsverket poll): had no feedback at all; now disabled + spinner + "Uppdaterar ..." while polling - Stripe settings "Synka nu": had disabled + label swap but a static icon - Skatteverket "Verifiera": had disabled + label swap but no spinner - AgentMemoryPanel "Dolj"/"Aterstall" row actions: static icons on async patch; now swap to spinner for the busy row Checked and intentionally unchanged: Arcim migration "Synka igen" and "Ateranslut" (the whole step flips to a spinner view synchronously on click), skattekonto "Forsok igen" (page flips to loading view), AgentChat "Generera om" (streaming indicator is the feedback). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
bb551d1d59
commit
8a9162b948
@@ -46,6 +46,9 @@ export function useBankSync() {
|
||||
const hasBankSync = useCapability(CAPABILITY.bank_sync)
|
||||
const [connections, setConnections] = useState<BankConn[] | null>(null)
|
||||
const [busyId, setBusyId] = useState<string | null>(null)
|
||||
// Holds isBusy true across the whole syncAll loop so the spinner doesn't
|
||||
// flicker off between per-connection syncs.
|
||||
const [syncingAll, setSyncingAll] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!company?.id) return
|
||||
@@ -156,14 +159,19 @@ export function useBankSync() {
|
||||
// active connection in turn; with only dead connections it jumps straight
|
||||
// to re-authorizing the first one (a retry can't revive a closed session).
|
||||
async function syncAll() {
|
||||
const conns = connections ?? []
|
||||
const active = conns.filter((c) => c.status === 'active')
|
||||
if (active.length === 0) {
|
||||
if (conns[0]) await reconnect(conns[0])
|
||||
return
|
||||
}
|
||||
for (const conn of active) {
|
||||
await syncConnection(conn)
|
||||
setSyncingAll(true)
|
||||
try {
|
||||
const conns = connections ?? []
|
||||
const active = conns.filter((c) => c.status === 'active')
|
||||
if (active.length === 0) {
|
||||
if (conns[0]) await reconnect(conns[0])
|
||||
return
|
||||
}
|
||||
for (const conn of active) {
|
||||
await syncConnection(conn)
|
||||
}
|
||||
} finally {
|
||||
setSyncingAll(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +185,7 @@ export function useBankSync() {
|
||||
return {
|
||||
connections,
|
||||
busyId,
|
||||
isBusy: busyId !== null,
|
||||
isBusy: busyId !== null || syncingAll,
|
||||
hasBankSync,
|
||||
reconnect,
|
||||
syncConnection,
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function TransactionStatusBar({
|
||||
const t = useTranslations('transactions')
|
||||
const router = useRouter()
|
||||
const { uiState, loaded } = useUiState()
|
||||
const { connections, hasBankSync, syncAll, lastSyncedAt } = useBankSync()
|
||||
const { connections, hasBankSync, syncAll, lastSyncedAt, isBusy } = useBankSync()
|
||||
const formatAge = useAgeFormatter()
|
||||
|
||||
// "Synka bank nu" (concept: first menu row) only renders once a bank is
|
||||
@@ -41,6 +41,8 @@ export default function TransactionStatusBar({
|
||||
key: 'synka',
|
||||
label: t('create_synka'),
|
||||
icon: RefreshCw,
|
||||
busy: isBusy,
|
||||
busyLabel: t('bank_sync_button_syncing'),
|
||||
description: lastSyncedAt
|
||||
? t('create_synka_desc_last', { age: formatAge(lastSyncedAt) })
|
||||
: t('create_synka_desc'),
|
||||
|
||||
Reference in New Issue
Block a user