From ede4ddc6c79a17ee584c8fc58f3982d5f5dff60d Mon Sep 17 00:00:00 2001 From: deo002 Date: Tue, 7 Jan 2025 12:28:08 +0530 Subject: [PATCH] feat(generate_source_code_bundle): Add ui for generate source code bundle table display --- messages/de.json | 3 + messages/en.json | 3 + messages/es.json | 3 + messages/fr.json | 3 + messages/ja.json | 3 + messages/ko.json | 3 + messages/pt-BR.json | 3 + messages/vi.json | 3 + messages/zh-CN.json | 3 + messages/zh-TW.json | 3 + .../[id]/components/LicenseClearing.tsx | 10 +- .../components/GenerateSourceCodeBundle.tsx | 346 +++++++++++++++--- src/styles/globals.css | 12 + 13 files changed, 333 insertions(+), 65 deletions(-) diff --git a/messages/de.json b/messages/de.json index ba697cff6..15afd6d18 100644 --- a/messages/de.json +++ b/messages/de.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "Vollständiger Name ist nicht null oder leer!", "Fullname shortname should not be null or empty": "Vollständiger Name und Kurzname sollten nicht null oder leer sein", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "QUELLCODEBUNDLE GENERIEREN", "GNU arch": "GNU arch", "General": "Allgemeines", "General Information": "Allgemeine Informationen", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "Bitte laden Sie das SBOM-Dokument {fileFormats hoch.", "URL": "URL", "Unauthorized request": "Unbefugte Anfrage!", + "USED_BY_PROJECTS": "Wird in {count} Projekten verwendet", "Unknown": "Unbekannt", "Unsubscribe": "Abbestellen", "Update": "Aktualisierung", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "Es gibt Probleme mit der Software. Bitte entfernen Sie sie bald, wenn Sie sie verwenden", "mainline_state_specific_tooltip": "Die Software wird im Allgemeinen nicht empfohlen, ist aber für spezielle Anwendungsfälle oder für diese bestimmte Version akzeptabel", "n a modified list": "n / A. (geänderte Liste)", + "not used in any project yet": "noch in keinem Projekt verwendet", "number of the vulnerabilities were matched by": "{count} der Schwachstellen wurden von {matchedBy}", "number_of_attachments_not_match_condition": "Es muss genau ein Quell-Ansatz sein, aber es gibt {count} bei dieser Freigabe. Bitte kommen Sie zurück, sobald Sie das korrigiert haben.", "other documents might use the licenses": "Hinweis: Andere Dokumente können die Lizenzen verwenden.", diff --git a/messages/en.json b/messages/en.json index b3f33a3a0..f2cdadb55 100644 --- a/messages/en.json +++ b/messages/en.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "Fullname not null or empty", "Fullname shortname should not be null or empty": "Fullname, shortname should not be null or Empty", "Further properties": "Further properties", + "GENERATE SOURCE CODE BUNDLE": "GENERATE SOURCE CODE BUNDLE", "GNU arch": "GNU arch", "General": "General", "General Information": "General Information", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "Please upload the {fileFormats} SBOM document.", "URL": "URL", "Unauthorized request": "Unauthorized request!", + "USED_BY_PROJECTS": "used in {count} projects", "Unknown": "Unknown", "Unsubscribe": "Unsubscribe", "Update": "Update", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "The software has issues, please consider removing it soon, if in use", "mainline_state_specific_tooltip": "The software is not recommended in general, but for special use case or for this particular version it is acceptable", "n a modified list": "n.a. (modified list)", + "not used in any project yet": "not used in any project yet", "number of the vulnerabilities were matched by": "{count} of the vulnerabilities were matched by {matchedBy}", "number_of_attachments_not_match_condition": "There has to be exactly one source attachment, but there are {count} at this release. Please come back once you corrected that.", "other documents might use the licenses": "Note: other documents might use the licenses.", diff --git a/messages/es.json b/messages/es.json index ac96a0c28..dbdf2d5ca 100644 --- a/messages/es.json +++ b/messages/es.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "¡El nombre completo no es nulo ni está vacío!", "Fullname shortname should not be null or empty": "Nombre completo, nombre corto no debe ser nulo o vacío", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "GENERAR PAQUETE DE CÓDIGO FUENTE", "GNU arch": "Archivo de GNU", "General": "General", "General Information": "Información general", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "Cargue el documento SBOM {fileFormats}.", "URL": "URL", "Unauthorized request": "Solicitud no autorizada!", + "USED_BY_PROJECTS": "utilizado en {count} proyectos", "Unknown": "Desconocido", "Unsubscribe": "Darse de baja", "Update": "Actualización", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "El software tiene problemas; considere eliminarlo pronto, si está en uso.", "mainline_state_specific_tooltip": "El software no se recomienda en general, pero para casos de uso especiales o para esta versión particular es aceptable.", "n a modified list": "n / A. (lista modificada)", + "not used in any project yet": "Aún no se ha utilizado en ningún proyecto.", "number of the vulnerabilities were matched by": "{contador} de las vulnerabilidades fueron igualadas por {matchedBy}", "number_of_attachments_not_match_condition": "Tiene que haber exactamente un apego fuente, pero hay {cuenta} en esta liberación. Por favor, vuelve una vez que lo corrigiste.", "other documents might use the licenses": "Nota: otros documentos podrían usar las licencias.", diff --git a/messages/fr.json b/messages/fr.json index e9377b399..4452f08de 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "Nom complet ni nul ni vide !", "Fullname shortname should not be null or empty": "Nom complet, nom court ne doit pas être nul ou vide", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "GÉNÉRER UN GROUPE DE CODE SOURCE", "GNU arch": "Arche GNU", "General": "Généralités", "General Information": "Informations générales", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "Veuillez télécharger le document SBOM {fileFormats>.", "URL": "URL", "Unauthorized request": "Demande non autorisée !", + "USED_BY_PROJECTS": "utilisé dans {count} projets", "Unknown": "Inconnu", "Unsubscribe": "Se désabonner", "Update": "Mise à jour", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "Le logiciel a des problèmes, veuillez envisager de le supprimer bientôt, s'il est utilisé", "mainline_state_specific_tooltip": "Le logiciel n'est pas recommandé en général, mais pour des cas d'utilisation particuliers ou pour cette version particulière, il est acceptable", "n a modified list": "n / A. (liste modifiée)", + "not used in any project yet": "pas encore utilisé dans aucun projet", "number of the vulnerabilities were matched by": "{compte} des vulnérabilités ont été jumelées par {comparé par}", "number_of_attachments_not_match_condition": "Il doit y avoir exactement une pièce jointe source, mais il y a {count} à cette publication. Revenez une fois que vous l'avez corrigé.", "other documents might use the licenses": "Remarque : d'autres documents pourraient utiliser les licences.", diff --git a/messages/ja.json b/messages/ja.json index cb58cca0d..89ad9796b 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "フルネームが null または空ではありません!", "Fullname shortname should not be null or empty": "フルネーム、ショートネームはnullまたは空であってはなりません", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "ソースコードバンドルの生成", "GNU arch": "GNU アーチ", "General": "一般的な", "General Information": "一般情報", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "{fileFormats} SBOM ドキュメントをアップロードしてください。", "URL": "URL", "Unauthorized request": "不正なリクエストです!", + "USED_BY_PROJECTS": "{count} 個のプロジェクトで使用されています", "Unknown": "未知", "Unsubscribe": "購読を解除する", "Update": "アップデート", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "ソフトウェアには問題があります。使用中の場合はすぐに削除することを検討してください。", "mainline_state_specific_tooltip": "このソフトウェアは一般的に推奨されませんが、特殊な使用例またはこの特定のバージョンでは許容されます。", "n a modified list": "いや。 (変更されたリスト)", + "not used in any project yet": "まだどのプロジェクトでも使用されていません", "number of the vulnerabilities were matched by": "{count} of the vulnerabilities were matched by {matchedBy}", "number_of_attachments_not_match_condition": "There has to be exactly one source attachment, but there are {count} at this release. Please come back once you corrected that.", "other documents might use the licenses": "注意:その他の文書はライセンスを使用する場合があります。", diff --git a/messages/ko.json b/messages/ko.json index 7e75f1a9b..ccdb31994 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "전체 이름은 null이거나 비어 있지 않습니다!", "Fullname shortname should not be null or empty": "전체 이름, 단축 이름은 null이거나 비어 있을 수 없습니다.", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "소스 코드 번들 생성", "GNU arch": "GNU 아카이브", "General": "주요사업", "General Information": "일반 정보", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "{fileFormats} SBOM 문서를 업로드하세요.", "URL": "사이트 맵", "Unauthorized request": "무단 요청!", + "USED_BY_PROJECTS": "{count}개 프로젝트에서 사용됨", "Unknown": "이름 *", "Unsubscribe": "구독 취소", "Update": "(주)", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "소프트웨어에 문제가 있습니다. 사용 중인 경우 즉시 제거하는 것이 좋습니다.", "mainline_state_specific_tooltip": "이 소프트웨어는 일반적으로 권장되지 않지만 특별한 사용 사례나 이 특정 버전의 경우 허용됩니다.", "n a modified list": "해당 없음 (수정된 목록)", + "not used in any project yet": "아직 어떤 프로젝트에도 사용되지 않았습니다", "number of the vulnerabilities were matched by": "이름 * 취약점의 일치 이름 *", "number_of_attachments_not_match_condition": "정확히 하나의 소스 첨부 파일이 있지만,이 릴리스에서 {count}이 있습니다. 한 번에 다시 오십시오.", "other documents might use the licenses": "참고 : 다른 문서는 라이센스를 사용할 수 있습니다.", diff --git a/messages/pt-BR.json b/messages/pt-BR.json index 783bcd004..fea34b6ff 100644 --- a/messages/pt-BR.json +++ b/messages/pt-BR.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "Nome completo não nulo ou vazio!", "Fullname shortname should not be null or empty": "Nome completo, nome abreviado não deve ser nulo ou vazio", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "GERAR PACOTE DE CÓDIGO FONTE", "GNU arch": "Arco GNU", "General": "Geral", "General Information": "Informações gerais", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "Faça upload do documento SBOM {fileFormats}.", "URL": "URL", "Unauthorized request": "Pedido não autorizado!", + "USED_BY_PROJECTS": "usado em {count} projetos", "Unknown": "Desconhecido", "Unsubscribe": "Cancelar inscrição", "Update": "Atualização", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "O software tem problemas, considere removê-lo logo, se estiver em uso", "mainline_state_specific_tooltip": "O software não é recomendado em geral, mas para casos de uso especiais ou para esta versão específica é aceitável", "n a modified list": "n / D. (lista modificada)", + "not used in any project yet": "ainda não usado em nenhum projeto", "number of the vulnerabilities were matched by": "{count} das vulnerabilidades foram correspondidas por {matchedBy}", "number_of_attachments_not_match_condition": "Tem que haver exatamente um anexo de origem, mas há {count} nesta versão. Por favor, volte depois de corrigir isso.", "other documents might use the licenses": "Nota: outros documentos podem utilizar as licenças.", diff --git a/messages/vi.json b/messages/vi.json index 7c9023714..65f761e2d 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "Tên đầy đủ không rỗng hoặc trống!", "Fullname shortname should not be null or empty": "Tên đầy đủ, tên viết tắt không được rỗng hoặc trống", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "TẠO BỘ MÃ NGUỒN", "GNU arch": "Vòm GNU", "General": "Tổng quan", "General Information": "Thông tin chung", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "Vui lòng tải lên tài liệu SBOM {fileFormats.", "URL": "URL", "Unauthorized request": "Yêu cầu trái phép!", + "USED_BY_PROJECTS": "được sử dụng trong các dự án {count", "Unknown": "không xác định", "Unsubscribe": "Hủy đăng ký", "Update": "Cập nhật", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "Phần mềm có vấn đề mong bạn cân nhắc gỡ bỏ sớm nếu đang sử dụng", "mainline_state_specific_tooltip": "Phần mềm nói chung không được khuyến nghị, nhưng đối với trường hợp sử dụng đặc biệt hoặc đối với phiên bản cụ thể này thì có thể chấp nhận được", "n a modified list": "không (danh sách đã sửa đổi)", + "not used in any project yet": "chưa được sử dụng trong bất kỳ dự án nào", "number of the vulnerabilities were matched by": "{count} số các lỗ hổng đã được so khớp bởi {matchedBy}", "number_of_attachments_not_match_condition": "Phải có chính xác một tệp đính kèm nguồn nhưng có {count} ở bản phát hành này. \nVui lòng quay lại sau khi bạn sửa lỗi đó.", "other documents might use the licenses": "Lưu ý: các tài liệu khác có thể sử dụng giấy phép.", diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 53f6ef64e..1b3990c86 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "全名不为空!", "Fullname shortname should not be null or empty": "全名、简称不得为空或为空", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "生成源代码包", "GNU arch": "GNU 拱门", "General": "一般的", "General Information": "一般信息", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "请上传{fileFormats} SBOM 文档。", "URL": "网址", "Unauthorized request": "未经授权的请求!", + "USED_BY_PROJECTS": "用于{count}个项目", "Unknown": "未知", "Unsubscribe": "退订", "Update": "更新", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "该软件有问题,如果正在使用,请考虑尽快删除", "mainline_state_specific_tooltip": "一般情况下不推荐使用该软件,但对于特殊用例或此特定版本是可以接受的", "n a modified list": "不适用(修改后的列表)", + "not used in any project yet": "尚未在任何项目中使用", "number of the vulnerabilities were matched by": "{count} 个漏洞与 {matchedBy} 匹配", "number_of_attachments_not_match_condition": "必须只有一个源附件,但此版本中有 {count} 个。\n更正后请回来。", "other documents might use the licenses": "注:其他文件可能使用许可证。", diff --git a/messages/zh-TW.json b/messages/zh-TW.json index dfa4b30da..4fc42115b 100644 --- a/messages/zh-TW.json +++ b/messages/zh-TW.json @@ -503,6 +503,7 @@ "Fullname not null or empty": "全名不為空!", "Fullname shortname should not be null or empty": "全名、簡稱不得為空或為空", "Further properties": "", + "GENERATE SOURCE CODE BUNDLE": "產生原始碼包", "GNU arch": "GNU 拱門", "General": "一般", "General Information": "一般資訊", @@ -1079,6 +1080,7 @@ "UPLOAD SBOM HEADER": "請上傳{fileFormats} SBOM 文件。", "URL": "網址", "Unauthorized request": "未經授權的請求!", + "USED_BY_PROJECTS": "用於{count}個項目", "Unknown": "未知", "Unsubscribe": "退訂", "Update": "更新", @@ -1208,6 +1210,7 @@ "mainline_state_phaseout_tooltip": "軟體有問題,如果正在使用,請考慮盡快刪除", "mainline_state_specific_tooltip": "一般情況下不建議使用該軟體,但對於特殊用例或此特定版本是可以接受的", "n a modified list": "不適用(修改後的清單)", + "not used in any project yet": "尚未在任何項目中使用", "number of the vulnerabilities were matched by": "{計數} 与脆弱性相匹配 {匹配By}", "number_of_attachments_not_match_condition": "必須有一個來源附件, 但此放行時有 {count} 。 校正了再回來吧", "other documents might use the licenses": "注:其他文件可能使用许可证。", diff --git a/src/app/[locale]/projects/detail/[id]/components/LicenseClearing.tsx b/src/app/[locale]/projects/detail/[id]/components/LicenseClearing.tsx index 1d78a006d..84e71f12a 100644 --- a/src/app/[locale]/projects/detail/[id]/components/LicenseClearing.tsx +++ b/src/app/[locale]/projects/detail/[id]/components/LicenseClearing.tsx @@ -45,8 +45,8 @@ export default function LicenseClearing({ const [showCreateClearingRequestModal, setShowCreateClearingRequestModal] = useState(false) const [showViewClearingRequestModal, setShowViewClearingRequestModal] = useState(false) - const generateSourceCodeBundle = () => { - router.push(`/projects/generateSourceCode/${projectId}`) + const generateSourceCodeBundle = (withSubProjects: boolean) => { + router.push(`/projects/generateSourceCode/${projectId}?withSubProjects=${withSubProjects ? "true" : "false"}`) } @@ -98,11 +98,13 @@ export default function LicenseClearing({ {t('Generate Source Code Bundle')} generateSourceCodeBundle()} + onClick = {() => generateSourceCodeBundle(false)} > {t('Projects only')} - + generateSourceCodeBundle(true)} + > {t('Projects with sub projects')} diff --git a/src/app/[locale]/projects/generateSourceCode/[id]/components/GenerateSourceCodeBundle.tsx b/src/app/[locale]/projects/generateSourceCode/[id]/components/GenerateSourceCodeBundle.tsx index 1a2eac6be..94d6d70b0 100644 --- a/src/app/[locale]/projects/generateSourceCode/[id]/components/GenerateSourceCodeBundle.tsx +++ b/src/app/[locale]/projects/generateSourceCode/[id]/components/GenerateSourceCodeBundle.tsx @@ -9,118 +9,342 @@ 'use client' -import { Table, _ } from "next-sw360" +import { TreeTable, _ } from 'next-sw360' import { useTranslations } from "next-intl" -import { Button, Col, Form, Row } from "react-bootstrap" +import { Button, Form, Spinner } from "react-bootstrap" import Link from "next/link" +import { useEffect, useState, ReactNode, Dispatch, SetStateAction } from "react" +import { useSession, getSession, signOut } from "next-auth/react" +import { ApiUtils, CommonUtils } from "@/utils" +import { AttachmentUsages, HttpStatus, Project, NodeData, Embedded, ProjectLinkedRelease, Release, Attachment, AttachmentUsage } from "@/object-types" +import { notFound, useSearchParams } from "next/navigation" -interface Props { - projectId: string - projectName?: string - projectVersion?: string +type LinkedProjects = Embedded + +const Capitalize = (text: string) => + text.split('_').reduce((s, c) => s + ' ' + (c.charAt(0) + c.substring(1).toLocaleLowerCase()), '') + +const fetchProjectAndReleaseDetails = (linkedProjects: Project[] | undefined, projectId: string, proj: Project[]) => { + if(!linkedProjects || proj.length !== 0) { + return + } + for(const p of linkedProjects) { + if(p['_links']['self']['href'].split('/').at(-1) === projectId + ) { + proj[0] = p + return + } else { + fetchProjectAndReleaseDetails(p["_embedded"]?.["sw360:linkedProjects"], projectId, proj) + } + } +} + +const filterSRCTypeAttachments = (attachmentUsages: AttachmentUsages) => { + for(const r of attachmentUsages['_embedded']['sw360:release']) { + r.attachments = (r.attachments ?? []).filter((att: Attachment) => att.attachmentType === 'SRC') + } } -export default function GenerateSourceCodeBundle({projectId, - projectName, - projectVersion - }: Props): JSX.Element { +export default function GenerateSourceCodeBundle({ projectId }: Readonly<{projectId: string}>): ReactNode { const t = useTranslations('default') + const { status } = useSession() + const [project, setProject] = useState() + const params = useSearchParams() + const [attachmentUsagesAndLinkedProjects, setAttachmentUsagesAndLinkedProjects] = useState<{ + attachmentUsages: AttachmentUsages + linkedProjects: LinkedProjects | null + }>() + const [data, setData] = useState() + const handleDownloadProject = (projectId: string) => { - console.log('download project', projectId, projectName, projectVersion) + console.log('download project', projectId) } + const formatReleaseAndAttachments = (attachmentUsage: AttachmentUsage[], r: Release, level: number): NodeData | undefined => { + if (!r.attachments) { + r.attachments = [] + } + const release_id = r._links?.self.href.split('/').at(-1) ?? '' + const attNodes: NodeData[] = [] + let row_color_class = 'green-cell' + if (r.attachments.length > 1) { + row_color_class = 'orange-cell' + } + for(const att of r.attachments) { + let usedIn = 0 + for(const usage of attachmentUsage) { + if(usage.attachmentContentId === att.attachmentContentId) { + usedIn++ + } + + } + const attNode: NodeData = { + rowData: [ +
+ +
, +
{level}
, +
{att.filename}
, +

+ { + usedIn !== 0 ? <>{ + t.rich('USED_BY_PROJECTS', { + count: usedIn, + strong: (chunks) => {chunks}, + }) + }: <>{t('not used in any project yet')} + } +

, +
+ {Capitalize(r.clearingState ?? '')} +
, +
+ + {att.createdBy} + +
, +
{att.checkedTeam ?? ''}
+ ] + } + attNodes.push(attNode) + } + if(attNodes.length === 0) { + return + } + const relNode: NodeData = { + rowData: [ +
, +
1
, + + {r.name ?? ''}{r.version ?? ` ${r.version}`} + , +
+ {r.componentType ?? ''} +
, +
+ {Capitalize(r.clearingState ?? '')} +
, +
, +
+ ], + children: attNodes + } + return relNode + } + + useEffect(() => { + const controller = new AbortController() + const signal = controller.signal + ;(async () => { + try { + const searchParams = Object.fromEntries(params) + if(Object.prototype.hasOwnProperty.call(searchParams, "withSubProjects") === false) { + return + } + const session = await getSession() + if (CommonUtils.isNullOrUndefined(session)) + return signOut() + const requests = [ApiUtils.GET(`projects/${projectId}`, session.user.access_token, signal)] + if(searchParams.withSubProjects === 'true') { + requests.push( + ApiUtils.GET(`projects/${projectId}/attachmentUsage?transitive=true`, session.user.access_token, signal), + ApiUtils.GET(`projects/${projectId}/linkedProjects?transitive=true`, session.user.access_token, signal) + ) + } else { + requests.push(ApiUtils.GET(`projects/${projectId}/attachmentUsage?transitive=false`, session.user.access_token, signal)) + } + const responses = await Promise.all(requests) + if (responses.filter(response => (response.status === HttpStatus.UNAUTHORIZED)).length !== 0) { + return signOut() + } else if (responses.filter(response => (response.status !== HttpStatus.OK)).length !== 0) { + return notFound() + } + + const proj = await responses[0].json() as Project + setProject(proj) + const attachmentUsages = await responses[1].json() as AttachmentUsages + const linkedProjects = (searchParams.withSubProjects === 'true') ? await responses[2].json() as LinkedProjects: null + setAttachmentUsagesAndLinkedProjects({ + attachmentUsages: attachmentUsages, + linkedProjects: linkedProjects + }) + } catch(e) { + console.error(e) + } + })() + return () => controller.abort() + }, [projectId, params]) + + + useEffect(() => { + if(attachmentUsagesAndLinkedProjects === undefined) + return + + const attachmentUsages = structuredClone(attachmentUsagesAndLinkedProjects.attachmentUsages) + filterSRCTypeAttachments(attachmentUsages) + + const tableData: NodeData[] = [] + + if(attachmentUsagesAndLinkedProjects.linkedProjects !== null) { + const linkedProjects = structuredClone(attachmentUsagesAndLinkedProjects.linkedProjects) + for (const p in attachmentUsages['linkedProjects'] ?? {}) { + const proj: Project[] = [] + fetchProjectAndReleaseDetails(linkedProjects['_embedded']['sw360:projects'], p, proj) + if(proj.length === 0) { + continue + } + const releases = new Set() + proj[0].linkedReleases?.map((r: ProjectLinkedRelease) => { + releases.add(r.release.split('/').at(-1) as string) + }) + const projectNode: NodeData = { + rowData: [ +
, +
1
, + + {proj[0].name}{proj[0].version !== undefined && ` ${proj[0].version}`} + , +
+ {Capitalize(proj[0].projectType ?? '')} +
, +
+ {Capitalize(proj[0].clearingState ?? '')} +
, +
, +
+ ], + children: [] + } + for(const r of attachmentUsages['_embedded']['sw360:release']) { + if(!r.attachments) + continue + const release_id = r._links?.self.href.split('/').at(-1) + if(release_id !== undefined && releases.has(release_id)) { + const relNode = formatReleaseAndAttachments(attachmentUsages._embedded["sw360:attachmentUsages"][0], r, 2) + if(!relNode) + continue + projectNode.children?.push(relNode) + } + } + attachmentUsages['_embedded']['sw360:release'] = + attachmentUsages['_embedded']['sw360:release'].filter((r: Release) => + !releases.has(r['_links']?.['self']['href'].split('/').at(-1) ?? '') + ) + if(projectNode.children && projectNode.children.length !== 0) { + tableData.push(projectNode) + } + } + } + + for (const r of attachmentUsages._embedded["sw360:release"]) { + const relNode = formatReleaseAndAttachments(attachmentUsages._embedded["sw360:attachmentUsages"][0], r, 1) + if(!relNode) + continue + tableData.push(relNode) + } + setData(tableData) + + }, [attachmentUsagesAndLinkedProjects]) + const columns = [ { id: 'genereateSourceCodeBundle.checkbox', - name: _(), - formatter: (id: string) => - ( -
- -
- ), + name: _(), width: '5%', sort: false, }, { id: 'genereateSourceCodeBundle.Lvl', name: t('Lvl'), - formatter: (lvl: string) => _(
{lvl}
), - width: '8%', + width: '5%', sort: true, }, { id: 'genereateSourceCodeBundle.name', name: t('Name'), - width: '18%', - formatter: (id: string, name: string) => - _( - - {name} - - ), + width: '30%', sort: true, }, { id: 'genereateSourceCodeBundle.type', name: t('Type'), width: '10%', - formatter: (type: string) => _(
{type}
), sort: true, }, { id: 'genereateSourceCodeBundle.clearingState', name: t('Clearing State'), - formatter: (clearingState: string) => _(
{clearingState}
), width: '10%', sort: true, }, { id: 'genereateSourceCodeBundle.uploadedBy', name: t('Uploaded By'), - formatter: (uploadedBy: string) => _(
{uploadedBy}
), width: '15%', sort: true, }, { id: 'genereateSourceCodeBundle.clearingTeam', name: t('Clearing Team'), - formatter: (clearingTeam: string) => _(
{clearingTeam}
), - width: '15%', + width: '8%', sort: true, }, ] - return ( - <> -
-
- - - {'GENERATE SOURCE CODE BUNDLE'} - - - {projectId && `${projectId}`} - - -
- -
- {t('No previous selection found If you have writing permissions to this project your selection will be stored automatically when downloading')} + if (status === 'unauthenticated') { + return signOut() + } else if(project === undefined) { + return ( +
+ +
+ ) + } else { + return ( + <> +
+
+
+
+ {t('GENERATE SOURCE CODE BUNDLE')} +
+
+ {project.name}{' '}{(project.version !== undefined) && `(${project.version})`} +
+
+
+ +
+ {t('No previous selection found If you have writing permissions to this project your selection will be stored automatically when downloading')} +
+ { + data + ? >} selector={true} sort={false} /> + :
+ +
+ }
- - - - - ) + + ) + } } diff --git a/src/styles/globals.css b/src/styles/globals.css index 2eb08c7e9..0f89176be 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -788,6 +788,18 @@ input::-webkit-datetime-edit { padding-left: 10rem; } +.gridjs-td:has(.green-cell) { + background-color: #edf9f0; + border-left: none; + border-right: none; +} + +.gridjs-td:has(.orange-cell) { + background-color: #fff4ec; + border-left: none; + border-right: none; +} + .header-underlined { color: #5D8EA9; border-bottom: 1px solid #5D8EA9;