Display information of project on home tab

This commit is contained in:
Takeshi Kimata
2024-05-06 13:35:32 +09:00
parent ac9af2d604
commit 3de1bd2d42
15 changed files with 323 additions and 36 deletions

View File

@@ -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) => (

View File

@@ -21,6 +21,7 @@ export default function Page({
high: t("high"),
medium: t("medium"),
low: t("low"),
noCasesFound: t("no_cases_found"),
};
return (

View File

@@ -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%"}
/>
);
}

View File

@@ -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>
);
}

View File

@@ -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} />
</>
);
}