'use client' import { useState } from 'react' import { useQuery } from '@tanstack/react-query' import { useSession } from 'next-auth/react' import { createCrossplaneClient } from '@/lib/crossplane-client' import { Card, CardContent, CardHeader, CardTitle } from '../ui/Card' import { Input } from '../ui/Input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select' import { Badge } from '../ui/badge' import { Button } from '../ui/Button' import { Server, Database, Network, HardDrive } from 'lucide-react' interface CrossplaneResource { apiVersion: string kind: string metadata: { name: string namespace: string uid: string creationTimestamp: string labels?: Record } spec?: any status?: any } export default function CrossplaneResourceBrowser() { const { data: session } = useSession() const [search, setSearch] = useState('') const [filterKind, setFilterKind] = useState('all') const [filterNamespace, setFilterNamespace] = useState('all') // This would need to be implemented to query Crossplane resources // For now, we'll use a placeholder that shows the structure const { data: resources = [], isLoading } = useQuery({ queryKey: ['crossplane-resources', filterKind, filterNamespace], queryFn: async () => { // In a real implementation, this would query the Kubernetes API // for Crossplane managed resources const crossplane = createCrossplaneClient(session?.accessToken as string) // Get VMs as an example try { const vms = await crossplane.getVMs() return vms.map((vm) => ({ apiVersion: process.env.NEXT_PUBLIC_CROSSPLANE_API_GROUP || 'proxmox.sankofa.nexus/v1alpha1', kind: 'ProxmoxVM', metadata: vm.metadata, spec: vm.spec, status: vm.status, })) } catch (error) { console.error('Failed to fetch Crossplane resources:', error) return [] } }, enabled: !!session, }) const getResourceIcon = (kind: string) => { if (kind.includes('VM') || kind.includes('VirtualMachine')) { return } if (kind.includes('Storage') || kind.includes('Volume')) { return } if (kind.includes('Network')) { return } if (kind.includes('Database')) { return } return } const getResourceStatusColor = (status: any) => { if (!status) return 'bg-gray-500' const state = status.state || status.phase || 'Unknown' switch (state.toLowerCase()) { case 'running': case 'ready': case 'active': return 'bg-green-500' case 'pending': case 'provisioning': return 'bg-yellow-500' case 'failed': case 'error': return 'bg-red-500' default: return 'bg-gray-500' } } const filteredResources = resources.filter((resource) => { const matchesSearch = resource.metadata.name.toLowerCase().includes(search.toLowerCase()) const matchesKind = filterKind === 'all' || resource.kind === filterKind const matchesNamespace = filterNamespace === 'all' || resource.metadata.namespace === filterNamespace return matchesSearch && matchesKind && matchesNamespace }) const uniqueKinds = Array.from(new Set(resources.map((r) => r.kind))) const uniqueNamespaces = Array.from(new Set(resources.map((r) => r.metadata.namespace))) return (

Crossplane Resources

setSearch(e.target.value)} className="flex-1" />
{isLoading ? (
) : filteredResources.length === 0 ? ( No Crossplane resources found ) : (
{filteredResources.map((resource) => (
{getResourceIcon(resource.kind)} {resource.metadata.name}
Kind: {resource.kind}
Namespace: {resource.metadata.namespace}
API Version: {resource.apiVersion}
{resource.status?.state && (
State: {resource.status.state}
)} {resource.status?.ipAddress && (
IP Address: {resource.status.ipAddress}
)} {resource.metadata.labels && Object.keys(resource.metadata.labels).length > 0 && (
Labels:
{Object.entries(resource.metadata.labels).map(([key, value]) => ( {key}={value} ))}
)}
))}
)}
) }