Skip to content

Commit

Permalink
frontend: Add create resource UI
Browse files Browse the repository at this point in the history
These changes introduce a new UI feature that allows users to create
resources from the associated list view. Clicking the 'Create' button
opens up the EditorDialog used in the generic 'Create / Apply' button,
now accepting generic YAML/JSON text rather than explicitly expecting an
item that looks like a Kubernetes resource. The dialog box also includes
a generic template for each resource.

Fixes: #1820

Signed-off-by: Evangelos Skopelitis <[email protected]>
  • Loading branch information
skoeva committed Jul 17, 2024
1 parent c309b09 commit a335812
Show file tree
Hide file tree
Showing 20 changed files with 305 additions and 55 deletions.
43 changes: 43 additions & 0 deletions frontend/src/components/common/CreateResourceButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { Provider } from 'react-redux';
import ConfigMap from '../../lib/k8s/configMap';
import { Lease } from '../../lib/k8s/lease';
import { RuntimeClass } from '../../lib/k8s/runtime';
import Secret from '../../lib/k8s/secret';
import store from '../../redux/stores/store';
import { CreateResourceButton, CreateResourceButtonProps } from './CreateResourceButton';

export default {
title: 'CreateResourceButton',
component: CreateResourceButton,
decorators: [
Story => (
<Provider store={store}>
<Story />
</Provider>
),
],
} as Meta;

const Template: StoryFn<CreateResourceButtonProps> = args => <CreateResourceButton {...args} />;

export const ConfigMapStory = Template.bind({});
ConfigMapStory.args = {
resourceClass: ConfigMap,
};

export const LeaseStory = Template.bind({});
LeaseStory.args = {
resourceClass: Lease,
};

export const RuntimeClassStory = Template.bind({});
RuntimeClassStory.args = {
resourceClass: RuntimeClass,
};

export const SecretStory = Template.bind({});
SecretStory.args = {
resourceClass: Secret,
};
126 changes: 126 additions & 0 deletions frontend/src/components/common/CreateResourceButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { getCluster } from '../../lib/cluster';
import { apply } from '../../lib/k8s/apiProxy';
import { ClassWithBaseObject, KubeObject, KubeObjectInterface } from '../../lib/k8s/cluster';
import { clusterAction } from '../../redux/clusterActionSlice';
import { EventStatus, HeadlampEventType, useEventCallback } from '../../redux/headlampEventSlice';
import { ActionButton, AuthVisible, EditorDialog } from '../common';

export interface CreateResourceButtonProps {
resourceClass: ClassWithBaseObject<KubeObject>;
}

