feat: debounce search, tambah filter source & date range di bug-reports, hapus seafile.ts

- Debounce search input (400ms, min 3 karakter) sesuai konvensi
- Tambah filter source (QC/SYSTEM/USER) dan date range di bug-reports
- Backend /api/bugs support query param source, dateFrom, dateTo
- Update API_URLS.getBugs dengan param baru
- Hapus seafile.ts (dead code, tidak digunakan)
This commit is contained in:
2026-05-25 11:31:37 +08:00
parent cc81c8b91e
commit 8c33003b17
4 changed files with 73 additions and 261 deletions

View File

@@ -51,8 +51,13 @@ export const API_URLS = {
createOperator: () => `/api/operators`,
editOperator: (id: string) => `/api/operators/${id}`,
deleteOperator: (id: string) => `/api/operators/${id}`,
getBugs: (page: number, search: string, app: string, status: string) =>
`/api/bugs?page=${page}&search=${encodeURIComponent(search)}&app=${app}&status=${status}`,
getBugs: (page: number, search: string, app: string, status: string, source?: string, dateFrom?: string, dateTo?: string) => {
const params = new URLSearchParams({ page: String(page), search: encodeURIComponent(search), app, status })
if (source && source !== 'all') params.set('source', source)
if (dateFrom) params.set('dateFrom', dateFrom)
if (dateTo) params.set('dateTo', dateTo)
return `/api/bugs?${params}`
},
createBug: () => `/api/bugs`,
uploadImage: () => `/api/upload/image`,
updateBugStatus: (id: string) => `/api/bugs/${id}/status`,

View File

@@ -27,12 +27,13 @@ import {
Title,
Tooltip,
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'
import { DatePickerInput, type DatesRangeValue } from '@mantine/dates'
import { notifications } from '@mantine/notifications'
import { useQuery } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
import dayjs from 'dayjs'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import {
TbAlertTriangle,
TbBug,
@@ -71,18 +72,35 @@ const STATUS_LABEL: Record<string, string> = {
function ListErrorsPage() {
const [page, setPage] = useState(1)
const [search, setSearch] = useState('')
const [searchQuery, setSearchQuery] = useState('')
const [app, setApp] = useState('all')
const [status, setStatus] = useState('all')
const [source, setSource] = useState('all')
const [dateRange, setDateRange] = useState<DatesRangeValue>([null, null])
const [debouncedSearch] = useDebouncedValue(search, 400)
useEffect(() => {
if (debouncedSearch.length >= 3 || debouncedSearch.length === 0) {
setSearchQuery(debouncedSearch)
setPage(1)
}
}, [debouncedSearch])
useEffect(() => { setPage(1) }, [app, status, source, dateRange])
const [showLogs, setShowLogs] = useState<Record<string, boolean>>({})
const [showStackTrace, setShowStackTrace] = useState<Record<string, boolean>>({})
const dateFrom = dateRange[0] ? dayjs(dateRange[0]).format('YYYY-MM-DD') : undefined
const dateTo = dateRange[1] ? dayjs(dateRange[1]).format('YYYY-MM-DD') : undefined
const toggleLogs = (bugId: string) => setShowLogs((prev) => ({ ...prev, [bugId]: !prev[bugId] }))
const toggleStackTrace = (bugId: string) => setShowStackTrace((prev) => ({ ...prev, [bugId]: !prev[bugId] }))
const { data, isLoading, refetch } = useQuery({
queryKey: ['bugs', { page, search, app, status }],
queryFn: () => fetch(API_URLS.getBugs(page, search, app, status)).then((r) => r.json()),
queryKey: ['bugs', { page, searchQuery, app, status, source, dateFrom, dateTo }],
queryFn: () => fetch(API_URLS.getBugs(page, searchQuery, app, status, source, dateFrom, dateTo)).then((r) => r.json()),
})
const { data: appsList } = useQuery({
@@ -411,7 +429,7 @@ function ListErrorsPage() {
</Modal>
<Paper withBorder radius="2xl" className="glass" p="md">
<SimpleGrid cols={{ base: 1, sm: 4 }} mb="lg">
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} mb="sm">
<TextInput
label="Search"
placeholder="Description, device, OS..."
@@ -444,12 +462,35 @@ function ListErrorsPage() {
onChange={(val) => setStatus(val || 'all')}
radius="md"
/>
<Select
label="Source"
size="sm"
data={[
{ value: 'all', label: 'All Sources' },
{ value: 'QC', label: 'QC' },
{ value: 'SYSTEM', label: 'System' },
{ value: 'USER', label: 'User' },
]}
value={source}
onChange={(val) => setSource(val || 'all')}
radius="md"
/>
<DatePickerInput
type="range"
label="Date Range"
placeholder="Pick date range"
size="sm"
radius="md"
value={dateRange}
onChange={setDateRange}
clearable
/>
<Stack justify="flex-end">
<Button
variant="filled"
color="violet"
size="sm"
onClick={() => { setSearch(''); setApp('all'); setStatus('all') }}
onClick={() => { setSearch(''); setApp('all'); setStatus('all'); setSource('all'); setDateRange([null, null]) }}
>
Reset Filters
</Button>