Skip to content

✨ Added KEDA monitoring card#1694

Closed
xonas1101 wants to merge 8 commits intokubestellar:mainfrom
xonas1101:card-keda
Closed

✨ Added KEDA monitoring card#1694
xonas1101 wants to merge 8 commits intokubestellar:mainfrom
xonas1101:card-keda

Conversation

@xonas1101
Copy link
Copy Markdown
Collaborator

📌 Fixes

Fixes kubestellar/console-marketplace#23


📝 Summary of Changes

Added KEDA monitoring card


Changes Made

  • Updated/Added KEDA monitoring card
  • Refactored ...
  • Fixed ...
  • Added tests for ...

Checklist

Please ensure the following before submitting your PR:

  • I have reviewed the project's contribution guidelines.
  • I have written unit tests for the changes (if applicable).
  • I have updated the documentation (if applicable).
  • I have tested the changes locally and ensured they work as expected.
  • My code follows the project's coding standards.

Screenshots or Logs (if applicable)


👀 Reviewer Notes

Add any special notes for the reviewer here

Copilot AI review requested due to automatic review settings March 5, 2026 15:10
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Mar 5, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented Mar 5, 2026

Deploy Preview for kubestellarconsole ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 1a810c3
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/69b39bae21599f00086c0f0e
😎 Deploy Preview https://deploy-preview-1694.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 5, 2026

👋 Hey @xonas1101 — thanks for opening this PR!

🤖 This project is developed exclusively using AI coding assistants.

Please do not attempt to code anything for this project manually.
All contributions should be authored using an AI coding tool such as:

This ensures consistency in code style, architecture patterns, test coverage,
and commit quality across the entire codebase.


This is an automated message.

@kubestellar-prow kubestellar-prow Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Mar 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 5, 2026

Welcome to KubeStellar! 🚀 Thank you for submitting this Pull Request.

Before your PR can be merged, please ensure:

DCO Sign-off - All commits must be signed off with git commit -s to certify the Developer Certificate of Origin

PR Title - Must start with an emoji: ✨ (feature), 🐛 (bug fix), 📖 (docs), 🌱 (infra/tests), ⚠️ (breaking change)

Getting Started with KubeStellar:

Contributor Resources:


🌟 Help KubeStellar Grow - We Need Adopters!

Our roadmap is driven entirely by adopter feedback. Whether you're using KubeStellar yourself or know someone who could benefit from multi-cluster Kubernetes:

📋 Take our Multi-Cluster Survey - Share your use cases and help shape our direction!


A maintainer will review your PR soon. Feel free to ask questions in the comments or on Slack!

@xonas1101 xonas1101 changed the title Added KEDA monitoring card ✨ Added KEDA monitoring card Mar 5, 2026
Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1693 ✨ Added Fluentd Card xonas1101
1692 Extract hardcoded user-facing strings to i18n translations app/copilot-swe-agent
1691 Add Auto-QA checks: color contrast, bundle analysis, large file detection, import cost app/copilot-swe-agent
1690 Standardize modal visibility patterns using useModalState hook app/copilot-swe-agent
1685 Fix swallowed errors in MissionBrowser index fetch with user-facing error messages app/copilot-swe-agent
1684 Fix memory leaks: clean up setTimeout on component unmount app/copilot-swe-agent
1683 Remove debug console.log statements from production hooks app/copilot-swe-agent
1679 [Test] PAT validation - no code changes required app/copilot-swe-agent
1676 🌱 test(ui): refactor alerts, auth, and gitops component tests to use React Testing Library mrhapile
1675 🌱 test(ui): refactor core resource dashboard tests to use React Testing Library mrhapile
1670 ✨ Added OpenFeature card xonas1101
1599 ✨ Added lima card xonas1101
1525 🌱 test: enforce structural integrity of card registry and lazy-loaded c… mrhapile

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new KEDA (Kubernetes Event-Driven Autoscaling) monitoring card to the KubeStellar Console dashboard. The card detects KEDA operator pods via the existing /api/mcp/pods backend endpoint and displays their health status, with rich demo data showing ScaledObject metrics, trigger queue depths, and replica scaling visualization. It follows the established pattern of similar status cards (CRI-O, CoreDNS, Flatcar).

