Skip to content

Conversation

@filipevrevez
Copy link

@filipevrevez filipevrevez commented Dec 5, 2025

This pull request introduces the initial setup for the Radius Headlamp plugin, which provides visualization and management capabilities for Radius applications within the Headlamp Kubernetes UI. The changes include configuration files, documentation, core React components for listing and viewing application details, a status label component for Radius provisioning states, and plugin settings management.

Core plugin components:

  • Implemented Applications component (src/applications/index.tsx) to list all Radius applications in a table with links to details and environment views.
  • Implemented ApplicationDetailView component (src/applications/ApplicationDetailView.tsx) to display detailed information about a selected application, including resources and system data.

Supporting utilities and components:

  • Added RadiusStatusLabel component (src/components/RadiusStatusLabel.tsx) to map and display Radius provisioning states using Headlamp's status label styles.
  • Added getUCPApiVersion utility (src/config.ts) to manage the UCP API version configuration for the plugin, with fallback to a default value.

There's still some refinement to be done on a future version:

  • Change logos to the official Radius ones
  • Add view for Custom Resources
  • Change radius resources view to the sidebar

Copilot AI review requested due to automatic review settings December 5, 2025 12:31
Copy link

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 pull request introduces a comprehensive Headlamp plugin for Radius, a cloud-native application platform. The plugin provides visualization and management capabilities for Radius resources through the Kubernetes API Aggregation Layer, adding new views for applications, environments, and other Radius resources to the Headlamp UI.

Key Changes

  • Core plugin infrastructure: Setup with TypeScript configuration, plugin registration, sidebar navigation, and routing system for multiple views
  • API integration: Implementation of custom hooks to fetch Radius data via UCP (Universal Control Plane) API endpoints through Kubernetes API Aggregation
  • UI components: Five main views (Overview dashboard, Applications list/detail, Environments list/detail, Resources list/detail) with tables, status indicators, and navigation links

Reviewed changes

Copilot reviewed 16 out of 19 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
radius/package.json Package configuration with dependencies and build scripts for the Headlamp plugin
radius/tsconfig.json TypeScript configuration extending Headlamp plugin defaults
radius/.gitignore Standard ignore patterns for Node.js projects and Headlamp plugin artifacts
radius/README.md Documentation describing the plugin, installation instructions, and resource links
radius/src/index.tsx Main plugin entry point with route registration, sidebar configuration, and settings component
radius/src/config.ts Configuration utility for retrieving UCP API version from plugin settings
radius/src/logo.svg Radius logo SVG for branding in the UI
radius/src/headlamp-plugin.d.ts TypeScript type definitions reference for Headlamp plugin types
radius/src/models/radius.ts Core data models, type definitions, and custom hooks for fetching Radius resources via UCP API
radius/src/components/RadiusStatusLabel.tsx Reusable status label component mapping Radius provisioning states to Headlamp status styles
radius/src/applications/index.tsx Applications list view with table displaying all Radius applications
radius/src/applications/ApplicationDetailView.tsx Detailed view for individual applications showing properties, resources, and system metadata
radius/src/environments/index.tsx Environments list view with table showing all Radius environments
radius/src/environments/EnvironmentDetailView.tsx Detailed environment view with tabs for overview, recipes, and associated resources
radius/src/resources/index.tsx Resources list view aggregating all Radius resource types across providers
radius/src/resources/ResourceDetailView.tsx Detailed view for individual resources with general info and Kubernetes resource links
radius/src/radius-overview/index.tsx Dashboard overview with status charts and summary tables for applications and environments
README.md Updated root README to include the Radius plugin in the official plugins list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@skoeva skoeva left a comment

Choose a reason for hiding this comment

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

Hi, nice work! The CI is complaining about signed commits, for that just squash the Copilot changes into your commit(s) and make sure they're signed

filipevrevez and others added 5 commits December 11, 2025 11:54
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
Copy link

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

Copilot reviewed 16 out of 19 changed files in this pull request and generated 24 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +339 to +340
Cell: ({ row }: any) => <Chip label={row.original.templateKind} size="small" />,
},
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

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

Type casting to 'any' is used for row parameters in table Cell renderers. This bypasses TypeScript's type checking and could lead to runtime errors. Consider defining proper types for better type safety.

