Skip to content

Commit 948cb8c

Browse files
authored
Merge pull request #1824 from headlamp-k8s/create-minikube
frontend: plugin: minikube: Add hooks for cluster providers to modify the UI in some spots
2 parents 75146bb + 10f77f5 commit 948cb8c

File tree

19 files changed

+390
-32
lines changed

19 files changed

+390
-32
lines changed

Diff for: app/electron/runCmd.ts

-2
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@ export function handleRunCommand(
113113
eventData: CommandData,
114114
mainWindow: BrowserWindow | null
115115
): void {
116-
return; // Disable this until we figure out a better way to do this
117-
118116
if (mainWindow === null) {
119117
console.error('Main window is null, cannot show dialog');
120118
return;
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { InlineIcon } from '@iconify/react';
2+
import { Button, Card, CardContent, CardHeader, Grid, Typography } from '@mui/material';
3+
import React from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
import { useHistory } from 'react-router-dom';
6+
import { createRouteURL } from '../../../lib/router';
7+
import { ClusterProviderInfo } from '../../../redux/clusterProviderSlice';
8+
import { useTypedSelector } from '../../../redux/reducers/reducers';
9+
import { DialogProps, PageGrid, SectionBox } from '../../common';
10+
11+
function AddClusterProvider({ title, icon, description, url }: ClusterProviderInfo) {
12+
const history = useHistory();
13+
const { t } = useTranslation(['translation']);
14+
const Icon = icon;
15+
const avatar = <Icon width={24} height={24} />;
16+
17+
return (
18+
<Card variant="outlined">
19+
<CardHeader title={title} avatar={avatar} />
20+
<CardContent>
21+
<Typography>{description}</Typography>
22+
<Button variant="contained" onClick={() => history.push(url)} sx={{ mt: 2 }}>
23+
{t('translation|Add')}
24+
</Button>
25+
</CardContent>
26+
</Card>
27+
);
28+
}
29+
30+
export default function AddCluster(props: DialogProps & { onChoice: () => void }) {
31+
const { open } = props;
32+
const { t } = useTranslation(['translation']);
33+
const history = useHistory();
34+
const addClusterProviders = useTypedSelector(state => state.clusterProvider.clusterProviders);
35+
36+
if (!open) {
37+
return null;
38+
}
39+
40+
return (
41+
<PageGrid>
42+
<SectionBox backLink title={t('translation|Add Cluster')} py={2} mt={[4, 0, 0]}>
43+
<Grid container justifyContent="flex-start" alignItems="stretch" spacing={4}>
44+
<Grid item xs={12}>
45+
<Typography>
46+
{t('Proceed to select your preferred method for cluster creation and addition')}
47+
</Typography>
48+
</Grid>
49+
<Grid item xs={12}>
50+
<Card variant="outlined">
51+
<CardContent>
52+
<Button
53+
onClick={() => history.push(createRouteURL('loadKubeConfig'))}
54+
startIcon={<InlineIcon icon="mdi:plus-box-outline" />}
55+
>
56+
{t('translation|Load from KubeConfig')}
57+
</Button>
58+
</CardContent>
59+
</Card>
60+
</Grid>
61+
{addClusterProviders.length > 0 && (
62+
<Grid item xs={12}>
63+
<Typography variant="h4">{t('translation|Providers')}</Typography>
64+
</Grid>
65+
)}
66+
{addClusterProviders.length > 0 && (
67+
<Grid item xs={12}>
68+
{addClusterProviders.map(addClusterProviderInfo => (
69+
<AddClusterProvider {...addClusterProviderInfo} />
70+
))}
71+
</Grid>
72+
)}
73+
</Grid>
74+
</SectionBox>
75+
</PageGrid>
76+
);
77+
}

Diff for: frontend/src/components/App/Home/RecentClusters.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export default function RecentClusters(props: RecentClustersProps) {
106106
<Grid item>
107107
<SquareButton
108108
onClick={() => {
109-
history.push(createRouteURL('loadKubeConfig'));
109+
history.push(createRouteURL('addCluster'));
110110
}}
111111
label={t('Load cluster')}
112112
icon="mdi:plus-circle-outline"

Diff for: frontend/src/components/App/Home/index.tsx

+44-9
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,27 @@ import Event from '../../../lib/k8s/event';
2020
import { createRouteURL } from '../../../lib/router';
2121
import { useId } from '../../../lib/util';
2222
import { setConfig } from '../../../redux/configSlice';
23+
import { useTypedSelector } from '../../../redux/reducers/reducers';
2324
import { Link, PageGrid, SectionBox, SectionFilterHeader } from '../../common';
2425
import { ConfirmDialog } from '../../common';
26+
import ErrorBoundary from '../../common/ErrorBoundary/ErrorBoundary';
2527
import ResourceTable from '../../common/Resource/ResourceTable';
2628
import RecentClusters from './RecentClusters';
2729

28-
function ContextMenu({ cluster }: { cluster: Cluster }) {
30+
interface ContextMenuProps {
31+
/** The cluster for the context menu to act on. */
32+
cluster: Cluster;
33+
}
34+
35+
function ContextMenu({ cluster }: ContextMenuProps) {
2936
const { t } = useTranslation(['translation']);
3037
const history = useHistory();
3138
const dispatch = useDispatch();
3239
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
3340
const menuId = useId('context-menu');
34-
const [openConfirmDialog, setOpenConfirmDialog] = React.useState(false);
41+
const [openConfirmDialog, setOpenConfirmDialog] = React.useState<string | null>(null);
42+
const dialogs = useTypedSelector(state => state.clusterProvider.dialogs);
43+
const menuItems = useTypedSelector(state => state.clusterProvider.menuItems);
3544

3645
function removeCluster(cluster: Cluster) {
3746
deleteCluster(cluster.name || '')
@@ -94,20 +103,30 @@ function ContextMenu({ cluster }: { cluster: Cluster }) {
94103
{helpers.isElectron() && cluster.meta_data?.source === 'dynamic_cluster' && (
95104
<MenuItem
96105
onClick={() => {
97-
setOpenConfirmDialog(true);
106+
setOpenConfirmDialog('deleteDynamic');
98107
handleMenuClose();
99108
}}
100109
>
101110
<ListItemText>{t('translation|Delete')}</ListItemText>
102111
</MenuItem>
103112
)}
104-
</Menu>
105113

114+
{menuItems.map((Item, index) => {
115+
return (
116+
<Item
117+
cluster={cluster}
118+
setOpenConfirmDialog={setOpenConfirmDialog}
119+
handleMenuClose={handleMenuClose}
120+
key={index}
121+
/>
122+
);
123+
})}
124+
</Menu>
106125
<ConfirmDialog
107-
open={openConfirmDialog}
108-
handleClose={() => setOpenConfirmDialog(false)}
126+
open={openConfirmDialog === 'deleteDynamic'}
127+
handleClose={() => setOpenConfirmDialog('')}
109128
onConfirm={() => {
110-
setOpenConfirmDialog(false);
129+
setOpenConfirmDialog('');
111130
removeCluster(cluster);
112131
}}
113132
title={t('translation|Delete Cluster')}
@@ -118,6 +137,19 @@ function ContextMenu({ cluster }: { cluster: Cluster }) {
118137
}
119138
)}
120139
/>
140+
{openConfirmDialog !== null &&
141+
dialogs.map((Dialog, index) => {
142+
return (
143+
<ErrorBoundary>
144+
<Dialog
145+
cluster={cluster}
146+
openConfirmDialog={openConfirmDialog}
147+
setOpenConfirmDialog={setOpenConfirmDialog}
148+
key={index}
149+
/>
150+
</ErrorBoundary>
151+
);
152+
})}
121153
</>
122154
);
123155
}
@@ -307,11 +339,14 @@ function HomeComponent(props: HomeComponentProps) {
307339
},
308340
{
309341
label: '',
310-
getValue: () => '',
342+
getValue: cluster =>
343+
errors[cluster.name] === null ? 'Active' : errors[cluster.name]?.message,
311344
cellProps: {
312345
align: 'right',
313346
},
314-
render: cluster => <ContextMenu cluster={cluster} />,
347+
render: cluster => {
348+
return <ContextMenu cluster={cluster} />;
349+
},
315350
},
316351
]}
317352
data={Object.values(customNameClusters)}

Diff for: frontend/src/components/App/runCommand.ts

-4
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ export function runCommand(
3737
throw new Error('runCommand only works in Headlamp app mode.');
3838
}
3939

40-
if (import.meta.env.REACT_APP_ENABLE_RUN_CMD !== 'true') {
41-
throw new Error('Running commands is disabled.');
42-
}
43-
4440
// Generate a unique ID for the command, so that we can distinguish between
4541
// multiple commands running at the same time.
4642
const id = `${new Date().getTime()}-${Math.random().toString(36)}`;

Diff for: frontend/src/components/Sidebar/Sidebar.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ function AddClusterButton() {
6161
<Box pb={2}>
6262
{isOpen ? (
6363
<Button
64-
onClick={() => history.push(createRouteURL('loadKubeConfig'))}
64+
onClick={() => history.push(createRouteURL('addCluster'))}
6565
startIcon={<InlineIcon icon="mdi:plus-box-outline" />}
6666
>
6767
{t('translation|Add Cluster')}
6868
</Button>
6969
) : (
7070
<ActionButton
71-
onClick={() => history.push(createRouteURL('loadKubeConfig'))}
71+
onClick={() => history.push(createRouteURL('addCluster'))}
7272
icon="mdi:plus-box-outline"
7373
description={t('translation|Add Cluster')}
7474
color="#adadad"

Diff for: frontend/src/i18n/locales/de/translation.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"Cancel": "Abbrechen",
1010
"Authenticate": "Authentifizieren Sie",
1111
"Error authenticating": "Fehler beim Authentifizieren",
12+
"Add Cluster": "Cluster hinzufügen",
13+
"Proceed to select your preferred method for cluster creation and addition": "",
14+
"Load from KubeConfig": "Laden aus KubeConfig",
15+
"Providers": "",
1216
"Actions": "Aktionen",
1317
"View": "Ansicht",
1418
"Settings": "Einstellungen",
@@ -121,7 +125,6 @@
121125
"Cluster: {{cluster}}": "Cluster: {{cluster}}",
122126
"Current//context:cluster": "Aktuell",
123127
"Choose cluster": "Cluster auswählen",
124-
"Add Cluster": "Cluster hinzufügen",
125128
"Failed to load resources": "",
126129
"You don't have permissions to view this resource": "",
127130
"Resource not found": "",
@@ -141,7 +144,6 @@
141144
"Setting up clusters": "Einrichten der Cluster",
142145
"Clusters successfully set up!": "Cluster erfolgreich eingerichtet!",
143146
"Finish": "Beenden",
144-
"Load from KubeConfig": "Laden aus KubeConfig",
145147
"Overview": "Übersicht",
146148
"Only warnings ({{ numWarnings }})": "Nur Warnungen ({{ numWarnings }})",
147149
"Type": "Typ",

Diff for: frontend/src/i18n/locales/en/translation.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"Cancel": "Cancel",
1010
"Authenticate": "Authenticate",
1111
"Error authenticating": "Error authenticating",
12+
"Add Cluster": "Add Cluster",
13+
"Proceed to select your preferred method for cluster creation and addition": "Proceed to select your preferred method for cluster creation and addition",
14+
"Load from KubeConfig": "Load from KubeConfig",
15+
"Providers": "Providers",
1216
"Actions": "Actions",
1317
"View": "View",
1418
"Settings": "Settings",
@@ -121,7 +125,6 @@
121125
"Cluster: {{cluster}}": "Cluster: {{cluster}}",
122126
"Current//context:cluster": "Current",
123127
"Choose cluster": "Choose cluster",
124-
"Add Cluster": "Add Cluster",
125128
"Failed to load resources": "Failed to load resources",
126129
"You don't have permissions to view this resource": "You don't have permissions to view this resource",
127130
"Resource not found": "Resource not found",
@@ -141,7 +144,6 @@
141144
"Setting up clusters": "Setting up clusters",
142145
"Clusters successfully set up!": "Clusters successfully set up!",
143146
"Finish": "Finish",
144-
"Load from KubeConfig": "Load from KubeConfig",
145147
"Overview": "Overview",
146148
"Only warnings ({{ numWarnings }})": "Only warnings ({{ numWarnings }})",
147149
"Type": "Type",

Diff for: frontend/src/i18n/locales/es/translation.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"Cancel": "Cancelar",
1010
"Authenticate": "Autenticar",
1111
"Error authenticating": "Error al autenticarse",
12+
"Add Cluster": "Añadir cluster",
13+
"Proceed to select your preferred method for cluster creation and addition": "",
14+
"Load from KubeConfig": "Cargar desde KubeConfig",
15+
"Providers": "",
1216
"Actions": "Acciones",
1317
"View": "Ver",
1418
"Settings": "Definiciones",
@@ -121,7 +125,6 @@
121125
"Cluster: {{cluster}}": "Cluster: {{cluster}}",
122126
"Current//context:cluster": "Actuales",
123127
"Choose cluster": "Elegir cluster",
124-
"Add Cluster": "Añadir cluster",
125128
"Failed to load resources": "",
126129
"You don't have permissions to view this resource": "",
127130
"Resource not found": "",
@@ -141,7 +144,6 @@
141144
"Setting up clusters": "Configurando los clusters",
142145
"Clusters successfully set up!": "¡Clusters configurados con éxito!",
143146
"Finish": "Finalizar",
144-
"Load from KubeConfig": "Cargar desde KubeConfig",
145147
"Overview": "Resumen",
146148
"Only warnings ({{ numWarnings }})": "Solo advertencias ({{ numWarnings }})",
147149
"Type": "Tipo",

Diff for: frontend/src/i18n/locales/fr/translation.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"Cancel": "Cancel",
1010
"Authenticate": "Authentifier",
1111
"Error authenticating": "Erreur d'authentification",
12+
"Add Cluster": "Ajouter un cluster",
13+
"Proceed to select your preferred method for cluster creation and addition": "",
14+
"Load from KubeConfig": "Charger à partir d'un KubeConfig",
15+
"Providers": "",
1216
"Actions": "Actions",
1317
"View": "Vue",
1418
"Settings": "Paramètres",
@@ -121,7 +125,6 @@
121125
"Cluster: {{cluster}}": "Cluster: {{cluster}}",
122126
"Current//context:cluster": "Actuel",
123127
"Choose cluster": "Choisir un cluster",
124-
"Add Cluster": "Ajouter un cluster",
125128
"Failed to load resources": "",
126129
"You don't have permissions to view this resource": "",
127130
"Resource not found": "",
@@ -141,7 +144,6 @@
141144
"Setting up clusters": "Configuration des clusters",
142145
"Clusters successfully set up!": "Clusters configurés avec succès !",
143146
"Finish": "Terminer",
144-
"Load from KubeConfig": "Charger à partir d'un KubeConfig",
145147
"Overview": "Aperçu",
146148
"Only warnings ({{ numWarnings }})": "Seulement les avertissements ({{ numWarnings }})",
147149
"Type": "Type",

Diff for: frontend/src/i18n/locales/it/translation.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"Cancel": "Annulla",
1010
"Authenticate": "Autenticati",
1111
"Error authenticating": "Errore durante l'autenticazione",
12+
"Add Cluster": "Aggiungi Cluster",
13+
"Proceed to select your preferred method for cluster creation and addition": "",
14+
"Load from KubeConfig": "Carica da KubeConfig",
15+
"Providers": "",
1216
"Actions": "Azioni",
1317
"View": "Visualizza",
1418
"Settings": "Impostazioni",
@@ -121,7 +125,6 @@
121125
"Cluster: {{cluster}}": "Cluster: {{cluster}}",
122126
"Current//context:cluster": "Corrente//context:cluster",
123127
"Choose cluster": "Scegli cluster",
124-
"Add Cluster": "Aggiungi Cluster",
125128
"Failed to load resources": "Impossibile caricare le risorse",
126129
"You don't have permissions to view this resource": "Non hai i permessi per visualizzare questa risorsa",
127130
"Resource not found": "Risorsa non trovata",
@@ -141,7 +144,6 @@
141144
"Setting up clusters": "Configurazione dei cluster",
142145
"Clusters successfully set up!": "Cluster configurati con successo!",
143146
"Finish": "Completa",
144-
"Load from KubeConfig": "Carica da KubeConfig",
145147
"Overview": "Panoramica",
146148
"Only warnings ({{ numWarnings }})": "Solo avvertenze ({{ numWarnings }})",
147149
"Type": "Tipo",

Diff for: frontend/src/i18n/locales/pt/translation.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"Cancel": "Cancelar",
1010
"Authenticate": "Autenticar",
1111
"Error authenticating": "Erro ao autenticar",
12+
"Add Cluster": "Adicionar Cluster",
13+
"Proceed to select your preferred method for cluster creation and addition": "",
14+
"Load from KubeConfig": "Carregar a partir de um KubeConfig",
15+
"Providers": "",
1216
"Actions": "Acções",
1317
"View": "Ver",
1418
"Settings": "Definições",
@@ -121,7 +125,6 @@
121125
"Cluster: {{cluster}}": "Cluster: {{cluster}}",
122126
"Current//context:cluster": "Actual",
123127
"Choose cluster": "Escolha o cluster",
124-
"Add Cluster": "Adicionar Cluster",
125128
"Failed to load resources": "",
126129
"You don't have permissions to view this resource": "",
127130
"Resource not found": "",
@@ -141,7 +144,6 @@
141144
"Setting up clusters": "A configurar clusters",
142145
"Clusters successfully set up!": "Clusters configurados com sucesso!",
143146
"Finish": "Terminar",
144-
"Load from KubeConfig": "Carregar a partir de um KubeConfig",
145147
"Overview": "Resumo",
146148
"Only warnings ({{ numWarnings }})": "Só avisos ({{ numWarnings }})",
147149
"Type": "Tipo",

0 commit comments

Comments
 (0)