From 553a2e91036e0c0d67fa0c3e76836e5178d2ec58 Mon Sep 17 00:00:00 2001 From: Davit Date: Sun, 30 Jun 2024 21:03:05 +0400 Subject: [PATCH 01/16] use dynamic length for deployments --- src/components/DeploymentsByFilter/index.js | 102 ++++++++++---------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/src/components/DeploymentsByFilter/index.js b/src/components/DeploymentsByFilter/index.js index f9849b9f..93be94a9 100644 --- a/src/components/DeploymentsByFilter/index.js +++ b/src/components/DeploymentsByFilter/index.js @@ -118,10 +118,12 @@ const DeploymentsByFilter = ({ deployments }) => { }); }; + const sortedFilteredItems = sortedItems.filter(deployment => filterResults(deployment)); + return (
- + { - {!sortedItems.filter(deployment => filterResults(deployment)).length && ( -
No deployments
- )} - {sortedItems - .filter(deployment => filterResults(deployment)) - .map(deployment => { - return ( -
-
- - {formatString(deployment.environment.project.name, 'project')} - -
-
- - {formatString(deployment.environment.name, 'environment')} - -
-
{formatString(deployment.environment.openshift.name, 'cluster')}
-
- - {deployment.name} - -
-
{deployment.priority}
-
- {moment.utc(deployment.created).local().format('DD MMM YYYY, HH:mm:ss (Z)')} -
-
- {deployment.status.charAt(0).toUpperCase() + deployment.status.slice(1)} - - {!['complete', 'cancelled', 'failed'].includes(deployment.status) && deployment.buildStep && ( - - )} -
-
{getDeploymentDuration(deployment)}
-
- {['new', 'pending', 'queued', 'running'].includes(deployment.status) && ( - - )} -
+ {!sortedFilteredItems.length &&
No deployments
} + {sortedFilteredItems.map(deployment => { + return ( +
+
+ + {formatString(deployment.environment.project.name, 'project')} + +
+
+ + {formatString(deployment.environment.name, 'environment')} + +
+
{formatString(deployment.environment.openshift.name, 'cluster')}
+
+ + {deployment.name} + +
+
{deployment.priority}
+
+ {moment.utc(deployment.created).local().format('DD MMM YYYY, HH:mm:ss (Z)')} +
+
+ {deployment.status.charAt(0).toUpperCase() + deployment.status.slice(1)} + + {!['complete', 'cancelled', 'failed'].includes(deployment.status) && deployment.buildStep && ( + + )} +
+
{getDeploymentDuration(deployment)}
+
+ {['new', 'pending', 'queued', 'running'].includes(deployment.status) && ( + + )}
- ); - })} +
+ ); + })} ); From 1d6e6044508bddf6d0daaf3d31e779b59cfe0645 Mon Sep 17 00:00:00 2001 From: Davit Date: Sun, 30 Jun 2024 22:29:28 +0400 Subject: [PATCH 02/16] show which user is being removed --- src/components/Organizations/SharedStyles.tsx | 3 +++ src/components/Organizations/Users/index.js | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Organizations/SharedStyles.tsx b/src/components/Organizations/SharedStyles.tsx index ba652c07..4fe024a6 100644 --- a/src/components/Organizations/SharedStyles.tsx +++ b/src/components/Organizations/SharedStyles.tsx @@ -450,6 +450,9 @@ export const RemoveModalParagraph = styled.p` line-height: 24px; span { font-weight: bold; + &.highlight { + color: #4b84ff; + } } `; diff --git a/src/components/Organizations/Users/index.js b/src/components/Organizations/Users/index.js index ef62e6c0..fb7882bd 100644 --- a/src/components/Organizations/Users/index.js +++ b/src/components/Organizations/Users/index.js @@ -132,7 +132,7 @@ const Users = ({ users = [], organization, organizationId, organizationName, ref {!user.email.startsWith('default-user') ? ( <> - + { @@ -144,7 +144,8 @@ const Users = ({ users = [], organization, organizationId, organizationName, ref Remove user? - This action will remove this user from all groups, you might not be able to reverse this. + This action will remove user {user.email} from all groups, you + might not be able to reverse this.
From eb95f7be584393fffdb9d775dbc766bd7d342c74 Mon Sep 17 00:00:00 2001 From: Davit Date: Mon, 1 Jul 2024 01:55:20 +0400 Subject: [PATCH 03/16] change wording --- src/components/Organizations/Users/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Organizations/Users/index.js b/src/components/Organizations/Users/index.js index fb7882bd..e3579f8c 100644 --- a/src/components/Organizations/Users/index.js +++ b/src/components/Organizations/Users/index.js @@ -144,8 +144,8 @@ const Users = ({ users = [], organization, organizationId, organizationName, ref Remove user? - This action will remove user {user.email} from all groups, you - might not be able to reverse this. + This action will remove user {user.email} from all groups in this + organization, you might not be able to reverse this.
From 0b1d4f52825b2036157e40002dfe58e36da32e48 Mon Sep 17 00:00:00 2001 From: Davit Date: Mon, 1 Jul 2024 12:36:23 +0400 Subject: [PATCH 04/16] add friendlyname and description debounced filtering --- .../Organizations/OrganizationsSkeleton.tsx | 14 +- .../Organizations/Organizations/index.tsx | 133 +++++++++++++----- src/pages/organizations/index.js | 9 +- 3 files changed, 117 insertions(+), 39 deletions(-) diff --git a/src/components/Organizations/Organizations/OrganizationsSkeleton.tsx b/src/components/Organizations/Organizations/OrganizationsSkeleton.tsx index 40191e0c..e0d9a676 100644 --- a/src/components/Organizations/Organizations/OrganizationsSkeleton.tsx +++ b/src/components/Organizations/Organizations/OrganizationsSkeleton.tsx @@ -5,7 +5,11 @@ import Box from 'components/Box'; import { Organization, OrganizationsPage, OrgsHeader, SearchInput } from './StyledOrganizations'; -const OrganizationsSkeleton = () => { +interface Props { + setSearch: React.Dispatch>; +} + +const OrganizationsSkeleton = ({ setSearch }: Props) => { const RenderSkeletonBox = (index: number) => { return ( @@ -27,7 +31,13 @@ const OrganizationsSkeleton = () => { - + setSearch(e.target.value)} + aria-labelledby="search" + className="searchInput" + type="text" + placeholder="Type to search" + /> <>{[...Array(numberOfItems)].map((_, idx) => RenderSkeletonBox(idx))} diff --git a/src/components/Organizations/Organizations/index.tsx b/src/components/Organizations/Organizations/index.tsx index 8d95711a..2ac00fb5 100644 --- a/src/components/Organizations/Organizations/index.tsx +++ b/src/components/Organizations/Organizations/index.tsx @@ -1,8 +1,11 @@ -import React, { useState } from 'react'; +import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import Highlighter from 'react-highlight-words'; +import { LoadingOutlined } from '@ant-design/icons'; +import { Spin } from 'antd'; import Box from 'components/Box'; import OrganizationLink from 'components/link/Organizations/Organization'; +import { debounce } from 'lib/util'; import { Organization, OrganizationsPage, OrgsHeader, SearchInput } from './StyledOrganizations'; @@ -14,29 +17,112 @@ export interface IOrganization { __typename: 'Organization'; } +interface OrganizationProps { + organizations: IOrganization[]; + initialSearch: string; +} /** * The primary list of organizations. */ -const Organizations = ({ organizations = [] }: { organizations: IOrganization[] }) => { - const [searchInput, setSearchInput] = useState(''); +const Organizations: FC = ({ organizations = [], initialSearch }) => { + const [searchInput, setSearchInput] = useState(initialSearch || ''); + + const [isFiltering, setIsFiltering] = useState(false); + const [filteredOrgs, setFilteredOrgs] = useState(organizations); + + const searchInputRef = useRef(null); + + useEffect(() => { + if (initialSearch && searchInputRef.current) { + searchInputRef.current.focus(); + } + }, []); + + const timerLengthPercentage = useMemo( + () => Math.min(1000, Math.max(40, Math.floor(organizations.length * 0.0725))), + [organizations.length] + ); + + const debouncedSearch = useCallback( + debounce((searchVal: string) => { + setSearchInput(searchVal); + }, timerLengthPercentage), + [] + ); + + const handleSearch = (searchVal: string) => { + setIsFiltering(true); + debouncedSearch(searchVal); + }; - const filteredOrganizations = organizations.filter(key => { - const sortByName = key.name.toLowerCase().includes(searchInput.toLowerCase()); - const sortByUrl = ''; - return ['name', 'environments', '__typename'].includes(key.name) ? false : (true && sortByName) || sortByUrl; - }); + useEffect(() => { + const filterOrgs = async (): Promise => { + return new Promise(resolve => { + const filteredOrganizations = organizations.filter(org => { + const searchStrLowerCase = searchInput.toLowerCase(); + const filterFn = (key?: string) => key?.toLowerCase().includes(searchStrLowerCase); + + const sortByName = filterFn(org.name); + const sortByDesc = filterFn(org.description); + const sortByFriendlyName = filterFn(org.friendlyName); + + if (['__typename', 'name', 'id'].includes(org.name)) { + return false; + } + return sortByName || sortByFriendlyName || sortByDesc; + }); + + resolve(filteredOrganizations); + }); + }; + + filterOrgs() + .then(filtered => setFilteredOrgs(filtered)) + .finally(() => setIsFiltering(false)); + }, [searchInput, organizations]); + + const filteredMappedOrgs = useMemo(() => { + return filteredOrgs.map(organization => ( + + + +

+ +

+
+ +
+
+
+
+
+ )); + }, [filteredOrgs]); return ( - + setSearchInput(e.target.value)} + onChange={e => handleSearch(e.target.value)} placeholder="Type to search" disabled={organizations.length === 0} /> @@ -48,37 +134,14 @@ const Organizations = ({ organizations = [] }: { organizations: IOrganization[]
)} - {searchInput && !filteredOrganizations.length && ( + {searchInput && !filteredMappedOrgs.length && (

No organizations matching "{searchInput}"

)} - {filteredOrganizations.map(organization => ( - - - -

- -

-
- -
-
-
-
-
- ))} + {filteredMappedOrgs} ); }; diff --git a/src/pages/organizations/index.js b/src/pages/organizations/index.js index c6d341f4..b95c99dd 100644 --- a/src/pages/organizations/index.js +++ b/src/pages/organizations/index.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import Head from 'next/head'; @@ -18,6 +18,7 @@ const OrganizationsPage = () => { const { data, error, loading } = useQuery(AllOrganizationsQuery, { displayName: 'AllOrganizationsQuery', }); + const [searchInput, setSearchInput] = useState(''); if (error) { return ; @@ -31,7 +32,11 @@ const OrganizationsPage = () => {

Organizations

- {loading ? : } + {loading ? ( + + ) : ( + + )}
From 0509a78dfd571ca0583385ba8d8b4ac9640c0e75 Mon Sep 17 00:00:00 2001 From: Davit Date: Mon, 1 Jul 2024 12:42:31 +0400 Subject: [PATCH 05/16] linr --- src/components/Organizations/Organizations/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Organizations/Organizations/index.tsx b/src/components/Organizations/Organizations/index.tsx index 2ac00fb5..00ac463c 100644 --- a/src/components/Organizations/Organizations/index.tsx +++ b/src/components/Organizations/Organizations/index.tsx @@ -76,7 +76,7 @@ const Organizations: FC = ({ organizations = [], initialSearc }); }; - filterOrgs() + void filterOrgs() .then(filtered => setFilteredOrgs(filtered)) .finally(() => setIsFiltering(false)); }, [searchInput, organizations]); From 3e31ce863e5bb8de879d1112b11cf92aa8942315 Mon Sep 17 00:00:00 2001 From: Davit Date: Mon, 1 Jul 2024 16:50:10 +0400 Subject: [PATCH 06/16] properly update notifications --- .../Organizations/Notifications/index.js | 800 +++++++++--------- 1 file changed, 405 insertions(+), 395 deletions(-) diff --git a/src/components/Organizations/Notifications/index.js b/src/components/Organizations/Notifications/index.js index cfa06c0b..e3e19dd3 100644 --- a/src/components/Organizations/Notifications/index.js +++ b/src/components/Organizations/Notifications/index.js @@ -100,6 +100,7 @@ const OrgNotifications = ({ open: false, type: '', current: {}, + updated: {}, }; const [editState, setEditState] = useState(initialEditState); @@ -130,8 +131,8 @@ const OrgNotifications = ({ const newValue = e.target.value; setEditState(prevState => ({ ...prevState, - current: { - ...prevState.current, + updated: { + ...prevState.updated, [property]: newValue, }, })); @@ -185,94 +186,95 @@ const OrgNotifications = ({ - - -
- -
- -
- -
- -
- -
-
-
- console.error(e)}> - {(updateSlack, { called, error, data }) => { - if (error) { - return
{error.message}
; - } - if (data) { - refresh().then(() => { - closeEditModal(); - }); - } - return ( - - ); - }} -
- -
-
- + }); + }} + > + Continue + + ); + }} + + +
+
+ )} console.error(e)}> {(removeNotification, { called, error, data }) => { if (data) { @@ -316,93 +318,95 @@ const OrgNotifications = ({ - - -
- -
- -
- -
- -
- -
-
-
- console.error(e)}> - {(updateRocketChat, { called, error, data }) => { - if (error) { - return
{error.message}
; - } - if (data) { - refresh().then(() => { - closeEditModal(); - }); - } - return ( - - ); - }} -
- -
-
+ }); + }} + > + Continue + + ); + }} +
+ +
+
+ )} console.error(e)}> {(removeNotification, { called, error, data }) => { if (error) { @@ -449,82 +453,84 @@ const OrgNotifications = ({
- - -
- -
- -
- - {!isValidEmail &&

Invalid email address

} -
-
-
- console.error(e)}> - {(updateEmail, { called, error, data }) => { - if (error) { - return
{error.message}
; - } - if (data) { - refresh().then(() => { - closeEditModal(); - }); - } - return ( - - ); - }} -
- -
-
+ {editState.current && editState.current.name === notification.name && ( + + +
+ +
+ +
+ + {!isValidEmail &&

Invalid email address

} +
+
+
+ console.error(e)}> + {(updateEmail, { called, error, data }) => { + if (error) { + return
{error.message}
; + } + if (data) { + refresh().then(() => { + closeEditModal(); + }); + } + return ( + + ); + }} +
+ +
+
+ )} console.error(e)}> {(removeNotification, { called, error, data }) => { if (data) { @@ -574,80 +580,81 @@ const OrgNotifications = ({
- - -
- -
- -
- -
-
-
- console.error(e)}> - {(updateWebhook, { called, error, data }) => { - if (error) { - return
{error.message}
; - } - if (data) { - refresh().then(() => { - closeEditModal(); - }); - } - return ( - - ); - }} -
- -
-
- + }); + }} + > + Continue + + ); + }} + + + + + )} console.error(e)}> {(removeNotification, { called, error, data }) => { if (data) { @@ -693,79 +700,82 @@ const OrgNotifications = ({ - - -
- -
- -
- -
-
-
- console.error(e)}> - {(updateTeams, { called, error, data }) => { - if (error) { - return
{error.message}
; - } - if (data) { - refresh().then(() => { - closeEditModal(); - }); - } - return ( - - ); - }} -
- -
-
+ }); + }} + > + Continue + + ); + }} +
+ + + + )} console.error(e)}> {(removeNotification, { called, error, data }) => { if (data) { From dce718b174081d0ebbf9beb0b8bee3ed0c82292d Mon Sep 17 00:00:00 2001 From: Toby Bellwood Date: Wed, 3 Jul 2024 16:57:37 +1000 Subject: [PATCH 07/16] chore: set network-timeout on yarn run build --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e5dd783e..bdae267f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Node builder image -FROM uselagoon/node-20-builder:latest as builder +FROM uselagoon/node-20-builder:latest AS builder COPY . /app/ @@ -25,7 +25,7 @@ ARG GRAPHQL_API ENV GRAPHQL_API=$GRAPHQL_API # Build app -RUN yarn run build +RUN yarn --network-timeout 300000 run build EXPOSE 3000 -CMD ["yarn", "start"] \ No newline at end of file +CMD ["yarn", "start"] From c0b73468f568b34853490591257407b2672f868f Mon Sep 17 00:00:00 2001 From: Toby Bellwood Date: Wed, 3 Jul 2024 17:26:17 +1000 Subject: [PATCH 08/16] chore: set network-timeout on yarn install --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index bdae267f..2805fc09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM uselagoon/node-20-builder:latest AS builder COPY . /app/ -RUN yarn install +RUN yarn install --network-timeout 300000 # Node service image @@ -25,7 +25,7 @@ ARG GRAPHQL_API ENV GRAPHQL_API=$GRAPHQL_API # Build app -RUN yarn --network-timeout 300000 run build +RUN yarn run build EXPOSE 3000 CMD ["yarn", "start"] From f3a42244220dba8b59102dcfa5b432f383941aad Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Wed, 3 Jul 2024 19:33:24 +1000 Subject: [PATCH 09/16] chore: add sidecar --- test/docker-compose.yaml | 14 +++++++++++++- test/keycloak/configure-keycloak.sh | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/docker-compose.yaml b/test/docker-compose.yaml index c49e7404..83cbf2bc 100644 --- a/test/docker-compose.yaml +++ b/test/docker-compose.yaml @@ -28,6 +28,12 @@ services: condition: service_completed_successfully # don't start the lagoon migrations until the db migrations is completed keycloak: condition: service_started + api-sidecar-handler: + # this is neded for the internal dns references + container_name: apisidecarhandler + image: testlagoon/api-sidecar-handler:main + ports: + - '3333:3333' api: image: testlagoon/api:main ports: @@ -44,8 +50,14 @@ services: - S3_BAAS_ACCESS_KEY_ID=minio - S3_BAAS_SECRET_ACCESS_KEY=minio123 - CONSOLE_LOGGING_LEVEL=trace + - SIDECAR_HANDLER_HOST=apisidecarhandler depends_on: - - api-lagoon-migrations + api-lagoon-migrations: + condition: service_started + keycloak: + condition: service_started + api-sidecar-handler: + condition: service_started api-redis: image: testlagoon/api-redis:main keycloak: diff --git a/test/keycloak/configure-keycloak.sh b/test/keycloak/configure-keycloak.sh index 0aa788c0..aee9bf06 100755 --- a/test/keycloak/configure-keycloak.sh +++ b/test/keycloak/configure-keycloak.sh @@ -10,7 +10,7 @@ function is_keycloak_running { function configure_user_passwords { LAGOON_DEMO_USERS=("guest@example.com" "reporter@example.com" "developer@example.com" "maintainer@example.com" "owner@example.com") - LAGOON_DEMO_ORG_USERS=("orguser@example.com" "orgviewer@example.com" "orgowner@example.com" "platformowner@example.com") + LAGOON_DEMO_ORG_USERS=("orguser@example.com" "orgviewer@example.com" "orgadmin@example.com" "orgowner@example.com" "platformowner@example.com") for i in ${LAGOON_DEMO_USERS[@]} do From 34d1e3a89aa81b68d46aa395d820d3818ff6ab70 Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Wed, 3 Jul 2024 19:58:21 +1000 Subject: [PATCH 10/16] chore: increase command timeout --- cypress/cypress.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress/cypress.config.ts b/cypress/cypress.config.ts index 9954393f..27492a32 100644 --- a/cypress/cypress.config.ts +++ b/cypress/cypress.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'cypress'; export default defineConfig({ requestTimeout: 15000, + defaultCommandTimeout: 8000, e2e: { env: { api: 'http://0.0.0.0:33000/graphql', From ef6950b701fce8f0446108be8b94bc9b18484b0b Mon Sep 17 00:00:00 2001 From: Davit Darsavelidze <76407236+DaveDarsa@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:11:34 +0400 Subject: [PATCH 11/16] change: add branches and pullrequests during org project creation (#276) * add branches and pullrequests * textboxes for branches and prs * add tooltips * reduce spacing, add default indicator * fix pr/branches text * format tooltips as lists * cypress deployment tests * remove only * format * org project tests --- cypress/e2e/organizations/overview.cy.ts | 2 +- cypress/e2e/rbac/developer.cy.ts | 4 +- cypress/e2e/rbac/guest.cy.ts | 2 +- cypress/e2e/rbac/maintainer.cy.ts | 4 +- cypress/e2e/rbac/reporter.cy.ts | 4 +- .../actions/organizations/ProjectsActions.ts | 4 +- .../NewProject/StyledNewProject.tsx | 3 + .../Organizations/NewProject/index.js | 78 ++++++++++++++++++- src/layouts/GlobalStyles/index.tsx | 15 ++++ 9 files changed, 106 insertions(+), 10 deletions(-) diff --git a/cypress/e2e/organizations/overview.cy.ts b/cypress/e2e/organizations/overview.cy.ts index a02daa23..18debc2e 100644 --- a/cypress/e2e/organizations/overview.cy.ts +++ b/cypress/e2e/organizations/overview.cy.ts @@ -23,7 +23,7 @@ describe('Organization overview page', () => { overview.doQuotaFieldCheck(); }); - it.only('Changes org friendly name/description', () => { + it('Changes org friendly name/description', () => { registerIdleHandler('idle'); overview.changeOrgFriendlyname(testData.organizations.overview.friendlyName); diff --git a/cypress/e2e/rbac/developer.cy.ts b/cypress/e2e/rbac/developer.cy.ts index da29806a..a12fc488 100644 --- a/cypress/e2e/rbac/developer.cy.ts +++ b/cypress/e2e/rbac/developer.cy.ts @@ -114,7 +114,7 @@ describe('DEVELOPER permission test suites', () => { environmentOverview.doDeleteEnvironmentError('main'); }); - it('Deletes stating environment', () => { + it('Deletes staging environment', () => { cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-staging`); cy.intercept('POST', Cypress.env('api'), req => { @@ -147,7 +147,7 @@ describe('DEVELOPER permission test suites', () => { }); it('Fails to cancel any deployment - no permission to CANCEL for DEVELOPER', () => { - cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-staging/deployments`); + cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-main/deployments`); registerIdleHandler('idle'); diff --git a/cypress/e2e/rbac/guest.cy.ts b/cypress/e2e/rbac/guest.cy.ts index 6a5679f6..0bed5fe7 100644 --- a/cypress/e2e/rbac/guest.cy.ts +++ b/cypress/e2e/rbac/guest.cy.ts @@ -146,7 +146,7 @@ describe('GUEST permission test suites', () => { }); it('Fails to do cancel a deployment - no permission for GUEST', () => { - cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-staging/deployments`); + cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-main/deployments`); registerIdleHandler('idle'); cy.intercept('POST', Cypress.env('api'), req => { diff --git a/cypress/e2e/rbac/maintainer.cy.ts b/cypress/e2e/rbac/maintainer.cy.ts index 9809a2b2..e7492e52 100644 --- a/cypress/e2e/rbac/maintainer.cy.ts +++ b/cypress/e2e/rbac/maintainer.cy.ts @@ -136,7 +136,7 @@ describe('MAINTAINER permission test suites', () => { environmentOverview.doDeleteEnvironmentError('main'); }); - it('Deletes stating environment', () => { + it('Deletes staging environment', () => { cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-staging`); cy.intercept('POST', Cypress.env('api'), req => { @@ -169,7 +169,7 @@ describe('MAINTAINER permission test suites', () => { }); it('Cancels a staging deployment', () => { - cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-staging/deployments`); + cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-main/deployments`); registerIdleHandler('idle'); diff --git a/cypress/e2e/rbac/reporter.cy.ts b/cypress/e2e/rbac/reporter.cy.ts index 48c90573..4f916a7b 100644 --- a/cypress/e2e/rbac/reporter.cy.ts +++ b/cypress/e2e/rbac/reporter.cy.ts @@ -143,7 +143,7 @@ describe('REPORTER permission test suites', () => { }); it('Fails to do cancel a deployment - no permission for REPORTER', () => { - cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-staging/deployments`); + cy.visit(`${Cypress.env('url')}/projects/lagoon-demo/lagoon-demo-main/deployments`); registerIdleHandler('idle'); @@ -168,6 +168,8 @@ describe('REPORTER permission test suites', () => { cy.waitForNetworkIdle('@idle', 500); deployment.navigateToRunningDeployment(); + + cy.waitForNetworkIdle('@idle', 500); deployment.doFailedCancelDeployment(); }); }); diff --git a/cypress/support/actions/organizations/ProjectsActions.ts b/cypress/support/actions/organizations/ProjectsActions.ts index 98bbc50b..18547b44 100644 --- a/cypress/support/actions/organizations/ProjectsActions.ts +++ b/cypress/support/actions/organizations/ProjectsActions.ts @@ -17,7 +17,7 @@ export default class ProjectsActions { projects.selectTarget(); - projects.getAddConfirm().click(); + projects.getAddConfirm().click({ force: true }); cy.wait('@gqladdProjectToOrganizationMutation'); @@ -32,7 +32,7 @@ export default class ProjectsActions { projects.selectTarget(); - projects.getAddConfirm().click(); + projects.getAddConfirm().click({ force: true }); cy.wait('@gqladdProjectToOrganizationMutation').then(interception => { expect(interception.response?.statusCode).to.eq(200); diff --git a/src/components/Organizations/NewProject/StyledNewProject.tsx b/src/components/Organizations/NewProject/StyledNewProject.tsx index ace03180..a5148fd5 100644 --- a/src/components/Organizations/NewProject/StyledNewProject.tsx +++ b/src/components/Organizations/NewProject/StyledNewProject.tsx @@ -20,6 +20,9 @@ export const StyledNewProject = styled.div` .form-box { margin-bottom: 1rem; + &.spacetop { + margin-top: 1rem; + } } .docs-link { diff --git a/src/components/Organizations/NewProject/index.js b/src/components/Organizations/NewProject/index.js index fd38b7ee..36c2ce42 100644 --- a/src/components/Organizations/NewProject/index.js +++ b/src/components/Organizations/NewProject/index.js @@ -72,6 +72,9 @@ const OrgNewProject = ({ refresh, }) => { const [addUserToProject, setAddUserToProject] = React.useState(true); + const [pullRequests, setPullRequests] = React.useState(''); + const [branches, setBranches] = React.useState(''); + return (
@@ -95,6 +98,8 @@ const OrgNewProject = ({ setProjectName({ target: { value: '' } }); setGitURL({ target: { value: '' } }); setProdEnv({ target: { value: '' } }); + setPullRequests(''); + setBranches(''); setSelectedDeployTarget({ target: { value: '' } }); closeModal(); }); @@ -174,6 +179,74 @@ const OrgNewProject = ({ /> + +
+ +
+ +
+ +
+ Add my user to this project +
@@ -219,7 +293,7 @@ const OrgNewProject = ({ inputGitURL.indexOf(' ') > 0 || inputProdEnv === '' || inputProdEnv.indexOf(' ') > 0 || - selectedDeployTarget === undefined + selectedDeployTarget == undefined } action={() => { addGroupProject({ @@ -230,6 +304,8 @@ const OrgNewProject = ({ productionEnvironment: inputProdEnv, organization: parseInt(organizationId, 10), addOrgOwner: addUserToProject, + ...(pullRequests ? { pullRequests } : {}), + ...(branches ? { branches } : {}), }, }); }} diff --git a/src/layouts/GlobalStyles/index.tsx b/src/layouts/GlobalStyles/index.tsx index 815bc326..401b7a36 100644 --- a/src/layouts/GlobalStyles/index.tsx +++ b/src/layouts/GlobalStyles/index.tsx @@ -41,6 +41,21 @@ body { .ant-tooltip-content .ant-tooltip-inner{ color: ${props => (props.theme.colorScheme === 'dark' ? '#000' : '#fff')}; background: ${props => (props.theme.colorScheme === 'dark' ? '#fff' : '#000')}; + + .tooltiplist{ + margin-left: 0.812rem; + li{ + margin-bottom: initial; + padding-left: 0; + list-style: disc; + } + } + } + &.lg{ + .ant-tooltip-content{ + width:max-content; + } + } } .componentTooltip { From 3f01f916313fa3271cc74794d9e52015e81a45af Mon Sep 17 00:00:00 2001 From: Davit Darsavelidze <76407236+DaveDarsa@users.noreply.github.com> Date: Thu, 4 Jul 2024 17:06:52 +0400 Subject: [PATCH 12/16] fix: autofill values on edit (#275) * autofill values on edit * cypress --------- Co-authored-by: Toby Bellwood --- .../actions/organizations/OverviewAction.ts | 9 ++--- .../Organizations/Organization/index.js | 34 +++++++++++++++---- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/cypress/support/actions/organizations/OverviewAction.ts b/cypress/support/actions/organizations/OverviewAction.ts index d087f5b0..0742173c 100644 --- a/cypress/support/actions/organizations/OverviewAction.ts +++ b/cypress/support/actions/organizations/OverviewAction.ts @@ -34,7 +34,8 @@ export default class OverviewAction { changeOrgFriendlyname(friendlyName: string) { overviewRepo.getNameEditButton('edit-name').click(); - overviewRepo.getEditField().type(friendlyName); + + overviewRepo.getEditField().clear().type(friendlyName); overviewRepo.getSubmitButton().click(); cy.wait('@gqlupdateOrganizationFriendlyNameMutation'); @@ -53,7 +54,7 @@ export default class OverviewAction { } doFailedChangeOrgFriendlyname(friendlyName: string) { overviewRepo.getNameEditButton('edit-name').click(); - overviewRepo.getEditField().type(friendlyName); + overviewRepo.getEditField().clear().type(friendlyName); overviewRepo.getSubmitButton().click(); cy.wait('@gqlupdateOrganizationFriendlyNameMutation').then(interception => { @@ -68,7 +69,7 @@ export default class OverviewAction { changeOrgDescription(description: string) { overviewRepo.getDescEditButton('edit-description').click(); - overviewRepo.getEditField().type(description); + overviewRepo.getEditField().clear().type(description); overviewRepo.getSubmitButton().click(); cy.wait('@gqlupdateOrganizationFriendlyNameMutation'); @@ -85,7 +86,7 @@ export default class OverviewAction { } doFailedChangeOrgDescription(description: string) { overviewRepo.getDescEditButton('edit-description').click(); - overviewRepo.getEditField().type(description); + overviewRepo.getEditField().clear().type(description); overviewRepo.getSubmitButton().click(); cy.wait('@gqlupdateOrganizationFriendlyNameMutation').then(interception => { diff --git a/src/components/Organizations/Organization/index.js b/src/components/Organizations/Organization/index.js index ddc226e6..6c413d08 100644 --- a/src/components/Organizations/Organization/index.js +++ b/src/components/Organizations/Organization/index.js @@ -48,10 +48,10 @@ const Organization = ({ organization, refetch }) => { } const [nameModalOpen, setNameModalOpen] = useState(false); - const [friendlyName, setFriendlyName] = useState(''); + const [friendlyName, setFriendlyName] = useState(organization.friendlyName); const [descModalOpen, setDescModalOpen] = useState(false); - const [description, setDescription] = useState(''); + const [description, setDescription] = useState(organization.description); const quotaDisplay = (quota, quotaNumber, quotaLimit, showLink = true) => { const pluralName = quota + 's'; @@ -93,9 +93,29 @@ const Organization = ({ organization, refetch }) => { }; const renderEditBtn = type => ( - + { + if (type === 'name') { + setFriendlyName(organization.friendlyName); + } + if (type === 'description') { + setDescription(organization.description); + } + + modalAction('open', type)(); + }} + /> ); + const handleFriendlyNameChange = e => { + setFriendlyName(e.target.value); + }; + + const handleDescriptionChange = e => { + setDescription(e.target.value); + }; return ( @@ -117,12 +137,12 @@ const Organization = ({ organization, refetch }) => {
@@ -188,12 +208,12 @@ const Organization = ({ organization, refetch }) => {
From b51d2a81ff0e58ef82d829ec1f8becede0f6a07f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 5 Jul 2024 00:24:35 +1000 Subject: [PATCH 13/16] feat: disable subscriptions (#280) --- src/lib/ApiConnection.js | 4 +- src/next.config.js | 1 + src/pages/backups.js | 84 +++++++++++++++++++++------------------- src/pages/deployments.js | 72 ++++++++++++++++++---------------- src/pages/tasks.js | 71 ++++++++++++++++++--------------- 5 files changed, 126 insertions(+), 106 deletions(-) diff --git a/src/lib/ApiConnection.js b/src/lib/ApiConnection.js index 34d50b56..9ad4fd1a 100644 --- a/src/lib/ApiConnection.js +++ b/src/lib/ApiConnection.js @@ -16,6 +16,8 @@ import ErrorPage from 'pages/_error.js'; const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); +const disableSubscriptions = publicRuntimeConfig.DISABLE_SUBSCRIPTIONS?.toLowerCase() === 'true'; + const ApiConnection = ({ children }) => ( {auth => { @@ -34,13 +36,13 @@ const ApiConnection = ({ children }) => ( const wsLink = new WebSocketLink({ uri: publicRuntimeConfig.GRAPHQL_API.replace(/https/, 'wss').replace(/http/, 'ws'), options: { + lazy: disableSubscriptions, reconnect: true, connectionParams: { authToken: auth.apiToken, }, }, }); - return ApolloLink.split( ({ query }) => { const { kind, operation } = getMainDefinition(query); diff --git a/src/next.config.js b/src/next.config.js index ff468f9b..7171916f 100644 --- a/src/next.config.js +++ b/src/next.config.js @@ -32,6 +32,7 @@ module.exports = { LAGOON_UI_VIEW_ENV_VARIABLES: process.env.LAGOON_UI_VIEW_ENV_VARIABLES, PLUGIN_SCRIPTS: pluginRegistry, WEBHOOK_URL: process.env.WEBHOOK_URL, + DISABLE_SUBSCRIPTIONS: process.env.DISABLE_SUBSCRIPTIONS, }, distDir: '../build', webpack(config, options) { diff --git a/src/pages/backups.js b/src/pages/backups.js index e1932671..b605be6f 100644 --- a/src/pages/backups.js +++ b/src/pages/backups.js @@ -27,6 +27,8 @@ const { publicRuntimeConfig } = getConfig(); const envLimit = parseInt(publicRuntimeConfig.LAGOON_UI_BACKUPS_LIMIT, 10); const customMessage = publicRuntimeConfig.LAGOON_UI_BACKUPS_LIMIT_MESSAGE; +const disableSubscriptions = publicRuntimeConfig.DISABLE_SUBSCRIPTIONS?.toLowerCase() === 'true'; + /** * Displays the backups page, given the name of an openshift project. */ @@ -117,50 +119,52 @@ export const PageBackups = ({ router }) => { ); } - subscribeToMore({ - document: BackupsSubscription, - variables: { environment: environment.id }, - updateQuery: (prevStore, { subscriptionData }) => { - if (!subscriptionData.data) return prevStore; - const prevBackups = prevStore.environment.backups; - const incomingBackup = subscriptionData.data.backupChanged; - const existingIndex = prevBackups.findIndex(prevBackup => prevBackup.id === incomingBackup.id); - let newBackups; - - // New backup. - if (existingIndex === -1) { - // Don't add new deleted backups. - if (incomingBackup.deleted !== '0000-00-00 00:00:00') { - return prevStore; + if (!disableSubscriptions) { + subscribeToMore({ + document: BackupsSubscription, + variables: { environment: environment.id }, + updateQuery: (prevStore, { subscriptionData }) => { + if (!subscriptionData.data) return prevStore; + const prevBackups = prevStore.environment.backups; + const incomingBackup = subscriptionData.data.backupChanged; + const existingIndex = prevBackups.findIndex(prevBackup => prevBackup.id === incomingBackup.id); + let newBackups; + + // New backup. + if (existingIndex === -1) { + // Don't add new deleted backups. + if (incomingBackup.deleted !== '0000-00-00 00:00:00') { + return prevStore; + } + + newBackups = [incomingBackup, ...prevBackups]; } - - newBackups = [incomingBackup, ...prevBackups]; - } - // Existing backup. - else { - // Updated backup - if (incomingBackup.deleted === '0000-00-00 00:00:00') { - newBackups = Object.assign([...prevBackups], { - [existingIndex]: incomingBackup, - }); - } - // Deleted backup + // Existing backup. else { - newBackups = R.remove(existingIndex, 1, prevBackups); + // Updated backup + if (incomingBackup.deleted === '0000-00-00 00:00:00') { + newBackups = Object.assign([...prevBackups], { + [existingIndex]: incomingBackup, + }); + } + // Deleted backup + else { + newBackups = R.remove(existingIndex, 1, prevBackups); + } } - } - const newStore = { - ...prevStore, - environment: { - ...prevStore.environment, - backups: newBackups, - }, - }; - - return newStore; - }, - }); + const newStore = { + ...prevStore, + environment: { + ...prevStore.environment, + backups: newBackups, + }, + }; + + return newStore; + }, + }); + } return ( <> diff --git a/src/pages/deployments.js b/src/pages/deployments.js index 4b780716..aed722ca 100644 --- a/src/pages/deployments.js +++ b/src/pages/deployments.js @@ -28,6 +28,8 @@ const { publicRuntimeConfig } = getConfig(); const envLimit = parseInt(publicRuntimeConfig.LAGOON_UI_DEPLOYMENTS_LIMIT, 10); const customMessage = publicRuntimeConfig.LAGOON_UI_DEPLOYMENTS_LIMIT_MESSAGE; +const disableSubscriptions = publicRuntimeConfig.DISABLE_SUBSCRIPTIONS?.toLowerCase() === 'true'; + /** * Displays the deployments page, given the openshift project name. */ @@ -44,7 +46,10 @@ export const PageDeployments = ({ router }) => { }); const handleRefetch = async () => - await refetch({ openshiftProjectName: router.query.openshiftProjectName, limit: resultLimit }); + await refetch({ + openshiftProjectName: router.query.openshiftProjectName, + limit: resultLimit, + }); useEffect(() => { let urlResultLimit = envLimit; @@ -118,39 +123,40 @@ export const PageDeployments = ({ router }) => { /> ); } + if (!disableSubscriptions) { + subscribeToMore({ + document: DeploymentsSubscription, + variables: { environment: environment.id }, + updateQuery: (prevStore, { subscriptionData }) => { + if (!subscriptionData.data) return prevStore; + const prevDeployments = prevStore.environment.deployments; + const incomingDeployment = subscriptionData.data.deploymentChanged; + const existingIndex = prevDeployments.findIndex(prevDeployment => prevDeployment.id === incomingDeployment.id); + let newDeployments; + + // New deployment. + if (existingIndex === -1) { + newDeployments = [incomingDeployment, ...prevDeployments]; + } + // Updated deployment + else { + newDeployments = Object.assign([...prevDeployments], { + [existingIndex]: incomingDeployment, + }); + } - subscribeToMore({ - document: DeploymentsSubscription, - variables: { environment: environment.id }, - updateQuery: (prevStore, { subscriptionData }) => { - if (!subscriptionData.data) return prevStore; - const prevDeployments = prevStore.environment.deployments; - const incomingDeployment = subscriptionData.data.deploymentChanged; - const existingIndex = prevDeployments.findIndex(prevDeployment => prevDeployment.id === incomingDeployment.id); - let newDeployments; - - // New deployment. - if (existingIndex === -1) { - newDeployments = [incomingDeployment, ...prevDeployments]; - } - // Updated deployment - else { - newDeployments = Object.assign([...prevDeployments], { - [existingIndex]: incomingDeployment, - }); - } - - const newStore = { - ...prevStore, - environment: { - ...prevStore.environment, - deployments: newDeployments, - }, - }; - - return newStore; - }, - }); + const newStore = { + ...prevStore, + environment: { + ...prevStore.environment, + deployments: newDeployments, + }, + }; + + return newStore; + }, + }); + } return ( <> diff --git a/src/pages/tasks.js b/src/pages/tasks.js index 32699d63..4f6a7c4e 100644 --- a/src/pages/tasks.js +++ b/src/pages/tasks.js @@ -28,6 +28,8 @@ const { publicRuntimeConfig } = getConfig(); const envLimit = parseInt(publicRuntimeConfig.LAGOON_UI_TASKS_LIMIT, 10); const customMessage = publicRuntimeConfig.AGOON_UI_TASKS_LIMIT_MESSAGE; +const disableSubscriptions = publicRuntimeConfig.DISABLE_SUBSCRIPTIONS?.toLowerCase() === 'true'; + /** * Displays the tasks page, given the openshift project name. */ @@ -43,7 +45,10 @@ export const PageTasks = ({ router, renderAddTasks }) => { }); const handleRefetch = async () => - await refetch({ openshiftProjectName: router.query.openshiftProjectName, limit: resultLimit }); + await refetch({ + openshiftProjectName: router.query.openshiftProjectName, + limit: resultLimit, + }); useEffect(() => { let urlResultLimit = envLimit; @@ -120,38 +125,40 @@ export const PageTasks = ({ router, renderAddTasks }) => { ); } - subscribeToMore({ - document: TasksSubscription, - variables: { environment: environment.id }, - updateQuery: (prevStore, { subscriptionData }) => { - if (!subscriptionData.data) return prevStore; - const prevTasks = prevStore.environment.tasks; - const incomingTask = subscriptionData.data.taskChanged; - const existingIndex = prevTasks.findIndex(prevTask => prevTask.id === incomingTask.id); - let newTasks; - - // New task. - if (existingIndex === -1) { - newTasks = [incomingTask, ...prevTasks]; - } - // Updated task - else { - newTasks = Object.assign([...prevTasks], { - [existingIndex]: incomingTask, - }); - } - - const newStore = { - ...prevStore, - environment: { - ...prevStore.environment, - tasks: newTasks, - }, - }; + if (!disableSubscriptions) { + subscribeToMore({ + document: TasksSubscription, + variables: { environment: environment.id }, + updateQuery: (prevStore, { subscriptionData }) => { + if (!subscriptionData.data) return prevStore; + const prevTasks = prevStore.environment.tasks; + const incomingTask = subscriptionData.data.taskChanged; + const existingIndex = prevTasks.findIndex(prevTask => prevTask.id === incomingTask.id); + let newTasks; + + // New task. + if (existingIndex === -1) { + newTasks = [incomingTask, ...prevTasks]; + } + // Updated task + else { + newTasks = Object.assign([...prevTasks], { + [existingIndex]: incomingTask, + }); + } - return newStore; - }, - }); + const newStore = { + ...prevStore, + environment: { + ...prevStore.environment, + tasks: newTasks, + }, + }; + + return newStore; + }, + }); + } return ( <> From cfd83cfc64c141a024b3e8d6edea339283227d5c Mon Sep 17 00:00:00 2001 From: Chris Goodwin <40746380+CGoodwin90@users.noreply.github.com> Date: Fri, 5 Jul 2024 00:43:23 +1000 Subject: [PATCH 14/16] Fix: Environment variables - allows multiline values & clears form data on close (#260) * Initial commit * Env-var fixes --- src/components/AddVariable/StyledAddVariable.tsx | 3 ++- src/components/AddVariable/index.js | 7 +++---- src/components/AddVariable/logic.js | 8 +++++--- .../ViewVariableValue/StyledViewVariableValue.tsx | 1 + 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/AddVariable/StyledAddVariable.tsx b/src/components/AddVariable/StyledAddVariable.tsx index 8cc30cab..3ec7f03d 100644 --- a/src/components/AddVariable/StyledAddVariable.tsx +++ b/src/components/AddVariable/StyledAddVariable.tsx @@ -33,7 +33,8 @@ export const NewVariableModal = styled.div` font-family: 'source-code-pro', sans-serif; font-weight: bold; } - input { + input, + textarea { margin-right: 10px; width: 100%; } diff --git a/src/components/AddVariable/index.js b/src/components/AddVariable/index.js index 789e0ba3..0bf3674f 100644 --- a/src/components/AddVariable/index.js +++ b/src/components/AddVariable/index.js @@ -145,16 +145,15 @@ export const AddVariable = ({ />
- - Value +
diff --git a/src/components/AddVariable/logic.js b/src/components/AddVariable/logic.js index 8b1237a8..4ad019ff 100644 --- a/src/components/AddVariable/logic.js +++ b/src/components/AddVariable/logic.js @@ -28,9 +28,11 @@ const withModalHandlers = withHandlers({ () => setOpen(true), closeModal: - ({ setOpen }) => - () => - setOpen(false), + ({ setOpen, setClear }) => + () => { + setOpen(false); + setClear(); + }, }); export default compose( diff --git a/src/components/ViewVariableValue/StyledViewVariableValue.tsx b/src/components/ViewVariableValue/StyledViewVariableValue.tsx index 9604ff5e..70f0b795 100644 --- a/src/components/ViewVariableValue/StyledViewVariableValue.tsx +++ b/src/components/ViewVariableValue/StyledViewVariableValue.tsx @@ -34,6 +34,7 @@ export const StyledViewVariableValueModal = styled.div` .col-3 { width: fit-content; line-break: anywhere; + white-space: break-spaces; } a.external-link { color: ${color.brightBlue}; From 91b8d91ca7b4664b80c96a87a5bb89d76ad81678 Mon Sep 17 00:00:00 2001 From: Davit Darsavelidze <76407236+DaveDarsa@users.noreply.github.com> Date: Thu, 4 Jul 2024 19:01:13 +0400 Subject: [PATCH 15/16] change: add "type project name to confirm" to org project deletion (#271) * add "type project name to confirm" * fix cypress - use "type to confirm" * cypress - properly delete a project --- .../actions/organizations/ProjectsActions.ts | 8 +++++- .../organizations/ProjectsRepository.ts | 7 +++-- .../Organizations/Projects/index.js | 26 ++++++++++++++++--- src/components/Organizations/SharedStyles.tsx | 5 ++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/cypress/support/actions/organizations/ProjectsActions.ts b/cypress/support/actions/organizations/ProjectsActions.ts index 18547b44..dae3d962 100644 --- a/cypress/support/actions/organizations/ProjectsActions.ts +++ b/cypress/support/actions/organizations/ProjectsActions.ts @@ -44,7 +44,13 @@ export default class ProjectsActions { }); } doDeleteProject(projectName: string) { - projects.getDeleteBtn().eq(1).click(); + projects.getDeleteBtn(projectName).click(); + + cy.get('.highlight') + .invoke('text') + .then(highlightText => { + projects.getDeleteConfirmInput().focus().type(highlightText); + }); projects.getDeleteConfirm().click(); diff --git a/cypress/support/repositories/organizations/ProjectsRepository.ts b/cypress/support/repositories/organizations/ProjectsRepository.ts index 65c6ed9f..dc43de46 100644 --- a/cypress/support/repositories/organizations/ProjectsRepository.ts +++ b/cypress/support/repositories/organizations/ProjectsRepository.ts @@ -23,8 +23,11 @@ export default class ProjectsRepository { getProjectRows() { return cy.getBySel('table-row'); } - getDeleteBtn() { - return cy.get("[aria-label='delete']"); + getDeleteBtn(projectName: string) { + return cy.getBySel('table-row').filter(`:contains(${projectName})`).find('[data-cy=deleteProject]'); + } + getDeleteConfirmInput() { + return cy.getBySel('deleteProjectConfirm'); } getDeleteConfirm() { return cy.getBySel('deleteConfirm'); diff --git a/src/components/Organizations/Projects/index.js b/src/components/Organizations/Projects/index.js index a40eec5f..7b6651ad 100644 --- a/src/components/Organizations/Projects/index.js +++ b/src/components/Organizations/Projects/index.js @@ -13,7 +13,14 @@ import { IconDashboard } from '../CustomIcons/OrganizationIcons'; import { DeleteButton } from '../Groups/Styles'; import NewProject from '../NewProject'; import PaginatedTable from '../PaginatedTable/PaginatedTable'; -import { Footer, RemoveModalHeader, RemoveModalParagraph, TableActions, Tag } from '../SharedStyles'; +import { + Footer, + RemoveModalConfirmInput, + RemoveModalHeader, + RemoveModalParagraph, + TableActions, + Tag, +} from '../SharedStyles'; import { ProjectDashboard, StyledOrgProjects } from './Styles'; const DELETE_PROJECT = gql` @@ -29,6 +36,7 @@ const OrgProjects = ({ projects = [], organizationId, organizationName, refresh, const [modalState, setModalState] = useState({ open: false, current: null, + confirmValue: '', }); const Columns = [ @@ -94,6 +102,7 @@ const OrgProjects = ({ projects = [], organizationId, organizationName, refresh, { setModalState({ open: true, current: project.name }); }} @@ -106,9 +115,20 @@ const OrgProjects = ({ projects = [], organizationId, organizationName, refresh, > Are you sure? - This action will delete project {project.name} from Lagoon and the organization. + This action will delete project {project.name} from Lagoon and the + organization. + Type the name of the project to confirm. + + { + setModalState({ ...modalState, confirmValue: e.target.value }); + }} + /> +
console.error(e)}> {(deleteProject, { called, error, data }) => { @@ -124,7 +144,7 @@ const OrgProjects = ({ projects = [], organizationId, organizationName, refresh,
+
-