Converted web app to react + js
This commit is contained in:
214
src/pages/SearchPage/SearchPage.jsx
Normal file
214
src/pages/SearchPage/SearchPage.jsx
Normal file
@@ -0,0 +1,214 @@
|
||||
import { useState, useMemo } from 'react'
|
||||
import ItemCard from '../../components/ItemCard/ItemCard.jsx'
|
||||
import { searchInventory, getLocations, formatDate, getExpiryStatus } from '../../utils/searchUtils.js'
|
||||
import { getPaginatedResults } from '../../utils/paginationUtils.js'
|
||||
import './SearchPage.css'
|
||||
|
||||
function ExpiryBadge({ expiryDate }) {
|
||||
const status = getExpiryStatus(expiryDate)
|
||||
const badgeClass = status.status === 'Expired' ? 'expiry-expired'
|
||||
: (status.status === 'Soon' || status.status === 'Today') ? 'expiry-soon'
|
||||
: 'expiry-fresh'
|
||||
|
||||
return (
|
||||
<div className={`item-expiry-badge ${badgeClass}`}>
|
||||
{formatDate(expiryDate)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SearchPage() {
|
||||
const locations = useMemo(() => getLocations(), [])
|
||||
|
||||
const [searchName, setSearchName] = useState('')
|
||||
const [location, setLocation] = useState('All')
|
||||
const [minQuantity, setMinQuantity] = useState('0')
|
||||
const [maxQuantity, setMaxQuantity] = useState('999')
|
||||
const [minExpiryDate, setMinExpiryDate] = useState('')
|
||||
const [maxExpiryDate, setMaxExpiryDate] = useState('')
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(15)
|
||||
const [results, setResults] = useState(() => searchInventory())
|
||||
|
||||
function performSearch() {
|
||||
const minQty = parseInt(minQuantity) || 0
|
||||
const maxQty = parseInt(maxQuantity) || Infinity
|
||||
|
||||
if (minExpiryDate && maxExpiryDate && minExpiryDate > maxExpiryDate) {
|
||||
alert('Start date cannot be after end date')
|
||||
return
|
||||
}
|
||||
|
||||
setCurrentPage(1)
|
||||
setResults(searchInventory(searchName, location, minQty, maxQty, minExpiryDate, maxExpiryDate))
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
setSearchName('')
|
||||
setLocation('All')
|
||||
setMinQuantity('0')
|
||||
setMaxQuantity('999')
|
||||
setMinExpiryDate('')
|
||||
setMaxExpiryDate('')
|
||||
setCurrentPage(1)
|
||||
setPageSize(15)
|
||||
setResults(searchInventory())
|
||||
}
|
||||
|
||||
const paginationData = getPaginatedResults(results, currentPage, pageSize)
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Search Inventory</h2>
|
||||
<hr />
|
||||
|
||||
<div className="search-container">
|
||||
<div className="search-form">
|
||||
<div className="form-group">
|
||||
<label htmlFor="search-name">Item Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="search-name"
|
||||
placeholder="e.g., Milk, Pasta, Chicken..."
|
||||
autoComplete="off"
|
||||
value={searchName}
|
||||
onChange={e => setSearchName(e.target.value)}
|
||||
onKeyDown={e => { if (e.key === 'Enter') performSearch() }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="search-location">Storage Location:</label>
|
||||
<select id="search-location" value={location} onChange={e => setLocation(e.target.value)}>
|
||||
{locations.map(loc => (
|
||||
<option key={loc} value={loc}>{loc}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Quantity Range:</label>
|
||||
<div className="quantity-range">
|
||||
<div className="form-group">
|
||||
<label htmlFor="min-quantity" style={{ fontSize: '12px' }}>Min:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="min-quantity"
|
||||
min="0"
|
||||
value={minQuantity}
|
||||
onChange={e => setMinQuantity(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="max-quantity" style={{ fontSize: '12px' }}>Max:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="max-quantity"
|
||||
min="0"
|
||||
value={maxQuantity}
|
||||
onChange={e => setMaxQuantity(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Expiry Date Range:</label>
|
||||
<div className="date-range">
|
||||
<div className="form-group">
|
||||
<label htmlFor="min-expiry-date" style={{ fontSize: '12px' }}>Start Date:</label>
|
||||
<input
|
||||
type="date"
|
||||
id="min-expiry-date"
|
||||
value={minExpiryDate}
|
||||
onChange={e => setMinExpiryDate(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="max-expiry-date" style={{ fontSize: '12px' }}>End Date:</label>
|
||||
<input
|
||||
type="date"
|
||||
id="max-expiry-date"
|
||||
value={maxExpiryDate}
|
||||
onChange={e => setMaxExpiryDate(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="search-buttons">
|
||||
<button type="button" className="btn btn-primary" onClick={performSearch}>Search</button>
|
||||
<button type="button" className="btn btn-secondary" onClick={resetForm}>Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="results-container">
|
||||
<div className="results-info">
|
||||
{results.length === 0
|
||||
? 'No results'
|
||||
: `Found ${results.length} item${results.length !== 1 ? 's' : ''} (showing ${paginationData.startIndex}-${paginationData.endIndex})`
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="pagination-controls">
|
||||
<div className="pagination-size">
|
||||
<label htmlFor="page-size-select">Items per page:</label>
|
||||
<select
|
||||
id="page-size-select"
|
||||
value={pageSize}
|
||||
onChange={e => { setPageSize(parseInt(e.target.value)); setCurrentPage(1) }}
|
||||
>
|
||||
<option value="15">15</option>
|
||||
<option value="30">30</option>
|
||||
<option value="60">60</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="pagination-nav">
|
||||
<button
|
||||
type="button"
|
||||
className="btn"
|
||||
disabled={paginationData.currentPage === 1}
|
||||
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
|
||||
>
|
||||
← Previous
|
||||
</button>
|
||||
<div className="pagination-info">
|
||||
Page {paginationData.currentPage} of {paginationData.totalPages}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn"
|
||||
disabled={paginationData.currentPage === paginationData.totalPages}
|
||||
onClick={() => setCurrentPage(p => p + 1)}
|
||||
>
|
||||
Next →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{results.length === 0 ? (
|
||||
<div className="no-results">No items found matching your search criteria.</div>
|
||||
) : (
|
||||
<div className="item-component-grid">
|
||||
{paginationData.items.map(item => (
|
||||
<ItemCard
|
||||
key={item.id}
|
||||
imgSrc={item.img}
|
||||
imgAlt={item.name}
|
||||
text1={item.name}
|
||||
text2={`${item.location} — ${item.quantity} ${item.unit}`}
|
||||
text3={`Expires: ${formatDate(item.expiryDate)}`}
|
||||
editable={false}
|
||||
>
|
||||
<ExpiryBadge expiryDate={item.expiryDate} />
|
||||
</ItemCard>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchPage
|
||||
Reference in New Issue
Block a user