Skip to content

Commit

Permalink
feat(closes #525): List search: added SearchBar, added new filter to …
Browse files Browse the repository at this point in the history
…inventory (#526)

* #525 List search: added SearchBar, added new filter to inventory

* #525 List search: requested changes

* List search: requested changes

* List search: requested changes

* fix(inventory): make tests pass

* removed jest

* Fixed seeds and crops categories, added test as requested.

* Fixed seeds and crops categories, added test as requested.

* refactor(inventory): use module import paths

---------

Co-authored-by: Jeremy Kahn <me@jeremyckahn.com>
  • Loading branch information
az1plo and jeremyckahn authored Nov 29, 2024
1 parent 2b01a75 commit 0e0be98
Show file tree
Hide file tree
Showing 16 changed files with 797 additions and 300 deletions.
24 changes: 24 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
"typeface-francois-one": "0.0.71",
"typeface-public-sans": "^1.1.4",
"url": "^0.11.0",
"usehooks-ts": "^3.1.0",
"uuid": "^3.4.0",
"zlib": "npm:browserify-zlib@^0.2.0"
},
Expand Down
27 changes: 25 additions & 2 deletions src/components/Cellar/CellarInventoryTabPanel.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/** @typedef {import("../../index").farmhand.keg} keg */
import React, { useContext } from 'react'
import React, { useContext, useState } from 'react'
import { number } from 'prop-types'
import Divider from '@mui/material/Divider/index.js'
import Card from '@mui/material/Card/index.js'
import CardContent from '@mui/material/CardContent/index.js'
import ReactMarkdown from 'react-markdown'

import SearchBar from '../SearchBar/index.js'
import FarmhandContext from '../Farmhand/Farmhand.context.js'
import {
KEG_INTEREST_RATE,
Expand All @@ -15,6 +16,8 @@ import {
} from '../../constants.js'

import { integerString } from '../../utils/index.js'
import { itemsMap } from '../../data/maps.js'
import { FERMENTED_CROP_NAME } from '../../templates.js'

import { TabPanel } from './TabPanel/index.js'
import { Keg } from './Keg.js'
Expand All @@ -25,6 +28,8 @@ import { Keg } from './Keg.js'
* @param {number} props.currentTab
*/
export const CellarInventoryTabPanel = ({ index, currentTab }) => {
const [searchQuery, setSearchQuery] = useState('')

/**
* @type {{
* gameState: {
Expand All @@ -37,14 +42,32 @@ export const CellarInventoryTabPanel = ({ index, currentTab }) => {
gameState: { cellarInventory, purchasedCellar },
} = useContext(FarmhandContext)

const searchTerms = searchQuery
.toLowerCase()
.split(' ')
.filter(term => term.length > 0)

const filteredKegs = cellarInventory.filter(keg => {
const item = itemsMap[keg.itemId]
const itemName = item.name.toLowerCase()
const fermentationRecipeName = `${FERMENTED_CROP_NAME}${itemName}`

return searchTerms.every(
term => fermentationRecipeName.includes(term) || itemName.includes(term)
)
})

return (
<TabPanel value={currentTab} index={index}>
<h3>
Capacity: {integerString(cellarInventory.length)} /{' '}
{integerString(PURCHASEABLE_CELLARS.get(purchasedCellar).space)}
</h3>
{cellarInventory.length > 0 && (
<SearchBar placeholder="Search kegs..." onSearch={setSearchQuery} />
)}
<ul className="card-list">
{cellarInventory.map(keg => (
{filteredKegs.map(keg => (
<li key={keg.id}>
<Keg keg={keg} />
</li>
Expand Down
223 changes: 142 additions & 81 deletions src/components/CowPenContextMenu/CowPenContextMenu.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import { array, func, number, object, string } from 'prop-types'
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward.js'
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward.js'
Expand All @@ -23,9 +23,9 @@ import { PURCHASEABLE_COW_PENS } from '../../constants.js'
import cowShopInventory from '../../data/shop-inventory-cow.js'

import CowCard from '../CowCard/index.js'
import SearchBar from '../SearchBar/index.js'

import { TabPanel, a11yProps } from './TabPanel/index.js'

import './CowPenContextMenu.sass'

const { AGE, COLOR, GENDER, HAPPINESS, VALUE, WEIGHT } = enumify([
Expand Down Expand Up @@ -84,6 +84,23 @@ export const CowPenContextMenu = ({
const [sortType, setSortType] = useState(AGE)
const [isAscending, setIsAscending] = useState(false)
const [currentTab, setCurrentTab] = useState(0)
const [searchQuery, setSearchQuery] = useState('')

useEffect(() => {
setSearchQuery('')
}, [currentTab])

const filteredCowInventory = searchQuery
? cowInventory.filter(cow =>
cow.name.toLowerCase().includes(searchQuery.toLowerCase())
)
: cowInventory

const filteredShopInventory = searchQuery
? cowShopInventory.filter(item =>
item.name.toLowerCase().includes(searchQuery.toLowerCase())
)
: cowShopInventory

return (
<div className="CowPenContextMenu">
Expand All @@ -108,95 +125,139 @@ export const CowPenContextMenu = ({
Capacity: {cowInventory.length} /{' '}
{PURCHASEABLE_COW_PENS.get(purchasedCowPen).cows}
</h3>
{cowInventory.length > 1 && (
<div {...{ className: 'sort-wrapper' }}>
<Fab
{...{
'aria-label': 'Toggle sorting order',
onClick: () => setIsAscending(!isAscending),
color: 'primary',
}}
>
{isAscending ? <ArrowUpwardIcon /> : <ArrowDownwardIcon />}
</Fab>
<Select
variant="standard"
{...{
className: 'sort-select',
displayEmpty: true,
value: sortType,
onChange: ({ target: { value } }) => setSortType(value),
}}
>
<MenuItem {...{ value: VALUE }}>Sort by Value</MenuItem>
<MenuItem {...{ value: AGE }}>Sort by Age</MenuItem>
<MenuItem {...{ value: HAPPINESS }}>Sort by Happiness</MenuItem>
<MenuItem {...{ value: WEIGHT }}>Sort by Weight</MenuItem>
<MenuItem {...{ value: GENDER }}>Sort by Gender</MenuItem>
<MenuItem {...{ value: COLOR }}>Sort by Color</MenuItem>
</Select>
</div>

{cowInventory.length > 0 && (
<SearchBar
placeholder="Search cows by name..."
onSearch={setSearchQuery}
/>
)}

<ul className="card-list purchased-cows">
{sortCows(cowInventory, sortType, isAscending).map(cow =>
isCowInBreedingPen(cow, cowBreedingPen) ? null : (
<li
{...{
key: cow.id,
onFocus: () => handleCowSelect(cow),
onClick: () => handleCowSelect(cow),
}}
>
<CowCard
{filteredCowInventory.length > 0 && (
<>
{filteredCowInventory.length > 1 && (
<div {...{ className: 'sort-wrapper' }}>
<Fab
{...{
cow,
handleCowAutomaticHugChange,
handleCowBreedChange,
handleCowHugClick,
handleCowNameInputChange,
handleCowOfferClick,
handleCowSellClick,
handleCowWithdrawClick,
isCowPurchased: true,
isSelected: cow.id === selectedCowId,
'aria-label': 'Toggle sorting order',
onClick: () => setIsAscending(!isAscending),
color: 'primary',
}}
/>
</li>
)
)}
</ul>
</TabPanel>
<TabPanel value={currentTab} index={1}>
<h3>Capacity: {numberOfCowsBreeding(cowBreedingPen)} / 2</h3>
<ul className="card-list purchased-cows breeding-cows">
{nullArray(numberOfCowsBreeding(cowBreedingPen)).map((_null, i) => {
const cowId = cowBreedingPen[`cowId${i + 1}`]
const cow = findCowById(cowInventory, cowId)
return (
<li {...{ key: cowId }}>
<CowCard
>
{isAscending ? <ArrowUpwardIcon /> : <ArrowDownwardIcon />}
</Fab>
<Select
variant="standard"
{...{
cow,
handleCowAutomaticHugChange,
handleCowBreedChange,
handleCowHugClick,
handleCowNameInputChange,
handleCowOfferClick,
handleCowSellClick,
handleCowWithdrawClick,
isCowPurchased: true,
isSelected: cow.id === selectedCowId,
className: 'sort-select',
displayEmpty: true,
value: sortType,
onChange: ({ target: { value } }) => setSortType(value),
}}
>
<MenuItem {...{ value: VALUE }}>Sort by Value</MenuItem>
<MenuItem {...{ value: AGE }}>Sort by Age</MenuItem>
<MenuItem {...{ value: HAPPINESS }}>
Sort by Happiness
</MenuItem>
<MenuItem {...{ value: WEIGHT }}>Sort by Weight</MenuItem>
<MenuItem {...{ value: GENDER }}>Sort by Gender</MenuItem>
<MenuItem {...{ value: COLOR }}>Sort by Color</MenuItem>
</Select>
</div>
)}

<ul className="card-list purchased-cows">
{sortCows(filteredCowInventory, sortType, isAscending).map(cow =>
isCowInBreedingPen(cow, cowBreedingPen) ? null : (
<li
{...{
key: cow.id,
onFocus: () => handleCowSelect(cow),
onClick: () => handleCowSelect(cow),
}}
>
<CowCard
{...{
cow,
handleCowAutomaticHugChange,
handleCowBreedChange,
handleCowHugClick,
handleCowNameInputChange,
handleCowOfferClick,
handleCowSellClick,
handleCowWithdrawClick,
isCowPurchased: true,
isSelected: cow.id === selectedCowId,
}}
/>
</li>
)
)}
</ul>
</>
)}
</TabPanel>
<TabPanel value={currentTab} index={1}>
{(() => {
const filteredCows = nullArray(numberOfCowsBreeding(cowBreedingPen))
.map((_null, i) => {
const cowId = cowBreedingPen[`cowId${i + 1}`]
const cow = findCowById(cowInventory, cowId)

if (
!cow ||
!cow.name.toLowerCase().includes(searchQuery.toLowerCase())
) {
return null
}

return cow
})
.filter(Boolean)

return (
<>
<h3>Capacity: {numberOfCowsBreeding(cowBreedingPen)} / 2</h3>
{cowInventory.length > 0 && (
<SearchBar
placeholder="Search cows by name..."
onSearch={setSearchQuery}
/>
</li>
)
})}
</ul>
)}
<ul className="card-list purchased-cows breeding-cows">
{filteredCows.map(cow => (
<li key={cow.id}>
<CowCard
{...{
cow,
handleCowAutomaticHugChange,
handleCowBreedChange,
handleCowHugClick,
handleCowNameInputChange,
handleCowOfferClick,
handleCowSellClick,
handleCowWithdrawClick,
isCowPurchased: true,
isSelected: cow.id === selectedCowId,
}}
/>
</li>
))}
</ul>
</>
)
})()}
</TabPanel>
<TabPanel value={currentTab} index={2}>
{cowShopInventory.length > 0 && (
<SearchBar
placeholder="Search supplies..."
onSearch={setSearchQuery}
/>
)}
<ul className="card-list">
{cowShopInventory.map(item => (
{filteredShopInventory.map(item => (
<li key={item.id}>
<Item
{...{
Expand Down
Loading

0 comments on commit 0e0be98

Please sign in to comment.