diff --git a/.eslintrc.json b/.eslintrc.json index 5bd5b33b..ea770d76 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,20 +4,22 @@ "es6": true, "node": true }, + "plugins": ["css-import-order"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "plugin:import/recommended", "plugin:import/electron", - "plugin:import/typescript" + "plugin:import/typescript", + "plugin:css-import-order/recommended" ], "parser": "@typescript-eslint/parser", "settings": { "import/resolver": { "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] + "extensions": [".js", ".jsx", ".ts", ".tsx", "json"] } } - }, + } } diff --git a/package-lock.json b/package-lock.json index cba1fc13..f78820df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,6 +86,7 @@ "electron-packager": "^16.0.0", "electron-rebuild": "^3.2.9", "eslint": "^8.24.0", + "eslint-plugin-css-import-order": "^1.1.0", "eslint-plugin-import": "^2.26.0", "fork-ts-checker-webpack-plugin": "^7.2.13", "json-loader": "^0.5.7", @@ -11001,6 +11002,15 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-css-import-order": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-css-import-order/-/eslint-plugin-css-import-order-1.1.0.tgz", + "integrity": "sha512-43ODxP1sXpmgI4c+NCtXqmhkLsYGe8El1ewOlvsXKchLjWLxJw5zfp4eEg31Eni+is3jGkBL2TrNyUOOnbOMDg==", + "dev": true, + "peerDependencies": { + "eslint": ">= 6.0.0 < 9.0.0" + } + }, "node_modules/eslint-plugin-flowtype": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", diff --git a/package.json b/package.json index bfafcec5..d8fecbf4 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "electron-packager": "^16.0.0", "electron-rebuild": "^3.2.9", "eslint": "^8.24.0", + "eslint-plugin-css-import-order": "^1.1.0", "eslint-plugin-import": "^2.26.0", "fork-ts-checker-webpack-plugin": "^7.2.13", "json-loader": "^0.5.7", diff --git a/src/App.tsx b/src/App.tsx index 2edebd88..6d4738b9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,37 +1,34 @@ import React from "react"; -import { HashRouter, Routes, Route } from "react-router-dom"; +import { HashRouter, Route,Routes } from "react-router-dom"; import { ToastContainer } from "react-toastify"; +import DropZone from "./components/DropZone/DropZone"; +import Layout from "./components/Layout/Layout"; import { ROUTES } from "./constants/routes"; import { WorkbenchDBProvider } from "./contexts/dbContext"; - -import Layout from "./components/Layout/Layout"; -import DropZone from "./components/DropZone/DropZone"; - -import Home from "./pages/Home/Home"; -import TableView from "./pages/TableView/TableView"; +import { WorkbenchStateProvider } from "./contexts/stateContext"; +import About from "./pages/About/About"; +import ChartView from "./pages/ChartView/ChartView"; import FileInfoDash from "./pages/FileInfoDash/FileInfoDash"; +import Home from "./pages/Home/Home"; +import LicenseDetections from "./pages/LicenseDetections/LicenseDetections"; import LicenseInfoDash from "./pages/LicenseInfoDash/LicenseInfoDash"; import PackageInfoDash from "./pages/PackageInfoDash/PackageInfoDash"; import Packages from "./pages/Packages/Packages"; -import LicenseDetections from "./pages/LicenseDetections/LicenseDetections"; -import ChartView from "./pages/ChartView/ChartView"; -import ScanInfo from "./pages/ScanInfo/ScanInfo"; -import About from "./pages/About/About"; import PageNotFound from "./pages/PageNotFound"; +import ScanInfo from "./pages/ScanInfo/ScanInfo"; +import TableView from "./pages/TableView/TableView"; import "./utils/ensureRendererDeps"; - import "./fontawesome"; + import "rc-tree/assets/index.css"; import "react-toastify/dist/ReactToastify.css"; import "bootstrap/dist/css/bootstrap.min.css"; import "react-tooltip/dist/react-tooltip.css"; - import "./app.css"; import "./dashStyles.css"; import "./customFaColors.css"; -import { WorkbenchStateProvider } from "./contexts/stateContext"; const App = () => { return ( diff --git a/src/components/FileTree/FileTree.tsx b/src/components/FileTree/FileTree.tsx index b2b73f8e..f6aa02cc 100644 --- a/src/components/FileTree/FileTree.tsx +++ b/src/components/FileTree/FileTree.tsx @@ -16,7 +16,6 @@ const FileTree = (props: React.HTMLProps) => { initialized, importedSqliteFilePath, currentPath, - currentPathType, updateCurrentPath, } = workbenchDB; @@ -38,7 +37,9 @@ const FileTree = (props: React.HTMLProps) => { updateCurrentPath(path, pathType); } - // console.log("Current path & type", currentPath, currentPathType); + // useEffect(() => { + // console.log("Current path", currentPath); + // }, [currentPath]); if (!treeData) { return ( @@ -58,6 +59,7 @@ const FileTree = (props: React.HTMLProps) => { showLine treeData={treeData} switcherIcon={SwitcherIcon} + selectedKeys={[currentPath]} onSelect={(keys, info) => { if (keys && keys[0]) selectPath( diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 6575622c..d60650e7 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -10,13 +10,14 @@ import ImportFallback from '../ImportFallback/ImportFallback'; import { useWorkbenchDB } from '../../contexts/dbContext'; import { FILE_TREE_ROUTES, IMPORT_FALLBACK_ROUTES } from '../../constants/routes'; +import ProgressLoader from '../ProgressLoader/ProgressLoader'; -import './layout.css'; import "allotment/dist/style.css"; +import './layout.css'; const Layout = (props: React.PropsWithChildren) => { const { pathname } = useLocation(); - const { initialized } = useWorkbenchDB(); + const { initialized, loadingStatus } = useWorkbenchDB(); const isImportFallbackRoute = IMPORT_FALLBACK_ROUTES.find(route => pathname.includes(route)) !== undefined; const showFileTree = FILE_TREE_ROUTES.find(route => pathname.includes(route)) !== undefined; @@ -36,7 +37,14 @@ const Layout = (props: React.PropsWithChildren) => {
- { isImportFallbackRoute && !initialized ? : props.children } + { + isImportFallbackRoute && !initialized ? ( + loadingStatus !== null ? + + : + + ) : props.children + }
diff --git a/src/components/Layout/layout.css b/src/components/Layout/layout.css index a5830e4a..19c4e718 100644 --- a/src/components/Layout/layout.css +++ b/src/components/Layout/layout.css @@ -15,5 +15,5 @@ padding: 10px; padding-top: 5px; padding-bottom: 5px; - min-height: 95%; + min-height: 100%; } \ No newline at end of file diff --git a/src/components/LicenseDetection/FileRegionTableCols.ts b/src/components/LicenseDetection/FileRegionTableCols.ts index c9bdb78a..8c75bce9 100644 --- a/src/components/LicenseDetection/FileRegionTableCols.ts +++ b/src/components/LicenseDetection/FileRegionTableCols.ts @@ -1,4 +1,5 @@ import { ColDef } from "ag-grid-community"; +import { TickRenderer } from "../../pages/TableView/CustomCellRenderers"; const MINI_FIELD_WIDTH = 90; @@ -29,4 +30,11 @@ export const DetectionFileRegionCols: ColDef[] = [ field: 'end_line', width: MINI_FIELD_WIDTH, }, + { + headerName: 'From package', + field: 'from_package', + cellRenderer: TickRenderer, + maxWidth: 110, + suppressMenu: true, + }, ] \ No newline at end of file diff --git a/src/components/LicenseDetection/LicenseDetectionEntity.tsx b/src/components/LicenseDetection/LicenseDetectionEntity.tsx index 2db24ea7..85213a15 100644 --- a/src/components/LicenseDetection/LicenseDetectionEntity.tsx +++ b/src/components/LicenseDetection/LicenseDetectionEntity.tsx @@ -1,115 +1,95 @@ -import React, { useEffect } from 'react' -import ReactJson from '@microlink/react-json-view'; -import { AgGridReact } from 'ag-grid-react'; +import ReactJson from "@microlink/react-json-view"; +import { AgGridReact } from "ag-grid-react"; +import React from "react"; -import { DEFAULT_MATCHES_COL_DEF, DetectionMatchesCols } from './MatchesTableCols'; -import { LicenseDetectionDetails } from '../../pages/LicenseDetections/licenseDefinitions'; +import { LicenseDetectionDetails } from "../../pages/LicenseDetections/licenseDefinitions"; +import { + DEFAULT_FILE_REGION_COL_DEF, + DetectionFileRegionCols, +} from "./FileRegionTableCols"; +import { + DEFAULT_MATCHES_COL_DEF, + DetectionMatchesCols, +} from "./MatchesTableCols"; -import './licenseDetection.css' -import '../../styles/entityCommonStyles.css' -import { DEFAULT_FILE_REGION_COL_DEF, DetectionFileRegionCols } from './FileRegionTableCols'; +import "../../styles/entityCommonStyles.css"; +import "./licenseDetection.css"; interface LicenseDetectionEntityProps { - licenseDetection: LicenseDetectionDetails | null, + licenseDetection: LicenseDetectionDetails | null; } const LicenseDetectionEntity = (props: LicenseDetectionEntityProps) => { const { licenseDetection } = props; const matches = licenseDetection?.matches || []; const file_regions = licenseDetection?.file_regions || []; - useEffect(() => { - // if(matches) - // matches.push({ - // ...matches[0] - // }) - }, [matches]); - - if(!licenseDetection){ + if (!licenseDetection) { return (
-
- No License Detection to show -
+
No License Detection to show
- ) + ); } return ( -
+
- { licenseDetection.license_expression } -
+ {licenseDetection.license_expression} +
-
- { - [ - // [ "License Expression:", licenseDetection.license_expression || "NA" ], - [ "License Identifier:", licenseDetection.identifier || "NA" ], - [ "Instances:", (licenseDetection.detection_count || 0).toString() ], - ...( - licenseDetection.detection_log && - Array.isArray(licenseDetection.detection_log) && licenseDetection.detection_log.length && +
+ {[ + // [ "License Expression:", licenseDetection.license_expression || "NA" ], + ["License Identifier:", licenseDetection.identifier || "NA"], + ["Instances:", (licenseDetection.detection_count || 0).toString()], + ...(licenseDetection.detection_log && + Array.isArray(licenseDetection.detection_log) && + licenseDetection.detection_log.length && [ [ - [ - "Detection log ", - <> -
    - { - licenseDetection.detection_log.map((log_item, idx) => ( -
  • - { log_item } -
  • - )) - } -
- - ] - ] - ) - ].map(entry => ( - - - { entry[0] || "" } - - - { entry[1] || "" } - -
-
- )) - } + "Detection log ", + <> +
    + {licenseDetection.detection_log.map((log_item, idx) => ( +
  • {log_item}
  • + ))} +
+ , + ], + ]), + ].map((entry) => ( + + {entry[0] || ""} + {entry[1] || ""} +
+
+ ))}
- {/*



*/} -
+
Matches -
+
File regions -
+
Raw license detection: { collapsed={0} />
- ) -} + ); +}; -export default LicenseDetectionEntity \ No newline at end of file +export default LicenseDetectionEntity; diff --git a/src/components/LicenseDetection/MatchesTableCols.ts b/src/components/LicenseDetection/MatchesTableCols.ts index a2c15764..e5f7f496 100644 --- a/src/components/LicenseDetection/MatchesTableCols.ts +++ b/src/components/LicenseDetection/MatchesTableCols.ts @@ -18,15 +18,6 @@ export const DetectionMatchesCols: ColDef[] = [ cellRenderer: MatchLicenseExpressionRenderer, width: 270, }, - { - headerName: 'SPDX License expression', - field: 'license_expression_spdx', - cellRenderer: MatchLicenseExpressionRenderer, - cellRendererParams: { - spdxLicense: true, - }, - width: 270, - }, { headerName: 'Score', field: 'score', @@ -75,7 +66,16 @@ export const DetectionMatchesCols: ColDef[] = [ cellRendererParams: { customTextField: 'rule_identifier' }, - width: 270, + width: 250, + }, + { + headerName: 'SPDX License expression', + field: 'license_expression_spdx', + cellRenderer: MatchLicenseExpressionRenderer, + cellRendererParams: { + spdxLicense: true, + }, + width: 250, }, // { // headerName: 'LDB URL', diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index 8ae04af9..b36930b2 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -1,9 +1,8 @@ +import { faArchive, faBars, faChartColumn, faFileCode, faFileLines, faGavel, faHome, faInfoCircle, faList,faTable } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import React, { useState } from 'react' +import { Menu, MenuItem, ProSidebar, SidebarContent,SidebarFooter } from 'react-pro-sidebar'; import { useLocation, useNavigate } from 'react-router-dom' -import { ProSidebar, Menu, MenuItem, SidebarFooter, SidebarContent } from 'react-pro-sidebar'; - -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArchive, faBars, faChartColumn, faGavel, faHome, faInfoCircle, faFileCode, faTable, faFileLines, faList } from '@fortawesome/free-solid-svg-icons'; import { ROUTES } from '../../constants/routes'; @@ -12,7 +11,7 @@ import './navbar.css'; const MENU_ITEMS = [ { - title: "Welcome page", + title: "Home", route: ROUTES.HOME, icon: faHome, }, @@ -37,7 +36,7 @@ const MENU_ITEMS = [ icon: faArchive, }, { - title: "License detections Explorer", + title: "License Detections Explorer", route: "/" + ROUTES.LICENSE_DETECTIONS, icon: faList, }, @@ -57,7 +56,7 @@ const MENU_ITEMS = [ icon: faFileCode, }, { - title: "About workbench", + title: "About Workbench", route: "/" + ROUTES.ABOUT, icon: faInfoCircle, }, diff --git a/src/components/NoDataSection.tsx b/src/components/NoDataSection.tsx new file mode 100644 index 00000000..665ea4bf --- /dev/null +++ b/src/components/NoDataSection.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +interface NoDataFallbackProps { + text: string; +} +const NoDataFallback = (props: NoDataFallbackProps) => { + return ( +
+

+ {props.text || "No Data"} +

+
+ ); +}; + +export default NoDataFallback; diff --git a/src/components/PackagesEntityDetails/PackageEntity.tsx b/src/components/PackagesEntityDetails/PackageEntity.tsx index 3c7fd02b..868b0f52 100644 --- a/src/components/PackagesEntityDetails/PackageEntity.tsx +++ b/src/components/PackagesEntityDetails/PackageEntity.tsx @@ -1,8 +1,6 @@ import ReactJson from '@microlink/react-json-view'; -import React, { useEffect } from 'react' -// import { useNavigate } from 'react-router-dom'; -// import { ROUTES } from '../../constants/routes'; -// import { useWorkbenchDB } from '../../contexts/workbenchContext'; +import React from 'react' +import { useWorkbenchDB } from '../../contexts/dbContext'; import { DependencyDetails, PackageDetails } from '../../pages/Packages/packageDefinitions' import '../../styles/entityCommonStyles.css'; @@ -14,18 +12,8 @@ interface PackageEntityProps { } const PackageEntity = (props: PackageEntityProps) => { const { goToDependency, package: activePackage} = props; - // const navigate = useNavigate(); - // const { updateCurrentPath } = useWorkbenchDB(); - - useEffect(() => { - console.log("Active package", activePackage); - }, [activePackage]) + // const { goToFileInTableView } = useWorkbenchDB(); - // function goToFile(path: string){ - // // updateCurrentPath(path, 'file'); // Not two-way yet - // navigate(ROUTES.TABLE_VIEW); - // } - if(!activePackage){ return (
@@ -35,6 +23,7 @@ const PackageEntity = (props: PackageEntityProps) => {
) } + return (
@@ -92,7 +81,7 @@ const PackageEntity = (props: PackageEntityProps) => { // goToFile(datafile_path)} + // onClick={() => goToFileInTableView(datafile_path)} // > { datafile_path } diff --git a/src/contexts/dbContext.tsx b/src/contexts/dbContext.tsx index 5992c846..950b7516 100644 --- a/src/contexts/dbContext.tsx +++ b/src/contexts/dbContext.tsx @@ -1,15 +1,11 @@ -import moment from "moment"; -import * as electronFs from "fs"; import electron from "electron"; -import { toast } from "react-toastify"; -import { useNavigate } from "react-router-dom"; +import * as electronFs from "fs"; +import moment from "moment"; import React, { createContext, useContext, useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { toast } from "react-toastify"; import packageJson from "../../package.json"; -import { DEFAULT_ROUTE_ON_IMPORT } from "../constants/routes"; -import { AddEntry, GetHistory, RemoveEntry } from "../services/historyStore"; -import { isSchemaChanged } from "../utils/checks"; -import { WorkbenchDB } from "../services/workbenchDB"; import { IMPORT_REPLY_CHANNEL, JSON_IMPORT_REPLY_FORMAT, @@ -23,6 +19,10 @@ import { SQLITE_SAVE_REPLY_FORMAT, UTIL_CHANNEL, } from "../constants/IpcConnection"; +import { DEFAULT_ROUTE_ON_IMPORT, ROUTES } from "../constants/routes"; +import { AddEntry, GetHistory, RemoveEntry } from "../services/historyStore"; +import { WorkbenchDB } from "../services/workbenchDB"; +import { isSchemaChanged } from "../utils/checks"; const { version: workbenchVersion } = packageJson; const { ipcRenderer } = electron; @@ -48,6 +48,7 @@ interface WorkbenchContextProperties extends BasicValueState { importJsonFile: (jsonFilePath: string) => void; updateLoadingStatus: React.Dispatch>; updateCurrentPath: (newPath: string, type: PathType) => void; + goToFileInTableView: (path: string) => void; updateWorkbenchDB: (db: WorkbenchDB, sqliteFilePath: string) => void; } @@ -65,6 +66,7 @@ export const defaultWorkbenchContextValue: WorkbenchContextProperties = { startImport: () => null, abortImport: () => null, updateCurrentPath: () => null, + goToFileInTableView: () => null, updateWorkbenchDB: () => null, }; @@ -90,6 +92,11 @@ export const WorkbenchDBProvider = ( setCurrentPath(path); setCurrentPathType(pathType); } + + function goToFileInTableView(path: string){ + updateCurrentPath(path, 'file'); + navigate("/" + ROUTES.TABLE_VIEW); + } const startImport = () => { updateLoadingStatus(0); @@ -455,6 +462,7 @@ export const WorkbenchDBProvider = ( startImport, abortImport, updateCurrentPath, + goToFileInTableView, updateWorkbenchDB, }} > diff --git a/src/contexts/stateContext.tsx b/src/contexts/stateContext.tsx index bde44318..21823718 100644 --- a/src/contexts/stateContext.tsx +++ b/src/contexts/stateContext.tsx @@ -1,8 +1,9 @@ import { ColDef, ColumnApi, ColumnState } from "ag-grid-community"; import React, { createContext, useContext, useEffect, useState } from "react"; + import { COL_DEF_STATE } from "../constants/keys"; -import { COLUMN_GROUPS } from "../pages/TableView/columnGroups"; import { ALL_COLUMNS_MAP } from "../pages/TableView/columnDefs"; +import { COLUMN_GROUPS } from "../pages/TableView/columnGroups"; interface CachedState { columnState: ColumnState[]; @@ -20,8 +21,8 @@ export const defaultWorkbenchStateContextValue: WorkbenchStateContextProperties { columnDefs: [], columnState: [], - updateColState: () => {}, - updateColDefs: () => {}, + updateColState: () => null, + updateColDefs: () => null, setColumnDefs: () => null, }; @@ -39,15 +40,6 @@ export const WorkbenchStateProvider = ( return colState.map((col) => { const colDef = ALL_COLUMNS_MAP.get(col.colId); colDef.width = col.width || colDef.initialWidth; - if (colDef.colId == "path") { - console.log( - "Set path width", - colDef.width, - "Options", - col.width, - colDef.initialWidth - ); - } return colDef; }); } @@ -62,7 +54,7 @@ export const WorkbenchStateProvider = ( ) => { const newColumnState = columnApi.getColumnState(); - console.log("Update state request ---"); + // console.log("Update state request ---"); // Need to perform this in all cases, for width to be updated const newColumnDefs = getColumnDefsFromState(newColumnState); @@ -77,7 +69,7 @@ export const WorkbenchStateProvider = ( function saveColumnStatustoLocalStorage(newColumnState: ColumnState[]) { if (!newColumnState || !newColumnState.length) return; - console.log("Save columnstatus to localstorage", newColumnState); + // console.log("Save columnstatus to localstorage", newColumnState); const newCachedState: CachedState = { columnState: newColumnState, }; @@ -90,7 +82,7 @@ export const WorkbenchStateProvider = ( localStorage.getItem(COL_DEF_STATE) ); if (newCachedState && Array.isArray(newCachedState?.columnState)) { - console.log("Revive saved state:", newCachedState); + // console.log("Revive saved state:", newCachedState); setColumnDefs(getColumnDefsFromState(newCachedState.columnState)); setColumnState(newCachedState.columnState); } @@ -103,10 +95,6 @@ export const WorkbenchStateProvider = ( reviveSavedColState(); }, []); - useEffect(() => { - console.log("Coldefs", columnDefs); - }, [columnDefs]); - return ( { const [searchParams] = useSearchParams(); - const [activeLicenseDetection, setActiveLicenseDetection] = useState(null); - const [licenseDetections, setLicenseDetections] = useState(null); + const [activeLicenseDetection, setActiveLicenseDetection] = + useState(null); + const [licenseDetections, setLicenseDetections] = useState< + LicenseDetectionDetails[] | null + >(null); const { db } = useWorkbenchDB(); useEffect(() => { - if(!licenseDetections || !licenseDetections.length) - return; - const queriedDetectionIdentifier = searchParams.get(QUERY_KEYS.LICENSE_DETECTION); - const foundDetection = licenseDetections.find(detection => detection.identifier == queriedDetectionIdentifier); - if(foundDetection) - setActiveLicenseDetection(foundDetection); + if (!licenseDetections || !licenseDetections.length) return; + const queriedDetectionIdentifier = searchParams.get( + QUERY_KEYS.LICENSE_DETECTION + ); + const foundDetection = licenseDetections.find( + (detection) => detection.identifier == queriedDetectionIdentifier + ); + if (foundDetection) setActiveLicenseDetection(foundDetection); }, [searchParams]); useEffect(() => { - db.sync - .then(async () => { - const newLicenseDetections = (await db.getAllLicenseDetections()).map(detection => ({ - detection_count: Number(detection.getDataValue('detection_count')), - identifier: detection.getDataValue('identifier').toString({}), - license_expression: detection.getDataValue('license_expression')?.toString({}), - detection_log: JSON.parse(detection.getDataValue('detection_log')?.toString({}) || "[]"), - matches: JSON.parse(detection.getDataValue('matches')?.toString({}) || "[]"), - file_regions: JSON.parse(detection.getDataValue('file_regions')?.toString({}) || "[]"), - })); - setLicenseDetections(newLicenseDetections); - - const queriedDetectionIdentifier = searchParams.get(QUERY_KEYS.LICENSE_DETECTION); - const foundDetection = newLicenseDetections.find(detection => detection.identifier == queriedDetectionIdentifier); - if(foundDetection) - setActiveLicenseDetection(foundDetection); - }); + db.sync.then(async () => { + const newLicenseDetections: LicenseDetectionDetails[] = ( + await db.getAllLicenseDetections() + ).map((detection) => ({ + detection_count: Number(detection.getDataValue("detection_count")), + identifier: detection.getDataValue("identifier").toString({}), + license_expression: detection + .getDataValue("license_expression") + ?.toString({}), + detection_log: JSON.parse( + detection.getDataValue("detection_log")?.toString({}) || "[]" + ), + matches: JSON.parse( + detection.getDataValue("matches")?.toString({}) || "[]" + ), + file_regions: JSON.parse( + detection.getDataValue("file_regions")?.toString({}) || "[]" + ), + })); + setLicenseDetections(newLicenseDetections); + + const queriedDetectionIdentifier: string | null = searchParams.get( + QUERY_KEYS.LICENSE_DETECTION + ); + const queriedDetection: LicenseDetectionDetails | null = + queriedDetectionIdentifier + ? newLicenseDetections.find( + (detection) => detection.identifier == queriedDetectionIdentifier + ) + : null; + + if (queriedDetectionIdentifier && queriedDetection) { + console.log( + `Activate queried detection (${queriedDetectionIdentifier}): `, + queriedDetection + ); + setActiveLicenseDetection(queriedDetection); + } else { + setActiveLicenseDetection(newLicenseDetections[0]); + } + }); }, []); - if(!licenseDetections){ + if (!licenseDetections) { return ( - { ); } - if(!licenseDetections.length) - return
No license detections :/
; - + if (!licenseDetections.length) + return + return ( -
-

- License Detections -

+
+

License Detections

- + - { - licenseDetections.map(licenseDetection => { - const isLicenseDetectionActive = licenseDetection === activeLicenseDetection; + {licenseDetections.map((licenseDetection) => { + const isLicenseDetectionActive = + licenseDetection === activeLicenseDetection; return ( setActiveLicenseDetection(licenseDetection)} key={licenseDetection.identifier} - className='license-detection-group-item' + className="license-detection-group-item" + > +
-
-
- { licenseDetection.license_expression } +
+ {licenseDetection.license_expression} {/* { isPackageActive && @@ -95,32 +129,27 @@ const LicenseDetections = () => { } */}
-
- - { licenseDetection.detection_count } +
+ + {licenseDetection.detection_count}
- ) - }) - } + ); + })} - - { - - } + {}
- ) -} + ); +}; -export default LicenseDetections \ No newline at end of file +export default LicenseDetections; diff --git a/src/pages/Packages/Packages.tsx b/src/pages/Packages/Packages.tsx index 43b6a44c..ed19b740 100644 --- a/src/pages/Packages/Packages.tsx +++ b/src/pages/Packages/Packages.tsx @@ -1,21 +1,21 @@ // eslint-disable-next-line import/namespace import { Allotment } from 'allotment'; -import { ThreeDots } from 'react-loader-spinner'; import React, { useEffect, useState } from 'react'; import { Badge, Collapse, ListGroup, ListGroupItem } from 'react-bootstrap'; -// import { PackageURL } from 'packageurl-js'; - -import { useWorkbenchDB } from '../../contexts/dbContext'; -import { DEPENDENCY_SCOPES } from '../../services/models/dependencies'; -import DependencyEntity from '../../components/PackagesEntityDetails/DependencyEntity'; -import { PackageDetails, DependencyDetails, PackageTypeGroupDetails } from './packageDefinitions'; +import { ThreeDots } from 'react-loader-spinner'; +import { useSearchParams } from 'react-router-dom'; import RightArrowIcon from "../../assets/icons/rightArrow.svg"; +import NoDataFallback from '../../components/NoDataSection'; +import DependencyEntity from '../../components/PackagesEntityDetails/DependencyEntity'; import PackageEntity from '../../components/PackagesEntityDetails/PackageEntity'; +import { QUERY_KEYS } from '../../constants/params'; +// import { PackageURL } from 'packageurl-js'; +import { useWorkbenchDB } from '../../contexts/dbContext'; +import { DEPENDENCY_SCOPES } from '../../services/models/dependencies'; +import { DependencyDetails, PackageDetails, PackageTypeGroupDetails } from './packageDefinitions'; import './packages.css'; -import { useSearchParams } from 'react-router-dom'; -import { QUERY_KEYS } from '../../constants/params'; const Packages = () => { @@ -69,7 +69,12 @@ const Packages = () => { .then(async () => { const packages = await db.getAllPackages(); const deps = await db.getAllDependencies(); - // console.log("Raw Packages", packages); + console.log("Raw Packages & deps", packages, deps); + if(!packages.length && !deps.length){ + console.log("No package or deps available"); + setPackageGroups([]); + return; + } // const type_other = 'type-other'; const packageMapping = new Map( @@ -117,8 +122,8 @@ const Packages = () => { } ] )); - const OTHERS = 'others'; - const OTHERS_PACKAGE: PackageDetails = { + const MISC_DEPS = 'others'; + const MISC_PACKAGE: PackageDetails = { package_uid: 'misc', name: 'Misc dependencies', type: 'Other', @@ -158,12 +163,12 @@ const Packages = () => { datasource_ids: [], purl: null, }; - packageMapping.set(OTHERS, OTHERS_PACKAGE); + packageMapping.set(MISC_DEPS, MISC_PACKAGE); // Group dependencies in their respective packages deps.forEach(dependencyInfo => { const targetPackageUid: string | null = dependencyInfo.getDataValue('for_package_uid')?.toString({}); - packageMapping.get(targetPackageUid || OTHERS).dependencies.push({ + packageMapping.get(targetPackageUid || MISC_DEPS).dependencies.push({ // ...dependencyInfo, // For debugging purl: dependencyInfo.getDataValue('purl').toString({}), extracted_requirement: dependencyInfo.getDataValue('extracted_requirement')?.toString({}) || "", @@ -178,6 +183,12 @@ const Packages = () => { datasource_id: dependencyInfo.getDataValue('datasource_id').toString({}), }) }); + + // Ignore misc package if no misc dependencies found + if(!MISC_PACKAGE.dependencies.length){ + packageMapping.delete(MISC_DEPS); + } + const parsedPackageWithDeps = Array.from(packageMapping.values()); // @TODO - What are qualifiers ? parsedPackageWithDeps.forEach(pkg => { @@ -251,7 +262,7 @@ const Packages = () => { } if(!packageGroups.length) - return
No packages :/
; + return return (
diff --git a/src/pages/TableView/AgDataTable.tsx b/src/pages/TableView/AgDataTable.tsx index 57cde80c..64348c72 100644 --- a/src/pages/TableView/AgDataTable.tsx +++ b/src/pages/TableView/AgDataTable.tsx @@ -62,7 +62,6 @@ const AgDataTable = (props: AgDataTableProps) => { rowData={tableData} components={frameworkComponents} className="ag-theme-alpine ag-grid-customClass" - ensureDomOrder enableCellTextSelection diff --git a/src/pages/TableView/CustomCellRenderers/LicenseDetections/MatchLicenseExpressionRenderer.tsx b/src/pages/TableView/CustomCellRenderers/LicenseDetections/MatchLicenseExpressionRenderer.tsx index cfaceef6..c8d49f12 100644 --- a/src/pages/TableView/CustomCellRenderers/LicenseDetections/MatchLicenseExpressionRenderer.tsx +++ b/src/pages/TableView/CustomCellRenderers/LicenseDetections/MatchLicenseExpressionRenderer.tsx @@ -49,18 +49,18 @@ const MatchLicenseExpressionRenderer = ( license_expression_spdx_keys.map((expKey) => [expKey.key, expKey]) ); - console.log( - "Keys mapper", - licenseExpressionKeysMap, - licenseExpressionSpdxKeysMap - ); + // console.log( + // "Keys mapper", + // licenseExpressionKeysMap, + // licenseExpressionSpdxKeysMap + // ); let newParsedComponents: ParsedTokens[]; if (spdxLicense) { - console.log( - "SPDX Tokens", - parseTokensFromExpression(license_expression_spdx) - ); + // console.log( + // "SPDX Tokens", + // parseTokensFromExpression(license_expression_spdx) + // ); newParsedComponents = parseTokensFromExpression( license_expression_spdx @@ -75,7 +75,7 @@ const MatchLicenseExpressionRenderer = ( return { value: token }; }); } else { - console.log("Tokens", parseTokensFromExpression(license_expression)); + // console.log("Tokens", parseTokensFromExpression(license_expression)); newParsedComponents = parseTokensFromExpression(license_expression).map( (token) => { @@ -90,7 +90,7 @@ const MatchLicenseExpressionRenderer = ( } ); } - console.log("Parsed components", newParsedComponents); + // console.log("Parsed components", newParsedComponents); return newParsedComponents; }, [data]); diff --git a/src/pages/TableView/CustomCellRenderers/TickRenderer.tsx b/src/pages/TableView/CustomCellRenderers/TickRenderer.tsx new file mode 100644 index 00000000..5a5c09db --- /dev/null +++ b/src/pages/TableView/CustomCellRenderers/TickRenderer.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { faCheck, faX } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +interface UrlRendererProps { + value: boolean; + data: unknown; +} + +const TickRenderer = (props: UrlRendererProps) => { + const { value } = props; + + const icon = value ? faCheck : faX; + const color = value ? "#03ba00" : "#ff2b2b"; + + return ( + <> + + + + + ); +}; + +export default TickRenderer; diff --git a/src/pages/TableView/CustomCellRenderers/index.tsx b/src/pages/TableView/CustomCellRenderers/index.tsx index 63db44ef..2b03f5e0 100644 --- a/src/pages/TableView/CustomCellRenderers/index.tsx +++ b/src/pages/TableView/CustomCellRenderers/index.tsx @@ -3,9 +3,11 @@ import MatchLicenseExpressionRenderer from './LicenseDetections/MatchLicenseExpr import ListCellRenderer from './ListCellRenderer'; import UrlListCellRenderer from './UrlListCellRenderer'; import UrlRenderer from './UrlRenderer'; +import TickRenderer from './TickRenderer'; export { UrlRenderer, + TickRenderer, ListCellRenderer, UrlListCellRenderer, MatchLicenseExpressionRenderer, diff --git a/src/pages/TableView/CustomFilterComponent.tsx b/src/pages/TableView/CustomFilterComponent.tsx index e94c0032..042687b7 100644 --- a/src/pages/TableView/CustomFilterComponent.tsx +++ b/src/pages/TableView/CustomFilterComponent.tsx @@ -11,16 +11,24 @@ export interface CustomParams extends IFloatingFilterParams { color: string; } +const FILTER_MAX_OPTION_LENGTH = 75; + +function trimString(str: string){ + if (str.length > FILTER_MAX_OPTION_LENGTH) { + return str.trimEnd().slice(0, FILTER_MAX_OPTION_LENGTH) + '...'; + } + return str.trimEnd(); +} function parseProbableStringifiedArray(str: string){ try { const result = JSON.parse(str); if(Array.isArray(result)){ const parseableResultArray = result as string[][]; - return parseableResultArray.map(subEntry => subEntry.join(',')).join(','); + return trimString(parseableResultArray.map(subEntry => subEntry.join(',')).join(',')); } - return str; + return trimString(str); } catch (e) { - return str; + return trimString(str); } } @@ -49,6 +57,8 @@ const CustomFilterComponent = forwardRef((props: CustomParams, ref) => { }); function selectionChanged(value: string){ + // console.log("Option changed to", value); + if (value === optionValues[0]) { parentFilterInstance(instance => { instance.onFloatingFilterChanged('equals', null); @@ -60,6 +70,11 @@ const CustomFilterComponent = forwardRef((props: CustomParams, ref) => { }); } + // useEffect(() => { + // console.log("Opt vals", optionValues); + + // }, [optionValues]); + return (