chore: Include test case title query in filters to improve UI consistency (#284)

This commit is contained in:
kimatata
2025-09-23 11:22:14 +09:00
committed by GitHub
parent 81805c605a
commit 0a8cccfad4
12 changed files with 164 additions and 215 deletions

View File

@@ -11,7 +11,7 @@ export default function (sequelize) {
const Case = defineCase(sequelize, DataTypes); const Case = defineCase(sequelize, DataTypes);
router.get('/', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => { router.get('/', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => {
const { folderId, priority, type, q } = req.query; const { folderId, title, priority, type } = req.query;
if (!folderId) { if (!folderId) {
return res.status(400).json({ error: 'folderId is required' }); return res.status(400).json({ error: 'folderId is required' });
@@ -22,15 +22,15 @@ export default function (sequelize) {
folderId: folderId, folderId: folderId,
}; };
if (q) { if (title) {
const searchTerm = q.trim(); const searchTerm = title.trim();
if (searchTerm.length > 100) { if (searchTerm.length > 100) {
return res.status(400).json({ error: 'Search term too long' }); return res.status(400).json({ error: 'too long title param' });
} }
if (searchTerm.length >= 2) { if (searchTerm.length >= 1) {
whereClause[Op.or] = [{ title: { [Op.like]: `%${q}%` } }]; whereClause[Op.or] = [{ title: { [Op.like]: `%${searchTerm}%` } }];
} }
} }

View File

@@ -204,13 +204,13 @@
"case_description": "Test Case Description", "case_description": "Test Case Description",
"create": "Create", "create": "Create",
"please_enter": "Please enter test case title", "please_enter": "Please enter test case title",
"filter": "Filter",
"clear_all": "Clear all", "clear_all": "Clear all",
"apply": "Apply", "apply": "Apply",
"select_priorities": "Select priorities", "select_priorities": "Select priorities",
"selected": "Selected", "selected": "Selected",
"type": "Type", "type": "Type",
"select_types": "Select types", "select_types": "Select types"
"search_placeholder": "Type to search..."
}, },
"Case": { "Case": {
"back_to_cases": "Back to test cases", "back_to_cases": "Back to test cases",

View File

@@ -205,13 +205,13 @@
"case_description": "テストケース詳細", "case_description": "テストケース詳細",
"create": "作成", "create": "作成",
"please_enter": "テストケースタイトルを入力してください", "please_enter": "テストケースタイトルを入力してください",
"filter": "フィルター",
"clear_all": "すべてクリア", "clear_all": "すべてクリア",
"apply": "適用", "apply": "適用",
"select_priorities": "優先度を選択", "select_priorities": "優先度を選択",
"selected": "選択済み", "selected": "選択済み",
"type": "タイプ", "type": "タイプ",
"select_types": "タイプを選択", "select_types": "タイプを選択"
"search_placeholder": "入力して検索..."
}, },
"Case": { "Case": {
"back_to_cases": "テストケース一覧に戻る", "back_to_cases": "テストケース一覧に戻る",

View File

@@ -204,13 +204,13 @@
"case_description": "Descrição do Caso de Teste", "case_description": "Descrição do Caso de Teste",
"create": "Criar", "create": "Criar",
"please_enter": "Por favor, insira o título do caso de teste", "please_enter": "Por favor, insira o título do caso de teste",
"filter": "Filtrar",
"clear_all": "Limpar tudo", "clear_all": "Limpar tudo",
"apply": "Aplicar", "apply": "Aplicar",
"select_priorities": "Selecionar prioridades", "select_priorities": "Selecionar prioridades",
"selected": "Selecionado", "selected": "Selecionado",
"type": "Tipo", "type": "Tipo",
"select_types": "Selecionar tipos", "select_types": "Selecionar tipos"
"search_placeholder": "Digite para buscar..."
}, },
"Case": { "Case": {
"back_to_cases": "Voltar para os casos de teste", "back_to_cases": "Voltar para os casos de teste",

View File

@@ -32,20 +32,25 @@ export default function CasesPane({
}: Props) { }: Props) {
const [cases, setCases] = useState<CaseType[]>([]); const [cases, setCases] = useState<CaseType[]>([]);
const [isCaseDialogOpen, setIsCaseDialogOpen] = useState(false); const [isCaseDialogOpen, setIsCaseDialogOpen] = useState(false);
const [titleFilter, setTitleFilter] = useState('');
const [priorityFilter, setPriorityFilter] = useState<number[]>([]); const [priorityFilter, setPriorityFilter] = useState<number[]>([]);
const [typeFilter, setTypeFilter] = useState<number[]>([]); const [typeFilter, setTypeFilter] = useState<number[]>([]);
const [queryTerm, setQueryTerm] = useState('');
const [isDeleteConfirmDialogOpen, setIsDeleteConfirmDialogOpen] = useState(false); const [isDeleteConfirmDialogOpen, setIsDeleteConfirmDialogOpen] = useState(false);
const [deleteCaseIds, setDeleteCaseIds] = useState<number[]>([]); const [deleteCaseIds, setDeleteCaseIds] = useState<number[]>([]);
const [isSearching, setIsSearching] = useState(false);
const context = useContext(TokenContext); const context = useContext(TokenContext);
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const updateUrlParams = (updates: { priority?: number[]; type?: number[]; q?: string }) => { const updateUrlParams = (updates: { title?: string; priority?: number[]; type?: number[] }) => {
const currentParams = new URLSearchParams(searchParams.toString()); const currentParams = new URLSearchParams(searchParams.toString());
if (updates.title) {
currentParams.set('title', updates.title);
} else {
currentParams.delete('title');
}
if (updates.priority && updates.priority.length > 0) { if (updates.priority && updates.priority.length > 0) {
currentParams.set('priority', updates.priority.join(',')); currentParams.set('priority', updates.priority.join(','));
} else { } else {
@@ -58,12 +63,6 @@ export default function CasesPane({
currentParams.delete('type'); currentParams.delete('type');
} }
if (updates.q) {
currentParams.set('q', updates.q);
} else {
currentParams.delete('q');
}
const newUrl = `${window.location.pathname}?${currentParams.toString()}`; const newUrl = `${window.location.pathname}?${currentParams.toString()}`;
router.push(newUrl, { scroll: false }); router.push(newUrl, { scroll: false });
}; };
@@ -72,27 +71,25 @@ export default function CasesPane({
async function fetchDataEffect() { async function fetchDataEffect() {
if (!context.isSignedIn()) return; if (!context.isSignedIn()) return;
const titleParam = searchParams.get('title') || '';
const priorityParam = parseQueryParam(searchParams.get('priority')); const priorityParam = parseQueryParam(searchParams.get('priority'));
const typeParam = parseQueryParam(searchParams.get('type')); const typeParam = parseQueryParam(searchParams.get('type'));
const queryParam = searchParams.get('q') || '';
setTitleFilter(titleParam);
setPriorityFilter(priorityParam); setPriorityFilter(priorityParam);
setTypeFilter(typeParam); setTypeFilter(typeParam);
setQueryTerm(queryParam);
try { try {
const data = await fetchCases( const data = await fetchCases(
context.token.access_token, context.token.access_token,
Number(folderId), Number(folderId),
titleParam || undefined,
priorityParam.length > 0 ? priorityParam : undefined, priorityParam.length > 0 ? priorityParam : undefined,
typeParam.length > 0 ? typeParam : undefined, typeParam.length > 0 ? typeParam : undefined
queryParam || undefined
); );
setCases(data); setCases(data);
} catch (error: unknown) { } catch (error: unknown) {
logError('Error fetching cases:', error); logError('Error fetching cases:', error);
} finally {
setIsSearching(false);
} }
} }
@@ -134,18 +131,11 @@ export default function CasesPane({
await exportCases(context.token.access_token, Number(folderId), type); await exportCases(context.token.access_token, Number(folderId), type);
}; };
const handleFilterChange = (priorities: number[], types: number[]) => { const handleFilterChange = (title: string, priorities: number[], types: number[]) => {
setTitleFilter(title);
setPriorityFilter(priorities); setPriorityFilter(priorities);
setTypeFilter(types); setTypeFilter(types);
updateUrlParams({ priority: priorities, type: types, q: queryTerm }); updateUrlParams({ title: title, priority: priorities, type: types });
};
const handleQueryChange = (q: string) => {
setQueryTerm(q);
if (q.trim()) {
setIsSearching(true);
}
updateUrlParams({ priority: priorityFilter, type: typeFilter, q });
}; };
return ( return (
@@ -159,15 +149,13 @@ export default function CasesPane({
onDeleteCases={onDeleteCases} onDeleteCases={onDeleteCases}
onExportCases={onExportCases} onExportCases={onExportCases}
onFilterChange={handleFilterChange} onFilterChange={handleFilterChange}
onQueryChange={handleQueryChange} activeTitleFilter={titleFilter}
activePriorityFilters={priorityFilter} activePriorityFilters={priorityFilter}
activeTypeFilters={typeFilter} activeTypeFilters={typeFilter}
messages={messages} messages={messages}
priorityMessages={priorityMessages} priorityMessages={priorityMessages}
testTypeMessages={testTypeMessages} testTypeMessages={testTypeMessages}
locale={locale} locale={locale}
queryTerm={queryTerm}
isSearching={isSearching}
/> />
<CaseDialog isOpen={isCaseDialogOpen} onCancel={closeDialog} onSubmit={onSubmit} messages={messages} /> <CaseDialog isOpen={isCaseDialogOpen} onCancel={closeDialog} onSubmit={onSubmit} messages={messages} />

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Button, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Selection } from '@heroui/react'; import { Button, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Selection, Input } from '@heroui/react';
import { ChevronDown, Circle } from 'lucide-react'; import { SearchIcon, ChevronDown, Circle } from 'lucide-react';
import { PriorityMessages } from '@/types/priority'; import { PriorityMessages } from '@/types/priority';
import { TestTypeMessages } from '@/types/testType'; import { TestTypeMessages } from '@/types/testType';
import { CasesMessages } from '@/types/case'; import { CasesMessages } from '@/types/case';
@@ -10,24 +10,29 @@ type TestCaseFilterProps = {
messages: CasesMessages; messages: CasesMessages;
priorityMessages: PriorityMessages; priorityMessages: PriorityMessages;
testTypeMessages: TestTypeMessages; testTypeMessages: TestTypeMessages;
activeTitleFilter: string;
activePriorityFilters: number[]; activePriorityFilters: number[];
activeTypeFilters: number[]; activeTypeFilters: number[];
onFilterChange: (priorities: number[], types: number[]) => void; onFilterChange: (title: string, priorities: number[], types: number[]) => void;
handleFilterChange: () => void;
}; };
export default function TestCaseFilter({ export default function TestCaseFilter({
messages, messages,
priorityMessages, priorityMessages,
testTypeMessages, testTypeMessages,
activeTitleFilter,
activePriorityFilters, activePriorityFilters,
activeTypeFilters, activeTypeFilters,
onFilterChange, onFilterChange,
handleFilterChange,
}: TestCaseFilterProps) { }: TestCaseFilterProps) {
const [title, setTitle] = useState<string>('');
const [selectedPriorities, setSelectedPriorities] = useState<Selection>(new Set([])); const [selectedPriorities, setSelectedPriorities] = useState<Selection>(new Set([]));
const [selectedTypes, setSelectedTypes] = useState<Selection>(new Set([])); const [selectedTypes, setSelectedTypes] = useState<Selection>(new Set([]));
useEffect(() => {
setTitle(activeTitleFilter);
}, [activeTitleFilter]);
useEffect(() => { useEffect(() => {
if (activePriorityFilters.length > 0) { if (activePriorityFilters.length > 0) {
const activeKeys = activePriorityFilters.map((index) => priorities[index]?.uid).filter(Boolean); const activeKeys = activePriorityFilters.map((index) => priorities[index]?.uid).filter(Boolean);
@@ -69,28 +74,40 @@ export default function TestCaseFilter({
.filter((index) => index !== -1); .filter((index) => index !== -1);
} }
onFilterChange(priorityIndices, typeIndices); onFilterChange(title, priorityIndices, typeIndices);
handleFilterChange();
}; };
const handleClearFilter = () => { const handleClearFilter = () => {
setSelectedPriorities(new Set([])); setSelectedPriorities(new Set([]));
setSelectedTypes(new Set([])); setSelectedTypes(new Set([]));
onFilterChange([], []); onFilterChange('', [], []);
handleFilterChange();
}; };
const isFilterEmpty =
(selectedPriorities === 'all' || (selectedPriorities instanceof Set && selectedPriorities.size === 0)) &&
(selectedTypes === 'all' || (selectedTypes instanceof Set && selectedTypes.size === 0));
return ( return (
<div className="flex items-end border-t border-default-200 p-3"> <div className="p-3">
<div className="flex-col space-y-2 mr-2"> <div className="mb-3 space-y-1">
<h3 className="text-default-500 text-small">{messages.caseTitle}</h3>
<Input
variant="bordered"
classNames={{
base: 'max-w-full h-8',
mainWrapper: 'h-full',
input: 'text-small',
}}
size="sm"
startContent={<SearchIcon size={18} />}
type="search"
value={title}
onValueChange={setTitle}
maxLength={100}
/>
</div>
<div className="mb-3 flex justify-between gap-2">
<div className="flex-col space-y-1">
<h3 className="text-default-500 text-small">{messages.priority}</h3> <h3 className="text-default-500 text-small">{messages.priority}</h3>
<Dropdown> <Dropdown>
<DropdownTrigger> <DropdownTrigger>
<Button size="sm" variant="bordered" endContent={<ChevronDown size={16} />}> <Button size="sm" variant="bordered" className="w-32" endContent={<ChevronDown size={16} />}>
{selectedPriorities === 'all' || selectedPriorities.size === 0 {selectedPriorities === 'all' || selectedPriorities.size === 0
? messages.selectPriorities ? messages.selectPriorities
: `${selectedPriorities.size} ${messages.selected || 'selected'}`} : `${selectedPriorities.size} ${messages.selected || 'selected'}`}
@@ -103,7 +120,11 @@ export default function TestCaseFilter({
onSelectionChange={handlePrioritySelectionChange} onSelectionChange={handlePrioritySelectionChange}
> >
{priorities.map((priority) => ( {priorities.map((priority) => (
<DropdownItem key={priority.uid} textValue={priorityMessages[priority.uid]} className="flex items-center"> <DropdownItem
key={priority.uid}
textValue={priorityMessages[priority.uid]}
className="flex items-center"
>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Circle size={8} color={priority.color} fill={priority.color} /> <Circle size={8} color={priority.color} fill={priority.color} />
<span className="text-sm">{priorityMessages[priority.uid]}</span> <span className="text-sm">{priorityMessages[priority.uid]}</span>
@@ -113,12 +134,11 @@ export default function TestCaseFilter({
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
</div> </div>
<div className="flex-col space-y-1">
<div className="flex-col space-y-2 mr-2">
<h3 className="text-default-500 text-small">{messages.type}</h3> <h3 className="text-default-500 text-small">{messages.type}</h3>
<Dropdown> <Dropdown>
<DropdownTrigger> <DropdownTrigger>
<Button size="sm" variant="bordered" endContent={<ChevronDown size={16} />}> <Button size="sm" variant="bordered" className="w-32" endContent={<ChevronDown size={16} />}>
{selectedTypes === 'all' || selectedTypes.size === 0 {selectedTypes === 'all' || selectedTypes.size === 0
? messages.selectTypes || 'Select Types' ? messages.selectTypes || 'Select Types'
: `${selectedTypes.size} ${messages.selected || 'selected'}`} : `${selectedTypes.size} ${messages.selected || 'selected'}`}
@@ -139,12 +159,12 @@ export default function TestCaseFilter({
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
</div> </div>
</div>
<div className="ml-auto"> <div className="flex justify-end">
<Button className="me-2" size="sm" variant="light" onPress={handleClearFilter} isDisabled={isFilterEmpty}> <Button className="me-2" size="sm" variant="light" onPress={handleClearFilter}>
{messages.clearAll} {messages.clearAll}
</Button> </Button>
<Button size="sm" variant="solid" color="primary" onPress={handleApplyFilter} isDisabled={isFilterEmpty}> <Button size="sm" variant="solid" color="primary" onPress={handleApplyFilter}>
{messages.apply} {messages.apply}
</Button> </Button>
</div> </div>

View File

@@ -1,4 +1,4 @@
import { useState, useMemo, useCallback, ReactNode, useEffect } from 'react'; import { useState, useMemo, useCallback, ReactNode } from 'react';
import { import {
Table, Table,
TableHeader, TableHeader,
@@ -13,22 +13,12 @@ import {
DropdownItem, DropdownItem,
Selection, Selection,
SortDescriptor, SortDescriptor,
cn,
Badge, Badge,
Input, Popover,
Spinner, PopoverContent,
PopoverTrigger,
} from '@heroui/react'; } from '@heroui/react';
import { import { Plus, MoreVertical, Trash, FileDown, ChevronDown, Filter, FileJson, FileSpreadsheet } from 'lucide-react';
Plus,
MoreVertical,
Trash,
FileDown,
ChevronDown,
Filter,
FileJson,
FileSpreadsheet,
SearchIcon,
} from 'lucide-react';
import TestCaseFilter from './TestCaseFilter'; import TestCaseFilter from './TestCaseFilter';
import { Link } from '@/src/i18n/routing'; import { Link } from '@/src/i18n/routing';
import { CaseType, CasesMessages } from '@/types/case'; import { CaseType, CasesMessages } from '@/types/case';
@@ -36,7 +26,6 @@ import { PriorityMessages } from '@/types/priority';
import { TestTypeMessages } from '@/types/testType'; import { TestTypeMessages } from '@/types/testType';
import TestCasePriority from '@/components/TestCasePriority'; import TestCasePriority from '@/components/TestCasePriority';
import { LocaleCodeType } from '@/types/locale'; import { LocaleCodeType } from '@/types/locale';
import useDebounce from '@/utils/useDebounce';
import { highlightSearchTerm } from '@/utils/highlightSearchTerm'; import { highlightSearchTerm } from '@/utils/highlightSearchTerm';
type Props = { type Props = {
@@ -47,16 +36,14 @@ type Props = {
onDeleteCase: (caseId: number) => void; onDeleteCase: (caseId: number) => void;
onDeleteCases: (caseIds: number[]) => void; onDeleteCases: (caseIds: number[]) => void;
onExportCases: (type: string) => void; onExportCases: (type: string) => void;
onFilterChange: (priorities: number[], types: number[]) => void; onFilterChange: (query: string, priorities: number[], types: number[]) => void;
onQueryChange: (q: string) => void; activeTitleFilter: string;
queryTerm: string;
activePriorityFilters: number[]; activePriorityFilters: number[];
activeTypeFilters: number[]; activeTypeFilters: number[];
messages: CasesMessages; messages: CasesMessages;
priorityMessages: PriorityMessages; priorityMessages: PriorityMessages;
testTypeMessages: TestTypeMessages; testTypeMessages: TestTypeMessages;
locale: LocaleCodeType; locale: LocaleCodeType;
isSearching: boolean;
}; };
export default function TestCaseTable({ export default function TestCaseTable({
@@ -68,15 +55,13 @@ export default function TestCaseTable({
onDeleteCases, onDeleteCases,
onExportCases, onExportCases,
onFilterChange, onFilterChange,
onQueryChange, activeTitleFilter,
activePriorityFilters, activePriorityFilters,
activeTypeFilters, activeTypeFilters,
messages, messages,
priorityMessages, priorityMessages,
testTypeMessages, testTypeMessages,
locale, locale,
queryTerm,
isSearching,
}: Props) { }: Props) {
const headerColumns = [ const headerColumns = [
{ name: messages.id, uid: 'id', sortable: true }, { name: messages.id, uid: 'id', sortable: true },
@@ -91,15 +76,6 @@ export default function TestCaseTable({
direction: 'ascending', direction: 'ascending',
}); });
const [showFilter, setShowFilter] = useState(false); const [showFilter, setShowFilter] = useState(false);
const [localQueryTerm, setLocalQueryTerm] = useState(queryTerm);
const debouncedQuery = useDebounce((value: unknown) => {
onQueryChange(value as string);
}, 500);
useEffect(() => {
setLocalQueryTerm(queryTerm);
}, [queryTerm]);
const sortedItems = useMemo(() => { const sortedItems = useMemo(() => {
if (cases.length === 0) { if (cases.length === 0) {
@@ -118,17 +94,6 @@ export default function TestCaseTable({
onDeleteCase(deleteCaseId); onDeleteCase(deleteCaseId);
}; };
const handleFilterChange = () => {
setShowFilter(!showFilter);
};
const handleQueryChange = (value: string) => {
setLocalQueryTerm(value);
if (value.length >= 2 || value.length === 0) {
debouncedQuery(value);
}
};
const renderCell = useCallback( const renderCell = useCallback(
(testCase: CaseType, columnKey: string): ReactNode => { (testCase: CaseType, columnKey: string): ReactNode => {
const cellValue = testCase[columnKey as keyof CaseType]; const cellValue = testCase[columnKey as keyof CaseType];
@@ -148,7 +113,7 @@ export default function TestCaseTable({
> >
{highlightSearchTerm({ {highlightSearchTerm({
text: cellValue as string, text: cellValue as string,
searchTerm: localQueryTerm, searchTerm: activeTitleFilter,
})} })}
</Button> </Button>
); );
@@ -179,7 +144,7 @@ export default function TestCaseTable({
} }
}, },
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
[localQueryTerm] [activeTitleFilter]
); );
const handleDeleteCases = () => { const handleDeleteCases = () => {
@@ -193,7 +158,7 @@ export default function TestCaseTable({
setSelectedKeys(new Set([])); setSelectedKeys(new Set([]));
}; };
const hasActiveFilters = activePriorityFilters.length > 0 || activeTypeFilters.length > 0; const activeFilterNum = (activeTitleFilter ? 1 : 0) + activePriorityFilters.length + activeTypeFilters.length;
const classNames = useMemo( const classNames = useMemo(
() => ({ () => ({
@@ -233,42 +198,35 @@ export default function TestCaseTable({
{messages.delete} {messages.delete}
</Button> </Button>
)} )}
<Input <Popover placement="bottom" isOpen={showFilter} onOpenChange={(open) => setShowFilter(open)}>
className="me-2"
variant="bordered"
classNames={{
base: 'max-w-full sm:max-w-[12rem] h-8',
mainWrapper: 'h-full',
input: 'text-small',
}}
placeholder={messages.searchPlaceholder}
size="sm"
startContent={<SearchIcon size={18} />}
endContent={isSearching && <Spinner size="sm" />}
type="search"
value={localQueryTerm}
onValueChange={handleQueryChange}
aria-label={messages.searchPlaceholder}
maxLength={100}
/>
<Badge <Badge
color="warning" color="danger"
content="" content={activeFilterNum}
isInvisible={!hasActiveFilters} isInvisible={activeFilterNum === 0}
shape="circle" shape="circle"
size="sm" placement="top-left"
placement="bottom-left"
> >
<Button <PopoverTrigger>
size="sm" <Button startContent={<Filter size={16} />} size="sm" variant="bordered" className="me-2">
variant="bordered" {messages.filter}
isIconOnly
onPress={handleFilterChange}
className={cn('me-2', showFilter && 'bg-primary')}
>
<Filter size={16} className={cn('text-default-500', showFilter && 'text-white')} />
</Button> </Button>
</PopoverTrigger>
</Badge> </Badge>
<PopoverContent>
<TestCaseFilter
messages={messages}
priorityMessages={priorityMessages}
testTypeMessages={testTypeMessages}
activeTitleFilter={activeTitleFilter}
activePriorityFilters={activePriorityFilters}
activeTypeFilters={activeTypeFilters}
onFilterChange={(newTitleFilter, newPriorityFilters, newTypeFilters) => {
setShowFilter(false);
onFilterChange(newTitleFilter, newPriorityFilters, newTypeFilters);
}}
/>
</PopoverContent>
</Popover>
<Dropdown> <Dropdown>
<DropdownTrigger> <DropdownTrigger>
<Button <Button
@@ -305,17 +263,6 @@ export default function TestCaseTable({
</Button> </Button>
</div> </div>
</div> </div>
{showFilter && (
<TestCaseFilter
messages={messages}
priorityMessages={priorityMessages}
testTypeMessages={testTypeMessages}
activePriorityFilters={activePriorityFilters}
activeTypeFilters={activeTypeFilters}
onFilterChange={onFilterChange}
handleFilterChange={handleFilterChange}
/>
)}
</div> </div>
<Table <Table

View File

@@ -34,12 +34,12 @@ export default function Page({ params }: { params: { projectId: string; folderId
create: t('create'), create: t('create'),
pleaseEnter: t('please_enter'), pleaseEnter: t('please_enter'),
apply: t('apply'), apply: t('apply'),
filter: t('filter'),
clearAll: t('clear_all'), clearAll: t('clear_all'),
selectPriorities: t('select_priorities'), selectPriorities: t('select_priorities'),
selected: t('selected'), selected: t('selected'),
type: t('type'), type: t('type'),
selectTypes: t('select_types'), selectTypes: t('select_types'),
searchPlaceholder: t('search_placeholder'),
}; };
const priorityTranslation = useTranslations('Priority'); const priorityTranslation = useTranslations('Priority');

View File

@@ -14,7 +14,6 @@ import {
DropdownMenu, DropdownMenu,
DropdownItem, DropdownItem,
addToast, addToast,
ButtonGroup,
Badge, Badge,
} from '@heroui/react'; } from '@heroui/react';
import { import {
@@ -103,7 +102,6 @@ export default function RunEditor({
const [isNameInvalid] = useState<boolean>(false); const [isNameInvalid] = useState<boolean>(false);
const [isUpdating, setIsUpdating] = useState<boolean>(false); const [isUpdating, setIsUpdating] = useState<boolean>(false);
const [isDirty, setIsDirty] = useState(false); const [isDirty, setIsDirty] = useState(false);
const [exportType, setExportType] = useState(new Set(['xml']));
const router = useRouter(); const router = useRouter();
useFormGuard(isDirty, messages.areYouSureLeave); useFormGuard(isDirty, messages.areYouSureLeave);
@@ -201,10 +199,6 @@ export default function RunEditor({
setIsDirty(false); setIsDirty(false);
}; };
const handleExportTypeChange = (keys: Selection) => {
setExportType(new Set(Array.from(keys as Set<string>)));
};
return ( return (
<> <>
<div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between"> <div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between">

View File

@@ -77,13 +77,13 @@ type CasesMessages = {
caseDescription: string; caseDescription: string;
create: string; create: string;
pleaseEnter: string; pleaseEnter: string;
filter: string;
clearAll: string; clearAll: string;
apply: string; apply: string;
selectPriorities: string; selectPriorities: string;
selected: string; selected: string;
type: string; type: string;
selectTypes: string; selectTypes: string;
searchPlaceholder: string;
}; };
type CaseMessages = { type CaseMessages = {

View File

@@ -26,9 +26,13 @@ async function fetchCase(jwt: string, caseId: number) {
} }
} }
async function fetchCases(jwt: string, folderId: number, priority?: number[], type?: number[], q?: string) { async function fetchCases(jwt: string, folderId: number, title?: string, priority?: number[], type?: number[]) {
const queryParams = [`folderId=${folderId}`]; const queryParams = [`folderId=${folderId}`];
if (title) {
queryParams.push(`title=${title}`);
}
if (priority && priority.length > 0) { if (priority && priority.length > 0) {
queryParams.push(`priority=${priority.join(',')}`); queryParams.push(`priority=${priority.join(',')}`);
} }
@@ -37,10 +41,6 @@ async function fetchCases(jwt: string, folderId: number, priority?: number[], ty
queryParams.push(`type=${type.join(',')}`); queryParams.push(`type=${type.join(',')}`);
} }
if (q) {
queryParams.push(`q=${q}`);
}
const query = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; const query = queryParams.length > 0 ? `?${queryParams.join('&')}` : '';
const url = `${apiServer}/cases${query}`; const url = `${apiServer}/cases${query}`;

View File

@@ -12,7 +12,7 @@ export function highlightSearchTerm({
text, text,
searchTerm, searchTerm,
className, className,
minSearchLength = 2, minSearchLength = 1,
}: HighlightSearchTermProps): ReactNode { }: HighlightSearchTermProps): ReactNode {
if (!text || !searchTerm || searchTerm.length < minSearchLength) { if (!text || !searchTerm || searchTerm.length < minSearchLength) {
return text || null; return text || null;