Untitled
unknown
typescript
a year ago
81 kB
7
Indexable
api comfig RUN_TRANSFORM: `${mainURL}api/builder/run-Transform/`, analyze builder ) : item?.entryPoint && item?.packageName === "Analyze" && (item.packageRunStatus = "Completed") ? ( <li key={index} className="relative mr-px flex cursor-pointer items-center gap-2 rounded-lg px-2 py-1.5 transition-colors" > <div className="pb-px text-xs font-bold"> {item.entryPoint} </div> <div className="pb-px text-tiny text-cool-500"> {item.description} </div> <span className="ml-auto"> <span className="text-tiny text-cool-500 mr-5"> {item.name} </span> </span> </li> transform builder drawer @ -0,0 +1,346 @@ // @ts-nocheck import { Transform, Checkmark, Cross, Crossdrawer, Search, } from "@/components/icons"; import React, { Dispatch, FC, ReactNode, SetStateAction, useEffect, useState, } from "react"; import formatData, { EntryPoints } from "./helper/formatEntryPoints"; import axiosService from "@/api/axios.service"; import { ENDPOINTS } from "@/api_config"; import { useQuery } from "@tanstack/react-query"; import { TProjectDetails } from "../../dashboard/page"; import { cn, localStorageService } from "@/lib"; import { errorMessage, successMessage } from "@/components"; export interface IStartTransformResponse { code: number; hasError: boolean; messages: [""]; data: null; dataList: null; } interface IBuilderDrawer { showTransformBuildModal: boolean; setShowTransformBuildModal: Dispatch<SetStateAction<boolean>>; setProjectTransformStarted: Dispatch<SetStateAction<boolean>>; setShowBuildTransformDrawer: Dispatch<SetStateAction<boolean>>; } interface IAdvanceSettings { rebuild: boolean; edgeLimiter: number | null; cfMaxLimit: number | null; debugFlag: boolean; } const BuilderDrawer: FC<IBuilderDrawer> = ({ showTransformBuildModal, setShowTransformBuildModal, setProjectTransformStarted, }) => { const projectId = localStorageService.get("Averi_last_project_id"); const [searchTerm, setSearchTerm] = useState(""); const [selectedEntryPoints, setSelectedEntryPoints] = useState<EntryPoints[]>( [] ); const [advancedSettings, setAdvancedSettings] = useState<IAdvanceSettings>({ rebuild: false, edgeLimiter: 0, cfMaxLimit: 0, debugFlag: false, }); const { data: appOverviewData = [], isLoading } = useQuery({ queryKey: ["app-overview", { projectId }], queryFn: async () => { return await axiosService.get<EntryPoints[]>(ENDPOINTS.APP_OVERVIEW); }, }); const result = appOverviewData.length && formatData(appOverviewData); const filteredData = result && Object.entries(result).reduce((obj, [key, value]) => { const filteredChildNames = value.filter( (item: { name: string; entryPoint: string }) => item.name.toLowerCase().includes(searchTerm.toLowerCase()) || item.entryPoint.toLowerCase().includes(searchTerm.toLowerCase()) ); if (filteredChildNames.length > 0) { obj[key] = filteredChildNames; } return obj; }, {}); const GetAdvancedSettings = async (): Promise<void> => { const resp = await axiosService.get<TProjectDetails | void>( ENDPOINTS.GET_PROJECT_DETAILS + `${projectId}` ); if (resp?.code === 200) { setAdvancedSettings({ ...advancedSettings, cfMaxLimit: resp.data.controlFlowMaxLimit!, edgeLimiter: resp.data.edgeLimiter!, debugFlag: resp.data.debugFlag!, }); } }; useEffect(() => { GetAdvancedSettings(); }, []); const addSelectedEntryPoint = (selectedItem: EntryPoints): void => { // Check if the item is already selected const isSelected = selectedEntryPoints.some( (item) => item.id === selectedItem.id ); if (isSelected) { // If selected, remove it from the array const updatedList = selectedEntryPoints?.filter( (item) => item.id !== selectedItem.id ); setSelectedEntryPoints(updatedList); } else { // If not selected, add it to the array setSelectedEntryPoints((prevList) => [...prevList, selectedItem]); } }; const removeSelectedEntryPoint = (selectedItem: EntryPoints): void => { const updatedList = selectedEntryPoints?.filter( (item) => item.id !== selectedItem.id ); setSelectedEntryPoints(updatedList); }; const selectAllEntryPoints = (): void => { // Use Object.values to get an array of the nested arrays const nestedArrays = Object.values(filteredData); // Use flatMap to flatten the nested arrays and get an array of objects const allObjects = nestedArrays.flatMap((nestedArray) => nestedArray); // Set the array of objects using the provided setter function setSelectedEntryPoints(allObjects); }; const selectedItems: ReactNode = selectedEntryPoints.map((item) => { return ( <button key={item?.id} type="button" onClick={(): void => { removeSelectedEntryPoint(item); }} className="flex h-5 items-center justify-center gap-0.5 rounded-full bg-teal-50 px-2 text-teal-500 ring-1 ring-inset ring-teal-100 transition-colors hover:bg-teal-100 hover:text-teal-600" > <span className="pb-px text-xs text-cool-800">{item?.entryPoint}</span> <Crossdrawer className="-mr-0.5 h-4 w-4" /> </button> ); }); // API that triggers transform and returns if the transform started successfully or not const startProjectTransform = async (): Promise<void> => { if (selectedEntryPoints.length) { return await axiosService .post<IStartTransformResponse>( `${ENDPOINTS.RUN_TRANSFORM}${projectId}`, selectedEntryPoints.map((item) => item?.catalogFileId) ) .then((resp: IStartTransformResponse) => { setProjectTransformStarted(true); setShowTransformBuildModal(true); successMessage(resp.messages[0]); }) .catch((e: IStartTransformResponse) => { setShowTransformBuildModal(false); errorMessage(e?.messages[0]); }); } }; return ( <div className="absolute bottom-0 right-0 top-0 flex w-full flex-col overflow-hidden rounded-l-20 bg-white shadow-popover transition-all group-aria-expanded/drawer:w-screen group-aria-expanded/drawer:rounded-l-none group-aria-hidden/drawer:translate-x-full group-aria-hidden/drawer:shadow-none"> <div className="border-b border-warm-150 px-6 py-4"> <div className="flex items-center gap-2"> <div className="text-m font-semibold">Build Transform</div> </div> </div> <div className="absolute bottom-0 left-0 right-0 top-[55px] overflow-auto p-10 pb-30"> <div className="mb-8 flex flex-col items-center justify-center gap-4"> <div className="relative flex h-15 w-15 items-center justify-center rounded-full bg-red-50 text-pink-500"> <Transform className="h-6 w-6" /> </div> <div className="font-serif text-h1"> Select Entry Programs to Transform </div> </div> <div id="user-selector"> <div className="relative"> <div className="scrollbar peer flex h-14 items-center gap-1 overflow-auto rounded-t-lg border border-warm-150 pb-2 pl-4 pr-14 pt-6"> {selectedItems} </div> <div className="absolute bottom-px right-px top-2 w-14 bg-gradient-to-r from-transparent via-white via-25% to-white"></div> <label className={cn( "pointer-events-none absolute left-4 top-0 flex h-14 origin-top-left items-center pb-px text-m text-cool-500 transition-transform peer-[.filled]:-translate-y-1 peer-[.filled]:scale-[0.6875] peer-[.filled]:font-semibold", selectedEntryPoints.length && "font-semibold scale-[0.6875] -translate-y-1" )} > Entry Programs </label> <div className="pointer-events-none absolute right-3.5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity peer-[.filled]:pointer-events-auto peer-[.filled]:opacity-100"> <button type="button" id="user-selector-clear" className="flex h-7 items-center justify-center gap-1 rounded-lg px-2 text-xs text-cool-800 transition-colors hover:bg-warm-100 active:bg-warm-200" > <Cross className="h-4 w-4" /> </button> </div> </div> <div className="-mt-px rounded-b-lg border border-warm-150 p-3"> <div className="relative w-full"> <input type="text" placeholder="Search" value={searchTerm} onChange={(e): void => setSearchTerm(e.target.value)} className="flex h-10 w-full items-center rounded-lg bg-white pb-px pl-10 pr-20 text-xs ring-1 ring-inset ring-warm-200 transition placeholder:text-cool-500 hover:ring-warm-400 focus:bg-white focus:shadow-field focus:ring-teal-500" /> <Search className="pointer-events-none absolute left-3 top-3 h-4 w-4 text-cool-500" /> <div className="absolute right-1.5 top-1.5"> <button type="button" onClick={(): void => selectAllEntryPoints()} className="flex h-7 items-center justify-center gap-1 rounded-lg px-2 text-xs text-teal-550 transition-colors hover:bg-teal-50 hover:text-teal-600 active:bg-teal-100 active:text-teal-600" > <span className="pb-px">Select All</span> </button> </div> </div> <div className="scrollbar relative -mb-2 mt-1 h-56 space-y-px overflow-auto"> {!isLoading && filteredData && !Object.entries(filteredData).length && searchTerm.length ? ( <div className="absolute inset-0 flex flex-col items-center justify-center gap-3 aria-hidden:hidden"> <div className="text-xs text-cool-600"> No matching entry programs were found </div> </div> ) : filteredData ? ( Object.entries(filteredData).map( ([parentNames, items]: [string, unknown]) => ( <div key={parentNames}> <h3 className="text-tiny text-cool-500 px-2 py-1.5 mr-px"> {parentNames} </h3> <ul> {items.map((item, index: number) => selectedEntryPoints.some( (entryPoint) => entryPoint.id === item.id ) ? ( <li key={index} onClick={(): void => addSelectedEntryPoint(item)} className="relative mr-px flex cursor-pointer items-center gap-2 rounded-lg px-2 py-1.5 transition-colors hover:bg-warm-50 bg-teal-50" > <div className="pb-px text-xs font-bold"> {item.entryPoint} </div> <div className="pb-px text-tiny text-cool-500"> {item.description} </div> <span className="ml-auto"> <span className="text-tiny text-cool-500 mr-5"> {item.name} </span> <Checkmark className="inline h-4 w-4 shrink-0 text-teal-500" /> </span> </li> ) : item?.entryPoint && item?.packageName === "Transform" && (item.packageRunStatus = "Completed") ? ( <li key={index} className="relative mr-px flex cursor-pointer items-center gap-2 rounded-lg px-2 py-1.5 transition-colors" > <div className="pb-px text-xs font-bold"> {item.entryPoint} </div> <div className="pb-px text-tiny text-cool-500"> {item.description} </div> <span className="ml-auto"> <span className="text-tiny text-cool-500 mr-5"> {item.name} </span> </span> </li> ) : ( <li key={index} onClick={(): void => addSelectedEntryPoint(item)} className="relative mr-px flex cursor-pointer items-center gap-2 rounded-lg px-2 py-1.5 transition-colors hover:bg-warm-50" > <div className="pb-px text-xs font-bold"> {item.entryPoint} </div> <div className="pb-px text-tiny text-cool-500"> {item.description} </div> <span className="ml-auto"> <span className="text-tiny text-cool-500 mr-5"> {item.name} </span> </span> </li> ) )} </ul> </div> ) ) ) : ( <></> )} </div> </div> </div> <div className="mt-1 text-tiny text-cool-500"> Only analyzed entry programs are available for Transform, if you’re not seeing an entry program please ensure that it was analyzed. </div> </div> <div className="absolute bottom-0 left-10 right-10 bg-white pb-10 after:pointer-events-none after:absolute after:bottom-full after:left-0 after:right-0 after:h-10 after:bg-gradient-to-b after:from-white/0 after:to-white after:content-['']"> <button onClick={(): void => { selectedEntryPoints.length && startProjectTransform(); }} className={cn( "relative flex h-10 w-full items-center justify-center gap-1.5 rounded-lg bg-warm-300 px-4 text-white shadow-field transition-colors hover:bg-teal-600", selectedEntryPoints.length && "bg-teal-700" )} > <span className="pb-px">Transform Entry Programs</span> </button> </div> </div> ); }; export default BuilderDrawer; format entry points ts for transform @ -0,0 +1,119 @@ /* eslint-disable no-unused-vars */ // @ts-nocheck export interface EntryPoints { id: string; businessAlignmentDocumentID: number; name: string; description: BadName; entryPoint: string; level: number; parent: null | string; expanded: boolean; loaded: boolean; isLeaf: boolean; isChecked: boolean; catalogFileId: number; entryPointID: number; fit: null; cost: null; complexity: null; risk: null; notes: null; priority: null; type: null; badName: BadName; businessActivityId: number; businessRequirementId: number; businessFunctionId: number; businessActivityName: null | string; businessActivityDescription: BadName | null; isDeleted: boolean; adlGenerated: boolean; fumlGenerated: boolean; excelGenerated: boolean; businessRequirementName: null | string; requirementDesc: BadName | null; businessFunctionName: null | string; functionDesc: BadName | null; docDescription: BadName | null; translationData: boolean; language: null; fileType: null | string; fileSubType: null | string; uploadedBy: UploadedBy; uploadedAt: Date; modelLastEditedBy: null; modelLastEditedOn: null; packageName: PackageName | null; packageRunStatus: PackageRunStatus | null; userData: null; commentsCount: number; } export enum BadName { AutoBPD = "AutoBPD", BusinessActivity = "Business Activity", BusinessFunction = "Business Function", BusinessRequirement = "Business Requirement", InitiatorUnknown = "Initiator Unknown", } export enum PackageName { Discover = "Discover", } export enum PackageRunStatus { Completed = "Completed", } export enum UploadedBy { NA = "n/a", } function formatData(data: EntryPoints[]): void { const result = {}; // Mapping IDs to their respective objects for quicker access const idToData = {}; data?.forEach((item) => { idToData[item.id] = item; }); // Iterate through each item in the data data?.forEach((item) => { const currentName = item.name; let parent = item.parent; if (parent === null) { return; } const hierarchy = [currentName]; while (parent !== null && hierarchy.length < 4) { // Max depth of 4 const parentData = idToData[parent]; if (parentData) { hierarchy.unshift(parentData.name); parent = parentData.parent; } else { break; } } // Only include items that have exactly 4 levels of nesting if (hierarchy.length === 4) { // Build the key in the required format const key = hierarchy.slice(0, 3).join(" > "); // Create the structure if it doesn't exist result[key] = result[key] || []; // Add the current item to the corresponding key result[key].push(item); } }); return result; } export default formatData; TRANSFORM PAGE /* eslint-disable @typescript-eslint/no-explicit-any */ "use client"; // top level imports import React, { FC, useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import Link from "next/link"; // components import { Button, Card, DoughnutChart, Modal, TDoughnutChart, errorMessage, successMessage, } from "@/components"; import { Background, QuestionCircle, ArrowRightWithTail, FileSearch, Transform, Automation, Crossdrawer, ExpandBtn, } from "@/components/icons"; import { BounceLoader } from "react-spinners"; // API utils import { ENDPOINTS } from "@/api_config"; import axiosService from "@/api/axios.service"; import PackageCommentViewer from "@/components/package_comment_viewer/PackageCommentViewer"; import ReadyToBuildTransformDialog from "@/components/dialog/ready-to-build/ReadyToBuildTransformDialog"; import { LicenseDetailsType } from "@/mockdata/mocks"; import { IScanDashboardResponse } from "../../(scan)/scan/page"; import { localStorageService } from "@/lib"; import Drawer from "@/components/drawer/Drawer"; import BuilderDrawer from "./BuilderDrawer"; import { IProjectStatusResponse } from "../../dashboard/page"; import EmptySummary from "@/components/empty_summary/EmptySummary"; import LoadingProgressBar from "@/components/loader/LoadingProgressBar"; import HelpDrawer, { IHelpDrawerData, } from "@/components/help-drawer-modal/page"; interface ITransformSummaryResponse { code: number; hasError: boolean; messages: string; data: ITransformSummary; } interface ITransformSummary { entitiesCount: number; screensCount: number; modelsGeneratedCount: number; transformedFilesCount: number; totalEntryPointCount: number; entryPointCount: number; totalProgramCount: number; programCount: number; } export interface ITransformStatusResponse { code: number; hasError: boolean; messages: [""]; data: Data; dataList: null; } export interface IStartTransformResponse { code: number; hasError: boolean; messages: [""]; data: null; dataList: null; } export interface Data { packageStatusID: number; projectId: number; lkPackageID: number; startTime: Date; endTime: null; lkPackage: null; project: null; } export interface ITransformStatusFailResponse { code: number; hasError: boolean; messages: [""]; data: null; dataList: null; } export interface ITransformLogResponse { code: number; hasError: boolean; messages: [""]; data: [""]; } interface IOpenDrawer { setOpenLog: React.Dispatch<React.SetStateAction<boolean>>; transformCompleted: boolean; } interface ILogData { logData: [""] | undefined; transformCompleted: boolean; setcloseDrawer: (val: boolean) => void; } const SummaryCard: FC<ITransformSummary> = ({ entitiesCount, screensCount, modelsGeneratedCount, transformedFilesCount, }) => ( <Card customClass="mt-4 grid grid-cols-4 py-6 text-center"> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Entities</div> <div className="mt-1 font-serif text-h1">{entitiesCount}</div> </div> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Screens</div> <div className="mt-1 font-serif text-h1">{screensCount}</div> </div> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Models Generated</div> <div className="mt-1 font-serif text-h1">{modelsGeneratedCount}</div> </div> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Transformed Files</div> <div className="mt-1 font-serif text-h1">{transformedFilesCount}</div> </div> </Card> ); const ActionItemsCard: FC = () => { const router = useRouter(); return ( <Card customClass="mt-4 space-y-4 p-6"> <div className="flex items-center gap-4"> <div className="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-l from-orange-300 to-red-400 text-white"> <Background className="absolute -left-1 -top-1 opacity-20" /> <FileSearch className="relative h-6 w-6 stroke-1333" /> </div> <div className="grow"> <h4 className="font-semibold">Review Entity Diagram</h4> <p className="mt-1.5 text-xs text-cool-550"> Scan has provided helpful statistics to better understand your environment, allowing you to determine a more clear path forward for modernization. </p> </div> <div className="shrink-0"> <Button endIcon={ <ArrowRightWithTail className="h-4 w-4 shrink-0 first:last:-mx-1" /> } onClick={(): void => router.push("/entities/")} > Review </Button> </div> </div> <hr className="border-cool-100" /> <div className="flex items-center gap-4"> <div className="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-l from-orange-300 to-red-400 text-white"> <Background className="absolute -left-1 -top-1 opacity-20" /> <Automation className="relative h-6 w-6 stroke-1333" /> </div> <div className="grow"> <h4 className="font-semibold">Generate Models</h4> <p className="mt-1.5 text-xs text-cool-550"> Your source code has been evaluated for 9 proven modernization risk factors. Review these to learn where you may face the most complexity and difficulty. </p> </div> <div className="shrink-0"> <Button endIcon={ <ArrowRightWithTail className="h-4 w-4 shrink-0 first:last:-mx-1" /> } onClick={(): void => router.push("/modeling/")} > Generate </Button> </div> </div> <hr className="border-cool-100" /> <div className="flex items-center gap-4"> <div className="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-l from-orange-300 to-red-400 text-white"> <Background className="absolute -left-1 -top-1 opacity-20" /> <Transform className="relative h-6 w-6 stroke-1333" /> </div> <div className="grow"> <h4 className="font-semibold">Transform Code</h4> <p className="mt-1.5 text-xs text-cool-550"> Description of action item, setting context and why it’s important to the application modernization process. </p> </div> <div className="shrink-0"> <Button endIcon={ <ArrowRightWithTail className="h-4 w-4 shrink-0 first:last:-mx-1" /> } onClick={(): void => router.push("/modeling/")} > Transform </Button> </div> </div> </Card> ); }; const TransformModal: FC = () => { const staticInventoryData = [ { icon: <FileSearch className="h-6 w-6" />, title: "Review Entity Diagram", description: [ "Lorem ipsum dolor sit amet consectetur. Tincidunt augue malesuada phasellus cursus. Id mauris interdum elementum varius id at. Diam aliquet eget mi varius amet semper etiam tincidunt", ], }, { icon: <Automation className="h-6 w-6" />, title: "Generate Models", description: [ "Diam aliquet eget mi varius amet semper etiam tincidunt. Vitae dictum urna urna velit elementum sit risus. Aliquam in bibendum eget sodales.", ], }, { icon: <Transform className="h-6 w-6" />, title: "Examine dead code", description: [ "Lorem ipsum dolor sit amet consectetur. Tincidunt augue malesuada phasellus cursus. Id mauris interdum elementum varius id at. Diam aliquet eget mi varius amet semper etiam tincidunt", ], }, ]; return ( <div className="p-2 bg-white"> <div className="-mt-1 text-h3"> About Your Transform Suggested Actions </div> <div className="mt-5 space-y-5"> {staticInventoryData?.map((data) => { return ( <> <hr className="border-cool-100" /> <div className="flex items-center gap-4"> <div className="relative flex h-12 w-12 shrink-0 items-center justify-center self-start overflow-hidden rounded-lg bg-gradient-to-l from-orange-300 to-red-400 text-white"> {data.icon} </div> <div className="grow"> <h4 className="text-m font-semibold">{data.title}</h4> {data?.description?.map((descriptionLine, index) => ( <p key={index} className="mt-1.5 text-cool-600"> {descriptionLine} </p> ))} </div> </div> </> ); })} </div> <div className="mt-5 flex items-center justify-between gap-4 rounded-lg bg-warm-100 p-4"> <div className="pb-px text-xs font-semibold text-cool-500"> Still not sure what to do? Contact us so we can help at{" "} <a target="_blank" rel="noreferrer" href="mailto:support@averisource.com" > support@averisource.com </a> </div> {/* <div className="-my-1"> <button type="button" className="flex h-7 items-center justify-center gap-1 rounded-lg bg-warm-100 px-2 text-xs text-cool-800 shadow-field ring-1 ring-inset ring-warm-300 transition hover:ring-warm-500 active:text-teal-550 active:ring-teal-550" > <span className="pb-px">Contact Us</span> </button> </div> */} </div> </div> ); }; const TransformDashboard: FC<ITransformSummary> = ({ totalEntryPointCount, entryPointCount, totalProgramCount, programCount, ...rest }) => { const [show, toggleModal] = useState<boolean>(false); const [showHelp, toggleHelpDrawer] = useState<boolean>(false); const [helpDrawerData] = useState<IHelpDrawerData>({ heading: "Transform Help", text: 'AveriSource Transform generates modernized code from the legacy codebase. It uses AI techniques such as cluster analysis, pattern matching, templates, and programming language models to generate modernized code in a new target language. The generated code produced by the AveriSource Transform is independent and does not depend on any specific JAR files or other libraries owned by AveriSource ensuring there is no "vendor lock”. AveriSource Transform’s code generation functionality aims to provide flexibility and portability, allowing organizations to freely use and maintain the generated code without being dependent on AveriSource or its specific libraries.', }); const [entryPercentage] = useState<number>( (entryPointCount / totalEntryPointCount) * 100 ); const [programPercentage] = useState<number>( (programCount / totalProgramCount) * 100 ); const entitycolors: string[] = ["#CD5189", "#F3F2F0"]; const programColors: string[] = ["#7C4DD5", "#F3F2F0"]; const entryPointchartProps: TDoughnutChart = { type: "doughnut", data: { labels: ["Entry Points", ""], datasets: [ { data: [entryPercentage, 100 - entryPercentage], backgroundColor: entitycolors, hoverBackgroundColor: entitycolors, }, ], }, options: {}, }; const programschartProps: TDoughnutChart = { type: "doughnut", data: { labels: ["Programs", ""], datasets: [ { data: [programPercentage, 100 - programPercentage], backgroundColor: programColors, hoverBackgroundColor: programColors, }, ], }, options: {}, }; return ( <div className="mx-auto box-content max-w-[1140px] p-3"> <div className="flex items-center justify-between border-b border-warm-150 pb-3"> <h1 className="pb-0.5 font-serif text-h1">AveriSource Transform</h1> <div className="flex items-center gap-3"> <Button type="button" variant="secondary" className="h-8 bg-warm-100 px-3" onClick={(): void => { toggleHelpDrawer(true); }} > <QuestionCircle className="h-4 w-4" /> <span className="pb-px">Help</span> </Button> </div> <Modal show={show} toggle={(): void => toggleModal(false)} closable={true} filledIcon={false} > <TransformModal /> </Modal> <Drawer open={showHelp} toggleDrawer={toggleHelpDrawer}> <HelpDrawer drawerData={helpDrawerData} toggleHelpDrawer={toggleHelpDrawer} /> </Drawer> </div> <div> <div className="grid grid-cols-3 gap-8"> <div className="col-span-2"> <div className="mt-8"> <div className="flex items-center gap-2"> <h2 className="font-serif text-h2">Suggested Actions</h2> <div className="-mx-1.5 -my-0.5 ml-auto"> <Button variant="tertiary-teal" size="small" onClick={(): void => toggleModal(true)} > <span className="pb-px">Why are these necessary?</span> </Button> </div> </div> <ActionItemsCard /> </div> <div className="mt-8"> <h2 className="font-serif text-h2">Summary</h2> <SummaryCard {...rest} entryPointCount={entryPointCount} programCount={programCount} totalEntryPointCount={totalEntryPointCount} totalProgramCount={totalProgramCount} /> </div> </div> <div className="flex flex-col"> <div className="relative mt-8 grow"> <PackageCommentViewer packageLabel="Transform" /> </div> </div> </div> <div className="mt-8"> <h2 className="font-serif text-h2">{`Entry Points & Programs`}</h2> <div className="grid grid-cols-3 mt-4 gap-3"> <Card customClass="col-span-1 items-center p-6"> <DoughnutChart {...entryPointchartProps} InitialDataDisplay="Entry Points" > <div className="absolute left-1/2 top-1/2 flex h-36 w-36 -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center rounded-full bg-white"> <div className="text-tiny font-semibold">Entry Points</div> <div className="flex items-center gap-2"> <div className="font-serif text-wh2s font-semibold"> {entryPointCount} </div> <div className="mt-2 h-4 rotate-[13deg] border-l border-warm-200"></div> <div className="mt-2 text-cool-500"> {totalEntryPointCount} </div> </div> </div> </DoughnutChart> <hr className="border-warm-150"></hr> <div className="flex items-center justify-center py-3"> <Link href="/modeling?tab=entrypoints" className="flex h-7 items-center justify-center gap-1 rounded-lg px-2 text-xs text-teal-550 transition-colors hover:bg-teal-50 hover:text-teal-600 active:bg-teal-100 active:text-teal-600" > <span className="pb-px">View Available Entry Points</span> </Link> </div> </Card> <Card customClass="col-span-1 items-center p-6"> <DoughnutChart {...programschartProps} InitialDataDisplay="Programs" > <div className="absolute left-1/2 top-1/2 flex h-36 w-36 -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center rounded-full bg-white"> <div className="text-tiny font-semibold">Programs</div> <div className="flex items-center gap-2"> <div className="font-serif text-wh2s font-semibold"> {programCount} </div> <div className="mt-2 h-4 rotate-[13deg] border-l border-warm-200"></div> <div className="mt-2 text-cool-500"> {totalProgramCount} </div> </div> </div> </DoughnutChart> <hr className="border-warm-150"></hr> <div className="flex items-center justify-center py-3"> <Link href="/modeling?tab=programs" className="flex h-7 items-center justify-center gap-1 rounded-lg px-2 text-xs text-teal-550 transition-colors hover:bg-teal-50 hover:text-teal-600 active:bg-teal-100 active:text-teal-600" > <span className="pb-px">View Available Programs</span> </Link> </div> </Card> </div> </div> </div> </div> ); }; const TransformDashboardWrapper: FC = () => { const projectId = localStorageService.get("Averi_last_project_id"); const [showBuildTransformDrawer, setShowBuildTransformDrawer] = useState(false); const [logData, setLogData] = useState<[""] | undefined>(); const [showLogDrawer, setShowLogDrawer] = useState(false); const [projectTransformStarted, setProjectTransformStarted] = useState<boolean>(false); const [transformCompleted, setTransformCompleted] = useState<boolean>(false); const [showTransformBuildModal, setShowTransformBuildModal] = useState(false); const [projectTransformStatus, setprojectTransformStatus] = useState<ITransformStatusResponse>(); const [summaryTransformCompleted, setSummaryTransformCompleted] = useState<boolean>(false); const TransformBuildModal: FC<IOpenDrawer> = ({ setOpenLog, transformCompleted, }) => { return ( <div> <div className="rounded-b-2xl bg-white p-10 text-center pb-3"> <div className="font-serif text-h1"> {transformCompleted ? "Transform has been successfully built" : "Transform Is Building"} </div> <p className="mt-3 text-m text-cool-600"> {transformCompleted ? "You can close this window now." : "Transform will continue building in the background—we’ll alert you when your reports are ready, but you can always check back on progress later."} </p> <LoadingProgressBar klassNames="mt-8" completed={transformCompleted} /> </div> <div className="flex items-center justify-end gap-2 px-6 py-4"> <Button className="flex h-8 items-center justify-center gap-1 rounded-lg bg-white-100 px-3 text-cool-800 shadow-field ring-1 ring-inset ring-warm-300 transition hover:ring-warm-500 active:text-teal-550 active:ring-teal-550" variant="tertiary-teal" onClick={(): void => setOpenLog(true)} > <span className="pb-px">View Log</span> </Button> </div> </div> ); }; const LogDrawer: FC<ILogData> = ({ logData, transformCompleted, setcloseDrawer, }) => { return ( <div> <div className="px-4 pb-3 items-center w-full"> <div className="inline-flex justify-between w-full"> <div className="flex items-center gap-2"> <div className="text-m font-semibold">Transform Build Log</div> </div> <div className="flex"> <div className="flex h-7 w-7 items-center rounded-lg justify-center text-xs text-cool-800 transition-colors hover:bg-warm-100 active:bg-warm-200"> <ExpandBtn /> </div> <div className="flex w-7 items-center rounded-lg justify-center text-xs text-cool-800 transition-colors hover:bg-warm-100 active:bg-warm-200" onClick={(): void => { setcloseDrawer(true); }} > <Crossdrawer /> </div> </div> </div> </div> <div className="absolute bottom-0 left-0 right-0 top-[55px] flex flex-col justify-center p-10 text-center"> <div className="font-serif text-h1"> {transformCompleted ? "Transform has been successfully built" : "Transform Is Building"} </div> <LoadingProgressBar klassNames="mt-4 mb-0" completed={transformCompleted} /> <div className="relative mt-8 grow overflow-hidden rounded-lg border border-warm-150"> <div className="scrollbar snap-end absolute inset-0 overflow-auto p-5 text-left font-mono text-xs break-normal"> {logData?.map((item: "", index: number, array: ""[]) => ( <ul key={item}> <div>{item}</div> </ul> ))} </div> </div> </div> </div> ); }; const { data: projectStatus } = useQuery({ queryKey: ["project-status", projectId], queryFn: async () => { return await axiosService.get<IProjectStatusResponse>( `${ENDPOINTS.PROJECT_STATUS}${projectId}` ); }, }); const { data, isLoading, refetch } = useQuery({ queryKey: ["transform-summary", projectId], queryFn: async () => { return await axiosService.get<ITransformSummaryResponse>( `${ENDPOINTS.TRANSFROM_SUMMARY}/${projectId}` ); }, }); useEffect(() => { if (summaryTransformCompleted) { refetch(); } }, [summaryTransformCompleted]); const { data: licenseDetails } = useQuery({ queryKey: ["get-active-license"], queryFn: () => { return axiosService.get<LicenseDetailsType>(ENDPOINTS.LICENSE_DETAILS); }, }); const { data: scanData } = useQuery({ queryKey: ["get-scan-info"], queryFn: () => { return axiosService.get<IScanDashboardResponse>( ENDPOINTS.SCAN_DASHBOARD + projectId ); }, }); useEffect(() => { if (showTransformBuildModal) { setTransformCompleted(false); setLogData([""]); } }, [showTransformBuildModal]); // API to continuously fetch Transform status const checkProjectTransformStatus = async (): Promise<void> => { return await axiosService .get<ITransformStatusResponse>(`${ENDPOINTS.PROJECT_STATUS}${projectId}`) .then((resp: ITransformStatusResponse) => { setprojectTransformStatus(resp); successMessage(resp?.messages?.[0]); }) .catch((e: ITransformStatusFailResponse) => errorMessage(e?.messages?.[0]) ); }; const { refetch: refetchTransformStatus } = useQuery( ["transform-status"], () => { return checkProjectTransformStatus(); }, { enabled: projectTransformStarted, } ); const getTransformLogData = async (): Promise<void> => { return await axiosService .get<ITransformLogResponse>(`${ENDPOINTS.RUNNER_LOGS}${projectId}`) .then((resp: ITransformLogResponse) => { setLogData(resp.data); }) .catch((e: ITransformLogResponse) => errorMessage(e?.messages?.[0])); }; const { refetch: refetchTransformLogs } = useQuery( ["transform-logs"], () => { return getTransformLogData(); }, { enabled: projectTransformStarted, } ); useEffect(() => { if (projectTransformStarted) { const intervalId = setInterval(async () => { refetchTransformStatus(); refetchTransformLogs(); const endTime = projectTransformStatus?.data?.endTime; if (endTime !== null) { setTransformCompleted(true); setProjectTransformStarted(false); setSummaryTransformCompleted?.(true); clearInterval(intervalId); } }, 5000); return () => clearInterval(intervalId); } }, [projectTransformStatus?.data?.endTime, projectTransformStarted]); const showAbout = (): void => { // To be implemented later }; if (projectStatus?.data?.lkPackageID! <= 3) { if (showTransformBuildModal) { return ( <> <Modal topDivClasses="static" show={showTransformBuildModal} toggle={setShowTransformBuildModal} closable={true} > <TransformBuildModal setOpenLog={setShowLogDrawer} transformCompleted={transformCompleted} /> </Modal> {showLogDrawer && ( <Drawer open={showLogDrawer} toggleDrawer={(): void => setShowLogDrawer(false)} > <LogDrawer setcloseDrawer={setShowLogDrawer} logData={logData} transformCompleted={transformCompleted} /> </Drawer> )} </> ); } else if (!showBuildTransformDrawer) { return ( <> <EmptySummary title="AveriSource Transform" /> <ReadyToBuildTransformDialog {...licenseDetails?.licenseInfo} aboutAction={showAbout} buildAction={(): void => { !showBuildTransformDrawer && setShowBuildTransformDrawer(true); }} // @ts-expect-error locRequested={scanData?.Data?.keyStatistics?.linesOfCode} /> </> ); } else return ( <> <EmptySummary title="AveriSource Transform" /> <Drawer open={showBuildTransformDrawer} toggleDrawer={(): void => setShowBuildTransformDrawer(false)} > <BuilderDrawer showTransformBuildModal={showBuildTransformDrawer} setShowTransformBuildModal={setShowTransformBuildModal} setProjectTransformStarted={setProjectTransformStarted} setShowBuildTransformDrawer={setShowBuildTransformDrawer} /> </Drawer> </> ); } else if (data && !isLoading) { return <TransformDashboard {...data.data} />; } else if (isLoading) { return ( <div className="flex items-center justify-center h-[calc(100dvh-90px)]"> <BounceLoader size="80px" color="#33B0A5" /> </div> ); } }; export default TransformDashboardWrapper; ANALYZE PAGE /* eslint-disable @typescript-eslint/no-explicit-any */ "use client"; import "ag-grid-community/styles/ag-grid.css"; import "ag-grid-community/styles/ag-theme-alpine.css"; import "ag-grid-enterprise"; // top level imports import React, { FC, useEffect, useMemo, useRef, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { CellMouseOverEvent, ColDef, ICellRendererParams, } from "ag-grid-community"; import { AgGridReact } from "ag-grid-react"; import { localStorageService } from "@/lib"; import { BounceLoader } from "react-spinners"; // components import { Button, Card, DoughnutChart, Modal, TDoughnutChart, errorMessage, successMessage, } from "@/components"; import { QuestionCircle, ArrowRightWithTail, CopyWhite, AlertTriangle, Settings, Background, Code, PriceTag, Crossdrawer, ExpandBtn, } from "@/components/icons"; import BuilderDrawer from "./BuilderDrawer"; import Drawer from "@/components/drawer/Drawer"; import PackageCommentViewer from "@/components/package_comment_viewer/PackageCommentViewer"; import ReadyToBuildAnalyzeDialog from "@/components/dialog/ready-to-build/ReadyToBuildAnalyazeDialog"; // API utils import { ENDPOINTS } from "@/api_config"; import axiosService from "@/api/axios.service"; // types import { TAnalyzeSummary, TRuleTypeSummary } from "@/types/Analyze.types"; import { IScanDashboardResponse } from "../../(scan)/scan/page"; import { LicenseDetailsType } from "@/mockdata/mocks"; import { ChartConfiguration } from "chart.js"; import LoadingProgressBar from "@/components/loader/LoadingProgressBar"; import HelpDrawer, { IHelpDrawerData } from "../../help-drawer-modal/page"; type TAnalyzeDashboard = { analyzeSummary: TAnalyzeSummary; }; type TSummaryCard = { data: TAnalyzeSummary; }; interface IOpenDrawer { setOpenLog: React.Dispatch<React.SetStateAction<boolean>>; analyzeCompleted: boolean; } interface ILogData { logData: [""] | undefined; analyzeCompleted: boolean; setcloseDrawer: (val: boolean) => void; } export interface IAnalyzeStatusResponse { code: number; hasError: boolean; messages: [""]; data: Data; dataList: null; } export interface IStartAnalyzeResponse { code: number; hasError: boolean; messages: [""]; data: null; dataList: null; } export interface Data { packageStatusID: number; projectId: number; lkPackageID: number; startTime: Date; endTime: null; lkPackage: null; project: null; } export interface IAnalyzeStatusFailResponse { code: number; hasError: boolean; messages: [""]; data: null; dataList: null; } export interface IAnalyzeLogResponse { code: number; hasError: boolean; messages: [""]; data: [""]; } const SummaryCard: FC<TSummaryCard> = ({ data }) => ( <Card customClass="mt-4 grid grid-cols-4 py-6 text-center"> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Code Anomalies</div> <div className="mt-1 font-serif text-h1"> {data.codeAnamoliesCount.toLocaleString()} </div> </div> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Files With Dead Code</div> <div className="mt-1 font-serif text-h1"> {data.deadCodeFilesCount.toLocaleString()} </div> </div> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Variables</div> <div className="mt-1 font-serif text-h1"> {data.variablesCount.toLocaleString()} </div> </div> <div className="border-r border-cool-100 last:border-0"> <div className="text-cool-550">Files With Flowcharts</div> <div className="mt-1 font-serif text-h1"> {data.flowcharFilesCount.toLocaleString()} </div> </div> </Card> ); const CommonText: FC<ICellRendererParams> = (params) => { const { value, colDef, data } = params; const isPercentageField = colDef?.field === "percentage"; const updatedValue = isPercentageField ? value?.toFixed(5) : value; const isTotalRow = data.businessRuleType === "Totals"; if (isTotalRow) { return ( <div className="font-semibold"> {value ? updatedValue.toLocaleString() : 0} {isPercentageField ? "%" : ""} </div> ); } return ( <div className={!value ? "text-cool-400" : "text-cool-700"}> {value ? updatedValue.toLocaleString() : 0} {isPercentageField ? "%" : ""} </div> ); }; const lightenHexColorByPercentage = ( color: String, percentage: number ): String => { const multiplier = (100 + percentage) / 100; return Math.min(Math.floor(Number(`0x${color}`) * multiplier), 255) .toString(16) .padStart(2, "0"); }; export const changeColorTone = (color: String): String => { color = color.slice(1); const percentLighter = 50; const r = lightenHexColorByPercentage(color.slice(0, 2), percentLighter); const g = lightenHexColorByPercentage(color.slice(2, 4), percentLighter); const b = lightenHexColorByPercentage(color.slice(4, 6), percentLighter); return `#${[r, g, b].join("")}`; }; const BusinessRuleTypeRender: FC< ICellRendererParams & { hovered: () => string } > = (props) => { const { data } = props; const color = data.color; const isTotalRow = data.businessRuleType === "Totals"; return ( <div className="flex items-center gap-2"> {!isTotalRow ? ( <div className="inline-flex h-5.5 items-center gap-1.5 rounded-full bg-yellow-100 px-1.5" style={{ background: color }} > <PriceTag className="h-3 w-3 shrink-0" style={{ color: changeColorTone(color) }} /> <div className="pb-px font-sans-cond text-h7 uppercase"> {data.businessRuleType} </div> </div> ) : ( <div>Totals</div> )} </div> ); }; const AnalyzeModal: FC = () => { return ( <div> <div className="-mt-1 text-h3">Business Rules</div> <p className="mt-4 text-cool-550"> These are the set of conditions, constraints, policies, and guidelines that define or govern business processes, objectives, and standards. These rules are crucial for ensuring that the code migration process aligns with your organization`'`s strategic goals, compliance requirements, operational standards, and best practices. </p> </div> ); }; const ActionModal: FC = () => { const staticAnalyzeData = [ { icon: <Settings className="h-6 w-6" />, title: "Evaluate business rules", description: [ "AveriSource identifies potential business rule candidates across all the programs. It categorizes them further based on the rule type, example IO, UI, CRUD, technical rules etc. Evaluating and flagging the rules of interest are important both from documenting the application perspective as well as for re-engineering/re-architecting the application as part of its Modernization journey.", ], }, { icon: <CopyWhite className="h-6 w-6" />, title: "Assess potential code block redundancies", description: [ "AveriSource Analyze identifies and reports code blocks that are 100% similar and also blocks that are over 70% similar. This is done by comparing a code block with other code blocks in the same program as well as code blocks in other application programs. Evaluating the list of code blocks that are largely similar helps prune/optimize the application before moving forward in the modernization journey.", ], }, { icon: <Code className="h-6 w-6" />, title: "Examine dead code", description: [ "Dead code indicates lines of code that can never get executed as it does not fall in the path of the control flow. A larger percentage of dead code, if not removed, indicates high maintainability down the line as there would be effort spent analyzing, modernizing and maintaining dead code, which not only increases the application footprint but also does not add any value.", ], }, { icon: <AlertTriangle className="h-6 w-6" />, title: "Review code anomalies", description: [ "Code anomalies are those code constructs which can affect the application parameters like performance, maintainability, code comprehension, potential security threats etc. Reviewing and addressing code anomalies where applicable improves the quality of the application whether it is retained on the legacy platform or chosen for modernization.", ], }, ]; return ( <div className="p-2 bg-white"> <div className="-mt-1 text-h3">About Your Analyze Suggested Actions</div> <div className="mt-5 space-y-5"> {staticAnalyzeData?.map((data) => { return ( <> <hr className="border-cool-100" /> <div className="flex items-center gap-4"> <div className="relative flex h-12 w-12 shrink-0 items-center justify-center self-start overflow-hidden rounded-lg bg-gradient-to-l from-red-300 to-pink-500 text-white"> {data.icon} </div> <div className="grow"> <h4 className="text-m font-semibold">{data.title}</h4> {data?.description?.map((descriptionLine, index) => ( <p key={index} className="mt-1.5 text-cool-600"> {descriptionLine} </p> ))} </div> </div> </> ); })} </div> <div className="mt-5 flex items-center justify-between gap-4 rounded-lg bg-warm-100 p-4"> <div className="pb-px text-xs font-semibold text-cool-500"> Still not sure what to do? Contact us so we can help at{" "} <a target="_blank" rel="noreferrer" href="mailto:support@averisource.com" > support@averisource.com </a> </div> {/* <div className="-my-1"> <button type="button" className="flex h-7 items-center justify-center gap-1 rounded-lg bg-warm-100 px-2 text-xs text-cool-800 shadow-field ring-1 ring-inset ring-warm-300 transition hover:ring-warm-500 active:text-teal-550 active:ring-teal-550" > <span className="pb-px">Contact Us</span> </button> </div> */} </div> </div> ); }; const AnalyzeDashboard: FC<TAnalyzeDashboard> = ({ analyzeSummary }) => { const router = useRouter(); const tableRef = useRef<AgGridReact | null>(null); const hoveredRowRef = useRef<string | undefined>(); const [hoveredRow, setHoveredRow] = useState<string | undefined>(); const [show, toggleModal] = useState<boolean>(false); const [showActions, toggleActionModal] = useState<boolean>(false); const [showHelp, toggleHelpDrawer] = useState<boolean>(false); const [helpDrawerData] = useState<IHelpDrawerData>({ heading: "Analyze", text: "The Analyze dashboard provides a snapshot of the key findings in Analyze process. This includes Business Rules Overview, which lists all the types of rules found in the application code along with the total no of rules under each rule type, files with code anomalies, dead code etc. It also provides a list of suggested next steps as “Action Items”. This includes reviewing the dead code and code anomalies to further decide how to address them. The dashboard also has a comments section where users can provide their inputs or questions on the dashboard.", }); useEffect(() => { // 👇 dynamically overrides global grid table theme just for this route/page const styleNode = document.createElement("style"); styleNode.innerHTML = ` .ag-theme-alpine { --ag-borders: none !important; --ag-odd-row-background-color: #fff !important; --ag-header-background-color: #fff !important; --ag-row-hover-color: #fff !important; } `; document.head.appendChild(styleNode); return () => { // 👇 Clean up the styleNode when the component unmounts document.head.removeChild(styleNode); }; }, []); useEffect(() => { hoveredRowRef.current = hoveredRow; }, [hoveredRow]); // eslint-disable-next-line no-unused-vars const [colDefs, setColDefs] = useState<ColDef[]>([ { field: "businessRuleType", headerName: " BUSINESS RULE TYPE", cellRenderer: BusinessRuleTypeRender, cellRendererParams: { // ⚠️ there seems to be bug with providing object that is tied to a state. // 🙌 But workaround is to track state with ref. // ➡️ Github issue: https://github.com/ag-grid/ag-grid/issues/4858#issuecomment-1185578378 hovered: () => hoveredRowRef.current, }, cellStyle: (_) => ({ display: "flex", alignItems: "center", justifyContent: "left", }), }, { field: "totalRules", headerName: "TOTAL RULES", cellRendererSelector: (params: ICellRendererParams): any => ({ component: CommonText, }), cellStyle: (_) => ({ display: "flex", alignItems: "center", justifyContent: "right", }), }, { field: "percentage", headerName: "PERCENTAGE", cellRendererSelector: (params: ICellRendererParams): any => ({ component: CommonText, }), cellStyle: (_) => ({ display: "flex", alignItems: "center", justifyContent: "right", }), }, ]); const defaultColDef = useMemo(() => { return { sortable: false, filter: false, resizable: false, pinnable: false, }; }, []); const { brsOverview } = analyzeSummary; const rowData = [ ...brsOverview, { totalRules: String( brsOverview .reduce((acc, i) => acc + (i.totalRules ?? 0), 0) .toLocaleString() ), percentage: 100, businessRuleType: "Totals", }, ]; const colors = brsOverview.map((i: TRuleTypeSummary) => i.color); const chartProps: TDoughnutChart = { type: "doughnut", data: { labels: brsOverview.map((i: TRuleTypeSummary) => i.businessRuleType), datasets: [ { data: brsOverview.map((i: TRuleTypeSummary) => i.percentage.toFixed(5) ) as unknown as ChartConfiguration<"doughnut">["data"]["datasets"][0]["data"], backgroundColor: colors, hoverBackgroundColor: colors, }, ], }, options: {}, InitialDataDisplay: "analyze", }; const onMouseOver = (params: CellMouseOverEvent<any, any>): void => { setHoveredRow(params.data.businessRuleType); }; const onMouseOut = (params: CellMouseOverEvent<any, any>): void => { setHoveredRow(undefined); }; return ( <div className="mx-auto box-content max-w-[1140px] p-8"> <div className="flex items-center justify-between border-b border-warm-150 pb-3"> <h1 className="pb-0.5 font-serif text-h1">AveriSource Analyze</h1> <div className="flex items-center gap-3"> <Button type="button" variant="secondary" className="h-8 bg-warm-100 px-3" onClick={(): void => { toggleHelpDrawer(true); }} > <QuestionCircle className="h-4 w-4" /> <span className="pb-px">Help</span> </Button> </div> </div> <Drawer open={showHelp} toggleDrawer={toggleHelpDrawer}> <HelpDrawer drawerData={helpDrawerData} toggleHelpDrawer={toggleHelpDrawer} /> </Drawer> <Modal show={show} toggle={(): void => toggleModal(false)} closable={true} filledIcon={false} > <AnalyzeModal /> </Modal> <Modal show={showActions} toggle={(): void => toggleActionModal(false)} closable={true} filledIcon={false} > <ActionModal /> </Modal> <div> <div className="grid grid-cols-3 gap-8"> <div className="col-span-2"> <div className="mt-8"> <div className="flex items-center gap-2"> <h2 className="font-serif text-h2">Suggested Actions</h2> <div className="-mx-1.5 -my-0.5 ml-auto"> <Button variant="tertiary-teal" size="small" onClick={(): void => toggleActionModal(true)} > <span className="pb-px">Why are these necessary?</span> </Button> </div> </div> <div className="mt-4 space-y-4 rounded-xl bg-white p-6 shadow-card"> <div className="flex items-center gap-4"> <div className="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-l from-red-300 to-pink-500 text-white"> <Background className="absolute -left-1 -top-1 opacity-20" /> <Settings className="relative h-6 w-6 stroke-1333" /> </div> <div className="grow"> <h4 className="font-semibold">Evaluate business rules</h4> <p className="mt-1.5 text-xs text-cool-550"> Developer and Business Analysts should work together to interpret the code and determine business significance for each rule. </p> </div> <div className="shrink-0"> <Button onClick={(_): void => router.push("/business-rules")} endIcon={ <ArrowRightWithTail className="h-4 w-4 shrink-0 first:last:-mx-1" /> } > Business Rules </Button> </div> </div> <hr className="border-cool-100" /> <div className="flex items-center gap-4"> <div className="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-l from-red-300 to-pink-500 text-white"> <Background className="absolute -left-1 -top-1 opacity-20" /> <CopyWhite className="relative h-6 w-6 stroke-1333" /> </div> <div className="grow"> <h4 className="font-semibold"> Assess potential code block redundancies </h4> <p className="mt-1.5 text-xs text-cool-550"> We’ve made a list of files and code blocks that are at least 70% similar & 10 lines long. Please review them to identify redundancies. </p> </div> <div className="shrink-0"> <Button onClick={(_): void => router.push("/code-file-comparison") } endIcon={ <ArrowRightWithTail className="h-4 w-4 shrink-0 first:last:-mx-1" /> } > Block Comparison </Button> </div> </div> <hr className="border-cool-100" /> <div className="flex items-center gap-4"> <div className="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-l from-red-300 to-pink-500 text-white"> <Background className="absolute -left-1 -top-1 opacity-20" /> <Code className="relative h-6 w-6 stroke-1333" /> </div> <div className="grow"> <h4 className="font-semibold">Examine dead code</h4> <p className="mt-1.5 text-xs text-cool-550"> Review the statements or conditions that have been marked as “Dead code” because their paragraph/subroutine was never executed. </p> </div> <div className="shrink-0"> <Button onClick={(_): void => router.push("/dead-code")} endIcon={ <ArrowRightWithTail className="h-4 w-4 shrink-0 first:last:-mx-1" /> } > Dead Code </Button> </div> </div> <hr className="border-cool-100" /> <div className="flex items-center gap-4"> <div className="relative flex h-14 w-14 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-l from-red-300 to-pink-500 text-white"> <Background className="absolute -left-1 -top-1 opacity-20" /> <AlertTriangle className="relative h-6 w-6 stroke-1333" /> </div> <div className="grow"> <h4 className="font-semibold">Review code anomalies</h4> <p className="mt-1.5 text-xs text-cool-550"> Find the system dependencies and see their impact on various business aspects. </p> </div> <div className="shrink-0"> <Button onClick={(_): void => router.push("/code-anomalies")} endIcon={ <ArrowRightWithTail className="h-4 w-4 shrink-0 first:last:-mx-1" /> } > Code Anomalies </Button> </div> </div> </div> </div> <div className="mt-8"> <h2 className="font-serif text-h2">Summary</h2> <SummaryCard data={analyzeSummary} /> </div> </div> <div className="flex flex-col"> <div className="relative mt-8 grow"> <PackageCommentViewer packageLabel="Analyze" /> </div> </div> </div> <div className="mt-8"> <div className="flex items-center justify-between gap-2"> <h2 className="font-serif text-h2">Business Rule Overview</h2> <div className="-mx-1.5 -my-0.5 ml-auto"> <Button variant="tertiary-teal" size="small" onClick={(): void => toggleModal(true)} > <span className="pb-px">What are business rules?</span> </Button> </div> </div> <div className="mt-4 grid grid-cols-3 items-center gap-6 rounded-xl bg-white p-6 shadow-card"> <div className="col-span-1"> <div className="relative aspect-square"> <DoughnutChart {...chartProps} /> </div> </div> <div className="col-span-2"> <div className="ag-theme-alpine" style={{ height: "500px" }}> <AgGridReact ref={tableRef} columnDefs={colDefs} rowData={rowData} defaultColDef={defaultColDef} onCellMouseOver={onMouseOver} onCellMouseOut={onMouseOut} /> </div> </div> </div> </div> </div> </div> ); }; const AnalyzeDashboardWrapper: FC = () => { const projectId = localStorageService.get("Averi_last_project_id"); const [showBuildAnalyzeDrawer, setShowBuildAnalyzeDrawer] = useState<boolean>(false); const [projectAnalyzeStarted, setProjectAnalyzeStarted] = useState<boolean>(false); const [showAnalyzeBuildModal, setShowAnalyzeBuildModal] = useState(false); const [showLogDrawer, setShowLogDrawer] = useState(false); const [summaryAnalyzeCompleted, setSummaryAnalyzeCompleted] = useState<boolean>(false); const [projectAnalyzeStatus, setprojectAnalyzeStatus] = useState<IAnalyzeStatusResponse>(); const [analyzeCompleted, setAnalyzeCompleted] = useState<boolean>(false); const [logData, setLogData] = useState<[""] | undefined>(); const AnalyzeBuildModal: FC<IOpenDrawer> = ({ setOpenLog, analyzeCompleted, }) => { return ( <div> <div className="rounded-b-2xl bg-white p-10 text-center pb-3"> <div className="font-serif text-h1"> {analyzeCompleted ? "Analyze has been successfully built" : "Analyze Is Building"} </div> <p className="mt-3 text-m text-cool-600"> {analyzeCompleted ? "You can close this window now." : "Analyze will continue building in the background—we’ll alert you when your reports are ready, but you can always check back on progress later."} </p> <LoadingProgressBar klassNames="mt-8" completed={analyzeCompleted} /> </div> <div className="flex items-center justify-end gap-2 px-6 py-4"> <Button className="flex h-8 items-center justify-center gap-1 rounded-lg bg-white-100 px-3 text-cool-800 shadow-field ring-1 ring-inset ring-warm-300 transition hover:ring-warm-500 active:text-teal-550 active:ring-teal-550" variant="tertiary-teal" onClick={(): void => setOpenLog(true)} > <span className="pb-px">View Log</span> </Button> </div> </div> ); }; const LogDrawer: FC<ILogData> = ({ logData, analyzeCompleted, setcloseDrawer, }) => { return ( <div> <div className="px-4 pb-3 items-center w-full"> <div className="inline-flex justify-between w-full"> <div className="flex items-center gap-2"> <div className="text-m font-semibold">Analyze Build Log</div> </div> <div className="flex"> <div className="flex h-7 w-7 items-center rounded-lg justify-center text-xs text-cool-800 transition-colors hover:bg-warm-100 active:bg-warm-200"> <ExpandBtn /> </div> <div className="flex w-7 items-center rounded-lg justify-center text-xs text-cool-800 transition-colors hover:bg-warm-100 active:bg-warm-200" onClick={(): void => { setcloseDrawer(true); }} > <Crossdrawer /> </div> </div> </div> </div> <div className="absolute bottom-0 left-0 right-0 top-[55px] flex flex-col justify-center p-10 text-center"> <div className="font-serif text-h1"> {analyzeCompleted ? "Analyze has been successfully built" : "Analyze Is Building"} </div> <LoadingProgressBar klassNames="mt-4 mb-0" completed={analyzeCompleted} /> <div className="relative mt-8 grow overflow-hidden rounded-lg border border-warm-150"> <div className="scrollbar snap-end absolute inset-0 overflow-auto p-5 text-left font-mono text-xs break-normal"> {logData?.map((item: "", index: number, array: ""[]) => ( <ul key={item}> <div>{item}</div> </ul> ))} </div> </div> </div> </div> ); }; const { data: analyzeSummary, isLoading: analyzeSummaryIsLoading, refetch, } = useQuery({ queryKey: [projectId], queryFn: async () => { return await axiosService.get<TAnalyzeSummary>( ENDPOINTS.ANALYZE_DASHBOARD ); }, }); useEffect(() => { if (summaryAnalyzeCompleted) { refetch(); } }, [summaryAnalyzeCompleted]); const { data: licenseDetails } = useQuery({ queryKey: ["get-active-license"], queryFn: () => { return axiosService.get<LicenseDetailsType>(ENDPOINTS.LICENSE_DETAILS); }, }); const { data: scanData } = useQuery({ queryKey: ["get-scan-info"], queryFn: () => { return axiosService.get<IScanDashboardResponse>( ENDPOINTS.SCAN_DASHBOARD + projectId ); }, }); useEffect(() => { if (showAnalyzeBuildModal) { setAnalyzeCompleted(false); setLogData([""]); } }, [showAnalyzeBuildModal]); // API to continuously fetch analyze status const checkProjectAnalyzeStatus = async (): Promise<void> => { return await axiosService .get<IAnalyzeStatusResponse>(`${ENDPOINTS.PROJECT_STATUS}${projectId}`) .then((resp: IAnalyzeStatusResponse) => { setprojectAnalyzeStatus(resp); successMessage(resp?.messages?.[0]); }) .catch((e: IAnalyzeStatusFailResponse) => errorMessage(e?.messages?.[0])); }; const { refetch: refetchAnalyzeStatus } = useQuery( ["analyze-status"], () => { return checkProjectAnalyzeStatus(); }, { enabled: projectAnalyzeStarted, } ); const getAnalyzeLogData = async (): Promise<void> => { return await axiosService .get<IAnalyzeLogResponse>(`${ENDPOINTS.RUNNER_LOGS}${projectId}`) .then((resp: IAnalyzeLogResponse) => { setLogData(resp.data); }) .catch((e: IAnalyzeLogResponse) => errorMessage(e?.messages?.[0])); }; const { refetch: refetchAnalyzeLogs } = useQuery( ["analyze-logs"], () => { return getAnalyzeLogData(); }, { enabled: projectAnalyzeStarted, } ); useEffect(() => { if (projectAnalyzeStarted) { const intervalId = setInterval(async () => { refetchAnalyzeStatus(); refetchAnalyzeLogs(); const endTime = projectAnalyzeStatus?.data?.endTime; if (endTime !== null) { setAnalyzeCompleted(true); setProjectAnalyzeStarted(false); setSummaryAnalyzeCompleted?.(true); clearInterval(intervalId); } }, 5000); return () => clearInterval(intervalId); } }, [projectAnalyzeStatus?.data?.endTime, projectAnalyzeStarted]); const showAbout = (): void => { // To be implemented later }; // Data is loading if (analyzeSummaryIsLoading) { return ( <div className="flex items-center justify-center h-[calc(100dvh-90px)]"> <BounceLoader size="80px" color="#33B0A5" /> </div> ); // No Data was found } else if (analyzeSummary?.brsOverview?.length && !analyzeSummaryIsLoading) { return <AnalyzeDashboard analyzeSummary={analyzeSummary} />; } // Analyze build has started else if (showAnalyzeBuildModal) { return ( <> <Modal topDivClasses="static" show={showAnalyzeBuildModal} toggle={setShowAnalyzeBuildModal} closable={true} > <AnalyzeBuildModal setOpenLog={setShowLogDrawer} analyzeCompleted={analyzeCompleted} /> </Modal> {showLogDrawer && ( <Drawer open={showLogDrawer} toggleDrawer={(): void => setShowLogDrawer(false)} > <LogDrawer setcloseDrawer={setShowLogDrawer} logData={logData} analyzeCompleted={analyzeCompleted} /> </Drawer> )} </> ); // Showing ready to build dialog } else if (!showBuildAnalyzeDrawer) { return ( <ReadyToBuildAnalyzeDialog {...licenseDetails?.licenseInfo} aboutAction={showAbout} buildAction={(): void => { !showBuildAnalyzeDrawer && setShowBuildAnalyzeDrawer(true); }} // @ts-expect-error locRequested={scanData?.Data?.keyStatistics?.linesOfCode} /> ); } // Analyze drawer is open else if (showBuildAnalyzeDrawer) { return ( <Drawer open={showBuildAnalyzeDrawer} toggleDrawer={(): void => setShowBuildAnalyzeDrawer(false)} > <BuilderDrawer showAnalyzeBuildModal={showAnalyzeBuildModal} setShowAnalyzeBuildModal={setShowAnalyzeBuildModal} setProjectAnalyzeStarted={setProjectAnalyzeStarted} setShowBuildAnalyzeDrawer={setShowBuildAnalyzeDrawer} /> </Drawer> ); } }; export default AnalyzeDashboardWrapper;
Editor is loading...
Leave a Comment