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" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "/index.html", "url": "/index.html",
"revision": "0.sbo84r6raa" "revision": "0.daul0044rt"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), { 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 { PmWorkCenter, WorkOrderStatusEnum } from "./pm";
import { import {
MmMaterial, MmMaterial,
@ -7,6 +6,7 @@ import {
QualityStatusEnum, QualityStatusEnum,
} from "./mm"; } from "./mm";
import { PriorityEnum } from "./common"; import { PriorityEnum } from "./common";
import { CrmSalesOrder } from "./crm";
export interface MrpProductionOrder { export interface MrpProductionOrder {
// Üretim Emri // Üretim Emri
@ -196,7 +196,7 @@ export interface MrpDemandForecast {
notes: string; notes: string;
} }
export interface MrpPurchaseSuggestionn extends MrpRecommendation { export interface MrpPurchaseSuggestion extends MrpRecommendation {
// Satınalma Tavsiyesi // Satınalma Tavsiyesi
supplierId: string; supplierId: string;
supplier?: MmMaterialSupplier; 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 ( return (
<div className="space-y-3 py-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -336,7 +336,7 @@ const CashManagement: React.FC<CashManagementProps> = ({
.reduce((sum, m) => sum + m.amount, 0); .reduce((sum, m) => sum + m.amount, 0);
return ( return (
<div className="space-y-3 py-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -662,7 +662,7 @@ const CheckNoteManagement: React.FC<CheckNoteManagementProps> = ({
}) })
return ( return (
<div className="space-y-3 py-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -20,8 +20,12 @@ import CurrentAccountDetails from "./CurrentAccountDetails";
import CurrentAccountMovementForm from "./CurrentAccountMovementForm"; import CurrentAccountMovementForm from "./CurrentAccountMovementForm";
import Widget from "../../../components/common/Widget"; import Widget from "../../../components/common/Widget";
import { import {
getAccountTypeColor,
getAccountTypeText,
getFiDocumentTypeColor, getFiDocumentTypeColor,
getFiDocumentTypeText, getFiDocumentTypeText,
getRiskGroupColor,
getRiskGroupText,
} from "../../../utils/erp"; } from "../../../utils/erp";
interface CurrentAccountManagementProps { 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 formatBalance = (balance: number) => {
const isDebit = balance > 0; const isDebit = balance > 0;
const absBalance = Math.abs(balance); const absBalance = Math.abs(balance);
@ -438,7 +402,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
account.type account.type
)}`} )}`}
> >
{getAccountTypeLabel(account.type)} {getAccountTypeText(account.type)}
</span> </span>
), ),
}, },
@ -486,7 +450,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
account.riskGroup account.riskGroup
)}`} )}`}
> >
{getRiskGroupLabel(account.riskGroup)} {getRiskGroupText(account.riskGroup)}
</span> </span>
), ),
}, },
@ -566,7 +530,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
); );
return ( return (
<div className="space-y-3 py-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
@ -682,7 +646,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
<option value="all">Tüm Türler</option> <option value="all">Tüm Türler</option>
{Object.values(AccountTypeEnum).map((type) => ( {Object.values(AccountTypeEnum).map((type) => (
<option key={type} value={type}> <option key={type} value={type}>
{getAccountTypeLabel(type)} {getAccountTypeText(type)}
</option> </option>
))} ))}
</select> </select>
@ -699,7 +663,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
<option value="all">Tüm Risk Grupları</option> <option value="all">Tüm Risk Grupları</option>
{Object.values(RiskGroupEnum).map((risk) => ( {Object.values(RiskGroupEnum).map((risk) => (
<option key={risk} value={risk}> <option key={risk} value={risk}>
{getRiskGroupLabel(risk)} {getRiskGroupText(risk)}
</option> </option>
))} ))}
</select> </select>

View file

@ -311,7 +311,7 @@ const InvoiceManagement: React.FC<InvoiceManagementProps> = ({
})); }));
return ( return (
<div className="space-y-3 py-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -310,7 +310,7 @@ const WaybillManagement: React.FC<WaybillManagementProps> = ({
})); }));
return ( return (
<div className="space-y-3 py-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <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, getActivityStatusColor,
getActivityStatusText, getActivityStatusText,
getActivityTypeIcon, getActivityTypeIcon,
getPsActivityTypeText,
getPriorityColor, getPriorityColor,
getPriorityText, getPriorityText,
getActivityTypeText, getActivityTypeText,

View file

@ -253,7 +253,7 @@ const ActivityRecords: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -113,7 +113,7 @@ const CustomerCards: React.FC = () => {
} }
return ( return (
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header Actions */} {/* Header Actions */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">

View file

@ -166,7 +166,7 @@ const CustomerEdit: React.FC = () => {
return ( return (
<Container> <Container>
<div className="min-h-screen bg-gray-50"> <div className="space-y-2">
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
{/* Header */} {/* Header */}
<div className="bg-white border-b border-gray-200"> <div className="bg-white border-b border-gray-200">
@ -196,8 +196,8 @@ const CustomerEdit: React.FC = () => {
</button> </button>
<div className="flex items-center space-x-4"> <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"> <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-6 h-6" /> <FaBuilding className="w-8 h-8" />
</div> </div>
<div> <div>

View file

@ -123,7 +123,7 @@ const CustomerForm: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -121,7 +121,7 @@ const CustomerForm: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -82,7 +82,7 @@ const CustomerList: React.FC = () => {
} }
return ( return (
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header Actions */} {/* Header Actions */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">

View file

@ -12,7 +12,7 @@ const CustomerListWithToggle: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header with View Toggle */} {/* Header with View Toggle */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div> <div>

View file

@ -128,7 +128,7 @@ const CustomerView: React.FC = () => {
return ( return (
<Container> <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="bg-white border-b border-gray-200">
<div className="mx-auto px-4 py-3"> <div className="mx-auto px-4 py-3">
{/* Breadcrumb */} {/* Breadcrumb */}

View file

@ -221,7 +221,7 @@ const LossReasons: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-3 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
@ -391,79 +391,79 @@ const LossReasons: React.FC = () => {
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p> <p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
</div> </div>
)} )}
</div>
{/* Modals */} {/* Modals */}
<LossReasonModal <LossReasonModal
isOpen={isModalOpen} isOpen={isModalOpen}
onClose={() => { onClose={() => {
setIsModalOpen(false) setIsModalOpen(false)
setEditingReason(null) setEditingReason(null)
}} }}
onSave={handleSave} onSave={handleSave}
editingReason={editingReason} editingReason={editingReason}
mode={modalMode} mode={modalMode}
/> />
{/* Delete Confirmation Modal */} {/* Delete Confirmation Modal */}
{isDeleteModalOpen && ( {isDeleteModalOpen && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> <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"> <div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between p-3 border-b"> <div className="flex items-center justify-between p-3 border-b">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<FaExclamationTriangle className="w-6 h-6 text-red-600" /> <FaExclamationTriangle className="w-6 h-6 text-red-600" />
<h3 className="text-sm font-semibold text-gray-900">Silme Onayı</h3> <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> </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 <button
onClick={() => { onClick={() => {
setIsDeleteModalOpen(false) setIsDeleteModalOpen(false)
setReasonToDelete(null) 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> </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> </div>
)} </div>
</div> )}
</Container> </Container>
) )
} }

View file

@ -237,7 +237,7 @@ const OpportunityManagement: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-3 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
@ -384,23 +384,23 @@ const OpportunityManagement: React.FC = () => {
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p> <p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
</div> </div>
)} )}
{/* Modals */}
<OpportunityForm
isOpen={isFormOpen}
onClose={handleCloseForm}
onSave={handleSaveOpportunity}
opportunity={selectedOpportunity}
mode={formMode}
/>
<OpportunityDetails
isOpen={isDetailsOpen}
onClose={handleCloseDetails}
onEdit={handleEditFromDetails}
opportunity={selectedOpportunity}
/>
</div> </div>
{/* Modals */}
<OpportunityForm
isOpen={isFormOpen}
onClose={handleCloseForm}
onSave={handleSaveOpportunity}
opportunity={selectedOpportunity}
mode={formMode}
/>
<OpportunityDetails
isOpen={isDetailsOpen}
onClose={handleCloseDetails}
onEdit={handleEditFromDetails}
opportunity={selectedOpportunity}
/>
</Container> </Container>
) )
} }

View file

@ -246,7 +246,7 @@ const SalesOrderForm: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-6 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -73,7 +73,7 @@ const SalesOrderView: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4"> <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">

View file

@ -203,7 +203,7 @@ const SalesOrders: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-3 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -133,7 +133,7 @@ const SalesTeamCreate: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">

View file

@ -181,7 +181,7 @@ const SalesTeamEdit: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">

View file

@ -72,7 +72,7 @@ const SalesTeamView: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-6 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">

View file

