Warehouse Management

This commit is contained in:
Sedat Öztürk 2025-09-16 00:11:40 +03:00
parent 8b88970fe2
commit bb2e39c2c3
13 changed files with 4751 additions and 5982 deletions

View file

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import {
FaBox, FaBox,
FaMapMarkerAlt, FaMapMarkerAlt,
@ -8,71 +8,61 @@ import {
FaEye, FaEye,
FaEdit, FaEdit,
FaPlus, FaPlus,
} from "react-icons/fa"; } from 'react-icons/fa'
// using modals for create actions; no navigation needed here // using modals for create actions; no navigation needed here
import { import { MmLotNumber, MmSerialNumber, QualityStatusEnum, SerialStatusEnum } from '../../../types/mm'
MmLotNumber, import LotForm from './LotForm'
MmSerialNumber, import SerialForm from './SerialForm'
QualityStatusEnum, import { mockLotNumbers } from '../../../mocks/mockLotNumbers'
SerialStatusEnum, import { mockSerialNumbers } from '../../../mocks/mockSerialNumbers'
} from "../../../types/mm"; import { mockMaterials } from '../../../mocks/mockMaterials'
import LotForm from "./LotForm"; import { mockUnits } from '../../../mocks/mockUnits'
import SerialForm from "./SerialForm"; import { mockBusinessParties } from '../../../mocks/mockBusinessParties'
import { mockLotNumbers } from "../../../mocks/mockLotNumbers"; import Widget from '../../../components/common/Widget'
import { mockSerialNumbers } from "../../../mocks/mockSerialNumbers"; import { PartyType } from '../../../types/common'
import { mockMaterials } from "../../../mocks/mockMaterials"; import { getQualityStatusInfo, getSerialStatusInfo } from '../../../utils/erp'
import { mockUnits } from "../../../mocks/mockUnits"; import { Container } from '@/components/shared'
import { mockBusinessParties } from "../../../mocks/mockBusinessParties";
import Widget from "../../../components/common/Widget";
import { PartyType } from "../../../types/common";
import { getQualityStatusInfo, getSerialStatusInfo } from "../../../utils/erp";
const InventoryTracking: React.FC = () => { const InventoryTracking: React.FC = () => {
const [activeTab, setActiveTab] = useState<"lots" | "serials">("lots"); const [activeTab, setActiveTab] = useState<'lots' | 'serials'>('lots')
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [showCreateMenu, setShowCreateMenu] = useState(false); const [showCreateMenu, setShowCreateMenu] = useState(false)
// Mock data için // Mock data için
const [lotNumbers, setLotNumbers] = useState<MmLotNumber[]>(mockLotNumbers); const [lotNumbers, setLotNumbers] = useState<MmLotNumber[]>(mockLotNumbers)
const [serialNumbers, setSerialNumbers] = const [serialNumbers, setSerialNumbers] = useState<MmSerialNumber[]>(mockSerialNumbers)
useState<MmSerialNumber[]>(mockSerialNumbers);
// Modal control state // Modal control state
const [openLotModal, setOpenLotModal] = useState(false); const [openLotModal, setOpenLotModal] = useState(false)
const [openSerialModal, setOpenSerialModal] = useState(false); const [openSerialModal, setOpenSerialModal] = useState(false)
const [currentLot, setCurrentLot] = useState<MmLotNumber | null>(null); const [currentLot, setCurrentLot] = useState<MmLotNumber | null>(null)
const [currentSerial, setCurrentSerial] = useState<MmSerialNumber | null>( const [currentSerial, setCurrentSerial] = useState<MmSerialNumber | null>(null)
null
);
const filteredLots = lotNumbers.filter( const filteredLots = lotNumbers.filter(
(lot) => (lot) =>
lot.lotNumber.toLowerCase().includes(searchTerm.toLowerCase()) || lot.lotNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase()) lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase()),
); )
const filteredSerials = serialNumbers.filter( const filteredSerials = serialNumbers.filter(
(serial) => (serial) =>
serial.serialNumber.toLowerCase().includes(searchTerm.toLowerCase()) || serial.serialNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase()) serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase()),
); )
return ( return (
<div className="space-y-3 py-2"> <Container>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-2xl font-bold text-gray-900">Envanter Takibi</h2> <h2 className="text-2xl font-bold text-gray-900">Envanter Takibi</h2>
<p className="text-gray-600"> <p className="text-gray-600">Lot ve seri numarası takiplerini yönetin</p>
Lot ve seri numarası takiplerini yönetin
</p>
</div> </div>
<div className="relative"> <div className="relative">
<button <button
onClick={() => onClick={() => setShowCreateMenu((s) => !s)} /* Kontrol küçültüldü */
setShowCreateMenu((s) => !s)
} /* Kontrol küçültüldü */
className="bg-blue-600 text-white px-2.5 py-1 rounded-md hover:bg-blue-700 flex items-center space-x-1.5 text-sm" className="bg-blue-600 text-white px-2.5 py-1 rounded-md hover:bg-blue-700 flex items-center space-x-1.5 text-sm"
> >
<FaPlus className="h-4 w-4" /> <FaPlus className="h-4 w-4" />
@ -84,8 +74,8 @@ const InventoryTracking: React.FC = () => {
<div className="absolute right-0 mt-2 w-44 bg-white border rounded shadow-lg z-10"> <div className="absolute right-0 mt-2 w-44 bg-white border rounded shadow-lg z-10">
<button <button
onClick={() => { onClick={() => {
setOpenLotModal(true); setOpenLotModal(true)
setShowCreateMenu(false); setShowCreateMenu(false)
}} }}
className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50" className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50"
> >
@ -93,8 +83,8 @@ const InventoryTracking: React.FC = () => {
</button> </button>
<button <button
onClick={() => { onClick={() => {
setOpenSerialModal(true); setOpenSerialModal(true)
setShowCreateMenu(false); setShowCreateMenu(false)
}} }}
className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50" className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50"
> >
@ -107,19 +97,12 @@ const InventoryTracking: React.FC = () => {
{/* Summary Statistics */} {/* Summary Statistics */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-2"> <div className="grid grid-cols-1 md:grid-cols-4 gap-2">
<Widget <Widget title="Toplam Lot" value={lotNumbers.length} color="blue" icon="FaBox" />
title="Toplam Lot"
value={lotNumbers.length}
color="blue"
icon="FaBox"
/>
<Widget <Widget
title="Onaylı Lot" title="Onaylı Lot"
value={ value={
lotNumbers.filter( lotNumbers.filter((lot) => lot.qualityStatus === QualityStatusEnum.Approved).length
(lot) => lot.qualityStatus === QualityStatusEnum.Approved
).length
} }
color="green" color="green"
icon="FaBox" icon="FaBox"
@ -135,9 +118,7 @@ const InventoryTracking: React.FC = () => {
<Widget <Widget
title="Müsait Seri" title="Müsait Seri"
value={ value={
serialNumbers.filter( serialNumbers.filter((serial) => serial.status === SerialStatusEnum.Available).length
(serial) => serial.status === SerialStatusEnum.Available
).length
} }
color="orange" color="orange"
icon="FaHashtag" icon="FaHashtag"
@ -160,22 +141,22 @@ const InventoryTracking: React.FC = () => {
<div className="border-b border-gray-200"> <div className="border-b border-gray-200">
<nav className="flex -mb-px"> <nav className="flex -mb-px">
<button <button
onClick={() => setActiveTab("lots")} onClick={() => setActiveTab('lots')}
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${ className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
activeTab === "lots" activeTab === 'lots'
? "border-blue-500 text-blue-600" ? 'border-blue-500 text-blue-600'
: "border-transparent text-gray-500 hover:text-gray-700" : 'border-transparent text-gray-500 hover:text-gray-700'
}`} }`}
> >
<FaBox className="h-4 w-4 mr-2" /> <FaBox className="h-4 w-4 mr-2" />
Lot Numaraları ({filteredLots.length}) Lot Numaraları ({filteredLots.length})
</button> </button>
<button <button
onClick={() => setActiveTab("serials")} onClick={() => setActiveTab('serials')}
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${ className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
activeTab === "serials" activeTab === 'serials'
? "border-blue-500 text-blue-600" ? 'border-blue-500 text-blue-600'
: "border-transparent text-gray-500 hover:text-gray-700" : 'border-transparent text-gray-500 hover:text-gray-700'
}`} }`}
> >
<FaHashtag className="h-4 w-4 mr-2" /> <FaHashtag className="h-4 w-4 mr-2" />
@ -185,7 +166,7 @@ const InventoryTracking: React.FC = () => {
</div> </div>
{/* Lot Numbers Tab */} {/* Lot Numbers Tab */}
{activeTab === "lots" && ( {activeTab === 'lots' && (
<div className="bg-white shadow-sm rounded-lg overflow-hidden"> <div className="bg-white shadow-sm rounded-lg overflow-hidden">
<table className="min-w-full divide-y divide-gray-200"> <table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50"> <thead className="bg-gray-50">
@ -218,20 +199,18 @@ const InventoryTracking: React.FC = () => {
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{filteredLots.map((lot) => { {filteredLots.map((lot) => {
const qualityInfo = getQualityStatusInfo(lot.qualityStatus); const qualityInfo = getQualityStatusInfo(lot.qualityStatus)
const isExpiringSoon = const isExpiringSoon =
lot.expiryDate && lot.expiryDate &&
new Date(lot.expiryDate).getTime() - new Date().getTime() < new Date(lot.expiryDate).getTime() - new Date().getTime() <
30 * 24 * 60 * 60 * 1000; 30 * 24 * 60 * 60 * 1000
return ( return (
<tr key={lot.id} className="hover:bg-gray-50 text-xs"> <tr key={lot.id} className="hover:bg-gray-50 text-xs">
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
<div className="flex items-center"> <div className="flex items-center">
<FaBox className="h-4 w-4 text-gray-400 mr-2" /> <FaBox className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs font-medium text-gray-900"> <div className="text-xs font-medium text-gray-900">{lot.lotNumber}</div>
{lot.lotNumber}
</div>
</div> </div>
</td> </td>
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
@ -248,9 +227,7 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center"> <div className="flex items-center">
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" /> <FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs text-gray-900"> <div className="text-xs text-gray-900">
{new Date(lot.productionDate).toLocaleDateString( {new Date(lot.productionDate).toLocaleDateString('tr-TR')}
"tr-TR"
)}
</div> </div>
</div> </div>
</td> </td>
@ -258,16 +235,12 @@ const InventoryTracking: React.FC = () => {
{lot.expiryDate ? ( {lot.expiryDate ? (
<div <div
className={`text-xs ${ className={`text-xs ${
isExpiringSoon isExpiringSoon ? 'text-red-600 font-medium' : 'text-gray-900'
? "text-red-600 font-medium"
: "text-gray-900"
}`} }`}
> >
{new Date(lot.expiryDate).toLocaleDateString("tr-TR")} {new Date(lot.expiryDate).toLocaleDateString('tr-TR')}
{isExpiringSoon && ( {isExpiringSoon && (
<div className="text-xs text-red-500"> <div className="text-xs text-red-500">Yakında Dolacak</div>
Yakında Dolacak
</div>
)} )}
</div> </div>
) : ( ) : (
@ -275,9 +248,7 @@ const InventoryTracking: React.FC = () => {
)} )}
</td> </td>
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
<div className="text-xs text-gray-900"> <div className="text-xs text-gray-900">{lot.supplierId || '-'}</div>
{lot.supplierId || "-"}
</div>
</td> </td>
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
<span <span
@ -290,8 +261,8 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center justify-end space-x-2"> <div className="flex items-center justify-end space-x-2">
<button <button
onClick={() => { onClick={() => {
setCurrentLot(lot); setCurrentLot(lot)
setOpenLotModal(true); setOpenLotModal(true)
}} }}
className="text-blue-600 hover:text-blue-900" className="text-blue-600 hover:text-blue-900"
> >
@ -299,8 +270,8 @@ const InventoryTracking: React.FC = () => {
</button> </button>
<button <button
onClick={() => { onClick={() => {
setCurrentLot(lot); setCurrentLot(lot)
setOpenLotModal(true); setOpenLotModal(true)
}} }}
className="text-green-600 hover:text-green-900" className="text-green-600 hover:text-green-900"
> >
@ -309,37 +280,31 @@ const InventoryTracking: React.FC = () => {
</div> </div>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
<LotForm <LotForm
isOpen={openLotModal} isOpen={openLotModal}
onClose={() => { onClose={() => {
setOpenLotModal(false); setOpenLotModal(false)
setCurrentLot(null); setCurrentLot(null)
}} }}
onSave={(lot) => setLotNumbers((prev) => [lot, ...prev])} onSave={(lot) => setLotNumbers((prev) => [lot, ...prev])}
onUpdate={(updated) => onUpdate={(updated) =>
setLotNumbers((prev) => setLotNumbers((prev) => prev.map((l) => (l.id === updated.id ? updated : l)))
prev.map((l) => (l.id === updated.id ? updated : l))
)
} }
materials={mockMaterials} materials={mockMaterials}
units={mockUnits} units={mockUnits}
suppliers={mockBusinessParties.filter( suppliers={mockBusinessParties.filter((bp) => bp.partyType === PartyType.Supplier)}
(bp) => bp.partyType === PartyType.Supplier
)}
initial={currentLot} initial={currentLot}
mode={currentLot ? "edit" : "create"} mode={currentLot ? 'edit' : 'create'}
/> />
{filteredLots.length === 0 && ( {filteredLots.length === 0 && (
<div className="text-center py-12"> <div className="text-center py-12">
<FaBox className="mx-auto h-12 w-12 text-gray-400" /> <FaBox className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900"> <h3 className="mt-2 text-sm font-medium text-gray-900">Lot bulunamadı</h3>
Lot bulunamadı
</h3>
<p className="mt-1 text-sm text-gray-500"> <p className="mt-1 text-sm text-gray-500">
Arama kriterlerinize uygun lot kaydı bulunmuyor. Arama kriterlerinize uygun lot kaydı bulunmuyor.
</p> </p>
@ -349,7 +314,7 @@ const InventoryTracking: React.FC = () => {
)} )}
{/* Serial Numbers Tab */} {/* Serial Numbers Tab */}
{activeTab === "serials" && ( {activeTab === 'serials' && (
<div className="bg-white shadow-sm rounded-lg overflow-hidden"> <div className="bg-white shadow-sm rounded-lg overflow-hidden">
<table className="min-w-full divide-y divide-gray-200"> <table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50"> <thead className="bg-gray-50">
@ -382,12 +347,11 @@ const InventoryTracking: React.FC = () => {
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{filteredSerials.map((serial) => { {filteredSerials.map((serial) => {
const statusInfo = getSerialStatusInfo(serial.status); const statusInfo = getSerialStatusInfo(serial.status)
const isWarrantyExpiring = const isWarrantyExpiring =
serial.warrantyExpiryDate && serial.warrantyExpiryDate &&
new Date(serial.warrantyExpiryDate).getTime() - new Date(serial.warrantyExpiryDate).getTime() - new Date().getTime() <
new Date().getTime() < 30 * 24 * 60 * 60 * 1000
30 * 24 * 60 * 60 * 1000;
return ( return (
<tr key={serial.id} className="hover:bg-gray-50 text-xs"> <tr key={serial.id} className="hover:bg-gray-50 text-xs">
@ -411,7 +375,7 @@ const InventoryTracking: React.FC = () => {
LOT-{serial.lotId} LOT-{serial.lotId}
</span> </span>
) : ( ) : (
"-" '-'
)} )}
</div> </div>
</td> </td>
@ -419,9 +383,7 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center"> <div className="flex items-center">
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" /> <FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs text-gray-900"> <div className="text-xs text-gray-900">
{new Date(serial.productionDate).toLocaleDateString( {new Date(serial.productionDate).toLocaleDateString('tr-TR')}
"tr-TR"
)}
</div> </div>
</div> </div>
</td> </td>
@ -429,18 +391,12 @@ const InventoryTracking: React.FC = () => {
{serial.warrantyExpiryDate ? ( {serial.warrantyExpiryDate ? (
<div <div
className={`text-xs ${ className={`text-xs ${
isWarrantyExpiring isWarrantyExpiring ? 'text-red-600 font-medium' : 'text-gray-900'
? "text-red-600 font-medium"
: "text-gray-900"
}`} }`}
> >
{new Date( {new Date(serial.warrantyExpiryDate).toLocaleDateString('tr-TR')}
serial.warrantyExpiryDate
).toLocaleDateString("tr-TR")}
{isWarrantyExpiring && ( {isWarrantyExpiring && (
<div className="text-xs text-red-500"> <div className="text-xs text-red-500">Yakında Dolacak</div>
Yakında Dolacak
</div>
)} )}
</div> </div>
) : ( ) : (
@ -451,7 +407,7 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center"> <div className="flex items-center">
<FaMapMarkerAlt className="h-4 w-4 text-gray-400 mr-2" /> <FaMapMarkerAlt className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs text-gray-900"> <div className="text-xs text-gray-900">
{serial.currentLocationId || "-"} {serial.currentLocationId || '-'}
</div> </div>
</div> </div>
</td> </td>
@ -466,8 +422,8 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center justify-end space-x-2"> <div className="flex items-center justify-end space-x-2">
<button <button
onClick={() => { onClick={() => {
setCurrentSerial(serial); setCurrentSerial(serial)
setOpenSerialModal(true); setOpenSerialModal(true)
}} }}
className="text-blue-600 hover:text-blue-900" className="text-blue-600 hover:text-blue-900"
> >
@ -475,8 +431,8 @@ const InventoryTracking: React.FC = () => {
</button> </button>
<button <button
onClick={() => { onClick={() => {
setCurrentSerial(serial); setCurrentSerial(serial)
setOpenSerialModal(true); setOpenSerialModal(true)
}} }}
className="text-green-600 hover:text-green-900" className="text-green-600 hover:text-green-900"
> >
@ -485,7 +441,7 @@ const InventoryTracking: React.FC = () => {
</div> </div>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
@ -493,27 +449,23 @@ const InventoryTracking: React.FC = () => {
<SerialForm <SerialForm
isOpen={openSerialModal} isOpen={openSerialModal}
onClose={() => { onClose={() => {
setOpenSerialModal(false); setOpenSerialModal(false)
setCurrentSerial(null); setCurrentSerial(null)
}} }}
onSave={(serial) => setSerialNumbers((prev) => [serial, ...prev])} onSave={(serial) => setSerialNumbers((prev) => [serial, ...prev])}
onUpdate={(updated) => onUpdate={(updated) =>
setSerialNumbers((prev) => setSerialNumbers((prev) => prev.map((s) => (s.id === updated.id ? updated : s)))
prev.map((s) => (s.id === updated.id ? updated : s))
)
} }
materials={mockMaterials} materials={mockMaterials}
lots={lotNumbers.map((l) => ({ id: l.id, label: l.lotNumber }))} lots={lotNumbers.map((l) => ({ id: l.id, label: l.lotNumber }))}
initial={currentSerial} initial={currentSerial}
mode={currentSerial ? "edit" : "create"} mode={currentSerial ? 'edit' : 'create'}
/> />
{filteredSerials.length === 0 && ( {filteredSerials.length === 0 && (
<div className="text-center py-12"> <div className="text-center py-12">
<FaHashtag className="mx-auto h-12 w-12 text-gray-400" /> <FaHashtag className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900"> <h3 className="mt-2 text-sm font-medium text-gray-900">Seri numarası bulunamadı</h3>
Seri numarası bulunamadı
</h3>
<p className="mt-1 text-sm text-gray-500"> <p className="mt-1 text-sm text-gray-500">
Arama kriterlerinize uygun seri numarası bulunmuyor. Arama kriterlerinize uygun seri numarası bulunmuyor.
</p> </p>
@ -522,7 +474,8 @@ const InventoryTracking: React.FC = () => {
</div> </div>
)} )}
</div> </div>
); </Container>
}; )
}
export default InventoryTracking; export default InventoryTracking

View file

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import {
FaSearch, FaSearch,
FaMapMarkerAlt, FaMapMarkerAlt,
@ -7,45 +7,43 @@ import {
FaEye, FaEye,
FaTh, FaTh,
FaList, FaList,
} from "react-icons/fa"; } from 'react-icons/fa'
import { mockWarehouses } from "../../../mocks/mockWarehouses"; import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { mockLocations } from "../../../mocks/mockLocations"; import { mockLocations } from '../../../mocks/mockLocations'
import { mockStockItems } from "../../../mocks/mockStockItems"; import { mockStockItems } from '../../../mocks/mockStockItems'
import { getStockStatusColor, getStockStatusText } from "../../../utils/erp"; import { getStockStatusColor, getStockStatusText } from '../../../utils/erp'
import { Container } from '@/components/shared'
const LocationTracking: React.FC = () => { const LocationTracking: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [selectedWarehouse, setSelectedWarehouse] = useState<string>(""); const [selectedWarehouse, setSelectedWarehouse] = useState<string>('')
const [selectedLocation, setSelectedLocation] = useState<string>(""); const [selectedLocation, setSelectedLocation] = useState<string>('')
const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
const getLocationUtilization = (locationId: string) => { const getLocationUtilization = (locationId: string) => {
const location = mockLocations.find((l) => l.id === locationId); const location = mockLocations.find((l) => l.id === locationId)
if (!location) return 0; if (!location) return 0
return (location.currentStock / location.capacity) * 100; return (location.currentStock / location.capacity) * 100
}; }
const getLocationStockItems = (locationId: string) => { const getLocationStockItems = (locationId: string) => {
return mockStockItems.filter((item) => item.locationId === locationId); return mockStockItems.filter((item) => item.locationId === locationId)
}; }
const filteredLocations = mockLocations.filter((location) => { const filteredLocations = mockLocations.filter((location) => {
const matchesSearch = const matchesSearch =
location.name.toLowerCase().includes(searchTerm.toLowerCase()) || location.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
location.locationCode.toLowerCase().includes(searchTerm.toLowerCase()); location.locationCode.toLowerCase().includes(searchTerm.toLowerCase())
const matchesWarehouse = const matchesWarehouse = selectedWarehouse === '' || location.warehouseId === selectedWarehouse
selectedWarehouse === "" || location.warehouseId === selectedWarehouse; return matchesSearch && matchesWarehouse
return matchesSearch && matchesWarehouse; })
});
const GridView = () => ( const GridView = () => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredLocations.map((location) => { {filteredLocations.map((location) => {
const warehouse = mockWarehouses.find( const warehouse = mockWarehouses.find((w) => w.id === location.warehouseId)
(w) => w.id === location.warehouseId const locationStockItems = getLocationStockItems(location.id)
); const utilization = getLocationUtilization(location.id)
const locationStockItems = getLocationStockItems(location.id);
const utilization = getLocationUtilization(location.id);
return ( return (
<div <div
@ -59,9 +57,7 @@ const LocationTracking: React.FC = () => {
</div> </div>
<div> <div>
<h3 className="font-medium text-gray-900">{location.name}</h3> <h3 className="font-medium text-gray-900">{location.name}</h3>
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">{location.locationCode}</p>
{location.locationCode}
</p>
</div> </div>
</div> </div>
<button <button
@ -84,18 +80,16 @@ const LocationTracking: React.FC = () => {
<div className="space-y-1"> <div className="space-y-1">
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span className="text-gray-600">Doluluk Oranı</span> <span className="text-gray-600">Doluluk Oranı</span>
<span className="font-medium"> <span className="font-medium">%{Math.round(utilization)}</span>
%{Math.round(utilization)}
</span>
</div> </div>
<div className="w-full bg-gray-200 rounded-full h-2"> <div className="w-full bg-gray-200 rounded-full h-2">
<div <div
className={`h-2 rounded-full ${ className={`h-2 rounded-full ${
utilization > 90 utilization > 90
? "bg-red-500" ? 'bg-red-500'
: utilization > 70 : utilization > 70
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-green-500" : 'bg-green-500'
}`} }`}
style={{ width: `${utilization}%` }} style={{ width: `${utilization}%` }}
/> />
@ -108,29 +102,22 @@ const LocationTracking: React.FC = () => {
{/* Stock Items Summary */} {/* Stock Items Summary */}
<div className="bg-gray-50 rounded-lg p-2"> <div className="bg-gray-50 rounded-lg p-2">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<h4 className="text-sm font-medium text-gray-900"> <h4 className="text-sm font-medium text-gray-900">Malzemeler</h4>
Malzemeler <span className="text-xs text-gray-500">{locationStockItems.length} çeşit</span>
</h4>
<span className="text-xs text-gray-500">
{locationStockItems.length} çeşit
</span>
</div> </div>
{locationStockItems.length > 0 ? ( {locationStockItems.length > 0 ? (
<div className="space-y-1"> <div className="space-y-1">
{locationStockItems.slice(0, 3).map((item) => ( {locationStockItems.slice(0, 3).map((item) => (
<div <div key={item.id} className="flex justify-between items-center text-xs">
key={item.id}
className="flex justify-between items-center text-xs"
>
<span className="text-gray-700 truncate"> <span className="text-gray-700 truncate">
{item.material?.code || "N/A"} {item.material?.code || 'N/A'}
</span> </span>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="font-medium">{item.quantity}</span> <span className="font-medium">{item.quantity}</span>
<span <span
className={`px-1.5 py-0.5 rounded-full text-xs ${getStockStatusColor( className={`px-1.5 py-0.5 rounded-full text-xs ${getStockStatusColor(
item.status item.status,
)}`} )}`}
> >
{getStockStatusText(item.status)} {getStockStatusText(item.status)}
@ -145,22 +132,16 @@ const LocationTracking: React.FC = () => {
)} )}
</div> </div>
) : ( ) : (
<div className="text-xs text-gray-500 text-center py-2"> <div className="text-xs text-gray-500 text-center py-2">Malzeme bulunmuyor</div>
Malzeme bulunmuyor
</div>
)} )}
</div> </div>
{/* Restrictions */} {/* Restrictions */}
{location.restrictions && location.restrictions.length > 0 && ( {location.restrictions && location.restrictions.length > 0 && (
<div> <div>
<h4 className="text-sm font-medium text-gray-900 mb-1"> <h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
Kısıtlamalar
</h4>
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{location.restrictions {location.restrictions.slice(0, 2).map((restriction, index) => (
.slice(0, 2)
.map((restriction, index) => (
<span <span
key={index} key={index}
className="inline-flex px-2 py-1 text-xs bg-yellow-100 text-yellow-800 rounded" className="inline-flex px-2 py-1 text-xs bg-yellow-100 text-yellow-800 rounded"
@ -193,23 +174,21 @@ const LocationTracking: React.FC = () => {
)} )}
</div> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
Son hareket:{" "} Son hareket:{' '}
{locationStockItems.length > 0 {locationStockItems.length > 0
? new Date( ? new Date(
Math.max( Math.max(
...locationStockItems.map((item) => ...locationStockItems.map((item) => item.lastMovementDate.getTime()),
item.lastMovementDate.getTime() ),
).toLocaleDateString('tr-TR')
: 'N/A'}
</div>
</div>
</div>
) )
)
).toLocaleDateString("tr-TR")
: "N/A"}
</div>
</div>
</div>
);
})} })}
</div> </div>
); )
const ListView = () => ( const ListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200"> <div className="bg-white rounded-lg shadow-sm border border-gray-200">
@ -242,11 +221,9 @@ const LocationTracking: React.FC = () => {
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{filteredLocations.map((location) => { {filteredLocations.map((location) => {
const warehouse = mockWarehouses.find( const warehouse = mockWarehouses.find((w) => w.id === location.warehouseId)
(w) => w.id === location.warehouseId const locationStockItems = getLocationStockItems(location.id)
); const utilization = getLocationUtilization(location.id)
const locationStockItems = getLocationStockItems(location.id);
const utilization = getLocationUtilization(location.id);
return ( return (
<tr key={location.id} className="hover:bg-gray-50"> <tr key={location.id} className="hover:bg-gray-50">
@ -256,22 +233,14 @@ const LocationTracking: React.FC = () => {
<FaMapMarkerAlt className="w-4 h-4 text-blue-600" /> <FaMapMarkerAlt className="w-4 h-4 text-blue-600" />
</div> </div>
<div> <div>
<div className="text-sm font-medium text-gray-900"> <div className="text-sm font-medium text-gray-900">{location.name}</div>
{location.name} <div className="text-sm text-gray-500">{location.locationCode}</div>
</div>
<div className="text-sm text-gray-500">
{location.locationCode}
</div>
</div> </div>
</div> </div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">{warehouse?.name}</div>
{warehouse?.name} <div className="text-sm text-gray-500">{warehouse?.code}</div>
</div>
<div className="text-sm text-gray-500">
{warehouse?.code}
</div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center"> <div className="flex items-center">
@ -279,17 +248,15 @@ const LocationTracking: React.FC = () => {
<div <div
className={`h-2 rounded-full ${ className={`h-2 rounded-full ${
utilization > 90 utilization > 90
? "bg-red-500" ? 'bg-red-500'
: utilization > 70 : utilization > 70
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-green-500" : 'bg-green-500'
}`} }`}
style={{ width: `${utilization}%` }} style={{ width: `${utilization}%` }}
/> />
</div> </div>
<span className="text-sm text-gray-900"> <span className="text-sm text-gray-900">%{Math.round(utilization)}</span>
%{Math.round(utilization)}
</span>
</div> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
{location.currentStock} / {location.capacity} {location.currentStock} / {location.capacity}
@ -302,12 +269,10 @@ const LocationTracking: React.FC = () => {
{locationStockItems.length > 0 {locationStockItems.length > 0
? new Date( ? new Date(
Math.max( Math.max(
...locationStockItems.map((item) => ...locationStockItems.map((item) => item.lastMovementDate.getTime()),
item.lastMovementDate.getTime() ),
) ).toLocaleDateString('tr-TR')
) : 'N/A'}
).toLocaleDateString("tr-TR")
: "N/A"}
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
{location.isActive ? ( {location.isActive ? (
@ -331,44 +296,37 @@ const LocationTracking: React.FC = () => {
</button> </button>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
); )
const LocationDetailModal = () => { const LocationDetailModal = () => {
const location = mockLocations.find((l) => l.id === selectedLocation); const location = mockLocations.find((l) => l.id === selectedLocation)
const locationStockItems = getLocationStockItems(selectedLocation); const locationStockItems = getLocationStockItems(selectedLocation)
if (!selectedLocation || !location) return null; if (!selectedLocation || !location) return null
return ( return (
<div className="fixed inset-0 z-50 overflow-y-auto"> <div className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> <div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div <div
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
onClick={() => setSelectedLocation("")} onClick={() => setSelectedLocation('')}
/> />
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full"> <div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div className="bg-white px-4 pt-4 pb-4 sm:p-4"> <div className="bg-white px-4 pt-4 pb-4 sm:p-4">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-medium text-gray-900"> <h3 className="text-lg font-medium text-gray-900">{location.name} - Detaylar</h3>
{location.name} - Detaylar
</h3>
<button <button
onClick={() => setSelectedLocation("")} onClick={() => setSelectedLocation('')}
className="text-gray-400 hover:text-gray-600" className="text-gray-400 hover:text-gray-600"
> >
<svg <svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path <path
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
@ -382,9 +340,7 @@ const LocationTracking: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Location Info */} {/* Location Info */}
<div className="bg-gray-50 rounded-lg p-3"> <div className="bg-gray-50 rounded-lg p-3">
<h4 className="font-medium text-gray-900 mb-3"> <h4 className="font-medium text-gray-900 mb-3">Lokasyon Bilgileri</h4>
Lokasyon Bilgileri
</h4>
<div className="space-y-2 text-sm"> <div className="space-y-2 text-sm">
<div> <div>
<strong>Kod:</strong> {location.locationCode} <strong>Kod:</strong> {location.locationCode}
@ -399,14 +355,12 @@ const LocationTracking: React.FC = () => {
<strong>Kapasite:</strong> {location.capacity} birim <strong>Kapasite:</strong> {location.capacity} birim
</div> </div>
<div> <div>
<strong>Mevcut Stok:</strong> {location.currentStock}{" "} <strong>Mevcut Stok:</strong> {location.currentStock} birim
birim
</div> </div>
{location.dimensions && ( {location.dimensions && (
<div> <div>
<strong>Boyutlar:</strong> {location.dimensions.length}x <strong>Boyutlar:</strong> {location.dimensions.length}x
{location.dimensions.width}x{location.dimensions.height} {location.dimensions.width}x{location.dimensions.height}m
m
</div> </div>
)} )}
</div> </div>
@ -419,22 +373,15 @@ const LocationTracking: React.FC = () => {
</h4> </h4>
<div className="space-y-2 max-h-64 overflow-y-auto"> <div className="space-y-2 max-h-64 overflow-y-auto">
{locationStockItems.map((item) => ( {locationStockItems.map((item) => (
<div <div key={item.id} className="bg-white rounded p-3 border">
key={item.id}
className="bg-white rounded p-3 border"
>
<div className="flex justify-between items-start mb-2"> <div className="flex justify-between items-start mb-2">
<div> <div>
<div className="font-medium text-sm"> <div className="font-medium text-sm">{item.material?.code}</div>
{item.material?.code} <div className="text-xs text-gray-500">{item.material?.code}</div>
</div>
<div className="text-xs text-gray-500">
{item.material?.code}
</div>
</div> </div>
<span <span
className={`px-2 py-1 rounded-full text-xs ${getStockStatusColor( className={`px-2 py-1 rounded-full text-xs ${getStockStatusColor(
item.status item.status,
)}`} )}`}
> >
{getStockStatusText(item.status)} {getStockStatusText(item.status)}
@ -450,11 +397,10 @@ const LocationTracking: React.FC = () => {
<div> <div>
Rezerve: {item.reservedQuantity} {item.unitId} Rezerve: {item.reservedQuantity} {item.unitId}
</div> </div>
<div>Lot: {item.lotNumber || "N/A"}</div> <div>Lot: {item.lotNumber || 'N/A'}</div>
</div> </div>
<div className="mt-2 text-xs text-gray-500"> <div className="mt-2 text-xs text-gray-500">
Son hareket:{" "} Son hareket: {item.lastMovementDate.toLocaleDateString('tr-TR')}
{item.lastMovementDate.toLocaleDateString("tr-TR")}
</div> </div>
</div> </div>
))} ))}
@ -470,38 +416,35 @@ const LocationTracking: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
}; }
return ( return (
<div className="space-y-4 pt-2"> <Container>
<div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-2xl font-bold text-gray-900"> <h2 className="text-2xl font-bold text-gray-900">Raf/Lokasyon Bazlı Takip</h2>
Raf/Lokasyon Bazlı Takip <p className="text-gray-600">Lokasyonlardaki stok durumunu takip edin</p>
</h2>
<p className="text-gray-600">
Lokasyonlardaki stok durumunu takip edin
</p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={() => setViewMode("grid")} onClick={() => setViewMode('grid')}
className={`p-1.5 rounded-lg ${ className={`p-1.5 rounded-lg ${
viewMode === "grid" viewMode === 'grid'
? "bg-blue-100 text-blue-600" ? 'bg-blue-100 text-blue-600'
: "text-gray-400 hover:text-gray-600" : 'text-gray-400 hover:text-gray-600'
}`} }`}
> >
<FaTh className="w-4 h-4" /> <FaTh className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => setViewMode("list")} onClick={() => setViewMode('list')}
className={`p-1.5 rounded-lg ${ className={`p-1.5 rounded-lg ${
viewMode === "list" viewMode === 'list'
? "bg-blue-100 text-blue-600" ? 'bg-blue-100 text-blue-600'
: "text-gray-400 hover:text-gray-600" : 'text-gray-400 hover:text-gray-600'
}`} }`}
> >
<FaList className="w-4 h-4" /> <FaList className="w-4 h-4" />
@ -536,12 +479,13 @@ const LocationTracking: React.FC = () => {
</div> </div>
{/* Content */} {/* Content */}
{viewMode === "grid" ? <GridView /> : <ListView />} {viewMode === 'grid' ? <GridView /> : <ListView />}
</div>
{/* Location Detail Modal */} {/* Location Detail Modal */}
<LocationDetailModal /> <LocationDetailModal />
</div> </Container>
); )
}; }
export default LocationTracking; export default LocationTracking

View file

@ -1,31 +1,26 @@
import React from "react"; import React from 'react'
import { useFormik } from "formik"; import { useFormik } from 'formik'
import * as Yup from "yup"; import * as Yup from 'yup'
import { FaSave, FaTimes } from "react-icons/fa"; import { FaSave, FaTimes } from 'react-icons/fa'
import { import { QualityStatusEnum, MmLotNumber, MmUnit, MmMaterial } from '../../../types/mm'
QualityStatusEnum, import { BusinessParty } from '../../../types/common'
MmLotNumber,
MmUnit,
MmMaterial,
} from "../../../types/mm";
import { BusinessParty } from "../../../types/common";
const validationSchema = Yup.object({ const validationSchema = Yup.object({
materialId: Yup.string().required("Malzeme seçimi zorunlu"), materialId: Yup.string().required('Malzeme seçimi zorunlu'),
lotNumber: Yup.string().required("Lot numarası zorunlu"), lotNumber: Yup.string().required('Lot numarası zorunlu'),
quantity: Yup.number().min(0, "Miktar 0'dan büyük olmalıdır"), quantity: Yup.number().min(0, "Miktar 0'dan büyük olmalıdır"),
}); })
interface LotFormProps { interface LotFormProps {
isOpen: boolean; isOpen: boolean
onClose: () => void; onClose: () => void
onSave: (lot: MmLotNumber) => void; onSave: (lot: MmLotNumber) => void
onUpdate?: (lot: MmLotNumber) => void; onUpdate?: (lot: MmLotNumber) => void
materials: MmMaterial[]; materials: MmMaterial[]
suppliers?: BusinessParty[]; suppliers?: BusinessParty[]
units?: MmUnit[]; units?: MmUnit[]
initial?: MmLotNumber | null; initial?: MmLotNumber | null
mode?: "create" | "edit" | "view"; mode?: 'create' | 'edit' | 'view'
} }
const LotForm: React.FC<LotFormProps> = ({ const LotForm: React.FC<LotFormProps> = ({
@ -37,17 +32,17 @@ const LotForm: React.FC<LotFormProps> = ({
suppliers = [], suppliers = [],
units = [], units = [],
initial = null, initial = null,
mode = "create", mode = 'create',
}) => { }) => {
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
materialId: "", materialId: '',
lotNumber: "", lotNumber: '',
productionDate: "", productionDate: '',
expiryDate: "", expiryDate: '',
quantity: 0, quantity: 0,
unitId: "KG", unitId: 'KG',
supplierId: suppliers.length ? suppliers[0].id : "", supplierId: suppliers.length ? suppliers[0].id : '',
qualityStatus: QualityStatusEnum.Pending, qualityStatus: QualityStatusEnum.Pending,
isActive: true, isActive: true,
}, },
@ -57,52 +52,48 @@ const LotForm: React.FC<LotFormProps> = ({
id: (initial && initial.id) || Date.now().toString(), id: (initial && initial.id) || Date.now().toString(),
materialId: values.materialId, materialId: values.materialId,
lotNumber: values.lotNumber, lotNumber: values.lotNumber,
productionDate: values.productionDate productionDate: values.productionDate ? new Date(values.productionDate) : new Date(),
? new Date(values.productionDate)
: new Date(),
expiryDate: values.expiryDate ? new Date(values.expiryDate) : undefined, expiryDate: values.expiryDate ? new Date(values.expiryDate) : undefined,
quantity: Number(values.quantity), quantity: Number(values.quantity),
unitId: values.unitId, unitId: values.unitId,
supplierId: values.supplierId || undefined, supplierId: values.supplierId || undefined,
qualityStatus: values.qualityStatus, qualityStatus: values.qualityStatus,
isActive: !!values.isActive, isActive: !!values.isActive,
}; }
// simulate API // simulate API
await new Promise((r) => setTimeout(r, 300)); await new Promise((r) => setTimeout(r, 300))
if (mode === "edit" && onUpdate) { if (mode === 'edit' && onUpdate) {
onUpdate(newLot); onUpdate(newLot)
} else { } else {
onSave(newLot); onSave(newLot)
} }
onClose(); onClose()
}, },
}); })
// sync initial values when editing/viewing // sync initial values when editing/viewing
React.useEffect(() => { React.useEffect(() => {
if (initial) { if (initial) {
const src = initial; const src = initial
formik.setValues({ formik.setValues({
materialId: src.materialId || "", materialId: src.materialId || '',
lotNumber: src.lotNumber || "", lotNumber: src.lotNumber || '',
productionDate: src.productionDate productionDate: src.productionDate
? new Date(src.productionDate).toISOString().slice(0, 10) ? new Date(src.productionDate).toISOString().slice(0, 10)
: "", : '',
expiryDate: src.expiryDate expiryDate: src.expiryDate ? new Date(src.expiryDate).toISOString().slice(0, 10) : '',
? new Date(src.expiryDate).toISOString().slice(0, 10)
: "",
quantity: src.quantity || 0, quantity: src.quantity || 0,
unitId: src.unitId || (units.length ? units[0].id : ""), unitId: src.unitId || (units.length ? units[0].id : ''),
supplierId: src.supplierId || (suppliers.length ? suppliers[0].id : ""), supplierId: src.supplierId || (suppliers.length ? suppliers[0].id : ''),
qualityStatus: src.qualityStatus || QualityStatusEnum.Pending, qualityStatus: src.qualityStatus || QualityStatusEnum.Pending,
isActive: !!src.isActive, isActive: !!src.isActive,
}); })
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [initial]); }, [initial])
if (!isOpen) return null; if (!isOpen) return null
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="fixed inset-0 z-50 flex items-center justify-center">
@ -112,18 +103,18 @@ const LotForm: React.FC<LotFormProps> = ({
<div className="flex items-center justify-between p-2 border-b"> <div className="flex items-center justify-between p-2 border-b">
<div> <div>
<h2 className="text-2xl font-bold text-gray-900"> <h2 className="text-2xl font-bold text-gray-900">
{mode === "create" {mode === 'create'
? "Yeni Lot Kaydı" ? 'Yeni Lot Kaydı'
: mode === "edit" : mode === 'edit'
? "Lot Düzenle" ? 'Lot Düzenle'
: "Lot Detayı"} : 'Lot Detayı'}
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600">
{mode === "create" {mode === 'create'
? "Lot bilgilerini girin" ? 'Lot bilgilerini girin'
: mode === "edit" : mode === 'edit'
? "Mevcut lot bilgilerini güncelleyin" ? 'Mevcut lot bilgilerini güncelleyin'
: "Lot bilgileri (sadece gösterim)"} : 'Lot bilgileri (sadece gösterim)'}
</p> </p>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
@ -134,7 +125,7 @@ const LotForm: React.FC<LotFormProps> = ({
> >
<FaTimes className="inline mr-1 h-3 w-3" /> Kapat <FaTimes className="inline mr-1 h-3 w-3" /> Kapat
</button> </button>
{mode !== "view" && ( {mode !== 'view' && (
<button <button
type="submit" type="submit"
className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center" className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"
@ -147,12 +138,10 @@ const LotForm: React.FC<LotFormProps> = ({
<div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2"> <div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2">
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Malzeme *</label>
Malzeme *
</label>
<select <select
{...formik.getFieldProps("materialId")} {...formik.getFieldProps('materialId')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
> >
<option value="">Seçiniz...</option> <option value="">Seçiniz...</option>
@ -165,61 +154,51 @@ const LotForm: React.FC<LotFormProps> = ({
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Lot Numarası *</label>
Lot Numarası *
</label>
<input <input
type="text" type="text"
{...formik.getFieldProps("lotNumber")} {...formik.getFieldProps('lotNumber')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Üretim Tarihi</label>
Üretim Tarihi
</label>
<input <input
type="date" type="date"
{...formik.getFieldProps("productionDate")} {...formik.getFieldProps('productionDate')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Son Kullanma Tarihi</label>
Son Kullanma Tarihi
</label>
<input <input
type="date" type="date"
{...formik.getFieldProps("expiryDate")} {...formik.getFieldProps('expiryDate')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Miktar</label>
Miktar
</label>
<input <input
type="number" type="number"
step="0.01" step="0.01"
{...formik.getFieldProps("quantity")} {...formik.getFieldProps('quantity')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Birim</label>
Birim
</label>
<select <select
{...formik.getFieldProps("unitId")} {...formik.getFieldProps('unitId')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
> >
{units.map((u) => ( {units.map((u) => (
@ -231,12 +210,10 @@ const LotForm: React.FC<LotFormProps> = ({
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Tedarikçi</label>
Tedarikçi
</label>
<select <select
{...formik.getFieldProps("supplierId")} {...formik.getFieldProps('supplierId')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
> >
<option value="">Seçiniz...</option> <option value="">Seçiniz...</option>
@ -249,11 +226,9 @@ const LotForm: React.FC<LotFormProps> = ({
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Kalite Durumu</label>
Kalite Durumu
</label>
<select <select
{...formik.getFieldProps("qualityStatus")} {...formik.getFieldProps('qualityStatus')}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
> >
<option value={QualityStatusEnum.Pending}>Beklemede</option> <option value={QualityStatusEnum.Pending}>Beklemede</option>
@ -266,7 +241,7 @@ const LotForm: React.FC<LotFormProps> = ({
</form> </form>
</div> </div>
</div> </div>
); )
}; }
export default LotForm; export default LotForm

View file

@ -1,92 +1,81 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from 'react'
import { useParams } from "react-router-dom"; import { useParams } from 'react-router-dom'
import { FaDownload, FaCalendar, FaBox } from "react-icons/fa"; import { FaDownload, FaCalendar, FaBox } from 'react-icons/fa'
import { MmStockMovement, MovementTypeEnum } from "../../../types/mm"; import { MmStockMovement, MovementTypeEnum } from '../../../types/mm'
import { FaRepeat } from "react-icons/fa6"; import { FaRepeat } from 'react-icons/fa6'
import { getMovementTypeInfo } from "../../../utils/erp"; import { getMovementTypeInfo } from '../../../utils/erp'
import { mockStockMovements } from "../../../mocks/mockStockMovements"; import { mockStockMovements } from '../../../mocks/mockStockMovements'
import Widget, { colorType } from "../../../components/common/Widget"; import Widget, { colorType } from '../../../components/common/Widget'
import { Container } from '@/components/shared'
const MaterialMovements: React.FC<{ materialId?: string }> = ({ const MaterialMovements: React.FC<{ materialId?: string }> = ({ materialId }) => {
materialId, const params = useParams<Record<string, string | undefined>>()
}) => { const routeMaterialId = materialId || params.materialId || params.id || undefined
const params = useParams<Record<string, string | undefined>>(); const [movements, setMovements] = useState<MmStockMovement[]>([])
const routeMaterialId = const [loading, setLoading] = useState(false)
materialId || params.materialId || params.id || undefined;
const [movements, setMovements] = useState<MmStockMovement[]>([]);
const [loading, setLoading] = useState(false);
const [filters, setFilters] = useState({ const [filters, setFilters] = useState({
startDate: "", startDate: '',
endDate: "", endDate: '',
movementType: "", movementType: '',
warehouseId: "", warehouseId: '',
}); })
const loadMovements = useCallback(() => { const loadMovements = useCallback(() => {
setLoading(true); setLoading(true)
// Simulate API call // Simulate API call
setTimeout(() => { setTimeout(() => {
let filteredMovements = mockStockMovements; let filteredMovements = mockStockMovements
const activeMaterialId = routeMaterialId; const activeMaterialId = routeMaterialId
if (activeMaterialId) { if (activeMaterialId) {
filteredMovements = filteredMovements.filter( filteredMovements = filteredMovements.filter((m) => m.materialId === activeMaterialId)
(m) => m.materialId === activeMaterialId
);
} }
if (filters.movementType) { if (filters.movementType) {
filteredMovements = filteredMovements.filter( filteredMovements = filteredMovements.filter(
(m) => (m) => m.movementType === (filters.movementType as unknown as MovementTypeEnum),
m.movementType === )
(filters.movementType as unknown as MovementTypeEnum)
);
} }
setMovements(filteredMovements); setMovements(filteredMovements)
setLoading(false); setLoading(false)
}, 1000); }, 1000)
}, [routeMaterialId, filters]); }, [routeMaterialId, filters])
useEffect(() => { useEffect(() => {
loadMovements(); loadMovements()
}, [loadMovements]); }, [loadMovements])
const calculateRunningBalance = () => { const calculateRunningBalance = () => {
let balance = 0; let balance = 0
return movements return movements
.sort( .sort((a, b) => new Date(a.movementDate).getTime() - new Date(b.movementDate).getTime())
(a, b) =>
new Date(a.movementDate).getTime() -
new Date(b.movementDate).getTime()
)
.map((movement) => { .map((movement) => {
balance += movement.quantity; balance += movement.quantity
return { ...movement, runningBalance: balance }; return { ...movement, runningBalance: balance }
}); })
}; }
const movementsWithBalance = calculateRunningBalance(); const movementsWithBalance = calculateRunningBalance()
return ( return (
<div className="space-y-3 py-2"> <Container>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-2xl font-bold text-gray-900"> <h2 className="text-2xl font-bold text-gray-900">
{materialId ? "Stok Hareketleri" : "Tüm Stok Hareketleri"} {materialId ? 'Stok Hareketleri' : 'Tüm Stok Hareketleri'}
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600">
{materialId {materialId ? `${materialId} kodlu malzeme için` : 'Tüm malzemeler için'} stok
? `${materialId} kodlu malzeme için` hareketlerini görüntüleyin
: "Tüm malzemeler için"}{" "}
stok hareketlerini görüntüleyin
</p> </p>
</div> </div>
<div className="flex items-center space-x-1.5"> <div className="flex items-center space-x-1.5">
<button <button
onClick={() => alert("Dışa aktarma özelliği yakında eklenecek")} onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
className="bg-gray-600 text-white px-2.5 py-1 rounded-md hover:bg-gray-700 flex items-center space-x-1.5 text-sm" className="bg-gray-600 text-white px-2.5 py-1 rounded-md hover:bg-gray-700 flex items-center space-x-1.5 text-sm"
> >
<FaDownload className="h-4 w-4" /> <FaDownload className="h-4 w-4" />
@ -106,28 +95,28 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
<div className="grid grid-cols-1 md:grid-cols-4 gap-2"> <div className="grid grid-cols-1 md:grid-cols-4 gap-2">
{[ {[
{ {
title: "Toplam Giriş", title: 'Toplam Giriş',
value: "125 KG", value: '125 KG',
color: "green", color: 'green',
icon: "FaArrowDown", icon: 'FaArrowDown',
}, },
{ {
title: "Toplam Çıkış", title: 'Toplam Çıkış',
value: "50 KG", value: '50 KG',
color: "red", color: 'red',
icon: "FaArrowUp", icon: 'FaArrowUp',
}, },
{ {
title: "Net Bakiye", title: 'Net Bakiye',
value: "75 KG", value: '75 KG',
color: "blue", color: 'blue',
icon: "FaBalanceScale", icon: 'FaBalanceScale',
}, },
{ {
title: "Hareket Sayısı", title: 'Hareket Sayısı',
value: movements.length, value: movements.length,
color: "gray", color: 'gray',
icon: "FaExchangeAlt", icon: 'FaExchangeAlt',
}, },
].map((stat) => ( ].map((stat) => (
<Widget <Widget
@ -150,36 +139,26 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
<input <input
type="date" type="date"
value={filters.startDate} value={filters.startDate}
onChange={(e) => onChange={(e) => setFilters({ ...filters, startDate: e.target.value })}
setFilters({ ...filters, startDate: e.target.value })
}
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/> />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<label className="block text-xs font-medium text-gray-700 mb-1"> <label className="block text-xs font-medium text-gray-700 mb-1">Bitiş Tarihi</label>
Bitiş Tarihi
</label>
<input <input
type="date" type="date"
value={filters.endDate} value={filters.endDate}
onChange={(e) => onChange={(e) => setFilters({ ...filters, endDate: e.target.value })}
setFilters({ ...filters, endDate: e.target.value })
}
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/> />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<label className="block text-xs font-medium text-gray-700 mb-1"> <label className="block text-xs font-medium text-gray-700 mb-1">Hareket Tipi</label>
Hareket Tipi
</label>
<select <select
value={filters.movementType} value={filters.movementType}
onChange={(e) => onChange={(e) => setFilters({ ...filters, movementType: e.target.value })}
setFilters({ ...filters, movementType: e.target.value })
}
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
> >
<option value="">Tümü</option> <option value="">Tümü</option>
@ -190,14 +169,10 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
</div> </div>
<div className="flex-1"> <div className="flex-1">
<label className="block text-xs font-medium text-gray-700 mb-1"> <label className="block text-xs font-medium text-gray-700 mb-1">Depo</label>
Depo
</label>
<select <select
value={filters.warehouseId} value={filters.warehouseId}
onChange={(e) => onChange={(e) => setFilters({ ...filters, warehouseId: e.target.value })}
setFilters({ ...filters, warehouseId: e.target.value })
}
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
> >
<option value="">Tümü</option> <option value="">Tümü</option>
@ -257,8 +232,8 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{movementsWithBalance.map((movement) => { {movementsWithBalance.map((movement) => {
const typeInfo = getMovementTypeInfo(movement.movementType); const typeInfo = getMovementTypeInfo(movement.movementType)
const Icon = typeInfo.icon; const Icon = typeInfo.icon
return ( return (
<tr key={movement.id} className="hover:bg-gray-50 text-xs"> <tr key={movement.id} className="hover:bg-gray-50 text-xs">
@ -267,14 +242,10 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
<FaCalendar className="h-4 w-4 text-gray-400" /> <FaCalendar className="h-4 w-4 text-gray-400" />
<div> <div>
<div className="text-xs font-medium text-gray-900"> <div className="text-xs font-medium text-gray-900">
{new Date( {new Date(movement.movementDate).toLocaleDateString('tr-TR')}
movement.movementDate
).toLocaleDateString("tr-TR")}
</div> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
{new Date( {new Date(movement.movementDate).toLocaleTimeString('tr-TR')}
movement.movementDate
).toLocaleTimeString("tr-TR")}
</div> </div>
</div> </div>
</div> </div>
@ -298,17 +269,13 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
<span <span
className={`text-sm font-medium ${ className={`text-sm font-medium ${
movement.quantity > 0 movement.quantity > 0 ? 'text-green-600' : 'text-red-600'
? "text-green-600"
: "text-red-600"
}`} }`}
> >
{movement.quantity > 0 ? "+" : ""} {movement.quantity > 0 ? '+' : ''}
{movement.quantity} {movement.quantity}
</span> </span>
<span className="text-xs text-gray-500"> <span className="text-xs text-gray-500">{movement.unit?.code}</span>
{movement.unit?.code}
</span>
</div> </div>
</td> </td>
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
@ -351,12 +318,10 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
)} )}
</td> </td>
<td className="px-2 py-1.5"> <td className="px-2 py-1.5">
<div className="text-xs text-gray-900"> <div className="text-xs text-gray-900">{movement.description}</div>
{movement.description}
</div>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
@ -366,9 +331,7 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
{!loading && movements.length === 0 && ( {!loading && movements.length === 0 && (
<div className="text-center py-10"> <div className="text-center py-10">
<FaBox className="mx-auto h-10 w-10 text-gray-400" /> <FaBox className="mx-auto h-10 w-10 text-gray-400" />
<h3 className="mt-2 text-base font-medium text-gray-900"> <h3 className="mt-2 text-base font-medium text-gray-900">Hareket bulunamadı</h3>
Hareket bulunamadı
</h3>
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-gray-500">
Seçilen kriterlere uygun hareket kaydı bulunmuyor. Seçilen kriterlere uygun hareket kaydı bulunmuyor.
</p> </p>
@ -376,7 +339,8 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
)} )}
</div> </div>
</div> </div>
); </Container>
}; )
}
export default MaterialMovements; export default MaterialMovements

View file

@ -1,5 +1,5 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { PutawayStrategyEnum } from "../../../types/wm"; import { PutawayStrategyEnum } from '../../../types/wm'
import { import {
FaSearch, FaSearch,
FaPlus, FaPlus,
@ -13,174 +13,161 @@ import {
FaArrowDown, FaArrowDown,
FaTh, FaTh,
FaList, FaList,
} from "react-icons/fa"; } from 'react-icons/fa'
import { mockPutawayRules } from "../../../mocks/mockPutawayRules"; import { mockPutawayRules } from '../../../mocks/mockPutawayRules'
import { mockWarehouses } from "../../../mocks/mockWarehouses"; import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { mockZones } from "../../../mocks/mockZones"; import { mockZones } from '../../../mocks/mockZones'
import { mockLocations } from "../../../mocks/mockLocations"; import { mockLocations } from '../../../mocks/mockLocations'
import { mockMaterialTypes } from "../../../mocks/mockMaterialTypes"; import { mockMaterialTypes } from '../../../mocks/mockMaterialTypes'
import { mockMaterialGroups } from "../../../mocks/mockMaterialGroups"; import { mockMaterialGroups } from '../../../mocks/mockMaterialGroups'
import { import {
getPutawayStrategyColor, getPutawayStrategyColor,
getPutawayStrategyText, getPutawayStrategyText,
getConditionTypeText, getConditionTypeText,
getConditionOperatorText, getConditionOperatorText,
} from "../../../utils/erp"; } from '../../../utils/erp'
import { Container } from '@/components/shared'
const PutawayRules: React.FC = () => { const PutawayRules: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [selectedStrategy, setSelectedStrategy] = useState< const [selectedStrategy, setSelectedStrategy] = useState<PutawayStrategyEnum | ''>('')
PutawayStrategyEnum | "" const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
>(""); const [showRuleForm, setShowRuleForm] = useState(false)
const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const [editingRuleId, setEditingRuleId] = useState<string | null>(null)
const [showRuleForm, setShowRuleForm] = useState(false); const [selectedRule, setSelectedRule] = useState<string>('')
const [editingRuleId, setEditingRuleId] = useState<string | null>(null);
const [selectedRule, setSelectedRule] = useState<string>("");
// Form States // Form States
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
name: "", name: '',
ruleCode: "", ruleCode: '',
description: "", description: '',
warehouseId: "", warehouseId: '',
materialTypeId: "", materialTypeId: '',
materialGroupId: "", materialGroupId: '',
priority: 1, priority: 1,
targetZoneId: "", targetZoneId: '',
targetLocationId: "", targetLocationId: '',
strategy: PutawayStrategyEnum.FIFO, strategy: PutawayStrategyEnum.FIFO,
isActive: true, isActive: true,
}); })
// Helper functions for names // Helper functions for names
const getWarehouseName = (warehouseId: string | undefined) => { const getWarehouseName = (warehouseId: string | undefined) => {
if (!warehouseId) return "Belirtilmemiş"; if (!warehouseId) return 'Belirtilmemiş'
const warehouse = mockWarehouses.find((w) => w.id === warehouseId); const warehouse = mockWarehouses.find((w) => w.id === warehouseId)
return warehouse ? warehouse.name : `Depo ${warehouseId}`; return warehouse ? warehouse.name : `Depo ${warehouseId}`
}; }
const getZoneName = (zoneId: string | undefined) => { const getZoneName = (zoneId: string | undefined) => {
if (!zoneId) return "Belirtilmemiş"; if (!zoneId) return 'Belirtilmemiş'
const zone = mockZones.find((z) => z.id === zoneId); const zone = mockZones.find((z) => z.id === zoneId)
return zone ? zone.name : `Bölge ${zoneId}`; return zone ? zone.name : `Bölge ${zoneId}`
}; }
const getLocationName = (locationId: string | undefined) => { const getLocationName = (locationId: string | undefined) => {
if (!locationId) return "Belirtilmemiş"; if (!locationId) return 'Belirtilmemiş'
const location = mockLocations.find((l) => l.id === locationId); const location = mockLocations.find((l) => l.id === locationId)
return location ? location.name : `Lokasyon ${locationId}`; return location ? location.name : `Lokasyon ${locationId}`
}; }
const getMaterialTypeName = (materialTypeId: string | undefined) => { const getMaterialTypeName = (materialTypeId: string | undefined) => {
if (!materialTypeId) return "Belirtilmemiş"; if (!materialTypeId) return 'Belirtilmemiş'
const materialType = mockMaterialTypes.find( const materialType = mockMaterialTypes.find((mt) => mt.id === materialTypeId)
(mt) => mt.id === materialTypeId return materialType ? materialType.name : `Tip ${materialTypeId}`
); }
return materialType ? materialType.name : `Tip ${materialTypeId}`;
};
const getMaterialGroupName = (materialGroupId: string | undefined) => { const getMaterialGroupName = (materialGroupId: string | undefined) => {
if (!materialGroupId) return "Belirtilmemiş"; if (!materialGroupId) return 'Belirtilmemiş'
const materialGroup = mockMaterialGroups.find( const materialGroup = mockMaterialGroups.find((mg) => mg.id === materialGroupId)
(mg) => mg.id === materialGroupId return materialGroup ? materialGroup.name : `Grup ${materialGroupId}`
); }
return materialGroup ? materialGroup.name : `Grup ${materialGroupId}`;
};
// Form Functions // Form Functions
const resetForm = () => { const resetForm = () => {
setFormData({ setFormData({
name: "", name: '',
ruleCode: "", ruleCode: '',
description: "", description: '',
warehouseId: "", warehouseId: '',
materialTypeId: "", materialTypeId: '',
materialGroupId: "", materialGroupId: '',
priority: 1, priority: 1,
targetZoneId: "", targetZoneId: '',
targetLocationId: "", targetLocationId: '',
strategy: PutawayStrategyEnum.FIFO, strategy: PutawayStrategyEnum.FIFO,
isActive: true, isActive: true,
}); })
}; }
const handleEditRule = (ruleId: string) => { const handleEditRule = (ruleId: string) => {
const rule = mockPutawayRules.find((r) => r.id === ruleId); const rule = mockPutawayRules.find((r) => r.id === ruleId)
if (rule) { if (rule) {
setFormData({ setFormData({
name: rule.name, name: rule.name,
ruleCode: rule.code, ruleCode: rule.code,
description: rule.description || "", description: rule.description || '',
warehouseId: rule.warehouseId, warehouseId: rule.warehouseId,
materialTypeId: rule.materialTypeId || "", materialTypeId: rule.materialTypeId || '',
materialGroupId: rule.materialGroupId || "", materialGroupId: rule.materialGroupId || '',
priority: rule.priority, priority: rule.priority,
targetZoneId: rule.targetZoneId || "", targetZoneId: rule.targetZoneId || '',
targetLocationId: rule.targetLocationId || "", targetLocationId: rule.targetLocationId || '',
strategy: rule.strategy, strategy: rule.strategy,
isActive: rule.isActive, isActive: rule.isActive,
}); })
}
setEditingRuleId(ruleId)
setShowRuleForm(true)
} }
setEditingRuleId(ruleId);
setShowRuleForm(true);
};
const handleNewRule = () => { const handleNewRule = () => {
resetForm(); resetForm()
setEditingRuleId(null); setEditingRuleId(null)
setShowRuleForm(true); setShowRuleForm(true)
}; }
const handleCloseForm = () => { const handleCloseForm = () => {
setShowRuleForm(false); setShowRuleForm(false)
setEditingRuleId(null); setEditingRuleId(null)
resetForm(); resetForm()
}; }
const handleSubmitForm = () => { const handleSubmitForm = () => {
// Form validation and submission logic here // Form validation and submission logic here
console.log("Form Data:", formData); console.log('Form Data:', formData)
handleCloseForm(); handleCloseForm()
}; }
const filteredRules = mockPutawayRules.filter((rule) => { const filteredRules = mockPutawayRules.filter((rule) => {
const matchesSearch = const matchesSearch =
rule.name.toLowerCase().includes(searchTerm.toLowerCase()) || rule.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
rule.code.toLowerCase().includes(searchTerm.toLowerCase()); rule.code.toLowerCase().includes(searchTerm.toLowerCase())
const matchesStrategy = const matchesStrategy = selectedStrategy === '' || rule.strategy === selectedStrategy
selectedStrategy === "" || rule.strategy === selectedStrategy; return matchesSearch && matchesStrategy
return matchesSearch && matchesStrategy; })
});
const RuleDetailModal = () => { const RuleDetailModal = () => {
const rule = mockPutawayRules.find((r) => r.id === selectedRule); const rule = mockPutawayRules.find((r) => r.id === selectedRule)
if (!selectedRule || !rule) return null; if (!selectedRule || !rule) return null
return ( return (
<div className="fixed inset-0 z-50 overflow-y-auto"> <div className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> <div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div <div
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
onClick={() => setSelectedRule("")} onClick={() => setSelectedRule('')}
/> />
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full"> <div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> <div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-medium text-gray-900"> <h3 className="text-lg font-medium text-gray-900">{rule.name} - Kural Detayları</h3>
{rule.name} - Kural Detayları
</h3>
<button <button
onClick={() => setSelectedRule("")} onClick={() => setSelectedRule('')}
className="text-gray-400 hover:text-gray-600" className="text-gray-400 hover:text-gray-600"
> >
<svg <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
className="w-5 h-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path <path
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
@ -194,9 +181,7 @@ const PutawayRules: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* Rule Info */} {/* Rule Info */}
<div className="bg-gray-50 rounded-lg p-3"> <div className="bg-gray-50 rounded-lg p-3">
<h4 className="font-medium text-sm text-gray-900 mb-2"> <h4 className="font-medium text-sm text-gray-900 mb-2">Kural Bilgileri</h4>
Kural Bilgileri
</h4>
<div className="space-y-2 text-sm"> <div className="space-y-2 text-sm">
<div> <div>
<strong>Kod:</strong> {rule.code} <strong>Kod:</strong> {rule.code}
@ -211,7 +196,7 @@ const PutawayRules: React.FC = () => {
<strong>Strateji:</strong> <strong>Strateji:</strong>
<span <span
className={`ml-2 inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor( className={`ml-2 inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
rule.strategy rule.strategy,
)}`} )}`}
> >
{getPutawayStrategyText(rule.strategy)} {getPutawayStrategyText(rule.strategy)}
@ -241,14 +226,9 @@ const PutawayRules: React.FC = () => {
</h4> </h4>
<div className="space-y-2"> <div className="space-y-2">
{rule.conditions.map((condition, index) => ( {rule.conditions.map((condition, index) => (
<div <div key={condition.id} className="bg-white rounded p-2 border">
key={condition.id}
className="bg-white rounded p-2 border"
>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm font-medium"> <span className="text-sm font-medium">Koşul {index + 1}</span>
Koşul {index + 1}
</span>
</div> </div>
<div className="mt-2 text-sm text-gray-600"> <div className="mt-2 text-sm text-gray-600">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -278,31 +258,25 @@ const PutawayRules: React.FC = () => {
{/* Target Info */} {/* Target Info */}
<div className="mt-4 bg-gray-50 rounded-lg p-3"> <div className="mt-4 bg-gray-50 rounded-lg p-3">
<h4 className="font-medium text-sm text-gray-900 mb-2"> <h4 className="font-medium text-sm text-gray-900 mb-2">Hedef Lokasyon</h4>
Hedef Lokasyon
</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-sm"> <div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-sm">
<div> <div>
<strong>Depo:</strong> {getWarehouseName(rule.warehouseId)} <strong>Depo:</strong> {getWarehouseName(rule.warehouseId)}
</div> </div>
<div> <div>
<strong>Hedef Bölge:</strong>{" "} <strong>Hedef Bölge:</strong> {getZoneName(rule.targetZoneId)}
{getZoneName(rule.targetZoneId)}
</div> </div>
<div> <div>
<strong>Hedef Lokasyon:</strong>{" "} <strong>Hedef Lokasyon:</strong> {getLocationName(rule.targetLocationId)}
{getLocationName(rule.targetLocationId)}
</div> </div>
{rule.materialTypeId && ( {rule.materialTypeId && (
<div> <div>
<strong>Malzeme Tipi:</strong>{" "} <strong>Malzeme Tipi:</strong> {getMaterialTypeName(rule.materialTypeId)}
{getMaterialTypeName(rule.materialTypeId)}
</div> </div>
)} )}
{rule.materialGroupId && ( {rule.materialGroupId && (
<div> <div>
<strong>Malzeme Grubu:</strong>{" "} <strong>Malzeme Grubu:</strong> {getMaterialGroupName(rule.materialGroupId)}
{getMaterialGroupName(rule.materialGroupId)}
</div> </div>
)} )}
</div> </div>
@ -311,40 +285,37 @@ const PutawayRules: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
}; }
return ( return (
<div className="space-y-4 pt-2"> <Container>
<div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-2xl font-bold text-gray-900"> <h2 className="text-2xl font-bold text-gray-900">Yerleştirme Kuralları</h2>
Yerleştirme Kuralları <p className="text-gray-600">Malzeme yerleştirme kurallarını tanımlayın ve yönetin</p>
</h2>
<p className="text-gray-600">
Malzeme yerleştirme kurallarını tanımlayın ve yönetin
</p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{/* View Mode Toggle */} {/* View Mode Toggle */}
<div className="flex items-center bg-gray-100 rounded-lg p-1"> <div className="flex items-center bg-gray-100 rounded-lg p-1">
<button <button
onClick={() => setViewMode("grid")} onClick={() => setViewMode('grid')}
className={`p-1.5 rounded-md ${ className={`p-1.5 rounded-md ${
viewMode === "grid" viewMode === 'grid'
? "bg-white text-blue-600 shadow-sm" ? 'bg-white text-blue-600 shadow-sm'
: "text-gray-500 hover:text-gray-700" : 'text-gray-500 hover:text-gray-700'
}`} }`}
title="Kart Görünümü" title="Kart Görünümü"
> >
<FaTh className="w-4 h-4" /> <FaTh className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => setViewMode("list")} onClick={() => setViewMode('list')}
className={`p-1.5 rounded-md ${ className={`p-1.5 rounded-md ${
viewMode === "list" viewMode === 'list'
? "bg-white text-blue-600 shadow-sm" ? 'bg-white text-blue-600 shadow-sm'
: "text-gray-500 hover:text-gray-700" : 'text-gray-500 hover:text-gray-700'
}`} }`}
title="Liste Görünümü" title="Liste Görünümü"
> >
@ -375,9 +346,7 @@ const PutawayRules: React.FC = () => {
</div> </div>
<select <select
value={selectedStrategy} value={selectedStrategy}
onChange={(e) => onChange={(e) => setSelectedStrategy(e.target.value as PutawayStrategyEnum | '')}
setSelectedStrategy(e.target.value as PutawayStrategyEnum | "")
}
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" 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 Stratejiler</option> <option value="">Tüm Stratejiler</option>
@ -394,7 +363,7 @@ const PutawayRules: React.FC = () => {
</div> </div>
{/* Rules Display */} {/* Rules Display */}
{viewMode === "grid" ? ( {viewMode === 'grid' ? (
// Grid View (Kart Görünümü) // Grid View (Kart Görünümü)
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredRules.map((rule) => ( {filteredRules.map((rule) => (
@ -431,7 +400,7 @@ const PutawayRules: React.FC = () => {
<span <span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor( className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
rule.strategy rule.strategy,
)}`} )}`}
> >
{getPutawayStrategyText(rule.strategy)} {getPutawayStrategyText(rule.strategy)}
@ -452,9 +421,7 @@ const PutawayRules: React.FC = () => {
<span className="font-medium"> <span className="font-medium">
{getConditionTypeText(condition.conditionType)} {getConditionTypeText(condition.conditionType)}
</span> </span>
<span> <span>{getConditionOperatorText(condition.operator)}</span>
{getConditionOperatorText(condition.operator)}
</span>
<span className="font-medium">{condition.value}</span> <span className="font-medium">{condition.value}</span>
</div> </div>
</div> </div>
@ -466,9 +433,7 @@ const PutawayRules: React.FC = () => {
)} )}
</div> </div>
) : ( ) : (
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">Koşul tanımlanmamış</div>
Koşul tanımlanmamış
</div>
)} )}
{/* Target Info */} {/* Target Info */}
@ -477,25 +442,15 @@ const PutawayRules: React.FC = () => {
<div className="bg-gray-50 rounded p-2"> <div className="bg-gray-50 rounded p-2">
<div className="text-xs text-gray-600 space-y-0.5"> <div className="text-xs text-gray-600 space-y-0.5">
<div>Depo: {getWarehouseName(rule.warehouseId)}</div> <div>Depo: {getWarehouseName(rule.warehouseId)}</div>
{rule.targetZoneId && ( {rule.targetZoneId && <div>Bölge: {getZoneName(rule.targetZoneId)}</div>}
<div>Bölge: {getZoneName(rule.targetZoneId)}</div>
)}
{rule.targetLocationId && ( {rule.targetLocationId && (
<div> <div>Lokasyon: {getLocationName(rule.targetLocationId)}</div>
Lokasyon: {getLocationName(rule.targetLocationId)}
</div>
)} )}
{rule.materialTypeId && ( {rule.materialTypeId && (
<div> <div>Malzeme Tipi: {getMaterialTypeName(rule.materialTypeId)}</div>
Malzeme Tipi:{" "}
{getMaterialTypeName(rule.materialTypeId)}
</div>
)} )}
{rule.materialGroupId && ( {rule.materialGroupId && (
<div> <div>Malzeme Grubu: {getMaterialGroupName(rule.materialGroupId)}</div>
Malzeme Grubu:{" "}
{getMaterialGroupName(rule.materialGroupId)}
</div>
)} )}
</div> </div>
</div> </div>
@ -585,16 +540,14 @@ const PutawayRules: React.FC = () => {
> >
{rule.name} {rule.name}
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">{rule.code}</div>
{rule.code}
</div>
</div> </div>
</div> </div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<span <span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor( className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
rule.strategy rule.strategy,
)}`} )}`}
> >
{getPutawayStrategyText(rule.strategy)} {getPutawayStrategyText(rule.strategy)}
@ -605,13 +558,9 @@ const PutawayRules: React.FC = () => {
</td> </td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900"> <td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
<div className="space-y-1"> <div className="space-y-1">
{rule.targetZoneId && ( {rule.targetZoneId && <div>Bölge: {getZoneName(rule.targetZoneId)}</div>}
<div>Bölge: {getZoneName(rule.targetZoneId)}</div>
)}
{rule.targetLocationId && ( {rule.targetLocationId && (
<div> <div>Lokasyon: {getLocationName(rule.targetLocationId)}</div>
Lokasyon: {getLocationName(rule.targetLocationId)}
</div>
)} )}
</div> </div>
</td> </td>
@ -620,9 +569,8 @@ const PutawayRules: React.FC = () => {
<div className="space-y-1"> <div className="space-y-1">
{rule.conditions.slice(0, 1).map((condition) => ( {rule.conditions.slice(0, 1).map((condition) => (
<div key={condition.id} className="text-xs"> <div key={condition.id} className="text-xs">
{getConditionTypeText(condition.conditionType)}{" "} {getConditionTypeText(condition.conditionType)}{' '}
{getConditionOperatorText(condition.operator)}{" "} {getConditionOperatorText(condition.operator)} {condition.value}
{condition.value}
</div> </div>
))} ))}
{rule.conditions.length > 1 && ( {rule.conditions.length > 1 && (
@ -645,9 +593,7 @@ const PutawayRules: React.FC = () => {
{rule.isActive ? ( {rule.isActive ? (
<> <>
<FaCheckCircle className="w-4 h-4 text-green-500 mr-2" /> <FaCheckCircle className="w-4 h-4 text-green-500 mr-2" />
<span className="text-sm text-green-600"> <span className="text-sm text-green-600">Aktif</span>
Aktif
</span>
</> </>
) : ( ) : (
<> <>
@ -692,9 +638,7 @@ const PutawayRules: React.FC = () => {
{filteredRules.length === 0 && ( {filteredRules.length === 0 && (
<div className="text-center py-12"> <div className="text-center py-12">
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2"> <h3 className="text-lg font-medium text-gray-900 mb-2">Kural bulunamadı</h3>
Kural bulunamadı
</h3>
<p className="text-gray-500 mb-4"> <p className="text-gray-500 mb-4">
Arama kriterlerinize uygun yerleştirme kuralı bulunamadı. Arama kriterlerinize uygun yerleştirme kuralı bulunamadı.
</p> </p>
@ -706,6 +650,7 @@ const PutawayRules: React.FC = () => {
</button> </button>
</div> </div>
)} )}
</div>
{/* Rule Detail Modal */} {/* Rule Detail Modal */}
<RuleDetailModal /> <RuleDetailModal />
@ -717,8 +662,8 @@ const PutawayRules: React.FC = () => {
<div <div
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
onClick={() => { onClick={() => {
setShowRuleForm(false); setShowRuleForm(false)
setEditingRuleId(null); setEditingRuleId(null)
}} }}
/> />
@ -726,20 +671,13 @@ const PutawayRules: React.FC = () => {
<div className="bg-white rounded-lg w-full max-w-4xl mx-4 max-h-[90vh] overflow-y-auto"> <div className="bg-white rounded-lg w-full max-w-4xl mx-4 max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between p-4 border-b"> <div className="flex items-center justify-between p-4 border-b">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-base font-medium text-gray-900">
{editingRuleId {editingRuleId ? 'Yerleştirme Kuralını Güncelle' : 'Yeni Yerleştirme Kuralı'}
? "Yerleştirme Kuralını Güncelle"
: "Yeni Yerleştirme Kuralı"}
</h3> </h3>
<button <button
onClick={handleCloseForm} onClick={handleCloseForm}
className="text-gray-400 hover:text-gray-600 p-1 rounded-full" className="text-gray-400 hover:text-gray-600 p-1 rounded-full"
> >
<svg <svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path <path
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
@ -761,9 +699,7 @@ const PutawayRules: React.FC = () => {
<input <input
type="text" type="text"
value={formData.name} value={formData.name}
onChange={(e) => onChange={(e) => setFormData({ ...formData, name: e.target.value })}
setFormData({ ...formData, name: e.target.value })
}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Kural adını girin" placeholder="Kural adını girin"
required required
@ -776,9 +712,7 @@ const PutawayRules: React.FC = () => {
<input <input
type="text" type="text"
value={formData.ruleCode} value={formData.ruleCode}
onChange={(e) => onChange={(e) => setFormData({ ...formData, ruleCode: e.target.value })}
setFormData({ ...formData, ruleCode: e.target.value })
}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="PR001" placeholder="PR001"
required required
@ -787,9 +721,7 @@ const PutawayRules: React.FC = () => {
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">ıklama</label>
ıklama
</label>
<textarea <textarea
value={formData.description} value={formData.description}
onChange={(e) => onChange={(e) =>
@ -806,9 +738,7 @@ const PutawayRules: React.FC = () => {
{/* Warehouse and Location Settings */} {/* Warehouse and Location Settings */}
<div className="border-t pt-4"> <div className="border-t pt-4">
<h4 className="text-base font-medium text-gray-900 mb-2"> <h4 className="text-base font-medium text-gray-900 mb-2">Lokasyon Ayarları</h4>
Lokasyon Ayarları
</h4>
<div className="space-y-3"> <div className="space-y-3">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
@ -820,9 +750,9 @@ const PutawayRules: React.FC = () => {
setFormData({ setFormData({
...formData, ...formData,
warehouseId: e.target.value, warehouseId: e.target.value,
targetZoneId: "", targetZoneId: '',
targetLocationId: "", targetLocationId: '',
}); })
}} }}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required required
@ -847,17 +777,14 @@ const PutawayRules: React.FC = () => {
setFormData({ setFormData({
...formData, ...formData,
targetZoneId: e.target.value, targetZoneId: e.target.value,
targetLocationId: "", targetLocationId: '',
}); })
}} }}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
> >
<option value="">Bölge seçin</option> <option value="">Bölge seçin</option>
{mockZones {mockZones
.filter( .filter((zone) => zone.warehouseId === formData.warehouseId)
(zone) =>
zone.warehouseId === formData.warehouseId
)
.map((zone) => ( .map((zone) => (
<option key={zone.id} value={zone.id}> <option key={zone.id} value={zone.id}>
{zone.name} {zone.name}
@ -884,10 +811,7 @@ const PutawayRules: React.FC = () => {
> >
<option value="">Lokasyon seçin</option> <option value="">Lokasyon seçin</option>
{mockLocations {mockLocations
.filter( .filter((location) => location.zoneId === formData.targetZoneId)
(location) =>
location.zoneId === formData.targetZoneId
)
.map((location) => ( .map((location) => (
<option key={location.id} value={location.id}> <option key={location.id} value={location.id}>
{location.name} {location.name}
@ -901,9 +825,7 @@ const PutawayRules: React.FC = () => {
{/* Material Settings */} {/* Material Settings */}
<div className="border-t pt-4"> <div className="border-t pt-4">
<h4 className="text-base font-medium text-gray-900 mb-2"> <h4 className="text-base font-medium text-gray-900 mb-2">Malzeme Ayarları</h4>
Malzeme Ayarları
</h4>
<div className="space-y-3"> <div className="space-y-3">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
@ -982,9 +904,7 @@ const PutawayRules: React.FC = () => {
<option value={PutawayStrategyEnum.NearestLocation}> <option value={PutawayStrategyEnum.NearestLocation}>
En Yakın Lokasyon En Yakın Lokasyon
</option> </option>
<option value={PutawayStrategyEnum.EmptyLocation}> <option value={PutawayStrategyEnum.EmptyLocation}>Boş Lokasyon</option>
Boş Lokasyon
</option>
<option value={PutawayStrategyEnum.SameProduct}> <option value={PutawayStrategyEnum.SameProduct}>
Aynı Ürün Gruplaması Aynı Ürün Gruplaması
</option> </option>
@ -1027,10 +947,7 @@ const PutawayRules: React.FC = () => {
} }
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/> />
<label <label htmlFor="isActive" className="ml-2 block text-sm text-gray-900">
htmlFor="isActive"
className="ml-2 block text-sm text-gray-900"
>
Bu kural aktif Bu kural aktif
</label> </label>
</div> </div>
@ -1051,7 +968,7 @@ const PutawayRules: React.FC = () => {
onClick={handleSubmitForm} onClick={handleSubmitForm}
className="px-4 py-1.5 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500" className="px-4 py-1.5 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
> >
{editingRuleId ? "Güncelle" : "Kaydet"} {editingRuleId ? 'Güncelle' : 'Kaydet'}
</button> </button>
</div> </div>
</div> </div>
@ -1059,8 +976,8 @@ const PutawayRules: React.FC = () => {
</div> </div>
</div> </div>
)} )}
</div> </Container>
); )
}; }
export default PutawayRules; export default PutawayRules

View file

@ -1,24 +1,24 @@
import React from "react"; import React from 'react'
import { useFormik } from "formik"; import { useFormik } from 'formik'
import * as Yup from "yup"; import * as Yup from 'yup'
import { FaSave, FaTimes } from "react-icons/fa"; import { FaSave, FaTimes } from 'react-icons/fa'
import { SerialStatusEnum, MmSerialNumber, MmMaterial } from "../../../types/mm"; import { SerialStatusEnum, MmSerialNumber, MmMaterial } from '../../../types/mm'
export interface SerialFormProps { export interface SerialFormProps {
isOpen: boolean; isOpen: boolean
onClose: () => void; onClose: () => void
onSave: (serial: MmSerialNumber) => void; onSave: (serial: MmSerialNumber) => void
onUpdate?: (serial: MmSerialNumber) => void; onUpdate?: (serial: MmSerialNumber) => void
materials: MmMaterial[]; materials: MmMaterial[]
lots?: { id: string; label: string }[]; lots?: { id: string; label: string }[]
initial?: MmSerialNumber | null; initial?: MmSerialNumber | null
mode?: "create" | "edit" | "view"; mode?: 'create' | 'edit' | 'view'
} }
const validationSchema = Yup.object({ const validationSchema = Yup.object({
materialId: Yup.string().required("Malzeme seçimi zorunlu"), materialId: Yup.string().required('Malzeme seçimi zorunlu'),
serialNumber: Yup.string().required("Seri numarası zorunlu"), serialNumber: Yup.string().required('Seri numarası zorunlu'),
}); })
const SerialForm: React.FC<SerialFormProps> = ({ const SerialForm: React.FC<SerialFormProps> = ({
isOpen, isOpen,
@ -28,16 +28,16 @@ const SerialForm: React.FC<SerialFormProps> = ({
materials, materials,
lots = [], lots = [],
initial = null, initial = null,
mode = "create", mode = 'create',
}) => { }) => {
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
materialId: "", materialId: '',
serialNumber: "", serialNumber: '',
lotId: lots.length ? lots[0].id : "", lotId: lots.length ? lots[0].id : '',
productionDate: "", productionDate: '',
warrantyExpiryDate: "", warrantyExpiryDate: '',
currentLocationId: "", currentLocationId: '',
status: SerialStatusEnum.Available, status: SerialStatusEnum.Available,
isActive: true, isActive: true,
}, },
@ -48,51 +48,49 @@ const SerialForm: React.FC<SerialFormProps> = ({
materialId: values.materialId, materialId: values.materialId,
serialNumber: values.serialNumber, serialNumber: values.serialNumber,
lotId: values.lotId || undefined, lotId: values.lotId || undefined,
productionDate: values.productionDate productionDate: values.productionDate ? new Date(values.productionDate) : new Date(),
? new Date(values.productionDate)
: new Date(),
warrantyExpiryDate: values.warrantyExpiryDate warrantyExpiryDate: values.warrantyExpiryDate
? new Date(values.warrantyExpiryDate) ? new Date(values.warrantyExpiryDate)
: undefined, : undefined,
currentLocationId: values.currentLocationId || undefined, currentLocationId: values.currentLocationId || undefined,
status: values.status, status: values.status,
isActive: !!values.isActive, isActive: !!values.isActive,
}; }
// simulate API // simulate API
await new Promise((r) => setTimeout(r, 300)); await new Promise((r) => setTimeout(r, 300))
if (mode === "edit" && onUpdate) { if (mode === 'edit' && onUpdate) {
onUpdate(newSerial); onUpdate(newSerial)
} else { } else {
onSave(newSerial); onSave(newSerial)
} }
onClose(); onClose()
}, },
}); })
// sync initial values when editing/viewing // sync initial values when editing/viewing
React.useEffect(() => { React.useEffect(() => {
if (initial) { if (initial) {
const src = initial; const src = initial
formik.setValues({ formik.setValues({
materialId: src.materialId || "", materialId: src.materialId || '',
serialNumber: src.serialNumber || "", serialNumber: src.serialNumber || '',
lotId: src.lotId || (lots.length ? lots[0].id : ""), lotId: src.lotId || (lots.length ? lots[0].id : ''),
productionDate: src.productionDate productionDate: src.productionDate
? new Date(src.productionDate).toISOString().slice(0, 10) ? new Date(src.productionDate).toISOString().slice(0, 10)
: "", : '',
warrantyExpiryDate: src.warrantyExpiryDate warrantyExpiryDate: src.warrantyExpiryDate
? new Date(src.warrantyExpiryDate).toISOString().slice(0, 10) ? new Date(src.warrantyExpiryDate).toISOString().slice(0, 10)
: "", : '',
currentLocationId: src.currentLocationId || "", currentLocationId: src.currentLocationId || '',
status: src.status || SerialStatusEnum.Available, status: src.status || SerialStatusEnum.Available,
isActive: !!src.isActive, isActive: !!src.isActive,
}); })
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [initial]); }, [initial])
if (!isOpen) return null; if (!isOpen) return null
return ( return (
<div className="fixed inset-0 z-50 flex items-center justify-center"> <div className="fixed inset-0 z-50 flex items-center justify-center">
@ -102,18 +100,18 @@ const SerialForm: React.FC<SerialFormProps> = ({
<div className="flex items-center justify-between p-2 border-b"> <div className="flex items-center justify-between p-2 border-b">
<div> <div>
<h2 className="text-xl font-bold text-gray-900"> <h2 className="text-xl font-bold text-gray-900">
{mode === "create" {mode === 'create'
? "Yeni Seri Kaydı" ? 'Yeni Seri Kaydı'
: mode === "edit" : mode === 'edit'
? "Seri Düzenle" ? 'Seri Düzenle'
: "Seri Detayı"} : 'Seri Detayı'}
</h2> </h2>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">
{mode === "create" {mode === 'create'
? "Seri numarası girin" ? 'Seri numarası girin'
: mode === "edit" : mode === 'edit'
? "Mevcut seri bilgisini güncelleyin" ? 'Mevcut seri bilgisini güncelleyin'
: "Seri bilgileri (sadece gösterim)"} : 'Seri bilgileri (sadece gösterim)'}
</p> </p>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
@ -124,7 +122,7 @@ const SerialForm: React.FC<SerialFormProps> = ({
> >
<FaTimes className="inline mr-1 h-3 w-3" /> Kapat <FaTimes className="inline mr-1 h-3 w-3" /> Kapat
</button> </button>
{mode !== "view" && ( {mode !== 'view' && (
<button <button
type="submit" type="submit"
className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center" className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"
@ -137,12 +135,10 @@ const SerialForm: React.FC<SerialFormProps> = ({
<div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2"> <div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2">
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Malzeme *</label>
Malzeme *
</label>
<select <select
{...formik.getFieldProps("materialId")} {...formik.getFieldProps('materialId')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
> >
<option value="">Seçiniz...</option> <option value="">Seçiniz...</option>
@ -155,24 +151,20 @@ const SerialForm: React.FC<SerialFormProps> = ({
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Seri Numarası *</label>
Seri Numarası *
</label>
<input <input
type="text" type="text"
{...formik.getFieldProps("serialNumber")} {...formik.getFieldProps('serialNumber')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Lot</label>
Lot
</label>
<select <select
{...formik.getFieldProps("lotId")} {...formik.getFieldProps('lotId')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
> >
<option value="">Seçiniz...</option> <option value="">Seçiniz...</option>
@ -185,36 +177,30 @@ const SerialForm: React.FC<SerialFormProps> = ({
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Üretim Tarihi</label>
Üretim Tarihi
</label>
<input <input
type="date" type="date"
{...formik.getFieldProps("productionDate")} {...formik.getFieldProps('productionDate')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Garanti Bitiş</label>
Garanti Bitiş
</label>
<input <input
type="date" type="date"
{...formik.getFieldProps("warrantyExpiryDate")} {...formik.getFieldProps('warrantyExpiryDate')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Durum</label>
Durum
</label>
<select <select
{...formik.getFieldProps("status")} {...formik.getFieldProps('status')}
disabled={mode === "view"} disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm" className="w-full border rounded-md px-2 py-1 text-sm"
> >
<option value={SerialStatusEnum.Available}>Müsait</option> <option value={SerialStatusEnum.Available}>Müsait</option>
@ -227,7 +213,7 @@ const SerialForm: React.FC<SerialFormProps> = ({
</form> </form>
</div> </div>
</div> </div>
); )
}; }
export default SerialForm; export default SerialForm

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,5 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import { WmWarehouse, WarehouseTypeEnum, WmZone, WmLocation } from '../../../types/wm'
WmWarehouse,
WarehouseTypeEnum,
WmZone,
WmLocation,
} from "../../../types/wm";
import { import {
FaPlus, FaPlus,
FaSearch, FaSearch,
@ -18,101 +13,91 @@ import {
FaEye, FaEye,
FaTh, FaTh,
FaList, FaList,
} from "react-icons/fa"; } from 'react-icons/fa'
import { WarehouseModal, ZoneModal, LocationModal } from "./modals"; import { WarehouseModal, ZoneModal, LocationModal } from './modals'
import { mockWarehouses } from "../../../mocks/mockWarehouses"; import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { mockZones } from "../../../mocks/mockZones"; import { mockZones } from '../../../mocks/mockZones'
import { mockLocations } from "../../../mocks/mockLocations"; import { mockLocations } from '../../../mocks/mockLocations'
import { import {
getWarehouseTypeColor, getWarehouseTypeColor,
getWarehouseTypeText, getWarehouseTypeText,
getZoneTypeText, getZoneTypeText,
getLocationTypeText, getLocationTypeText,
} from "../../../utils/erp"; } from '../../../utils/erp'
import { Container } from '@/components/shared'
const WarehouseDefinitions: React.FC = () => { const WarehouseDefinitions: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [selectedType, setSelectedType] = useState<WarehouseTypeEnum | "">(""); const [selectedType, setSelectedType] = useState<WarehouseTypeEnum | ''>('')
const [activeTab, setActiveTab] = useState< const [activeTab, setActiveTab] = useState<'warehouses' | 'zones' | 'locations'>('warehouses')
"warehouses" | "zones" | "locations" const [selectedWarehouse, setSelectedWarehouse] = useState<string>('')
>("warehouses"); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
const [selectedWarehouse, setSelectedWarehouse] = useState<string>("");
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
// Modal states // Modal states
const [warehouseModal, setWarehouseModal] = useState<{ const [warehouseModal, setWarehouseModal] = useState<{
isOpen: boolean; isOpen: boolean
mode: "create" | "edit" | "view"; mode: 'create' | 'edit' | 'view'
warehouse: WmWarehouse | null; warehouse: WmWarehouse | null
}>({ }>({
isOpen: false, isOpen: false,
mode: "create", mode: 'create',
warehouse: null, warehouse: null,
}); })
const [zoneModal, setZoneModal] = useState<{ const [zoneModal, setZoneModal] = useState<{
isOpen: boolean; isOpen: boolean
mode: "create" | "edit" | "view"; mode: 'create' | 'edit' | 'view'
zone: WmZone | null; zone: WmZone | null
}>({ }>({
isOpen: false, isOpen: false,
mode: "create", mode: 'create',
zone: null, zone: null,
}); })
const [locationModal, setLocationModal] = useState<{ const [locationModal, setLocationModal] = useState<{
isOpen: boolean; isOpen: boolean
mode: "create" | "edit" | "view"; mode: 'create' | 'edit' | 'view'
location: WmLocation | null; location: WmLocation | null
}>({ }>({
isOpen: false, isOpen: false,
mode: "create", mode: 'create',
location: null, location: null,
}); })
// Data states (in a real app, these would come from an API) // Data states (in a real app, these would come from an API)
const [warehouses, setWarehouses] = useState<WmWarehouse[]>(mockWarehouses); const [warehouses, setWarehouses] = useState<WmWarehouse[]>(mockWarehouses)
const [zones, setZones] = useState<WmZone[]>(mockZones); const [zones, setZones] = useState<WmZone[]>(mockZones)
const [locations, setLocations] = useState<WmLocation[]>(mockLocations); const [locations, setLocations] = useState<WmLocation[]>(mockLocations)
// Modal handlers // Modal handlers
const handleWarehouseAction = ( const handleWarehouseAction = (mode: 'create' | 'edit' | 'view', warehouse?: WmWarehouse) => {
mode: "create" | "edit" | "view",
warehouse?: WmWarehouse
) => {
setWarehouseModal({ setWarehouseModal({
isOpen: true, isOpen: true,
mode, mode,
warehouse: warehouse || null, warehouse: warehouse || null,
}); })
}; }
const handleZoneAction = ( const handleZoneAction = (mode: 'create' | 'edit' | 'view', zone?: WmZone) => {
mode: "create" | "edit" | "view",
zone?: WmZone
) => {
setZoneModal({ setZoneModal({
isOpen: true, isOpen: true,
mode, mode,
zone: zone || null, zone: zone || null,
}); })
}; }
const handleLocationAction = ( const handleLocationAction = (mode: 'create' | 'edit' | 'view', location?: WmLocation) => {
mode: "create" | "edit" | "view",
location?: WmLocation
) => {
setLocationModal({ setLocationModal({
isOpen: true, isOpen: true,
mode, mode,
location: location || null, location: location || null,
}); })
}; }
const handleWarehouseSave = (warehouseData: Partial<WmWarehouse>) => { const handleWarehouseSave = (warehouseData: Partial<WmWarehouse>) => {
if (warehouseModal.mode === "create") { if (warehouseModal.mode === 'create') {
const newWarehouse: WmWarehouse = { const newWarehouse: WmWarehouse = {
...warehouseData, ...warehouseData,
id: Date.now().toString(), id: Date.now().toString(),
@ -120,9 +105,9 @@ const WarehouseDefinitions: React.FC = () => {
zones: [], zones: [],
creationTime: new Date(), creationTime: new Date(),
lastModificationTime: new Date(), lastModificationTime: new Date(),
} as WmWarehouse; } as WmWarehouse
setWarehouses([...warehouses, newWarehouse]); setWarehouses([...warehouses, newWarehouse])
} else if (warehouseModal.mode === "edit" && warehouseModal.warehouse) { } else if (warehouseModal.mode === 'edit' && warehouseModal.warehouse) {
setWarehouses( setWarehouses(
warehouses.map((w) => warehouses.map((w) =>
w.id === warehouseModal.warehouse!.id w.id === warehouseModal.warehouse!.id
@ -131,91 +116,82 @@ const WarehouseDefinitions: React.FC = () => {
...warehouseData, ...warehouseData,
lastModificationTime: new Date(), lastModificationTime: new Date(),
} as WmWarehouse) } as WmWarehouse)
: w : w,
),
) )
);
} }
}; }
const handleZoneSave = (zoneData: Partial<WmZone>) => { const handleZoneSave = (zoneData: Partial<WmZone>) => {
if (zoneModal.mode === "create") { if (zoneModal.mode === 'create') {
const newZone: WmZone = { const newZone: WmZone = {
...zoneData, ...zoneData,
id: Date.now().toString(), id: Date.now().toString(),
locations: [], locations: [],
} as WmZone; } as WmZone
setZones([...zones, newZone]); setZones([...zones, newZone])
} else if (zoneModal.mode === "edit" && zoneModal.zone) { } else if (zoneModal.mode === 'edit' && zoneModal.zone) {
setZones( setZones(
zones.map((z) => zones.map((z) => (z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z)),
z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z
) )
);
} }
}; }
const handleLocationSave = (locationData: Partial<WmLocation>) => { const handleLocationSave = (locationData: Partial<WmLocation>) => {
if (locationModal.mode === "create") { if (locationModal.mode === 'create') {
const newLocation: WmLocation = { const newLocation: WmLocation = {
...locationData, ...locationData,
id: Date.now().toString(), id: Date.now().toString(),
stockItems: [], stockItems: [],
} as WmLocation; } as WmLocation
setLocations([...locations, newLocation]); setLocations([...locations, newLocation])
} else if (locationModal.mode === "edit" && locationModal.location) { } else if (locationModal.mode === 'edit' && locationModal.location) {
setLocations( setLocations(
locations.map((l) => locations.map((l) =>
l.id === locationModal.location!.id l.id === locationModal.location!.id ? ({ ...l, ...locationData } as WmLocation) : l,
? ({ ...l, ...locationData } as WmLocation) ),
: l
) )
);
} }
}; }
const handleDelete = ( const handleDelete = (type: 'warehouse' | 'zone' | 'location', id: string) => {
type: "warehouse" | "zone" | "location",
id: string
) => {
if ( if (
window.confirm( window.confirm(
`Bu ${ `Bu ${
type === "warehouse" ? "depo" : type === "zone" ? "bölge" : "lokasyon" type === 'warehouse' ? 'depo' : type === 'zone' ? 'bölge' : 'lokasyon'
}u silmek istediğinizden emin misiniz?` }u silmek istediğinizden emin misiniz?`,
) )
) { ) {
switch (type) { switch (type) {
case "warehouse": case 'warehouse':
setWarehouses(warehouses.filter((w) => w.id !== id)); setWarehouses(warehouses.filter((w) => w.id !== id))
break; break
case "zone": case 'zone':
setZones(zones.filter((z) => z.id !== id)); setZones(zones.filter((z) => z.id !== id))
break; break
case "location": case 'location':
setLocations(locations.filter((l) => l.id !== id)); setLocations(locations.filter((l) => l.id !== id))
break; break
}
} }
} }
};
// Filtered data // Filtered data
const filteredWarehouses = warehouses.filter((warehouse) => { const filteredWarehouses = warehouses.filter((warehouse) => {
const matchesSearch = const matchesSearch =
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()) || warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase()); warehouse.code.toLowerCase().includes(searchTerm.toLowerCase())
const matchesType = const matchesType = selectedType === '' || warehouse.warehouseType === selectedType
selectedType === "" || warehouse.warehouseType === selectedType; return matchesSearch && matchesType
return matchesSearch && matchesType; })
});
const filteredZones = zones.filter( const filteredZones = zones.filter(
(zone) => selectedWarehouse === "" || zone.warehouseId === selectedWarehouse (zone) => selectedWarehouse === '' || zone.warehouseId === selectedWarehouse,
); )
const filteredLocations = locations.filter( const filteredLocations = locations.filter(
(location) => (location) => selectedWarehouse === '' || location.warehouseId === selectedWarehouse,
selectedWarehouse === "" || location.warehouseId === selectedWarehouse )
);
// Warehouse Components // Warehouse Components
const WarehousesGridView = () => ( const WarehousesGridView = () => (
@ -231,9 +207,7 @@ const WarehouseDefinitions: React.FC = () => {
<FaBuilding className="w-4 h-4 text-blue-600" /> <FaBuilding className="w-4 h-4 text-blue-600" />
</div> </div>
<div> <div>
<h3 className="font-medium text-sm text-gray-900"> <h3 className="font-medium text-sm text-gray-900">{warehouse.name}</h3>
{warehouse.name}
</h3>
<p className="text-gray-600">{warehouse.code}</p> <p className="text-gray-600">{warehouse.code}</p>
</div> </div>
</div> </div>
@ -248,7 +222,7 @@ const WarehouseDefinitions: React.FC = () => {
<div> <div>
<span <span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor( className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
warehouse.warehouseType warehouse.warehouseType,
)}`} )}`}
> >
{getWarehouseTypeText(warehouse.warehouseType)} {getWarehouseTypeText(warehouse.warehouseType)}
@ -278,18 +252,12 @@ const WarehouseDefinitions: React.FC = () => {
<div <div
className="bg-blue-600 h-2 rounded-full" className="bg-blue-600 h-2 rounded-full"
style={{ style={{
width: `${ width: `${(warehouse.currentUtilization / warehouse.capacity) * 100}%`,
(warehouse.currentUtilization / warehouse.capacity) * 100
}%`,
}} }}
></div> ></div>
</div> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
% %{Math.round((warehouse.currentUtilization / warehouse.capacity) * 100)} dolu
{Math.round(
(warehouse.currentUtilization / warehouse.capacity) * 100
)}{" "}
dolu
</div> </div>
</div> </div>
</div> </div>
@ -310,21 +278,21 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<button <button
onClick={() => handleWarehouseAction("view", warehouse)} onClick={() => handleWarehouseAction('view', warehouse)}
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md" className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md"
title="Görüntüle" title="Görüntüle"
> >
<FaEye className="w-4 h-4" /> <FaEye className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleWarehouseAction("edit", warehouse)} onClick={() => handleWarehouseAction('edit', warehouse)}
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md" className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
title="Düzenle" title="Düzenle"
> >
<FaEdit className="w-4 h-4" /> <FaEdit className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleDelete("warehouse", warehouse.id)} onClick={() => handleDelete('warehouse', warehouse.id)}
className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md" className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md"
title="Sil" title="Sil"
> >
@ -335,7 +303,7 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
))} ))}
</div> </div>
); )
const WarehousesListView = () => ( const WarehousesListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200"> <div className="bg-white rounded-lg shadow-sm border border-gray-200">
@ -369,7 +337,7 @@ const WarehouseDefinitions: React.FC = () => {
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{filteredWarehouses.map((warehouse) => { {filteredWarehouses.map((warehouse) => {
const utilizationPercentage = const utilizationPercentage =
(warehouse.currentUtilization / warehouse.capacity) * 100; (warehouse.currentUtilization / warehouse.capacity) * 100
return ( return (
<tr key={warehouse.id} className="hover:bg-gray-50"> <tr key={warehouse.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
@ -386,16 +354,14 @@ const WarehouseDefinitions: React.FC = () => {
</span> </span>
)} )}
</div> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">{warehouse.code}</div>
{warehouse.code}
</div>
</div> </div>
</div> </div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<span <span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor( className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
warehouse.warehouseType warehouse.warehouseType,
)}`} )}`}
> >
{getWarehouseTypeText(warehouse.warehouseType)} {getWarehouseTypeText(warehouse.warehouseType)}
@ -405,9 +371,7 @@ const WarehouseDefinitions: React.FC = () => {
<div className="text-xs text-gray-900"> <div className="text-xs text-gray-900">
{warehouse.address?.city}, {warehouse.address?.state} {warehouse.address?.city}, {warehouse.address?.state}
</div> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">{warehouse.address?.street}</div>
{warehouse.address?.street}
</div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900"> <td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{warehouse.capacity.toLocaleString()} birim {warehouse.capacity.toLocaleString()} birim
@ -418,10 +382,10 @@ const WarehouseDefinitions: React.FC = () => {
<div <div
className={`h-2 rounded-full ${ className={`h-2 rounded-full ${
utilizationPercentage > 90 utilizationPercentage > 90
? "bg-red-500" ? 'bg-red-500'
: utilizationPercentage > 70 : utilizationPercentage > 70
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-blue-500" : 'bg-blue-500'
}`} }`}
style={{ width: `${utilizationPercentage}%` }} style={{ width: `${utilizationPercentage}%` }}
></div> ></div>
@ -431,7 +395,7 @@ const WarehouseDefinitions: React.FC = () => {
</span> </span>
</div> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
{warehouse.currentUtilization.toLocaleString()} /{" "} {warehouse.currentUtilization.toLocaleString()} /{' '}
{warehouse.capacity.toLocaleString()} {warehouse.capacity.toLocaleString()}
</div> </div>
</td> </td>
@ -451,21 +415,21 @@ const WarehouseDefinitions: React.FC = () => {
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium"> <td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2"> <div className="flex items-center justify-end gap-2">
<button <button
onClick={() => handleWarehouseAction("view", warehouse)} onClick={() => handleWarehouseAction('view', warehouse)}
className="text-green-600 hover:text-green-900" className="text-green-600 hover:text-green-900"
title="Görüntüle" title="Görüntüle"
> >
<FaEye className="w-4 h-4" /> <FaEye className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleWarehouseAction("edit", warehouse)} onClick={() => handleWarehouseAction('edit', warehouse)}
className="text-blue-600 hover:text-blue-900" className="text-blue-600 hover:text-blue-900"
title="Düzenle" title="Düzenle"
> >
<FaEdit className="w-4 h-4" /> <FaEdit className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleDelete("warehouse", warehouse.id)} onClick={() => handleDelete('warehouse', warehouse.id)}
className="text-red-600 hover:text-red-900" className="text-red-600 hover:text-red-900"
title="Sil" title="Sil"
> >
@ -474,13 +438,13 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
); )
const WarehousesTab = () => ( const WarehousesTab = () => (
<div className="space-y-4 pt-2"> <div className="space-y-4 pt-2">
@ -498,9 +462,7 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
<select <select
value={selectedType} value={selectedType}
onChange={(e) => onChange={(e) => setSelectedType(e.target.value as WarehouseTypeEnum | '')}
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" 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> <option value="">Tüm Tipler</option>
@ -511,7 +473,7 @@ const WarehouseDefinitions: React.FC = () => {
))} ))}
</select> </select>
<button <button
onClick={() => handleWarehouseAction("create")} 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" 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" /> <FaPlus className="w-4 h-4" />
@ -520,15 +482,15 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
{/* Content based on view mode */} {/* Content based on view mode */}
{viewMode === "grid" ? <WarehousesGridView /> : <WarehousesListView />} {viewMode === 'grid' ? <WarehousesGridView /> : <WarehousesListView />}
</div> </div>
); )
// Zone Components // Zone Components
const ZonesGridView = () => ( const ZonesGridView = () => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{filteredZones.map((zone) => { {filteredZones.map((zone) => {
const warehouse = warehouses.find((w) => w.id === zone.warehouseId); const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
return ( return (
<div <div
key={zone.id} key={zone.id}
@ -540,9 +502,7 @@ const WarehouseDefinitions: React.FC = () => {
<FaLayerGroup className="w-4 h-4 text-purple-600" /> <FaLayerGroup className="w-4 h-4 text-purple-600" />
</div> </div>
<div> <div>
<h3 className="font-medium text-sm text-gray-900"> <h3 className="font-medium text-sm text-gray-900">{zone.name}</h3>
{zone.name}
</h3>
<p className="text-gray-600">{zone.zoneCode}</p> <p className="text-gray-600">{zone.zoneCode}</p>
</div> </div>
</div> </div>
@ -564,13 +524,9 @@ const WarehouseDefinitions: React.FC = () => {
{(zone.temperature || zone.humidity) && ( {(zone.temperature || zone.humidity) && (
<div className="bg-gray-50 rounded-lg p-2"> <div className="bg-gray-50 rounded-lg p-2">
<h4 className="text-xs font-medium text-gray-900 mb-1"> <h4 className="text-xs font-medium text-gray-900 mb-1">Çevresel Koşullar</h4>
Çevresel Koşullar
</h4>
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600"> <div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
{zone.temperature && ( {zone.temperature && <div>Sıcaklık: {zone.temperature}°C</div>}
<div>Sıcaklık: {zone.temperature}°C</div>
)}
{zone.humidity && <div>Nem: %{zone.humidity}</div>} {zone.humidity && <div>Nem: %{zone.humidity}</div>}
</div> </div>
</div> </div>
@ -593,21 +549,21 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={() => handleZoneAction("view", zone)} onClick={() => handleZoneAction('view', zone)}
className="p-1 text-gray-400 hover:text-green-600" className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle" title="Görüntüle"
> >
<FaEye className="w-4 h-4" /> <FaEye className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleZoneAction("edit", zone)} onClick={() => handleZoneAction('edit', zone)}
className="p-1 text-gray-400 hover:text-blue-600" className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle" title="Düzenle"
> >
<FaEdit className="w-4 h-4" /> <FaEdit className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleDelete("zone", zone.id)} onClick={() => handleDelete('zone', zone.id)}
className="p-1 text-gray-400 hover:text-red-600" className="p-1 text-gray-400 hover:text-red-600"
title="Sil" title="Sil"
> >
@ -616,10 +572,10 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
})} })}
</div> </div>
); )
const ZonesListView = () => ( const ZonesListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200"> <div className="bg-white rounded-lg shadow-sm border border-gray-200">
@ -652,9 +608,7 @@ const WarehouseDefinitions: React.FC = () => {
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{filteredZones.map((zone) => { {filteredZones.map((zone) => {
const warehouse = warehouses.find( const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
(w) => w.id === zone.warehouseId
);
return ( return (
<tr key={zone.id} className="hover:bg-gray-50"> <tr key={zone.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
@ -663,22 +617,14 @@ const WarehouseDefinitions: React.FC = () => {
<FaLayerGroup className="w-4 h-4 text-purple-600" /> <FaLayerGroup className="w-4 h-4 text-purple-600" />
</div> </div>
<div> <div>
<div className="text-sm font-medium text-gray-900"> <div className="text-sm font-medium text-gray-900">{zone.name}</div>
{zone.name} <div className="text-sm text-gray-500">{zone.zoneCode}</div>
</div>
<div className="text-sm text-gray-500">
{zone.zoneCode}
</div>
</div> </div>
</div> </div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">{warehouse?.name}</div>
{warehouse?.name} <div className="text-sm text-gray-500">{warehouse?.code}</div>
</div>
<div className="text-sm text-gray-500">
{warehouse?.code}
</div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <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"> <span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
@ -686,10 +632,10 @@ const WarehouseDefinitions: React.FC = () => {
</span> </span>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900"> <td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{zone.temperature ? `${zone.temperature}°C` : "-"} {zone.temperature ? `${zone.temperature}°C` : '-'}
</td> </td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900"> <td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{zone.humidity ? `%${zone.humidity}` : "-"} {zone.humidity ? `%${zone.humidity}` : '-'}
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
{zone.isActive ? ( {zone.isActive ? (
@ -707,21 +653,21 @@ const WarehouseDefinitions: React.FC = () => {
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium"> <td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2"> <div className="flex items-center justify-end gap-2">
<button <button
onClick={() => handleZoneAction("view", zone)} onClick={() => handleZoneAction('view', zone)}
className="text-green-600 hover:text-green-900" className="text-green-600 hover:text-green-900"
title="Görüntüle" title="Görüntüle"
> >
<FaEye className="w-4 h-4" /> <FaEye className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleZoneAction("edit", zone)} onClick={() => handleZoneAction('edit', zone)}
className="text-blue-600 hover:text-blue-900" className="text-blue-600 hover:text-blue-900"
title="Düzenle" title="Düzenle"
> >
<FaEdit className="w-4 h-4" /> <FaEdit className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleDelete("zone", zone.id)} onClick={() => handleDelete('zone', zone.id)}
className="text-red-600 hover:text-red-900" className="text-red-600 hover:text-red-900"
title="Sil" title="Sil"
> >
@ -730,13 +676,13 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
); )
const ZonesTab = () => ( const ZonesTab = () => (
<div className="space-y-4 pt-2"> <div className="space-y-4 pt-2">
@ -755,7 +701,7 @@ const WarehouseDefinitions: React.FC = () => {
))} ))}
</select> </select>
<button <button
onClick={() => handleZoneAction("create")} 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" 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" /> <FaPlus className="w-4 h-4" />
@ -764,18 +710,17 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
{/* Content based on view mode */} {/* Content based on view mode */}
{viewMode === "grid" ? <ZonesGridView /> : <ZonesListView />} {viewMode === 'grid' ? <ZonesGridView /> : <ZonesListView />}
</div> </div>
); )
// Location Components // Location Components
const LocationsGridView = () => ( const LocationsGridView = () => (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredLocations.map((location) => { {filteredLocations.map((location) => {
const warehouse = warehouses.find((w) => w.id === location.warehouseId); const warehouse = warehouses.find((w) => w.id === location.warehouseId)
const zone = zones.find((z) => z.id === location.zoneId); const zone = zones.find((z) => z.id === location.zoneId)
const utilizationPercentage = const utilizationPercentage = (location.currentStock / location.capacity) * 100
(location.currentStock / location.capacity) * 100;
return ( return (
<div <div
@ -789,9 +734,7 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
<div> <div>
<h3 className="font-medium text-gray-900">{location.name}</h3> <h3 className="font-medium text-gray-900">{location.name}</h3>
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">{location.locationCode}</p>
{location.locationCode}
</p>
</div> </div>
</div> </div>
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800"> <span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
@ -815,9 +758,7 @@ const WarehouseDefinitions: React.FC = () => {
{/* Dimensions */} {/* Dimensions */}
{location.dimensions && ( {location.dimensions && (
<div className="bg-gray-50 rounded-lg p-2 mt-2"> <div className="bg-gray-50 rounded-lg p-2 mt-2">
<h4 className="text-sm font-medium text-gray-900 mb-2"> <h4 className="text-sm font-medium text-gray-900 mb-2">Boyutlar</h4>
Boyutlar
</h4>
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600"> <div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
<div>Uzunluk: {location.dimensions.length}m</div> <div>Uzunluk: {location.dimensions.length}m</div>
<div>Genişlik: {location.dimensions.width}m</div> <div>Genişlik: {location.dimensions.width}m</div>
@ -842,10 +783,10 @@ const WarehouseDefinitions: React.FC = () => {
<div <div
className={`h-2 rounded-full ${ className={`h-2 rounded-full ${
utilizationPercentage > 90 utilizationPercentage > 90
? "bg-red-500" ? 'bg-red-500'
: utilizationPercentage > 70 : utilizationPercentage > 70
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-green-500" : 'bg-green-500'
}`} }`}
style={{ width: `${utilizationPercentage}%` }} style={{ width: `${utilizationPercentage}%` }}
></div> ></div>
@ -858,9 +799,7 @@ const WarehouseDefinitions: React.FC = () => {
{/* Restrictions */} {/* Restrictions */}
{location.restrictions && location.restrictions.length > 0 && ( {location.restrictions && location.restrictions.length > 0 && (
<div className="pt-2"> <div className="pt-2">
<h4 className="text-sm font-medium text-gray-900 mb-1"> <h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
Kısıtlamalar
</h4>
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{location.restrictions.map((restriction, index) => ( {location.restrictions.map((restriction, index) => (
<span <span
@ -891,21 +830,21 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={() => handleLocationAction("view", location)} onClick={() => handleLocationAction('view', location)}
className="p-1 text-gray-400 hover:text-green-600" className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle" title="Görüntüle"
> >
<FaEye className="w-4 h-4" /> <FaEye className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleLocationAction("edit", location)} onClick={() => handleLocationAction('edit', location)}
className="p-1 text-gray-400 hover:text-blue-600" className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle" title="Düzenle"
> >
<FaEdit className="w-4 h-4" /> <FaEdit className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleDelete("location", location.id)} onClick={() => handleDelete('location', location.id)}
className="p-1 text-gray-400 hover:text-red-600" className="p-1 text-gray-400 hover:text-red-600"
title="Sil" title="Sil"
> >
@ -914,10 +853,10 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
})} })}
</div> </div>
); )
const LocationsListView = () => ( const LocationsListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200"> <div className="bg-white rounded-lg shadow-sm border border-gray-200">
@ -953,12 +892,9 @@ const WarehouseDefinitions: React.FC = () => {
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{filteredLocations.map((location) => { {filteredLocations.map((location) => {
const warehouse = warehouses.find( const warehouse = warehouses.find((w) => w.id === location.warehouseId)
(w) => w.id === location.warehouseId const zone = zones.find((z) => z.id === location.zoneId)
); const utilizationPercentage = (location.currentStock / location.capacity) * 100
const zone = zones.find((z) => z.id === location.zoneId);
const utilizationPercentage =
(location.currentStock / location.capacity) * 100;
return ( return (
<tr key={location.id} className="hover:bg-gray-50"> <tr key={location.id} className="hover:bg-gray-50">
@ -968,30 +904,18 @@ const WarehouseDefinitions: React.FC = () => {
<FaMapMarkerAlt className="w-4 h-4 text-orange-600" /> <FaMapMarkerAlt className="w-4 h-4 text-orange-600" />
</div> </div>
<div> <div>
<div className="text-sm font-medium text-gray-900"> <div className="text-sm font-medium text-gray-900">{location.name}</div>
{location.name} <div className="text-sm text-gray-500">{location.locationCode}</div>
</div>
<div className="text-sm text-gray-500">
{location.locationCode}
</div>
</div> </div>
</div> </div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">{warehouse?.name}</div>
{warehouse?.name} <div className="text-sm text-gray-500">{warehouse?.code}</div>
</div>
<div className="text-sm text-gray-500">
{warehouse?.code}
</div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">{zone?.name || '-'}</div>
{zone?.name || "-"} <div className="text-sm text-gray-500">{zone?.zoneCode || '-'}</div>
</div>
<div className="text-sm text-gray-500">
{zone?.zoneCode || "-"}
</div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <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"> <span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
@ -1007,10 +931,10 @@ const WarehouseDefinitions: React.FC = () => {
<div <div
className={`h-2 rounded-full ${ className={`h-2 rounded-full ${
utilizationPercentage > 90 utilizationPercentage > 90
? "bg-red-500" ? 'bg-red-500'
: utilizationPercentage > 70 : utilizationPercentage > 70
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-green-500" : 'bg-green-500'
}`} }`}
style={{ width: `${utilizationPercentage}%` }} style={{ width: `${utilizationPercentage}%` }}
></div> ></div>
@ -1039,21 +963,21 @@ const WarehouseDefinitions: React.FC = () => {
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium"> <td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2"> <div className="flex items-center justify-end gap-2">
<button <button
onClick={() => handleLocationAction("view", location)} onClick={() => handleLocationAction('view', location)}
className="text-green-600 hover:text-green-900" className="text-green-600 hover:text-green-900"
title="Görüntüle" title="Görüntüle"
> >
<FaEye className="w-4 h-4" /> <FaEye className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleLocationAction("edit", location)} onClick={() => handleLocationAction('edit', location)}
className="text-blue-600 hover:text-blue-900" className="text-blue-600 hover:text-blue-900"
title="Düzenle" title="Düzenle"
> >
<FaEdit className="w-4 h-4" /> <FaEdit className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleDelete("location", location.id)} onClick={() => handleDelete('location', location.id)}
className="text-red-600 hover:text-red-900" className="text-red-600 hover:text-red-900"
title="Sil" title="Sil"
> >
@ -1062,13 +986,13 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
); )
const LocationsTab = () => ( const LocationsTab = () => (
<div className="space-y-4 pt-2"> <div className="space-y-4 pt-2">
@ -1087,7 +1011,7 @@ const WarehouseDefinitions: React.FC = () => {
))} ))}
</select> </select>
<button <button
onClick={() => handleLocationAction("create")} 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" 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" /> <FaPlus className="w-4 h-4" />
@ -1096,42 +1020,39 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
{/* Content based on view mode */} {/* Content based on view mode */}
{viewMode === "grid" ? <LocationsGridView /> : <LocationsListView />} {viewMode === 'grid' ? <LocationsGridView /> : <LocationsListView />}
</div> </div>
); )
return ( return (
<div className="space-y-4 pt-2"> <Container>
<div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-2xl font-bold text-gray-900">Depo Tanımları</h2> <h2 className="text-2xl font-bold text-gray-900">Depo Tanımları</h2>
<p className="text-gray-600"> <p className="text-gray-600">Depolar, bölgeler ve lokasyonları yönetin</p>
Depolar, bölgeler ve lokasyonları yönetin
</p>
</div> </div>
{/* View Toggle Buttons */} {/* View Toggle Buttons */}
{(activeTab === "warehouses" || {(activeTab === 'warehouses' || activeTab === 'zones' || activeTab === 'locations') && (
activeTab === "zones" ||
activeTab === "locations") && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={() => setViewMode("grid")} onClick={() => setViewMode('grid')}
className={`p-1.5 rounded-lg ${ className={`p-1.5 rounded-lg ${
viewMode === "grid" viewMode === 'grid'
? "bg-blue-100 text-blue-600" ? 'bg-blue-100 text-blue-600'
: "text-gray-400 hover:text-gray-600" : 'text-gray-400 hover:text-gray-600'
}`} }`}
title="Kart Görünümü" title="Kart Görünümü"
> >
<FaTh className="w-4 h-4" /> <FaTh className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => setViewMode("list")} onClick={() => setViewMode('list')}
className={`p-1.5 rounded-lg ${ className={`p-1.5 rounded-lg ${
viewMode === "list" viewMode === 'list'
? "bg-blue-100 text-blue-600" ? 'bg-blue-100 text-blue-600'
: "text-gray-400 hover:text-gray-600" : 'text-gray-400 hover:text-gray-600'
}`} }`}
title="Liste Görünümü" title="Liste Görünümü"
> >
@ -1145,11 +1066,11 @@ const WarehouseDefinitions: React.FC = () => {
<div className="border-b border-gray-200"> <div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8" aria-label="Tabs"> <nav className="-mb-px flex space-x-8" aria-label="Tabs">
<button <button
onClick={() => setActiveTab("warehouses")} onClick={() => setActiveTab('warehouses')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${ className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === "warehouses" activeTab === 'warehouses'
? "border-blue-500 text-blue-600" ? 'border-blue-500 text-blue-600'
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300" : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`} }`}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -1158,11 +1079,11 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
</button> </button>
<button <button
onClick={() => setActiveTab("zones")} onClick={() => setActiveTab('zones')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${ className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === "zones" activeTab === 'zones'
? "border-blue-500 text-blue-600" ? 'border-blue-500 text-blue-600'
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300" : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`} }`}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -1171,11 +1092,11 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
</button> </button>
<button <button
onClick={() => setActiveTab("locations")} onClick={() => setActiveTab('locations')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${ className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === "locations" activeTab === 'locations'
? "border-blue-500 text-blue-600" ? 'border-blue-500 text-blue-600'
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300" : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`} }`}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -1187,9 +1108,10 @@ const WarehouseDefinitions: React.FC = () => {
</div> </div>
{/* Tab Content */} {/* Tab Content */}
{activeTab === "warehouses" && <WarehousesTab />} {activeTab === 'warehouses' && <WarehousesTab />}
{activeTab === "zones" && <ZonesTab />} {activeTab === 'zones' && <ZonesTab />}
{activeTab === "locations" && <LocationsTab />} {activeTab === 'locations' && <LocationsTab />}
</div>
{/* Modals */} {/* Modals */}
<WarehouseModal <WarehouseModal
@ -1218,8 +1140,8 @@ const WarehouseDefinitions: React.FC = () => {
warehouses={warehouses} warehouses={warehouses}
zones={zones} zones={zones}
/> />
</div> </Container>
); )
}; }
export default WarehouseDefinitions; export default WarehouseDefinitions

View file

@ -1,42 +1,36 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from 'react'
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from 'react-router-dom'
import { import { FaSave, FaTimes, FaBox, FaMapMarkerAlt, FaLayerGroup, FaBuilding } from 'react-icons/fa'
FaSave, import LoadingSpinner from '../../../components/common/LoadingSpinner'
FaTimes, import { WmWarehouse, WarehouseTypeEnum } from '../../../types/wm'
FaBox, import { SecurityLevelEnum } from '../../../types/mrp'
FaMapMarkerAlt, import { mockWarehouses } from '../../../mocks/mockWarehouses'
FaLayerGroup, import { Container } from '@/components/shared'
FaBuilding,
} from "react-icons/fa";
import LoadingSpinner from "../../../components/common/LoadingSpinner";
import { WmWarehouse, WarehouseTypeEnum } from "../../../types/wm";
import { SecurityLevelEnum } from "../../../types/mrp";
import { mockWarehouses } from "../../../mocks/mockWarehouses";
interface ValidationErrors { interface ValidationErrors {
[key: string]: string; [key: string]: string
} }
const WarehouseForm: React.FC = () => { const WarehouseForm: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate()
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>()
const isEdit = Boolean(id); const isEdit = Boolean(id)
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false)
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false)
const [errors, setErrors] = useState<ValidationErrors>({}); const [errors, setErrors] = useState<ValidationErrors>({})
const [formData, setFormData] = useState<WmWarehouse>({ const [formData, setFormData] = useState<WmWarehouse>({
id: "", id: '',
code: "", code: '',
name: "", name: '',
description: "", description: '',
address: { address: {
street: "", street: '',
city: "", city: '',
state: "", state: '',
postalCode: "", postalCode: '',
country: "Türkiye", country: 'Türkiye',
}, },
warehouseType: WarehouseTypeEnum.RawMaterials, warehouseType: WarehouseTypeEnum.RawMaterials,
locations: [], locations: [],
@ -50,132 +44,129 @@ const WarehouseForm: React.FC = () => {
managerId: 0, managerId: 0,
creationTime: new Date(), creationTime: new Date(),
lastModificationTime: new Date(), lastModificationTime: new Date(),
}); })
const loadFormData = useCallback(async () => { const loadFormData = useCallback(async () => {
setLoading(true); setLoading(true)
try { try {
if (isEdit && id) { if (isEdit && id) {
// Simulate API call // Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000))
const warehouse = mockWarehouses.find((w) => w.id === id); const warehouse = mockWarehouses.find((w) => w.id === id)
if (warehouse) { if (warehouse) {
setFormData(warehouse); setFormData(warehouse)
} }
} }
} catch (error) { } catch (error) {
console.error("Error loading form data:", error); console.error('Error loading form data:', error)
} finally { } finally {
setLoading(false); setLoading(false)
} }
}, [isEdit, id]); }, [isEdit, id])
useEffect(() => { useEffect(() => {
loadFormData(); loadFormData()
}, [loadFormData]); }, [loadFormData])
const validateForm = (): boolean => { const validateForm = (): boolean => {
const newErrors: ValidationErrors = {}; const newErrors: ValidationErrors = {}
if (!formData.code.trim()) { if (!formData.code.trim()) {
newErrors.code = "Depo kodu zorunludur"; newErrors.code = 'Depo kodu zorunludur'
} }
if (!formData.name.trim()) { if (!formData.name.trim()) {
newErrors.name = "Depo adı zorunludur"; newErrors.name = 'Depo adı zorunludur'
} }
if (!formData.warehouseType) { if (!formData.warehouseType) {
newErrors.warehouseType = "Depo tipi seçilmelidir"; newErrors.warehouseType = 'Depo tipi seçilmelidir'
} }
if (!formData.address?.street.trim()) { if (!formData.address?.street.trim()) {
newErrors["address.street"] = "Adres zorunludur"; newErrors['address.street'] = 'Adres zorunludur'
} }
if (!formData.address?.city.trim()) { if (!formData.address?.city.trim()) {
newErrors["address.city"] = "Şehir zorunludur"; newErrors['address.city'] = 'Şehir zorunludur'
} }
if (!formData.address?.country.trim()) { if (!formData.address?.country.trim()) {
newErrors["address.country"] = "Ülke zorunludur"; newErrors['address.country'] = 'Ülke zorunludur'
} }
if (formData.capacity <= 0) { if (formData.capacity <= 0) {
newErrors.capacity = "Kapasite 0'dan büyük olmalıdır"; newErrors.capacity = "Kapasite 0'dan büyük olmalıdır"
} }
setErrors(newErrors); setErrors(newErrors)
return Object.keys(newErrors).length === 0; return Object.keys(newErrors).length === 0
}; }
const handleInputChange = (field: string, value: any) => { const handleInputChange = (field: string, value: any) => {
setFormData((prev) => { setFormData((prev) => {
const updated = { ...prev }; const updated = { ...prev }
const keys = field.split("."); const keys = field.split('.')
let current: any = updated; let current: any = updated
keys.forEach((key, index) => { keys.forEach((key, index) => {
if (index === keys.length - 1) { if (index === keys.length - 1) {
current[key] = value; current[key] = value
} else { } else {
current[key] = { ...current[key] }; current[key] = { ...current[key] }
current = current[key]; current = current[key]
} }
}); })
return updated; return updated
}); })
if (errors[field]) { if (errors[field]) {
setErrors((prev) => ({ ...prev, [field]: "" })); setErrors((prev) => ({ ...prev, [field]: '' }))
}
} }
};
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault()
if (!validateForm()) { if (!validateForm()) {
return; return
} }
setSaving(true); setSaving(true)
try { try {
// Simulate API call // Simulate API call
await new Promise((resolve) => setTimeout(resolve, 2000)); await new Promise((resolve) => setTimeout(resolve, 2000))
console.log("Warehouse data:", { console.log('Warehouse data:', {
...formData, ...formData,
id: isEdit ? id : undefined, id: isEdit ? id : undefined,
}); })
// Show success message // Show success message
alert( alert(isEdit ? 'Depo başarıyla güncellendi!' : 'Depo başarıyla oluşturuldu!')
isEdit ? "Depo başarıyla güncellendi!" : "Depo başarıyla oluşturuldu!"
);
// Navigate back to list // Navigate back to list
navigate("/admin/warehouse"); navigate('/admin/warehouse')
} catch (error) { } catch (error) {
console.error("Error saving warehouse:", error); console.error('Error saving warehouse:', error)
alert("Bir hata oluştu. Lütfen tekrar deneyin."); alert('Bir hata oluştu. Lütfen tekrar deneyin.')
} finally { } finally {
setSaving(false); setSaving(false)
}
} }
};
const handleCancel = () => { const handleCancel = () => {
navigate("/admin/warehouse"); navigate('/admin/warehouse')
}; }
if (loading) { if (loading) {
return <LoadingSpinner />; return <LoadingSpinner />
} }
return ( return (
<div className="space-y-4 pt-2"> <Container>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-xl font-semibold text-gray-900"> <h2 className="text-xl font-semibold text-gray-900">
{isEdit ? "Depo Düzenle" : "Yeni Depo"} {isEdit ? 'Depo Düzenle' : 'Yeni Depo'}
</h2> </h2>
<p className="text-sm text-gray-600 mt-1"> <p className="text-sm text-gray-600 mt-1">
{isEdit {isEdit ? 'Mevcut depo bilgilerini güncelleyin' : 'Yeni depo bilgilerini girin'}
? "Mevcut depo bilgilerini güncelleyin"
: "Yeni depo bilgilerini girin"}
</p> </p>
</div> </div>
</div> </div>
@ -200,17 +191,15 @@ const WarehouseForm: React.FC = () => {
<input <input
type="text" type="text"
value={formData.code} value={formData.code}
onChange={(e) => handleInputChange("code", e.target.value)} onChange={(e) => handleInputChange('code', e.target.value)}
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${ className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
errors.code errors.code
? "border-red-300 focus:border-red-500 focus:ring-red-500" ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500" : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`} }`}
placeholder="Örn: WH001" placeholder="Örn: WH001"
/> />
{errors.code && ( {errors.code && <p className="mt-1 text-sm text-red-600">{errors.code}</p>}
<p className="mt-1 text-sm text-red-600">{errors.code}</p>
)}
</div> </div>
<div> <div>
@ -219,13 +208,11 @@ const WarehouseForm: React.FC = () => {
</label> </label>
<select <select
value={formData.warehouseType} value={formData.warehouseType}
onChange={(e) => onChange={(e) => handleInputChange('warehouseType', e.target.value)}
handleInputChange("warehouseType", e.target.value)
}
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${ className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
errors.warehouseType errors.warehouseType
? "border-red-300 focus:border-red-500 focus:ring-red-500" ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500" : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`} }`}
> >
<option value="">Tip seçin</option> <option value="">Tip seçin</option>
@ -239,42 +226,32 @@ const WarehouseForm: React.FC = () => {
<option value="HAZARDOUS">Tehlikeli Madde Deposu</option> <option value="HAZARDOUS">Tehlikeli Madde Deposu</option>
</select> </select>
{errors.warehouseType && ( {errors.warehouseType && (
<p className="mt-1 text-sm text-red-600"> <p className="mt-1 text-sm text-red-600">{errors.warehouseType}</p>
{errors.warehouseType}
</p>
)} )}
</div> </div>
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">Depo Adı *</label>
Depo Adı *
</label>
<input <input
type="text" type="text"
value={formData.name} value={formData.name}
onChange={(e) => handleInputChange("name", e.target.value)} onChange={(e) => handleInputChange('name', e.target.value)}
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${ className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
errors.name errors.name
? "border-red-300 focus:border-red-500 focus:ring-red-500" ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500" : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`} }`}
placeholder="Depo adı" placeholder="Depo adı"
/> />
{errors.name && ( {errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
<p className="mt-1 text-sm text-red-600">{errors.name}</p>
)}
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">ıklama</label>
ıklama
</label>
<textarea <textarea
value={formData.description} value={formData.description}
onChange={(e) => onChange={(e) => handleInputChange('description', e.target.value)}
handleInputChange("description", e.target.value)
}
rows={3} rows={3}
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500" className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
placeholder="Depo açıklaması" placeholder="Depo açıklaması"
@ -291,16 +268,11 @@ const WarehouseForm: React.FC = () => {
min="0" min="0"
step="0.01" step="0.01"
value={formData.capacity} value={formData.capacity}
onChange={(e) => onChange={(e) => handleInputChange('capacity', parseFloat(e.target.value) || 0)}
handleInputChange(
"capacity",
parseFloat(e.target.value) || 0
)
}
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${ className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
errors.capacity errors.capacity
? "border-red-300 focus:border-red-500 focus:ring-red-500" ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500" : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`} }`}
placeholder="0" placeholder="0"
/> />
@ -315,15 +287,10 @@ const WarehouseForm: React.FC = () => {
type="checkbox" type="checkbox"
id="isMainWarehouse" id="isMainWarehouse"
checked={formData.isMainWarehouse} checked={formData.isMainWarehouse}
onChange={(e) => onChange={(e) => handleInputChange('isMainWarehouse', e.target.checked)}
handleInputChange("isMainWarehouse", e.target.checked)
}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/> />
<label <label htmlFor="isMainWarehouse" className="ml-2 block text-sm text-gray-900">
htmlFor="isMainWarehouse"
className="ml-2 block text-sm text-gray-900"
>
Ana Depo Ana Depo
</label> </label>
</div> </div>
@ -333,15 +300,10 @@ const WarehouseForm: React.FC = () => {
type="checkbox" type="checkbox"
id="isActive" id="isActive"
checked={formData.isActive} checked={formData.isActive}
onChange={(e) => onChange={(e) => handleInputChange('isActive', e.target.checked)}
handleInputChange("isActive", e.target.checked)
}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/> />
<label <label htmlFor="isActive" className="ml-2 block text-sm text-gray-900">
htmlFor="isActive"
className="ml-2 block text-sm text-gray-900"
>
Aktif Aktif
</label> </label>
</div> </div>
@ -361,105 +323,79 @@ const WarehouseForm: React.FC = () => {
<div className="p-4 space-y-3"> <div className="p-4 space-y-3">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">Adres *</label>
Adres *
</label>
<input <input
type="text" type="text"
value={formData.address?.street} value={formData.address?.street}
onChange={(e) => onChange={(e) => handleInputChange('address.street', e.target.value)}
handleInputChange("address.street", e.target.value)
}
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${ className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
errors["address.street"] errors['address.street']
? "border-red-300 focus:border-red-500 focus:ring-red-500" ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500" : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`} }`}
placeholder="Sokak, cadde, mahalle" placeholder="Sokak, cadde, mahalle"
/> />
{errors["address.street"] && ( {errors['address.street'] && (
<p className="mt-1 text-sm text-red-600"> <p className="mt-1 text-sm text-red-600">{errors['address.street']}</p>
{errors["address.street"]}
</p>
)} )}
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">Şehir *</label>
Şehir *
</label>
<input <input
type="text" type="text"
value={formData.address?.city} value={formData.address?.city}
onChange={(e) => onChange={(e) => handleInputChange('address.city', e.target.value)}
handleInputChange("address.city", e.target.value)
}
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${ className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
errors["address.city"] errors['address.city']
? "border-red-300 focus:border-red-500 focus:ring-red-500" ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500" : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`} }`}
placeholder="Şehir" placeholder="Şehir"
/> />
{errors["address.city"] && ( {errors['address.city'] && (
<p className="mt-1 text-sm text-red-600"> <p className="mt-1 text-sm text-red-600">{errors['address.city']}</p>
{errors["address.city"]}
</p>
)} )}
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">İl/Bölge</label>
İl/Bölge
</label>
<input <input
type="text" type="text"
value={formData.address?.state} value={formData.address?.state}
onChange={(e) => onChange={(e) => handleInputChange('address.state', e.target.value)}
handleInputChange("address.state", e.target.value)
}
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500" className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
placeholder="İl/Bölge" placeholder="İl/Bölge"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">Posta Kodu</label>
Posta Kodu
</label>
<input <input
type="text" type="text"
value={formData.address?.postalCode} value={formData.address?.postalCode}
onChange={(e) => onChange={(e) => handleInputChange('address.postalCode', e.target.value)}
handleInputChange("address.postalCode", e.target.value)
}
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500" className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
placeholder="34000" placeholder="34000"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">Ülke *</label>
Ülke *
</label>
<input <input
type="text" type="text"
value={formData.address?.country} value={formData.address?.country}
onChange={(e) => onChange={(e) => handleInputChange('address.country', e.target.value)}
handleInputChange("address.country", e.target.value)
}
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${ className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
errors["address.country"] errors['address.country']
? "border-red-300 focus:border-red-500 focus:ring-red-500" ? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500" : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`} }`}
placeholder="Ülke" placeholder="Ülke"
/> />
{errors["address.country"] && ( {errors['address.country'] && (
<p className="mt-1 text-sm text-red-600"> <p className="mt-1 text-sm text-red-600">{errors['address.country']}</p>
{errors["address.country"]}
</p>
)} )}
</div> </div>
</div> </div>
@ -479,25 +415,17 @@ const WarehouseForm: React.FC = () => {
<div className="bg-gray-50 rounded-lg p-4"> <div className="bg-gray-50 rounded-lg p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm"> <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
<div> <div>
<span className="font-medium text-gray-700"> <span className="font-medium text-gray-700">Toplam Kapasite:</span>
Toplam Kapasite:
</span>
<div className="text-lg font-semibold text-blue-600"> <div className="text-lg font-semibold text-blue-600">
{formData.capacity.toLocaleString()} m³ {formData.capacity.toLocaleString()} m³
</div> </div>
</div> </div>
<div> <div>
<span className="font-medium text-gray-700"> <span className="font-medium text-gray-700">Mevcut Kullanım:</span>
Mevcut Kullanım: <div className="text-lg font-semibold text-green-600">0 m³ (0%)</div>
</span>
<div className="text-lg font-semibold text-green-600">
0 m³ (0%)
</div>
</div> </div>
<div> <div>
<span className="font-medium text-gray-700"> <span className="font-medium text-gray-700">Müsait Kapasite:</span>
Müsait Kapasite:
</span>
<div className="text-lg font-semibold text-gray-600"> <div className="text-lg font-semibold text-gray-600">
{formData.capacity.toLocaleString()} m³ {formData.capacity.toLocaleString()} m³
</div> </div>
@ -506,10 +434,7 @@ const WarehouseForm: React.FC = () => {
<div className="mt-4"> <div className="mt-4">
<div className="w-full bg-gray-200 rounded-full h-2"> <div className="w-full bg-gray-200 rounded-full h-2">
<div <div className="bg-blue-600 h-2 rounded-full" style={{ width: '0%' }} />
className="bg-blue-600 h-2 rounded-full"
style={{ width: "0%" }}
/>
</div> </div>
<p className="text-xs text-gray-600 mt-1">Kullanım oranı: 0%</p> <p className="text-xs text-gray-600 mt-1">Kullanım oranı: 0%</p>
</div> </div>
@ -532,25 +457,20 @@ const WarehouseForm: React.FC = () => {
<div className="flex"> <div className="flex">
<div className="ml-3"> <div className="ml-3">
<p className="text-sm text-blue-700"> <p className="text-sm text-blue-700">
{formData.warehouseType === {formData.warehouseType === WarehouseTypeEnum.RawMaterials &&
WarehouseTypeEnum.RawMaterials && 'Hammadde depolama için özel koşullara sahip olacaktır.'}
"Hammadde depolama için özel koşullara sahip olacaktır."} {formData.warehouseType === WarehouseTypeEnum.FinishedGoods &&
{formData.warehouseType === 'Mamul depolama ve sevkiyat operasyonları için optimize edilecektir.'}
WarehouseTypeEnum.FinishedGoods && {formData.warehouseType === WarehouseTypeEnum.SpareParts &&
"Mamul depolama ve sevkiyat operasyonları için optimize edilecektir."} 'Yedek parça yönetimi için özel raflar ve kategorilendirme yapılacaktır.'}
{formData.warehouseType ===
WarehouseTypeEnum.SpareParts &&
"Yedek parça yönetimi için özel raflar ve kategorilendirme yapılacaktır."}
{formData.warehouseType === WarehouseTypeEnum.Returns && {formData.warehouseType === WarehouseTypeEnum.Returns &&
"Konsinye stok yönetimi için ayrı izleme sistemi kullanılacaktır."} 'Konsinye stok yönetimi için ayrı izleme sistemi kullanılacaktır.'}
{formData.warehouseType === {formData.warehouseType === WarehouseTypeEnum.Quarantine &&
WarehouseTypeEnum.Quarantine && 'Karantina prosedürleri ve onay mekanizmaları aktif olacaktır.'}
"Karantina prosedürleri ve onay mekanizmaları aktif olacaktır."}
{formData.warehouseType === WarehouseTypeEnum.Transit && {formData.warehouseType === WarehouseTypeEnum.Transit &&
"Sıcaklık kontrollü depolama sistemi kurulacaktır."} 'Sıcaklık kontrollü depolama sistemi kurulacaktır.'}
{formData.warehouseType === {formData.warehouseType === WarehouseTypeEnum.WorkInProgress &&
WarehouseTypeEnum.WorkInProgress && 'Tehlikeli madde depolama güvenlik protokolleri uygulanacaktır.'}
"Tehlikeli madde depolama güvenlik protokolleri uygulanacaktır."}
</p> </p>
</div> </div>
</div> </div>
@ -582,14 +502,15 @@ const WarehouseForm: React.FC = () => {
) : ( ) : (
<> <>
<FaSave className="w-4 h-4 mr-2" /> <FaSave className="w-4 h-4 mr-2" />
{isEdit ? "Güncelle" : "Kaydet"} {isEdit ? 'Güncelle' : 'Kaydet'}
</> </>
)} )}
</button> </button>
</div> </div>
</form> </form>
</div> </div>
); </Container>
}; )
}
export default WarehouseForm; export default WarehouseForm

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom'
import { useQuery } from "@tanstack/react-query"; import { useQuery } from '@tanstack/react-query'
import { import {
FaBuilding, FaBuilding,
FaPlus, FaPlus,
@ -14,45 +14,42 @@ import {
FaArrowUp, FaArrowUp,
FaExclamationTriangle, FaExclamationTriangle,
FaChartLine, FaChartLine,
} from "react-icons/fa"; } from 'react-icons/fa'
import classNames from "classnames"; import classNames from 'classnames'
import { WarehouseTypeEnum } from "../../../types/wm"; import { WarehouseTypeEnum } from '../../../types/wm'
import { mockWarehouses } from "../../../mocks/mockWarehouses"; import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { import { getWarehouseTypeColor, getWarehouseTypeText } from '../../../utils/erp'
getWarehouseTypeColor, import { Container } from '@/components/shared'
getWarehouseTypeText,
} from "../../../utils/erp";
const WarehouseList: React.FC = () => { const WarehouseList: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [filterType, setFilterType] = useState("all"); const [filterType, setFilterType] = useState('all')
const [showFilters, setShowFilters] = useState(false); const [showFilters, setShowFilters] = useState(false)
const { const {
data: warehouses, data: warehouses,
isLoading, isLoading,
error, error,
} = useQuery({ } = useQuery({
queryKey: ["warehouses", searchTerm, filterType], queryKey: ['warehouses', searchTerm, filterType],
queryFn: async () => { queryFn: async () => {
await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500))
return mockWarehouses.filter((warehouse) => { return mockWarehouses.filter((warehouse) => {
const matchesSearch = const matchesSearch =
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase()) || warehouse.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()); warehouse.name.toLowerCase().includes(searchTerm.toLowerCase())
const matchesType = const matchesType = filterType === 'all' || warehouse.warehouseType === filterType
filterType === "all" || warehouse.warehouseType === filterType; return matchesSearch && matchesType
return matchesSearch && matchesType; })
});
}, },
}); })
const getUtilizationColor = (utilization: number) => { const getUtilizationColor = (utilization: number) => {
const percentage = utilization; const percentage = utilization
if (percentage >= 90) return "text-red-600"; if (percentage >= 90) return 'text-red-600'
if (percentage >= 75) return "text-yellow-600"; if (percentage >= 75) return 'text-yellow-600'
return "text-green-600"; return 'text-green-600'
}; }
if (isLoading) { if (isLoading) {
return ( return (
@ -60,7 +57,7 @@ const WarehouseList: React.FC = () => {
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div> <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<span className="ml-3 text-gray-600">Depolar yükleniyor...</span> <span className="ml-3 text-gray-600">Depolar yükleniyor...</span>
</div> </div>
); )
} }
if (error) { if (error) {
@ -71,11 +68,12 @@ const WarehouseList: React.FC = () => {
<span className="text-red-800">Depolar yüklenirken hata oluştu.</span> <span className="text-red-800">Depolar yüklenirken hata oluştu.</span>
</div> </div>
</div> </div>
); )
} }
return ( return (
<div className="space-y-4 pt-2"> <Container>
<div className="space-y-2">
{/* Header Actions */} {/* Header Actions */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
@ -96,10 +94,10 @@ const WarehouseList: React.FC = () => {
<button <button
onClick={() => setShowFilters(!showFilters)} onClick={() => setShowFilters(!showFilters)}
className={classNames( className={classNames(
"flex items-center px-3 py-1.5 text-sm border rounded-lg transition-colors", 'flex items-center px-3 py-1.5 text-sm border rounded-lg transition-colors',
showFilters showFilters
? "border-blue-500 bg-blue-50 text-blue-700" ? 'border-blue-500 bg-blue-50 text-blue-700'
: "border-gray-300 bg-white text-gray-700 hover:bg-gray-50" : 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
)} )}
> >
<FaFilter size={14} className="mr-2" /> <FaFilter size={14} className="mr-2" />
@ -109,7 +107,7 @@ const WarehouseList: React.FC = () => {
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<button <button
onClick={() => alert("Dışa aktarma özelliği yakında eklenecek")} onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors" className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
> >
<FaDownload size={14} className="mr-2" /> <FaDownload size={14} className="mr-2" />
@ -131,9 +129,7 @@ const WarehouseList: React.FC = () => {
<div className="bg-white border border-gray-200 rounded-lg p-3"> <div className="bg-white border border-gray-200 rounded-lg p-3">
<div className="grid grid-cols-1 md:grid-cols-3 gap-3"> <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
<div> <div>
<label className="block text-xs font-medium text-gray-700 mb-1"> <label className="block text-xs font-medium text-gray-700 mb-1">Depo Türü</label>
Depo Türü
</label>
<select <select
value={filterType} value={filterType}
onChange={(e) => setFilterType(e.target.value)} onChange={(e) => setFilterType(e.target.value)}
@ -142,12 +138,8 @@ const WarehouseList: React.FC = () => {
<option value="all">Tümü</option> <option value="all">Tümü</option>
<option value={WarehouseTypeEnum.RawMaterials}>Hammadde</option> <option value={WarehouseTypeEnum.RawMaterials}>Hammadde</option>
<option value={WarehouseTypeEnum.FinishedGoods}>Mamul</option> <option value={WarehouseTypeEnum.FinishedGoods}>Mamul</option>
<option value={WarehouseTypeEnum.WorkInProgress}> <option value={WarehouseTypeEnum.WorkInProgress}>Yarı Mamul</option>
Yarı Mamul <option value={WarehouseTypeEnum.SpareParts}>Yedek Parça</option>
</option>
<option value={WarehouseTypeEnum.SpareParts}>
Yedek Parça
</option>
<option value={WarehouseTypeEnum.Quarantine}>Karantina</option> <option value={WarehouseTypeEnum.Quarantine}>Karantina</option>
</select> </select>
</div> </div>
@ -155,8 +147,8 @@ const WarehouseList: React.FC = () => {
<div className="flex items-end"> <div className="flex items-end">
<button <button
onClick={() => { onClick={() => {
setFilterType("all"); setFilterType('all')
setSearchTerm(""); setSearchTerm('')
}} }}
className="w-full px-4 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors" className="w-full px-4 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
> >
@ -173,9 +165,7 @@ const WarehouseList: React.FC = () => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-gray-600">Toplam Depo</p> <p className="text-sm font-medium text-gray-600">Toplam Depo</p>
<p className="text-xl font-bold text-gray-900"> <p className="text-xl font-bold text-gray-900">{warehouses?.length || 0}</p>
{warehouses?.length || 0}
</p>
</div> </div>
<FaBuilding className="h-6 w-6 text-blue-500" /> <FaBuilding className="h-6 w-6 text-blue-500" />
</div> </div>
@ -184,13 +174,9 @@ const WarehouseList: React.FC = () => {
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3"> <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-gray-600"> <p className="text-sm font-medium text-gray-600">Toplam Kapasite</p>
Toplam Kapasite
</p>
<p className="text-xl font-bold text-green-600"> <p className="text-xl font-bold text-green-600">
{warehouses {warehouses?.reduce((acc, w) => acc + w.capacity, 0).toLocaleString() || 0}
?.reduce((acc, w) => acc + w.capacity, 0)
.toLocaleString() || 0}
</p> </p>
</div> </div>
<FaBox className="h-6 w-6 text-green-500" /> <FaBox className="h-6 w-6 text-green-500" />
@ -200,19 +186,13 @@ const WarehouseList: React.FC = () => {
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3"> <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm font-medium text-gray-600"> <p className="text-sm font-medium text-gray-600">Kullanım Oranı</p>
Kullanım Oranı
</p>
<p className="text-xl font-bold text-yellow-600"> <p className="text-xl font-bold text-yellow-600">
{warehouses?.length {warehouses?.length
? Math.round( ? Math.round(
(warehouses.reduce( (warehouses.reduce((acc, w) => acc + w.currentUtilization, 0) /
(acc, w) => acc + w.currentUtilization, (warehouses.reduce((acc, w) => acc + w.capacity, 0) || 1)) *
0 100,
) /
(warehouses.reduce((acc, w) => acc + w.capacity, 0) ||
1)) *
100
) )
: 0} : 0}
% %
@ -272,14 +252,11 @@ const WarehouseList: React.FC = () => {
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{warehouses?.map((warehouse) => { {warehouses?.map((warehouse) => {
const utilizationPercentage = Math.round( const utilizationPercentage = Math.round(
(warehouse.currentUtilization / warehouse.capacity) * 100 (warehouse.currentUtilization / warehouse.capacity) * 100,
); )
return ( return (
<tr <tr key={warehouse.id} className="hover:bg-gray-50 transition-colors">
key={warehouse.id}
className="hover:bg-gray-50 transition-colors"
>
<td className="px-4 py-3"> <td className="px-4 py-3">
<div className="flex items-center"> <div className="flex items-center">
<div className="flex-shrink-0 h-8 w-8"> <div className="flex-shrink-0 h-8 w-8">
@ -307,16 +284,15 @@ const WarehouseList: React.FC = () => {
<div className="space-y-1"> <div className="space-y-1">
<span <span
className={classNames( className={classNames(
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium", 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
getWarehouseTypeColor(warehouse.warehouseType) getWarehouseTypeColor(warehouse.warehouseType),
)} )}
> >
{getWarehouseTypeText(warehouse.warehouseType)} {getWarehouseTypeText(warehouse.warehouseType)}
</span> </span>
<div className="flex items-center text-xs text-gray-500"> <div className="flex items-center text-xs text-gray-500">
<FaMapMarkerAlt size={14} className="mr-1" /> <FaMapMarkerAlt size={14} className="mr-1" />
{warehouse.address?.city},{" "} {warehouse.address?.city}, {warehouse.address?.country}
{warehouse.address?.country}
</div> </div>
</div> </div>
</td> </td>
@ -325,17 +301,13 @@ const WarehouseList: React.FC = () => {
<div className="text-sm font-medium text-gray-900"> <div className="text-sm font-medium text-gray-900">
{warehouse.capacity.toLocaleString()} m³ {warehouse.capacity.toLocaleString()} m³
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">Toplam Kapasite</div>
Toplam Kapasite
</div>
</td> </td>
<td className="px-4 py-3"> <td className="px-4 py-3">
<div className="space-y-1"> <div className="space-y-1">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-xs text-gray-600"> <span className="text-xs text-gray-600">Kullanılan:</span>
Kullanılan:
</span>
<span className="text-sm font-medium text-gray-900"> <span className="text-sm font-medium text-gray-900">
{warehouse.currentUtilization.toLocaleString()} m³ {warehouse.currentUtilization.toLocaleString()} m³
</span> </span>
@ -343,12 +315,12 @@ const WarehouseList: React.FC = () => {
<div className="w-full bg-gray-200 rounded-full h-2"> <div className="w-full bg-gray-200 rounded-full h-2">
<div <div
className={classNames( className={classNames(
"h-2 rounded-full", 'h-2 rounded-full',
utilizationPercentage >= 90 utilizationPercentage >= 90
? "bg-red-500" ? 'bg-red-500'
: utilizationPercentage >= 75 : utilizationPercentage >= 75
? "bg-yellow-500" ? 'bg-yellow-500'
: "bg-green-500" : 'bg-green-500',
)} )}
style={{ style={{
width: `${Math.min(utilizationPercentage, 100)}%`, width: `${Math.min(utilizationPercentage, 100)}%`,
@ -357,8 +329,8 @@ const WarehouseList: React.FC = () => {
</div> </div>
<div <div
className={classNames( className={classNames(
"text-sm font-medium", 'text-sm font-medium',
getUtilizationColor(utilizationPercentage) getUtilizationColor(utilizationPercentage),
)} )}
> >
%{utilizationPercentage} %{utilizationPercentage}
@ -369,13 +341,8 @@ const WarehouseList: React.FC = () => {
<td className="px-4 py-3"> <td className="px-4 py-3">
<div className="space-y-1"> <div className="space-y-1">
<div className="flex items-center"> <div className="flex items-center">
<FaChartLine <FaChartLine size={14} className="text-green-500 mr-1" />
size={14} <span className="text-sm text-green-600">Verimli</span>
className="text-green-500 mr-1"
/>
<span className="text-sm text-green-600">
Verimli
</span>
</div> </div>
<div className="text-xs text-gray-500">Son 30 gün</div> <div className="text-xs text-gray-500">Son 30 gün</div>
</div> </div>
@ -384,13 +351,13 @@ const WarehouseList: React.FC = () => {
<td className="px-4 py-3"> <td className="px-4 py-3">
<span <span
className={classNames( className={classNames(
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium", 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
warehouse.isActive warehouse.isActive
? "bg-green-100 text-green-800" ? 'bg-green-100 text-green-800'
: "bg-red-100 text-red-800" : 'bg-red-100 text-red-800',
)} )}
> >
{warehouse.isActive ? "Aktif" : "Pasif"} {warehouse.isActive ? 'Aktif' : 'Pasif'}
</span> </span>
</td> </td>
@ -414,7 +381,7 @@ const WarehouseList: React.FC = () => {
</div> </div>
</td> </td>
</tr> </tr>
); )
})} })}
</tbody> </tbody>
</table> </table>
@ -423,12 +390,8 @@ const WarehouseList: React.FC = () => {
{(!warehouses || warehouses.length === 0) && ( {(!warehouses || warehouses.length === 0) && (
<div className="text-center py-12"> <div className="text-center py-12">
<FaBuilding className="mx-auto h-12 w-12 text-gray-400" /> <FaBuilding className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900"> <h3 className="mt-2 text-sm font-medium text-gray-900">Depo bulunamadı</h3>
Depo bulunamadı <p className="mt-1 text-sm text-gray-500">Yeni depo ekleyerek başlayın.</p>
</h3>
<p className="mt-1 text-sm text-gray-500">
Yeni depo ekleyerek başlayın.
</p>
<div className="mt-6"> <div className="mt-6">
<Link <Link
to="/admin/warehouse/new" to="/admin/warehouse/new"
@ -442,7 +405,8 @@ const WarehouseList: React.FC = () => {
)} )}
</div> </div>
</div> </div>
); </Container>
}; )
}
export default WarehouseList; export default WarehouseList

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff