Add persistent deployment progress bar with minimize support.
Deployments can continue in the background via a dashboard progress bar; tables use truncation and admin migration status for cleaner layout. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useQuery, useQueries } from '@tanstack/react-query';
|
||||
import api from '@/lib/api';
|
||||
import type { Application } from '@/types';
|
||||
import { useAuthStore } from '@/lib/store';
|
||||
import { useDeployProgressStore } from '@/lib/deploy-progress-store';
|
||||
import { getAppsInProgress, isActiveBuildProgress } from '@/lib/deployment-progress';
|
||||
import { BuildProgressModal, type BuildProgress } from '@/components/build-progress-modal';
|
||||
import { DeploymentProgressBar } from '@/components/deployment-progress-bar';
|
||||
|
||||
export function DeploymentProgressManager() {
|
||||
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
||||
const pathname = usePathname();
|
||||
const { minimized, focusedAppId, minimize, expand } = useDeployProgressStore();
|
||||
|
||||
const { data: apps = [] } = useQuery<Application[]>({
|
||||
queryKey: ['applications'],
|
||||
queryFn: () => api.get('/applications').then((r) => r.data),
|
||||
enabled: isAuthenticated,
|
||||
refetchInterval: (query) => {
|
||||
const list = query.state.data ?? [];
|
||||
return getAppsInProgress(list).length > 0 ? 3000 : false;
|
||||
},
|
||||
});
|
||||
|
||||
const deployingApps = useMemo(() => getAppsInProgress(apps), [apps]);
|
||||
|
||||
const progressQueries = useQueries({
|
||||
queries: deployingApps.map((app) => ({
|
||||
queryKey: ['build-progress', app.id],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get<{ progress: BuildProgress | null }>(`/deployments/applications/${app.id}/build-progress`)
|
||||
.then((r) => r.data.progress),
|
||||
refetchInterval: 1500,
|
||||
})),
|
||||
});
|
||||
|
||||
const activeItems = useMemo(() => {
|
||||
return deployingApps
|
||||
.map((app, i) => ({
|
||||
app,
|
||||
progress: progressQueries[i]?.data ?? null,
|
||||
}))
|
||||
.filter(({ progress }) => isActiveBuildProgress(progress));
|
||||
}, [deployingApps, progressQueries]);
|
||||
|
||||
const routeAppId = pathname.match(/\/dashboard\/apps\/([^/]+)/)?.[1];
|
||||
|
||||
useEffect(() => {
|
||||
if (activeItems.length === 0) {
|
||||
useDeployProgressStore.setState({ minimized: false, focusedAppId: null });
|
||||
}
|
||||
}, [activeItems.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeItems.length === 0) return;
|
||||
const { focusedAppId: current } = useDeployProgressStore.getState();
|
||||
const ids = new Set(activeItems.map((item) => item.app.id));
|
||||
if (current && ids.has(current)) return;
|
||||
const preferred =
|
||||
(routeAppId && ids.has(routeAppId) ? routeAppId : null) ?? activeItems[0].app.id;
|
||||
useDeployProgressStore.setState({ focusedAppId: preferred });
|
||||
}, [activeItems, routeAppId]);
|
||||
|
||||
const focusedItem =
|
||||
activeItems.find((item) => item.app.id === focusedAppId) ?? activeItems[0];
|
||||
|
||||
const focusedProgress = focusedItem?.progress;
|
||||
const showModal =
|
||||
!minimized &&
|
||||
!!focusedItem &&
|
||||
!!focusedProgress &&
|
||||
isActiveBuildProgress(focusedProgress);
|
||||
|
||||
const handleMinimize = () => {
|
||||
if (focusedItem) minimize(focusedItem.app.id);
|
||||
};
|
||||
|
||||
const handleExpand = (appId: string) => {
|
||||
expand(appId);
|
||||
};
|
||||
|
||||
if (activeItems.length === 0) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{minimized && (
|
||||
<div className="sticky top-0 z-40">
|
||||
<DeploymentProgressBar items={activeItems} onExpand={handleExpand} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showModal && focusedProgress && (
|
||||
<BuildProgressModal
|
||||
appId={focusedItem.app.id}
|
||||
appName={focusedItem.app.name}
|
||||
progress={focusedProgress}
|
||||
onMinimize={handleMinimize}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user