@ -201,7 +201,7 @@ const SalesTeams: React.FC = () => {
return ( return (
<Container> <Container>
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <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 ( return (
<div className="space-y-3 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -27,6 +27,7 @@ import {
getCriticalityLevelColor, getCriticalityLevelColor,
getCriticalityLevelText, getCriticalityLevelText,
} from '../../../utils/erp' } from '../../../utils/erp'
import { Container } from '@/components/shared'
const WorkCenterList: React.FC = () => { const WorkCenterList: React.FC = () => {
const [searchTerm, setSearchTerm] = useState('') const [searchTerm, setSearchTerm] = useState('')
@ -75,330 +76,332 @@ const WorkCenterList: React.FC = () => {
} }
return ( return (
<div className="space-y-3 pt-2"> <Container>
{/* Header Actions */} <div className="space-y-2">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"> {/* Header Actions */}
<div className="flex items-center space-x-3"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div className="relative"> <div className="flex items-center space-x-3">
<FaSearch <div className="relative">
size={20} <FaSearch
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={20}
/> className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
<input />
type="text" <input
placeholder="İş merkezi kodu veya adı..." type="text"
value={searchTerm} placeholder="İş merkezi kodu veya adı..."
onChange={(e) => setSearchTerm(e.target.value)} value={searchTerm}
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" 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> </div>
<button <div className="flex items-center space-x-2">
onClick={() => setShowFilters(!showFilters)} <button
className={classNames( onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
'flex items-center px-4 py-2 border rounded-lg transition-colors', className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
showFilters >
? 'border-blue-500 bg-blue-50 text-blue-700' <FaDownload size={16} className="mr-2" />
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50', Dışa Aktar
)} </button>
>
<FaFilter size={16} className="mr-2" />
Filtreler
</button>
</div>
<div className="flex items-center space-x-2"> <Link
<button to="/admin/maintenance/equipment/new"
onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')} className="flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors" >
> <FaPlus size={16} className="mr-2" />
<FaDownload size={16} className="mr-2" /> Yeni İş Merkezi
Dışa Aktar </Link>
</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" />
</div> </div>
</div> </div>
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3"> {/* Filters Panel */}
<div className="flex items-center justify-between"> {showFilters && (
<div> <div className="bg-white border border-gray-200 rounded-lg p-3">
<p className="text-sm font-medium text-gray-600">Operasyonel</p> <div className="grid grid-cols-1 md:grid-cols-4 gap-3">
<p className="text-xl font-bold text-green-600"> <div>
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational).length || <label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
0} <select
</p> value={filterStatus}
</div> onChange={(e) => setFilterStatus(e.target.value)}
<FaCheckCircle className="h-8 w-8 text-green-600" /> 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"
</div> >
</div> <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>
<div className="flex items-center justify-between"> <label className="block text-sm font-medium text-gray-700 mb-2">Kritiklik</label>
<div> <select
<p className="text-sm font-medium text-gray-600">Bakımda</p> value={filterCriticality}
<p className="text-xl font-bold text-yellow-600"> onChange={(e) => setFilterCriticality(e.target.value)}
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.UnderMaintenance) 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"
.length || 0} >
</p> <option value="all">Tümü</option>
</div> <option value={CriticalityLevelEnum.Low}>Düşük</option>
<FaWrench className="h-8 w-8 text-yellow-600" /> <option value={CriticalityLevelEnum.Medium}>Orta</option>
</div> <option value={CriticalityLevelEnum.High}>Yüksek</option>
</div> <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-end">
<div className="flex items-center justify-between"> <button
<div> onClick={() => {
<p className="text-sm font-medium text-gray-600">Kritik İş Merkezi</p> setFilterStatus('all')
<p className="text-xl font-bold text-red-600"> setFilterCriticality('all')
{workCenter?.filter((e) => e.criticality === CriticalityLevelEnum.Critical) setSearchTerm('')
.length || 0} }}
</p> 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"
</div> >
<FaExclamationTriangle className="h-8 w-8 text-red-600" /> Filtreleri Temizle
</div> </button>
</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>
)} )}
{/* 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>
</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 { import {
FaPlus, FaPlus,
FaSearch, FaSearch,
@ -10,347 +10,325 @@ import {
FaCopy, FaCopy,
FaExclamationTriangle, FaExclamationTriangle,
FaClock, FaClock,
} from "react-icons/fa"; } from 'react-icons/fa'
import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from "../../../types/mrp"; import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from '../../../types/mrp'
import BOMFormModal from "./BOMFormModal"; import BOMFormModal from './BOMFormModal'
import { getBOMTypeColor, getBOMTypeName } from "../../../utils/erp"; import { getBOMTypeColor, getBOMTypeName } from '../../../utils/erp'
import { mockBOMs } from "../../../mocks/mockBOMs"; import { mockBOMs } from '../../../mocks/mockBOMs'
import { Container } from '@/components/shared'
const BOMManagement: React.FC = () => { const BOMManagement: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false)
const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null); const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null)
const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null); const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null)
// Mock data - replace with actual API calls // Mock data - replace with actual API calls
const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs); const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs)
const filteredBOMs = boms.filter( const filteredBOMs = boms.filter(
(bom) => (bom) =>
bom.bomCode.toLowerCase().includes(searchTerm.toLowerCase()) || bom.bomCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
bom.materialId.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[]) => { const getTotalOperationTime = (operations: MrpBOMOperation[]) => {
return operations.reduce( return operations.reduce((total, op) => total + op.setupTime + op.runTime, 0)
(total, op) => total + op.setupTime + op.runTime, }
0
);
};
const getTotalComponents = (components: MrpBOMComponent[]) => { const getTotalComponents = (components: MrpBOMComponent[]) => {
return components.length; return components.length
}; }
const getActiveComponents = (components: MrpBOMComponent[]) => { const getActiveComponents = (components: MrpBOMComponent[]) => {
return components.filter((comp) => comp.isActive).length; return components.filter((comp) => comp.isActive).length
}; }
const handleEdit = (bom: MrpBOM) => { const handleEdit = (bom: MrpBOM) => {
setEditingBOM(bom); setEditingBOM(bom)
setShowModal(true); setShowModal(true)
}; }
const handleAddNew = () => { const handleAddNew = () => {
setEditingBOM(null); setEditingBOM(null)
setShowModal(true); setShowModal(true)
}; }
const handleViewDetails = (bom: MrpBOM) => { const handleViewDetails = (bom: MrpBOM) => {
setSelectedBOM(bom); setSelectedBOM(bom)
console.log(bom); console.log(bom)
}; }
const handleCopy = (bom: MrpBOM) => { const handleCopy = (bom: MrpBOM) => {
console.log("Copying BOM:", bom.bomCode); console.log('Copying BOM:', bom.bomCode)
// Implementation for copying BOM // Implementation for copying BOM
}; }
const handleSave = (bom: MrpBOM) => { const handleSave = (bom: MrpBOM) => {
setBoms((prev) => { setBoms((prev) => {
const existing = prev.find((b) => b.id === bom.id); const existing = prev.find((b) => b.id === bom.id)
if (existing) return prev.map((b) => (b.id === bom.id ? bom : b)); if (existing) return prev.map((b) => (b.id === bom.id ? bom : b))
return [...prev, { ...bom, id: bom.id || String(Date.now()) }]; return [...prev, { ...bom, id: bom.id || String(Date.now()) }]
}); })
setShowModal(false); setShowModal(false)
}; }
return ( return (
<div className="space-y-3 pt-2"> <Container>
{/* Header */} <div className="space-y-2">
<div className="flex items-center justify-between"> {/* Header */}
<div> <div className="flex items-center justify-between">
<h2 className="text-xl font-bold text-gray-900"> <div>
Ürün Ağaçları (BOM) <h2 className="text-xl font-bold text-gray-900">Ürün Ağaçları (BOM)</h2>
</h2> <p className="text-sm text-gray-600 mt-0.5">
<p className="text-sm text-gray-600 mt-0.5"> Ürün bileşenlerini ve üretim operasyonlarını yönetin
Ürün bileşenlerini ve üretim operasyonlarını yönetin </p>
</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> </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 */} {/* Search Bar */}
<div className="relative"> <div className="relative">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<input <input
type="text" type="text"
placeholder="BOM ara..." placeholder="BOM ara..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} 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" 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>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* BOM List */} {/* BOM List */}
<div className="space-y-3 pt-2"> <div className="space-y-3 pt-2">
<h3 className="text-lg font-semibold text-gray-900">BOM Listesi</h3> <h3 className="text-lg font-semibold text-gray-900">BOM Listesi</h3>
{filteredBOMs.map((bom) => ( {filteredBOMs.map((bom) => (
<div <div
key={bom.id} key={bom.id}
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow" 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 items-start justify-between mb-2">
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center space-x-2 mb-2"> <div className="flex items-center space-x-2 mb-2">
<FaCodeBranch className="w-4 h-4 text-gray-600" /> <FaCodeBranch className="w-4 h-4 text-gray-600" />
<h4 className="text-lg font-semibold text-gray-900"> <h4 className="text-lg font-semibold text-gray-900">{bom.bomCode}</h4>
{bom.bomCode} <span
</h4> 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 <span
className={`px-2 py-1 rounded-full text-xs font-medium ${getBOMTypeColor( className={`px-2 py-1 rounded-full text-xs font-medium ${
bom.bomType bom.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
)}`} }`}
> >
{getBOMTypeName(bom.bomType)} {bom.isActive ? 'Aktif' : 'Pasif'}
</span> </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> </div>
<p className="text-sm text-gray-600 mb-0.5"> <span className="text-xs text-gray-400">
Malzeme: {bom.material?.code} - {bom.material?.name} - v {bom.lastModificationTime.toLocaleDateString('tr-TR')}
{bom.version} </span>
</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> </div>
))}
<div className="grid grid-cols-2 gap-3 text-sm mb-2"> {filteredBOMs.length === 0 && (
<div className="flex items-center justify-between"> <div className="text-center py-8">
<span className="text-gray-500 flex items-center"> <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" /> <FaBox className="w-4 h-4 mr-1" />
Bileşenler Bileşenler ({selectedBOM.components.length})
</span> </h5>
<span className="font-medium"> <div className="space-y-2">
{getActiveComponents(bom.components)}/ {selectedBOM.components.map((component) => (
{getTotalComponents(bom.components)} <div
</span> key={component.id}
</div> className="border border-gray-200 rounded p-1.5 text-sm"
<div className="flex items-center justify-between"> >
<span className="text-gray-500 flex items-center"> <div className="flex items-center justify-between mb-1">
<FaClock className="w-4 h-4 mr-1" /> <span className="font-medium">
Toplam Süre {component.position}. {component.material?.code} -{' '}
</span> {component.material?.name}
<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"}
</span> </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> </div>
<div className="text-gray-600"> ))}
Miktar: {component.quantity} {component.unitId} | Fire: </div>
%{component.scrapPercentage}
</div>
</div>
))}
</div> </div>
</div>
{/* Operations */} {/* Operations */}
<div> <div>
<h5 className="font-medium text-gray-900 mb-2 flex items-center"> <h5 className="font-medium text-gray-900 mb-2 flex items-center">
<FaCodeBranch className="w-4 h-4 mr-1" /> <FaCodeBranch className="w-4 h-4 mr-1" />
Operasyonlar ({selectedBOM.operations.length}) Operasyonlar ({selectedBOM.operations.length})
</h5> </h5>
<div className="space-y-2"> <div className="space-y-2">
{selectedBOM.operations.map((operation) => ( {selectedBOM.operations.map((operation) => (
<div <div
key={operation.id} key={operation.id}
className="border border-gray-200 rounded p-2 text-sm" className="border border-gray-200 rounded p-2 text-sm"
> >
<div className="flex items-center justify-between mb-1"> <div className="flex items-center justify-between mb-1">
<span className="font-medium"> <span className="font-medium">
{operation.sequence}. {operation.operation?.code} -{" "} {operation.sequence}. {operation.operation?.code} -{' '}
{operation.operation?.name} {operation.operation?.name}
</span> </span>
<span <span
className={`text-xs px-1 rounded ${ className={`text-xs px-1 rounded ${
operation.isActive operation.isActive
? "bg-green-100 text-green-800" ? 'bg-green-100 text-green-800'
: "bg-red-100 text-red-800" : 'bg-red-100 text-red-800'
}`} }`}
> >
{operation.isActive ? "Aktif" : "Pasif"} {operation.isActive ? 'Aktif' : 'Pasif'}
</span> </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>
<div className="text-gray-600"> ))}
Hazırlık: {operation.setupTime}dk | İşlem:{" "} </div>
{operation.runTime}dk | Kuyruk: {operation.queueTime}dk
| Taşıma: {operation.moveTime}dk
</div>
</div>
))}
</div> </div>
</div> </div>
</div> ) : (
) : ( <div className="bg-gray-50 rounded-lg p-6 text-center">
<div className="bg-gray-50 rounded-lg p-6 text-center"> <FaBox className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<FaBox className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <h3 className="text-lg font-medium text-gray-900 mb-2">
<h3 className="text-lg font-medium text-gray-900 mb-2"> BOM Detaylarını Görüntüle
BOM Detaylarını Görüntüle </h3>
</h3> <p className="text-gray-500">Detaylarını görmek için sol taraftan bir BOM seçin.</p>
<p className="text-gray-500"> </div>
Detaylarını görmek için sol taraftan bir BOM seçin. )}
</p> </div>
</div>
)}
</div> </div>
</div> </div>
@ -360,8 +338,8 @@ const BOMManagement: React.FC = () => {
onSave={handleSave} onSave={handleSave}
onClose={() => setShowModal(false)} 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 { import {
FaPlus, FaPlus,
FaSearch, FaSearch,
@ -11,402 +11,363 @@ import {
FaWrench, FaWrench,
FaTh, FaTh,
FaList, FaList,
} from "react-icons/fa"; } from 'react-icons/fa'
import { MrpOperation } from "../../../types/mrp"; import { MrpOperation } from '../../../types/mrp'
import OperationFormModal from "./OperationFormModal"; import OperationFormModal from './OperationFormModal'
import { mockOperations } from "../../../mocks/mockOperations"; import { mockOperations } from '../../../mocks/mockOperations'
import { Container } from '@/components/shared'
const OperationDefinitions: React.FC = () => { const OperationDefinitions: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false)
const [editingOperation, setEditingOperation] = useState<MrpOperation | null>( const [editingOperation, setEditingOperation] = useState<MrpOperation | null>(null)
null const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
);
const [viewMode, setViewMode] = useState<"card" | "list">("card");
const getTotalCost = (operation: MrpOperation) => { 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 // Mock data - replace with actual API calls
const [operations, setOperations] = useState<MrpOperation[]>(mockOperations); const [operations, setOperations] = useState<MrpOperation[]>(mockOperations)
const filteredOperations = operations.filter( const filteredOperations = operations.filter(
(operation) => (operation) =>
operation.name.toLowerCase().includes(searchTerm.toLowerCase()) || operation.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
operation.code.toLowerCase().includes(searchTerm.toLowerCase()) || operation.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
operation.workCenter?.name operation.workCenter?.name.toLowerCase().includes(searchTerm.toLowerCase()),
.toLowerCase() )
.includes(searchTerm.toLowerCase())
);
const handleEdit = (operation: MrpOperation) => { const handleEdit = (operation: MrpOperation) => {
setEditingOperation(operation); setEditingOperation(operation)
setShowModal(true); setShowModal(true)
}; }
const handleAddNew = () => { const handleAddNew = () => {
setEditingOperation(null); setEditingOperation(null)
setShowModal(true); setShowModal(true)
}; }
const handleSave = (op: MrpOperation) => { const handleSave = (op: MrpOperation) => {
setOperations((prev) => { setOperations((prev) => {
const existing = prev.find((p) => p.id === op.id); const existing = prev.find((p) => p.id === op.id)
if (existing) return prev.map((p) => (p.id === op.id ? op : p)); if (existing) return prev.map((p) => (p.id === op.id ? op : p))
return [...prev, { ...op, id: op.id || String(Date.now()) }]; return [...prev, { ...op, id: op.id || String(Date.now()) }]
}); })
setShowModal(false); setShowModal(false)
}; }
return ( return (
<div className="space-y-3 pt-2"> <Container>
{/* Header */} <div className="space-y-2">
<div className="flex items-center justify-between"> {/* Header */}
<div> <div className="flex items-center justify-between">
<h2 className="text-xl font-bold text-gray-900"> <div>
Operasyon Tanımları <h2 className="text-xl font-bold text-gray-900">Operasyon Tanımları</h2>
</h2> <p className="text-sm text-gray-600 mt-0.5">
<p className="text-sm text-gray-600 mt-0.5"> İş merkezlerinde gerçekleştirilen operasyonları tanımlayın
İş merkezlerinde gerçekleştirilen operasyonları tanımlayın </p>
</p> </div>
</div> <div className="flex items-center space-x-2">
<div className="flex items-center space-x-2"> {/* View Toggle */}
{/* View Toggle */} <div className="flex bg-gray-100 rounded-lg p-0.5">
<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 <button
onClick={() => setViewMode("card")} onClick={handleAddNew}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${ 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"
viewMode === "card"
? "bg-white text-blue-600 shadow-sm"
: "text-gray-600 hover:text-gray-900"
}`}
> >
<FaTh className="w-4 h-4" /> <FaPlus className="w-4 h-4" />
</button> <span>Yeni Operasyon</span>
<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> </button>
</div> </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>
</div>
{/* Search Bar */} {/* Search Bar */}
<div className="relative"> <div className="relative">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<input <input
type="text" type="text"
placeholder="Operasyon ara..." placeholder="Operasyon ara..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} 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" 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>
))}
</div> </div>
) : (
/* List View */ {/* Operations Display */}
<div className="bg-white shadow rounded-lg overflow-hidden"> {viewMode === 'card' ? (
<table className="min-w-full divide-y divide-gray-200"> /* Card View */
<thead className="bg-gray-50"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<tr> {filteredOperations.map((operation) => (
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <div
Operasyon key={operation.id}
</th> className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> >
İş Merkezi <div className="flex items-start justify-between mb-2">
</th> <div className="flex-1">
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <div className="flex items-center space-x-2 mb-1">
Tür <h3 className="text-lg font-semibold text-gray-900">{operation.name}</h3>
</th> <span className="px-2 py-1 rounded-full text-xs font-medium">
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> {operation.operationType?.code}
Süre (dk) </span>
</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> </div>
</td> <p className="text-sm text-gray-600 mb-1">{operation.code}</p>
<td className="px-2 py-1.5 whitespace-nowrap"> {operation.description && (
<div className="text-sm text-gray-900"> <p className="text-sm text-gray-500 mb-2">{operation.description}</p>
{operation.workCenter?.name} )}
</div> </div>
<div className="text-sm text-gray-500"> <div className="flex space-x-1">
{operation.workCenter?.code} <button
</div> onClick={() => handleEdit(operation)}
</td> className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
<td className="px-2 py-1.5 whitespace-nowrap">
<span
className={"px-2 py-1 rounded-full text-xs font-medium"}
> >
{operation.operationType?.code} <FaEdit className="w-4 h-4" />
</span> </button>
</td> <button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900"> <FaTrash className="w-4 h-4" />
<div className="space-y-1"> </button>
<div className="flex items-center"> </div>
<FaClock className="w-3 h-3 mr-1 text-gray-400" /> </div>
<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 && ( {/* Work Center Info */}
<div className="text-center py-12"> <div className="bg-gray-50 rounded-lg p-2 mb-2">
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <div className="flex items-center space-x-2 mb-2">
<h3 className="text-lg font-medium text-gray-900 mb-2"> <FaWrench className="w-4 h-4 text-gray-600" />
Operasyon bulunamadı <span className="font-medium text-gray-900">İş Merkezi</span>
</h3> </div>
<p className="text-gray-500"> <p className="text-sm text-gray-700">{operation.workCenter?.name}</p>
Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin. <p className="text-sm text-gray-600">{operation.workCenter?.code}</p>
</p> </div>
</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 */} {/* Modal for Add/Edit */}
<OperationFormModal <OperationFormModal
@ -415,8 +376,8 @@ const OperationDefinitions: React.FC = () => {
onSave={handleSave} onSave={handleSave}
onClose={() => setShowModal(false)} onClose={() => setShowModal(false)}
/> />
</div> </Container>
); )
}; }
export default OperationDefinitions; export default OperationDefinitions

View file

@ -176,7 +176,7 @@ const OperationForm: React.FC = () => {
} }
return ( return (
<div className="space-y-4 pt-2"> <div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>

View file

@ -56,7 +56,7 @@ const OperationFormModal: React.FC<OperationFormProps> = ({
{form.id ? "Operasyonu Düzenle" : "Yeni Operasyon"} {form.id ? "Operasyonu Düzenle" : "Yeni Operasyon"}
</h3> </h3>
<div className="space-y-3"> <div className="space-y-2">
{/* First Row - Code and Name */} {/* First Row - Code and Name */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-3"> <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<div> <div>

View file

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import {
FaPlus, FaPlus,
FaSearch, FaSearch,
@ -9,353 +9,335 @@ import {
FaCheckCircle, FaCheckCircle,
FaTh, FaTh,
FaList, FaList,
} from "react-icons/fa"; } from 'react-icons/fa'
import { MrpOperationTypeDefinition } from "../../../types/mrp"; import { MrpOperationTypeDefinition } from '../../../types/mrp'
import OperationTypeFormModal from "./OperationTypeFormModal"; import OperationTypeFormModal from './OperationTypeFormModal'
import { mockOperationTypes } from "../../../mocks/mockOperationTypes"; import { mockOperationTypes } from '../../../mocks/mockOperationTypes'
import { import {
getOperationCategoryColor, getOperationCategoryColor,
getOperationTypeColor, getOperationTypeColor,
getOperationTypeText, getOperationTypeText,
getSkillLevelText, getSkillLevelText,
} from "../../../utils/erp"; } from '../../../utils/erp'
import { Container } from '@/components/shared'
const OperationTypes: React.FC = () => { const OperationTypes: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false)
const [editingType, setEditingType] = const [editingType, setEditingType] = useState<MrpOperationTypeDefinition | null>(null)
useState<MrpOperationTypeDefinition | null>(null); const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
const [viewMode, setViewMode] = useState<"card" | "list">("card");
const [operationTypes] = const [operationTypes] = useState<MrpOperationTypeDefinition[]>(mockOperationTypes)
useState<MrpOperationTypeDefinition[]>(mockOperationTypes);
const filteredTypes = operationTypes.filter( const filteredTypes = operationTypes.filter(
(type) => (type) =>
type.name.toLowerCase().includes(searchTerm.toLowerCase()) || type.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
type.code.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) => { const handleEdit = (type: MrpOperationTypeDefinition) => {
setEditingType(type); setEditingType(type)
setShowModal(true); setShowModal(true)
}; }
const handleAddNew = () => { const handleAddNew = () => {
setEditingType(null); setEditingType(null)
setShowModal(true); setShowModal(true)
}; }
return ( return (
<div className="space-y-3 pt-2"> <Container>
{/* Header */} <div className="space-y-2">
<div className="flex items-center justify-between"> {/* Header */}
<div> <div className="flex items-center justify-between">
<h2 className="text-xl font-bold text-gray-900">Operasyon Türleri</h2> <div>
<p className="text-sm text-gray-600 mt-0.5"> <h2 className="text-xl font-bold text-gray-900">Operasyon Türleri</h2>
İş merkezlerinde kullanılacak operasyon türlerini tanımlayın <p className="text-sm text-gray-600 mt-0.5">
</p> İş merkezlerinde kullanılacak operasyon türlerini tanımlayın
</div> </p>
<div className="flex items-center space-x-2"> </div>
{/* View Toggle */} <div className="flex items-center space-x-2">
<div className="flex bg-gray-100 rounded-lg p-0.5"> {/* 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 <button
onClick={() => setViewMode("card")} onClick={handleAddNew}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${ 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"
viewMode === "card"
? "bg-white text-blue-600 shadow-sm"
: "text-gray-600 hover:text-gray-900"
}`}
> >
<FaTh className="w-4 h-4" /> <FaPlus className="w-4 h-4" />
</button> <span>Yeni Operasyon Türü</span>
<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> </button>
</div> </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>
</div>
{/* Search Bar */} {/* Search Bar */}
<div className="relative"> <div className="relative">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<input <input
type="text" type="text"
placeholder="Operasyon türü ara..." placeholder="Operasyon türü ara..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} 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" 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> </div>
{/* Operation Types Display */} {/* Operation Types Display */}
{viewMode === "card" ? ( {viewMode === 'card' ? (
/* Card View */ /* Card View */
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filteredTypes.map((type) => ( {filteredTypes.map((type) => (
<div <div
key={type.id} key={type.id}
className="bg-white rounded-lg shadow-md border border-gray-200 p-4 hover:shadow-lg transition-shadow" 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 items-start justify-between mb-3">
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center space-x-2 mb-2"> <div className="flex items-center space-x-2 mb-2">
<h3 className="text-lg font-semibold text-gray-900"> <h3 className="text-lg font-semibold text-gray-900">{type.name}</h3>
{type.name} <span
</h3> className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor(
<span type.category,
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> </span>
</div> </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>
<div className="flex space-x-1">
<button <div className="mt-3 pt-3 border-t border-gray-100">
onClick={() => handleEdit(type)} <div className="flex items-center justify-between">
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors" <span
> className={`px-2 py-1 rounded-full text-xs font-medium ${
<FaEdit className="w-4 h-4" /> type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
</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" /> {type.isActive ? 'Aktif' : 'Pasif'}
</button> </span>
<span className="text-xs text-gray-400">
{type.lastModificationTime.toLocaleDateString('tr-TR')}
</span>
</div>
</div> </div>
</div> </div>
))}
<div className="space-y-1.5"> </div>
<div className="flex items-center justify-between text-sm"> ) : (
<span className="text-gray-500 flex items-center"> /* List View */
<FaClock className="w-4 h-4 mr-1" /> <div className="bg-white shadow rounded-lg overflow-hidden">
Varsayılan Süre <table className="min-w-full divide-y divide-gray-200">
</span> <thead className="bg-gray-50">
<span className="font-medium">{type.defaultDuration} dk</span> <tr>
</div> <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Operasyon Türü
<div className="flex items-center justify-between text-sm"> </th>
<span className="text-gray-500">Beceri Seviyesi</span> <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<span className="font-medium"> Kategori
{getSkillLevelText(type.skillLevelRequired)} </th>
</span> <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
</div> Süre
</th>
<div className="flex items-center justify-between text-sm"> <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<span className="text-gray-500 flex items-center"> Beceri Seviyesi
<FaCog className="w-4 h-4 mr-1" /> </th>
Hazırlık Gerekli <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
</span> Hazırlık
<span </th>
className={`font-medium ${ <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
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ü Kalite Kontrolü
</span> </th>
<span <th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
className={`font-medium ${ Durum
type.qualityCheckRequired </th>
? "text-purple-600" <th className="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
: "text-gray-600" İşlemler
}`} </th>
>
{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>
</tr> </tr>
))} </thead>
</tbody> <tbody className="bg-white divide-y divide-gray-200">
</table> {filteredTypes.map((type) => (
</div> <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 && ( {filteredTypes.length === 0 && (
<div className="text-center py-12"> <div className="text-center py-12">
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2"> <h3 className="text-lg font-medium text-gray-900 mb-2">Operasyon türü bulunamadı</h3>
Operasyon türü bulunamadı <p className="text-gray-500">
</h3> Arama kriterlerinizi değiştirin veya yeni bir operasyon türü ekleyin.
<p className="text-gray-500"> </p>
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü </div>
ekleyin. )}
</p> </div>
</div>
)}
{/* Modal for Add/Edit - Placeholder */} {/* Modal for Add/Edit - Placeholder */}
<OperationTypeFormModal <OperationTypeFormModal
@ -364,12 +346,12 @@ const OperationTypes: React.FC = () => {
onClose={() => setShowModal(false)} onClose={() => setShowModal(false)}
onSave={(data) => { onSave={(data) => {
// TODO: persist to API; for now just close modal // TODO: persist to API; for now just close modal
console.log("Saved operation type:", data); console.log('Saved operation type:', data)
setShowModal(false); setShowModal(false)
}} }}
/> />
</div> </Container>
); )
}; }
export default OperationTypes; export default OperationTypes

View file

@ -16,12 +16,13 @@ import {
getProductionOrderStatus, getProductionOrderStatus,
getWorkOrderStatus, getWorkOrderStatus,
} from "../../../utils/erp"; } from "../../../utils/erp";
import { Container } from "@/components/shared";
interface PlanningGanttProps { interface PlanningGanttProps {
workCenterId?: string; workCenterId?: string;
} }
export const PlanningGantt: React.FC<PlanningGanttProps> = ({ const PlanningGantt: React.FC<PlanningGanttProps> = ({
workCenterId, workCenterId,
}) => { }) => {
const getInitialExpandedItems = () => { const getInitialExpandedItems = () => {
@ -502,8 +503,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
}; };
return ( return (
<> <Container>
<div className="mb-3 mt-2"> <div className="mb-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div> <div>
<h2 className="text-xl font-bold text-gray-900"> <h2 className="text-xl font-bold text-gray-900">
@ -618,6 +619,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
{filteredData.map((task) => renderTask(task))} {filteredData.map((task) => renderTask(task))}
</div> </div>
</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 React, { useMemo } from 'react'
import { useParams, useNavigate } from "react-router-dom"; import { useParams, useNavigate } from 'react-router-dom'
import { useQuery } from "@tanstack/react-query"; import { useQuery } from '@tanstack/react-query'
import { mockProductionOrders } from "../../../mocks/mockProductionOrders"; import { mockProductionOrders } from '../../../mocks/mockProductionOrders'
import { import { MrpProductionOrder, MrpProductionOrderMaterial } from '../../../types/mrp'
MrpProductionOrder,
MrpProductionOrderMaterial,
} from "../../../types/mrp";
import { import {
FaCog, FaCog,
FaCalendarAlt, FaCalendarAlt,
@ -14,29 +11,28 @@ import {
FaMoneyBillWave, FaMoneyBillWave,
FaArrowLeft, FaArrowLeft,
FaExclamationTriangle, FaExclamationTriangle,
} from "react-icons/fa"; } from 'react-icons/fa'
import StatusBadge from "../../../components/common/StatusBadge"; import StatusBadge from '../../../components/common/StatusBadge'
import { getPriorityColor } from "../../../utils/erp"; import { getPriorityColor } from '../../../utils/erp'
import { Container } from '@/components/shared'
const ProductionOrderView: React.FC = () => { const ProductionOrderView: React.FC = () => {
const { id } = useParams(); const { id } = useParams()
const navigate = useNavigate(); const navigate = useNavigate()
const { data: productionOrder, isLoading } = useQuery({ const { data: productionOrder, isLoading } = useQuery({
queryKey: ["production-order", id], queryKey: ['production-order', id],
queryFn: async () => { queryFn: async () => {
await new Promise((r) => setTimeout(r, 200)); await new Promise((r) => setTimeout(r, 200))
return mockProductionOrders.find((p) => p.id === id) as return mockProductionOrders.find((p) => p.id === id) as MrpProductionOrder | undefined
| MrpProductionOrder
| undefined;
}, },
enabled: !!id, enabled: !!id,
}); })
const materials: MrpProductionOrderMaterial[] = useMemo( const materials: MrpProductionOrderMaterial[] = useMemo(
() => productionOrder?.materials || [], () => productionOrder?.materials || [],
[productionOrder] [productionOrder],
); )
if (isLoading) { if (isLoading) {
return ( return (
@ -46,7 +42,7 @@ const ProductionOrderView: React.FC = () => {
<p className="text-gray-600 font-medium">Üretim emri yükleniyor...</p> <p className="text-gray-600 font-medium">Üretim emri yükleniyor...</p>
</div> </div>
</div> </div>
); )
} }
if (!productionOrder) { if (!productionOrder) {
@ -56,14 +52,12 @@ const ProductionOrderView: React.FC = () => {
<div className="mb-6"> <div className="mb-6">
<FaExclamationTriangle className="mx-auto h-16 w-16 text-gray-400" /> <FaExclamationTriangle className="mx-auto h-16 w-16 text-gray-400" />
</div> </div>
<h2 className="text-2xl font-bold text-gray-900 mb-2"> <h2 className="text-2xl font-bold text-gray-900 mb-2">Üretim Emri Bulunamadı</h2>
Üretim Emri Bulunamadı
</h2>
<p className="text-gray-600 mb-6"> <p className="text-gray-600 mb-6">
İstenen üretim emri mevcut değil veya silinmiş olabilir. İstenen üretim emri mevcut değil veya silinmiş olabilir.
</p> </p>
<button <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" 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" /> <FaArrowLeft className="mr-2" />
@ -71,436 +65,385 @@ const ProductionOrderView: React.FC = () => {
</button> </button>
</div> </div>
</div> </div>
); )
} }
const getProgressPercentage = () => { const getProgressPercentage = () => {
if (productionOrder.plannedQuantity === 0) return 0; if (productionOrder.plannedQuantity === 0) return 0
return Math.round( return Math.round((productionOrder.confirmedQuantity / productionOrder.plannedQuantity) * 100)
(productionOrder.confirmedQuantity / productionOrder.plannedQuantity) * }
100
);
};
return ( return (
<div className="mx-auto mt-2"> <Container>
{/* Header */} <div className="space-y-2">
<div className="mb-6"> {/* Header */}
<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 */}
<div className="mb-6"> <div className="mb-6">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between">
<div className="text-sm font-medium text-gray-700"> <div className="flex items-center space-x-3">
Üretim İlerlemesi <button
</div> onClick={() => navigate('/admin/mrp/production-orders')}
<div className="text-sm text-gray-500"> 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"
{productionOrder.confirmedQuantity} /{" "} >
{productionOrder.plannedQuantity} adet <FaArrowLeft className="mr-2 text-sm" />
</div> Geri
</div> </button>
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden"> <div>
<div <h1 className="text-2xl font-bold text-gray-900">Üretim Emri Detayları</h1>
className="bg-gradient-to-r from-blue-500 to-blue-600 h-3 rounded-full transition-all duration-500 ease-out" <p className="text-gray-600 mt-1 text-sm">
style={{ width: `${getProgressPercentage()}%` }} {productionOrder.orderNumber} - Detaylı bilgiler
></div> </p>
</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>
</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>
</div> </div>
{/* Schedule Card */} {/* Order Header Card */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200"> <div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 mb-4">
<div className="flex items-center gap-2 mb-2"> <div className="flex flex-wrap justify-between items-start mb-4">
<div className="p-1.5 bg-purple-100 rounded-lg"> <div className="space-y-2">
<FaCalendarAlt className="text-purple-600 text-base" /> <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>
<h3 className="text-base font-bold text-gray-900">Zamanlama</h3>
</div> </div>
<div className="space-y-2">
<div className="py-1.5 border-b border-gray-100"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1"> <div className="bg-gradient-to-br from-blue-50 to-blue-100 p-4 rounded-xl border border-blue-200">
Plan Başlangıç <div className="text-sm font-medium text-blue-700 mb-2 uppercase tracking-wide">
Öncelik
</div> </div>
<div className="font-semibold text-gray-900"> <span
{new Date(productionOrder.plannedStartDate).toLocaleDateString( className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
"tr-TR", productionOrder.priority,
{ )}`}
weekday: "long", >
year: "numeric", {productionOrder.priority}
month: "long", </span>
day: "numeric", </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> </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"> <div className="bg-gradient-to-br from-orange-50 to-orange-100 p-4 rounded-xl border border-orange-200">
Plan Bitiş <div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
İlerleme
</div> </div>
<div className="font-semibold text-gray-900"> <div className="text-xl font-bold text-orange-900">{getProgressPercentage()}%</div>
{new Date(productionOrder.plannedEndDate).toLocaleDateString( </div>
"tr-TR", </div>
{
weekday: "long", {/* Progress Bar */}
year: "numeric", <div className="mb-6">
month: "long", <div className="flex items-center justify-between mb-3">
day: "numeric", <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> </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="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1"> <div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Gerçek Başlangıç Plan Başlangıç
</div> </div>
<div className="font-semibold text-green-600"> <div className="font-semibold text-gray-900">
{new Date(productionOrder.actualStartDate).toLocaleDateString( {new Date(productionOrder.plannedStartDate).toLocaleDateString('tr-TR', {
"tr-TR", weekday: 'long',
{ year: 'numeric',
weekday: "long", month: 'long',
year: "numeric", day: '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>
</div> </div>
)}
{productionOrder.actualEndDate && (
<div className="py-1.5"> <div className="py-1.5">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1"> <div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Gerçek Bitiş Para Birimi
</div> </div>
<div className="font-semibold text-green-600"> <div className="font-semibold text-gray-900">{productionOrder.currency}</div>
{new Date(productionOrder.actualEndDate).toLocaleDateString( </div>
"tr-TR", </div>
{ </div>
weekday: "long",
year: "numeric", {/* Metadata Card */}
month: "long", <div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
day: "numeric", <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>
)} <div className="py-1.5 border-b border-gray-100">
</div> <div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
</div> Son Güncelleme
</div>
{/* Costs Card */} <div className="font-semibold text-gray-900">
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200"> {new Date(productionOrder.lastModificationTime).toLocaleDateString('tr-TR', {
<div className="flex items-center gap-2 mb-2"> year: 'numeric',
<div className="p-1.5 bg-green-100 rounded-lg"> month: 'short',
<FaMoneyBillWave className="text-green-600 text-base" /> day: 'numeric',
</div> })}
<h3 className="text-base font-bold text-gray-900"> </div>
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> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Metadata Card */} {/* Materials List */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200"> <div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4">
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center justify-between mb-3">
<div className="p-1.5 bg-gray-100 rounded-lg"> <div className="flex items-center gap-2">
<FaClipboardList className="text-gray-600 text-base" /> <div className="p-1.5 bg-indigo-100 rounded-lg">
</div> <FaTruck className="text-indigo-600 text-base" />
<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>
<div className="font-semibold text-gray-900"> <div>
{new Date(productionOrder.creationTime).toLocaleDateString( <h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
"tr-TR", <p className="text-gray-600 mt-1 text-sm">Üretim emrine bağlı malzeme satırları</p>
{ year: "numeric", month: "short", day: "numeric" }
)}
</div> </div>
</div> </div>
<div className="py-1.5 border-b border-gray-100"> <div className="text-right">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1"> <div className="text-lg font-bold text-indigo-600">{materials.length}</div>
Son Güncelleme <div className="text-sm text-gray-500">Malzeme</div>
</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>
</div>
</div>
{/* Materials List */} {materials.length === 0 ? (
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4"> <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">
<div className="flex items-center justify-between mb-3"> <FaTruck className="mx-auto h-16 w-16 text-gray-400 mb-4" />
<div className="flex items-center gap-2"> <p className="text-gray-500 text-base font-medium">
<div className="p-1.5 bg-indigo-100 rounded-lg"> Bu üretim emrine bağlı malzeme bulunmamaktadır.
<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ı
</p> </p>
</div> </div>
</div> ) : (
<div className="text-right"> <div className="space-y-3 max-h-96 overflow-y-auto">
<div className="text-lg font-bold text-indigo-600"> {materials.map((m: MrpProductionOrderMaterial) => (
{materials.length} <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 className="text-sm text-gray-500">Malzeme</div> )}
</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>
</div> </Container>
); )
}; }
export default ProductionOrderView; export default ProductionOrderView

View file

@ -1,209 +1,188 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { FaBox, FaIndustry, FaShoppingCart } from "react-icons/fa"; import { FaBox, FaIndustry, FaShoppingCart } from 'react-icons/fa'
import { import {
MrpMaterialRequirement, MrpMaterialRequirement,
MrpProductionSuggestion, MrpProductionSuggestion,
MrpPurchaseSuggestion, MrpPurchaseSuggestion,
} from "../../../types/mrp"; } from '../../../types/mrp'
import RunMrpModal from "./RunMrpModal"; import RunMrpModal from './RunMrpModal'
import ProductionSuggestions from "./ProductionSuggestions"; import ProductionSuggestions from './ProductionSuggestions'
import PurchaseSuggestions from "./PurchaseSuggestions"; import PurchaseSuggestions from './PurchaseSuggestions'
import MaterialRequirements from "./MaterialRequirements"; import MaterialRequirements from './MaterialRequirements'
import { import {
mockMaterialRequirements, mockMaterialRequirements,
mockProductionSuggestions, mockProductionSuggestions,
mockPurchaseSuggestions, mockPurchaseSuggestions,
} from "../../../mocks/mockMRP"; } from '../../../mocks/mockMRP'
import Widget from "../../../components/common/Widget"; import Widget from '../../../components/common/Widget'
import { Container } from '@/components/shared'
const Requirements: React.FC = () => { const Requirements: React.FC = () => {
const [materialRequirements, setMaterialRequirements] = useState< const [materialRequirements, setMaterialRequirements] = useState<MrpMaterialRequirement[]>([])
MrpMaterialRequirement[] const [purchaseSuggestions, setPurchaseSuggestions] = useState<MrpPurchaseSuggestion[]>([])
>([]); const [productionSuggestions, setProductionSuggestions] = useState<MrpProductionSuggestion[]>([])
const [purchaseSuggestions, setPurchaseSuggestions] = useState<
MrpPurchaseSuggestion[]
>([]);
const [productionSuggestions, setProductionSuggestions] = useState<
MrpProductionSuggestion[]
>([]);
const runMrp = () => { const runMrp = () => {
// This is where the actual MRP logic would run. // This is where the actual MRP logic would run.
// For now, we'll just populate the state with mock data. // For now, we'll just populate the state with mock data.
setMaterialRequirements(mockMaterialRequirements); setMaterialRequirements(mockMaterialRequirements)
setPurchaseSuggestions(mockPurchaseSuggestions); setPurchaseSuggestions(mockPurchaseSuggestions)
setProductionSuggestions(mockProductionSuggestions); setProductionSuggestions(mockProductionSuggestions)
}; }
const [isMrpModalOpen, setIsMrpModalOpen] = useState(false); const [isMrpModalOpen, setIsMrpModalOpen] = useState(false)
const [activeTab, setActiveTab] = useState< const [activeTab, setActiveTab] = useState<'requirements' | 'production' | 'purchase'>(
"requirements" | "production" | "purchase" 'requirements',
>("requirements"); )
const getUrgencyLevel = (requirement: MrpMaterialRequirement) => { const getUrgencyLevel = (requirement: MrpMaterialRequirement) => {
const today = new Date(); const today = new Date()
const reqDate = new Date(requirement.requirementDate); const reqDate = new Date(requirement.requirementDate)
const daysUntilRequired = Math.ceil( const daysUntilRequired = Math.ceil(
(reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24) (reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24),
); )
if (daysUntilRequired < 0) if (daysUntilRequired < 0)
return { return {
level: "overdue", level: 'overdue',
color: "bg-red-100 text-red-800", color: 'bg-red-100 text-red-800',
label: "Gecikmiş", label: 'Gecikmiş',
}; }
if (daysUntilRequired <= 7) if (daysUntilRequired <= 7)
return { return {
level: "urgent", level: 'urgent',
color: "bg-orange-100 text-orange-800", color: 'bg-orange-100 text-orange-800',
label: "Acil", label: 'Acil',
}; }
if (daysUntilRequired <= 30) if (daysUntilRequired <= 30)
return { return {
level: "soon", level: 'soon',
color: "bg-yellow-100 text-yellow-800", color: 'bg-yellow-100 text-yellow-800',
label: "Yakın", label: 'Yakın',
}; }
return { return {
level: "normal", level: 'normal',
color: "bg-green-100 text-green-800", color: 'bg-green-100 text-green-800',
label: "Normal", label: 'Normal',
}; }
}; }
// Calculate statistics // Calculate statistics
const totalRequirements = materialRequirements.length; const totalRequirements = materialRequirements.length
const shortageRequirements = materialRequirements.filter( const shortageRequirements = materialRequirements.filter((r) => r.netRequirement > 0).length
(r) => r.netRequirement > 0
).length;
const urgentRequirements = materialRequirements.filter( const urgentRequirements = materialRequirements.filter(
(r) => getUrgencyLevel(r).level === "urgent" (r) => getUrgencyLevel(r).level === 'urgent',
).length; ).length
const openProductionSuggestions = productionSuggestions.filter( const openProductionSuggestions = productionSuggestions.filter((s) => s.status === 'OPEN').length
(s) => s.status === "OPEN" const openPurchaseSuggestions = purchaseSuggestions.filter((s) => s.status === 'OPEN').length
).length;
const openPurchaseSuggestions = purchaseSuggestions.filter(
(s) => s.status === "OPEN"
).length;
return ( return (
<div className="space-y-3 pt-2"> <Container>
{/* Header */} <div className="space-y-2">
<div className="flex items-center justify-between"> {/* Header */}
<div> <div className="flex items-center justify-between">
<h2 className="text-xl font-bold text-gray-900"> <div>
Malzeme İhtiyaçları <h2 className="text-xl font-bold text-gray-900">Malzeme İhtiyaçları</h2>
</h2> <p className="text-sm text-gray-600">
<p className="text-sm text-gray-600"> Malzeme ihtiyaç hesaplama ve satın alma önerileri
Malzeme ihtiyaç hesaplama ve satın alma önerileri </p>
</p> </div>
</div> <button
<button onClick={() => setIsMrpModalOpen(true)}
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"
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">
<FaBox className="w-4 h-4" /> <FaBox className="w-4 h-4" />
Malzeme İhtiyaçları ({totalRequirements}) MRP Çalıştır
</div> </button>
</button> </div>
<button
onClick={() => setActiveTab("production")} {/* Stats Cards */}
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${ <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
activeTab === "production" <Widget title="Toplam İhtiyaç" value={totalRequirements} color="blue" icon="FaBox" />
? "bg-white text-gray-900 shadow-sm"
: "text-gray-600 hover:text-gray-900" <Widget title="Eksik Stok" value={shortageRequirements} color="red" icon="FaArrowDown" />
}`}
> <Widget
<div className="flex items-center justify-center gap-2"> title="Acil İhtiyaç"
<FaIndustry className="w-4 h-4" /> value={urgentRequirements}
Üretim Önerileri ({openProductionSuggestions}) color="orange"
</div> icon="FaExclamationTriangle"
</button> />
<button
onClick={() => setActiveTab("purchase")} <Widget
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${ title="Açık Öneri"
activeTab === "purchase" value={openProductionSuggestions + openPurchaseSuggestions}
? "bg-white text-gray-900 shadow-sm" color="purple"
: "text-gray-600 hover:text-gray-900" icon="FaBullseye"
}`} />
> </div>
<div className="flex items-center justify-center gap-2">
<FaShoppingCart className="w-4 h-4" /> {/* Tabs */}
Satınalma Önerileri ({openPurchaseSuggestions}) <div className="flex bg-gray-100 rounded-lg p-1">
</div> <button
</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> </div>
{/* Data Table */}
{activeTab === "requirements" && (
<MaterialRequirements materialRequirements={materialRequirements} />
)}
{activeTab === "production" && (
<ProductionSuggestions productionSuggestions={productionSuggestions} />
)}
{activeTab === "purchase" && (
<PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} />
)}
<RunMrpModal <RunMrpModal
isOpen={isMrpModalOpen} isOpen={isMrpModalOpen}
onClose={() => setIsMrpModalOpen(false)} onClose={() => setIsMrpModalOpen(false)}
onRunComplete={() => { onRunComplete={() => {
runMrp(); runMrp()
setIsMrpModalOpen(false); setIsMrpModalOpen(false)
}} }}
/> />
</div> </Container>
); )
}; }
export default Requirements; export default Requirements

View file

@ -1,64 +1,52 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import { FaCog, FaPlus, FaEdit, FaTrash, FaClock, FaCheckCircle, FaPlay } from 'react-icons/fa'
FaCog, import { MrpWorkOrder } from '../../../types/mrp'
FaPlus, import DataTable, { Column } from '../../../components/common/DataTable'
FaEdit, import { mockWorkOrders } from '../../../mocks/mockWorkOrders'
FaTrash, import NewWorkOrderForm from './NewWorkOrderForm'
FaClock, import EditWorkOrderForm from './EditWorkOrderForm'
FaCheckCircle, import ViewWorkOrderModal from './ViewWorkOrderModal'
FaPlay, import CompleteWorkOrderModal from './CompleteWorkOrderModal'
} from "react-icons/fa"; import Widget from '../../../components/common/Widget'
import { MrpWorkOrder } from "../../../types/mrp"; import { PriorityEnum } from '../../../types/common'
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 { import {
getPriorityColor, getPriorityColor,
getPriorityText, getPriorityText,
getWorkOrderStatusColor, getWorkOrderStatusColor,
getWorkOrderStatusText, getWorkOrderStatusText,
} from "../../../utils/erp"; } from '../../../utils/erp'
import { WorkOrderStatusEnum } from "../../../types/pm"; import { WorkOrderStatusEnum } from '../../../types/pm'
import { Container } from '@/components/shared'
const WorkOrders: React.FC = () => { const WorkOrders: React.FC = () => {
const [workOrders, setWorkOrders] = useState<MrpWorkOrder[]>(mockWorkOrders); const [workOrders, setWorkOrders] = useState<MrpWorkOrder[]>(mockWorkOrders)
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [selectedStatus, setSelectedStatus] = useState< const [selectedStatus, setSelectedStatus] = useState<WorkOrderStatusEnum | 'all'>('all')
WorkOrderStatusEnum | "all" const [selectedPriority, setSelectedPriority] = useState<PriorityEnum | 'all'>('all')
>("all");
const [selectedPriority, setSelectedPriority] = useState<
PriorityEnum | "all"
>("all");
// Modal states // Modal states
const [isNewWorkOrderOpen, setIsNewWorkOrderOpen] = useState(false); const [isNewWorkOrderOpen, setIsNewWorkOrderOpen] = useState(false)
const [isEditWorkOrderOpen, setIsEditWorkOrderOpen] = useState(false); const [isEditWorkOrderOpen, setIsEditWorkOrderOpen] = useState(false)
const [isViewWorkOrderOpen, setIsViewWorkOrderOpen] = useState(false); const [isViewWorkOrderOpen, setIsViewWorkOrderOpen] = useState(false)
const [isCompleteWorkOrderOpen, setIsCompleteWorkOrderOpen] = useState(false); const [isCompleteWorkOrderOpen, setIsCompleteWorkOrderOpen] = useState(false)
const [selectedWorkOrder, setSelectedWorkOrder] = const [selectedWorkOrder, setSelectedWorkOrder] = useState<MrpWorkOrder | null>(null)
useState<MrpWorkOrder | null>(null);
// Event handlers // Event handlers
const handleAdd = () => { const handleAdd = () => {
setIsNewWorkOrderOpen(true); setIsNewWorkOrderOpen(true)
}; }
const handleEdit = (workOrder: MrpWorkOrder) => { const handleEdit = (workOrder: MrpWorkOrder) => {
setSelectedWorkOrder(workOrder); setSelectedWorkOrder(workOrder)
setIsEditWorkOrderOpen(true); setIsEditWorkOrderOpen(true)
}; }
const handleDelete = (id: string) => { const handleDelete = (id: string) => {
if (window.confirm("Bu iş emrini silmek istediğinizden emin misiniz?")) { if (window.confirm('Bu iş emrini silmek istediğinizden emin misiniz?')) {
setWorkOrders((prev) => prev.filter((wo) => wo.id !== id)); setWorkOrders((prev) => prev.filter((wo) => wo.id !== id))
} }
}; }
const handleStart = (workOrder: MrpWorkOrder) => { const handleStart = (workOrder: MrpWorkOrder) => {
setWorkOrders((prev) => setWorkOrders((prev) =>
@ -69,57 +57,54 @@ const WorkOrders: React.FC = () => {
status: WorkOrderStatusEnum.InProgress, status: WorkOrderStatusEnum.InProgress,
lastModificationTime: new Date(), lastModificationTime: new Date(),
} }
: wo : wo,
) ),
); )
}; }
const handleComplete = (workOrder: MrpWorkOrder) => { const handleComplete = (workOrder: MrpWorkOrder) => {
setSelectedWorkOrder(workOrder); setSelectedWorkOrder(workOrder)
setIsCompleteWorkOrderOpen(true); setIsCompleteWorkOrderOpen(true)
}; }
const handleViewDetails = (workOrder: MrpWorkOrder) => { const handleViewDetails = (workOrder: MrpWorkOrder) => {
setSelectedWorkOrder(workOrder); setSelectedWorkOrder(workOrder)
setIsViewWorkOrderOpen(true); setIsViewWorkOrderOpen(true)
}; }
// Modal handlers // Modal handlers
const handleNewWorkOrderSave = ( const handleNewWorkOrderSave = (
newWorkOrderData: Omit< newWorkOrderData: Omit<MrpWorkOrder, 'id' | 'creationTime' | 'lastModificationTime'>,
MrpWorkOrder,
"id" | "creationTime" | "lastModificationTime"
>
) => { ) => {
const newWorkOrder: MrpWorkOrder = { const newWorkOrder: MrpWorkOrder = {
...newWorkOrderData, ...newWorkOrderData,
id: `WO-${Date.now()}`, id: `WO-${Date.now()}`,
creationTime: new Date(), creationTime: new Date(),
lastModificationTime: new Date(), lastModificationTime: new Date(),
}; }
setWorkOrders((prev) => [...prev, newWorkOrder]); setWorkOrders((prev) => [...prev, newWorkOrder])
}; }
const handleEditWorkOrderSave = (updatedWorkOrder: MrpWorkOrder) => { const handleEditWorkOrderSave = (updatedWorkOrder: MrpWorkOrder) => {
setWorkOrders((prev) => setWorkOrders((prev) =>
prev.map((wo) => (wo.id === updatedWorkOrder.id ? updatedWorkOrder : wo)) prev.map((wo) => (wo.id === updatedWorkOrder.id ? updatedWorkOrder : wo)),
); )
}; }
const handleCompleteWorkOrder = ( const handleCompleteWorkOrder = (
workOrderId: string, workOrderId: string,
confirmedQuantity: number, confirmedQuantity: number,
scrapQuantity: number scrapQuantity: number,
) => { ) => {
setWorkOrders((prev) => setWorkOrders((prev) =>
prev.map((wo) => { prev.map((wo) => {
if (wo.id === workOrderId) { if (wo.id === workOrderId) {
const newConfirmedQuantity = wo.confirmedQuantity + confirmedQuantity; const newConfirmedQuantity = wo.confirmedQuantity + confirmedQuantity
const newScrapQuantity = wo.scrapQuantity + scrapQuantity; const newScrapQuantity = wo.scrapQuantity + scrapQuantity
const newStatus = const newStatus =
newConfirmedQuantity + newScrapQuantity >= wo.plannedQuantity newConfirmedQuantity + newScrapQuantity >= wo.plannedQuantity
? WorkOrderStatusEnum.Completed ? WorkOrderStatusEnum.Completed
: wo.status; : wo.status
return { return {
...wo, ...wo,
@ -127,80 +112,69 @@ const WorkOrders: React.FC = () => {
scrapQuantity: newScrapQuantity, scrapQuantity: newScrapQuantity,
status: newStatus, status: newStatus,
lastModificationTime: new Date(), lastModificationTime: new Date(),
}; }
} }
return wo; return wo
}) }),
); )
}; }
const filteredWorkOrders = workOrders.filter((workOrder) => { const filteredWorkOrders = workOrders.filter((workOrder) => {
if ( if (
searchTerm && searchTerm &&
!workOrder.workOrderNumber !workOrder.workOrderNumber.toLowerCase().includes(searchTerm.toLowerCase()) &&
.toLowerCase() !workOrder.productionOrder?.orderNumber?.toLowerCase().includes(searchTerm.toLowerCase())
.includes(searchTerm.toLowerCase()) &&
!workOrder.productionOrder?.orderNumber
?.toLowerCase()
.includes(searchTerm.toLowerCase())
) { ) {
return false; return false
} }
if (selectedStatus !== "all" && workOrder.status !== selectedStatus) { if (selectedStatus !== 'all' && workOrder.status !== selectedStatus) {
return false; return false
} }
if ( if (selectedPriority !== 'all' && workOrder.productionOrder?.priority !== selectedPriority) {
selectedPriority !== "all" && return false
workOrder.productionOrder?.priority !== selectedPriority
) {
return false;
} }
return true; return true
}); })
const columns: Column<MrpWorkOrder>[] = [ const columns: Column<MrpWorkOrder>[] = [
{ {
key: "workOrderNumber", key: 'workOrderNumber',
header: "İş Emri No", header: 'İş Emri No',
sortable: true, sortable: true,
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<div> <div>
<div className="font-medium text-gray-900"> <div className="font-medium text-gray-900">{workOrder.workOrderNumber}</div>
{workOrder.workOrderNumber} <div className="text-sm text-gray-500">Sıra: {workOrder.sequence}</div>
</div>
<div className="text-sm text-gray-500">
Sıra: {workOrder.sequence}
</div>
</div> </div>
), ),
}, },
{ {
key: "productionOrder", key: 'productionOrder',
header: "Üretim Emri", header: 'Üretim Emri',
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<div> <div>
<div className="font-medium text-gray-900"> <div className="font-medium text-gray-900">
{workOrder.productionOrder?.orderNumber || "N/A"} {workOrder.productionOrder?.orderNumber || 'N/A'}
</div> </div>
</div> </div>
), ),
}, },
{ {
key: "material", key: 'material',
header: "Malzeme", header: 'Malzeme',
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<div> <div>
<div className="text-gray-900"> <div className="text-gray-900">
{workOrder.material?.code} {workOrder.material?.code}
{" - "} {' - '}
{workOrder.material?.name || workOrder.materialId} {workOrder.material?.name || workOrder.materialId}
</div> </div>
</div> </div>
), ),
}, },
{ {
key: "workCenter", key: 'workCenter',
header: "İş Merkezi", header: 'İş Merkezi',
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<div> <div>
<div className="font-medium text-gray-900"> <div className="font-medium text-gray-900">
@ -209,15 +183,13 @@ const WorkOrders: React.FC = () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span>{workOrder.operation?.name || workOrder.operationId}</span> <span>{workOrder.operation?.name || workOrder.operationId}</span>
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">{workOrder.assignedOperators.length} Operatör</div>
{workOrder.assignedOperators.length} Operatör
</div>
</div> </div>
), ),
}, },
{ {
key: "quantities", key: 'quantities',
header: "Miktarlar", header: 'Miktarlar',
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<div className="text-sm"> <div className="text-sm">
<div>Plan: {workOrder.plannedQuantity}</div> <div>Plan: {workOrder.plannedQuantity}</div>
@ -229,50 +201,46 @@ const WorkOrders: React.FC = () => {
), ),
}, },
{ {
key: "schedule", key: 'schedule',
header: "Planlama", header: 'Planlama',
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<div className="text-sm"> <div className="text-sm">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FaClock className="w-3 h-3 text-gray-400" /> <FaClock className="w-3 h-3 text-gray-400" />
<span> <span>
Başlangıç:{" "} Başlangıç: {new Date(workOrder.plannedStartDate).toLocaleDateString('tr-TR')}
{new Date(workOrder.plannedStartDate).toLocaleDateString("tr-TR")}
</span> </span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FaClock className="w-3 h-3 text-gray-400" /> <FaClock className="w-3 h-3 text-gray-400" />
<span> <span>Bitiş: {new Date(workOrder.plannedEndDate).toLocaleDateString('tr-TR')}</span>
Bitiş:{" "}
{new Date(workOrder.plannedEndDate).toLocaleDateString("tr-TR")}
</span>
</div> </div>
</div> </div>
), ),
}, },
{ {
key: "priority", key: 'priority',
header: "Öncelik", header: 'Öncelik',
render: (workOrder: MrpWorkOrder) => render: (workOrder: MrpWorkOrder) =>
workOrder.productionOrder?.priority ? ( workOrder.productionOrder?.priority ? (
<span <span
className={`px-2 py-1 text-xs font-medium rounded-full ${getPriorityColor( className={`px-2 py-1 text-xs font-medium rounded-full ${getPriorityColor(
workOrder.productionOrder.priority workOrder.productionOrder.priority,
)}`} )}`}
> >
{getPriorityText(workOrder.productionOrder.priority)} {getPriorityText(workOrder.productionOrder.priority)}
</span> </span>
) : ( ) : (
"-" '-'
), ),
}, },
{ {
key: "status", key: 'status',
header: "Durum", header: 'Durum',
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<span <span
className={`px-2 py-1 text-xs font-medium rounded-full ${getWorkOrderStatusColor( className={`px-2 py-1 text-xs font-medium rounded-full ${getWorkOrderStatusColor(
workOrder.status workOrder.status,
)}`} )}`}
> >
{getWorkOrderStatusText(workOrder.status)} {getWorkOrderStatusText(workOrder.status)}
@ -280,8 +248,8 @@ const WorkOrders: React.FC = () => {
), ),
}, },
{ {
key: "actions", key: 'actions',
header: "İşlemler", header: 'İşlemler',
render: (workOrder: MrpWorkOrder) => ( render: (workOrder: MrpWorkOrder) => (
<div className="flex gap-1"> <div className="flex gap-1">
{workOrder.status === WorkOrderStatusEnum.Released && ( {workOrder.status === WorkOrderStatusEnum.Released && (
@ -326,167 +294,129 @@ const WorkOrders: React.FC = () => {
</div> </div>
), ),
}, },
]; ]
// Calculate statistics // Calculate statistics
const totalWorkOrders = workOrders.length; const totalWorkOrders = workOrders.length
const inProgressOrders = workOrders.filter( const inProgressOrders = workOrders.filter(
(wo) => wo.status === WorkOrderStatusEnum.InProgress (wo) => wo.status === WorkOrderStatusEnum.InProgress,
).length; ).length
const completedOrders = workOrders.filter( const completedOrders = workOrders.filter(
(wo) => wo.status === WorkOrderStatusEnum.Completed (wo) => wo.status === WorkOrderStatusEnum.Completed,
).length; ).length
const delayedOrders = workOrders.filter( const delayedOrders = workOrders.filter(
(wo) => (wo) => wo.status !== WorkOrderStatusEnum.Completed && new Date(wo.plannedEndDate) < new Date(),
wo.status !== WorkOrderStatusEnum.Completed && ).length
new Date(wo.plannedEndDate) < new Date()
).length;
// Status distribution // Status distribution
const statusDistribution = Object.values(WorkOrderStatusEnum).map( const statusDistribution = Object.values(WorkOrderStatusEnum).map((status) => ({
(status) => ({ status,
status, count: workOrders.filter((wo) => wo.status === status).length,
count: workOrders.filter((wo) => wo.status === status).length, percentage:
percentage: (workOrders.filter((wo) => wo.status === status).length / workOrders.length) * 100 || 0,
(workOrders.filter((wo) => wo.status === status).length / }))
workOrders.length) *
100 || 0,
})
);
return ( return (
<div className="space-y-3 pt-2"> <Container>
{/* Header */} <div className="space-y-2">
<div className="flex items-center justify-between"> {/* Header */}
<div> <div className="flex items-center justify-between">
<h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2> <div>
<p className="text-sm text-gray-600"> <h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2>
Üretim operasyonlarının detaylı takibi <p className="text-sm text-gray-600">Üretim operasyonlarının detaylı takibi</p>
</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> </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 */} {/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3"> <div className="grid grid-cols-1 md:grid-cols-4 gap-3">
<Widget <Widget title="Toplam İş Emri" value={totalWorkOrders} color="blue" icon="FaCog" />
title="Toplam İş Emri"
value={totalWorkOrders}
color="blue"
icon="FaCog"
/>
<Widget <Widget title="İşlemde" value={inProgressOrders} color="yellow" icon="FaPlay" />
title="İşlemde"
value={inProgressOrders}
color="yellow"
icon="FaPlay"
/>
<Widget <Widget title="Tamamlanan" value={completedOrders} color="green" icon="FaCheckCircle" />
title="Tamamlanan"
value={completedOrders}
color="green"
icon="FaCheckCircle"
/>
<Widget <Widget title="Geciken" value={delayedOrders} color="red" icon="FaExclamationCircle" />
title="Geciken" </div>
value={delayedOrders}
color="red"
icon="FaExclamationCircle"
/>
</div>
{/* Status Distribution */} {/* Status Distribution */}
<div className="bg-white rounded-lg shadow-sm border p-3"> <div className="bg-white rounded-lg shadow-sm border p-3">
<h3 className="text-sm font-semibold text-gray-900 mb-2"> <h3 className="text-sm font-semibold text-gray-900 mb-2">Durum Dağılımı</h3>
Durum Dağılımı <div className="grid grid-cols-2 md:grid-cols-5 gap-2">
</h3> {statusDistribution.map(({ status, count, percentage }) => (
<div className="grid grid-cols-2 md:grid-cols-5 gap-2"> <div key={status} className="text-center p-2 border rounded-lg">
{statusDistribution.map(({ status, count, percentage }) => ( <div
<div key={status} className="text-center p-2 border rounded-lg"> className={`inline-block px-1.5 py-0.5 text-xs font-medium rounded-full mb-1 ${getWorkOrderStatusColor(
<div status,
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)} {getWorkOrderStatusText(status)}
</div> </option>
<div className="text-xl font-bold text-gray-900">{count}</div> ))}
<div className="text-xs text-gray-500"> </select>
{percentage.toFixed(1)}%
</div>
</div>
))}
</div>
</div>
{/* Filters */} <select
<div className="flex gap-2 items-center"> value={selectedPriority}
<div className="flex-1"> onChange={(e) => setSelectedPriority(e.target.value as PriorityEnum | 'all')}
<input className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
type="text" >
placeholder="İş emri numarası ara..." <option value="all">Tüm Öncelikler</option>
value={searchTerm} {Object.values(PriorityEnum).map((priority) => (
onChange={(e) => setSearchTerm(e.target.value)} <option key={priority} value={priority}>
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" {getPriorityText(priority)}
/> </option>
))}
</select>
</div> </div>
<select {/* Data Table */}
value={selectedStatus} <div className="bg-white rounded-lg shadow-sm border">
onChange={(e) => <DataTable data={filteredWorkOrders} columns={columns} />
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>
</div> </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 */} {/* Modals */}
<NewWorkOrderForm <NewWorkOrderForm
@ -514,8 +444,8 @@ const WorkOrders: React.FC = () => {
onConfirm={handleCompleteWorkOrder} onConfirm={handleCompleteWorkOrder}
workOrder={selectedWorkOrder} 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;