import React, { useCallback, useEffect, useState } from "react"; import { Alert, Box, Button, Chip, LinearProgress, Paper, Stack, Typography, } from "@mui/material"; import { api, getApiErrorMessage } from "../api"; import { useI18n } from "../i18n/I18nProvider"; import { notificationDateLabel, UserNotification } from "../notifications"; type Operation = { id: string; taskType: string; status: string; subjectType?: string | null; createdAtUtc: string; completedAtUtc?: string | null; cancellationRequestedAtUtc?: string | null; progressStage?: string | null; progressPercent?: number | null; failureCategory?: string | null; canCancel: boolean; canRetry: boolean; }; const statusLabel = (value: string) => value.replaceAll("_", " "); export default function OperationsPage() { const { language, t } = useI18n(); const [operations, setOperations] = useState([]); const [notifications, setNotifications] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [busyKey, setBusyKey] = useState(null); const load = useCallback(async (showLoading = false) => { if (showLoading) setLoading(true); try { const [operationResponse, notificationResponse] = await Promise.all([ api.get("/operations?limit=50"), api.get("/notifications?limit=50"), ]); setOperations(operationResponse.data ?? []); setNotifications(notificationResponse.data ?? []); setError(null); } catch (requestError) { setError(getApiErrorMessage(requestError, t("operationsLoadFailed"))); } finally { setLoading(false); } }, [language]); useEffect(() => { void load(); const interval = window.setInterval(() => void load(), 15000); return () => window.clearInterval(interval); }, [load]); const runAction = async (key: string, action: () => Promise, notificationsChanged = false) => { if (busyKey) return; setBusyKey(key); try { await action(); await load(); if (notificationsChanged) window.dispatchEvent(new Event("notifications-changed")); } catch (requestError) { setError(getApiErrorMessage(requestError, t("operationsActionFailed"))); } finally { setBusyKey(null); } }; return ( {t("operationsSubtitle")} {error ? {error} : null} {loading ? : null} {t("notifications")} {notifications.length === 0 && !loading ? {t("operationsNoNotifications")} : null} {notifications.map((notification) => ( {notification.title} {notification.message} {notificationDateLabel(notification.createdAtUtc, language === "nb" ? "nb-NO" : "en")} {!notification.readAtUtc ? ( ) : null} ))} {t("operations")} {operations.length === 0 && !loading ? {t("operationsEmpty")} : null} {operations.map((operation) => ( {operation.taskType} {t("operationsStarted", { date: notificationDateLabel(operation.createdAtUtc, language === "nb" ? "nb-NO" : "en") })} {operation.progressStage ? {operation.progressStage} : null} {operation.progressPercent != null ? : null} {operation.cancellationRequestedAtUtc ? {t("operationsCancellationRequested")} : null} {operation.failureCategory ? {t("operationsFailed", { category: statusLabel(operation.failureCategory) })} : null} {operation.canCancel ? ( ) : null} {operation.canRetry ? ( ) : null} ))} ); }