Changes:

  • Added a new keda_status card with component, data fetching hook, demo data, and barrel export under web/src/components/cards/keda_status/
  • Registered the card in cardRegistry.ts (lazy import, component mapping, default width) and added a new "Orchestration" category in AddCardModal.tsx
  • Implemented live KEDA operator pod detection via label and name matching against the /api/mcp/pods endpoint

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/src/components/cards/keda_status/KedaStatus.tsx Main card component with health badge, stats grid, ScaledObject list, search, and skeleton/error/empty states
web/src/components/cards/keda_status/useKedaStatus.ts Data fetching hook using useCache, detects KEDA pods via /api/mcp/pods
web/src/components/cards/keda_status/demoData.ts Type definitions and demo data with 5 sample ScaledObjects across various triggers
web/src/components/cards/keda_status/index.ts Barrel export for the KedaStatus component
web/src/components/cards/cardRegistry.ts Registers keda_status as a lazy-loaded card with default width of 6 columns
web/src/components/dashboard/AddCardModal.tsx Adds KEDA to the card catalog under a new "Orchestration" category

Comment on lines +60 to +76
* Uses GET /api/mcp/pods to detect KEDA operator pods.
* ScaledObject CRD data is not available through the current stock API
* endpoints, so we surface pod health only in live mode.
*/
async function fetchKedaStatus(): Promise<KedaStatus> {
const resp = await fetch('/api/mcp/pods', {
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(FETCH_DEFAULT_TIMEOUT_MS),
})

if (!resp.ok) {
throw new Error(`HTTP ${resp.status}`)
}

const body: { pods?: BackendPodInfo[] } = await resp.json()
const pods = Array.isArray(body?.pods) ? body.pods : []

Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchKedaStatus function calls /api/mcp/pods without any query parameters, which fetches ALL pods across ALL clusters and namespaces. It then client-side filters for KEDA operator pods. Since KEDA typically installs in the keda-system namespace (or keda), this could transfer significantly more data than needed, especially in large clusters with thousands of pods.

The backend endpoint supports a namespace query parameter (see pkg/api/handlers/mcp.go:205). However, since KEDA can be installed in various namespaces and the pod name-based fallback detection on line 40-41 relies on scanning all pods, a namespace filter alone won't fully solve this. Consider using the labelSelector query parameter (also supported by the endpoint at line 206) with a KEDA-specific label like app.kubernetes.io/part-of=keda-operator as a first attempt, falling back to the unfiltered call if no pods are found.

Suggested change
* Uses GET /api/mcp/pods to detect KEDA operator pods.
* ScaledObject CRD data is not available through the current stock API
* endpoints, so we surface pod health only in live mode.
*/
async function fetchKedaStatus(): Promise<KedaStatus> {
const resp = await fetch('/api/mcp/pods', {
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(FETCH_DEFAULT_TIMEOUT_MS),
})
if (!resp.ok) {
throw new Error(`HTTP ${resp.status}`)
}
const body: { pods?: BackendPodInfo[] } = await resp.json()
const pods = Array.isArray(body?.pods) ? body.pods : []
* Prefer GET /api/mcp/pods with a KEDA-specific labelSelector to avoid
* fetching all pods across all clusters/namespaces. Fall back to the
* unfiltered call if no pods are found, preserving existing behavior.
* ScaledObject CRD data is not available through the current stock API
* endpoints, so we surface pod health only in live mode.
*/
async function fetchKedaStatus(): Promise<KedaStatus> {
// First, try to narrow the result set using a KEDA-specific label.
const labelSelector = encodeURIComponent('app.kubernetes.io/part-of=keda-operator')
const filteredResp = await fetch(`/api/mcp/pods?labelSelector=${labelSelector}`, {
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(FETCH_DEFAULT_TIMEOUT_MS),
})
if (!filteredResp.ok) {
throw new Error(`HTTP ${filteredResp.status}`)
}
const filteredBody: { pods?: BackendPodInfo[] } = await filteredResp.json()
let pods = Array.isArray(filteredBody?.pods) ? filteredBody.pods : []
// If no pods were returned with the label selector, fall back to the
// original behavior of querying all pods and filtering client-side.
if (pods.length === 0) {
const resp = await fetch('/api/mcp/pods', {
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(FETCH_DEFAULT_TIMEOUT_MS),
})
if (!resp.ok) {
throw new Error(`HTTP ${resp.status}`)
}
const body: { pods?: BackendPodInfo[] } = await resp.json()
pods = Array.isArray(body?.pods) ? body.pods : []
}

Copilot uses AI. Check for mistakes.
// CoreDNS card
const CoreDNSStatus = lazy(() => import('./coredns_status').then(m => ({ default: m.CoreDNSStatus })))
// KEDA autoscaler card
const KedaStatus = lazy(() => import('./keda_status').then(m => ({ default: m.KedaStatus })))
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keda_status is missing from the CARD_CHUNK_PRELOADERS map. Every other card with a standalone lazy-loaded chunk (e.g., crio_status at line 757, wasmcloud_status at line 749, flatcar_status at line 751) has a corresponding entry here. Without it, the KEDA card won't be prefetched when the dashboard mounts, causing a skeleton flash from the React.lazy() chunk load.

Add an entry like:
keda_status: () => import('./keda_status'),

Copilot uses AI. Check for mistakes.
Comment on lines +194 to +377
const { t } = useTranslation('cards')
const { data, error, showSkeleton, showEmptyState } = useKedaStatus()
const [search, setSearch] = useState('')

// Derived stats
const stats = useMemo(() => {
const objs = data.scaledObjects
return {
total: objs.length,
ready: objs.filter(o => o.status === 'ready').length,
degradedOrError: objs.filter(o => o.status === 'degraded' || o.status === 'error').length,
paused: objs.filter(o => o.status === 'paused').length,
}
}, [data.scaledObjects])

// Filtered list (local search)
const filteredObjects = useMemo(() => {
if (!search.trim()) return data.scaledObjects
const q = search.toLowerCase()
return data.scaledObjects.filter(
o =>
o.name.toLowerCase().includes(q) ||
o.namespace.toLowerCase().includes(q) ||
o.target.toLowerCase().includes(q) ||
o.triggers.some(tr => tr.type.includes(q) || tr.source.toLowerCase().includes(q)),
)
}, [data.scaledObjects, search])

// ── Loading ──────────────────────────────────────────────────────────────
if (showSkeleton) {
return (
<div className="h-full flex flex-col min-h-card gap-4">
<div className="flex items-center justify-between">
<Skeleton variant="rounded" width={120} height={28} />
<Skeleton variant="rounded" width={80} height={20} />
</div>
<SkeletonStats className="grid-cols-4" />
<Skeleton variant="rounded" height={32} />
<SkeletonList items={3} className="flex-1" />
</div>
)
}

// ── Error ─────────────────────────────────────────────────────────────────
if (error && showEmptyState) {
return (
<div className="h-full flex flex-col items-center justify-center min-h-card text-muted-foreground gap-2">
<AlertTriangle className="w-6 h-6 text-red-400" />
<p className="text-sm text-red-400">
{t('keda.fetchError', 'Failed to fetch KEDA status')}
</p>
</div>
)
}

// ── Not installed ─────────────────────────────────────────────────────────
if (data.health === 'not-installed') {
return (
<div className="h-full flex flex-col items-center justify-center min-h-card text-muted-foreground gap-2">
<Layers className="w-6 h-6 text-muted-foreground/50" />
<p className="text-sm font-medium">
{t('keda.notInstalled', 'KEDA not detected')}
</p>
<p className="text-xs text-center max-w-xs">
{t(
'keda.notInstalledHint',
'No KEDA operator pods found. Deploy KEDA to enable event-driven autoscaling.',
)}
</p>
</div>
)
}

const isHealthy = data.health === 'healthy'
const healthColorClass = isHealthy
? 'bg-green-500/15 text-green-400'
: 'bg-yellow-500/15 text-yellow-400'

return (
<div className="h-full flex flex-col min-h-card content-loaded gap-4 overflow-hidden">
{/* ── Header: health badge + operator pods + last check ── */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div
className={`flex items-center gap-2 px-3 py-1.5 rounded-full text-sm font-medium ${healthColorClass}`}
>
{isHealthy ? (
<CheckCircle className="w-4 h-4" />
) : (
<AlertTriangle className="w-4 h-4" />
)}
{isHealthy
? t('keda.healthy', 'Healthy')
: t('keda.degraded', 'Degraded')}
</div>
<span className="text-xs text-muted-foreground flex items-center gap-1">
<Server className="w-3 h-3" />
{data.operatorPods.ready}/{data.operatorPods.total}{' '}
{t('keda.operatorPods', 'pods')}
</span>
</div>
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<RefreshCw className="w-3 h-3" />
<span>{formatRelativeTime(data.lastCheckTime)}</span>
</div>
</div>

{/* ── Stats grid ── */}
{data.scaledObjects.length > 0 && (
<div className="grid grid-cols-4 gap-2">
<StatTile
icon={<TrendingUp className="w-4 h-4 text-blue-400" />}
label={t('keda.total', 'Total')}
value={stats.total + data.totalScaledJobs}
colorClass="text-blue-400"
borderClass="border-blue-500/20"
/>
<StatTile
icon={<CheckCircle className="w-4 h-4 text-green-400" />}
label={t('keda.ready', 'Ready')}
value={stats.ready}
colorClass="text-green-400"
borderClass="border-green-500/20"
/>
<StatTile
icon={<AlertTriangle className="w-4 h-4 text-red-400" />}
label={t('keda.issues', 'Issues')}
value={stats.degradedOrError}
colorClass="text-red-400"
borderClass="border-red-500/20"
/>
<StatTile
icon={<PauseCircle className="w-4 h-4 text-blue-400" />}
label={t('keda.paused', 'Paused')}
value={stats.paused}
colorClass="text-blue-400"
borderClass="border-blue-500/20"
/>
</div>
)}

{/* ── Search ── */}
{data.scaledObjects.length > 0 && (
<CardSearchInput
value={search}
onChange={setSearch}
placeholder={t('keda.searchPlaceholder', 'Search scaled objects…')}
/>
)}

{/* ── ScaledObjects list ── */}
<div className="flex-1 space-y-2 overflow-y-auto">
{filteredObjects.length > 0 ? (
filteredObjects.map(obj => (
<ScaledObjectRow key={`${obj.namespace}/${obj.name}`} obj={obj} />
))
) : data.scaledObjects.length === 0 ? (
// Live mode: operator running but no ScaledObjects available via API
<div className="flex flex-col items-center justify-center h-full text-muted-foreground gap-1 py-6">
<TrendingUp className="w-6 h-6 opacity-40" />
<p className="text-sm">{t('keda.noScaledObjects', 'Operator running')}</p>
<p className="text-xs text-center">
{t(
'keda.noScaledObjectsHint',
'ScaledObject data requires the KEDA CRD API.',
)}
</p>
</div>
) : (
<div className="flex items-center justify-center py-6 text-xs text-muted-foreground">
{t('keda.noSearchResults', 'No scaled objects match your search.')}
</div>
)}
</div>

{/* ── Footer ── */}
{data.totalScaledJobs > 0 && (
<div className="pt-2 border-t border-border/50 text-xs text-muted-foreground">
+{data.totalScaledJobs} ScaledJob{data.totalScaledJobs !== 1 ? 's' : ''}
</div>
)}
</div>
)
}
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The translation keys used in this component (e.g., keda.fetchError, keda.healthy, keda.notInstalled, keda.total, etc.) are not defined in any of the locale files (web/src/locales/*/cards.json). While the inline fallback strings prevent runtime errors, this means:

  1. The card text won't be translated for non-English locales.
  2. The card title (titles.keda_status) and description (descriptions.keda_status) used in the Add Card modal are also missing.
  3. The new categories.orchestration key is missing from the locale files.

Following the convention of other cards (e.g., crio keys at line ~2647 in web/src/locales/en/cards.json), a "keda" section should be added to each locale file with keys matching those used in t() calls, plus titles.keda_status, descriptions.keda_status, and categories.orchestration entries.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: KEDA Monitoring Card

Good work adding KEDA monitoring! A few things need fixing before merge:

Required Changes

  1. Missing locale entries — The card adds user-facing strings but doesn't add corresponding entries to web/src/locales/en/common.json. All hardcoded strings in the component need i18n keys.

  2. Missing CNCF preset registration — New CNCF ecosystem cards should be added to the CNCF preset in web/src/lib/presets.ts (or wherever presets are defined).

  3. Missing DEMO_DATA_CARDS entry — The card needs to be registered in DEMO_DATA_CARDS so it renders properly in demo mode with sample data.

  4. isDemoData wiring — If the card uses useCached* hooks, make sure to destructure isDemoData and pass it to useCardLoadingState(). Without this, the card shows demo data without the Demo badge/yellow outline.

Please address these and update the PR. Happy to re-review!

Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1693 ✨ Added Fluentd Card xonas1101
1676 🌱 test(ui): refactor alerts, auth, and gitops component tests to use React Testing Library mrhapile
1675 🌱 test(ui): refactor core resource dashboard tests to use React Testing Library mrhapile
1670 ✨ Added OpenFeature card xonas1101
1599 ✨ Added lima card xonas1101
1525 🌱 test: enforce structural integrity of card registry and lazy-loaded c… mrhapile

Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid KEDA card — well-structured with good demo data. Minor items to fix:

1. Magic number in demoData.ts:

lastCheckTime: new Date(Date.now() - 45 * 1000).toISOString()

Should be:

const DEMO_LAST_CHECK_OFFSET_MS = 45_000
lastCheckTime: new Date(Date.now() - DEMO_LAST_CHECK_OFFSET_MS).toISOString()

2. isRefreshing exposed but unused — The hook returns isRefreshing but the component never shows a refresh indicator. Either use it (e.g., spinning icon like CoreDNS does) or remove it from the return type.

3. Consider using useFormatRelativeTime hook pattern — Other cards (CRI-O, Contour, Flatcar, Thanos) define this as a hook. The KEDA card uses a bare function. Not blocking but would improve consistency.

Otherwise the card looks great — clean types, proper useCache/useCardLoadingState wiring, and honest handling of CRD API limitations.

Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on the KEDA card — the architecture, demo data, i18n (all 9 locales!), and hook wiring are well done and consistent with existing cards.

However, the branch has unresolved merge conflict markers in cardRegistry.ts that break the build. Please:

  1. Rebase onto current main and resolve all merge conflicts (the Fluentd card and several other changes were merged since this branch was created)
  2. Verify DEMO_DATA_CARDS registration is present after rebase
  3. Consider using t() for status labels in STATUS_CONFIG since translations already exist in the locale files

Once the conflicts are resolved and build passes, this should be ready to merge. Thanks!

@kubestellar-prow kubestellar-prow Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 7, 2026
@kubestellar-prow kubestellar-prow Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 7, 2026
@clubanderson
Copy link
Copy Markdown
Collaborator

PR Review — KEDA Monitoring Card

Critical Issues

1. English locale descriptions destroyed (regression)
web/src/locales/en/cards.json — The lima_status description was replaced with just "Lima" (was "Lima virtual machine instances and resource usage."), and keda_status only has "KEDA" instead of a real description. Other locales (de, es, fr, etc.) all have proper descriptions.

Fix: Restore lima_status and add a real keda_status description:

"lima_status": "Lima virtual machine instances and resource usage.",
"keda_status": "KEDA autoscaler status, scaled object metrics, and trigger queue depths."

2. Missing relative-time locale keys in all locales
The component uses keda.syncedJustNow, keda.syncedMinutesAgo, keda.syncedHoursAgo, keda.syncedDaysAgo but these keys are absent from every locale file. While defaultValue fallbacks prevent crashes, non-English users will always see English fallback text. Every other card (flatcar, contour, crio, fluentd, lima) includes these keys.

Fix: Add these 4 keys to the keda section of every locale file, following the pattern from other cards.

Important Issues

3. Trailing newline removed from en/cards.json — The diff shows \ No newline at end of file. All other locale files retain trailing newlines.

4. Unrelated change bundledfluentd_status preloader entry in cardRegistry.ts is unrelated to KEDA.

What Looks Good

  • cardRegistry.ts wiring is complete (all 4 registration points)
  • isDemoData wiring follows CLAUDE.md conventions correctly
  • No magic numbers — constants are named
  • Guard clauses protect against undefined arrays
  • Error handling uses AbortSignal.timeout() and checks resp.ok
  • All 9 locale files updated with translated keda section
  • Preset file follows standard format
  • Component structure matches established pattern

@clubanderson
Copy link
Copy Markdown
Collaborator

@xonas1101 Hey! I've posted a review above — there are 2 critical issues to address before merge, most notably the English locale descriptions being truncated (lima_status description was replaced with just "Lima" and keda_status only has "KEDA"). Please take a look when you get a chance.

@xonas1101
Copy link
Copy Markdown
Collaborator Author

/cc @clubanderson

@kubestellar-prow kubestellar-prow Bot requested a review from clubanderson March 9, 2026 15:57
@clubanderson
Copy link
Copy Markdown
Collaborator

@xonas1101 — Good KEDA card, correctly implements the isDemoFallback && !isLoading guard. Changes needed:

  1. Missing cardMetadata.ts entries (critical) — Add keda_status to both CARD_TITLES and CARD_DESCRIPTIONS in web/src/components/cards/cardMetadata.ts.

  2. English locale regression — The diff shows lima_status description was replaced with just "Lima" (losing "Lima virtual machine instances and resource usage."). Restore the original lima_status description and add a proper keda_status description.

  3. Missing isRefreshing in useCardLoadingState call — Per project card rules, isRefreshing must be passed to useCardLoadingState(). Add isRefreshing to the call.

@clubanderson clubanderson mentioned this pull request Mar 10, 2026
9 tasks
Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xonas1101 — isDemoData wiring and useCache pattern are correct. One critical data regression:

Critical

1. lima_status EN locale description destroyed + keda_status description is just "KEDA"

In web/src/locales/en/cards.json, the lima_status description was overwritten from "Lima virtual machine instances and resource usage." to just "Lima" (copy-paste error). Also keda_status description in EN is "KEDA" while all other locales have proper descriptions.

Fix: In en/cards.json descriptions section:

"lima_status": "Lima virtual machine instances and resource usage.",
"keda_status": "KEDA autoscaler status, scaled object metrics, and trigger queue depths."

Important

2. Missing cardMetadata.ts entries

No keda_status entry in CARD_TITLES or CARD_DESCRIPTIONS.
Fix: Add keda_status: 'KEDA' and keda_status: 'KEDA autoscaler status, scaled object metrics, and trigger queue depths.'

@kubestellar-prow kubestellar-prow Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 11, 2026
@kubestellar-prow kubestellar-prow Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 11, 2026
Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nearly ready — all previously flagged critical items are now addressed (cardMetadata.ts, isDemoData guard with && !isLoading, isRefreshing wiring with spin animation). The code quality is solid.

Minor remaining items (non-blocking):

  1. STATUS_CONFIG labels are hardcoded English ('Ready', 'Degraded') — locale files have keda.healthy/keda.degraded keys but STATUS_CONFIG is at module scope so can't use hooks. Consider computing labels inside the component.

  2. Duplicate useFormatRelativeTime pattern — same function exists in lima, fluentd, flatcar, and now KEDA. Consider using the shared createRelativeTimeFormatter from formatters.ts instead.

  3. Investigate the Netlify deploy failure.

/lgtm

@kubestellar-prow kubestellar-prow Bot removed the lgtm Indicates that a PR is ready to be merged. label Mar 12, 2026
@kubestellar-prow kubestellar-prow Bot requested a review from clubanderson March 12, 2026 01:11
@kubestellar-prow kubestellar-prow Bot added needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Mar 12, 2026
@kubestellar-prow kubestellar-prow Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 12, 2026
@xonas1101
Copy link
Copy Markdown
Collaborator Author

/cc @clubanderson I have performed the requested changed with Claude Opus 4.6, and rebased the PR.

@xonas1101
Copy link
Copy Markdown
Collaborator Author

@clubanderson Reviewed everything again. This should be fine now ig.

@kubestellar-prow kubestellar-prow Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 13, 2026
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
@kubestellar-prow kubestellar-prow Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 13, 2026
Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The card needs to support both demo and live data for ScaledObjects/ScaledJobs — not just operator pod detection.

Currently in live mode, useKedaStatus.ts returns scaledObjects: [] because the ScaledObject CRD data isn't queried. The card should:

  1. Query KEDA CRDs (scaledobjects.keda.sh, scaledjobs.keda.sh) in live mode to populate the ScaledObject list, trigger info, and replica counts with real cluster data
  2. Fall back to demo data when clusters aren't connected (this part already works)

Without live ScaledObject data, the card only shows pod health in live mode — the stats grid, search, ScaledObject list, and trigger details are all empty. That's the most valuable part of the card.

Please investigate how other cards query CRDs (check the backend MCP handlers) and wire up live data for ScaledObjects. The demo data types and component rendering are already solid — it's just the data fetching layer that needs the live path.

@clubanderson
Copy link
Copy Markdown
Collaborator

A few more items:

  1. Rebase needed — the branch has merge conflicts in cardRegistry.ts that break the Build Frontend CI. Please rebase onto current main and resolve conflicts.

  2. Scope creep in AddCardModal.tsx — this PR adds a Runtime category with wasmcloud_status, crio_status, lima_status entries. Those don't belong in a KEDA card PR — please remove them and keep this PR scoped to KEDA only.

  3. useFormatRelativeTime duplication (non-blocking) — the same helper function is duplicated across lima, fluentd, flatcar, and now KEDA cards. Consider using the shared createRelativeTimeFormatter from formatters.ts instead. Not blocking merge but would reduce duplication.

@clubanderson clubanderson mentioned this pull request Mar 13, 2026
9 tasks
@clubanderson
Copy link
Copy Markdown
Collaborator

Consolidated Review — All Remaining Items (Final)

@xonas1101 — This PR has been through many review rounds. Here's one consolidated checklist of everything that still needs fixing. Please address all of these in a single push.

Critical

  • Live data is hollow — In live mode, scaledObjects: [] because ScaledObject CRD data isn't queried. You need to query KEDA CRDs (scaledobjects.keda.sh, scaledjobs.keda.sh) to populate the ScaledObject list, trigger info, and replica counts with real cluster data. Without this, the stats grid, search, ScaledObject list, and trigger details are all empty in live mode.
  • Rebase needed — Build Frontend is failing. Rebase onto current main.

Must Fix

  • Hardcoded "ScaledJob" string in footer — The footer renders a non-localized string. Add a locale key.
  • Only first trigger displayedScaledObjectRow renders obj.triggers[0] silently ignoring additional triggers. Add a visual indicator (e.g., +2 more) when multiple triggers exist.
  • useFormatRelativeTime hardcodes English'just now', 'm ago', etc. should use i18n translation keys via useTranslation('cards').
  • STATUS_CONFIG labels are hardcoded English'Ready', 'Degraded' should use locale keys. Since STATUS_CONFIG is at module scope, compute labels inside the component.

This applies to issue kubestellar/console-marketplace#23. @clubanderson — as the issue author, please comment "approve" once the above items are addressed, or add comments on how to improve the PR.

clubanderson added a commit that referenced this pull request Mar 13, 2026
Add KEDA card that detects operator pods via label matching with
a fast label-filtered query followed by unfiltered fallback.
Shows broker health, scaled object stats with replica bars,
consumer group lag, and trigger details.

Review fixes:
- Remove scope creep: revert all shared file changes for other cards
- Only register keda_status in card registry and AddCardModal
- Add isRefreshing override to CardLoadingStateOptions
- Add i18n keys for queue/target labels
- Add Orchestration category to card catalog

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
clubanderson added a commit that referenced this pull request Mar 13, 2026
Add KEDA card that detects operator pods via label matching with
a fast label-filtered query followed by unfiltered fallback.
Shows broker health, scaled object stats with replica bars,
consumer group lag, and trigger details.

Review fixes:
- Remove scope creep: revert all shared file changes for other cards
- Only register keda_status in card registry and AddCardModal
- Add isRefreshing override to CardLoadingStateOptions
- Add i18n keys for queue/target labels
- Add Orchestration category to card catalog

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
clubanderson added a commit that referenced this pull request Mar 13, 2026
* Added KEDA monitoring card

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* Addressed feedback

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* Addressed feedback

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* Addressed feedback

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* Addressed feedback

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* Addressed feedback

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* Addressed Feedback

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* Fixed errors

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>

* feat: add KEDA autoscaler status card (supersedes #1694)

Add KEDA card that detects operator pods via label matching with
a fast label-filtered query followed by unfiltered fallback.
Shows broker health, scaled object stats with replica bars,
consumer group lag, and trigger details.

Review fixes:
- Remove scope creep: revert all shared file changes for other cards
- Only register keda_status in card registry and AddCardModal
- Add isRefreshing override to CardLoadingStateOptions
- Add i18n keys for queue/target labels
- Add Orchestration category to card catalog

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

* 🐛 Strip scope creep, add null guards to KEDA card

- Reset unrelated files (go.mod, go.sum, analytics_proxy, auth,
  feedback, middleware/auth, server.go, netlify functions) to main
- Add null guards on data.scaledObjects, data.operatorPods, and
  obj.triggers throughout KedaStatus.tsx to prevent crashes when
  API/cache returns undefined
- Guard hasAnyData in useKedaStatus.ts with optional chaining

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

---------

Signed-off-by: xonas1101 <aarushsingh1305@gmail.com>
Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Co-authored-by: xonas1101 <aarushsingh1305@gmail.com>
@clubanderson
Copy link
Copy Markdown
Collaborator

Superseded by the fix PR that was merged. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has signed the DCO. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

card: Implement KEDA monitoring card

3 participants