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:
2026-04-21 13:30:38 +03:00
parent 2ba1994022
commit a8d7e5ac09
5 changed files with 599 additions and 2 deletions

View File

@@ -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>
);
}