feat(kubernetes): implement Phase 1 & 2: resource discovery UIs and advanced features
- Add kubernetesStore.ts with Zustand state management (clusters, namespaces, resources, terminals, search, bulk selection)
- Create 15 resource list components (Secret, ReplicaSet, Job, CronJob, Ingress, PVC, PV, ServiceAccount, Role, ClusterRole, RoleBinding, ClusterRoleBinding, HPA, Node, Event, ConfigMap)
- Add advanced components (Terminal, YamlEditor, MetricsChart, SearchBar, ContextSwitcher, ApplicationView, PodDetail)
- Update KubernetesPage.tsx to integrate kubernetesStore and add cluster management
- Add ContextInfo and ResourceInfo types to tauriCommands.ts
- All components pass ESLint, TypeScript, and pass 114 tests
- Build successful
2026-06-07 15:24:26 +00:00
|
|
|
import { create } from "zustand";
|
2026-06-07 17:29:39 +00:00
|
|
|
import type { ClusterInfo, ContextInfo, ResourceInfo, KubeconfigInfo } from "@/lib/tauriCommands";
|
feat(kubernetes): implement Phase 1 & 2: resource discovery UIs and advanced features
- Add kubernetesStore.ts with Zustand state management (clusters, namespaces, resources, terminals, search, bulk selection)
- Create 15 resource list components (Secret, ReplicaSet, Job, CronJob, Ingress, PVC, PV, ServiceAccount, Role, ClusterRole, RoleBinding, ClusterRoleBinding, HPA, Node, Event, ConfigMap)
- Add advanced components (Terminal, YamlEditor, MetricsChart, SearchBar, ContextSwitcher, ApplicationView, PodDetail)
- Update KubernetesPage.tsx to integrate kubernetesStore and add cluster management
- Add ContextInfo and ResourceInfo types to tauriCommands.ts
- All components pass ESLint, TypeScript, and pass 114 tests
- Build successful
2026-06-07 15:24:26 +00:00
|
|
|
|
|
|
|
|
export type ResourceType =
|
|
|
|
|
| "pods"
|
|
|
|
|
| "services"
|
|
|
|
|
| "deployments"
|
|
|
|
|
| "statefulsets"
|
|
|
|
|
| "daemonsets"
|
|
|
|
|
| "replicasets"
|
|
|
|
|
| "jobs"
|
|
|
|
|
| "cronjobs"
|
|
|
|
|
| "ingresses"
|
|
|
|
|
| "persistentvolumes"
|
|
|
|
|
| "persistentvolumeclaims"
|
|
|
|
|
| "configmaps"
|
|
|
|
|
| "secrets"
|
|
|
|
|
| "serviceaccounts"
|
|
|
|
|
| "roles"
|
|
|
|
|
| "clusterroles"
|
|
|
|
|
| "rolebindings"
|
|
|
|
|
| "clusterrolebindings"
|
|
|
|
|
| "nodes"
|
|
|
|
|
| "events"
|
|
|
|
|
| "hpas";
|
|
|
|
|
|
|
|
|
|
interface KubernetesState {
|
|
|
|
|
// Selection state
|
|
|
|
|
selectedClusterId: string | null;
|
|
|
|
|
selectedNamespace: string;
|
|
|
|
|
|
|
|
|
|
// Data state
|
|
|
|
|
clusters: ClusterInfo[];
|
|
|
|
|
contexts: ContextInfo[];
|
|
|
|
|
namespaces: Record<string, string[]>; // clusterId -> [namespaces]
|
|
|
|
|
|
|
|
|
|
// Loaded resources tracking
|
|
|
|
|
loadedResources: Set<ResourceType>;
|
|
|
|
|
|
|
|
|
|
// Terminal sessions
|
|
|
|
|
terminalSessions: Record<string, {
|
|
|
|
|
id: string;
|
|
|
|
|
clusterId: string;
|
|
|
|
|
namespace: string;
|
|
|
|
|
pod: string;
|
|
|
|
|
container: string;
|
|
|
|
|
command: string
|
|
|
|
|
}>;
|
|
|
|
|
nextTerminalId: number;
|
|
|
|
|
|
|
|
|
|
// Search state
|
|
|
|
|
globalSearchQuery: string;
|
|
|
|
|
searchResults: Record<ResourceType, ResourceInfo[]>;
|
|
|
|
|
|
|
|
|
|
// Bulk selection
|
|
|
|
|
bulkSelection: Record<ResourceType, string[]>; // resourceType -> [resourceNames]
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
setSelectedCluster: (clusterId: string) => void;
|
|
|
|
|
setSelectedNamespace: (namespace: string) => void;
|
|
|
|
|
addCluster: (cluster: ClusterInfo) => void;
|
|
|
|
|
removeCluster: (clusterId: string) => void;
|
|
|
|
|
updateCluster: (clusterId: string, updates: Partial<ClusterInfo>) => void;
|
|
|
|
|
addContext: (context: ContextInfo) => void;
|
|
|
|
|
setNamespaces: (clusterId: string, namespaces: string[]) => void;
|
|
|
|
|
markResourceLoaded: (type: ResourceType) => void;
|
|
|
|
|
markResourceUnloaded: (type: ResourceType) => void;
|
|
|
|
|
isResourceLoaded: (type: ResourceType) => boolean;
|
|
|
|
|
addTerminalSession: (session: { clusterId: string; namespace: string; pod: string; container: string; command: string }) => string;
|
|
|
|
|
removeTerminalSession: (sessionId: string) => void;
|
|
|
|
|
setGlobalSearchQuery: (query: string) => void;
|
|
|
|
|
setSearchResults: (type: ResourceType, results: ResourceInfo[]) => void;
|
|
|
|
|
addToBulkSelection: (type: ResourceType, resourceName: string) => void;
|
|
|
|
|
removeFromBulkSelection: (type: ResourceType, resourceName: string) => void;
|
|
|
|
|
clearBulkSelection: (type: ResourceType) => void;
|
|
|
|
|
getBulkSelectionCount: (type: ResourceType) => number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const useKubernetesStore = create<KubernetesState>()((set, get) => ({
|
|
|
|
|
// Selection state
|
|
|
|
|
selectedClusterId: null,
|
|
|
|
|
selectedNamespace: "all",
|
|
|
|
|
|
|
|
|
|
// Data state
|
|
|
|
|
clusters: [],
|
|
|
|
|
contexts: [],
|
|
|
|
|
namespaces: {},
|
|
|
|
|
|
|
|
|
|
// Loaded resources tracking
|
2026-06-07 16:37:17 +00:00
|
|
|
loadedResources: new Set<ResourceType>(),
|
feat(kubernetes): implement Phase 1 & 2: resource discovery UIs and advanced features
- Add kubernetesStore.ts with Zustand state management (clusters, namespaces, resources, terminals, search, bulk selection)
- Create 15 resource list components (Secret, ReplicaSet, Job, CronJob, Ingress, PVC, PV, ServiceAccount, Role, ClusterRole, RoleBinding, ClusterRoleBinding, HPA, Node, Event, ConfigMap)
- Add advanced components (Terminal, YamlEditor, MetricsChart, SearchBar, ContextSwitcher, ApplicationView, PodDetail)
- Update KubernetesPage.tsx to integrate kubernetesStore and add cluster management
- Add ContextInfo and ResourceInfo types to tauriCommands.ts
- All components pass ESLint, TypeScript, and pass 114 tests
- Build successful
2026-06-07 15:24:26 +00:00
|
|
|
|
|
|
|
|
// Terminal sessions
|
|
|
|
|
terminalSessions: {},
|
|
|
|
|
nextTerminalId: 1,
|
|
|
|
|
|
|
|
|
|
// Search state
|
|
|
|
|
globalSearchQuery: "",
|
|
|
|
|
searchResults: {} as Record<ResourceType, ResourceInfo[]>,
|
|
|
|
|
|
|
|
|
|
// Bulk selection
|
|
|
|
|
bulkSelection: {} as Record<ResourceType, string[]>,
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
setSelectedCluster: (clusterId) => set({ selectedClusterId: clusterId, selectedNamespace: "all" }),
|
|
|
|
|
|
2026-06-07 17:29:39 +00:00
|
|
|
selectClusterFromKubeconfig: (kubeconfigs: KubeconfigInfo[]) => {
|
|
|
|
|
const activeConfig = kubeconfigs.find((c) => c.is_active);
|
|
|
|
|
if (activeConfig) {
|
|
|
|
|
set({ selectedClusterId: activeConfig.id, selectedNamespace: "all" });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
feat(kubernetes): implement Phase 1 & 2: resource discovery UIs and advanced features
- Add kubernetesStore.ts with Zustand state management (clusters, namespaces, resources, terminals, search, bulk selection)
- Create 15 resource list components (Secret, ReplicaSet, Job, CronJob, Ingress, PVC, PV, ServiceAccount, Role, ClusterRole, RoleBinding, ClusterRoleBinding, HPA, Node, Event, ConfigMap)
- Add advanced components (Terminal, YamlEditor, MetricsChart, SearchBar, ContextSwitcher, ApplicationView, PodDetail)
- Update KubernetesPage.tsx to integrate kubernetesStore and add cluster management
- Add ContextInfo and ResourceInfo types to tauriCommands.ts
- All components pass ESLint, TypeScript, and pass 114 tests
- Build successful
2026-06-07 15:24:26 +00:00
|
|
|
setSelectedNamespace: (namespace) => set({ selectedNamespace: namespace }),
|
|
|
|
|
|
|
|
|
|
addCluster: (cluster) => set((state) => ({
|
|
|
|
|
clusters: [...state.clusters, cluster],
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
removeCluster: (clusterId) => set((state) => ({
|
|
|
|
|
clusters: state.clusters.filter((c) => c.id !== clusterId),
|
|
|
|
|
selectedClusterId: state.selectedClusterId === clusterId ? null : state.selectedClusterId,
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
updateCluster: (clusterId, updates) => set((state) => ({
|
|
|
|
|
clusters: state.clusters.map((c) =>
|
|
|
|
|
c.id === clusterId ? { ...c, ...updates } : c
|
|
|
|
|
),
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
addContext: (context) => set((state) => ({
|
|
|
|
|
contexts: [...state.contexts, context],
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
setNamespaces: (clusterId, namespaces) => set((state) => ({
|
|
|
|
|
namespaces: { ...state.namespaces, [clusterId]: namespaces },
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
markResourceLoaded: (type) => set((state) => {
|
|
|
|
|
const newSet = new Set(state.loadedResources);
|
|
|
|
|
newSet.add(type);
|
|
|
|
|
return { loadedResources: newSet };
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
markResourceUnloaded: (type) => set((state) => {
|
|
|
|
|
const newSet = new Set(state.loadedResources);
|
|
|
|
|
newSet.delete(type);
|
|
|
|
|
return { loadedResources: newSet };
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
isResourceLoaded: (type) => get().loadedResources.has(type),
|
|
|
|
|
|
|
|
|
|
addTerminalSession: (session) => {
|
|
|
|
|
const sessionId = `terminal-${get().nextTerminalId}`;
|
|
|
|
|
set((state) => ({
|
|
|
|
|
terminalSessions: { ...state.terminalSessions, [sessionId]: { id: sessionId, ...session } },
|
|
|
|
|
nextTerminalId: state.nextTerminalId + 1,
|
|
|
|
|
}));
|
|
|
|
|
return sessionId;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
removeTerminalSession: (sessionId) => set((state) => ({
|
|
|
|
|
terminalSessions: Object.fromEntries(
|
|
|
|
|
Object.entries(state.terminalSessions).filter(([id]) => id !== sessionId)
|
|
|
|
|
),
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
setGlobalSearchQuery: (query) => set({ globalSearchQuery: query }),
|
|
|
|
|
|
|
|
|
|
setSearchResults: (type, results) => set((state) => ({
|
|
|
|
|
searchResults: { ...state.searchResults, [type]: results },
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
addToBulkSelection: (type, resourceName) => set((state) => ({
|
|
|
|
|
bulkSelection: {
|
|
|
|
|
...state.bulkSelection,
|
|
|
|
|
[type]: [...(state.bulkSelection[type] || []), resourceName],
|
|
|
|
|
},
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
removeFromBulkSelection: (type, resourceName) => set((state) => ({
|
|
|
|
|
bulkSelection: {
|
|
|
|
|
...state.bulkSelection,
|
|
|
|
|
[type]: (state.bulkSelection[type] || []).filter((name) => name !== resourceName),
|
|
|
|
|
},
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
clearBulkSelection: (type) => set((state) => ({
|
|
|
|
|
bulkSelection: { ...state.bulkSelection, [type]: [] },
|
|
|
|
|
})),
|
|
|
|
|
|
|
|
|
|
getBulkSelectionCount: (type) => (get().bulkSelection[type] || []).length,
|
|
|
|
|
}));
|