Warehouse Management
This commit is contained in:
parent
8b88970fe2
commit
bb2e39c2c3
13 changed files with 4751 additions and 5982 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
FaBox,
|
FaBox,
|
||||||
FaMapMarkerAlt,
|
FaMapMarkerAlt,
|
||||||
|
|
@ -8,71 +8,61 @@ import {
|
||||||
FaEye,
|
FaEye,
|
||||||
FaEdit,
|
FaEdit,
|
||||||
FaPlus,
|
FaPlus,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
// using modals for create actions; no navigation needed here
|
// using modals for create actions; no navigation needed here
|
||||||
import {
|
import { MmLotNumber, MmSerialNumber, QualityStatusEnum, SerialStatusEnum } from '../../../types/mm'
|
||||||
MmLotNumber,
|
import LotForm from './LotForm'
|
||||||
MmSerialNumber,
|
import SerialForm from './SerialForm'
|
||||||
QualityStatusEnum,
|
import { mockLotNumbers } from '../../../mocks/mockLotNumbers'
|
||||||
SerialStatusEnum,
|
import { mockSerialNumbers } from '../../../mocks/mockSerialNumbers'
|
||||||
} from "../../../types/mm";
|
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||||
import LotForm from "./LotForm";
|
import { mockUnits } from '../../../mocks/mockUnits'
|
||||||
import SerialForm from "./SerialForm";
|
import { mockBusinessParties } from '../../../mocks/mockBusinessParties'
|
||||||
import { mockLotNumbers } from "../../../mocks/mockLotNumbers";
|
import Widget from '../../../components/common/Widget'
|
||||||
import { mockSerialNumbers } from "../../../mocks/mockSerialNumbers";
|
import { PartyType } from '../../../types/common'
|
||||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
import { getQualityStatusInfo, getSerialStatusInfo } from '../../../utils/erp'
|
||||||
import { mockUnits } from "../../../mocks/mockUnits";
|
import { Container } from '@/components/shared'
|
||||||
import { mockBusinessParties } from "../../../mocks/mockBusinessParties";
|
|
||||||
import Widget from "../../../components/common/Widget";
|
|
||||||
import { PartyType } from "../../../types/common";
|
|
||||||
import { getQualityStatusInfo, getSerialStatusInfo } from "../../../utils/erp";
|
|
||||||
|
|
||||||
const InventoryTracking: React.FC = () => {
|
const InventoryTracking: React.FC = () => {
|
||||||
const [activeTab, setActiveTab] = useState<"lots" | "serials">("lots");
|
const [activeTab, setActiveTab] = useState<'lots' | 'serials'>('lots')
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [showCreateMenu, setShowCreateMenu] = useState(false);
|
const [showCreateMenu, setShowCreateMenu] = useState(false)
|
||||||
|
|
||||||
// Mock data için
|
// Mock data için
|
||||||
const [lotNumbers, setLotNumbers] = useState<MmLotNumber[]>(mockLotNumbers);
|
const [lotNumbers, setLotNumbers] = useState<MmLotNumber[]>(mockLotNumbers)
|
||||||
|
|
||||||
const [serialNumbers, setSerialNumbers] =
|
const [serialNumbers, setSerialNumbers] = useState<MmSerialNumber[]>(mockSerialNumbers)
|
||||||
useState<MmSerialNumber[]>(mockSerialNumbers);
|
|
||||||
|
|
||||||
// Modal control state
|
// Modal control state
|
||||||
const [openLotModal, setOpenLotModal] = useState(false);
|
const [openLotModal, setOpenLotModal] = useState(false)
|
||||||
const [openSerialModal, setOpenSerialModal] = useState(false);
|
const [openSerialModal, setOpenSerialModal] = useState(false)
|
||||||
const [currentLot, setCurrentLot] = useState<MmLotNumber | null>(null);
|
const [currentLot, setCurrentLot] = useState<MmLotNumber | null>(null)
|
||||||
const [currentSerial, setCurrentSerial] = useState<MmSerialNumber | null>(
|
const [currentSerial, setCurrentSerial] = useState<MmSerialNumber | null>(null)
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
const filteredLots = lotNumbers.filter(
|
const filteredLots = lotNumbers.filter(
|
||||||
(lot) =>
|
(lot) =>
|
||||||
lot.lotNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
lot.lotNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase())
|
lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||||
);
|
)
|
||||||
|
|
||||||
const filteredSerials = serialNumbers.filter(
|
const filteredSerials = serialNumbers.filter(
|
||||||
(serial) =>
|
(serial) =>
|
||||||
serial.serialNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
serial.serialNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase())
|
serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 py-2">
|
<Container>
|
||||||
|
<div className="space-y-2">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Envanter Takibi</h2>
|
<h2 className="text-2xl font-bold text-gray-900">Envanter Takibi</h2>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">Lot ve seri numarası takiplerini yönetin</p>
|
||||||
Lot ve seri numarası takiplerini yönetin
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() => setShowCreateMenu((s) => !s)} /* Kontrol küçültüldü */
|
||||||
setShowCreateMenu((s) => !s)
|
|
||||||
} /* Kontrol küçültüldü */
|
|
||||||
className="bg-blue-600 text-white px-2.5 py-1 rounded-md hover:bg-blue-700 flex items-center space-x-1.5 text-sm"
|
className="bg-blue-600 text-white px-2.5 py-1 rounded-md hover:bg-blue-700 flex items-center space-x-1.5 text-sm"
|
||||||
>
|
>
|
||||||
<FaPlus className="h-4 w-4" />
|
<FaPlus className="h-4 w-4" />
|
||||||
|
|
@ -84,8 +74,8 @@ const InventoryTracking: React.FC = () => {
|
||||||
<div className="absolute right-0 mt-2 w-44 bg-white border rounded shadow-lg z-10">
|
<div className="absolute right-0 mt-2 w-44 bg-white border rounded shadow-lg z-10">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenLotModal(true);
|
setOpenLotModal(true)
|
||||||
setShowCreateMenu(false);
|
setShowCreateMenu(false)
|
||||||
}}
|
}}
|
||||||
className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50"
|
className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
|
|
@ -93,8 +83,8 @@ const InventoryTracking: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenSerialModal(true);
|
setOpenSerialModal(true)
|
||||||
setShowCreateMenu(false);
|
setShowCreateMenu(false)
|
||||||
}}
|
}}
|
||||||
className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50"
|
className="w-full text-left px-2.5 py-1 text-sm hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
|
|
@ -107,19 +97,12 @@ const InventoryTracking: React.FC = () => {
|
||||||
|
|
||||||
{/* Summary Statistics */}
|
{/* Summary Statistics */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
||||||
<Widget
|
<Widget title="Toplam Lot" value={lotNumbers.length} color="blue" icon="FaBox" />
|
||||||
title="Toplam Lot"
|
|
||||||
value={lotNumbers.length}
|
|
||||||
color="blue"
|
|
||||||
icon="FaBox"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
<Widget
|
||||||
title="Onaylı Lot"
|
title="Onaylı Lot"
|
||||||
value={
|
value={
|
||||||
lotNumbers.filter(
|
lotNumbers.filter((lot) => lot.qualityStatus === QualityStatusEnum.Approved).length
|
||||||
(lot) => lot.qualityStatus === QualityStatusEnum.Approved
|
|
||||||
).length
|
|
||||||
}
|
}
|
||||||
color="green"
|
color="green"
|
||||||
icon="FaBox"
|
icon="FaBox"
|
||||||
|
|
@ -135,9 +118,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
<Widget
|
<Widget
|
||||||
title="Müsait Seri"
|
title="Müsait Seri"
|
||||||
value={
|
value={
|
||||||
serialNumbers.filter(
|
serialNumbers.filter((serial) => serial.status === SerialStatusEnum.Available).length
|
||||||
(serial) => serial.status === SerialStatusEnum.Available
|
|
||||||
).length
|
|
||||||
}
|
}
|
||||||
color="orange"
|
color="orange"
|
||||||
icon="FaHashtag"
|
icon="FaHashtag"
|
||||||
|
|
@ -160,22 +141,22 @@ const InventoryTracking: React.FC = () => {
|
||||||
<div className="border-b border-gray-200">
|
<div className="border-b border-gray-200">
|
||||||
<nav className="flex -mb-px">
|
<nav className="flex -mb-px">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab("lots")}
|
onClick={() => setActiveTab('lots')}
|
||||||
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
|
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
|
||||||
activeTab === "lots"
|
activeTab === 'lots'
|
||||||
? "border-blue-500 text-blue-600"
|
? 'border-blue-500 text-blue-600'
|
||||||
: "border-transparent text-gray-500 hover:text-gray-700"
|
: 'border-transparent text-gray-500 hover:text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FaBox className="h-4 w-4 mr-2" />
|
<FaBox className="h-4 w-4 mr-2" />
|
||||||
Lot Numaraları ({filteredLots.length})
|
Lot Numaraları ({filteredLots.length})
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab("serials")}
|
onClick={() => setActiveTab('serials')}
|
||||||
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
|
className={`flex items-center px-2.5 py-1 text-sm font-medium border-b-2 ${
|
||||||
activeTab === "serials"
|
activeTab === 'serials'
|
||||||
? "border-blue-500 text-blue-600"
|
? 'border-blue-500 text-blue-600'
|
||||||
: "border-transparent text-gray-500 hover:text-gray-700"
|
: 'border-transparent text-gray-500 hover:text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FaHashtag className="h-4 w-4 mr-2" />
|
<FaHashtag className="h-4 w-4 mr-2" />
|
||||||
|
|
@ -185,7 +166,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Lot Numbers Tab */}
|
{/* Lot Numbers Tab */}
|
||||||
{activeTab === "lots" && (
|
{activeTab === 'lots' && (
|
||||||
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
|
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
|
|
@ -218,20 +199,18 @@ const InventoryTracking: React.FC = () => {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{filteredLots.map((lot) => {
|
{filteredLots.map((lot) => {
|
||||||
const qualityInfo = getQualityStatusInfo(lot.qualityStatus);
|
const qualityInfo = getQualityStatusInfo(lot.qualityStatus)
|
||||||
const isExpiringSoon =
|
const isExpiringSoon =
|
||||||
lot.expiryDate &&
|
lot.expiryDate &&
|
||||||
new Date(lot.expiryDate).getTime() - new Date().getTime() <
|
new Date(lot.expiryDate).getTime() - new Date().getTime() <
|
||||||
30 * 24 * 60 * 60 * 1000;
|
30 * 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={lot.id} className="hover:bg-gray-50 text-xs">
|
<tr key={lot.id} className="hover:bg-gray-50 text-xs">
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<FaBox className="h-4 w-4 text-gray-400 mr-2" />
|
<FaBox className="h-4 w-4 text-gray-400 mr-2" />
|
||||||
<div className="text-xs font-medium text-gray-900">
|
<div className="text-xs font-medium text-gray-900">{lot.lotNumber}</div>
|
||||||
{lot.lotNumber}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
|
|
@ -248,9 +227,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
|
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
|
||||||
<div className="text-xs text-gray-900">
|
<div className="text-xs text-gray-900">
|
||||||
{new Date(lot.productionDate).toLocaleDateString(
|
{new Date(lot.productionDate).toLocaleDateString('tr-TR')}
|
||||||
"tr-TR"
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -258,16 +235,12 @@ const InventoryTracking: React.FC = () => {
|
||||||
{lot.expiryDate ? (
|
{lot.expiryDate ? (
|
||||||
<div
|
<div
|
||||||
className={`text-xs ${
|
className={`text-xs ${
|
||||||
isExpiringSoon
|
isExpiringSoon ? 'text-red-600 font-medium' : 'text-gray-900'
|
||||||
? "text-red-600 font-medium"
|
|
||||||
: "text-gray-900"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{new Date(lot.expiryDate).toLocaleDateString("tr-TR")}
|
{new Date(lot.expiryDate).toLocaleDateString('tr-TR')}
|
||||||
{isExpiringSoon && (
|
{isExpiringSoon && (
|
||||||
<div className="text-xs text-red-500">
|
<div className="text-xs text-red-500">Yakında Dolacak</div>
|
||||||
Yakında Dolacak
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -275,9 +248,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
<div className="text-xs text-gray-900">
|
<div className="text-xs text-gray-900">{lot.supplierId || '-'}</div>
|
||||||
{lot.supplierId || "-"}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
<span
|
<span
|
||||||
|
|
@ -290,8 +261,8 @@ const InventoryTracking: React.FC = () => {
|
||||||
<div className="flex items-center justify-end space-x-2">
|
<div className="flex items-center justify-end space-x-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentLot(lot);
|
setCurrentLot(lot)
|
||||||
setOpenLotModal(true);
|
setOpenLotModal(true)
|
||||||
}}
|
}}
|
||||||
className="text-blue-600 hover:text-blue-900"
|
className="text-blue-600 hover:text-blue-900"
|
||||||
>
|
>
|
||||||
|
|
@ -299,8 +270,8 @@ const InventoryTracking: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentLot(lot);
|
setCurrentLot(lot)
|
||||||
setOpenLotModal(true);
|
setOpenLotModal(true)
|
||||||
}}
|
}}
|
||||||
className="text-green-600 hover:text-green-900"
|
className="text-green-600 hover:text-green-900"
|
||||||
>
|
>
|
||||||
|
|
@ -309,37 +280,31 @@ const InventoryTracking: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<LotForm
|
<LotForm
|
||||||
isOpen={openLotModal}
|
isOpen={openLotModal}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpenLotModal(false);
|
setOpenLotModal(false)
|
||||||
setCurrentLot(null);
|
setCurrentLot(null)
|
||||||
}}
|
}}
|
||||||
onSave={(lot) => setLotNumbers((prev) => [lot, ...prev])}
|
onSave={(lot) => setLotNumbers((prev) => [lot, ...prev])}
|
||||||
onUpdate={(updated) =>
|
onUpdate={(updated) =>
|
||||||
setLotNumbers((prev) =>
|
setLotNumbers((prev) => prev.map((l) => (l.id === updated.id ? updated : l)))
|
||||||
prev.map((l) => (l.id === updated.id ? updated : l))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
materials={mockMaterials}
|
materials={mockMaterials}
|
||||||
units={mockUnits}
|
units={mockUnits}
|
||||||
suppliers={mockBusinessParties.filter(
|
suppliers={mockBusinessParties.filter((bp) => bp.partyType === PartyType.Supplier)}
|
||||||
(bp) => bp.partyType === PartyType.Supplier
|
|
||||||
)}
|
|
||||||
initial={currentLot}
|
initial={currentLot}
|
||||||
mode={currentLot ? "edit" : "create"}
|
mode={currentLot ? 'edit' : 'create'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{filteredLots.length === 0 && (
|
{filteredLots.length === 0 && (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<FaBox className="mx-auto h-12 w-12 text-gray-400" />
|
<FaBox className="mx-auto h-12 w-12 text-gray-400" />
|
||||||
<h3 className="mt-2 text-sm font-medium text-gray-900">
|
<h3 className="mt-2 text-sm font-medium text-gray-900">Lot bulunamadı</h3>
|
||||||
Lot bulunamadı
|
|
||||||
</h3>
|
|
||||||
<p className="mt-1 text-sm text-gray-500">
|
<p className="mt-1 text-sm text-gray-500">
|
||||||
Arama kriterlerinize uygun lot kaydı bulunmuyor.
|
Arama kriterlerinize uygun lot kaydı bulunmuyor.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -349,7 +314,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Serial Numbers Tab */}
|
{/* Serial Numbers Tab */}
|
||||||
{activeTab === "serials" && (
|
{activeTab === 'serials' && (
|
||||||
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
|
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
|
|
@ -382,12 +347,11 @@ const InventoryTracking: React.FC = () => {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{filteredSerials.map((serial) => {
|
{filteredSerials.map((serial) => {
|
||||||
const statusInfo = getSerialStatusInfo(serial.status);
|
const statusInfo = getSerialStatusInfo(serial.status)
|
||||||
const isWarrantyExpiring =
|
const isWarrantyExpiring =
|
||||||
serial.warrantyExpiryDate &&
|
serial.warrantyExpiryDate &&
|
||||||
new Date(serial.warrantyExpiryDate).getTime() -
|
new Date(serial.warrantyExpiryDate).getTime() - new Date().getTime() <
|
||||||
new Date().getTime() <
|
30 * 24 * 60 * 60 * 1000
|
||||||
30 * 24 * 60 * 60 * 1000;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={serial.id} className="hover:bg-gray-50 text-xs">
|
<tr key={serial.id} className="hover:bg-gray-50 text-xs">
|
||||||
|
|
@ -411,7 +375,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
LOT-{serial.lotId}
|
LOT-{serial.lotId}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
"-"
|
'-'
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -419,9 +383,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
|
<FaCalendar className="h-4 w-4 text-gray-400 mr-2" />
|
||||||
<div className="text-xs text-gray-900">
|
<div className="text-xs text-gray-900">
|
||||||
{new Date(serial.productionDate).toLocaleDateString(
|
{new Date(serial.productionDate).toLocaleDateString('tr-TR')}
|
||||||
"tr-TR"
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -429,18 +391,12 @@ const InventoryTracking: React.FC = () => {
|
||||||
{serial.warrantyExpiryDate ? (
|
{serial.warrantyExpiryDate ? (
|
||||||
<div
|
<div
|
||||||
className={`text-xs ${
|
className={`text-xs ${
|
||||||
isWarrantyExpiring
|
isWarrantyExpiring ? 'text-red-600 font-medium' : 'text-gray-900'
|
||||||
? "text-red-600 font-medium"
|
|
||||||
: "text-gray-900"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{new Date(
|
{new Date(serial.warrantyExpiryDate).toLocaleDateString('tr-TR')}
|
||||||
serial.warrantyExpiryDate
|
|
||||||
).toLocaleDateString("tr-TR")}
|
|
||||||
{isWarrantyExpiring && (
|
{isWarrantyExpiring && (
|
||||||
<div className="text-xs text-red-500">
|
<div className="text-xs text-red-500">Yakında Dolacak</div>
|
||||||
Yakında Dolacak
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -451,7 +407,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<FaMapMarkerAlt className="h-4 w-4 text-gray-400 mr-2" />
|
<FaMapMarkerAlt className="h-4 w-4 text-gray-400 mr-2" />
|
||||||
<div className="text-xs text-gray-900">
|
<div className="text-xs text-gray-900">
|
||||||
{serial.currentLocationId || "-"}
|
{serial.currentLocationId || '-'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -466,8 +422,8 @@ const InventoryTracking: React.FC = () => {
|
||||||
<div className="flex items-center justify-end space-x-2">
|
<div className="flex items-center justify-end space-x-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentSerial(serial);
|
setCurrentSerial(serial)
|
||||||
setOpenSerialModal(true);
|
setOpenSerialModal(true)
|
||||||
}}
|
}}
|
||||||
className="text-blue-600 hover:text-blue-900"
|
className="text-blue-600 hover:text-blue-900"
|
||||||
>
|
>
|
||||||
|
|
@ -475,8 +431,8 @@ const InventoryTracking: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentSerial(serial);
|
setCurrentSerial(serial)
|
||||||
setOpenSerialModal(true);
|
setOpenSerialModal(true)
|
||||||
}}
|
}}
|
||||||
className="text-green-600 hover:text-green-900"
|
className="text-green-600 hover:text-green-900"
|
||||||
>
|
>
|
||||||
|
|
@ -485,7 +441,7 @@ const InventoryTracking: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -493,27 +449,23 @@ const InventoryTracking: React.FC = () => {
|
||||||
<SerialForm
|
<SerialForm
|
||||||
isOpen={openSerialModal}
|
isOpen={openSerialModal}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpenSerialModal(false);
|
setOpenSerialModal(false)
|
||||||
setCurrentSerial(null);
|
setCurrentSerial(null)
|
||||||
}}
|
}}
|
||||||
onSave={(serial) => setSerialNumbers((prev) => [serial, ...prev])}
|
onSave={(serial) => setSerialNumbers((prev) => [serial, ...prev])}
|
||||||
onUpdate={(updated) =>
|
onUpdate={(updated) =>
|
||||||
setSerialNumbers((prev) =>
|
setSerialNumbers((prev) => prev.map((s) => (s.id === updated.id ? updated : s)))
|
||||||
prev.map((s) => (s.id === updated.id ? updated : s))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
materials={mockMaterials}
|
materials={mockMaterials}
|
||||||
lots={lotNumbers.map((l) => ({ id: l.id, label: l.lotNumber }))}
|
lots={lotNumbers.map((l) => ({ id: l.id, label: l.lotNumber }))}
|
||||||
initial={currentSerial}
|
initial={currentSerial}
|
||||||
mode={currentSerial ? "edit" : "create"}
|
mode={currentSerial ? 'edit' : 'create'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{filteredSerials.length === 0 && (
|
{filteredSerials.length === 0 && (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<FaHashtag className="mx-auto h-12 w-12 text-gray-400" />
|
<FaHashtag className="mx-auto h-12 w-12 text-gray-400" />
|
||||||
<h3 className="mt-2 text-sm font-medium text-gray-900">
|
<h3 className="mt-2 text-sm font-medium text-gray-900">Seri numarası bulunamadı</h3>
|
||||||
Seri numarası bulunamadı
|
|
||||||
</h3>
|
|
||||||
<p className="mt-1 text-sm text-gray-500">
|
<p className="mt-1 text-sm text-gray-500">
|
||||||
Arama kriterlerinize uygun seri numarası bulunmuyor.
|
Arama kriterlerinize uygun seri numarası bulunmuyor.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -522,7 +474,8 @@ const InventoryTracking: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
</Container>
|
||||||
};
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default InventoryTracking;
|
export default InventoryTracking
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
FaSearch,
|
FaSearch,
|
||||||
FaMapMarkerAlt,
|
FaMapMarkerAlt,
|
||||||
|
|
@ -7,45 +7,43 @@ import {
|
||||||
FaEye,
|
FaEye,
|
||||||
FaTh,
|
FaTh,
|
||||||
FaList,
|
FaList,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { mockWarehouses } from "../../../mocks/mockWarehouses";
|
import { mockWarehouses } from '../../../mocks/mockWarehouses'
|
||||||
import { mockLocations } from "../../../mocks/mockLocations";
|
import { mockLocations } from '../../../mocks/mockLocations'
|
||||||
import { mockStockItems } from "../../../mocks/mockStockItems";
|
import { mockStockItems } from '../../../mocks/mockStockItems'
|
||||||
import { getStockStatusColor, getStockStatusText } from "../../../utils/erp";
|
import { getStockStatusColor, getStockStatusText } from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const LocationTracking: React.FC = () => {
|
const LocationTracking: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [selectedWarehouse, setSelectedWarehouse] = useState<string>("");
|
const [selectedWarehouse, setSelectedWarehouse] = useState<string>('')
|
||||||
const [selectedLocation, setSelectedLocation] = useState<string>("");
|
const [selectedLocation, setSelectedLocation] = useState<string>('')
|
||||||
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
|
||||||
|
|
||||||
const getLocationUtilization = (locationId: string) => {
|
const getLocationUtilization = (locationId: string) => {
|
||||||
const location = mockLocations.find((l) => l.id === locationId);
|
const location = mockLocations.find((l) => l.id === locationId)
|
||||||
if (!location) return 0;
|
if (!location) return 0
|
||||||
return (location.currentStock / location.capacity) * 100;
|
return (location.currentStock / location.capacity) * 100
|
||||||
};
|
}
|
||||||
|
|
||||||
const getLocationStockItems = (locationId: string) => {
|
const getLocationStockItems = (locationId: string) => {
|
||||||
return mockStockItems.filter((item) => item.locationId === locationId);
|
return mockStockItems.filter((item) => item.locationId === locationId)
|
||||||
};
|
}
|
||||||
|
|
||||||
const filteredLocations = mockLocations.filter((location) => {
|
const filteredLocations = mockLocations.filter((location) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
location.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
location.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
location.locationCode.toLowerCase().includes(searchTerm.toLowerCase());
|
location.locationCode.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
const matchesWarehouse =
|
const matchesWarehouse = selectedWarehouse === '' || location.warehouseId === selectedWarehouse
|
||||||
selectedWarehouse === "" || location.warehouseId === selectedWarehouse;
|
return matchesSearch && matchesWarehouse
|
||||||
return matchesSearch && matchesWarehouse;
|
})
|
||||||
});
|
|
||||||
|
|
||||||
const GridView = () => (
|
const GridView = () => (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{filteredLocations.map((location) => {
|
{filteredLocations.map((location) => {
|
||||||
const warehouse = mockWarehouses.find(
|
const warehouse = mockWarehouses.find((w) => w.id === location.warehouseId)
|
||||||
(w) => w.id === location.warehouseId
|
const locationStockItems = getLocationStockItems(location.id)
|
||||||
);
|
const utilization = getLocationUtilization(location.id)
|
||||||
const locationStockItems = getLocationStockItems(location.id);
|
|
||||||
const utilization = getLocationUtilization(location.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -59,9 +57,7 @@ const LocationTracking: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-gray-900">{location.name}</h3>
|
<h3 className="font-medium text-gray-900">{location.name}</h3>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">{location.locationCode}</p>
|
||||||
{location.locationCode}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
@ -84,18 +80,16 @@ const LocationTracking: React.FC = () => {
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-600">Doluluk Oranı</span>
|
<span className="text-gray-600">Doluluk Oranı</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">%{Math.round(utilization)}</span>
|
||||||
%{Math.round(utilization)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||||
<div
|
<div
|
||||||
className={`h-2 rounded-full ${
|
className={`h-2 rounded-full ${
|
||||||
utilization > 90
|
utilization > 90
|
||||||
? "bg-red-500"
|
? 'bg-red-500'
|
||||||
: utilization > 70
|
: utilization > 70
|
||||||
? "bg-yellow-500"
|
? 'bg-yellow-500'
|
||||||
: "bg-green-500"
|
: 'bg-green-500'
|
||||||
}`}
|
}`}
|
||||||
style={{ width: `${utilization}%` }}
|
style={{ width: `${utilization}%` }}
|
||||||
/>
|
/>
|
||||||
|
|
@ -108,29 +102,22 @@ const LocationTracking: React.FC = () => {
|
||||||
{/* Stock Items Summary */}
|
{/* Stock Items Summary */}
|
||||||
<div className="bg-gray-50 rounded-lg p-2">
|
<div className="bg-gray-50 rounded-lg p-2">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<h4 className="text-sm font-medium text-gray-900">
|
<h4 className="text-sm font-medium text-gray-900">Malzemeler</h4>
|
||||||
Malzemeler
|
<span className="text-xs text-gray-500">{locationStockItems.length} çeşit</span>
|
||||||
</h4>
|
|
||||||
<span className="text-xs text-gray-500">
|
|
||||||
{locationStockItems.length} çeşit
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{locationStockItems.length > 0 ? (
|
{locationStockItems.length > 0 ? (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{locationStockItems.slice(0, 3).map((item) => (
|
{locationStockItems.slice(0, 3).map((item) => (
|
||||||
<div
|
<div key={item.id} className="flex justify-between items-center text-xs">
|
||||||
key={item.id}
|
|
||||||
className="flex justify-between items-center text-xs"
|
|
||||||
>
|
|
||||||
<span className="text-gray-700 truncate">
|
<span className="text-gray-700 truncate">
|
||||||
{item.material?.code || "N/A"}
|
{item.material?.code || 'N/A'}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="font-medium">{item.quantity}</span>
|
<span className="font-medium">{item.quantity}</span>
|
||||||
<span
|
<span
|
||||||
className={`px-1.5 py-0.5 rounded-full text-xs ${getStockStatusColor(
|
className={`px-1.5 py-0.5 rounded-full text-xs ${getStockStatusColor(
|
||||||
item.status
|
item.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getStockStatusText(item.status)}
|
{getStockStatusText(item.status)}
|
||||||
|
|
@ -145,22 +132,16 @@ const LocationTracking: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-xs text-gray-500 text-center py-2">
|
<div className="text-xs text-gray-500 text-center py-2">Malzeme bulunmuyor</div>
|
||||||
Malzeme bulunmuyor
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Restrictions */}
|
{/* Restrictions */}
|
||||||
{location.restrictions && location.restrictions.length > 0 && (
|
{location.restrictions && location.restrictions.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-medium text-gray-900 mb-1">
|
<h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
|
||||||
Kısıtlamalar
|
|
||||||
</h4>
|
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{location.restrictions
|
{location.restrictions.slice(0, 2).map((restriction, index) => (
|
||||||
.slice(0, 2)
|
|
||||||
.map((restriction, index) => (
|
|
||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
className="inline-flex px-2 py-1 text-xs bg-yellow-100 text-yellow-800 rounded"
|
className="inline-flex px-2 py-1 text-xs bg-yellow-100 text-yellow-800 rounded"
|
||||||
|
|
@ -193,23 +174,21 @@ const LocationTracking: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
Son hareket:{" "}
|
Son hareket:{' '}
|
||||||
{locationStockItems.length > 0
|
{locationStockItems.length > 0
|
||||||
? new Date(
|
? new Date(
|
||||||
Math.max(
|
Math.max(
|
||||||
...locationStockItems.map((item) =>
|
...locationStockItems.map((item) => item.lastMovementDate.getTime()),
|
||||||
item.lastMovementDate.getTime()
|
),
|
||||||
|
).toLocaleDateString('tr-TR')
|
||||||
|
: 'N/A'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
)
|
|
||||||
).toLocaleDateString("tr-TR")
|
|
||||||
: "N/A"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const ListView = () => (
|
const ListView = () => (
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||||
|
|
@ -242,11 +221,9 @@ const LocationTracking: React.FC = () => {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{filteredLocations.map((location) => {
|
{filteredLocations.map((location) => {
|
||||||
const warehouse = mockWarehouses.find(
|
const warehouse = mockWarehouses.find((w) => w.id === location.warehouseId)
|
||||||
(w) => w.id === location.warehouseId
|
const locationStockItems = getLocationStockItems(location.id)
|
||||||
);
|
const utilization = getLocationUtilization(location.id)
|
||||||
const locationStockItems = getLocationStockItems(location.id);
|
|
||||||
const utilization = getLocationUtilization(location.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={location.id} className="hover:bg-gray-50">
|
<tr key={location.id} className="hover:bg-gray-50">
|
||||||
|
|
@ -256,22 +233,14 @@ const LocationTracking: React.FC = () => {
|
||||||
<FaMapMarkerAlt className="w-4 h-4 text-blue-600" />
|
<FaMapMarkerAlt className="w-4 h-4 text-blue-600" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<div className="text-sm font-medium text-gray-900">{location.name}</div>
|
||||||
{location.name}
|
<div className="text-sm text-gray-500">{location.locationCode}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{location.locationCode}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<div className="text-sm text-gray-900">
|
<div className="text-sm text-gray-900">{warehouse?.name}</div>
|
||||||
{warehouse?.name}
|
<div className="text-sm text-gray-500">{warehouse?.code}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{warehouse?.code}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
|
@ -279,17 +248,15 @@ const LocationTracking: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
className={`h-2 rounded-full ${
|
className={`h-2 rounded-full ${
|
||||||
utilization > 90
|
utilization > 90
|
||||||
? "bg-red-500"
|
? 'bg-red-500'
|
||||||
: utilization > 70
|
: utilization > 70
|
||||||
? "bg-yellow-500"
|
? 'bg-yellow-500'
|
||||||
: "bg-green-500"
|
: 'bg-green-500'
|
||||||
}`}
|
}`}
|
||||||
style={{ width: `${utilization}%` }}
|
style={{ width: `${utilization}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm text-gray-900">
|
<span className="text-sm text-gray-900">%{Math.round(utilization)}</span>
|
||||||
%{Math.round(utilization)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
{location.currentStock} / {location.capacity}
|
{location.currentStock} / {location.capacity}
|
||||||
|
|
@ -302,12 +269,10 @@ const LocationTracking: React.FC = () => {
|
||||||
{locationStockItems.length > 0
|
{locationStockItems.length > 0
|
||||||
? new Date(
|
? new Date(
|
||||||
Math.max(
|
Math.max(
|
||||||
...locationStockItems.map((item) =>
|
...locationStockItems.map((item) => item.lastMovementDate.getTime()),
|
||||||
item.lastMovementDate.getTime()
|
),
|
||||||
)
|
).toLocaleDateString('tr-TR')
|
||||||
)
|
: 'N/A'}
|
||||||
).toLocaleDateString("tr-TR")
|
|
||||||
: "N/A"}
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
{location.isActive ? (
|
{location.isActive ? (
|
||||||
|
|
@ -331,44 +296,37 @@ const LocationTracking: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const LocationDetailModal = () => {
|
const LocationDetailModal = () => {
|
||||||
const location = mockLocations.find((l) => l.id === selectedLocation);
|
const location = mockLocations.find((l) => l.id === selectedLocation)
|
||||||
const locationStockItems = getLocationStockItems(selectedLocation);
|
const locationStockItems = getLocationStockItems(selectedLocation)
|
||||||
|
|
||||||
if (!selectedLocation || !location) return null;
|
if (!selectedLocation || !location) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||||
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||||
onClick={() => setSelectedLocation("")}
|
onClick={() => setSelectedLocation('')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
||||||
<div className="bg-white px-4 pt-4 pb-4 sm:p-4">
|
<div className="bg-white px-4 pt-4 pb-4 sm:p-4">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="text-lg font-medium text-gray-900">
|
<h3 className="text-lg font-medium text-gray-900">{location.name} - Detaylar</h3>
|
||||||
{location.name} - Detaylar
|
|
||||||
</h3>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedLocation("")}
|
onClick={() => setSelectedLocation('')}
|
||||||
className="text-gray-400 hover:text-gray-600"
|
className="text-gray-400 hover:text-gray-600"
|
||||||
>
|
>
|
||||||
<svg
|
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
className="w-6 h-6"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
|
|
@ -382,9 +340,7 @@ const LocationTracking: React.FC = () => {
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
{/* Location Info */}
|
{/* Location Info */}
|
||||||
<div className="bg-gray-50 rounded-lg p-3">
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
<h4 className="font-medium text-gray-900 mb-3">
|
<h4 className="font-medium text-gray-900 mb-3">Lokasyon Bilgileri</h4>
|
||||||
Lokasyon Bilgileri
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<strong>Kod:</strong> {location.locationCode}
|
<strong>Kod:</strong> {location.locationCode}
|
||||||
|
|
@ -399,14 +355,12 @@ const LocationTracking: React.FC = () => {
|
||||||
<strong>Kapasite:</strong> {location.capacity} birim
|
<strong>Kapasite:</strong> {location.capacity} birim
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Mevcut Stok:</strong> {location.currentStock}{" "}
|
<strong>Mevcut Stok:</strong> {location.currentStock} birim
|
||||||
birim
|
|
||||||
</div>
|
</div>
|
||||||
{location.dimensions && (
|
{location.dimensions && (
|
||||||
<div>
|
<div>
|
||||||
<strong>Boyutlar:</strong> {location.dimensions.length}x
|
<strong>Boyutlar:</strong> {location.dimensions.length}x
|
||||||
{location.dimensions.width}x{location.dimensions.height}
|
{location.dimensions.width}x{location.dimensions.height}m
|
||||||
m
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -419,22 +373,15 @@ const LocationTracking: React.FC = () => {
|
||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-2 max-h-64 overflow-y-auto">
|
<div className="space-y-2 max-h-64 overflow-y-auto">
|
||||||
{locationStockItems.map((item) => (
|
{locationStockItems.map((item) => (
|
||||||
<div
|
<div key={item.id} className="bg-white rounded p-3 border">
|
||||||
key={item.id}
|
|
||||||
className="bg-white rounded p-3 border"
|
|
||||||
>
|
|
||||||
<div className="flex justify-between items-start mb-2">
|
<div className="flex justify-between items-start mb-2">
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-sm">
|
<div className="font-medium text-sm">{item.material?.code}</div>
|
||||||
{item.material?.code}
|
<div className="text-xs text-gray-500">{item.material?.code}</div>
|
||||||
</div>
|
|
||||||
<div className="text-xs text-gray-500">
|
|
||||||
{item.material?.code}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs ${getStockStatusColor(
|
className={`px-2 py-1 rounded-full text-xs ${getStockStatusColor(
|
||||||
item.status
|
item.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getStockStatusText(item.status)}
|
{getStockStatusText(item.status)}
|
||||||
|
|
@ -450,11 +397,10 @@ const LocationTracking: React.FC = () => {
|
||||||
<div>
|
<div>
|
||||||
Rezerve: {item.reservedQuantity} {item.unitId}
|
Rezerve: {item.reservedQuantity} {item.unitId}
|
||||||
</div>
|
</div>
|
||||||
<div>Lot: {item.lotNumber || "N/A"}</div>
|
<div>Lot: {item.lotNumber || 'N/A'}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 text-xs text-gray-500">
|
<div className="mt-2 text-xs text-gray-500">
|
||||||
Son hareket:{" "}
|
Son hareket: {item.lastMovementDate.toLocaleDateString('tr-TR')}
|
||||||
{item.lastMovementDate.toLocaleDateString("tr-TR")}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -470,38 +416,35 @@ const LocationTracking: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">Raf/Lokasyon Bazlı Takip</h2>
|
||||||
Raf/Lokasyon Bazlı Takip
|
<p className="text-gray-600">Lokasyonlardaki stok durumunu takip edin</p>
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Lokasyonlardaki stok durumunu takip edin
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("grid")}
|
onClick={() => setViewMode('grid')}
|
||||||
className={`p-1.5 rounded-lg ${
|
className={`p-1.5 rounded-lg ${
|
||||||
viewMode === "grid"
|
viewMode === 'grid'
|
||||||
? "bg-blue-100 text-blue-600"
|
? 'bg-blue-100 text-blue-600'
|
||||||
: "text-gray-400 hover:text-gray-600"
|
: 'text-gray-400 hover:text-gray-600'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FaTh className="w-4 h-4" />
|
<FaTh className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("list")}
|
onClick={() => setViewMode('list')}
|
||||||
className={`p-1.5 rounded-lg ${
|
className={`p-1.5 rounded-lg ${
|
||||||
viewMode === "list"
|
viewMode === 'list'
|
||||||
? "bg-blue-100 text-blue-600"
|
? 'bg-blue-100 text-blue-600'
|
||||||
: "text-gray-400 hover:text-gray-600"
|
: 'text-gray-400 hover:text-gray-600'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FaList className="w-4 h-4" />
|
<FaList className="w-4 h-4" />
|
||||||
|
|
@ -536,12 +479,13 @@ const LocationTracking: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
{viewMode === "grid" ? <GridView /> : <ListView />}
|
{viewMode === 'grid' ? <GridView /> : <ListView />}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Location Detail Modal */}
|
{/* Location Detail Modal */}
|
||||||
<LocationDetailModal />
|
<LocationDetailModal />
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default LocationTracking;
|
export default LocationTracking
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,26 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { useFormik } from "formik";
|
import { useFormik } from 'formik'
|
||||||
import * as Yup from "yup";
|
import * as Yup from 'yup'
|
||||||
import { FaSave, FaTimes } from "react-icons/fa";
|
import { FaSave, FaTimes } from 'react-icons/fa'
|
||||||
import {
|
import { QualityStatusEnum, MmLotNumber, MmUnit, MmMaterial } from '../../../types/mm'
|
||||||
QualityStatusEnum,
|
import { BusinessParty } from '../../../types/common'
|
||||||
MmLotNumber,
|
|
||||||
MmUnit,
|
|
||||||
MmMaterial,
|
|
||||||
} from "../../../types/mm";
|
|
||||||
import { BusinessParty } from "../../../types/common";
|
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
materialId: Yup.string().required("Malzeme seçimi zorunlu"),
|
materialId: Yup.string().required('Malzeme seçimi zorunlu'),
|
||||||
lotNumber: Yup.string().required("Lot numarası zorunlu"),
|
lotNumber: Yup.string().required('Lot numarası zorunlu'),
|
||||||
quantity: Yup.number().min(0, "Miktar 0'dan büyük olmalıdır"),
|
quantity: Yup.number().min(0, "Miktar 0'dan büyük olmalıdır"),
|
||||||
});
|
})
|
||||||
|
|
||||||
interface LotFormProps {
|
interface LotFormProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (lot: MmLotNumber) => void;
|
onSave: (lot: MmLotNumber) => void
|
||||||
onUpdate?: (lot: MmLotNumber) => void;
|
onUpdate?: (lot: MmLotNumber) => void
|
||||||
materials: MmMaterial[];
|
materials: MmMaterial[]
|
||||||
suppliers?: BusinessParty[];
|
suppliers?: BusinessParty[]
|
||||||
units?: MmUnit[];
|
units?: MmUnit[]
|
||||||
initial?: MmLotNumber | null;
|
initial?: MmLotNumber | null
|
||||||
mode?: "create" | "edit" | "view";
|
mode?: 'create' | 'edit' | 'view'
|
||||||
}
|
}
|
||||||
|
|
||||||
const LotForm: React.FC<LotFormProps> = ({
|
const LotForm: React.FC<LotFormProps> = ({
|
||||||
|
|
@ -37,17 +32,17 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
suppliers = [],
|
suppliers = [],
|
||||||
units = [],
|
units = [],
|
||||||
initial = null,
|
initial = null,
|
||||||
mode = "create",
|
mode = 'create',
|
||||||
}) => {
|
}) => {
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
materialId: "",
|
materialId: '',
|
||||||
lotNumber: "",
|
lotNumber: '',
|
||||||
productionDate: "",
|
productionDate: '',
|
||||||
expiryDate: "",
|
expiryDate: '',
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
unitId: "KG",
|
unitId: 'KG',
|
||||||
supplierId: suppliers.length ? suppliers[0].id : "",
|
supplierId: suppliers.length ? suppliers[0].id : '',
|
||||||
qualityStatus: QualityStatusEnum.Pending,
|
qualityStatus: QualityStatusEnum.Pending,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
|
|
@ -57,52 +52,48 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
id: (initial && initial.id) || Date.now().toString(),
|
id: (initial && initial.id) || Date.now().toString(),
|
||||||
materialId: values.materialId,
|
materialId: values.materialId,
|
||||||
lotNumber: values.lotNumber,
|
lotNumber: values.lotNumber,
|
||||||
productionDate: values.productionDate
|
productionDate: values.productionDate ? new Date(values.productionDate) : new Date(),
|
||||||
? new Date(values.productionDate)
|
|
||||||
: new Date(),
|
|
||||||
expiryDate: values.expiryDate ? new Date(values.expiryDate) : undefined,
|
expiryDate: values.expiryDate ? new Date(values.expiryDate) : undefined,
|
||||||
quantity: Number(values.quantity),
|
quantity: Number(values.quantity),
|
||||||
unitId: values.unitId,
|
unitId: values.unitId,
|
||||||
supplierId: values.supplierId || undefined,
|
supplierId: values.supplierId || undefined,
|
||||||
qualityStatus: values.qualityStatus,
|
qualityStatus: values.qualityStatus,
|
||||||
isActive: !!values.isActive,
|
isActive: !!values.isActive,
|
||||||
};
|
}
|
||||||
|
|
||||||
// simulate API
|
// simulate API
|
||||||
await new Promise((r) => setTimeout(r, 300));
|
await new Promise((r) => setTimeout(r, 300))
|
||||||
if (mode === "edit" && onUpdate) {
|
if (mode === 'edit' && onUpdate) {
|
||||||
onUpdate(newLot);
|
onUpdate(newLot)
|
||||||
} else {
|
} else {
|
||||||
onSave(newLot);
|
onSave(newLot)
|
||||||
}
|
}
|
||||||
onClose();
|
onClose()
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
// sync initial values when editing/viewing
|
// sync initial values when editing/viewing
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (initial) {
|
if (initial) {
|
||||||
const src = initial;
|
const src = initial
|
||||||
formik.setValues({
|
formik.setValues({
|
||||||
materialId: src.materialId || "",
|
materialId: src.materialId || '',
|
||||||
lotNumber: src.lotNumber || "",
|
lotNumber: src.lotNumber || '',
|
||||||
productionDate: src.productionDate
|
productionDate: src.productionDate
|
||||||
? new Date(src.productionDate).toISOString().slice(0, 10)
|
? new Date(src.productionDate).toISOString().slice(0, 10)
|
||||||
: "",
|
: '',
|
||||||
expiryDate: src.expiryDate
|
expiryDate: src.expiryDate ? new Date(src.expiryDate).toISOString().slice(0, 10) : '',
|
||||||
? new Date(src.expiryDate).toISOString().slice(0, 10)
|
|
||||||
: "",
|
|
||||||
quantity: src.quantity || 0,
|
quantity: src.quantity || 0,
|
||||||
unitId: src.unitId || (units.length ? units[0].id : ""),
|
unitId: src.unitId || (units.length ? units[0].id : ''),
|
||||||
supplierId: src.supplierId || (suppliers.length ? suppliers[0].id : ""),
|
supplierId: src.supplierId || (suppliers.length ? suppliers[0].id : ''),
|
||||||
qualityStatus: src.qualityStatus || QualityStatusEnum.Pending,
|
qualityStatus: src.qualityStatus || QualityStatusEnum.Pending,
|
||||||
isActive: !!src.isActive,
|
isActive: !!src.isActive,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [initial]);
|
}, [initial])
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||||
|
|
@ -112,18 +103,18 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
<div className="flex items-center justify-between p-2 border-b">
|
<div className="flex items-center justify-between p-2 border-b">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">
|
||||||
{mode === "create"
|
{mode === 'create'
|
||||||
? "Yeni Lot Kaydı"
|
? 'Yeni Lot Kaydı'
|
||||||
: mode === "edit"
|
: mode === 'edit'
|
||||||
? "Lot Düzenle"
|
? 'Lot Düzenle'
|
||||||
: "Lot Detayı"}
|
: 'Lot Detayı'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
{mode === "create"
|
{mode === 'create'
|
||||||
? "Lot bilgilerini girin"
|
? 'Lot bilgilerini girin'
|
||||||
: mode === "edit"
|
: mode === 'edit'
|
||||||
? "Mevcut lot bilgilerini güncelleyin"
|
? 'Mevcut lot bilgilerini güncelleyin'
|
||||||
: "Lot bilgileri (sadece gösterim)"}
|
: 'Lot bilgileri (sadece gösterim)'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -134,7 +125,7 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
>
|
>
|
||||||
<FaTimes className="inline mr-1 h-3 w-3" /> Kapat
|
<FaTimes className="inline mr-1 h-3 w-3" /> Kapat
|
||||||
</button>
|
</button>
|
||||||
{mode !== "view" && (
|
{mode !== 'view' && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"
|
className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"
|
||||||
|
|
@ -147,12 +138,10 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Malzeme *</label>
|
||||||
Malzeme *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
{...formik.getFieldProps("materialId")}
|
{...formik.getFieldProps('materialId')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
>
|
>
|
||||||
<option value="">Seçiniz...</option>
|
<option value="">Seçiniz...</option>
|
||||||
|
|
@ -165,61 +154,51 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Lot Numarası *</label>
|
||||||
Lot Numarası *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
{...formik.getFieldProps("lotNumber")}
|
{...formik.getFieldProps('lotNumber')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Üretim Tarihi</label>
|
||||||
Üretim Tarihi
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
{...formik.getFieldProps("productionDate")}
|
{...formik.getFieldProps('productionDate')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Son Kullanma Tarihi</label>
|
||||||
Son Kullanma Tarihi
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
{...formik.getFieldProps("expiryDate")}
|
{...formik.getFieldProps('expiryDate')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Miktar</label>
|
||||||
Miktar
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
{...formik.getFieldProps("quantity")}
|
{...formik.getFieldProps('quantity')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Birim</label>
|
||||||
Birim
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
{...formik.getFieldProps("unitId")}
|
{...formik.getFieldProps('unitId')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
>
|
>
|
||||||
{units.map((u) => (
|
{units.map((u) => (
|
||||||
|
|
@ -231,12 +210,10 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Tedarikçi</label>
|
||||||
Tedarikçi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
{...formik.getFieldProps("supplierId")}
|
{...formik.getFieldProps('supplierId')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
>
|
>
|
||||||
<option value="">Seçiniz...</option>
|
<option value="">Seçiniz...</option>
|
||||||
|
|
@ -249,11 +226,9 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Kalite Durumu</label>
|
||||||
Kalite Durumu
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
{...formik.getFieldProps("qualityStatus")}
|
{...formik.getFieldProps('qualityStatus')}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
>
|
>
|
||||||
<option value={QualityStatusEnum.Pending}>Beklemede</option>
|
<option value={QualityStatusEnum.Pending}>Beklemede</option>
|
||||||
|
|
@ -266,7 +241,7 @@ const LotForm: React.FC<LotFormProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default LotForm;
|
export default LotForm
|
||||||
|
|
|
||||||
|
|
@ -1,92 +1,81 @@
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from 'react'
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from 'react-router-dom'
|
||||||
import { FaDownload, FaCalendar, FaBox } from "react-icons/fa";
|
import { FaDownload, FaCalendar, FaBox } from 'react-icons/fa'
|
||||||
import { MmStockMovement, MovementTypeEnum } from "../../../types/mm";
|
import { MmStockMovement, MovementTypeEnum } from '../../../types/mm'
|
||||||
import { FaRepeat } from "react-icons/fa6";
|
import { FaRepeat } from 'react-icons/fa6'
|
||||||
import { getMovementTypeInfo } from "../../../utils/erp";
|
import { getMovementTypeInfo } from '../../../utils/erp'
|
||||||
import { mockStockMovements } from "../../../mocks/mockStockMovements";
|
import { mockStockMovements } from '../../../mocks/mockStockMovements'
|
||||||
import Widget, { colorType } from "../../../components/common/Widget";
|
import Widget, { colorType } from '../../../components/common/Widget'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
const MaterialMovements: React.FC<{ materialId?: string }> = ({ materialId }) => {
|
||||||
materialId,
|
const params = useParams<Record<string, string | undefined>>()
|
||||||
}) => {
|
const routeMaterialId = materialId || params.materialId || params.id || undefined
|
||||||
const params = useParams<Record<string, string | undefined>>();
|
const [movements, setMovements] = useState<MmStockMovement[]>([])
|
||||||
const routeMaterialId =
|
const [loading, setLoading] = useState(false)
|
||||||
materialId || params.materialId || params.id || undefined;
|
|
||||||
const [movements, setMovements] = useState<MmStockMovement[]>([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [filters, setFilters] = useState({
|
const [filters, setFilters] = useState({
|
||||||
startDate: "",
|
startDate: '',
|
||||||
endDate: "",
|
endDate: '',
|
||||||
movementType: "",
|
movementType: '',
|
||||||
warehouseId: "",
|
warehouseId: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const loadMovements = useCallback(() => {
|
const loadMovements = useCallback(() => {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
// Simulate API call
|
// Simulate API call
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let filteredMovements = mockStockMovements;
|
let filteredMovements = mockStockMovements
|
||||||
|
|
||||||
const activeMaterialId = routeMaterialId;
|
const activeMaterialId = routeMaterialId
|
||||||
if (activeMaterialId) {
|
if (activeMaterialId) {
|
||||||
filteredMovements = filteredMovements.filter(
|
filteredMovements = filteredMovements.filter((m) => m.materialId === activeMaterialId)
|
||||||
(m) => m.materialId === activeMaterialId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters.movementType) {
|
if (filters.movementType) {
|
||||||
filteredMovements = filteredMovements.filter(
|
filteredMovements = filteredMovements.filter(
|
||||||
(m) =>
|
(m) => m.movementType === (filters.movementType as unknown as MovementTypeEnum),
|
||||||
m.movementType ===
|
)
|
||||||
(filters.movementType as unknown as MovementTypeEnum)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setMovements(filteredMovements);
|
setMovements(filteredMovements)
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
}, 1000);
|
}, 1000)
|
||||||
}, [routeMaterialId, filters]);
|
}, [routeMaterialId, filters])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadMovements();
|
loadMovements()
|
||||||
}, [loadMovements]);
|
}, [loadMovements])
|
||||||
|
|
||||||
const calculateRunningBalance = () => {
|
const calculateRunningBalance = () => {
|
||||||
let balance = 0;
|
let balance = 0
|
||||||
return movements
|
return movements
|
||||||
.sort(
|
.sort((a, b) => new Date(a.movementDate).getTime() - new Date(b.movementDate).getTime())
|
||||||
(a, b) =>
|
|
||||||
new Date(a.movementDate).getTime() -
|
|
||||||
new Date(b.movementDate).getTime()
|
|
||||||
)
|
|
||||||
.map((movement) => {
|
.map((movement) => {
|
||||||
balance += movement.quantity;
|
balance += movement.quantity
|
||||||
return { ...movement, runningBalance: balance };
|
return { ...movement, runningBalance: balance }
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const movementsWithBalance = calculateRunningBalance();
|
const movementsWithBalance = calculateRunningBalance()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 py-2">
|
<Container>
|
||||||
|
<div className="space-y-2">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">
|
||||||
{materialId ? "Stok Hareketleri" : "Tüm Stok Hareketleri"}
|
{materialId ? 'Stok Hareketleri' : 'Tüm Stok Hareketleri'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
{materialId
|
{materialId ? `${materialId} kodlu malzeme için` : 'Tüm malzemeler için'} stok
|
||||||
? `${materialId} kodlu malzeme için`
|
hareketlerini görüntüleyin
|
||||||
: "Tüm malzemeler için"}{" "}
|
|
||||||
stok hareketlerini görüntüleyin
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-1.5">
|
<div className="flex items-center space-x-1.5">
|
||||||
<button
|
<button
|
||||||
onClick={() => alert("Dışa aktarma özelliği yakında eklenecek")}
|
onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
|
||||||
className="bg-gray-600 text-white px-2.5 py-1 rounded-md hover:bg-gray-700 flex items-center space-x-1.5 text-sm"
|
className="bg-gray-600 text-white px-2.5 py-1 rounded-md hover:bg-gray-700 flex items-center space-x-1.5 text-sm"
|
||||||
>
|
>
|
||||||
<FaDownload className="h-4 w-4" />
|
<FaDownload className="h-4 w-4" />
|
||||||
|
|
@ -106,28 +95,28 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
title: "Toplam Giriş",
|
title: 'Toplam Giriş',
|
||||||
value: "125 KG",
|
value: '125 KG',
|
||||||
color: "green",
|
color: 'green',
|
||||||
icon: "FaArrowDown",
|
icon: 'FaArrowDown',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Toplam Çıkış",
|
title: 'Toplam Çıkış',
|
||||||
value: "50 KG",
|
value: '50 KG',
|
||||||
color: "red",
|
color: 'red',
|
||||||
icon: "FaArrowUp",
|
icon: 'FaArrowUp',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Net Bakiye",
|
title: 'Net Bakiye',
|
||||||
value: "75 KG",
|
value: '75 KG',
|
||||||
color: "blue",
|
color: 'blue',
|
||||||
icon: "FaBalanceScale",
|
icon: 'FaBalanceScale',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Hareket Sayısı",
|
title: 'Hareket Sayısı',
|
||||||
value: movements.length,
|
value: movements.length,
|
||||||
color: "gray",
|
color: 'gray',
|
||||||
icon: "FaExchangeAlt",
|
icon: 'FaExchangeAlt',
|
||||||
},
|
},
|
||||||
].map((stat) => (
|
].map((stat) => (
|
||||||
<Widget
|
<Widget
|
||||||
|
|
@ -150,36 +139,26 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.startDate}
|
value={filters.startDate}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFilters({ ...filters, startDate: e.target.value })}
|
||||||
setFilters({ ...filters, startDate: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
<label className="block text-xs font-medium text-gray-700 mb-1">Bitiş Tarihi</label>
|
||||||
Bitiş Tarihi
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.endDate}
|
value={filters.endDate}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFilters({ ...filters, endDate: e.target.value })}
|
||||||
setFilters({ ...filters, endDate: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
<label className="block text-xs font-medium text-gray-700 mb-1">Hareket Tipi</label>
|
||||||
Hareket Tipi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={filters.movementType}
|
value={filters.movementType}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFilters({ ...filters, movementType: e.target.value })}
|
||||||
setFilters({ ...filters, movementType: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">Tümü</option>
|
<option value="">Tümü</option>
|
||||||
|
|
@ -190,14 +169,10 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
<label className="block text-xs font-medium text-gray-700 mb-1">Depo</label>
|
||||||
Depo
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={filters.warehouseId}
|
value={filters.warehouseId}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFilters({ ...filters, warehouseId: e.target.value })}
|
||||||
setFilters({ ...filters, warehouseId: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">Tümü</option>
|
<option value="">Tümü</option>
|
||||||
|
|
@ -257,8 +232,8 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{movementsWithBalance.map((movement) => {
|
{movementsWithBalance.map((movement) => {
|
||||||
const typeInfo = getMovementTypeInfo(movement.movementType);
|
const typeInfo = getMovementTypeInfo(movement.movementType)
|
||||||
const Icon = typeInfo.icon;
|
const Icon = typeInfo.icon
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={movement.id} className="hover:bg-gray-50 text-xs">
|
<tr key={movement.id} className="hover:bg-gray-50 text-xs">
|
||||||
|
|
@ -267,14 +242,10 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
<FaCalendar className="h-4 w-4 text-gray-400" />
|
<FaCalendar className="h-4 w-4 text-gray-400" />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs font-medium text-gray-900">
|
<div className="text-xs font-medium text-gray-900">
|
||||||
{new Date(
|
{new Date(movement.movementDate).toLocaleDateString('tr-TR')}
|
||||||
movement.movementDate
|
|
||||||
).toLocaleDateString("tr-TR")}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
{new Date(
|
{new Date(movement.movementDate).toLocaleTimeString('tr-TR')}
|
||||||
movement.movementDate
|
|
||||||
).toLocaleTimeString("tr-TR")}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -298,17 +269,13 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<span
|
<span
|
||||||
className={`text-sm font-medium ${
|
className={`text-sm font-medium ${
|
||||||
movement.quantity > 0
|
movement.quantity > 0 ? 'text-green-600' : 'text-red-600'
|
||||||
? "text-green-600"
|
|
||||||
: "text-red-600"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{movement.quantity > 0 ? "+" : ""}
|
{movement.quantity > 0 ? '+' : ''}
|
||||||
{movement.quantity}
|
{movement.quantity}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500">
|
<span className="text-xs text-gray-500">{movement.unit?.code}</span>
|
||||||
{movement.unit?.code}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
|
|
@ -351,12 +318,10 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1.5">
|
<td className="px-2 py-1.5">
|
||||||
<div className="text-xs text-gray-900">
|
<div className="text-xs text-gray-900">{movement.description}</div>
|
||||||
{movement.description}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -366,9 +331,7 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
{!loading && movements.length === 0 && (
|
{!loading && movements.length === 0 && (
|
||||||
<div className="text-center py-10">
|
<div className="text-center py-10">
|
||||||
<FaBox className="mx-auto h-10 w-10 text-gray-400" />
|
<FaBox className="mx-auto h-10 w-10 text-gray-400" />
|
||||||
<h3 className="mt-2 text-base font-medium text-gray-900">
|
<h3 className="mt-2 text-base font-medium text-gray-900">Hareket bulunamadı</h3>
|
||||||
Hareket bulunamadı
|
|
||||||
</h3>
|
|
||||||
<p className="mt-1 text-xs text-gray-500">
|
<p className="mt-1 text-xs text-gray-500">
|
||||||
Seçilen kriterlere uygun hareket kaydı bulunmuyor.
|
Seçilen kriterlere uygun hareket kaydı bulunmuyor.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -376,7 +339,8 @@ const MaterialMovements: React.FC<{ materialId?: string }> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
</Container>
|
||||||
};
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default MaterialMovements;
|
export default MaterialMovements
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { PutawayStrategyEnum } from "../../../types/wm";
|
import { PutawayStrategyEnum } from '../../../types/wm'
|
||||||
import {
|
import {
|
||||||
FaSearch,
|
FaSearch,
|
||||||
FaPlus,
|
FaPlus,
|
||||||
|
|
@ -13,174 +13,161 @@ import {
|
||||||
FaArrowDown,
|
FaArrowDown,
|
||||||
FaTh,
|
FaTh,
|
||||||
FaList,
|
FaList,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { mockPutawayRules } from "../../../mocks/mockPutawayRules";
|
import { mockPutawayRules } from '../../../mocks/mockPutawayRules'
|
||||||
import { mockWarehouses } from "../../../mocks/mockWarehouses";
|
import { mockWarehouses } from '../../../mocks/mockWarehouses'
|
||||||
import { mockZones } from "../../../mocks/mockZones";
|
import { mockZones } from '../../../mocks/mockZones'
|
||||||
import { mockLocations } from "../../../mocks/mockLocations";
|
import { mockLocations } from '../../../mocks/mockLocations'
|
||||||
import { mockMaterialTypes } from "../../../mocks/mockMaterialTypes";
|
import { mockMaterialTypes } from '../../../mocks/mockMaterialTypes'
|
||||||
import { mockMaterialGroups } from "../../../mocks/mockMaterialGroups";
|
import { mockMaterialGroups } from '../../../mocks/mockMaterialGroups'
|
||||||
import {
|
import {
|
||||||
getPutawayStrategyColor,
|
getPutawayStrategyColor,
|
||||||
getPutawayStrategyText,
|
getPutawayStrategyText,
|
||||||
getConditionTypeText,
|
getConditionTypeText,
|
||||||
getConditionOperatorText,
|
getConditionOperatorText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const PutawayRules: React.FC = () => {
|
const PutawayRules: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [selectedStrategy, setSelectedStrategy] = useState<
|
const [selectedStrategy, setSelectedStrategy] = useState<PutawayStrategyEnum | ''>('')
|
||||||
PutawayStrategyEnum | ""
|
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
|
||||||
>("");
|
const [showRuleForm, setShowRuleForm] = useState(false)
|
||||||
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
const [editingRuleId, setEditingRuleId] = useState<string | null>(null)
|
||||||
const [showRuleForm, setShowRuleForm] = useState(false);
|
const [selectedRule, setSelectedRule] = useState<string>('')
|
||||||
const [editingRuleId, setEditingRuleId] = useState<string | null>(null);
|
|
||||||
const [selectedRule, setSelectedRule] = useState<string>("");
|
|
||||||
|
|
||||||
// Form States
|
// Form States
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: "",
|
name: '',
|
||||||
ruleCode: "",
|
ruleCode: '',
|
||||||
description: "",
|
description: '',
|
||||||
warehouseId: "",
|
warehouseId: '',
|
||||||
materialTypeId: "",
|
materialTypeId: '',
|
||||||
materialGroupId: "",
|
materialGroupId: '',
|
||||||
priority: 1,
|
priority: 1,
|
||||||
targetZoneId: "",
|
targetZoneId: '',
|
||||||
targetLocationId: "",
|
targetLocationId: '',
|
||||||
strategy: PutawayStrategyEnum.FIFO,
|
strategy: PutawayStrategyEnum.FIFO,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
// Helper functions for names
|
// Helper functions for names
|
||||||
const getWarehouseName = (warehouseId: string | undefined) => {
|
const getWarehouseName = (warehouseId: string | undefined) => {
|
||||||
if (!warehouseId) return "Belirtilmemiş";
|
if (!warehouseId) return 'Belirtilmemiş'
|
||||||
const warehouse = mockWarehouses.find((w) => w.id === warehouseId);
|
const warehouse = mockWarehouses.find((w) => w.id === warehouseId)
|
||||||
return warehouse ? warehouse.name : `Depo ${warehouseId}`;
|
return warehouse ? warehouse.name : `Depo ${warehouseId}`
|
||||||
};
|
}
|
||||||
|
|
||||||
const getZoneName = (zoneId: string | undefined) => {
|
const getZoneName = (zoneId: string | undefined) => {
|
||||||
if (!zoneId) return "Belirtilmemiş";
|
if (!zoneId) return 'Belirtilmemiş'
|
||||||
const zone = mockZones.find((z) => z.id === zoneId);
|
const zone = mockZones.find((z) => z.id === zoneId)
|
||||||
return zone ? zone.name : `Bölge ${zoneId}`;
|
return zone ? zone.name : `Bölge ${zoneId}`
|
||||||
};
|
}
|
||||||
|
|
||||||
const getLocationName = (locationId: string | undefined) => {
|
const getLocationName = (locationId: string | undefined) => {
|
||||||
if (!locationId) return "Belirtilmemiş";
|
if (!locationId) return 'Belirtilmemiş'
|
||||||
const location = mockLocations.find((l) => l.id === locationId);
|
const location = mockLocations.find((l) => l.id === locationId)
|
||||||
return location ? location.name : `Lokasyon ${locationId}`;
|
return location ? location.name : `Lokasyon ${locationId}`
|
||||||
};
|
}
|
||||||
|
|
||||||
const getMaterialTypeName = (materialTypeId: string | undefined) => {
|
const getMaterialTypeName = (materialTypeId: string | undefined) => {
|
||||||
if (!materialTypeId) return "Belirtilmemiş";
|
if (!materialTypeId) return 'Belirtilmemiş'
|
||||||
const materialType = mockMaterialTypes.find(
|
const materialType = mockMaterialTypes.find((mt) => mt.id === materialTypeId)
|
||||||
(mt) => mt.id === materialTypeId
|
return materialType ? materialType.name : `Tip ${materialTypeId}`
|
||||||
);
|
}
|
||||||
return materialType ? materialType.name : `Tip ${materialTypeId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMaterialGroupName = (materialGroupId: string | undefined) => {
|
const getMaterialGroupName = (materialGroupId: string | undefined) => {
|
||||||
if (!materialGroupId) return "Belirtilmemiş";
|
if (!materialGroupId) return 'Belirtilmemiş'
|
||||||
const materialGroup = mockMaterialGroups.find(
|
const materialGroup = mockMaterialGroups.find((mg) => mg.id === materialGroupId)
|
||||||
(mg) => mg.id === materialGroupId
|
return materialGroup ? materialGroup.name : `Grup ${materialGroupId}`
|
||||||
);
|
}
|
||||||
return materialGroup ? materialGroup.name : `Grup ${materialGroupId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Form Functions
|
// Form Functions
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setFormData({
|
setFormData({
|
||||||
name: "",
|
name: '',
|
||||||
ruleCode: "",
|
ruleCode: '',
|
||||||
description: "",
|
description: '',
|
||||||
warehouseId: "",
|
warehouseId: '',
|
||||||
materialTypeId: "",
|
materialTypeId: '',
|
||||||
materialGroupId: "",
|
materialGroupId: '',
|
||||||
priority: 1,
|
priority: 1,
|
||||||
targetZoneId: "",
|
targetZoneId: '',
|
||||||
targetLocationId: "",
|
targetLocationId: '',
|
||||||
strategy: PutawayStrategyEnum.FIFO,
|
strategy: PutawayStrategyEnum.FIFO,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEditRule = (ruleId: string) => {
|
const handleEditRule = (ruleId: string) => {
|
||||||
const rule = mockPutawayRules.find((r) => r.id === ruleId);
|
const rule = mockPutawayRules.find((r) => r.id === ruleId)
|
||||||
if (rule) {
|
if (rule) {
|
||||||
setFormData({
|
setFormData({
|
||||||
name: rule.name,
|
name: rule.name,
|
||||||
ruleCode: rule.code,
|
ruleCode: rule.code,
|
||||||
description: rule.description || "",
|
description: rule.description || '',
|
||||||
warehouseId: rule.warehouseId,
|
warehouseId: rule.warehouseId,
|
||||||
materialTypeId: rule.materialTypeId || "",
|
materialTypeId: rule.materialTypeId || '',
|
||||||
materialGroupId: rule.materialGroupId || "",
|
materialGroupId: rule.materialGroupId || '',
|
||||||
priority: rule.priority,
|
priority: rule.priority,
|
||||||
targetZoneId: rule.targetZoneId || "",
|
targetZoneId: rule.targetZoneId || '',
|
||||||
targetLocationId: rule.targetLocationId || "",
|
targetLocationId: rule.targetLocationId || '',
|
||||||
strategy: rule.strategy,
|
strategy: rule.strategy,
|
||||||
isActive: rule.isActive,
|
isActive: rule.isActive,
|
||||||
});
|
})
|
||||||
|
}
|
||||||
|
setEditingRuleId(ruleId)
|
||||||
|
setShowRuleForm(true)
|
||||||
}
|
}
|
||||||
setEditingRuleId(ruleId);
|
|
||||||
setShowRuleForm(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNewRule = () => {
|
const handleNewRule = () => {
|
||||||
resetForm();
|
resetForm()
|
||||||
setEditingRuleId(null);
|
setEditingRuleId(null)
|
||||||
setShowRuleForm(true);
|
setShowRuleForm(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCloseForm = () => {
|
const handleCloseForm = () => {
|
||||||
setShowRuleForm(false);
|
setShowRuleForm(false)
|
||||||
setEditingRuleId(null);
|
setEditingRuleId(null)
|
||||||
resetForm();
|
resetForm()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmitForm = () => {
|
const handleSubmitForm = () => {
|
||||||
// Form validation and submission logic here
|
// Form validation and submission logic here
|
||||||
console.log("Form Data:", formData);
|
console.log('Form Data:', formData)
|
||||||
handleCloseForm();
|
handleCloseForm()
|
||||||
};
|
}
|
||||||
|
|
||||||
const filteredRules = mockPutawayRules.filter((rule) => {
|
const filteredRules = mockPutawayRules.filter((rule) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
rule.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
rule.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
rule.code.toLowerCase().includes(searchTerm.toLowerCase());
|
rule.code.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
const matchesStrategy =
|
const matchesStrategy = selectedStrategy === '' || rule.strategy === selectedStrategy
|
||||||
selectedStrategy === "" || rule.strategy === selectedStrategy;
|
return matchesSearch && matchesStrategy
|
||||||
return matchesSearch && matchesStrategy;
|
})
|
||||||
});
|
|
||||||
|
|
||||||
const RuleDetailModal = () => {
|
const RuleDetailModal = () => {
|
||||||
const rule = mockPutawayRules.find((r) => r.id === selectedRule);
|
const rule = mockPutawayRules.find((r) => r.id === selectedRule)
|
||||||
|
|
||||||
if (!selectedRule || !rule) return null;
|
if (!selectedRule || !rule) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||||
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||||
onClick={() => setSelectedRule("")}
|
onClick={() => setSelectedRule('')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
||||||
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="text-lg font-medium text-gray-900">
|
<h3 className="text-lg font-medium text-gray-900">{rule.name} - Kural Detayları</h3>
|
||||||
{rule.name} - Kural Detayları
|
|
||||||
</h3>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedRule("")}
|
onClick={() => setSelectedRule('')}
|
||||||
className="text-gray-400 hover:text-gray-600"
|
className="text-gray-400 hover:text-gray-600"
|
||||||
>
|
>
|
||||||
<svg
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
className="w-5 h-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
|
|
@ -194,9 +181,7 @@ const PutawayRules: React.FC = () => {
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
{/* Rule Info */}
|
{/* Rule Info */}
|
||||||
<div className="bg-gray-50 rounded-lg p-3">
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
<h4 className="font-medium text-sm text-gray-900 mb-2">
|
<h4 className="font-medium text-sm text-gray-900 mb-2">Kural Bilgileri</h4>
|
||||||
Kural Bilgileri
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<strong>Kod:</strong> {rule.code}
|
<strong>Kod:</strong> {rule.code}
|
||||||
|
|
@ -211,7 +196,7 @@ const PutawayRules: React.FC = () => {
|
||||||
<strong>Strateji:</strong>
|
<strong>Strateji:</strong>
|
||||||
<span
|
<span
|
||||||
className={`ml-2 inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
|
className={`ml-2 inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
|
||||||
rule.strategy
|
rule.strategy,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getPutawayStrategyText(rule.strategy)}
|
{getPutawayStrategyText(rule.strategy)}
|
||||||
|
|
@ -241,14 +226,9 @@ const PutawayRules: React.FC = () => {
|
||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{rule.conditions.map((condition, index) => (
|
{rule.conditions.map((condition, index) => (
|
||||||
<div
|
<div key={condition.id} className="bg-white rounded p-2 border">
|
||||||
key={condition.id}
|
|
||||||
className="bg-white rounded p-2 border"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">Koşul {index + 1}</span>
|
||||||
Koşul {index + 1}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 text-sm text-gray-600">
|
<div className="mt-2 text-sm text-gray-600">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -278,31 +258,25 @@ const PutawayRules: React.FC = () => {
|
||||||
|
|
||||||
{/* Target Info */}
|
{/* Target Info */}
|
||||||
<div className="mt-4 bg-gray-50 rounded-lg p-3">
|
<div className="mt-4 bg-gray-50 rounded-lg p-3">
|
||||||
<h4 className="font-medium text-sm text-gray-900 mb-2">
|
<h4 className="font-medium text-sm text-gray-900 mb-2">Hedef Lokasyon</h4>
|
||||||
Hedef Lokasyon
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-sm">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<strong>Depo:</strong> {getWarehouseName(rule.warehouseId)}
|
<strong>Depo:</strong> {getWarehouseName(rule.warehouseId)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Hedef Bölge:</strong>{" "}
|
<strong>Hedef Bölge:</strong> {getZoneName(rule.targetZoneId)}
|
||||||
{getZoneName(rule.targetZoneId)}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Hedef Lokasyon:</strong>{" "}
|
<strong>Hedef Lokasyon:</strong> {getLocationName(rule.targetLocationId)}
|
||||||
{getLocationName(rule.targetLocationId)}
|
|
||||||
</div>
|
</div>
|
||||||
{rule.materialTypeId && (
|
{rule.materialTypeId && (
|
||||||
<div>
|
<div>
|
||||||
<strong>Malzeme Tipi:</strong>{" "}
|
<strong>Malzeme Tipi:</strong> {getMaterialTypeName(rule.materialTypeId)}
|
||||||
{getMaterialTypeName(rule.materialTypeId)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{rule.materialGroupId && (
|
{rule.materialGroupId && (
|
||||||
<div>
|
<div>
|
||||||
<strong>Malzeme Grubu:</strong>{" "}
|
<strong>Malzeme Grubu:</strong> {getMaterialGroupName(rule.materialGroupId)}
|
||||||
{getMaterialGroupName(rule.materialGroupId)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -311,40 +285,37 @@ const PutawayRules: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">Yerleştirme Kuralları</h2>
|
||||||
Yerleştirme Kuralları
|
<p className="text-gray-600">Malzeme yerleştirme kurallarını tanımlayın ve yönetin</p>
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Malzeme yerleştirme kurallarını tanımlayın ve yönetin
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* View Mode Toggle */}
|
{/* View Mode Toggle */}
|
||||||
<div className="flex items-center bg-gray-100 rounded-lg p-1">
|
<div className="flex items-center bg-gray-100 rounded-lg p-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("grid")}
|
onClick={() => setViewMode('grid')}
|
||||||
className={`p-1.5 rounded-md ${
|
className={`p-1.5 rounded-md ${
|
||||||
viewMode === "grid"
|
viewMode === 'grid'
|
||||||
? "bg-white text-blue-600 shadow-sm"
|
? 'bg-white text-blue-600 shadow-sm'
|
||||||
: "text-gray-500 hover:text-gray-700"
|
: 'text-gray-500 hover:text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
title="Kart Görünümü"
|
title="Kart Görünümü"
|
||||||
>
|
>
|
||||||
<FaTh className="w-4 h-4" />
|
<FaTh className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("list")}
|
onClick={() => setViewMode('list')}
|
||||||
className={`p-1.5 rounded-md ${
|
className={`p-1.5 rounded-md ${
|
||||||
viewMode === "list"
|
viewMode === 'list'
|
||||||
? "bg-white text-blue-600 shadow-sm"
|
? 'bg-white text-blue-600 shadow-sm'
|
||||||
: "text-gray-500 hover:text-gray-700"
|
: 'text-gray-500 hover:text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
title="Liste Görünümü"
|
title="Liste Görünümü"
|
||||||
>
|
>
|
||||||
|
|
@ -375,9 +346,7 @@ const PutawayRules: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
value={selectedStrategy}
|
value={selectedStrategy}
|
||||||
onChange={(e) =>
|
onChange={(e) => setSelectedStrategy(e.target.value as PutawayStrategyEnum | '')}
|
||||||
setSelectedStrategy(e.target.value as PutawayStrategyEnum | "")
|
|
||||||
}
|
|
||||||
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">Tüm Stratejiler</option>
|
<option value="">Tüm Stratejiler</option>
|
||||||
|
|
@ -394,7 +363,7 @@ const PutawayRules: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Rules Display */}
|
{/* Rules Display */}
|
||||||
{viewMode === "grid" ? (
|
{viewMode === 'grid' ? (
|
||||||
// Grid View (Kart Görünümü)
|
// Grid View (Kart Görünümü)
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
{filteredRules.map((rule) => (
|
{filteredRules.map((rule) => (
|
||||||
|
|
@ -431,7 +400,7 @@ const PutawayRules: React.FC = () => {
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
|
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
|
||||||
rule.strategy
|
rule.strategy,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getPutawayStrategyText(rule.strategy)}
|
{getPutawayStrategyText(rule.strategy)}
|
||||||
|
|
@ -452,9 +421,7 @@ const PutawayRules: React.FC = () => {
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{getConditionTypeText(condition.conditionType)}
|
{getConditionTypeText(condition.conditionType)}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>{getConditionOperatorText(condition.operator)}</span>
|
||||||
{getConditionOperatorText(condition.operator)}
|
|
||||||
</span>
|
|
||||||
<span className="font-medium">{condition.value}</span>
|
<span className="font-medium">{condition.value}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -466,9 +433,7 @@ const PutawayRules: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">Koşul tanımlanmamış</div>
|
||||||
Koşul tanımlanmamış
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Target Info */}
|
{/* Target Info */}
|
||||||
|
|
@ -477,25 +442,15 @@ const PutawayRules: React.FC = () => {
|
||||||
<div className="bg-gray-50 rounded p-2">
|
<div className="bg-gray-50 rounded p-2">
|
||||||
<div className="text-xs text-gray-600 space-y-0.5">
|
<div className="text-xs text-gray-600 space-y-0.5">
|
||||||
<div>Depo: {getWarehouseName(rule.warehouseId)}</div>
|
<div>Depo: {getWarehouseName(rule.warehouseId)}</div>
|
||||||
{rule.targetZoneId && (
|
{rule.targetZoneId && <div>Bölge: {getZoneName(rule.targetZoneId)}</div>}
|
||||||
<div>Bölge: {getZoneName(rule.targetZoneId)}</div>
|
|
||||||
)}
|
|
||||||
{rule.targetLocationId && (
|
{rule.targetLocationId && (
|
||||||
<div>
|
<div>Lokasyon: {getLocationName(rule.targetLocationId)}</div>
|
||||||
Lokasyon: {getLocationName(rule.targetLocationId)}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
{rule.materialTypeId && (
|
{rule.materialTypeId && (
|
||||||
<div>
|
<div>Malzeme Tipi: {getMaterialTypeName(rule.materialTypeId)}</div>
|
||||||
Malzeme Tipi:{" "}
|
|
||||||
{getMaterialTypeName(rule.materialTypeId)}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
{rule.materialGroupId && (
|
{rule.materialGroupId && (
|
||||||
<div>
|
<div>Malzeme Grubu: {getMaterialGroupName(rule.materialGroupId)}</div>
|
||||||
Malzeme Grubu:{" "}
|
|
||||||
{getMaterialGroupName(rule.materialGroupId)}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -585,16 +540,14 @@ const PutawayRules: React.FC = () => {
|
||||||
>
|
>
|
||||||
{rule.name}
|
{rule.name}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">{rule.code}</div>
|
||||||
{rule.code}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<span
|
<span
|
||||||
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
|
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
|
||||||
rule.strategy
|
rule.strategy,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getPutawayStrategyText(rule.strategy)}
|
{getPutawayStrategyText(rule.strategy)}
|
||||||
|
|
@ -605,13 +558,9 @@ const PutawayRules: React.FC = () => {
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{rule.targetZoneId && (
|
{rule.targetZoneId && <div>Bölge: {getZoneName(rule.targetZoneId)}</div>}
|
||||||
<div>Bölge: {getZoneName(rule.targetZoneId)}</div>
|
|
||||||
)}
|
|
||||||
{rule.targetLocationId && (
|
{rule.targetLocationId && (
|
||||||
<div>
|
<div>Lokasyon: {getLocationName(rule.targetLocationId)}</div>
|
||||||
Lokasyon: {getLocationName(rule.targetLocationId)}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -620,9 +569,8 @@ const PutawayRules: React.FC = () => {
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{rule.conditions.slice(0, 1).map((condition) => (
|
{rule.conditions.slice(0, 1).map((condition) => (
|
||||||
<div key={condition.id} className="text-xs">
|
<div key={condition.id} className="text-xs">
|
||||||
{getConditionTypeText(condition.conditionType)}{" "}
|
{getConditionTypeText(condition.conditionType)}{' '}
|
||||||
{getConditionOperatorText(condition.operator)}{" "}
|
{getConditionOperatorText(condition.operator)} {condition.value}
|
||||||
{condition.value}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{rule.conditions.length > 1 && (
|
{rule.conditions.length > 1 && (
|
||||||
|
|
@ -645,9 +593,7 @@ const PutawayRules: React.FC = () => {
|
||||||
{rule.isActive ? (
|
{rule.isActive ? (
|
||||||
<>
|
<>
|
||||||
<FaCheckCircle className="w-4 h-4 text-green-500 mr-2" />
|
<FaCheckCircle className="w-4 h-4 text-green-500 mr-2" />
|
||||||
<span className="text-sm text-green-600">
|
<span className="text-sm text-green-600">Aktif</span>
|
||||||
Aktif
|
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
@ -692,9 +638,7 @@ const PutawayRules: React.FC = () => {
|
||||||
{filteredRules.length === 0 && (
|
{filteredRules.length === 0 && (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
<h3 className="text-lg font-medium text-gray-900 mb-2">Kural bulunamadı</h3>
|
||||||
Kural bulunamadı
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-500 mb-4">
|
<p className="text-gray-500 mb-4">
|
||||||
Arama kriterlerinize uygun yerleştirme kuralı bulunamadı.
|
Arama kriterlerinize uygun yerleştirme kuralı bulunamadı.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -706,6 +650,7 @@ const PutawayRules: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Rule Detail Modal */}
|
{/* Rule Detail Modal */}
|
||||||
<RuleDetailModal />
|
<RuleDetailModal />
|
||||||
|
|
@ -717,8 +662,8 @@ const PutawayRules: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowRuleForm(false);
|
setShowRuleForm(false)
|
||||||
setEditingRuleId(null);
|
setEditingRuleId(null)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -726,20 +671,13 @@ const PutawayRules: React.FC = () => {
|
||||||
<div className="bg-white rounded-lg w-full max-w-4xl mx-4 max-h-[90vh] overflow-y-auto">
|
<div className="bg-white rounded-lg w-full max-w-4xl mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
<div className="flex items-center justify-between p-4 border-b">
|
<div className="flex items-center justify-between p-4 border-b">
|
||||||
<h3 className="text-base font-medium text-gray-900">
|
<h3 className="text-base font-medium text-gray-900">
|
||||||
{editingRuleId
|
{editingRuleId ? 'Yerleştirme Kuralını Güncelle' : 'Yeni Yerleştirme Kuralı'}
|
||||||
? "Yerleştirme Kuralını Güncelle"
|
|
||||||
: "Yeni Yerleştirme Kuralı"}
|
|
||||||
</h3>
|
</h3>
|
||||||
<button
|
<button
|
||||||
onClick={handleCloseForm}
|
onClick={handleCloseForm}
|
||||||
className="text-gray-400 hover:text-gray-600 p-1 rounded-full"
|
className="text-gray-400 hover:text-gray-600 p-1 rounded-full"
|
||||||
>
|
>
|
||||||
<svg
|
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
className="w-6 h-6"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
|
|
@ -761,9 +699,7 @@ const PutawayRules: React.FC = () => {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
setFormData({ ...formData, name: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
placeholder="Kural adını girin"
|
placeholder="Kural adını girin"
|
||||||
required
|
required
|
||||||
|
|
@ -776,9 +712,7 @@ const PutawayRules: React.FC = () => {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.ruleCode}
|
value={formData.ruleCode}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, ruleCode: e.target.value })}
|
||||||
setFormData({ ...formData, ruleCode: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
placeholder="PR001"
|
placeholder="PR001"
|
||||||
required
|
required
|
||||||
|
|
@ -787,9 +721,7 @@ const PutawayRules: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama</label>
|
||||||
Açıklama
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -806,9 +738,7 @@ const PutawayRules: React.FC = () => {
|
||||||
|
|
||||||
{/* Warehouse and Location Settings */}
|
{/* Warehouse and Location Settings */}
|
||||||
<div className="border-t pt-4">
|
<div className="border-t pt-4">
|
||||||
<h4 className="text-base font-medium text-gray-900 mb-2">
|
<h4 className="text-base font-medium text-gray-900 mb-2">Lokasyon Ayarları</h4>
|
||||||
Lokasyon Ayarları
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
|
@ -820,9 +750,9 @@ const PutawayRules: React.FC = () => {
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
warehouseId: e.target.value,
|
warehouseId: e.target.value,
|
||||||
targetZoneId: "",
|
targetZoneId: '',
|
||||||
targetLocationId: "",
|
targetLocationId: '',
|
||||||
});
|
})
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
required
|
required
|
||||||
|
|
@ -847,17 +777,14 @@ const PutawayRules: React.FC = () => {
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
targetZoneId: e.target.value,
|
targetZoneId: e.target.value,
|
||||||
targetLocationId: "",
|
targetLocationId: '',
|
||||||
});
|
})
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
>
|
>
|
||||||
<option value="">Bölge seçin</option>
|
<option value="">Bölge seçin</option>
|
||||||
{mockZones
|
{mockZones
|
||||||
.filter(
|
.filter((zone) => zone.warehouseId === formData.warehouseId)
|
||||||
(zone) =>
|
|
||||||
zone.warehouseId === formData.warehouseId
|
|
||||||
)
|
|
||||||
.map((zone) => (
|
.map((zone) => (
|
||||||
<option key={zone.id} value={zone.id}>
|
<option key={zone.id} value={zone.id}>
|
||||||
{zone.name}
|
{zone.name}
|
||||||
|
|
@ -884,10 +811,7 @@ const PutawayRules: React.FC = () => {
|
||||||
>
|
>
|
||||||
<option value="">Lokasyon seçin</option>
|
<option value="">Lokasyon seçin</option>
|
||||||
{mockLocations
|
{mockLocations
|
||||||
.filter(
|
.filter((location) => location.zoneId === formData.targetZoneId)
|
||||||
(location) =>
|
|
||||||
location.zoneId === formData.targetZoneId
|
|
||||||
)
|
|
||||||
.map((location) => (
|
.map((location) => (
|
||||||
<option key={location.id} value={location.id}>
|
<option key={location.id} value={location.id}>
|
||||||
{location.name}
|
{location.name}
|
||||||
|
|
@ -901,9 +825,7 @@ const PutawayRules: React.FC = () => {
|
||||||
|
|
||||||
{/* Material Settings */}
|
{/* Material Settings */}
|
||||||
<div className="border-t pt-4">
|
<div className="border-t pt-4">
|
||||||
<h4 className="text-base font-medium text-gray-900 mb-2">
|
<h4 className="text-base font-medium text-gray-900 mb-2">Malzeme Ayarları</h4>
|
||||||
Malzeme Ayarları
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
|
@ -982,9 +904,7 @@ const PutawayRules: React.FC = () => {
|
||||||
<option value={PutawayStrategyEnum.NearestLocation}>
|
<option value={PutawayStrategyEnum.NearestLocation}>
|
||||||
En Yakın Lokasyon
|
En Yakın Lokasyon
|
||||||
</option>
|
</option>
|
||||||
<option value={PutawayStrategyEnum.EmptyLocation}>
|
<option value={PutawayStrategyEnum.EmptyLocation}>Boş Lokasyon</option>
|
||||||
Boş Lokasyon
|
|
||||||
</option>
|
|
||||||
<option value={PutawayStrategyEnum.SameProduct}>
|
<option value={PutawayStrategyEnum.SameProduct}>
|
||||||
Aynı Ürün Gruplaması
|
Aynı Ürün Gruplaması
|
||||||
</option>
|
</option>
|
||||||
|
|
@ -1027,10 +947,7 @@ const PutawayRules: React.FC = () => {
|
||||||
}
|
}
|
||||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
/>
|
/>
|
||||||
<label
|
<label htmlFor="isActive" className="ml-2 block text-sm text-gray-900">
|
||||||
htmlFor="isActive"
|
|
||||||
className="ml-2 block text-sm text-gray-900"
|
|
||||||
>
|
|
||||||
Bu kural aktif
|
Bu kural aktif
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1051,7 +968,7 @@ const PutawayRules: React.FC = () => {
|
||||||
onClick={handleSubmitForm}
|
onClick={handleSubmitForm}
|
||||||
className="px-4 py-1.5 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="px-4 py-1.5 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
>
|
>
|
||||||
{editingRuleId ? "Güncelle" : "Kaydet"}
|
{editingRuleId ? 'Güncelle' : 'Kaydet'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1059,8 +976,8 @@ const PutawayRules: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default PutawayRules;
|
export default PutawayRules
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { useFormik } from "formik";
|
import { useFormik } from 'formik'
|
||||||
import * as Yup from "yup";
|
import * as Yup from 'yup'
|
||||||
import { FaSave, FaTimes } from "react-icons/fa";
|
import { FaSave, FaTimes } from 'react-icons/fa'
|
||||||
import { SerialStatusEnum, MmSerialNumber, MmMaterial } from "../../../types/mm";
|
import { SerialStatusEnum, MmSerialNumber, MmMaterial } from '../../../types/mm'
|
||||||
|
|
||||||
export interface SerialFormProps {
|
export interface SerialFormProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (serial: MmSerialNumber) => void;
|
onSave: (serial: MmSerialNumber) => void
|
||||||
onUpdate?: (serial: MmSerialNumber) => void;
|
onUpdate?: (serial: MmSerialNumber) => void
|
||||||
materials: MmMaterial[];
|
materials: MmMaterial[]
|
||||||
lots?: { id: string; label: string }[];
|
lots?: { id: string; label: string }[]
|
||||||
initial?: MmSerialNumber | null;
|
initial?: MmSerialNumber | null
|
||||||
mode?: "create" | "edit" | "view";
|
mode?: 'create' | 'edit' | 'view'
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
materialId: Yup.string().required("Malzeme seçimi zorunlu"),
|
materialId: Yup.string().required('Malzeme seçimi zorunlu'),
|
||||||
serialNumber: Yup.string().required("Seri numarası zorunlu"),
|
serialNumber: Yup.string().required('Seri numarası zorunlu'),
|
||||||
});
|
})
|
||||||
|
|
||||||
const SerialForm: React.FC<SerialFormProps> = ({
|
const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
|
|
@ -28,16 +28,16 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
materials,
|
materials,
|
||||||
lots = [],
|
lots = [],
|
||||||
initial = null,
|
initial = null,
|
||||||
mode = "create",
|
mode = 'create',
|
||||||
}) => {
|
}) => {
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
materialId: "",
|
materialId: '',
|
||||||
serialNumber: "",
|
serialNumber: '',
|
||||||
lotId: lots.length ? lots[0].id : "",
|
lotId: lots.length ? lots[0].id : '',
|
||||||
productionDate: "",
|
productionDate: '',
|
||||||
warrantyExpiryDate: "",
|
warrantyExpiryDate: '',
|
||||||
currentLocationId: "",
|
currentLocationId: '',
|
||||||
status: SerialStatusEnum.Available,
|
status: SerialStatusEnum.Available,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
|
|
@ -48,51 +48,49 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
materialId: values.materialId,
|
materialId: values.materialId,
|
||||||
serialNumber: values.serialNumber,
|
serialNumber: values.serialNumber,
|
||||||
lotId: values.lotId || undefined,
|
lotId: values.lotId || undefined,
|
||||||
productionDate: values.productionDate
|
productionDate: values.productionDate ? new Date(values.productionDate) : new Date(),
|
||||||
? new Date(values.productionDate)
|
|
||||||
: new Date(),
|
|
||||||
warrantyExpiryDate: values.warrantyExpiryDate
|
warrantyExpiryDate: values.warrantyExpiryDate
|
||||||
? new Date(values.warrantyExpiryDate)
|
? new Date(values.warrantyExpiryDate)
|
||||||
: undefined,
|
: undefined,
|
||||||
currentLocationId: values.currentLocationId || undefined,
|
currentLocationId: values.currentLocationId || undefined,
|
||||||
status: values.status,
|
status: values.status,
|
||||||
isActive: !!values.isActive,
|
isActive: !!values.isActive,
|
||||||
};
|
}
|
||||||
|
|
||||||
// simulate API
|
// simulate API
|
||||||
await new Promise((r) => setTimeout(r, 300));
|
await new Promise((r) => setTimeout(r, 300))
|
||||||
if (mode === "edit" && onUpdate) {
|
if (mode === 'edit' && onUpdate) {
|
||||||
onUpdate(newSerial);
|
onUpdate(newSerial)
|
||||||
} else {
|
} else {
|
||||||
onSave(newSerial);
|
onSave(newSerial)
|
||||||
}
|
}
|
||||||
onClose();
|
onClose()
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
// sync initial values when editing/viewing
|
// sync initial values when editing/viewing
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (initial) {
|
if (initial) {
|
||||||
const src = initial;
|
const src = initial
|
||||||
formik.setValues({
|
formik.setValues({
|
||||||
materialId: src.materialId || "",
|
materialId: src.materialId || '',
|
||||||
serialNumber: src.serialNumber || "",
|
serialNumber: src.serialNumber || '',
|
||||||
lotId: src.lotId || (lots.length ? lots[0].id : ""),
|
lotId: src.lotId || (lots.length ? lots[0].id : ''),
|
||||||
productionDate: src.productionDate
|
productionDate: src.productionDate
|
||||||
? new Date(src.productionDate).toISOString().slice(0, 10)
|
? new Date(src.productionDate).toISOString().slice(0, 10)
|
||||||
: "",
|
: '',
|
||||||
warrantyExpiryDate: src.warrantyExpiryDate
|
warrantyExpiryDate: src.warrantyExpiryDate
|
||||||
? new Date(src.warrantyExpiryDate).toISOString().slice(0, 10)
|
? new Date(src.warrantyExpiryDate).toISOString().slice(0, 10)
|
||||||
: "",
|
: '',
|
||||||
currentLocationId: src.currentLocationId || "",
|
currentLocationId: src.currentLocationId || '',
|
||||||
status: src.status || SerialStatusEnum.Available,
|
status: src.status || SerialStatusEnum.Available,
|
||||||
isActive: !!src.isActive,
|
isActive: !!src.isActive,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [initial]);
|
}, [initial])
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||||
|
|
@ -102,18 +100,18 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
<div className="flex items-center justify-between p-2 border-b">
|
<div className="flex items-center justify-between p-2 border-b">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-bold text-gray-900">
|
<h2 className="text-xl font-bold text-gray-900">
|
||||||
{mode === "create"
|
{mode === 'create'
|
||||||
? "Yeni Seri Kaydı"
|
? 'Yeni Seri Kaydı'
|
||||||
: mode === "edit"
|
: mode === 'edit'
|
||||||
? "Seri Düzenle"
|
? 'Seri Düzenle'
|
||||||
: "Seri Detayı"}
|
: 'Seri Detayı'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
{mode === "create"
|
{mode === 'create'
|
||||||
? "Seri numarası girin"
|
? 'Seri numarası girin'
|
||||||
: mode === "edit"
|
: mode === 'edit'
|
||||||
? "Mevcut seri bilgisini güncelleyin"
|
? 'Mevcut seri bilgisini güncelleyin'
|
||||||
: "Seri bilgileri (sadece gösterim)"}
|
: 'Seri bilgileri (sadece gösterim)'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -124,7 +122,7 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
>
|
>
|
||||||
<FaTimes className="inline mr-1 h-3 w-3" /> Kapat
|
<FaTimes className="inline mr-1 h-3 w-3" /> Kapat
|
||||||
</button>
|
</button>
|
||||||
{mode !== "view" && (
|
{mode !== 'view' && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"
|
className="px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"
|
||||||
|
|
@ -137,12 +135,10 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 p-2 gap-x-3 gap-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Malzeme *</label>
|
||||||
Malzeme *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
{...formik.getFieldProps("materialId")}
|
{...formik.getFieldProps('materialId')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
>
|
>
|
||||||
<option value="">Seçiniz...</option>
|
<option value="">Seçiniz...</option>
|
||||||
|
|
@ -155,24 +151,20 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Seri Numarası *</label>
|
||||||
Seri Numarası *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
{...formik.getFieldProps("serialNumber")}
|
{...formik.getFieldProps('serialNumber')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Lot</label>
|
||||||
Lot
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
{...formik.getFieldProps("lotId")}
|
{...formik.getFieldProps('lotId')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
>
|
>
|
||||||
<option value="">Seçiniz...</option>
|
<option value="">Seçiniz...</option>
|
||||||
|
|
@ -185,36 +177,30 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Üretim Tarihi</label>
|
||||||
Üretim Tarihi
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
{...formik.getFieldProps("productionDate")}
|
{...formik.getFieldProps('productionDate')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Garanti Bitiş</label>
|
||||||
Garanti Bitiş
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
{...formik.getFieldProps("warrantyExpiryDate")}
|
{...formik.getFieldProps('warrantyExpiryDate')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
{...formik.getFieldProps("status")}
|
{...formik.getFieldProps('status')}
|
||||||
disabled={mode === "view"}
|
disabled={mode === 'view'}
|
||||||
className="w-full border rounded-md px-2 py-1 text-sm"
|
className="w-full border rounded-md px-2 py-1 text-sm"
|
||||||
>
|
>
|
||||||
<option value={SerialStatusEnum.Available}>Müsait</option>
|
<option value={SerialStatusEnum.Available}>Müsait</option>
|
||||||
|
|
@ -227,7 +213,7 @@ const SerialForm: React.FC<SerialFormProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default SerialForm;
|
export default SerialForm
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,5 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import { WmWarehouse, WarehouseTypeEnum, WmZone, WmLocation } from '../../../types/wm'
|
||||||
WmWarehouse,
|
|
||||||
WarehouseTypeEnum,
|
|
||||||
WmZone,
|
|
||||||
WmLocation,
|
|
||||||
} from "../../../types/wm";
|
|
||||||
import {
|
import {
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaSearch,
|
FaSearch,
|
||||||
|
|
@ -18,101 +13,91 @@ import {
|
||||||
FaEye,
|
FaEye,
|
||||||
FaTh,
|
FaTh,
|
||||||
FaList,
|
FaList,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { WarehouseModal, ZoneModal, LocationModal } from "./modals";
|
import { WarehouseModal, ZoneModal, LocationModal } from './modals'
|
||||||
import { mockWarehouses } from "../../../mocks/mockWarehouses";
|
import { mockWarehouses } from '../../../mocks/mockWarehouses'
|
||||||
import { mockZones } from "../../../mocks/mockZones";
|
import { mockZones } from '../../../mocks/mockZones'
|
||||||
import { mockLocations } from "../../../mocks/mockLocations";
|
import { mockLocations } from '../../../mocks/mockLocations'
|
||||||
import {
|
import {
|
||||||
getWarehouseTypeColor,
|
getWarehouseTypeColor,
|
||||||
getWarehouseTypeText,
|
getWarehouseTypeText,
|
||||||
getZoneTypeText,
|
getZoneTypeText,
|
||||||
getLocationTypeText,
|
getLocationTypeText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const WarehouseDefinitions: React.FC = () => {
|
const WarehouseDefinitions: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [selectedType, setSelectedType] = useState<WarehouseTypeEnum | "">("");
|
const [selectedType, setSelectedType] = useState<WarehouseTypeEnum | ''>('')
|
||||||
const [activeTab, setActiveTab] = useState<
|
const [activeTab, setActiveTab] = useState<'warehouses' | 'zones' | 'locations'>('warehouses')
|
||||||
"warehouses" | "zones" | "locations"
|
const [selectedWarehouse, setSelectedWarehouse] = useState<string>('')
|
||||||
>("warehouses");
|
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
|
||||||
const [selectedWarehouse, setSelectedWarehouse] = useState<string>("");
|
|
||||||
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
|
||||||
|
|
||||||
// Modal states
|
// Modal states
|
||||||
const [warehouseModal, setWarehouseModal] = useState<{
|
const [warehouseModal, setWarehouseModal] = useState<{
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
mode: "create" | "edit" | "view";
|
mode: 'create' | 'edit' | 'view'
|
||||||
warehouse: WmWarehouse | null;
|
warehouse: WmWarehouse | null
|
||||||
}>({
|
}>({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
mode: "create",
|
mode: 'create',
|
||||||
warehouse: null,
|
warehouse: null,
|
||||||
});
|
})
|
||||||
|
|
||||||
const [zoneModal, setZoneModal] = useState<{
|
const [zoneModal, setZoneModal] = useState<{
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
mode: "create" | "edit" | "view";
|
mode: 'create' | 'edit' | 'view'
|
||||||
zone: WmZone | null;
|
zone: WmZone | null
|
||||||
}>({
|
}>({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
mode: "create",
|
mode: 'create',
|
||||||
zone: null,
|
zone: null,
|
||||||
});
|
})
|
||||||
|
|
||||||
const [locationModal, setLocationModal] = useState<{
|
const [locationModal, setLocationModal] = useState<{
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
mode: "create" | "edit" | "view";
|
mode: 'create' | 'edit' | 'view'
|
||||||
location: WmLocation | null;
|
location: WmLocation | null
|
||||||
}>({
|
}>({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
mode: "create",
|
mode: 'create',
|
||||||
location: null,
|
location: null,
|
||||||
});
|
})
|
||||||
|
|
||||||
// Data states (in a real app, these would come from an API)
|
// Data states (in a real app, these would come from an API)
|
||||||
const [warehouses, setWarehouses] = useState<WmWarehouse[]>(mockWarehouses);
|
const [warehouses, setWarehouses] = useState<WmWarehouse[]>(mockWarehouses)
|
||||||
|
|
||||||
const [zones, setZones] = useState<WmZone[]>(mockZones);
|
const [zones, setZones] = useState<WmZone[]>(mockZones)
|
||||||
|
|
||||||
const [locations, setLocations] = useState<WmLocation[]>(mockLocations);
|
const [locations, setLocations] = useState<WmLocation[]>(mockLocations)
|
||||||
|
|
||||||
// Modal handlers
|
// Modal handlers
|
||||||
const handleWarehouseAction = (
|
const handleWarehouseAction = (mode: 'create' | 'edit' | 'view', warehouse?: WmWarehouse) => {
|
||||||
mode: "create" | "edit" | "view",
|
|
||||||
warehouse?: WmWarehouse
|
|
||||||
) => {
|
|
||||||
setWarehouseModal({
|
setWarehouseModal({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
mode,
|
mode,
|
||||||
warehouse: warehouse || null,
|
warehouse: warehouse || null,
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleZoneAction = (
|
const handleZoneAction = (mode: 'create' | 'edit' | 'view', zone?: WmZone) => {
|
||||||
mode: "create" | "edit" | "view",
|
|
||||||
zone?: WmZone
|
|
||||||
) => {
|
|
||||||
setZoneModal({
|
setZoneModal({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
mode,
|
mode,
|
||||||
zone: zone || null,
|
zone: zone || null,
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLocationAction = (
|
const handleLocationAction = (mode: 'create' | 'edit' | 'view', location?: WmLocation) => {
|
||||||
mode: "create" | "edit" | "view",
|
|
||||||
location?: WmLocation
|
|
||||||
) => {
|
|
||||||
setLocationModal({
|
setLocationModal({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
mode,
|
mode,
|
||||||
location: location || null,
|
location: location || null,
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleWarehouseSave = (warehouseData: Partial<WmWarehouse>) => {
|
const handleWarehouseSave = (warehouseData: Partial<WmWarehouse>) => {
|
||||||
if (warehouseModal.mode === "create") {
|
if (warehouseModal.mode === 'create') {
|
||||||
const newWarehouse: WmWarehouse = {
|
const newWarehouse: WmWarehouse = {
|
||||||
...warehouseData,
|
...warehouseData,
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
|
|
@ -120,9 +105,9 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
zones: [],
|
zones: [],
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
} as WmWarehouse;
|
} as WmWarehouse
|
||||||
setWarehouses([...warehouses, newWarehouse]);
|
setWarehouses([...warehouses, newWarehouse])
|
||||||
} else if (warehouseModal.mode === "edit" && warehouseModal.warehouse) {
|
} else if (warehouseModal.mode === 'edit' && warehouseModal.warehouse) {
|
||||||
setWarehouses(
|
setWarehouses(
|
||||||
warehouses.map((w) =>
|
warehouses.map((w) =>
|
||||||
w.id === warehouseModal.warehouse!.id
|
w.id === warehouseModal.warehouse!.id
|
||||||
|
|
@ -131,91 +116,82 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
...warehouseData,
|
...warehouseData,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
} as WmWarehouse)
|
} as WmWarehouse)
|
||||||
: w
|
: w,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleZoneSave = (zoneData: Partial<WmZone>) => {
|
const handleZoneSave = (zoneData: Partial<WmZone>) => {
|
||||||
if (zoneModal.mode === "create") {
|
if (zoneModal.mode === 'create') {
|
||||||
const newZone: WmZone = {
|
const newZone: WmZone = {
|
||||||
...zoneData,
|
...zoneData,
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
locations: [],
|
locations: [],
|
||||||
} as WmZone;
|
} as WmZone
|
||||||
setZones([...zones, newZone]);
|
setZones([...zones, newZone])
|
||||||
} else if (zoneModal.mode === "edit" && zoneModal.zone) {
|
} else if (zoneModal.mode === 'edit' && zoneModal.zone) {
|
||||||
setZones(
|
setZones(
|
||||||
zones.map((z) =>
|
zones.map((z) => (z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z)),
|
||||||
z.id === zoneModal.zone!.id ? ({ ...z, ...zoneData } as WmZone) : z
|
|
||||||
)
|
)
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLocationSave = (locationData: Partial<WmLocation>) => {
|
const handleLocationSave = (locationData: Partial<WmLocation>) => {
|
||||||
if (locationModal.mode === "create") {
|
if (locationModal.mode === 'create') {
|
||||||
const newLocation: WmLocation = {
|
const newLocation: WmLocation = {
|
||||||
...locationData,
|
...locationData,
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
stockItems: [],
|
stockItems: [],
|
||||||
} as WmLocation;
|
} as WmLocation
|
||||||
setLocations([...locations, newLocation]);
|
setLocations([...locations, newLocation])
|
||||||
} else if (locationModal.mode === "edit" && locationModal.location) {
|
} else if (locationModal.mode === 'edit' && locationModal.location) {
|
||||||
setLocations(
|
setLocations(
|
||||||
locations.map((l) =>
|
locations.map((l) =>
|
||||||
l.id === locationModal.location!.id
|
l.id === locationModal.location!.id ? ({ ...l, ...locationData } as WmLocation) : l,
|
||||||
? ({ ...l, ...locationData } as WmLocation)
|
),
|
||||||
: l
|
|
||||||
)
|
)
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleDelete = (
|
const handleDelete = (type: 'warehouse' | 'zone' | 'location', id: string) => {
|
||||||
type: "warehouse" | "zone" | "location",
|
|
||||||
id: string
|
|
||||||
) => {
|
|
||||||
if (
|
if (
|
||||||
window.confirm(
|
window.confirm(
|
||||||
`Bu ${
|
`Bu ${
|
||||||
type === "warehouse" ? "depo" : type === "zone" ? "bölge" : "lokasyon"
|
type === 'warehouse' ? 'depo' : type === 'zone' ? 'bölge' : 'lokasyon'
|
||||||
}u silmek istediğinizden emin misiniz?`
|
}u silmek istediğinizden emin misiniz?`,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "warehouse":
|
case 'warehouse':
|
||||||
setWarehouses(warehouses.filter((w) => w.id !== id));
|
setWarehouses(warehouses.filter((w) => w.id !== id))
|
||||||
break;
|
break
|
||||||
case "zone":
|
case 'zone':
|
||||||
setZones(zones.filter((z) => z.id !== id));
|
setZones(zones.filter((z) => z.id !== id))
|
||||||
break;
|
break
|
||||||
case "location":
|
case 'location':
|
||||||
setLocations(locations.filter((l) => l.id !== id));
|
setLocations(locations.filter((l) => l.id !== id))
|
||||||
break;
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Filtered data
|
// Filtered data
|
||||||
const filteredWarehouses = warehouses.filter((warehouse) => {
|
const filteredWarehouses = warehouses.filter((warehouse) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase());
|
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
const matchesType =
|
const matchesType = selectedType === '' || warehouse.warehouseType === selectedType
|
||||||
selectedType === "" || warehouse.warehouseType === selectedType;
|
return matchesSearch && matchesType
|
||||||
return matchesSearch && matchesType;
|
})
|
||||||
});
|
|
||||||
|
|
||||||
const filteredZones = zones.filter(
|
const filteredZones = zones.filter(
|
||||||
(zone) => selectedWarehouse === "" || zone.warehouseId === selectedWarehouse
|
(zone) => selectedWarehouse === '' || zone.warehouseId === selectedWarehouse,
|
||||||
);
|
)
|
||||||
|
|
||||||
const filteredLocations = locations.filter(
|
const filteredLocations = locations.filter(
|
||||||
(location) =>
|
(location) => selectedWarehouse === '' || location.warehouseId === selectedWarehouse,
|
||||||
selectedWarehouse === "" || location.warehouseId === selectedWarehouse
|
)
|
||||||
);
|
|
||||||
|
|
||||||
// Warehouse Components
|
// Warehouse Components
|
||||||
const WarehousesGridView = () => (
|
const WarehousesGridView = () => (
|
||||||
|
|
@ -231,9 +207,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<FaBuilding className="w-4 h-4 text-blue-600" />
|
<FaBuilding className="w-4 h-4 text-blue-600" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-sm text-gray-900">
|
<h3 className="font-medium text-sm text-gray-900">{warehouse.name}</h3>
|
||||||
{warehouse.name}
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-600">{warehouse.code}</p>
|
<p className="text-gray-600">{warehouse.code}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -248,7 +222,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
|
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
|
||||||
warehouse.warehouseType
|
warehouse.warehouseType,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWarehouseTypeText(warehouse.warehouseType)}
|
{getWarehouseTypeText(warehouse.warehouseType)}
|
||||||
|
|
@ -278,18 +252,12 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
className="bg-blue-600 h-2 rounded-full"
|
className="bg-blue-600 h-2 rounded-full"
|
||||||
style={{
|
style={{
|
||||||
width: `${
|
width: `${(warehouse.currentUtilization / warehouse.capacity) * 100}%`,
|
||||||
(warehouse.currentUtilization / warehouse.capacity) * 100
|
|
||||||
}%`,
|
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
%
|
%{Math.round((warehouse.currentUtilization / warehouse.capacity) * 100)} dolu
|
||||||
{Math.round(
|
|
||||||
(warehouse.currentUtilization / warehouse.capacity) * 100
|
|
||||||
)}{" "}
|
|
||||||
dolu
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -310,21 +278,21 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleWarehouseAction("view", warehouse)}
|
onClick={() => handleWarehouseAction('view', warehouse)}
|
||||||
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md"
|
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md"
|
||||||
title="Görüntüle"
|
title="Görüntüle"
|
||||||
>
|
>
|
||||||
<FaEye className="w-4 h-4" />
|
<FaEye className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleWarehouseAction("edit", warehouse)}
|
onClick={() => handleWarehouseAction('edit', warehouse)}
|
||||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
|
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
|
||||||
title="Düzenle"
|
title="Düzenle"
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete("warehouse", warehouse.id)}
|
onClick={() => handleDelete('warehouse', warehouse.id)}
|
||||||
className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md"
|
className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md"
|
||||||
title="Sil"
|
title="Sil"
|
||||||
>
|
>
|
||||||
|
|
@ -335,7 +303,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const WarehousesListView = () => (
|
const WarehousesListView = () => (
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||||
|
|
@ -369,7 +337,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{filteredWarehouses.map((warehouse) => {
|
{filteredWarehouses.map((warehouse) => {
|
||||||
const utilizationPercentage =
|
const utilizationPercentage =
|
||||||
(warehouse.currentUtilization / warehouse.capacity) * 100;
|
(warehouse.currentUtilization / warehouse.capacity) * 100
|
||||||
return (
|
return (
|
||||||
<tr key={warehouse.id} className="hover:bg-gray-50">
|
<tr key={warehouse.id} className="hover:bg-gray-50">
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
|
@ -386,16 +354,14 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">{warehouse.code}</div>
|
||||||
{warehouse.code}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<span
|
<span
|
||||||
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
|
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getWarehouseTypeColor(
|
||||||
warehouse.warehouseType
|
warehouse.warehouseType,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWarehouseTypeText(warehouse.warehouseType)}
|
{getWarehouseTypeText(warehouse.warehouseType)}
|
||||||
|
|
@ -405,9 +371,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<div className="text-xs text-gray-900">
|
<div className="text-xs text-gray-900">
|
||||||
{warehouse.address?.city}, {warehouse.address?.state}
|
{warehouse.address?.city}, {warehouse.address?.state}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">{warehouse.address?.street}</div>
|
||||||
{warehouse.address?.street}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||||
{warehouse.capacity.toLocaleString()} birim
|
{warehouse.capacity.toLocaleString()} birim
|
||||||
|
|
@ -418,10 +382,10 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
className={`h-2 rounded-full ${
|
className={`h-2 rounded-full ${
|
||||||
utilizationPercentage > 90
|
utilizationPercentage > 90
|
||||||
? "bg-red-500"
|
? 'bg-red-500'
|
||||||
: utilizationPercentage > 70
|
: utilizationPercentage > 70
|
||||||
? "bg-yellow-500"
|
? 'bg-yellow-500'
|
||||||
: "bg-blue-500"
|
: 'bg-blue-500'
|
||||||
}`}
|
}`}
|
||||||
style={{ width: `${utilizationPercentage}%` }}
|
style={{ width: `${utilizationPercentage}%` }}
|
||||||
></div>
|
></div>
|
||||||
|
|
@ -431,7 +395,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
{warehouse.currentUtilization.toLocaleString()} /{" "}
|
{warehouse.currentUtilization.toLocaleString()} /{' '}
|
||||||
{warehouse.capacity.toLocaleString()}
|
{warehouse.capacity.toLocaleString()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -451,21 +415,21 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleWarehouseAction("view", warehouse)}
|
onClick={() => handleWarehouseAction('view', warehouse)}
|
||||||
className="text-green-600 hover:text-green-900"
|
className="text-green-600 hover:text-green-900"
|
||||||
title="Görüntüle"
|
title="Görüntüle"
|
||||||
>
|
>
|
||||||
<FaEye className="w-4 h-4" />
|
<FaEye className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleWarehouseAction("edit", warehouse)}
|
onClick={() => handleWarehouseAction('edit', warehouse)}
|
||||||
className="text-blue-600 hover:text-blue-900"
|
className="text-blue-600 hover:text-blue-900"
|
||||||
title="Düzenle"
|
title="Düzenle"
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete("warehouse", warehouse.id)}
|
onClick={() => handleDelete('warehouse', warehouse.id)}
|
||||||
className="text-red-600 hover:text-red-900"
|
className="text-red-600 hover:text-red-900"
|
||||||
title="Sil"
|
title="Sil"
|
||||||
>
|
>
|
||||||
|
|
@ -474,13 +438,13 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const WarehousesTab = () => (
|
const WarehousesTab = () => (
|
||||||
<div className="space-y-4 pt-2">
|
<div className="space-y-4 pt-2">
|
||||||
|
|
@ -498,9 +462,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
value={selectedType}
|
value={selectedType}
|
||||||
onChange={(e) =>
|
onChange={(e) => setSelectedType(e.target.value as WarehouseTypeEnum | '')}
|
||||||
setSelectedType(e.target.value as WarehouseTypeEnum | "")
|
|
||||||
}
|
|
||||||
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option value="">Tüm Tipler</option>
|
<option value="">Tüm Tipler</option>
|
||||||
|
|
@ -511,7 +473,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleWarehouseAction("create")}
|
onClick={() => handleWarehouseAction('create')}
|
||||||
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
|
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
|
|
@ -520,15 +482,15 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content based on view mode */}
|
{/* Content based on view mode */}
|
||||||
{viewMode === "grid" ? <WarehousesGridView /> : <WarehousesListView />}
|
{viewMode === 'grid' ? <WarehousesGridView /> : <WarehousesListView />}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
// Zone Components
|
// Zone Components
|
||||||
const ZonesGridView = () => (
|
const ZonesGridView = () => (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||||
{filteredZones.map((zone) => {
|
{filteredZones.map((zone) => {
|
||||||
const warehouse = warehouses.find((w) => w.id === zone.warehouseId);
|
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={zone.id}
|
key={zone.id}
|
||||||
|
|
@ -540,9 +502,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<FaLayerGroup className="w-4 h-4 text-purple-600" />
|
<FaLayerGroup className="w-4 h-4 text-purple-600" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-sm text-gray-900">
|
<h3 className="font-medium text-sm text-gray-900">{zone.name}</h3>
|
||||||
{zone.name}
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-600">{zone.zoneCode}</p>
|
<p className="text-gray-600">{zone.zoneCode}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -564,13 +524,9 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
|
|
||||||
{(zone.temperature || zone.humidity) && (
|
{(zone.temperature || zone.humidity) && (
|
||||||
<div className="bg-gray-50 rounded-lg p-2">
|
<div className="bg-gray-50 rounded-lg p-2">
|
||||||
<h4 className="text-xs font-medium text-gray-900 mb-1">
|
<h4 className="text-xs font-medium text-gray-900 mb-1">Çevresel Koşullar</h4>
|
||||||
Çevresel Koşullar
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
|
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
|
||||||
{zone.temperature && (
|
{zone.temperature && <div>Sıcaklık: {zone.temperature}°C</div>}
|
||||||
<div>Sıcaklık: {zone.temperature}°C</div>
|
|
||||||
)}
|
|
||||||
{zone.humidity && <div>Nem: %{zone.humidity}</div>}
|
{zone.humidity && <div>Nem: %{zone.humidity}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -593,21 +549,21 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleZoneAction("view", zone)}
|
onClick={() => handleZoneAction('view', zone)}
|
||||||
className="p-1 text-gray-400 hover:text-green-600"
|
className="p-1 text-gray-400 hover:text-green-600"
|
||||||
title="Görüntüle"
|
title="Görüntüle"
|
||||||
>
|
>
|
||||||
<FaEye className="w-4 h-4" />
|
<FaEye className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleZoneAction("edit", zone)}
|
onClick={() => handleZoneAction('edit', zone)}
|
||||||
className="p-1 text-gray-400 hover:text-blue-600"
|
className="p-1 text-gray-400 hover:text-blue-600"
|
||||||
title="Düzenle"
|
title="Düzenle"
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete("zone", zone.id)}
|
onClick={() => handleDelete('zone', zone.id)}
|
||||||
className="p-1 text-gray-400 hover:text-red-600"
|
className="p-1 text-gray-400 hover:text-red-600"
|
||||||
title="Sil"
|
title="Sil"
|
||||||
>
|
>
|
||||||
|
|
@ -616,10 +572,10 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const ZonesListView = () => (
|
const ZonesListView = () => (
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||||
|
|
@ -652,9 +608,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{filteredZones.map((zone) => {
|
{filteredZones.map((zone) => {
|
||||||
const warehouse = warehouses.find(
|
const warehouse = warehouses.find((w) => w.id === zone.warehouseId)
|
||||||
(w) => w.id === zone.warehouseId
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<tr key={zone.id} className="hover:bg-gray-50">
|
<tr key={zone.id} className="hover:bg-gray-50">
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
|
@ -663,22 +617,14 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<FaLayerGroup className="w-4 h-4 text-purple-600" />
|
<FaLayerGroup className="w-4 h-4 text-purple-600" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<div className="text-sm font-medium text-gray-900">{zone.name}</div>
|
||||||
{zone.name}
|
<div className="text-sm text-gray-500">{zone.zoneCode}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{zone.zoneCode}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<div className="text-sm text-gray-900">
|
<div className="text-sm text-gray-900">{warehouse?.name}</div>
|
||||||
{warehouse?.name}
|
<div className="text-sm text-gray-500">{warehouse?.code}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{warehouse?.code}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
|
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
|
||||||
|
|
@ -686,10 +632,10 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||||
{zone.temperature ? `${zone.temperature}°C` : "-"}
|
{zone.temperature ? `${zone.temperature}°C` : '-'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||||
{zone.humidity ? `%${zone.humidity}` : "-"}
|
{zone.humidity ? `%${zone.humidity}` : '-'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
{zone.isActive ? (
|
{zone.isActive ? (
|
||||||
|
|
@ -707,21 +653,21 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleZoneAction("view", zone)}
|
onClick={() => handleZoneAction('view', zone)}
|
||||||
className="text-green-600 hover:text-green-900"
|
className="text-green-600 hover:text-green-900"
|
||||||
title="Görüntüle"
|
title="Görüntüle"
|
||||||
>
|
>
|
||||||
<FaEye className="w-4 h-4" />
|
<FaEye className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleZoneAction("edit", zone)}
|
onClick={() => handleZoneAction('edit', zone)}
|
||||||
className="text-blue-600 hover:text-blue-900"
|
className="text-blue-600 hover:text-blue-900"
|
||||||
title="Düzenle"
|
title="Düzenle"
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete("zone", zone.id)}
|
onClick={() => handleDelete('zone', zone.id)}
|
||||||
className="text-red-600 hover:text-red-900"
|
className="text-red-600 hover:text-red-900"
|
||||||
title="Sil"
|
title="Sil"
|
||||||
>
|
>
|
||||||
|
|
@ -730,13 +676,13 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const ZonesTab = () => (
|
const ZonesTab = () => (
|
||||||
<div className="space-y-4 pt-2">
|
<div className="space-y-4 pt-2">
|
||||||
|
|
@ -755,7 +701,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleZoneAction("create")}
|
onClick={() => handleZoneAction('create')}
|
||||||
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
|
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
|
|
@ -764,18 +710,17 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content based on view mode */}
|
{/* Content based on view mode */}
|
||||||
{viewMode === "grid" ? <ZonesGridView /> : <ZonesListView />}
|
{viewMode === 'grid' ? <ZonesGridView /> : <ZonesListView />}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
// Location Components
|
// Location Components
|
||||||
const LocationsGridView = () => (
|
const LocationsGridView = () => (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
{filteredLocations.map((location) => {
|
{filteredLocations.map((location) => {
|
||||||
const warehouse = warehouses.find((w) => w.id === location.warehouseId);
|
const warehouse = warehouses.find((w) => w.id === location.warehouseId)
|
||||||
const zone = zones.find((z) => z.id === location.zoneId);
|
const zone = zones.find((z) => z.id === location.zoneId)
|
||||||
const utilizationPercentage =
|
const utilizationPercentage = (location.currentStock / location.capacity) * 100
|
||||||
(location.currentStock / location.capacity) * 100;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -789,9 +734,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-gray-900">{location.name}</h3>
|
<h3 className="font-medium text-gray-900">{location.name}</h3>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">{location.locationCode}</p>
|
||||||
{location.locationCode}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
|
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
|
||||||
|
|
@ -815,9 +758,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
{/* Dimensions */}
|
{/* Dimensions */}
|
||||||
{location.dimensions && (
|
{location.dimensions && (
|
||||||
<div className="bg-gray-50 rounded-lg p-2 mt-2">
|
<div className="bg-gray-50 rounded-lg p-2 mt-2">
|
||||||
<h4 className="text-sm font-medium text-gray-900 mb-2">
|
<h4 className="text-sm font-medium text-gray-900 mb-2">Boyutlar</h4>
|
||||||
Boyutlar
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
|
<div className="grid grid-cols-2 gap-2 text-xs text-gray-600">
|
||||||
<div>Uzunluk: {location.dimensions.length}m</div>
|
<div>Uzunluk: {location.dimensions.length}m</div>
|
||||||
<div>Genişlik: {location.dimensions.width}m</div>
|
<div>Genişlik: {location.dimensions.width}m</div>
|
||||||
|
|
@ -842,10 +783,10 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
className={`h-2 rounded-full ${
|
className={`h-2 rounded-full ${
|
||||||
utilizationPercentage > 90
|
utilizationPercentage > 90
|
||||||
? "bg-red-500"
|
? 'bg-red-500'
|
||||||
: utilizationPercentage > 70
|
: utilizationPercentage > 70
|
||||||
? "bg-yellow-500"
|
? 'bg-yellow-500'
|
||||||
: "bg-green-500"
|
: 'bg-green-500'
|
||||||
}`}
|
}`}
|
||||||
style={{ width: `${utilizationPercentage}%` }}
|
style={{ width: `${utilizationPercentage}%` }}
|
||||||
></div>
|
></div>
|
||||||
|
|
@ -858,9 +799,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
{/* Restrictions */}
|
{/* Restrictions */}
|
||||||
{location.restrictions && location.restrictions.length > 0 && (
|
{location.restrictions && location.restrictions.length > 0 && (
|
||||||
<div className="pt-2">
|
<div className="pt-2">
|
||||||
<h4 className="text-sm font-medium text-gray-900 mb-1">
|
<h4 className="text-sm font-medium text-gray-900 mb-1">Kısıtlamalar</h4>
|
||||||
Kısıtlamalar
|
|
||||||
</h4>
|
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{location.restrictions.map((restriction, index) => (
|
{location.restrictions.map((restriction, index) => (
|
||||||
<span
|
<span
|
||||||
|
|
@ -891,21 +830,21 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleLocationAction("view", location)}
|
onClick={() => handleLocationAction('view', location)}
|
||||||
className="p-1 text-gray-400 hover:text-green-600"
|
className="p-1 text-gray-400 hover:text-green-600"
|
||||||
title="Görüntüle"
|
title="Görüntüle"
|
||||||
>
|
>
|
||||||
<FaEye className="w-4 h-4" />
|
<FaEye className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleLocationAction("edit", location)}
|
onClick={() => handleLocationAction('edit', location)}
|
||||||
className="p-1 text-gray-400 hover:text-blue-600"
|
className="p-1 text-gray-400 hover:text-blue-600"
|
||||||
title="Düzenle"
|
title="Düzenle"
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete("location", location.id)}
|
onClick={() => handleDelete('location', location.id)}
|
||||||
className="p-1 text-gray-400 hover:text-red-600"
|
className="p-1 text-gray-400 hover:text-red-600"
|
||||||
title="Sil"
|
title="Sil"
|
||||||
>
|
>
|
||||||
|
|
@ -914,10 +853,10 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const LocationsListView = () => (
|
const LocationsListView = () => (
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||||
|
|
@ -953,12 +892,9 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{filteredLocations.map((location) => {
|
{filteredLocations.map((location) => {
|
||||||
const warehouse = warehouses.find(
|
const warehouse = warehouses.find((w) => w.id === location.warehouseId)
|
||||||
(w) => w.id === location.warehouseId
|
const zone = zones.find((z) => z.id === location.zoneId)
|
||||||
);
|
const utilizationPercentage = (location.currentStock / location.capacity) * 100
|
||||||
const zone = zones.find((z) => z.id === location.zoneId);
|
|
||||||
const utilizationPercentage =
|
|
||||||
(location.currentStock / location.capacity) * 100;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={location.id} className="hover:bg-gray-50">
|
<tr key={location.id} className="hover:bg-gray-50">
|
||||||
|
|
@ -968,30 +904,18 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<FaMapMarkerAlt className="w-4 h-4 text-orange-600" />
|
<FaMapMarkerAlt className="w-4 h-4 text-orange-600" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<div className="text-sm font-medium text-gray-900">{location.name}</div>
|
||||||
{location.name}
|
<div className="text-sm text-gray-500">{location.locationCode}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{location.locationCode}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<div className="text-sm text-gray-900">
|
<div className="text-sm text-gray-900">{warehouse?.name}</div>
|
||||||
{warehouse?.name}
|
<div className="text-sm text-gray-500">{warehouse?.code}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{warehouse?.code}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<div className="text-sm text-gray-900">
|
<div className="text-sm text-gray-900">{zone?.name || '-'}</div>
|
||||||
{zone?.name || "-"}
|
<div className="text-sm text-gray-500">{zone?.zoneCode || '-'}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{zone?.zoneCode || "-"}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
|
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-800">
|
||||||
|
|
@ -1007,10 +931,10 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
className={`h-2 rounded-full ${
|
className={`h-2 rounded-full ${
|
||||||
utilizationPercentage > 90
|
utilizationPercentage > 90
|
||||||
? "bg-red-500"
|
? 'bg-red-500'
|
||||||
: utilizationPercentage > 70
|
: utilizationPercentage > 70
|
||||||
? "bg-yellow-500"
|
? 'bg-yellow-500'
|
||||||
: "bg-green-500"
|
: 'bg-green-500'
|
||||||
}`}
|
}`}
|
||||||
style={{ width: `${utilizationPercentage}%` }}
|
style={{ width: `${utilizationPercentage}%` }}
|
||||||
></div>
|
></div>
|
||||||
|
|
@ -1039,21 +963,21 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleLocationAction("view", location)}
|
onClick={() => handleLocationAction('view', location)}
|
||||||
className="text-green-600 hover:text-green-900"
|
className="text-green-600 hover:text-green-900"
|
||||||
title="Görüntüle"
|
title="Görüntüle"
|
||||||
>
|
>
|
||||||
<FaEye className="w-4 h-4" />
|
<FaEye className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleLocationAction("edit", location)}
|
onClick={() => handleLocationAction('edit', location)}
|
||||||
className="text-blue-600 hover:text-blue-900"
|
className="text-blue-600 hover:text-blue-900"
|
||||||
title="Düzenle"
|
title="Düzenle"
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete("location", location.id)}
|
onClick={() => handleDelete('location', location.id)}
|
||||||
className="text-red-600 hover:text-red-900"
|
className="text-red-600 hover:text-red-900"
|
||||||
title="Sil"
|
title="Sil"
|
||||||
>
|
>
|
||||||
|
|
@ -1062,13 +986,13 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
const LocationsTab = () => (
|
const LocationsTab = () => (
|
||||||
<div className="space-y-4 pt-2">
|
<div className="space-y-4 pt-2">
|
||||||
|
|
@ -1087,7 +1011,7 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleLocationAction("create")}
|
onClick={() => handleLocationAction('create')}
|
||||||
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
|
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
|
|
@ -1096,42 +1020,39 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content based on view mode */}
|
{/* Content based on view mode */}
|
||||||
{viewMode === "grid" ? <LocationsGridView /> : <LocationsListView />}
|
{viewMode === 'grid' ? <LocationsGridView /> : <LocationsListView />}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Depo Tanımları</h2>
|
<h2 className="text-2xl font-bold text-gray-900">Depo Tanımları</h2>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">Depolar, bölgeler ve lokasyonları yönetin</p>
|
||||||
Depolar, bölgeler ve lokasyonları yönetin
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* View Toggle Buttons */}
|
{/* View Toggle Buttons */}
|
||||||
{(activeTab === "warehouses" ||
|
{(activeTab === 'warehouses' || activeTab === 'zones' || activeTab === 'locations') && (
|
||||||
activeTab === "zones" ||
|
|
||||||
activeTab === "locations") && (
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("grid")}
|
onClick={() => setViewMode('grid')}
|
||||||
className={`p-1.5 rounded-lg ${
|
className={`p-1.5 rounded-lg ${
|
||||||
viewMode === "grid"
|
viewMode === 'grid'
|
||||||
? "bg-blue-100 text-blue-600"
|
? 'bg-blue-100 text-blue-600'
|
||||||
: "text-gray-400 hover:text-gray-600"
|
: 'text-gray-400 hover:text-gray-600'
|
||||||
}`}
|
}`}
|
||||||
title="Kart Görünümü"
|
title="Kart Görünümü"
|
||||||
>
|
>
|
||||||
<FaTh className="w-4 h-4" />
|
<FaTh className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("list")}
|
onClick={() => setViewMode('list')}
|
||||||
className={`p-1.5 rounded-lg ${
|
className={`p-1.5 rounded-lg ${
|
||||||
viewMode === "list"
|
viewMode === 'list'
|
||||||
? "bg-blue-100 text-blue-600"
|
? 'bg-blue-100 text-blue-600'
|
||||||
: "text-gray-400 hover:text-gray-600"
|
: 'text-gray-400 hover:text-gray-600'
|
||||||
}`}
|
}`}
|
||||||
title="Liste Görünümü"
|
title="Liste Görünümü"
|
||||||
>
|
>
|
||||||
|
|
@ -1145,11 +1066,11 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
<div className="border-b border-gray-200">
|
<div className="border-b border-gray-200">
|
||||||
<nav className="-mb-px flex space-x-8" aria-label="Tabs">
|
<nav className="-mb-px flex space-x-8" aria-label="Tabs">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab("warehouses")}
|
onClick={() => setActiveTab('warehouses')}
|
||||||
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
|
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
|
||||||
activeTab === "warehouses"
|
activeTab === 'warehouses'
|
||||||
? "border-blue-500 text-blue-600"
|
? 'border-blue-500 text-blue-600'
|
||||||
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -1158,11 +1079,11 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab("zones")}
|
onClick={() => setActiveTab('zones')}
|
||||||
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
|
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
|
||||||
activeTab === "zones"
|
activeTab === 'zones'
|
||||||
? "border-blue-500 text-blue-600"
|
? 'border-blue-500 text-blue-600'
|
||||||
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -1171,11 +1092,11 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab("locations")}
|
onClick={() => setActiveTab('locations')}
|
||||||
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
|
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm ${
|
||||||
activeTab === "locations"
|
activeTab === 'locations'
|
||||||
? "border-blue-500 text-blue-600"
|
? 'border-blue-500 text-blue-600'
|
||||||
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -1187,9 +1108,10 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tab Content */}
|
{/* Tab Content */}
|
||||||
{activeTab === "warehouses" && <WarehousesTab />}
|
{activeTab === 'warehouses' && <WarehousesTab />}
|
||||||
{activeTab === "zones" && <ZonesTab />}
|
{activeTab === 'zones' && <ZonesTab />}
|
||||||
{activeTab === "locations" && <LocationsTab />}
|
{activeTab === 'locations' && <LocationsTab />}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Modals */}
|
{/* Modals */}
|
||||||
<WarehouseModal
|
<WarehouseModal
|
||||||
|
|
@ -1218,8 +1140,8 @@ const WarehouseDefinitions: React.FC = () => {
|
||||||
warehouses={warehouses}
|
warehouses={warehouses}
|
||||||
zones={zones}
|
zones={zones}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default WarehouseDefinitions;
|
export default WarehouseDefinitions
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,36 @@
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from 'react'
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import {
|
import { FaSave, FaTimes, FaBox, FaMapMarkerAlt, FaLayerGroup, FaBuilding } from 'react-icons/fa'
|
||||||
FaSave,
|
import LoadingSpinner from '../../../components/common/LoadingSpinner'
|
||||||
FaTimes,
|
import { WmWarehouse, WarehouseTypeEnum } from '../../../types/wm'
|
||||||
FaBox,
|
import { SecurityLevelEnum } from '../../../types/mrp'
|
||||||
FaMapMarkerAlt,
|
import { mockWarehouses } from '../../../mocks/mockWarehouses'
|
||||||
FaLayerGroup,
|
import { Container } from '@/components/shared'
|
||||||
FaBuilding,
|
|
||||||
} from "react-icons/fa";
|
|
||||||
import LoadingSpinner from "../../../components/common/LoadingSpinner";
|
|
||||||
import { WmWarehouse, WarehouseTypeEnum } from "../../../types/wm";
|
|
||||||
import { SecurityLevelEnum } from "../../../types/mrp";
|
|
||||||
import { mockWarehouses } from "../../../mocks/mockWarehouses";
|
|
||||||
|
|
||||||
interface ValidationErrors {
|
interface ValidationErrors {
|
||||||
[key: string]: string;
|
[key: string]: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const WarehouseForm: React.FC = () => {
|
const WarehouseForm: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>()
|
||||||
const isEdit = Boolean(id);
|
const isEdit = Boolean(id)
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false)
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false)
|
||||||
const [errors, setErrors] = useState<ValidationErrors>({});
|
const [errors, setErrors] = useState<ValidationErrors>({})
|
||||||
|
|
||||||
const [formData, setFormData] = useState<WmWarehouse>({
|
const [formData, setFormData] = useState<WmWarehouse>({
|
||||||
id: "",
|
id: '',
|
||||||
code: "",
|
code: '',
|
||||||
name: "",
|
name: '',
|
||||||
description: "",
|
description: '',
|
||||||
address: {
|
address: {
|
||||||
street: "",
|
street: '',
|
||||||
city: "",
|
city: '',
|
||||||
state: "",
|
state: '',
|
||||||
postalCode: "",
|
postalCode: '',
|
||||||
country: "Türkiye",
|
country: 'Türkiye',
|
||||||
},
|
},
|
||||||
warehouseType: WarehouseTypeEnum.RawMaterials,
|
warehouseType: WarehouseTypeEnum.RawMaterials,
|
||||||
locations: [],
|
locations: [],
|
||||||
|
|
@ -50,132 +44,129 @@ const WarehouseForm: React.FC = () => {
|
||||||
managerId: 0,
|
managerId: 0,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
});
|
})
|
||||||
|
|
||||||
const loadFormData = useCallback(async () => {
|
const loadFormData = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
if (isEdit && id) {
|
if (isEdit && id) {
|
||||||
// Simulate API call
|
// Simulate API call
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
const warehouse = mockWarehouses.find((w) => w.id === id);
|
const warehouse = mockWarehouses.find((w) => w.id === id)
|
||||||
if (warehouse) {
|
if (warehouse) {
|
||||||
setFormData(warehouse);
|
setFormData(warehouse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading form data:", error);
|
console.error('Error loading form data:', error)
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [isEdit, id]);
|
}, [isEdit, id])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadFormData();
|
loadFormData()
|
||||||
}, [loadFormData]);
|
}, [loadFormData])
|
||||||
|
|
||||||
const validateForm = (): boolean => {
|
const validateForm = (): boolean => {
|
||||||
const newErrors: ValidationErrors = {};
|
const newErrors: ValidationErrors = {}
|
||||||
|
|
||||||
if (!formData.code.trim()) {
|
if (!formData.code.trim()) {
|
||||||
newErrors.code = "Depo kodu zorunludur";
|
newErrors.code = 'Depo kodu zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.name.trim()) {
|
if (!formData.name.trim()) {
|
||||||
newErrors.name = "Depo adı zorunludur";
|
newErrors.name = 'Depo adı zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.warehouseType) {
|
if (!formData.warehouseType) {
|
||||||
newErrors.warehouseType = "Depo tipi seçilmelidir";
|
newErrors.warehouseType = 'Depo tipi seçilmelidir'
|
||||||
}
|
}
|
||||||
if (!formData.address?.street.trim()) {
|
if (!formData.address?.street.trim()) {
|
||||||
newErrors["address.street"] = "Adres zorunludur";
|
newErrors['address.street'] = 'Adres zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.address?.city.trim()) {
|
if (!formData.address?.city.trim()) {
|
||||||
newErrors["address.city"] = "Şehir zorunludur";
|
newErrors['address.city'] = 'Şehir zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.address?.country.trim()) {
|
if (!formData.address?.country.trim()) {
|
||||||
newErrors["address.country"] = "Ülke zorunludur";
|
newErrors['address.country'] = 'Ülke zorunludur'
|
||||||
}
|
}
|
||||||
if (formData.capacity <= 0) {
|
if (formData.capacity <= 0) {
|
||||||
newErrors.capacity = "Kapasite 0'dan büyük olmalıdır";
|
newErrors.capacity = "Kapasite 0'dan büyük olmalıdır"
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputChange = (field: string, value: any) => {
|
const handleInputChange = (field: string, value: any) => {
|
||||||
setFormData((prev) => {
|
setFormData((prev) => {
|
||||||
const updated = { ...prev };
|
const updated = { ...prev }
|
||||||
const keys = field.split(".");
|
const keys = field.split('.')
|
||||||
let current: any = updated;
|
let current: any = updated
|
||||||
keys.forEach((key, index) => {
|
keys.forEach((key, index) => {
|
||||||
if (index === keys.length - 1) {
|
if (index === keys.length - 1) {
|
||||||
current[key] = value;
|
current[key] = value
|
||||||
} else {
|
} else {
|
||||||
current[key] = { ...current[key] };
|
current[key] = { ...current[key] }
|
||||||
current = current[key];
|
current = current[key]
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return updated;
|
return updated
|
||||||
});
|
})
|
||||||
|
|
||||||
if (errors[field]) {
|
if (errors[field]) {
|
||||||
setErrors((prev) => ({ ...prev, [field]: "" }));
|
setErrors((prev) => ({ ...prev, [field]: '' }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
|
|
||||||
if (!validateForm()) {
|
if (!validateForm()) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setSaving(true);
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
// Simulate API call
|
// Simulate API call
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||||
|
|
||||||
console.log("Warehouse data:", {
|
console.log('Warehouse data:', {
|
||||||
...formData,
|
...formData,
|
||||||
id: isEdit ? id : undefined,
|
id: isEdit ? id : undefined,
|
||||||
});
|
})
|
||||||
|
|
||||||
// Show success message
|
// Show success message
|
||||||
alert(
|
alert(isEdit ? 'Depo başarıyla güncellendi!' : 'Depo başarıyla oluşturuldu!')
|
||||||
isEdit ? "Depo başarıyla güncellendi!" : "Depo başarıyla oluşturuldu!"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Navigate back to list
|
// Navigate back to list
|
||||||
navigate("/admin/warehouse");
|
navigate('/admin/warehouse')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving warehouse:", error);
|
console.error('Error saving warehouse:', error)
|
||||||
alert("Bir hata oluştu. Lütfen tekrar deneyin.");
|
alert('Bir hata oluştu. Lütfen tekrar deneyin.')
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
navigate("/admin/warehouse");
|
navigate('/admin/warehouse')
|
||||||
};
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <LoadingSpinner />;
|
return <LoadingSpinner />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
|
<div className="space-y-2">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-semibold text-gray-900">
|
<h2 className="text-xl font-semibold text-gray-900">
|
||||||
{isEdit ? "Depo Düzenle" : "Yeni Depo"}
|
{isEdit ? 'Depo Düzenle' : 'Yeni Depo'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-gray-600 mt-1">
|
<p className="text-sm text-gray-600 mt-1">
|
||||||
{isEdit
|
{isEdit ? 'Mevcut depo bilgilerini güncelleyin' : 'Yeni depo bilgilerini girin'}
|
||||||
? "Mevcut depo bilgilerini güncelleyin"
|
|
||||||
: "Yeni depo bilgilerini girin"}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -200,17 +191,15 @@ const WarehouseForm: React.FC = () => {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.code}
|
value={formData.code}
|
||||||
onChange={(e) => handleInputChange("code", e.target.value)}
|
onChange={(e) => handleInputChange('code', e.target.value)}
|
||||||
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
||||||
errors.code
|
errors.code
|
||||||
? "border-red-300 focus:border-red-500 focus:ring-red-500"
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
|
||||||
}`}
|
}`}
|
||||||
placeholder="Örn: WH001"
|
placeholder="Örn: WH001"
|
||||||
/>
|
/>
|
||||||
{errors.code && (
|
{errors.code && <p className="mt-1 text-sm text-red-600">{errors.code}</p>}
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.code}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -219,13 +208,11 @@ const WarehouseForm: React.FC = () => {
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={formData.warehouseType}
|
value={formData.warehouseType}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('warehouseType', e.target.value)}
|
||||||
handleInputChange("warehouseType", e.target.value)
|
|
||||||
}
|
|
||||||
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
||||||
errors.warehouseType
|
errors.warehouseType
|
||||||
? "border-red-300 focus:border-red-500 focus:ring-red-500"
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<option value="">Tip seçin</option>
|
<option value="">Tip seçin</option>
|
||||||
|
|
@ -239,42 +226,32 @@ const WarehouseForm: React.FC = () => {
|
||||||
<option value="HAZARDOUS">Tehlikeli Madde Deposu</option>
|
<option value="HAZARDOUS">Tehlikeli Madde Deposu</option>
|
||||||
</select>
|
</select>
|
||||||
{errors.warehouseType && (
|
{errors.warehouseType && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">{errors.warehouseType}</p>
|
||||||
{errors.warehouseType}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Depo Adı *</label>
|
||||||
Depo Adı *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) => handleInputChange("name", e.target.value)}
|
onChange={(e) => handleInputChange('name', e.target.value)}
|
||||||
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
||||||
errors.name
|
errors.name
|
||||||
? "border-red-300 focus:border-red-500 focus:ring-red-500"
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
|
||||||
}`}
|
}`}
|
||||||
placeholder="Depo adı"
|
placeholder="Depo adı"
|
||||||
/>
|
/>
|
||||||
{errors.name && (
|
{errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.name}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama</label>
|
||||||
Açıklama
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||||
handleInputChange("description", e.target.value)
|
|
||||||
}
|
|
||||||
rows={3}
|
rows={3}
|
||||||
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
|
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
|
||||||
placeholder="Depo açıklaması"
|
placeholder="Depo açıklaması"
|
||||||
|
|
@ -291,16 +268,11 @@ const WarehouseForm: React.FC = () => {
|
||||||
min="0"
|
min="0"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
value={formData.capacity}
|
value={formData.capacity}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('capacity', parseFloat(e.target.value) || 0)}
|
||||||
handleInputChange(
|
|
||||||
"capacity",
|
|
||||||
parseFloat(e.target.value) || 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
||||||
errors.capacity
|
errors.capacity
|
||||||
? "border-red-300 focus:border-red-500 focus:ring-red-500"
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
|
||||||
}`}
|
}`}
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
/>
|
/>
|
||||||
|
|
@ -315,15 +287,10 @@ const WarehouseForm: React.FC = () => {
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="isMainWarehouse"
|
id="isMainWarehouse"
|
||||||
checked={formData.isMainWarehouse}
|
checked={formData.isMainWarehouse}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('isMainWarehouse', e.target.checked)}
|
||||||
handleInputChange("isMainWarehouse", e.target.checked)
|
|
||||||
}
|
|
||||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
/>
|
/>
|
||||||
<label
|
<label htmlFor="isMainWarehouse" className="ml-2 block text-sm text-gray-900">
|
||||||
htmlFor="isMainWarehouse"
|
|
||||||
className="ml-2 block text-sm text-gray-900"
|
|
||||||
>
|
|
||||||
Ana Depo
|
Ana Depo
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -333,15 +300,10 @@ const WarehouseForm: React.FC = () => {
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="isActive"
|
id="isActive"
|
||||||
checked={formData.isActive}
|
checked={formData.isActive}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('isActive', e.target.checked)}
|
||||||
handleInputChange("isActive", e.target.checked)
|
|
||||||
}
|
|
||||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
/>
|
/>
|
||||||
<label
|
<label htmlFor="isActive" className="ml-2 block text-sm text-gray-900">
|
||||||
htmlFor="isActive"
|
|
||||||
className="ml-2 block text-sm text-gray-900"
|
|
||||||
>
|
|
||||||
Aktif
|
Aktif
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -361,105 +323,79 @@ const WarehouseForm: React.FC = () => {
|
||||||
|
|
||||||
<div className="p-4 space-y-3">
|
<div className="p-4 space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Adres *</label>
|
||||||
Adres *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.address?.street}
|
value={formData.address?.street}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('address.street', e.target.value)}
|
||||||
handleInputChange("address.street", e.target.value)
|
|
||||||
}
|
|
||||||
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
||||||
errors["address.street"]
|
errors['address.street']
|
||||||
? "border-red-300 focus:border-red-500 focus:ring-red-500"
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
|
||||||
}`}
|
}`}
|
||||||
placeholder="Sokak, cadde, mahalle"
|
placeholder="Sokak, cadde, mahalle"
|
||||||
/>
|
/>
|
||||||
{errors["address.street"] && (
|
{errors['address.street'] && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">{errors['address.street']}</p>
|
||||||
{errors["address.street"]}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Şehir *</label>
|
||||||
Şehir *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.address?.city}
|
value={formData.address?.city}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('address.city', e.target.value)}
|
||||||
handleInputChange("address.city", e.target.value)
|
|
||||||
}
|
|
||||||
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
||||||
errors["address.city"]
|
errors['address.city']
|
||||||
? "border-red-300 focus:border-red-500 focus:ring-red-500"
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
|
||||||
}`}
|
}`}
|
||||||
placeholder="Şehir"
|
placeholder="Şehir"
|
||||||
/>
|
/>
|
||||||
{errors["address.city"] && (
|
{errors['address.city'] && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">{errors['address.city']}</p>
|
||||||
{errors["address.city"]}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">İl/Bölge</label>
|
||||||
İl/Bölge
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.address?.state}
|
value={formData.address?.state}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('address.state', e.target.value)}
|
||||||
handleInputChange("address.state", e.target.value)
|
|
||||||
}
|
|
||||||
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
|
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
|
||||||
placeholder="İl/Bölge"
|
placeholder="İl/Bölge"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Posta Kodu</label>
|
||||||
Posta Kodu
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.address?.postalCode}
|
value={formData.address?.postalCode}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('address.postalCode', e.target.value)}
|
||||||
handleInputChange("address.postalCode", e.target.value)
|
|
||||||
}
|
|
||||||
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
|
className="block w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:border-blue-500 focus:ring-blue-500"
|
||||||
placeholder="34000"
|
placeholder="34000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Ülke *</label>
|
||||||
Ülke *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.address?.country}
|
value={formData.address?.country}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('address.country', e.target.value)}
|
||||||
handleInputChange("address.country", e.target.value)
|
|
||||||
}
|
|
||||||
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
className={`block w-full px-3 py-1.5 text-sm border rounded-md shadow-sm focus:outline-none focus:ring-1 ${
|
||||||
errors["address.country"]
|
errors['address.country']
|
||||||
? "border-red-300 focus:border-red-500 focus:ring-red-500"
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
: "border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'
|
||||||
}`}
|
}`}
|
||||||
placeholder="Ülke"
|
placeholder="Ülke"
|
||||||
/>
|
/>
|
||||||
{errors["address.country"] && (
|
{errors['address.country'] && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">{errors['address.country']}</p>
|
||||||
{errors["address.country"]}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -479,25 +415,17 @@ const WarehouseForm: React.FC = () => {
|
||||||
<div className="bg-gray-50 rounded-lg p-4">
|
<div className="bg-gray-50 rounded-lg p-4">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-gray-700">
|
<span className="font-medium text-gray-700">Toplam Kapasite:</span>
|
||||||
Toplam Kapasite:
|
|
||||||
</span>
|
|
||||||
<div className="text-lg font-semibold text-blue-600">
|
<div className="text-lg font-semibold text-blue-600">
|
||||||
{formData.capacity.toLocaleString()} m³
|
{formData.capacity.toLocaleString()} m³
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-gray-700">
|
<span className="font-medium text-gray-700">Mevcut Kullanım:</span>
|
||||||
Mevcut Kullanım:
|
<div className="text-lg font-semibold text-green-600">0 m³ (0%)</div>
|
||||||
</span>
|
|
||||||
<div className="text-lg font-semibold text-green-600">
|
|
||||||
0 m³ (0%)
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-gray-700">
|
<span className="font-medium text-gray-700">Müsait Kapasite:</span>
|
||||||
Müsait Kapasite:
|
|
||||||
</span>
|
|
||||||
<div className="text-lg font-semibold text-gray-600">
|
<div className="text-lg font-semibold text-gray-600">
|
||||||
{formData.capacity.toLocaleString()} m³
|
{formData.capacity.toLocaleString()} m³
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -506,10 +434,7 @@ const WarehouseForm: React.FC = () => {
|
||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||||
<div
|
<div className="bg-blue-600 h-2 rounded-full" style={{ width: '0%' }} />
|
||||||
className="bg-blue-600 h-2 rounded-full"
|
|
||||||
style={{ width: "0%" }}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-600 mt-1">Kullanım oranı: 0%</p>
|
<p className="text-xs text-gray-600 mt-1">Kullanım oranı: 0%</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -532,25 +457,20 @@ const WarehouseForm: React.FC = () => {
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<p className="text-sm text-blue-700">
|
<p className="text-sm text-blue-700">
|
||||||
{formData.warehouseType ===
|
{formData.warehouseType === WarehouseTypeEnum.RawMaterials &&
|
||||||
WarehouseTypeEnum.RawMaterials &&
|
'Hammadde depolama için özel koşullara sahip olacaktır.'}
|
||||||
"Hammadde depolama için özel koşullara sahip olacaktır."}
|
{formData.warehouseType === WarehouseTypeEnum.FinishedGoods &&
|
||||||
{formData.warehouseType ===
|
'Mamul depolama ve sevkiyat operasyonları için optimize edilecektir.'}
|
||||||
WarehouseTypeEnum.FinishedGoods &&
|
{formData.warehouseType === WarehouseTypeEnum.SpareParts &&
|
||||||
"Mamul depolama ve sevkiyat operasyonları için optimize edilecektir."}
|
'Yedek parça yönetimi için özel raflar ve kategorilendirme yapılacaktır.'}
|
||||||
{formData.warehouseType ===
|
|
||||||
WarehouseTypeEnum.SpareParts &&
|
|
||||||
"Yedek parça yönetimi için özel raflar ve kategorilendirme yapılacaktır."}
|
|
||||||
{formData.warehouseType === WarehouseTypeEnum.Returns &&
|
{formData.warehouseType === WarehouseTypeEnum.Returns &&
|
||||||
"Konsinye stok yönetimi için ayrı izleme sistemi kullanılacaktır."}
|
'Konsinye stok yönetimi için ayrı izleme sistemi kullanılacaktır.'}
|
||||||
{formData.warehouseType ===
|
{formData.warehouseType === WarehouseTypeEnum.Quarantine &&
|
||||||
WarehouseTypeEnum.Quarantine &&
|
'Karantina prosedürleri ve onay mekanizmaları aktif olacaktır.'}
|
||||||
"Karantina prosedürleri ve onay mekanizmaları aktif olacaktır."}
|
|
||||||
{formData.warehouseType === WarehouseTypeEnum.Transit &&
|
{formData.warehouseType === WarehouseTypeEnum.Transit &&
|
||||||
"Sıcaklık kontrollü depolama sistemi kurulacaktır."}
|
'Sıcaklık kontrollü depolama sistemi kurulacaktır.'}
|
||||||
{formData.warehouseType ===
|
{formData.warehouseType === WarehouseTypeEnum.WorkInProgress &&
|
||||||
WarehouseTypeEnum.WorkInProgress &&
|
'Tehlikeli madde depolama güvenlik protokolleri uygulanacaktır.'}
|
||||||
"Tehlikeli madde depolama güvenlik protokolleri uygulanacaktır."}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -582,14 +502,15 @@ const WarehouseForm: React.FC = () => {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<FaSave className="w-4 h-4 mr-2" />
|
<FaSave className="w-4 h-4 mr-2" />
|
||||||
{isEdit ? "Güncelle" : "Kaydet"}
|
{isEdit ? 'Güncelle' : 'Kaydet'}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
</Container>
|
||||||
};
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default WarehouseForm;
|
export default WarehouseForm
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from 'react-router-dom'
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import {
|
import {
|
||||||
FaBuilding,
|
FaBuilding,
|
||||||
FaPlus,
|
FaPlus,
|
||||||
|
|
@ -14,45 +14,42 @@ import {
|
||||||
FaArrowUp,
|
FaArrowUp,
|
||||||
FaExclamationTriangle,
|
FaExclamationTriangle,
|
||||||
FaChartLine,
|
FaChartLine,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import classNames from "classnames";
|
import classNames from 'classnames'
|
||||||
import { WarehouseTypeEnum } from "../../../types/wm";
|
import { WarehouseTypeEnum } from '../../../types/wm'
|
||||||
import { mockWarehouses } from "../../../mocks/mockWarehouses";
|
import { mockWarehouses } from '../../../mocks/mockWarehouses'
|
||||||
import {
|
import { getWarehouseTypeColor, getWarehouseTypeText } from '../../../utils/erp'
|
||||||
getWarehouseTypeColor,
|
import { Container } from '@/components/shared'
|
||||||
getWarehouseTypeText,
|
|
||||||
} from "../../../utils/erp";
|
|
||||||
|
|
||||||
const WarehouseList: React.FC = () => {
|
const WarehouseList: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [filterType, setFilterType] = useState("all");
|
const [filterType, setFilterType] = useState('all')
|
||||||
const [showFilters, setShowFilters] = useState(false);
|
const [showFilters, setShowFilters] = useState(false)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: warehouses,
|
data: warehouses,
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ["warehouses", searchTerm, filterType],
|
queryKey: ['warehouses', searchTerm, filterType],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||||
return mockWarehouses.filter((warehouse) => {
|
return mockWarehouses.filter((warehouse) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
warehouse.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase());
|
warehouse.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
const matchesType =
|
const matchesType = filterType === 'all' || warehouse.warehouseType === filterType
|
||||||
filterType === "all" || warehouse.warehouseType === filterType;
|
return matchesSearch && matchesType
|
||||||
return matchesSearch && matchesType;
|
})
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const getUtilizationColor = (utilization: number) => {
|
const getUtilizationColor = (utilization: number) => {
|
||||||
const percentage = utilization;
|
const percentage = utilization
|
||||||
if (percentage >= 90) return "text-red-600";
|
if (percentage >= 90) return 'text-red-600'
|
||||||
if (percentage >= 75) return "text-yellow-600";
|
if (percentage >= 75) return 'text-yellow-600'
|
||||||
return "text-green-600";
|
return 'text-green-600'
|
||||||
};
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -60,7 +57,7 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||||
<span className="ml-3 text-gray-600">Depolar yükleniyor...</span>
|
<span className="ml-3 text-gray-600">Depolar yükleniyor...</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
@ -71,11 +68,12 @@ const WarehouseList: React.FC = () => {
|
||||||
<span className="text-red-800">Depolar yüklenirken hata oluştu.</span>
|
<span className="text-red-800">Depolar yüklenirken hata oluştu.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
|
<div className="space-y-2">
|
||||||
{/* Header Actions */}
|
{/* Header Actions */}
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -96,10 +94,10 @@ const WarehouseList: React.FC = () => {
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowFilters(!showFilters)}
|
onClick={() => setShowFilters(!showFilters)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"flex items-center px-3 py-1.5 text-sm border rounded-lg transition-colors",
|
'flex items-center px-3 py-1.5 text-sm border rounded-lg transition-colors',
|
||||||
showFilters
|
showFilters
|
||||||
? "border-blue-500 bg-blue-50 text-blue-700"
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||||
: "border-gray-300 bg-white text-gray-700 hover:bg-gray-50"
|
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<FaFilter size={14} className="mr-2" />
|
<FaFilter size={14} className="mr-2" />
|
||||||
|
|
@ -109,7 +107,7 @@ const WarehouseList: React.FC = () => {
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => alert("Dışa aktarma özelliği yakında eklenecek")}
|
onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
|
||||||
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||||
>
|
>
|
||||||
<FaDownload size={14} className="mr-2" />
|
<FaDownload size={14} className="mr-2" />
|
||||||
|
|
@ -131,9 +129,7 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="bg-white border border-gray-200 rounded-lg p-3">
|
<div className="bg-white border border-gray-200 rounded-lg p-3">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
<label className="block text-xs font-medium text-gray-700 mb-1">Depo Türü</label>
|
||||||
Depo Türü
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={filterType}
|
value={filterType}
|
||||||
onChange={(e) => setFilterType(e.target.value)}
|
onChange={(e) => setFilterType(e.target.value)}
|
||||||
|
|
@ -142,12 +138,8 @@ const WarehouseList: React.FC = () => {
|
||||||
<option value="all">Tümü</option>
|
<option value="all">Tümü</option>
|
||||||
<option value={WarehouseTypeEnum.RawMaterials}>Hammadde</option>
|
<option value={WarehouseTypeEnum.RawMaterials}>Hammadde</option>
|
||||||
<option value={WarehouseTypeEnum.FinishedGoods}>Mamul</option>
|
<option value={WarehouseTypeEnum.FinishedGoods}>Mamul</option>
|
||||||
<option value={WarehouseTypeEnum.WorkInProgress}>
|
<option value={WarehouseTypeEnum.WorkInProgress}>Yarı Mamul</option>
|
||||||
Yarı Mamul
|
<option value={WarehouseTypeEnum.SpareParts}>Yedek Parça</option>
|
||||||
</option>
|
|
||||||
<option value={WarehouseTypeEnum.SpareParts}>
|
|
||||||
Yedek Parça
|
|
||||||
</option>
|
|
||||||
<option value={WarehouseTypeEnum.Quarantine}>Karantina</option>
|
<option value={WarehouseTypeEnum.Quarantine}>Karantina</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -155,8 +147,8 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="flex items-end">
|
<div className="flex items-end">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFilterType("all");
|
setFilterType('all')
|
||||||
setSearchTerm("");
|
setSearchTerm('')
|
||||||
}}
|
}}
|
||||||
className="w-full px-4 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
className="w-full px-4 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||||
>
|
>
|
||||||
|
|
@ -173,9 +165,7 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600">Toplam Depo</p>
|
<p className="text-sm font-medium text-gray-600">Toplam Depo</p>
|
||||||
<p className="text-xl font-bold text-gray-900">
|
<p className="text-xl font-bold text-gray-900">{warehouses?.length || 0}</p>
|
||||||
{warehouses?.length || 0}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<FaBuilding className="h-6 w-6 text-blue-500" />
|
<FaBuilding className="h-6 w-6 text-blue-500" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -184,13 +174,9 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600">
|
<p className="text-sm font-medium text-gray-600">Toplam Kapasite</p>
|
||||||
Toplam Kapasite
|
|
||||||
</p>
|
|
||||||
<p className="text-xl font-bold text-green-600">
|
<p className="text-xl font-bold text-green-600">
|
||||||
{warehouses
|
{warehouses?.reduce((acc, w) => acc + w.capacity, 0).toLocaleString() || 0}
|
||||||
?.reduce((acc, w) => acc + w.capacity, 0)
|
|
||||||
.toLocaleString() || 0}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<FaBox className="h-6 w-6 text-green-500" />
|
<FaBox className="h-6 w-6 text-green-500" />
|
||||||
|
|
@ -200,19 +186,13 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600">
|
<p className="text-sm font-medium text-gray-600">Kullanım Oranı</p>
|
||||||
Kullanım Oranı
|
|
||||||
</p>
|
|
||||||
<p className="text-xl font-bold text-yellow-600">
|
<p className="text-xl font-bold text-yellow-600">
|
||||||
{warehouses?.length
|
{warehouses?.length
|
||||||
? Math.round(
|
? Math.round(
|
||||||
(warehouses.reduce(
|
(warehouses.reduce((acc, w) => acc + w.currentUtilization, 0) /
|
||||||
(acc, w) => acc + w.currentUtilization,
|
(warehouses.reduce((acc, w) => acc + w.capacity, 0) || 1)) *
|
||||||
0
|
100,
|
||||||
) /
|
|
||||||
(warehouses.reduce((acc, w) => acc + w.capacity, 0) ||
|
|
||||||
1)) *
|
|
||||||
100
|
|
||||||
)
|
)
|
||||||
: 0}
|
: 0}
|
||||||
%
|
%
|
||||||
|
|
@ -272,14 +252,11 @@ const WarehouseList: React.FC = () => {
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{warehouses?.map((warehouse) => {
|
{warehouses?.map((warehouse) => {
|
||||||
const utilizationPercentage = Math.round(
|
const utilizationPercentage = Math.round(
|
||||||
(warehouse.currentUtilization / warehouse.capacity) * 100
|
(warehouse.currentUtilization / warehouse.capacity) * 100,
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr key={warehouse.id} className="hover:bg-gray-50 transition-colors">
|
||||||
key={warehouse.id}
|
|
||||||
className="hover:bg-gray-50 transition-colors"
|
|
||||||
>
|
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="flex-shrink-0 h-8 w-8">
|
<div className="flex-shrink-0 h-8 w-8">
|
||||||
|
|
@ -307,16 +284,15 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium",
|
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
|
||||||
getWarehouseTypeColor(warehouse.warehouseType)
|
getWarehouseTypeColor(warehouse.warehouseType),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{getWarehouseTypeText(warehouse.warehouseType)}
|
{getWarehouseTypeText(warehouse.warehouseType)}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center text-xs text-gray-500">
|
<div className="flex items-center text-xs text-gray-500">
|
||||||
<FaMapMarkerAlt size={14} className="mr-1" />
|
<FaMapMarkerAlt size={14} className="mr-1" />
|
||||||
{warehouse.address?.city},{" "}
|
{warehouse.address?.city}, {warehouse.address?.country}
|
||||||
{warehouse.address?.country}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -325,17 +301,13 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<div className="text-sm font-medium text-gray-900">
|
||||||
{warehouse.capacity.toLocaleString()} m³
|
{warehouse.capacity.toLocaleString()} m³
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">Toplam Kapasite</div>
|
||||||
Toplam Kapasite
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-xs text-gray-600">
|
<span className="text-xs text-gray-600">Kullanılan:</span>
|
||||||
Kullanılan:
|
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium text-gray-900">
|
<span className="text-sm font-medium text-gray-900">
|
||||||
{warehouse.currentUtilization.toLocaleString()} m³
|
{warehouse.currentUtilization.toLocaleString()} m³
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -343,12 +315,12 @@ const WarehouseList: React.FC = () => {
|
||||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"h-2 rounded-full",
|
'h-2 rounded-full',
|
||||||
utilizationPercentage >= 90
|
utilizationPercentage >= 90
|
||||||
? "bg-red-500"
|
? 'bg-red-500'
|
||||||
: utilizationPercentage >= 75
|
: utilizationPercentage >= 75
|
||||||
? "bg-yellow-500"
|
? 'bg-yellow-500'
|
||||||
: "bg-green-500"
|
: 'bg-green-500',
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
width: `${Math.min(utilizationPercentage, 100)}%`,
|
width: `${Math.min(utilizationPercentage, 100)}%`,
|
||||||
|
|
@ -357,8 +329,8 @@ const WarehouseList: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"text-sm font-medium",
|
'text-sm font-medium',
|
||||||
getUtilizationColor(utilizationPercentage)
|
getUtilizationColor(utilizationPercentage),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
%{utilizationPercentage}
|
%{utilizationPercentage}
|
||||||
|
|
@ -369,13 +341,8 @@ const WarehouseList: React.FC = () => {
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<FaChartLine
|
<FaChartLine size={14} className="text-green-500 mr-1" />
|
||||||
size={14}
|
<span className="text-sm text-green-600">Verimli</span>
|
||||||
className="text-green-500 mr-1"
|
|
||||||
/>
|
|
||||||
<span className="text-sm text-green-600">
|
|
||||||
Verimli
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">Son 30 gün</div>
|
<div className="text-xs text-gray-500">Son 30 gün</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -384,13 +351,13 @@ const WarehouseList: React.FC = () => {
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium",
|
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
|
||||||
warehouse.isActive
|
warehouse.isActive
|
||||||
? "bg-green-100 text-green-800"
|
? 'bg-green-100 text-green-800'
|
||||||
: "bg-red-100 text-red-800"
|
: 'bg-red-100 text-red-800',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{warehouse.isActive ? "Aktif" : "Pasif"}
|
{warehouse.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
|
@ -414,7 +381,7 @@ const WarehouseList: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -423,12 +390,8 @@ const WarehouseList: React.FC = () => {
|
||||||
{(!warehouses || warehouses.length === 0) && (
|
{(!warehouses || warehouses.length === 0) && (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<FaBuilding className="mx-auto h-12 w-12 text-gray-400" />
|
<FaBuilding className="mx-auto h-12 w-12 text-gray-400" />
|
||||||
<h3 className="mt-2 text-sm font-medium text-gray-900">
|
<h3 className="mt-2 text-sm font-medium text-gray-900">Depo bulunamadı</h3>
|
||||||
Depo bulunamadı
|
<p className="mt-1 text-sm text-gray-500">Yeni depo ekleyerek başlayın.</p>
|
||||||
</h3>
|
|
||||||
<p className="mt-1 text-sm text-gray-500">
|
|
||||||
Yeni depo ekleyerek başlayın.
|
|
||||||
</p>
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<Link
|
<Link
|
||||||
to="/admin/warehouse/new"
|
to="/admin/warehouse/new"
|
||||||
|
|
@ -442,7 +405,8 @@ const WarehouseList: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
</Container>
|
||||||
};
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default WarehouseList;
|
export default WarehouseList
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue