From 6dfb016f6e1c734745b593b324a3170bb0ff9f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Goral?= Date: Thu, 17 Jul 2025 10:59:10 +0200 Subject: [PATCH 1/7] init --- src/components/Projects/ProjectsList.tsx | 30 ++++++++++--- .../Projects/ProjectsListItemMenu.tsx | 45 +++++++++++++++++++ 2 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 src/components/Projects/ProjectsListItemMenu.tsx diff --git a/src/components/Projects/ProjectsList.tsx b/src/components/Projects/ProjectsList.tsx index bbaf8c69..cc0cdc5c 100644 --- a/src/components/Projects/ProjectsList.tsx +++ b/src/components/Projects/ProjectsList.tsx @@ -1,7 +1,4 @@ -import { - AnalyticalTable, - AnalyticalTableColumnDefinition, -} from '@ui5/webcomponents-react'; +import { AnalyticalTable, AnalyticalTableColumnDefinition } from '@ui5/webcomponents-react'; import { ThemingParameters } from '@ui5/webcomponents-react-base'; import { CopyButton } from '../Shared/CopyButton.tsx'; import useLuigiNavigate from '../Shared/useLuigiNavigate.tsx'; @@ -14,6 +11,7 @@ import { ListProjectNames } from '../../lib/api/types/crate/listProjectNames'; import { t } from 'i18next'; import { YamlViewButtonWithLoader } from '../Yaml/YamlViewButtonWithLoader.tsx'; import { useMemo } from 'react'; +import { ProjectsListItemMenu } from './ProjectsListItemMenu.tsx'; export default function ProjectsList() { const navigate = useLuigiNavigate(); @@ -94,6 +92,26 @@ export default function ProjectsList() { ), }, + { + Header: t('common.options'), + accessor: 'options', + width: 85, + disableFilters: true, + hAlign: 'Center', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Cell: (instance: any) => ( +
+ {}} /> +
+ ), + }, ], [], ); @@ -109,9 +127,7 @@ export default function ProjectsList() { data={stabilizedData} // eslint-disable-next-line @typescript-eslint/no-explicit-any onRowClick={(e: any) => { - navigate( - `/mcp/projects/${data ? [e.detail.row.values.projectName] : ''}`, - ); + navigate(`/mcp/projects/${data ? [e.detail.row.values.projectName] : ''}`); }} /> diff --git a/src/components/Projects/ProjectsListItemMenu.tsx b/src/components/Projects/ProjectsListItemMenu.tsx new file mode 100644 index 00000000..3a6e3bb6 --- /dev/null +++ b/src/components/Projects/ProjectsListItemMenu.tsx @@ -0,0 +1,45 @@ +import { Button, ButtonDomRef, Menu, MenuItem, Ui5CustomEvent, MenuDomRef } from '@ui5/webcomponents-react'; +import type { ButtonClickEventDetail } from '@ui5/webcomponents/dist/Button.js'; +import { Dispatch, FC, SetStateAction, useRef, useState } from 'react'; +import '@ui5/webcomponents-icons/dist/copy'; +import '@ui5/webcomponents-icons/dist/accept'; + +import { useTranslation } from 'react-i18next'; + +type ProjectsListItemMenuProps = { + setDialogDeleteProjectIsOpen: Dispatch>; +}; + +export const ProjectsListItemMenu: FC = ({ setDialogDeleteProjectIsOpen }) => { + const popoverRef = useRef(null); + const [open, setOpen] = useState(false); + + const { t } = useTranslation(); + + const handleOpenerClick = (e: Ui5CustomEvent) => { + if (popoverRef.current && e.currentTarget) { + popoverRef.current.opener = e.currentTarget as HTMLElement; + setOpen((prev) => !prev); + } + }; + + return ( +
+
+ ); +}; From e32727e842ade6cced7adfd645ec0252a150f184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Goral?= Date: Thu, 17 Jul 2025 12:48:01 +0200 Subject: [PATCH 2/7] init --- public/locales/en.json | 7 +++- .../Dialogs/DeleteConfirmationDialog.tsx | 27 +++---------- src/components/Projects/ProjectsList.tsx | 4 +- .../Projects/ProjectsListItemMenu.tsx | 39 ++++++++++++++++--- .../Yaml/YamlViewButtonWithLoader.tsx | 12 +----- src/lib/api/types/crate/deleteProject.ts | 15 +++++++ 6 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 src/lib/api/types/crate/deleteProject.ts diff --git a/public/locales/en.json b/public/locales/en.json index 1850be24..1110e3fe 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -157,7 +157,9 @@ }, "ProjectsListView": { "pageTitle": "Let's get started", - "title": "Projects" + "title": "Projects", + "deleteProject": "Delete project", + "deleteConfirmationDialog": "Project deleted" }, "ControlPlaneView": { "accessError": "Managed Control Plane does not have access information yet", @@ -288,7 +290,8 @@ "search": "Search", "components": "Components", "notSelected": "Not selected", - "btp": "BTP" + "btp": "BTP", + "options": "Options" }, "buttons": { "viewResource": "View resource", diff --git a/src/components/Dialogs/DeleteConfirmationDialog.tsx b/src/components/Dialogs/DeleteConfirmationDialog.tsx index c63f1c57..6b51f8be 100644 --- a/src/components/Dialogs/DeleteConfirmationDialog.tsx +++ b/src/components/Dialogs/DeleteConfirmationDialog.tsx @@ -1,15 +1,5 @@ import { ReactNode, useEffect, useRef, useState } from 'react'; -import { - Bar, - Button, - Dialog, - Form, - FormGroup, - FormItem, - Input, - InputDomRef, - Label, -} from '@ui5/webcomponents-react'; +import { Bar, Button, Dialog, Form, FormGroup, FormItem, Input, InputDomRef, Label } from '@ui5/webcomponents-react'; import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; import { useTranslation } from 'react-i18next'; @@ -74,7 +64,7 @@ export function DeleteConfirmationDialog({ - - - } + + + + {kubectl} + + } + /> + } + > +
+ + , + }} /> - } - > -
- - - - - - - {t('DeleteConfirmationDialog.deleteMessageType')} {resourceName}{' '} - {t('DeleteConfirmationDialog.deleteMessageConfirm')} - - } - > - - - {kubectl} - -
-
- + + + + + ); } diff --git a/src/components/Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteProject.tsx b/src/components/Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteProject.tsx index 183162f6..7e02d8e3 100644 --- a/src/components/Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteProject.tsx +++ b/src/components/Dialogs/KubectlCommandInfo/Controllers/KubectlDeleteProject.tsx @@ -1,13 +1,13 @@ import { useState } from 'react'; import { KubectlInfoButton } from '../KubectlInfoButton'; -import { DeleteProjectDialog } from '../KubectlDeleteProjectDialog.tsx'; +import { KubectlDeleteProjectDialog } from '../KubectlDeleteProjectDialog.tsx'; -interface KubectlDeleteWorkspaceProps { +interface KubectlDeleteProjectProps { projectName?: string; } -export const KubectlDeleteProject = ({ projectName }: KubectlDeleteWorkspaceProps) => { +export const KubectlDeleteProject = ({ projectName }: KubectlDeleteProjectProps) => { const [isInfoDialogOpen, setIsInfoDialogOpen] = useState(false); const openInfoDialog = () => setIsInfoDialogOpen(true); @@ -16,7 +16,7 @@ export const KubectlDeleteProject = ({ projectName }: KubectlDeleteWorkspaceProp return ( <> - + ); }; diff --git a/src/components/Dialogs/KubectlCommandInfo/KubectlDeleteProjectDialog.tsx b/src/components/Dialogs/KubectlCommandInfo/KubectlDeleteProjectDialog.tsx index 58ecafdc..1ae3d95c 100644 --- a/src/components/Dialogs/KubectlCommandInfo/KubectlDeleteProjectDialog.tsx +++ b/src/components/Dialogs/KubectlCommandInfo/KubectlDeleteProjectDialog.tsx @@ -3,14 +3,14 @@ import { Text } from '@ui5/webcomponents-react'; import { useTranslation, Trans } from 'react-i18next'; import { Fragment } from 'react/jsx-runtime'; -interface DeleteProjectDialogProps { +interface KubectlDeleteProjectDialogProps { onClose: () => void; resourceName?: string; projectName?: string; isOpen: boolean; } -export const DeleteProjectDialog = ({ onClose, projectName, isOpen }: DeleteProjectDialogProps) => { +export const KubectlDeleteProjectDialog = ({ onClose, projectName, isOpen }: KubectlDeleteProjectDialogProps) => { const { t } = useTranslation(); const projectNamespace = projectName ?? '"'; @@ -32,7 +32,7 @@ export const DeleteProjectDialog = ({ onClose, projectName, isOpen }: DeleteProj , bold2: , diff --git a/src/lib/api/types/crate/deleteProject.ts b/src/lib/api/types/crate/deleteProject.ts index ff3b237d..4b27abcf 100644 --- a/src/lib/api/types/crate/deleteProject.ts +++ b/src/lib/api/types/crate/deleteProject.ts @@ -1,10 +1,5 @@ import { Resource } from '../resource'; -export interface DeleteProjectType { - name: string; - namespace: string; -} - export const DeleteProjectResource = (projectName: string): Resource => { return { path: `/apis/core.openmcp.cloud/v1alpha1/projects/${projectName}`,