import { useCollection } from "@awsui/collection-hooks"; import { Box, Button, ButtonDropdown, Modal, Pagination, SpaceBetween, Table, TableProps, TextFilter } from "@awsui/components-react"; import StatusIndicator from "@awsui/components-react/status-indicator"; import React, { useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useHistory, useLocation } from "react-router-dom"; import { v4 as uuid } from "uuid"; import { externalUrls } from "../../constants/externalUrls"; import { usePortingAssistantSelector } from "../../createReduxStore"; import { PreTriggerData, SolutionToSolutionDetails } from "../../models/project"; import { MetricSource, MetricType, ReactMetric } from "../../models/reactmetric"; import { analyzeSolution, cancelAssessment,exportSolution, openSolutionInIDE, removeSolution } from "../../store/actions/backend"; import { pushCurrentMessageUpdate, removeCurrentMessageUpdate } from "../../store/actions/error"; import { removePortedSolution } from "../../store/actions/porting"; import { RootState } from "../../store/reducers"; import { selectCurrentSolutionDetails, selectCurrentSolutionStatus, selectSolutionToSolutionDetails, selectSolutioToStatus} from "../../store/selectors/solutionSelectors"; import { getErrorCounts, selectDashboardTableData, selectSolutionToApiAnalysis } from "../../store/selectors/tableSelectors"; import { checkInternetAccess } from "../../utils/checkInternetAccess"; import { filteringCountText } from "../../utils/FilteringCountText"; import { getCompatibleApi } from "../../utils/getCompatibleApi"; import { getErrorMetric } from "../../utils/getErrorMetric"; import { getHash } from "../../utils/getHash"; import { getTargetFramework } from "../../utils/getTargetFramework"; import { isLoaded, isLoading, isLoadingWithData } from "../../utils/Loadable"; import { useNugetFlashbarMessages } from "../AssessShared/useNugetFlashbarMessages"; import { InfoLink } from "../InfoLink"; import { LinkComponent } from "../LinkComponent"; import { TableHeader } from "../TableHeader"; import styles from "./DashboardTable.module.scss"; import { useSolutionFlashbarMessage } from "./useSolutionFlashbarMessage"; export interface DashboardTableData { name: string; path: string; portedProjects?: number; totalProjects?: number; incompatiblePackages?: number; totalPackages?: number; incompatibleApis?: number; totalApis?: number; buildErrors?: number; portingActions?: number; failed?: boolean; } const DashboardTableInternal: React.FC = () => { const [selectedItems, setSelectedItems] = useState(Array()); const [showDeleteModal, setShowDeleteModal] = useState(false); const dispatch = useDispatch(); const history = useHistory(); const solutionToSolutionDetails = useSelector(selectSolutionToSolutionDetails); const solutionToSolutionStatus = useSelector(selectSolutioToStatus); const tableData = useSelector(selectDashboardTableData); const targetFramework = getTargetFramework(); useNugetFlashbarMessages(); useSolutionFlashbarMessage(tableData, solutionToSolutionStatus); const apiAnalysis = useSelector(selectSolutionToApiAnalysis); const deleteSolution = useMemo( () => (solutionPath: string) => { let clickMetric: ReactMetric = { SolutionPath: getHash(solutionPath), MetricSource: MetricSource.RemoveSolution, MetricType: MetricType.UIClickEvent } window.electron.writeReactLog(clickMetric); if (solutionToSolutionStatus[solutionPath]?.isAssessmentRunning){ cancelSolutionAssessment(solutionPath); } dispatch(removeSolution(solutionPath)); dispatch(removePortedSolution(solutionPath)); setSelectedItems([]); }, [dispatch] ); const cancelSolutionAssessment = useMemo( () => async (solutionPath: string) => { try { dispatch(cancelAssessment(solutionPath)); window.backend.cancelAssessment(solutionPath); dispatch( removeCurrentMessageUpdate({ groupId: "assess" }) ); let clickMetric: ReactMetric = { SolutionPath: getHash(solutionPath), MetricSource: MetricSource.CancelAssessment, MetricType: MetricType.UIClickEvent } window.electron.writeReactLog(clickMetric); } catch (err) { const errorMetric = getErrorMetric(err, MetricSource.CancelAssessment) window.electron.writeReactLog(errorMetric) throw err } },[dispatch] ) const reassessSolution = useMemo( () => async (solutionPath: string) => { try { dispatch( removeCurrentMessageUpdate({ groupId: "assessSuccess" }) ); const runId = uuid(); let clickMetric: ReactMetric = { SolutionPath: getHash(solutionPath), RunId: runId, MetricSource: MetricSource.ReassessSolution, MetricType: MetricType.UIClickEvent } window.electron.writeReactLog(clickMetric); var projectDictionary: {[project: string]: PreTriggerData} = {} const solutionDetails = solutionToSolutionDetails[solutionPath]; const projectToApiAnalysis = apiAnalysis[solutionPath]; if (isLoaded(solutionDetails)) { solutionDetails.data.projects.forEach((project) => { if (project.projectName != null) { const apis = getCompatibleApi( solutionDetails, projectToApiAnalysis, project.projectFilePath, null, targetFramework ); projectDictionary[project.projectName] = { projectName: project.projectName, projectPath: project.projectFilePath || "-", solutionPath: solutionPath || "-", targetFramework: project.targetFrameworks?.join(", ") || "-", incompatibleApis: apis.isApisLoading ? null : apis.values[1] - apis.values[0], totalApis: apis.values[1], buildErrors: getErrorCounts(projectToApiAnalysis, project.projectFilePath, null), ported: false } } }) } const haveInternet = await checkInternetAccess(solutionPath, dispatch); if (haveInternet) { dispatch( analyzeSolution.request({ solutionPath: solutionPath, runId: runId, triggerType: "UserRequest", settings: { ignoredProjects: [], targetFramework: targetFramework, continiousEnabled: false, actionsOnly: false, compatibleOnly: false }, preTriggerData: projectDictionary, force: true }) ); } } catch (err) { const errorMetric = getErrorMetric(err, MetricSource.ReassessSolution) window.electron.writeReactLog(errorMetric) throw err } }, [apiAnalysis, dispatch, solutionToSolutionDetails, targetFramework] ); const header = useMemo( () => (

Select a solution to view the solution details, including an assessment overview. You can also port your projects when you have attained your desired compatibility for the projects. To reassess a solution, choose Reassess solution. To assess a new solution choose the Assess a new solution button.

Build errors

The total number of build errors for solutions.

Porting actions

The total number of porting actions for solutions.

} learnMoreLinks={[ { externalUrl: externalUrls.howItWorks, text: "How Porting Assistant for .NET works" } ]} /> } description="Porting Assistant for .NET has successfully assessed the following solutions for .NET Core compatibility. Improve the compatibility of your solutions by refactoring the code in your IDE." totalItems={Object.values(solutionToSolutionDetails).map(solutionDetail => isLoaded(solutionDetail) || isLoadingWithData(solutionDetail) ? solutionDetail.data : undefined )} selectedItems={selectedItems} actionButtons={ { switch (event.detail.id) { case "download": dispatch(exportSolution({ solutionPath: selectedItems[0].path })); break; case "delete": setShowDeleteModal(true); break; case "view": dispatch(openSolutionInIDE(selectedItems[0].path)); } }} > Actions } onDismiss={() => setShowDeleteModal(false)} > The assessment report for your solution will be removed from the Porting Assistant for .NET tool. Your source code will not be deleted. } /> ), [solutionToSolutionDetails, selectedItems, solutionToSolutionStatus, showDeleteModal, history, dispatch, reassessSolution, cancelSolutionAssessment, deleteSolution] ); const empty = useMemo( () => ( No solutions No solutions to display. ), [history] ); const { items, filteredItemsCount, collectionProps, filterProps, paginationProps } = useCollection(tableData, { filtering: { empty: empty, fields: ["solutionName"] }, pagination: {}, sorting: {} }); return ( {...collectionProps} id="dashboard-table" loadingText="Loading solutions" columnDefinitions={columnDefinitions} loading={tableData == null} items={items} selectionType="single" trackBy="path" selectedItems={selectedItems} onSelectionChange={event => setSelectedItems(event.detail.selectedItems)} filter={ } pagination={} header={header} empty={empty} > ); }; const escapeNonAlphaNumeric = (solutionPath: string) => { return solutionPath?.replace(/[^0-9a-zA-Z]/gi, ""); }; const columnDefinitions: TableProps.ColumnDefinition[] = [ { id: "name", header: "Name", cell: item => (item.incompatiblePackages == null && item.incompatibleApis == null) || (item.totalApis === 0 && item.totalPackages === 0 && item.totalProjects === 0) ? ( ) : ( {item.name} ), sortingField: "name" }, { id: "ported-projects", header: "Ported projects", cell: item => (
{item.portedProjects == null ? inProgress() : `${item.portedProjects} of ${item.totalProjects}`}
), sortingField: "portedProjects" }, { id: "incompatible-packages", header: "Incompatible packages", cell: item => (
{item.incompatiblePackages == null ? inProgress() : `${item.incompatiblePackages} of ${item.totalPackages}`}
), sortingField: "incompatiblePackages" }, { id: "incompatible-apis", header: "Incompatible APIs", cell: item => (
{item.incompatibleApis == null ? inProgress() : `${item.incompatibleApis} of ${item.totalApis}`}
), sortingField: "incompatibleApis" }, { id: "build-error", header: "Build errors", cell: item => (
{item.failed === true ? ( Build failed ) : item.buildErrors == null ? ( inProgress() ) : ( item.buildErrors )}
), sortingField: "buildErrors" }, { id: "porting-action", header: "Porting actions", cell: item => (
{item.portingActions == null ? inProgress() : item.portingActions}
), sortingField: "portingActions" } ]; const inProgress = () => In progress; export const DashboardTable = React.memo(DashboardTableInternal);