Skip to content

Commit 45b128c

Browse files
committed
chore: lazy load
Signed-off-by: Zzde <zhangxh1997@gmail.com>
1 parent d0c10a8 commit 45b128c

21 files changed

+1382
-1141
lines changed

ui/src/App.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import './App.css'
22

3-
import { useEffect } from 'react'
3+
import { lazy, Suspense, useEffect } from 'react'
44
import { useTranslation } from 'react-i18next'
55
import { Outlet, useSearchParams } from 'react-router-dom'
66

77
import { AIChatbox } from './components/ai-chat/ai-chatbox'
88
import { AppSidebar } from './components/app-sidebar'
9-
import { FloatingTerminal } from './components/floating-terminal'
109
import { GlobalSearch } from './components/global-search'
1110
import {
1211
GlobalSearchProvider,
@@ -17,10 +16,20 @@ import { SidebarInset, SidebarProvider } from './components/ui/sidebar'
1716
import { Toaster } from './components/ui/sonner'
1817
import { AIChatProvider } from './contexts/ai-chat-context'
1918
import { ClusterProvider } from './contexts/cluster-context'
20-
import { TerminalProvider } from './contexts/terminal-context'
19+
import { TerminalProvider, useTerminal } from './contexts/terminal-context'
2120
import { useCluster } from './hooks/use-cluster'
2221
import { apiClient } from './lib/api-client'
2322

23+
const appModules = import.meta.glob(['./components/floating-terminal.tsx'])
24+
25+
const FloatingTerminal = lazy(async () => {
26+
const module = (await appModules[
27+
'./components/floating-terminal.tsx'
28+
]()) as typeof import('./components/floating-terminal')
29+
30+
return { default: module.FloatingTerminal }
31+
})
32+
2433
function ClusterAwareApp() {
2534
const { t } = useTranslation()
2635
const { currentCluster, isLoading, error } = useCluster()
@@ -57,6 +66,7 @@ function ClusterAwareApp() {
5766

5867
function AppContent() {
5968
const { isOpen, closeSearch } = useGlobalSearch()
69+
const { isOpen: isTerminalOpen } = useTerminal()
6070
const [searchParams] = useSearchParams()
6171
const isIframe = searchParams.get('iframe') === 'true'
6272

@@ -80,7 +90,11 @@ function AppContent() {
8090
</SidebarInset>
8191
</SidebarProvider>
8292
<GlobalSearch open={isOpen} onOpenChange={closeSearch} />
83-
<FloatingTerminal />
93+
{isTerminalOpen ? (
94+
<Suspense fallback={null}>
95+
<FloatingTerminal />
96+
</Suspense>
97+
) : null}
8498
<AIChatbox />
8599
<Toaster />
86100
</>
Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react'
1+
import { useState } from 'react'
22
import { DialogDescription } from '@radix-ui/react-dialog'
33
import { Container } from 'kubernetes-types/core/v1'
44
import { useParams } from 'react-router-dom'
@@ -21,19 +21,38 @@ interface ContainerEditDialogProps {
2121
onSave: (updatedContainer: Container) => void
2222
}
2323

24+
function cloneContainer(container: Container) {
25+
return JSON.parse(JSON.stringify(container)) as Container
26+
}
27+
2428
export function ContainerEditDialog({
2529
open,
2630
onOpenChange,
2731
container,
2832
onSave,
2933
}: ContainerEditDialogProps) {
30-
const [editedContainer, setEditedContainer] = useState<Container>(container)
34+
return (
35+
<Dialog open={open} onOpenChange={onOpenChange}>
36+
{open ? (
37+
<ContainerEditDialogContent
38+
container={container}
39+
onOpenChange={onOpenChange}
40+
onSave={onSave}
41+
/>
42+
) : null}
43+
</Dialog>
44+
)
45+
}
3146

47+
function ContainerEditDialogContent({
48+
container,
49+
onOpenChange,
50+
onSave,
51+
}: Omit<ContainerEditDialogProps, 'open'>) {
3252
const { namespace } = useParams()
33-
34-
useEffect(() => {
35-
setEditedContainer({ ...container })
36-
}, [container])
53+
const [editedContainer, setEditedContainer] = useState<Container>(() =>
54+
cloneContainer(container)
55+
)
3756

3857
const handleSave = () => {
3958
onSave(editedContainer)
@@ -45,49 +64,44 @@ export function ContainerEditDialog({
4564
}
4665

4766
return (
48-
<Dialog open={open} onOpenChange={onOpenChange}>
49-
<DialogContent className="!max-w-4xl max-h-[90vh] overflow-y-auto sm:!max-w-4xl">
50-
<DialogHeader>
51-
<DialogTitle>Edit Container: {container.name}</DialogTitle>
52-
<DialogDescription className="text-sm text-muted-foreground">
53-
More complex changes can be made by modifying in YAML.
54-
</DialogDescription>
55-
</DialogHeader>
67+
<DialogContent className="!max-w-4xl max-h-[90vh] overflow-y-auto sm:!max-w-4xl">
68+
<DialogHeader>
69+
<DialogTitle>Edit Container: {editedContainer.name}</DialogTitle>
70+
<DialogDescription className="text-sm text-muted-foreground">
71+
More complex changes can be made by modifying in YAML.
72+
</DialogDescription>
73+
</DialogHeader>
5674

57-
<Tabs defaultValue="image" className="w-full">
58-
<TabsList className="grid w-full grid-cols-3">
59-
<TabsTrigger value="image">Image</TabsTrigger>
60-
<TabsTrigger value="resources">Resources</TabsTrigger>
61-
<TabsTrigger value="environment">Environment</TabsTrigger>
62-
</TabsList>
75+
<Tabs defaultValue="image" className="w-full">
76+
<TabsList className="grid w-full grid-cols-3">
77+
<TabsTrigger value="image">Image</TabsTrigger>
78+
<TabsTrigger value="resources">Resources</TabsTrigger>
79+
<TabsTrigger value="environment">Environment</TabsTrigger>
80+
</TabsList>
6381

64-
<TabsContent value="image" className="space-y-4">
65-
<ImageEditor container={editedContainer} onUpdate={handleUpdate} />
66-
</TabsContent>
82+
<TabsContent value="image" className="space-y-4">
83+
<ImageEditor container={editedContainer} onUpdate={handleUpdate} />
84+
</TabsContent>
6785

68-
<TabsContent value="resources" className="space-y-6">
69-
<ResourceEditor
70-
container={editedContainer}
71-
onUpdate={handleUpdate}
72-
/>
73-
</TabsContent>
86+
<TabsContent value="resources" className="space-y-6">
87+
<ResourceEditor container={editedContainer} onUpdate={handleUpdate} />
88+
</TabsContent>
7489

75-
<TabsContent value="environment" className="space-y-4">
76-
<EnvironmentEditor
77-
container={editedContainer}
78-
onUpdate={handleUpdate}
79-
namespace={namespace!}
80-
/>
81-
</TabsContent>
82-
</Tabs>
90+
<TabsContent value="environment" className="space-y-4">
91+
<EnvironmentEditor
92+
container={editedContainer}
93+
onUpdate={handleUpdate}
94+
namespace={namespace!}
95+
/>
96+
</TabsContent>
97+
</Tabs>
8398

84-
<DialogFooter>
85-
<Button variant="outline" onClick={() => onOpenChange(false)}>
86-
Cancel
87-
</Button>
88-
<Button onClick={handleSave}>Save Changes</Button>
89-
</DialogFooter>
90-
</DialogContent>
91-
</Dialog>
99+
<DialogFooter>
100+
<Button variant="outline" onClick={() => onOpenChange(false)}>
101+
Cancel
102+
</Button>
103+
<Button onClick={handleSave}>Save Changes</Button>
104+
</DialogFooter>
105+
</DialogContent>
92106
)
93107
}
Lines changed: 72 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react'
1+
import { useState } from 'react'
22
import { IconLoader2 } from '@tabler/icons-react'
33
import { useTranslation } from 'react-i18next'
44
import { toast } from 'sonner'
@@ -33,19 +33,24 @@ export function CreateResourceDialog({
3333
open,
3434
onOpenChange,
3535
}: CreateResourceDialogProps) {
36+
return (
37+
<Dialog open={open} onOpenChange={onOpenChange}>
38+
{open ? (
39+
<CreateResourceDialogContent onOpenChange={onOpenChange} />
40+
) : null}
41+
</Dialog>
42+
)
43+
}
44+
45+
function CreateResourceDialogContent({
46+
onOpenChange,
47+
}: Omit<CreateResourceDialogProps, 'open'>) {
3648
const { t } = useTranslation()
3749
const { data: templates = [] } = useTemplates()
3850
const [selectedTemplateId, setSelectedTemplateId] = useState<string>('')
3951
const [yamlContent, setYamlContent] = useState('')
4052
const [isLoading, setIsLoading] = useState(false)
4153

42-
useEffect(() => {
43-
if (open) {
44-
setYamlContent('')
45-
setSelectedTemplateId('')
46-
}
47-
}, [open])
48-
4954
const handleTemplateChange = (templateName: string) => {
5055
if (templateName === 'empty') {
5156
setYamlContent('')
@@ -85,71 +90,69 @@ export function CreateResourceDialog({
8590
}
8691

8792
return (
88-
<Dialog open={open} onOpenChange={onOpenChange}>
89-
<DialogContent className="!max-w-4xl sm:!max-w-4xl max-h-[80vh] flex flex-col">
90-
<DialogHeader>
91-
<DialogTitle>Create Resource</DialogTitle>
92-
<DialogDescription>
93-
Paste any Kubernetes resource YAML configuration and apply it to the
94-
cluster
95-
</DialogDescription>
96-
</DialogHeader>
93+
<DialogContent className="!max-w-4xl sm:!max-w-4xl max-h-[80vh] flex flex-col">
94+
<DialogHeader>
95+
<DialogTitle>Create Resource</DialogTitle>
96+
<DialogDescription>
97+
Paste any Kubernetes resource YAML configuration and apply it to the
98+
cluster
99+
</DialogDescription>
100+
</DialogHeader>
97101

98-
<div className="flex-1 space-y-4">
99-
<div className="space-y-2">
100-
<Label htmlFor="template">Template</Label>
101-
<Select
102-
value={selectedTemplateId || 'empty'}
103-
onValueChange={handleTemplateChange}
104-
>
105-
<SelectTrigger>
106-
<SelectValue
107-
placeholder={t(
108-
'createResource.selectTemplate',
109-
'Select a template'
110-
)}
111-
/>
112-
</SelectTrigger>
113-
<SelectContent>
114-
<SelectItem value="empty">
115-
{t('createResource.emptyTemplate', 'Empty Template')}
116-
</SelectItem>
117-
{templates.map((template) => (
118-
<SelectItem key={template.name} value={template.name}>
119-
{template.name}
120-
</SelectItem>
121-
))}
122-
</SelectContent>
123-
</Select>
124-
</div>
125-
<div className="space-y-2">
126-
<Label htmlFor="yaml">YAML Configuration</Label>
127-
<div className="min-h-[300px] border rounded-md">
128-
<SimpleYamlEditor
129-
value={yamlContent}
130-
onChange={(value) => setYamlContent(value || '')}
131-
height="400px"
102+
<div className="flex-1 space-y-4">
103+
<div className="space-y-2">
104+
<Label htmlFor="template">Template</Label>
105+
<Select
106+
value={selectedTemplateId || 'empty'}
107+
onValueChange={handleTemplateChange}
108+
>
109+
<SelectTrigger>
110+
<SelectValue
111+
placeholder={t(
112+
'createResource.selectTemplate',
113+
'Select a template'
114+
)}
132115
/>
133-
</div>
116+
</SelectTrigger>
117+
<SelectContent>
118+
<SelectItem value="empty">
119+
{t('createResource.emptyTemplate', 'Empty Template')}
120+
</SelectItem>
121+
{templates.map((template) => (
122+
<SelectItem key={template.name} value={template.name}>
123+
{template.name}
124+
</SelectItem>
125+
))}
126+
</SelectContent>
127+
</Select>
128+
</div>
129+
<div className="space-y-2">
130+
<Label htmlFor="yaml">YAML Configuration</Label>
131+
<div className="min-h-[300px] border rounded-md">
132+
<SimpleYamlEditor
133+
value={yamlContent}
134+
onChange={(value) => setYamlContent(value || '')}
135+
height="400px"
136+
/>
134137
</div>
135138
</div>
139+
</div>
136140

137-
<DialogFooter>
138-
<Button variant="outline" onClick={handleCancel} disabled={isLoading}>
139-
Cancel
140-
</Button>
141-
<Button onClick={handleApply} disabled={isLoading || !yamlContent}>
142-
{isLoading ? (
143-
<>
144-
<IconLoader2 className="mr-2 h-4 w-4 animate-spin" />
145-
{t('common.applying', 'Applying...')}
146-
</>
147-
) : (
148-
t('common.apply', 'Apply')
149-
)}
150-
</Button>
151-
</DialogFooter>
152-
</DialogContent>
153-
</Dialog>
141+
<DialogFooter>
142+
<Button variant="outline" onClick={handleCancel} disabled={isLoading}>
143+
Cancel
144+
</Button>
145+
<Button onClick={handleApply} disabled={isLoading || !yamlContent}>
146+
{isLoading ? (
147+
<>
148+
<IconLoader2 className="mr-2 h-4 w-4 animate-spin" />
149+
{t('common.applying', 'Applying...')}
150+
</>
151+
) : (
152+
t('common.apply', 'Apply')
153+
)}
154+
</Button>
155+
</DialogFooter>
156+
</DialogContent>
154157
)
155158
}

ui/src/components/editors/environment-editor.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react'
1+
import { useState } from 'react'
22
import {
33
Container,
44
EnvFromSource,
@@ -32,13 +32,10 @@ export function EnvironmentEditor({
3232
namespace,
3333
onUpdate,
3434
}: EnvironmentEditorProps) {
35-
const [envVars, setEnvVars] = useState<EnvVar[]>([])
36-
const [envFromSources, setEnvFromSources] = useState<EnvFromSource[]>([])
37-
38-
useEffect(() => {
39-
setEnvVars(container.env || [])
40-
setEnvFromSources(container.envFrom || [])
41-
}, [container.env, container.envFrom])
35+
const [envVars, setEnvVars] = useState<EnvVar[]>(() => container.env || [])
36+
const [envFromSources, setEnvFromSources] = useState<EnvFromSource[]>(
37+
() => container.envFrom || []
38+
)
4239

4340
const addEnvVar = () => {
4441
const newEnvVars = [...envVars, { name: '', value: '' }]

0 commit comments

Comments
 (0)