erp-platform/ui/src/views/warehouse/components/WarehouseDefinitions.tsx
2025-09-18 15:46:07 +03:00

1147 lines
45 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react'
import { WmWarehouse, WarehouseTypeEnum, WmZone, WmLocation } from '../../../types/wm'
import {
FaPlus,
FaSearch,
FaEdit,
FaTrash,
FaBuilding,
FaMapMarkerAlt,
FaLayerGroup,
FaExclamationCircle,
FaCheckCircle,
FaEye,
FaTh,
FaList,
} 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'
import {
getWarehouseTypeColor,
getWarehouseTypeText,
getZoneTypeText,
getLocationTypeText,
} from '../../../utils/erp'
import { Container } from '@/components/shared'
const WarehouseDefinitions: React.FC = () => {
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')
// Modal states
const [warehouseModal, setWarehouseModal] = useState<{
isOpen: boolean
mode: 'create' | 'edit' | 'view'
warehouse: WmWarehouse | null
}>({
isOpen: false,
mode: 'create',
warehouse: null,
})
const [zoneModal, setZoneModal] = useState<{
isOpen: boolean
mode: 'create' | 'edit' | 'view'
zone: WmZone | null
}>({
isOpen: false,
mode: 'create',
zone: null,
})
const [locationModal, setLocationModal] = useState<{
isOpen: boolean
mode: 'create' | 'edit' | 'view'
location: WmLocation | null
}>({
isOpen: false,
mode: 'create',
location: null,
})
// Data states (in a real app, these would come from an API)
const [warehouses, setWarehouses] = useState<WmWarehouse[]>(mockWarehouses)
const [zones, setZones] = useState<WmZone[]>(mockZones)
const [locations, setLocations] = useState<WmLocation[]>(mockLocations)
// Modal handlers
const handleWarehouseAction = (mode: 'create' | 'edit' | 'view', warehouse?: WmWarehouse) => {
setWarehouseModal({
isOpen: true,
mode,
warehouse: warehouse || null,
})
}
const handleZoneAction = (mode: 'create' | 'edit' | 'view', zone?: WmZone) => {
setZoneModal({
isOpen: true,
mode,
zone: zone || null,
})
}
const handleLocationAction = (mode: 'create' | 'edit' | 'view', location?: WmLocation) => {
setLocationModal({
isOpen: true,
mode,
location: location || null,
})
}
const handleWarehouseSave = (warehouseData: Partial<WmWarehouse>) => {
if (warehouseModal.mode === 'create') {
const newWarehouse: WmWarehouse = {
...warehouseData,
id: Date.now().toString(),
locations: [],
zones: [],
creationTime: new Date(),
lastModificationTime: new Date(),
} as WmWarehouse
setWarehouses([...warehouses, newWarehouse])
} else if (warehouseModal.mode === 'edit' && warehouseModal.warehouse) {
setWarehouses(
warehouses.map((w) =>
w.id === warehouseModal.warehouse!.id
? ({
...w,
...warehouseData,
lastModificationTime: new Date(),
} as WmWarehouse)
: w,
),
)
}
}
const handleZoneSave = (zoneData: Partial<WmZone>) => {
if (zoneModal.mode === 'create') {
const newZone: WmZone = {
...zoneData,
id: Date.now().toString(),
locations: [],
} as WmZone
setZones([...zones, newZone])
} else if (zoneModal.mode === 'edit' && zoneModal.zone) {
setZones(
zones.map((z) => (z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z)),
)
}
}
const handleLocationSave = (locationData: Partial<WmLocation>) => {
if (locationModal.mode === 'create') {
const newLocation: WmLocation = {
...locationData,
id: Date.now().toString(),
stockItems: [],
} as WmLocation
setLocations([...locations, newLocation])
} else if (locationModal.mode === 'edit' && locationModal.location) {
setLocations(
locations.map((l) =>
l.id === locationModal.location!.id ? ({ ...l, ...locationData } as WmLocation) : l,
),
)
}
}
const handleDelete = (type: 'warehouse' | 'zone' | 'location', id: string) => {
if (
window.confirm(
`Bu ${
type === 'warehouse' ? 'depo' : type === 'zone' ? 'bölge' : 'lokasyon'
}u silmek istediğinizden emin misiniz?`,
)
) {
switch (type) {
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
}
}
}
// Filtered data
const filteredWarehouses = warehouses.filter((warehouse) => {
const matchesSearch =
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase())
const matchesType = selectedType === '' || warehouse.warehouseType === selectedType
return matchesSearch && matchesType
})
const filteredZones = zones.filter(
(zone) => selectedWarehouse === '' || zone.warehouseId === selectedWarehouse,
)
const filteredLocations = locations.filter(
(location) => selectedWarehouse === '' || location.warehouseId === selectedWarehouse,
)
// 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>
<h3 className="font-medium text-sm text-gray-900">{warehouse.name}</h3>
<p className="text-gray-600">{warehouse.code}</p>
</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(
warehouse.warehouseType,
)}`}
>
{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={{
width: `${(warehouse.currentUtilization / warehouse.capacity) * 100}%`,
}}
></div>
</div>
<div className="text-xs text-gray-500">
%{Math.round((warehouse.currentUtilization / warehouse.capacity) * 100)} dolu
</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
onClick={() => handleWarehouseAction('view', warehouse)}
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
onClick={() => handleWarehouseAction('edit', warehouse)}
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
onClick={() => handleDelete('warehouse', warehouse.id)}
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>
)
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 =
(warehouse.currentUtilization / warehouse.capacity) * 100
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>
<div className="text-xs text-gray-500">{warehouse.code}</div>
</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(
warehouse.warehouseType,
)}`}
>
{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>
<div className="text-xs text-gray-500">{warehouse.address?.street}</div>
</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
? 'bg-red-500'
: utilizationPercentage > 70
? 'bg-yellow-500'
: 'bg-blue-500'
}`}
style={{ width: `${utilizationPercentage}%` }}
></div>
</div>
<span className="text-xs text-gray-900">
%{Math.round(utilizationPercentage)}
</span>
</div>
<div className="text-xs text-gray-500">
{warehouse.currentUtilization.toLocaleString()} /{' '}
{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
onClick={() => handleWarehouseAction('view', warehouse)}
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleWarehouseAction('edit', warehouse)}
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete('warehouse', warehouse.id)}
className="text-red-600 hover:text-red-900"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
)
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}
onChange={(e) => setSelectedType(e.target.value as WarehouseTypeEnum | '')}
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
onClick={() => handleWarehouseAction('create')}
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 */}
{viewMode === 'grid' ? <WarehousesGridView /> : <WarehousesListView />}
</div>
)
// 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) => {
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
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>
<h3 className="font-medium text-sm text-gray-900">{zone.name}</h3>
<p className="text-gray-600">{zone.zoneCode}</p>
</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">
<h4 className="text-xs font-medium text-gray-900 mb-1">Çevresel Koşullar</h4>
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
{zone.temperature && <div>Sıcaklık: {zone.temperature}°C</div>}
{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
onClick={() => handleZoneAction('view', zone)}
className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleZoneAction('edit', zone)}
className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete('zone', zone.id)}
className="p-1 text-gray-400 hover:text-red-600"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</div>
</div>
)
})}
</div>
)
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) => {
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
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>
<div className="text-sm font-medium text-gray-900">{zone.name}</div>
<div className="text-sm text-gray-500">{zone.zoneCode}</div>
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900">{warehouse?.name}</div>
<div className="text-sm text-gray-500">{warehouse?.code}</div>
</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">
{zone.temperature ? `${zone.temperature}°C` : '-'}
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{zone.humidity ? `%${zone.humidity}` : '-'}
</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
onClick={() => handleZoneAction('view', zone)}
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleZoneAction('edit', zone)}
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete('zone', zone.id)}
className="text-red-600 hover:text-red-900"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
)
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
onClick={() => handleZoneAction('create')}
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 */}
{viewMode === 'grid' ? <ZonesGridView /> : <ZonesListView />}
</div>
)
// Location Components
const LocationsGridView = () => (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredLocations.map((location) => {
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
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>
<div className="text-xs font-medium text-gray-900">{location.name}</div>
<p className="text-sm text-gray-500">{location.locationCode}</p>
</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">
<h4 className="text-sm font-medium text-gray-900 mb-2">Boyutlar</h4>
<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
? 'bg-red-500'
: utilizationPercentage > 70
? 'bg-yellow-500'
: 'bg-green-500'
}`}
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">
<h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
<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
onClick={() => handleLocationAction('view', location)}
className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleLocationAction('edit', location)}
className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete('location', location.id)}
className="p-1 text-gray-400 hover:text-red-600"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</div>
</div>
)
})}
</div>
)
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) => {
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
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>
<div className="text-sm font-medium text-gray-900">{location.name}</div>
<div className="text-sm text-gray-500">{location.locationCode}</div>
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900">{warehouse?.name}</div>
<div className="text-sm text-gray-500">{warehouse?.code}</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900">{zone?.name || '-'}</div>
<div className="text-sm text-gray-500">{zone?.zoneCode || '-'}</div>
</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
? 'bg-red-500'
: utilizationPercentage > 70
? 'bg-yellow-500'
: 'bg-green-500'
}`}
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
onClick={() => handleLocationAction('view', location)}
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleLocationAction('edit', location)}
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete('location', location.id)}
className="text-red-600 hover:text-red-900"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
)
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
onClick={() => handleLocationAction('create')}
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 */}
{viewMode === 'grid' ? <LocationsGridView /> : <LocationsListView />}
</div>
)
return (
<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>
)}
</div>
{/* Tabs */}
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8" aria-label="Tabs">
<button
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'
}`}
>
<div className="flex items-center gap-2">
<FaBuilding className="w-4 h-4" />
Depolar
</div>
</button>
<button
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'
}`}
>
<div className="flex items-center gap-2">
<FaLayerGroup className="w-4 h-4" />
Bölgeler
</div>
</button>
<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>
{/* Tab Content */}
{activeTab === 'warehouses' && <WarehousesTab />}
{activeTab === 'zones' && <ZonesTab />}
{activeTab === 'locations' && <LocationsTab />}
</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}
/>
</Container>
)
}
export default WarehouseDefinitions