feat(ui): metrikleri odaklanma ve gezinme durumunda yenile

Metriklerin güncel kalması için pencere odaklanıldığında ve sayfa
gezinildiğinde verilerin yeniden yüklenmesi eklendi. Backend deployment
servisinde tip tanımları güncellendi.
This commit is contained in:
2026-02-02 19:03:41 +00:00
parent 003ddfcbd1
commit a117275efe
4 changed files with 37 additions and 5 deletions

View File

@@ -88,6 +88,10 @@ router.get("/env-examples", async (req, res) => {
router.get("/metrics/summary", async (req, res) => { router.get("/metrics/summary", async (req, res) => {
authMiddleware(req, res, async () => { authMiddleware(req, res, async () => {
const deploymentCount = await DeploymentProject.countDocuments();
if (deploymentCount === 0) {
await deploymentService.bootstrapFromFilesystem();
}
const since = new Date(); const since = new Date();
since.setDate(since.getDate() - 7); since.setDate(since.getDate() - 7);

View File

@@ -30,6 +30,10 @@ router.get("/", async (_req, res) => {
}); });
router.get("/metrics/summary", async (_req, res) => { router.get("/metrics/summary", async (_req, res) => {
const jobCount = await Job.countDocuments();
if (jobCount === 0) {
await jobService.bootstrapFromFilesystem();
}
const since = new Date(); const since = new Date();
since.setDate(since.getDate() - 7); since.setDate(since.getDate() - 7);

View File

@@ -385,7 +385,7 @@ class DeploymentService {
this.io.except(`deployment:${deploymentId}`).emit("deployment:log", { deploymentId, line }); this.io.except(`deployment:${deploymentId}`).emit("deployment:log", { deploymentId, line });
} }
private emitRun(deploymentId: string, run: DeploymentRun) { private emitRun(deploymentId: string, run: DeploymentRunDocument) {
if (!this.io) return; if (!this.io) return;
this.io.to(`deployment:${deploymentId}`).emit("deployment:run", { this.io.to(`deployment:${deploymentId}`).emit("deployment:run", {
deploymentId, deploymentId,

View File

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { import {
Line, Line,
LineChart, LineChart,
@@ -18,6 +18,7 @@ import { JobStatusBadge } from "../components/JobStatusBadge";
import { RepoIcon } from "../components/RepoIcon"; import { RepoIcon } from "../components/RepoIcon";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faClockRotateLeft, faListCheck, faFlaskVial, faRocket } from "@fortawesome/free-solid-svg-icons"; import { faClockRotateLeft, faListCheck, faFlaskVial, faRocket } from "@fortawesome/free-solid-svg-icons";
import { useAuth } from "../providers/auth-provider";
function formatDuration(ms?: number) { function formatDuration(ms?: number) {
if (!ms || Number.isNaN(ms)) return "-"; if (!ms || Number.isNaN(ms)) return "-";
@@ -41,9 +42,14 @@ export function HomePage() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const { jobStreams } = useLiveData(); const { jobStreams } = useLiveData();
const { token } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation();
useEffect(() => { const loadMetrics = useCallback(() => {
if (!token) return;
setLoading(true);
setError(null);
Promise.allSettled([fetchJobMetrics(), fetchDeploymentMetrics()]) Promise.allSettled([fetchJobMetrics(), fetchDeploymentMetrics()])
.then(([jobResult, deployResult]) => { .then(([jobResult, deployResult]) => {
if (jobResult.status === "fulfilled") { if (jobResult.status === "fulfilled") {
@@ -65,7 +71,25 @@ export function HomePage() {
} }
}) })
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, []); }, [token]);
useEffect(() => {
loadMetrics();
}, [loadMetrics, location.key]);
useEffect(() => {
const handleFocus = () => {
if (document.visibilityState === "visible") {
loadMetrics();
}
};
window.addEventListener("focus", handleFocus);
document.addEventListener("visibilitychange", handleFocus);
return () => {
window.removeEventListener("focus", handleFocus);
document.removeEventListener("visibilitychange", handleFocus);
};
}, [loadMetrics]);
const chartData = useMemo(() => { const chartData = useMemo(() => {
if (!metrics) { if (!metrics) {