Skip to content

Commit

Permalink
Allow senators to be searchable (#451)
Browse files Browse the repository at this point in the history
* Allow senators to be searchable

* Fix React build errors
  • Loading branch information
iamlogand authored Mar 22, 2024
1 parent 70960b4 commit 53f06c6
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 48 deletions.
158 changes: 120 additions & 38 deletions frontend/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,56 @@ import CloseIcon from "@mui/icons-material/Close"
import { InputAdornment } from "@mui/material"
import termComponents from "@/componentTables/termComponents"
import { useGameContext } from "@/contexts/GameContext"
import TermLink from "./TermLink"
import SelectedDetail from "@/types/SelectedDetail"
import Senator from "@/classes/Senator"
import { Neutral100 } from "@/themes/colors"
import { useCookieContext } from "@/contexts/CookieContext"

interface SearchResult {
name: string
type: string
id?: number
sortingName?: string
}

const Search = () => {
const { dialog, setDialog } = useGameContext()
const { darkMode } = useCookieContext()
const { allSenators, dialog, setDialog, setSelectedDetail } = useGameContext()

const searchInputRef = useRef<HTMLInputElement>(null)
const [filteredTerms, setFilteredTerms] = useState<string[]>([])
const [filteredItems, setFilteredItems] = useState<SearchResult[]>([])
const [query, setQuery] = useState<string>("")

const handleQueryChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value)
},
[]
)

// Focus on search input when dialog opens
useEffect(() => {
if (searchInputRef.current) {
searchInputRef.current.focus()
}
}, [dialog])

// Filter terms based on the query
// Filter items based on the query
useEffect(() => {
const filteredTerms = Object.keys(termComponents).filter((term) =>
term.toLowerCase().includes(query.toLowerCase())
)
setFilteredTerms(filteredTerms)
}, [query])
const filteredTermItems = Object.keys(termComponents)
.filter((term) => term.toLowerCase().includes(query.toLowerCase()))
.map((term) => ({ name: term, type: "Term" }))
const filteredSenatorItems = allSenators.asArray
.filter((senator: Senator) =>
senator.displayName.toLowerCase().includes(query.toLowerCase())
)
.map((senator: Senator) => ({
name: senator.displayName,
type: "Senator",
id: senator.id,
sortingName: senator.name + String(senator.generation).padStart(3, "0"),
}))
const filteredItems = filteredTermItems
.concat(filteredSenatorItems)
.sort((a: SearchResult, b: SearchResult) =>
(a.sortingName ?? a.name).localeCompare(b.sortingName ?? b.name)
)

setFilteredItems(filteredItems)
}, [query, allSenators])

// Clear query when dialog closes
useEffect(() => {
Expand All @@ -45,14 +65,49 @@ const Search = () => {
}
}, [dialog])

const handleQueryChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value)
},
[]
)

const handleClick = useCallback(
(
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
item: SearchResult
) => {
event.preventDefault()
setDialog(null)
if (item.type === "Term") {
setSelectedDetail({
type: "Term",
name: item.name,
} as SelectedDetail)
} else {
setSelectedDetail({
type: item.type,
id: item.id,
} as SelectedDetail)
}
},
[setDialog, setSelectedDetail]
)

