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

1067 lines
44 KiB
TypeScript
Raw Normal View History

2025-09-15 09:31:47 +00:00
import React, { useState } from "react";
import { PutawayStrategyEnum } from "../../../types/wm";
import {
FaSearch,
FaPlus,
FaEdit,
FaTrash,
FaCog,
FaBullseye,
FaCheckCircle,
FaExclamationCircle,
FaFilter,
FaArrowDown,
FaTh,
FaList,
} from "react-icons/fa";
import { mockPutawayRules } from "../../../mocks/mockPutawayRules";
import { mockWarehouses } from "../../../mocks/mockWarehouses";
import { mockZones } from "../../../mocks/mockZones";
import { mockLocations } from "../../../mocks/mockLocations";
import { mockMaterialTypes } from "../../../mocks/mockMaterialTypes";
import { mockMaterialGroups } from "../../../mocks/mockMaterialGroups";
import {
getPutawayStrategyColor,
getPutawayStrategyText,
getConditionTypeText,
getConditionOperatorText,
} from "../../../utils/erp";
const PutawayRules: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [selectedStrategy, setSelectedStrategy] = useState<
PutawayStrategyEnum | ""
>("");
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
const [showRuleForm, setShowRuleForm] = useState(false);
const [editingRuleId, setEditingRuleId] = useState<string | null>(null);
const [selectedRule, setSelectedRule] = useState<string>("");
// Form States
const [formData, setFormData] = useState({
name: "",
ruleCode: "",
description: "",
warehouseId: "",
materialTypeId: "",
materialGroupId: "",
priority: 1,
targetZoneId: "",
targetLocationId: "",
strategy: PutawayStrategyEnum.FIFO,
isActive: true,
});
// Helper functions for names
const getWarehouseName = (warehouseId: string | undefined) => {
if (!warehouseId) return "Belirtilmemiş";
const warehouse = mockWarehouses.find((w) => w.id === warehouseId);
return warehouse ? warehouse.name : `Depo ${warehouseId}`;
};
const getZoneName = (zoneId: string | undefined) => {
if (!zoneId) return "Belirtilmemiş";
const zone = mockZones.find((z) => z.id === zoneId);
return zone ? zone.name : `Bölge ${zoneId}`;
};
const getLocationName = (locationId: string | undefined) => {
if (!locationId) return "Belirtilmemiş";
const location = mockLocations.find((l) => l.id === locationId);
return location ? location.name : `Lokasyon ${locationId}`;
};
const getMaterialTypeName = (materialTypeId: string | undefined) => {
if (!materialTypeId) return "Belirtilmemiş";
const materialType = mockMaterialTypes.find(
(mt) => mt.id === materialTypeId
);
return materialType ? materialType.name : `Tip ${materialTypeId}`;
};
const getMaterialGroupName = (materialGroupId: string | undefined) => {
if (!materialGroupId) return "Belirtilmemiş";
const materialGroup = mockMaterialGroups.find(
(mg) => mg.id === materialGroupId
);
return materialGroup ? materialGroup.name : `Grup ${materialGroupId}`;
};
// Form Functions
const resetForm = () => {
setFormData({
name: "",
ruleCode: "",
description: "",
warehouseId: "",
materialTypeId: "",
materialGroupId: "",
priority: 1,
targetZoneId: "",
targetLocationId: "",
strategy: PutawayStrategyEnum.FIFO,
isActive: true,
});
};
const handleEditRule = (ruleId: string) => {
const rule = mockPutawayRules.find((r) => r.id === ruleId);
if (rule) {
setFormData({
name: rule.name,
ruleCode: rule.code,
description: rule.description || "",
warehouseId: rule.warehouseId,
materialTypeId: rule.materialTypeId || "",
materialGroupId: rule.materialGroupId || "",
priority: rule.priority,
targetZoneId: rule.targetZoneId || "",
targetLocationId: rule.targetLocationId || "",
strategy: rule.strategy,
isActive: rule.isActive,
});
}
setEditingRuleId(ruleId);
setShowRuleForm(true);
};
const handleNewRule = () => {
resetForm();
setEditingRuleId(null);
setShowRuleForm(true);
};
const handleCloseForm = () => {
setShowRuleForm(false);
setEditingRuleId(null);
resetForm();
};
const handleSubmitForm = () => {
// Form validation and submission logic here
console.log("Form Data:", formData);
handleCloseForm();
};
const filteredRules = mockPutawayRules.filter((rule) => {
const matchesSearch =
rule.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
rule.code.toLowerCase().includes(searchTerm.toLowerCase());
const matchesStrategy =
selectedStrategy === "" || rule.strategy === selectedStrategy;
return matchesSearch && matchesStrategy;
});
const RuleDetailModal = () => {
const rule = mockPutawayRules.find((r) => r.id === selectedRule);
if (!selectedRule || !rule) return null;
return (
<div className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
onClick={() => setSelectedRule("")}
/>
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-medium text-gray-900">
{rule.name} - Kural Detayları
</h3>
<button
onClick={() => setSelectedRule("")}
className="text-gray-400 hover:text-gray-600"
>
<svg
className="w-5 h-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* Rule Info */}
<div className="bg-gray-50 rounded-lg p-3">
<h4 className="font-medium text-sm text-gray-900 mb-2">
Kural Bilgileri
</h4>
<div className="space-y-2 text-sm">
<div>
<strong>Kod:</strong> {rule.code}
</div>
<div>
<strong>ıklama:</strong> {rule.description}
</div>
<div>
<strong>Öncelik:</strong> {rule.priority}
</div>
<div>
<strong>Strateji:</strong>
<span
className={`ml-2 inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
rule.strategy
)}`}
>
{getPutawayStrategyText(rule.strategy)}
</span>
</div>
<div>
<strong>Durum:</strong>
{rule.isActive ? (
<span className="ml-2 inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800">
<FaCheckCircle className="w-3 h-3 mr-1" />
Aktif
</span>
) : (
<span className="ml-2 inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-red-100 text-red-800">
<FaExclamationCircle className="w-3 h-3 mr-1" />
Pasif
</span>
)}
</div>
</div>
</div>
{/* Conditions */}
<div className="bg-gray-50 rounded-lg p-3">
<h4 className="font-medium text-sm text-gray-900 mb-2">
Koşullar ({rule.conditions.length})
</h4>
<div className="space-y-2">
{rule.conditions.map((condition, index) => (
<div
key={condition.id}
className="bg-white rounded p-2 border"
>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">
Koşul {index + 1}
</span>
</div>
<div className="mt-2 text-sm text-gray-600">
<div className="flex items-center gap-2">
<span className="bg-blue-100 text-blue-800 px-1.5 py-0.5 rounded text-xs">
{getConditionTypeText(condition.conditionType)}
</span>
<FaArrowDown className="w-3 h-3 text-gray-400" />
<span className="bg-gray-100 text-gray-800 px-1.5 py-0.5 rounded text-xs">
{getConditionOperatorText(condition.operator)}
</span>
<FaArrowDown className="w-3 h-3 text-gray-400" />
<span className="bg-green-100 text-green-800 px-1.5 py-0.5 rounded text-xs">
{condition.value}
</span>
</div>
</div>
</div>
))}
{rule.conditions.length === 0 && (
<div className="text-sm text-gray-500 text-center py-4">
Koşul tanımlanmamış
</div>
)}
</div>
</div>
</div>
{/* Target Info */}
<div className="mt-4 bg-gray-50 rounded-lg p-3">
<h4 className="font-medium text-sm text-gray-900 mb-2">
Hedef Lokasyon
</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-sm">
<div>
<strong>Depo:</strong> {getWarehouseName(rule.warehouseId)}
</div>
<div>
<strong>Hedef Bölge:</strong>{" "}
{getZoneName(rule.targetZoneId)}
</div>
<div>
<strong>Hedef Lokasyon:</strong>{" "}
{getLocationName(rule.targetLocationId)}
</div>
{rule.materialTypeId && (
<div>
<strong>Malzeme Tipi:</strong>{" "}
{getMaterialTypeName(rule.materialTypeId)}
</div>
)}
{rule.materialGroupId && (
<div>
<strong>Malzeme Grubu:</strong>{" "}
{getMaterialGroupName(rule.materialGroupId)}
</div>
)}
</div>
</div>
</div>
</div>
</div>
</div>
);
};
return (
<div className="space-y-4 pt-2">
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-bold text-gray-900">
Yerleştirme Kuralları
</h2>
<p className="text-sm text-gray-600 mt-1">
Malzeme yerleştirme kurallarını tanımlayın ve yönetin
</p>
</div>
<div className="flex items-center gap-2">
{/* View Mode Toggle */}
<div className="flex items-center bg-gray-100 rounded-lg p-1">
<button
onClick={() => setViewMode("grid")}
className={`p-1.5 rounded-md ${
viewMode === "grid"
? "bg-white text-blue-600 shadow-sm"
: "text-gray-500 hover:text-gray-700"
}`}
title="Kart Görünümü"
>
<FaTh className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode("list")}
className={`p-1.5 rounded-md ${
viewMode === "list"
? "bg-white text-blue-600 shadow-sm"
: "text-gray-500 hover:text-gray-700"
}`}
title="Liste Görünümü"
>
<FaList className="w-4 h-4" />
</button>
</div>
<button
onClick={handleNewRule}
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center gap-2"
>
<FaPlus className="w-4 h-4" />
Yeni Kural
</button>
</div>
</div>
{/* Filters */}
<div className="flex flex-col sm:flex-row gap-4">
<div className="relative flex-1">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<input
type="text"
placeholder="Kural ara..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 pr-4 py-1.5 text-sm w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<select
value={selectedStrategy}
onChange={(e) =>
setSelectedStrategy(e.target.value as PutawayStrategyEnum | "")
}
className="px-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Tüm Stratejiler</option>
{Object.values(PutawayStrategyEnum).map((strategy) => (
<option key={strategy} value={strategy}>
{getPutawayStrategyText(strategy)}
</option>
))}
</select>
<button className="bg-gray-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-gray-700 flex items-center gap-2">
<FaFilter className="w-4 h-4" />
Filtrele
</button>
</div>
{/* Rules Display */}
{viewMode === "grid" ? (
// Grid View (Kart Görünümü)
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredRules.map((rule) => (
<div
key={rule.id}
className="bg-white rounded-lg shadow-sm border border-gray-200 p-3"
>
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-2">
<div className="p-2 bg-blue-100 rounded-lg">
<FaCog className="w-5 h-5 text-blue-600" />
</div>
<div>
<h3 className="font-medium text-gray-900">{rule.name}</h3>
<p className="text-sm text-gray-500">{rule.code}</p>
</div>
</div>
<div className="flex items-center gap-2">
<span className="bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full">
Öncelik: {rule.priority}
</span>
{rule.isActive ? (
<FaCheckCircle className="w-4 h-4 text-green-500" />
) : (
<FaExclamationCircle className="w-4 h-4 text-red-500" />
)}
</div>
</div>
<div className="space-y-3">
<div className="text-xs text-gray-600">
<p>{rule.description}</p>
</div>
<span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
rule.strategy
)}`}
>
{getPutawayStrategyText(rule.strategy)}
</span>
{/* Conditions Summary */}
<h4 className="text-xs font-medium text-gray-900">
Koşullar ({rule.conditions.length})
</h4>
{rule.conditions.length > 0 ? (
<div className="space-y-1">
{rule.conditions.slice(0, 2).map((condition) => (
<div
key={condition.id}
className="text-xs text-gray-600 bg-gray-50 rounded p-1.5"
>
<div className="flex items-center gap-1">
<span className="font-medium">
{getConditionTypeText(condition.conditionType)}
</span>
<span>
{getConditionOperatorText(condition.operator)}
</span>
<span className="font-medium">{condition.value}</span>
</div>
</div>
))}
{rule.conditions.length > 2 && (
<div className="text-xs text-gray-500">
+{rule.conditions.length - 2} koşul daha
</div>
)}
</div>
) : (
<div className="text-xs text-gray-500">
Koşul tanımlanmamış
</div>
)}
{/* Target Info */}
<h4 className="text-xs font-medium">Hedef</h4>
<div>
<div className="bg-gray-50 rounded p-2">
<div className="text-xs text-gray-600 space-y-0.5">
<div>Depo: {getWarehouseName(rule.warehouseId)}</div>
{rule.targetZoneId && (
<div>Bölge: {getZoneName(rule.targetZoneId)}</div>
)}
{rule.targetLocationId && (
<div>
Lokasyon: {getLocationName(rule.targetLocationId)}
</div>
)}
{rule.materialTypeId && (
<div>
Malzeme Tipi:{" "}
{getMaterialTypeName(rule.materialTypeId)}
</div>
)}
{rule.materialGroupId && (
<div>
Malzeme Grubu:{" "}
{getMaterialGroupName(rule.materialGroupId)}
</div>
)}
</div>
</div>
</div>
</div>
<div className="flex items-center justify-between pt-3 mt-3 border-t border-gray-100">
<div className="flex items-center gap-2">
{rule.isActive ? (
<>
<FaCheckCircle className="w-4 h-4 text-green-500" />
<span className="text-sm text-green-600">Aktif</span>
</>
) : (
<>
<FaExclamationCircle className="w-4 h-4 text-red-500" />
<span className="text-sm text-red-600">Pasif</span>
</>
)}
</div>
<div className="flex items-center gap-2">
<button
onClick={() => setSelectedRule(rule.id)}
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
>
<FaBullseye className="w-4 h-4" />
</button>
<button
onClick={() => handleEditRule(rule.id)}
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
>
<FaEdit className="w-4 h-4" />
</button>
<button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md">
<FaTrash className="w-4 h-4" />
</button>
</div>
</div>
</div>
))}
</div>
) : (
// List View (Liste Görünümü)
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Kural
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Strateji
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Depo
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Hedef Lokasyon
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Koşullar
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Öncelik
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Durum
</th>
<th className="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
İşlemler
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredRules.map((rule) => (
<tr key={rule.id} className="hover:bg-gray-50">
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
<div className="p-2 bg-blue-100 rounded-lg mr-3">
<FaCog className="w-4 h-4 text-blue-600" />
</div>
<div>
<div
className="text-sm font-medium text-gray-900 max-w-[150px] truncate"
title={rule.name}
>
{rule.name}
</div>
<div className="text-sm text-gray-500">
{rule.code}
</div>
</div>
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span
className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${getPutawayStrategyColor(
rule.strategy
)}`}
>
{getPutawayStrategyText(rule.strategy)}
</span>
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
{getWarehouseName(rule.warehouseId)}
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
<div className="space-y-1">
{rule.targetZoneId && (
<div>Bölge: {getZoneName(rule.targetZoneId)}</div>
)}
{rule.targetLocationId && (
<div>
Lokasyon: {getLocationName(rule.targetLocationId)}
</div>
)}
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-500">
{rule.conditions.length > 0 ? (
<div className="space-y-1">
{rule.conditions.slice(0, 1).map((condition) => (
<div key={condition.id} className="text-xs">
{getConditionTypeText(condition.conditionType)}{" "}
{getConditionOperatorText(condition.operator)}{" "}
{condition.value}
</div>
))}
{rule.conditions.length > 1 && (
<div className="text-xs text-gray-400">
+{rule.conditions.length - 1} koşul daha
</div>
)}
</div>
) : (
<span className="text-gray-400">Koşul yok</span>
)}
</td>
<td className="px-3 py-2 whitespace-nowrap">
<span className="bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full">
{rule.priority}
</span>
</td>
<td className="px-3 py-2 whitespace-nowrap">
<div className="flex items-center">
{rule.isActive ? (
<>
<FaCheckCircle className="w-4 h-4 text-green-500 mr-2" />
<span className="text-sm text-green-600">
Aktif
</span>
</>
) : (
<>
<FaExclamationCircle className="w-4 h-4 text-red-500 mr-2" />
<span className="text-sm text-red-600">Pasif</span>
</>
)}
</div>
</td>
<td className="px-3 py-2 whitespace-nowrap text-right text-sm text-gray-500">
<div className="flex items-center gap-2">
<button
onClick={() => setSelectedRule(rule.id)}
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
title="Görüntüle"
>
<FaBullseye className="w-4 h-4" />
</button>
<button
onClick={() => handleEditRule(rule.id)}
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md"
title="Düzenle"
>
<FaEdit className="w-4 h-4" />
</button>
<button
className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md"
title="Sil"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{filteredRules.length === 0 && (
<div className="text-center py-12">
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">
Kural bulunamadı
</h3>
<p className="text-gray-500 mb-4">
Arama kriterlerinize uygun yerleştirme kuralı bulunamadı.
</p>
<button
onClick={handleNewRule}
className="bg-blue-600 text-white px-4 py-1.5 text-sm rounded-lg hover:bg-blue-700"
>
İlk Kuralı Oluştur
</button>
</div>
)}
{/* Rule Detail Modal */}
<RuleDetailModal />
{/* Rule Form Modal */}
{showRuleForm && (
<div className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex min-h-screen pt-4 px-4 pb-20 text-left sm:block sm:p-0">
<div
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
onClick={() => {
setShowRuleForm(false);
setEditingRuleId(null);
}}
/>
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg w-full max-w-4xl mx-4 max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between p-4 border-b">
<h3 className="text-base font-medium text-gray-900">
{editingRuleId
? "Yerleştirme Kuralını Güncelle"
: "Yeni Yerleştirme Kuralı"}
</h3>
<button
onClick={handleCloseForm}
className="text-gray-400 hover:text-gray-600 p-1 rounded-full"
>
<svg
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
{/* Form Content */}
<form className="space-y-4 p-4">
{/* Basic Information */}
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Kural Adı *
</label>
<input
type="text"
value={formData.name}
onChange={(e) =>
setFormData({ ...formData, name: e.target.value })
}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Kural adını girin"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Kural Kodu *
</label>
<input
type="text"
value={formData.ruleCode}
onChange={(e) =>
setFormData({ ...formData, ruleCode: e.target.value })
}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="PR001"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
ıklama
</label>
<textarea
value={formData.description}
onChange={(e) =>
setFormData({
...formData,
description: e.target.value,
})
}
rows={2}
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 açıklaması"
/>
</div>
{/* Warehouse and Location Settings */}
<div className="border-t pt-4">
<h4 className="text-base font-medium text-gray-900 mb-2">
Lokasyon Ayarları
</h4>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Depo *
</label>
<select
value={formData.warehouseId}
onChange={(e) => {
setFormData({
...formData,
warehouseId: e.target.value,
targetZoneId: "",
targetLocationId: "",
});
}}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
>
<option value="">Depo seçin</option>
{mockWarehouses.map((warehouse) => (
<option key={warehouse.id} value={warehouse.id}>
{warehouse.name}
</option>
))}
</select>
</div>
{formData.warehouseId && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Hedef Bölge
</label>
<select
value={formData.targetZoneId}
onChange={(e) => {
setFormData({
...formData,
targetZoneId: e.target.value,
targetLocationId: "",
});
}}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">Bölge seçin</option>
{mockZones
.filter(
(zone) =>
zone.warehouseId === formData.warehouseId
)
.map((zone) => (
<option key={zone.id} value={zone.id}>
{zone.name}
</option>
))}
</select>
</div>
)}
{formData.targetZoneId && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Hedef Lokasyon
</label>
<select
value={formData.targetLocationId}
onChange={(e) =>
setFormData({
...formData,
targetLocationId: 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"
>
<option value="">Lokasyon seçin</option>
{mockLocations
.filter(
(location) =>
location.zoneId === formData.targetZoneId
)
.map((location) => (
<option key={location.id} value={location.id}>
{location.name}
</option>
))}
</select>
</div>
)}
</div>
</div>
{/* Material Settings */}
<div className="border-t pt-4">
<h4 className="text-base font-medium text-gray-900 mb-2">
Malzeme Ayarları
</h4>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Malzeme Tipi
</label>
<select
value={formData.materialTypeId}
onChange={(e) =>
setFormData({
...formData,
materialTypeId: 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"
>
<option value="">Malzeme tipi seçin</option>
{mockMaterialTypes.map((type) => (
<option key={type.id} value={type.id}>
{type.name}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Malzeme Grubu
</label>
<select
value={formData.materialGroupId}
onChange={(e) =>
setFormData({
...formData,
materialGroupId: 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"
>
<option value="">Malzeme grubu seçin</option>
{mockMaterialGroups.map((group) => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</select>
</div>
</div>
</div>
{/* Strategy and Priority */}
<div className="border-t pt-4">
<h4 className="text-base font-medium text-gray-900 mb-2">
Strateji ve Öncelik
</h4>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Yerleştirme Stratejisi *
</label>
<select
value={formData.strategy}
onChange={(e) =>
setFormData({
...formData,
strategy: e.target.value as PutawayStrategyEnum,
})
}
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
>
<option value={PutawayStrategyEnum.FIFO}>
İlk Giren İlk Çıkar (FIFO)
</option>
<option value={PutawayStrategyEnum.LIFO}>
Son Giren İlk Çıkar (LIFO)
</option>
<option value={PutawayStrategyEnum.NearestLocation}>
En Yakın Lokasyon
</option>
<option value={PutawayStrategyEnum.EmptyLocation}>
Boş Lokasyon
</option>
<option value={PutawayStrategyEnum.SameProduct}>
Aynı Ürün Gruplaması
</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Öncelik *
</label>
<input
type="number"
min="1"
max="100"
value={formData.priority}
onChange={(e) =>
setFormData({
...formData,
priority: parseInt(e.target.value) || 1,
})
}
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
/>
</div>
</div>
</div>
{/* Status */}
<div className="border-t pt-4">
<div className="flex items-center">
<input
type="checkbox"
id="isActive"
checked={formData.isActive}
onChange={(e) =>
setFormData({
...formData,
isActive: e.target.checked,
})
}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label
htmlFor="isActive"
className="ml-2 block text-sm text-gray-900"
>
Bu kural aktif
</label>
</div>
</div>
</form>
{/* Form Actions */}
<div className="flex items-center justify-end gap-2 mt-4 pt-4 border-t p-4">
<button
type="button"
onClick={handleCloseForm}
className="px-4 py-1.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
İptal
</button>
<button
type="button"
onClick={handleSubmitForm}
className="px-4 py-1.5 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{editingRuleId ? "Güncelle" : "Kaydet"}
</button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
);
};
export default PutawayRules;