Introduce prettier
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Listbox, ListboxItem } from "@nextui-org/react";
|
||||
import { Home, Files, FlaskConical } from "lucide-react";
|
||||
import { usePathname, useRouter } from "@/src/navigation";
|
||||
import useGetCurrentIds from "@/utils/useGetCurrentIds";
|
||||
import { ProjectMessages } from "@/types/project";
|
||||
'use client';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Listbox, ListboxItem } from '@nextui-org/react';
|
||||
import { Home, Files, FlaskConical } from 'lucide-react';
|
||||
import { usePathname, useRouter } from '@/src/navigation';
|
||||
import useGetCurrentIds from '@/utils/useGetCurrentIds';
|
||||
import { ProjectMessages } from '@/types/project';
|
||||
|
||||
export type Props = {
|
||||
messages: ProjectMessages;
|
||||
@@ -16,28 +16,28 @@ export default function Sidebar({ messages, locale }: Props) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const [currentKey, setCurrentTab] = useState("home");
|
||||
const baseClass = "p-3";
|
||||
const [currentKey, setCurrentTab] = useState('home');
|
||||
const baseClass = 'p-3';
|
||||
const selectedClass = `${baseClass} bg-neutral-200 dark:bg-neutral-700`;
|
||||
|
||||
const handleTabClick = (key: string) => {
|
||||
if (key === "home") {
|
||||
if (key === 'home') {
|
||||
router.push(`/projects/${projectId}/home`, { locale: locale });
|
||||
} else if (key === "cases") {
|
||||
} else if (key === 'cases') {
|
||||
router.push(`/projects/${projectId}/folders`, { locale: locale });
|
||||
} else if (key === "runs") {
|
||||
} else if (key === 'runs') {
|
||||
router.push(`/projects/${projectId}/runs`, { locale: locale });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleRouteChange = (currentPath: string) => {
|
||||
if (currentPath.includes("home")) {
|
||||
setCurrentTab("home");
|
||||
} else if (currentPath.includes("folders")) {
|
||||
setCurrentTab("cases");
|
||||
} else if (currentPath.includes("runs")) {
|
||||
setCurrentTab("runs");
|
||||
if (currentPath.includes('home')) {
|
||||
setCurrentTab('home');
|
||||
} else if (currentPath.includes('folders')) {
|
||||
setCurrentTab('cases');
|
||||
} else if (currentPath.includes('runs')) {
|
||||
setCurrentTab('runs');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,17 +46,17 @@ export default function Sidebar({ messages, locale }: Props) {
|
||||
|
||||
const tabItems = [
|
||||
{
|
||||
key: "home",
|
||||
key: 'home',
|
||||
text: messages.home,
|
||||
startContent: <Home strokeWidth={1} size={28} />,
|
||||
},
|
||||
{
|
||||
key: "cases",
|
||||
key: 'cases',
|
||||
text: messages.testCases,
|
||||
startContent: <Files strokeWidth={1} size={28} />,
|
||||
},
|
||||
{
|
||||
key: "runs",
|
||||
key: 'runs',
|
||||
text: messages.testRuns,
|
||||
startContent: <FlaskConical strokeWidth={1} size={28} />,
|
||||
},
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Textarea,
|
||||
Modal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
} from "@nextui-org/react";
|
||||
import { FolderType, FoldersMessages } from "@/types/folder";
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Button, Input, Textarea, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@nextui-org/react';
|
||||
import { FolderType, FoldersMessages } from '@/types/folder';
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
@@ -21,23 +12,17 @@ type Props = {
|
||||
messages: FoldersMessages;
|
||||
};
|
||||
|
||||
export default function FolderDialog({
|
||||
isOpen,
|
||||
editingFolder,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
messages,
|
||||
}: Props) {
|
||||
export default function FolderDialog({ isOpen, editingFolder, onCancel, onSubmit, messages }: Props) {
|
||||
const [folderName, setFolderName] = useState({
|
||||
text: editingFolder ? editingFolder.name : "",
|
||||
text: editingFolder ? editingFolder.name : '',
|
||||
isValid: false,
|
||||
errorMessage: "",
|
||||
errorMessage: '',
|
||||
});
|
||||
|
||||
const [folderDetail, setFolderDetail] = useState({
|
||||
text: editingFolder ? editingFolder.detail : "",
|
||||
text: editingFolder ? editingFolder.detail : '',
|
||||
isValid: false,
|
||||
errorMessage: "",
|
||||
errorMessage: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -49,17 +34,17 @@ export default function FolderDialog({
|
||||
|
||||
setFolderDetail({
|
||||
...folderDetail,
|
||||
text: editingFolder.detail ? editingFolder.detail : "",
|
||||
text: editingFolder.detail ? editingFolder.detail : '',
|
||||
});
|
||||
} else {
|
||||
setFolderName({
|
||||
...folderName,
|
||||
text: "",
|
||||
text: '',
|
||||
});
|
||||
|
||||
setFolderDetail({
|
||||
...folderDetail,
|
||||
text: "",
|
||||
text: '',
|
||||
});
|
||||
}
|
||||
}, [editingFolder]);
|
||||
@@ -67,20 +52,20 @@ export default function FolderDialog({
|
||||
const clear = () => {
|
||||
setFolderName({
|
||||
isValid: false,
|
||||
text: "",
|
||||
errorMessage: "",
|
||||
text: '',
|
||||
errorMessage: '',
|
||||
});
|
||||
setFolderDetail({
|
||||
isValid: false,
|
||||
text: "",
|
||||
errorMessage: "",
|
||||
text: '',
|
||||
errorMessage: '',
|
||||
});
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
if (!folderName.text) {
|
||||
setFolderName({
|
||||
text: "",
|
||||
text: '',
|
||||
isValid: false,
|
||||
errorMessage: messages.pleaseEnter,
|
||||
});
|
||||
@@ -100,9 +85,7 @@ export default function FolderDialog({
|
||||
}}
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
{messages.folder}
|
||||
</ModalHeader>
|
||||
<ModalHeader className="flex flex-col gap-1">{messages.folder}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Input
|
||||
type="text"
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
DropdownTrigger,
|
||||
DropdownMenu,
|
||||
DropdownItem,
|
||||
} from "@nextui-org/react";
|
||||
import { MoreVertical } from "lucide-react";
|
||||
import { FolderType, FoldersMessages } from "@/types/folder";
|
||||
import { Button, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem } from '@nextui-org/react';
|
||||
import { MoreVertical } from 'lucide-react';
|
||||
import { FolderType, FoldersMessages } from '@/types/folder';
|
||||
|
||||
type Props = {
|
||||
folder: FolderType;
|
||||
@@ -15,12 +9,7 @@ type Props = {
|
||||
messages: FoldersMessages;
|
||||
};
|
||||
|
||||
export default function FolderEditMenu({
|
||||
folder,
|
||||
onEditClick,
|
||||
onDeleteClick,
|
||||
messages,
|
||||
}: Props) {
|
||||
export default function FolderEditMenu({ folder, onEditClick, onDeleteClick, messages }: Props) {
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
@@ -32,12 +21,7 @@ export default function FolderEditMenu({
|
||||
<DropdownItem key="edit" onClick={() => onEditClick(folder)}>
|
||||
{messages.editFolder}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
onClick={() => onDeleteClick(folder.id)}
|
||||
>
|
||||
<DropdownItem key="delete" className="text-danger" color="danger" onClick={() => onDeleteClick(folder.id)}>
|
||||
{messages.deleteFolder}
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { FolderType, FoldersMessages } from "@/types/folder";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button, Listbox, ListboxItem } from "@nextui-org/react";
|
||||
import { Folder, Plus } from "lucide-react";
|
||||
import { usePathname, useRouter } from "@/src/navigation";
|
||||
import useGetCurrentIds from "@/utils/useGetCurrentIds";
|
||||
import FolderDialog from "./FolderDialog";
|
||||
import FolderEditMenu from "./FolderEditMenu";
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { FolderType, FoldersMessages } from '@/types/folder';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Button, Listbox, ListboxItem } from '@nextui-org/react';
|
||||
import { Folder, Plus } from 'lucide-react';
|
||||
import { usePathname, useRouter } from '@/src/navigation';
|
||||
import useGetCurrentIds from '@/utils/useGetCurrentIds';
|
||||
import FolderDialog from './FolderDialog';
|
||||
import FolderEditMenu from './FolderEditMenu';
|
||||
|
||||
import {
|
||||
fetchFolders,
|
||||
createFolder,
|
||||
updateFolder,
|
||||
deleteFolder,
|
||||
} from "./foldersControl";
|
||||
import { fetchFolders, createFolder, updateFolder, deleteFolder } from './foldersControl';
|
||||
|
||||
type Props = {
|
||||
projectId: string;
|
||||
@@ -45,16 +40,8 @@ export default function FoldersPane({ projectId, messages, locale }: Props) {
|
||||
|
||||
const onSubmit = async (name: string, detail: string) => {
|
||||
if (editingFolder) {
|
||||
const updatedProject = await updateFolder(
|
||||
editingFolder.id,
|
||||
name,
|
||||
detail,
|
||||
projectId,
|
||||
null
|
||||
);
|
||||
const updatedProjects = folders.map((project) =>
|
||||
project.id === updatedProject.id ? updatedProject : project
|
||||
);
|
||||
const updatedProject = await updateFolder(editingFolder.id, name, detail, projectId, null);
|
||||
const updatedProjects = folders.map((project) => (project.id === updatedProject.id ? updatedProject : project));
|
||||
setFolders(updatedProjects);
|
||||
} else {
|
||||
const newProject = await createFolder(name, detail, projectId, null);
|
||||
@@ -79,28 +66,23 @@ export default function FoldersPane({ projectId, messages, locale }: Props) {
|
||||
const data = await fetchFolders(projectId);
|
||||
setFolders(data);
|
||||
|
||||
const selectedFolderFromUrl = data.find(
|
||||
(folder) => folder.id === folderId
|
||||
);
|
||||
const selectedFolderFromUrl = data.find((folder) => folder.id === folderId);
|
||||
setSelectedFolder(selectedFolderFromUrl);
|
||||
|
||||
// Redirect to the smallest folder ID page if the path is "projects/[projectId]/folders
|
||||
if (pathname === `/projects/${projectId}/folders`) {
|
||||
const smallestFolderId = Math.min(...data.map((folder) => folder.id));
|
||||
router.push(
|
||||
`/projects/${projectId}/folders/${smallestFolderId}/cases`,
|
||||
{ locale: locale }
|
||||
);
|
||||
router.push(`/projects/${projectId}/folders/${smallestFolderId}/cases`, { locale: locale });
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Error in effect:", error.message);
|
||||
console.error('Error in effect:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
fetchDataEffect();
|
||||
}, [folderId]);
|
||||
|
||||
const baseClass = "";
|
||||
const baseClass = '';
|
||||
const selectedClass = `${baseClass} bg-neutral-200 dark:bg-neutral-700`;
|
||||
|
||||
return (
|
||||
@@ -119,18 +101,9 @@ export default function FoldersPane({ projectId, messages, locale }: Props) {
|
||||
{folders.map((folder, index) => (
|
||||
<ListboxItem
|
||||
key={index}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/projects/${projectId}/folders/${folder.id}/cases`,
|
||||
{ locale: locale }
|
||||
)
|
||||
}
|
||||
onClick={() => router.push(`/projects/${projectId}/folders/${folder.id}/cases`, { locale: locale })}
|
||||
startContent={<Folder size={20} color="#F7C24E" fill="#F7C24E" />}
|
||||
className={
|
||||
selectedFolder && folder.id === selectedFolder.id
|
||||
? selectedClass
|
||||
: baseClass
|
||||
}
|
||||
className={selectedFolder && folder.id === selectedFolder.id ? selectedClass : baseClass}
|
||||
endContent={
|
||||
<FolderEditMenu
|
||||
folder={folder}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import TestCaseTable from "./TestCaseTable";
|
||||
import { fetchCases, createCase, deleteCase, deleteCases } from "./caseControl";
|
||||
import { CasesMessages } from "@/types/case";
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react';
|
||||
import TestCaseTable from './TestCaseTable';
|
||||
import { fetchCases, createCase, deleteCase, deleteCases } from './caseControl';
|
||||
import { CasesMessages } from '@/types/case';
|
||||
|
||||
type Props = {
|
||||
projectId: string;
|
||||
@@ -11,12 +11,7 @@ type Props = {
|
||||
locale: string;
|
||||
};
|
||||
|
||||
export default function CasesPane({
|
||||
projectId,
|
||||
folderId,
|
||||
messages,
|
||||
locale,
|
||||
}: Props) {
|
||||
export default function CasesPane({ projectId, folderId, messages, locale }: Props) {
|
||||
const [cases, setCases] = useState([]);
|
||||
useEffect(() => {
|
||||
async function fetchDataEffect() {
|
||||
@@ -24,7 +19,7 @@ export default function CasesPane({
|
||||
const data = await fetchCases(folderId);
|
||||
setCases(data);
|
||||
} catch (error: any) {
|
||||
console.error("Error in effect:", error.message);
|
||||
console.error('Error in effect:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo, useCallback } from "react";
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
DropdownItem,
|
||||
Selection,
|
||||
SortDescriptor,
|
||||
} from "@nextui-org/react";
|
||||
import { Link, NextUiLinkClasses } from "@/src/navigation";
|
||||
import { Plus, MoreVertical, Trash, Circle } from "lucide-react";
|
||||
import { CasesMessages } from "@/types/case";
|
||||
import { priorities } from "@/config/selection";
|
||||
} from '@nextui-org/react';
|
||||
import { Link, NextUiLinkClasses } from '@/src/navigation';
|
||||
import { Plus, MoreVertical, Trash, Circle } from 'lucide-react';
|
||||
import { CasesMessages } from '@/types/case';
|
||||
import { priorities } from '@/config/selection';
|
||||
|
||||
type Case = {
|
||||
id: number;
|
||||
@@ -55,16 +55,16 @@ export default function TestCaseTable({
|
||||
locale,
|
||||
}: Props) {
|
||||
const headerColumns = [
|
||||
{ name: messages.id, uid: "id", sortable: true },
|
||||
{ name: messages.title, uid: "title", sortable: true },
|
||||
{ name: messages.priority, uid: "priority", sortable: true },
|
||||
{ name: messages.actions, uid: "actions" },
|
||||
{ name: messages.id, uid: 'id', sortable: true },
|
||||
{ name: messages.title, uid: 'title', sortable: true },
|
||||
{ name: messages.priority, uid: 'priority', sortable: true },
|
||||
{ name: messages.actions, uid: 'actions' },
|
||||
];
|
||||
|
||||
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
|
||||
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
|
||||
column: "id",
|
||||
direction: "ascending",
|
||||
column: 'id',
|
||||
direction: 'ascending',
|
||||
});
|
||||
|
||||
const sortedItems = useMemo(() => {
|
||||
@@ -73,7 +73,7 @@ export default function TestCaseTable({
|
||||
const second = b[sortDescriptor.column as keyof Case] as number;
|
||||
const cmp = first < second ? -1 : first > second ? 1 : 0;
|
||||
|
||||
return sortDescriptor.direction === "descending" ? -cmp : cmp;
|
||||
return sortDescriptor.direction === 'descending' ? -cmp : cmp;
|
||||
});
|
||||
}, [sortDescriptor, cases]);
|
||||
|
||||
@@ -81,15 +81,11 @@ export default function TestCaseTable({
|
||||
const cellValue = testCase[columnKey as keyof Case];
|
||||
|
||||
switch (columnKey) {
|
||||
case "id":
|
||||
case 'id':
|
||||
return <span>{cellValue}</span>;
|
||||
case "title":
|
||||
case 'title':
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="light"
|
||||
className="data-[hover=true]:bg-transparent"
|
||||
>
|
||||
<Button size="sm" variant="light" className="data-[hover=true]:bg-transparent">
|
||||
<Link
|
||||
href={`/projects/${projectId}/folders/${testCase.folderId}/cases/${testCase.id}`}
|
||||
locale={locale}
|
||||
@@ -99,18 +95,14 @@ export default function TestCaseTable({
|
||||
</Link>
|
||||
</Button>
|
||||
);
|
||||
case "priority":
|
||||
case 'priority':
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Circle
|
||||
size={8}
|
||||
color={priorities[cellValue].color}
|
||||
fill={priorities[cellValue].color}
|
||||
/>
|
||||
<Circle size={8} color={priorities[cellValue].color} fill={priorities[cellValue].color} />
|
||||
<div className="ms-3">{messages[priorities[cellValue].uid]}</div>
|
||||
</div>
|
||||
);
|
||||
case "actions":
|
||||
case 'actions':
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
@@ -119,10 +111,7 @@ export default function TestCaseTable({
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label="test case actions">
|
||||
<DropdownItem
|
||||
className="text-danger"
|
||||
onClick={() => onDeleteCase(testCase.id)}
|
||||
>
|
||||
<DropdownItem className="text-danger" onClick={() => onDeleteCase(testCase.id)}>
|
||||
{messages.deleteCase}
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
@@ -135,25 +124,25 @@ export default function TestCaseTable({
|
||||
|
||||
const classNames = useMemo(
|
||||
() => ({
|
||||
wrapper: ["max-w-3xl"],
|
||||
th: ["bg-transparent", "text-default-500", "border-b", "border-divider"],
|
||||
wrapper: ['max-w-3xl'],
|
||||
th: ['bg-transparent', 'text-default-500', 'border-b', 'border-divider'],
|
||||
td: [
|
||||
// changing the rows border radius
|
||||
// first
|
||||
"group-data-[first=true]:first:before:rounded-none",
|
||||
"group-data-[first=true]:last:before:rounded-none",
|
||||
'group-data-[first=true]:first:before:rounded-none',
|
||||
'group-data-[first=true]:last:before:rounded-none',
|
||||
// middle
|
||||
"group-data-[middle=true]:before:rounded-none",
|
||||
'group-data-[middle=true]:before:rounded-none',
|
||||
// last
|
||||
"group-data-[last=true]:first:before:rounded-none",
|
||||
"group-data-[last=true]:last:before:rounded-none",
|
||||
'group-data-[last=true]:first:before:rounded-none',
|
||||
'group-data-[last=true]:last:before:rounded-none',
|
||||
],
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const onDeleteCasesClick = async () => {
|
||||
if (selectedKeys === "all") {
|
||||
if (selectedKeys === 'all') {
|
||||
const allKeys = sortedItems.map((item) => item.id);
|
||||
onDeleteCases(allKeys);
|
||||
} else {
|
||||
@@ -167,7 +156,7 @@ export default function TestCaseTable({
|
||||
<div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between">
|
||||
<h3 className="font-bold">{messages.testCaseList}</h3>
|
||||
<div>
|
||||
{(selectedKeys.size > 0 || selectedKeys === "all") && (
|
||||
{(selectedKeys.size > 0 || selectedKeys === 'all') && (
|
||||
<Button
|
||||
startContent={<Trash size={16} />}
|
||||
size="sm"
|
||||
@@ -178,12 +167,7 @@ export default function TestCaseTable({
|
||||
{messages.delete}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
startContent={<Plus size={16} />}
|
||||
size="sm"
|
||||
color="primary"
|
||||
onClick={onCreateCase}
|
||||
>
|
||||
<Button startContent={<Plus size={16} />} size="sm" color="primary" onClick={onCreateCase}>
|
||||
{messages.newTestCase}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -204,7 +188,7 @@ export default function TestCaseTable({
|
||||
{(column) => (
|
||||
<TableColumn
|
||||
key={column.uid}
|
||||
align={column.uid === "actions" ? "center" : "start"}
|
||||
align={column.uid === 'actions' ? 'center' : 'start'}
|
||||
allowsSorting={column.sortable}
|
||||
>
|
||||
{column.name}
|
||||
@@ -213,11 +197,7 @@ export default function TestCaseTable({
|
||||
</TableHeader>
|
||||
<TableBody emptyContent={messages.noCasesFound} items={sortedItems}>
|
||||
{(item) => (
|
||||
<TableRow key={item.id}>
|
||||
{(columnKey) => (
|
||||
<TableCell>{renderCell(item, columnKey)}</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
<TableRow key={item.id}>{(columnKey) => <TableCell>{renderCell(item, columnKey)}</TableCell>}</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { Image, Button, Tooltip, Card, CardBody } from "@nextui-org/react";
|
||||
import { AttachmentType, CaseMessages } from "@/types/case";
|
||||
import { Trash, ArrowDownToLine } from "lucide-react";
|
||||
import { isImage } from "./isImage";
|
||||
import { Image, Button, Tooltip, Card, CardBody } from '@nextui-org/react';
|
||||
import { AttachmentType, CaseMessages } from '@/types/case';
|
||||
import { Trash, ArrowDownToLine } from 'lucide-react';
|
||||
import { isImage } from './isImage';
|
||||
|
||||
type Props = {
|
||||
attachments: AttachmentType[];
|
||||
onAttachmentDownload: (
|
||||
attachmentId: number,
|
||||
downloadFileName: string
|
||||
) => void;
|
||||
onAttachmentDownload: (attachmentId: number, downloadFileName: string) => void;
|
||||
onAttachmentDelete: (attachmentId: number) => void;
|
||||
messages: CaseMessages,
|
||||
messages: CaseMessages;
|
||||
};
|
||||
|
||||
export default function CaseAttachmentsEditor({
|
||||
@@ -35,11 +32,7 @@ export default function CaseAttachmentsEditor({
|
||||
{images.map((image, index) => (
|
||||
<Card key={index} radius="sm" className="mt-2 me-2 max-w-md">
|
||||
<CardBody>
|
||||
<Image
|
||||
alt={image.title}
|
||||
src={image.path}
|
||||
className="object-cover h-40 w-40"
|
||||
/>
|
||||
<Image alt={image.title} src={image.path} className="object-cover h-40 w-40" />
|
||||
<div className="flex items-center justify-between">
|
||||
<p>{image.title}</p>
|
||||
<Tooltip content={messages.delete}>
|
||||
|
||||
@@ -1,39 +1,27 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Input,
|
||||
Textarea,
|
||||
Select,
|
||||
SelectItem,
|
||||
Button,
|
||||
Divider,
|
||||
Tooltip,
|
||||
} from "@nextui-org/react";
|
||||
import { useRouter } from "@/src/navigation";
|
||||
import { Save, Plus, ArrowLeft, ArrowUpFromLine, Circle } from "lucide-react";
|
||||
import { priorities, testTypes, templates } from "@/config/selection";
|
||||
import CaseStepsEditor from "./CaseStepsEditor";
|
||||
import CaseAttachmentsEditor from "./CaseAttachmentsEditor";
|
||||
import { CaseType, AttachmentType, CaseMessages } from "@/types/case";
|
||||
import { fetchCase, updateCase } from "../caseControl";
|
||||
import { fetchCreateStep, fetchDeleteStep } from "./stepControl";
|
||||
import {
|
||||
fetchCreateAttachments,
|
||||
fetchDownloadAttachment,
|
||||
fetchDeleteAttachment,
|
||||
} from "./attachmentControl";
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Input, Textarea, Select, SelectItem, Button, Divider, Tooltip } from '@nextui-org/react';
|
||||
import { useRouter } from '@/src/navigation';
|
||||
import { Save, Plus, ArrowLeft, ArrowUpFromLine, Circle } from 'lucide-react';
|
||||
import { priorities, testTypes, templates } from '@/config/selection';
|
||||
import CaseStepsEditor from './CaseStepsEditor';
|
||||
import CaseAttachmentsEditor from './CaseAttachmentsEditor';
|
||||
import { CaseType, AttachmentType, CaseMessages } from '@/types/case';
|
||||
import { fetchCase, updateCase } from '../caseControl';
|
||||
import { fetchCreateStep, fetchDeleteStep } from './stepControl';
|
||||
import { fetchCreateAttachments, fetchDownloadAttachment, fetchDeleteAttachment } from './attachmentControl';
|
||||
|
||||
const defaultTestCase = {
|
||||
id: 0,
|
||||
title: "",
|
||||
title: '',
|
||||
state: 0,
|
||||
priority: 0,
|
||||
type: 0,
|
||||
automationStatus: 0,
|
||||
description: "",
|
||||
description: '',
|
||||
template: 0,
|
||||
preConditions: "",
|
||||
expectedResults: "",
|
||||
preConditions: '',
|
||||
expectedResults: '',
|
||||
folderId: 0,
|
||||
};
|
||||
|
||||
@@ -45,13 +33,7 @@ type Props = {
|
||||
locale: string;
|
||||
};
|
||||
|
||||
export default function CaseEditor({
|
||||
projectId,
|
||||
folderId,
|
||||
caseId,
|
||||
messages,
|
||||
locale,
|
||||
}: Props) {
|
||||
export default function CaseEditor({ projectId, folderId, caseId, messages, locale }: Props) {
|
||||
const [testCase, setTestCase] = useState<CaseType>(defaultTestCase);
|
||||
const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false);
|
||||
const [isUpdating, setIsUpdating] = useState<boolean>(false);
|
||||
@@ -122,10 +104,7 @@ export default function CaseEditor({
|
||||
handleFetchCreateAttachments(caseId, event.target.files);
|
||||
};
|
||||
|
||||
const handleFetchCreateAttachments = async (
|
||||
caseId: number,
|
||||
files: File[]
|
||||
) => {
|
||||
const handleFetchCreateAttachments = async (caseId: number, files: File[]) => {
|
||||
const newAttachments = await fetchCreateAttachments(caseId, files);
|
||||
|
||||
if (newAttachments) {
|
||||
@@ -147,9 +126,7 @@ export default function CaseEditor({
|
||||
const onAttachmentDelete = async (attachmentId: number) => {
|
||||
await fetchDeleteAttachment(attachmentId);
|
||||
|
||||
const filteredAttachments = testCase.Attachments.filter(
|
||||
(attachment) => attachment.id !== attachmentId
|
||||
);
|
||||
const filteredAttachments = testCase.Attachments.filter((attachment) => attachment.id !== attachmentId);
|
||||
|
||||
setTestCase({
|
||||
...testCase,
|
||||
@@ -163,7 +140,7 @@ export default function CaseEditor({
|
||||
const data = await fetchCase(caseId);
|
||||
setTestCase(data);
|
||||
} catch (error: any) {
|
||||
console.error("Error in effect:", error.message);
|
||||
console.error('Error in effect:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,12 +156,7 @@ export default function CaseEditor({
|
||||
isIconOnly
|
||||
size="sm"
|
||||
className="rounded-full bg-neutral-50 dark:bg-neutral-600"
|
||||
onPress={() =>
|
||||
router.push(
|
||||
`/projects/${projectId}/folders/${folderId}/cases`,
|
||||
{ locale: locale }
|
||||
)
|
||||
}
|
||||
onPress={() => router.push(`/projects/${projectId}/folders/${folderId}/cases`, { locale: locale })}
|
||||
>
|
||||
<ArrowLeft size={16} />
|
||||
</Button>
|
||||
@@ -215,7 +187,7 @@ export default function CaseEditor({
|
||||
label={messages.title}
|
||||
value={testCase.title}
|
||||
isInvalid={isTitleInvalid}
|
||||
errorMessage={isTitleInvalid ? messages.pleaseEnterTitle : ""}
|
||||
errorMessage={isTitleInvalid ? messages.pleaseEnterTitle : ''}
|
||||
onChange={(e) => {
|
||||
setTestCase({ ...testCase, title: e.target.value });
|
||||
}}
|
||||
@@ -241,17 +213,11 @@ export default function CaseEditor({
|
||||
selectedKeys={[priorities[testCase.priority].uid]}
|
||||
onSelectionChange={(e) => {
|
||||
const selectedUid = e.anchorKey;
|
||||
const index = priorities.findIndex(
|
||||
(priority) => priority.uid === selectedUid
|
||||
);
|
||||
const index = priorities.findIndex((priority) => priority.uid === selectedUid);
|
||||
setTestCase({ ...testCase, priority: index });
|
||||
}}
|
||||
startContent={
|
||||
<Circle
|
||||
size={8}
|
||||
color={priorities[testCase.priority].color}
|
||||
fill={priorities[testCase.priority].color}
|
||||
/>
|
||||
<Circle size={8} color={priorities[testCase.priority].color} fill={priorities[testCase.priority].color} />
|
||||
}
|
||||
label={messages.priority}
|
||||
className="mt-3 max-w-xs"
|
||||
@@ -271,9 +237,7 @@ export default function CaseEditor({
|
||||
selectedKeys={[testTypes[testCase.type].uid]}
|
||||
onSelectionChange={(e) => {
|
||||
const selectedUid = e.anchorKey;
|
||||
const index = testTypes.findIndex(
|
||||
(type) => type.uid === selectedUid
|
||||
);
|
||||
const index = testTypes.findIndex((type) => type.uid === selectedUid);
|
||||
setTestCase({ ...testCase, type: index });
|
||||
}}
|
||||
label={messages.type}
|
||||
@@ -294,9 +258,7 @@ export default function CaseEditor({
|
||||
selectedKeys={[templates[testCase.template].uid]}
|
||||
onSelectionChange={(e) => {
|
||||
const selectedUid = e.anchorKey;
|
||||
const index = templates.findIndex(
|
||||
(template) => template.uid === selectedUid
|
||||
);
|
||||
const index = templates.findIndex((template) => template.uid === selectedUid);
|
||||
setTestCase({ ...testCase, template: index });
|
||||
}}
|
||||
label={messages.template}
|
||||
@@ -311,7 +273,7 @@ export default function CaseEditor({
|
||||
</div>
|
||||
|
||||
<Divider className="my-6" />
|
||||
{templates[testCase.template].uid === "text" ? (
|
||||
{templates[testCase.template].uid === 'text' ? (
|
||||
<div>
|
||||
<h6 className="font-bold">{messages.testDetail}</h6>
|
||||
<div className="flex">
|
||||
@@ -377,10 +339,9 @@ export default function CaseEditor({
|
||||
<h6 className="font-bold">{messages.attachments}</h6>
|
||||
<CaseAttachmentsEditor
|
||||
attachments={testCase.Attachments}
|
||||
onAttachmentDownload={(
|
||||
attachmentId: number,
|
||||
downloadFileName: string
|
||||
) => fetchDownloadAttachment(attachmentId, downloadFileName)}
|
||||
onAttachmentDownload={(attachmentId: number, downloadFileName: string) =>
|
||||
fetchDownloadAttachment(attachmentId, downloadFileName)
|
||||
}
|
||||
onAttachmentDelete={onAttachmentDelete}
|
||||
messages={messages}
|
||||
/>
|
||||
@@ -399,17 +360,9 @@ export default function CaseEditor({
|
||||
<span className="font-semibold">{messages.clickToUpload}</span>
|
||||
<span>{messages.orDragAndDrop}</span>
|
||||
</p>
|
||||
<p className="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
{messages.maxFileSize}: 50 MB
|
||||
</p>
|
||||
<p className="text-xs text-neutral-500 dark:text-neutral-400">{messages.maxFileSize}: 50 MB</p>
|
||||
</div>
|
||||
<input
|
||||
id="dropzone-file"
|
||||
type="file"
|
||||
className="hidden"
|
||||
onChange={handleInput}
|
||||
multiple
|
||||
/>
|
||||
<input id="dropzone-file" type="file" className="hidden" onChange={handleInput} multiple />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Textarea, Button, Tooltip } from "@nextui-org/react";
|
||||
import { CaseMessages, StepType } from "@/types/case";
|
||||
import { Plus, Trash } from "lucide-react";
|
||||
import { Textarea, Button, Tooltip } from '@nextui-org/react';
|
||||
import { CaseMessages, StepType } from '@/types/case';
|
||||
import { Plus, Trash } from 'lucide-react';
|
||||
|
||||
type Props = {
|
||||
steps: StepType[];
|
||||
@@ -10,13 +10,7 @@ type Props = {
|
||||
messages: CaseMessages;
|
||||
};
|
||||
|
||||
export default function StepsEditor({
|
||||
steps,
|
||||
onStepUpdate,
|
||||
onStepPlus,
|
||||
onStepDelete,
|
||||
messages,
|
||||
}: Props) {
|
||||
export default function StepsEditor({ steps, onStepUpdate, onStepPlus, onStepDelete, messages }: Props) {
|
||||
// sort steps by junction table's column
|
||||
const sortedSteps = steps.slice().sort((a, b) => {
|
||||
const stepNoA = a.caseSteps.stepNo;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import CaseEditor from "./CaseEditor";
|
||||
import { useTranslations } from "next-intl";
|
||||
import CaseEditor from './CaseEditor';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function Page({
|
||||
params,
|
||||
@@ -11,53 +11,53 @@ export default function Page({
|
||||
locale: string;
|
||||
};
|
||||
}) {
|
||||
const t = useTranslations("Case");
|
||||
const t = useTranslations('Case');
|
||||
const messages = {
|
||||
backToCases: t("back_to_cases"),
|
||||
updating: t("updating"),
|
||||
update: t("update"),
|
||||
basic: t("basic"),
|
||||
title: t("title"),
|
||||
pleaseEnterTitle: t("please_enter_title"),
|
||||
description: t("description"),
|
||||
testCaseDescription: t("test_case_description"),
|
||||
priority: t("priority"),
|
||||
critical: t("critical"),
|
||||
high: t("high"),
|
||||
medium: t("medium"),
|
||||
low: t("low"),
|
||||
type: t("type"),
|
||||
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"),
|
||||
template: t("template"),
|
||||
testDetail: t("test_detail"),
|
||||
preconditions: t("preconditions"),
|
||||
step: t("step"),
|
||||
text: t("text"),
|
||||
steps: t("steps"),
|
||||
newStep: t("new_step"),
|
||||
detailsOfTheStep: t("details_of_the_step"),
|
||||
expectedResult: t("expected_result"),
|
||||
deleteThisStep: t("delete_this_step"),
|
||||
insertStep: t("insert_step"),
|
||||
attachments: t("attachments"),
|
||||
delete: t("delete"),
|
||||
download: t("download"),
|
||||
deleteFile: t("delete_file"),
|
||||
clickToUpload: t("click_to_upload"),
|
||||
orDragAndDrop: t("or_drag_and_drop"),
|
||||
maxFileSize: t("max_file_size"),
|
||||
backToCases: t('back_to_cases'),
|
||||
updating: t('updating'),
|
||||
update: t('update'),
|
||||
basic: t('basic'),
|
||||
title: t('title'),
|
||||
pleaseEnterTitle: t('please_enter_title'),
|
||||
description: t('description'),
|
||||
testCaseDescription: t('test_case_description'),
|
||||
priority: t('priority'),
|
||||
critical: t('critical'),
|
||||
high: t('high'),
|
||||
medium: t('medium'),
|
||||
low: t('low'),
|
||||
type: t('type'),
|
||||
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'),
|
||||
template: t('template'),
|
||||
testDetail: t('test_detail'),
|
||||
preconditions: t('preconditions'),
|
||||
step: t('step'),
|
||||
text: t('text'),
|
||||
steps: t('steps'),
|
||||
newStep: t('new_step'),
|
||||
detailsOfTheStep: t('details_of_the_step'),
|
||||
expectedResult: t('expected_result'),
|
||||
deleteThisStep: t('delete_this_step'),
|
||||
insertStep: t('insert_step'),
|
||||
attachments: t('attachments'),
|
||||
delete: t('delete'),
|
||||
download: t('download'),
|
||||
deleteFile: t('delete_file'),
|
||||
clickToUpload: t('click_to_upload'),
|
||||
orDragAndDrop: t('or_drag_and_drop'),
|
||||
maxFileSize: t('max_file_size'),
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,37 +1,28 @@
|
||||
import CasesPane from "./CasesPane";
|
||||
import { useTranslations } from "next-intl";
|
||||
import CasesPane from './CasesPane';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function Page({
|
||||
params,
|
||||
}: {
|
||||
params: { projectId: string; folderId: string; locale: string };
|
||||
}) {
|
||||
const t = useTranslations("Cases");
|
||||
export default function Page({ params }: { params: { projectId: string; folderId: string; locale: string } }) {
|
||||
const t = useTranslations('Cases');
|
||||
const messages = {
|
||||
testCaseList: t("test_case_list"),
|
||||
id: t("id"),
|
||||
title: t("title"),
|
||||
priority: t("priority"),
|
||||
actions: t("actions"),
|
||||
deleteCase: t("delete_case"),
|
||||
delete: t("delete"),
|
||||
newTestCase: t("new_test_case"),
|
||||
status: t("status"),
|
||||
critical: t("critical"),
|
||||
high: t("high"),
|
||||
medium: t("medium"),
|
||||
low: t("low"),
|
||||
noCasesFound: t("no_cases_found"),
|
||||
testCaseList: t('test_case_list'),
|
||||
id: t('id'),
|
||||
title: t('title'),
|
||||
priority: t('priority'),
|
||||
actions: t('actions'),
|
||||
deleteCase: t('delete_case'),
|
||||
delete: t('delete'),
|
||||
newTestCase: t('new_test_case'),
|
||||
status: t('status'),
|
||||
critical: t('critical'),
|
||||
high: t('high'),
|
||||
medium: t('medium'),
|
||||
low: t('low'),
|
||||
noCasesFound: t('no_cases_found'),
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CasesPane
|
||||
projectId={params.projectId}
|
||||
folderId={params.folderId}
|
||||
locale={params.locale}
|
||||
messages={messages}
|
||||
/>
|
||||
<CasesPane projectId={params.projectId} folderId={params.folderId} locale={params.locale} messages={messages} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import FoldersPane from "./FoldersPane";
|
||||
import { useTranslations } from "next-intl";
|
||||
import FoldersPane from './FoldersPane';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function FoldersLayout({
|
||||
children,
|
||||
@@ -8,27 +8,23 @@ export default function FoldersLayout({
|
||||
children: React.ReactNode;
|
||||
params: { projectId: string; locale: string };
|
||||
}) {
|
||||
const t = useTranslations("Folders");
|
||||
const t = useTranslations('Folders');
|
||||
const messages = {
|
||||
folder: t("folder"),
|
||||
newFolder: t("new_folder"),
|
||||
editFolder: t("edit_folder"),
|
||||
deleteFolder: t("delete_folder"),
|
||||
folderName: t("folder_name"),
|
||||
folderDetail: t("folder_detail"),
|
||||
close: t("close"),
|
||||
create: t("create"),
|
||||
update: t("update"),
|
||||
pleaseEnter: t("please_enter"),
|
||||
folder: t('folder'),
|
||||
newFolder: t('new_folder'),
|
||||
editFolder: t('edit_folder'),
|
||||
deleteFolder: t('delete_folder'),
|
||||
folderName: t('folder_name'),
|
||||
folderDetail: t('folder_detail'),
|
||||
close: t('close'),
|
||||
create: t('create'),
|
||||
update: t('update'),
|
||||
pleaseEnter: t('please_enter'),
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full">
|
||||
<FoldersPane
|
||||
projectId={params.projectId}
|
||||
messages={messages}
|
||||
locale={params.locale}
|
||||
/>
|
||||
<FoldersPane projectId={params.projectId} messages={messages} locale={params.locale} />
|
||||
<div className="flex-grow w-full">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
This is folders page.
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <>This is folders page.</>;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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 });
|
||||
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[];
|
||||
@@ -12,11 +12,7 @@ type Props = {
|
||||
theme: string;
|
||||
};
|
||||
|
||||
export default function TestPriorityDonutChart({
|
||||
priorityCounts,
|
||||
messages,
|
||||
theme,
|
||||
}: Props) {
|
||||
export default function TestPriorityDonutChart({ priorityCounts, messages, theme }: Props) {
|
||||
const [chartData, setChartData] = useState({
|
||||
series: [],
|
||||
options: {
|
||||
@@ -38,10 +34,10 @@ export default function TestPriorityDonutChart({
|
||||
const legend = {
|
||||
labels: {
|
||||
colors: priorities.map((entry) => {
|
||||
if (theme === "light") {
|
||||
return "black";
|
||||
if (theme === 'light') {
|
||||
return 'black';
|
||||
} else {
|
||||
return "white";
|
||||
return 'white';
|
||||
}
|
||||
}),
|
||||
},
|
||||
@@ -57,13 +53,5 @@ export default function TestPriorityDonutChart({
|
||||
updateChartDate();
|
||||
}, [priorityCounts, theme]);
|
||||
|
||||
return (
|
||||
<Chart
|
||||
options={chartData.options}
|
||||
series={chartData.series}
|
||||
type="donut"
|
||||
width={"100%"}
|
||||
height={"100%"}
|
||||
/>
|
||||
);
|
||||
return <Chart options={chartData.options} series={chartData.series} type="donut" width={'100%'} height={'100%'} />;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { ProgressSeriesType } from "@/types/run";
|
||||
import { testRunCaseStatus } from "@/config/selection";
|
||||
const Chart = dynamic(() => import("react-apexcharts"), { ssr: false });
|
||||
import React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { ProgressSeriesType } from '@/types/run';
|
||||
import { testRunCaseStatus } from '@/config/selection';
|
||||
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
|
||||
|
||||
type Props = {
|
||||
progressSeries: ProgressSeriesType[];
|
||||
@@ -11,11 +11,7 @@ type Props = {
|
||||
theme: string;
|
||||
};
|
||||
|
||||
export default function TestProgressBarChart({
|
||||
progressSeries,
|
||||
progressCategories,
|
||||
theme,
|
||||
}: Props) {
|
||||
export default function TestProgressBarChart({ progressSeries, progressCategories, theme }: Props) {
|
||||
const [chartData, setChartData] = useState({
|
||||
series: [],
|
||||
options: {
|
||||
@@ -28,14 +24,14 @@ export default function TestProgressBarChart({
|
||||
const updateChartDate = () => {
|
||||
if (progressSeries) {
|
||||
const legendsLabelColors = testRunCaseStatus.map((itr) => {
|
||||
if (theme === "light") {
|
||||
return "black";
|
||||
if (theme === 'light') {
|
||||
return 'black';
|
||||
} else {
|
||||
return "white";
|
||||
return 'white';
|
||||
}
|
||||
});
|
||||
|
||||
const xaxisLabelColor = theme === "light" ? "black" : "white";
|
||||
const xaxisLabelColor = theme === 'light' ? 'black' : 'white';
|
||||
|
||||
setChartData({
|
||||
series: progressSeries,
|
||||
@@ -47,7 +43,7 @@ export default function TestProgressBarChart({
|
||||
stacked: true,
|
||||
},
|
||||
legend: {
|
||||
position: "right",
|
||||
position: 'right',
|
||||
labels: {
|
||||
colors: legendsLabelColors,
|
||||
},
|
||||
@@ -56,7 +52,7 @@ export default function TestProgressBarChart({
|
||||
return itr.chartColor;
|
||||
}),
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
type: 'datetime',
|
||||
categories: progressCategories,
|
||||
labels: {
|
||||
style: {
|
||||
@@ -75,13 +71,5 @@ export default function TestProgressBarChart({
|
||||
updateChartDate();
|
||||
}, [progressSeries, progressCategories, theme]);
|
||||
|
||||
return (
|
||||
<Chart
|
||||
options={chartData.options}
|
||||
series={chartData.series}
|
||||
type="bar"
|
||||
width={"100%"}
|
||||
height={"100%"}
|
||||
/>
|
||||
);
|
||||
return <Chart options={chartData.options} series={chartData.series} type="bar" width={'100%'} height={'100%'} />;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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 });
|
||||
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[];
|
||||
@@ -12,11 +12,7 @@ type Props = {
|
||||
theme: string;
|
||||
};
|
||||
|
||||
export default function TestTypesDonutChart({
|
||||
typesCounts,
|
||||
messages,
|
||||
theme,
|
||||
}: Props) {
|
||||
export default function TestTypesDonutChart({ typesCounts, messages, theme }: Props) {
|
||||
const [chartData, setChartData] = useState({
|
||||
series: [],
|
||||
options: {
|
||||
@@ -38,10 +34,10 @@ export default function TestTypesDonutChart({
|
||||
const legend = {
|
||||
labels: {
|
||||
colors: testTypes.map((entry) => {
|
||||
if (theme === "light") {
|
||||
return "black";
|
||||
if (theme === 'light') {
|
||||
return 'black';
|
||||
} else {
|
||||
return "white";
|
||||
return 'white';
|
||||
}
|
||||
}),
|
||||
},
|
||||
@@ -57,13 +53,5 @@ export default function TestTypesDonutChart({
|
||||
updateChartDate();
|
||||
}, [typesCounts, theme]);
|
||||
|
||||
return (
|
||||
<Chart
|
||||
options={chartData.options}
|
||||
series={chartData.series}
|
||||
type="donut"
|
||||
width={"100%"}
|
||||
height={"100%"}
|
||||
/>
|
||||
);
|
||||
return <Chart options={chartData.options} series={chartData.series} type="donut" width={'100%'} height={'100%'} />;
|
||||
}
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Divider } from "@nextui-org/react";
|
||||
import { title, subtitle } from "@/components/primitives";
|
||||
import { Card, CardBody, Chip } from "@nextui-org/react";
|
||||
import { Folder, Clipboard, FlaskConical } from "lucide-react";
|
||||
import { CaseTypeCountType, CasePriorityCountType } from "@/types/case";
|
||||
import { ProgressSeriesType } from "@/types/run";
|
||||
import { HomeMessages } from "./page";
|
||||
import {
|
||||
aggregateBasicInfo,
|
||||
aggregateTestPriority,
|
||||
aggregateTestType,
|
||||
aggregateProgress,
|
||||
} from "./aggregate";
|
||||
import TestTypesChart from "./TestTypesDonutChart";
|
||||
import TestPriorityChart from "./TestPriorityDonutChart";
|
||||
import TestProgressBarChart from "./TestProgressColumnChart";
|
||||
import Config from "@/config/config";
|
||||
import { useTheme } from "next-themes";
|
||||
'use client';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Divider } from '@nextui-org/react';
|
||||
import { title, subtitle } from '@/components/primitives';
|
||||
import { Card, CardBody, Chip } from '@nextui-org/react';
|
||||
import { Folder, Clipboard, FlaskConical } from 'lucide-react';
|
||||
import { CaseTypeCountType, CasePriorityCountType } from '@/types/case';
|
||||
import { ProgressSeriesType } from '@/types/run';
|
||||
import { HomeMessages } from './page';
|
||||
import { aggregateBasicInfo, aggregateTestPriority, aggregateTestType, aggregateProgress } from './aggregate';
|
||||
import TestTypesChart from './TestTypesDonutChart';
|
||||
import TestPriorityChart from './TestPriorityDonutChart';
|
||||
import TestProgressBarChart from './TestProgressColumnChart';
|
||||
import Config from '@/config/config';
|
||||
import { useTheme } from 'next-themes';
|
||||
const apiServer = Config.apiServer;
|
||||
|
||||
async function fetchProject(url) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -36,7 +31,7 @@ async function fetchProject(url) {
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
console.error("Error fetching data:", error.message);
|
||||
console.error('Error fetching data:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +43,8 @@ type Props = {
|
||||
export function Home({ projectId, messages }: Props) {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const [project, setProject] = useState({
|
||||
name: "",
|
||||
detail: "",
|
||||
name: '',
|
||||
detail: '',
|
||||
Folders: [{ Cases: [] }],
|
||||
Runs: [{ RunCases: [] }],
|
||||
});
|
||||
@@ -57,8 +52,7 @@ export function Home({ projectId, messages }: Props) {
|
||||
const [caseNum, setCaseNum] = useState(0);
|
||||
const [runNum, setRunNum] = useState(0);
|
||||
const [typesCounts, setTypesCounts] = useState<CaseTypeCountType[]>();
|
||||
const [priorityCounts, setPriorityCounts] =
|
||||
useState<CasePriorityCountType[]>();
|
||||
const [priorityCounts, setPriorityCounts] = useState<CasePriorityCountType[]>();
|
||||
const [progressCategories, setProgressCategories] = useState<string[]>();
|
||||
const [progressSeries, setProgressSeries] = useState<ProgressSeriesType[]>();
|
||||
const url = `${apiServer}/home/${projectId}`;
|
||||
@@ -69,7 +63,7 @@ export function Home({ projectId, messages }: Props) {
|
||||
const data = await fetchProject(url);
|
||||
setProject(data);
|
||||
} catch (error: any) {
|
||||
console.error("Error in effect:", error.message);
|
||||
console.error('Error in effect:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,68 +93,41 @@ export function Home({ projectId, messages }: Props) {
|
||||
|
||||
return (
|
||||
<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>
|
||||
<div className="mt-4">
|
||||
<Chip
|
||||
variant="flat"
|
||||
startContent={<Folder size={16} />}
|
||||
className="px-3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<Chip variant="flat" startContent={<FlaskConical size={16} />} className="px-3 ms-2">
|
||||
{runNum} {messages.testRuns}
|
||||
</Chip>
|
||||
</div>
|
||||
|
||||
{project.detail && (
|
||||
<Card
|
||||
className="mt-3 bg-neutral-100 dark:bg-neutral-700 dark:text-white"
|
||||
shadow="none"
|
||||
>
|
||||
<Card className="mt-3 bg-neutral-100 dark:bg-neutral-700 dark:text-white" shadow="none">
|
||||
<CardBody>{project.detail}</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Divider className="my-8" />
|
||||
<h2 className={subtitle()}>{messages.progress}</h2>
|
||||
<div style={{ height: "18rem" }}>
|
||||
<TestProgressBarChart
|
||||
progressSeries={progressSeries}
|
||||
progressCategories={progressCategories}
|
||||
theme={theme}
|
||||
/>
|
||||
<div style={{ height: '18rem' }}>
|
||||
<TestProgressBarChart progressSeries={progressSeries} progressCategories={progressCategories} theme={theme} />
|
||||
</div>
|
||||
|
||||
<Divider className="my-12" />
|
||||
<h2 className={subtitle()}>{messages.testClassification}</h2>
|
||||
<div className="flex pb-20">
|
||||
<div style={{ width: "32rem", height: "18rem" }}>
|
||||
<div style={{ width: '32rem', height: '18rem' }}>
|
||||
<h3>{messages.byType}</h3>
|
||||
<TestTypesChart
|
||||
typesCounts={typesCounts}
|
||||
messages={messages}
|
||||
theme={theme}
|
||||
/>
|
||||
<TestTypesChart typesCounts={typesCounts} messages={messages} theme={theme} />
|
||||
</div>
|
||||
<div style={{ width: "30rem", height: "18rem" }}>
|
||||
<div style={{ width: '30rem', height: '18rem' }}>
|
||||
<h3>{messages.byPriority}</h3>
|
||||
<TestPriorityChart
|
||||
priorityCounts={priorityCounts}
|
||||
messages={messages}
|
||||
theme={theme}
|
||||
/>
|
||||
<TestPriorityChart priorityCounts={priorityCounts} messages={messages} theme={theme} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Home } from "./home";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Home } from './home';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export type HomeMessages = {
|
||||
folders: string;
|
||||
@@ -35,37 +35,37 @@ export type HomeMessages = {
|
||||
};
|
||||
|
||||
export default function Page({ params }: { params: { projectId: string } }) {
|
||||
const t = useTranslations("Home");
|
||||
const t = useTranslations('Home');
|
||||
const messages = {
|
||||
folders: t("Folders"),
|
||||
testCases: t("test_cases"),
|
||||
testRuns: t("test_runs"),
|
||||
progress: t("progress"),
|
||||
untested: t("untested"),
|
||||
passed: t("passed"),
|
||||
failed: t("failed"),
|
||||
retest: t("retest"),
|
||||
skipped: t("skipped"),
|
||||
testClassification: t("test_classification"),
|
||||
byType: t("by_type"),
|
||||
byPriority: t("by_priority"),
|
||||
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"),
|
||||
critical: t("critical"),
|
||||
high: t("high"),
|
||||
medium: t("medium"),
|
||||
low: t("low"),
|
||||
folders: t('Folders'),
|
||||
testCases: t('test_cases'),
|
||||
testRuns: t('test_runs'),
|
||||
progress: t('progress'),
|
||||
untested: t('untested'),
|
||||
passed: t('passed'),
|
||||
failed: t('failed'),
|
||||
retest: t('retest'),
|
||||
skipped: t('skipped'),
|
||||
testClassification: t('test_classification'),
|
||||
byType: t('by_type'),
|
||||
byPriority: t('by_priority'),
|
||||
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'),
|
||||
critical: t('critical'),
|
||||
high: t('high'),
|
||||
medium: t('medium'),
|
||||
low: t('low'),
|
||||
};
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Sidebar from "./Sidebar";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Sidebar from './Sidebar';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function SidebarLayout({
|
||||
children,
|
||||
@@ -8,11 +8,11 @@ export default function SidebarLayout({
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
const t = useTranslations("Project");
|
||||
const t = useTranslations('Project');
|
||||
const messages = {
|
||||
home: t("home"),
|
||||
testCases: t("test_cases"),
|
||||
testRuns: t("test_runs"),
|
||||
home: t('home'),
|
||||
testCases: t('test_cases'),
|
||||
testRuns: t('test_runs'),
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "@nextui-org/react";
|
||||
import { Plus } from "lucide-react";
|
||||
import RunsTable from "./RunsTable";
|
||||
import { fetchRuns, createRun, deleteRun } from "./runsControl";
|
||||
import { RunsMessages } from "@/types/run";
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Button } from '@nextui-org/react';
|
||||
import { Plus } from 'lucide-react';
|
||||
import RunsTable from './RunsTable';
|
||||
import { fetchRuns, createRun, deleteRun } from './runsControl';
|
||||
import { RunsMessages } from '@/types/run';
|
||||
|
||||
type Props = {
|
||||
projectId: string;
|
||||
@@ -21,7 +21,7 @@ export default function RunsPage({ projectId, locale, messages }: Props) {
|
||||
const data = await fetchRuns(projectId);
|
||||
setRuns(data);
|
||||
} catch (error: any) {
|
||||
console.error("Error in effect:", error.message);
|
||||
console.error('Error in effect:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function RunsPage({ projectId, locale, messages }: Props) {
|
||||
updateRuns.push(newRun);
|
||||
setRuns(updateRuns);
|
||||
} catch (error: any) {
|
||||
console.error("Error deleting run:", error);
|
||||
console.error('Error deleting run:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function RunsPage({ projectId, locale, messages }: Props) {
|
||||
const data = await fetchRuns(projectId);
|
||||
setRuns(data);
|
||||
} catch (error: any) {
|
||||
console.error("Error deleting run:", error);
|
||||
console.error('Error deleting run:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,24 +54,13 @@ export default function RunsPage({ projectId, locale, messages }: Props) {
|
||||
<div className="w-full p-3 flex items-center justify-between">
|
||||
<h3 className="font-bold">{messages.runList}</h3>
|
||||
<div>
|
||||
<Button
|
||||
startContent={<Plus size={16} />}
|
||||
size="sm"
|
||||
color="primary"
|
||||
onClick={onCreateClick}
|
||||
>
|
||||
<Button startContent={<Plus size={16} />} size="sm" color="primary" onClick={onCreateClick}>
|
||||
{messages.newRun}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RunsTable
|
||||
projectId={projectId}
|
||||
runs={runs}
|
||||
onDeleteRun={onDeleteClick}
|
||||
messages={messages}
|
||||
locale={locale}
|
||||
/>
|
||||
<RunsTable projectId={projectId} runs={runs} onDeleteRun={onDeleteClick} messages={messages} locale={locale} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo, useCallback } from "react";
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
@@ -12,11 +12,11 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownItem,
|
||||
SortDescriptor,
|
||||
} from "@nextui-org/react";
|
||||
import { Link, NextUiLinkClasses } from "@/src/navigation";
|
||||
import { MoreVertical } from "lucide-react";
|
||||
import { RunsMessages, RunType } from "@/types/run";
|
||||
import dayjs from "dayjs";
|
||||
} from '@nextui-org/react';
|
||||
import { Link, NextUiLinkClasses } from '@/src/navigation';
|
||||
import { MoreVertical } from 'lucide-react';
|
||||
import { RunsMessages, RunType } from '@/types/run';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
type Props = {
|
||||
projectId: string;
|
||||
@@ -26,24 +26,18 @@ type Props = {
|
||||
locale: string;
|
||||
};
|
||||
|
||||
export default function RunsTable({
|
||||
projectId,
|
||||
runs,
|
||||
onDeleteRun,
|
||||
messages,
|
||||
locale,
|
||||
}: Props) {
|
||||
export default function RunsTable({ projectId, runs, onDeleteRun, messages, locale }: Props) {
|
||||
const headerColumns = [
|
||||
{ name: messages.id, uid: "id", sortable: true },
|
||||
{ name: messages.name, uid: "name", sortable: true },
|
||||
{ name: messages.description, uid: "description", sortable: true },
|
||||
{ name: messages.lastUpdate, uid: "updatedAt", sortable: true },
|
||||
{ name: messages.actions, uid: "actions" },
|
||||
{ name: messages.id, uid: 'id', sortable: true },
|
||||
{ name: messages.name, uid: 'name', sortable: true },
|
||||
{ name: messages.description, uid: 'description', sortable: true },
|
||||
{ name: messages.lastUpdate, uid: 'updatedAt', sortable: true },
|
||||
{ name: messages.actions, uid: 'actions' },
|
||||
];
|
||||
|
||||
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
|
||||
column: "id",
|
||||
direction: "ascending",
|
||||
column: 'id',
|
||||
direction: 'ascending',
|
||||
});
|
||||
|
||||
const sortedItems = useMemo(() => {
|
||||
@@ -52,31 +46,27 @@ export default function RunsTable({
|
||||
const second = b[sortDescriptor.column as keyof RunType] as number;
|
||||
const cmp = first < second ? -1 : first > second ? 1 : 0;
|
||||
|
||||
return sortDescriptor.direction === "descending" ? -cmp : cmp;
|
||||
return sortDescriptor.direction === 'descending' ? -cmp : cmp;
|
||||
});
|
||||
}, [sortDescriptor, runs]);
|
||||
|
||||
const truncateText = (text: string, maxLength: number) => {
|
||||
return text.length > maxLength ? text.slice(0, maxLength) + "..." : text;
|
||||
return text.length > maxLength ? text.slice(0, maxLength) + '...' : text;
|
||||
};
|
||||
|
||||
const renderCell = useCallback((run: RunType, columnKey: Key) => {
|
||||
const cellValue = run[columnKey as keyof RunType];
|
||||
|
||||
switch (columnKey) {
|
||||
case "id":
|
||||
case 'id':
|
||||
return <span>{cellValue}</span>;
|
||||
case "name":
|
||||
case 'name':
|
||||
return (
|
||||
<Link
|
||||
href={`/projects/${projectId}/runs/${run.id}`}
|
||||
locale={locale}
|
||||
className={NextUiLinkClasses}
|
||||
>
|
||||
<Link href={`/projects/${projectId}/runs/${run.id}`} locale={locale} className={NextUiLinkClasses}>
|
||||
{cellValue}
|
||||
</Link>
|
||||
);
|
||||
case "detail":
|
||||
case 'detail':
|
||||
const maxLength = 20;
|
||||
const truncatedValue = truncateText(cellValue, maxLength);
|
||||
return (
|
||||
@@ -84,9 +74,9 @@ export default function RunsTable({
|
||||
<div>{truncatedValue}</div>
|
||||
</div>
|
||||
);
|
||||
case "updatedAt":
|
||||
return <span>{dayjs(cellValue).format("YYYY/MM/DD HH:mm")}</span>;
|
||||
case "actions":
|
||||
case 'updatedAt':
|
||||
return <span>{dayjs(cellValue).format('YYYY/MM/DD HH:mm')}</span>;
|
||||
case 'actions':
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
@@ -95,10 +85,7 @@ export default function RunsTable({
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label="run actions">
|
||||
<DropdownItem
|
||||
className="text-danger"
|
||||
onClick={() => onDeleteRun(run.id)}
|
||||
>
|
||||
<DropdownItem className="text-danger" onClick={() => onDeleteRun(run.id)}>
|
||||
{messages.deleteRun}
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
@@ -111,18 +98,18 @@ export default function RunsTable({
|
||||
|
||||
const classNames = useMemo(
|
||||
() => ({
|
||||
wrapper: ["max-w-3xl"],
|
||||
th: ["bg-transparent", "text-default-500", "border-b", "border-divider"],
|
||||
wrapper: ['max-w-3xl'],
|
||||
th: ['bg-transparent', 'text-default-500', 'border-b', 'border-divider'],
|
||||
td: [
|
||||
// changing the rows border radius
|
||||
// first
|
||||
"group-data-[first=true]:first:before:rounded-none",
|
||||
"group-data-[first=true]:last:before:rounded-none",
|
||||
'group-data-[first=true]:first:before:rounded-none',
|
||||
'group-data-[first=true]:last:before:rounded-none',
|
||||
// middle
|
||||
"group-data-[middle=true]:before:rounded-none",
|
||||
'group-data-[middle=true]:before:rounded-none',
|
||||
// last
|
||||
"group-data-[last=true]:first:before:rounded-none",
|
||||
"group-data-[last=true]:last:before:rounded-none",
|
||||
'group-data-[last=true]:first:before:rounded-none',
|
||||
'group-data-[last=true]:last:before:rounded-none',
|
||||
],
|
||||
}),
|
||||
[]
|
||||
@@ -141,7 +128,7 @@ export default function RunsTable({
|
||||
{(column) => (
|
||||
<TableColumn
|
||||
key={column.uid}
|
||||
align={column.uid === "actions" ? "center" : "start"}
|
||||
align={column.uid === 'actions' ? 'center' : 'start'}
|
||||
allowsSorting={column.sortable}
|
||||
>
|
||||
{column.name}
|
||||
@@ -150,11 +137,7 @@ export default function RunsTable({
|
||||
</TableHeader>
|
||||
<TableBody emptyContent={messages.noRunsFound} items={sortedItems}>
|
||||
{(item) => (
|
||||
<TableRow key={item.id}>
|
||||
{(columnKey) => (
|
||||
<TableCell>{renderCell(item, columnKey)}</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
<TableRow key={item.id}>{(columnKey) => <TableCell>{renderCell(item, columnKey)}</TableCell>}</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "@/src/navigation";
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRouter } from '@/src/navigation';
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
@@ -17,28 +17,14 @@ import {
|
||||
Dropdown,
|
||||
DropdownMenu,
|
||||
DropdownItem,
|
||||
} from "@nextui-org/react";
|
||||
import {
|
||||
Save,
|
||||
ArrowLeft,
|
||||
Folder,
|
||||
ChevronDown,
|
||||
CopyPlus,
|
||||
CopyMinus,
|
||||
RotateCw,
|
||||
} from "lucide-react";
|
||||
import RunProgressChart from "./RunPregressDonutChart";
|
||||
import TestCaseSelector from "./TestCaseSelector";
|
||||
import { testRunStatus } from "@/config/selection";
|
||||
import {
|
||||
RunType,
|
||||
RunCaseType,
|
||||
RunCaseInfoType,
|
||||
RunStatusCountType,
|
||||
RunMessages,
|
||||
} from "@/types/run";
|
||||
import { CaseType } from "@/types/case";
|
||||
import { FolderType } from "@/types/folder";
|
||||
} from '@nextui-org/react';
|
||||
import { Save, ArrowLeft, Folder, ChevronDown, CopyPlus, CopyMinus, RotateCw } from 'lucide-react';
|
||||
import RunProgressChart from './RunPregressDonutChart';
|
||||
import TestCaseSelector from './TestCaseSelector';
|
||||
import { testRunStatus } from '@/config/selection';
|
||||
import { RunType, RunCaseType, RunCaseInfoType, RunStatusCountType, RunMessages } from '@/types/run';
|
||||
import { CaseType } from '@/types/case';
|
||||
import { FolderType } from '@/types/folder';
|
||||
import {
|
||||
fetchRun,
|
||||
updateRun,
|
||||
@@ -48,16 +34,16 @@ import {
|
||||
bulkCreateRunCases,
|
||||
deleteRunCase,
|
||||
bulkDeleteRunCases,
|
||||
} from "../runsControl";
|
||||
import { fetchFolders } from "../../folders/foldersControl";
|
||||
import { fetchCases } from "../../folders/[folderId]/cases/caseControl";
|
||||
import { useTheme } from "next-themes";
|
||||
} from '../runsControl';
|
||||
import { fetchFolders } from '../../folders/foldersControl';
|
||||
import { fetchCases } from '../../folders/[folderId]/cases/caseControl';
|
||||
import { useTheme } from 'next-themes';
|
||||
|
||||
const defaultTestRun = {
|
||||
id: 0,
|
||||
name: "",
|
||||
name: '',
|
||||
configurations: 0,
|
||||
description: "",
|
||||
description: '',
|
||||
state: 0,
|
||||
projectId: 0,
|
||||
};
|
||||
@@ -69,18 +55,12 @@ type Props = {
|
||||
locale: string;
|
||||
};
|
||||
|
||||
export default function RunEditor({
|
||||
projectId,
|
||||
runId,
|
||||
messages,
|
||||
locale,
|
||||
}: Props) {
|
||||
export default function RunEditor({ projectId, runId, messages, locale }: Props) {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const [testRun, setTestRun] = useState<RunType>(defaultTestRun);
|
||||
const [folders, setFolders] = useState([]);
|
||||
const [runCases, setRunCases] = useState<RunCaseType[]>([]);
|
||||
const [runStatusCounts, setRunStatusCounts] =
|
||||
useState<RunStatusCountType[]>();
|
||||
const [runStatusCounts, setRunStatusCounts] = useState<RunStatusCountType[]>();
|
||||
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
|
||||
const [selectedFolder, setSelectedFolder] = useState<FolderType>({});
|
||||
const [testcases, setTestCases] = useState<CaseType[]>([]);
|
||||
@@ -102,7 +82,7 @@ export default function RunEditor({
|
||||
setFolders(foldersData);
|
||||
setSelectedFolder(foldersData[0]);
|
||||
} catch (error: any) {
|
||||
console.error("Error in effect:", error.message);
|
||||
console.error('Error in effect:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +100,7 @@ export default function RunEditor({
|
||||
// Check if each testCase has an association with testRun
|
||||
// and add "isIncluded" property
|
||||
const updatedTestCasesData = testCasesData.map((testCase) => {
|
||||
const runCase = latestRunCases.find(
|
||||
(runCase) => runCase.caseId === testCase.id
|
||||
);
|
||||
const runCase = latestRunCases.find((runCase) => runCase.caseId === testCase.id);
|
||||
|
||||
const isIncluded = runCase ? true : false;
|
||||
const runStatus = runCase ? runCase.status : 0;
|
||||
@@ -135,7 +113,7 @@ export default function RunEditor({
|
||||
|
||||
setTestCases(updatedTestCasesData);
|
||||
} catch (error: any) {
|
||||
console.error("Error fetching cases data:", error.message);
|
||||
console.error('Error fetching cases data:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,10 +133,7 @@ export default function RunEditor({
|
||||
});
|
||||
};
|
||||
|
||||
const handleIncludeExcludeCase = async (
|
||||
isInclude: boolean,
|
||||
clickedTestCaseId: number
|
||||
) => {
|
||||
const handleIncludeExcludeCase = async (isInclude: boolean, clickedTestCaseId: number) => {
|
||||
if (isInclude) {
|
||||
const createdRunCase = await createRunCase(runId, clickedTestCaseId);
|
||||
setRunCases((prevRunCases) => {
|
||||
@@ -167,9 +142,7 @@ export default function RunEditor({
|
||||
} else {
|
||||
await deleteRunCase(runId, clickedTestCaseId);
|
||||
setRunCases((prevRunCases) => {
|
||||
return prevRunCases.filter(
|
||||
(runCase) => runCase.caseId !== clickedTestCaseId
|
||||
);
|
||||
return prevRunCases.filter((runCase) => runCase.caseId !== clickedTestCaseId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -185,7 +158,7 @@ export default function RunEditor({
|
||||
|
||||
const handleBulkIncludeExcludeCases = async (isInclude: boolean) => {
|
||||
let keys: number[] = [];
|
||||
if (selectedKeys === "all") {
|
||||
if (selectedKeys === 'all') {
|
||||
keys = testcases.map((item) => item.id);
|
||||
} else {
|
||||
keys = Array.from(selectedKeys).map(Number);
|
||||
@@ -218,7 +191,7 @@ export default function RunEditor({
|
||||
setSelectedKeys(new Set([]));
|
||||
};
|
||||
|
||||
const baseClass = "";
|
||||
const baseClass = '';
|
||||
const selectedClass = `${baseClass} bg-neutral-200 dark:bg-neutral-700`;
|
||||
|
||||
return (
|
||||
@@ -230,9 +203,7 @@ export default function RunEditor({
|
||||
isIconOnly
|
||||
size="sm"
|
||||
className="rounded-full bg-neutral-50 dark:bg-neutral-600"
|
||||
onPress={() =>
|
||||
router.push(`/projects/${projectId}/runs`, { locale: locale })
|
||||
}
|
||||
onPress={() => router.push(`/projects/${projectId}/runs`, { locale: locale })}
|
||||
>
|
||||
<ArrowLeft size={16} />
|
||||
</Button>
|
||||
@@ -272,11 +243,7 @@ export default function RunEditor({
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<RunProgressChart
|
||||
statusCounts={runStatusCounts}
|
||||
messages={messages}
|
||||
theme={theme}
|
||||
/>
|
||||
<RunProgressChart statusCounts={runStatusCounts} messages={messages} theme={theme} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
@@ -287,7 +254,7 @@ export default function RunEditor({
|
||||
label={messages.title}
|
||||
value={testRun.name}
|
||||
isInvalid={isNameInvalid}
|
||||
errorMessage={isNameInvalid ? messages.pleaseEnter : ""}
|
||||
errorMessage={isNameInvalid ? messages.pleaseEnter : ''}
|
||||
onChange={(e) => {
|
||||
setTestRun({ ...testRun, name: e.target.value });
|
||||
}}
|
||||
@@ -312,9 +279,7 @@ export default function RunEditor({
|
||||
selectedKeys={[testRunStatus[testRun.state].uid]}
|
||||
onSelectionChange={(e) => {
|
||||
const selectedUid = e.anchorKey;
|
||||
const index = testRunStatus.findIndex(
|
||||
(template) => template.uid === selectedUid
|
||||
);
|
||||
const index = testRunStatus.findIndex((template) => template.uid === selectedUid);
|
||||
setTestRun({ ...testRun, state: index });
|
||||
}}
|
||||
label={messages.status}
|
||||
@@ -334,14 +299,10 @@ export default function RunEditor({
|
||||
<div className="flex items-center justify-between">
|
||||
<h6 className="h-8 font-bold">{messages.selectTestCase}</h6>
|
||||
<div>
|
||||
{(selectedKeys.size > 0 || selectedKeys === "all") && (
|
||||
{(selectedKeys.size > 0 || selectedKeys === 'all') && (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
endContent={<ChevronDown size={16} />}
|
||||
>
|
||||
<Button size="sm" color="primary" endContent={<ChevronDown size={16} />}>
|
||||
{messages.testCaseSelection}
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
@@ -371,14 +332,8 @@ export default function RunEditor({
|
||||
<ListboxItem
|
||||
key={index}
|
||||
onClick={() => setSelectedFolder(folder)}
|
||||
startContent={
|
||||
<Folder size={20} color="#F7C24E" fill="#F7C24E" />
|
||||
}
|
||||
className={
|
||||
selectedFolder && folder.id === selectedFolder.id
|
||||
? selectedClass
|
||||
: baseClass
|
||||
}
|
||||
startContent={<Folder size={20} color="#F7C24E" fill="#F7C24E" />}
|
||||
className={selectedFolder && folder.id === selectedFolder.id ? selectedClass : baseClass}
|
||||
>
|
||||
{folder.name}
|
||||
</ListboxItem>
|
||||
@@ -391,12 +346,8 @@ export default function RunEditor({
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectionChange={setSelectedKeys}
|
||||
onStatusChange={handleChangeStatus}
|
||||
onIncludeCase={(includeTestId) =>
|
||||
handleIncludeExcludeCase(true, includeTestId)
|
||||
}
|
||||
onExcludeCase={(excludeCaseId) =>
|
||||
handleIncludeExcludeCase(false, excludeCaseId)
|
||||
}
|
||||
onIncludeCase={(includeTestId) => handleIncludeExcludeCase(true, includeTestId)}
|
||||
onExcludeCase={(excludeCaseId) => handleIncludeExcludeCase(false, excludeCaseId)}
|
||||
messages={messages}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { testRunCaseStatus } from "@/config/selection";
|
||||
import { RunStatusCountType, RunMessages } from "@/types/run";
|
||||
const Chart = dynamic(() => import("react-apexcharts"), { ssr: false });
|
||||
import React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { testRunCaseStatus } from '@/config/selection';
|
||||
import { RunStatusCountType, RunMessages } from '@/types/run';
|
||||
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
|
||||
|
||||
type Props = {
|
||||
statusCounts: RunStatusCountType[];
|
||||
@@ -11,11 +11,7 @@ type Props = {
|
||||
theme: string;
|
||||
};
|
||||
|
||||
export default function RunProgressDounut({
|
||||
statusCounts,
|
||||
messages,
|
||||
theme,
|
||||
}: Props) {
|
||||
export default function RunProgressDounut({ statusCounts, messages, theme }: Props) {
|
||||
const [chartData, setChartData] = useState({
|
||||
series: [],
|
||||
options: {
|
||||
@@ -37,10 +33,10 @@ export default function RunProgressDounut({
|
||||
const legend = {
|
||||
labels: {
|
||||
colors: testRunCaseStatus.map((entry) => {
|
||||
if (theme === "light") {
|
||||
return "black";
|
||||
if (theme === 'light') {
|
||||
return 'black';
|
||||
} else {
|
||||
return "white";
|
||||
return 'white';
|
||||
}
|
||||
}),
|
||||
},
|
||||
@@ -56,13 +52,5 @@ export default function RunProgressDounut({
|
||||
updateChartDate();
|
||||
}, [statusCounts, theme]);
|
||||
|
||||
return (
|
||||
<Chart
|
||||
options={chartData.options}
|
||||
series={chartData.series}
|
||||
type="donut"
|
||||
width={"100%"}
|
||||
height={"100%"}
|
||||
/>
|
||||
);
|
||||
return <Chart options={chartData.options} series={chartData.series} type="donut" width={'100%'} height={'100%'} />;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { useState, useMemo } from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
DropdownItem,
|
||||
Selection,
|
||||
SortDescriptor,
|
||||
} from "@nextui-org/react";
|
||||
} from '@nextui-org/react';
|
||||
import {
|
||||
ChevronDown,
|
||||
MoreVertical,
|
||||
@@ -24,10 +24,10 @@ import {
|
||||
CircleDashed,
|
||||
CircleX,
|
||||
CircleSlash2,
|
||||
} from "lucide-react";
|
||||
import { priorities, testRunCaseStatus } from "@/config/selection";
|
||||
import { CaseType } from "@/types/case";
|
||||
import { RunsMessages } from "@/types/run";
|
||||
} from 'lucide-react';
|
||||
import { priorities, testRunCaseStatus } from '@/config/selection';
|
||||
import { CaseType } from '@/types/case';
|
||||
import { RunsMessages } from '@/types/run';
|
||||
|
||||
type Props = {
|
||||
cases: CaseType[];
|
||||
@@ -49,16 +49,16 @@ export default function TestCaseSelector({
|
||||
messages,
|
||||
}: Props) {
|
||||
const headerColumns = [
|
||||
{ name: messages.id, uid: "id", sortable: true },
|
||||
{ name: messages.title, uid: "title", sortable: true },
|
||||
{ name: messages.priority, uid: "priority", sortable: true },
|
||||
{ name: messages.status, uid: "runStatus", sortable: true },
|
||||
{ name: messages.actions, uid: "actions" },
|
||||
{ name: messages.id, uid: 'id', sortable: true },
|
||||
{ name: messages.title, uid: 'title', sortable: true },
|
||||
{ name: messages.priority, uid: 'priority', sortable: true },
|
||||
{ name: messages.status, uid: 'runStatus', sortable: true },
|
||||
{ name: messages.actions, uid: 'actions' },
|
||||
];
|
||||
|
||||
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
|
||||
column: "id",
|
||||
direction: "ascending",
|
||||
column: 'id',
|
||||
direction: 'ascending',
|
||||
});
|
||||
|
||||
const sortedItems = useMemo(() => {
|
||||
@@ -67,23 +67,23 @@ export default function TestCaseSelector({
|
||||
const second = b[sortDescriptor.column as keyof CaseType] as number;
|
||||
const cmp = first < second ? -1 : first > second ? 1 : 0;
|
||||
|
||||
return sortDescriptor.direction === "descending" ? -cmp : cmp;
|
||||
return sortDescriptor.direction === 'descending' ? -cmp : cmp;
|
||||
});
|
||||
}, [sortDescriptor, cases]);
|
||||
|
||||
const notIncludedCaseClass = "text-neutral-200 dark:text-neutral-600";
|
||||
const chipBaseClass = "flex items-center text-default-600";
|
||||
const notIncludedCaseClass = 'text-neutral-200 dark:text-neutral-600';
|
||||
const chipBaseClass = 'flex items-center text-default-600';
|
||||
|
||||
const renderStatusIcon = (uid: string) => {
|
||||
if (uid === "untested") {
|
||||
if (uid === 'untested') {
|
||||
return <Circle size={16} color="#d4d4d8" />;
|
||||
} else if (uid === "passed") {
|
||||
} else if (uid === 'passed') {
|
||||
return <CircleCheck size={16} color="#17c964" />;
|
||||
} else if (uid === "retest") {
|
||||
} else if (uid === 'retest') {
|
||||
return <CircleDashed size={16} color="#f5a524" />;
|
||||
} else if (uid === "failed") {
|
||||
} else if (uid === 'failed') {
|
||||
return <CircleX size={16} color="#f31260" />;
|
||||
} else if (uid === "skipped") {
|
||||
} else if (uid === 'skipped') {
|
||||
return <CircleSlash2 size={16} color="#52525b" />;
|
||||
}
|
||||
};
|
||||
@@ -93,22 +93,18 @@ export default function TestCaseSelector({
|
||||
const isIncluded = testCase.isIncluded;
|
||||
|
||||
switch (columnKey) {
|
||||
case "priority":
|
||||
case 'priority':
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
isIncluded ? chipBaseClass : chipBaseClass + notIncludedCaseClass
|
||||
}
|
||||
>
|
||||
<div className={isIncluded ? chipBaseClass : chipBaseClass + notIncludedCaseClass}>
|
||||
<Circle
|
||||
size={8}
|
||||
color={isIncluded ? priorities[cellValue].color : "#d4d4d8"}
|
||||
fill={isIncluded ? priorities[cellValue].color : "#d4d4d8"}
|
||||
color={isIncluded ? priorities[cellValue].color : '#d4d4d8'}
|
||||
fill={isIncluded ? priorities[cellValue].color : '#d4d4d8'}
|
||||
/>
|
||||
<div className="ms-3">{messages[priorities[cellValue].uid]}</div>
|
||||
</div>
|
||||
);
|
||||
case "runStatus":
|
||||
case 'runStatus':
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
@@ -116,15 +112,10 @@ export default function TestCaseSelector({
|
||||
size="sm"
|
||||
variant="light"
|
||||
isDisabled={!isIncluded}
|
||||
startContent={
|
||||
isIncluded &&
|
||||
renderStatusIcon(testRunCaseStatus[cellValue].uid)
|
||||
}
|
||||
startContent={isIncluded && renderStatusIcon(testRunCaseStatus[cellValue].uid)}
|
||||
endContent={isIncluded && <ChevronDown size={16} />}
|
||||
>
|
||||
<span className="w-12">
|
||||
{isIncluded && messages[testRunCaseStatus[cellValue].uid]}
|
||||
</span>
|
||||
<span className="w-12">{isIncluded && messages[testRunCaseStatus[cellValue].uid]}</span>
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label="test case actions">
|
||||
@@ -140,7 +131,7 @@ export default function TestCaseSelector({
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
);
|
||||
case "actions":
|
||||
case 'actions':
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
@@ -173,18 +164,18 @@ export default function TestCaseSelector({
|
||||
|
||||
const classNames = useMemo(
|
||||
() => ({
|
||||
wrapper: ["min-w-3xl"],
|
||||
th: ["bg-transparent", "text-default-500", "border-b", "border-divider"],
|
||||
wrapper: ['min-w-3xl'],
|
||||
th: ['bg-transparent', 'text-default-500', 'border-b', 'border-divider'],
|
||||
td: [
|
||||
// changing the rows border radius
|
||||
// first
|
||||
"group-data-[first=true]:first:before:rounded-none",
|
||||
"group-data-[first=true]:last:before:rounded-none",
|
||||
'group-data-[first=true]:first:before:rounded-none',
|
||||
'group-data-[first=true]:last:before:rounded-none',
|
||||
// middle
|
||||
"group-data-[middle=true]:before:rounded-none",
|
||||
'group-data-[middle=true]:before:rounded-none',
|
||||
// last
|
||||
"group-data-[last=true]:first:before:rounded-none",
|
||||
"group-data-[last=true]:last:before:rounded-none",
|
||||
'group-data-[last=true]:first:before:rounded-none',
|
||||
'group-data-[last=true]:last:before:rounded-none',
|
||||
],
|
||||
}),
|
||||
[]
|
||||
@@ -211,7 +202,7 @@ export default function TestCaseSelector({
|
||||
{(column) => (
|
||||
<TableColumn
|
||||
key={column.uid}
|
||||
align={column.uid === "actions" ? "center" : "start"}
|
||||
align={column.uid === 'actions' ? 'center' : 'start'}
|
||||
allowsSorting={column.sortable}
|
||||
>
|
||||
{column.name}
|
||||
@@ -220,13 +211,8 @@ export default function TestCaseSelector({
|
||||
</TableHeader>
|
||||
<TableBody emptyContent={messages.noCasesFound} items={sortedItems}>
|
||||
{(item) => (
|
||||
<TableRow
|
||||
key={item.id}
|
||||
className={!item.isIncluded ? notIncludedCaseClass : ""}
|
||||
>
|
||||
{(columnKey) => (
|
||||
<TableCell>{renderCell(item, columnKey)}</TableCell>
|
||||
)}
|
||||
<TableRow key={item.id} className={!item.isIncluded ? notIncludedCaseClass : ''}>
|
||||
{(columnKey) => <TableCell>{renderCell(item, columnKey)}</TableCell>}
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
|
||||
@@ -1,53 +1,42 @@
|
||||
import RunEditor from "./RunEditor";
|
||||
import { useTranslations } from "next-intl";
|
||||
import RunEditor from './RunEditor';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function Page({
|
||||
params,
|
||||
}: {
|
||||
params: { projectId: string; runId: string; locale: string };
|
||||
}) {
|
||||
const t = useTranslations("Run");
|
||||
export default function Page({ params }: { params: { projectId: string; runId: string; locale: string } }) {
|
||||
const t = useTranslations('Run');
|
||||
const messages = {
|
||||
backToRuns: t("back_to_runs"),
|
||||
updating: t("updating"),
|
||||
update: t("update"),
|
||||
progress: t("progress"),
|
||||
refresh: t("refresh"),
|
||||
id: t("id"),
|
||||
title: t("title"),
|
||||
pleaseEnter: t("please_enter"),
|
||||
description: t("description"),
|
||||
new: t("new"),
|
||||
inProgress: t("inProgress"),
|
||||
underReview: t("underReview"),
|
||||
rejected: t("rejected"),
|
||||
done: t("done"),
|
||||
closed: t("closed"),
|
||||
priority: t("priority"),
|
||||
actions: t("actions"),
|
||||
status: t("status"),
|
||||
critical: t("critical"),
|
||||
high: t("high"),
|
||||
medium: t("medium"),
|
||||
low: t("low"),
|
||||
untested: t("untested"),
|
||||
passed: t("passed"),
|
||||
failed: t("failed"),
|
||||
retest: t("retest"),
|
||||
skipped: t("skipped"),
|
||||
selectTestCase: t("select_test_case"),
|
||||
testCaseSelection: t("test_case_selection"),
|
||||
includeInRun: t("include_in_run"),
|
||||
excludeFromRun: t("exclude_from_run"),
|
||||
noCasesFound: t("no_cases_found"),
|
||||
backToRuns: t('back_to_runs'),
|
||||
updating: t('updating'),
|
||||
update: t('update'),
|
||||
progress: t('progress'),
|
||||
refresh: t('refresh'),
|
||||
id: t('id'),
|
||||
title: t('title'),
|
||||
pleaseEnter: t('please_enter'),
|
||||
description: t('description'),
|
||||
new: t('new'),
|
||||
inProgress: t('inProgress'),
|
||||
underReview: t('underReview'),
|
||||
rejected: t('rejected'),
|
||||
done: t('done'),
|
||||
closed: t('closed'),
|
||||
priority: t('priority'),
|
||||
actions: t('actions'),
|
||||
status: t('status'),
|
||||
critical: t('critical'),
|
||||
high: t('high'),
|
||||
medium: t('medium'),
|
||||
low: t('low'),
|
||||
untested: t('untested'),
|
||||
passed: t('passed'),
|
||||
failed: t('failed'),
|
||||
retest: t('retest'),
|
||||
skipped: t('skipped'),
|
||||
selectTestCase: t('select_test_case'),
|
||||
testCaseSelection: t('test_case_selection'),
|
||||
includeInRun: t('include_in_run'),
|
||||
excludeFromRun: t('exclude_from_run'),
|
||||
noCasesFound: t('no_cases_found'),
|
||||
};
|
||||
|
||||
return (
|
||||
<RunEditor
|
||||
projectId={params.projectId}
|
||||
runId={params.runId}
|
||||
messages={messages}
|
||||
locale={params.locale}
|
||||
/>
|
||||
);
|
||||
return <RunEditor projectId={params.projectId} runId={params.runId} messages={messages} locale={params.locale} />;
|
||||
}
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
import RunsPage from "./RunsPage";
|
||||
import { useTranslations } from "next-intl";
|
||||
import RunsPage from './RunsPage';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export default function Page({
|
||||
params,
|
||||
}: {
|
||||
params: { projectId: string; locale: string };
|
||||
}) {
|
||||
const t = useTranslations("Runs");
|
||||
export default function Page({ params }: { params: { projectId: string; locale: string } }) {
|
||||
const t = useTranslations('Runs');
|
||||
const messages = {
|
||||
runList: t("run_list"),
|
||||
id: t("id"),
|
||||
name: t("name"),
|
||||
description: t("description"),
|
||||
lastUpdate: t("last_update"),
|
||||
actions: t("actions"),
|
||||
newRun: t("new_run"),
|
||||
deleteRun: t("delete_run"),
|
||||
noRunsFound: t("no_runs_found"),
|
||||
runList: t('run_list'),
|
||||
id: t('id'),
|
||||
name: t('name'),
|
||||
description: t('description'),
|
||||
lastUpdate: t('last_update'),
|
||||
actions: t('actions'),
|
||||
newRun: t('new_run'),
|
||||
deleteRun: t('delete_run'),
|
||||
noRunsFound: t('no_runs_found'),
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<RunsPage
|
||||
projectId={params.projectId}
|
||||
locale={params.locale}
|
||||
messages={messages}
|
||||
/>
|
||||
<RunsPage projectId={params.projectId} locale={params.locale} messages={messages} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user