generated from gc/template-java-maven
feat: Request Logs 조회 기간 프리셋 + 필터 UI 개선
- 날짜 프리셋 버튼 추가 (오늘/어제/최근7일/이번달/지난달/직접선택) - 필터 영역 한 줄로 통합 (서비스/상태/Method/검색/초기화) - IP 입력 필드 제거
This commit is contained in:
부모
97e5a24343
커밋
765d0e01c6
@ -26,14 +26,13 @@ const REQUEST_STATUSES = ['SUCCESS', 'FAIL', 'DENIED', 'EXPIRED', 'INVALID_KEY',
|
|||||||
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE'];
|
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE'];
|
||||||
const DEFAULT_PAGE_SIZE = 20;
|
const DEFAULT_PAGE_SIZE = 20;
|
||||||
|
|
||||||
const getTodayString = (): string => {
|
const formatDate = (d: Date): string => {
|
||||||
const d = new Date();
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||||
const year = d.getFullYear();
|
|
||||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(d.getDate()).padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getToday = (): string => formatDate(new Date());
|
||||||
|
const getTodayString = getToday;
|
||||||
|
|
||||||
const formatDateTime = (dateStr: string): string => {
|
const formatDateTime = (dateStr: string): string => {
|
||||||
const d = new Date(dateStr);
|
const d = new Date(dateStr);
|
||||||
const year = d.getFullYear();
|
const year = d.getFullYear();
|
||||||
@ -50,10 +49,10 @@ const RequestLogsPage = () => {
|
|||||||
|
|
||||||
const [startDate, setStartDate] = useState(getTodayString());
|
const [startDate, setStartDate] = useState(getTodayString());
|
||||||
const [endDate, setEndDate] = useState(getTodayString());
|
const [endDate, setEndDate] = useState(getTodayString());
|
||||||
|
const [datePreset, setDatePreset] = useState('오늘');
|
||||||
const [serviceId, setServiceId] = useState('');
|
const [serviceId, setServiceId] = useState('');
|
||||||
const [requestStatus, setRequestStatus] = useState('');
|
const [requestStatus, setRequestStatus] = useState('');
|
||||||
const [requestMethod, setRequestMethod] = useState('');
|
const [requestMethod, setRequestMethod] = useState('');
|
||||||
const [requestIp, setRequestIp] = useState('');
|
|
||||||
|
|
||||||
const [services, setServices] = useState<ServiceInfo[]>([]);
|
const [services, setServices] = useState<ServiceInfo[]>([]);
|
||||||
const [result, setResult] = useState<PageResponse<RequestLog> | null>(null);
|
const [result, setResult] = useState<PageResponse<RequestLog> | null>(null);
|
||||||
@ -84,7 +83,6 @@ const RequestLogsPage = () => {
|
|||||||
serviceId: serviceId ? Number(serviceId) : undefined,
|
serviceId: serviceId ? Number(serviceId) : undefined,
|
||||||
requestStatus: requestStatus || undefined,
|
requestStatus: requestStatus || undefined,
|
||||||
requestMethod: requestMethod || undefined,
|
requestMethod: requestMethod || undefined,
|
||||||
requestIp: requestIp || undefined,
|
|
||||||
page,
|
page,
|
||||||
size: DEFAULT_PAGE_SIZE,
|
size: DEFAULT_PAGE_SIZE,
|
||||||
};
|
};
|
||||||
@ -104,10 +102,10 @@ const RequestLogsPage = () => {
|
|||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setStartDate(getTodayString());
|
setStartDate(getTodayString());
|
||||||
setEndDate(getTodayString());
|
setEndDate(getTodayString());
|
||||||
|
setDatePreset('오늘');
|
||||||
setServiceId('');
|
setServiceId('');
|
||||||
setRequestStatus('');
|
setRequestStatus('');
|
||||||
setRequestMethod('');
|
setRequestMethod('');
|
||||||
setRequestIp('');
|
|
||||||
setCurrentPage(0);
|
setCurrentPage(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -169,45 +167,68 @@ const RequestLogsPage = () => {
|
|||||||
{/* Search Form */}
|
{/* Search Form */}
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||||
<div>
|
<div className="md:col-span-3">
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">기간</label>
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">기간</label>
|
||||||
|
<div className="flex items-center gap-2 flex-wrap mb-2">
|
||||||
|
{([
|
||||||
|
{ label: '오늘', fn: () => { const t = getToday(); setStartDate(t); setEndDate(t); setDatePreset('오늘'); } },
|
||||||
|
{ label: '어제', fn: () => { const d = new Date(); d.setDate(d.getDate() - 1); const y = formatDate(d); setStartDate(y); setEndDate(y); setDatePreset('어제'); } },
|
||||||
|
{ label: '최근 7일', fn: () => { const d = new Date(); d.setDate(d.getDate() - 6); setStartDate(formatDate(d)); setEndDate(getToday()); setDatePreset('최근 7일'); } },
|
||||||
|
{ label: '이번 달', fn: () => { const d = new Date(); setStartDate(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-01`); setEndDate(getToday()); setDatePreset('이번 달'); } },
|
||||||
|
{ label: '지난 달', fn: () => { const d = new Date(); d.setMonth(d.getMonth() - 1); const s = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-01`; const e = new Date(d.getFullYear(), d.getMonth() + 1, 0); setStartDate(s); setEndDate(formatDate(e)); setDatePreset('지난 달'); } },
|
||||||
|
{ label: '직접 선택', fn: () => { setDatePreset('직접 선택'); } },
|
||||||
|
]).map((btn) => (
|
||||||
|
<button
|
||||||
|
key={btn.label}
|
||||||
|
type="button"
|
||||||
|
onClick={btn.fn}
|
||||||
|
className={`px-3 py-1.5 text-xs font-medium rounded-lg border transition-colors ${
|
||||||
|
datePreset === btn.label
|
||||||
|
? 'bg-blue-50 dark:bg-blue-900/20 border-blue-300 dark:border-blue-700 text-blue-600 dark:text-blue-400'
|
||||||
|
: 'border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700/50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{btn.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={startDate}
|
value={startDate}
|
||||||
onChange={(e) => setStartDate(e.target.value)}
|
onChange={(e) => { setStartDate(e.target.value); setDatePreset('직접 선택'); }}
|
||||||
className="flex-1 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
className="flex-1 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||||
/>
|
/>
|
||||||
<span className="text-gray-500 dark:text-gray-400">~</span>
|
<span className="text-gray-500 dark:text-gray-400">~</span>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={endDate}
|
value={endDate}
|
||||||
onChange={(e) => setEndDate(e.target.value)}
|
onChange={(e) => { setEndDate(e.target.value); setDatePreset('직접 선택'); }}
|
||||||
className="flex-1 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
className="flex-1 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-end gap-3 flex-wrap">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">서비스</label>
|
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">서비스</label>
|
||||||
<select
|
<select
|
||||||
value={serviceId}
|
value={serviceId}
|
||||||
onChange={(e) => setServiceId(e.target.value)}
|
onChange={(e) => setServiceId(e.target.value)}
|
||||||
className="w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
className="border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||||
>
|
>
|
||||||
<option value="">전체</option>
|
<option value="">전체</option>
|
||||||
{services.map((s) => (
|
{services.map((s) => (
|
||||||
<option key={s.serviceId} value={s.serviceId}>
|
<option key={s.serviceId} value={s.serviceId}>{s.serviceName}</option>
|
||||||
{s.serviceName}
|
|
||||||
</option>
|
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">상태</label>
|
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">상태</label>
|
||||||
<select
|
<select
|
||||||
value={requestStatus}
|
value={requestStatus}
|
||||||
onChange={(e) => setRequestStatus(e.target.value)}
|
onChange={(e) => setRequestStatus(e.target.value)}
|
||||||
className="w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
className="border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||||
>
|
>
|
||||||
<option value="">전체</option>
|
<option value="">전체</option>
|
||||||
{REQUEST_STATUSES.map((s) => (
|
{REQUEST_STATUSES.map((s) => (
|
||||||
@ -215,14 +236,12 @@ const RequestLogsPage = () => {
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">HTTP Method</label>
|
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Method</label>
|
||||||
<select
|
<select
|
||||||
value={requestMethod}
|
value={requestMethod}
|
||||||
onChange={(e) => setRequestMethod(e.target.value)}
|
onChange={(e) => setRequestMethod(e.target.value)}
|
||||||
className="w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
className="border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
||||||
>
|
>
|
||||||
<option value="">전체</option>
|
<option value="">전체</option>
|
||||||
{HTTP_METHODS.map((m) => (
|
{HTTP_METHODS.map((m) => (
|
||||||
@ -230,17 +249,7 @@ const RequestLogsPage = () => {
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex items-end gap-2 ml-auto">
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">IP</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={requestIp}
|
|
||||||
onChange={(e) => setRequestIp(e.target.value)}
|
|
||||||
placeholder="IP 주소"
|
|
||||||
className="w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-end gap-2">
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSearch(0)}
|
onClick={() => handleSearch(0)}
|
||||||
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium"
|
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium"
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user