erp-core-frontend-v2/src/features/warehouses/components/WarehouseCard.tsx
Adrian Flores Cortes 158ebcb57b [TASK-MASTER] feat: FE-001 products + FE-002 warehouses features
FE-001: Products Feature (16 archivos)
- types/index.ts - Interfaces TypeScript
- api/products.api.ts, categories.api.ts - Clientes Axios
- hooks/useProducts, useCategories, useProductPricing
- components/ProductForm, ProductCard, CategoryTree, VariantSelector, PricingTable
- pages/ProductsPage, ProductDetailPage, CategoriesPage

FE-002: Warehouses Feature (15 archivos)
- types/index.ts - Interfaces TypeScript
- api/warehouses.api.ts - Cliente Axios
- hooks/useWarehouses, useLocations
- components/WarehouseCard, LocationGrid, WarehouseLayout, ZoneCard, badges
- pages/WarehousesPage, WarehouseDetailPage, LocationsPage, ZonesPage

Ambos siguen patrones de inventory y React Query + react-hook-form + zod

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 00:16:34 -06:00

153 lines
5.4 KiB
TypeScript

import { cn } from '@utils/cn';
import { WarehouseTypeBadge } from './WarehouseTypeBadge';
import type { Warehouse } from '../types';
export interface WarehouseCardProps {
warehouse: Warehouse;
onClick?: () => void;
onEdit?: () => void;
onDelete?: () => void;
className?: string;
}
export function WarehouseCard({
warehouse,
onClick,
onEdit,
onDelete,
className,
}: WarehouseCardProps) {
const address = [
warehouse.addressLine1,
warehouse.city,
warehouse.state,
warehouse.country,
].filter(Boolean).join(', ');
return (
<div
className={cn(
'rounded-lg border border-gray-200 bg-white p-4 shadow-sm transition-shadow hover:shadow-md dark:border-gray-700 dark:bg-gray-800',
onClick && 'cursor-pointer',
className
)}
onClick={onClick}
>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
{warehouse.name}
</h3>
{warehouse.isDefault && (
<span className="rounded bg-primary-100 px-2 py-0.5 text-xs font-medium text-primary-800 dark:bg-primary-900 dark:text-primary-300">
Por defecto
</span>
)}
</div>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
Codigo: {warehouse.code}
</p>
</div>
<div className="flex items-center gap-2">
<WarehouseTypeBadge type={warehouse.warehouseType} />
<span
className={cn(
'inline-flex h-2 w-2 rounded-full',
warehouse.isActive ? 'bg-green-500' : 'bg-gray-300'
)}
title={warehouse.isActive ? 'Activo' : 'Inactivo'}
/>
</div>
</div>
{warehouse.description && (
<p className="mt-2 text-sm text-gray-600 dark:text-gray-300 line-clamp-2">
{warehouse.description}
</p>
)}
{address && (
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
<span className="inline-block w-4" title="Direccion">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-4 w-4">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" />
</svg>
</span>
{address}
</p>
)}
{warehouse.managerName && (
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
<span className="inline-block w-4" title="Responsable">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-4 w-4">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />
</svg>
</span>
{warehouse.managerName}
</p>
)}
{(warehouse.capacityUnits || warehouse.capacityVolume || warehouse.capacityWeight) && (
<div className="mt-3 flex gap-4 border-t border-gray-100 pt-3 dark:border-gray-700">
{warehouse.capacityUnits && (
<div className="text-center">
<p className="text-sm font-medium text-gray-900 dark:text-white">
{warehouse.capacityUnits.toLocaleString()}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">Unidades</p>
</div>
)}
{warehouse.capacityVolume && (
<div className="text-center">
<p className="text-sm font-medium text-gray-900 dark:text-white">
{warehouse.capacityVolume.toLocaleString()} m3
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">Volumen</p>
</div>
)}
{warehouse.capacityWeight && (
<div className="text-center">
<p className="text-sm font-medium text-gray-900 dark:text-white">
{warehouse.capacityWeight.toLocaleString()} kg
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">Peso</p>
</div>
)}
</div>
)}
{(onEdit || onDelete) && (
<div className="mt-3 flex justify-end gap-2 border-t border-gray-100 pt-3 dark:border-gray-700">
{onEdit && (
<button
type="button"
onClick={(e) => {
e.stopPropagation();
onEdit();
}}
className="rounded px-3 py-1 text-sm text-primary-600 hover:bg-primary-50 dark:text-primary-400 dark:hover:bg-primary-900/20"
>
Editar
</button>
)}
{onDelete && (
<button
type="button"
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
className="rounded px-3 py-1 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
>
Eliminar
</button>
)}
</div>
)}
</div>
);
}