feat(agent-office): ActivityFilters (agent/type/status/days)
This commit is contained in:
64
src/pages/agent-office/components/ActivityFilters.jsx
Normal file
64
src/pages/agent-office/components/ActivityFilters.jsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// src/pages/agent-office/components/ActivityFilters.jsx
|
||||||
|
import { ACTIVE_AGENT_IDS, AGENT_META } from '../constants.js';
|
||||||
|
|
||||||
|
const TYPE_OPTIONS = [
|
||||||
|
{ value: '', label: '전체' },
|
||||||
|
{ value: 'task', label: 'Task' },
|
||||||
|
{ value: 'log', label: 'Log' },
|
||||||
|
];
|
||||||
|
const STATUS_OPTIONS = [
|
||||||
|
{ value: '', label: '전체' },
|
||||||
|
{ value: 'succeeded', label: '완료' },
|
||||||
|
{ value: 'failed', label: '실패' },
|
||||||
|
{ value: 'pending', label: '대기' },
|
||||||
|
];
|
||||||
|
const DAYS_OPTIONS = [
|
||||||
|
{ value: 1, label: '1일' },
|
||||||
|
{ value: 7, label: '7일' },
|
||||||
|
{ value: 30, label: '30일' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ActivityFilters({ filters, onChange }) {
|
||||||
|
const set = (patch) => onChange({ ...filters, ...patch });
|
||||||
|
const statusDisabled = filters.type === 'log';
|
||||||
|
return (
|
||||||
|
<div className="ao-activity-filters">
|
||||||
|
<select
|
||||||
|
className="ao-activity-select"
|
||||||
|
aria-label="에이전트 필터"
|
||||||
|
value={filters.agent_id || ''}
|
||||||
|
onChange={e => set({ agent_id: e.target.value })}
|
||||||
|
>
|
||||||
|
<option value="">모든 에이전트</option>
|
||||||
|
{ACTIVE_AGENT_IDS.map(id => (
|
||||||
|
<option key={id} value={id}>{AGENT_META[id]?.displayName || id}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
className="ao-activity-select"
|
||||||
|
aria-label="타입 필터"
|
||||||
|
value={filters.type || ''}
|
||||||
|
onChange={e => set(e.target.value === 'log' ? { type: 'log', status: '' } : { type: e.target.value })}
|
||||||
|
>
|
||||||
|
{TYPE_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
className="ao-activity-select"
|
||||||
|
aria-label="상태 필터"
|
||||||
|
value={filters.status || ''}
|
||||||
|
disabled={statusDisabled}
|
||||||
|
onChange={e => set({ status: e.target.value })}
|
||||||
|
>
|
||||||
|
{STATUS_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
className="ao-activity-select"
|
||||||
|
aria-label="기간 필터"
|
||||||
|
value={filters.days}
|
||||||
|
onChange={e => set({ days: Number(e.target.value) })}
|
||||||
|
>
|
||||||
|
{DAYS_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
src/pages/agent-office/components/ActivityFilters.test.jsx
Normal file
26
src/pages/agent-office/components/ActivityFilters.test.jsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
|
import ActivityFilters from './ActivityFilters.jsx';
|
||||||
|
|
||||||
|
const base = { agent_id: '', type: '', status: '', days: 7 };
|
||||||
|
|
||||||
|
describe('ActivityFilters', () => {
|
||||||
|
it('type=log이면 상태 필터가 비활성화된다', () => {
|
||||||
|
render(<ActivityFilters filters={{ ...base, type: 'log' }} onChange={() => {}} />);
|
||||||
|
expect(screen.getByLabelText('상태 필터')).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('기간 변경 시 onChange가 days와 함께 호출된다', () => {
|
||||||
|
const onChange = vi.fn();
|
||||||
|
render(<ActivityFilters filters={base} onChange={onChange} />);
|
||||||
|
fireEvent.change(screen.getByLabelText('기간 필터'), { target: { value: '30' } });
|
||||||
|
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ days: 30 }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('type을 log로 바꾸면 status를 비운다', () => {
|
||||||
|
const onChange = vi.fn();
|
||||||
|
render(<ActivityFilters filters={{ ...base, status: 'succeeded' }} onChange={onChange} />);
|
||||||
|
fireEvent.change(screen.getByLabelText('타입 필터'), { target: { value: 'log' } });
|
||||||
|
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ type: 'log', status: '' }));
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user