Display information of project on home tab

This commit is contained in:
Takeshi Kimata
2024-05-06 17:51:45 +09:00
parent 3de1bd2d42
commit 6ccbf1be29
10 changed files with 177 additions and 46 deletions

View File

@@ -1,8 +1,8 @@
const priorities = [ const priorities = [
{ uid: "critical", color: "#d00002" }, { uid: "critical", color: "#d00002", chartColor: "#d00002" },
{ uid: "high", color: "#ee6b4e" }, { uid: "high", color: "#ee6b4e", chartColor: "#ee6b4e" },
{ uid: "medium", color: "#fccb69" }, { uid: "medium", color: "#fccb69", chartColor: "#fccb69" },
{ uid: "low", color: "#0b62e8" }, { uid: "low", color: "#0b62e8", chartColor: "#0b62e8" },
]; ];
const testTypes = [ const testTypes = [

View File

@@ -30,7 +30,7 @@
"Project": { "Project": {
"home": "Home", "home": "Home",
"test_cases": "Test Cases", "test_cases": "Test Cases",
"test_run": "Test Runs" "test_runs": "Test Runs"
}, },
"Home": { "Home": {
"Folders": "Folders", "Folders": "Folders",
@@ -49,7 +49,12 @@
"destructive": "Destructive", "destructive": "Destructive",
"regression": "Regression", "regression": "Regression",
"automated": "Automated", "automated": "Automated",
"manual": "Manual" "manual": "Manual",
"priority": "Priority",
"critical": "Critical",
"high": "High",
"medium": "Medium",
"low": "Low"
}, },
"Folders": { "Folders": {
"folder": "Folder", "folder": "Folder",

View File

@@ -49,7 +49,12 @@
"destructive": "破壊", "destructive": "破壊",
"regression": "回帰", "regression": "回帰",
"automated": "自動", "automated": "自動",
"manual": "手動" "manual": "手動",
"priority": "優先度",
"critical": "致",
"high": "高",
"medium": "中",
"low": "低"
}, },
"Folders": { "Folders": {
"folder": "フォルダー", "folder": "フォルダー",

View File

@@ -0,0 +1,56 @@
import React from "react";
import { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import { priorities } from "@/config/selection";
import { CasePriorityCountType } from "@/types/case";
import { HomeMessages } from "./page";
const Chart = dynamic(() => import("react-apexcharts"), { ssr: false });
type Props = {
priorityCounts: CasePriorityCountType[];
messages: HomeMessages;
};
export default function TestPriorityDonutChart({
priorityCounts,
messages,
}: Props) {
const [chartData, setChartData] = useState({
series: [],
options: {
labels: [],
colors: [],
},
});
useEffect(() => {
const updateChartDate = () => {
if (priorityCounts) {
const series = priorities.map((entry, index) => {
const found = priorityCounts.find((itr) => itr.priority === index);
return found ? found.count : 0;
});
const labels = priorities.map((entry) => messages[entry.uid]);
const colors = priorities.map((entry) => entry.chartColor);
setChartData({
series,
options: { labels, colors },
});
}
};
updateChartDate();
}, [priorityCounts]);
return (
<Chart
options={chartData.options}
series={chartData.series}
type="donut"
width={"100%"}
height={"100%"}
/>
);
}

View File

@@ -0,0 +1,52 @@
import { ProjectType } from "@/types/project";
import { testTypes, priorities } from "@/config/selection";
// aggregate folder, case, run mum
function aggregateBasicInfo(project: ProjectType) {
const folderNum = project.Folders.length;
const runNum = project.Runs.length;
let caseNum = 0;
project.Folders.forEach((folder) => {
caseNum += folder.Cases.length;
});
return { folderNum, runNum, caseNum };
}
// aggregate test types of each case
function aggregateTestType(project: ProjectType) {
const typesCounts = {};
project.Folders.forEach((folder) => {
folder.Cases.forEach((testcase) => {
const type = testcase.type;
typesCounts[type] = (typesCounts[type] || 0) + 1;
});
});
const result = [];
for (let type = 0; type <= testTypes.length; type++) {
result.push({ type: type, count: typesCounts[type] || 0 });
}
return result;
}
function aggregateTestPriority(project: ProjectType) {
const priorityCounts = {};
project.Folders.forEach((folder) => {
folder.Cases.forEach((testcase) => {
const priority = testcase.priority;
priorityCounts[priority] = (priorityCounts[priority] || 0) + 1;
});
});
const result = [];
for (let priority = 0; priority <= priorities.length; priority++) {
result.push({ priority: priority, count: priorityCounts[priority] || 0 });
}
return result;
}
export { aggregateBasicInfo, aggregateTestType, aggregateTestPriority };

View File

@@ -4,10 +4,15 @@ import { Divider } from "@nextui-org/react";
import { title } from "@/components/primitives"; import { title } from "@/components/primitives";
import { Card, CardBody, Chip } from "@nextui-org/react"; import { Card, CardBody, Chip } from "@nextui-org/react";
import { Folder, Clipboard, FlaskConical } from "lucide-react"; import { Folder, Clipboard, FlaskConical } from "lucide-react";
import { CaseTypeCountType } from "@/types/case"; import { CaseTypeCountType, CasePriorityCountType } from "@/types/case";
import { HomeMessages } from "./page"; import { HomeMessages } from "./page";
import { testTypes } from "@/config/selection"; import {
aggregateBasicInfo,
aggregateTestPriority,
aggregateTestType,
} from "./aggregate";
import TestTypesChart from "./TestTypesDonutChart"; import TestTypesChart from "./TestTypesDonutChart";
import TestPriorityChart from "./TestPriorityDonutChart";
import Config from "@/config/config"; import Config from "@/config/config";
const apiServer = Config.apiServer; const apiServer = Config.apiServer;
@@ -47,6 +52,8 @@ export function Home({ projectId, messages }: Props) {
const [caseNum, setCaseNum] = useState(0); const [caseNum, setCaseNum] = useState(0);
const [runNum, setRunNum] = useState(0); const [runNum, setRunNum] = useState(0);
const [typesCounts, setTypesCounts] = useState<CaseTypeCountType[]>(); const [typesCounts, setTypesCounts] = useState<CaseTypeCountType[]>();
const [priorityCounts, setPriorityCounts] =
useState<CasePriorityCountType[]>();
const url = `${apiServer}/home/${projectId}`; const url = `${apiServer}/home/${projectId}`;
useEffect(() => { useEffect(() => {
@@ -65,42 +72,21 @@ export function Home({ projectId, messages }: Props) {
useEffect(() => { useEffect(() => {
async function aggregate() { async function aggregate() {
aggregateBasicInfo(); const { folderNum, runNum, caseNum } = aggregateBasicInfo(project);
aggregateTestType(); setFolderNum(folderNum);
setRunNum(runNum);
setCaseNum(caseNum);
const typeRet = aggregateTestType(project);
setTypesCounts([...typeRet]);
const priorityRet = aggregateTestPriority(project);
setPriorityCounts([...priorityRet]);
} }
aggregate(); aggregate();
}, [project]); }, [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 ( return (
<div className="container mx-auto max-w-5xl pt-6 px-6 flex-grow"> <div className="container mx-auto max-w-5xl pt-6 px-6 flex-grow">
<h1 className={title({ size: "sm" })}>{project.name}</h1> <h1 className={title({ size: "sm" })}>{project.name}</h1>
@@ -133,17 +119,21 @@ export function Home({ projectId, messages }: Props) {
</Card> </Card>
<Divider className="my-6" /> <Divider className="my-6" />
<div className="flex"> <div className="flex">
<div style={{ width: "32rem", height: "32rem" }}> <div style={{ width: "32rem", height: "18rem" }}>
<h3>{messages.testTypes}</h3> <h3>{messages.testTypes}</h3>
<TestTypesChart typesCounts={typesCounts} messages={messages} /> <TestTypesChart typesCounts={typesCounts} messages={messages} />
</div> </div>
<div style={{ width: "32rem", height: "32rem" }}> <div style={{ width: "30rem", height: "18rem" }}>
<h3>{messages.testTypes}</h3> <h3>{messages.priority}</h3>
<TestTypesChart typesCounts={typesCounts} messages={messages} /> <TestPriorityChart
priorityCounts={priorityCounts}
messages={messages}
/>
</div> </div>
</div> </div>
<Divider className="my-6" />
</div> </div>
); );
} }

View File

@@ -19,6 +19,11 @@ export type HomeMessages = {
regression: string; regression: string;
automated: string; automated: string;
manual: string; manual: string;
priority: string;
critical: string;
high: string;
medium: string;
low: string;
}; };
export default function Page({ params }: { params: { projectId: string } }) { export default function Page({ params }: { params: { projectId: string } }) {
@@ -41,6 +46,11 @@ export default function Page({ params }: { params: { projectId: string } }) {
regression: t("regression"), regression: t("regression"),
automated: t("automated"), automated: t("automated"),
manual: t("manual"), manual: t("manual"),
priority: t("priority"),
critical: t("critical"),
high: t("high"),
medium: t("medium"),
low: t("low"),
}; };
return ( return (
<> <>

View File

@@ -54,6 +54,11 @@ type CaseTypeCountType = {
count: number; count: number;
}; };
type CasePriorityCountType = {
priority: number;
count: number;
};
export type CasesMessages = { export type CasesMessages = {
testCaseList: string; testCaseList: string;
id: string; id: string;
@@ -119,4 +124,4 @@ export type CaseMessages = {
maxFileSize: string; maxFileSize: string;
}; };
export { CaseType, StepType, AttachmentType, CaseTypeCountType, CasesMessages, CaseMessages }; export { CaseType, StepType, AttachmentType, CaseTypeCountType, CasePriorityCountType, CasesMessages, CaseMessages };

View File

@@ -1,3 +1,5 @@
import { CaseType } from "./case";
export type FolderType = { export type FolderType = {
id: number; id: number;
name: string; name: string;
@@ -6,6 +8,7 @@ export type FolderType = {
parentFolderId: number | null; parentFolderId: number | null;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
Cases: CaseType[]; // additional property
}; };
export type FoldersMessages = { export type FoldersMessages = {

View File

@@ -1,9 +1,14 @@
import { FolderType } from "./folder";
import { RunType } from "./run";
export type ProjectType = { export type ProjectType = {
id: number; id: number;
name: string; name: string;
detail: string; detail: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
Folders: FolderType[]; // additional property
Runs: RunType[]; // additional property
}; };
export type ProjectsMessages = { export type ProjectsMessages = {