From 45b09b5e959971f1d9842cd375dbe9f4b2233483 Mon Sep 17 00:00:00 2001 From: tuannn2 Date: Thu, 22 Feb 2024 10:24:52 +0700 Subject: [PATCH 1/5] feat(license): Define object for license, obligation Signed-off-by: tuannn2 --- src/object-types/LicensePayload.ts | 34 ++++++++++++++++++++++++++++++ src/object-types/Obligation.ts | 29 +++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/object-types/LicensePayload.ts create mode 100644 src/object-types/Obligation.ts diff --git a/src/object-types/LicensePayload.ts b/src/object-types/LicensePayload.ts new file mode 100644 index 000000000..38f876d2f --- /dev/null +++ b/src/object-types/LicensePayload.ts @@ -0,0 +1,34 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 + +import Obligation from "./Obligation" + +export default interface LicensePayload { + shortName?: string + fullName?: string + externalLicenseLink?: string + note?: string + OSIApproved?: string + FSFLibre?: string + obligations?: Array + obligationDatabaseIds?: Array + text?: string + checked?: boolean + licenseType?: { + id: string + licenseType: string + } + licenseTypeDatabaseId?: string + _links?: { + self: { + href: string + } + } +} diff --git a/src/object-types/Obligation.ts b/src/object-types/Obligation.ts new file mode 100644 index 000000000..ad678d770 --- /dev/null +++ b/src/object-types/Obligation.ts @@ -0,0 +1,29 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 + +export default interface Obligation { + id?: string + type?: string + text?: string + whitelist?: Array + development?: string + distribution?: string + title?: string + obligationLevel?: string + obligationType?: string + node?: string + issetBitfield?: number + customPropertyToValue?: Map + _links?: { + self: { + href: string + } + } +} From c9a44e638dfc50478933936992361240b2f77a18 Mon Sep 17 00:00:00 2001 From: tuannn2 Date: Thu, 22 Feb 2024 10:26:03 +0700 Subject: [PATCH 2/5] feat(license): Update show page for License page Signed-off-by: tuannn2 --- messages/en.json | 2 + src/app/[locale]/licenses/LicensePage.tsx | 123 --------------- .../licenses/components/LicensePage.tsx | 145 ++++++++++++++++++ src/app/[locale]/licenses/page.tsx | 6 +- src/object-types/index.ts | 4 + 5 files changed, 154 insertions(+), 126 deletions(-) delete mode 100644 src/app/[locale]/licenses/LicensePage.tsx create mode 100644 src/app/[locale]/licenses/components/LicensePage.tsx diff --git a/messages/en.json b/messages/en.json index 9244ee915..89b621a33 100644 --- a/messages/en.json +++ b/messages/en.json @@ -391,6 +391,7 @@ "Internal": "Internal", "Internal Project": "Internal Project", "Is Checked": "Is Checked", + "Is Checked?": "Is Checked?", "Is a subproject": "Is a subproject", "Key User": "Key User", "Keyword Search": "Keyword Search", @@ -429,6 +430,7 @@ "License Types": "License Types", "Licenses": "Licenses", "Licenses uploaded successfully": "Licenses uploaded successfully", + "License removed successfully!": "License removed successfully!", "Lifecycle": "Lifecycle", "Link Projects": "Link Projects", "Link Release to Project": "Link Release to Project", diff --git a/src/app/[locale]/licenses/LicensePage.tsx b/src/app/[locale]/licenses/LicensePage.tsx deleted file mode 100644 index 693effe13..000000000 --- a/src/app/[locale]/licenses/LicensePage.tsx +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) Helio Chissini de Castro, 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 { signOut, useSession } from 'next-auth/react' -import { useTranslations } from 'next-intl' -import Link from 'next/link' -import { useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect, useState } from 'react' -import { Spinner } from 'react-bootstrap' -import { BsCheck2Circle, BsXCircle } from 'react-icons/bs' - -import { Embedded, HttpStatus, Licenses } from '@/object-types' -import { ApiUtils, CommonUtils } from '@/utils' -import { PageButtonHeader, QuickFilter, Table, _ } from 'next-sw360' - -type EmbeddedLicenses = Embedded - -function LicensesPage() { - const params = useSearchParams() - const t = useTranslations('default') - const [search, setSearch] = useState({}) - const [loading, setLoading] = useState(true) - const [licenseData, setLicenseData] = useState([]) - const { data: session } = useSession() - - const headerButtons = { - 'Add License': { link: '/licenses/add', type: 'primary', name: t('Add License') }, - 'Export Spreadsheet': { link: '/licenses/export', type: 'secondary', name: t('Export Spreadsheet') }, - } - - const fetchData: any = useCallback( - async (queryUrl: string, signal: unknown) => { - const licensesResponse = await ApiUtils.GET(queryUrl, session.user.access_token, signal) - if (licensesResponse.status == HttpStatus.OK) { - const licenses = await licensesResponse.json() - return licenses - } else if (licensesResponse.status == HttpStatus.UNAUTHORIZED) { - signOut() - } else { - return [] - } - }, - [session] - ) - - useEffect(() => { - setLoading(true) - const searchParams = Object.fromEntries(params) - const queryUrl = CommonUtils.createUrlWithParams('licenses', searchParams) - const controller = new AbortController() - const signal = controller.signal - - fetchData(queryUrl, signal).then((licenses: EmbeddedLicenses) => { - if (!CommonUtils.isNullOrUndefined(licenses['_embedded']['sw360:licenses'])) { - setLicenseData( - licenses['_embedded']['sw360:licenses'].map((item: Licenses) => [ - _({item._links.self.href.split('/').pop()}), - item.fullName, - _( -
- {item.checked ? ( - - ) : ( - - )} -
- ), - ]) - ) - // setNumberOfComponent(data.length) - setLoading(false) - } - }) - - return () => { - controller.abort() - } - }, [fetchData, params]) - - const columns = [ - { name: t('License Shortname'), width: '25%' }, - { name: t('License Fullname'), width: '45%' }, - { name: t('Is Checked'), width: '10%' }, - { name: t('License Type'), width: '15%' }, - ] - - const doSearch = (event: React.KeyboardEvent) => { - setSearch({ keyword: event.currentTarget.value }) - } - - return ( -
-
-
- -
-
-
- - {loading == false ? ( - - ) : ( -
- -
- )} -
- - - - - ) -} - -export default LicensesPage diff --git a/src/app/[locale]/licenses/components/LicensePage.tsx b/src/app/[locale]/licenses/components/LicensePage.tsx new file mode 100644 index 000000000..0943a05f7 --- /dev/null +++ b/src/app/[locale]/licenses/components/LicensePage.tsx @@ -0,0 +1,145 @@ +// Copyright (c) Helio Chissini de Castro, 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 { Embedded, LicensePayload, ToastData } from '@/object-types' +import DownloadService from '@/services/download.service' +import { CommonUtils } from '@/utils' +import { SW360_API_URL } from '@/utils/env' +import { signOut, useSession } from 'next-auth/react' +import { useTranslations } from 'next-intl' +import { PageButtonHeader, QuickFilter, Table, ToastMessage, _ } from 'next-sw360' +import Link from 'next/link' +import { useSearchParams } from 'next/navigation' +import React, { useEffect, useState } from 'react' +import { ToastContainer } from 'react-bootstrap' +import { BsCheck2Circle, BsXCircle } from 'react-icons/bs' + +function LicensePage() { + const params = useSearchParams() + const searchParams = Object.fromEntries(params) + const t = useTranslations('default') + const [search, setSearch] = useState({}) + const [numberLicense, setNumberLicense] = useState(0) + const { data: session, status } = useSession() + const deleteLicense = params.get('delete') + + const [toastData, setToastData] = useState({ + show: false, + type: '', + message: '', + contextual: '', + }) + + const alert = (show_data: boolean, status_type: string, message: string, contextual: string) => { + setToastData({ + show: show_data, + type: status_type, + message: message, + contextual: contextual, + }) + } + + useEffect(() => { + if (!CommonUtils.isNullEmptyOrUndefinedString(deleteLicense)) { + alert(true, 'Success', t('License removed successfully!'), 'success') + } + }, [params]) + + const handleExportLicense = () => { + DownloadService.download(`reports?module=licenses`, session, `Licenses.xlsx`) + } + + const headerButtons = { + 'Add License': { link: '/licenses/add', type: 'primary', name: t('Add License') }, + 'Export Spreadsheet': { + link: '/licenses', + onClick: handleExportLicense, + type: 'secondary', + name: t('Export Spreadsheet'), + }, + } + + const server = { + url: CommonUtils.createUrlWithParams(`${SW360_API_URL}/resource/api/licenses`, searchParams), + then: (data: Embedded) => { + setNumberLicense(data.page.totalElements) + return data._embedded['sw360:licenses'].map((item: LicensePayload) => [ + item._links.self.href.split('/').pop(), + item.fullName, + _( +
+ {item.checked ? : } +
+ ), + _(<>{item.licenseType ? item.licenseType.licenseType : '--'}), + ]) + }, + total: (data: Embedded) => data.page.totalElements, + headers: { Authorization: `Bearer ${status === 'authenticated' ? session.user.access_token : ''}` }, + } + + const columns = [ + { + name: t('License Shortname'), + formatter: (id: string) => + _( + + {id} + + ), + sort: true, + }, + { name: t('License Fullname'), width: '45%', sort: true }, + { name: t('Is Checked?'), width: '10%', sort: true }, + { name: t('License Type'), width: '15%', sort: true }, + ] + + const doSearch = (event: React.KeyboardEvent) => { + setSearch({ keyword: event.currentTarget.value }) + } + if (status === 'unauthenticated') { + signOut() + } else { + return ( +
+ + setToastData({ ...toastData, show: false })} + setShowToast={setToastData} + /> + +
+
+ +
+
+
+
+ +
+ +
+ + + + + + ) + } +} +export default LicensePage diff --git a/src/app/[locale]/licenses/page.tsx b/src/app/[locale]/licenses/page.tsx index c807d5eb7..f6c5baf90 100644 --- a/src/app/[locale]/licenses/page.tsx +++ b/src/app/[locale]/licenses/page.tsx @@ -8,14 +8,14 @@ // License-Filename: LICENSE import { Metadata } from 'next' -import LicensePage from './LicensePage' +import LicensePage from './components/LicensePage' export const metadata: Metadata = { title: 'Licenses', } -async function Licenses() { +async function LicensesPage() { return } -export default Licenses +export default LicensesPage diff --git a/src/object-types/index.ts b/src/object-types/index.ts index fa9beedc8..9cef4c604 100644 --- a/src/object-types/index.ts +++ b/src/object-types/index.ts @@ -33,11 +33,13 @@ import LinkedAttachments from './LinkedAttachments' import LinkedRelease from './LinkedRelease' import LinkedVulnerability from './LinkedVulnerability' import Links from './Links' +import LicensePayload from './LicensePayload' import Moderators from './Moderators' import ModeratorsType from './ModeratorsType' import NavList from './NavList' import NodeData from './NodeData' import OAuthClient from './OAuthClient' +import Obligation from './Obligation' import Package from './Package' import Preferences from './Preferences' import Project from './Project' @@ -89,10 +91,12 @@ export type { LinkedRelease, LinkedVulnerability, Links, + LicensePayload, Moderators, ModeratorsType, NodeData, OAuthClient, + Obligation, Package, Project, ProjectData, From 11bc6bd5df81cb470bf2a66e3657bb7f7dc79073 Mon Sep 17 00:00:00 2001 From: tuannn2 Date: Thu, 22 Feb 2024 11:17:05 +0700 Subject: [PATCH 3/5] feat(license): Add Detail for License Signed-off-by: tuannn2 --- messages/en.json | 25 +- .../licenses/detail/components/Detail.tsx | 190 ++++++++++++ .../components/LicenseDetailOverview.tsx | 289 ++++++++++++++++++ .../detail/components/Obligations.tsx | 240 +++++++++++++++ .../licenses/detail/components/Text.tsx | 42 +++ .../licenses/detail/detail.module.css | 0 src/app/[locale]/licenses/detail/page.tsx | 20 ++ .../LinkedObligations/LinkedObligations.tsx | 43 +++ .../LinkedObligations/TableLicense.tsx | 131 ++++++++ .../TableLinkedObligations/FilterSearch.tsx | 41 +++ .../TableLinkedObligations.module.css | 37 +++ .../TableLinkedObligations.tsx | 143 +++++++++ .../PageButtonHeader/PageButtonHeader.tsx | 87 +++++- .../PageButtonHeader.types.ts | 6 +- .../pagebuttonheader.module.css | 5 + src/object-types/LicenseDetail.ts | 29 ++ src/object-types/enums/LicenseTabIds.ts | 18 ++ src/object-types/index.ts | 4 + 18 files changed, 1339 insertions(+), 11 deletions(-) create mode 100644 src/app/[locale]/licenses/detail/components/Detail.tsx create mode 100644 src/app/[locale]/licenses/detail/components/LicenseDetailOverview.tsx create mode 100644 src/app/[locale]/licenses/detail/components/Obligations.tsx create mode 100644 src/app/[locale]/licenses/detail/components/Text.tsx create mode 100644 src/app/[locale]/licenses/detail/detail.module.css create mode 100644 src/app/[locale]/licenses/detail/page.tsx create mode 100644 src/components/LinkedObligations/LinkedObligations.tsx create mode 100644 src/components/LinkedObligations/TableLicense.tsx create mode 100644 src/components/LinkedObligations/TableLinkedObligations/FilterSearch.tsx create mode 100644 src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.module.css create mode 100644 src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.tsx create mode 100644 src/object-types/LicenseDetail.ts create mode 100644 src/object-types/enums/LicenseTabIds.ts diff --git a/messages/en.json b/messages/en.json index 89b621a33..6b94f463a 100644 --- a/messages/en.json +++ b/messages/en.json @@ -212,6 +212,7 @@ "Delete release success!": "Delete release success!", "Delivery start": "Delivery start", "Delivery start date": "Delivery start date", + "Details": "Details", "Deparment Name": "Deparment Name", "Department": "Department", "DependencyNotes": "Dependency Notes", @@ -254,7 +255,9 @@ "Edit User Secondary Departments And Role": "Edit User Secondary Departments And Role", "Edit Vendor": "Edit Vendor", "Edit component": "Edit component", + "Edit License": "Edit License", "Edit release": "Edit release", + "Edit Whitelist": "Edit Whitelist", "Email": "Email", "Email Address": "Email Address", "Embedded Software": "Embedded Software", @@ -338,6 +341,7 @@ "External URL": "External URL", "External URLs": "External URLs", "External ids": "External ids", + "External link for more information": "External link for more information", "Field Name": "Field Name", "File name": "File name", "Finalized License Scan Report": "Finalized License Scan Report", @@ -347,6 +351,9 @@ "Found source attachment": "Found source attachment", "Freeware": "Freeware", "Full Name": "Full Name", + "Fullname": "Fullname", + "FSF Free/Libre?": "FSF Free/Libre?", + "Further properties": "Further properties", "GNU arch": "GNU arch", "General": "General", "General Information": "General Information", @@ -391,6 +398,7 @@ "Internal": "Internal", "Internal Project": "Internal Project", "Is Checked": "Is Checked", + "Is checked": "Is checked", "Is Checked?": "Is Checked?", "Is a subproject": "Is a subproject", "Key User": "Key User", @@ -428,9 +436,13 @@ "License Shortname": "License Shortname", "License Type": "License Type", "License Types": "License Types", + "License Text": "License Text", "Licenses": "Licenses", + "License Details": "License Details", "Licenses uploaded successfully": "Licenses uploaded successfully", "License removed successfully!": "License removed successfully!", + "License updated successfully!": "License updated successfully!", + "License updated failed!": "License updated failed!", "Lifecycle": "Lifecycle", "Link Projects": "Link Projects", "Link Release to Project": "Link Release to Project", @@ -490,9 +502,12 @@ "NoProjectsFound": "There are no projects found with your selection.", "NoTasksAssigned": "There are no tasks assigned to you.", "None": "None", + "Note": "Note", "Not Applicable": "Not Applicable", "NotOwnComponent": "You do not own any components.", "Number of Security Vulnerabilities": "Number of Security Vulnerabilities", + "Obligation": "Obligation", + "Obligation Type": "Obligation Type", "OAuth Client": "OAuth Client", "OPEN": "Open", "OPTIONAL": "Optional", @@ -512,6 +527,7 @@ "Owner Accounting Unit": "Owner Accounting Unit", "Owner Billing Group": "Owner Billing Group", "Owner Country": "Owner Country", + "OSI Approved?": "OSI Approved?", "PARTIAL": "PARTIAL", "PHASEOUT": "Phaseout", "PROJECTS": "PROJECTS", @@ -616,6 +632,7 @@ "Revoke Token": "Revoke Token", "Revoke token sucessfully": "Revoke token sucessfully", "Roles": "Roles", + "Save": "Save", "SAP Design Time Repository (DTR)": "SAP Design Time Repository (DTR)", "SBOM": "SBOM", "SCAN_AVAILABLE": "Scan available", @@ -670,6 +687,7 @@ "Set To Outdated": "Set To Outdated", "Set to default text": "Set to default text", "Short Name": "Short Name", + "Shortname": "Shortname", "Show": "Show", "Show All": "Show All", "Show Assessment Summary Info": "Show Assessment Summary Info", @@ -730,6 +748,7 @@ "This function is meant to be followed by a new license import": "This function is meant to be followed by a new license import.", "This project contains": "This project {name} contains", "This release contains": "This release {name} contains: ", + "This license is": "This license is", "Title": "Title", "To": "To", "Toggle": "Toggle", @@ -750,6 +769,9 @@ "Update Setting": "Update Setting", "Update Vulnerability": "Update Vulnerability", "Update create comment": "Update create comment", + "Update Whitelist": "Update Whitelist", + "Update External Link Success!": "Update External Link Success!", + "Update External Link Failed!": "Update External Link Failed!", "Upload": "Upload", "Upload Attachment": "Upload Attachment", "Upload BOM document as": "Upload BOM document as", @@ -839,6 +861,7 @@ "project_cannot_be_created": "Project cannot be created/updated", "to edit the project relation": "to edit the project relation", "vector": "vector", - "wrong_spdx_information": "If the wrong SPDX is entered, the information will not be registered correctly" + "wrong_spdx_information": "If the wrong SPDX is entered, the information will not be registered correctly", + "(n/a)": "(n/a)" } } diff --git a/src/app/[locale]/licenses/detail/components/Detail.tsx b/src/app/[locale]/licenses/detail/components/Detail.tsx new file mode 100644 index 000000000..c295f5800 --- /dev/null +++ b/src/app/[locale]/licenses/detail/components/Detail.tsx @@ -0,0 +1,190 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 { HttpStatus, LicenseDetail, LicensePayload, ToastData } from '@/object-types' +import { ApiUtils } from '@/utils/index' +import { useSession } from 'next-auth/react' +import { useTranslations } from 'next-intl' +import { ToastMessage } from 'next-sw360' +import { useRouter } from 'next/navigation' +import { Dispatch, SetStateAction, useState } from 'react' +import { Button, ToastContainer } from 'react-bootstrap' +import styles from '../detail.module.css' +import { FiCheckCircle } from 'react-icons/fi' +import { BiXCircle } from 'react-icons/bi' +import { BsXCircle } from 'react-icons/bs' + +interface Props { + license: LicensePayload + setLicense: Dispatch> +} + +const Detail = ({ license, setLicense }: Props) => { + const t = useTranslations('default') + const { data: session } = useSession() + const router = useRouter() + + const hanldeExternalLicenseLink = (e: React.ChangeEvent) => { + setLicense({ + ...license, + externalLicenseLink: e.target.value, + }) + } + + const [toastData, setToastData] = useState({ + show: false, + type: '', + message: '', + contextual: '', + }) + + const alert = (show_data: boolean, status_type: string, message: string, contextual: string) => { + setToastData({ + show: show_data, + type: status_type, + message: message, + contextual: contextual, + }) + } + + const updateExternalLicenseLink = async () => { + const response = await ApiUtils.PATCH(`licenses/${license.shortName}`, license, session.user.access_token) + if (response.status == HttpStatus.OK) { + const data = (await response.json()) as LicenseDetail + alert(true, 'Success', t('Update External Link Success!'), 'success') + router.push('/licenses/detail?id=' + data.shortName) + } else { + alert(true, 'Fail', t('Update External Link Failed!'), 'danger') + } + } + + return ( +
+ + setToastData({ ...toastData, show: false })} + setShowToast={setToastData} + /> + + {!license.checked && ( +
+ {t('This license is')} UNCHECKED +
+ )} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{t('License Details')}
{t('Fullname')}:{license.fullName ?? ''}
{t('Shortname')}:{license.shortName ?? ''}
{t('Is checked')}: + {' '} + {license && license.checked == true ? ( + + + + ) : ( + + + + )} +
{t('Type')}:{license.licenseType?.licenseType ?? ''}
{t('OSI Approved?')}: + {' '} + {license && license.OSIApproved == 'YES' ? ( + + {t('Yes')} + + ) : ( + + {t('(n/a)')} + + )} +
{t('FSF Free/Libre?')}: + {' '} + {license && license.FSFLibre == 'YES' ? ( + + {t('Yes')} + + ) : ( + + {t('(n/a)')} + + )} +
+