export function CreateResourceButton(props: CreateResourceButtonProps) {
const { resourceClass } = props;
const { t } = useTranslation(['glossary', 'translation']);
const [openDialog, setOpenDialog] = React.useState(false);
const [errorMessage, setErrorMessage] = React.useState('');
const dispatchCreateEvent = useEventCallback(HeadlampEventType.CREATE_RESOURCE);
const dispatch = useDispatch();

const applyFunc = async (newItems: KubeObjectInterface[], clusterName: string) => {
await Promise.allSettled(newItems.map(newItem => apply(newItem, clusterName))).then(
(values: any) => {
values.forEach((value: any, index: number) => {
if (value.status === 'rejected') {
let msg;
const kind = newItems[index].kind;
const name = newItems[index].metadata.name;
const apiVersion = newItems[index].apiVersion;
if (newItems.length === 1) {
msg = t('translation|Failed to create {{ kind }} {{ name }}.', { kind, name });
} else {
msg = t('translation|Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.', {
kind,
name,
apiVersion,
});
}
setErrorMessage(msg);
setOpenDialog(true);
throw msg;
}
});
}
);
};

function handleSave(newItemDefs: KubeObjectInterface[]) {
let massagedNewItemDefs = newItemDefs;
const cancelUrl = location.pathname;

// check if all yaml objects are valid
for (let i = 0; i < massagedNewItemDefs.length; i++) {
if (massagedNewItemDefs[i].kind === 'List') {
// flatten this List kind with the items that it has which is a list of valid k8s resources
const deletedItem = massagedNewItemDefs.splice(i, 1);
massagedNewItemDefs = massagedNewItemDefs.concat(deletedItem[0].items);
}
if (!massagedNewItemDefs[i].metadata?.name) {
setErrorMessage(
t(`translation|Invalid: One or more of resources doesn't have a name property`)
);
return;
}
if (!massagedNewItemDefs[i].kind) {
setErrorMessage(t('translation|Invalid: Please set a kind to the resource'));
return;
}
}
// all resources name
const resourceNames = massagedNewItemDefs.map(newItemDef => newItemDef.metadata.name);
setOpenDialog(false);

const clusterName = getCluster() || '';

dispatch(
clusterAction(() => applyFunc(massagedNewItemDefs, clusterName), {
startMessage: t('translation|Applying {{ newItemName }}…', {
newItemName: resourceNames.join(','),
}),
cancelledMessage: t('translation|Cancelled applying {{ newItemName }}.', {
newItemName: resourceNames.join(','),
}),
successMessage: t('translation|Applied {{ newItemName }}.', {
newItemName: resourceNames.join(','),
}),
errorMessage: t('translation|Failed to apply {{ newItemName }}.', {
newItemName: resourceNames.join(','),
}),
cancelUrl,
})
);

dispatchCreateEvent({
status: EventStatus.CONFIRMED,
});
}
const baseObject = resourceClass.getBaseObject() || {};
const resourceName = resourceClass.kind;

return (
<AuthVisible item={resourceClass} authVerb="create">
<ActionButton
color="primary"
description={t('translation|Create {{ resourceName }}', { resourceName })}

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

Unhandled error

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17 This error originated in "src/storybook.test.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "List". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > crd/CustomResourceDefinition > Details

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > crd/CustomResourceList > List

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > DaemonSet/List > DaemonSets

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > endpoints/EndpointsListView > Items

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > hpa/HpaListView > Items

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > IngressClass/ListView > Items

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > Ingress/ListView > Items

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > Namespace/ListView > Regular

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17

Check failure on line 107 in frontend/src/components/common/CreateResourceButton.tsx

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-22.04)

src/storybook.test.tsx > Storybook Tests > node/List > Nodes

TypeError: resourceClass.getBaseObject is not a function ❯ CreateResourceButton src/components/common/CreateResourceButton.tsx:107:24 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:14985:18 ❯ mountIndeterminateComponent node_modules/react-dom/cjs/react-dom.development.js:17811:13 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:19049:16 ❯ HTMLUnknownElement.callCallback node_modules/react-dom/cjs/react-dom.development.js:3945:14 ❯ HTMLUnknownElement.callTheUserObjectsOperation node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30 ❯ innerInvokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:350:25 ❯ invokeEventListeners node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:286:3 ❯ HTMLUnknownElementImpl._dispatch node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:233:9 ❯ HTMLUnknownElementImpl.dispatchEvent node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:104:17
icon={'mdi:plus-circle'}
onClick={() => {
setOpenDialog(true);
}}
/>

<EditorDialog
item={baseObject}
open={openDialog}
onClose={() => setOpenDialog(false)}
onSave={handleSave}
saveLabel={t('translation|Apply')}
errorMessage={errorMessage}
onEditorChanged={() => setErrorMessage('')}
title={t('translation|Create {{ resourceName }}', { resourceName })}
/>
</AuthVisible>
);
}
6 changes: 3 additions & 3 deletions frontend/src/components/common/Resource/EditorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ export default function EditorDialog(props: EditorDialogProps) {
setCode(originalCodeRef.current);
}

// async function applyFunc(newItems)

