feat: add table search filter component and integrate with tags page

This commit is contained in:
fredy.siswanto 2025-03-13 14:05:56 +07:00
parent 8a9cacf7b4
commit ff51941647
3 changed files with 89 additions and 7 deletions

View File

@ -0,0 +1,69 @@
import { Field, Input, Label, Select } from '@headlessui/react'
import { MagnifyingGlassIcon } from '@heroicons/react/20/solid'
import { useState } from 'react'
interface SearchFilterProperties {
title: string
columns?: string[]
onSearch: (value: string) => void
onStatusFilter?: (value: string) => Promise<void>
}
export const TableSearchFilter: React.FC<SearchFilterProperties> = ({
onSearch,
onStatusFilter,
title,
}: SearchFilterProperties) => {
const [searchTerm, setSearchTerm] = useState<string>('')
const [statusFilter, setStatusFilter] = useState<string>('')
const handleSearch = (searchValue: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(searchValue.target.value)
onSearch(searchValue.target.value)
}
const handleStatusFilter = (
searchValue: React.ChangeEvent<HTMLSelectElement>,
) => {
setStatusFilter(searchValue.target.value)
onStatusFilter?.(searchValue.target.value)
}
return (
<div className="flex items-center gap-5 rounded-lg bg-gray-50 text-[#363636]">
<div className="w-[400px]">
<Field>
<Label className="mb-2 block text-sm font-medium">Cari {title}</Label>
<div className="relative">
<Input
type="text"
value={searchTerm}
onChange={handleSearch}
placeholder={`Cari Nama ${title}`}
className="w-full rounded-lg bg-white p-2 pr-10 pl-4 shadow focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none"
/>
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
<MagnifyingGlassIcon className="h-5 w-5 text-[#363636]" />
</div>
</div>
</Field>
</div>
{onStatusFilter && (
// will handel if filter have dropdown status
<div className="w-[235px]">
<Field>
<Label className="mb-2 block text-sm font-medium">Status</Label>
<Select
value={statusFilter}
onChange={handleStatusFilter}
className="w-full rounded-lg bg-white p-2 shadow focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none"
>
<option>Pilih Status</option>
<option>Aktif</option>
<option>Nonaktif</option>
</Select>
</Field>
</div>
)}
</div>
)
}

View File

@ -32,9 +32,9 @@ export const UiTable: React.FC<UiTableProperties> = ({
thead.classList.add('text-left') thead.classList.add('text-left')
}, },
paging: true, paging: true,
searching: true, searching: false,
ordering: true, ordering: true,
info: true, info: false,
language: { language: {
paginate: { paginate: {
first: renderPaginationIcon( first: renderPaginationIcon(

View File

@ -12,6 +12,7 @@ import type { TTagResponse } from '~/apis/common/get-tags'
import { DialogDelete } from '~/components/dialog/delete' import { DialogDelete } from '~/components/dialog/delete'
import { Button } from '~/components/ui/button' import { Button } from '~/components/ui/button'
import { UiTable } from '~/components/ui/table' import { UiTable } from '~/components/ui/table'
import { TableSearchFilter } from '~/components/ui/table-search'
import { TitleDashboard } from '~/components/ui/title-dashboard' import { TitleDashboard } from '~/components/ui/title-dashboard'
import type { loader } from '~/routes/_admin.lg-admin._dashboard' import type { loader } from '~/routes/_admin.lg-admin._dashboard'
@ -19,6 +20,7 @@ export const TagsPage = () => {
const loaderData = useRouteLoaderData<typeof loader>( const loaderData = useRouteLoaderData<typeof loader>(
'routes/_admin.lg-admin._dashboard', 'routes/_admin.lg-admin._dashboard',
) )
const [searchTerm, setSearchTerm] = useState<string>('')
const [selectedTag, setSelectedTag] = useState<TTagResponse>() const [selectedTag, setSelectedTag] = useState<TTagResponse>()
const { tagsData: dataTable } = loaderData || {} const { tagsData: dataTable } = loaderData || {}
@ -73,16 +75,27 @@ export const TagsPage = () => {
} }
const dataOptions: Config = { const dataOptions: Config = {
paging: true, paging: true,
searching: true,
ordering: true,
info: true, info: true,
} }
const filteredData = dataTable?.filter((item) => {
const matchesSearch = Object.keys(item).some((key) =>
item[key as keyof TTagResponse]
?.toString()
.toLowerCase()
.includes(searchTerm.toLowerCase()),
)
return matchesSearch
})
return ( return (
<div className="relative"> <div className="relative">
<TitleDashboard title="Tags" /> <TitleDashboard title="Tags" />
<div className="mb-8 flex items-end justify-between gap-5"> <div className="mb-8 flex items-end justify-between gap-5">
<div className="flex-1">{/* TODO: Filter */}</div> <div className="flex-1">
<TableSearchFilter
onSearch={setSearchTerm}
title="Tag"
/>
</div>
<Button <Button
as={Link} as={Link}
to="/lg-admin/tags/create" to="/lg-admin/tags/create"
@ -94,7 +107,7 @@ export const TagsPage = () => {
</div> </div>
<UiTable <UiTable
data={dataTable || []} data={filteredData || []}
columns={dataColumns} columns={dataColumns}
options={dataOptions} options={dataOptions}
slots={dataSlot} slots={dataSlot}