feat(ui): Stream deployment and service action progress#76
Conversation
Starting, stopping, restarting and rebuilding a deployment or a single service showed only a spinner until the request finished, and the outcome vanished on reload. The dashboard now enqueues the action, streams its compose output live, and falls back to polling if the socket drops. The job id is remembered so reopening the page resumes an action still in flight and shows the final result. Single-service actions render their progress inline under the service, and the rebuild dialog can rebuild one service at a time. The operation dialog's status moves into a compact header indicator since the streamed output now carries the detail.
Tinted surfaces (modals, badges, status panels) stayed light in dark mode because the status colour tints were never adapted for the dark theme, and the split action menu fell back to a light hover colour. The dark theme now remaps the tint and on-tint text shades so every tinted surface reads correctly, and the menu hover uses a themed surface.
Deploying flatrun-ui with
|
| Latest commit: |
1495208
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d075a7de.flatrun-ui.pages.dev |
| Branch Preview URL: | https://feat-150-streamed-deployment.flatrun-ui.pages.dev |
Code Review SummaryThe PR successfully implements real-time progress streaming for deployment and service-level actions using WebSockets with a graceful polling fallback. It also includes critical dark-theme accessibility fixes. 🚀 Key Improvements
💡 Minor Suggestions
|
| let authed = false; | ||
| const token = localStorage.getItem("auth_token"); | ||
|
|
||
| try { |
There was a problem hiding this comment.
Initializing the WebSocket inside a try-catch block is good, but many browsers do not throw on a malformed URL; they fail asynchronously via the onerror or onclose handlers. It would be safer to explicitly handle socket.onerror to trigger the fallback.
| try { | |
| try { | |
| socket = new WebSocket(deploymentJobWsUrl(state.deploymentName, jobId)); | |
| socket.onerror = () => { | |
| if (!authed) { | |
| socket?.close(); | |
| pollUntilDone(jobId); | |
| } | |
| }; | |
| } catch { |
| fetchDeployments(); | ||
| onMounted(async () => { | ||
| await fetchDeployments(); | ||
| for (const d of deployments.value) { |
There was a problem hiding this comment.
The current implementation resumes the first job it finds and stops checking others. While the UI only supports one global operation modal at a time, this logic assumes only one job across all deployments could be active in the user's session. This is likely correct for the current UI, but break makes it impossible to detect other background tasks if the UI ever supports multi-tasking.
| for (const d of deployments.value) { | |
| for (const d of deployments.value) { | |
| await deploymentJob.resume(d.name); | |
| } |
Pairs with flatrun/agent#150 (agent side returns action progress as streamed jobs).
The dashboard now enqueues an action, streams its compose output live, and falls back to polling if the websocket drops. The job id is remembered, so reopening the page resumes an action still in flight and shows its final result. Single-service actions render progress inline under the service (triggerable from the service buttons or the actions dropdown), and the rebuild dialog can rebuild one service at a time. The operation dialog's status moved into a compact header indicator since the streamed output now carries the detail.
Also folds in a dark-mode fix (separate commit): tinted surfaces (modals, badges, status panels) stayed light in dark mode because the status colour tints were never adapted for the dark theme, and the split action menu fell back to a light hover. The dark theme now remaps the tint and on-tint text shades, fixing every tinted surface at once.