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,6 +391,7 @@ 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
@ -463,7 +464,6 @@ const LossReasons: React.FC = () => {
</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,6 +384,7 @@ 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>
)} )}
</div>
{/* Modals */} {/* Modals */}
<OpportunityForm <OpportunityForm
@ -400,7 +401,6 @@ const OpportunityManagement: React.FC = () => {
onEdit={handleEditFromDetails} onEdit={handleEditFromDetails}
opportunity={selectedOpportunity} opportunity={selectedOpportunity}
/> />
</div>
</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;

View file

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import {
FaPlus, FaPlus,
FaSearch, FaSearch,
@ -13,95 +13,84 @@ import {
FaTh, FaTh,
FaList, FaList,
FaIndustry, FaIndustry,
} from "react-icons/fa"; } from 'react-icons/fa'
import { import {
PmWorkCenter, PmWorkCenter,
WorkCenterStatusEnum, WorkCenterStatusEnum,
CriticalityLevelEnum, CriticalityLevelEnum,
PmMaintenancePlan, PmMaintenancePlan,
} from "../../../types/pm"; } from '../../../types/pm'
import { mockWorkCenters } from "../../../mocks/mockWorkCenters"; import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
import NewWorkCenterModal from "./NewWorkCenterModal"; import NewWorkCenterModal from './NewWorkCenterModal'
import ViewWorkCenterModal from "./ViewWorkCenterModal"; import ViewWorkCenterModal from './ViewWorkCenterModal'
import EditWorkCenterModal from "./EditWorkCenterModal"; import EditWorkCenterModal from './EditWorkCenterModal'
import MaintenancePlanModal from "./MaintenancePlanModal"; import MaintenancePlanModal from './MaintenancePlanModal'
import StatusUpdateModal from "./StatusUpdateModal"; import StatusUpdateModal from './StatusUpdateModal'
import Widget from "../../../components/common/Widget"; import Widget from '../../../components/common/Widget'
import { import {
getWorkCenterStatusColor, getWorkCenterStatusColor,
getWorkCenterStatusText, getWorkCenterStatusText,
getCriticalityLevelColor, getCriticalityLevelColor,
getCriticalityLevelText, getCriticalityLevelText,
getWorkCenterStatusIcon, getWorkCenterStatusIcon,
} from "../../../utils/erp"; } from '../../../utils/erp'
import { Container } from '@/components/shared'
const WorkCenterCards: React.FC = () => { const WorkCenterCards: React.FC = () => {
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState('')
const [statusFilter, setStatusFilter] = useState< const [statusFilter, setStatusFilter] = useState<WorkCenterStatusEnum | 'all'>('all')
WorkCenterStatusEnum | "all" const [criticalityFilter, setCriticalityFilter] = useState<CriticalityLevelEnum | 'all'>('all')
>("all"); const [showModal, setShowModal] = useState(false)
const [criticalityFilter, setCriticalityFilter] = useState< const [showViewModal, setShowViewModal] = useState(false)
CriticalityLevelEnum | "all" const [showEditModal, setShowEditModal] = useState(false)
>("all"); const [showMaintenancePlanModal, setShowMaintenancePlanModal] = useState(false)
const [showModal, setShowModal] = useState(false); const [showStatusUpdateModal, setShowStatusUpdateModal] = useState(false)
const [showViewModal, setShowViewModal] = useState(false); const [editingWorkCenter, setEditingWorkCenter] = useState<PmWorkCenter | null>(null)
const [showEditModal, setShowEditModal] = useState(false); const [viewingWorkCenter, setViewingWorkCenter] = useState<PmWorkCenter | null>(null)
const [showMaintenancePlanModal, setShowMaintenancePlanModal] = const [selectedWorkCenters, setSelectedWorkCenters] = useState<string[]>([])
useState(false); const [workCenters, setWorkCenters] = useState<PmWorkCenter[]>(mockWorkCenters)
const [showStatusUpdateModal, setShowStatusUpdateModal] = useState(false); const [viewMode, setViewMode] = useState<'cards' | 'list'>('cards')
const [editingWorkCenter, setEditingWorkCenter] =
useState<PmWorkCenter | null>(null);
const [viewingWorkCenter, setViewingWorkCenter] =
useState<PmWorkCenter | null>(null);
const [selectedWorkCenters, setSelectedWorkCenters] = useState<string[]>([]);
const [workCenters, setWorkCenters] =
useState<PmWorkCenter[]>(mockWorkCenters);
const [viewMode, setViewMode] = useState<"cards" | "list">("cards");
const filteredWorkCenters = workCenters.filter((workCenter) => { const filteredWorkCenters = workCenters.filter((workCenter) => {
const matchesSearch = const matchesSearch =
workCenter.code.toLowerCase().includes(searchTerm.toLowerCase()) || workCenter.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
workCenter.name.toLowerCase().includes(searchTerm.toLowerCase()) || workCenter.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
workCenter.manufacturer workCenter.manufacturer?.toLowerCase().includes(searchTerm.toLowerCase()) ||
?.toLowerCase() workCenter.location.toLowerCase().includes(searchTerm.toLowerCase())
.includes(searchTerm.toLowerCase()) || const matchesStatus = statusFilter === 'all' || workCenter.status === statusFilter
workCenter.location.toLowerCase().includes(searchTerm.toLowerCase());
const matchesStatus =
statusFilter === "all" || workCenter.status === statusFilter;
const matchesCriticality = const matchesCriticality =
criticalityFilter === "all" || criticalityFilter === 'all' || workCenter.criticality === criticalityFilter
workCenter.criticality === criticalityFilter; return matchesSearch && matchesStatus && matchesCriticality
return matchesSearch && matchesStatus && matchesCriticality; })
});
const isWarrantyExpiring = (workCenter: PmWorkCenter) => { const isWarrantyExpiring = (workCenter: PmWorkCenter) => {
if (!workCenter.warrantyExpiry) return false; if (!workCenter.warrantyExpiry) return false
const today = new Date(); const today = new Date()
const diffTime = workCenter.warrantyExpiry.getTime() - today.getTime(); const diffTime = workCenter.warrantyExpiry.getTime() - today.getTime()
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
return diffDays <= 90 && diffDays > 0; return diffDays <= 90 && diffDays > 0
}; }
const isWarrantyExpired = (workCenter: PmWorkCenter) => { const isWarrantyExpired = (workCenter: PmWorkCenter) => {
if (!workCenter.warrantyExpiry) return false; if (!workCenter.warrantyExpiry) return false
const today = new Date(); const today = new Date()
return workCenter.warrantyExpiry < today; return workCenter.warrantyExpiry < today
}; }
const handleAddWorkCenter = () => { const handleAddWorkCenter = () => {
setEditingWorkCenter(null); setEditingWorkCenter(null)
setShowModal(true); setShowModal(true)
}; }
const handleView = (workCenter: PmWorkCenter) => { const handleView = (workCenter: PmWorkCenter) => {
setViewingWorkCenter(workCenter); setViewingWorkCenter(workCenter)
setShowViewModal(true); setShowViewModal(true)
}; }
const handleEdit = (workCenter: PmWorkCenter) => { const handleEdit = (workCenter: PmWorkCenter) => {
setEditingWorkCenter(workCenter); setEditingWorkCenter(workCenter)
setShowEditModal(true); setShowEditModal(true)
}; }
const handleSaveNewWorkCenter = (workCenter: Partial<PmWorkCenter>) => { const handleSaveNewWorkCenter = (workCenter: Partial<PmWorkCenter>) => {
const newWorkCenter: PmWorkCenter = { const newWorkCenter: PmWorkCenter = {
@ -109,85 +98,82 @@ const WorkCenterCards: React.FC = () => {
id: `EQP${Date.now()}`, id: `EQP${Date.now()}`,
creationTime: new Date(), creationTime: new Date(),
lastModificationTime: new Date(), lastModificationTime: new Date(),
} as PmWorkCenter; } as PmWorkCenter
setWorkCenters([...workCenters, newWorkCenter]); setWorkCenters([...workCenters, newWorkCenter])
}; }
const handleSaveEditWorkCenter = (workCenter: PmWorkCenter) => { const handleSaveEditWorkCenter = (workCenter: PmWorkCenter) => {
setWorkCenters( setWorkCenters(workCenters.map((eq) => (eq.id === workCenter.id ? workCenter : eq)))
workCenters.map((eq) => (eq.id === workCenter.id ? workCenter : eq)) }
);
};
const handleCreateMaintenancePlan = (plans: PmMaintenancePlan[]) => { const handleCreateMaintenancePlan = (plans: PmMaintenancePlan[]) => {
// In a real app, this would save to the backend // In a real app, this would save to the backend
console.log("Creating maintenance plans:", plans); console.log('Creating maintenance plans:', plans)
alert(`${plans.length} bakım planı oluşturuldu!`); alert(`${plans.length} bakım planı oluşturuldu!`)
setSelectedWorkCenters([]); setSelectedWorkCenters([])
}; }
const handleUpdateStatus = (updatedWorkCenters: PmWorkCenter[]) => { const handleUpdateStatus = (updatedWorkCenters: PmWorkCenter[]) => {
// Update work centers in state // Update work centers in state
const updatedIds = updatedWorkCenters.map((eq) => eq.id); const updatedIds = updatedWorkCenters.map((eq) => eq.id)
setWorkCenters( setWorkCenters(
workCenters.map((eq) => workCenters.map((eq) =>
updatedIds.includes(eq.id) updatedIds.includes(eq.id)
? updatedWorkCenters.find((updated) => updated.id === eq.id) || eq ? updatedWorkCenters.find((updated) => updated.id === eq.id) || eq
: eq : eq,
),
) )
); setSelectedWorkCenters([])
setSelectedWorkCenters([]); alert(`${updatedWorkCenters.length} iş merkezinin durumu güncellendi!`)
alert(`${updatedWorkCenters.length} iş merkezinin durumu güncellendi!`); }
};
const getSelectedWorkCenterObjects = () => { const getSelectedWorkCenterObjects = () => {
return workCenters.filter((eq) => selectedWorkCenters.includes(eq.id)); return workCenters.filter((eq) => selectedWorkCenters.includes(eq.id))
}; }
const handleSelectWorkCenter = (workCenterId: string) => { const handleSelectWorkCenter = (workCenterId: string) => {
setSelectedWorkCenters((prev) => setSelectedWorkCenters((prev) =>
prev.includes(workCenterId) prev.includes(workCenterId)
? prev.filter((id) => id !== workCenterId) ? prev.filter((id) => id !== workCenterId)
: [...prev, workCenterId] : [...prev, workCenterId],
); )
}; }
const getWorkCenterAge = (installationDate: Date) => { const getWorkCenterAge = (installationDate: Date) => {
const today = new Date(); const today = new Date()
const diffTime = today.getTime() - installationDate.getTime(); const diffTime = today.getTime() - installationDate.getTime()
const diffYears = Math.floor(diffTime / (1000 * 60 * 60 * 24 * 365)); const diffYears = Math.floor(diffTime / (1000 * 60 * 60 * 24 * 365))
return diffYears; return diffYears
}; }
return ( return (
<div className="space-y-3 pt-2"> <Container>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-xl font-bold text-gray-900">İş Merkezleri</h2> <h2 className="text-xl font-bold text-gray-900">İş Merkezleri</h2>
<p className="text-gray-600 mt-1"> <p className="text-gray-600 mt-1">Tüm merkezlerini yönetin ve takip edin</p>
Tüm merkezlerini yönetin ve takip edin
</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"> <div className="flex bg-gray-100 rounded-lg">
<button <button
onClick={() => setViewMode("cards")} onClick={() => setViewMode('cards')}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${ className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
viewMode === "cards" viewMode === 'cards'
? "bg-white text-gray-900 shadow-sm" ? 'bg-white text-gray-900 shadow-sm'
: "text-gray-500 hover:text-gray-700" : 'text-gray-500 hover:text-gray-700'
}`} }`}
> >
<FaTh className="w-4 h-4 inline" /> <FaTh className="w-4 h-4 inline" />
</button> </button>
<button <button
onClick={() => setViewMode("list")} onClick={() => setViewMode('list')}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${ className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
viewMode === "list" viewMode === 'list'
? "bg-white text-gray-900 shadow-sm" ? 'bg-white text-gray-900 shadow-sm'
: "text-gray-500 hover:text-gray-700" : 'text-gray-500 hover:text-gray-700'
}`} }`}
> >
<FaList className="w-4 h-4 inline" /> <FaList className="w-4 h-4 inline" />
@ -206,31 +192,18 @@ const WorkCenterCards: React.FC = () => {
{/* Summary Cards */} {/* Summary Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Widget <Widget title="Toplam İş Merkezi" value={workCenters.length} color="blue" icon="FaCog" />
title="Toplam İş Merkezi"
value={workCenters.length}
color="blue"
icon="FaCog"
/>
<Widget <Widget
title="Operasyonel" title="Operasyonel"
value={ value={workCenters.filter((e) => e.status === WorkCenterStatusEnum.Operational).length}
workCenters.filter(
(e) => e.status === WorkCenterStatusEnum.Operational
).length
}
color="green" color="green"
icon="FaCheckCircle" icon="FaCheckCircle"
/> />
<Widget <Widget
title="Arızalı" title="Arızalı"
value={ value={workCenters.filter((e) => e.status === WorkCenterStatusEnum.OutOfOrder).length}
workCenters.filter(
(e) => e.status === WorkCenterStatusEnum.OutOfOrder
).length
}
color="red" color="red"
icon="FaExclamationTriangle" icon="FaExclamationTriangle"
/> />
@ -238,9 +211,7 @@ const WorkCenterCards: React.FC = () => {
<Widget <Widget
title="Bakımda" title="Bakımda"
value={ value={
workCenters.filter( workCenters.filter((e) => e.status === WorkCenterStatusEnum.UnderMaintenance).length
(e) => e.status === WorkCenterStatusEnum.UnderMaintenance
).length
} }
color="orange" color="orange"
icon="FaWrench" icon="FaWrench"
@ -263,18 +234,12 @@ const WorkCenterCards: React.FC = () => {
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<select <select
value={statusFilter} value={statusFilter}
onChange={(e) => onChange={(e) => setStatusFilter(e.target.value as WorkCenterStatusEnum | 'all')}
setStatusFilter(e.target.value as WorkCenterStatusEnum | "all")
}
className="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="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"
> >
<option value="all">Tüm Durumlar</option> <option value="all">Tüm Durumlar</option>
<option value={WorkCenterStatusEnum.Operational}> <option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
Operasyonel <option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
</option>
<option value={WorkCenterStatusEnum.UnderMaintenance}>
Bakımda
</option>
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option> <option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
<option value={WorkCenterStatusEnum.Retired}>Emekli</option> <option value={WorkCenterStatusEnum.Retired}>Emekli</option>
</select> </select>
@ -282,11 +247,7 @@ const WorkCenterCards: React.FC = () => {
<div className="relative"> <div className="relative">
<select <select
value={criticalityFilter} value={criticalityFilter}
onChange={(e) => onChange={(e) => setCriticalityFilter(e.target.value as CriticalityLevelEnum | 'all')}
setCriticalityFilter(
e.target.value as CriticalityLevelEnum | "all"
)
}
className="pl-4 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="pl-4 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
> >
<option value="all">Tüm Kritiklik</option> <option value="all">Tüm Kritiklik</option>
@ -299,7 +260,7 @@ const WorkCenterCards: React.FC = () => {
</div> </div>
{/* Work Center Content */} {/* Work Center Content */}
{viewMode === "cards" ? ( {viewMode === 'cards' ? (
/* Work Center Cards */ /* Work Center Cards */
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4">
{filteredWorkCenters.map((workCenter) => ( {filteredWorkCenters.map((workCenter) => (
@ -316,24 +277,18 @@ const WorkCenterCards: React.FC = () => {
onChange={() => handleSelectWorkCenter(workCenter.id)} onChange={() => handleSelectWorkCenter(workCenter.id)}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500" className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/> />
<h3 className="text-base font-semibold text-gray-900"> <h3 className="text-base font-semibold text-gray-900">{workCenter.code}</h3>
{workCenter.code}
</h3>
<span <span
className={`px-2 py-0.5 rounded-full text-xs font-medium flex items-center space-x-1 ${getWorkCenterStatusColor( className={`px-2 py-0.5 rounded-full text-xs font-medium flex items-center space-x-1 ${getWorkCenterStatusColor(
workCenter.status workCenter.status,
)}`} )}`}
> >
{getWorkCenterStatusIcon(workCenter.status)} {getWorkCenterStatusIcon(workCenter.status)}
<span>{getWorkCenterStatusText(workCenter.status)}</span> <span>{getWorkCenterStatusText(workCenter.status)}</span>
</span> </span>
</div> </div>
<h4 className="font-medium text-gray-700 mb-1 text-sm"> <h4 className="font-medium text-gray-700 mb-1 text-sm">{workCenter.name}</h4>
{workCenter.name} <p className="text-xs text-gray-500 mb-1.5">{workCenter.description}</p>
</h4>
<p className="text-xs text-gray-500 mb-1.5">
{workCenter.description}
</p>
</div> </div>
<div className="flex space-x-1"> <div className="flex space-x-1">
<button <button
@ -369,9 +324,7 @@ const WorkCenterCards: React.FC = () => {
</div> </div>
<div> <div>
<span className="text-gray-500">Seri No</span> <span className="text-gray-500">Seri No</span>
<p className="font-medium text-gray-900"> <p className="font-medium text-gray-900">{workCenter.serialNumber}</p>
{workCenter.serialNumber}
</p>
</div> </div>
<div> <div>
<span className="text-gray-500">Konum</span> <span className="text-gray-500">Konum</span>
@ -391,14 +344,12 @@ const WorkCenterCards: React.FC = () => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span <span
className={`px-2 py-0.5 rounded-full text-xs font-medium ${getCriticalityLevelColor( className={`px-2 py-0.5 rounded-full text-xs font-medium ${getCriticalityLevelColor(
workCenter.criticality workCenter.criticality,
)}`} )}`}
> >
{getCriticalityLevelText(workCenter.criticality)} Kritiklik {getCriticalityLevelText(workCenter.criticality)} Kritiklik
</span> </span>
<span className="text-xs text-gray-500"> <span className="text-xs text-gray-500">{workCenter.workCenterType?.name}</span>
{workCenter.workCenterType?.name}
</span>
</div> </div>
{workCenter.warrantyExpiry && ( {workCenter.warrantyExpiry && (
@ -408,23 +359,18 @@ const WorkCenterCards: React.FC = () => {
<span <span
className={`font-medium ${ className={`font-medium ${
isWarrantyExpired(workCenter) isWarrantyExpired(workCenter)
? "text-red-600" ? 'text-red-600'
: isWarrantyExpiring(workCenter) : isWarrantyExpiring(workCenter)
? "text-orange-600" ? 'text-orange-600'
: "text-gray-900" : 'text-gray-900'
}`} }`}
> >
{workCenter.warrantyExpiry.toLocaleDateString("tr-TR")} {workCenter.warrantyExpiry.toLocaleDateString('tr-TR')}
{isWarrantyExpiring(workCenter) && {isWarrantyExpiring(workCenter) && !isWarrantyExpired(workCenter) && (
!isWarrantyExpired(workCenter) && ( <span className="text-xs text-orange-600 ml-1">(Yakında)</span>
<span className="text-xs text-orange-600 ml-1">
(Yakında)
</span>
)} )}
{isWarrantyExpired(workCenter) && ( {isWarrantyExpired(workCenter) && (
<span className="text-xs text-red-600 ml-1"> <span className="text-xs text-red-600 ml-1">(Doldu)</span>
(Doldu)
</span>
)} )}
</span> </span>
</div> </div>
@ -435,15 +381,11 @@ const WorkCenterCards: React.FC = () => {
{/* Specifications Preview */} {/* Specifications Preview */}
{workCenter.specifications.length > 0 && ( {workCenter.specifications.length > 0 && (
<div className="border-t border-gray-100 pt-2"> <div className="border-t border-gray-100 pt-2">
<h5 className="text-xs font-medium text-gray-700 mb-2"> <h5 className="text-xs font-medium text-gray-700 mb-2">Teknik Özellikler</h5>
Teknik Özellikler
</h5>
<div className="grid grid-cols-2 gap-1.5 text-xs"> <div className="grid grid-cols-2 gap-1.5 text-xs">
{workCenter.specifications.slice(0, 4).map((spec) => ( {workCenter.specifications.slice(0, 4).map((spec) => (
<div key={spec.id} className="flex justify-between"> <div key={spec.id} className="flex justify-between">
<span className="text-gray-500"> <span className="text-gray-500">{spec.specificationName}:</span>
{spec.specificationName}:
</span>
<span className="text-gray-900 font-medium"> <span className="text-gray-900 font-medium">
{spec.specificationValue} {spec.unit} {spec.specificationValue} {spec.unit}
</span> </span>
@ -465,17 +407,14 @@ const WorkCenterCards: React.FC = () => {
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<FaCalendar className="w-3 h-3" /> <FaCalendar className="w-3 h-3" />
<span> <span>
Kurulum:{" "} Kurulum: {workCenter.installationDate.toLocaleDateString('tr-TR')}
{workCenter.installationDate.toLocaleDateString("tr-TR")}
</span> </span>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<FaChartBar className="w-3 h-3" /> <FaChartBar className="w-3 h-3" />
<span> <span>
Son Güncelleme:{" "} Son Güncelleme:{' '}
{workCenter.lastModificationTime.toLocaleDateString( {workCenter.lastModificationTime.toLocaleDateString('tr-TR')}
"tr-TR"
)}
</span> </span>
</div> </div>
</div> </div>
@ -494,17 +433,14 @@ const WorkCenterCards: React.FC = () => {
<input <input
type="checkbox" type="checkbox"
checked={ checked={
selectedWorkCenters.length === selectedWorkCenters.length === filteredWorkCenters.length &&
filteredWorkCenters.length &&
filteredWorkCenters.length > 0 filteredWorkCenters.length > 0
} }
onChange={(e) => { onChange={(e) => {
if (e.target.checked) { if (e.target.checked) {
setSelectedWorkCenters( setSelectedWorkCenters(filteredWorkCenters.map((eq) => eq.id))
filteredWorkCenters.map((eq) => eq.id)
);
} else { } else {
setSelectedWorkCenters([]); setSelectedWorkCenters([])
} }
}} }}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500" className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
@ -555,9 +491,7 @@ const WorkCenterCards: React.FC = () => {
<div className="text-sm font-medium text-gray-900"> <div className="text-sm font-medium text-gray-900">
{workCenter.code} {workCenter.code}
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">{workCenter.name}</div>
{workCenter.name}
</div>
<div className="text-xs text-gray-400"> <div className="text-xs text-gray-400">
{workCenter.manufacturer} {workCenter.model} {workCenter.manufacturer} {workCenter.model}
</div> </div>
@ -567,19 +501,17 @@ const WorkCenterCards: React.FC = () => {
<td className="px-2 py-2 whitespace-nowrap"> <td className="px-2 py-2 whitespace-nowrap">
<span <span
className={`inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${getWorkCenterStatusColor( className={`inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${getWorkCenterStatusColor(
workCenter.status workCenter.status,
)}`} )}`}
> >
{getWorkCenterStatusIcon(workCenter.status)} {getWorkCenterStatusIcon(workCenter.status)}
<span className="ml-1"> <span className="ml-1">{getWorkCenterStatusText(workCenter.status)}</span>
{getWorkCenterStatusText(workCenter.status)}
</span>
</span> </span>
</td> </td>
<td className="px-2 py-2 whitespace-nowrap"> <td className="px-2 py-2 whitespace-nowrap">
<span <span
className={`inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${getCriticalityLevelColor( className={`inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${getCriticalityLevelColor(
workCenter.criticality workCenter.criticality,
)}`} )}`}
> >
{getCriticalityLevelText(workCenter.criticality)} {getCriticalityLevelText(workCenter.criticality)}
@ -599,20 +531,15 @@ const WorkCenterCards: React.FC = () => {
<span <span
className={`${ className={`${
isWarrantyExpired(workCenter) isWarrantyExpired(workCenter)
? "text-red-600" ? 'text-red-600'
: isWarrantyExpiring(workCenter) : isWarrantyExpiring(workCenter)
? "text-orange-600" ? 'text-orange-600'
: "text-gray-900" : 'text-gray-900'
}`} }`}
> >
{workCenter.warrantyExpiry.toLocaleDateString( {workCenter.warrantyExpiry.toLocaleDateString('tr-TR')}
"tr-TR" {isWarrantyExpiring(workCenter) && !isWarrantyExpired(workCenter) && (
)} <div className="text-xs text-orange-600">Yakında dolacak</div>
{isWarrantyExpiring(workCenter) &&
!isWarrantyExpired(workCenter) && (
<div className="text-xs text-orange-600">
Yakında dolacak
</div>
)} )}
{isWarrantyExpired(workCenter) && ( {isWarrantyExpired(workCenter) && (
<div className="text-xs text-red-600">Dolmuş</div> <div className="text-xs text-red-600">Dolmuş</div>
@ -657,9 +584,7 @@ const WorkCenterCards: React.FC = () => {
{filteredWorkCenters.length === 0 && ( {filteredWorkCenters.length === 0 && (
<div className="text-center py-10"> <div className="text-center py-10">
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-3" /> <FaCog className="w-12 h-12 text-gray-400 mx-auto mb-3" />
<h3 className="text-base font-medium text-gray-900 mb-1.5"> <h3 className="text-base font-medium text-gray-900 mb-1.5">İş Merkezi bulunamadı</h3>
İş Merkezi bulunamadı
</h3>
<p className="text-sm text-gray-500 mb-3"> <p className="text-sm text-gray-500 mb-3">
Arama kriterlerinizi değiştirin veya yeni bir merkezi ekleyin. Arama kriterlerinizi değiştirin veya yeni bir merkezi ekleyin.
</p> </p>
@ -702,6 +627,7 @@ const WorkCenterCards: React.FC = () => {
</div> </div>
</div> </div>
)} )}
</div>
{/* Modal for Add/Edit */} {/* Modal for Add/Edit */}
<NewWorkCenterModal <NewWorkCenterModal
@ -744,8 +670,8 @@ const WorkCenterCards: React.FC = () => {
onSave={handleUpdateStatus} onSave={handleUpdateStatus}
selectedWorkCenters={getSelectedWorkCenterObjects()} selectedWorkCenters={getSelectedWorkCenterObjects()}
/> />
</div> </Container>
); )
}; }
export default WorkCenterCards; export default WorkCenterCards

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,7 +76,8 @@ const WorkCenterList: React.FC = () => {
} }
return ( return (
<div className="space-y-3 pt-2"> <Container>
<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-3"> <div className="flex items-center space-x-3">
@ -193,8 +195,8 @@ const WorkCenterList: React.FC = () => {
<div> <div>
<p className="text-sm font-medium text-gray-600">Operasyonel</p> <p className="text-sm font-medium text-gray-600">Operasyonel</p>
<p className="text-xl font-bold text-green-600"> <p className="text-xl font-bold text-green-600">
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational).length || {workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational)
0} .length || 0}
</p> </p>
</div> </div>
<FaCheckCircle className="h-8 w-8 text-green-600" /> <FaCheckCircle className="h-8 w-8 text-green-600" />
@ -399,6 +401,7 @@ const WorkCenterList: React.FC = () => {
)} )}
</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,81 +10,78 @@ 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>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-xl font-bold text-gray-900"> <h2 className="text-xl font-bold text-gray-900">Ürün Ağaçları (BOM)</h2>
Ürün Ağaçları (BOM)
</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>
@ -123,24 +120,19 @@ const BOMManagement: React.FC = () => {
<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}
</h4>
<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 ${getBOMTypeColor(
bom.bomType bom.bomType,
)}`} )}`}
> >
{getBOMTypeName(bom.bomType)} {getBOMTypeName(bom.bomType)}
</span> </span>
</div> </div>
<p className="text-sm text-gray-600 mb-0.5"> <p className="text-sm text-gray-600 mb-0.5">
Malzeme: {bom.material?.code} - {bom.material?.name} - v Malzeme: {bom.material?.code} - {bom.material?.name} - v{bom.version}
{bom.version}
</p>
<p className="text-sm text-gray-500">
Temel Miktar: {bom.baseQuantity}
</p> </p>
<p className="text-sm text-gray-500">Temel Miktar: {bom.baseQuantity}</p>
</div> </div>
<div className="flex space-x-1"> <div className="flex space-x-1">
<button <button
@ -180,8 +172,7 @@ const BOMManagement: React.FC = () => {
Bileşenler Bileşenler
</span> </span>
<span className="font-medium"> <span className="font-medium">
{getActiveComponents(bom.components)}/ {getActiveComponents(bom.components)}/{getTotalComponents(bom.components)}
{getTotalComponents(bom.components)}
</span> </span>
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -189,15 +180,11 @@ const BOMManagement: React.FC = () => {
<FaClock className="w-4 h-4 mr-1" /> <FaClock className="w-4 h-4 mr-1" />
Toplam Süre Toplam Süre
</span> </span>
<span className="font-medium"> <span className="font-medium">{getTotalOperationTime(bom.operations)} dk</span>
{getTotalOperationTime(bom.operations)} dk
</span>
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-gray-500">Geçerlilik</span> <span className="text-gray-500">Geçerlilik</span>
<span className="font-medium"> <span className="font-medium">{bom.validFrom.toLocaleDateString('tr-TR')}</span>
{bom.validFrom.toLocaleDateString("tr-TR")}
</span>
</div> </div>
</div> </div>
@ -206,12 +193,10 @@ const BOMManagement: React.FC = () => {
<div className="flex space-x-2"> <div className="flex space-x-2">
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
bom.isActive bom.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
}`} }`}
> >
{bom.isActive ? "Aktif" : "Pasif"} {bom.isActive ? 'Aktif' : 'Pasif'}
</span> </span>
{bom.validTo && new Date() > bom.validTo && ( {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"> <span className="px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 flex items-center">
@ -221,7 +206,7 @@ const BOMManagement: React.FC = () => {
)} )}
</div> </div>
<span className="text-xs text-gray-400"> <span className="text-xs text-gray-400">
{bom.lastModificationTime.toLocaleDateString("tr-TR")} {bom.lastModificationTime.toLocaleDateString('tr-TR')}
</span> </span>
</div> </div>
</div> </div>
@ -230,9 +215,7 @@ const BOMManagement: React.FC = () => {
{filteredBOMs.length === 0 && ( {filteredBOMs.length === 0 && (
<div className="text-center py-8"> <div className="text-center py-8">
<FaCodeBranch className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <FaCodeBranch 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 bulunamadı</h3>
BOM bulunamadı
</h3>
<p className="text-gray-500"> <p className="text-gray-500">
Arama kriterlerinizi değiştirin veya yeni bir BOM ekleyin. Arama kriterlerinizi değiştirin veya yeni bir BOM ekleyin.
</p> </p>
@ -246,13 +229,10 @@ const BOMManagement: React.FC = () => {
{selectedBOM ? ( {selectedBOM ? (
<div className="bg-white rounded-lg shadow-md border border-gray-200 p-3"> <div className="bg-white rounded-lg shadow-md border border-gray-200 p-3">
<div className="mb-3"> <div className="mb-3">
<h4 className="font-semibold text-gray-900 mb-2"> <h4 className="font-semibold text-gray-900 mb-2">{selectedBOM.bomCode}</h4>
{selectedBOM.bomCode}
</h4>
<div className="grid grid-cols-2 gap-1 text-sm text-gray-600"> <div className="grid grid-cols-2 gap-1 text-sm text-gray-600">
<div> <div>
Malzeme: {selectedBOM.material?.code} -{" "} Malzeme: {selectedBOM.material?.code} - {selectedBOM.material?.name}
{selectedBOM.material?.name}
</div> </div>
<div>Versiyon: {selectedBOM.version}</div> <div>Versiyon: {selectedBOM.version}</div>
<div>Tip: {getBOMTypeName(selectedBOM.bomType)}</div> <div>Tip: {getBOMTypeName(selectedBOM.bomType)}</div>
@ -274,7 +254,7 @@ const BOMManagement: React.FC = () => {
> >
<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">
{component.position}. {component.material?.code} -{" "} {component.position}. {component.material?.code} -{' '}
{component.material?.name} {component.material?.name}
</span> </span>
<div className="flex space-x-2"> <div className="flex space-x-2">
@ -286,17 +266,17 @@ const BOMManagement: React.FC = () => {
<span <span
className={`text-xs px-1 rounded ${ className={`text-xs px-1 rounded ${
component.isActive component.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'
}`} }`}
> >
{component.isActive ? "Aktif" : "Pasif"} {component.isActive ? 'Aktif' : 'Pasif'}
</span> </span>
</div> </div>
</div> </div>
<div className="text-gray-600"> <div className="text-gray-600">
Miktar: {component.quantity} {component.unitId} | Fire: Miktar: {component.quantity} {component.unitId} | Fire: %
%{component.scrapPercentage} {component.scrapPercentage}
</div> </div>
</div> </div>
))} ))}
@ -317,23 +297,22 @@ const BOMManagement: React.FC = () => {
> >
<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>
<div className="text-gray-600"> <div className="text-gray-600">
Hazırlık: {operation.setupTime}dk | İşlem:{" "} Hazırlık: {operation.setupTime}dk | İşlem: {operation.runTime}dk | Kuyruk:{' '}
{operation.runTime}dk | Kuyruk: {operation.queueTime}dk {operation.queueTime}dk | Taşıma: {operation.moveTime}dk
| Taşıma: {operation.moveTime}dk
</div> </div>
</div> </div>
))} ))}
@ -346,13 +325,12 @@ const BOMManagement: React.FC = () => {
<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"> <p className="text-gray-500">Detaylarını görmek için sol taraftan bir BOM seçin.</p>
Detaylarını görmek için sol taraftan bir BOM seçin.
</p>
</div> </div>
)} )}
</div> </div>
</div> </div>
</div>
<BOMFormModal <BOMFormModal
open={showModal} open={showModal}
@ -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

View file

@ -1,60 +1,43 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import { FaArrowUp, FaBullseye, FaPlus, FaEdit, FaTrash, FaCalculator } from 'react-icons/fa'
FaArrowUp,
FaBullseye,
FaPlus,
FaEdit,
FaTrash,
FaCalculator,
} from "react-icons/fa";
import { import {
MrpDemandForecast, MrpDemandForecast,
ForecastMethodEnum, ForecastMethodEnum,
MrpMaterialRequirement, MrpMaterialRequirement,
RequirementSourceTypeEnum, RequirementSourceTypeEnum,
} from "../../../types/mrp"; } from '../../../types/mrp'
import DataTable, { Column } from "../../../components/common/DataTable"; import DataTable, { Column } from '../../../components/common/DataTable'
import { import { mockDemandForecasts, mockMaterialRequirements } from '../../../mocks/mockDemandPlanning'
mockDemandForecasts, import { mockMaterials } from '../../../mocks/mockMaterials'
mockMaterialRequirements, import DemandForecastFormModal from './DemandForecastFormModal'
} from "../../../mocks/mockDemandPlanning"; import MaterialRequirementFormModal from './MaterialRequirementFormModal'
import { mockMaterials } from "../../../mocks/mockMaterials"; import Widget from '../../../components/common/Widget'
import DemandForecastFormModal from "./DemandForecastFormModal"; import { Container } from '@/components/shared'
import MaterialRequirementFormModal from "./MaterialRequirementFormModal";
import Widget from "../../../components/common/Widget";
const DemandPlanning: React.FC = () => { const DemandPlanning: React.FC = () => {
// Mock data - in a real app, this would come from a store/API // Mock data - in a real app, this would come from a store/API
const [demandForecasts, setDemandForecasts] = const [demandForecasts, setDemandForecasts] = useState<MrpDemandForecast[]>(mockDemandForecasts)
useState<MrpDemandForecast[]>(mockDemandForecasts); const [materialRequirements, setMaterialRequirements] =
const [materialRequirements, setMaterialRequirements] = useState< useState<MrpMaterialRequirement[]>(mockMaterialRequirements)
MrpMaterialRequirement[] const [searchTerm, setSearchTerm] = useState('')
>(mockMaterialRequirements); const [selectedMethod, setSelectedMethod] = useState<ForecastMethodEnum | 'all'>('all')
const [searchTerm, setSearchTerm] = useState(""); const [activeTab, setActiveTab] = useState<'forecasts' | 'requirements'>('forecasts')
const [selectedMethod, setSelectedMethod] = useState<
ForecastMethodEnum | "all"
>("all");
const [activeTab, setActiveTab] = useState<"forecasts" | "requirements">(
"forecasts"
);
// Modal state // Modal state
const [isForecastModalOpen, setIsForecastModalOpen] = useState(false); const [isForecastModalOpen, setIsForecastModalOpen] = useState(false)
const [editingForecast, setEditingForecast] = const [editingForecast, setEditingForecast] = useState<MrpDemandForecast | null>(null)
useState<MrpDemandForecast | null>(null); const [isRequirementModalOpen, setIsRequirementModalOpen] = useState(false)
const [isRequirementModalOpen, setIsRequirementModalOpen] = useState(false); const [editingRequirement, setEditingRequirement] = useState<MrpMaterialRequirement | null>(null)
const [editingRequirement, setEditingRequirement] =
useState<MrpMaterialRequirement | null>(null);
// Event handlers // Event handlers
const handleAddForecast = () => { const handleAddForecast = () => {
console.log("Add forecast"); console.log('Add forecast')
setEditingForecast(null); setEditingForecast(null)
setIsForecastModalOpen(true); setIsForecastModalOpen(true)
}; }
const handleSaveForecast = ( const handleSaveForecast = (
forecastData: Omit<MrpDemandForecast, "id" | "material"> & { id?: string } forecastData: Omit<MrpDemandForecast, 'id' | 'material'> & { id?: string },
) => { ) => {
if (editingForecast && forecastData.id) { if (editingForecast && forecastData.id) {
// Update existing forecast // Update existing forecast
@ -63,10 +46,10 @@ const DemandPlanning: React.FC = () => {
...forecastData, ...forecastData,
material: mockMaterials.find((m) => m.id === forecastData.materialId), material: mockMaterials.find((m) => m.id === forecastData.materialId),
lastModificationTime: new Date(), lastModificationTime: new Date(),
}; }
setDemandForecasts((prev) => setDemandForecasts((prev) =>
prev.map((f) => (f.id === forecastData.id ? updatedForecast : f)) prev.map((f) => (f.id === forecastData.id ? updatedForecast : f)),
); )
} else { } else {
// Create new forecast // Create new forecast
const newForecast: MrpDemandForecast = { const newForecast: MrpDemandForecast = {
@ -75,37 +58,37 @@ const DemandPlanning: React.FC = () => {
material: mockMaterials.find((m) => m.id === forecastData.materialId), material: mockMaterials.find((m) => m.id === forecastData.materialId),
creationTime: new Date(), creationTime: new Date(),
lastModificationTime: new Date(), lastModificationTime: new Date(),
};
setDemandForecasts((prev) => [...prev, newForecast]);
} }
handleCloseModal(); setDemandForecasts((prev) => [...prev, newForecast])
}; }
handleCloseModal()
}
const handleCloseModal = () => { const handleCloseModal = () => {
setIsForecastModalOpen(false); setIsForecastModalOpen(false)
setEditingForecast(null); setEditingForecast(null)
setIsRequirementModalOpen(false); setIsRequirementModalOpen(false)
setEditingRequirement(null); setEditingRequirement(null)
}; }
const handleEditForecast = (forecast: MrpDemandForecast) => { const handleEditForecast = (forecast: MrpDemandForecast) => {
console.log("Edit forecast:", forecast); console.log('Edit forecast:', forecast)
setEditingForecast(forecast); setEditingForecast(forecast)
setIsForecastModalOpen(true); setIsForecastModalOpen(true)
}; }
const handleAddRequirement = () => { const handleAddRequirement = () => {
setEditingRequirement(null); setEditingRequirement(null)
setIsRequirementModalOpen(true); setIsRequirementModalOpen(true)
}; }
const handleEditRequirement = (requirement: MrpMaterialRequirement) => { const handleEditRequirement = (requirement: MrpMaterialRequirement) => {
setEditingRequirement(requirement); setEditingRequirement(requirement)
setIsRequirementModalOpen(true); setIsRequirementModalOpen(true)
}; }
const handleSaveRequirement = ( const handleSaveRequirement = (
reqData: Omit<MrpMaterialRequirement, "id" | "material"> & { id?: string } reqData: Omit<MrpMaterialRequirement, 'id' | 'material'> & { id?: string },
) => { ) => {
if (editingRequirement && reqData.id) { if (editingRequirement && reqData.id) {
// Update // Update
@ -113,102 +96,90 @@ const DemandPlanning: React.FC = () => {
...editingRequirement, ...editingRequirement,
...reqData, ...reqData,
material: mockMaterials.find((m) => m.id === reqData.materialId), material: mockMaterials.find((m) => m.id === reqData.materialId),
}; }
setMaterialRequirements((prev) => setMaterialRequirements((prev) => prev.map((r) => (r.id === reqData.id ? updatedReq : r)))
prev.map((r) => (r.id === reqData.id ? updatedReq : r))
);
} else { } else {
// Create // Create
const newReq: MrpMaterialRequirement = { const newReq: MrpMaterialRequirement = {
...reqData, ...reqData,
id: `mr-${Date.now()}`, id: `mr-${Date.now()}`,
material: mockMaterials.find((m) => m.id === reqData.materialId), material: mockMaterials.find((m) => m.id === reqData.materialId),
};
setMaterialRequirements((prev) => [...prev, newReq]);
} }
handleCloseModal(); setMaterialRequirements((prev) => [...prev, newReq])
}; }
handleCloseModal()
}
const handleDeleteForecast = (id: string) => { const handleDeleteForecast = (id: string) => {
console.log("Delete forecast:", id); console.log('Delete forecast:', id)
if (window.confirm("Bu tahmini silmek istediğinizden emin misiniz?")) { if (window.confirm('Bu tahmini silmek istediğinizden emin misiniz?')) {
setDemandForecasts((prev) => prev.filter((f) => f.id !== id)); setDemandForecasts((prev) => prev.filter((f) => f.id !== id))
}
} }
};
const handleDeleteRequirement = (id: string) => { const handleDeleteRequirement = (id: string) => {
if ( if (window.confirm('Bu malzeme ihtiyacını silmek istediğinizden emin misiniz?')) {
window.confirm( setMaterialRequirements((prev) => prev.filter((r) => r.id !== id))
"Bu malzeme ihtiyacını silmek istediğinizden emin misiniz?" }
)
) {
setMaterialRequirements((prev) => prev.filter((r) => r.id !== id));
} }
};
const handleCalculateDemand = () => { const handleCalculateDemand = () => {
// Mock implementation to simulate calculating actual demand and accuracy // Mock implementation to simulate calculating actual demand and accuracy
if ( if (
!window.confirm( !window.confirm(
"Mevcut tahminler için gerçekleşen talep ve doğruluk oranları hesaplanacaktır. Bu işlem geri alınamaz. Devam etmek istiyor musunuz?" 'Mevcut tahminler için gerçekleşen talep ve doğruluk oranları hesaplanacaktır. Bu işlem geri alınamaz. Devam etmek istiyor musunuz?',
) )
) { ) {
return; return
} }
setDemandForecasts((prevForecasts) => setDemandForecasts((prevForecasts) =>
prevForecasts.map((forecast) => { prevForecasts.map((forecast) => {
// Simulate an actual quantity, e.g., +/- 20% of the forecast // Simulate an actual quantity, e.g., +/- 20% of the forecast
const randomFactor = 0.8 + Math.random() * 0.4; // between 0.8 and 1.2 const randomFactor = 0.8 + Math.random() * 0.4 // between 0.8 and 1.2
const actualQuantity = Math.round( const actualQuantity = Math.round(forecast.forecastQuantity * randomFactor)
forecast.forecastQuantity * randomFactor
);
// Calculate accuracy: 100 - |(actual - forecast) / forecast| * 100 // Calculate accuracy: 100 - |(actual - forecast) / forecast| * 100
const accuracy = const accuracy =
forecast.forecastQuantity > 0 forecast.forecastQuantity > 0
? 100 - ? 100 -
(Math.abs(actualQuantity - forecast.forecastQuantity) / (Math.abs(actualQuantity - forecast.forecastQuantity) / forecast.forecastQuantity) *
forecast.forecastQuantity) *
100 100
: 0; : 0
return { return {
...forecast, ...forecast,
actualQuantity, actualQuantity,
accuracy: Math.max(0, accuracy), // Ensure accuracy is not negative accuracy: Math.max(0, accuracy), // Ensure accuracy is not negative
lastModificationTime: new Date(), lastModificationTime: new Date(),
}; }
}) }),
); )
alert("Talep ve doğruluk oranları başarıyla hesaplandı!"); alert('Talep ve doğruluk oranları başarıyla hesaplandı!')
}; }
const handleGenerateRequirements = () => { const handleGenerateRequirements = () => {
if ( if (
!window.confirm( !window.confirm(
"Mevcut tahminlerden yeni malzeme ihtiyaçları oluşturulacaktır. Devam etmek istiyor musunuz?" 'Mevcut tahminlerden yeni malzeme ihtiyaçları oluşturulacaktır. Devam etmek istiyor musunuz?',
) )
) { ) {
return; return
} }
const newRequirements: MrpMaterialRequirement[] = demandForecasts const newRequirements: MrpMaterialRequirement[] = demandForecasts
.filter( .filter(
(forecast) => (forecast) => !materialRequirements.some((req) => req.sourceDocumentId === forecast.id),
!materialRequirements.some(
(req) => req.sourceDocumentId === forecast.id
)
) // Henüz ihtiyacı oluşturulmamış olanları filtrele ) // Henüz ihtiyacı oluşturulmamış olanları filtrele
.map((forecast, index) => { .map((forecast, index) => {
const grossRequirement = forecast.forecastQuantity; const grossRequirement = forecast.forecastQuantity
// Mock projected available stock and scheduled receipts // Mock projected available stock and scheduled receipts
const projectedAvailable = Math.round(Math.random() * 500); const projectedAvailable = Math.round(Math.random() * 500)
const scheduledReceipts = Math.round(Math.random() * 200); const scheduledReceipts = Math.round(Math.random() * 200)
const netRequirement = Math.max( const netRequirement = Math.max(
0, 0,
grossRequirement - projectedAvailable - scheduledReceipts grossRequirement - projectedAvailable - scheduledReceipts,
); )
return { return {
id: `mr-${Date.now()}-${index}-${forecast.materialId}`, id: `mr-${Date.now()}-${index}-${forecast.materialId}`,
@ -226,127 +197,111 @@ const DemandPlanning: React.FC = () => {
requirementDate: forecast.startDate, requirementDate: forecast.startDate,
plannedReceiptDate: forecast.startDate, plannedReceiptDate: forecast.startDate,
plannedReleaseDate: new Date( plannedReleaseDate: new Date(
new Date(forecast.startDate).setDate( new Date(forecast.startDate).setDate(forecast.startDate.getDate() - 14),
forecast.startDate.getDate() - 14
)
), // 2 hafta önce ), // 2 hafta önce
creationTime: new Date(), creationTime: new Date(),
lastModificationTime: new Date(), lastModificationTime: new Date(),
}; }
}); })
if (newRequirements.length > 0) { if (newRequirements.length > 0) {
setMaterialRequirements((prev) => [...prev, ...newRequirements]); setMaterialRequirements((prev) => [...prev, ...newRequirements])
alert( alert(`${newRequirements.length} adet yeni malzeme ihtiyacı başarıyla oluşturuldu!`)
`${newRequirements.length} adet yeni malzeme ihtiyacı başarıyla oluşturuldu!` setActiveTab('requirements') // Kullanıcının sonucu görmesi için sekmeyi değiştir
);
setActiveTab("requirements"); // Kullanıcının sonucu görmesi için sekmeyi değiştir
} else { } else {
alert( alert(
"Mevcut tahminler için zaten malzeme ihtiyaçları oluşturulmuş veya yeni tahmin bulunmuyor." 'Mevcut tahminler için zaten malzeme ihtiyaçları oluşturulmuş veya yeni tahmin bulunmuyor.',
); )
}
} }
};
const filteredForecasts = demandForecasts.filter((forecast) => { const filteredForecasts = demandForecasts.filter((forecast) => {
if ( if (
searchTerm && searchTerm &&
!forecast.material?.name !forecast.material?.name?.toLowerCase().includes(searchTerm.toLowerCase()) &&
?.toLowerCase()
.includes(searchTerm.toLowerCase()) &&
!forecast.forecastPeriod.toLowerCase().includes(searchTerm.toLowerCase()) !forecast.forecastPeriod.toLowerCase().includes(searchTerm.toLowerCase())
) { ) {
return false; return false
} }
if ( if (selectedMethod !== 'all' && forecast.forecastMethod !== selectedMethod) {
selectedMethod !== "all" && return false
forecast.forecastMethod !== selectedMethod
) {
return false;
} }
return true; return true
}); })
const filteredRequirements = materialRequirements.filter((req) => { const filteredRequirements = materialRequirements.filter((req) => {
if ( if (searchTerm && !req.material?.name?.toLowerCase().includes(searchTerm.toLowerCase())) {
searchTerm && return false
!req.material?.name?.toLowerCase().includes(searchTerm.toLowerCase())
) {
return false;
} }
return true; return true
}); })
const getMethodLabel = (method: ForecastMethodEnum) => { const getMethodLabel = (method: ForecastMethodEnum) => {
const methodLabels = { const methodLabels = {
[ForecastMethodEnum.MovingAverage]: "Hareketli Ortalama", [ForecastMethodEnum.MovingAverage]: 'Hareketli Ortalama',
[ForecastMethodEnum.ExponentialSmoothing]: "Üstel Yumuşatma", [ForecastMethodEnum.ExponentialSmoothing]: 'Üstel Yumuşatma',
[ForecastMethodEnum.LinearRegression]: "Doğrusal Regresyon", [ForecastMethodEnum.LinearRegression]: 'Doğrusal Regresyon',
[ForecastMethodEnum.Seasonal]: "Mevsimsel", [ForecastMethodEnum.Seasonal]: 'Mevsimsel',
}; }
return methodLabels[method]; return methodLabels[method]
}; }
const getSourceTypeLabel = (sourceType: RequirementSourceTypeEnum) => { const getSourceTypeLabel = (sourceType: RequirementSourceTypeEnum) => {
const sourceLabels = { const sourceLabels = {
[RequirementSourceTypeEnum.SalesOrder]: "Satış Siparişi", [RequirementSourceTypeEnum.SalesOrder]: 'Satış Siparişi',
[RequirementSourceTypeEnum.Forecast]: "Tahmin", [RequirementSourceTypeEnum.Forecast]: 'Tahmin',
[RequirementSourceTypeEnum.SafetyStock]: "Güvenlik Stoku", [RequirementSourceTypeEnum.SafetyStock]: 'Güvenlik Stoku',
[RequirementSourceTypeEnum.ProductionOrder]: "Üretim Emri", [RequirementSourceTypeEnum.ProductionOrder]: 'Üretim Emri',
}; }
return sourceLabels[sourceType]; return sourceLabels[sourceType]
}; }
const getSourceTypeColor = (sourceType: RequirementSourceTypeEnum) => { const getSourceTypeColor = (sourceType: RequirementSourceTypeEnum) => {
const sourceColors = { const sourceColors = {
[RequirementSourceTypeEnum.SalesOrder]: "bg-blue-100 text-blue-800", [RequirementSourceTypeEnum.SalesOrder]: 'bg-blue-100 text-blue-800',
[RequirementSourceTypeEnum.Forecast]: "bg-green-100 text-green-800", [RequirementSourceTypeEnum.Forecast]: 'bg-green-100 text-green-800',
[RequirementSourceTypeEnum.SafetyStock]: "bg-orange-100 text-orange-800", [RequirementSourceTypeEnum.SafetyStock]: 'bg-orange-100 text-orange-800',
[RequirementSourceTypeEnum.ProductionOrder]: [RequirementSourceTypeEnum.ProductionOrder]: 'bg-purple-100 text-purple-800',
"bg-purple-100 text-purple-800", }
}; return sourceColors[sourceType]
return sourceColors[sourceType]; }
};
const getAccuracyColor = (accuracy: number) => { const getAccuracyColor = (accuracy: number) => {
if (accuracy >= 90) return "text-green-600"; if (accuracy >= 90) return 'text-green-600'
if (accuracy >= 75) return "text-yellow-600"; if (accuracy >= 75) return 'text-yellow-600'
return "text-red-600"; return 'text-red-600'
}; }
const forecastColumns: Column<MrpDemandForecast>[] = [ const forecastColumns: Column<MrpDemandForecast>[] = [
{ {
key: "material", key: 'material',
header: "Malzeme", header: 'Malzeme',
sortable: true, sortable: true,
render: (forecast: MrpDemandForecast) => ( render: (forecast: MrpDemandForecast) => (
<div> <div>
<div className="font-medium text-gray-900"> <div className="font-medium text-gray-900">
{forecast.material?.name || {forecast.material?.name || `Material-${forecast.materialId.substring(0, 8)}`}
`Material-${forecast.materialId.substring(0, 8)}`}
</div> </div>
<div className="text-sm text-gray-500">{forecast.material?.code}</div> <div className="text-sm text-gray-500">{forecast.material?.code}</div>
</div> </div>
), ),
}, },
{ {
key: "period", key: 'period',
header: "Dönem", header: 'Dönem',
render: (forecast: MrpDemandForecast) => ( render: (forecast: MrpDemandForecast) => (
<div> <div>
<div className="font-medium text-gray-900"> <div className="font-medium text-gray-900">{forecast.forecastPeriod}</div>
{forecast.forecastPeriod}
</div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
{new Date(forecast.startDate).toLocaleDateString("tr-TR")} - {new Date(forecast.startDate).toLocaleDateString('tr-TR')} -
{new Date(forecast.endDate).toLocaleDateString("tr-TR")} {new Date(forecast.endDate).toLocaleDateString('tr-TR')}
</div> </div>
</div> </div>
), ),
}, },
{ {
key: "method", key: 'method',
header: "Yöntem", header: 'Yöntem',
render: (forecast: MrpDemandForecast) => ( render: (forecast: MrpDemandForecast) => (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<FaCalculator className="w-4 h-4 text-gray-400" /> <FaCalculator className="w-4 h-4 text-gray-400" />
@ -355,36 +310,30 @@ const DemandPlanning: React.FC = () => {
), ),
}, },
{ {
key: "quantities", key: 'quantities',
header: "Miktarlar", header: 'Miktarlar',
render: (forecast: MrpDemandForecast) => ( render: (forecast: MrpDemandForecast) => (
<div className="text-sm"> <div className="text-sm">
<div> <div>
Tahmin:{" "} Tahmin:{' '}
<span className="font-medium"> <span className="font-medium">{forecast.forecastQuantity.toLocaleString()}</span>
{forecast.forecastQuantity.toLocaleString()}
</span>
</div> </div>
{forecast.actualQuantity !== undefined && ( {forecast.actualQuantity !== undefined && (
<div> <div>
Gerçek:{" "} Gerçek:{' '}
<span className="font-medium"> <span className="font-medium">{forecast.actualQuantity.toLocaleString()}</span>
{forecast.actualQuantity.toLocaleString()}
</span>
</div> </div>
)} )}
</div> </div>
), ),
}, },
{ {
key: "accuracy", key: 'accuracy',
header: "Doğruluk", header: 'Doğruluk',
render: (forecast: MrpDemandForecast) => ( render: (forecast: MrpDemandForecast) => (
<div> <div>
{forecast.accuracy !== undefined ? ( {forecast.accuracy !== undefined ? (
<div <div className={`font-medium ${getAccuracyColor(forecast.accuracy)}`}>
className={`font-medium ${getAccuracyColor(forecast.accuracy)}`}
>
{forecast.accuracy.toFixed(1)}% {forecast.accuracy.toFixed(1)}%
</div> </div>
) : ( ) : (
@ -394,22 +343,20 @@ const DemandPlanning: React.FC = () => {
), ),
}, },
{ {
key: "factors", key: 'factors',
header: "Faktörler", header: 'Faktörler',
render: (forecast: MrpDemandForecast) => ( render: (forecast: MrpDemandForecast) => (
<div className="text-sm"> <div className="text-sm">
{forecast.seasonalityFactor && ( {forecast.seasonalityFactor && (
<div>Mevsimsellik: {forecast.seasonalityFactor.toFixed(2)}</div> <div>Mevsimsellik: {forecast.seasonalityFactor.toFixed(2)}</div>
)} )}
{forecast.trendFactor && ( {forecast.trendFactor && <div>Trend: {forecast.trendFactor.toFixed(2)}</div>}
<div>Trend: {forecast.trendFactor.toFixed(2)}</div>
)}
</div> </div>
), ),
}, },
{ {
key: "actions", key: 'actions',
header: "İşlemler", header: 'İşlemler',
render: (forecast: MrpDemandForecast) => ( render: (forecast: MrpDemandForecast) => (
<div className="flex gap-2"> <div className="flex gap-2">
<button <button
@ -429,12 +376,12 @@ const DemandPlanning: React.FC = () => {
</div> </div>
), ),
}, },
]; ]
const requirementColumns: Column<MrpMaterialRequirement>[] = [ const requirementColumns: Column<MrpMaterialRequirement>[] = [
{ {
key: "material", key: 'material',
header: "Malzeme", header: 'Malzeme',
sortable: true, sortable: true,
render: (req: MrpMaterialRequirement) => ( render: (req: MrpMaterialRequirement) => (
<div> <div>
@ -446,12 +393,12 @@ const DemandPlanning: React.FC = () => {
), ),
}, },
{ {
key: "sourceType", key: 'sourceType',
header: "Kaynak", header: 'Kaynak',
render: (req: MrpMaterialRequirement) => ( render: (req: MrpMaterialRequirement) => (
<span <span
className={`px-2 py-1 text-xs font-medium rounded-full ${getSourceTypeColor( className={`px-2 py-1 text-xs font-medium rounded-full ${getSourceTypeColor(
req.sourceType req.sourceType,
)}`} )}`}
> >
{getSourceTypeLabel(req.sourceType)} {getSourceTypeLabel(req.sourceType)}
@ -459,63 +406,46 @@ const DemandPlanning: React.FC = () => {
), ),
}, },
{ {
key: "requirements", key: 'requirements',
header: "İhtiyaçlar", header: 'İhtiyaçlar',
render: (req: MrpMaterialRequirement) => ( render: (req: MrpMaterialRequirement) => (
<div className="text-sm"> <div className="text-sm">
<div> <div>
Brüt:{" "} Brüt: <span className="font-medium">{req.grossRequirement.toLocaleString()}</span>
<span className="font-medium">
{req.grossRequirement.toLocaleString()}
</span>
</div> </div>
<div> <div>
Net:{" "} Net: <span className="font-medium">{req.netRequirement.toLocaleString()}</span>
<span className="font-medium">
{req.netRequirement.toLocaleString()}
</span>
</div> </div>
</div> </div>
), ),
}, },
{ {
key: "planned", key: 'planned',
header: "Planlanan", header: 'Planlanan',
render: (req: MrpMaterialRequirement) => ( render: (req: MrpMaterialRequirement) => (
<div className="text-sm"> <div className="text-sm">
<div> <div>
Giriş:{" "} Giriş: <span className="font-medium">{req.plannedOrderReceipt.toLocaleString()}</span>
<span className="font-medium">
{req.plannedOrderReceipt.toLocaleString()}
</span>
</div> </div>
<div> <div>
Çıkış:{" "} Çıkış: <span className="font-medium">{req.plannedOrderRelease.toLocaleString()}</span>
<span className="font-medium">
{req.plannedOrderRelease.toLocaleString()}
</span>
</div> </div>
</div> </div>
), ),
}, },
{ {
key: "dates", key: 'dates',
header: "Tarihler", header: 'Tarihler',
render: (req: MrpMaterialRequirement) => ( render: (req: MrpMaterialRequirement) => (
<div className="text-sm"> <div className="text-sm">
<div> <div>İhtiyaç: {new Date(req.requirementDate).toLocaleDateString('tr-TR')}</div>
İhtiyaç: {new Date(req.requirementDate).toLocaleDateString("tr-TR")} <div>Plan Giriş: {new Date(req.plannedReceiptDate).toLocaleDateString('tr-TR')}</div>
</div>
<div>
Plan Giriş:{" "}
{new Date(req.plannedReceiptDate).toLocaleDateString("tr-TR")}
</div>
</div> </div>
), ),
}, },
{ {
key: "projected", key: 'projected',
header: "Mevcut Stok", header: 'Mevcut Stok',
render: (req: MrpMaterialRequirement) => ( render: (req: MrpMaterialRequirement) => (
<div> <div>
<span className="font-medium text-gray-900"> <span className="font-medium text-gray-900">
@ -525,8 +455,8 @@ const DemandPlanning: React.FC = () => {
), ),
}, },
{ {
key: "actions", key: 'actions',
header: "İşlemler", header: 'İşlemler',
render: (req: MrpMaterialRequirement) => ( render: (req: MrpMaterialRequirement) => (
<div className="flex gap-2"> <div className="flex gap-2">
<button <button
@ -546,52 +476,45 @@ const DemandPlanning: React.FC = () => {
</div> </div>
), ),
}, },
]; ]
// Calculate statistics // Calculate statistics
const totalForecasts = demandForecasts.length; const totalForecasts = demandForecasts.length
const averageAccuracy = const averageAccuracy =
demandForecasts demandForecasts
.filter((f) => f.accuracy !== undefined) .filter((f) => f.accuracy !== undefined)
.reduce((sum, f) => sum + (f.accuracy || 0), 0) / .reduce((sum, f) => sum + (f.accuracy || 0), 0) /
demandForecasts.filter((f) => f.accuracy !== undefined).length || 0; demandForecasts.filter((f) => f.accuracy !== undefined).length || 0
const totalRequirements = materialRequirements.length; const totalRequirements = materialRequirements.length
const criticalRequirements = materialRequirements.filter( const criticalRequirements = materialRequirements.filter(
(r) => r.netRequirement > 0 && r.projectedAvailable < r.grossRequirement (r) => r.netRequirement > 0 && r.projectedAvailable < r.grossRequirement,
).length; ).length
// Method distribution // Method distribution
const methodDistribution = Object.values(ForecastMethodEnum).map( const methodDistribution = Object.values(ForecastMethodEnum).map((method) => ({
(method) => ({
method, method,
count: demandForecasts.filter((f) => f.forecastMethod === method).length, count: demandForecasts.filter((f) => f.forecastMethod === method).length,
accuracy: accuracy:
demandForecasts demandForecasts
.filter( .filter((f) => f.forecastMethod === method && f.accuracy !== undefined)
(f) => f.forecastMethod === method && f.accuracy !== undefined
)
.reduce((sum, f) => sum + (f.accuracy || 0), 0) / .reduce((sum, f) => sum + (f.accuracy || 0), 0) /
demandForecasts.filter( demandForecasts.filter((f) => f.forecastMethod === method && f.accuracy !== undefined)
(f) => f.forecastMethod === method && f.accuracy !== undefined .length || 0,
).length || 0, }))
})
);
// Source type distribution // Source type distribution
const sourceDistribution = Object.values(RequirementSourceTypeEnum).map( const sourceDistribution = Object.values(RequirementSourceTypeEnum).map((sourceType) => ({
(sourceType) => ({
sourceType, sourceType,
count: materialRequirements.filter((r) => r.sourceType === sourceType) count: materialRequirements.filter((r) => r.sourceType === sourceType).length,
.length,
totalRequirement: materialRequirements totalRequirement: materialRequirements
.filter((r) => r.sourceType === sourceType) .filter((r) => r.sourceType === sourceType)
.reduce((sum, r) => sum + r.grossRequirement, 0), .reduce((sum, r) => sum + r.grossRequirement, 0),
}) }))
);
return ( return (
<div className="space-y-3 pt-2"> <Container>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
@ -616,27 +539,18 @@ const DemandPlanning: React.FC = () => {
İhtiyaç Oluştur İhtiyaç Oluştur
</button> </button>
<button <button
onClick={ onClick={activeTab === 'forecasts' ? handleAddForecast : handleAddRequirement}
activeTab === "forecasts"
? handleAddForecast
: handleAddRequirement
}
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors" className="flex items-center gap-2 px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
> >
<FaPlus className="w-4 h-4" /> <FaPlus className="w-4 h-4" />
{activeTab === "forecasts" ? "Yeni Tahmin" : "Yeni İhtiyaç"} {activeTab === 'forecasts' ? 'Yeni Tahmin' : 'Yeni İhtiyaç'}
</button> </button>
</div> </div>
</div> </div>
{/* Stats Cards */} {/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Widget <Widget title="Toplam Tahmin" value={totalForecasts} color="blue" icon="FaArrowUp" />
title="Toplam Tahmin"
value={totalForecasts}
color="blue"
icon="FaArrowUp"
/>
<Widget <Widget
title="Ortalama Doğruluk" title="Ortalama Doğruluk"
@ -663,28 +577,19 @@ const DemandPlanning: React.FC = () => {
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Method Distribution */} {/* Method Distribution */}
<div> <div>
<h3 className="text-lg font-semibold text-gray-900 mb-3"> <h3 className="text-lg font-semibold text-gray-900 mb-3">Tahmin Yöntemi Dağılımı</h3>
Tahmin Yöntemi Dağılımı
</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-3"> <div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{methodDistribution.map(({ method, count, accuracy }) => ( {methodDistribution.map(({ method, count, accuracy }) => (
<div <div key={method} className="text-center p-3 border rounded-lg bg-white">
key={method}
className="text-center p-3 border rounded-lg bg-white"
>
<div className="flex items-center justify-center gap-2 mb-2"> <div className="flex items-center justify-center gap-2 mb-2">
<FaCalculator className="w-4 h-4 text-gray-400" /> <FaCalculator className="w-4 h-4 text-gray-400" />
<span className="text-sm font-medium text-gray-900"> <span className="text-sm font-medium text-gray-900">
{getMethodLabel(method)} {getMethodLabel(method)}
</span> </span>
</div> </div>
<div className="text-2xl font-bold text-gray-900 mb-1"> <div className="text-2xl font-bold text-gray-900 mb-1">{count}</div>
{count}
</div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
{accuracy > 0 {accuracy > 0 ? `${accuracy.toFixed(1)}% Doğruluk` : 'Doğruluk Yok'}
? `${accuracy.toFixed(1)}% Doğruluk`
: "Doğruluk Yok"}
</div> </div>
</div> </div>
))} ))}
@ -693,32 +598,23 @@ const DemandPlanning: React.FC = () => {
{/* Source Distribution */} {/* Source Distribution */}
<div> <div>
<h3 className="text-lg font-semibold text-gray-900 mb-3"> <h3 className="text-lg font-semibold text-gray-900 mb-3">İhtiyaç Kaynağı Dağılımı</h3>
İhtiyaç Kaynağı Dağılımı
</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-3"> <div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{sourceDistribution.map( {sourceDistribution.map(({ sourceType, count, totalRequirement }) => (
({ sourceType, count, totalRequirement }) => ( <div key={sourceType} className="text-center p-3 border rounded-lg bg-white">
<div
key={sourceType}
className="text-center p-3 border rounded-lg bg-white"
>
<div <div
className={`inline-block px-2 py-1 text-xs font-medium rounded-full mb-2 ${getSourceTypeColor( className={`inline-block px-2 py-1 text-xs font-medium rounded-full mb-2 ${getSourceTypeColor(
sourceType sourceType,
)}`} )}`}
> >
{getSourceTypeLabel(sourceType)} {getSourceTypeLabel(sourceType)}
</div> </div>
<div className="text-2xl font-bold text-gray-900 mb-1"> <div className="text-2xl font-bold text-gray-900 mb-1">{count}</div>
{count}
</div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">
{totalRequirement.toLocaleString()} Miktar {totalRequirement.toLocaleString()} Miktar
</div> </div>
</div> </div>
) ))}
)}
</div> </div>
</div> </div>
</div> </div>
@ -726,11 +622,11 @@ const DemandPlanning: React.FC = () => {
{/* Tabs */} {/* Tabs */}
<div className="flex bg-gray-100 rounded-lg p-0.5"> <div className="flex bg-gray-100 rounded-lg p-0.5">
<button <button
onClick={() => setActiveTab("forecasts")} onClick={() => setActiveTab('forecasts')}
className={`flex-1 py-2 px-4 rounded-md text-sm font-medium transition-colors ${ className={`flex-1 py-2 px-4 rounded-md text-sm font-medium transition-colors ${
activeTab === "forecasts" activeTab === 'forecasts'
? "bg-white text-gray-900 shadow-sm" ? 'bg-white text-gray-900 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
@ -739,11 +635,11 @@ const DemandPlanning: React.FC = () => {
</div> </div>
</button> </button>
<button <button
onClick={() => setActiveTab("requirements")} onClick={() => setActiveTab('requirements')}
className={`flex-1 py-2 px-4 rounded-md text-sm font-medium transition-colors ${ className={`flex-1 py-2 px-4 rounded-md text-sm font-medium transition-colors ${
activeTab === "requirements" activeTab === 'requirements'
? "bg-white text-gray-900 shadow-sm" ? 'bg-white text-gray-900 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
@ -759,9 +655,7 @@ const DemandPlanning: React.FC = () => {
<input <input
type="text" type="text"
placeholder={ placeholder={
activeTab === "forecasts" activeTab === 'forecasts' ? 'Malzeme adı veya dönem ara...' : 'Malzeme adı ara...'
? "Malzeme adı veya dönem ara..."
: "Malzeme adı ara..."
} }
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
@ -769,12 +663,10 @@ const DemandPlanning: React.FC = () => {
/> />
</div> </div>
{activeTab === "forecasts" && ( {activeTab === 'forecasts' && (
<select <select
value={selectedMethod} value={selectedMethod}
onChange={(e) => onChange={(e) => setSelectedMethod(e.target.value as ForecastMethodEnum | 'all')}
setSelectedMethod(e.target.value as ForecastMethodEnum | "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" 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 Yöntemler</option> <option value="all">Tüm Yöntemler</option>
@ -789,7 +681,7 @@ const DemandPlanning: React.FC = () => {
{/* Data Table */} {/* Data Table */}
<div className="bg-white rounded-lg shadow-sm border"> <div className="bg-white rounded-lg shadow-sm border">
{activeTab === "forecasts" ? ( {activeTab === 'forecasts' ? (
<DataTable data={filteredForecasts} columns={forecastColumns} /> <DataTable data={filteredForecasts} columns={forecastColumns} />
) : ( ) : (
<DataTable data={filteredRequirements} columns={requirementColumns} /> <DataTable data={filteredRequirements} columns={requirementColumns} />
@ -797,25 +689,21 @@ const DemandPlanning: React.FC = () => {
</div> </div>
{/* Empty State */} {/* Empty State */}
{((activeTab === "forecasts" && filteredForecasts.length === 0) || {((activeTab === 'forecasts' && filteredForecasts.length === 0) ||
(activeTab === "requirements" && (activeTab === 'requirements' && filteredRequirements.length === 0)) && (
filteredRequirements.length === 0)) && (
<div className="text-center py-12"> <div className="text-center py-12">
{activeTab === "forecasts" ? ( {activeTab === 'forecasts' ? (
<FaArrowUp className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <FaArrowUp className="w-12 h-12 text-gray-400 mx-auto mb-4" />
) : ( ) : (
<FaBullseye className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <FaBullseye 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">
{activeTab === "forecasts" {activeTab === 'forecasts' ? 'Tahmin bulunamadı' : 'İhtiyaç bulunamadı'}
? "Tahmin bulunamadı"
: "İhtiyaç bulunamadı"}
</h3> </h3>
<p className="text-gray-500"> <p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
Arama kriterlerinizi değiştirmeyi deneyin.
</p>
</div> </div>
)} )}
</div>
{/* Modals */} {/* Modals */}
<DemandForecastFormModal <DemandForecastFormModal
@ -830,8 +718,8 @@ const DemandPlanning: React.FC = () => {
onSave={handleSaveRequirement} onSave={handleSaveRequirement}
initialData={editingRequirement} initialData={editingRequirement}
/> />
</div> </Container>
); )
}; }
export default DemandPlanning; export default DemandPlanning

View file

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useState } from 'react'
import { import {
FaPlus, FaPlus,
FaSearch, FaSearch,
@ -11,62 +11,58 @@ 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>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-xl font-bold text-gray-900"> <h2 className="text-xl font-bold text-gray-900">Operasyon Tanımları</h2>
Operasyon Tanımları
</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>
@ -75,21 +71,21 @@ const OperationDefinitions: React.FC = () => {
{/* 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 <button
onClick={() => setViewMode("card")} onClick={() => setViewMode('card')}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${ className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
viewMode === "card" viewMode === 'card'
? "bg-white text-blue-600 shadow-sm" ? 'bg-white text-blue-600 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<FaTh className="w-4 h-4" /> <FaTh className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => setViewMode("list")} onClick={() => setViewMode('list')}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${ className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
viewMode === "list" viewMode === 'list'
? "bg-white text-blue-600 shadow-sm" ? 'bg-white text-blue-600 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<FaList className="w-4 h-4" /> <FaList className="w-4 h-4" />
@ -118,7 +114,7 @@ const OperationDefinitions: React.FC = () => {
</div> </div>
{/* Operations Display */} {/* Operations Display */}
{viewMode === "card" ? ( {viewMode === 'card' ? (
/* Card View */ /* Card View */
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{filteredOperations.map((operation) => ( {filteredOperations.map((operation) => (
@ -129,18 +125,14 @@ const OperationDefinitions: React.FC = () => {
<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-1"> <div className="flex items-center space-x-2 mb-1">
<h3 className="text-lg font-semibold text-gray-900"> <h3 className="text-lg font-semibold text-gray-900">{operation.name}</h3>
{operation.name}
</h3>
<span className="px-2 py-1 rounded-full text-xs font-medium"> <span className="px-2 py-1 rounded-full text-xs font-medium">
{operation.operationType?.code} {operation.operationType?.code}
</span> </span>
</div> </div>
<p className="text-sm text-gray-600 mb-1">{operation.code}</p> <p className="text-sm text-gray-600 mb-1">{operation.code}</p>
{operation.description && ( {operation.description && (
<p className="text-sm text-gray-500 mb-2"> <p className="text-sm text-gray-500 mb-2">{operation.description}</p>
{operation.description}
</p>
)} )}
</div> </div>
<div className="flex space-x-1"> <div className="flex space-x-1">
@ -162,12 +154,8 @@ const OperationDefinitions: React.FC = () => {
<FaWrench className="w-4 h-4 text-gray-600" /> <FaWrench className="w-4 h-4 text-gray-600" />
<span className="font-medium text-gray-900">İş Merkezi</span> <span className="font-medium text-gray-900">İş Merkezi</span>
</div> </div>
<p className="text-sm text-gray-700"> <p className="text-sm text-gray-700">{operation.workCenter?.name}</p>
{operation.workCenter?.name} <p className="text-sm text-gray-600">{operation.workCenter?.code}</p>
</p>
<p className="text-sm text-gray-600">
{operation.workCenter?.code}
</p>
</div> </div>
<div className="grid grid-cols-2 gap-3 mb-2"> <div className="grid grid-cols-2 gap-3 mb-2">
@ -177,9 +165,7 @@ const OperationDefinitions: React.FC = () => {
<FaClock className="w-4 h-4 mr-1" /> <FaClock className="w-4 h-4 mr-1" />
Standart Süre Standart Süre
</span> </span>
<span className="font-medium"> <span className="font-medium">{operation.standardTime} dk</span>
{operation.standardTime} dk
</span>
</div> </div>
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
@ -187,9 +173,7 @@ const OperationDefinitions: React.FC = () => {
<FaCog className="w-4 h-4 mr-1" /> <FaCog className="w-4 h-4 mr-1" />
Hazırlık Süresi Hazırlık Süresi
</span> </span>
<span className="font-medium"> <span className="font-medium">{operation.setupTime} dk</span>
{operation.setupTime} dk
</span>
</div> </div>
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
@ -204,16 +188,12 @@ const OperationDefinitions: React.FC = () => {
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<span className="text-gray-500">Makine Maliyeti</span> <span className="text-gray-500">Makine Maliyeti</span>
<span className="font-medium"> <span className="font-medium">{operation.machineCost}</span>
{operation.machineCost}
</span>
</div> </div>
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<span className="text-gray-500">Genel Gider</span> <span className="text-gray-500">Genel Gider</span>
<span className="font-medium"> <span className="font-medium">{operation.overheadCost}</span>
{operation.overheadCost}
</span>
</div> </div>
<div className="flex items-center justify-between text-sm border-t pt-2"> <div className="flex items-center justify-between text-sm border-t pt-2">
@ -221,21 +201,15 @@ const OperationDefinitions: React.FC = () => {
<FaDollarSign className="w-4 h-4 mr-1" /> <FaDollarSign className="w-4 h-4 mr-1" />
Toplam Maliyet Toplam Maliyet
</span> </span>
<span className="font-bold text-blue-600"> <span className="font-bold text-blue-600">{getTotalCost(operation)}</span>
{getTotalCost(operation)}
</span>
</div> </div>
</div> </div>
</div> </div>
{operation.instructions && ( {operation.instructions && (
<div className="bg-blue-50 rounded-lg p-2 mb-2"> <div className="bg-blue-50 rounded-lg p-2 mb-2">
<h4 className="text-sm font-medium text-blue-900 mb-1"> <h4 className="text-sm font-medium text-blue-900 mb-1">Talimatlar</h4>
Talimatlar <p className="text-sm text-blue-700">{operation.instructions}</p>
</h4>
<p className="text-sm text-blue-700">
{operation.instructions}
</p>
</div> </div>
)} )}
@ -244,26 +218,26 @@ const OperationDefinitions: React.FC = () => {
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
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>
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
operation.qualityCheckRequired operation.qualityCheckRequired
? "bg-purple-100 text-purple-800" ? 'bg-purple-100 text-purple-800'
: "bg-gray-100 text-gray-800" : 'bg-gray-100 text-gray-800'
}`} }`}
> >
{operation.qualityCheckRequired {operation.qualityCheckRequired
? "Kalite Kontrolü Gerekli" ? 'Kalite Kontrolü Gerekli'
: "Kalite Kontrolü İsteğe Bağlı"} : 'Kalite Kontrolü İsteğe Bağlı'}
</span> </span>
</div> </div>
<span className="text-xs text-gray-400"> <span className="text-xs text-gray-400">
{operation.lastModificationTime.toLocaleDateString("tr-TR")} {operation.lastModificationTime.toLocaleDateString('tr-TR')}
</span> </span>
</div> </div>
</div> </div>
@ -303,31 +277,19 @@ const OperationDefinitions: React.FC = () => {
<tr key={operation.id} className="hover:bg-gray-50 text-sm"> <tr key={operation.id} className="hover:bg-gray-50 text-sm">
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
<div> <div>
<div className="text-sm font-medium text-gray-900"> <div className="text-sm font-medium text-gray-900">{operation.name}</div>
{operation.name} <div className="text-sm text-gray-500">{operation.code}</div>
</div>
<div className="text-sm text-gray-500">
{operation.code}
</div>
{operation.description && ( {operation.description && (
<div className="text-xs text-gray-400 mt-1"> <div className="text-xs text-gray-400 mt-1">{operation.description}</div>
{operation.description}
</div>
)} )}
</div> </div>
</td> </td>
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">{operation.workCenter?.name}</div>
{operation.workCenter?.name} <div className="text-sm text-gray-500">{operation.workCenter?.code}</div>
</div>
<div className="text-sm text-gray-500">
{operation.workCenter?.code}
</div>
</td> </td>
<td className="px-2 py-1.5 whitespace-nowrap"> <td className="px-2 py-1.5 whitespace-nowrap">
<span <span className={'px-2 py-1 rounded-full text-xs font-medium'}>
className={"px-2 py-1 rounded-full text-xs font-medium"}
>
{operation.operationType?.code} {operation.operationType?.code}
</span> </span>
</td> </td>
@ -357,22 +319,22 @@ const OperationDefinitions: React.FC = () => {
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
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>
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
operation.qualityCheckRequired operation.qualityCheckRequired
? "bg-purple-100 text-purple-800" ? 'bg-purple-100 text-purple-800'
: "bg-gray-100 text-gray-800" : 'bg-gray-100 text-gray-800'
}`} }`}
> >
{operation.qualityCheckRequired {operation.qualityCheckRequired
? "Kalite Gerekli" ? 'Kalite Gerekli'
: "Kalite İsteğe Bağlı"} : 'Kalite İsteğe Bağlı'}
</span> </span>
</div> </div>
</td> </td>
@ -399,14 +361,13 @@ const OperationDefinitions: React.FC = () => {
{filteredOperations.length === 0 && ( {filteredOperations.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 bulunamadı</h3>
Operasyon bulunamadı
</h3>
<p className="text-gray-500"> <p className="text-gray-500">
Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin. Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin.
</p> </p>
</div> </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,46 +9,46 @@ 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>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
@ -61,21 +61,21 @@ const OperationTypes: React.FC = () => {
{/* 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 <button
onClick={() => setViewMode("card")} onClick={() => setViewMode('card')}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${ className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
viewMode === "card" viewMode === 'card'
? "bg-white text-blue-600 shadow-sm" ? 'bg-white text-blue-600 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<FaTh className="w-4 h-4" /> <FaTh className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => setViewMode("list")} onClick={() => setViewMode('list')}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${ className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
viewMode === "list" viewMode === 'list'
? "bg-white text-blue-600 shadow-sm" ? 'bg-white text-blue-600 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<FaList className="w-4 h-4" /> <FaList className="w-4 h-4" />
@ -104,7 +104,7 @@ const OperationTypes: React.FC = () => {
</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) => (
@ -115,12 +115,10 @@ const OperationTypes: React.FC = () => {
<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}
</h3>
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor( className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor(
type.category type.category,
)}`} )}`}
> >
{getOperationTypeText(type.category)} {getOperationTypeText(type.category)}
@ -128,9 +126,7 @@ const OperationTypes: React.FC = () => {
</div> </div>
<p className="text-sm text-gray-600 mb-2">{type.code}</p> <p className="text-sm text-gray-600 mb-2">{type.code}</p>
{type.description && ( {type.description && (
<p className="text-sm text-gray-500 mb-2"> <p className="text-sm text-gray-500 mb-2">{type.description}</p>
{type.description}
</p>
)} )}
</div> </div>
<div className="flex space-x-1"> <div className="flex space-x-1">
@ -169,10 +165,10 @@ const OperationTypes: React.FC = () => {
</span> </span>
<span <span
className={`font-medium ${ className={`font-medium ${
type.requiresSetup ? "text-orange-600" : "text-green-600" type.requiresSetup ? 'text-orange-600' : 'text-green-600'
}`} }`}
> >
{type.requiresSetup ? "Evet" : "Hayır"} {type.requiresSetup ? 'Evet' : 'Hayır'}
</span> </span>
</div> </div>
@ -180,12 +176,10 @@ const OperationTypes: React.FC = () => {
<span className="text-gray-500">Paralel İşlem</span> <span className="text-gray-500">Paralel İşlem</span>
<span <span
className={`font-medium ${ className={`font-medium ${
type.allowsParallelOperation type.allowsParallelOperation ? 'text-green-600' : 'text-gray-600'
? "text-green-600"
: "text-gray-600"
}`} }`}
> >
{type.allowsParallelOperation ? "Evet" : "Hayır"} {type.allowsParallelOperation ? 'Evet' : 'Hayır'}
</span> </span>
</div> </div>
@ -196,12 +190,10 @@ const OperationTypes: React.FC = () => {
</span> </span>
<span <span
className={`font-medium ${ className={`font-medium ${
type.qualityCheckRequired type.qualityCheckRequired ? 'text-purple-600' : 'text-gray-600'
? "text-purple-600"
: "text-gray-600"
}`} }`}
> >
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"} {type.qualityCheckRequired ? 'Gerekli' : 'İsteğe Bağlı'}
</span> </span>
</div> </div>
</div> </div>
@ -210,15 +202,13 @@ const OperationTypes: React.FC = () => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
type.isActive type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
}`} }`}
> >
{type.isActive ? "Aktif" : "Pasif"} {type.isActive ? 'Aktif' : 'Pasif'}
</span> </span>
<span className="text-xs text-gray-400"> <span className="text-xs text-gray-400">
{type.lastModificationTime.toLocaleDateString("tr-TR")} {type.lastModificationTime.toLocaleDateString('tr-TR')}
</span> </span>
</div> </div>
</div> </div>
@ -262,21 +252,17 @@ const OperationTypes: React.FC = () => {
<tr key={type.id} className="hover:bg-gray-50 text-sm"> <tr key={type.id} className="hover:bg-gray-50 text-sm">
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<div> <div>
<div className="text-sm font-medium text-gray-900"> <div className="text-sm font-medium text-gray-900">{type.name}</div>
{type.name}
</div>
<div className="text-sm text-gray-500">{type.code}</div> <div className="text-sm text-gray-500">{type.code}</div>
{type.description && ( {type.description && (
<div className="text-xs text-gray-400 mt-0.5"> <div className="text-xs text-gray-400 mt-0.5">{type.description}</div>
{type.description}
</div>
)} )}
</div> </div>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationCategoryColor( className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationCategoryColor(
type.category type.category,
)}`} )}`}
> >
{type.category} {type.category}
@ -295,33 +281,31 @@ const OperationTypes: React.FC = () => {
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
type.requiresSetup type.requiresSetup
? "bg-orange-100 text-orange-800" ? 'bg-orange-100 text-orange-800'
: "bg-green-100 text-green-800" : 'bg-green-100 text-green-800'
}`} }`}
> >
{type.requiresSetup ? "Gerekli" : "Gerekli Değil"} {type.requiresSetup ? 'Gerekli' : 'Gerekli Değil'}
</span> </span>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
type.qualityCheckRequired type.qualityCheckRequired
? "bg-purple-100 text-purple-800" ? 'bg-purple-100 text-purple-800'
: "bg-gray-100 text-gray-800" : 'bg-gray-100 text-gray-800'
}`} }`}
> >
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"} {type.qualityCheckRequired ? 'Gerekli' : 'İsteğe Bağlı'}
</span> </span>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap"> <td className="px-3 py-2 whitespace-nowrap">
<span <span
className={`px-2 py-1 rounded-full text-xs font-medium ${ className={`px-2 py-1 rounded-full text-xs font-medium ${
type.isActive type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
}`} }`}
> >
{type.isActive ? "Aktif" : "Pasif"} {type.isActive ? 'Aktif' : 'Pasif'}
</span> </span>
</td> </td>
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium"> <td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
@ -347,15 +331,13 @@ const OperationTypes: React.FC = () => {
{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ı
</h3>
<p className="text-gray-500"> <p className="text-gray-500">
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü Arama kriterlerinizi değiştirin veya yeni bir operasyon türü ekleyin.
ekleyin.
</p> </p>
</div> </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;

View file

@ -1,13 +1,13 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } 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, MrpProductionOrder,
ProductionOrderStatusEnum, ProductionOrderStatusEnum,
MrpProductionOrderMaterial, MrpProductionOrderMaterial,
ProductionOrderTypeEnum, ProductionOrderTypeEnum,
} from "../../../types/mrp"; } from '../../../types/mrp'
import { import {
FaPlus, FaPlus,
FaTimes, FaTimes,
@ -21,28 +21,27 @@ import {
FaTruck, FaTruck,
FaCheckCircle, FaCheckCircle,
FaExclamationTriangle, FaExclamationTriangle,
} from "react-icons/fa"; } from 'react-icons/fa'
import { mockMaterials } from "../../../mocks/mockMaterials"; import { mockMaterials } from '../../../mocks/mockMaterials'
import { PriorityEnum } from "../../../types/common"; import { PriorityEnum } from '../../../types/common'
import { Container } from '@/components/shared'
const ProductionOrderForm: React.FC = () => { const ProductionOrderForm: 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 && id !== "new", enabled: !!id && id !== 'new',
}); })
// Form state // Form state
const [formData, setFormData] = useState<Partial<MrpProductionOrder>>({ const [formData, setFormData] = useState<Partial<MrpProductionOrder>>({
orderNumber: "", orderNumber: '',
orderType: ProductionOrderTypeEnum.Standard, orderType: ProductionOrderTypeEnum.Standard,
status: ProductionOrderStatusEnum.Created, status: ProductionOrderStatusEnum.Created,
priority: PriorityEnum.Normal, priority: PriorityEnum.Normal,
@ -50,28 +49,26 @@ const ProductionOrderForm: React.FC = () => {
confirmedQuantity: 0, confirmedQuantity: 0,
requiredQuantity: 0, requiredQuantity: 0,
scrapQuantity: 0, scrapQuantity: 0,
unitId: "ADET", unitId: 'ADET',
plannedCost: 0, plannedCost: 0,
actualCost: 0, actualCost: 0,
currency: "TRY", currency: 'TRY',
plannedStartDate: new Date(), plannedStartDate: new Date(),
plannedEndDate: new Date(new Date().setDate(new Date().getDate() + 14)), plannedEndDate: new Date(new Date().setDate(new Date().getDate() + 14)),
materials: [], materials: [],
}); })
// Materials management // Materials management
const [materials, setMaterials] = useState<MrpProductionOrderMaterial[]>([]); const [materials, setMaterials] = useState<MrpProductionOrderMaterial[]>([])
const [showNewMaterialForm, setShowNewMaterialForm] = useState(false); const [showNewMaterialForm, setShowNewMaterialForm] = useState(false)
const [newMaterial, setNewMaterial] = useState< const [newMaterial, setNewMaterial] = useState<Partial<MrpProductionOrderMaterial>>({
Partial<MrpProductionOrderMaterial> materialId: '',
>({
materialId: "",
plannedQuantity: 0, plannedQuantity: 0,
confirmedQuantity: 0, confirmedQuantity: 0,
requiredQuantity: 0, requiredQuantity: 0,
scrapQuantity: 0, scrapQuantity: 0,
unitId: "ADET", unitId: 'ADET',
}); })
// Initialize form with data when available // Initialize form with data when available
useEffect(() => { useEffect(() => {
@ -82,110 +79,106 @@ const ProductionOrderForm: React.FC = () => {
plannedEndDate: productionOrder.plannedEndDate, plannedEndDate: productionOrder.plannedEndDate,
actualStartDate: productionOrder.actualStartDate, actualStartDate: productionOrder.actualStartDate,
actualEndDate: productionOrder.actualEndDate, actualEndDate: productionOrder.actualEndDate,
}); })
setMaterials(productionOrder.materials || []); setMaterials(productionOrder.materials || [])
} }
}, [productionOrder]); }, [productionOrder])
// Handle form field changes // Handle form field changes
const handleChange = ( const handleChange = (
e: React.ChangeEvent< e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
>
) => { ) => {
const { name, value, type } = e.target; const { name, value, type } = e.target
if (type === "number") { if (type === 'number') {
setFormData({ setFormData({
...formData, ...formData,
[name]: parseFloat(value) || 0, [name]: parseFloat(value) || 0,
}); })
} else if (type === "date") { } else if (type === 'date') {
setFormData({ setFormData({
...formData, ...formData,
[name]: new Date(value), [name]: new Date(value),
}); })
} else { } else {
setFormData({ setFormData({
...formData, ...formData,
[name]: value, [name]: value,
}); })
}
} }
};
// Handle new material form changes // Handle new material form changes
const handleNewMaterialChange = ( const handleNewMaterialChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement> const { name, value, type } = e.target
) => {
const { name, value, type } = e.target;
if (type === "number") { if (type === 'number') {
setNewMaterial({ setNewMaterial({
...newMaterial, ...newMaterial,
[name]: parseFloat(value) || 0, [name]: parseFloat(value) || 0,
}); })
} else { } else {
setNewMaterial({ setNewMaterial({
...newMaterial, ...newMaterial,
[name]: value, [name]: value,
}); })
}
} }
};
// Add new material to the list // Add new material to the list
const handleAddMaterial = () => { const handleAddMaterial = () => {
if (!newMaterial.materialId) { if (!newMaterial.materialId) {
alert("Lütfen malzeme seçiniz"); alert('Lütfen malzeme seçiniz')
return; return
} }
const newMaterialComplete: MrpProductionOrderMaterial = { const newMaterialComplete: MrpProductionOrderMaterial = {
id: Date.now().toString(), // Temporary ID for UI id: Date.now().toString(), // Temporary ID for UI
productionOrderId: id || "new", productionOrderId: id || 'new',
materialId: newMaterial.materialId || "", materialId: newMaterial.materialId || '',
plannedQuantity: newMaterial.plannedQuantity || 0, plannedQuantity: newMaterial.plannedQuantity || 0,
confirmedQuantity: newMaterial.confirmedQuantity || 0, confirmedQuantity: newMaterial.confirmedQuantity || 0,
requiredQuantity: newMaterial.requiredQuantity || 0, requiredQuantity: newMaterial.requiredQuantity || 0,
scrapQuantity: newMaterial.scrapQuantity || 0, scrapQuantity: newMaterial.scrapQuantity || 0,
unitId: newMaterial.unitId || "ADET", unitId: newMaterial.unitId || 'ADET',
}; }
setMaterials([...materials, newMaterialComplete]); setMaterials([...materials, newMaterialComplete])
setNewMaterial({ setNewMaterial({
materialId: "", materialId: '',
plannedQuantity: 0, plannedQuantity: 0,
confirmedQuantity: 0, confirmedQuantity: 0,
requiredQuantity: 0, requiredQuantity: 0,
scrapQuantity: 0, scrapQuantity: 0,
unitId: "ADET", unitId: 'ADET',
}); })
setShowNewMaterialForm(false); setShowNewMaterialForm(false)
}; }
// Remove material from the list // Remove material from the list
const handleRemoveMaterial = (materialId: string) => { const handleRemoveMaterial = (materialId: string) => {
setMaterials(materials.filter((m) => m.id !== materialId)); setMaterials(materials.filter((m) => m.id !== materialId))
}; }
// Save the production order // Save the production order
const handleSave = () => { const handleSave = () => {
const completeFormData: Partial<MrpProductionOrder> = { const completeFormData: Partial<MrpProductionOrder> = {
...formData, ...formData,
materials: materials, materials: materials,
}; }
console.log("Saving production order:", completeFormData); console.log('Saving production order:', completeFormData)
// Here you would normally call an API to save the data // Here you would normally call an API to save the data
navigate(`/admin/mrp/production-orders/${id || "new"}`); navigate(`/admin/mrp/production-orders/${id || 'new'}`)
}; }
// Format date for input fields // Format date for input fields
const formatDateForInput = (date: Date | undefined) => { const formatDateForInput = (date: Date | undefined) => {
if (!date) return ""; if (!date) return ''
const d = new Date(date); const d = new Date(date)
return d.toISOString().split("T")[0]; return d.toISOString().split('T')[0]
}; }
if (isLoading) { if (isLoading) {
return ( return (
@ -195,17 +188,18 @@ const ProductionOrderForm: 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>
); )
} }
return ( return (
<div className="mx-auto mt-2"> <Container>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="mb-4"> <div className="mb-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<button <button
onClick={() => navigate("/admin/mrp/production-orders")} 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" 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" /> <FaArrowLeft className="mr-2 text-sm" />
@ -213,11 +207,11 @@ const ProductionOrderForm: React.FC = () => {
</button> </button>
<div> <div>
<h1 className="text-xl font-bold text-gray-900"> <h1 className="text-xl font-bold text-gray-900">
{id === "new" ? "Yeni Üretim Emri" : "Üretim Emri Düzenle"} {id === 'new' ? 'Yeni Üretim Emri' : 'Üretim Emri Düzenle'}
</h1> </h1>
<p className="text-gray-600 mt-0.5 text-sm"> <p className="text-gray-600 mt-0.5 text-sm">
{id === "new" {id === 'new'
? "Yeni üretim emri oluşturun" ? 'Yeni üretim emri oluşturun'
: `${formData.orderNumber} düzenleniyor`} : `${formData.orderNumber} düzenleniyor`}
</p> </p>
</div> </div>
@ -241,16 +235,12 @@ const ProductionOrderForm: React.FC = () => {
<div className="p-1.5 bg-blue-100 rounded-lg"> <div className="p-1.5 bg-blue-100 rounded-lg">
<FaClipboardList className="text-blue-600 text-base" /> <FaClipboardList className="text-blue-600 text-base" />
</div> </div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Temel Bilgiler</h3>
Temel Bilgiler
</h3>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-3"> <div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-3">
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Sipariş No</label>
Sipariş No
</label>
<input <input
type="text" type="text"
name="orderNumber" name="orderNumber"
@ -262,63 +252,39 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Sipariş Tipi</label>
Sipariş Tipi
</label>
<select <select
name="orderType" name="orderType"
value={formData.orderType} value={formData.orderType}
onChange={handleChange} onChange={handleChange}
className="w-full px-2 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-gray-50 focus:bg-white" className="w-full px-2 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-gray-50 focus:bg-white"
> >
<option value={ProductionOrderTypeEnum.Standard}> <option value={ProductionOrderTypeEnum.Standard}>Standart</option>
Standart <option value={ProductionOrderTypeEnum.Rework}>Yeniden İşleme</option>
</option> <option value={ProductionOrderTypeEnum.Maintenance}>Bakım</option>
<option value={ProductionOrderTypeEnum.Rework}>
Yeniden İşleme
</option>
<option value={ProductionOrderTypeEnum.Maintenance}>
Bakım
</option>
<option value={ProductionOrderTypeEnum.Sample}>Numune</option> <option value={ProductionOrderTypeEnum.Sample}>Numune</option>
</select> </select>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Durum</label>
Durum
</label>
<select <select
name="status" name="status"
value={formData.status} value={formData.status}
onChange={handleChange} onChange={handleChange}
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-gray-50 focus:bg-white" className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-gray-50 focus:bg-white"
> >
<option value={ProductionOrderStatusEnum.Created}> <option value={ProductionOrderStatusEnum.Created}>Oluşturuldu</option>
Oluşturuldu <option value={ProductionOrderStatusEnum.Released}>Yayınlandı</option>
</option> <option value={ProductionOrderStatusEnum.InProgress}>İşlemde</option>
<option value={ProductionOrderStatusEnum.Released}> <option value={ProductionOrderStatusEnum.Completed}>Tamamlandı</option>
Yayınlandı <option value={ProductionOrderStatusEnum.Cancelled}>İptal Edildi</option>
</option> <option value={ProductionOrderStatusEnum.OnHold}>Beklemede</option>
<option value={ProductionOrderStatusEnum.InProgress}>
İşlemde
</option>
<option value={ProductionOrderStatusEnum.Completed}>
Tamamlandı
</option>
<option value={ProductionOrderStatusEnum.Cancelled}>
İptal Edildi
</option>
<option value={ProductionOrderStatusEnum.OnHold}>
Beklemede
</option>
</select> </select>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Öncelik</label>
Öncelik
</label>
<select <select
name="priority" name="priority"
value={formData.priority} value={formData.priority}
@ -340,9 +306,7 @@ const ProductionOrderForm: React.FC = () => {
<div className="p-1.5 bg-purple-100 rounded-lg"> <div className="p-1.5 bg-purple-100 rounded-lg">
<FaCog className="text-purple-600 text-base" /> <FaCog className="text-purple-600 text-base" />
</div> </div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Miktar Bilgileri</h3>
Miktar Bilgileri
</h3>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 mb-3"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 mb-3">
@ -361,9 +325,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Üretilen Miktar</label>
Üretilen Miktar
</label>
<input <input
type="number" type="number"
name="confirmedQuantity" name="confirmedQuantity"
@ -375,9 +337,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Gereken Miktar</label>
Gereken Miktar
</label>
<input <input
type="number" type="number"
name="requiredQuantity" name="requiredQuantity"
@ -389,9 +349,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Fire Miktarı</label>
Fire Miktarı
</label>
<input <input
type="number" type="number"
name="scrapQuantity" name="scrapQuantity"
@ -428,9 +386,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Planlanan Bitiş</label>
Planlanan Bitiş
</label>
<input <input
type="date" type="date"
name="plannedEndDate" name="plannedEndDate"
@ -454,9 +410,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Gerçek Bitiş</label>
Gerçek Bitiş
</label>
<input <input
type="date" type="date"
name="actualEndDate" name="actualEndDate"
@ -474,9 +428,7 @@ const ProductionOrderForm: React.FC = () => {
<div className="p-1.5 bg-yellow-100 rounded-lg"> <div className="p-1.5 bg-yellow-100 rounded-lg">
<FaMoneyBillWave className="text-yellow-600 text-base" /> <FaMoneyBillWave className="text-yellow-600 text-base" />
</div> </div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Maliyet Bilgileri</h3>
Maliyet Bilgileri
</h3>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 mb-3"> <div className="grid grid-cols-1 md:grid-cols-3 gap-3 mb-3">
@ -496,9 +448,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Gerçek Maliyet</label>
Gerçek Maliyet
</label>
<input <input
type="number" type="number"
name="actualCost" name="actualCost"
@ -511,9 +461,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Para Birimi</label>
Para Birimi
</label>
<select <select
name="currency" name="currency"
value={formData.currency} value={formData.currency}
@ -535,14 +483,12 @@ const ProductionOrderForm: React.FC = () => {
<div className="p-1.5 bg-indigo-100 rounded-lg"> <div className="p-1.5 bg-indigo-100 rounded-lg">
<FaExclamationTriangle className="text-indigo-600 text-base" /> <FaExclamationTriangle className="text-indigo-600 text-base" />
</div> </div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Müşteri Talebi</h3>
Müşteri Talebi
</h3>
</div> </div>
<textarea <textarea
name="customerRequirement" name="customerRequirement"
value={formData.customerRequirement || ""} value={formData.customerRequirement || ''}
onChange={handleChange} onChange={handleChange}
className="w-full px-2 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all duration-200 bg-gray-50 focus:bg-white resize-none" className="w-full px-2 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all duration-200 bg-gray-50 focus:bg-white resize-none"
placeholder="Müşteri özel talebi varsa buraya yazınız..." placeholder="Müşteri özel talebi varsa buraya yazınız..."
@ -558,12 +504,8 @@ const ProductionOrderForm: React.FC = () => {
<FaTruck className="text-orange-600 text-base" /> <FaTruck className="text-orange-600 text-base" />
</div> </div>
<div> <div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
Malzemeler <p className="text-gray-600 mt-0.5 text-sm">Üretim için gerekli malzemeler</p>
</h3>
<p className="text-gray-600 mt-0.5 text-sm">
Üretim için gerekli malzemeler
</p>
</div> </div>
</div> </div>
<button <button
@ -579,9 +521,7 @@ const ProductionOrderForm: React.FC = () => {
{showNewMaterialForm && ( {showNewMaterialForm && (
<div className="bg-gradient-to-r from-orange-50 to-yellow-50 p-4 rounded-xl mb-4 border border-orange-200"> <div className="bg-gradient-to-r from-orange-50 to-yellow-50 p-4 rounded-xl mb-4 border border-orange-200">
<div className="flex justify-between items-center mb-3"> <div className="flex justify-between items-center mb-3">
<h4 className="font-bold text-orange-800 text-base"> <h4 className="font-bold text-orange-800 text-base">Yeni Malzeme</h4>
Yeni Malzeme
</h4>
<button <button
onClick={() => setShowNewMaterialForm(false)} onClick={() => setShowNewMaterialForm(false)}
className="text-orange-800 hover:text-orange-900 transition-colors" className="text-orange-800 hover:text-orange-900 transition-colors"
@ -591,9 +531,7 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div className="space-y-3"> <div className="space-y-3">
<div className="space-y-1"> <div className="space-y-1">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700">Malzeme</label>
Malzeme
</label>
<select <select
value={newMaterial.materialId} value={newMaterial.materialId}
onChange={handleNewMaterialChange} onChange={handleNewMaterialChange}
@ -653,9 +591,7 @@ const ProductionOrderForm: React.FC = () => {
{materials.length === 0 ? ( {materials.length === 0 ? (
<div className="p-4 text-center bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border-2 border-dashed border-gray-300"> <div className="p-4 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-12 w-12 text-gray-400 mb-4" /> <FaTruck className="mx-auto h-12 w-12 text-gray-400 mb-4" />
<p className="text-gray-500 text-base font-medium"> <p className="text-gray-500 text-base font-medium">Henüz malzeme eklenmemiş</p>
Henüz malzeme eklenmemiş
</p>
<p className="text-gray-400 text-sm mt-2"> <p className="text-gray-400 text-sm mt-2">
Yukarıdaki butona tıklayarak malzeme ekleyebilirsiniz Yukarıdaki butona tıklayarak malzeme ekleyebilirsiniz
</p> </p>
@ -674,23 +610,22 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
<div> <div>
<div className="text-gray-600"> <div className="text-gray-600">
{material.salesOrder?.orderNumber || {material.salesOrder?.orderNumber || material.salesOrder?.id}
material.salesOrder?.id}
</div> </div>
<div className="font-bold text-gray-900 text-sm"> <div className="font-bold text-gray-900 text-sm">
{material.material && {material.material &&
`${material.material.code} - ${material.material.name}`} `${material.material.code} - ${material.material.name}`}
</div> </div>
<div className="text-sm text-gray-600 mt-1"> <div className="text-sm text-gray-600 mt-1">
Planlanan:{" "} Planlanan:{' '}
<span className="font-semibold text-blue-600"> <span className="font-semibold text-blue-600">
{material.plannedQuantity} {material.plannedQuantity}
</span>{" "} </span>{' '}
Gereken:{" "} Gereken:{' '}
<span className="font-semibold text-green-600"> <span className="font-semibold text-green-600">
{material.requiredQuantity} {material.requiredQuantity}
</span> </span>
Fire:{" "} Fire:{' '}
<span className="font-semibold text-green-600"> <span className="font-semibold text-green-600">
{material.scrapQuantity} {material.scrapQuantity}
</span> </span>
@ -712,7 +647,8 @@ const ProductionOrderForm: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); </Container>
}; )
}
export default ProductionOrderForm; export default ProductionOrderForm

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,34 +65,30 @@ 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>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="mb-6"> <div className="mb-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<button <button
onClick={() => navigate("/admin/mrp/production-orders")} 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" 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" /> <FaArrowLeft className="mr-2 text-sm" />
Geri Geri
</button> </button>
<div> <div>
<h1 className="text-2xl font-bold text-gray-900"> <h1 className="text-2xl font-bold text-gray-900">Üretim Emri Detayları</h1>
Üretim Emri Detayları
</h1>
<p className="text-gray-600 mt-1 text-sm"> <p className="text-gray-600 mt-1 text-sm">
{productionOrder.orderNumber} - Detaylı bilgiler {productionOrder.orderNumber} - Detaylı bilgiler
</p> </p>
@ -114,15 +104,11 @@ const ProductionOrderView: React.FC = () => {
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide"> <div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
Üretim Emri No Üretim Emri No
</div> </div>
<div className="text-xl font-bold text-gray-900"> <div className="text-xl font-bold text-gray-900">{productionOrder.orderNumber}</div>
{productionOrder.orderNumber}
</div>
</div> </div>
<div className="space-y-2 text-right"> <div className="space-y-2 text-right">
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide"> <div className="text-sm font-medium text-gray-500 uppercase tracking-wide">Durum</div>
Durum
</div>
<StatusBadge status={productionOrder.status} /> <StatusBadge status={productionOrder.status} />
</div> </div>
</div> </div>
@ -134,7 +120,7 @@ const ProductionOrderView: React.FC = () => {
</div> </div>
<span <span
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor( className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
productionOrder.priority productionOrder.priority,
)}`} )}`}
> >
{productionOrder.priority} {productionOrder.priority}
@ -154,21 +140,16 @@ const ProductionOrderView: React.FC = () => {
<div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide"> <div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
İlerleme İlerleme
</div> </div>
<div className="text-xl font-bold text-orange-900"> <div className="text-xl font-bold text-orange-900">{getProgressPercentage()}%</div>
{getProgressPercentage()}%
</div>
</div> </div>
</div> </div>
{/* Progress Bar */} {/* 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 mb-3">
<div className="text-sm font-medium text-gray-700"> <div className="text-sm font-medium text-gray-700">Üretim İlerlemesi</div>
Üretim İlerlemesi
</div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
{productionOrder.confirmedQuantity} /{" "} {productionOrder.confirmedQuantity} / {productionOrder.plannedQuantity} adet
{productionOrder.plannedQuantity} adet
</div> </div>
</div> </div>
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden"> <div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
@ -187,9 +168,7 @@ const ProductionOrderView: React.FC = () => {
Müşteri Talebi Müşteri Talebi
</div> </div>
</div> </div>
<p className="text-blue-700 leading-relaxed"> <p className="text-blue-700 leading-relaxed">{productionOrder.customerRequirement}</p>
{productionOrder.customerRequirement}
</p>
</div> </div>
)} )}
</div> </div>
@ -202,16 +181,12 @@ const ProductionOrderView: React.FC = () => {
<div className="p-1.5 bg-blue-100 rounded-lg"> <div className="p-1.5 bg-blue-100 rounded-lg">
<FaCog className="text-blue-600 text-base" /> <FaCog className="text-blue-600 text-base" />
</div> </div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Miktar Bilgileri</h3>
Miktar Bilgileri
</h3>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex justify-between items-center py-1.5 border-b border-gray-100"> <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="text-gray-600 font-medium">Planlanan:</span>
<span className="font-bold text-gray-900"> <span className="font-bold text-gray-900">{productionOrder.plannedQuantity}</span>
{productionOrder.plannedQuantity}
</span>
</div> </div>
<div className="flex justify-between items-center py-1.5 border-b border-gray-100"> <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="text-gray-600 font-medium">Üretilen:</span>
@ -221,15 +196,11 @@ const ProductionOrderView: React.FC = () => {
</div> </div>
<div className="flex justify-between items-center py-1.5 border-b border-gray-100"> <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="text-gray-600 font-medium">Gereken:</span>
<span className="font-bold text-blue-600"> <span className="font-bold text-blue-600">{productionOrder.requiredQuantity}</span>
{productionOrder.requiredQuantity}
</span>
</div> </div>
<div className="flex justify-between items-center py-2"> <div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Fire:</span> <span className="text-gray-600 font-medium">Fire:</span>
<span className="font-bold text-red-600"> <span className="font-bold text-red-600">{productionOrder.scrapQuantity}</span>
{productionOrder.scrapQuantity}
</span>
</div> </div>
</div> </div>
</div> </div>
@ -248,15 +219,12 @@ const ProductionOrderView: React.FC = () => {
Plan Başlangıç Plan Başlangıç
</div> </div>
<div className="font-semibold text-gray-900"> <div className="font-semibold text-gray-900">
{new Date(productionOrder.plannedStartDate).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> </div>
<div className="py-1.5 border-b border-gray-100"> <div className="py-1.5 border-b border-gray-100">
@ -264,15 +232,12 @@ const ProductionOrderView: React.FC = () => {
Plan Bitiş Plan Bitiş
</div> </div>
<div className="font-semibold text-gray-900"> <div className="font-semibold text-gray-900">
{new Date(productionOrder.plannedEndDate).toLocaleDateString( {new Date(productionOrder.plannedEndDate).toLocaleDateString('tr-TR', {
"tr-TR", weekday: 'long',
{ year: 'numeric',
weekday: "long", month: 'long',
year: "numeric", day: 'numeric',
month: "long", })}
day: "numeric",
}
)}
</div> </div>
</div> </div>
{productionOrder.actualStartDate && ( {productionOrder.actualStartDate && (
@ -281,15 +246,12 @@ const ProductionOrderView: React.FC = () => {
Gerçek Başlangıç Gerçek Başlangıç
</div> </div>
<div className="font-semibold text-green-600"> <div className="font-semibold text-green-600">
{new Date(productionOrder.actualStartDate).toLocaleDateString( {new Date(productionOrder.actualStartDate).toLocaleDateString('tr-TR', {
"tr-TR", weekday: 'long',
{ year: 'numeric',
weekday: "long", month: 'long',
year: "numeric", day: 'numeric',
month: "long", })}
day: "numeric",
}
)}
</div> </div>
</div> </div>
)} )}
@ -299,15 +261,12 @@ const ProductionOrderView: React.FC = () => {
Gerçek Bitiş Gerçek Bitiş
</div> </div>
<div className="font-semibold text-green-600"> <div className="font-semibold text-green-600">
{new Date(productionOrder.actualEndDate).toLocaleDateString( {new Date(productionOrder.actualEndDate).toLocaleDateString('tr-TR', {
"tr-TR", weekday: 'long',
{ year: 'numeric',
weekday: "long", month: 'long',
year: "numeric", day: 'numeric',
month: "long", })}
day: "numeric",
}
)}
</div> </div>
</div> </div>
)} )}
@ -320,9 +279,7 @@ const ProductionOrderView: React.FC = () => {
<div className="p-1.5 bg-green-100 rounded-lg"> <div className="p-1.5 bg-green-100 rounded-lg">
<FaMoneyBillWave className="text-green-600 text-base" /> <FaMoneyBillWave className="text-green-600 text-base" />
</div> </div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Maliyet Bilgileri</h3>
Maliyet Bilgileri
</h3>
</div> </div>
<div className="space-y-2"> <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">
@ -330,8 +287,8 @@ const ProductionOrderView: React.FC = () => {
Planlanan Maliyet Planlanan Maliyet
</div> </div>
<div className="text-lg font-bold text-gray-900"> <div className="text-lg font-bold text-gray-900">
{new Intl.NumberFormat("tr-TR", { {new Intl.NumberFormat('tr-TR', {
style: "currency", style: 'currency',
currency: productionOrder.currency, currency: productionOrder.currency,
}).format(productionOrder.plannedCost)} }).format(productionOrder.plannedCost)}
</div> </div>
@ -341,8 +298,8 @@ const ProductionOrderView: React.FC = () => {
Gerçekleşen Maliyet Gerçekleşen Maliyet
</div> </div>
<div className="text-lg font-bold text-green-600"> <div className="text-lg font-bold text-green-600">
{new Intl.NumberFormat("tr-TR", { {new Intl.NumberFormat('tr-TR', {
style: "currency", style: 'currency',
currency: productionOrder.currency, currency: productionOrder.currency,
}).format(productionOrder.actualCost)} }).format(productionOrder.actualCost)}
</div> </div>
@ -351,9 +308,7 @@ const ProductionOrderView: React.FC = () => {
<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">
Para Birimi Para Birimi
</div> </div>
<div className="font-semibold text-gray-900"> <div className="font-semibold text-gray-900">{productionOrder.currency}</div>
{productionOrder.currency}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -364,9 +319,7 @@ const ProductionOrderView: React.FC = () => {
<div className="p-1.5 bg-gray-100 rounded-lg"> <div className="p-1.5 bg-gray-100 rounded-lg">
<FaClipboardList className="text-gray-600 text-base" /> <FaClipboardList className="text-gray-600 text-base" />
</div> </div>
<h3 className="text-base font-bold text-gray-900"> <h3 className="text-base font-bold text-gray-900">Sistem Bilgileri</h3>
Sistem Bilgileri
</h3>
</div> </div>
<div className="space-y-2"> <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">
@ -374,10 +327,11 @@ const ProductionOrderView: React.FC = () => {
Oluşturulma Oluşturulma
</div> </div>
<div className="font-semibold text-gray-900"> <div className="font-semibold text-gray-900">
{new Date(productionOrder.creationTime).toLocaleDateString( {new Date(productionOrder.creationTime).toLocaleDateString('tr-TR', {
"tr-TR", year: 'numeric',
{ year: "numeric", month: "short", day: "numeric" } month: 'short',
)} day: 'numeric',
})}
</div> </div>
</div> </div>
<div className="py-1.5 border-b border-gray-100"> <div className="py-1.5 border-b border-gray-100">
@ -385,12 +339,10 @@ const ProductionOrderView: React.FC = () => {
Son Güncelleme Son Güncelleme
</div> </div>
<div className="font-semibold text-gray-900"> <div className="font-semibold text-gray-900">
{new Date( {new Date(productionOrder.lastModificationTime).toLocaleDateString('tr-TR', {
productionOrder.lastModificationTime year: 'numeric',
).toLocaleDateString("tr-TR", { month: 'short',
year: "numeric", day: 'numeric',
month: "short",
day: "numeric",
})} })}
</div> </div>
</div> </div>
@ -407,15 +359,11 @@ const ProductionOrderView: React.FC = () => {
</div> </div>
<div> <div>
<h3 className="text-base font-bold text-gray-900">Malzemeler</h3> <h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
<p className="text-gray-600 mt-1 text-sm"> <p className="text-gray-600 mt-1 text-sm">Üretim emrine bağlı malzeme satırları</p>
Üretim emrine bağlı malzeme satırları
</p>
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-lg font-bold text-indigo-600"> <div className="text-lg font-bold text-indigo-600">{materials.length}</div>
{materials.length}
</div>
<div className="text-sm text-gray-500">Malzeme</div> <div className="text-sm text-gray-500">Malzeme</div>
</div> </div>
</div> </div>
@ -445,8 +393,8 @@ const ProductionOrderView: React.FC = () => {
</div> </div>
<div className="font-bold text-gray-900 text-base"> <div className="font-bold text-gray-900 text-base">
{m.material?.code || m.materialId} {m.material?.code || m.materialId}
{" - "} {' - '}
{m.material?.name || "Malzeme adı bulunamadı"} {m.material?.name || 'Malzeme adı bulunamadı'}
</div> </div>
</div> </div>
</div> </div>
@ -462,9 +410,7 @@ const ProductionOrderView: React.FC = () => {
<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">
Planlanan Planlanan
</div> </div>
<div className="font-bold text-gray-900 text-base"> <div className="font-bold text-gray-900 text-base">{m.plannedQuantity}</div>
{m.plannedQuantity}
</div>
</div> </div>
<div className="text-center p-2 bg-green-50 rounded-lg"> <div className="text-center p-2 bg-green-50 rounded-lg">
@ -480,18 +426,14 @@ const ProductionOrderView: React.FC = () => {
<div className="text-xs font-medium text-blue-600 uppercase tracking-wide mb-1"> <div className="text-xs font-medium text-blue-600 uppercase tracking-wide mb-1">
Gereken Gereken
</div> </div>
<div className="font-bold text-blue-700 text-base"> <div className="font-bold text-blue-700 text-base">{m.requiredQuantity}</div>
{m.requiredQuantity}
</div>
</div> </div>
<div className="text-center p-2 bg-red-50 rounded-lg"> <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"> <div className="text-xs font-medium text-red-600 uppercase tracking-wide mb-1">
Fire Fire
</div> </div>
<div className="font-bold text-red-700 text-base"> <div className="font-bold text-red-700 text-base">{m.scrapQuantity}</div>
{m.scrapQuantity}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -500,7 +442,8 @@ const ProductionOrderView: React.FC = () => {
)} )}
</div> </div>
</div> </div>
); </Container>
}; )
}
export default ProductionOrderView; export default ProductionOrderView

View file

@ -1,100 +1,88 @@
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>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-xl font-bold text-gray-900"> <h2 className="text-xl font-bold text-gray-900">Malzeme İhtiyaçları</h2>
Malzeme İhtiyaçları
</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>
@ -110,19 +98,9 @@ const Requirements: React.FC = () => {
{/* Stats Cards */} {/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Widget <Widget title="Toplam İhtiyaç" value={totalRequirements} color="blue" icon="FaBox" />
title="Toplam İhtiyaç"
value={totalRequirements}
color="blue"
icon="FaBox"
/>
<Widget <Widget title="Eksik Stok" value={shortageRequirements} color="red" icon="FaArrowDown" />
title="Eksik Stok"
value={shortageRequirements}
color="red"
icon="FaArrowDown"
/>
<Widget <Widget
title="Acil İhtiyaç" title="Acil İhtiyaç"
@ -142,11 +120,11 @@ const Requirements: React.FC = () => {
{/* Tabs */} {/* Tabs */}
<div className="flex bg-gray-100 rounded-lg p-1"> <div className="flex bg-gray-100 rounded-lg p-1">
<button <button
onClick={() => setActiveTab("requirements")} onClick={() => setActiveTab('requirements')}
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${ className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
activeTab === "requirements" activeTab === 'requirements'
? "bg-white text-gray-900 shadow-sm" ? 'bg-white text-gray-900 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
@ -155,11 +133,11 @@ const Requirements: React.FC = () => {
</div> </div>
</button> </button>
<button <button
onClick={() => setActiveTab("production")} onClick={() => setActiveTab('production')}
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${ className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
activeTab === "production" activeTab === 'production'
? "bg-white text-gray-900 shadow-sm" ? 'bg-white text-gray-900 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
@ -168,11 +146,11 @@ const Requirements: React.FC = () => {
</div> </div>
</button> </button>
<button <button
onClick={() => setActiveTab("purchase")} onClick={() => setActiveTab('purchase')}
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${ className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
activeTab === "purchase" activeTab === 'purchase'
? "bg-white text-gray-900 shadow-sm" ? 'bg-white text-gray-900 shadow-sm'
: "text-gray-600 hover:text-gray-900" : 'text-gray-600 hover:text-gray-900'
}`} }`}
> >
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
@ -183,27 +161,28 @@ const Requirements: React.FC = () => {
</div> </div>
{/* Data Table */} {/* Data Table */}
{activeTab === "requirements" && ( {activeTab === 'requirements' && (
<MaterialRequirements materialRequirements={materialRequirements} /> <MaterialRequirements materialRequirements={materialRequirements} />
)} )}
{activeTab === "production" && ( {activeTab === 'production' && (
<ProductionSuggestions productionSuggestions={productionSuggestions} /> <ProductionSuggestions productionSuggestions={productionSuggestions} />
)} )}
{activeTab === "purchase" && ( {activeTab === 'purchase' && (
<PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} /> <PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} />
)} )}
</div>
<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,43 +294,36 @@ 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.filter((wo) => wo.status === status).length / workOrders.length) * 100 || 0,
workOrders.length) * }))
100 || 0,
})
);
return ( return (
<div className="space-y-3 pt-2"> <Container>
<div className="space-y-2">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2> <h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">Üretim operasyonlarının detaylı takibi</p>
Üretim operasyonlarının detaylı takibi
</p>
</div> </div>
<button <button
onClick={handleAdd} onClick={handleAdd}
@ -375,54 +336,30 @@ const WorkOrders: React.FC = () => {
{/* 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"
value={delayedOrders}
color="red"
icon="FaExclamationCircle"
/>
</div> </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ı
</h3>
<div className="grid grid-cols-2 md:grid-cols-5 gap-2"> <div className="grid grid-cols-2 md:grid-cols-5 gap-2">
{statusDistribution.map(({ status, count, percentage }) => ( {statusDistribution.map(({ status, count, percentage }) => (
<div key={status} className="text-center p-2 border rounded-lg"> <div key={status} className="text-center p-2 border rounded-lg">
<div <div
className={`inline-block px-1.5 py-0.5 text-xs font-medium rounded-full mb-1 ${getWorkOrderStatusColor( className={`inline-block px-1.5 py-0.5 text-xs font-medium rounded-full mb-1 ${getWorkOrderStatusColor(
status status,
)}`} )}`}
> >
{getWorkOrderStatusText(status)} {getWorkOrderStatusText(status)}
</div> </div>
<div className="text-xl font-bold text-gray-900">{count}</div> <div className="text-xl font-bold text-gray-900">{count}</div>
<div className="text-xs text-gray-500"> <div className="text-xs text-gray-500">{percentage.toFixed(1)}%</div>
{percentage.toFixed(1)}%
</div>
</div> </div>
))} ))}
</div> </div>
@ -442,9 +379,7 @@ const WorkOrders: React.FC = () => {
<select <select
value={selectedStatus} value={selectedStatus}
onChange={(e) => onChange={(e) => setSelectedStatus(e.target.value as WorkOrderStatusEnum | 'all')}
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" 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> <option value="all">Tüm Durumlar</option>
@ -457,9 +392,7 @@ const WorkOrders: React.FC = () => {
<select <select
value={selectedPriority} value={selectedPriority}
onChange={(e) => onChange={(e) => setSelectedPriority(e.target.value as PriorityEnum | 'all')}
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" 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> <option value="all">Tüm Öncelikler</option>
@ -479,14 +412,11 @@ const WorkOrders: React.FC = () => {
{filteredWorkOrders.length === 0 && ( {filteredWorkOrders.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">İş emri bulunamadı</h3>
İş emri bulunamadı <p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
</h3>
<p className="text-gray-500">
Arama kriterlerinizi değiştirmeyi deneyin.
</p>
</div> </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;