Added search bar for expense list page (#52)

* Added search bar for expense list page

* Change search input styling

---------

Co-authored-by: Sebastien Castiel <sebastien@castiel.me>
This commit is contained in:
Ankit Bahl
2024-01-19 13:38:18 -08:00
committed by GitHub
parent 3735509fea
commit ae7cb2ccc8
2 changed files with 128 additions and 77 deletions

View File

@@ -1,6 +1,7 @@
'use client' 'use client'
import { CategoryIcon } from '@/app/groups/[groupId]/expenses/category-icon' import { CategoryIcon } from '@/app/groups/[groupId]/expenses/category-icon'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { SearchBar } from '@/components/ui/search-bar'
import { getGroupExpenses } from '@/lib/api' import { getGroupExpenses } from '@/lib/api'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Expense, Participant } from '@prisma/client' import { Expense, Participant } from '@prisma/client'
@@ -8,7 +9,7 @@ import dayjs, { type Dayjs } from 'dayjs'
import { ChevronRight } from 'lucide-react' import { ChevronRight } from 'lucide-react'
import Link from 'next/link' import Link from 'next/link'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { Fragment, useEffect } from 'react' import { Fragment, useEffect, useState } from 'react'
type Props = { type Props = {
expenses: Awaited<ReturnType<typeof getGroupExpenses>> expenses: Awaited<ReturnType<typeof getGroupExpenses>>
@@ -63,6 +64,7 @@ export function ExpenseList({
participants, participants,
groupId, groupId,
}: Props) { }: Props) {
const [searchText, setSearchText] = useState('')
useEffect(() => { useEffect(() => {
const activeUser = localStorage.getItem('newGroup-activeUser') const activeUser = localStorage.getItem('newGroup-activeUser')
const newUser = localStorage.getItem(`${groupId}-newUser`) const newUser = localStorage.getItem(`${groupId}-newUser`)
@@ -86,9 +88,10 @@ export function ExpenseList({
const router = useRouter() const router = useRouter()
const groupedExpensesByDate = getGroupedExpensesByDate(expenses) const groupedExpensesByDate = getGroupedExpensesByDate(expenses)
return expenses.length > 0 ? ( return expenses.length > 0 ? (
Object.values(EXPENSE_GROUPS).map((expenseGroup: string) => { <>
<SearchBar onChange={(e) => setSearchText(e.target.value)} />
{Object.values(EXPENSE_GROUPS).map((expenseGroup: string) => {
const groupExpenses = groupedExpensesByDate[expenseGroup] const groupExpenses = groupedExpensesByDate[expenseGroup]
if (!groupExpenses) return null if (!groupExpenses) return null
return ( return (
@@ -100,7 +103,13 @@ export function ExpenseList({
> >
{expenseGroup} {expenseGroup}
</div> </div>
{groupExpenses.map((expense: any) => ( {groupExpenses
.filter(
(exp) =>
exp.title.toLowerCase().match(searchText.toLowerCase()) !==
null,
)
.map((expense: any) => (
<div <div
key={expense.id} key={expense.id}
className={cn( className={cn(
@@ -108,7 +117,9 @@ export function ExpenseList({
expense.isReimbursement && 'italic', expense.isReimbursement && 'italic',
)} )}
onClick={() => { onClick={() => {
router.push(`/groups/${groupId}/expenses/${expense.id}/edit`) router.push(
`/groups/${groupId}/expenses/${expense.id}/edit`,
)
}} }}
> >
<CategoryIcon <CategoryIcon
@@ -117,13 +128,17 @@ export function ExpenseList({
/> />
<div className="flex-1"> <div className="flex-1">
<div <div
className={cn('mb-1', expense.isReimbursement && 'italic')} className={cn(
'mb-1',
expense.isReimbursement && 'italic',
)}
> >
{expense.title} {expense.title}
</div> </div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
Paid by{' '} Paid by{' '}
<strong>{getParticipant(expense.paidById)?.name}</strong> for{' '} <strong>{getParticipant(expense.paidById)?.name}</strong>{' '}
for{' '}
{expense.paidFor.map((paidFor: any, index: number) => ( {expense.paidFor.map((paidFor: any, index: number) => (
<Fragment key={index}> <Fragment key={index}>
{index !== 0 && <>, </>} {index !== 0 && <>, </>}
@@ -157,7 +172,9 @@ export function ExpenseList({
className="self-center hidden sm:flex" className="self-center hidden sm:flex"
asChild asChild
> >
<Link href={`/groups/${groupId}/expenses/${expense.id}/edit`}> <Link
href={`/groups/${groupId}/expenses/${expense.id}/edit`}
>
<ChevronRight className="w-4 h-4" /> <ChevronRight className="w-4 h-4" />
</Link> </Link>
</Button> </Button>
@@ -165,7 +182,8 @@ export function ExpenseList({
))} ))}
</div> </div>
) )
}) })}
</>
) : ( ) : (
<p className="px-6 text-sm py-6"> <p className="px-6 text-sm py-6">
Your group doesnt contain any expense yet.{' '} Your group doesnt contain any expense yet.{' '}

View File

@@ -0,0 +1,33 @@
import * as React from "react"
import {Input} from "@/components/ui/input";
import {cn} from "@/lib/utils";
import {
Search
} from 'lucide-react'
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const SearchBar = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<div className="mx-4 sm:mx-6 flex relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Input
type={type}
className={cn(
"pl-10 text-sm focus:text-base bg-muted border-none text-muted-foreground",
className
)}
ref={ref}
placeholder="Search for an expense…"
{...props}
/>
</div>
)
}
)
SearchBar.displayName = "SearchBar"
export { SearchBar }