return (
<>
<IconButton
aria-label="debugging tools"
<button
aria-label="Search"
onClick={() => setDialog("search")}
className={
"p-2 px-3 border border-solid rounded flex min-w-[140px] items-center \
text-neutral-500 dark:text-neutral-300 bg-white dark:bg-neutral-650 \
border-neutral-300 dark:border-neutral-500 hover:border-neutral-400 dark:hover:border-neutral-300"
}
>
<SearchIcon />
</IconButton>
<span className="grow">Search...</span>
</button>
<Dialog
open={dialog === "search"}
onClose={() => setDialog(null)}
Expand All @@ -63,7 +118,14 @@ const Search = () => {
}
},
}}
maxWidth="xs"
fullWidth
>
<div className="absolute right-2 top-2 bg-inherit z-10">
<IconButton aria-label="close" onClick={() => setDialog(null)}>
<CloseIcon />
</IconButton>
</div>
<div className="p-6 pb-0 flex items-center gap-4">
<TextField
inputRef={searchInputRef}
Expand All @@ -76,31 +138,51 @@ const Search = () => {
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
<SearchIcon sx={darkMode ? { color: Neutral100 } : {}} />
</InputAdornment>
),
autoComplete: "off",
}}
/>
</div>
<div className="absolute right-2 top-2 bg-inherit">
<IconButton aria-label="close" onClick={() => setDialog(null)}>
<CloseIcon />
</IconButton>
</div>
<div className="m-6 h-[400px] border border-solid border-neutral-200 rounded shadow-inner overflow-y-scroll overflow-x-auto">
<div className="m-4 mx-6 box-border flex flex-col items-start gap-3">
{filteredTerms.map((term, index) => (
<div
key={index}
className="w-full flex items-center justify-between gap-4"
>
<span className="w-[300px]">
<TermLink name={term} />
<div
className={
"m-6 h-[400px] border border-solid rounded shadow-inner overflow-y-scroll overflow-x-auto\
border-neutral-200 dark:border-neutral-750 dark:bg-neutral-650"
}
>
{filteredItems.length > 0 ? (
<ul className="list-none p-0 m-2 box-border flex flex-col items-start gap-2">
{filteredItems.map((item: SearchResult, index: number) => (
<li key={index} className="w-full">
<button
onClick={(
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => handleClick(event, item)}
className={
"w-full px-4 py-2.5 rounded border-none flex items-center justify-between gap-4 \
bg-neutral-100 dark:bg-neutral-700 hover:text-white dark:hover:text-black hover:bg-tyrian-700 dark:hover:bg-tyrian-300"
}
>
<span>{item.name}</span>
<span className="grow text-sm flex justify-end">
{item.type}
</span>
</button>
</li>
))}
</ul>
) : (
<div className="flex items-center justify-center h-[100px]">
<span className="text-neutral-500 dark:text-neutral-200">
No results for &quot;
<span className="text-tyrian-600 dark:text-tyrian-200 text-lg">
{query}
</span>
<span className="text-neutral-400">Term</span>
</div>
))}
</div>
&quot;
</span>
</div>
)}
</div>
</Dialog>
</>
Expand Down
16 changes: 8 additions & 8 deletions frontend/components/SenatorPortrait.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,13 +280,6 @@ const SenatorPortrait = ({
)}
{!round && (
<>
{size > 120 && (
<Tooltip title="Senator ID" arrow>
<div className="absolute z-60 bottom-1 px-[6px] py-0.5 text-sm text-white bg-[rgba(0,_0,_0,_0.4)] user-none">
# {senator.code}
</div>
</Tooltip>
)}
{senator.alive === false && showIcons && (
<Image
src={DeadIcon}
Expand All @@ -297,10 +290,17 @@ const SenatorPortrait = ({
/>
)}
{debugShowEntityIds && (
<div className="z-100 absolute top-1 px-1 text-lg text-white bg-black/60 ">
<div className="z-40 absolute top-1 px-1 text-lg text-white bg-black/60 ">
{senator.id}
</div>
)}
{size > 120 && (
<Tooltip title="Senator ID" arrow>
<div className="absolute z-50 bottom-1 px-[6px] py-0.5 text-sm text-white bg-[rgba(0,_0,_0,_0.4)] user-none">
# {senator.code}
</div>
</Tooltip>
)}
</>
)}
</figure>
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/TermLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const TermLink = ({
includeIcon,
disabled,
}: TermLinkProps) => {
const { selectedDetail, setSelectedDetail, dialog, setDialog } =
const { selectedDetail, setSelectedDetail, setDialog } =
useGameContext()

// Use the name to get the correct image
Expand Down
2 changes: 1 addition & 1 deletion frontend/themes/darkTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const darkTheme = createTheme({
MuiDialog: {
styleOverrides: {
paper: {
backgroundColor: Neutral600,
backgroundColor: Neutral700,
},
},
},
Expand Down

0 comments on commit 53f06c6

Please sign in to comment.