Container güncellemesi

This commit is contained in:
Sedat Öztürk 2025-09-15 22:22:43 +03:00
parent 7d52573765
commit 4e5322ba0c
49 changed files with 4049 additions and 5079 deletions

View file

@ -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"), {

View file

@ -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;

View file

@ -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;

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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;

View file

@ -20,7 +20,6 @@ import {
getActivityStatusColor,
getActivityStatusText,
getActivityTypeIcon,
getPsActivityTypeText,
getPriorityColor,
getPriorityText,
getActivityTypeText,

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -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 */}

View file

@ -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>
)
}

View file

@ -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>
)
}

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -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">

View file

@ -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">

View file

@ -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">

View file

@ -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>

View file

@ -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;

View file

@ -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

View file

@ -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>

View file

@ -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 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 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>
)
}

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;