feat(phase2): add admin photo replacement button with ItemDetailModal
- Add ItemDetailModal component for viewing item details and replacing photos
- Add photo replacement/deletion endpoints to API layer (PUT/DELETE /items/{id}/photo)
- Update InventoryTable to open detail modal on item click
- Show current photo thumbnail with Replace/Delete buttons
- Support uploading new photo with ItemPhotoUpload component
- Delete old photo on backend when replacing (no orphaned files)
- Full test coverage: 18 tests for ItemDetailModal component
- All 393 tests passing, zero TypeScript errors
- Build verified successfully
This commit is contained in:
@@ -5,6 +5,7 @@ import { Item } from '@/lib/db';
|
||||
import { ChevronRight, ChevronDown, Layers, Package } from 'lucide-react';
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import ItemDetailModal from '@/components/ItemDetailModal';
|
||||
|
||||
function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
@@ -29,6 +30,22 @@ export default function InventoryTable({
|
||||
onEditCategory,
|
||||
categoriesList = []
|
||||
}: InventoryTableProps) {
|
||||
const [selectedItemDetail, setSelectedItemDetail] = useState<Item | null>(null);
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||
|
||||
const handleItemClick = (item: Item) => {
|
||||
setSelectedItemDetail(item);
|
||||
onItemClick(item);
|
||||
};
|
||||
|
||||
const handleCloseDetail = () => {
|
||||
setSelectedItemDetail(null);
|
||||
};
|
||||
|
||||
const handleItemRefresh = () => {
|
||||
setRefreshTrigger(prev => prev + 1);
|
||||
};
|
||||
|
||||
if (categories.length === 0) {
|
||||
return (
|
||||
<div className="py-20 text-center text-secondary">
|
||||
@@ -81,7 +98,7 @@ export default function InventoryTable({
|
||||
{categoryItems.map(item => (
|
||||
<div
|
||||
key={item.id}
|
||||
onClick={() => onItemClick(item)}
|
||||
onClick={() => handleItemClick(item)}
|
||||
className="bg-background/40 border border-slate-800/50 p-4 rounded-2xl flex items-center justify-between hover:border-primary/40 cursor-pointer transition-all active:scale-[0.98]"
|
||||
>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0 pr-4">
|
||||
@@ -109,6 +126,14 @@ export default function InventoryTable({
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{selectedItemDetail && (
|
||||
<ItemDetailModal
|
||||
item={selectedItemDetail}
|
||||
onClose={handleCloseDetail}
|
||||
onItemRefresh={handleItemRefresh}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user