{t('External link for more information')}:

+
+
+ + +
+
{t('Note')}:{license.note ?? ''}
+
+ ) +} + +export default Detail diff --git a/src/app/[locale]/licenses/detail/components/LicenseDetailOverview.tsx b/src/app/[locale]/licenses/detail/components/LicenseDetailOverview.tsx new file mode 100644 index 000000000..b910262cd --- /dev/null +++ b/src/app/[locale]/licenses/detail/components/LicenseDetailOverview.tsx @@ -0,0 +1,289 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 ChangeLogDetail from '@/components/ChangeLog/ChangeLogDetail/ChangeLogDetail' +import ChangeLogList from '@/components/ChangeLog/ChangeLogList/ChangeLogList' +import { PageButtonHeader, SideBar, ToastMessage } from '@/components/sw360' +import { Changelogs, HttpStatus, LicensePayload, LicenseTabIds, ToastData } from '@/object-types' +import { ApiUtils, CommonUtils } from '@/utils' +import { signOut, useSession } from 'next-auth/react' +import { useTranslations } from 'next-intl' +import { notFound, useSearchParams } from 'next/navigation' +import { useEffect, useState } from 'react' +import { ToastContainer } from 'react-bootstrap' +import Detail from './Detail' +import Obligations from './Obligations' +import Text from './Text' + +interface Props { + licenseId?: string +} + +const tabList = [ + { + id: LicenseTabIds.DETAILS, + name: 'Details', + }, + { + id: LicenseTabIds.TEXT, + name: 'Text', + }, + { + id: LicenseTabIds.OBLIGATIONS, + name: 'Obligations', + }, + { + id: LicenseTabIds.CHANGE_LOG, + name: 'Change Log', + }, +] + +const LicenseDetailOverview = ({ licenseId }: Props) => { + const t = useTranslations('default') + const [selectedTab, setSelectedTab] = useState(LicenseTabIds.DETAILS) + const [changesLogTab, setChangesLogTab] = useState('list-change') + const [changeLogIndex, setChangeLogIndex] = useState(-1) + const [license, setLicenseDetail] = useState(undefined) + const [changeLogList, setChangeLogList] = useState>([]) + const [isEditWhitelist, setIsEditWhitelist] = useState(false) + const [whitelist, setWhitelist] = useState>() + const { data: session, status } = useSession() + const params = useSearchParams() + const updateLicense = params.get('update') + + useEffect(() => { + if (!CommonUtils.isNullEmptyOrUndefinedString(updateLicense)) { + alert(true, 'Success', t('License updated successfully!'), 'success') + } + const controller = new AbortController() + const signal = controller.signal + ;(async () => { + try { + const response = await ApiUtils.GET(`licenses/${licenseId}`, session.user.access_token, signal) + if (response.status === HttpStatus.UNAUTHORIZED) { + return signOut() + } else if (response.status !== HttpStatus.OK) { + return notFound() + } + + const licenses = await response.json() + setLicenseDetail(licenses) + } catch (e) { + console.error(e) + } + })() + ;(async () => { + try { + const response = await ApiUtils.GET( + `changelog/document/${licenseId}`, + session.user.access_token, + signal + ) + if (response.status === HttpStatus.UNAUTHORIZED) { + return signOut() + } else if (response.status !== HttpStatus.OK) { + return notFound() + } + + const data = await response.json() + + setChangeLogList( + CommonUtils.isNullOrUndefined(data['_embedded']['sw360:changeLogs']) + ? [] + : data['_embedded']['sw360:changeLogs'] + ) + } catch (e) { + console.error(e) + } + })() + + return () => controller.abort() + }, [params, session, licenseId]) + + const handleEditWhitelist = () => { + setIsEditWhitelist(true) + } + const handleCancel = () => { + setIsEditWhitelist(false) + } + + const [toastData, setToastData] = useState({ + show: false, + type: '', + message: '', + contextual: '', + }) + + const alert = (show_data: boolean, status_type: string, message: string, contextual: string) => { + setToastData({ + show: show_data, + type: status_type, + message: message, + contextual: contextual, + }) + } + + const handleUpdateWhitelist = async () => { + const whitelistObj = Object.fromEntries(whitelist) + const response = await ApiUtils.PATCH( + `licenses/${licenseId}/whitelist`, + whitelistObj, + session.user.access_token + ) + if (response.status == HttpStatus.OK) { + alert(true, 'Success', t('License updated successfully!'), 'success') + window.location.reload() + } else { + alert(true, 'Failed', t('License updated failed!'), 'danger') + } + } + + const headerButtons = { + 'Edit License': { + link: `/licenses/edit?id=${encodeURIComponent(licenseId)}`, + type: 'primary', + name: t('Edit License'), + }, + } + + const headerButtonsEditWhitelist = { + 'Edit License': { + link: `/licenses/edit?id=${encodeURIComponent(licenseId)}`, + type: 'primary', + name: t('Edit License'), + }, + 'Edit Whitelist': { link: '', type: 'secondary', onClick: handleEditWhitelist, name: t('Edit Whitelist') }, + } + + const headerButtonsUpdateWhitelist = { + 'Update Whitelist': { link: '', type: 'primary', onClick: handleUpdateWhitelist, name: t('Update Whitelist') }, + Cancel: { link: '', type: 'light', onClick: handleCancel, name: t('Cancel') }, + } + + if (status === 'unauthenticated') { + signOut() + } else { + return ( + license && ( +
+
+ + setToastData({ ...toastData, show: false })} + setShowToast={setToastData} + /> + +
+ +
+
+
+ {selectedTab === LicenseTabIds.OBLIGATIONS ? ( + <> + {!CommonUtils.isNullEmptyOrUndefinedArray(license.obligationDatabaseIds) ? ( + <> + {isEditWhitelist ? ( + + ) : ( + + )} + + ) : ( + + )} + + ) : ( + <> + {isEditWhitelist ? ( + + ) : ( + <> + {selectedTab === LicenseTabIds.CHANGE_LOG ? ( + + ) : ( + + )} + + )} + + )} +
+ + + + +
+
+
+ ) + ) + } +} + +export default LicenseDetailOverview diff --git a/src/app/[locale]/licenses/detail/components/Obligations.tsx b/src/app/[locale]/licenses/detail/components/Obligations.tsx new file mode 100644 index 000000000..293110f2a --- /dev/null +++ b/src/app/[locale]/licenses/detail/components/Obligations.tsx @@ -0,0 +1,240 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 TableLicense from '@/components/LinkedObligations/TableLicense' +import { HttpStatus, Obligation } from '@/object-types' +import { ApiUtils, CommonUtils } from '@/utils/index' +import { signOut, useSession } from 'next-auth/react' +import { useTranslations } from 'next-intl' +import { _ } from 'next-sw360' +import { notFound, useSearchParams } from 'next/navigation' +import { useEffect, useState } from 'react' +import { Form } from 'react-bootstrap' +import styles from '../detail.module.css' +interface Props { + licenseId?: string + isEditWhitelist?: boolean + whitelist: Map + setWhitelist: React.Dispatch>> +} + +const Obligations = ({ licenseId, isEditWhitelist, whitelist, setWhitelist }: Props) => { + const t = useTranslations('default') + const { data: session } = useSession() + const [data, setData] = useState([]) + const [dataEditWhitelist, setDataEditWhitelist] = useState([]) + const params = useSearchParams() + + const buildAttachmentDetail = (item: Obligation) => { + return (event: React.MouseEvent) => { + if ((event.target as HTMLElement).className == styles.expand) { + ;(event.target as HTMLElement).className = styles.collapse + } else { + ;(event.target as HTMLElement).className = styles.expand + } + + const obligationDetail = document.getElementById(item.title) + if (!obligationDetail) { + const parent = (event.target as HTMLElement).parentElement.parentElement.parentElement + const html = ` + + + + + + +
${ + item.text.replace(/[\r\n]/g, '
').replace(/\t/g, '    ') ?? '' + }
+ ` + const tr = document.createElement('tr') + tr.id = item.title + tr.innerHTML = html + + parent.parentNode.insertBefore(tr, parent.nextSibling) + } else { + if (obligationDetail.hidden == true) { + obligationDetail.hidden = false + } else { + obligationDetail.hidden = true + } + } + } + } + + useEffect(() => { + const controller = new AbortController() + const signal = controller.signal + + ;(async () => { + try { + const response = await ApiUtils.GET(`licenses/${licenseId}`, session.user.access_token, signal) + if (response.status === HttpStatus.UNAUTHORIZED) { + return signOut() + } else if (response.status !== HttpStatus.OK) { + return notFound() + } + + const license = await response.json() + if (license._embedded === undefined) { + setData([]) + } else { + const data = license._embedded['sw360:obligations'] + .map((item: Obligation) => [ + item, + item.title, + item.obligationType && + item.obligationType.charAt(0) + item.obligationType.slice(1).toLowerCase(), + item.customPropertyToValue, + item.text, + item.whitelist, + ]) + .filter((item: any) => item[5].length !== 0) + const whitelist = new Map() + data.forEach((element: any) => { + whitelist.set(CommonUtils.getIdFromUrl(element[0]._links.self.href), true) + }) + setWhitelist(whitelist) + setData(data) + const dataEditWhitelist = license._embedded['sw360:obligations'].map((item: Obligation) => [ + item, + item.text, + item.customPropertyToValue, + ]) + setDataEditWhitelist(dataEditWhitelist) + } + } catch (e) { + console.error(e) + } + })() + return () => controller.abort() + }, [params, session, licenseId]) + + const columns = [ + { + id: 'check', + name: _(), + formatter: (item: Obligation) => + _(), + sort: false, + width: '10%', + }, + { + id: 'obligation', + name: t('Obligation'), + sort: true, + width: '30%', + }, + { + id: 'obligationType', + name: t('Obligation Type'), + sort: true, + width: '30%', + }, + { + id: 'furtherProperties', + name: t('Further properties'), + sort: true, + width: '30%', + }, + ] + + const handlerRadioButton = (item: any) => { + const id: string = CommonUtils.getIdFromUrl(item._links.self.href) + if (whitelist.has(id)) { + if (!whitelist.get(id)) { + whitelist.set(id, true) + } else { + whitelist.set(id, false) + } + } else { + whitelist.set(id, true) + } + setWhitelist(whitelist) + } + + const columnEditWhitelists = [ + { + id: 'obligationId', + name: 'Whitelist', + formatter: (item: any) => + _( + { + handlerRadioButton(item) + }} + > + ), + }, + { + id: 'text', + name: 'Obligation', + sort: true, + width: '30%', + }, + { + id: 'Further properties', + name: 'Further properties', + sort: true, + width: '10%', + }, + ] + const style = { + table: { + fontSize: '14px', + }, + th: { + 'text-align': 'center', + 'font-size': '14px', + }, + td: { + 'text-align': 'center', + }, + } + + const styleEditWhiteList = { + table: { + fontSize: '14px', + }, + th: { + 'text-align': 'left', + 'font-size': '14px', + }, + td: { + 'text-align': 'left', + }, + } + + return ( +
+ {isEditWhitelist ? ( + + ) : ( +
+ +
+ )} +
+ ) +} + +export default Obligations diff --git a/src/app/[locale]/licenses/detail/components/Text.tsx b/src/app/[locale]/licenses/detail/components/Text.tsx new file mode 100644 index 000000000..579e38600 --- /dev/null +++ b/src/app/[locale]/licenses/detail/components/Text.tsx @@ -0,0 +1,42 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 { LicenseDetail } from '@/object-types' +import { useTranslations } from 'next-intl' +import styles from '../detail.module.css' + +interface Props { + license: LicenseDetail +} + +const Text = ({ license }: Props) => { + const t = useTranslations('default') + return ( +
+ + + + + + + + + + + +
{t('License Text')}
+
{license.text ?? ''}
+
+
+ ) +} + +export default Text diff --git a/src/app/[locale]/licenses/detail/detail.module.css b/src/app/[locale]/licenses/detail/detail.module.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/[locale]/licenses/detail/page.tsx b/src/app/[locale]/licenses/detail/page.tsx new file mode 100644 index 000000000..427107043 --- /dev/null +++ b/src/app/[locale]/licenses/detail/page.tsx @@ -0,0 +1,20 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 { useSearchParams } from 'next/navigation' +import LicenseDetailOverview from './components/LicenseDetailOverview' + +const Detail = () => { + const searchParams = useSearchParams() + const licenseId = searchParams.get('id') + return +} + +export default Detail diff --git a/src/components/LinkedObligations/LinkedObligations.tsx b/src/components/LinkedObligations/LinkedObligations.tsx new file mode 100644 index 000000000..9d591bc91 --- /dev/null +++ b/src/components/LinkedObligations/LinkedObligations.tsx @@ -0,0 +1,43 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 LicensePayload from '../../object-types/LicensePayload' +import TableLinkedObligations from './TableLinkedObligations/TableLinkedObligations' + +interface Props { + data: Array + setData: (data: Array) => void + licensePayload?: LicensePayload + setLicensePayload?: React.Dispatch> +} + +const LinkedObligations = ({ data, setData, licensePayload, setLicensePayload }: Props) => { + const setObligationIdToLicensePayLoad = (obligationIds: Array) => { + setLicensePayload({ + ...licensePayload, + obligationDatabaseIds: obligationIds, + }) + } + + return ( +
+
+ +
+
+ ) +} + +export default LinkedObligations diff --git a/src/components/LinkedObligations/TableLicense.tsx b/src/components/LinkedObligations/TableLicense.tsx new file mode 100644 index 000000000..a2e745a78 --- /dev/null +++ b/src/components/LinkedObligations/TableLicense.tsx @@ -0,0 +1,131 @@ +// Copyright (c) Helio Chissini de Castro, 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 FilterSearch from '@/components/LinkedObligations/TableLinkedObligations/FilterSearch' +import { Config, Grid } from 'gridjs' +import * as React from 'react' +import { Component, RefObject, createRef } from 'react' +import { Form } from 'react-bootstrap' + +const defaultOptions = { + pagination: { limit: 10 }, + search: false, + selector: true, + sort: false, +} +type MessageFormat = (...args: []) => string +type Message = string | MessageFormat +export type Language = { [key: string]: Message | Language } + +interface TableProps extends Partial { + title?: string + searchFunction?: (event: React.KeyboardEvent) => void + selector?: boolean + language?: Language +} + +class TableLicense extends Component { + private wrapper: RefObject = createRef() + // Grid.js instance + private readonly instance: Grid = null + private tableProps: TableProps = {} + + constructor(props: TableProps) { + super(props) + this.tableProps = { ...defaultOptions, ...props } + + if (this.tableProps.server) { + this.tableProps = { + ...this.tableProps, + pagination: { + limit: 10, + server: { + url: (prev: string, page: number, limit: number) => + `${prev}${prev.includes('?') ? '&' : '?'}page=${page}&page_entries=${limit}`, + }, + }, + data: undefined, + } + } + + this.instance = new Grid(this.tableProps) + } + + getInstance(): Grid { + return this.instance + } + + componentDidMount(): void { + if (this.wrapper.current.childNodes.length > 0) { + this.wrapper.current.innerHTML = '' + } + this.instance.render(this.wrapper.current) + } + + componentDidUpdate(): void { + this.instance.config.plugin.remove('pagination') + this.instance.config.plugin.remove('search') + this.instance.updateConfig(this.props).forceRender() + } + + handlePageSizeChange = (event: React.ChangeEvent) => { + this.instance.config.plugin.remove('pagination') + const pageSize = parseInt(event.target.value, 10) + this.instance + .updateConfig({ + pagination: { + limit: pageSize, + server: this.tableProps.server + ? { + url: (prev: string, page: number, limit: number) => + `${prev}${prev.includes('?') ? '&' : '?'}page=${page}&page_entries=${limit}`, + } + : undefined, + }, + }) + .forceRender() + } + + render(): React.ReactElement { + return ( + <> +
+ {this.props.selector && ( + <> +
+
+ Show + + entries +
+
+ {this.props.searchFunction && ( + + )} + + )} +
+
+ + ) + } +} + +export default TableLicense diff --git a/src/components/LinkedObligations/TableLinkedObligations/FilterSearch.tsx b/src/components/LinkedObligations/TableLinkedObligations/FilterSearch.tsx new file mode 100644 index 000000000..3c2aa1eef --- /dev/null +++ b/src/components/LinkedObligations/TableLinkedObligations/FilterSearch.tsx @@ -0,0 +1,41 @@ +// Copyright (c) Helio Chissini de Castro, 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' + +interface Props { + title?: string + searchFunction?: (event: React.KeyboardEvent) => void +} +function FilterSearch({ title, searchFunction }: Props) { + return ( +
+ +    + +
+ ) +} + +export default FilterSearch diff --git a/src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.module.css b/src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.module.css new file mode 100644 index 000000000..efbb8989b --- /dev/null +++ b/src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.module.css @@ -0,0 +1,37 @@ +/* + Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. + Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 +*/ + +.expand:before { + content: '▼'; +} + +.collapse:before { + content: '►'; +} + +.expand:hover { + cursor: pointer; +} + +.collapse:hover { + cursor: pointer; +} + +.attachment-table table:first-child tr td:first-child div { + text-align: center; +} + +.delete-btn { + color: black; +} + +.delete-btn:hover { + cursor: pointer; +} diff --git a/src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.tsx b/src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.tsx new file mode 100644 index 000000000..d8a666706 --- /dev/null +++ b/src/components/LinkedObligations/TableLinkedObligations/TableLinkedObligations.tsx @@ -0,0 +1,143 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 + +import React, { useState } from 'react' +import { FaTrashAlt } from 'react-icons/fa' + +import DeleteObligationDialog from '@/app/[locale]/licenses/components/DeleteObligationDialog' +import { useTranslations } from 'next-intl' +import { _ } from 'next-sw360' +import Obligation from '../../../object-types/Obligation' +import TableLicense from '../TableLicense' +import styles from './TableLinkedObligations.module.css' + +interface Props { + data: Array + setData: (data: Array) => void + setObligationIdToLicensePayLoad?: (releaseIdToRelationships: Array) => void +} + +export default function TableLinkedObligations({ data, setData, setObligationIdToLicensePayLoad }: Props) { + const t = useTranslations('default') + const [obligation, setObligation] = useState() + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) + const deleteObligation = (item: Obligation) => { + setObligation(item) + setDeleteDialogOpen(true) + } + + const buildAttachmentDetail = (item: Obligation) => { + return (event: React.MouseEvent) => { + if ((event.target as HTMLElement).className == styles.expand) { + ;(event.target as HTMLElement).className = styles.collapse + } else { + ;(event.target as HTMLElement).className = styles.expand + } + + const attachmentDetail = document.getElementById(item.title) + if (!attachmentDetail) { + const parent = (event.target as HTMLElement).parentElement.parentElement.parentElement + const html = ` + + + + + + +
${ + item.text.replace(/[\r\n]/g, '
').replace(/\t/g, '    ') ?? '' + }
+ ` + const tr = document.createElement('tr') + tr.id = item.title + tr.innerHTML = html + + parent.parentNode.insertBefore(tr, parent.nextSibling) + } else { + if (attachmentDetail.hidden == true) { + attachmentDetail.hidden = false + } else { + attachmentDetail.hidden = true + } + } + } + } + + const style = { + table: { + fontSize: '14px', + }, + th: { + 'text-align': 'center', + 'font-size': '14px', + }, + td: { + 'text-align': 'center', + }, + } + + const columns = [ + { + id: 'check', + name: _(), + formatter: (item: Obligation) => + _(), + sort: false, + }, + { + id: 'obligation', + name: t('Obligation Title'), + sort: true, + }, + { + id: 'obligationType', + name: t('Obligation Type'), + sort: true, + }, + { + id: 'action', + name: t('Action'), + formatter: (item: Obligation) => + _( +
+ + deleteObligation(item)} /> + +
+ ), + }, + ] + const [search, setSearch] = useState({}) + const doSearch = (event: React.KeyboardEvent) => { + setSearch({ keyword: event.currentTarget.value }) + } + + return ( +
+ + +
+ ) +} diff --git a/src/components/sw360/PageButtonHeader/PageButtonHeader.tsx b/src/components/sw360/PageButtonHeader/PageButtonHeader.tsx index 65127ec76..b02cd54a3 100644 --- a/src/components/sw360/PageButtonHeader/PageButtonHeader.tsx +++ b/src/components/sw360/PageButtonHeader/PageButtonHeader.tsx @@ -11,12 +11,19 @@ import Link from 'next/link' +import { useTranslations } from 'next-intl' import { PageButtonHeaderProps } from './PageButtonHeader.types' import styles from './pagebuttonheader.module.css' -import { Button } from 'react-bootstrap' - -function PageButtonHeader({ title, buttons, children }: PageButtonHeaderProps) { +function PageButtonHeader({ + title, + buttons, + children, + checked, + changesLogTab, + changeLogIndex, + setChangesLogTab, +}: PageButtonHeaderProps) { let buttonList: JSX.Element[] = [] if (buttons) { buttonList = Object.entries(buttons).map(([key, value]) => { @@ -24,19 +31,81 @@ function PageButtonHeader({ title, buttons, children }: PageButtonHeaderProps) { // Button needs to link to the referenced page from props (value) // and switch to the correct tab (key) - + ) }) } + const t = useTranslations('default') return ( -
- {buttonList} - {children} - {title &&
{title}
} +
+
+
+
+ {buttonList} + {children} +
+
+
+ {title && ( +
+
+ {title} + {checked != undefined && ( + <> + {checked ? ( + + CHECKED + + ) : ( + + UNCHECKED + + )} + + )} +
+
+ )} + {changesLogTab && ( + + )}
) } diff --git a/src/components/sw360/PageButtonHeader/PageButtonHeader.types.ts b/src/components/sw360/PageButtonHeader/PageButtonHeader.types.ts index 985002991..577c53f52 100644 --- a/src/components/sw360/PageButtonHeader/PageButtonHeader.types.ts +++ b/src/components/sw360/PageButtonHeader/PageButtonHeader.types.ts @@ -11,6 +11,10 @@ import { ReactNode } from 'react' export interface PageButtonHeaderProps { title?: string - buttons?: { [key: string]: { [key: string]: any} } + buttons?: { [key: string]: { [key: string]: any } } children?: ReactNode + checked?: boolean + changesLogTab?: string + changeLogIndex?: number + setChangesLogTab?: (key: string) => void } diff --git a/src/components/sw360/PageButtonHeader/pagebuttonheader.module.css b/src/components/sw360/PageButtonHeader/pagebuttonheader.module.css index da1bb0fa1..526b535ea 100644 --- a/src/components/sw360/PageButtonHeader/pagebuttonheader.module.css +++ b/src/components/sw360/PageButtonHeader/pagebuttonheader.module.css @@ -8,3 +8,8 @@ white-space: nowrap; color: rgba(108, 117, 125, 0.4); } + +.buttonheader-toolbar { + margin-bottom: 1rem; + align-items: center; +} diff --git a/src/object-types/LicenseDetail.ts b/src/object-types/LicenseDetail.ts new file mode 100644 index 000000000..b00b0817b --- /dev/null +++ b/src/object-types/LicenseDetail.ts @@ -0,0 +1,29 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 +import Obligation from './Obligation' + +export default interface LicenseDetail { + id?: string + type?: string + shortName?: string + fullName?: string + externalLicenseLink?: string + note?: string + externalIds?: { + externalId?: string + } + OSIApproved?: string + FSFLibre?: string + obligations?: Array + obligationDatabaseIds?: Array + text?: string + checked?: boolean + issetBitfield?: number +} diff --git a/src/object-types/enums/LicenseTabIds.ts b/src/object-types/enums/LicenseTabIds.ts new file mode 100644 index 000000000..c3b8da659 --- /dev/null +++ b/src/object-types/enums/LicenseTabIds.ts @@ -0,0 +1,18 @@ +// Copyright (C) TOSHIBA CORPORATION, 2023. Part of the SW360 Frontend Project. +// Copyright (C) Toshiba Software Development (Vietnam) Co., Ltd., 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 + +enum LicenseTabIds { + DETAILS = 'tab-details', + TEXT = 'tab-text', + OBLIGATIONS = 'tab-obligations', + CHANGE_LOG = 'tab-changeLogs', +} + +export default LicenseTabIds diff --git a/src/object-types/index.ts b/src/object-types/index.ts index 9cef4c604..f68ec5942 100644 --- a/src/object-types/index.ts +++ b/src/object-types/index.ts @@ -34,6 +34,7 @@ import LinkedRelease from './LinkedRelease' import LinkedVulnerability from './LinkedVulnerability' import Links from './Links' import LicensePayload from './LicensePayload' +import LicenseDetail from './LicenseDetail' import Moderators from './Moderators' import ModeratorsType from './ModeratorsType' import NavList from './NavList' @@ -92,6 +93,7 @@ export type { LinkedVulnerability, Links, LicensePayload, + LicenseDetail, Moderators, ModeratorsType, NodeData, @@ -135,6 +137,7 @@ import AttachmentType from './enums/AttachmentTypes' import CommonTabIds from './enums/CommonTabsIds' import ComponentTabIds from './enums/ComponentTabIds' import DocumentTypes from './enums/DocumentTypes' +import LicenseTabIds from './enums/LicenseTabIds' import HttpStatus from './enums/HttpStatus' import ReleaseTabIds from './enums/ReleaseTabIds' import VulnerabilitiesVerificationState from './enums/VulnerabilitiesVerificationState' @@ -145,6 +148,7 @@ export { CommonTabIds, ComponentTabIds, DocumentTypes, + LicenseTabIds, HttpStatus, ReleaseTabIds, VulnerabilitiesVerificationState, From 7e5e0120eb16c04e40ce95c4cd190e72fa8056e6 Mon Sep 17 00:00:00 2001 From: tuannn2 Date: Thu, 22 Feb 2024 14:51:44 +0700 Subject: [PATCH 4/5] feat(license): Handle css for license detail Signed-off-by: tuannn2 --- messages/en.json | 1 + .../licenses/detail/components/Detail.tsx | 5 ++-- .../components/LicenseDetailOverview.tsx | 3 ++- .../licenses/detail/components/Text.tsx | 2 +- .../licenses/detail/detail.module.css | 20 ++++++++++++++ .../LinkedObligations/TableLicense.tsx | 5 ++-- .../TableLinkedObligations/FilterSearch.tsx | 13 +++------ .../TableLinkedObligations.module.css | 24 +++++++++++++++++ .../TableLinkedObligations.tsx | 27 ++++--------------- .../PageButtonHeader/PageButtonHeader.tsx | 13 +++++---- .../pagebuttonheader.module.css | 25 +++++++++++++++++ 11 files changed, 92 insertions(+), 46 deletions(-) diff --git a/messages/en.json b/messages/en.json index 6b94f463a..d2917a0cd 100644 --- a/messages/en.json +++ b/messages/en.json @@ -508,6 +508,7 @@ "Number of Security Vulnerabilities": "Number of Security Vulnerabilities", "Obligation": "Obligation", "Obligation Type": "Obligation Type", + "Obligation Title": "Obligation Title", "OAuth Client": "OAuth Client", "OPEN": "Open", "OPTIONAL": "Optional", diff --git a/src/app/[locale]/licenses/detail/components/Detail.tsx b/src/app/[locale]/licenses/detail/components/Detail.tsx index c295f5800..0add3467b 100644 --- a/src/app/[locale]/licenses/detail/components/Detail.tsx +++ b/src/app/[locale]/licenses/detail/components/Detail.tsx @@ -80,8 +80,7 @@ const Detail = ({ license, setLicense }: Props) => { {!license.checked && (
{t('This license is')} UNCHECKED
@@ -168,7 +167,7 @@ const Detail = ({ license, setLicense }: Props) => { />