Copilot uses AI. Check for mistakes.
Comment on lines 91 to 103
let percentages = [
Math.round((status.success / total) * 100),
Math.round((status.failed / total) * 100),
Math.round((status.processing / total) * 100),
Math.round((status.suspended / total) * 100),
];

// Adjust to ensure total is 100% by modifying the largest percentage
const sum = percentages.reduce((a, b) => a + b, 0);
if (sum !== 100) {
const idx = percentages.indexOf(Math.max(...percentages));
percentages[idx] += 100 - sum;
}
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

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

The percentage adjustment logic has a potential bug when all values are 0. When all percentages are 0, Math.max(...percentages) returns 0, indexOf(0) returns the first index (0), and percentages[0] becomes 100. This means a chart with no resources would show 100% in the first category (success). Consider checking if the total resource count is 0 before calculating percentages.

Copilot uses AI. Check for mistakes.
Comment on lines 116 to 126
fill: '#DC7501',
},
{
name: 'processing',
value: processingPercentage,
fill: '#2196F3',
},
{
name: 'suspended',
value: suspendedPercentage,
fill: '#FDE100',
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

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

Hardcoded hex color values '#DC7501', '#2196F3', and '#FDE100' are used for status visualization. These hardcoded colors may not respect the user's theme (light/dark mode) and could have poor contrast in some themes. Consider using theme palette colors or defining these colors in a theme-aware way.

Suggested change
fill: '#DC7501',
},
{
name: 'processing',
value: processingPercentage,
fill: '#2196F3',
},
{
name: 'suspended',
value: suspendedPercentage,
fill: '#FDE100',
fill: theme.palette.error.main,
},
{
name: 'processing',
value: processingPercentage,
fill: theme.palette.info.main,
},
{
name: 'suspended',
value: suspendedPercentage,
fill: theme.palette.warning.light,

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +102
const computeStatus = resource.properties.status?.compute as any;
const computeNamespace = computeStatus?.namespace;

let k8sResourceName: string | undefined;
let k8sResourceType: 'deployment' | 'pod' | undefined;

// Check for resource property which might contain the K8s resource name
if (isContainer && resource.properties.resource) {
k8sResourceName = (resource.properties.resource as Record<string, any>)?.name || resource.name;
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

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

Type casting to 'any' is used for row parameters, computeStatus, and resource properties. This bypasses TypeScript's type checking and could lead to runtime errors. Consider defining proper types for better type safety.

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +54
function TabPanel({ children, value, index }: TabPanelProps) {
return (
<div role="tabpanel" hidden={value !== index}>
{value === index && <Box sx={{ p: 3 }}>{children}</Box>}
</div>
);
}
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

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

The TabPanel component is missing documentation. Consider adding a JSDoc comment explaining its purpose and parameters for better code maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +104
function ApplicationContent({
application,
environmentName,
}: Readonly<{
application: UCPApplication;
environmentName: string;
}>) {
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

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

The ApplicationContent component lacks documentation. Consider adding a JSDoc comment explaining its purpose and why it's separated from the main ApplicationDetailView component.

Copilot uses AI. Check for mistakes.
Comment on lines +146 to +191
Cell: ({ row }: any) => {
const resourceName = row.original.name;
return (
<Link routeName="resource-detail" params={{ resourceName }}>
{resourceName}
</Link>
);
},
},
{
header: 'Resource Group',
accessorKey: 'resourceGroup',
},
{
header: 'Type',
accessorKey: 'type',
},
{
header: 'Application',
accessorKey: 'application',
Cell: ({ row }: any) => {
const appName = row.original.application;
if (appName === 'N/A' || appName === '-') {
return <Typography variant="body2">{appName}</Typography>;
}
return (
<Link routeName="application-detail" params={{ applicationName: appName }}>
{appName}
</Link>
);
},
},
{
header: 'Environment',
accessorKey: 'environment',
Cell: ({ row }: any) => {
const envName = row.original.environment;
if (envName === 'N/A' || envName === '-') {
return <Typography variant="body2">{envName}</Typography>;
}
return (
<Link routeName="environment-detail" params={{ environmentName: envName }}>
{envName}
</Link>
);
},
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

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

Type casting to 'any' is used for row parameters and other type assertions. This bypasses TypeScript's type checking and could lead to runtime errors. Consider defining proper types or using the types provided by the components.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@skoeva skoeva left a comment

Choose a reason for hiding this comment

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

Thanks for making the changes, it would also be good to use atomic commits whenever possible (as mentioned here)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants