From 8eaac86e0230e639c6d40eff93f02c401d34d7ca Mon Sep 17 00:00:00 2001 From: beatz174-bit Date: Fri, 21 Nov 2025 16:22:22 +1000 Subject: [PATCH] Add edit and delete controls for areas and products --- src/components/ProductRow.tsx | 175 ++++++++++++++++++++++++--- src/screens/ManageAreasScreen.tsx | 79 +++++++++++- src/screens/ManageProductsScreen.tsx | 25 +++- 3 files changed, 256 insertions(+), 23 deletions(-) diff --git a/src/components/ProductRow.tsx b/src/components/ProductRow.tsx index 14cc8cb..d494209 100644 --- a/src/components/ProductRow.tsx +++ b/src/components/ProductRow.tsx @@ -1,26 +1,161 @@ -import { Card, CardContent, Stack, Typography } from '@mui/material'; +import { + Card, + CardActions, + CardContent, + IconButton, + MenuItem, + Stack, + TextField, + Typography, +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; +import { ChangeEvent, useEffect, useState } from 'react'; import { Product } from '../models/Product'; interface ProductRowProps { product: Product; + categories: string[]; + onSave: ( + productId: string, + updates: { + name: string; + category: string; + unit_type: string; + bulk_name?: string; + units_per_bulk?: number; + }, + ) => Promise | void; + onDelete: (productId: string) => Promise | void; } -export const ProductRow = ({ product }: ProductRowProps) => ( - - - -
- {product.name} - - {product.category} • {product.unit_type} - -
- {product.bulk_name && product.units_per_bulk ? ( - - {product.units_per_bulk} per {product.bulk_name} - - ) : null} -
-
-
-); +interface ProductFormState { + name: string; + category: string; + unitType: string; + bulkName: string; + unitsPerBulk: string; +} + +const getInitialFormState = (product: Product): ProductFormState => ({ + name: product.name, + category: product.category, + unitType: product.unit_type, + bulkName: product.bulk_name ?? '', + unitsPerBulk: product.units_per_bulk?.toString() ?? '', +}); + +export const ProductRow = ({ product, categories, onSave, onDelete }: ProductRowProps) => { + const [isEditing, setIsEditing] = useState(false); + const [formState, setFormState] = useState(() => getInitialFormState(product)); + + useEffect(() => { + setFormState(getInitialFormState(product)); + }, [product]); + + const handleChange = (field: keyof ProductFormState) => (event: ChangeEvent) => { + setFormState((prev) => ({ ...prev, [field]: event.target.value })); + }; + + const handleSave = async () => { + if (!formState.name) return; + await onSave(product.id, { + name: formState.name, + category: formState.category, + unit_type: formState.unitType, + bulk_name: formState.bulkName || undefined, + units_per_bulk: formState.unitsPerBulk ? Number(formState.unitsPerBulk) : undefined, + }); + setIsEditing(false); + }; + + const handleCancel = () => { + setIsEditing(false); + setFormState(getInitialFormState(product)); + }; + + return ( + + + {isEditing ? ( + + + + {categories.map((cat) => ( + + {cat} + + ))} + + + + + + + + ) : ( + +
+ {product.name} + + {product.category} • {product.unit_type} + +
+ {product.bulk_name && product.units_per_bulk ? ( + + {product.units_per_bulk} per {product.bulk_name} + + ) : null} +
+ )} +
+ + {isEditing ? ( + <> + + + + + + + + ) : ( + <> + setIsEditing(true)}> + + + onDelete(product.id)}> + + + + )} + +
+ ); +}; diff --git a/src/screens/ManageAreasScreen.tsx b/src/screens/ManageAreasScreen.tsx index c565550..61cd5db 100644 --- a/src/screens/ManageAreasScreen.tsx +++ b/src/screens/ManageAreasScreen.tsx @@ -1,4 +1,18 @@ -import { Button, Container, List, ListItem, ListItemText, Stack, TextField, Typography } from '@mui/material'; +import { + Button, + Container, + IconButton, + List, + ListItem, + ListItemText, + Stack, + TextField, + Typography, +} from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; import { useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; import { useAreas } from '../hooks/dataHooks'; @@ -8,6 +22,8 @@ export const ManageAreasScreen = () => { const db = useDatabase(); const areas = useAreas(); const [name, setName] = useState(''); + const [editingAreaId, setEditingAreaId] = useState(null); + const [editName, setEditName] = useState(''); const addArea = async () => { if (!name) return; @@ -15,6 +31,30 @@ export const ManageAreasScreen = () => { setName(''); }; + const startEditing = (areaId: string, currentName: string) => { + setEditingAreaId(areaId); + setEditName(currentName); + }; + + const saveArea = async () => { + if (!editingAreaId || !editName) return; + await db.areas.update(editingAreaId, { name: editName, updated_at: Date.now() }); + setEditingAreaId(null); + setEditName(''); + }; + + const cancelEditing = () => { + setEditingAreaId(null); + setEditName(''); + }; + + const deleteArea = async (areaId: string) => { + await db.areas.delete(areaId); + if (editingAreaId === areaId) { + cancelEditing(); + } + }; + return ( @@ -30,7 +70,42 @@ export const ManageAreasScreen = () => { {areas.map((area) => ( - + {editingAreaId === area.id ? ( + + setEditName(event.target.value)} + /> + + + + + + + + ) : ( + + + + startEditing(area.id, area.name)} + aria-label={`Edit ${area.name}`} + size="small" + > + + + deleteArea(area.id)} + aria-label={`Delete ${area.name}`} + size="small" + > + + + + + )} ))} diff --git a/src/screens/ManageProductsScreen.tsx b/src/screens/ManageProductsScreen.tsx index 9a4eaa4..451fd15 100644 --- a/src/screens/ManageProductsScreen.tsx +++ b/src/screens/ManageProductsScreen.tsx @@ -50,6 +50,23 @@ export const ManageProductsScreen = () => { setName(''); }; + const updateProduct = async ( + productId: string, + updates: { + name: string; + category: string; + unit_type: string; + bulk_name?: string; + units_per_bulk?: number; + }, + ) => { + await db.products.update(productId, { ...updates, updated_at: Date.now() }); + }; + + const deleteProduct = async (productId: string) => { + await db.products.delete(productId); + }; + return ( @@ -85,7 +102,13 @@ export const ManageProductsScreen = () => { {filtered.map((product) => ( - + ))}