Container güncellemesi
This commit is contained in:
parent
7d52573765
commit
4e5322ba0c
49 changed files with 4049 additions and 5079 deletions
|
|
@ -82,7 +82,7 @@ define(['./workbox-a959eb95'], (function (workbox) { 'use strict';
|
|||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||
}, {
|
||||
"url": "/index.html",
|
||||
"revision": "0.sbo84r6raa"
|
||||
"revision": "0.daul0044rt"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { CrmSalesOrder } from "./admin/crm";
|
||||
import { PmWorkCenter, WorkOrderStatusEnum } from "./pm";
|
||||
import {
|
||||
MmMaterial,
|
||||
|
|
@ -7,6 +6,7 @@ import {
|
|||
QualityStatusEnum,
|
||||
} from "./mm";
|
||||
import { PriorityEnum } from "./common";
|
||||
import { CrmSalesOrder } from "./crm";
|
||||
|
||||
export interface MrpProductionOrder {
|
||||
// Üretim Emri
|
||||
|
|
@ -196,7 +196,7 @@ export interface MrpDemandForecast {
|
|||
notes: string;
|
||||
}
|
||||
|
||||
export interface MrpPurchaseSuggestionn extends MrpRecommendation {
|
||||
export interface MrpPurchaseSuggestion extends MrpRecommendation {
|
||||
// Satınalma Tavsiyesi
|
||||
supplierId: string;
|
||||
supplier?: MmMaterialSupplier;
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const AccountingManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (
|
||||
pathname === "/admin/accounting" ||
|
||||
pathname === "/admin/accounting/current-accounts"
|
||||
)
|
||||
return "Cari Hesap Yönetimi";
|
||||
if (pathname === "/admin/accounting/waybills") return "İrsaliye Yönetimi";
|
||||
if (pathname === "/admin/accounting/invoices") return "Fatura Yönetimi";
|
||||
if (pathname === "/admin/accounting/cash") return "Kasa Yönetimi";
|
||||
if (pathname === "/admin/accounting/bank") return "Banka Yönetimi";
|
||||
if (pathname === "/admin/accounting/check-note") return "Çek & Senet Takibi";
|
||||
return "Muhasebe Yönetimi";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / CRM Yönetimi / Sayfanın Adı
|
||||
const breadcrumbs = [{ name: "CRM Yönetimi", href: "/admin/crm" }];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader
|
||||
title={pageTitle}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountingManagement;
|
||||
|
|
@ -458,7 +458,7 @@ const BankManagement: React.FC<BankManagementProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -336,7 +336,7 @@ const CashManagement: React.FC<CashManagementProps> = ({
|
|||
.reduce((sum, m) => sum + m.amount, 0);
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -662,7 +662,7 @@ const CheckNoteManagement: React.FC<CheckNoteManagementProps> = ({
|
|||
})
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -20,8 +20,12 @@ import CurrentAccountDetails from "./CurrentAccountDetails";
|
|||
import CurrentAccountMovementForm from "./CurrentAccountMovementForm";
|
||||
import Widget from "../../../components/common/Widget";
|
||||
import {
|
||||
getAccountTypeColor,
|
||||
getAccountTypeText,
|
||||
getFiDocumentTypeColor,
|
||||
getFiDocumentTypeText,
|
||||
getRiskGroupColor,
|
||||
getRiskGroupText,
|
||||
} from "../../../utils/erp";
|
||||
|
||||
interface CurrentAccountManagementProps {
|
||||
|
|
@ -351,46 +355,6 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
|
|||
}
|
||||
});
|
||||
|
||||
const getAccountTypeLabel = (type: AccountTypeEnum) => {
|
||||
const typeLabels = {
|
||||
[AccountTypeEnum.Customer]: "Müşteri",
|
||||
[AccountTypeEnum.Supplier]: "Tedarikçi",
|
||||
[AccountTypeEnum.Both]: "Müşteri/Tedarikçi",
|
||||
[AccountTypeEnum.Other]: "Diğer",
|
||||
};
|
||||
return typeLabels[type];
|
||||
};
|
||||
|
||||
const getAccountTypeColor = (type: AccountTypeEnum) => {
|
||||
const typeColors = {
|
||||
[AccountTypeEnum.Customer]: "bg-blue-100 text-blue-800",
|
||||
[AccountTypeEnum.Supplier]: "bg-green-100 text-green-800",
|
||||
[AccountTypeEnum.Both]: "bg-purple-100 text-purple-800",
|
||||
[AccountTypeEnum.Other]: "bg-gray-100 text-gray-800",
|
||||
};
|
||||
return typeColors[type];
|
||||
};
|
||||
|
||||
const getRiskGroupLabel = (riskGroup: RiskGroupEnum) => {
|
||||
const riskLabels = {
|
||||
[RiskGroupEnum.Low]: "Düşük Risk",
|
||||
[RiskGroupEnum.Medium]: "Orta Risk",
|
||||
[RiskGroupEnum.High]: "Yüksek Risk",
|
||||
[RiskGroupEnum.Blocked]: "Blokeli",
|
||||
};
|
||||
return riskLabels[riskGroup];
|
||||
};
|
||||
|
||||
const getRiskGroupColor = (riskGroup: RiskGroupEnum) => {
|
||||
const riskColors = {
|
||||
[RiskGroupEnum.Low]: "bg-green-100 text-green-800",
|
||||
[RiskGroupEnum.Medium]: "bg-yellow-100 text-yellow-800",
|
||||
[RiskGroupEnum.High]: "bg-orange-100 text-orange-800",
|
||||
[RiskGroupEnum.Blocked]: "bg-red-100 text-red-800",
|
||||
};
|
||||
return riskColors[riskGroup];
|
||||
};
|
||||
|
||||
const formatBalance = (balance: number) => {
|
||||
const isDebit = balance > 0;
|
||||
const absBalance = Math.abs(balance);
|
||||
|
|
@ -438,7 +402,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
|
|||
account.type
|
||||
)}`}
|
||||
>
|
||||
{getAccountTypeLabel(account.type)}
|
||||
{getAccountTypeText(account.type)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
|
|
@ -486,7 +450,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
|
|||
account.riskGroup
|
||||
)}`}
|
||||
>
|
||||
{getRiskGroupLabel(account.riskGroup)}
|
||||
{getRiskGroupText(account.riskGroup)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
|
|
@ -566,7 +530,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
@ -682,7 +646,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
|
|||
<option value="all">Tüm Türler</option>
|
||||
{Object.values(AccountTypeEnum).map((type) => (
|
||||
<option key={type} value={type}>
|
||||
{getAccountTypeLabel(type)}
|
||||
{getAccountTypeText(type)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
@ -699,7 +663,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
|
|||
<option value="all">Tüm Risk Grupları</option>
|
||||
{Object.values(RiskGroupEnum).map((risk) => (
|
||||
<option key={risk} value={risk}>
|
||||
{getRiskGroupLabel(risk)}
|
||||
{getRiskGroupText(risk)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ const InvoiceManagement: React.FC<InvoiceManagementProps> = ({
|
|||
}));
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ const WaybillManagement: React.FC<WaybillManagementProps> = ({
|
|||
}));
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const CRMManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (pathname === "/admin/crm" || pathname === "/admin/crm/customers") return "Müşteriler";
|
||||
if (pathname === "/admin/crm/sales-teams") return "Satış Takımları";
|
||||
if (pathname === "/admin/crm/sales-teams/new") return "Yeni Satış Takımı";
|
||||
if (pathname.includes("/admin/crm/sales-teams/edit/")) return "Satış Takımı Düzenle";
|
||||
if (pathname.includes("/admin/crm/sales-teams/") && !pathname.includes("edit") && !pathname.includes("new")) return "Satış Takımı Detayları";
|
||||
if (pathname === "/admin/crm/loss-reasons") return "Kayıp Nedenleri";
|
||||
if (pathname === "/admin/crm/opportunities") return "Fırsatlar";
|
||||
if (pathname === "/admin/crm/activities") return "Aktiviteler";
|
||||
if (pathname === "/admin/crm/sales-orders") return "Satış Siparişleri";
|
||||
if (pathname === "/admin/crm/sales-orders/new") return "Yeni Satış Siparişi";
|
||||
if (pathname.includes("/admin/crm/sales-orders/edit/")) return "Satış Siparişi Düzenle";
|
||||
if (pathname.includes("/admin/crm/sales-orders/") && !pathname.includes("edit") && !pathname.includes("new")) return "Satış Siparişi Detayları";
|
||||
if (pathname === "/admin/crm/customers/new") return "Yeni Müşteri";
|
||||
if (pathname.includes("/admin/crm/customers/edit/")) return "Müşteri Düzenle";
|
||||
if (pathname.includes("/admin/crm/customers/") && !pathname.includes("edit") && !pathname.includes("new")) return "Müşteri Detayları";
|
||||
return "CRM Yönetimi";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / CRM Yönetimi / Sayfanın Adı
|
||||
const breadcrumbs = [
|
||||
{ name: "CRM Yönetimi", href: "/admin/crm" }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader
|
||||
title={pageTitle}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CRMManagement;
|
||||
|
|
@ -20,7 +20,6 @@ import {
|
|||
getActivityStatusColor,
|
||||
getActivityStatusText,
|
||||
getActivityTypeIcon,
|
||||
getPsActivityTypeText,
|
||||
getPriorityColor,
|
||||
getPriorityText,
|
||||
getActivityTypeText,
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ const ActivityRecords: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ const CustomerCards: React.FC = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header Actions */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ const CustomerEdit: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="space-y-2">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* Header */}
|
||||
<div className="bg-white border-b border-gray-200">
|
||||
|
|
@ -196,8 +196,8 @@ const CustomerEdit: React.FC = () => {
|
|||
</button>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center text-white shadow-lg">
|
||||
<FaBuilding className="w-6 h-6" />
|
||||
<div className="w-12 h-12 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center text-white shadow-lg">
|
||||
<FaBuilding className="w-8 h-8" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ const CustomerForm: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ const CustomerForm: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const CustomerList: React.FC = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header Actions */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const CustomerListWithToggle: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header with View Toggle */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ const CustomerView: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="space-y-2">
|
||||
<div className="bg-white border-b border-gray-200">
|
||||
<div className="mx-auto px-4 py-3">
|
||||
{/* Breadcrumb */}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ const LossReasons: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-3 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
@ -391,79 +391,79 @@ const LossReasons: React.FC = () => {
|
|||
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modals */}
|
||||
<LossReasonModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => {
|
||||
setIsModalOpen(false)
|
||||
setEditingReason(null)
|
||||
}}
|
||||
onSave={handleSave}
|
||||
editingReason={editingReason}
|
||||
mode={modalMode}
|
||||
/>
|
||||
{/* Modals */}
|
||||
<LossReasonModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => {
|
||||
setIsModalOpen(false)
|
||||
setEditingReason(null)
|
||||
}}
|
||||
onSave={handleSave}
|
||||
editingReason={editingReason}
|
||||
mode={modalMode}
|
||||
/>
|
||||
|
||||
{/* Delete Confirmation Modal */}
|
||||
{isDeleteModalOpen && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-3 border-b">
|
||||
<div className="flex items-center gap-3">
|
||||
<FaExclamationTriangle className="w-6 h-6 text-red-600" />
|
||||
<h3 className="text-sm font-semibold text-gray-900">Silme Onayı</h3>
|
||||
{/* Delete Confirmation Modal */}
|
||||
{isDeleteModalOpen && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-3 border-b">
|
||||
<div className="flex items-center gap-3">
|
||||
<FaExclamationTriangle className="w-6 h-6 text-red-600" />
|
||||
<h3 className="text-sm font-semibold text-gray-900">Silme Onayı</h3>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsDeleteModalOpen(false)
|
||||
setReasonToDelete(null)
|
||||
}}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-3">
|
||||
<div className="text-center">
|
||||
<div className="mx-auto flex items-center justify-center h-10 w-10 rounded-full bg-red-100 mb-3">
|
||||
<FaTrash className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Kayıp nedeni silinsin mi?
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 mb-6">
|
||||
<span className="font-medium">"{reasonToDelete?.name}"</span> kayıp nedenini
|
||||
silmek üzeresiniz. Bu işlem geri alınamaz.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Buttons */}
|
||||
<div className="flex justify-end gap-1.5">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsDeleteModalOpen(false)
|
||||
setReasonToDelete(null)
|
||||
}}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
className="px-3 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<FaTimes className="w-5 h-5" />
|
||||
İptal
|
||||
</button>
|
||||
<button
|
||||
onClick={confirmDelete}
|
||||
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors"
|
||||
>
|
||||
<FaTrash className="w-4 h-4" />
|
||||
Sil
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-3">
|
||||
<div className="text-center">
|
||||
<div className="mx-auto flex items-center justify-center h-10 w-10 rounded-full bg-red-100 mb-3">
|
||||
<FaTrash className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Kayıp nedeni silinsin mi?
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 mb-6">
|
||||
<span className="font-medium">"{reasonToDelete?.name}"</span> kayıp nedenini
|
||||
silmek üzeresiniz. Bu işlem geri alınamaz.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Buttons */}
|
||||
<div className="flex justify-end gap-1.5">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsDeleteModalOpen(false)
|
||||
setReasonToDelete(null)
|
||||
}}
|
||||
className="px-3 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
İptal
|
||||
</button>
|
||||
<button
|
||||
onClick={confirmDelete}
|
||||
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors"
|
||||
>
|
||||
<FaTrash className="w-4 h-4" />
|
||||
Sil
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ const OpportunityManagement: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-3 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
@ -384,23 +384,23 @@ const OpportunityManagement: React.FC = () => {
|
|||
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Modals */}
|
||||
<OpportunityForm
|
||||
isOpen={isFormOpen}
|
||||
onClose={handleCloseForm}
|
||||
onSave={handleSaveOpportunity}
|
||||
opportunity={selectedOpportunity}
|
||||
mode={formMode}
|
||||
/>
|
||||
|
||||
<OpportunityDetails
|
||||
isOpen={isDetailsOpen}
|
||||
onClose={handleCloseDetails}
|
||||
onEdit={handleEditFromDetails}
|
||||
opportunity={selectedOpportunity}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Modals */}
|
||||
<OpportunityForm
|
||||
isOpen={isFormOpen}
|
||||
onClose={handleCloseForm}
|
||||
onSave={handleSaveOpportunity}
|
||||
opportunity={selectedOpportunity}
|
||||
mode={formMode}
|
||||
/>
|
||||
|
||||
<OpportunityDetails
|
||||
isOpen={isDetailsOpen}
|
||||
onClose={handleCloseDetails}
|
||||
onEdit={handleEditFromDetails}
|
||||
opportunity={selectedOpportunity}
|
||||
/>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ const SalesOrderForm: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-6 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ const SalesOrderView: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ const SalesOrders: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-3 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ const SalesTeamCreate: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ const SalesTeamEdit: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const SalesTeamView: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-6 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ const SalesTeams: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Container>
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const HRManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (pathname === "/admin/hr" || pathname === "/admin/hr/") return "Personel Listesi";
|
||||
if (pathname === "/admin/hr/departments") return "Departman Yönetimi";
|
||||
if (pathname === "/admin/hr/cost-centers") return "Maliyet Merkezi Yönetimi";
|
||||
if (pathname === "/admin/hr/organization") return "Organizasyon Şeması";
|
||||
if (pathname === "/admin/hr/leave-management") return "İzin Yönetimi";
|
||||
if (pathname === "/admin/hr/overtimes-management") return "Mesai Yönetimi";
|
||||
if (pathname === "/admin/hr/payroll") return "Bordro Yönetimi";
|
||||
if (pathname === "/admin/hr/job-positions") return "İş Pozisyonları";
|
||||
if (pathname === "/admin/hr/employment-types") return "İstihdam Türleri";
|
||||
if (pathname === "/admin/hr/badges") return "Rozet Yönetimi";
|
||||
if (pathname === "/admin/hr/360-templates") return "360 Derece Şablonları";
|
||||
if (pathname === "/admin/hr/360-evaluation") return "360 Derece Değerlendirme";
|
||||
if (pathname === "/admin/hr/employees/new") return "Yeni Çalışan";
|
||||
if (pathname.includes("/admin/hr/employees/edit/")) return "Çalışan Düzenle";
|
||||
if (pathname.includes("/admin/hr/employees/") && !pathname.includes("edit") && !pathname.includes("new")) return "Çalışan Detayları";
|
||||
return "İnsan Kaynakları";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / İnsan Kaynakları / Sayfanın Adı
|
||||
const breadcrumbs = [
|
||||
{ name: "İnsan Kaynakları", href: "/admin/hr" }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader
|
||||
title={pageTitle}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HRManagement;
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const MaintenanceManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (pathname === "/admin/maintenance" || pathname === "/admin/maintenance/" || pathname === "/admin/maintenance/equipment") return "İş Merkezleri";
|
||||
if (pathname === "/admin/maintenance/workcenters") return "İş Merkezleri";
|
||||
if (pathname === "/admin/maintenance/plans") return "Bakım Planları";
|
||||
if (pathname === "/admin/maintenance/calendar") return "Bakım Takvimi";
|
||||
if (pathname === "/admin/maintenance/teams") return "Bakım Takımları";
|
||||
if (pathname === "/admin/maintenance/faults") return "Arıza Bildirimleri";
|
||||
if (pathname === "/admin/maintenance/workorders") return "Bakım İş Emirleri";
|
||||
return "Bakım Yönetimi";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / Bakım Yönetimi / Sayfanın Adı
|
||||
const breadcrumbs = [
|
||||
{ name: "Bakım Yönetimi", href: "/admin/maintenance" }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader
|
||||
title={pageTitle}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MaintenanceManagement;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -224,7 +224,7 @@ const WorkCenterForm: React.FC = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
getCriticalityLevelColor,
|
||||
getCriticalityLevelText,
|
||||
} from '../../../utils/erp'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const WorkCenterList: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
|
|
@ -75,330 +76,332 @@ const WorkCenterList: React.FC = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3 pt-2">
|
||||
{/* Header Actions */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="relative">
|
||||
<FaSearch
|
||||
size={20}
|
||||
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="İş merkezi kodu veya adı..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10 pr-4 py-1.5 text-sm w-80 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header Actions */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="relative">
|
||||
<FaSearch
|
||||
size={20}
|
||||
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="İş merkezi kodu veya adı..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10 pr-4 py-1.5 text-sm w-80 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className={classNames(
|
||||
'flex items-center px-4 py-2 border rounded-lg transition-colors',
|
||||
showFilters
|
||||
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
|
||||
)}
|
||||
>
|
||||
<FaFilter size={16} className="mr-2" />
|
||||
Filtreler
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className={classNames(
|
||||
'flex items-center px-4 py-2 border rounded-lg transition-colors',
|
||||
showFilters
|
||||
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
|
||||
)}
|
||||
>
|
||||
<FaFilter size={16} className="mr-2" />
|
||||
Filtreler
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
|
||||
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<FaDownload size={16} className="mr-2" />
|
||||
Dışa Aktar
|
||||
</button>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
|
||||
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<FaDownload size={16} className="mr-2" />
|
||||
Dışa Aktar
|
||||
</button>
|
||||
|
||||
<Link
|
||||
to="/admin/maintenance/equipment/new"
|
||||
className="flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<FaPlus size={16} className="mr-2" />
|
||||
Yeni İş Merkezi
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters Panel */}
|
||||
{showFilters && (
|
||||
<div className="bg-white border border-gray-200 rounded-lg p-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||
<select
|
||||
value={filterStatus}
|
||||
onChange={(e) => setFilterStatus(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Tümü</option>
|
||||
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Kritiklik</label>
|
||||
<select
|
||||
value={filterCriticality}
|
||||
onChange={(e) => setFilterCriticality(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Tümü</option>
|
||||
<option value={CriticalityLevelEnum.Low}>Düşük</option>
|
||||
<option value={CriticalityLevelEnum.Medium}>Orta</option>
|
||||
<option value={CriticalityLevelEnum.High}>Yüksek</option>
|
||||
<option value={CriticalityLevelEnum.Critical}>Kritik</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end">
|
||||
<button
|
||||
onClick={() => {
|
||||
setFilterStatus('all')
|
||||
setFilterCriticality('all')
|
||||
setSearchTerm('')
|
||||
}}
|
||||
className="w-full px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
Filtreleri Temizle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Statistics Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Toplam İş Merkezi</p>
|
||||
<p className="text-xl font-bold text-gray-900">{workCenter?.length || 0}</p>
|
||||
</div>
|
||||
<FaCog className="h-8 w-8 text-blue-600" />
|
||||
<Link
|
||||
to="/admin/maintenance/equipment/new"
|
||||
className="flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<FaPlus size={16} className="mr-2" />
|
||||
Yeni İş Merkezi
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Operasyonel</p>
|
||||
<p className="text-xl font-bold text-green-600">
|
||||
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational).length ||
|
||||
0}
|
||||
</p>
|
||||
</div>
|
||||
<FaCheckCircle className="h-8 w-8 text-green-600" />
|
||||
</div>
|
||||
</div>
|
||||
{/* Filters Panel */}
|
||||
{showFilters && (
|
||||
<div className="bg-white border border-gray-200 rounded-lg p-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||
<select
|
||||
value={filterStatus}
|
||||
onChange={(e) => setFilterStatus(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Tümü</option>
|
||||
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Bakımda</p>
|
||||
<p className="text-xl font-bold text-yellow-600">
|
||||
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.UnderMaintenance)
|
||||
.length || 0}
|
||||
</p>
|
||||
</div>
|
||||
<FaWrench className="h-8 w-8 text-yellow-600" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Kritiklik</label>
|
||||
<select
|
||||
value={filterCriticality}
|
||||
onChange={(e) => setFilterCriticality(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
>
|
||||
<option value="all">Tümü</option>
|
||||
<option value={CriticalityLevelEnum.Low}>Düşük</option>
|
||||
<option value={CriticalityLevelEnum.Medium}>Orta</option>
|
||||
<option value={CriticalityLevelEnum.High}>Yüksek</option>
|
||||
<option value={CriticalityLevelEnum.Critical}>Kritik</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Kritik İş Merkezi</p>
|
||||
<p className="text-xl font-bold text-red-600">
|
||||
{workCenter?.filter((e) => e.criticality === CriticalityLevelEnum.Critical)
|
||||
.length || 0}
|
||||
</p>
|
||||
</div>
|
||||
<FaExclamationTriangle className="h-8 w-8 text-red-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* WorkCenter Table */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="px-4 py-3 border-b border-gray-200">
|
||||
<h2 className="text-xl font-bold text-gray-900">WorkCenter Listesi</h2>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İş Merkezi Bilgileri
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Üretici / Model
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Lokasyon / Departman
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Durum / Kritiklik
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Kurulum / Garanti
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Performans
|
||||
</th>
|
||||
<th className="px-4 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">
|
||||
{workCenter?.map((eq) => (
|
||||
<tr key={eq.id} className="hover:bg-gray-50 transition-colors">
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0 h-10 w-10">
|
||||
<div className="h-10 w-10 rounded-lg bg-blue-100 flex items-center justify-center">
|
||||
<FaCog className="h-5 w-5 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<div className="text-sm font-medium text-gray-900">{eq.code}</div>
|
||||
<div className="text-sm text-gray-500 max-w-xs truncate">{eq.name}</div>
|
||||
{eq.serialNumber && (
|
||||
<div className="text-xs text-gray-400 mt-1">S/N: {eq.serialNumber}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{eq.manufacturer || '-'}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">{eq.model || '-'}</div>
|
||||
<div className="text-xs text-gray-400 mt-1">{eq.workCenterType?.name}</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">{eq.location}</div>
|
||||
<div className="text-sm text-gray-500">{eq.departmentId}</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div className="space-y-2">
|
||||
<span
|
||||
className={classNames(
|
||||
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
|
||||
getWorkCenterStatusColor(eq.status),
|
||||
)}
|
||||
>
|
||||
{getWorkCenterStatusIcon(eq.status)}
|
||||
<span className="ml-1">{getWorkCenterStatusText(eq.status)}</span>
|
||||
</span>
|
||||
<div
|
||||
className={classNames(
|
||||
'text-sm font-medium',
|
||||
getCriticalityLevelColor(eq.criticality),
|
||||
)}
|
||||
>
|
||||
{getCriticalityLevelText(eq.criticality)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center text-sm text-gray-900">
|
||||
<FaCalendar size={14} className="mr-1" />
|
||||
{dayjs(eq.installationDate).format('DD.MM.YYYY')}
|
||||
</div>
|
||||
{eq.warrantyExpiry && (
|
||||
<div className="text-sm text-gray-500">
|
||||
Garanti: {dayjs(eq.warrantyExpiry).format('DD.MM.YYYY')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center">
|
||||
<FaChartLine size={14} className="text-green-500 mr-1" />
|
||||
<span className="text-sm text-green-600">98.5% Uptime</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<FaArrowUp size={14} className="text-blue-500 mr-1" />
|
||||
<span className="text-sm text-blue-600">Verimli</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3 text-right">
|
||||
<div className="flex items-center justify-end space-x-1">
|
||||
<Link
|
||||
to={`/admin/maintenance/equipment/${eq.id}`}
|
||||
className="p-1.5 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
|
||||
title="Detayları Görüntüle"
|
||||
>
|
||||
<FaEye size={16} />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to={`/admin/maintenance/equipment/edit/${eq.id}`}
|
||||
className="p-1.5 text-gray-600 hover:text-yellow-600 hover:bg-yellow-50 rounded-lg transition-colors"
|
||||
title="Düzenle"
|
||||
>
|
||||
<FaEdit size={16} />
|
||||
</Link>
|
||||
|
||||
<button
|
||||
onClick={() => alert('Bakım emri oluşturma özelliği yakında eklenecek')}
|
||||
className="p-1.5 text-gray-600 hover:text-green-600 hover:bg-green-50 rounded-lg transition-colors"
|
||||
title="Bakım Emri Oluştur"
|
||||
>
|
||||
<FaWrench size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{(!workCenter || workCenter.length === 0) && (
|
||||
<div className="text-center py-10">
|
||||
<FaCog className="mx-auto h-10 w-10 text-gray-400" />
|
||||
<h3 className="mt-1.5 text-sm font-medium text-gray-900">İş Merkezi bulunamadı</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">Yeni iş merkezi ekleyerek başlayın.</p>
|
||||
<div className="mt-4">
|
||||
<Link
|
||||
to="/admin/maintenance/equipment/new"
|
||||
className="inline-flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<FaPlus size={16} className="mr-2" />
|
||||
Yeni İş Merkezi Ekle
|
||||
</Link>
|
||||
<div className="flex items-end">
|
||||
<button
|
||||
onClick={() => {
|
||||
setFilterStatus('all')
|
||||
setFilterCriticality('all')
|
||||
setSearchTerm('')
|
||||
}}
|
||||
className="w-full px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
Filtreleri Temizle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Statistics Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Toplam İş Merkezi</p>
|
||||
<p className="text-xl font-bold text-gray-900">{workCenter?.length || 0}</p>
|
||||
</div>
|
||||
<FaCog className="h-8 w-8 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Operasyonel</p>
|
||||
<p className="text-xl font-bold text-green-600">
|
||||
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational)
|
||||
.length || 0}
|
||||
</p>
|
||||
</div>
|
||||
<FaCheckCircle className="h-8 w-8 text-green-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Bakımda</p>
|
||||
<p className="text-xl font-bold text-yellow-600">
|
||||
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.UnderMaintenance)
|
||||
.length || 0}
|
||||
</p>
|
||||
</div>
|
||||
<FaWrench className="h-8 w-8 text-yellow-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Kritik İş Merkezi</p>
|
||||
<p className="text-xl font-bold text-red-600">
|
||||
{workCenter?.filter((e) => e.criticality === CriticalityLevelEnum.Critical)
|
||||
.length || 0}
|
||||
</p>
|
||||
</div>
|
||||
<FaExclamationTriangle className="h-8 w-8 text-red-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* WorkCenter Table */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="px-4 py-3 border-b border-gray-200">
|
||||
<h2 className="text-xl font-bold text-gray-900">WorkCenter Listesi</h2>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İş Merkezi Bilgileri
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Üretici / Model
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Lokasyon / Departman
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Durum / Kritiklik
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Kurulum / Garanti
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Performans
|
||||
</th>
|
||||
<th className="px-4 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">
|
||||
{workCenter?.map((eq) => (
|
||||
<tr key={eq.id} className="hover:bg-gray-50 transition-colors">
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0 h-10 w-10">
|
||||
<div className="h-10 w-10 rounded-lg bg-blue-100 flex items-center justify-center">
|
||||
<FaCog className="h-5 w-5 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<div className="text-sm font-medium text-gray-900">{eq.code}</div>
|
||||
<div className="text-sm text-gray-500 max-w-xs truncate">{eq.name}</div>
|
||||
{eq.serialNumber && (
|
||||
<div className="text-xs text-gray-400 mt-1">S/N: {eq.serialNumber}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{eq.manufacturer || '-'}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">{eq.model || '-'}</div>
|
||||
<div className="text-xs text-gray-400 mt-1">{eq.workCenterType?.name}</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">{eq.location}</div>
|
||||
<div className="text-sm text-gray-500">{eq.departmentId}</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div className="space-y-2">
|
||||
<span
|
||||
className={classNames(
|
||||
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
|
||||
getWorkCenterStatusColor(eq.status),
|
||||
)}
|
||||
>
|
||||
{getWorkCenterStatusIcon(eq.status)}
|
||||
<span className="ml-1">{getWorkCenterStatusText(eq.status)}</span>
|
||||
</span>
|
||||
<div
|
||||
className={classNames(
|
||||
'text-sm font-medium',
|
||||
getCriticalityLevelColor(eq.criticality),
|
||||
)}
|
||||
>
|
||||
{getCriticalityLevelText(eq.criticality)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center text-sm text-gray-900">
|
||||
<FaCalendar size={14} className="mr-1" />
|
||||
{dayjs(eq.installationDate).format('DD.MM.YYYY')}
|
||||
</div>
|
||||
{eq.warrantyExpiry && (
|
||||
<div className="text-sm text-gray-500">
|
||||
Garanti: {dayjs(eq.warrantyExpiry).format('DD.MM.YYYY')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center">
|
||||
<FaChartLine size={14} className="text-green-500 mr-1" />
|
||||
<span className="text-sm text-green-600">98.5% Uptime</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<FaArrowUp size={14} className="text-blue-500 mr-1" />
|
||||
<span className="text-sm text-blue-600">Verimli</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3 text-right">
|
||||
<div className="flex items-center justify-end space-x-1">
|
||||
<Link
|
||||
to={`/admin/maintenance/equipment/${eq.id}`}
|
||||
className="p-1.5 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
|
||||
title="Detayları Görüntüle"
|
||||
>
|
||||
<FaEye size={16} />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to={`/admin/maintenance/equipment/edit/${eq.id}`}
|
||||
className="p-1.5 text-gray-600 hover:text-yellow-600 hover:bg-yellow-50 rounded-lg transition-colors"
|
||||
title="Düzenle"
|
||||
>
|
||||
<FaEdit size={16} />
|
||||
</Link>
|
||||
|
||||
<button
|
||||
onClick={() => alert('Bakım emri oluşturma özelliği yakında eklenecek')}
|
||||
className="p-1.5 text-gray-600 hover:text-green-600 hover:bg-green-50 rounded-lg transition-colors"
|
||||
title="Bakım Emri Oluştur"
|
||||
>
|
||||
<FaWrench size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{(!workCenter || workCenter.length === 0) && (
|
||||
<div className="text-center py-10">
|
||||
<FaCog className="mx-auto h-10 w-10 text-gray-400" />
|
||||
<h3 className="mt-1.5 text-sm font-medium text-gray-900">İş Merkezi bulunamadı</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">Yeni iş merkezi ekleyerek başlayın.</p>
|
||||
<div className="mt-4">
|
||||
<Link
|
||||
to="/admin/maintenance/equipment/new"
|
||||
className="inline-flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<FaPlus size={16} className="mr-2" />
|
||||
Yeni İş Merkezi Ekle
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const MRPManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (pathname === "/admin/mrp/operation-types") return "Operasyon Türleri";
|
||||
if (pathname === "/admin/mrp/workcenters") return "İş Merkezleri";
|
||||
if (pathname === "/admin/mrp/operations") return "Operasyon Tanımları";
|
||||
if (pathname === "/admin/mrp/bom") return "Ürün Ağaçları (BOM)";
|
||||
if (pathname === "/admin/mrp/production-orders") return "Üretim Emirleri";
|
||||
if (pathname === "/admin/mrp/work-orders") return "İş Emirleri";
|
||||
if (pathname === "/admin/mrp/demand-planning") return "Talep Planlama";
|
||||
if (pathname === "/admin/mrp/material-requirements") return "Malzeme İhtiyaçları";
|
||||
if (pathname === "/admin/mrp/planning-gantt") return "Planlama Gantt";
|
||||
return "MRP Yönetimi";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / MRP Yönetimi / Sayfanın Adı
|
||||
const breadcrumbs = [{ name: "MRP Yönetimi", href: "/admin/mrp" }];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader
|
||||
title={pageTitle}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MRPManagement;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
FaPlus,
|
||||
FaSearch,
|
||||
|
|
@ -10,347 +10,325 @@ import {
|
|||
FaCopy,
|
||||
FaExclamationTriangle,
|
||||
FaClock,
|
||||
} from "react-icons/fa";
|
||||
import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from "../../../types/mrp";
|
||||
import BOMFormModal from "./BOMFormModal";
|
||||
import { getBOMTypeColor, getBOMTypeName } from "../../../utils/erp";
|
||||
import { mockBOMs } from "../../../mocks/mockBOMs";
|
||||
} from 'react-icons/fa'
|
||||
import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from '../../../types/mrp'
|
||||
import BOMFormModal from './BOMFormModal'
|
||||
import { getBOMTypeColor, getBOMTypeName } from '../../../utils/erp'
|
||||
import { mockBOMs } from '../../../mocks/mockBOMs'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const BOMManagement: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null);
|
||||
const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null);
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null)
|
||||
const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null)
|
||||
|
||||
// Mock data - replace with actual API calls
|
||||
const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs);
|
||||
const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs)
|
||||
|
||||
const filteredBOMs = boms.filter(
|
||||
(bom) =>
|
||||
bom.bomCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
bom.materialId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
bom.version.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
bom.version.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
)
|
||||
|
||||
const getTotalOperationTime = (operations: MrpBOMOperation[]) => {
|
||||
return operations.reduce(
|
||||
(total, op) => total + op.setupTime + op.runTime,
|
||||
0
|
||||
);
|
||||
};
|
||||
return operations.reduce((total, op) => total + op.setupTime + op.runTime, 0)
|
||||
}
|
||||
|
||||
const getTotalComponents = (components: MrpBOMComponent[]) => {
|
||||
return components.length;
|
||||
};
|
||||
return components.length
|
||||
}
|
||||
|
||||
const getActiveComponents = (components: MrpBOMComponent[]) => {
|
||||
return components.filter((comp) => comp.isActive).length;
|
||||
};
|
||||
return components.filter((comp) => comp.isActive).length
|
||||
}
|
||||
|
||||
const handleEdit = (bom: MrpBOM) => {
|
||||
setEditingBOM(bom);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingBOM(bom)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const handleAddNew = () => {
|
||||
setEditingBOM(null);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingBOM(null)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const handleViewDetails = (bom: MrpBOM) => {
|
||||
setSelectedBOM(bom);
|
||||
setSelectedBOM(bom)
|
||||
|
||||
console.log(bom);
|
||||
};
|
||||
console.log(bom)
|
||||
}
|
||||
|
||||
const handleCopy = (bom: MrpBOM) => {
|
||||
console.log("Copying BOM:", bom.bomCode);
|
||||
console.log('Copying BOM:', bom.bomCode)
|
||||
// Implementation for copying BOM
|
||||
};
|
||||
}
|
||||
|
||||
const handleSave = (bom: MrpBOM) => {
|
||||
setBoms((prev) => {
|
||||
const existing = prev.find((b) => b.id === bom.id);
|
||||
if (existing) return prev.map((b) => (b.id === bom.id ? bom : b));
|
||||
return [...prev, { ...bom, id: bom.id || String(Date.now()) }];
|
||||
});
|
||||
setShowModal(false);
|
||||
};
|
||||
const existing = prev.find((b) => b.id === bom.id)
|
||||
if (existing) return prev.map((b) => (b.id === bom.id ? bom : b))
|
||||
return [...prev, { ...bom, id: bom.id || String(Date.now()) }]
|
||||
})
|
||||
setShowModal(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">
|
||||
Ürün Ağaçları (BOM)
|
||||
</h2>
|
||||
<p className="text-sm text-gray-600 mt-0.5">
|
||||
Ürün bileşenlerini ve üretim operasyonlarını yönetin
|
||||
</p>
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">Ürün Ağaçları (BOM)</h2>
|
||||
<p className="text-sm text-gray-600 mt-0.5">
|
||||
Ürün bileşenlerini ve üretim operasyonlarını yönetin
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddNew}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni BOM</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddNew}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni BOM</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="BOM ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
{/* Search Bar */}
|
||||
<div className="relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="BOM ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* BOM List */}
|
||||
<div className="space-y-3 pt-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">BOM Listesi</h3>
|
||||
{filteredBOMs.map((bom) => (
|
||||
<div
|
||||
key={bom.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<FaCodeBranch className="w-4 h-4 text-gray-600" />
|
||||
<h4 className="text-lg font-semibold text-gray-900">
|
||||
{bom.bomCode}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* BOM List */}
|
||||
<div className="space-y-3 pt-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">BOM Listesi</h3>
|
||||
{filteredBOMs.map((bom) => (
|
||||
<div
|
||||
key={bom.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<FaCodeBranch className="w-4 h-4 text-gray-600" />
|
||||
<h4 className="text-lg font-semibold text-gray-900">{bom.bomCode}</h4>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getBOMTypeColor(
|
||||
bom.bomType,
|
||||
)}`}
|
||||
>
|
||||
{getBOMTypeName(bom.bomType)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-0.5">
|
||||
Malzeme: {bom.material?.code} - {bom.material?.name} - v{bom.version}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">Temel Miktar: {bom.baseQuantity}</p>
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleViewDetails(bom)}
|
||||
className="p-2 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
||||
title="Detayları Görüntüle"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleCopy(bom)}
|
||||
className="p-2 text-gray-400 hover:text-purple-600 hover:bg-purple-50 rounded-md transition-colors"
|
||||
title="Kopyala"
|
||||
>
|
||||
<FaCopy className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEdit(bom)}
|
||||
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
title="Düzenle"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors"
|
||||
title="Sil"
|
||||
>
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 text-sm mb-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaBox className="w-4 h-4 mr-1" />
|
||||
Bileşenler
|
||||
</span>
|
||||
<span className="font-medium">
|
||||
{getActiveComponents(bom.components)}/{getTotalComponents(bom.components)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1" />
|
||||
Toplam Süre
|
||||
</span>
|
||||
<span className="font-medium">{getTotalOperationTime(bom.operations)} dk</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-500">Geçerlilik</span>
|
||||
<span className="font-medium">{bom.validFrom.toLocaleDateString('tr-TR')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status and Warnings */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex space-x-2">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getBOMTypeColor(
|
||||
bom.bomType
|
||||
)}`}
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
bom.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{getBOMTypeName(bom.bomType)}
|
||||
{bom.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
{bom.validTo && new Date() > bom.validTo && (
|
||||
<span className="px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 flex items-center">
|
||||
<FaExclamationTriangle className="w-3 h-3 mr-1" />
|
||||
Süresi Dolmuş
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-0.5">
|
||||
Malzeme: {bom.material?.code} - {bom.material?.name} - v
|
||||
{bom.version}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
Temel Miktar: {bom.baseQuantity}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleViewDetails(bom)}
|
||||
className="p-2 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
||||
title="Detayları Görüntüle"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleCopy(bom)}
|
||||
className="p-2 text-gray-400 hover:text-purple-600 hover:bg-purple-50 rounded-md transition-colors"
|
||||
title="Kopyala"
|
||||
>
|
||||
<FaCopy className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEdit(bom)}
|
||||
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
title="Düzenle"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors"
|
||||
title="Sil"
|
||||
>
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
<span className="text-xs text-gray-400">
|
||||
{bom.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 text-sm mb-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
{filteredBOMs.length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<FaCodeBranch className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">BOM bulunamadı</h3>
|
||||
<p className="text-gray-500">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir BOM ekleyin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* BOM Details */}
|
||||
<div className="space-y-4 pt-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">BOM Detayları</h3>
|
||||
{selectedBOM ? (
|
||||
<div className="bg-white rounded-lg shadow-md border border-gray-200 p-3">
|
||||
<div className="mb-3">
|
||||
<h4 className="font-semibold text-gray-900 mb-2">{selectedBOM.bomCode}</h4>
|
||||
<div className="grid grid-cols-2 gap-1 text-sm text-gray-600">
|
||||
<div>
|
||||
Malzeme: {selectedBOM.material?.code} - {selectedBOM.material?.name}
|
||||
</div>
|
||||
<div>Versiyon: {selectedBOM.version}</div>
|
||||
<div>Tip: {getBOMTypeName(selectedBOM.bomType)}</div>
|
||||
<div>Temel Miktar: {selectedBOM.baseQuantity}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Components */}
|
||||
<div className="mb-3">
|
||||
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
||||
<FaBox className="w-4 h-4 mr-1" />
|
||||
Bileşenler
|
||||
</span>
|
||||
<span className="font-medium">
|
||||
{getActiveComponents(bom.components)}/
|
||||
{getTotalComponents(bom.components)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1" />
|
||||
Toplam Süre
|
||||
</span>
|
||||
<span className="font-medium">
|
||||
{getTotalOperationTime(bom.operations)} dk
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-500">Geçerlilik</span>
|
||||
<span className="font-medium">
|
||||
{bom.validFrom.toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status and Warnings */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex space-x-2">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
bom.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{bom.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
{bom.validTo && new Date() > bom.validTo && (
|
||||
<span className="px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 flex items-center">
|
||||
<FaExclamationTriangle className="w-3 h-3 mr-1" />
|
||||
Süresi Dolmuş
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-xs text-gray-400">
|
||||
{bom.lastModificationTime.toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{filteredBOMs.length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<FaCodeBranch className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
BOM bulunamadı
|
||||
</h3>
|
||||
<p className="text-gray-500">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir BOM ekleyin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* BOM Details */}
|
||||
<div className="space-y-4 pt-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">BOM Detayları</h3>
|
||||
{selectedBOM ? (
|
||||
<div className="bg-white rounded-lg shadow-md border border-gray-200 p-3">
|
||||
<div className="mb-3">
|
||||
<h4 className="font-semibold text-gray-900 mb-2">
|
||||
{selectedBOM.bomCode}
|
||||
</h4>
|
||||
<div className="grid grid-cols-2 gap-1 text-sm text-gray-600">
|
||||
<div>
|
||||
Malzeme: {selectedBOM.material?.code} -{" "}
|
||||
{selectedBOM.material?.name}
|
||||
</div>
|
||||
<div>Versiyon: {selectedBOM.version}</div>
|
||||
<div>Tip: {getBOMTypeName(selectedBOM.bomType)}</div>
|
||||
<div>Temel Miktar: {selectedBOM.baseQuantity}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Components */}
|
||||
<div className="mb-3">
|
||||
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
||||
<FaBox className="w-4 h-4 mr-1" />
|
||||
Bileşenler ({selectedBOM.components.length})
|
||||
</h5>
|
||||
<div className="space-y-2">
|
||||
{selectedBOM.components.map((component) => (
|
||||
<div
|
||||
key={component.id}
|
||||
className="border border-gray-200 rounded p-1.5 text-sm"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="font-medium">
|
||||
{component.position}. {component.material?.code} -{" "}
|
||||
{component.material?.name}
|
||||
</span>
|
||||
<div className="flex space-x-2">
|
||||
{component.isPhantom && (
|
||||
<span className="bg-purple-100 text-purple-800 text-xs px-1 rounded-lg">
|
||||
Phantom
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={`text-xs px-1 rounded ${
|
||||
component.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{component.isActive ? "Aktif" : "Pasif"}
|
||||
Bileşenler ({selectedBOM.components.length})
|
||||
</h5>
|
||||
<div className="space-y-2">
|
||||
{selectedBOM.components.map((component) => (
|
||||
<div
|
||||
key={component.id}
|
||||
className="border border-gray-200 rounded p-1.5 text-sm"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="font-medium">
|
||||
{component.position}. {component.material?.code} -{' '}
|
||||
{component.material?.name}
|
||||
</span>
|
||||
<div className="flex space-x-2">
|
||||
{component.isPhantom && (
|
||||
<span className="bg-purple-100 text-purple-800 text-xs px-1 rounded-lg">
|
||||
Phantom
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={`text-xs px-1 rounded ${
|
||||
component.isActive
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{component.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-gray-600">
|
||||
Miktar: {component.quantity} {component.unitId} | Fire: %
|
||||
{component.scrapPercentage}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-gray-600">
|
||||
Miktar: {component.quantity} {component.unitId} | Fire:
|
||||
%{component.scrapPercentage}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Operations */}
|
||||
<div>
|
||||
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
||||
<FaCodeBranch className="w-4 h-4 mr-1" />
|
||||
Operasyonlar ({selectedBOM.operations.length})
|
||||
</h5>
|
||||
<div className="space-y-2">
|
||||
{selectedBOM.operations.map((operation) => (
|
||||
<div
|
||||
key={operation.id}
|
||||
className="border border-gray-200 rounded p-2 text-sm"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="font-medium">
|
||||
{operation.sequence}. {operation.operation?.code} -{" "}
|
||||
{operation.operation?.name}
|
||||
</span>
|
||||
<span
|
||||
className={`text-xs px-1 rounded ${
|
||||
operation.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{operation.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
{/* Operations */}
|
||||
<div>
|
||||
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
||||
<FaCodeBranch className="w-4 h-4 mr-1" />
|
||||
Operasyonlar ({selectedBOM.operations.length})
|
||||
</h5>
|
||||
<div className="space-y-2">
|
||||
{selectedBOM.operations.map((operation) => (
|
||||
<div
|
||||
key={operation.id}
|
||||
className="border border-gray-200 rounded p-2 text-sm"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="font-medium">
|
||||
{operation.sequence}. {operation.operation?.code} -{' '}
|
||||
{operation.operation?.name}
|
||||
</span>
|
||||
<span
|
||||
className={`text-xs px-1 rounded ${
|
||||
operation.isActive
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{operation.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-gray-600">
|
||||
Hazırlık: {operation.setupTime}dk | İşlem: {operation.runTime}dk | Kuyruk:{' '}
|
||||
{operation.queueTime}dk | Taşıma: {operation.moveTime}dk
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-gray-600">
|
||||
Hazırlık: {operation.setupTime}dk | İşlem:{" "}
|
||||
{operation.runTime}dk | Kuyruk: {operation.queueTime}dk
|
||||
| Taşıma: {operation.moveTime}dk
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-gray-50 rounded-lg p-6 text-center">
|
||||
<FaBox className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
BOM Detaylarını Görüntüle
|
||||
</h3>
|
||||
<p className="text-gray-500">
|
||||
Detaylarını görmek için sol taraftan bir BOM seçin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
) : (
|
||||
<div className="bg-gray-50 rounded-lg p-6 text-center">
|
||||
<FaBox className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
BOM Detaylarını Görüntüle
|
||||
</h3>
|
||||
<p className="text-gray-500">Detaylarını görmek için sol taraftan bir BOM seçin.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -360,8 +338,8 @@ const BOMManagement: React.FC = () => {
|
|||
onSave={handleSave}
|
||||
onClose={() => setShowModal(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default BOMManagement;
|
||||
export default BOMManagement
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
FaPlus,
|
||||
FaSearch,
|
||||
|
|
@ -11,402 +11,363 @@ import {
|
|||
FaWrench,
|
||||
FaTh,
|
||||
FaList,
|
||||
} from "react-icons/fa";
|
||||
import { MrpOperation } from "../../../types/mrp";
|
||||
import OperationFormModal from "./OperationFormModal";
|
||||
import { mockOperations } from "../../../mocks/mockOperations";
|
||||
} from 'react-icons/fa'
|
||||
import { MrpOperation } from '../../../types/mrp'
|
||||
import OperationFormModal from './OperationFormModal'
|
||||
import { mockOperations } from '../../../mocks/mockOperations'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const OperationDefinitions: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [editingOperation, setEditingOperation] = useState<MrpOperation | null>(
|
||||
null
|
||||
);
|
||||
const [viewMode, setViewMode] = useState<"card" | "list">("card");
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [editingOperation, setEditingOperation] = useState<MrpOperation | null>(null)
|
||||
const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
|
||||
|
||||
const getTotalCost = (operation: MrpOperation) => {
|
||||
return operation.laborCost + operation.machineCost + operation.overheadCost;
|
||||
};
|
||||
return operation.laborCost + operation.machineCost + operation.overheadCost
|
||||
}
|
||||
|
||||
// Mock data - replace with actual API calls
|
||||
const [operations, setOperations] = useState<MrpOperation[]>(mockOperations);
|
||||
const [operations, setOperations] = useState<MrpOperation[]>(mockOperations)
|
||||
|
||||
const filteredOperations = operations.filter(
|
||||
(operation) =>
|
||||
operation.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
operation.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
operation.workCenter?.name
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase())
|
||||
);
|
||||
operation.workCenter?.name.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
)
|
||||
|
||||
const handleEdit = (operation: MrpOperation) => {
|
||||
setEditingOperation(operation);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingOperation(operation)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const handleAddNew = () => {
|
||||
setEditingOperation(null);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingOperation(null)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const handleSave = (op: MrpOperation) => {
|
||||
setOperations((prev) => {
|
||||
const existing = prev.find((p) => p.id === op.id);
|
||||
if (existing) return prev.map((p) => (p.id === op.id ? op : p));
|
||||
return [...prev, { ...op, id: op.id || String(Date.now()) }];
|
||||
});
|
||||
setShowModal(false);
|
||||
};
|
||||
const existing = prev.find((p) => p.id === op.id)
|
||||
if (existing) return prev.map((p) => (p.id === op.id ? op : p))
|
||||
return [...prev, { ...op, id: op.id || String(Date.now()) }]
|
||||
})
|
||||
setShowModal(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">
|
||||
Operasyon Tanımları
|
||||
</h2>
|
||||
<p className="text-sm text-gray-600 mt-0.5">
|
||||
İş merkezlerinde gerçekleştirilen operasyonları tanımlayın
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
{/* View Toggle */}
|
||||
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">Operasyon Tanımları</h2>
|
||||
<p className="text-sm text-gray-600 mt-0.5">
|
||||
İş merkezlerinde gerçekleştirilen operasyonları tanımlayın
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
{/* View Toggle */}
|
||||
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
||||
<button
|
||||
onClick={() => setViewMode('card')}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === 'card'
|
||||
? 'bg-white text-blue-600 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<FaTh className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode('list')}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === 'list'
|
||||
? 'bg-white text-blue-600 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<FaList className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setViewMode("card")}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === "card"
|
||||
? "bg-white text-blue-600 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
onClick={handleAddNew}
|
||||
className="bg-blue-600 text-white px-2 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||
>
|
||||
<FaTh className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("list")}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === "list"
|
||||
? "bg-white text-blue-600 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
>
|
||||
<FaList className="w-4 h-4" />
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Operasyon</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddNew}
|
||||
className="bg-blue-600 text-white px-2 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Operasyon</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Operasyon ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Operations Display */}
|
||||
{viewMode === "card" ? (
|
||||
/* Card View */
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{filteredOperations.map((operation) => (
|
||||
<div
|
||||
key={operation.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-1">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{operation.name}
|
||||
</h3>
|
||||
<span className="px-2 py-1 rounded-full text-xs font-medium">
|
||||
{operation.operationType?.code}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-1">{operation.code}</p>
|
||||
{operation.description && (
|
||||
<p className="text-sm text-gray-500 mb-2">
|
||||
{operation.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleEdit(operation)}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
<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 transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Work Center Info */}
|
||||
<div className="bg-gray-50 rounded-lg p-2 mb-2">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<FaWrench className="w-4 h-4 text-gray-600" />
|
||||
<span className="font-medium text-gray-900">İş Merkezi</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-700">
|
||||
{operation.workCenter?.name}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
{operation.workCenter?.code}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 mb-2">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1" />
|
||||
Standart Süre
|
||||
</span>
|
||||
<span className="font-medium">
|
||||
{operation.standardTime} dk
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaCog className="w-4 h-4 mr-1" />
|
||||
Hazırlık Süresi
|
||||
</span>
|
||||
<span className="font-medium">
|
||||
{operation.setupTime} dk
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaUser className="w-4 h-4 mr-1" />
|
||||
İşçilik
|
||||
</span>
|
||||
<span className="font-medium">₺{operation.laborCost}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Makine Maliyeti</span>
|
||||
<span className="font-medium">
|
||||
₺{operation.machineCost}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Genel Gider</span>
|
||||
<span className="font-medium">
|
||||
₺{operation.overheadCost}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm border-t pt-2">
|
||||
<span className="text-gray-700 font-medium flex items-center">
|
||||
<FaDollarSign className="w-4 h-4 mr-1" />
|
||||
Toplam Maliyet
|
||||
</span>
|
||||
<span className="font-bold text-blue-600">
|
||||
₺{getTotalCost(operation)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{operation.instructions && (
|
||||
<div className="bg-blue-50 rounded-lg p-2 mb-2">
|
||||
<h4 className="text-sm font-medium text-blue-900 mb-1">
|
||||
Talimatlar
|
||||
</h4>
|
||||
<p className="text-sm text-blue-700">
|
||||
{operation.instructions}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between mt-2 pt-2 border-t">
|
||||
<div className="flex space-x-3">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{operation.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.qualityCheckRequired
|
||||
? "bg-purple-100 text-purple-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{operation.qualityCheckRequired
|
||||
? "Kalite Kontrolü Gerekli"
|
||||
: "Kalite Kontrolü İsteğe Bağlı"}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-400">
|
||||
{operation.lastModificationTime.toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* Search Bar */}
|
||||
<div className="relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Operasyon ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
/* List View */
|
||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Operasyon
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İş Merkezi
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Tür
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Süre (dk)
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Maliyet (₺)
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Durum
|
||||
</th>
|
||||
<th className="px-2 py-1.5 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">
|
||||
{filteredOperations.map((operation) => (
|
||||
<tr key={operation.id} className="hover:bg-gray-50 text-sm">
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{operation.name}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{operation.code}
|
||||
</div>
|
||||
{operation.description && (
|
||||
<div className="text-xs text-gray-400 mt-1">
|
||||
{operation.description}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Operations Display */}
|
||||
{viewMode === 'card' ? (
|
||||
/* Card View */
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{filteredOperations.map((operation) => (
|
||||
<div
|
||||
key={operation.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-1">
|
||||
<h3 className="text-lg font-semibold text-gray-900">{operation.name}</h3>
|
||||
<span className="px-2 py-1 rounded-full text-xs font-medium">
|
||||
{operation.operationType?.code}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
{operation.workCenter?.name}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{operation.workCenter?.code}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<span
|
||||
className={"px-2 py-1 rounded-full text-xs font-medium"}
|
||||
<p className="text-sm text-gray-600 mb-1">{operation.code}</p>
|
||||
{operation.description && (
|
||||
<p className="text-sm text-gray-500 mb-2">{operation.description}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleEdit(operation)}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
{operation.operationType?.code}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center">
|
||||
<FaClock className="w-3 h-3 mr-1 text-gray-400" />
|
||||
<span>Standart: {operation.standardTime}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<FaCog className="w-3 h-3 mr-1 text-gray-400" />
|
||||
<span>Hazırlık: {operation.setupTime}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
||||
<div className="space-y-1">
|
||||
<div>İşçilik: ₺{operation.laborCost}</div>
|
||||
<div>Makine: ₺{operation.machineCost}</div>
|
||||
<div className="font-medium text-blue-600">
|
||||
Toplam: ₺{getTotalCost(operation)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{operation.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.qualityCheckRequired
|
||||
? "bg-purple-100 text-purple-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{operation.qualityCheckRequired
|
||||
? "Kalite Gerekli"
|
||||
: "Kalite İsteğe Bağlı"}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex justify-end space-x-2">
|
||||
<button
|
||||
onClick={() => handleEdit(operation)}
|
||||
className="text-blue-600 hover:text-blue-900"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="text-red-600 hover:text-red-900">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
<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 transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{filteredOperations.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">
|
||||
Operasyon bulunamadı
|
||||
</h3>
|
||||
<p className="text-gray-500">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{/* Work Center Info */}
|
||||
<div className="bg-gray-50 rounded-lg p-2 mb-2">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<FaWrench className="w-4 h-4 text-gray-600" />
|
||||
<span className="font-medium text-gray-900">İş Merkezi</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-700">{operation.workCenter?.name}</p>
|
||||
<p className="text-sm text-gray-600">{operation.workCenter?.code}</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 mb-2">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1" />
|
||||
Standart Süre
|
||||
</span>
|
||||
<span className="font-medium">{operation.standardTime} dk</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaCog className="w-4 h-4 mr-1" />
|
||||
Hazırlık Süresi
|
||||
</span>
|
||||
<span className="font-medium">{operation.setupTime} dk</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaUser className="w-4 h-4 mr-1" />
|
||||
İşçilik
|
||||
</span>
|
||||
<span className="font-medium">₺{operation.laborCost}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Makine Maliyeti</span>
|
||||
<span className="font-medium">₺{operation.machineCost}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Genel Gider</span>
|
||||
<span className="font-medium">₺{operation.overheadCost}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm border-t pt-2">
|
||||
<span className="text-gray-700 font-medium flex items-center">
|
||||
<FaDollarSign className="w-4 h-4 mr-1" />
|
||||
Toplam Maliyet
|
||||
</span>
|
||||
<span className="font-bold text-blue-600">₺{getTotalCost(operation)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{operation.instructions && (
|
||||
<div className="bg-blue-50 rounded-lg p-2 mb-2">
|
||||
<h4 className="text-sm font-medium text-blue-900 mb-1">Talimatlar</h4>
|
||||
<p className="text-sm text-blue-700">{operation.instructions}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between mt-2 pt-2 border-t">
|
||||
<div className="flex space-x-3">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.isActive
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{operation.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.qualityCheckRequired
|
||||
? 'bg-purple-100 text-purple-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{operation.qualityCheckRequired
|
||||
? 'Kalite Kontrolü Gerekli'
|
||||
: 'Kalite Kontrolü İsteğe Bağlı'}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-400">
|
||||
{operation.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
/* List View */
|
||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Operasyon
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İş Merkezi
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Tür
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Süre (dk)
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Maliyet (₺)
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Durum
|
||||
</th>
|
||||
<th className="px-2 py-1.5 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">
|
||||
{filteredOperations.map((operation) => (
|
||||
<tr key={operation.id} className="hover:bg-gray-50 text-sm">
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">{operation.name}</div>
|
||||
<div className="text-sm text-gray-500">{operation.code}</div>
|
||||
{operation.description && (
|
||||
<div className="text-xs text-gray-400 mt-1">{operation.description}</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">{operation.workCenter?.name}</div>
|
||||
<div className="text-sm text-gray-500">{operation.workCenter?.code}</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<span className={'px-2 py-1 rounded-full text-xs font-medium'}>
|
||||
{operation.operationType?.code}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center">
|
||||
<FaClock className="w-3 h-3 mr-1 text-gray-400" />
|
||||
<span>Standart: {operation.standardTime}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<FaCog className="w-3 h-3 mr-1 text-gray-400" />
|
||||
<span>Hazırlık: {operation.setupTime}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
||||
<div className="space-y-1">
|
||||
<div>İşçilik: ₺{operation.laborCost}</div>
|
||||
<div>Makine: ₺{operation.machineCost}</div>
|
||||
<div className="font-medium text-blue-600">
|
||||
Toplam: ₺{getTotalCost(operation)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.isActive
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{operation.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
operation.qualityCheckRequired
|
||||
? 'bg-purple-100 text-purple-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{operation.qualityCheckRequired
|
||||
? 'Kalite Gerekli'
|
||||
: 'Kalite İsteğe Bağlı'}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2 py-1.5 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex justify-end space-x-2">
|
||||
<button
|
||||
onClick={() => handleEdit(operation)}
|
||||
className="text-blue-600 hover:text-blue-900"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="text-red-600 hover:text-red-900">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{filteredOperations.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">Operasyon bulunamadı</h3>
|
||||
<p className="text-gray-500">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modal for Add/Edit */}
|
||||
<OperationFormModal
|
||||
|
|
@ -415,8 +376,8 @@ const OperationDefinitions: React.FC = () => {
|
|||
onSave={handleSave}
|
||||
onClose={() => setShowModal(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default OperationDefinitions;
|
||||
export default OperationDefinitions
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ const OperationForm: React.FC = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ const OperationFormModal: React.FC<OperationFormProps> = ({
|
|||
{form.id ? "Operasyonu Düzenle" : "Yeni Operasyon"}
|
||||
</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
{/* First Row - Code and Name */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
FaPlus,
|
||||
FaSearch,
|
||||
|
|
@ -9,353 +9,335 @@ import {
|
|||
FaCheckCircle,
|
||||
FaTh,
|
||||
FaList,
|
||||
} from "react-icons/fa";
|
||||
import { MrpOperationTypeDefinition } from "../../../types/mrp";
|
||||
import OperationTypeFormModal from "./OperationTypeFormModal";
|
||||
import { mockOperationTypes } from "../../../mocks/mockOperationTypes";
|
||||
} from 'react-icons/fa'
|
||||
import { MrpOperationTypeDefinition } from '../../../types/mrp'
|
||||
import OperationTypeFormModal from './OperationTypeFormModal'
|
||||
import { mockOperationTypes } from '../../../mocks/mockOperationTypes'
|
||||
import {
|
||||
getOperationCategoryColor,
|
||||
getOperationTypeColor,
|
||||
getOperationTypeText,
|
||||
getSkillLevelText,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const OperationTypes: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [editingType, setEditingType] =
|
||||
useState<MrpOperationTypeDefinition | null>(null);
|
||||
const [viewMode, setViewMode] = useState<"card" | "list">("card");
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [editingType, setEditingType] = useState<MrpOperationTypeDefinition | null>(null)
|
||||
const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
|
||||
|
||||
const [operationTypes] =
|
||||
useState<MrpOperationTypeDefinition[]>(mockOperationTypes);
|
||||
const [operationTypes] = useState<MrpOperationTypeDefinition[]>(mockOperationTypes)
|
||||
|
||||
const filteredTypes = operationTypes.filter(
|
||||
(type) =>
|
||||
type.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
type.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
type.category.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
type.category.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
)
|
||||
|
||||
const handleEdit = (type: MrpOperationTypeDefinition) => {
|
||||
setEditingType(type);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingType(type)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const handleAddNew = () => {
|
||||
setEditingType(null);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingType(null)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">Operasyon Türleri</h2>
|
||||
<p className="text-sm text-gray-600 mt-0.5">
|
||||
İş merkezlerinde kullanılacak operasyon türlerini tanımlayın
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
{/* View Toggle */}
|
||||
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">Operasyon Türleri</h2>
|
||||
<p className="text-sm text-gray-600 mt-0.5">
|
||||
İş merkezlerinde kullanılacak operasyon türlerini tanımlayın
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
{/* View Toggle */}
|
||||
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
||||
<button
|
||||
onClick={() => setViewMode('card')}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === 'card'
|
||||
? 'bg-white text-blue-600 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<FaTh className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode('list')}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === 'list'
|
||||
? 'bg-white text-blue-600 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<FaList className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setViewMode("card")}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === "card"
|
||||
? "bg-white text-blue-600 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
onClick={handleAddNew}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||
>
|
||||
<FaTh className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("list")}
|
||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||
viewMode === "list"
|
||||
? "bg-white text-blue-600 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
>
|
||||
<FaList className="w-4 h-4" />
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Operasyon Türü</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddNew}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Operasyon Türü</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Operasyon türü ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
{/* Search Bar */}
|
||||
<div className="relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Operasyon türü ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Operation Types Display */}
|
||||
{viewMode === "card" ? (
|
||||
/* Card View */
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{filteredTypes.map((type) => (
|
||||
<div
|
||||
key={type.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-4 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{type.name}
|
||||
</h3>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor(
|
||||
type.category
|
||||
)}`}
|
||||
{/* Operation Types Display */}
|
||||
{viewMode === 'card' ? (
|
||||
/* Card View */
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{filteredTypes.map((type) => (
|
||||
<div
|
||||
key={type.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-4 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">{type.name}</h3>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor(
|
||||
type.category,
|
||||
)}`}
|
||||
>
|
||||
{getOperationTypeText(type.category)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-2">{type.code}</p>
|
||||
{type.description && (
|
||||
<p className="text-sm text-gray-500 mb-2">{type.description}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleEdit(type)}
|
||||
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
{getOperationTypeText(type.category)}
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1" />
|
||||
Varsayılan Süre
|
||||
</span>
|
||||
<span className="font-medium">{type.defaultDuration} dk</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Beceri Seviyesi</span>
|
||||
<span className="font-medium">
|
||||
{getSkillLevelText(type.skillLevelRequired)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaCog className="w-4 h-4 mr-1" />
|
||||
Hazırlık Gerekli
|
||||
</span>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
type.requiresSetup ? 'text-orange-600' : 'text-green-600'
|
||||
}`}
|
||||
>
|
||||
{type.requiresSetup ? 'Evet' : 'Hayır'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Paralel İşlem</span>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
type.allowsParallelOperation ? 'text-green-600' : 'text-gray-600'
|
||||
}`}
|
||||
>
|
||||
{type.allowsParallelOperation ? 'Evet' : 'Hayır'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaCheckCircle className="w-4 h-4 mr-1" />
|
||||
Kalite Kontrolü
|
||||
</span>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
type.qualityCheckRequired ? 'text-purple-600' : 'text-gray-600'
|
||||
}`}
|
||||
>
|
||||
{type.qualityCheckRequired ? 'Gerekli' : 'İsteğe Bağlı'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-2">{type.code}</p>
|
||||
{type.description && (
|
||||
<p className="text-sm text-gray-500 mb-2">
|
||||
{type.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleEdit(type)}
|
||||
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
|
||||
<div className="mt-3 pt-3 border-t border-gray-100">
|
||||
<div className="flex items-center justify-between">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{type.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400">
|
||||
{type.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1" />
|
||||
Varsayılan Süre
|
||||
</span>
|
||||
<span className="font-medium">{type.defaultDuration} dk</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Beceri Seviyesi</span>
|
||||
<span className="font-medium">
|
||||
{getSkillLevelText(type.skillLevelRequired)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaCog className="w-4 h-4 mr-1" />
|
||||
Hazırlık Gerekli
|
||||
</span>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
type.requiresSetup ? "text-orange-600" : "text-green-600"
|
||||
}`}
|
||||
>
|
||||
{type.requiresSetup ? "Evet" : "Hayır"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">Paralel İşlem</span>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
type.allowsParallelOperation
|
||||
? "text-green-600"
|
||||
: "text-gray-600"
|
||||
}`}
|
||||
>
|
||||
{type.allowsParallelOperation ? "Evet" : "Hayır"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500 flex items-center">
|
||||
<FaCheckCircle className="w-4 h-4 mr-1" />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
/* List View */
|
||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||
<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">
|
||||
Operasyon Türü
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Kategori
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Süre
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Beceri Seviyesi
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Hazırlık
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Kalite Kontrolü
|
||||
</span>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
type.qualityCheckRequired
|
||||
? "text-purple-600"
|
||||
: "text-gray-600"
|
||||
}`}
|
||||
>
|
||||
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 pt-3 border-t border-gray-100">
|
||||
<div className="flex items-center justify-between">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{type.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400">
|
||||
{type.lastModificationTime.toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
/* List View */
|
||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||
<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">
|
||||
Operasyon Türü
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Kategori
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Süre
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Beceri Seviyesi
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Hazırlık
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Kalite Kontrolü
|
||||
</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">
|
||||
{filteredTypes.map((type) => (
|
||||
<tr key={type.id} className="hover:bg-gray-50 text-sm">
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{type.name}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">{type.code}</div>
|
||||
{type.description && (
|
||||
<div className="text-xs text-gray-400 mt-0.5">
|
||||
{type.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationCategoryColor(
|
||||
type.category
|
||||
)}`}
|
||||
>
|
||||
{type.category}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||
<div className="flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1 text-gray-400" />
|
||||
{type.defaultDuration} dk
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||
{getSkillLevelText(type.skillLevelRequired)}
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.requiresSetup
|
||||
? "bg-orange-100 text-orange-800"
|
||||
: "bg-green-100 text-green-800"
|
||||
}`}
|
||||
>
|
||||
{type.requiresSetup ? "Gerekli" : "Gerekli Değil"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.qualityCheckRequired
|
||||
? "bg-purple-100 text-purple-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{type.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex justify-end space-x-2">
|
||||
<button
|
||||
onClick={() => handleEdit(type)}
|
||||
className="text-blue-600 hover:text-blue-900"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="text-red-600 hover:text-red-900">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</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>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{filteredTypes.map((type) => (
|
||||
<tr key={type.id} className="hover:bg-gray-50 text-sm">
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">{type.name}</div>
|
||||
<div className="text-sm text-gray-500">{type.code}</div>
|
||||
{type.description && (
|
||||
<div className="text-xs text-gray-400 mt-0.5">{type.description}</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationCategoryColor(
|
||||
type.category,
|
||||
)}`}
|
||||
>
|
||||
{type.category}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||
<div className="flex items-center">
|
||||
<FaClock className="w-4 h-4 mr-1 text-gray-400" />
|
||||
{type.defaultDuration} dk
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||
{getSkillLevelText(type.skillLevelRequired)}
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.requiresSetup
|
||||
? 'bg-orange-100 text-orange-800'
|
||||
: 'bg-green-100 text-green-800'
|
||||
}`}
|
||||
>
|
||||
{type.requiresSetup ? 'Gerekli' : 'Gerekli Değil'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.qualityCheckRequired
|
||||
? 'bg-purple-100 text-purple-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{type.qualityCheckRequired ? 'Gerekli' : 'İsteğe Bağlı'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{type.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex justify-end space-x-2">
|
||||
<button
|
||||
onClick={() => handleEdit(type)}
|
||||
className="text-blue-600 hover:text-blue-900"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="text-red-600 hover:text-red-900">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{filteredTypes.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">
|
||||
Operasyon türü bulunamadı
|
||||
</h3>
|
||||
<p className="text-gray-500">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü
|
||||
ekleyin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{filteredTypes.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">Operasyon türü bulunamadı</h3>
|
||||
<p className="text-gray-500">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü ekleyin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modal for Add/Edit - Placeholder */}
|
||||
<OperationTypeFormModal
|
||||
|
|
@ -364,12 +346,12 @@ const OperationTypes: React.FC = () => {
|
|||
onClose={() => setShowModal(false)}
|
||||
onSave={(data) => {
|
||||
// TODO: persist to API; for now just close modal
|
||||
console.log("Saved operation type:", data);
|
||||
setShowModal(false);
|
||||
console.log('Saved operation type:', data)
|
||||
setShowModal(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default OperationTypes;
|
||||
export default OperationTypes
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@ import {
|
|||
getProductionOrderStatus,
|
||||
getWorkOrderStatus,
|
||||
} from "../../../utils/erp";
|
||||
import { Container } from "@/components/shared";
|
||||
|
||||
interface PlanningGanttProps {
|
||||
workCenterId?: string;
|
||||
}
|
||||
|
||||
export const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
||||
const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
||||
workCenterId,
|
||||
}) => {
|
||||
const getInitialExpandedItems = () => {
|
||||
|
|
@ -502,8 +503,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-3 mt-2">
|
||||
<Container>
|
||||
<div className="mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">
|
||||
|
|
@ -618,6 +619,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
|||
{filteredData.map((task) => renderTask(task))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlanningGantt;
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,8 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { mockProductionOrders } from "../../../mocks/mockProductionOrders";
|
||||
import {
|
||||
MrpProductionOrder,
|
||||
MrpProductionOrderMaterial,
|
||||
} from "../../../types/mrp";
|
||||
import React, { useMemo } from 'react'
|
||||
import { useParams, useNavigate } from 'react-router-dom'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { mockProductionOrders } from '../../../mocks/mockProductionOrders'
|
||||
import { MrpProductionOrder, MrpProductionOrderMaterial } from '../../../types/mrp'
|
||||
import {
|
||||
FaCog,
|
||||
FaCalendarAlt,
|
||||
|
|
@ -14,29 +11,28 @@ import {
|
|||
FaMoneyBillWave,
|
||||
FaArrowLeft,
|
||||
FaExclamationTriangle,
|
||||
} from "react-icons/fa";
|
||||
import StatusBadge from "../../../components/common/StatusBadge";
|
||||
import { getPriorityColor } from "../../../utils/erp";
|
||||
} from 'react-icons/fa'
|
||||
import StatusBadge from '../../../components/common/StatusBadge'
|
||||
import { getPriorityColor } from '../../../utils/erp'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const ProductionOrderView: React.FC = () => {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const { data: productionOrder, isLoading } = useQuery({
|
||||
queryKey: ["production-order", id],
|
||||
queryKey: ['production-order', id],
|
||||
queryFn: async () => {
|
||||
await new Promise((r) => setTimeout(r, 200));
|
||||
return mockProductionOrders.find((p) => p.id === id) as
|
||||
| MrpProductionOrder
|
||||
| undefined;
|
||||
await new Promise((r) => setTimeout(r, 200))
|
||||
return mockProductionOrders.find((p) => p.id === id) as MrpProductionOrder | undefined
|
||||
},
|
||||
enabled: !!id,
|
||||
});
|
||||
})
|
||||
|
||||
const materials: MrpProductionOrderMaterial[] = useMemo(
|
||||
() => productionOrder?.materials || [],
|
||||
[productionOrder]
|
||||
);
|
||||
[productionOrder],
|
||||
)
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
|
@ -46,7 +42,7 @@ const ProductionOrderView: React.FC = () => {
|
|||
<p className="text-gray-600 font-medium">Üretim emri yükleniyor...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
if (!productionOrder) {
|
||||
|
|
@ -56,14 +52,12 @@ const ProductionOrderView: React.FC = () => {
|
|||
<div className="mb-6">
|
||||
<FaExclamationTriangle className="mx-auto h-16 w-16 text-gray-400" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
Üretim Emri Bulunamadı
|
||||
</h2>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">Üretim Emri Bulunamadı</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
İstenen üretim emri mevcut değil veya silinmiş olabilir.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => navigate("/admin/mrp/production-orders")}
|
||||
onClick={() => navigate('/admin/mrp/production-orders')}
|
||||
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white font-medium rounded-lg hover:from-blue-700 hover:to-blue-800 transition-all duration-200 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
<FaArrowLeft className="mr-2" />
|
||||
|
|
@ -71,436 +65,385 @@ const ProductionOrderView: React.FC = () => {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
const getProgressPercentage = () => {
|
||||
if (productionOrder.plannedQuantity === 0) return 0;
|
||||
return Math.round(
|
||||
(productionOrder.confirmedQuantity / productionOrder.plannedQuantity) *
|
||||
100
|
||||
);
|
||||
};
|
||||
if (productionOrder.plannedQuantity === 0) return 0
|
||||
return Math.round((productionOrder.confirmedQuantity / productionOrder.plannedQuantity) * 100)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto mt-2">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<button
|
||||
onClick={() => navigate("/admin/mrp/production-orders")}
|
||||
className="inline-flex items-center px-3 py-2 bg-white text-gray-700 font-medium rounded-lg hover:bg-gray-50 transition-all duration-200 shadow-sm border border-gray-200"
|
||||
>
|
||||
<FaArrowLeft className="mr-2 text-sm" />
|
||||
Geri
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
Üretim Emri Detayları
|
||||
</h1>
|
||||
<p className="text-gray-600 mt-1 text-sm">
|
||||
{productionOrder.orderNumber} - Detaylı bilgiler
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Order Header Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 mb-4">
|
||||
<div className="flex flex-wrap justify-between items-start mb-4">
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
|
||||
Üretim Emri No
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900">
|
||||
{productionOrder.orderNumber}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 text-right">
|
||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
|
||||
Durum
|
||||
</div>
|
||||
<StatusBadge status={productionOrder.status} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
||||
<div className="bg-gradient-to-br from-blue-50 to-blue-100 p-4 rounded-xl border border-blue-200">
|
||||
<div className="text-sm font-medium text-blue-700 mb-2 uppercase tracking-wide">
|
||||
Öncelik
|
||||
</div>
|
||||
<span
|
||||
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
|
||||
productionOrder.priority
|
||||
)}`}
|
||||
>
|
||||
{productionOrder.priority}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-br from-purple-50 to-purple-100 p-4 rounded-xl border border-purple-200">
|
||||
<div className="text-sm font-medium text-purple-700 mb-2 uppercase tracking-wide">
|
||||
Tür
|
||||
</div>
|
||||
<div className="text-base font-semibold text-purple-900">
|
||||
{productionOrder.orderType}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-br from-orange-50 to-orange-100 p-4 rounded-xl border border-orange-200">
|
||||
<div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
|
||||
İlerleme
|
||||
</div>
|
||||
<div className="text-xl font-bold text-orange-900">
|
||||
{getProgressPercentage()}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="text-sm font-medium text-gray-700">
|
||||
Üretim İlerlemesi
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{productionOrder.confirmedQuantity} /{" "}
|
||||
{productionOrder.plannedQuantity} adet
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
|
||||
<div
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-600 h-3 rounded-full transition-all duration-500 ease-out"
|
||||
style={{ width: `${getProgressPercentage()}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{productionOrder.customerRequirement && (
|
||||
<div className="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200">
|
||||
<div className="flex items-center mb-3">
|
||||
<FaClipboardList className="text-blue-600 mr-2" />
|
||||
<div className="text-sm font-semibold text-blue-800 uppercase tracking-wide">
|
||||
Müşteri Talebi
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<button
|
||||
onClick={() => navigate('/admin/mrp/production-orders')}
|
||||
className="inline-flex items-center px-3 py-2 bg-white text-gray-700 font-medium rounded-lg hover:bg-gray-50 transition-all duration-200 shadow-sm border border-gray-200"
|
||||
>
|
||||
<FaArrowLeft className="mr-2 text-sm" />
|
||||
Geri
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Üretim Emri Detayları</h1>
|
||||
<p className="text-gray-600 mt-1 text-sm">
|
||||
{productionOrder.orderNumber} - Detaylı bilgiler
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-blue-700 leading-relaxed">
|
||||
{productionOrder.customerRequirement}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Info Cards Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
|
||||
{/* Quantities Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-blue-100 rounded-lg">
|
||||
<FaCog className="text-blue-600 text-base" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">
|
||||
Miktar Bilgileri
|
||||
</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||
<span className="text-gray-600 font-medium">Planlanan:</span>
|
||||
<span className="font-bold text-gray-900">
|
||||
{productionOrder.plannedQuantity}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||
<span className="text-gray-600 font-medium">Üretilen:</span>
|
||||
<span className="font-bold text-green-600">
|
||||
{productionOrder.confirmedQuantity}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||
<span className="text-gray-600 font-medium">Gereken:</span>
|
||||
<span className="font-bold text-blue-600">
|
||||
{productionOrder.requiredQuantity}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-600 font-medium">Fire:</span>
|
||||
<span className="font-bold text-red-600">
|
||||
{productionOrder.scrapQuantity}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Schedule Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-purple-100 rounded-lg">
|
||||
<FaCalendarAlt className="text-purple-600 text-base" />
|
||||
{/* Order Header Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 mb-4">
|
||||
<div className="flex flex-wrap justify-between items-start mb-4">
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
|
||||
Üretim Emri No
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900">{productionOrder.orderNumber}</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 text-right">
|
||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">Durum</div>
|
||||
<StatusBadge status={productionOrder.status} />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">Zamanlama</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Plan Başlangıç
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
||||
<div className="bg-gradient-to-br from-blue-50 to-blue-100 p-4 rounded-xl border border-blue-200">
|
||||
<div className="text-sm font-medium text-blue-700 mb-2 uppercase tracking-wide">
|
||||
Öncelik
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(productionOrder.plannedStartDate).toLocaleDateString(
|
||||
"tr-TR",
|
||||
{
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
}
|
||||
)}
|
||||
<span
|
||||
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
|
||||
productionOrder.priority,
|
||||
)}`}
|
||||
>
|
||||
{productionOrder.priority}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-br from-purple-50 to-purple-100 p-4 rounded-xl border border-purple-200">
|
||||
<div className="text-sm font-medium text-purple-700 mb-2 uppercase tracking-wide">
|
||||
Tür
|
||||
</div>
|
||||
<div className="text-base font-semibold text-purple-900">
|
||||
{productionOrder.orderType}
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Plan Bitiş
|
||||
|
||||
<div className="bg-gradient-to-br from-orange-50 to-orange-100 p-4 rounded-xl border border-orange-200">
|
||||
<div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
|
||||
İlerleme
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(productionOrder.plannedEndDate).toLocaleDateString(
|
||||
"tr-TR",
|
||||
{
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
}
|
||||
)}
|
||||
<div className="text-xl font-bold text-orange-900">{getProgressPercentage()}%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="text-sm font-medium text-gray-700">Üretim İlerlemesi</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{productionOrder.confirmedQuantity} / {productionOrder.plannedQuantity} adet
|
||||
</div>
|
||||
</div>
|
||||
{productionOrder.actualStartDate && (
|
||||
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
|
||||
<div
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-600 h-3 rounded-full transition-all duration-500 ease-out"
|
||||
style={{ width: `${getProgressPercentage()}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{productionOrder.customerRequirement && (
|
||||
<div className="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200">
|
||||
<div className="flex items-center mb-3">
|
||||
<FaClipboardList className="text-blue-600 mr-2" />
|
||||
<div className="text-sm font-semibold text-blue-800 uppercase tracking-wide">
|
||||
Müşteri Talebi
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-blue-700 leading-relaxed">{productionOrder.customerRequirement}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Info Cards Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
|
||||
{/* Quantities Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-blue-100 rounded-lg">
|
||||
<FaCog className="text-blue-600 text-base" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">Miktar Bilgileri</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||
<span className="text-gray-600 font-medium">Planlanan:</span>
|
||||
<span className="font-bold text-gray-900">{productionOrder.plannedQuantity}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||
<span className="text-gray-600 font-medium">Üretilen:</span>
|
||||
<span className="font-bold text-green-600">
|
||||
{productionOrder.confirmedQuantity}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||
<span className="text-gray-600 font-medium">Gereken:</span>
|
||||
<span className="font-bold text-blue-600">{productionOrder.requiredQuantity}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-600 font-medium">Fire:</span>
|
||||
<span className="font-bold text-red-600">{productionOrder.scrapQuantity}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Schedule Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-purple-100 rounded-lg">
|
||||
<FaCalendarAlt className="text-purple-600 text-base" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">Zamanlama</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Gerçek Başlangıç
|
||||
Plan Başlangıç
|
||||
</div>
|
||||
<div className="font-semibold text-green-600">
|
||||
{new Date(productionOrder.actualStartDate).toLocaleDateString(
|
||||
"tr-TR",
|
||||
{
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
}
|
||||
)}
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(productionOrder.plannedStartDate).toLocaleDateString('tr-TR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Plan Bitiş
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(productionOrder.plannedEndDate).toLocaleDateString('tr-TR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{productionOrder.actualStartDate && (
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Gerçek Başlangıç
|
||||
</div>
|
||||
<div className="font-semibold text-green-600">
|
||||
{new Date(productionOrder.actualStartDate).toLocaleDateString('tr-TR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{productionOrder.actualEndDate && (
|
||||
<div className="py-1.5">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Gerçek Bitiş
|
||||
</div>
|
||||
<div className="font-semibold text-green-600">
|
||||
{new Date(productionOrder.actualEndDate).toLocaleDateString('tr-TR', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Costs Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-green-100 rounded-lg">
|
||||
<FaMoneyBillWave className="text-green-600 text-base" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">Maliyet Bilgileri</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Planlanan Maliyet
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900">
|
||||
{new Intl.NumberFormat('tr-TR', {
|
||||
style: 'currency',
|
||||
currency: productionOrder.currency,
|
||||
}).format(productionOrder.plannedCost)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Gerçekleşen Maliyet
|
||||
</div>
|
||||
<div className="text-lg font-bold text-green-600">
|
||||
{new Intl.NumberFormat('tr-TR', {
|
||||
style: 'currency',
|
||||
currency: productionOrder.currency,
|
||||
}).format(productionOrder.actualCost)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{productionOrder.actualEndDate && (
|
||||
<div className="py-1.5">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Gerçek Bitiş
|
||||
Para Birimi
|
||||
</div>
|
||||
<div className="font-semibold text-green-600">
|
||||
{new Date(productionOrder.actualEndDate).toLocaleDateString(
|
||||
"tr-TR",
|
||||
{
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
}
|
||||
)}
|
||||
<div className="font-semibold text-gray-900">{productionOrder.currency}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metadata Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-gray-100 rounded-lg">
|
||||
<FaClipboardList className="text-gray-600 text-base" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">Sistem Bilgileri</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Oluşturulma
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(productionOrder.creationTime).toLocaleDateString('tr-TR', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Costs Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-green-100 rounded-lg">
|
||||
<FaMoneyBillWave className="text-green-600 text-base" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">
|
||||
Maliyet Bilgileri
|
||||
</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Planlanan Maliyet
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900">
|
||||
{new Intl.NumberFormat("tr-TR", {
|
||||
style: "currency",
|
||||
currency: productionOrder.currency,
|
||||
}).format(productionOrder.plannedCost)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Gerçekleşen Maliyet
|
||||
</div>
|
||||
<div className="text-lg font-bold text-green-600">
|
||||
{new Intl.NumberFormat("tr-TR", {
|
||||
style: "currency",
|
||||
currency: productionOrder.currency,
|
||||
}).format(productionOrder.actualCost)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-1.5">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Para Birimi
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{productionOrder.currency}
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Son Güncelleme
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(productionOrder.lastModificationTime).toLocaleDateString('tr-TR', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metadata Card */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-gray-100 rounded-lg">
|
||||
<FaClipboardList className="text-gray-600 text-base" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-gray-900">
|
||||
Sistem Bilgileri
|
||||
</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Oluşturulma
|
||||
{/* Materials List */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="p-1.5 bg-indigo-100 rounded-lg">
|
||||
<FaTruck className="text-indigo-600 text-base" />
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(productionOrder.creationTime).toLocaleDateString(
|
||||
"tr-TR",
|
||||
{ year: "numeric", month: "short", day: "numeric" }
|
||||
)}
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
|
||||
<p className="text-gray-600 mt-1 text-sm">Üretim emrine bağlı malzeme satırları</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-1.5 border-b border-gray-100">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Son Güncelleme
|
||||
</div>
|
||||
<div className="font-semibold text-gray-900">
|
||||
{new Date(
|
||||
productionOrder.lastModificationTime
|
||||
).toLocaleDateString("tr-TR", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
})}
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-lg font-bold text-indigo-600">{materials.length}</div>
|
||||
<div className="text-sm text-gray-500">Malzeme</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Materials List */}
|
||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="p-1.5 bg-indigo-100 rounded-lg">
|
||||
<FaTruck className="text-indigo-600 text-base" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
|
||||
<p className="text-gray-600 mt-1 text-sm">
|
||||
Üretim emrine bağlı malzeme satırları
|
||||
{materials.length === 0 ? (
|
||||
<div className="p-8 text-center bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border-2 border-dashed border-gray-300">
|
||||
<FaTruck className="mx-auto h-16 w-16 text-gray-400 mb-4" />
|
||||
<p className="text-gray-500 text-base font-medium">
|
||||
Bu üretim emrine bağlı malzeme bulunmamaktadır.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-lg font-bold text-indigo-600">
|
||||
{materials.length}
|
||||
) : (
|
||||
<div className="space-y-3 max-h-96 overflow-y-auto">
|
||||
{materials.map((m: MrpProductionOrderMaterial) => (
|
||||
<div
|
||||
key={m.id}
|
||||
className="flex flex-col lg:flex-row lg:items-center justify-between p-4 border border-gray-200 rounded-xl hover:bg-gradient-to-r hover:from-blue-50 hover:to-indigo-50 hover:border-blue-300 transition-all duration-200 group"
|
||||
>
|
||||
<div className="mb-4 lg:mb-0 flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-blue-100 rounded-lg group-hover:bg-blue-200 transition-colors">
|
||||
<FaTruck className="text-blue-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-gray-600">
|
||||
{m.salesOrder?.orderNumber || m.salesOrder?.id}
|
||||
</div>
|
||||
<div className="font-bold text-gray-900 text-base">
|
||||
{m.material?.code || m.materialId}
|
||||
{' - '}
|
||||
{m.material?.name || 'Malzeme adı bulunamadı'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{m.customerRequirement && (
|
||||
<div className="text-sm text-blue-600 italic rounded-lg mt-3">
|
||||
"{m.customerRequirement}"
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4">
|
||||
<div className="text-center p-2 bg-gray-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Planlanan
|
||||
</div>
|
||||
<div className="font-bold text-gray-900 text-base">{m.plannedQuantity}</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center p-2 bg-green-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-green-600 uppercase tracking-wide mb-1">
|
||||
Üretilen
|
||||
</div>
|
||||
<div className="font-bold text-green-700 text-base">
|
||||
{m.confirmedQuantity}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center p-2 bg-blue-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-blue-600 uppercase tracking-wide mb-1">
|
||||
Gereken
|
||||
</div>
|
||||
<div className="font-bold text-blue-700 text-base">{m.requiredQuantity}</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center p-2 bg-red-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-red-600 uppercase tracking-wide mb-1">
|
||||
Fire
|
||||
</div>
|
||||
<div className="font-bold text-red-700 text-base">{m.scrapQuantity}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">Malzeme</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{materials.length === 0 ? (
|
||||
<div className="p-8 text-center bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border-2 border-dashed border-gray-300">
|
||||
<FaTruck className="mx-auto h-16 w-16 text-gray-400 mb-4" />
|
||||
<p className="text-gray-500 text-base font-medium">
|
||||
Bu üretim emrine bağlı malzeme bulunmamaktadır.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3 max-h-96 overflow-y-auto">
|
||||
{materials.map((m: MrpProductionOrderMaterial) => (
|
||||
<div
|
||||
key={m.id}
|
||||
className="flex flex-col lg:flex-row lg:items-center justify-between p-4 border border-gray-200 rounded-xl hover:bg-gradient-to-r hover:from-blue-50 hover:to-indigo-50 hover:border-blue-300 transition-all duration-200 group"
|
||||
>
|
||||
<div className="mb-4 lg:mb-0 flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="p-1.5 bg-blue-100 rounded-lg group-hover:bg-blue-200 transition-colors">
|
||||
<FaTruck className="text-blue-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-gray-600">
|
||||
{m.salesOrder?.orderNumber || m.salesOrder?.id}
|
||||
</div>
|
||||
<div className="font-bold text-gray-900 text-base">
|
||||
{m.material?.code || m.materialId}
|
||||
{" - "}
|
||||
{m.material?.name || "Malzeme adı bulunamadı"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{m.customerRequirement && (
|
||||
<div className="text-sm text-blue-600 italic rounded-lg mt-3">
|
||||
"{m.customerRequirement}"
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4">
|
||||
<div className="text-center p-2 bg-gray-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||
Planlanan
|
||||
</div>
|
||||
<div className="font-bold text-gray-900 text-base">
|
||||
{m.plannedQuantity}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center p-2 bg-green-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-green-600 uppercase tracking-wide mb-1">
|
||||
Üretilen
|
||||
</div>
|
||||
<div className="font-bold text-green-700 text-base">
|
||||
{m.confirmedQuantity}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center p-2 bg-blue-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-blue-600 uppercase tracking-wide mb-1">
|
||||
Gereken
|
||||
</div>
|
||||
<div className="font-bold text-blue-700 text-base">
|
||||
{m.requiredQuantity}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center p-2 bg-red-50 rounded-lg">
|
||||
<div className="text-xs font-medium text-red-600 uppercase tracking-wide mb-1">
|
||||
Fire
|
||||
</div>
|
||||
<div className="font-bold text-red-700 text-base">
|
||||
{m.scrapQuantity}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductionOrderView;
|
||||
export default ProductionOrderView
|
||||
|
|
|
|||
|
|
@ -1,209 +1,188 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaBox, FaIndustry, FaShoppingCart } from "react-icons/fa";
|
||||
import React, { useState } from 'react'
|
||||
import { FaBox, FaIndustry, FaShoppingCart } from 'react-icons/fa'
|
||||
import {
|
||||
MrpMaterialRequirement,
|
||||
MrpProductionSuggestion,
|
||||
MrpPurchaseSuggestion,
|
||||
} from "../../../types/mrp";
|
||||
import RunMrpModal from "./RunMrpModal";
|
||||
import ProductionSuggestions from "./ProductionSuggestions";
|
||||
import PurchaseSuggestions from "./PurchaseSuggestions";
|
||||
import MaterialRequirements from "./MaterialRequirements";
|
||||
} from '../../../types/mrp'
|
||||
import RunMrpModal from './RunMrpModal'
|
||||
import ProductionSuggestions from './ProductionSuggestions'
|
||||
import PurchaseSuggestions from './PurchaseSuggestions'
|
||||
import MaterialRequirements from './MaterialRequirements'
|
||||
import {
|
||||
mockMaterialRequirements,
|
||||
mockProductionSuggestions,
|
||||
mockPurchaseSuggestions,
|
||||
} from "../../../mocks/mockMRP";
|
||||
import Widget from "../../../components/common/Widget";
|
||||
} from '../../../mocks/mockMRP'
|
||||
import Widget from '../../../components/common/Widget'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const Requirements: React.FC = () => {
|
||||
const [materialRequirements, setMaterialRequirements] = useState<
|
||||
MrpMaterialRequirement[]
|
||||
>([]);
|
||||
const [purchaseSuggestions, setPurchaseSuggestions] = useState<
|
||||
MrpPurchaseSuggestion[]
|
||||
>([]);
|
||||
const [productionSuggestions, setProductionSuggestions] = useState<
|
||||
MrpProductionSuggestion[]
|
||||
>([]);
|
||||
const [materialRequirements, setMaterialRequirements] = useState<MrpMaterialRequirement[]>([])
|
||||
const [purchaseSuggestions, setPurchaseSuggestions] = useState<MrpPurchaseSuggestion[]>([])
|
||||
const [productionSuggestions, setProductionSuggestions] = useState<MrpProductionSuggestion[]>([])
|
||||
|
||||
const runMrp = () => {
|
||||
// This is where the actual MRP logic would run.
|
||||
// For now, we'll just populate the state with mock data.
|
||||
setMaterialRequirements(mockMaterialRequirements);
|
||||
setPurchaseSuggestions(mockPurchaseSuggestions);
|
||||
setProductionSuggestions(mockProductionSuggestions);
|
||||
};
|
||||
setMaterialRequirements(mockMaterialRequirements)
|
||||
setPurchaseSuggestions(mockPurchaseSuggestions)
|
||||
setProductionSuggestions(mockProductionSuggestions)
|
||||
}
|
||||
|
||||
const [isMrpModalOpen, setIsMrpModalOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<
|
||||
"requirements" | "production" | "purchase"
|
||||
>("requirements");
|
||||
const [isMrpModalOpen, setIsMrpModalOpen] = useState(false)
|
||||
const [activeTab, setActiveTab] = useState<'requirements' | 'production' | 'purchase'>(
|
||||
'requirements',
|
||||
)
|
||||
|
||||
const getUrgencyLevel = (requirement: MrpMaterialRequirement) => {
|
||||
const today = new Date();
|
||||
const reqDate = new Date(requirement.requirementDate);
|
||||
const today = new Date()
|
||||
const reqDate = new Date(requirement.requirementDate)
|
||||
const daysUntilRequired = Math.ceil(
|
||||
(reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)
|
||||
);
|
||||
(reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24),
|
||||
)
|
||||
|
||||
if (daysUntilRequired < 0)
|
||||
return {
|
||||
level: "overdue",
|
||||
color: "bg-red-100 text-red-800",
|
||||
label: "Gecikmiş",
|
||||
};
|
||||
level: 'overdue',
|
||||
color: 'bg-red-100 text-red-800',
|
||||
label: 'Gecikmiş',
|
||||
}
|
||||
if (daysUntilRequired <= 7)
|
||||
return {
|
||||
level: "urgent",
|
||||
color: "bg-orange-100 text-orange-800",
|
||||
label: "Acil",
|
||||
};
|
||||
level: 'urgent',
|
||||
color: 'bg-orange-100 text-orange-800',
|
||||
label: 'Acil',
|
||||
}
|
||||
if (daysUntilRequired <= 30)
|
||||
return {
|
||||
level: "soon",
|
||||
color: "bg-yellow-100 text-yellow-800",
|
||||
label: "Yakın",
|
||||
};
|
||||
level: 'soon',
|
||||
color: 'bg-yellow-100 text-yellow-800',
|
||||
label: 'Yakın',
|
||||
}
|
||||
return {
|
||||
level: "normal",
|
||||
color: "bg-green-100 text-green-800",
|
||||
label: "Normal",
|
||||
};
|
||||
};
|
||||
level: 'normal',
|
||||
color: 'bg-green-100 text-green-800',
|
||||
label: 'Normal',
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
const totalRequirements = materialRequirements.length;
|
||||
const shortageRequirements = materialRequirements.filter(
|
||||
(r) => r.netRequirement > 0
|
||||
).length;
|
||||
const totalRequirements = materialRequirements.length
|
||||
const shortageRequirements = materialRequirements.filter((r) => r.netRequirement > 0).length
|
||||
const urgentRequirements = materialRequirements.filter(
|
||||
(r) => getUrgencyLevel(r).level === "urgent"
|
||||
).length;
|
||||
const openProductionSuggestions = productionSuggestions.filter(
|
||||
(s) => s.status === "OPEN"
|
||||
).length;
|
||||
const openPurchaseSuggestions = purchaseSuggestions.filter(
|
||||
(s) => s.status === "OPEN"
|
||||
).length;
|
||||
(r) => getUrgencyLevel(r).level === 'urgent',
|
||||
).length
|
||||
const openProductionSuggestions = productionSuggestions.filter((s) => s.status === 'OPEN').length
|
||||
const openPurchaseSuggestions = purchaseSuggestions.filter((s) => s.status === 'OPEN').length
|
||||
|
||||
return (
|
||||
<div className="space-y-3 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">
|
||||
Malzeme İhtiyaçları
|
||||
</h2>
|
||||
<p className="text-sm text-gray-600">
|
||||
Malzeme ihtiyaç hesaplama ve satın alma önerileri
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setIsMrpModalOpen(true)}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||
>
|
||||
<FaBox className="w-4 h-4" />
|
||||
MRP Çalıştır
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Widget
|
||||
title="Toplam İhtiyaç"
|
||||
value={totalRequirements}
|
||||
color="blue"
|
||||
icon="FaBox"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Eksik Stok"
|
||||
value={shortageRequirements}
|
||||
color="red"
|
||||
icon="FaArrowDown"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Acil İhtiyaç"
|
||||
value={urgentRequirements}
|
||||
color="orange"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Açık Öneri"
|
||||
value={openProductionSuggestions + openPurchaseSuggestions}
|
||||
color="purple"
|
||||
icon="FaBullseye"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex bg-gray-100 rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => setActiveTab("requirements")}
|
||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||
activeTab === "requirements"
|
||||
? "bg-white text-gray-900 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">Malzeme İhtiyaçları</h2>
|
||||
<p className="text-sm text-gray-600">
|
||||
Malzeme ihtiyaç hesaplama ve satın alma önerileri
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setIsMrpModalOpen(true)}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||
>
|
||||
<FaBox className="w-4 h-4" />
|
||||
Malzeme İhtiyaçları ({totalRequirements})
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("production")}
|
||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||
activeTab === "production"
|
||||
? "bg-white text-gray-900 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<FaIndustry className="w-4 h-4" />
|
||||
Üretim Önerileri ({openProductionSuggestions})
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("purchase")}
|
||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||
activeTab === "purchase"
|
||||
? "bg-white text-gray-900 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<FaShoppingCart className="w-4 h-4" />
|
||||
Satınalma Önerileri ({openPurchaseSuggestions})
|
||||
</div>
|
||||
</button>
|
||||
MRP Çalıştır
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Widget title="Toplam İhtiyaç" value={totalRequirements} color="blue" icon="FaBox" />
|
||||
|
||||
<Widget title="Eksik Stok" value={shortageRequirements} color="red" icon="FaArrowDown" />
|
||||
|
||||
<Widget
|
||||
title="Acil İhtiyaç"
|
||||
value={urgentRequirements}
|
||||
color="orange"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Açık Öneri"
|
||||
value={openProductionSuggestions + openPurchaseSuggestions}
|
||||
color="purple"
|
||||
icon="FaBullseye"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex bg-gray-100 rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => setActiveTab('requirements')}
|
||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||
activeTab === 'requirements'
|
||||
? 'bg-white text-gray-900 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<FaBox className="w-4 h-4" />
|
||||
Malzeme İhtiyaçları ({totalRequirements})
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('production')}
|
||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||
activeTab === 'production'
|
||||
? 'bg-white text-gray-900 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<FaIndustry className="w-4 h-4" />
|
||||
Üretim Önerileri ({openProductionSuggestions})
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('purchase')}
|
||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||
activeTab === 'purchase'
|
||||
? 'bg-white text-gray-900 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<FaShoppingCart className="w-4 h-4" />
|
||||
Satınalma Önerileri ({openPurchaseSuggestions})
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Data Table */}
|
||||
{activeTab === 'requirements' && (
|
||||
<MaterialRequirements materialRequirements={materialRequirements} />
|
||||
)}
|
||||
|
||||
{activeTab === 'production' && (
|
||||
<ProductionSuggestions productionSuggestions={productionSuggestions} />
|
||||
)}
|
||||
{activeTab === 'purchase' && (
|
||||
<PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Data Table */}
|
||||
{activeTab === "requirements" && (
|
||||
<MaterialRequirements materialRequirements={materialRequirements} />
|
||||
)}
|
||||
|
||||
{activeTab === "production" && (
|
||||
<ProductionSuggestions productionSuggestions={productionSuggestions} />
|
||||
)}
|
||||
{activeTab === "purchase" && (
|
||||
<PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} />
|
||||
)}
|
||||
|
||||
<RunMrpModal
|
||||
isOpen={isMrpModalOpen}
|
||||
onClose={() => setIsMrpModalOpen(false)}
|
||||
onRunComplete={() => {
|
||||
runMrp();
|
||||
setIsMrpModalOpen(false);
|
||||
runMrp()
|
||||
setIsMrpModalOpen(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default Requirements;
|
||||
export default Requirements
|
||||
|
|
|
|||
|
|
@ -1,64 +1,52 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
FaCog,
|
||||
FaPlus,
|
||||
FaEdit,
|
||||
FaTrash,
|
||||
FaClock,
|
||||
FaCheckCircle,
|
||||
FaPlay,
|
||||
} from "react-icons/fa";
|
||||
import { MrpWorkOrder } from "../../../types/mrp";
|
||||
import DataTable, { Column } from "../../../components/common/DataTable";
|
||||
import { mockWorkOrders } from "../../../mocks/mockWorkOrders";
|
||||
import NewWorkOrderForm from "./NewWorkOrderForm";
|
||||
import EditWorkOrderForm from "./EditWorkOrderForm";
|
||||
import ViewWorkOrderModal from "./ViewWorkOrderModal";
|
||||
import CompleteWorkOrderModal from "./CompleteWorkOrderModal";
|
||||
import Widget from "../../../components/common/Widget";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
import React, { useState } from 'react'
|
||||
import { FaCog, FaPlus, FaEdit, FaTrash, FaClock, FaCheckCircle, FaPlay } from 'react-icons/fa'
|
||||
import { MrpWorkOrder } from '../../../types/mrp'
|
||||
import DataTable, { Column } from '../../../components/common/DataTable'
|
||||
import { mockWorkOrders } from '../../../mocks/mockWorkOrders'
|
||||
import NewWorkOrderForm from './NewWorkOrderForm'
|
||||
import EditWorkOrderForm from './EditWorkOrderForm'
|
||||
import ViewWorkOrderModal from './ViewWorkOrderModal'
|
||||
import CompleteWorkOrderModal from './CompleteWorkOrderModal'
|
||||
import Widget from '../../../components/common/Widget'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
import {
|
||||
getPriorityColor,
|
||||
getPriorityText,
|
||||
getWorkOrderStatusColor,
|
||||
getWorkOrderStatusText,
|
||||
} from "../../../utils/erp";
|
||||
import { WorkOrderStatusEnum } from "../../../types/pm";
|
||||
} from '../../../utils/erp'
|
||||
import { WorkOrderStatusEnum } from '../../../types/pm'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const WorkOrders: React.FC = () => {
|
||||
const [workOrders, setWorkOrders] = useState<MrpWorkOrder[]>(mockWorkOrders);
|
||||
const [workOrders, setWorkOrders] = useState<MrpWorkOrder[]>(mockWorkOrders)
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [selectedStatus, setSelectedStatus] = useState<
|
||||
WorkOrderStatusEnum | "all"
|
||||
>("all");
|
||||
const [selectedPriority, setSelectedPriority] = useState<
|
||||
PriorityEnum | "all"
|
||||
>("all");
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [selectedStatus, setSelectedStatus] = useState<WorkOrderStatusEnum | 'all'>('all')
|
||||
const [selectedPriority, setSelectedPriority] = useState<PriorityEnum | 'all'>('all')
|
||||
|
||||
// Modal states
|
||||
const [isNewWorkOrderOpen, setIsNewWorkOrderOpen] = useState(false);
|
||||
const [isEditWorkOrderOpen, setIsEditWorkOrderOpen] = useState(false);
|
||||
const [isViewWorkOrderOpen, setIsViewWorkOrderOpen] = useState(false);
|
||||
const [isCompleteWorkOrderOpen, setIsCompleteWorkOrderOpen] = useState(false);
|
||||
const [selectedWorkOrder, setSelectedWorkOrder] =
|
||||
useState<MrpWorkOrder | null>(null);
|
||||
const [isNewWorkOrderOpen, setIsNewWorkOrderOpen] = useState(false)
|
||||
const [isEditWorkOrderOpen, setIsEditWorkOrderOpen] = useState(false)
|
||||
const [isViewWorkOrderOpen, setIsViewWorkOrderOpen] = useState(false)
|
||||
const [isCompleteWorkOrderOpen, setIsCompleteWorkOrderOpen] = useState(false)
|
||||
const [selectedWorkOrder, setSelectedWorkOrder] = useState<MrpWorkOrder | null>(null)
|
||||
|
||||
// Event handlers
|
||||
const handleAdd = () => {
|
||||
setIsNewWorkOrderOpen(true);
|
||||
};
|
||||
setIsNewWorkOrderOpen(true)
|
||||
}
|
||||
|
||||
const handleEdit = (workOrder: MrpWorkOrder) => {
|
||||
setSelectedWorkOrder(workOrder);
|
||||
setIsEditWorkOrderOpen(true);
|
||||
};
|
||||
setSelectedWorkOrder(workOrder)
|
||||
setIsEditWorkOrderOpen(true)
|
||||
}
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
if (window.confirm("Bu iş emrini silmek istediğinizden emin misiniz?")) {
|
||||
setWorkOrders((prev) => prev.filter((wo) => wo.id !== id));
|
||||
if (window.confirm('Bu iş emrini silmek istediğinizden emin misiniz?')) {
|
||||
setWorkOrders((prev) => prev.filter((wo) => wo.id !== id))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleStart = (workOrder: MrpWorkOrder) => {
|
||||
setWorkOrders((prev) =>
|
||||
|
|
@ -69,57 +57,54 @@ const WorkOrders: React.FC = () => {
|
|||
status: WorkOrderStatusEnum.InProgress,
|
||||
lastModificationTime: new Date(),
|
||||
}
|
||||
: wo
|
||||
)
|
||||
);
|
||||
};
|
||||
: wo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const handleComplete = (workOrder: MrpWorkOrder) => {
|
||||
setSelectedWorkOrder(workOrder);
|
||||
setIsCompleteWorkOrderOpen(true);
|
||||
};
|
||||
setSelectedWorkOrder(workOrder)
|
||||
setIsCompleteWorkOrderOpen(true)
|
||||
}
|
||||
|
||||
const handleViewDetails = (workOrder: MrpWorkOrder) => {
|
||||
setSelectedWorkOrder(workOrder);
|
||||
setIsViewWorkOrderOpen(true);
|
||||
};
|
||||
setSelectedWorkOrder(workOrder)
|
||||
setIsViewWorkOrderOpen(true)
|
||||
}
|
||||
|
||||
// Modal handlers
|
||||
const handleNewWorkOrderSave = (
|
||||
newWorkOrderData: Omit<
|
||||
MrpWorkOrder,
|
||||
"id" | "creationTime" | "lastModificationTime"
|
||||
>
|
||||
newWorkOrderData: Omit<MrpWorkOrder, 'id' | 'creationTime' | 'lastModificationTime'>,
|
||||
) => {
|
||||
const newWorkOrder: MrpWorkOrder = {
|
||||
...newWorkOrderData,
|
||||
id: `WO-${Date.now()}`,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
setWorkOrders((prev) => [...prev, newWorkOrder]);
|
||||
};
|
||||
}
|
||||
setWorkOrders((prev) => [...prev, newWorkOrder])
|
||||
}
|
||||
|
||||
const handleEditWorkOrderSave = (updatedWorkOrder: MrpWorkOrder) => {
|
||||
setWorkOrders((prev) =>
|
||||
prev.map((wo) => (wo.id === updatedWorkOrder.id ? updatedWorkOrder : wo))
|
||||
);
|
||||
};
|
||||
prev.map((wo) => (wo.id === updatedWorkOrder.id ? updatedWorkOrder : wo)),
|
||||
)
|
||||
}
|
||||
|
||||
const handleCompleteWorkOrder = (
|
||||
workOrderId: string,
|
||||
confirmedQuantity: number,
|
||||
scrapQuantity: number
|
||||
scrapQuantity: number,
|
||||
) => {
|
||||
setWorkOrders((prev) =>
|
||||
prev.map((wo) => {
|
||||
if (wo.id === workOrderId) {
|
||||
const newConfirmedQuantity = wo.confirmedQuantity + confirmedQuantity;
|
||||
const newScrapQuantity = wo.scrapQuantity + scrapQuantity;
|
||||
const newConfirmedQuantity = wo.confirmedQuantity + confirmedQuantity
|
||||
const newScrapQuantity = wo.scrapQuantity + scrapQuantity
|
||||
const newStatus =
|
||||
newConfirmedQuantity + newScrapQuantity >= wo.plannedQuantity
|
||||
? WorkOrderStatusEnum.Completed
|
||||
: wo.status;
|
||||
: wo.status
|
||||
|
||||
return {
|
||||
...wo,
|
||||
|
|
@ -127,80 +112,69 @@ const WorkOrders: React.FC = () => {
|
|||
scrapQuantity: newScrapQuantity,
|
||||
status: newStatus,
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
}
|
||||
}
|
||||
return wo;
|
||||
})
|
||||
);
|
||||
};
|
||||
return wo
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const filteredWorkOrders = workOrders.filter((workOrder) => {
|
||||
if (
|
||||
searchTerm &&
|
||||
!workOrder.workOrderNumber
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()) &&
|
||||
!workOrder.productionOrder?.orderNumber
|
||||
?.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase())
|
||||
!workOrder.workOrderNumber.toLowerCase().includes(searchTerm.toLowerCase()) &&
|
||||
!workOrder.productionOrder?.orderNumber?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
if (selectedStatus !== "all" && workOrder.status !== selectedStatus) {
|
||||
return false;
|
||||
if (selectedStatus !== 'all' && workOrder.status !== selectedStatus) {
|
||||
return false
|
||||
}
|
||||
if (
|
||||
selectedPriority !== "all" &&
|
||||
workOrder.productionOrder?.priority !== selectedPriority
|
||||
) {
|
||||
return false;
|
||||
if (selectedPriority !== 'all' && workOrder.productionOrder?.priority !== selectedPriority) {
|
||||
return false
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return true
|
||||
})
|
||||
|
||||
const columns: Column<MrpWorkOrder>[] = [
|
||||
{
|
||||
key: "workOrderNumber",
|
||||
header: "İş Emri No",
|
||||
key: 'workOrderNumber',
|
||||
header: 'İş Emri No',
|
||||
sortable: true,
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">
|
||||
{workOrder.workOrderNumber}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
Sıra: {workOrder.sequence}
|
||||
</div>
|
||||
<div className="font-medium text-gray-900">{workOrder.workOrderNumber}</div>
|
||||
<div className="text-sm text-gray-500">Sıra: {workOrder.sequence}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "productionOrder",
|
||||
header: "Üretim Emri",
|
||||
key: 'productionOrder',
|
||||
header: 'Üretim Emri',
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">
|
||||
{workOrder.productionOrder?.orderNumber || "N/A"}
|
||||
{workOrder.productionOrder?.orderNumber || 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "material",
|
||||
header: "Malzeme",
|
||||
key: 'material',
|
||||
header: 'Malzeme',
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<div>
|
||||
<div className="text-gray-900">
|
||||
{workOrder.material?.code}
|
||||
{" - "}
|
||||
{' - '}
|
||||
{workOrder.material?.name || workOrder.materialId}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "workCenter",
|
||||
header: "İş Merkezi",
|
||||
key: 'workCenter',
|
||||
header: 'İş Merkezi',
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">
|
||||
|
|
@ -209,15 +183,13 @@ const WorkOrders: React.FC = () => {
|
|||
<div className="flex items-center gap-2">
|
||||
<span>{workOrder.operation?.name || workOrder.operationId}</span>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{workOrder.assignedOperators.length} Operatör
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">{workOrder.assignedOperators.length} Operatör</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "quantities",
|
||||
header: "Miktarlar",
|
||||
key: 'quantities',
|
||||
header: 'Miktarlar',
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<div className="text-sm">
|
||||
<div>Plan: {workOrder.plannedQuantity}</div>
|
||||
|
|
@ -229,50 +201,46 @@ const WorkOrders: React.FC = () => {
|
|||
),
|
||||
},
|
||||
{
|
||||
key: "schedule",
|
||||
header: "Planlama",
|
||||
key: 'schedule',
|
||||
header: 'Planlama',
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<div className="text-sm">
|
||||
<div className="flex items-center gap-1">
|
||||
<FaClock className="w-3 h-3 text-gray-400" />
|
||||
<span>
|
||||
Başlangıç:{" "}
|
||||
{new Date(workOrder.plannedStartDate).toLocaleDateString("tr-TR")}
|
||||
Başlangıç: {new Date(workOrder.plannedStartDate).toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<FaClock className="w-3 h-3 text-gray-400" />
|
||||
<span>
|
||||
Bitiş:{" "}
|
||||
{new Date(workOrder.plannedEndDate).toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
<span>Bitiş: {new Date(workOrder.plannedEndDate).toLocaleDateString('tr-TR')}</span>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "priority",
|
||||
header: "Öncelik",
|
||||
key: 'priority',
|
||||
header: 'Öncelik',
|
||||
render: (workOrder: MrpWorkOrder) =>
|
||||
workOrder.productionOrder?.priority ? (
|
||||
<span
|
||||
className={`px-2 py-1 text-xs font-medium rounded-full ${getPriorityColor(
|
||||
workOrder.productionOrder.priority
|
||||
workOrder.productionOrder.priority,
|
||||
)}`}
|
||||
>
|
||||
{getPriorityText(workOrder.productionOrder.priority)}
|
||||
</span>
|
||||
) : (
|
||||
"-"
|
||||
'-'
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
header: "Durum",
|
||||
key: 'status',
|
||||
header: 'Durum',
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<span
|
||||
className={`px-2 py-1 text-xs font-medium rounded-full ${getWorkOrderStatusColor(
|
||||
workOrder.status
|
||||
workOrder.status,
|
||||
)}`}
|
||||
>
|
||||
{getWorkOrderStatusText(workOrder.status)}
|
||||
|
|
@ -280,8 +248,8 @@ const WorkOrders: React.FC = () => {
|
|||
),
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
header: "İşlemler",
|
||||
key: 'actions',
|
||||
header: 'İşlemler',
|
||||
render: (workOrder: MrpWorkOrder) => (
|
||||
<div className="flex gap-1">
|
||||
{workOrder.status === WorkOrderStatusEnum.Released && (
|
||||
|
|
@ -326,167 +294,129 @@ const WorkOrders: React.FC = () => {
|
|||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
// Calculate statistics
|
||||
const totalWorkOrders = workOrders.length;
|
||||
const totalWorkOrders = workOrders.length
|
||||
const inProgressOrders = workOrders.filter(
|
||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress
|
||||
).length;
|
||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
|
||||
).length
|
||||
const completedOrders = workOrders.filter(
|
||||
(wo) => wo.status === WorkOrderStatusEnum.Completed
|
||||
).length;
|
||||
(wo) => wo.status === WorkOrderStatusEnum.Completed,
|
||||
).length
|
||||
const delayedOrders = workOrders.filter(
|
||||
(wo) =>
|
||||
wo.status !== WorkOrderStatusEnum.Completed &&
|
||||
new Date(wo.plannedEndDate) < new Date()
|
||||
).length;
|
||||
(wo) => wo.status !== WorkOrderStatusEnum.Completed && new Date(wo.plannedEndDate) < new Date(),
|
||||
).length
|
||||
|
||||
// Status distribution
|
||||
const statusDistribution = Object.values(WorkOrderStatusEnum).map(
|
||||
(status) => ({
|
||||
status,
|
||||
count: workOrders.filter((wo) => wo.status === status).length,
|
||||
percentage:
|
||||
(workOrders.filter((wo) => wo.status === status).length /
|
||||
workOrders.length) *
|
||||
100 || 0,
|
||||
})
|
||||
);
|
||||
const statusDistribution = Object.values(WorkOrderStatusEnum).map((status) => ({
|
||||
status,
|
||||
count: workOrders.filter((wo) => wo.status === status).length,
|
||||
percentage:
|
||||
(workOrders.filter((wo) => wo.status === status).length / workOrders.length) * 100 || 0,
|
||||
}))
|
||||
|
||||
return (
|
||||
<div className="space-y-3 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2>
|
||||
<p className="text-sm text-gray-600">
|
||||
Üretim operasyonlarının detaylı takibi
|
||||
</p>
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2>
|
||||
<p className="text-sm text-gray-600">Üretim operasyonlarının detaylı takibi</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAdd}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
Yeni İş Emri
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAdd}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
Yeni İş Emri
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<Widget
|
||||
title="Toplam İş Emri"
|
||||
value={totalWorkOrders}
|
||||
color="blue"
|
||||
icon="FaCog"
|
||||
/>
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<Widget title="Toplam İş Emri" value={totalWorkOrders} color="blue" icon="FaCog" />
|
||||
|
||||
<Widget
|
||||
title="İşlemde"
|
||||
value={inProgressOrders}
|
||||
color="yellow"
|
||||
icon="FaPlay"
|
||||
/>
|
||||
<Widget title="İşlemde" value={inProgressOrders} color="yellow" icon="FaPlay" />
|
||||
|
||||
<Widget
|
||||
title="Tamamlanan"
|
||||
value={completedOrders}
|
||||
color="green"
|
||||
icon="FaCheckCircle"
|
||||
/>
|
||||
<Widget title="Tamamlanan" value={completedOrders} color="green" icon="FaCheckCircle" />
|
||||
|
||||
<Widget
|
||||
title="Geciken"
|
||||
value={delayedOrders}
|
||||
color="red"
|
||||
icon="FaExclamationCircle"
|
||||
/>
|
||||
</div>
|
||||
<Widget title="Geciken" value={delayedOrders} color="red" icon="FaExclamationCircle" />
|
||||
</div>
|
||||
|
||||
{/* Status Distribution */}
|
||||
<div className="bg-white rounded-lg shadow-sm border p-3">
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">
|
||||
Durum Dağılımı
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
|
||||
{statusDistribution.map(({ status, count, percentage }) => (
|
||||
<div key={status} className="text-center p-2 border rounded-lg">
|
||||
<div
|
||||
className={`inline-block px-1.5 py-0.5 text-xs font-medium rounded-full mb-1 ${getWorkOrderStatusColor(
|
||||
status
|
||||
)}`}
|
||||
>
|
||||
{/* Status Distribution */}
|
||||
<div className="bg-white rounded-lg shadow-sm border p-3">
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">Durum Dağılımı</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
|
||||
{statusDistribution.map(({ status, count, percentage }) => (
|
||||
<div key={status} className="text-center p-2 border rounded-lg">
|
||||
<div
|
||||
className={`inline-block px-1.5 py-0.5 text-xs font-medium rounded-full mb-1 ${getWorkOrderStatusColor(
|
||||
status,
|
||||
)}`}
|
||||
>
|
||||
{getWorkOrderStatusText(status)}
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900">{count}</div>
|
||||
<div className="text-xs text-gray-500">{percentage.toFixed(1)}%</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex-1">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="İş emri numarası ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<select
|
||||
value={selectedStatus}
|
||||
onChange={(e) => setSelectedStatus(e.target.value as WorkOrderStatusEnum | 'all')}
|
||||
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
{Object.values(WorkOrderStatusEnum).map((status) => (
|
||||
<option key={status} value={status}>
|
||||
{getWorkOrderStatusText(status)}
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900">{count}</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
{percentage.toFixed(1)}%
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex-1">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="İş emri numarası ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<select
|
||||
value={selectedPriority}
|
||||
onChange={(e) => setSelectedPriority(e.target.value as PriorityEnum | 'all')}
|
||||
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Öncelikler</option>
|
||||
{Object.values(PriorityEnum).map((priority) => (
|
||||
<option key={priority} value={priority}>
|
||||
{getPriorityText(priority)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<select
|
||||
value={selectedStatus}
|
||||
onChange={(e) =>
|
||||
setSelectedStatus(e.target.value as WorkOrderStatusEnum | "all")
|
||||
}
|
||||
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
{Object.values(WorkOrderStatusEnum).map((status) => (
|
||||
<option key={status} value={status}>
|
||||
{getWorkOrderStatusText(status)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedPriority}
|
||||
onChange={(e) =>
|
||||
setSelectedPriority(e.target.value as PriorityEnum | "all")
|
||||
}
|
||||
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Öncelikler</option>
|
||||
{Object.values(PriorityEnum).map((priority) => (
|
||||
<option key={priority} value={priority}>
|
||||
{getPriorityText(priority)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Data Table */}
|
||||
<div className="bg-white rounded-lg shadow-sm border">
|
||||
<DataTable data={filteredWorkOrders} columns={columns} />
|
||||
</div>
|
||||
|
||||
{filteredWorkOrders.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">
|
||||
İş emri bulunamadı
|
||||
</h3>
|
||||
<p className="text-gray-500">
|
||||
Arama kriterlerinizi değiştirmeyi deneyin.
|
||||
</p>
|
||||
{/* Data Table */}
|
||||
<div className="bg-white rounded-lg shadow-sm border">
|
||||
<DataTable data={filteredWorkOrders} columns={columns} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{filteredWorkOrders.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">İş emri bulunamadı</h3>
|
||||
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modals */}
|
||||
<NewWorkOrderForm
|
||||
|
|
@ -514,8 +444,8 @@ const WorkOrders: React.FC = () => {
|
|||
onConfirm={handleCompleteWorkOrder}
|
||||
workOrder={selectedWorkOrder}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkOrders;
|
||||
export default WorkOrders
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const ProjectManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (pathname === "/admin/projects" || pathname === "/admin/projects/") return "Proje Listesi";
|
||||
if (pathname === "/admin/projects/tasks") return "Görev Yönetimi";
|
||||
if (pathname === "/admin/projects/phases") return "Proje Aşamaları";
|
||||
if (pathname === "/admin/projects/activities") return "Aktivite Türleri";
|
||||
if (pathname === "/admin/projects/cost-tracking") return "Maliyet Zaman Takibi";
|
||||
if (pathname === "/admin/projects/workload") return "İş Yükü Gantt";
|
||||
if (pathname === "/admin/projects/daily-updates") return "Görev Günlük Güncellemeler";
|
||||
if (pathname === "/admin/projects/new") return "Yeni Proje";
|
||||
if (pathname.includes("/admin/projects/edit/")) return "Proje Düzenle";
|
||||
if (pathname.includes("/admin/projects/") && !pathname.includes("edit") && !pathname.includes("new")) return "Proje Detayları";
|
||||
return "Proje Yönetimi";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / Proje Yönetimi / Sayfanın Adı
|
||||
const breadcrumbs = [
|
||||
{ name: "Proje Yönetimi", href: "/admin/projects" }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader
|
||||
title={pageTitle}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectManagement;
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const SupplyChainManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (pathname === "/admin/supplychain" || pathname === "/admin/supplychain/suppliers")
|
||||
return "Tedarikçiler";
|
||||
if (pathname === "/admin/supplychain/materials/groups") return "Malzeme Grupları";
|
||||
if (pathname === "/admin/supplychain/materials/types") return "Malzeme Tipleri";
|
||||
if (pathname === "/admin/supplychain/materials") return "Malzeme Listesi";
|
||||
if (pathname === "/admin/supplychain/requests") return "Satınalma Talepleri";
|
||||
if (pathname === "/admin/supplychain/requests/new")
|
||||
return "Yeni Satınalma Talebi";
|
||||
if (pathname.includes("/admin/supplychain/requests/edit/"))
|
||||
return "Satınalma Talebi Düzenle";
|
||||
if (pathname.includes("/admin/supplychain/requests/view/"))
|
||||
return "Satınalma Talebi Detayları";
|
||||
if (pathname === "/admin/supplychain/quotations") return "Teklif Yönetimi";
|
||||
if (pathname === "/admin/supplychain/quotations/new") return "Yeni Teklif";
|
||||
if (pathname.includes("/admin/supplychain/quotations/edit/"))
|
||||
return "Teklif Düzenle";
|
||||
if (pathname.includes("/admin/supplychain/quotations/view/"))
|
||||
return "Teklif Detayları";
|
||||
if (pathname === "/admin/supplychain/approvals") return "Onay İş Akışları";
|
||||
if (pathname === "/admin/supplychain/orders") return "Sipariş Yönetimi";
|
||||
if (pathname === "/admin/supplychain/orders/new") return "Yeni Sipariş";
|
||||
if (pathname.includes("/admin/supplychain/orders/edit/"))
|
||||
return "Sipariş Düzenle";
|
||||
if (pathname.includes("/admin/supplychain/orders/view/"))
|
||||
return "Sipariş Detayları";
|
||||
if (pathname === "/admin/supplychain/delivery") return "Teslimat Takibi";
|
||||
return "Satınalma Yönetimi";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / Satınalma Yönetimi / Sayfanın Adı
|
||||
const breadcrumbs = [{ name: "Satınalma Yönetimi", href: "/admin/supplychain" }];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader title={pageTitle} breadcrumbs={breadcrumbs} />
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplyChainManagement;
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import React from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
||||
|
||||
const WarehouseManagement: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
// Define page mappings for breadcrumbs
|
||||
const getPageTitle = (pathname: string) => {
|
||||
if (
|
||||
pathname === "/admin/warehouse" ||
|
||||
pathname === "/admin/warehouse/" ||
|
||||
pathname === "/admin/warehouse/definitions"
|
||||
)
|
||||
return "Depo Tanımları";
|
||||
if (pathname === "/admin/warehouse/tracking") return "Lokasyon Takibi";
|
||||
if (pathname === "/admin/warehouse/putaway") return "Yerleştirme Kuralları";
|
||||
if (pathname === "/admin/warehouse/inventory") return "Stok Durumu";
|
||||
if (pathname === "/admin/warehouse/receipt") return "Stok Giriş";
|
||||
if (pathname === "/admin/warehouse/issue") return "Stok Çıkış";
|
||||
if (pathname === "/admin/warehouse/transfer") return "Stok Transfer";
|
||||
if (pathname === "/admin/warehouse/movements") return "Stok Hareketleri";
|
||||
if (pathname === "/admin/warehouse/stocklevel") return "Envanter Takibi";
|
||||
return "Depo Yönetimi";
|
||||
};
|
||||
|
||||
const pageTitle = getPageTitle(location.pathname);
|
||||
|
||||
// Create breadcrumbs: Anasayfa / Depo Yönetimi / Sayfanın Adı
|
||||
const breadcrumbs = [{ name: "Depo Yönetimi", href: "/admin/warehouse" }];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<ModuleHeader title={pageTitle} breadcrumbs={breadcrumbs} />
|
||||
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WarehouseManagement;
|
||||
Loading…
Reference in a new issue