diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..338c655 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,34 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaFeatures": { "jsx": true }, + "sourceType": "module" + }, + "plugins": ["react", "react-hooks", "@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended" + ], + "settings": { + "react": { "version": "detect" } + }, + "rules": { + "react/react-in-jsx-scope": "off", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-unused-vars": "warn" + }, + "overrides": [ + { + "files": ["**/*.test.ts", "**/*.test.tsx"], + "rules": { + "@typescript-eslint/no-require-imports": "off" + } + } + ] +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9e57e30..ff4b233 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,6 +34,8 @@ jobs: test: runs-on: ubuntu-latest timeout-minutes: 10 + permissions: + checks: write steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/src/components/OverviewPage.tsx b/src/components/OverviewPage.tsx index 06ce770..f773e39 100644 --- a/src/components/OverviewPage.tsx +++ b/src/components/OverviewPage.tsx @@ -18,7 +18,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useTnsCsiContext } from '../api/TnsCsiDataContext'; import { formatAge, formatProtocol, phaseToStatus } from '../api/k8s'; import type { TnsCsiMetrics } from '../api/metrics'; -import { extractTnsCsiMetrics, fetchControllerMetrics, parsePrometheusText } from '../api/metrics'; +import { fetchControllerMetrics } from '../api/metrics'; import DriverStatusCard from './DriverStatusCard'; // --------------------------------------------------------------------------- @@ -85,6 +85,24 @@ export default function OverviewPage() { void fetchMetrics(); }, [fetchMetrics]); + const capacityByPool: Map = React.useMemo(() => { + const map = new Map(); + if (!metrics) return map; + const handleToPool = new Map(); + for (const pv of persistentVolumes) { + const handle = pv.spec.csi?.volumeHandle; + const pool = pv.spec.csi?.volumeAttributes?.['pool']; + if (handle && pool) handleToPool.set(handle, pool); + } + for (const sample of metrics.volumeCapacityBytes) { + const volumeId = sample.labels['volume_id']; + if (!volumeId) continue; + const pool = handleToPool.get(volumeId) ?? 'unknown'; + map.set(pool, (map.get(pool) ?? 0) + sample.value); + } + return map; + }, [metrics, persistentVolumes]); + if (loading) { return ; } @@ -111,27 +129,6 @@ export default function OverviewPage() { const chartData = protocolChartData(storageClasses); const totalScs = storageClasses.length; - // Capacity by pool: join volumeCapacityBytes samples (volume_id, protocol) - // with PV volumeHandle → pool name from volumeAttributes. - const capacityByPool: Map = React.useMemo(() => { - const map = new Map(); - if (!metrics) return map; - // Build lookup: volumeHandle → pool name - const handleToPool = new Map(); - for (const pv of persistentVolumes) { - const handle = pv.spec.csi?.volumeHandle; - const pool = pv.spec.csi?.volumeAttributes?.['pool']; - if (handle && pool) handleToPool.set(handle, pool); - } - for (const sample of metrics.volumeCapacityBytes) { - const volumeId = sample.labels['volume_id']; - if (!volumeId) continue; - const pool = handleToPool.get(volumeId) ?? 'unknown'; - map.set(pool, (map.get(pool) ?? 0) + sample.value); - } - return map; - }, [metrics, persistentVolumes]); - return ( <>