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>Açı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>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<h2 className="text-2xl font-bold text-gray-900">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
Yerleştirme Kuralları
|
|
|
|
|
|
</h2>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<p className="text-gray-600">
|
2025-09-15 09:31:47 +00:00
|
|
|
|
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">
|
|
|
|
|
|
Açı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;
|