erp-platform/ui/src/views/warehouse/components/WarehouseDefinitions.tsx

1148 lines
45 KiB
TypeScript
Raw Normal View History

2025-09-15 21:11:40 +00:00
import React, { useState } from 'react'
import { WmWarehouse, WarehouseTypeEnum, WmZone, WmLocation } from '../../../types/wm'
2025-09-15 09:31:47 +00:00
import {
FaPlus,
FaSearch,
FaEdit,
FaTrash,
FaBuilding,
FaMapMarkerAlt,
FaLayerGroup,
FaExclamationCircle,
FaCheckCircle,
FaEye,
FaTh,
FaList,
2025-09-15 21:11:40 +00:00
} from 'react-icons/fa'
import { WarehouseModal, ZoneModal, LocationModal } from './modals'
import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { mockZones } from '../../../mocks/mockZones'
import { mockLocations } from '../../../mocks/mockLocations'
2025-09-15 09:31:47 +00:00
import {
getWarehouseTypeColor,
getWarehouseTypeText,
getZoneTypeText,
getLocationTypeText,
2025-09-15 21:11:40 +00:00
} from '../../../utils/erp'
import { Container } from '@/components/shared'
2025-09-15 09:31:47 +00:00
const WarehouseDefinitions: React.FC = () => {
2025-09-15 21:11:40 +00:00
const [searchTerm, setSearchTerm] = useState('')
const [selectedType, setSelectedType] = useState<WarehouseTypeEnum | ''>('')
const [activeTab, setActiveTab] = useState<'warehouses' | 'zones' | 'locations'>('warehouses')
const [selectedWarehouse, setSelectedWarehouse] = useState<string>('')
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
2025-09-15 09:31:47 +00:00
// Modal states
const [warehouseModal, setWarehouseModal] = useState<{
2025-09-15 21:11:40 +00:00
isOpen: boolean
mode: 'create' | 'edit' | 'view'
warehouse: WmWarehouse | null
2025-09-15 09:31:47 +00:00
}>({
isOpen: false,
2025-09-15 21:11:40 +00:00
mode: 'create',
2025-09-15 09:31:47 +00:00
warehouse: null,
2025-09-15 21:11:40 +00:00
})
2025-09-15 09:31:47 +00:00
const [zoneModal, setZoneModal] = useState<{
2025-09-15 21:11:40 +00:00
isOpen: boolean
mode: 'create' | 'edit' | 'view'
zone: WmZone | null
2025-09-15 09:31:47 +00:00
}>({
isOpen: false,
2025-09-15 21:11:40 +00:00
mode: 'create',
2025-09-15 09:31:47 +00:00
zone: null,
2025-09-15 21:11:40 +00:00
})
2025-09-15 09:31:47 +00:00
const [locationModal, setLocationModal] = useState<{
2025-09-15 21:11:40 +00:00
isOpen: boolean
mode: 'create' | 'edit' | 'view'
location: WmLocation | null
2025-09-15 09:31:47 +00:00
}>({
isOpen: false,
2025-09-15 21:11:40 +00:00
mode: 'create',
2025-09-15 09:31:47 +00:00
location: null,
2025-09-15 21:11:40 +00:00
})
2025-09-15 09:31:47 +00:00
// Data states (in a real app, these would come from an API)
2025-09-15 21:11:40 +00:00
const [warehouses, setWarehouses] = useState<WmWarehouse[]>(mockWarehouses)
2025-09-15 09:31:47 +00:00
2025-09-15 21:11:40 +00:00
const [zones, setZones] = useState<WmZone[]>(mockZones)
2025-09-15 09:31:47 +00:00
2025-09-15 21:11:40 +00:00
const [locations, setLocations] = useState<WmLocation[]>(mockLocations)
2025-09-15 09:31:47 +00:00
// Modal handlers
2025-09-15 21:11:40 +00:00
const handleWarehouseAction = (mode: 'create' | 'edit' | 'view', warehouse?: WmWarehouse) => {
2025-09-15 09:31:47 +00:00
setWarehouseModal({
isOpen: true,
mode,
warehouse: warehouse || null,
2025-09-15 21:11:40 +00:00
})
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:11:40 +00:00
const handleZoneAction = (mode: 'create' | 'edit' | 'view', zone?: WmZone) => {
2025-09-15 09:31:47 +00:00
setZoneModal({
isOpen: true,
mode,
zone: zone || null,
2025-09-15 21:11:40 +00:00
})
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:11:40 +00:00
const handleLocationAction = (mode: 'create' | 'edit' | 'view', location?: WmLocation) => {
2025-09-15 09:31:47 +00:00
setLocationModal({
isOpen: true,
mode,
location: location || null,
2025-09-15 21:11:40 +00:00
})
}
2025-09-15 09:31:47 +00:00
const handleWarehouseSave = (warehouseData: Partial<WmWarehouse>) => {
2025-09-15 21:11:40 +00:00
if (warehouseModal.mode === 'create') {
2025-09-15 09:31:47 +00:00
const newWarehouse: WmWarehouse = {
...warehouseData,
id: Date.now().toString(),
locations: [],
zones: [],
creationTime: new Date(),
lastModificationTime: new Date(),
2025-09-15 21:11:40 +00:00
} as WmWarehouse
setWarehouses([...warehouses, newWarehouse])
} else if (warehouseModal.mode === 'edit' && warehouseModal.warehouse) {
2025-09-15 09:31:47 +00:00
setWarehouses(
warehouses.map((w) =>
w.id === warehouseModal.warehouse!.id
? ({
...w,
...warehouseData,
lastModificationTime: new Date(),
} as WmWarehouse)
2025-09-15 21:11:40 +00:00
: w,
),
)
2025-09-15 09:31:47 +00:00
}
2025-09-15 21:11:40 +00:00
}
2025-09-15 09:31:47 +00:00
const handleZoneSave = (zoneData: Partial<WmZone>) => {
2025-09-15 21:11:40 +00:00
if (zoneModal.mode === 'create') {
2025-09-15 09:31:47 +00:00
const newZone: WmZone = {
...zoneData,
id: Date.now().toString(),
locations: [],
2025-09-15 21:11:40 +00:00
} as WmZone
setZones([...zones, newZone])
} else if (zoneModal.mode === 'edit' && zoneModal.zone) {
2025-09-15 09:31:47 +00:00
setZones(
2025-09-15 21:11:40 +00:00
zones.map((z) => (z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z)),
)
2025-09-15 09:31:47 +00:00
}
2025-09-15 21:11:40 +00:00
}
2025-09-15 09:31:47 +00:00
const handleLocationSave = (locationData: Partial<WmLocation>) => {
2025-09-15 21:11:40 +00:00
if (locationModal.mode === 'create') {
2025-09-15 09:31:47 +00:00
const newLocation: WmLocation = {
...locationData,
id: Date.now().toString(),
stockItems: [],
2025-09-15 21:11:40 +00:00
} as WmLocation
setLocations([...locations, newLocation])
} else if (locationModal.mode === 'edit' && locationModal.location) {
2025-09-15 09:31:47 +00:00
setLocations(
locations.map((l) =>
2025-09-15 21:11:40 +00:00
l.id === locationModal.location!.id ? ({ ...l, ...locationData } as WmLocation) : l,
),
)
2025-09-15 09:31:47 +00:00
}
2025-09-15 21:11:40 +00:00
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:11:40 +00:00
const handleDelete = (type: 'warehouse' | 'zone' | 'location', id: string) => {
2025-09-15 09:31:47 +00:00
if (
window.confirm(
`Bu ${
2025-09-15 21:11:40 +00:00
type === 'warehouse' ? 'depo' : type === 'zone' ? 'bölge' : 'lokasyon'
}u silmek istediğinizden emin misiniz?`,
2025-09-15 09:31:47 +00:00
)
) {
switch (type) {
2025-09-15 21:11:40 +00:00
case 'warehouse':
setWarehouses(warehouses.filter((w) => w.id !== id))
break
case 'zone':
setZones(zones.filter((z) => z.id !== id))
break
case 'location':
setLocations(locations.filter((l) => l.id !== id))
break
2025-09-15 09:31:47 +00:00
}
}
2025-09-15 21:11:40 +00:00
}
2025-09-15 09:31:47 +00:00
// Filtered data
const filteredWarehouses = warehouses.filter((warehouse) => {
const matchesSearch =
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
2025-09-15 21:11:40 +00:00
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase())
const matchesType = selectedType === '' || warehouse.warehouseType === selectedType
return matchesSearch && matchesType
})
2025-09-15 09:31:47 +00:00
const filteredZones = zones.filter(
2025-09-15 21:11:40 +00:00
(zone) => selectedWarehouse === '' || zone.warehouseId === selectedWarehouse,
)
2025-09-15 09:31:47 +00:00
const filteredLocations = locations.filter(
2025-09-15 21:11:40 +00:00
(location) => selectedWarehouse === '' || location.warehouseId === selectedWarehouse,
)
2025-09-15 09:31:47 +00:00
// Warehouse Components
const WarehousesGridView = () => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{filteredWarehouses.map((warehouse) => (
<div
key={warehouse.id}
className="bg-white rounded-lg shadow-sm border border-gray-200 p-3 flex flex-col"
>
<div className="flex items-start justify-between mb-2">
<div className="flex items-center gap-2">
<div className="p-2 bg-blue-100 rounded-lg">
<FaBuilding className="w-4 h-4 text-blue-600" />
</div>
<div>
2025-09-15 21:11:40 +00:00
<h3 className="font-medium text-sm text-gray-900">{warehouse.name}</h3>
2025-09-15 21:02:48 +00:00
<p className="text-gray-600">{warehouse.code}</p>
2025-09-15 09:31:47 +00:00
</div>
</div>
{warehouse.isMainWarehouse && (
<span className="bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full">
Ana Depo
</span>
)}
</div>
<div className="space-y-2 flex-grow">
<div>
<span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
2025-09-15 21:11:40 +00:00
warehouse.warehouseType,
2025-09-15 09:31:47 +00:00
)}`}
>
{getWarehouseTypeText(warehouse.warehouseType)}
</span>
</div>
<div className="flex items-center gap-2 text-xs text-gray-600">
<FaMapMarkerAlt className="w-4 h-4" />
<span>
{warehouse.address?.city}, {warehouse.address?.state}
</span>
</div>
<div className="text-xs text-gray-600 flex-grow">
<p>{warehouse.description}</p>
</div>
{/* Capacity */}
<div className="space-y-1">
<div className="flex justify-between text-xs">
<span className="text-gray-600">Kapasite Kullanımı</span>
<span className="font-medium">
{warehouse.currentUtilization} / {warehouse.capacity}
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{
2025-09-15 21:11:40 +00:00
width: `${(warehouse.currentUtilization / warehouse.capacity) * 100}%`,
2025-09-15 09:31:47 +00:00
}}
></div>
</div>
<div className="text-xs text-gray-500">
2025-09-15 21:11:40 +00:00
%{Math.round((warehouse.currentUtilization / warehouse.capacity) * 100)} dolu
2025-09-15 09:31:47 +00:00
</div>
</div>
</div>
<div className="flex items-center justify-between pt-3 mt-3 border-t border-gray-100">
<div className="flex items-center gap-2">
{warehouse.isActive ? (
<>
<FaCheckCircle className="w-4 h-4 text-green-500" />
<span className="text-xs text-green-600">Aktif</span>
</>
) : (
<>
<FaExclamationCircle className="w-4 h-4 text-red-500" />
<span className="text-sm text-red-600">Pasif</span>
</>
)}
</div>
<div className="flex items-center gap-1">
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleWarehouseAction('view', warehouse)}
2025-09-15 09:31:47 +00:00
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleWarehouseAction('edit', warehouse)}
2025-09-15 09:31:47 +00:00
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleDelete('warehouse', warehouse.id)}
2025-09-15 09:31:47 +00:00
className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</div>
</div>
))}
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
const WarehousesListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Depo
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tip
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Adres
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Kapasite
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Kullanım
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Durum
</th>
<th className="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
İşlemler
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredWarehouses.map((warehouse) => {
const utilizationPercentage =
2025-09-15 21:11:40 +00:00
(warehouse.currentUtilization / warehouse.capacity) * 100
2025-09-15 09:31:47 +00:00
return (
<tr key={warehouse.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center ">
<div className="p-2 bg-blue-100 rounded-lg mr-3">
<FaBuilding className="w-4 h-4 text-blue-600" />
</div>
<div>
<div className="text-sm font-medium text-gray-900 flex items-center gap-2">
{warehouse.name}
{warehouse.isMainWarehouse && (
<span className="bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full">
Ana Depo
</span>
)}
</div>
2025-09-15 21:11:40 +00:00
<div className="text-xs text-gray-500">{warehouse.code}</div>
2025-09-15 09:31:47 +00:00
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
2025-09-15 21:11:40 +00:00
warehouse.warehouseType,
2025-09-15 09:31:47 +00:00
)}`}
>
{getWarehouseTypeText(warehouse.warehouseType)}
</span>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-xs text-gray-900">
{warehouse.address?.city}, {warehouse.address?.state}
</div>
2025-09-15 21:11:40 +00:00
<div className="text-xs text-gray-500">{warehouse.address?.street}</div>
2025-09-15 09:31:47 +00:00
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{warehouse.capacity.toLocaleString()} birim
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
<div className="w-16 bg-gray-200 rounded-full h-2 mr-2">
<div
className={`h-2 rounded-full ${
utilizationPercentage > 90
2025-09-15 21:11:40 +00:00
? 'bg-red-500'
2025-09-15 09:31:47 +00:00
: utilizationPercentage > 70
2025-09-15 21:11:40 +00:00
? 'bg-yellow-500'
: 'bg-blue-500'
2025-09-15 09:31:47 +00:00
}`}
style={{ width: `${utilizationPercentage}%` }}
></div>
</div>
<span className="text-xs text-gray-900">
%{Math.round(utilizationPercentage)}
</span>
</div>
<div className="text-xs text-gray-500">
2025-09-15 21:11:40 +00:00
{warehouse.currentUtilization.toLocaleString()} /{' '}
2025-09-15 09:31:47 +00:00
{warehouse.capacity.toLocaleString()}
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
{warehouse.isActive ? (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
<FaCheckCircle className="w-3 h-3 mr-1" />
Aktif
</span>
) : (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
<FaExclamationCircle className="w-3 h-3 mr-1" />
Pasif
</span>
)}
</td>
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2">
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleWarehouseAction('view', warehouse)}
2025-09-15 09:31:47 +00:00
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleWarehouseAction('edit', warehouse)}
2025-09-15 09:31:47 +00:00
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleDelete('warehouse', warehouse.id)}
2025-09-15 09:31:47 +00:00
className="text-red-600 hover:text-red-900"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</td>
</tr>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
})}
</tbody>
</table>
</div>
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
const WarehousesTab = () => (
<div className="space-y-4 pt-2">
{/* Filters */}
<div className="flex flex-col sm:flex-row gap-4">
<div className="relative flex-1">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<input
type="text"
placeholder="Depo ara..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 pr-4 py-1.5 text-sm w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<select
value={selectedType}
2025-09-15 21:11:40 +00:00
onChange={(e) => setSelectedType(e.target.value as WarehouseTypeEnum | '')}
2025-09-15 09:31:47 +00:00
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Tüm Tipler</option>
{Object.values(WarehouseTypeEnum).map((type) => (
<option key={type} value={type}>
{getWarehouseTypeText(type)}
</option>
))}
</select>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleWarehouseAction('create')}
2025-09-15 09:31:47 +00:00
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
>
<FaPlus className="w-4 h-4" />
Yeni Depo
</button>
</div>
{/* Content based on view mode */}
2025-09-15 21:11:40 +00:00
{viewMode === 'grid' ? <WarehousesGridView /> : <WarehousesListView />}
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
// Zone Components
const ZonesGridView = () => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{filteredZones.map((zone) => {
2025-09-15 21:11:40 +00:00
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
2025-09-15 09:31:47 +00:00
return (
<div
key={zone.id}
className="bg-white rounded-lg shadow-sm border border-gray-200 p-3 flex flex-col"
>
<div className="flex items-start justify-between mb-2">
<div className="flex items-center gap-2">
<div className="p-2 bg-purple-100 rounded-lg">
<FaLayerGroup className="w-4 h-4 text-purple-600" />
</div>
<div>
2025-09-15 21:11:40 +00:00
<h3 className="font-medium text-sm text-gray-900">{zone.name}</h3>
2025-09-15 21:02:48 +00:00
<p className="text-gray-600">{zone.zoneCode}</p>
2025-09-15 09:31:47 +00:00
</div>
</div>
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
{getZoneTypeText(zone.zoneType)}
</span>
</div>
<div className="space-y-2 flex-grow">
<div className="text-xs text-gray-600">
<p>
<strong>Depo:</strong> {warehouse?.name}
</p>
<p>
<strong>Kod:</strong> {warehouse?.code}
</p>
<p>{zone.description}</p>
</div>
{(zone.temperature || zone.humidity) && (
<div className="bg-gray-50 rounded-lg p-2">
2025-09-15 21:11:40 +00:00
<h4 className="text-xs font-medium text-gray-900 mb-1">Çevresel Koşullar</h4>
2025-09-15 09:31:47 +00:00
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
2025-09-15 21:11:40 +00:00
{zone.temperature && <div>Sıcaklık: {zone.temperature}°C</div>}
2025-09-15 09:31:47 +00:00
{zone.humidity && <div>Nem: %{zone.humidity}</div>}
</div>
</div>
)}
</div>
<div className="flex items-center justify-between pt-3 mt-3 border-t border-gray-100">
<div className="flex items-center gap-2">
{zone.isActive ? (
<>
<FaCheckCircle className="w-4 h-4 text-green-500" />
<span className="text-xs text-green-600">Aktif</span>
</>
) : (
<>
<FaExclamationCircle className="w-4 h-4 text-red-500" />
<span className="text-sm text-red-600">Pasif</span>
</>
)}
</div>
<div className="flex items-center gap-2">
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleZoneAction('view', zone)}
2025-09-15 09:31:47 +00:00
className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleZoneAction('edit', zone)}
2025-09-15 09:31:47 +00:00
className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleDelete('zone', zone.id)}
2025-09-15 09:31:47 +00:00
className="p-1 text-gray-400 hover:text-red-600"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</div>
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
})}
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
const ZonesListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Bölge
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Depo
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tip
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Sıcaklık
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Nem
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Durum
</th>
<th className="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
İşlemler
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredZones.map((zone) => {
2025-09-15 21:11:40 +00:00
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
2025-09-15 09:31:47 +00:00
return (
<tr key={zone.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
<div className="p-2 bg-purple-100 rounded-lg mr-3">
<FaLayerGroup className="w-4 h-4 text-purple-600" />
</div>
<div>
2025-09-15 21:11:40 +00:00
<div className="text-sm font-medium text-gray-900">{zone.name}</div>
<div className="text-sm text-gray-500">{zone.zoneCode}</div>
2025-09-15 09:31:47 +00:00
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
2025-09-15 21:11:40 +00:00
<div className="text-sm text-gray-900">{warehouse?.name}</div>
<div className="text-sm text-gray-500">{warehouse?.code}</div>
2025-09-15 09:31:47 +00:00
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
{getZoneTypeText(zone.zoneType)}
</span>
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
2025-09-15 21:11:40 +00:00
{zone.temperature ? `${zone.temperature}°C` : '-'}
2025-09-15 09:31:47 +00:00
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
2025-09-15 21:11:40 +00:00
{zone.humidity ? `%${zone.humidity}` : '-'}
2025-09-15 09:31:47 +00:00
</td>
<td className="px-3 py-2 whitespace-nowrap">
{zone.isActive ? (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
<FaCheckCircle className="w-3 h-3 mr-1" />
Aktif
</span>
) : (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
<FaExclamationCircle className="w-3 h-3 mr-1" />
Pasif
</span>
)}
</td>
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2">
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleZoneAction('view', zone)}
2025-09-15 09:31:47 +00:00
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleZoneAction('edit', zone)}
2025-09-15 09:31:47 +00:00
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleDelete('zone', zone.id)}
2025-09-15 09:31:47 +00:00
className="text-red-600 hover:text-red-900"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</td>
</tr>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
})}
</tbody>
</table>
</div>
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
const ZonesTab = () => (
<div className="space-y-4 pt-2">
{/* Warehouse Filter */}
<div className="flex flex-col sm:flex-row gap-4">
<select
value={selectedWarehouse}
onChange={(e) => setSelectedWarehouse(e.target.value)}
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Tüm Depolar</option>
{warehouses.map((warehouse) => (
<option key={warehouse.id} value={warehouse.id}>
{warehouse.name} ({warehouse.code})
</option>
))}
</select>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleZoneAction('create')}
2025-09-15 09:31:47 +00:00
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
>
<FaPlus className="w-4 h-4" />
Yeni Bölge
</button>
</div>
{/* Content based on view mode */}
2025-09-15 21:11:40 +00:00
{viewMode === 'grid' ? <ZonesGridView /> : <ZonesListView />}
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
// Location Components
const LocationsGridView = () => (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredLocations.map((location) => {
2025-09-15 21:11:40 +00:00
const warehouse = warehouses.find((w) => w.id === location.warehouseId)
const zone = zones.find((z) => z.id === location.zoneId)
const utilizationPercentage = (location.currentStock / location.capacity) * 100
2025-09-15 09:31:47 +00:00
return (
<div
key={location.id}
className="bg-white rounded-lg shadow-sm border border-gray-200 p-3"
>
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-2">
<div className="p-2 bg-orange-100 rounded-lg">
<FaMapMarkerAlt className="w-5 h-5 text-orange-600" />
</div>
<div>
2025-09-18 12:46:07 +00:00
<div className="text-xs font-medium text-gray-900">{location.name}</div>
2025-09-15 21:11:40 +00:00
<p className="text-sm text-gray-500">{location.locationCode}</p>
2025-09-15 09:31:47 +00:00
</div>
</div>
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
{getLocationTypeText(location.locationType)}
</span>
</div>
<div className="space-y-2 flex-grow">
<div className="text-xs text-gray-600">
<p>
<strong>Depo:</strong> {warehouse?.name}
</p>
{zone && (
<p>
<strong>Bölge:</strong> {zone.name}
</p>
)}
<p>{location.description}</p>
</div>
{/* Dimensions */}
{location.dimensions && (
<div className="bg-gray-50 rounded-lg p-2 mt-2">
2025-09-15 21:11:40 +00:00
<h4 className="text-sm font-medium text-gray-900 mb-2">Boyutlar</h4>
2025-09-15 09:31:47 +00:00
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
<div>Uzunluk: {location.dimensions.length}m</div>
<div>Genişlik: {location.dimensions.width}m</div>
<div>Yükseklik: {location.dimensions.height}m</div>
<div>
Maks. ırlık: {location.dimensions.weight}
{location.dimensions.unit}
</div>
</div>
</div>
)}
{/* Capacity */}
<div className="space-y-1">
<div className="flex justify-between text-xs">
<span className="text-gray-600">Kapasite Kullanımı</span>
<span className="font-medium">
{location.currentStock} / {location.capacity}
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className={`h-2 rounded-full ${
utilizationPercentage > 90
2025-09-15 21:11:40 +00:00
? 'bg-red-500'
2025-09-15 09:31:47 +00:00
: utilizationPercentage > 70
2025-09-15 21:11:40 +00:00
? 'bg-yellow-500'
: 'bg-green-500'
2025-09-15 09:31:47 +00:00
}`}
style={{ width: `${utilizationPercentage}%` }}
></div>
</div>
<div className="text-xs text-gray-500">
%{Math.round(utilizationPercentage)} dolu
</div>
</div>
{/* Restrictions */}
{location.restrictions && location.restrictions.length > 0 && (
<div className="pt-2">
2025-09-15 21:11:40 +00:00
<h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
2025-09-15 09:31:47 +00:00
<div className="flex flex-wrap gap-1">
{location.restrictions.map((restriction, index) => (
<span
key={index}
className="inline-flex px-2 py-1 text-xs bg-yellow-100 text-yellow-800 rounded"
>
{restriction}
</span>
))}
</div>
</div>
)}
</div>
<div className="flex items-center justify-between pt-3 mt-3 border-t border-gray-100">
<div className="flex items-center gap-2">
{location.isActive ? (
<>
<FaCheckCircle className="w-4 h-4 text-green-500" />
<span className="text-xs text-green-600">Aktif</span>
</>
) : (
<>
<FaExclamationCircle className="w-4 h-4 text-red-500" />
<span className="text-sm text-red-600">Pasif</span>
</>
)}
</div>
<div className="flex items-center gap-2">
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleLocationAction('view', location)}
2025-09-15 09:31:47 +00:00
className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleLocationAction('edit', location)}
2025-09-15 09:31:47 +00:00
className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleDelete('location', location.id)}
2025-09-15 09:31:47 +00:00
className="p-1 text-gray-400 hover:text-red-600"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</div>
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
})}
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
const LocationsListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Lokasyon
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Depo
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Bölge
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tip
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Kapasite
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Kullanım
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Durum
</th>
<th className="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
İşlemler
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredLocations.map((location) => {
2025-09-15 21:11:40 +00:00
const warehouse = warehouses.find((w) => w.id === location.warehouseId)
const zone = zones.find((z) => z.id === location.zoneId)
const utilizationPercentage = (location.currentStock / location.capacity) * 100
2025-09-15 09:31:47 +00:00
return (
<tr key={location.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
<div className="p-2 bg-orange-100 rounded-lg mr-3">
<FaMapMarkerAlt className="w-4 h-4 text-orange-600" />
</div>
<div>
2025-09-15 21:11:40 +00:00
<div className="text-sm font-medium text-gray-900">{location.name}</div>
<div className="text-sm text-gray-500">{location.locationCode}</div>
2025-09-15 09:31:47 +00:00
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
2025-09-15 21:11:40 +00:00
<div className="text-sm text-gray-900">{warehouse?.name}</div>
<div className="text-sm text-gray-500">{warehouse?.code}</div>
2025-09-15 09:31:47 +00:00
</td>
<td className="px-3 py-2 whitespace-nowrap">
2025-09-15 21:11:40 +00:00
<div className="text-sm text-gray-900">{zone?.name || '-'}</div>
<div className="text-sm text-gray-500">{zone?.zoneCode || '-'}</div>
2025-09-15 09:31:47 +00:00
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
{getLocationTypeText(location.locationType)}
</span>
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{location.capacity} birim
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
<div className="w-16 bg-gray-200 rounded-full h-2 mr-2">
<div
className={`h-2 rounded-full ${
utilizationPercentage > 90
2025-09-15 21:11:40 +00:00
? 'bg-red-500'
2025-09-15 09:31:47 +00:00
: utilizationPercentage > 70
2025-09-15 21:11:40 +00:00
? 'bg-yellow-500'
: 'bg-green-500'
2025-09-15 09:31:47 +00:00
}`}
style={{ width: `${utilizationPercentage}%` }}
></div>
</div>
<span className="text-sm text-gray-900">
%{Math.round(utilizationPercentage)}
</span>
</div>
<div className="text-xs text-gray-500">
{location.currentStock} / {location.capacity}
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
{location.isActive ? (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
<FaCheckCircle className="w-3 h-3 mr-1" />
Aktif
</span>
) : (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
<FaExclamationCircle className="w-3 h-3 mr-1" />
Pasif
</span>
)}
</td>
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2">
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleLocationAction('view', location)}
2025-09-15 09:31:47 +00:00
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleLocationAction('edit', location)}
2025-09-15 09:31:47 +00:00
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleDelete('location', location.id)}
2025-09-15 09:31:47 +00:00
className="text-red-600 hover:text-red-900"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</td>
</tr>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
})}
</tbody>
</table>
</div>
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
const LocationsTab = () => (
<div className="space-y-4 pt-2">
{/* Warehouse Filter */}
<div className="flex flex-col sm:flex-row gap-4">
<select
value={selectedWarehouse}
onChange={(e) => setSelectedWarehouse(e.target.value)}
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Tüm Depolar</option>
{warehouses.map((warehouse) => (
<option key={warehouse.id} value={warehouse.id}>
{warehouse.name} ({warehouse.code})
</option>
))}
</select>
<button
2025-09-15 21:11:40 +00:00
onClick={() => handleLocationAction('create')}
2025-09-15 09:31:47 +00:00
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
>
<FaPlus className="w-4 h-4" />
Yeni Lokasyon
</button>
</div>
{/* Content based on view mode */}
2025-09-15 21:11:40 +00:00
{viewMode === 'grid' ? <LocationsGridView /> : <LocationsListView />}
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 21:11:40 +00:00
)
2025-09-15 09:31:47 +00:00
return (
2025-09-15 21:11:40 +00:00
<Container>
<div className="space-y-2">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-gray-900">Depo Tanımları</h2>
<p className="text-gray-600">Depolar, bölgeler ve lokasyonları yönetin</p>
</div>
{/* View Toggle Buttons */}
{(activeTab === 'warehouses' || activeTab === 'zones' || activeTab === 'locations') && (
<div className="flex items-center gap-2">
<button
onClick={() => setViewMode('grid')}
className={`p-1.5 rounded-lg ${
viewMode === 'grid'
? 'bg-blue-100 text-blue-600'
: 'text-gray-400 hover:text-gray-600'
}`}
title="Kart Görünümü"
>
<FaTh className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode('list')}
className={`p-1.5 rounded-lg ${
viewMode === 'list'
? 'bg-blue-100 text-blue-600'
: 'text-gray-400 hover:text-gray-600'
}`}
title="Liste Görünümü"
>
<FaList className="w-4 h-4" />
</button>
</div>
)}
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 21:11:40 +00:00
{/* Tabs */}
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8" aria-label="Tabs">
2025-09-15 09:31:47 +00:00
<button
2025-09-15 21:11:40 +00:00
onClick={() => setActiveTab('warehouses')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === 'warehouses'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
2025-09-15 09:31:47 +00:00
}`}
>
2025-09-15 21:11:40 +00:00
<div className="flex items-center gap-2">
<FaBuilding className="w-4 h-4" />
Depolar
</div>
2025-09-15 09:31:47 +00:00
</button>
<button
2025-09-15 21:11:40 +00:00
onClick={() => setActiveTab('zones')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === 'zones'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
2025-09-15 09:31:47 +00:00
}`}
>
2025-09-15 21:11:40 +00:00
<div className="flex items-center gap-2">
<FaLayerGroup className="w-4 h-4" />
Bölgeler
</div>
2025-09-15 09:31:47 +00:00
</button>
2025-09-15 21:11:40 +00:00
<button
onClick={() => setActiveTab('locations')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === 'locations'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<div className="flex items-center gap-2">
<FaMapMarkerAlt className="w-4 h-4" />
Lokasyonlar
</div>
</button>
</nav>
</div>
2025-09-15 09:31:47 +00:00
2025-09-15 21:11:40 +00:00
{/* Tab Content */}
{activeTab === 'warehouses' && <WarehousesTab />}
{activeTab === 'zones' && <ZonesTab />}
{activeTab === 'locations' && <LocationsTab />}
2025-09-15 09:31:47 +00:00
</div>
{/* Modals */}
<WarehouseModal
isOpen={warehouseModal.isOpen}
onClose={() => setWarehouseModal({ ...warehouseModal, isOpen: false })}
onSave={handleWarehouseSave}
warehouse={warehouseModal.warehouse}
mode={warehouseModal.mode}
/>
<ZoneModal
isOpen={zoneModal.isOpen}
onClose={() => setZoneModal({ ...zoneModal, isOpen: false })}
onSave={handleZoneSave}
zone={zoneModal.zone}
mode={zoneModal.mode}
warehouses={warehouses}
/>
<LocationModal
isOpen={locationModal.isOpen}
onClose={() => setLocationModal({ ...locationModal, isOpen: false })}
onSave={handleLocationSave}
location={locationModal.location}
mode={locationModal.mode}
warehouses={warehouses}
zones={zones}
/>
2025-09-15 21:11:40 +00:00
</Container>
)
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:11:40 +00:00
export default WarehouseDefinitions