Merge tag '1.14.5_cleaned'

project was cleaned
This commit is contained in:
2026-04-23 11:47:44 +03:00
5758 changed files with 2046069 additions and 16179 deletions

View File

@@ -2,9 +2,12 @@
import { useState } from 'react';
import { Item } from '@/lib/db';
import { buildPhotoUrl } from '@/lib/api';
import { ChevronRight, ChevronDown, Layers, Package } from 'lucide-react';
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import ItemDetailModal from '@/components/ItemDetailModal';
import PhotoModal from '@/components/PhotoModal';
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@@ -18,6 +21,7 @@ interface InventoryTableProps {
onItemClick: (item: Item) => void;
onEditCategory?: (category: string) => void;
categoriesList?: any[];
backendUrl?: string;
}
export default function InventoryTable({
@@ -27,8 +31,26 @@ export default function InventoryTable({
onExpandCategory,
onItemClick,
onEditCategory,
categoriesList = []
categoriesList = [],
backendUrl = ''
}: InventoryTableProps) {
const [selectedItemDetail, setSelectedItemDetail] = useState<Item | null>(null);
const [refreshTrigger, setRefreshTrigger] = useState(0);
const [selectedPhotoItem, setSelectedPhotoItem] = useState<Item | null>(null);
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-2 lg:py-3 xl:py-40 text-center text-secondary">
@@ -81,26 +103,52 @@ export default function InventoryTable({
{categoryItems.map(item => (
<div
key={item.id}
onClick={() => onItemClick(item)}
className="bg-background/40 border border-slate-800/50 p-4 lg:p-5 xl:p-6 lg:p-8 xl:p-10 rounded-2xl flex items-center justify-between hover:border-primary/40 cursor-pointer transition-all active:scale-[0.98]"
className="bg-background/40 border border-slate-800/50 p-4 rounded-2xl flex items-center justify-between hover:border-primary/40 transition-all active:scale-[0.98]"
>
<div className="flex items-center gap-3 flex-1 min-w-0 pr-4">
<div className="w-8 h-8 rounded-xl bg-green-500/10 flex items-center justify-center text-green-500 shrink-0">
<Package size={14} />
</div>
<div
onClick={() => handleItemClick(item)}
className="flex items-center gap-3 flex-1 min-w-0 pr-4 cursor-pointer"
>
{item.photo_path || item.image_url ? (
<div
onClick={(e) => {
e.stopPropagation();
setSelectedPhotoItem(item);
}}
className="w-12 h-12 rounded-xl shrink-0 border-2 border-slate-300 overflow-hidden cursor-pointer hover:border-primary transition-colors active:scale-95"
>
<img
src={buildPhotoUrl(backendUrl, item.photo_path || item.image_url || '')}
alt={item.name}
className="w-full h-full object-cover"
loading="lazy"
/>
</div>
) : (
<div className="w-12 h-12 rounded-xl bg-green-500/10 flex items-center justify-center text-green-500 shrink-0">
<Package size={14} />
</div>
)}
<div className="truncate">
<h4 className="card-title truncate">{item.name}</h4>
<p className="card-subtitle mt-0 lowercase opacity-80 truncate">{item.specs}</p>
{item.photo_path || item.image_url ? (
<p className="card-subtitle mt-0 lowercase opacity-80 text-xs">Tap photo for details</p>
) : (
<>
<p className="card-subtitle mt-0 lowercase opacity-80 truncate">{item.specs}</p>
<p className="card-subtitle mt-0 text-xs">No photo</p>
</>
)}
</div>
</div>
<div className="text-right shrink-0">
<span className={cn(
"text-lg lg:text-xl xl:text-2xl lg:text-3xl xl:text-4xl font-black",
"text-lg font-normal",
item.quantity <= item.min_quantity ? "text-amber-500" : "text-primary"
)}>
{item.quantity}
</span>
<p className="text-sm lg:text-base xl:text-lg lg:text-xl xl:text-2xl lg:text-3xl xl:text-4xl text-muted font-bold tracking-tight">Stock</p>
<p className="text-sm text-muted font-normal tracking-tight">Stock</p>
</div>
</div>
))}
@@ -109,6 +157,23 @@ export default function InventoryTable({
</div>
);
})}
{selectedItemDetail && (
<ItemDetailModal
item={selectedItemDetail}
onClose={handleCloseDetail}
onItemRefresh={handleItemRefresh}
backendUrl={backendUrl}
/>
)}
{selectedPhotoItem && (selectedPhotoItem.photo_path || selectedPhotoItem.image_url) && (
<PhotoModal
photoUrl={selectedPhotoItem.photo_path || selectedPhotoItem.image_url || ''}
title={selectedPhotoItem.name}
onClose={() => setSelectedPhotoItem(null)}
/>
)}
</section>
);
}