Display information of project on home tab
This commit is contained in:
@@ -211,7 +211,7 @@ export default function TestCaseTable({
|
||||
</TableColumn>
|
||||
)}
|
||||
</TableHeader>
|
||||
<TableBody emptyContent={"No cases found"} items={sortedItems}>
|
||||
<TableBody emptyContent={messages.noCasesFound} items={sortedItems}>
|
||||
{(item) => (
|
||||
<TableRow key={item.id}>
|
||||
{(columnKey) => (
|
||||
|
||||
@@ -21,6 +21,7 @@ export default function Page({
|
||||
high: t("high"),
|
||||
medium: t("medium"),
|
||||
low: t("low"),
|
||||
noCasesFound: t("no_cases_found"),
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { testTypes } from "@/config/selection";
|
||||
import { CaseTypeCountType } from "@/types/case";
|
||||
import { HomeMessages } from "./page";
|
||||
const Chart = dynamic(() => import("react-apexcharts"), { ssr: false });
|
||||
|
||||
type Props = {
|
||||
typesCounts: CaseTypeCountType[];
|
||||
messages: HomeMessages;
|
||||
};
|
||||
|
||||
export default function TestTypesDonutChart({ typesCounts, messages }: Props) {
|
||||
const [chartData, setChartData] = useState({
|
||||
series: [],
|
||||
options: {
|
||||
labels: [],
|
||||
colors: [],
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const updateChartDate = () => {
|
||||
if (typesCounts) {
|
||||
const series = testTypes.map((entry, index) => {
|
||||
const found = typesCounts.find((itr) => itr.type === index);
|
||||
return found ? found.count : 0;
|
||||
});
|
||||
|
||||
const labels = testTypes.map((entry) => messages[entry.uid]);
|
||||
const colors = testTypes.map((entry) => entry.chartColor);
|
||||
|
||||
setChartData({
|
||||
series,
|
||||
options: { labels, colors },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateChartDate();
|
||||
}, [typesCounts]);
|
||||
|
||||
return (
|
||||
<Chart
|
||||
options={chartData.options}
|
||||
series={chartData.series}
|
||||
type="donut"
|
||||
width={"100%"}
|
||||
height={"100%"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Divider } from "@nextui-org/react";
|
||||
import { title } from "@/components/primitives";
|
||||
import { Card, CardBody, Chip } from "@nextui-org/react";
|
||||
import { Folder, Clipboard, FlaskConical } from "lucide-react";
|
||||
import { CaseTypeCountType } from "@/types/case";
|
||||
import { HomeMessages } from "./page";
|
||||
import { testTypes } from "@/config/selection";
|
||||
import TestTypesChart from "./TestTypesDonutChart";
|
||||
import Config from "@/config/config";
|
||||
const apiServer = Config.apiServer;
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
async function fetchProject(url) {
|
||||
try {
|
||||
@@ -25,13 +33,21 @@ async function fetchProject(url) {
|
||||
|
||||
type Props = {
|
||||
projectId: string;
|
||||
messages: HomeMessages;
|
||||
};
|
||||
|
||||
export function Home({ projectId }: Props) {
|
||||
export function Home({ projectId, messages }: Props) {
|
||||
const [project, setProject] = useState({
|
||||
Folders: [],
|
||||
name: "",
|
||||
detail: "",
|
||||
Folders: [{ Cases: [] }],
|
||||
Runs: [{ RunCases: [] }],
|
||||
});
|
||||
const url = `${apiServer}/projects/${projectId}`;
|
||||
const [folderNum, setFolderNum] = useState(0);
|
||||
const [caseNum, setCaseNum] = useState(0);
|
||||
const [runNum, setRunNum] = useState(0);
|
||||
const [typesCounts, setTypesCounts] = useState<CaseTypeCountType[]>();
|
||||
const url = `${apiServer}/home/${projectId}`;
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchDataEffect() {
|
||||
@@ -47,13 +63,87 @@ export function Home({ projectId }: Props) {
|
||||
fetchDataEffect();
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="font-bold ms-2">Home</h3>
|
||||
useEffect(() => {
|
||||
async function aggregate() {
|
||||
aggregateBasicInfo();
|
||||
aggregateTestType();
|
||||
}
|
||||
|
||||
<h4 className="font-bold ms-2 mt-5">
|
||||
Folder num: {project.Folders.length}
|
||||
</h4>
|
||||
</>
|
||||
aggregate();
|
||||
}, [project]);
|
||||
|
||||
// aggregate folder, case, run mum
|
||||
function aggregateBasicInfo() {
|
||||
let num = 0;
|
||||
setFolderNum(project.Folders.length);
|
||||
setRunNum(project.Runs.length);
|
||||
project.Folders.forEach((folder) => {
|
||||
num += folder.Cases.length;
|
||||
});
|
||||
setCaseNum(num);
|
||||
}
|
||||
|
||||
// aggregate test types of each case
|
||||
function aggregateTestType() {
|
||||
const tempTypesCounts = {};
|
||||
project.Folders.forEach((folder) => {
|
||||
folder.Cases.forEach((testcase) => {
|
||||
const type = testcase.type;
|
||||
tempTypesCounts[type] = (tempTypesCounts[type] || 0) + 1;
|
||||
});
|
||||
});
|
||||
|
||||
const result = [];
|
||||
for (let type = 0; type <= testTypes.length; type++) {
|
||||
result.push({ type: type, count: tempTypesCounts[type] || 0 });
|
||||
}
|
||||
|
||||
setTypesCounts([...result]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto max-w-5xl pt-6 px-6 flex-grow">
|
||||
<h1 className={title({ size: "sm" })}>{project.name}</h1>
|
||||
<div className="mt-4">
|
||||
<Chip
|
||||
variant="flat"
|
||||
startContent={<Folder size={16} />}
|
||||
className="px-3"
|
||||
>
|
||||
{folderNum} {messages.folders}
|
||||
</Chip>
|
||||
<Chip
|
||||
variant="flat"
|
||||
startContent={<Clipboard size={16} />}
|
||||
className="px-3 ms-2"
|
||||
>
|
||||
{caseNum} {messages.testCases}
|
||||
</Chip>
|
||||
<Chip
|
||||
variant="flat"
|
||||
startContent={<FlaskConical size={16} />}
|
||||
className="px-3 ms-2"
|
||||
>
|
||||
{runNum} {messages.testRuns}
|
||||
</Chip>
|
||||
</div>
|
||||
|
||||
<Card className="mt-3 bg-neutral-100" shadow="none">
|
||||
<CardBody>{project.detail}</CardBody>
|
||||
</Card>
|
||||
|
||||
<Divider className="my-6" />
|
||||
|
||||
<div className="flex">
|
||||
<div style={{ width: "32rem", height: "32rem" }}>
|
||||
<h3>{messages.testTypes}</h3>
|
||||
<TestTypesChart typesCounts={typesCounts} messages={messages} />
|
||||
</div>
|
||||
<div style={{ width: "32rem", height: "32rem" }}>
|
||||
<h3>{messages.testTypes}</h3>
|
||||
<TestTypesChart typesCounts={typesCounts} messages={messages} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,50 @@
|
||||
import { Home } from "./home";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export type HomeMessages = {
|
||||
folders: string;
|
||||
testCases: string;
|
||||
testRuns: string;
|
||||
testTypes: string;
|
||||
other: string;
|
||||
security: string;
|
||||
performance: string;
|
||||
accessibility: string;
|
||||
functional: string;
|
||||
acceptance: string;
|
||||
usability: string;
|
||||
smokeSanity: string;
|
||||
compatibility: string;
|
||||
destructive: string;
|
||||
regression: string;
|
||||
automated: string;
|
||||
manual: string;
|
||||
};
|
||||
|
||||
export default function Page({ params }: { params: { projectId: string } }) {
|
||||
const t = useTranslations("Home");
|
||||
const messages = {
|
||||
folders: t("Folders"),
|
||||
testCases: t("test_cases"),
|
||||
testRuns: t("test_runs"),
|
||||
testTypes: t("test_types"),
|
||||
other: t("other"),
|
||||
security: t("security"),
|
||||
performance: t("performance"),
|
||||
accessibility: t("accessibility"),
|
||||
functional: t("functional"),
|
||||
acceptance: t("acceptance"),
|
||||
usability: t("usability"),
|
||||
smokeSanity: t("smoke_sanity"),
|
||||
compatibility: t("compatibility"),
|
||||
destructive: t("destructive"),
|
||||
regression: t("regression"),
|
||||
automated: t("automated"),
|
||||
manual: t("manual"),
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Home projectId={params.projectId} />
|
||||
<Home projectId={params.projectId} messages={messages} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user