@@ -18,6 +18,7 @@ import DashboardSecurityOverviewWidget from './dashboard/components/DashboardSec
1818import DashboardUpdateBreakdownWidget from ' ./dashboard/components/DashboardUpdateBreakdownWidget.vue' ;
1919import {
2020 DASHBOARD_WIDGET_META ,
21+ type DashboardUpdateSequenceEntry ,
2122 type DashboardWidgetId ,
2223 type RecentUpdateRow ,
2324} from ' ./dashboard/dashboardTypes' ;
@@ -48,6 +49,7 @@ const dashboardUpdateError = ref<string | null>(null);
4849const dashboardPendingUpdateRows = ref <Map <string , { row: RecentUpdateRow ; startedAt: number }>>(
4950 new Map (),
5051);
52+ const dashboardUpdateSequence = ref <Map <string , DashboardUpdateSequenceEntry >>(new Map ());
5153const dashboardPendingUpdatePollTimer = ref <ReturnType <typeof setTimeout > | null >(null );
5254const dashboardPendingUpdatePollInFlight = ref (false );
5355const dashboardPendingUpdatePollDelayMs = ref (2_000 );
@@ -193,7 +195,10 @@ const {
193195});
194196
195197const pendingUpdates = computed (() =>
196- recentUpdates .value .filter ((r ) => r .status === ' pending' && ! r .blocked ),
198+ recentUpdates .value .filter (
199+ (row ) =>
200+ row .status === ' pending' && ! row .blocked && ! dashboardUpdateSequence .value .has (row .name ),
201+ ),
197202);
198203
199204const displayRecentUpdates = computed <RecentUpdateRow []>(() => {
@@ -214,8 +219,59 @@ function stopDashboardPendingUpdatePolling() {
214219 dashboardPendingUpdatePollDelayMs .value = DASHBOARD_PENDING_UPDATE_POLL_INTERVAL_MS ;
215220}
216221
222+ function hasDashboardTrackedUpdates() {
223+ return dashboardPendingUpdateRows .value .size > 0 || dashboardUpdateSequence .value .size > 0 ;
224+ }
225+
226+ function getVisibleDashboardTrackedUpdateNames() {
227+ const names = new Set (recentUpdates .value .map ((row ) => row .name ));
228+ for (const name of dashboardPendingUpdateRows .value .keys ()) {
229+ names .add (name );
230+ }
231+ return names ;
232+ }
233+
234+ function startDashboardPendingUpdateTracking() {
235+ if (! hasDashboardTrackedUpdates ()) {
236+ stopDashboardPendingUpdatePolling ();
237+ return ;
238+ }
239+ stopDashboardPendingUpdatePolling ();
240+ dashboardPendingUpdatePollDelayMs .value = DASHBOARD_PENDING_UPDATE_POLL_INTERVAL_MS ;
241+ startDashboardPendingUpdatePolling ();
242+ }
243+
244+ function syncDashboardUpdateSequenceValue(rowNames : string [], acceptedRowNames : string []) {
245+ const next = new Map (dashboardUpdateSequence .value );
246+ for (const name of rowNames ) {
247+ next .delete (name );
248+ }
249+ for (const [index, name] of acceptedRowNames .entries ()) {
250+ next .set (name , {
251+ position: index + 1 ,
252+ total: acceptedRowNames .length ,
253+ });
254+ }
255+ dashboardUpdateSequence .value = next ;
256+ }
257+
258+ function pruneDashboardUpdateSequence() {
259+ const visibleNames = getVisibleDashboardTrackedUpdateNames ();
260+ if (dashboardUpdateAllInProgress .value && visibleNames .size === 0 ) {
261+ return ;
262+ }
263+ const next = new Map (dashboardUpdateSequence .value );
264+ for (const name of dashboardUpdateSequence .value .keys ()) {
265+ if (! visibleNames .has (name )) {
266+ next .delete (name );
267+ }
268+ }
269+ dashboardUpdateSequence .value = next ;
270+ }
271+
217272function clearDashboardPendingUpdateRow(name : string ) {
218273 dashboardPendingUpdateRows .value .delete (name );
274+ pruneDashboardUpdateSequence ();
219275}
220276
221277function pruneDashboardPendingUpdateRows(now : number = Date .now ()) {
@@ -228,7 +284,8 @@ function pruneDashboardPendingUpdateRows(now: number = Date.now()) {
228284 clearDashboardPendingUpdateRow (name );
229285 }
230286 }
231- if (dashboardPendingUpdateRows .value .size === 0 ) {
287+ pruneDashboardUpdateSequence ();
288+ if (! hasDashboardTrackedUpdates ()) {
232289 stopDashboardPendingUpdatePolling ();
233290 }
234291}
@@ -243,9 +300,9 @@ async function pollDashboardPendingUpdateRows() {
243300 } finally {
244301 pruneDashboardPendingUpdateRows ();
245302 dashboardPendingUpdatePollInFlight .value = false ;
246- if (dashboardPendingUpdateRows . value . size > 0 ) {
303+ if (hasDashboardTrackedUpdates () ) {
247304 dashboardPendingUpdatePollDelayMs .value =
248- pendingUpdates .value .length > 0
305+ dashboardUpdateSequence . value . size > 0 || pendingUpdates .value .length > 0
249306 ? DASHBOARD_PENDING_UPDATE_POLL_INTERVAL_MS
250307 : Math .min (
251308 dashboardPendingUpdatePollDelayMs .value * 2 ,
@@ -281,12 +338,8 @@ function capturePendingDashboardRows(rows: RecentUpdateRow[]) {
281338 startedAt: existing ?.startedAt ?? Date .now (),
282339 });
283340 }
284- if (dashboardPendingUpdateRows .value .size > 0 ) {
285- stopDashboardPendingUpdatePolling ();
286- dashboardPendingUpdatePollDelayMs .value = DASHBOARD_PENDING_UPDATE_POLL_INTERVAL_MS ;
287- startDashboardPendingUpdatePolling ();
288- }
289341 pruneDashboardPendingUpdateRows ();
342+ startDashboardPendingUpdateTracking ();
290343}
291344
292345// Stat card data lookup by widget id
@@ -298,6 +351,7 @@ const statById = computed(() => {
298351
299352watch (containers , () => {
300353 pruneDashboardPendingUpdateRows ();
354+ pruneDashboardUpdateSequence ();
301355});
302356
303357onUnmounted (() => {
@@ -378,17 +432,31 @@ function confirmDashboardUpdateAll() {
378432 const pendingRowsSnapshot = pendingUpdates .value .filter ((row ) => ! row .blocked );
379433 dashboardUpdateAllInProgress .value = true ;
380434 dashboardUpdateError .value = null ;
435+ const snapshotRowNames = pendingRowsSnapshot .map ((row ) => row .name );
436+ let acceptedRowNames = [... snapshotRowNames ];
437+ syncDashboardUpdateSequenceValue (snapshotRowNames , acceptedRowNames );
438+ startDashboardPendingUpdateTracking ();
381439 try {
382- const updateResults = await Promise .allSettled (
383- pendingRowsSnapshot .map ((row ) => updateContainer (row .id )),
384- );
385- const successfulRows = pendingRowsSnapshot .filter (
386- (_ , index ) => updateResults [index ]?.status === ' fulfilled' ,
387- );
388- const staleRows = pendingRowsSnapshot .filter ((_ , index ) => {
389- const result = updateResults [index ];
390- return result ?.status === ' rejected' && isStaleDashboardUpdateError (result .reason );
391- });
440+ const successfulRows: RecentUpdateRow [] = [];
441+ const staleRows: RecentUpdateRow [] = [];
442+ let firstRejectedUpdate: unknown ;
443+
444+ for (const row of pendingRowsSnapshot ) {
445+ try {
446+ await updateContainer (row .id );
447+ successfulRows .push (row );
448+ } catch (e : unknown ) {
449+ acceptedRowNames = acceptedRowNames .filter ((name ) => name !== row .name );
450+ syncDashboardUpdateSequenceValue (snapshotRowNames , acceptedRowNames );
451+
452+ if (isStaleDashboardUpdateError (e )) {
453+ staleRows .push (row );
454+ } else if (! firstRejectedUpdate ) {
455+ firstRejectedUpdate = e ;
456+ }
457+ }
458+ }
459+
392460 await fetchDashboardData ();
393461 capturePendingDashboardRows (successfulRows );
394462 if (successfulRows .length > 0 ) {
@@ -401,16 +469,17 @@ function confirmDashboardUpdateAll() {
401469 : ` ${formatContainerCount (staleRows .length )} already up to date ` ,
402470 );
403471 }
404- const firstRejectedUpdate = updateResults .find (
405- (result ) => result .status === ' rejected' && ! isStaleDashboardUpdateError (result .reason ),
406- );
407- if (firstRejectedUpdate ?.status === ' rejected' ) {
472+ if (firstRejectedUpdate ) {
408473 dashboardUpdateError .value = errorMessage (
409- firstRejectedUpdate . reason ,
474+ firstRejectedUpdate ,
410475 ' Failed to update all containers' ,
411476 );
412477 }
413478 } finally {
479+ if (acceptedRowNames .length === 0 ) {
480+ syncDashboardUpdateSequenceValue (snapshotRowNames , []);
481+ pruneDashboardUpdateSequence ();
482+ }
414483 dashboardUpdateAllInProgress .value = false ;
415484 }
416485 },
@@ -543,6 +612,7 @@ function confirmDashboardUpdateAll() {
543612 :dashboard-update-error =" dashboardUpdateError"
544613 :dashboard-update-in-progress =" dashboardUpdateInProgress"
545614 :dashboard-update-all-in-progress =" dashboardUpdateAllInProgress"
615+ :dashboard-update-sequence =" dashboardUpdateSequence"
546616 :get-update-kind-color =" getUpdateKindColor"
547617 :get-update-kind-icon =" getUpdateKindIcon"
548618 :get-update-kind-muted-color =" getUpdateKindMutedColor"
0 commit comments