function handleSave() {
// Verify the YAML even means anything before trying to use it.
const { obj, format, error } = getObjectsFromCode(code);
Expand Down Expand Up @@ -311,9 +313,7 @@ export default function EditorDialog(props: EditorDialogProps) {
const errorLabel = error || errorMessage;
let dialogTitle = title;
if (!dialogTitle && item) {
const itemName = isKubeObjectIsh(item)
? item.metadata?.name || t('New Object')
: t('New Object');
const itemName = (isKubeObjectIsh(item) && item.metadata?.name) || t('New Object');
dialogTitle = isReadOnly()
? t('translation|View: {{ itemName }}', { itemName })
: t('translation|Edit: {{ itemName }}', { itemName });
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/components/common/Resource/ResourceListView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { PropsWithChildren } from 'react';
import { KubeObject } from '../../../lib/k8s/cluster';
import { ClassWithBaseObject, KubeObject } from '../../../lib/k8s/cluster';
import { CreateResourceButton } from '../CreateResourceButton';
import SectionBox from '../SectionBox';
import SectionFilterHeader, { SectionFilterHeaderProps } from '../SectionFilterHeader';
import ResourceTable, { ResourceTableProps } from './ResourceTable';
Expand All @@ -10,11 +11,9 @@ export interface ResourceListViewProps<ItemType>
headerProps?: Omit<SectionFilterHeaderProps, 'title'>;
}

type Class<T> = new (...args: any[]) => T;

export interface ResourceListViewWithResourceClassProps<ItemType>
extends Omit<ResourceListViewProps<ItemType>, 'data'> {
resourceClass: Class<ItemType>;
resourceClass: ClassWithBaseObject<ItemType>;
}

export default function ResourceListView<ItemType>(
Expand All @@ -23,6 +22,7 @@ export default function ResourceListView<ItemType>(
const { title, children, headerProps, ...tableProps } = props;
const withNamespaceFilter =
'resourceClass' in props && (props.resourceClass as KubeObject)?.isNamespaced;
const resourceClass = 'resourceClass' in props ? props.resourceClass : null;

return (
<SectionBox
Expand All @@ -32,6 +32,9 @@ export default function ResourceListView<ItemType>(
title={title}
noNamespaceFilter={!withNamespaceFilter}
{...headerProps}
titleSideActions={
resourceClass ? [<CreateResourceButton resourceClass={resourceClass} />] : undefined
}
/>
) : (
title
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<DocumentFragment />
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<DocumentFragment />
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<DocumentFragment />
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<DocumentFragment />
1 change: 1 addition & 0 deletions frontend/src/components/common/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const checkExports = [
'Chart',
'ConfirmDialog',
'ConfirmButton',
'CreateResourceButton',
'Dialog',
'EmptyContent',
'ErrorPage',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ export { default as ConfirmButton } from './ConfirmButton';
export * from './NamespacesAutocomplete';
export * from './Table/Table';
export { default as Table } from './Table';
export * from './CreateResourceButton';
17 changes: 9 additions & 8 deletions frontend/src/i18n/locales/de/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@
"Something went wrong.": "Etwas ist schief gelaufen.",
"No": "Nein",
"Yes": "Ja",
"Failed to create {{ kind }} {{ name }}.": "Erstellung von {{ kind }} fehlgeschlagen {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Fehler beim Erstellen von {{ kind }} {{ name }} in {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Ungültig: Eine oder mehrere der Ressourcen haben keine Namenseigenschaft",
"Invalid: Please set a kind to the resource": "Ungültig: Bitte geben Sie einen Typ für die Ressource an",
"Applying {{ newItemName }}…": "Anwenden von {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Die Anwendung von {{ newItemName }} wurde abgebrochen.",
"Applied {{ newItemName }}.": "Angewandt {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Die Anwendung von {{ newItemName }} ist fehlgeschlagen.",
"Create {{ resourceName }}": "",
"Toggle fullscreen": "Vollbild ein/aus",
"Close": "Schließen",
"Uh-oh! Something went wrong.": "Oh-oh! Etwas ist schief gelaufen.",
Expand Down Expand Up @@ -154,14 +163,6 @@
"Read more": "Mehr lesen",
"Dismiss": "Schließen",
"Install the metrics-server to get usage data.": "Installieren Sie den Metriken-Server, um Nutzungsdaten zu erhalten.",
"Failed to create {{ kind }} {{ name }}.": "Erstellung von {{ kind }} fehlgeschlagen {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Fehler beim Erstellen von {{ kind }} {{ name }} in {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Ungültig: Eine oder mehrere der Ressourcen haben keine Namenseigenschaft",
"Invalid: Please set a kind to the resource": "Ungültig: Bitte geben Sie einen Typ für die Ressource an",
"Applying {{ newItemName }}…": "Anwenden von {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Die Anwendung von {{ newItemName }} wurde abgebrochen.",
"Applied {{ newItemName }}.": "Angewandt {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Die Anwendung von {{ newItemName }} ist fehlgeschlagen.",
"Create / Apply": "Erstellen / Anwenden",
"Create": "Erstellen",
"Deleting item {{ itemName }}…": "Lösche Element {{ itemName }} …",
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@
"Something went wrong.": "Something went wrong.",
"No": "No",
"Yes": "Yes",
"Failed to create {{ kind }} {{ name }}.": "Failed to create {{ kind }} {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Invalid: One or more of resources doesn't have a name property",
"Invalid: Please set a kind to the resource": "Invalid: Please set a kind to the resource",
"Applying {{ newItemName }}…": "Applying {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Cancelled applying {{ newItemName }}.",
"Applied {{ newItemName }}.": "Applied {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Failed to apply {{ newItemName }}.",
"Create {{ resourceName }}": "Create {{ resourceName }}",
"Toggle fullscreen": "Toggle fullscreen",
"Close": "Close",
"Uh-oh! Something went wrong.": "Uh-oh! Something went wrong.",
Expand Down Expand Up @@ -154,14 +163,6 @@
"Read more": "Read more",
"Dismiss": "Dismiss",
"Install the metrics-server to get usage data.": "Install the metrics-server to get usage data.",
"Failed to create {{ kind }} {{ name }}.": "Failed to create {{ kind }} {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Invalid: One or more of resources doesn't have a name property",
"Invalid: Please set a kind to the resource": "Invalid: Please set a kind to the resource",
"Applying {{ newItemName }}…": "Applying {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Cancelled applying {{ newItemName }}.",
"Applied {{ newItemName }}.": "Applied {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Failed to apply {{ newItemName }}.",
"Create / Apply": "Create / Apply",
"Create": "Create",
"Deleting item {{ itemName }}…": "Deleting item {{ itemName }}…",
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/i18n/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@
"Something went wrong.": "Algo ha fallado.",
"No": "No",
"Yes": "",
"Failed to create {{ kind }} {{ name }}.": "Fallo al crear {{ kind }} {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Fallo al crear {{ kind }} {{ name }} en {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Inválido: Uno o más recursos no tiene la propriedad \"name\"",
"Invalid: Please set a kind to the resource": "Inválido: Por favor asigne el \"kind\" al recurso.",
"Applying {{ newItemName }}…": "Aplicando {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Se ha cancelado la aplicación de {{ newItemName }}.",
"Applied {{ newItemName }}.": "Se ha aplicado {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Fallo al aplicar {{ newItemName }}.",
"Create {{ resourceName }}": "",
"Toggle fullscreen": "Alternar pantalla completa",
"Close": "Cerrar",
"Uh-oh! Something went wrong.": "¡Ups! Algo ha fallado.",
Expand Down Expand Up @@ -155,14 +164,6 @@
"Read more": "Leer más",
"Dismiss": "Descartar",
"Install the metrics-server to get usage data.": "Instale el metrics-server para obtener datos de uso.",
"Failed to create {{ kind }} {{ name }}.": "Fallo al crear {{ kind }} {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Fallo al crear {{ kind }} {{ name }} en {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Inválido: Uno o más recursos no tiene la propriedad \"name\"",
"Invalid: Please set a kind to the resource": "Inválido: Por favor asigne el \"kind\" al recurso.",
"Applying {{ newItemName }}…": "Aplicando {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Se ha cancelado la aplicación de {{ newItemName }}.",
"Applied {{ newItemName }}.": "Se ha aplicado {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Fallo al aplicar {{ newItemName }}.",
"Create / Apply": "Crear / Aplicar",
"Create": "Crear",
"Deleting item {{ itemName }}…": "Eliminando item {{ itemName }}…",
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/i18n/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@
"Something went wrong.": "Quelque chose s'est mal passé.",
"No": "Non",
"Yes": "Oui",
"Failed to create {{ kind }} {{ name }}.": "Échec de la création de {{ kind }} {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Échec de la création de {{ kind }} {{ name }} dans {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Non valide : Une ou plusieurs ressources n'ont pas de propriété nom",
"Invalid: Please set a kind to the resource": "Non valide : Veuillez définir un type pour la ressource",
"Applying {{ newItemName }}…": "Application {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Annulation de l'application {{ newItemName }}.",
"Applied {{ newItemName }}.": "Appliqué {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Échec de l'application de {{ newItemName }}.",
"Create {{ resourceName }}": "",
"Toggle fullscreen": "Basculer en mode plein écran",
"Close": "Fermer",
"Uh-oh! Something went wrong.": "Uh-oh ! Quelque chose s'est mal passé.",
Expand Down Expand Up @@ -155,14 +164,6 @@
"Read more": "Lire la suite",
"Dismiss": "Rejeter",
"Install the metrics-server to get usage data.": "Installez le serveur de métriques pour obtenir des données d'utilisation.",
"Failed to create {{ kind }} {{ name }}.": "Échec de la création de {{ kind }} {{ name }}.",
"Failed to create {{ kind }} {{ name }} in {{ apiVersion }}.": "Échec de la création de {{ kind }} {{ name }} dans {{ apiVersion }}.",
"Invalid: One or more of resources doesn't have a name property": "Non valide : Une ou plusieurs ressources n'ont pas de propriété nom",
"Invalid: Please set a kind to the resource": "Non valide : Veuillez définir un type pour la ressource",
"Applying {{ newItemName }}…": "Application {{ newItemName }}…",
"Cancelled applying {{ newItemName }}.": "Annulation de l'application {{ newItemName }}.",
"Applied {{ newItemName }}.": "Appliqué {{ newItemName }}.",
"Failed to apply {{ newItemName }}.": "Échec de l'application de {{ newItemName }}.",
"Create / Apply": "Créer / Appliquer",
"Create": "Créer",
"Deleting item {{ itemName }}…": "Suppression de l'élément {{ itemName }}…",
Expand Down
Loading

0 comments on commit a335812

Please sign in to comment.