Added expiry date to the search page
This commit is contained in:
60
README.md
60
README.md
@@ -8,7 +8,7 @@ Webpage Playground is a lightweight, responsive web application for managing and
|
|||||||
|
|
||||||
- **Dynamic Navigation** — Easy page switching with a responsive navbar
|
- **Dynamic Navigation** — Easy page switching with a responsive navbar
|
||||||
- **Inventory Demo** — Display items using reusable item components
|
- **Inventory Demo** — Display items using reusable item components
|
||||||
- **Advanced Search** — Filter inventory by name, location, and quantity range
|
- **Advanced Search** — Filter inventory by name, location, quantity range, and expiry date
|
||||||
- **Barcode Scanner** — Scan barcodes for inventory management (keyboard and hardware scanner support)
|
- **Barcode Scanner** — Scan barcodes for inventory management (keyboard and hardware scanner support)
|
||||||
- **Data Visualization** — Pie chart showing inventory distribution
|
- **Data Visualization** — Pie chart showing inventory distribution
|
||||||
- **Responsive Design** — Works on desktop, tablet, and mobile devices
|
- **Responsive Design** — Works on desktop, tablet, and mobile devices
|
||||||
@@ -17,7 +17,7 @@ Webpage Playground is a lightweight, responsive web application for managing and
|
|||||||
|
|
||||||
### Pages
|
### Pages
|
||||||
- `index.html` — Homepage with item component demo and inventory pie chart
|
- `index.html` — Homepage with item component demo and inventory pie chart
|
||||||
- `search.html` — Search and filter inventory by name, location, and quantity
|
- `search.html` — Search and filter inventory by name, location, quantity, and expiry date
|
||||||
- `barcode.html` — Barcode scanner with console logging for testing
|
- `barcode.html` — Barcode scanner with console logging for testing
|
||||||
- `pantry.html` — Pantry inventory container
|
- `pantry.html` — Pantry inventory container
|
||||||
- `fridge.html` — Fridge inventory container
|
- `fridge.html` — Fridge inventory container
|
||||||
@@ -27,7 +27,7 @@ Webpage Playground is a lightweight, responsive web application for managing and
|
|||||||
- `navbar.js` — Dynamic navigation bar system loaded by all pages
|
- `navbar.js` — Dynamic navigation bar system loaded by all pages
|
||||||
- `main.css` — Global styles and responsive design
|
- `main.css` — Global styles and responsive design
|
||||||
- `item-component.js` — Reusable ES6 module for displaying inventory items in a grid
|
- `item-component.js` — Reusable ES6 module for displaying inventory items in a grid
|
||||||
- `search.js` — Search and filtering logic with 20 sample inventory items
|
- `search.js` — Search and filtering logic with 20 sample inventory items including expiry dates
|
||||||
- `barcode-scanner.js` — Barcode capture and console logging module
|
- `barcode-scanner.js` — Barcode capture and console logging module
|
||||||
- `piechart.js` — Inventory distribution pie chart visualization
|
- `piechart.js` — Inventory distribution pie chart visualization
|
||||||
|
|
||||||
@@ -55,20 +55,33 @@ Advanced inventory search and filtering interface.
|
|||||||
- Search by item name (case-insensitive)
|
- Search by item name (case-insensitive)
|
||||||
- Filter by storage location (All, Pantry, Fridge, Freezer)
|
- Filter by storage location (All, Pantry, Fridge, Freezer)
|
||||||
- Filter by quantity range (min/max values)
|
- Filter by quantity range (min/max values)
|
||||||
|
- **NEW: Filter by expiry date range (start date and end date)**
|
||||||
- Real-time result display with item cards
|
- Real-time result display with item cards
|
||||||
|
- Visual expiry status indicators (Fresh, Expiring Soon, Expired)
|
||||||
- Reset button to clear all filters
|
- Reset button to clear all filters
|
||||||
- Keyboard support (press Enter to search)
|
- Keyboard support (press Enter to search)
|
||||||
|
- Date range validation (start date ≤ end date)
|
||||||
|
|
||||||
|
**Expiry Date Features:**
|
||||||
|
- Each inventory item includes an expiry date
|
||||||
|
- Visual badges show expiry status:
|
||||||
|
- 🟢 **Fresh** — Items expiring in more than 7 days
|
||||||
|
- 🟠 **Expiring Soon** — Items expiring within 7 days
|
||||||
|
- 🔴 **Expired** — Items past expiry date
|
||||||
|
- Formatted expiry dates displayed on item cards
|
||||||
|
- Filter by start and end date to find items expiring in a specific timeframe
|
||||||
|
|
||||||
**Sample Usage:**
|
**Sample Usage:**
|
||||||
```
|
```
|
||||||
Search for "milk" in the Fridge with quantity >= 1
|
Search for items expiring between March 15 and April 15
|
||||||
Results displayed using item-component for consistency
|
Results show all items with expiry dates in that range
|
||||||
|
Items display formatted dates and expiry status badges
|
||||||
```
|
```
|
||||||
|
|
||||||
**Available Items (20 total across all locations):**
|
**Available Items (20 total across all locations):**
|
||||||
- Pantry: Pasta, Rice, Cereal, Flour, Sugar, Salt, Olive Oil, Canned Beans
|
- Pantry: Pasta (120 days), Rice (180 days), Cereal (60 days), Flour (150 days), Sugar (200 days), Salt (365 days), Olive Oil (90 days), Canned Beans (EXPIRED -10 days)
|
||||||
- Fridge: Milk, Cheese, Greek Yogurt, Eggs, Butter, Chicken Salad
|
- Fridge: Milk (5 days), Cheese (30 days), Greek Yogurt (7 days), Eggs (21 days), Butter (45 days), Chicken Salad (EXPIRED -2 days)
|
||||||
- Freezer: Ice Cream, Frozen Vegetables, Chicken Breast, Ground Beef, Pizza, Ice
|
- Freezer: Ice Cream (90 days), Frozen Vegetables (180 days), Chicken Breast (120 days), Ground Beef (150 days), Pizza (200 days), Ice (365 days)
|
||||||
|
|
||||||
### 📱 Barcode Scanner (`barcode.html`)
|
### 📱 Barcode Scanner (`barcode.html`)
|
||||||
Barcode capture interface with console logging for testing.
|
Barcode capture interface with console logging for testing.
|
||||||
@@ -129,7 +142,7 @@ python -m http.server 8000
|
|||||||
### Navigation
|
### Navigation
|
||||||
The navbar appears at the top of every page and provides links to:
|
The navbar appears at the top of every page and provides links to:
|
||||||
- Homepage — Main demo page with pie chart
|
- Homepage — Main demo page with pie chart
|
||||||
- Search — Advanced inventory search and filtering
|
- Search — Advanced inventory search and filtering with expiry date support
|
||||||
- Barcode Scanner — Barcode capture interface (testing via console)
|
- Barcode Scanner — Barcode capture interface (testing via console)
|
||||||
- Pantry, Fridge, Freezer — Individual storage location pages
|
- Pantry, Fridge, Freezer — Individual storage location pages
|
||||||
|
|
||||||
@@ -139,10 +152,16 @@ The navbar appears at the top of every page and provides links to:
|
|||||||
- Item name (optional)
|
- Item name (optional)
|
||||||
- Storage location (optional, defaults to "All")
|
- Storage location (optional, defaults to "All")
|
||||||
- Quantity range (optional, defaults to 0-999)
|
- Quantity range (optional, defaults to 0-999)
|
||||||
|
- **Expiry date range (optional) — Leave blank to ignore**
|
||||||
3. Click "Search" or press Enter
|
3. Click "Search" or press Enter
|
||||||
4. Results display as item cards using the item component
|
4. Results display as item cards using the item component with expiry status badges
|
||||||
5. Click "Reset" to clear all filters
|
5. Click "Reset" to clear all filters
|
||||||
|
|
||||||
|
**Expiry Date Search Examples:**
|
||||||
|
- Find items expiring soon: Set start date to today, end date to 7 days from today
|
||||||
|
- Find expired items: Set start date to 100 days ago, end date to today
|
||||||
|
- Find fresh items: Set start date to tomorrow, end date to 90 days from today
|
||||||
|
|
||||||
### Using the Barcode Scanner
|
### Using the Barcode Scanner
|
||||||
1. Open the Barcode Scanner page from the navbar
|
1. Open the Barcode Scanner page from the navbar
|
||||||
2. Click in the input field (auto-focused)
|
2. Click in the input field (auto-focused)
|
||||||
@@ -166,7 +185,7 @@ The `item-component` is a reusable UI building block used throughout the app. Se
|
|||||||
imgAlt: 'Milk',
|
imgAlt: 'Milk',
|
||||||
text1: 'Milk',
|
text1: 'Milk',
|
||||||
text2: 'Fridge — 1 carton',
|
text2: 'Fridge — 1 carton',
|
||||||
text3: 'Expires in 5 days'
|
text3: 'Expires Mar 15, 2026'
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('container').appendChild(item);
|
document.getElementById('container').appendChild(item);
|
||||||
@@ -179,15 +198,26 @@ The `item-component` is a reusable UI building block used throughout the app. Se
|
|||||||
Edit `search.js` and add items to the `inventoryData` array:
|
Edit `search.js` and add items to the `inventoryData` array:
|
||||||
```javascript
|
```javascript
|
||||||
export const inventoryData = [
|
export const inventoryData = [
|
||||||
{ id: 21, name: 'Coffee', location: 'Pantry', quantity: 2, unit: 'bags', img: 'https://picsum.photos/seed/coffee/200/200' },
|
{
|
||||||
|
id: 21,
|
||||||
|
name: 'Coffee',
|
||||||
|
location: 'Pantry',
|
||||||
|
quantity: 2,
|
||||||
|
unit: 'bags',
|
||||||
|
expiryDate: '2026-06-15',
|
||||||
|
img: 'https://picsum.photos/seed/coffee/200/200'
|
||||||
|
},
|
||||||
// ... more items
|
// ... more items
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Date Format:** Use ISO 8601 format (YYYY-MM-DD) for expiry dates.
|
||||||
|
|
||||||
### Styling
|
### Styling
|
||||||
- Override styles in `main.css` for global changes
|
- Override styles in `main.css` for global changes
|
||||||
- Page-specific styles are included in `<style>` tags within each HTML file
|
- Page-specific styles are included in `<style>` tags within each HTML file
|
||||||
- The item component injects responsive grid CSS automatically
|
- The item component injects responsive grid CSS automatically
|
||||||
|
- Expiry badge styles can be customized via `.expiry-fresh`, `.expiry-soon`, `.expiry-expired` classes
|
||||||
|
|
||||||
### Creating New Pages
|
### Creating New Pages
|
||||||
1. Create a new `.html` file following the template:
|
1. Create a new `.html` file following the template:
|
||||||
@@ -220,9 +250,11 @@ export const inventoryData = [
|
|||||||
- Replace placeholder images with real inventory photos
|
- Replace placeholder images with real inventory photos
|
||||||
- Implement backend storage (currently using static data)
|
- Implement backend storage (currently using static data)
|
||||||
- Add user authentication
|
- Add user authentication
|
||||||
|
- Integrate with real barcode/UPC database
|
||||||
- Consider a frontend framework (React, Vue, etc.) for scale
|
- Consider a frontend framework (React, Vue, etc.) for scale
|
||||||
- Add unit/integration tests
|
- Add unit/integration tests
|
||||||
- Set up CI/CD pipeline
|
- Set up CI/CD pipeline
|
||||||
|
- Implement database for persistent storage and expiry date tracking
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@@ -235,7 +267,9 @@ Contributions are welcome! Suggested improvements:
|
|||||||
- [ ] Implement item editing/deletion on storage pages
|
- [ ] Implement item editing/deletion on storage pages
|
||||||
- [ ] Add barcode/UPC lookup for real products
|
- [ ] Add barcode/UPC lookup for real products
|
||||||
- [ ] Convert item-component to custom element (`<item-component>`)
|
- [ ] Convert item-component to custom element (`<item-component>`)
|
||||||
- [ ] Add more detailed inventory tracking (expiration dates, locations within rooms, etc.)
|
- [ ] Add more detailed inventory tracking (expiration dates calculations, locations within rooms, batch numbers, etc.)
|
||||||
|
- [ ] Add email/notification alerts for items expiring soon
|
||||||
|
- [ ] Implement CSV import/export for inventory
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
84
search.html
84
search.html
@@ -46,13 +46,15 @@
|
|||||||
box-shadow: 0 0 5px rgba(76, 175, 80, 0.3);
|
box-shadow: 0 0 5px rgba(76, 175, 80, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.quantity-range {
|
.quantity-range,
|
||||||
|
.date-range {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quantity-range .form-group {
|
.quantity-range .form-group,
|
||||||
|
.date-range .form-group {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +104,30 @@
|
|||||||
color: #999;
|
color: #999;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-expiry-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expiry-fresh {
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
color: #2e7d32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expiry-soon {
|
||||||
|
background-color: #fff3e0;
|
||||||
|
color: #e65100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expiry-expired {
|
||||||
|
background-color: #ffebee;
|
||||||
|
color: #c62828;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -155,6 +181,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Expiry Date Range:</label>
|
||||||
|
<div class="date-range">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="min-expiry-date" style="font-size: 12px;">Start Date:</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
id="min-expiry-date"
|
||||||
|
placeholder="Start date"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="max-expiry-date" style="font-size: 12px;">End Date:</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
id="max-expiry-date"
|
||||||
|
placeholder="End date"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="search-buttons">
|
<div class="search-buttons">
|
||||||
<button type="button" class="btn btn-primary" id="search-btn">Search</button>
|
<button type="button" class="btn btn-primary" id="search-btn">Search</button>
|
||||||
<button type="button" class="btn btn-secondary" id="reset-btn">Reset</button>
|
<button type="button" class="btn btn-secondary" id="reset-btn">Reset</button>
|
||||||
@@ -170,7 +218,7 @@
|
|||||||
|
|
||||||
<script src="navbar.js"></script>
|
<script src="navbar.js"></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { searchInventory, getLocations } from './search.js';
|
import { searchInventory, getLocations, formatDate, getExpiryStatus } from './search.js';
|
||||||
import createItemComponent from './item-component.js';
|
import createItemComponent from './item-component.js';
|
||||||
|
|
||||||
const searchForm = document.getElementById('search-form');
|
const searchForm = document.getElementById('search-form');
|
||||||
@@ -178,6 +226,8 @@
|
|||||||
const locationSelect = document.getElementById('search-location');
|
const locationSelect = document.getElementById('search-location');
|
||||||
const minQuantityInput = document.getElementById('min-quantity');
|
const minQuantityInput = document.getElementById('min-quantity');
|
||||||
const maxQuantityInput = document.getElementById('max-quantity');
|
const maxQuantityInput = document.getElementById('max-quantity');
|
||||||
|
const minExpiryDateInput = document.getElementById('min-expiry-date');
|
||||||
|
const maxExpiryDateInput = document.getElementById('max-expiry-date');
|
||||||
const searchBtn = document.getElementById('search-btn');
|
const searchBtn = document.getElementById('search-btn');
|
||||||
const resetBtn = document.getElementById('reset-btn');
|
const resetBtn = document.getElementById('reset-btn');
|
||||||
const resultsGrid = document.getElementById('results-grid');
|
const resultsGrid = document.getElementById('results-grid');
|
||||||
@@ -192,6 +242,14 @@
|
|||||||
locationSelect.value = 'All';
|
locationSelect.value = 'All';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get expiry status badge HTML
|
||||||
|
function getExpiryBadgeHtml(expiryDate) {
|
||||||
|
const status = getExpiryStatus(expiryDate);
|
||||||
|
const badgeClass = status.status === 'Expired' ? 'expiry-expired' :
|
||||||
|
status.status === 'Soon' || status.status === 'Today' ? 'expiry-soon' : 'expiry-fresh';
|
||||||
|
return `<div class="item-expiry-badge ${badgeClass}">${formatDate(expiryDate)}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
// Display search results
|
// Display search results
|
||||||
function displayResults(results) {
|
function displayResults(results) {
|
||||||
resultsGrid.innerHTML = '';
|
resultsGrid.innerHTML = '';
|
||||||
@@ -207,9 +265,15 @@
|
|||||||
imgAlt: item.name,
|
imgAlt: item.name,
|
||||||
text1: item.name,
|
text1: item.name,
|
||||||
text2: `${item.location} — ${item.quantity} ${item.unit}`,
|
text2: `${item.location} — ${item.quantity} ${item.unit}`,
|
||||||
text3: `Total: ${item.quantity} ${item.unit}`,
|
text3: `Expires: ${formatDate(item.expiryDate)}`,
|
||||||
editable: false
|
editable: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add expiry badge
|
||||||
|
const badge = document.createElement('div');
|
||||||
|
badge.innerHTML = getExpiryBadgeHtml(item.expiryDate);
|
||||||
|
comp.appendChild(badge.firstChild);
|
||||||
|
|
||||||
resultsGrid.appendChild(comp);
|
resultsGrid.appendChild(comp);
|
||||||
});
|
});
|
||||||
resultsInfo.textContent = `Found ${results.length} item${results.length !== 1 ? 's' : ''}`;
|
resultsInfo.textContent = `Found ${results.length} item${results.length !== 1 ? 's' : ''}`;
|
||||||
@@ -222,8 +286,16 @@
|
|||||||
const location = locationSelect.value;
|
const location = locationSelect.value;
|
||||||
const minQty = parseInt(minQuantityInput.value) || 0;
|
const minQty = parseInt(minQuantityInput.value) || 0;
|
||||||
const maxQty = parseInt(maxQuantityInput.value) || Infinity;
|
const maxQty = parseInt(maxQuantityInput.value) || Infinity;
|
||||||
|
const minExpiry = minExpiryDateInput.value;
|
||||||
|
const maxExpiry = maxExpiryDateInput.value;
|
||||||
|
|
||||||
const results = searchInventory(searchName, location, minQty, maxQty);
|
// Validate date range
|
||||||
|
if (minExpiry && maxExpiry && minExpiry > maxExpiry) {
|
||||||
|
alert('Start date cannot be after end date');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = searchInventory(searchName, location, minQty, maxQty, minExpiry, maxExpiry);
|
||||||
displayResults(results);
|
displayResults(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +305,8 @@
|
|||||||
minQuantityInput.value = '0';
|
minQuantityInput.value = '0';
|
||||||
maxQuantityInput.value = '999';
|
maxQuantityInput.value = '999';
|
||||||
locationSelect.value = 'All';
|
locationSelect.value = 'All';
|
||||||
|
minExpiryDateInput.value = '';
|
||||||
|
maxExpiryDateInput.value = '';
|
||||||
resultsGrid.innerHTML = '';
|
resultsGrid.innerHTML = '';
|
||||||
resultsInfo.textContent = '';
|
resultsInfo.textContent = '';
|
||||||
}
|
}
|
||||||
|
|||||||
101
search.js
101
search.js
@@ -1,42 +1,87 @@
|
|||||||
// search.js - Search module with inventory data and filtering logic
|
// search.js - Search module with inventory data and filtering logic
|
||||||
|
|
||||||
|
// Helper function to create a date string
|
||||||
|
function getDate(daysFromNow) {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() + daysFromNow);
|
||||||
|
return date.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
|
||||||
export const inventoryData = [
|
export const inventoryData = [
|
||||||
// Pantry items
|
// Pantry items
|
||||||
{ id: 1, name: 'Pasta', location: 'Pantry', quantity: 5, unit: 'boxes', img: 'https://picsum.photos/seed/pasta/200/200' },
|
{ id: 1, name: 'Pasta', location: 'Pantry', quantity: 5, unit: 'boxes', expiryDate: getDate(120), img: 'https://picsum.photos/seed/pasta/200/200' },
|
||||||
{ id: 2, name: 'Rice', location: 'Pantry', quantity: 3, unit: 'bags', img: 'https://picsum.photos/seed/rice/200/200' },
|
{ id: 2, name: 'Rice', location: 'Pantry', quantity: 3, unit: 'bags', expiryDate: getDate(180), img: 'https://picsum.photos/seed/rice/200/200' },
|
||||||
{ id: 3, name: 'Cereal', location: 'Pantry', quantity: 2, unit: 'boxes', img: 'https://picsum.photos/seed/cereal/200/200' },
|
{ id: 3, name: 'Cereal', location: 'Pantry', quantity: 2, unit: 'boxes', expiryDate: getDate(60), img: 'https://picsum.photos/seed/cereal/200/200' },
|
||||||
{ id: 4, name: 'Flour', location: 'Pantry', quantity: 1, unit: 'bag', img: 'https://picsum.photos/seed/flour/200/200' },
|
{ id: 4, name: 'Flour', location: 'Pantry', quantity: 1, unit: 'bag', expiryDate: getDate(150), img: 'https://picsum.photos/seed/flour/200/200' },
|
||||||
{ id: 5, name: 'Sugar', location: 'Pantry', quantity: 2, unit: 'bags', img: 'https://picsum.photos/seed/sugar/200/200' },
|
{ id: 5, name: 'Sugar', location: 'Pantry', quantity: 2, unit: 'bags', expiryDate: getDate(200), img: 'https://picsum.photos/seed/sugar/200/200' },
|
||||||
{ id: 6, name: 'Salt', location: 'Pantry', quantity: 1, unit: 'box', img: 'https://picsum.photos/seed/salt/200/200' },
|
{ id: 6, name: 'Salt', location: 'Pantry', quantity: 1, unit: 'box', expiryDate: getDate(365), img: 'https://picsum.photos/seed/salt/200/200' },
|
||||||
{ id: 7, name: 'Olive Oil', location: 'Pantry', quantity: 2, unit: 'bottles', img: 'https://picsum.photos/seed/oil/200/200' },
|
{ id: 7, name: 'Olive Oil', location: 'Pantry', quantity: 2, unit: 'bottles', expiryDate: getDate(90), img: 'https://picsum.photos/seed/oil/200/200' },
|
||||||
{ id: 8, name: 'Canned Beans', location: 'Pantry', quantity: 12, unit: 'cans', img: 'https://picsum.photos/seed/beans/200/200' },
|
{ id: 8, name: 'Canned Beans', location: 'Pantry', quantity: 12, unit: 'cans', expiryDate: getDate(-10), img: 'https://picsum.photos/seed/beans/200/200' },
|
||||||
|
|
||||||
// Fridge items
|
// Fridge items
|
||||||
{ id: 9, name: 'Milk', location: 'Fridge', quantity: 1, unit: 'carton', img: 'https://picsum.photos/seed/milk/200/200' },
|
{ id: 9, name: 'Milk', location: 'Fridge', quantity: 1, unit: 'carton', expiryDate: getDate(5), img: 'https://picsum.photos/seed/milk/200/200' },
|
||||||
{ id: 10, name: 'Cheese', location: 'Fridge', quantity: 2, unit: 'blocks', img: 'https://picsum.photos/seed/cheese/200/200' },
|
{ id: 10, name: 'Cheese', location: 'Fridge', quantity: 2, unit: 'blocks', expiryDate: getDate(30), img: 'https://picsum.photos/seed/cheese/200/200' },
|
||||||
{ id: 11, name: 'Greek Yogurt', location: 'Fridge', quantity: 3, unit: 'containers', img: 'https://picsum.photos/seed/yogurt/200/200' },
|
{ id: 11, name: 'Greek Yogurt', location: 'Fridge', quantity: 3, unit: 'containers', expiryDate: getDate(7), img: 'https://picsum.photos/seed/yogurt/200/200' },
|
||||||
{ id: 12, name: 'Eggs', location: 'Fridge', quantity: 24, unit: 'eggs', img: 'https://picsum.photos/seed/eggs/200/200' },
|
{ id: 12, name: 'Eggs', location: 'Fridge', quantity: 24, unit: 'eggs', expiryDate: getDate(21), img: 'https://picsum.photos/seed/eggs/200/200' },
|
||||||
{ id: 13, name: 'Butter', location: 'Fridge', quantity: 1, unit: 'pack', img: 'https://picsum.photos/seed/butter/200/200' },
|
{ id: 13, name: 'Butter', location: 'Fridge', quantity: 1, unit: 'pack', expiryDate: getDate(45), img: 'https://picsum.photos/seed/butter/200/200' },
|
||||||
{ id: 14, name: 'Chicken Salad', location: 'Fridge', quantity: 2, unit: 'containers', img: 'https://picsum.photos/seed/salad/200/200' },
|
{ id: 14, name: 'Chicken Salad', location: 'Fridge', quantity: 2, unit: 'containers', expiryDate: getDate(-2), img: 'https://picsum.photos/seed/salad/200/200' },
|
||||||
|
|
||||||
// Freezer items
|
// Freezer items
|
||||||
{ id: 15, name: 'Ice Cream', location: 'Freezer', quantity: 1, unit: 'tub', img: 'https://picsum.photos/seed/icecream/200/200' },
|
{ id: 15, name: 'Ice Cream', location: 'Freezer', quantity: 1, unit: 'tub', expiryDate: getDate(90), img: 'https://picsum.photos/seed/icecream/200/200' },
|
||||||
{ id: 16, name: 'Frozen Vegetables', location: 'Freezer', quantity: 5, unit: 'bags', img: 'https://picsum.photos/seed/veggies/200/200' },
|
{ id: 16, name: 'Frozen Vegetables', location: 'Freezer', quantity: 5, unit: 'bags', expiryDate: getDate(180), img: 'https://picsum.photos/seed/veggies/200/200' },
|
||||||
{ id: 17, name: 'Chicken Breast', location: 'Freezer', quantity: 4, unit: 'packages', img: 'https://picsum.photos/seed/chicken/200/200' },
|
{ id: 17, name: 'Chicken Breast', location: 'Freezer', quantity: 4, unit: 'packages', expiryDate: getDate(120), img: 'https://picsum.photos/seed/chicken/200/200' },
|
||||||
{ id: 18, name: 'Ground Beef', location: 'Freezer', quantity: 3, unit: 'packages', img: 'https://picsum.photos/seed/beef/200/200' },
|
{ id: 18, name: 'Ground Beef', location: 'Freezer', quantity: 3, unit: 'packages', expiryDate: getDate(150), img: 'https://picsum.photos/seed/beef/200/200' },
|
||||||
{ id: 19, name: 'Pizza', location: 'Freezer', quantity: 2, unit: 'boxes', img: 'https://picsum.photos/seed/pizza/200/200' },
|
{ id: 19, name: 'Pizza', location: 'Freezer', quantity: 2, unit: 'boxes', expiryDate: getDate(200), img: 'https://picsum.photos/seed/pizza/200/200' },
|
||||||
{ id: 20, name: 'Ice', location: 'Freezer', quantity: 1, unit: 'bag', img: 'https://picsum.photos/seed/ice/200/200' },
|
{ id: 20, name: 'Ice', location: 'Freezer', quantity: 1, unit: 'bag', expiryDate: getDate(365), img: 'https://picsum.photos/seed/ice/200/200' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a date string (YYYY-MM-DD) to readable format (Mon DD, YYYY)
|
||||||
|
* @param {string} dateStr - ISO date string
|
||||||
|
* @returns {string} Formatted date
|
||||||
|
*/
|
||||||
|
export function formatDate(dateStr) {
|
||||||
|
if (!dateStr) return '';
|
||||||
|
const date = new Date(dateStr + 'T00:00:00');
|
||||||
|
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get expiry status of an item
|
||||||
|
* @param {string} expiryDate - ISO date string
|
||||||
|
* @returns {Object} Status object with text and color
|
||||||
|
*/
|
||||||
|
export function getExpiryStatus(expiryDate) {
|
||||||
|
if (!expiryDate) return { status: 'Unknown', color: '#999', text: '' };
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const expiry = new Date(expiryDate + 'T00:00:00');
|
||||||
|
const daysUntilExpiry = Math.floor((expiry - today) / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (daysUntilExpiry < 0) {
|
||||||
|
return { status: 'Expired', color: '#f44336', days: daysUntilExpiry, text: `Expired ${Math.abs(daysUntilExpiry)} days ago` };
|
||||||
|
} else if (daysUntilExpiry === 0) {
|
||||||
|
return { status: 'Today', color: '#ff9800', days: 0, text: 'Expires today' };
|
||||||
|
} else if (daysUntilExpiry <= 7) {
|
||||||
|
return { status: 'Soon', color: '#ff9800', days: daysUntilExpiry, text: `Expires in ${daysUntilExpiry} days` };
|
||||||
|
} else {
|
||||||
|
return { status: 'Fresh', color: '#4CAF50', days: daysUntilExpiry, text: `Expires in ${daysUntilExpiry} days` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search and filter inventory items
|
* Search and filter inventory items
|
||||||
* @param {string} searchName - Search term for item name (case insensitive)
|
* @param {string} searchName - Search term for item name (case insensitive)
|
||||||
* @param {string} selectedLocation - Filter by location ('All', 'Pantry', 'Fridge', 'Freezer')
|
* @param {string} selectedLocation - Filter by location ('All', 'Pantry', 'Fridge', 'Freezer')
|
||||||
* @param {number} minQuantity - Minimum quantity filter
|
* @param {number} minQuantity - Minimum quantity filter
|
||||||
* @param {number} maxQuantity - Maximum quantity filter
|
* @param {number} maxQuantity - Maximum quantity filter
|
||||||
|
* @param {string} minExpiryDate - Minimum expiry date (YYYY-MM-DD format)
|
||||||
|
* @param {string} maxExpiryDate - Maximum expiry date (YYYY-MM-DD format)
|
||||||
* @returns {Array} Filtered inventory items
|
* @returns {Array} Filtered inventory items
|
||||||
*/
|
*/
|
||||||
export function searchInventory(searchName = '', selectedLocation = 'All', minQuantity = 0, maxQuantity = Infinity) {
|
export function searchInventory(searchName = '', selectedLocation = 'All', minQuantity = 0, maxQuantity = Infinity, minExpiryDate = '', maxExpiryDate = '') {
|
||||||
return inventoryData.filter(item => {
|
return inventoryData.filter(item => {
|
||||||
// Filter by name
|
// Filter by name
|
||||||
const nameMatch = item.name.toLowerCase().includes(searchName.toLowerCase());
|
const nameMatch = item.name.toLowerCase().includes(searchName.toLowerCase());
|
||||||
@@ -47,7 +92,19 @@ export function searchInventory(searchName = '', selectedLocation = 'All', minQu
|
|||||||
// Filter by quantity range
|
// Filter by quantity range
|
||||||
const quantityMatch = item.quantity >= minQuantity && item.quantity <= maxQuantity;
|
const quantityMatch = item.quantity >= minQuantity && item.quantity <= maxQuantity;
|
||||||
|
|
||||||
return nameMatch && locationMatch && quantityMatch;
|
// Filter by expiry date range
|
||||||
|
let expiryMatch = true;
|
||||||
|
if (minExpiryDate || maxExpiryDate) {
|
||||||
|
const itemExpiry = item.expiryDate;
|
||||||
|
if (minExpiryDate && itemExpiry < minExpiryDate) {
|
||||||
|
expiryMatch = false;
|
||||||
|
}
|
||||||
|
if (maxExpiryDate && itemExpiry > maxExpiryDate) {
|
||||||
|
expiryMatch = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameMatch && locationMatch && quantityMatch && expiryMatch;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user