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 {
FaBox,
FaMapMarkerAlt,
@ -8,71 +8,61 @@ import {
FaEye,
FaEdit,
FaPlus,
} from "react-icons/fa";
} from 'react-icons/fa'
// using modals for create actions; no navigation needed here
import {
MmLotNumber,
MmSerialNumber,
QualityStatusEnum,
SerialStatusEnum,
} from "../../../types/mm";
import LotForm from "./LotForm";
import SerialForm from "./SerialForm";
import { mockLotNumbers } from "../../../mocks/mockLotNumbers";
import { mockSerialNumbers } from "../../../mocks/mockSerialNumbers";
import { mockMaterials } from "../../../mocks/mockMaterials";
import { mockUnits } from "../../../mocks/mockUnits";
import { mockBusinessParties } from "../../../mocks/mockBusinessParties";
import Widget from "../../../components/common/Widget";
import { PartyType } from "../../../types/common";
import { getQualityStatusInfo, getSerialStatusInfo } from "../../../utils/erp";
import { MmLotNumber, MmSerialNumber, QualityStatusEnum, SerialStatusEnum } from '../../../types/mm'
import LotForm from './LotForm'
import SerialForm from './SerialForm'
import { mockLotNumbers } from '../../../mocks/mockLotNumbers'
import { mockSerialNumbers } from '../../../mocks/mockSerialNumbers'
import { mockMaterials } from '../../../mocks/mockMaterials'
import { mockUnits } from '../../../mocks/mockUnits'
import { mockBusinessParties } from '../../../mocks/mockBusinessParties'
import Widget from '../../../components/common/Widget'
import { PartyType } from '../../../types/common'
import { getQualityStatusInfo, getSerialStatusInfo } from '../../../utils/erp'
import { Container } from '@/components/shared'
const InventoryTracking: React.FC = () => {
const [activeTab, setActiveTab] = useState<"lots" | "serials">("lots");
const [searchTerm, setSearchTerm] = useState("");
const [showCreateMenu, setShowCreateMenu] = useState(false);
const [activeTab, setActiveTab] = useState<'lots' | 'serials'>('lots')
const [searchTerm, setSearchTerm] = useState('')
const [showCreateMenu, setShowCreateMenu] = useState(false)
// Mock data için
const [lotNumbers, setLotNumbers] = useState<MmLotNumber[]>(mockLotNumbers);
const [lotNumbers, setLotNumbers] = useState<MmLotNumber[]>(mockLotNumbers)
const [serialNumbers, setSerialNumbers] =
useState<MmSerialNumber[]>(mockSerialNumbers);
const [serialNumbers, setSerialNumbers] = useState<MmSerialNumber[]>(mockSerialNumbers)
// Modal control state
const [openLotModal, setOpenLotModal] = useState(false);
const [openSerialModal, setOpenSerialModal] = useState(false);
const [currentLot, setCurrentLot] = useState<MmLotNumber | null>(null);
const [currentSerial, setCurrentSerial] = useState<MmSerialNumber | null>(
null
);
const [openLotModal, setOpenLotModal] = useState(false)
const [openSerialModal, setOpenSerialModal] = useState(false)
const [currentLot, setCurrentLot] = useState<MmLotNumber | null>(null)
const [currentSerial, setCurrentSerial] = useState<MmSerialNumber | null>(null)
const filteredLots = lotNumbers.filter(
(lot) =>
lot.lotNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase())
);
lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase()),
)
const filteredSerials = serialNumbers.filter(
(serial) =>
serial.serialNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase())
);
serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase()),
)
return (
<div className="space-y-3 py-2">
<Container>
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-gray-900">Envanter Takibi</h2>
<p className="text-gray-600">
Lot ve seri numarası takiplerini yönetin
</p>
<p className="text-gray-600">Lot ve seri numarası takiplerini yönetin</p>
</div>
<div className="relative">
<button
onClick={() =>
setShowCreateMenu((s) => !s)
} /* Kontrol küçültüldü */
onClick={() => 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"
>
<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">
<button
onClick={() => {
setOpenLotModal(true);
setShowCreateMenu(false);
setOpenLotModal(true)
setShowCreateMenu(false)
}}
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
onClick={() => {
setOpenSerialModal(true);
setShowCreateMenu(false);
setOpenSerialModal(true)
setShowCreateMenu(false)
}}
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 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
<Widget
title="Toplam Lot"
value={lotNumbers.length}
color="blue"
icon="FaBox"
/>
<Widget title="Toplam Lot" value={lotNumbers.length} color="blue" icon="FaBox" />
<Widget
title="Onaylı Lot"
value={
lotNumbers.filter(
(lot) => lot.qualityStatus === QualityStatusEnum.Approved
).length
lotNumbers.filter((lot) => lot.qualityStatus === QualityStatusEnum.Approved).length
}
color="green"
icon="FaBox"
@ -135,9 +118,7 @@ const InventoryTracking: React.FC = () => {
<Widget
title="Müsait Seri"
value={
serialNumbers.filter(
(serial) => serial.status === SerialStatusEnum.Available
).length
serialNumbers.filter((serial) => serial.status === SerialStatusEnum.Available).length
}
color="orange"
icon="FaHashtag"
@ -160,22 +141,22 @@ const InventoryTracking: React.FC = () => {
<div className="border-b border-gray-200">
<nav className="flex -mb-px">
<button
onClick={() => setActiveTab("lots")}
onClick={() => setActiveTab('lots')}
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
activeTab === "lots"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700"
activeTab === 'lots'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
<FaBox className="h-4 w-4 mr-2" />
Lot Numaraları ({filteredLots.length})
</button>
<button
onClick={() => setActiveTab("serials")}
onClick={() => setActiveTab('serials')}
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
activeTab === "serials"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700"
activeTab === 'serials'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
<FaHashtag className="h-4 w-4 mr-2" />
@ -185,7 +166,7 @@ const InventoryTracking: React.FC = () => {
</div>
{/* Lot Numbers Tab */}
{activeTab === "lots" && (
{activeTab === 'lots' && (
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
@ -218,20 +199,18 @@ const InventoryTracking: React.FC = () => {
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredLots.map((lot) => {
const qualityInfo = getQualityStatusInfo(lot.qualityStatus);
const qualityInfo = getQualityStatusInfo(lot.qualityStatus)
const isExpiringSoon =
lot.expiryDate &&
new Date(lot.expiryDate).getTime() - new Date().getTime() <
30 * 24 * 60 * 60 * 1000;
30 * 24 * 60 * 60 * 1000
return (
<tr key={lot.id} className="hover:bg-gray-50 text-xs">
<td className="px-2 py-1.5 whitespace-nowrap">
<div className="flex items-center">
<FaBox className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs font-medium text-gray-900">
{lot.lotNumber}
</div>
<div className="text-xs font-medium text-gray-900">{lot.lotNumber}</div>
</div>
</td>
<td className="px-2 py-1.5 whitespace-nowrap">
@ -248,9 +227,7 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center">
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs text-gray-900">
{new Date(lot.productionDate).toLocaleDateString(
"tr-TR"
)}
{new Date(lot.productionDate).toLocaleDateString('tr-TR')}
</div>
</div>
</td>
@ -258,16 +235,12 @@ const InventoryTracking: React.FC = () => {
{lot.expiryDate ? (
<div
className={`text-xs ${
isExpiringSoon
? "text-red-600 font-medium"
: "text-gray-900"
isExpiringSoon ? 'text-red-600 font-medium' : 'text-gray-900'
}`}
>
{new Date(lot.expiryDate).toLocaleDateString("tr-TR")}
{new Date(lot.expiryDate).toLocaleDateString('tr-TR')}
{isExpiringSoon && (
<div className="text-xs text-red-500">
Yakında Dolacak
</div>
<div className="text-xs text-red-500">Yakında Dolacak</div>
)}
</div>
) : (
@ -275,9 +248,7 @@ const InventoryTracking: React.FC = () => {
)}
</td>
<td className="px-2 py-1.5 whitespace-nowrap">
<div className="text-xs text-gray-900">
{lot.supplierId || "-"}
</div>
<div className="text-xs text-gray-900">{lot.supplierId || '-'}</div>
</td>
<td className="px-2 py-1.5 whitespace-nowrap">
<span
@ -290,8 +261,8 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center justify-end space-x-2">
<button
onClick={() => {
setCurrentLot(lot);
setOpenLotModal(true);
setCurrentLot(lot)
setOpenLotModal(true)
}}
className="text-blue-600 hover:text-blue-900"
>
@ -299,8 +270,8 @@ const InventoryTracking: React.FC = () => {
</button>
<button
onClick={() => {
setCurrentLot(lot);
setOpenLotModal(true);
setCurrentLot(lot)
setOpenLotModal(true)
}}
className="text-green-600 hover:text-green-900"
>
@ -309,37 +280,31 @@ const InventoryTracking: React.FC = () => {
</div>
</td>
</tr>
);
)
})}
</tbody>
</table>
<LotForm
isOpen={openLotModal}
onClose={() => {
setOpenLotModal(false);
setCurrentLot(null);
setOpenLotModal(false)
setCurrentLot(null)
}}
onSave={(lot) => setLotNumbers((prev) => [lot, ...prev])}
onUpdate={(updated) =>
setLotNumbers((prev) =>
prev.map((l) => (l.id === updated.id ? updated : l))
)
setLotNumbers((prev) => prev.map((l) => (l.id === updated.id ? updated : l)))
}
materials={mockMaterials}
units={mockUnits}
suppliers={mockBusinessParties.filter(
(bp) => bp.partyType === PartyType.Supplier
)}
suppliers={mockBusinessParties.filter((bp) => bp.partyType === PartyType.Supplier)}
initial={currentLot}
mode={currentLot ? "edit" : "create"}
mode={currentLot ? 'edit' : 'create'}
/>
{filteredLots.length === 0 && (
<div className="text-center py-12">
<FaBox className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Lot bulunamadı
</h3>
<h3 className="mt-2 text-sm font-medium text-gray-900">Lot bulunamadı</h3>
<p className="mt-1 text-sm text-gray-500">
Arama kriterlerinize uygun lot kaydı bulunmuyor.
</p>
@ -349,7 +314,7 @@ const InventoryTracking: React.FC = () => {
)}
{/* Serial Numbers Tab */}
{activeTab === "serials" && (
{activeTab === 'serials' && (
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
@ -382,12 +347,11 @@ const InventoryTracking: React.FC = () => {
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredSerials.map((serial) => {
const statusInfo = getSerialStatusInfo(serial.status);
const statusInfo = getSerialStatusInfo(serial.status)
const isWarrantyExpiring =
serial.warrantyExpiryDate &&
new Date(serial.warrantyExpiryDate).getTime() -
new Date().getTime() <
30 * 24 * 60 * 60 * 1000;
new Date(serial.warrantyExpiryDate).getTime() - new Date().getTime() <
30 * 24 * 60 * 60 * 1000
return (
<tr key={serial.id} className="hover:bg-gray-50 text-xs">
@ -411,7 +375,7 @@ const InventoryTracking: React.FC = () => {
LOT-{serial.lotId}
</span>
) : (
"-"
'-'
)}
</div>
</td>
@ -419,9 +383,7 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center">
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs text-gray-900">
{new Date(serial.productionDate).toLocaleDateString(
"tr-TR"
)}
{new Date(serial.productionDate).toLocaleDateString('tr-TR')}
</div>
</div>
</td>
@ -429,18 +391,12 @@ const InventoryTracking: React.FC = () => {
{serial.warrantyExpiryDate ? (
<div
className={`text-xs ${
isWarrantyExpiring
? "text-red-600 font-medium"
: "text-gray-900"
isWarrantyExpiring ? 'text-red-600 font-medium' : 'text-gray-900'
}`}
>
{new Date(
serial.warrantyExpiryDate
).toLocaleDateString("tr-TR")}
{new Date(serial.warrantyExpiryDate).toLocaleDateString('tr-TR')}
{isWarrantyExpiring && (
<div className="text-xs text-red-500">
Yakında Dolacak
</div>
<div className="text-xs text-red-500">Yakında Dolacak</div>
)}
</div>
) : (
@ -451,7 +407,7 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center">
<FaMapMarkerAlt className="h-4 w-4 text-gray-400 mr-2" />
<div className="text-xs text-gray-900">
{serial.currentLocationId || "-"}
{serial.currentLocationId || '-'}
</div>
</div>
</td>
@ -466,8 +422,8 @@ const InventoryTracking: React.FC = () => {
<div className="flex items-center justify-end space-x-2">
<button
onClick={() => {
setCurrentSerial(serial);
setOpenSerialModal(true);
setCurrentSerial(serial)
setOpenSerialModal(true)
}}
className="text-blue-600 hover:text-blue-900"
>
@ -475,8 +431,8 @@ const InventoryTracking: React.FC = () => {
</button>
<button
onClick={() => {
setCurrentSerial(serial);
setOpenSerialModal(true);
setCurrentSerial(serial)
setOpenSerialModal(true)
}}
className="text-green-600 hover:text-green-900"
>
@ -485,7 +441,7 @@ const InventoryTracking: React.FC = () => {
</div>
</td>
</tr>
);
)
})}
</tbody>
</table>
@ -493,27 +449,23 @@ const InventoryTracking: React.FC = () => {
<SerialForm
isOpen={openSerialModal}
onClose={() => {
setOpenSerialModal(false);
setCurrentSerial(null);
setOpenSerialModal(false)
setCurrentSerial(null)
}}
onSave={(serial) => setSerialNumbers((prev) => [serial, ...prev])}
onUpdate={(updated) =>
setSerialNumbers((prev) =>
prev.map((s) => (s.id === updated.id ? updated : s))
)
setSerialNumbers((prev) => prev.map((s) => (s.id === updated.id ? updated : s)))
}
materials={mockMaterials}
lots={lotNumbers.map((l) => ({ id: l.id, label: l.lotNumber }))}
initial={currentSerial}
mode={currentSerial ? "edit" : "create"}
mode={currentSerial ? 'edit' : 'create'}
/>
{filteredSerials.length === 0 && (
<div className="text-center py-12">
<FaHashtag className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Seri numarası bulunamadı
</h3>
<h3 className="mt-2 text-sm font-medium text-gray-900">Seri numarası bulunamadı</h3>
<p className="mt-1 text-sm text-gray-500">
Arama kriterlerinize uygun seri numarası bulunmuyor.
</p>
@ -522,7 +474,8 @@ const InventoryTracking: React.FC = () => {
</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 {
FaSearch,
FaMapMarkerAlt,
@ -7,45 +7,43 @@ import {
FaEye,
FaTh,
FaList,
} from "react-icons/fa";
import { mockWarehouses } from "../../../mocks/mockWarehouses";
import { mockLocations } from "../../../mocks/mockLocations";
import { mockStockItems } from "../../../mocks/mockStockItems";
import { getStockStatusColor, getStockStatusText } from "../../../utils/erp";
} from 'react-icons/fa'
import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { mockLocations } from '../../../mocks/mockLocations'
import { mockStockItems } from '../../../mocks/mockStockItems'
import { getStockStatusColor, getStockStatusText } from '../../../utils/erp'
import { Container } from '@/components/shared'
const LocationTracking: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [selectedWarehouse, setSelectedWarehouse] = useState<string>("");
const [selectedLocation, setSelectedLocation] = useState<string>("");
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
const [searchTerm, setSearchTerm] = useState('')
const [selectedWarehouse, setSelectedWarehouse] = useState<string>('')
const [selectedLocation, setSelectedLocation] = useState<string>('')
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
const getLocationUtilization = (locationId: string) => {
const location = mockLocations.find((l) => l.id === locationId);
if (!location) return 0;
return (location.currentStock / location.capacity) * 100;
};
const location = mockLocations.find((l) => l.id === locationId)
if (!location) return 0
return (location.currentStock / location.capacity) * 100
}
const getLocationStockItems = (locationId: string) => {
return mockStockItems.filter((item) => item.locationId === locationId);
};
return mockStockItems.filter((item) => item.locationId === locationId)
}
const filteredLocations = mockLocations.filter((location) => {
const matchesSearch =
location.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
location.locationCode.toLowerCase().includes(searchTerm.toLowerCase());
const matchesWarehouse =
selectedWarehouse === "" || location.warehouseId === selectedWarehouse;
return matchesSearch && matchesWarehouse;
});
location.locationCode.toLowerCase().includes(searchTerm.toLowerCase())
const matchesWarehouse = selectedWarehouse === '' || location.warehouseId === selectedWarehouse
return matchesSearch && matchesWarehouse
})
const GridView = () => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredLocations.map((location) => {
const warehouse = mockWarehouses.find(
(w) => w.id === location.warehouseId
);
const locationStockItems = getLocationStockItems(location.id);
const utilization = getLocationUtilization(location.id);
const warehouse = mockWarehouses.find((w) => w.id === location.warehouseId)
const locationStockItems = getLocationStockItems(location.id)
const utilization = getLocationUtilization(location.id)
return (
<div
@ -59,9 +57,7 @@ const LocationTracking: React.FC = () => {
</div>
<div>
<h3 className="font-medium text-gray-900">{location.name}</h3>
<p className="text-sm text-gray-500">
{location.locationCode}
</p>
<p className="text-sm text-gray-500">{location.locationCode}</p>
</div>
</div>
<button
@ -84,18 +80,16 @@ const LocationTracking: React.FC = () => {
<div className="space-y-1">
<div className="flex justify-between text-sm">
<span className="text-gray-600">Doluluk Oranı</span>
<span className="font-medium">
%{Math.round(utilization)}
</span>
<span className="font-medium">%{Math.round(utilization)}</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className={`h-2 rounded-full ${
utilization > 90
? "bg-red-500"
? 'bg-red-500'
: utilization > 70
? "bg-yellow-500"
: "bg-green-500"
? 'bg-yellow-500'
: 'bg-green-500'
}`}
style={{ width: `${utilization}%` }}
/>
@ -108,29 +102,22 @@ const LocationTracking: React.FC = () => {
{/* Stock Items Summary */}
<div className="bg-gray-50 rounded-lg p-2">
<div className="flex items-center justify-between mb-2">
<h4 className="text-sm font-medium text-gray-900">
Malzemeler
</h4>
<span className="text-xs text-gray-500">
{locationStockItems.length} çeşit
</span>
<h4 className="text-sm font-medium text-gray-900">Malzemeler</h4>
<span className="text-xs text-gray-500">{locationStockItems.length} çeşit</span>
</div>
{locationStockItems.length > 0 ? (
<div className="space-y-1">
{locationStockItems.slice(0, 3).map((item) => (
<div
key={item.id}
className="flex justify-between items-center text-xs"
>
<div key={item.id} className="flex justify-between items-center text-xs">
<span className="text-gray-700 truncate">
{item.material?.code || "N/A"}
{item.material?.code || 'N/A'}
</span>
<div className="flex items-center gap-2">
<span className="font-medium">{item.quantity}</span>
<span
className={`px-1.5 py-0.5 rounded-full text-xs ${getStockStatusColor(
item.status
item.status,
)}`}
>
{getStockStatusText(item.status)}
@ -145,22 +132,16 @@ const LocationTracking: React.FC = () => {
)}
</div>
) : (
<div className="text-xs text-gray-500 text-center py-2">
Malzeme bulunmuyor
</div>
<div className="text-xs text-gray-500 text-center py-2">Malzeme bulunmuyor</div>
)}
</div>
{/* Restrictions */}
{location.restrictions && location.restrictions.length > 0 && (
<div>
<h4 className="text-sm font-medium text-gray-900 mb-1">
Kısıtlamalar
</h4>
<h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
<div className="flex flex-wrap gap-1">
{location.restrictions
.slice(0, 2)
.map((restriction, index) => (
{location.restrictions.slice(0, 2).map((restriction, index) => (
<span
key={index}
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 className="text-xs text-gray-500">
Son hareket:{" "}
Son hareket:{' '}
{locationStockItems.length > 0
? new Date(
Math.max(
...locationStockItems.map((item) =>
item.lastMovementDate.getTime()
...locationStockItems.map((item) => item.lastMovementDate.getTime()),
),
).toLocaleDateString('tr-TR')
: 'N/A'}
</div>
</div>
</div>
)
)
).toLocaleDateString("tr-TR")
: "N/A"}
</div>
</div>
</div>
);
})}
</div>
);
)
const ListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
@ -242,11 +221,9 @@ const LocationTracking: React.FC = () => {
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredLocations.map((location) => {
const warehouse = mockWarehouses.find(
(w) => w.id === location.warehouseId
);
const locationStockItems = getLocationStockItems(location.id);
const utilization = getLocationUtilization(location.id);
const warehouse = mockWarehouses.find((w) => w.id === location.warehouseId)
const locationStockItems = getLocationStockItems(location.id)
const utilization = getLocationUtilization(location.id)
return (
<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" />
</div>
<div>
<div className="text-sm font-medium text-gray-900">
{location.name}
</div>
<div className="text-sm text-gray-500">
{location.locationCode}
</div>
<div className="text-sm font-medium text-gray-900">{location.name}</div>
<div className="text-sm text-gray-500">{location.locationCode}</div>
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900">
{warehouse?.name}
</div>
<div className="text-sm text-gray-500">
{warehouse?.code}
</div>
<div className="text-sm text-gray-900">{warehouse?.name}</div>
<div className="text-sm text-gray-500">{warehouse?.code}</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
@ -279,17 +248,15 @@ const LocationTracking: React.FC = () => {
<div
className={`h-2 rounded-full ${
utilization > 90
? "bg-red-500"
? 'bg-red-500'
: utilization > 70
? "bg-yellow-500"
: "bg-green-500"
? 'bg-yellow-500'
: 'bg-green-500'
}`}
style={{ width: `${utilization}%` }}
/>
</div>
<span className="text-sm text-gray-900">
%{Math.round(utilization)}
</span>
<span className="text-sm text-gray-900">%{Math.round(utilization)}</span>
</div>
<div className="text-xs text-gray-500">
{location.currentStock} / {location.capacity}
@ -302,12 +269,10 @@ const LocationTracking: React.FC = () => {
{locationStockItems.length > 0
? new Date(
Math.max(
...locationStockItems.map((item) =>
item.lastMovementDate.getTime()
)
)
).toLocaleDateString("tr-TR")
: "N/A"}
...locationStockItems.map((item) => item.lastMovementDate.getTime()),
),
).toLocaleDateString('tr-TR')
: 'N/A'}
</td>
<td className="px-3 py-2 whitespace-nowrap">
{location.isActive ? (
@ -331,44 +296,37 @@ const LocationTracking: React.FC = () => {
</button>
</td>
</tr>
);
)
})}
</tbody>
</table>
</div>
</div>
);
)
const LocationDetailModal = () => {
const location = mockLocations.find((l) => l.id === selectedLocation);
const locationStockItems = getLocationStockItems(selectedLocation);
const location = mockLocations.find((l) => l.id === selectedLocation)
const locationStockItems = getLocationStockItems(selectedLocation)
if (!selectedLocation || !location) return null;
if (!selectedLocation || !location) return null
return (
<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="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="bg-white px-4 pt-4 pb-4 sm:p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-medium text-gray-900">
{location.name} - Detaylar
</h3>
<h3 className="text-lg font-medium text-gray-900">{location.name} - Detaylar</h3>
<button
onClick={() => setSelectedLocation("")}
onClick={() => setSelectedLocation('')}
className="text-gray-400 hover:text-gray-600"
>
<svg
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
@ -382,9 +340,7 @@ const LocationTracking: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Location Info */}
<div className="bg-gray-50 rounded-lg p-3">
<h4 className="font-medium text-gray-900 mb-3">
Lokasyon Bilgileri
</h4>
<h4 className="font-medium text-gray-900 mb-3">Lokasyon Bilgileri</h4>
<div className="space-y-2 text-sm">
<div>
<strong>Kod:</strong> {location.locationCode}
@ -399,14 +355,12 @@ const LocationTracking: React.FC = () => {
<strong>Kapasite:</strong> {location.capacity} birim
</div>
<div>
<strong>Mevcut Stok:</strong> {location.currentStock}{" "}
birim
<strong>Mevcut Stok:</strong> {location.currentStock} birim
</div>
{location.dimensions && (
<div>
<strong>Boyutlar:</strong> {location.dimensions.length}x
{location.dimensions.width}x{location.dimensions.height}
m
{location.dimensions.width}x{location.dimensions.height}m
</div>
)}
</div>
@ -419,22 +373,15 @@ const LocationTracking: React.FC = () => {
</h4>
<div className="space-y-2 max-h-64 overflow-y-auto">
{locationStockItems.map((item) => (
<div
key={item.id}
className="bg-white rounded p-3 border"
>
<div key={item.id} className="bg-white rounded p-3 border">
<div className="flex justify-between items-start mb-2">
<div>
<div className="font-medium text-sm">
{item.material?.code}
</div>
<div className="text-xs text-gray-500">
{item.material?.code}
</div>
<div className="font-medium text-sm">{item.material?.code}</div>
<div className="text-xs text-gray-500">{item.material?.code}</div>
</div>
<span
className={`px-2 py-1 rounded-full text-xs ${getStockStatusColor(
item.status
item.status,
)}`}
>
{getStockStatusText(item.status)}
@ -450,11 +397,10 @@ const LocationTracking: React.FC = () => {
<div>
Rezerve: {item.reservedQuantity} {item.unitId}
</div>
<div>Lot: {item.lotNumber || "N/A"}</div>
<div>Lot: {item.lotNumber || 'N/A'}</div>
</div>
<div className="mt-2 text-xs text-gray-500">
Son hareket:{" "}
{item.lastMovementDate.toLocaleDateString("tr-TR")}
Son hareket: {item.lastMovementDate.toLocaleDateString('tr-TR')}
</div>
</div>
))}
@ -470,38 +416,35 @@ const LocationTracking: React.FC = () => {
</div>
</div>
</div>
);
};
)
}
return (
<div className="space-y-4 pt-2">
<Container>
<div className="space-y-2">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-gray-900">
Raf/Lokasyon Bazlı Takip
</h2>
<p className="text-gray-600">
Lokasyonlardaki stok durumunu takip edin
</p>
<h2 className="text-2xl font-bold text-gray-900">Raf/Lokasyon Bazlı Takip</h2>
<p className="text-gray-600">Lokasyonlardaki stok durumunu takip edin</p>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => setViewMode("grid")}
onClick={() => setViewMode('grid')}
className={`p-1.5 rounded-lg ${
viewMode === "grid"
? "bg-blue-100 text-blue-600"
: "text-gray-400 hover:text-gray-600"
viewMode === 'grid'
? 'bg-blue-100 text-blue-600'
: 'text-gray-400 hover:text-gray-600'
}`}
>
<FaTh className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode("list")}
onClick={() => setViewMode('list')}
className={`p-1.5 rounded-lg ${
viewMode === "list"
? "bg-blue-100 text-blue-600"
: "text-gray-400 hover:text-gray-600"
viewMode === 'list'
? 'bg-blue-100 text-blue-600'
: 'text-gray-400 hover:text-gray-600'
}`}
>
<FaList className="w-4 h-4" />
@ -536,12 +479,13 @@ const LocationTracking: React.FC = () => {
</div>
{/* Content */}
{viewMode === "grid" ? <GridView /> : <ListView />}
{viewMode === 'grid' ? <GridView /> : <ListView />}
</div>
{/* Location Detail Modal */}
<LocationDetailModal />
</div>
);
};
</Container>
)
}
export default LocationTracking;
export default LocationTracking

View file

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

View file

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

View file

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

View file

@ -1,24 +1,24 @@
import React from "react";
import { useFormik } from "formik";
import * as Yup from "yup";
import { FaSave, FaTimes } from "react-icons/fa";
import { SerialStatusEnum, MmSerialNumber, MmMaterial } from "../../../types/mm";
import React from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { FaSave, FaTimes } from 'react-icons/fa'
import { SerialStatusEnum, MmSerialNumber, MmMaterial } from '../../../types/mm'
export interface SerialFormProps {
isOpen: boolean;
onClose: () => void;
onSave: (serial: MmSerialNumber) => void;
onUpdate?: (serial: MmSerialNumber) => void;
materials: MmMaterial[];
lots?: { id: string; label: string }[];
initial?: MmSerialNumber | null;
mode?: "create" | "edit" | "view";
isOpen: boolean
onClose: () => void
onSave: (serial: MmSerialNumber) => void
onUpdate?: (serial: MmSerialNumber) => void
materials: MmMaterial[]
lots?: { id: string; label: string }[]
initial?: MmSerialNumber | null
mode?: 'create' | 'edit' | 'view'
}
const validationSchema = Yup.object({
materialId: Yup.string().required("Malzeme seçimi zorunlu"),
serialNumber: Yup.string().required("Seri numarası zorunlu"),
});
materialId: Yup.string().required('Malzeme seçimi zorunlu'),
serialNumber: Yup.string().required('Seri numarası zorunlu'),
})
const SerialForm: React.FC<SerialFormProps> = ({
isOpen,
@ -28,16 +28,16 @@ const SerialForm: React.FC<SerialFormProps> = ({
materials,
lots = [],
initial = null,
mode = "create",
mode = 'create',
}) => {
const formik = useFormik({
initialValues: {
materialId: "",
serialNumber: "",
lotId: lots.length ? lots[0].id : "",
productionDate: "",
warrantyExpiryDate: "",
currentLocationId: "",
materialId: '',
serialNumber: '',
lotId: lots.length ? lots[0].id : '',
productionDate: '',
warrantyExpiryDate: '',
currentLocationId: '',
status: SerialStatusEnum.Available,
isActive: true,
},
@ -48,51 +48,49 @@ const SerialForm: React.FC<SerialFormProps> = ({
materialId: values.materialId,
serialNumber: values.serialNumber,
lotId: values.lotId || undefined,
productionDate: values.productionDate
? new Date(values.productionDate)
: new Date(),
productionDate: values.productionDate ? new Date(values.productionDate) : new Date(),
warrantyExpiryDate: values.warrantyExpiryDate
? new Date(values.warrantyExpiryDate)
: undefined,
currentLocationId: values.currentLocationId || undefined,
status: values.status,
isActive: !!values.isActive,
};
}
// simulate API
await new Promise((r) => setTimeout(r, 300));
if (mode === "edit" && onUpdate) {
onUpdate(newSerial);
await new Promise((r) => setTimeout(r, 300))
if (mode === 'edit' && onUpdate) {
onUpdate(newSerial)
} else {
onSave(newSerial);
onSave(newSerial)
}
onClose();
onClose()
},
});
})
// sync initial values when editing/viewing
React.useEffect(() => {
if (initial) {
const src = initial;
const src = initial
formik.setValues({
materialId: src.materialId || "",
serialNumber: src.serialNumber || "",
lotId: src.lotId || (lots.length ? lots[0].id : ""),
materialId: src.materialId || '',
serialNumber: src.serialNumber || '',
lotId: src.lotId || (lots.length ? lots[0].id : ''),
productionDate: src.productionDate
? new Date(src.productionDate).toISOString().slice(0, 10)
: "",
: '',
warrantyExpiryDate: src.warrantyExpiryDate
? new Date(src.warrantyExpiryDate).toISOString().slice(0, 10)
: "",
currentLocationId: src.currentLocationId || "",
: '',
currentLocationId: src.currentLocationId || '',
status: src.status || SerialStatusEnum.Available,
isActive: !!src.isActive,
});
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initial]);
}, [initial])
if (!isOpen) return null;
if (!isOpen) return null
return (
<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>
<h2 className="text-xl font-bold text-gray-900">
{mode === "create"
? "Yeni Seri Kaydı"
: mode === "edit"
? "Seri Düzenle"
: "Seri Detayı"}
{mode === 'create'
? 'Yeni Seri Kaydı'
: mode === 'edit'
? 'Seri Düzenle'
: 'Seri Detayı'}
</h2>
<p className="text-sm text-gray-600">
{mode === "create"
? "Seri numarası girin"
: mode === "edit"
? "Mevcut seri bilgisini güncelleyin"
: "Seri bilgileri (sadece gösterim)"}
{mode === 'create'
? 'Seri numarası girin'
: mode === 'edit'
? 'Mevcut seri bilgisini güncelleyin'
: 'Seri bilgileri (sadece gösterim)'}
</p>
</div>
<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
</button>
{mode !== "view" && (
{mode !== 'view' && (
<button
type="submit"
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>
<label className="block text-sm font-medium text-gray-700">
Malzeme *
</label>
<label className="block text-sm font-medium text-gray-700">Malzeme *</label>
<select
{...formik.getFieldProps("materialId")}
disabled={mode === "view"}
{...formik.getFieldProps('materialId')}
disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm"
>
<option value="">Seçiniz...</option>
@ -155,24 +151,20 @@ const SerialForm: React.FC<SerialFormProps> = ({
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Seri Numarası *
</label>
<label className="block text-sm font-medium text-gray-700">Seri Numarası *</label>
<input
type="text"
{...formik.getFieldProps("serialNumber")}
disabled={mode === "view"}
{...formik.getFieldProps('serialNumber')}
disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Lot
</label>
<label className="block text-sm font-medium text-gray-700">Lot</label>
<select
{...formik.getFieldProps("lotId")}
disabled={mode === "view"}
{...formik.getFieldProps('lotId')}
disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm"
>
<option value="">Seçiniz...</option>
@ -185,36 +177,30 @@ const SerialForm: React.FC<SerialFormProps> = ({
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Üretim Tarihi
</label>
<label className="block text-sm font-medium text-gray-700">Üretim Tarihi</label>
<input
type="date"
{...formik.getFieldProps("productionDate")}
disabled={mode === "view"}
{...formik.getFieldProps('productionDate')}
disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Garanti Bitiş
</label>
<label className="block text-sm font-medium text-gray-700">Garanti Bitiş</label>
<input
type="date"
{...formik.getFieldProps("warrantyExpiryDate")}
disabled={mode === "view"}
{...formik.getFieldProps('warrantyExpiryDate')}
disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Durum
</label>
<label className="block text-sm font-medium text-gray-700">Durum</label>
<select
{...formik.getFieldProps("status")}
disabled={mode === "view"}
{...formik.getFieldProps('status')}
disabled={mode === 'view'}
className="w-full border rounded-md px-2 py-1 text-sm"
>
<option value={SerialStatusEnum.Available}>Müsait</option>
@ -227,7 +213,7 @@ const SerialForm: React.FC<SerialFormProps> = ({
</form>
</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 {
WmWarehouse,
WarehouseTypeEnum,
WmZone,
WmLocation,
} from "../../../types/wm";
import React, { useState } from 'react'
import { WmWarehouse, WarehouseTypeEnum, WmZone, WmLocation } from '../../../types/wm'
import {
FaPlus,
FaSearch,
@ -18,101 +13,91 @@ import {
FaEye,
FaTh,
FaList,
} from "react-icons/fa";
import { WarehouseModal, ZoneModal, LocationModal } from "./modals";
import { mockWarehouses } from "../../../mocks/mockWarehouses";
import { mockZones } from "../../../mocks/mockZones";
import { mockLocations } from "../../../mocks/mockLocations";
} from 'react-icons/fa'
import { WarehouseModal, ZoneModal, LocationModal } from './modals'
import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { mockZones } from '../../../mocks/mockZones'
import { mockLocations } from '../../../mocks/mockLocations'
import {
getWarehouseTypeColor,
getWarehouseTypeText,
getZoneTypeText,
getLocationTypeText,
} from "../../../utils/erp";
} from '../../../utils/erp'
import { Container } from '@/components/shared'
const WarehouseDefinitions: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [selectedType, setSelectedType] = useState<WarehouseTypeEnum | "">("");
const [activeTab, setActiveTab] = useState<
"warehouses" | "zones" | "locations"
>("warehouses");
const [selectedWarehouse, setSelectedWarehouse] = useState<string>("");
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
const [searchTerm, setSearchTerm] = useState('')
const [selectedType, setSelectedType] = useState<WarehouseTypeEnum | ''>('')
const [activeTab, setActiveTab] = useState<'warehouses' | 'zones' | 'locations'>('warehouses')
const [selectedWarehouse, setSelectedWarehouse] = useState<string>('')
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
// Modal states
const [warehouseModal, setWarehouseModal] = useState<{
isOpen: boolean;
mode: "create" | "edit" | "view";
warehouse: WmWarehouse | null;
isOpen: boolean
mode: 'create' | 'edit' | 'view'
warehouse: WmWarehouse | null
}>({
isOpen: false,
mode: "create",
mode: 'create',
warehouse: null,
});
})
const [zoneModal, setZoneModal] = useState<{
isOpen: boolean;
mode: "create" | "edit" | "view";
zone: WmZone | null;
isOpen: boolean
mode: 'create' | 'edit' | 'view'
zone: WmZone | null
}>({
isOpen: false,
mode: "create",
mode: 'create',
zone: null,
});
})
const [locationModal, setLocationModal] = useState<{
isOpen: boolean;
mode: "create" | "edit" | "view";
location: WmLocation | null;
isOpen: boolean
mode: 'create' | 'edit' | 'view'
location: WmLocation | null
}>({
isOpen: false,
mode: "create",
mode: 'create',
location: null,
});
})
// 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
const handleWarehouseAction = (
mode: "create" | "edit" | "view",
warehouse?: WmWarehouse
) => {
const handleWarehouseAction = (mode: 'create' | 'edit' | 'view', warehouse?: WmWarehouse) => {
setWarehouseModal({
isOpen: true,
mode,
warehouse: warehouse || null,
});
};
})
}
const handleZoneAction = (
mode: "create" | "edit" | "view",
zone?: WmZone
) => {
const handleZoneAction = (mode: 'create' | 'edit' | 'view', zone?: WmZone) => {
setZoneModal({
isOpen: true,
mode,
zone: zone || null,
});
};
})
}
const handleLocationAction = (
mode: "create" | "edit" | "view",
location?: WmLocation
) => {
const handleLocationAction = (mode: 'create' | 'edit' | 'view', location?: WmLocation) => {
setLocationModal({
isOpen: true,
mode,
location: location || null,
});
};
})
}
const handleWarehouseSave = (warehouseData: Partial<WmWarehouse>) => {
if (warehouseModal.mode === "create") {
if (warehouseModal.mode === 'create') {
const newWarehouse: WmWarehouse = {
...warehouseData,
id: Date.now().toString(),
@ -120,9 +105,9 @@ const WarehouseDefinitions: React.FC = () => {
zones: [],
creationTime: new Date(),
lastModificationTime: new Date(),
} as WmWarehouse;
setWarehouses([...warehouses, newWarehouse]);
} else if (warehouseModal.mode === "edit" && warehouseModal.warehouse) {
} as WmWarehouse
setWarehouses([...warehouses, newWarehouse])
} else if (warehouseModal.mode === 'edit' && warehouseModal.warehouse) {
setWarehouses(
warehouses.map((w) =>
w.id === warehouseModal.warehouse!.id
@ -131,91 +116,82 @@ const WarehouseDefinitions: React.FC = () => {
...warehouseData,
lastModificationTime: new Date(),
} as WmWarehouse)
: w
: w,
),
)
);
}
};
}
const handleZoneSave = (zoneData: Partial<WmZone>) => {
if (zoneModal.mode === "create") {
if (zoneModal.mode === 'create') {
const newZone: WmZone = {
...zoneData,
id: Date.now().toString(),
locations: [],
} as WmZone;
setZones([...zones, newZone]);
} else if (zoneModal.mode === "edit" && zoneModal.zone) {
} as WmZone
setZones([...zones, newZone])
} else if (zoneModal.mode === 'edit' && zoneModal.zone) {
setZones(
zones.map((z) =>
z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z
zones.map((z) => (z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z)),
)
);
}
};
}
const handleLocationSave = (locationData: Partial<WmLocation>) => {
if (locationModal.mode === "create") {
if (locationModal.mode === 'create') {
const newLocation: WmLocation = {
...locationData,
id: Date.now().toString(),
stockItems: [],
} as WmLocation;
setLocations([...locations, newLocation]);
} else if (locationModal.mode === "edit" && locationModal.location) {
} as WmLocation
setLocations([...locations, newLocation])
} else if (locationModal.mode === 'edit' && locationModal.location) {
setLocations(
locations.map((l) =>
l.id === locationModal.location!.id
? ({ ...l, ...locationData } as WmLocation)
: l
l.id === locationModal.location!.id ? ({ ...l, ...locationData } as WmLocation) : l,
),
)
);
}
};
}
const handleDelete = (
type: "warehouse" | "zone" | "location",
id: string
) => {
const handleDelete = (type: 'warehouse' | 'zone' | 'location', id: string) => {
if (
window.confirm(
`Bu ${
type === "warehouse" ? "depo" : type === "zone" ? "bölge" : "lokasyon"
}u silmek istediğinizden emin misiniz?`
type === 'warehouse' ? 'depo' : type === 'zone' ? 'bölge' : 'lokasyon'
}u silmek istediğinizden emin misiniz?`,
)
) {
switch (type) {
case "warehouse":
setWarehouses(warehouses.filter((w) => w.id !== id));
break;
case "zone":
setZones(zones.filter((z) => z.id !== id));
break;
case "location":
setLocations(locations.filter((l) => l.id !== id));
break;
case 'warehouse':
setWarehouses(warehouses.filter((w) => w.id !== id))
break
case 'zone':
setZones(zones.filter((z) => z.id !== id))
break
case 'location':
setLocations(locations.filter((l) => l.id !== id))
break
}
}
}
};
// Filtered data
const filteredWarehouses = warehouses.filter((warehouse) => {
const matchesSearch =
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase());
const matchesType =
selectedType === "" || warehouse.warehouseType === selectedType;
return matchesSearch && matchesType;
});
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase())
const matchesType = selectedType === '' || warehouse.warehouseType === selectedType
return matchesSearch && matchesType
})
const filteredZones = zones.filter(
(zone) => selectedWarehouse === "" || zone.warehouseId === selectedWarehouse
);
(zone) => selectedWarehouse === '' || zone.warehouseId === selectedWarehouse,
)
const filteredLocations = locations.filter(
(location) =>
selectedWarehouse === "" || location.warehouseId === selectedWarehouse
);
(location) => selectedWarehouse === '' || location.warehouseId === selectedWarehouse,
)
// Warehouse Components
const WarehousesGridView = () => (
@ -231,9 +207,7 @@ const WarehouseDefinitions: React.FC = () => {
<FaBuilding className="w-4 h-4 text-blue-600" />
</div>
<div>
<h3 className="font-medium text-sm text-gray-900">
{warehouse.name}
</h3>
<h3 className="font-medium text-sm text-gray-900">{warehouse.name}</h3>
<p className="text-gray-600">{warehouse.code}</p>
</div>
</div>
@ -248,7 +222,7 @@ const WarehouseDefinitions: React.FC = () => {
<div>
<span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
warehouse.warehouseType
warehouse.warehouseType,
)}`}
>
{getWarehouseTypeText(warehouse.warehouseType)}
@ -278,18 +252,12 @@ const WarehouseDefinitions: React.FC = () => {
<div
className="bg-blue-600 h-2 rounded-full"
style={{
width: `${
(warehouse.currentUtilization / warehouse.capacity) * 100
}%`,
width: `${(warehouse.currentUtilization / warehouse.capacity) * 100}%`,
}}
></div>
</div>
<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>
@ -310,21 +278,21 @@ const WarehouseDefinitions: React.FC = () => {
</div>
<div className="flex items-center gap-1">
<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"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</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"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</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"
title="Sil"
>
@ -335,7 +303,7 @@ const WarehouseDefinitions: React.FC = () => {
</div>
))}
</div>
);
)
const WarehousesListView = () => (
<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">
{filteredWarehouses.map((warehouse) => {
const utilizationPercentage =
(warehouse.currentUtilization / warehouse.capacity) * 100;
(warehouse.currentUtilization / warehouse.capacity) * 100
return (
<tr key={warehouse.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap">
@ -386,16 +354,14 @@ const WarehouseDefinitions: React.FC = () => {
</span>
)}
</div>
<div className="text-xs text-gray-500">
{warehouse.code}
</div>
<div className="text-xs text-gray-500">{warehouse.code}</div>
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
warehouse.warehouseType
warehouse.warehouseType,
)}`}
>
{getWarehouseTypeText(warehouse.warehouseType)}
@ -405,9 +371,7 @@ const WarehouseDefinitions: React.FC = () => {
<div className="text-xs text-gray-900">
{warehouse.address?.city}, {warehouse.address?.state}
</div>
<div className="text-xs text-gray-500">
{warehouse.address?.street}
</div>
<div className="text-xs text-gray-500">{warehouse.address?.street}</div>
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{warehouse.capacity.toLocaleString()} birim
@ -418,10 +382,10 @@ const WarehouseDefinitions: React.FC = () => {
<div
className={`h-2 rounded-full ${
utilizationPercentage > 90
? "bg-red-500"
? 'bg-red-500'
: utilizationPercentage > 70
? "bg-yellow-500"
: "bg-blue-500"
? 'bg-yellow-500'
: 'bg-blue-500'
}`}
style={{ width: `${utilizationPercentage}%` }}
></div>
@ -431,7 +395,7 @@ const WarehouseDefinitions: React.FC = () => {
</span>
</div>
<div className="text-xs text-gray-500">
{warehouse.currentUtilization.toLocaleString()} /{" "}
{warehouse.currentUtilization.toLocaleString()} /{' '}
{warehouse.capacity.toLocaleString()}
</div>
</td>
@ -451,21 +415,21 @@ const WarehouseDefinitions: React.FC = () => {
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2">
<button
onClick={() => handleWarehouseAction("view", warehouse)}
onClick={() => handleWarehouseAction('view', warehouse)}
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleWarehouseAction("edit", warehouse)}
onClick={() => handleWarehouseAction('edit', warehouse)}
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete("warehouse", warehouse.id)}
onClick={() => handleDelete('warehouse', warehouse.id)}
className="text-red-600 hover:text-red-900"
title="Sil"
>
@ -474,13 +438,13 @@ const WarehouseDefinitions: React.FC = () => {
</div>
</td>
</tr>
);
)
})}
</tbody>
</table>
</div>
</div>
);
)
const WarehousesTab = () => (
<div className="space-y-4 pt-2">
@ -498,9 +462,7 @@ const WarehouseDefinitions: React.FC = () => {
</div>
<select
value={selectedType}
onChange={(e) =>
setSelectedType(e.target.value as WarehouseTypeEnum | "")
}
onChange={(e) => setSelectedType(e.target.value as WarehouseTypeEnum | '')}
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Tüm Tipler</option>
@ -511,7 +473,7 @@ const WarehouseDefinitions: React.FC = () => {
))}
</select>
<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"
>
<FaPlus className="w-4 h-4" />
@ -520,15 +482,15 @@ const WarehouseDefinitions: React.FC = () => {
</div>
{/* Content based on view mode */}
{viewMode === "grid" ? <WarehousesGridView /> : <WarehousesListView />}
{viewMode === 'grid' ? <WarehousesGridView /> : <WarehousesListView />}
</div>
);
)
// Zone Components
const ZonesGridView = () => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{filteredZones.map((zone) => {
const warehouse = warehouses.find((w) => w.id === zone.warehouseId);
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
return (
<div
key={zone.id}
@ -540,9 +502,7 @@ const WarehouseDefinitions: React.FC = () => {
<FaLayerGroup className="w-4 h-4 text-purple-600" />
</div>
<div>
<h3 className="font-medium text-sm text-gray-900">
{zone.name}
</h3>
<h3 className="font-medium text-sm text-gray-900">{zone.name}</h3>
<p className="text-gray-600">{zone.zoneCode}</p>
</div>
</div>
@ -564,13 +524,9 @@ const WarehouseDefinitions: React.FC = () => {
{(zone.temperature || zone.humidity) && (
<div className="bg-gray-50 rounded-lg p-2">
<h4 className="text-xs font-medium text-gray-900 mb-1">
Çevresel Koşullar
</h4>
<h4 className="text-xs font-medium text-gray-900 mb-1">Çevresel Koşullar</h4>
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
{zone.temperature && (
<div>Sıcaklık: {zone.temperature}°C</div>
)}
{zone.temperature && <div>Sıcaklık: {zone.temperature}°C</div>}
{zone.humidity && <div>Nem: %{zone.humidity}</div>}
</div>
</div>
@ -593,21 +549,21 @@ const WarehouseDefinitions: React.FC = () => {
</div>
<div className="flex items-center gap-2">
<button
onClick={() => handleZoneAction("view", zone)}
onClick={() => handleZoneAction('view', zone)}
className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleZoneAction("edit", zone)}
onClick={() => handleZoneAction('edit', zone)}
className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete("zone", zone.id)}
onClick={() => handleDelete('zone', zone.id)}
className="p-1 text-gray-400 hover:text-red-600"
title="Sil"
>
@ -616,10 +572,10 @@ const WarehouseDefinitions: React.FC = () => {
</div>
</div>
</div>
);
)
})}
</div>
);
)
const ZonesListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
@ -652,9 +608,7 @@ const WarehouseDefinitions: React.FC = () => {
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredZones.map((zone) => {
const warehouse = warehouses.find(
(w) => w.id === zone.warehouseId
);
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
return (
<tr key={zone.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap">
@ -663,22 +617,14 @@ const WarehouseDefinitions: React.FC = () => {
<FaLayerGroup className="w-4 h-4 text-purple-600" />
</div>
<div>
<div className="text-sm font-medium text-gray-900">
{zone.name}
</div>
<div className="text-sm text-gray-500">
{zone.zoneCode}
</div>
<div className="text-sm font-medium text-gray-900">{zone.name}</div>
<div className="text-sm text-gray-500">{zone.zoneCode}</div>
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900">
{warehouse?.name}
</div>
<div className="text-sm text-gray-500">
{warehouse?.code}
</div>
<div className="text-sm text-gray-900">{warehouse?.name}</div>
<div className="text-sm text-gray-500">{warehouse?.code}</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
@ -686,10 +632,10 @@ const WarehouseDefinitions: React.FC = () => {
</span>
</td>
<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 className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{zone.humidity ? `%${zone.humidity}` : "-"}
{zone.humidity ? `%${zone.humidity}` : '-'}
</td>
<td className="px-3 py-2 whitespace-nowrap">
{zone.isActive ? (
@ -707,21 +653,21 @@ const WarehouseDefinitions: React.FC = () => {
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2">
<button
onClick={() => handleZoneAction("view", zone)}
onClick={() => handleZoneAction('view', zone)}
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleZoneAction("edit", zone)}
onClick={() => handleZoneAction('edit', zone)}
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete("zone", zone.id)}
onClick={() => handleDelete('zone', zone.id)}
className="text-red-600 hover:text-red-900"
title="Sil"
>
@ -730,13 +676,13 @@ const WarehouseDefinitions: React.FC = () => {
</div>
</td>
</tr>
);
)
})}
</tbody>
</table>
</div>
</div>
);
)
const ZonesTab = () => (
<div className="space-y-4 pt-2">
@ -755,7 +701,7 @@ const WarehouseDefinitions: React.FC = () => {
))}
</select>
<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"
>
<FaPlus className="w-4 h-4" />
@ -764,18 +710,17 @@ const WarehouseDefinitions: React.FC = () => {
</div>
{/* Content based on view mode */}
{viewMode === "grid" ? <ZonesGridView /> : <ZonesListView />}
{viewMode === 'grid' ? <ZonesGridView /> : <ZonesListView />}
</div>
);
)
// Location Components
const LocationsGridView = () => (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredLocations.map((location) => {
const warehouse = warehouses.find((w) => w.id === location.warehouseId);
const zone = zones.find((z) => z.id === location.zoneId);
const utilizationPercentage =
(location.currentStock / location.capacity) * 100;
const warehouse = warehouses.find((w) => w.id === location.warehouseId)
const zone = zones.find((z) => z.id === location.zoneId)
const utilizationPercentage = (location.currentStock / location.capacity) * 100
return (
<div
@ -789,9 +734,7 @@ const WarehouseDefinitions: React.FC = () => {
</div>
<div>
<h3 className="font-medium text-gray-900">{location.name}</h3>
<p className="text-sm text-gray-500">
{location.locationCode}
</p>
<p className="text-sm text-gray-500">{location.locationCode}</p>
</div>
</div>
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
@ -815,9 +758,7 @@ const WarehouseDefinitions: React.FC = () => {
{/* Dimensions */}
{location.dimensions && (
<div className="bg-gray-50 rounded-lg p-2 mt-2">
<h4 className="text-sm font-medium text-gray-900 mb-2">
Boyutlar
</h4>
<h4 className="text-sm font-medium text-gray-900 mb-2">Boyutlar</h4>
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
<div>Uzunluk: {location.dimensions.length}m</div>
<div>Genişlik: {location.dimensions.width}m</div>
@ -842,10 +783,10 @@ const WarehouseDefinitions: React.FC = () => {
<div
className={`h-2 rounded-full ${
utilizationPercentage > 90
? "bg-red-500"
? 'bg-red-500'
: utilizationPercentage > 70
? "bg-yellow-500"
: "bg-green-500"
? 'bg-yellow-500'
: 'bg-green-500'
}`}
style={{ width: `${utilizationPercentage}%` }}
></div>
@ -858,9 +799,7 @@ const WarehouseDefinitions: React.FC = () => {
{/* Restrictions */}
{location.restrictions && location.restrictions.length > 0 && (
<div className="pt-2">
<h4 className="text-sm font-medium text-gray-900 mb-1">
Kısıtlamalar
</h4>
<h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
<div className="flex flex-wrap gap-1">
{location.restrictions.map((restriction, index) => (
<span
@ -891,21 +830,21 @@ const WarehouseDefinitions: React.FC = () => {
</div>
<div className="flex items-center gap-2">
<button
onClick={() => handleLocationAction("view", location)}
onClick={() => handleLocationAction('view', location)}
className="p-1 text-gray-400 hover:text-green-600"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleLocationAction("edit", location)}
onClick={() => handleLocationAction('edit', location)}
className="p-1 text-gray-400 hover:text-blue-600"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete("location", location.id)}
onClick={() => handleDelete('location', location.id)}
className="p-1 text-gray-400 hover:text-red-600"
title="Sil"
>
@ -914,10 +853,10 @@ const WarehouseDefinitions: React.FC = () => {
</div>
</div>
</div>
);
)
})}
</div>
);
)
const LocationsListView = () => (
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
@ -953,12 +892,9 @@ const WarehouseDefinitions: React.FC = () => {
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredLocations.map((location) => {
const warehouse = warehouses.find(
(w) => w.id === location.warehouseId
);
const zone = zones.find((z) => z.id === location.zoneId);
const utilizationPercentage =
(location.currentStock / location.capacity) * 100;
const warehouse = warehouses.find((w) => w.id === location.warehouseId)
const zone = zones.find((z) => z.id === location.zoneId)
const utilizationPercentage = (location.currentStock / location.capacity) * 100
return (
<tr key={location.id} className="hover:bg-gray-50">
@ -968,30 +904,18 @@ const WarehouseDefinitions: React.FC = () => {
<FaMapMarkerAlt className="w-4 h-4 text-orange-600" />
</div>
<div>
<div className="text-sm font-medium text-gray-900">
{location.name}
</div>
<div className="text-sm text-gray-500">
{location.locationCode}
</div>
<div className="text-sm font-medium text-gray-900">{location.name}</div>
<div className="text-sm text-gray-500">{location.locationCode}</div>
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900">
{warehouse?.name}
</div>
<div className="text-sm text-gray-500">
{warehouse?.code}
</div>
<div className="text-sm text-gray-900">{warehouse?.name}</div>
<div className="text-sm text-gray-500">{warehouse?.code}</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="text-sm text-gray-900">
{zone?.name || "-"}
</div>
<div className="text-sm text-gray-500">
{zone?.zoneCode || "-"}
</div>
<div className="text-sm text-gray-900">{zone?.name || '-'}</div>
<div className="text-sm text-gray-500">{zone?.zoneCode || '-'}</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
@ -1007,10 +931,10 @@ const WarehouseDefinitions: React.FC = () => {
<div
className={`h-2 rounded-full ${
utilizationPercentage > 90
? "bg-red-500"
? 'bg-red-500'
: utilizationPercentage > 70
? "bg-yellow-500"
: "bg-green-500"
? 'bg-yellow-500'
: 'bg-green-500'
}`}
style={{ width: `${utilizationPercentage}%` }}
></div>
@ -1039,21 +963,21 @@ const WarehouseDefinitions: React.FC = () => {
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2">
<button
onClick={() => handleLocationAction("view", location)}
onClick={() => handleLocationAction('view', location)}
className="text-green-600 hover:text-green-900"
title="Görüntüle"
>
<FaEye className="w-4 h-4" />
</button>
<button
onClick={() => handleLocationAction("edit", location)}
onClick={() => handleLocationAction('edit', location)}
className="text-blue-600 hover:text-blue-900"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete("location", location.id)}
onClick={() => handleDelete('location', location.id)}
className="text-red-600 hover:text-red-900"
title="Sil"
>
@ -1062,13 +986,13 @@ const WarehouseDefinitions: React.FC = () => {
</div>
</td>
</tr>
);
)
})}
</tbody>
</table>
</div>
</div>
);
)
const LocationsTab = () => (
<div className="space-y-4 pt-2">
@ -1087,7 +1011,7 @@ const WarehouseDefinitions: React.FC = () => {
))}
</select>
<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"
>
<FaPlus className="w-4 h-4" />
@ -1096,42 +1020,39 @@ const WarehouseDefinitions: React.FC = () => {
</div>
{/* Content based on view mode */}
{viewMode === "grid" ? <LocationsGridView /> : <LocationsListView />}
{viewMode === 'grid' ? <LocationsGridView /> : <LocationsListView />}
</div>
);
)
return (
<div className="space-y-4 pt-2">
<Container>
<div className="space-y-2">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-gray-900">Depo Tanımları</h2>
<p className="text-gray-600">
Depolar, bölgeler ve lokasyonları yönetin
</p>
<p className="text-gray-600">Depolar, bölgeler ve lokasyonları yönetin</p>
</div>
{/* View Toggle Buttons */}
{(activeTab === "warehouses" ||
activeTab === "zones" ||
activeTab === "locations") && (
{(activeTab === 'warehouses' || activeTab === 'zones' || activeTab === 'locations') && (
<div className="flex items-center gap-2">
<button
onClick={() => setViewMode("grid")}
onClick={() => setViewMode('grid')}
className={`p-1.5 rounded-lg ${
viewMode === "grid"
? "bg-blue-100 text-blue-600"
: "text-gray-400 hover:text-gray-600"
viewMode === 'grid'
? 'bg-blue-100 text-blue-600'
: 'text-gray-400 hover:text-gray-600'
}`}
title="Kart Görünümü"
>
<FaTh className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode("list")}
onClick={() => setViewMode('list')}
className={`p-1.5 rounded-lg ${
viewMode === "list"
? "bg-blue-100 text-blue-600"
: "text-gray-400 hover:text-gray-600"
viewMode === 'list'
? 'bg-blue-100 text-blue-600'
: 'text-gray-400 hover:text-gray-600'
}`}
title="Liste Görünümü"
>
@ -1145,11 +1066,11 @@ const WarehouseDefinitions: React.FC = () => {
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8" aria-label="Tabs">
<button
onClick={() => setActiveTab("warehouses")}
onClick={() => setActiveTab('warehouses')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === "warehouses"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
activeTab === 'warehouses'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<div className="flex items-center gap-2">
@ -1158,11 +1079,11 @@ const WarehouseDefinitions: React.FC = () => {
</div>
</button>
<button
onClick={() => setActiveTab("zones")}
onClick={() => setActiveTab('zones')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === "zones"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
activeTab === 'zones'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<div className="flex items-center gap-2">
@ -1171,11 +1092,11 @@ const WarehouseDefinitions: React.FC = () => {
</div>
</button>
<button
onClick={() => setActiveTab("locations")}
onClick={() => setActiveTab('locations')}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === "locations"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
activeTab === 'locations'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<div className="flex items-center gap-2">
@ -1187,9 +1108,10 @@ const WarehouseDefinitions: React.FC = () => {
</div>
{/* Tab Content */}
{activeTab === "warehouses" && <WarehousesTab />}
{activeTab === "zones" && <ZonesTab />}
{activeTab === "locations" && <LocationsTab />}
{activeTab === 'warehouses' && <WarehousesTab />}
{activeTab === 'zones' && <ZonesTab />}
{activeTab === 'locations' && <LocationsTab />}
</div>
{/* Modals */}
<WarehouseModal
@ -1218,8 +1140,8 @@ const WarehouseDefinitions: React.FC = () => {
warehouses={warehouses}
zones={zones}
/>
</div>
);
};
</Container>
)
}
export default WarehouseDefinitions;
export default WarehouseDefinitions

View file

@ -1,42 +1,36 @@
import React, { useState, useEffect, useCallback } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
FaSave,
FaTimes,
FaBox,
FaMapMarkerAlt,
FaLayerGroup,
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";
import React, { useState, useEffect, useCallback } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { FaSave, FaTimes, FaBox, FaMapMarkerAlt, FaLayerGroup, 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'
import { Container } from '@/components/shared'
interface ValidationErrors {
[key: string]: string;
[key: string]: string
}
const WarehouseForm: React.FC = () => {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
const isEdit = Boolean(id);
const navigate = useNavigate()
const { id } = useParams<{ id: string }>()
const isEdit = Boolean(id)
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
const [errors, setErrors] = useState<ValidationErrors>({});
const [loading, setLoading] = useState(false)
const [saving, setSaving] = useState(false)
const [errors, setErrors] = useState<ValidationErrors>({})
const [formData, setFormData] = useState<WmWarehouse>({
id: "",
code: "",
name: "",
description: "",
id: '',
code: '',
name: '',
description: '',
address: {
street: "",
city: "",
state: "",
postalCode: "",
country: "Türkiye",
street: '',
city: '',
state: '',
postalCode: '',
country: 'Türkiye',
},
warehouseType: WarehouseTypeEnum.RawMaterials,
locations: [],
@ -50,132 +44,129 @@ const WarehouseForm: React.FC = () => {
managerId: 0,
creationTime: new Date(),
lastModificationTime: new Date(),
});
})
const loadFormData = useCallback(async () => {
setLoading(true);
setLoading(true)
try {
if (isEdit && id) {
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000));
const warehouse = mockWarehouses.find((w) => w.id === id);
await new Promise((resolve) => setTimeout(resolve, 1000))
const warehouse = mockWarehouses.find((w) => w.id === id)
if (warehouse) {
setFormData(warehouse);
setFormData(warehouse)
}
}
} catch (error) {
console.error("Error loading form data:", error);
console.error('Error loading form data:', error)
} finally {
setLoading(false);
setLoading(false)
}
}, [isEdit, id]);
}, [isEdit, id])
useEffect(() => {
loadFormData();
}, [loadFormData]);
loadFormData()
}, [loadFormData])
const validateForm = (): boolean => {
const newErrors: ValidationErrors = {};
const newErrors: ValidationErrors = {}
if (!formData.code.trim()) {
newErrors.code = "Depo kodu zorunludur";
newErrors.code = 'Depo kodu zorunludur'
}
if (!formData.name.trim()) {
newErrors.name = "Depo adı zorunludur";
newErrors.name = 'Depo adı zorunludur'
}
if (!formData.warehouseType) {
newErrors.warehouseType = "Depo tipi seçilmelidir";
newErrors.warehouseType = 'Depo tipi seçilmelidir'
}
if (!formData.address?.street.trim()) {
newErrors["address.street"] = "Adres zorunludur";
newErrors['address.street'] = 'Adres zorunludur'
}
if (!formData.address?.city.trim()) {
newErrors["address.city"] = "Şehir zorunludur";
newErrors['address.city'] = 'Şehir zorunludur'
}
if (!formData.address?.country.trim()) {
newErrors["address.country"] = "Ülke zorunludur";
newErrors['address.country'] = 'Ülke zorunludur'
}
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);
return Object.keys(newErrors).length === 0;
};
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
const handleInputChange = (field: string, value: any) => {
setFormData((prev) => {
const updated = { ...prev };
const keys = field.split(".");
let current: any = updated;
const updated = { ...prev }
const keys = field.split('.')
let current: any = updated
keys.forEach((key, index) => {
if (index === keys.length - 1) {
current[key] = value;
current[key] = value
} else {
current[key] = { ...current[key] };
current = current[key];
current[key] = { ...current[key] }
current = current[key]
}
});
return updated;
});
})
return updated
})
if (errors[field]) {
setErrors((prev) => ({ ...prev, [field]: "" }));
setErrors((prev) => ({ ...prev, [field]: '' }))
}
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
e.preventDefault()
if (!validateForm()) {
return;
return
}
setSaving(true);
setSaving(true)
try {
// 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,
id: isEdit ? id : undefined,
});
})
// Show success message
alert(
isEdit ? "Depo başarıyla güncellendi!" : "Depo başarıyla oluşturuldu!"
);
alert(isEdit ? 'Depo başarıyla güncellendi!' : 'Depo başarıyla oluşturuldu!')
// Navigate back to list
navigate("/admin/warehouse");
navigate('/admin/warehouse')
} catch (error) {
console.error("Error saving warehouse:", error);
alert("Bir hata oluştu. Lütfen tekrar deneyin.");
console.error('Error saving warehouse:', error)
alert('Bir hata oluştu. Lütfen tekrar deneyin.')
} finally {
setSaving(false);
setSaving(false)
}
}
};
const handleCancel = () => {
navigate("/admin/warehouse");
};
navigate('/admin/warehouse')
}
if (loading) {
return <LoadingSpinner />;
return <LoadingSpinner />
}
return (
<div className="space-y-4 pt-2">
<Container>
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-semibold text-gray-900">
{isEdit ? "Depo Düzenle" : "Yeni Depo"}
{isEdit ? 'Depo Düzenle' : 'Yeni Depo'}
</h2>
<p className="text-sm text-gray-600 mt-1">
{isEdit
? "Mevcut depo bilgilerini güncelleyin"
: "Yeni depo bilgilerini girin"}
{isEdit ? 'Mevcut depo bilgilerini güncelleyin' : 'Yeni depo bilgilerini girin'}
</p>
</div>
</div>
@ -200,17 +191,15 @@ const WarehouseForm: React.FC = () => {
<input
type="text"
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 ${
errors.code
? "border-red-300 focus:border-red-500 focus:ring-red-500"
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`}
placeholder="Örn: WH001"
/>
{errors.code && (
<p className="mt-1 text-sm text-red-600">{errors.code}</p>
)}
{errors.code && <p className="mt-1 text-sm text-red-600">{errors.code}</p>}
</div>
<div>
@ -219,13 +208,11 @@ const WarehouseForm: React.FC = () => {
</label>
<select
value={formData.warehouseType}
onChange={(e) =>
handleInputChange("warehouseType", e.target.value)
}
onChange={(e) => 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 ${
errors.warehouseType
? "border-red-300 focus:border-red-500 focus:ring-red-500"
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`}
>
<option value="">Tip seçin</option>
@ -239,42 +226,32 @@ const WarehouseForm: React.FC = () => {
<option value="HAZARDOUS">Tehlikeli Madde Deposu</option>
</select>
{errors.warehouseType && (
<p className="mt-1 text-sm text-red-600">
{errors.warehouseType}
</p>
<p className="mt-1 text-sm text-red-600">{errors.warehouseType}</p>
)}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Depo Adı *
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">Depo Adı *</label>
<input
type="text"
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 ${
errors.name
? "border-red-300 focus:border-red-500 focus:ring-red-500"
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`}
placeholder="Depo adı"
/>
{errors.name && (
<p className="mt-1 text-sm text-red-600">{errors.name}</p>
)}
{errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
ıklama
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">ıklama</label>
<textarea
value={formData.description}
onChange={(e) =>
handleInputChange("description", e.target.value)
}
onChange={(e) => handleInputChange('description', e.target.value)}
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"
placeholder="Depo açıklaması"
@ -291,16 +268,11 @@ const WarehouseForm: React.FC = () => {
min="0"
step="0.01"
value={formData.capacity}
onChange={(e) =>
handleInputChange(
"capacity",
parseFloat(e.target.value) || 0
)
}
onChange={(e) => 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 ${
errors.capacity
? "border-red-300 focus:border-red-500 focus:ring-red-500"
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`}
placeholder="0"
/>
@ -315,15 +287,10 @@ const WarehouseForm: React.FC = () => {
type="checkbox"
id="isMainWarehouse"
checked={formData.isMainWarehouse}
onChange={(e) =>
handleInputChange("isMainWarehouse", e.target.checked)
}
onChange={(e) => handleInputChange('isMainWarehouse', e.target.checked)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label
htmlFor="isMainWarehouse"
className="ml-2 block text-sm text-gray-900"
>
<label htmlFor="isMainWarehouse" className="ml-2 block text-sm text-gray-900">
Ana Depo
</label>
</div>
@ -333,15 +300,10 @@ const WarehouseForm: React.FC = () => {
type="checkbox"
id="isActive"
checked={formData.isActive}
onChange={(e) =>
handleInputChange("isActive", e.target.checked)
}
onChange={(e) => handleInputChange('isActive', e.target.checked)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label
htmlFor="isActive"
className="ml-2 block text-sm text-gray-900"
>
<label htmlFor="isActive" className="ml-2 block text-sm text-gray-900">
Aktif
</label>
</div>
@ -361,105 +323,79 @@ const WarehouseForm: React.FC = () => {
<div className="p-4 space-y-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Adres *
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">Adres *</label>
<input
type="text"
value={formData.address?.street}
onChange={(e) =>
handleInputChange("address.street", e.target.value)
}
onChange={(e) => 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 ${
errors["address.street"]
? "border-red-300 focus:border-red-500 focus:ring-red-500"
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
errors['address.street']
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`}
placeholder="Sokak, cadde, mahalle"
/>
{errors["address.street"] && (
<p className="mt-1 text-sm text-red-600">
{errors["address.street"]}
</p>
{errors['address.street'] && (
<p className="mt-1 text-sm text-red-600">{errors['address.street']}</p>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Şehir *
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">Şehir *</label>
<input
type="text"
value={formData.address?.city}
onChange={(e) =>
handleInputChange("address.city", e.target.value)
}
onChange={(e) => 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 ${
errors["address.city"]
? "border-red-300 focus:border-red-500 focus:ring-red-500"
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
errors['address.city']
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`}
placeholder="Şehir"
/>
{errors["address.city"] && (
<p className="mt-1 text-sm text-red-600">
{errors["address.city"]}
</p>
{errors['address.city'] && (
<p className="mt-1 text-sm text-red-600">{errors['address.city']}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
İl/Bölge
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">İl/Bölge</label>
<input
type="text"
value={formData.address?.state}
onChange={(e) =>
handleInputChange("address.state", e.target.value)
}
onChange={(e) => 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"
placeholder="İl/Bölge"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Posta Kodu
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">Posta Kodu</label>
<input
type="text"
value={formData.address?.postalCode}
onChange={(e) =>
handleInputChange("address.postalCode", e.target.value)
}
onChange={(e) => 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"
placeholder="34000"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Ülke *
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">Ülke *</label>
<input
type="text"
value={formData.address?.country}
onChange={(e) =>
handleInputChange("address.country", e.target.value)
}
onChange={(e) => 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 ${
errors["address.country"]
? "border-red-300 focus:border-red-500 focus:ring-red-500"
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
errors['address.country']
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
}`}
placeholder="Ülke"
/>
{errors["address.country"] && (
<p className="mt-1 text-sm text-red-600">
{errors["address.country"]}
</p>
{errors['address.country'] && (
<p className="mt-1 text-sm text-red-600">{errors['address.country']}</p>
)}
</div>
</div>
@ -479,25 +415,17 @@ const WarehouseForm: React.FC = () => {
<div className="bg-gray-50 rounded-lg p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
<div>
<span className="font-medium text-gray-700">
Toplam Kapasite:
</span>
<span className="font-medium text-gray-700">Toplam Kapasite:</span>
<div className="text-lg font-semibold text-blue-600">
{formData.capacity.toLocaleString()} m³
</div>
</div>
<div>
<span className="font-medium text-gray-700">
Mevcut Kullanım:
</span>
<div className="text-lg font-semibold text-green-600">
0 m³ (0%)
</div>
<span className="font-medium text-gray-700">Mevcut Kullanım:</span>
<div className="text-lg font-semibold text-green-600">0 m³ (0%)</div>
</div>
<div>
<span className="font-medium text-gray-700">
Müsait Kapasite:
</span>
<span className="font-medium text-gray-700">Müsait Kapasite:</span>
<div className="text-lg font-semibold text-gray-600">
{formData.capacity.toLocaleString()} m³
</div>
@ -506,10 +434,7 @@ const WarehouseForm: React.FC = () => {
<div className="mt-4">
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: "0%" }}
/>
<div className="bg-blue-600 h-2 rounded-full" style={{ width: '0%' }} />
</div>
<p className="text-xs text-gray-600 mt-1">Kullanım oranı: 0%</p>
</div>
@ -532,25 +457,20 @@ const WarehouseForm: React.FC = () => {
<div className="flex">
<div className="ml-3">
<p className="text-sm text-blue-700">
{formData.warehouseType ===
WarehouseTypeEnum.RawMaterials &&
"Hammadde depolama için özel koşullara sahip olacaktır."}
{formData.warehouseType ===
WarehouseTypeEnum.FinishedGoods &&
"Mamul depolama ve sevkiyat operasyonları için optimize edilecektir."}
{formData.warehouseType ===
WarehouseTypeEnum.SpareParts &&
"Yedek parça yönetimi için özel raflar ve kategorilendirme yapılacaktır."}
{formData.warehouseType === WarehouseTypeEnum.RawMaterials &&
'Hammadde depolama için özel koşullara sahip olacaktır.'}
{formData.warehouseType === WarehouseTypeEnum.FinishedGoods &&
'Mamul depolama ve sevkiyat operasyonları için optimize edilecektir.'}
{formData.warehouseType === WarehouseTypeEnum.SpareParts &&
'Yedek parça yönetimi için özel raflar ve kategorilendirme yapılacaktır.'}
{formData.warehouseType === WarehouseTypeEnum.Returns &&
"Konsinye stok yönetimi için ayrı izleme sistemi kullanılacaktır."}
{formData.warehouseType ===
WarehouseTypeEnum.Quarantine &&
"Karantina prosedürleri ve onay mekanizmaları aktif olacaktır."}
'Konsinye stok yönetimi için ayrı izleme sistemi kullanılacaktır.'}
{formData.warehouseType === WarehouseTypeEnum.Quarantine &&
'Karantina prosedürleri ve onay mekanizmaları aktif olacaktır.'}
{formData.warehouseType === WarehouseTypeEnum.Transit &&
"Sıcaklık kontrollü depolama sistemi kurulacaktır."}
{formData.warehouseType ===
WarehouseTypeEnum.WorkInProgress &&
"Tehlikeli madde depolama güvenlik protokolleri uygulanacaktır."}
'Sıcaklık kontrollü depolama sistemi kurulacaktır.'}
{formData.warehouseType === WarehouseTypeEnum.WorkInProgress &&
'Tehlikeli madde depolama güvenlik protokolleri uygulanacaktır.'}
</p>
</div>
</div>
@ -582,14 +502,15 @@ const WarehouseForm: React.FC = () => {
) : (
<>
<FaSave className="w-4 h-4 mr-2" />
{isEdit ? "Güncelle" : "Kaydet"}
{isEdit ? 'Güncelle' : 'Kaydet'}
</>
)}
</button>
</div>
</form>
</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 { Link } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import React, { useState } from 'react'
import { Link } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import {
FaBuilding,
FaPlus,
@ -14,45 +14,42 @@ import {
FaArrowUp,
FaExclamationTriangle,
FaChartLine,
} from "react-icons/fa";
import classNames from "classnames";
import { WarehouseTypeEnum } from "../../../types/wm";
import { mockWarehouses } from "../../../mocks/mockWarehouses";
import {
getWarehouseTypeColor,
getWarehouseTypeText,
} from "../../../utils/erp";
} from 'react-icons/fa'
import classNames from 'classnames'
import { WarehouseTypeEnum } from '../../../types/wm'
import { mockWarehouses } from '../../../mocks/mockWarehouses'
import { getWarehouseTypeColor, getWarehouseTypeText } from '../../../utils/erp'
import { Container } from '@/components/shared'
const WarehouseList: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [filterType, setFilterType] = useState("all");
const [showFilters, setShowFilters] = useState(false);
const [searchTerm, setSearchTerm] = useState('')
const [filterType, setFilterType] = useState('all')
const [showFilters, setShowFilters] = useState(false)
const {
data: warehouses,
isLoading,
error,
} = useQuery({
queryKey: ["warehouses", searchTerm, filterType],
queryKey: ['warehouses', searchTerm, filterType],
queryFn: async () => {
await new Promise((resolve) => setTimeout(resolve, 500));
await new Promise((resolve) => setTimeout(resolve, 500))
return mockWarehouses.filter((warehouse) => {
const matchesSearch =
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase());
const matchesType =
filterType === "all" || warehouse.warehouseType === filterType;
return matchesSearch && matchesType;
});
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase())
const matchesType = filterType === 'all' || warehouse.warehouseType === filterType
return matchesSearch && matchesType
})
},
});
})
const getUtilizationColor = (utilization: number) => {
const percentage = utilization;
if (percentage >= 90) return "text-red-600";
if (percentage >= 75) return "text-yellow-600";
return "text-green-600";
};
const percentage = utilization
if (percentage >= 90) return 'text-red-600'
if (percentage >= 75) return 'text-yellow-600'
return 'text-green-600'
}
if (isLoading) {
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>
<span className="ml-3 text-gray-600">Depolar yükleniyor...</span>
</div>
);
)
}
if (error) {
@ -71,11 +68,12 @@ const WarehouseList: React.FC = () => {
<span className="text-red-800">Depolar yüklenirken hata oluştu.</span>
</div>
</div>
);
)
}
return (
<div className="space-y-4 pt-2">
<Container>
<div className="space-y-2">
{/* Header Actions */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div className="flex items-center space-x-2">
@ -96,10 +94,10 @@ const WarehouseList: React.FC = () => {
<button
onClick={() => setShowFilters(!showFilters)}
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
? "border-blue-500 bg-blue-50 text-blue-700"
: "border-gray-300 bg-white text-gray-700 hover:bg-gray-50"
? 'border-blue-500 bg-blue-50 text-blue-700'
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
)}
>
<FaFilter size={14} className="mr-2" />
@ -109,7 +107,7 @@ const WarehouseList: React.FC = () => {
<div className="flex items-center space-x-2">
<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"
>
<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="grid grid-cols-1 md:grid-cols-3 gap-3">
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">
Depo Türü
</label>
<label className="block text-xs font-medium text-gray-700 mb-1">Depo Türü</label>
<select
value={filterType}
onChange={(e) => setFilterType(e.target.value)}
@ -142,12 +138,8 @@ const WarehouseList: React.FC = () => {
<option value="all">Tümü</option>
<option value={WarehouseTypeEnum.RawMaterials}>Hammadde</option>
<option value={WarehouseTypeEnum.FinishedGoods}>Mamul</option>
<option value={WarehouseTypeEnum.WorkInProgress}>
Yarı Mamul
</option>
<option value={WarehouseTypeEnum.SpareParts}>
Yedek Parça
</option>
<option value={WarehouseTypeEnum.WorkInProgress}>Yarı Mamul</option>
<option value={WarehouseTypeEnum.SpareParts}>Yedek Parça</option>
<option value={WarehouseTypeEnum.Quarantine}>Karantina</option>
</select>
</div>
@ -155,8 +147,8 @@ const WarehouseList: React.FC = () => {
<div className="flex items-end">
<button
onClick={() => {
setFilterType("all");
setSearchTerm("");
setFilterType('all')
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"
>
@ -173,9 +165,7 @@ const WarehouseList: React.FC = () => {
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Toplam Depo</p>
<p className="text-xl font-bold text-gray-900">
{warehouses?.length || 0}
</p>
<p className="text-xl font-bold text-gray-900">{warehouses?.length || 0}</p>
</div>
<FaBuilding className="h-6 w-6 text-blue-500" />
</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="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">
Toplam Kapasite
</p>
<p className="text-sm font-medium text-gray-600">Toplam Kapasite</p>
<p className="text-xl font-bold text-green-600">
{warehouses
?.reduce((acc, w) => acc + w.capacity, 0)
.toLocaleString() || 0}
{warehouses?.reduce((acc, w) => acc + w.capacity, 0).toLocaleString() || 0}
</p>
</div>
<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="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">
Kullanım Oranı
</p>
<p className="text-sm font-medium text-gray-600">Kullanım Oranı</p>
<p className="text-xl font-bold text-yellow-600">
{warehouses?.length
? Math.round(
(warehouses.reduce(
(acc, w) => acc + w.currentUtilization,
0
) /
(warehouses.reduce((acc, w) => acc + w.capacity, 0) ||
1)) *
100
(warehouses.reduce((acc, w) => acc + w.currentUtilization, 0) /
(warehouses.reduce((acc, w) => acc + w.capacity, 0) || 1)) *
100,
)
: 0}
%
@ -272,14 +252,11 @@ const WarehouseList: React.FC = () => {
<tbody className="bg-white divide-y divide-gray-200">
{warehouses?.map((warehouse) => {
const utilizationPercentage = Math.round(
(warehouse.currentUtilization / warehouse.capacity) * 100
);
(warehouse.currentUtilization / warehouse.capacity) * 100,
)
return (
<tr
key={warehouse.id}
className="hover:bg-gray-50 transition-colors"
>
<tr key={warehouse.id} className="hover:bg-gray-50 transition-colors">
<td className="px-4 py-3">
<div className="flex items-center">
<div className="flex-shrink-0 h-8 w-8">
@ -307,16 +284,15 @@ const WarehouseList: React.FC = () => {
<div className="space-y-1">
<span
className={classNames(
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium",
getWarehouseTypeColor(warehouse.warehouseType)
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
getWarehouseTypeColor(warehouse.warehouseType),
)}
>
{getWarehouseTypeText(warehouse.warehouseType)}
</span>
<div className="flex items-center text-xs text-gray-500">
<FaMapMarkerAlt size={14} className="mr-1" />
{warehouse.address?.city},{" "}
{warehouse.address?.country}
{warehouse.address?.city}, {warehouse.address?.country}
</div>
</div>
</td>
@ -325,17 +301,13 @@ const WarehouseList: React.FC = () => {
<div className="text-sm font-medium text-gray-900">
{warehouse.capacity.toLocaleString()} m³
</div>
<div className="text-sm text-gray-500">
Toplam Kapasite
</div>
<div className="text-sm text-gray-500">Toplam Kapasite</div>
</td>
<td className="px-4 py-3">
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-xs text-gray-600">
Kullanılan:
</span>
<span className="text-xs text-gray-600">Kullanılan:</span>
<span className="text-sm font-medium text-gray-900">
{warehouse.currentUtilization.toLocaleString()} m³
</span>
@ -343,12 +315,12 @@ const WarehouseList: React.FC = () => {
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className={classNames(
"h-2 rounded-full",
'h-2 rounded-full',
utilizationPercentage >= 90
? "bg-red-500"
? 'bg-red-500'
: utilizationPercentage >= 75
? "bg-yellow-500"
: "bg-green-500"
? 'bg-yellow-500'
: 'bg-green-500',
)}
style={{
width: `${Math.min(utilizationPercentage, 100)}%`,
@ -357,8 +329,8 @@ const WarehouseList: React.FC = () => {
</div>
<div
className={classNames(
"text-sm font-medium",
getUtilizationColor(utilizationPercentage)
'text-sm font-medium',
getUtilizationColor(utilizationPercentage),
)}
>
%{utilizationPercentage}
@ -369,13 +341,8 @@ const WarehouseList: React.FC = () => {
<td className="px-4 py-3">
<div className="space-y-1">
<div className="flex items-center">
<FaChartLine
size={14}
className="text-green-500 mr-1"
/>
<span className="text-sm text-green-600">
Verimli
</span>
<FaChartLine size={14} className="text-green-500 mr-1" />
<span className="text-sm text-green-600">Verimli</span>
</div>
<div className="text-xs text-gray-500">Son 30 gün</div>
</div>
@ -384,13 +351,13 @@ const WarehouseList: React.FC = () => {
<td className="px-4 py-3">
<span
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
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800',
)}
>
{warehouse.isActive ? "Aktif" : "Pasif"}
{warehouse.isActive ? 'Aktif' : 'Pasif'}
</span>
</td>
@ -414,7 +381,7 @@ const WarehouseList: React.FC = () => {
</div>
</td>
</tr>
);
)
})}
</tbody>
</table>
@ -423,12 +390,8 @@ const WarehouseList: React.FC = () => {
{(!warehouses || warehouses.length === 0) && (
<div className="text-center py-12">
<FaBuilding className="mx-auto h-12 w-12 text-gray-400" />
<h3 className="mt-2 text-sm font-medium text-gray-900">
Depo bulunamadı
</h3>
<p className="mt-1 text-sm text-gray-500">
Yeni depo ekleyerek başlayın.
</p>
<h3 className="mt-2 text-sm font-medium text-gray-900">Depo bulunamadı</h3>
<p className="mt-1 text-sm text-gray-500">Yeni depo ekleyerek başlayın.</p>
<div className="mt-6">
<Link
to="/admin/warehouse/new"
@ -442,7 +405,8 @@ const WarehouseList: React.FC = () => {
)}
</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