fix(ui): firmware state syncs across screens immediately (v0.16.7-beta)

Firmware actions on the device Config tab (check/install RouterOS,
check/install RouterBOOT) and Firmware-page actions now invalidate the
shared react-query caches on success — devices list, device header,
firmware overview, dashboard "Things to handle", and Security Center
findings. Previously they only updated local component state, so other
screens showed stale "update available" data for up to 30s until their
own background refetch fired, even though the backend had already
persisted the new state.

Browser-verified: after a firmware check on the Config tab, /api/devices
refetches within the same second (sidebar observer) and the Devices page
fetches fresh data on mount; both screens agree immediately.
This commit is contained in:
2GT-Rich
2026-07-11 22:05:12 -07:00
parent 27f155f778
commit 79e97e14cc
6 changed files with 26 additions and 6 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mikrotik-manager-frontend",
"version": "0.16.6-beta",
"version": "0.16.7-beta",
"private": true,
"type": "module",
"scripts": {
@@ -135,10 +135,23 @@ export default function SystemConfigTab({ deviceId, device }: Props) {
}
}, [device.id]);
// Firmware state is rendered on many screens (Devices list, Firmware page,
// dashboard "Things to handle", Security Center). The backend persists the
// new state immediately, so refresh those caches whenever it changes here —
// otherwise other pages keep stale "update available" data until their own
// 30s refetch fires.
const invalidateFirmwareState = () => {
queryClient.invalidateQueries({ queryKey: ['devices'] });
queryClient.invalidateQueries({ queryKey: ['device', deviceId] });
queryClient.invalidateQueries({ queryKey: ['fw-overview'] });
queryClient.invalidateQueries({ queryKey: ['ops-insights'] });
queryClient.invalidateQueries({ queryKey: ['security-fleet'] });
};
const checkUpdateMutation = useMutation({
mutationFn: () => devicesApi.checkUpdate(deviceId),
onMutate: () => { setUpdateChecking(true); setUpdateError(''); setInstallSuccess(''); },
onSuccess: (res) => { setUpdateInfo(res.data); setUpdateChecking(false); },
onSuccess: (res) => { setUpdateInfo(res.data); setUpdateChecking(false); invalidateFirmwareState(); },
onError: (err: unknown) => {
const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error;
setUpdateError(msg || 'Failed to check for updates');
@@ -151,6 +164,7 @@ export default function SystemConfigTab({ deviceId, device }: Props) {
onSuccess: () => {
setUpdateInfo(null);
setInstallSuccess('Update installation initiated. The device will reboot shortly.');
invalidateFirmwareState();
},
onError: (err: unknown) => {
const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error;
@@ -188,7 +202,7 @@ export default function SystemConfigTab({ deviceId, device }: Props) {
const checkRouterboardMutation = useMutation({
mutationFn: () => devicesApi.checkRouterboard(deviceId),
onMutate: () => { setRbChecking(true); setRbError(''); setRbInstallSuccess(''); },
onSuccess: (res) => { setRbInfo(res.data); setRbChecking(false); },
onSuccess: (res) => { setRbInfo(res.data); setRbChecking(false); invalidateFirmwareState(); },
onError: (err: unknown) => {
const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error;
setRbError(msg || 'Failed to check RouterBOOT');
@@ -201,6 +215,7 @@ export default function SystemConfigTab({ deviceId, device }: Props) {
onSuccess: () => {
setRbInfo(null);
setRbInstallSuccess('RouterBOOT upgrade initiated. The device will reboot shortly.');
invalidateFirmwareState();
},
onError: (err: unknown) => {
const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error;
+5
View File
@@ -147,6 +147,11 @@ export default function FirmwarePage() {
const invalidate = () => {
qc.invalidateQueries({ queryKey: ['fw-overview'] });
qc.invalidateQueries({ queryKey: ['fw-rollouts'] });
// Firmware flags also drive the Devices page badges, dashboard insights,
// and Security Center findings — keep those screens in sync too.
qc.invalidateQueries({ queryKey: ['devices'] });
qc.invalidateQueries({ queryKey: ['ops-insights'] });
qc.invalidateQueries({ queryKey: ['security-fleet'] });
};
const checkAll = useMutation({
+1 -1
View File
@@ -1 +1 @@
export const APP_VERSION = 'v0.16.6 Beta';
export const APP_VERSION = 'v0.16.7 Beta';