From 30ffa05f97f450281a961ef33412362d5dfa0aa9 Mon Sep 17 00:00:00 2001 From: deo002 Date: Mon, 11 Sep 2023 16:47:58 +0530 Subject: [PATCH] feat(link_projects): Integration of functionality to link projects --- messages/en.json | 8 + messages/ja.json | 9 + messages/vi.json | 10 +- messages/zh.json | 8 + .../projects/components/LinkProjects.tsx | 315 ++++++++++++++++++ .../detail/[id]/components/Changelog.tsx | 1 - .../[id]/components/ProjectDetailTab.tsx | 50 +-- src/object-types/enums/HttpStatus.ts | 3 +- src/styles/globals.css | 25 ++ 9 files changed, 402 insertions(+), 27 deletions(-) create mode 100644 src/app/[locale]/projects/components/LinkProjects.tsx diff --git a/messages/en.json b/messages/en.json index b23152b0..2ad7703b 100644 --- a/messages/en.json +++ b/messages/en.json @@ -89,6 +89,7 @@ "Code Snippet": "Code Snippet", "Committer": "Committer", "CODE_SNIPPET": "Code Snippet", + "Click": "Click", "Contributors": "Contributors", "CONTAINED": "Contained", "Comments": "Comments", @@ -207,6 +208,8 @@ "Enter Owner Accounting Unit Group": "Enter Owner Accounting Unit Group", "Enter Owner Accounting Unit": "Enter Owner Accounting Unit", "Enter Name": "Enter Name", + "Exact Match": "Exact Match", + "Enter Search Text": "Enter Search Text", "Enter Blog URL": "Enter Blog URL", "Enter Wiki URL": "Enter Wiki URL", "Enter email": "Enter email", @@ -251,6 +254,7 @@ "GNU arch": "GNU arch", "Group": "Group", "Git": "Git", + "here": "here", "Hardware": "Hardware", "Home": "Home", "Homepage": "Homepage", @@ -281,6 +285,7 @@ "key": "key", "Key User": "Key User", "Lifecycle": "Lifecycle", + "Link Projects": "Link Projects", "License Info Header": "License Info Header", "Link to Projects": "Link to Projects", "Last Name": "Last Name", @@ -369,6 +374,7 @@ "Project Owner": "Project Owner", "PROJECT_TYPE_INFO": "Customer: \n Internal: \n Product: \n Service: \n Inner Source: ", "Password": "Password", + "Project cannot be created/updated": "Project cannot be created/updated", "Pause": "Pause", "Perforce": "Perforce", "PHASEOUT": "Phaseout", @@ -508,6 +514,7 @@ "System test end date": "System test end date", "Team Foundation Server": "Team Foundation Server", "Technical writer": "Technical writer", + "to edit the project relation": "to edit the project relation", "Test Manager": "Test Manager", "The component cannot be deleted, since it contains releases Please delete the releases first": "The component {name} cannot be deleted, since it contains {releaseCount} releases. Please delete the releases first.", "This release contains": "This release {name} contains: ", @@ -515,6 +522,7 @@ "The release has been successfully linked to project": "The release {releaseName} has been successfully linked to project {projectName}", "Test and Diagnostics": "Test and Diagnostics", "Tag": "Tag", + "The projects have been successfully linked to project": "The projects have been successfully linked to project", "Title": "Title", "The verification of vulnerabilities will be changed to": "The verification of {number} vulnerabilities will be changed to", "To": "To", diff --git a/messages/ja.json b/messages/ja.json index 8d112aaf..28458f9b 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -78,6 +78,7 @@ "Change verification state of selected vulnerabilities to": "選択した脆弱性の検証状況", "Changes": "変更", "Checked By": "確認", + "Click": "Click", "Checked Comment": "Checked Comment", "Checked On": "Checked On", "Clearing Report": "クリアリングレポート", @@ -204,6 +205,8 @@ "Export Spreadsheet": "SBOMのインポート", "External Id": "外部ID", "External ids": "外部ID", + "Exact Match": "Exact Match", + "Enter Search Text": "Enter Search Text", "Expert": "エキスパート", "Enter email": "NOT TRANSLATED", "Enter Owner Billing Group": "所有者の請求先グループを入力してください", @@ -252,6 +255,7 @@ "Hardware": "NOT TRANSLATED", "Home": "NOT TRANSLATED", "Homepage": "ホームページ", + "here": "here", "Homepage Url": "ホームページの URL", "How it works": "どのように動作するか", "hand_when_got_stuck_fossology": "FOSSologyのプロセスか,添付ファイルが正しくないのであれば, FOSSologyののステータスをoutdatedにして最初から行うことができる.新しいものは自動で開始される.そのため,全ての添付ファイルや他の問題を,ステータス変更前に初めに確認しておくべき.", @@ -283,6 +287,7 @@ "Lead Architect": "NOT TRANSLATED", "Learn more about project visibilities": "NOT TRANSLATED", "Learn more about project types": "NOT TRANSLATED", + "Link Projects": "Link Projects", "Learn more about repository types": "Learn more about repository types", "Languages": "言語", "Legal evaluation report": "法的評価報告書", @@ -369,6 +374,7 @@ "Phaseout": "NOT TRANSLATED", "Phase-out date": "NOT TRANSLATED", "Platforms": "プラットフォーム", + "Project State": "Project State", "Platform": "NOT TRANSLATED", "Please comment your changes": "変更点にコメントしてください", "Preferences": "NOT TRANSLATED", @@ -378,6 +384,7 @@ "Project Name": "NOT TRANSLATED", "Project Responsible": "NOT TRANSLATED", "Project Responsible (Email)": "NOT TRANSLATED", + "Project cannot be created/updated": "Project cannot be created/updated", "Project Type": "NOT TRANSLATED", "Project Version": "NOT TRANSLATED", "Projects": "NOT TRANSLATED", @@ -498,6 +505,7 @@ "System test end date": "NOT TRANSLATED", "Team Foundation Server": "チームファンデーションサーバー", "Test Manager": "NOT TRANSLATED", + "to edit the project relation": "to edit the project relation", "Technical writer": "NOT TRANSLATED", "Tag": "NOT TRANSLATED", "Test and Diagnostics": "NOT TRANSLATED", @@ -507,6 +515,7 @@ "The release has been successfully linked to project": "The release {releaseName} has been successfully linked to project {projectName}", "The verification of vulnerabilities will be changed to": "{number} 件の脆弱性の検証を変更", "Title": "标题", + "The projects have been successfully linked to project": "The projects have been successfully linked to project", "To": "To", "TO_BE_REPLACED": "置き換わる", "Total vulnerabilities": "脆弱性の総数", diff --git a/messages/vi.json b/messages/vi.json index bb1b5908..9a65fb7b 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -90,6 +90,7 @@ "CONTAINED": "Chứa", "Comments": "Những giải thích", "Comment": "Comment", + "Click": "Click", "Component Clearing Report": "Báo cáo dọn dẹp thành phần", "Component license information (XML)": "Thông tin giấy phép thành phần (XML)", "Component license information (Combined)": "Thông tin giấy phép thành phần (Kết hợp)", @@ -206,6 +207,8 @@ "Export Spreadsheet": "Xuất bảng tính", "External Id": "Id bên ngoài", "External ids": "Id bên ngoài", + "Exact Match": "Exact Match", + "Enter Search Text": "Enter Search Text", "Expert": "Chuyên gia", "Enter email": "NOT TRANSLATED", "Enter Owner Billing Group": "Nhập Owner Billing Group", @@ -252,6 +255,7 @@ "GNU arch": "GNU 拱门", "Hardware": "NOT TRANSLATED", "Home": "NOT TRANSLATED", + "here": "here", "Homepage": "Trang chủ", "Homepage Url": "Trang chủ Url", "How it works": "Nó hoạt động như thế nào", @@ -285,6 +289,7 @@ "Lifecycle": "Lifecycle", "License Info Header": "License Info Header", "Learn more about project visibilities": "NOT TRANSLATED", + "Link Projects": "Link Projects", "Learn more about project types": "NOT TRANSLATED", "Learn more about repository types": "Learn more about repository types", "Learn more about project clearing state": "NOT TRANSLATED", @@ -361,6 +366,7 @@ "Project Owner": "NOT TRANSLATED", "Private": "NOT TRANSLATED", "PROJECT_TYPE_INFO": "NOT TRANSLATED", + "Project State": "Project State", "PHASEOUT": "Giai đoạn", "Phaseout": "NOT TRANSTALTED", "Phase-out date": "NOT TRANSLATED", @@ -374,7 +380,6 @@ "Priority": "Độ ưu tiên", "Product": "NOT TRANSLATED", "Programming Languages": "Ngôn ngữ lập trình", - "Project State": "NOT TRANSTALTED", "Project Name": "NOT TRANSLATED", "Project Responsible": "NOT TRANSLATED", "Project Responsible (Email)": "NOT TRANSLATED", @@ -386,6 +391,7 @@ "PTC Integrity": "Tính toàn vẹn của PTC", "Perforce": "lực lượng", "Project state": "Project state", + "Project cannot be created/updated": "Project cannot be created/updated", "Phase-out since": "Phase-out since", "Quick Filter": "NOT TRANSLATED", "ReadMe OSS": "ReadMe OSS", @@ -505,6 +511,7 @@ "This release contains": "Bản phát hành {name} này chứa: ", "This currently only supports SPDX RDF/XML files with a uniq described top level node": "This currently only supports SPDX RDF/XML files with a uniq described top level node", "The release has been successfully linked to project": "The release {releaseName} has been successfully linked to project {projectName}", + "The projects have been successfully linked to project": "The projects have been successfully linked to project", "The verification of vulnerabilities will be changed to": "Việc xác minh các lỗ hổng {number} sẽ được thay đổi thành", "Title": "Tiêu đề", "To": "Đến", @@ -513,6 +520,7 @@ "Total Number Of Files": "Total Number Of Files", "Type": "Loại", "Team Foundation Server": "Máy chủ nền tảng nhóm", + "to edit the project relation": "to edit the project relation", "URL": "URL", "UNDER_CLEARING": "Đang dọn dẹp", "Unknown": "NOT TRANSLATED", diff --git a/messages/zh.json b/messages/zh.json index 7e63099f..3c094f31 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -76,6 +76,7 @@ "Change verification state of selected vulnerabilities to": "将选择的漏洞的验证状态更改为", "Changes": "更改日志", "Checked By": "检查人", + "Click": "Click", "Checked Comment": "已检查的注释", "Checked On": "选中", "Clearing Report": "明确报告", @@ -203,6 +204,8 @@ "Error when processing!": "处理时出错!", "Export Spreadsheet": "导出电子表格", "External Id": "外部ID", + "Exact Match": "Exact Match", + "Enter Search Text": "Enter Search Text", "External ids": "外部ID", "Expert": "专家", "Enter Owner Billing Group": "输入所有者计费组", @@ -250,6 +253,7 @@ "Hardware": "NOT TRANSLATED", "Home": "NOT TRANSLATED", "Homepage": "主页", + "here": "here", "Homepage Url": "主页网址", "How it works": "它是如何工作的", "hand_when_got_stuck_fossology": "如果FOSSology进程卡住或源附件不正确,可以通过将当前FOSSology进程状态设置为状态OUTDATED 来重新开始。一个新的FOSSology进程将自动启动,因此请确保在更改状态之前先修复所有附件或其他的问题。", @@ -287,6 +291,7 @@ "Learn more about project visibilities": "NOT TRANSLATED", "Learn more about repository types": "Learn more about repository types", "Languages": "语言", + "Link Projects": "Link Projects", "Legal evaluation report": "法律评估报告", "License Agreement": "许可协议", "License Clearing": "NOT TRANSLATED", @@ -362,6 +367,7 @@ "Password": "密码", "Pause": "暫停", "PhaseOut": "NOT TRANSLATED", + "Project cannot be created/updated": "Project cannot be created/updated", "Platforms": "平台", "Platform": "NOT TRANSLATED", "Phaseout": "NOT TRANSLATED", @@ -496,7 +502,9 @@ "Test Manager": "NOT TRANSLATED", "Technical writer": "NOT TRANSLATED", "Tag": "NOT TRANSLATED", + "The projects have been successfully linked to project": "The projects have been successfully linked to project", "Test and Diagnostics": "NOT TRANSLATED", + "to edit the project relation": "to edit the project relation", "The component cannot be deleted, since it contains releases Please delete the releases first": "组件 {name} 无法被删除,因为它包含发行版本 {releaseCount}。请先删除发行版本。", "This release contains": "发行版本 {name} 包含:", "This currently only supports SPDX RDF/XML files with a uniq described top level node": "This currently only supports SPDX RDF/XML files with a uniq described top level node", diff --git a/src/app/[locale]/projects/components/LinkProjects.tsx b/src/app/[locale]/projects/components/LinkProjects.tsx new file mode 100644 index 00000000..1a5ae4a3 --- /dev/null +++ b/src/app/[locale]/projects/components/LinkProjects.tsx @@ -0,0 +1,315 @@ +// Copyright (C) Siemens AG, 2023. Part of the SW360 Frontend Project. + +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ + +// SPDX-License-Identifier: EPL-2.0 +// License-Filename: LICENSE + +'use client' + +import { useTranslations } from 'next-intl' +import { COMMON_NAMESPACE } from '@/object-types/Constants' +import { useState, useRef } from 'react' + +import ApiUtils from '@/utils/api/api.util' +import CommonUtils from '@/utils/common.utils' +import { Session } from '@/object-types/Session' +import HttpStatus from '@/object-types/enums/HttpStatus' +import { signOut } from 'next-auth/react' +import { notFound } from 'next/navigation' +import { Modal, Col, Row, Form, Button, OverlayTrigger, Tooltip, Alert } from 'react-bootstrap' +import { FaInfoCircle } from 'react-icons/fa' +import { Table, _ } from '@/components/sw360' +import Link from 'next/link' + +const Capitalize = (text: string) => + text.split('_').reduce((s, c) => s + ' ' + (c.charAt(0) + c.substring(1).toLocaleLowerCase()), '') + +interface AlertData { + variant: string + message: JSX.Element +} + +export default function LinkProjects({ + projectId, + session, + show, + setShow, +}: { + projectId: string + session: Session + show: boolean + setShow: (show: boolean) => void +}) { + const t = useTranslations(COMMON_NAMESPACE) + const [projectData, setProjectData] = useState(null) + const [linkProjects, setLinkProjects] = useState>(new Map()) + const [alert, setAlert] = useState(null) + const searchValueRef = useRef(null) + const topRef = useRef(null) + + const scrollToTop = () => { + topRef.current.scrollTo({ top: 0, left: 0 }) + } + + const columns = [ + { + id: 'linkProjects.selectProjectCheckbox', + name: '', + width: '8%', + formatter: (projectId: string) => + _( +
+ handleCheckboxes(projectId)} + /> +
+ ), + }, + { + id: 'linkProjects.name', + name: t('Name'), + sort: true, + }, + { + id: 'linkProjects.version', + name: t('Version'), + sort: true, + }, + { + id: 'linkProjects.state', + name: t('State'), + width: '15%', + formatter: ({ state, clearingState }: { state: string; clearingState: string }) => + _( + <> + {`${t('Project State')}: ${Capitalize(state)}`}}> + {state === 'ACTIVE' ? ( + + {'PS'} + + ) : ( + + {'PS'} + + )} + + + {`${t('Project Clearing State')}: ${Capitalize(clearingState)}`} + } + > + {clearingState === 'OPEN' ? ( + + {'CS'} + + ) : clearingState === 'IN_PROGRESS' ? ( + + {'CS'} + + ) : ( + + {'CS'} + + )} + + + ), + sort: true, + }, + { + id: 'linkProjects.projectResponsible', + name: t('Project Responsible'), + sort: true, + }, + { + id: 'linkProjects.description', + name: t('Description'), + sort: true, + }, + ] + + const handleSearch = async ({ searchValue, session }: { searchValue: string; session: Session }): Promise => { + try { + const response = await ApiUtils.GET(`projects?name=${searchValue}`, session.user.access_token) + if (response.status === HttpStatus.UNAUTHORIZED) { + return signOut() + } else if (response.status !== HttpStatus.OK) { + return notFound() + } + const data = await response.json() + const dataTableFormat = + CommonUtils.isNullOrUndefined(data['_embedded']) && + CommonUtils.isNullOrUndefined(data['_embedded']['sw360:projects']) + ? [] + : data['_embedded']['sw360:projects'].map((elem: any) => [ + elem['_links']['self']['href'].substring(elem['_links']['self']['href'].lastIndexOf('/') + 1), + elem.name, + elem.version, + { state: elem.state, clearingState: elem.clearingState }, + elem.projectResponsible, + elem.description, + ]) + setProjectData(dataTableFormat) + } catch (e) { + console.error(e) + } + } + + const handleCheckboxes = (projectId: string) => { + const m = new Map(linkProjects) + if (linkProjects.has(projectId)) { + m.delete(projectId) + } else { + m.set(projectId, { enableSvm: true, projectRelationship: 'CONTAINED' }) + } + setLinkProjects(m) + } + + const handleLinkProjects = async ({ + projectId, + session, + }: { + projectId: string + session: Session + }): Promise => { + try { + const data = { linkedProjects: Object.fromEntries(linkProjects) } + const response = await ApiUtils.PATCH(`projects/${projectId}`, data, session.user.access_token) + const res = await response.json() + if (response.status === HttpStatus.UNAUTHORIZED) { + return signOut() + } else if (response.status === HttpStatus.FORBIDDEN || response.status === HttpStatus.BAD_REQUEST) { + return setAlert({ + variant: 'danger', + message: ( + <> +

+ {t('Project cannot be created/updated')}. {res.message} +

+ + ), + }) + } else if (response.status !== HttpStatus.OK) { + return notFound() + } + setAlert({ + variant: 'success', + message: ( + <> +

+ {`${t('The projects have been successfully linked to project')} `} + {res.name}.{' '} +

+

+ {t('Click')}{' '} + + {t('here')} + {' '} + {t('to edit the project relation')}. +

+ + ), + }) + } catch (e) { + console.error(e) + } + } + + return ( + <> + { + setShow(false) + setProjectData(null) + setAlert(null) + setLinkProjects(new Map()) + }} + aria-labelledby={t('Link Projects')} + scrollable + > + + {t('Link Projects')} + + + {alert && ( + + {alert.message} + + )} +
+ + + + + + + + + + {t('Exact Match')}{' '} + + + + + + + + + + + {projectData && } + + + + + + + + + + ) +} diff --git a/src/app/[locale]/projects/detail/[id]/components/Changelog.tsx b/src/app/[locale]/projects/detail/[id]/components/Changelog.tsx index 21695610..ac0196f4 100644 --- a/src/app/[locale]/projects/detail/[id]/components/Changelog.tsx +++ b/src/app/[locale]/projects/detail/[id]/components/Changelog.tsx @@ -30,7 +30,6 @@ export default function ChangeLog({ projectId, session }: { projectId: string; s const [changeLogList, setChangeLogList] = useState>([]) const [changeLogIndex, setChangeLogIndex] = useState(-1) - console.log(key) useEffect(() => { const controller = new AbortController() const signal = controller.signal diff --git a/src/app/[locale]/projects/detail/[id]/components/ProjectDetailTab.tsx b/src/app/[locale]/projects/detail/[id]/components/ProjectDetailTab.tsx index 7459c287..022a9678 100644 --- a/src/app/[locale]/projects/detail/[id]/components/ProjectDetailTab.tsx +++ b/src/app/[locale]/projects/detail/[id]/components/ProjectDetailTab.tsx @@ -30,10 +30,13 @@ import { AdministrationDataType } from '@/object-types/AdministrationDataType' import { notFound } from 'next/navigation' +import LinkProjects from '../../../components/LinkProjects' + export default function ViewProjects({ session, projectId }: { session: Session; projectId: string }) { const t = useTranslations(COMMON_NAMESPACE) const [summaryData, setSummaryData] = useState(undefined) const [administrationData, setAdministrationData] = useState(undefined) + const [show, setShow] = useState(false) useEffect(() => { const controller = new AbortController() @@ -215,6 +218,7 @@ export default function ViewProjects({ session, projectId }: { session: Session; return ( <> +
@@ -253,30 +257,28 @@ export default function ViewProjects({ session, projectId }: { session: Session;
- - - - - - - - - {t('Export SBOM')} - - - {t('CycloneDX')} - - - - - - {summaryData && `${summaryData.name} (${summaryData.version})`} - - + + + + + + + + {t('Export SBOM')} + + + {t('CycloneDX')} + + + + + + {summaryData && `${summaryData.name} (${summaryData.version})`} + diff --git a/src/object-types/enums/HttpStatus.ts b/src/object-types/enums/HttpStatus.ts index d42e5da5..b3ba5fc6 100644 --- a/src/object-types/enums/HttpStatus.ts +++ b/src/object-types/enums/HttpStatus.ts @@ -16,9 +16,10 @@ enum HttpStatus { MULTIPLE_STATUS = 207, BAD_REQUEST = 400, UNAUTHORIZED = 401, + FORBIDDEN = 403, NOT_FOUND = 404, CONFLICT = 409, INTERNAL_SERVER_ERROR = 500, } -export default HttpStatus \ No newline at end of file +export default HttpStatus diff --git a/src/styles/globals.css b/src/styles/globals.css index d8b278e1..451a53e1 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -480,3 +480,28 @@ th { color: white !important; } +.capsule-left { + border-top-right-radius: 0%; + border-bottom-right-radius: 0%; +} + +.capsule-right { + border-top-left-radius: 0%; + border-bottom-left-radius: 0%; +} + +.bg-success { + background-color: #68c17c !important; +} + +.bg-danger { + background-color: #e6717c !important; +} + +.bg-secondary { + background-color: #dee2e6 !important; +} + +.bg-warning { + background-color: #ffd350 !important; +}