Container güncellemesi
This commit is contained in:
parent
7d52573765
commit
4e5322ba0c
49 changed files with 4049 additions and 5079 deletions
|
|
@ -82,7 +82,7 @@ define(['./workbox-a959eb95'], (function (workbox) { 'use strict';
|
||||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
"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"), {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const AccountingManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (
|
|
||||||
pathname === "/admin/accounting" ||
|
|
||||||
pathname === "/admin/accounting/current-accounts"
|
|
||||||
)
|
|
||||||
return "Cari Hesap Yönetimi";
|
|
||||||
if (pathname === "/admin/accounting/waybills") return "İrsaliye Yönetimi";
|
|
||||||
if (pathname === "/admin/accounting/invoices") return "Fatura Yönetimi";
|
|
||||||
if (pathname === "/admin/accounting/cash") return "Kasa Yönetimi";
|
|
||||||
if (pathname === "/admin/accounting/bank") return "Banka Yönetimi";
|
|
||||||
if (pathname === "/admin/accounting/check-note") return "Çek & Senet Takibi";
|
|
||||||
return "Muhasebe Yönetimi";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / CRM Yönetimi / Sayfanın Adı
|
|
||||||
const breadcrumbs = [{ name: "CRM Yönetimi", href: "/admin/crm" }];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader
|
|
||||||
title={pageTitle}
|
|
||||||
breadcrumbs={breadcrumbs}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AccountingManagement;
|
|
||||||
|
|
@ -458,7 +458,7 @@ const BankManagement: React.FC<BankManagementProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const CRMManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (pathname === "/admin/crm" || pathname === "/admin/crm/customers") return "Müşteriler";
|
|
||||||
if (pathname === "/admin/crm/sales-teams") return "Satış Takımları";
|
|
||||||
if (pathname === "/admin/crm/sales-teams/new") return "Yeni Satış Takımı";
|
|
||||||
if (pathname.includes("/admin/crm/sales-teams/edit/")) return "Satış Takımı Düzenle";
|
|
||||||
if (pathname.includes("/admin/crm/sales-teams/") && !pathname.includes("edit") && !pathname.includes("new")) return "Satış Takımı Detayları";
|
|
||||||
if (pathname === "/admin/crm/loss-reasons") return "Kayıp Nedenleri";
|
|
||||||
if (pathname === "/admin/crm/opportunities") return "Fırsatlar";
|
|
||||||
if (pathname === "/admin/crm/activities") return "Aktiviteler";
|
|
||||||
if (pathname === "/admin/crm/sales-orders") return "Satış Siparişleri";
|
|
||||||
if (pathname === "/admin/crm/sales-orders/new") return "Yeni Satış Siparişi";
|
|
||||||
if (pathname.includes("/admin/crm/sales-orders/edit/")) return "Satış Siparişi Düzenle";
|
|
||||||
if (pathname.includes("/admin/crm/sales-orders/") && !pathname.includes("edit") && !pathname.includes("new")) return "Satış Siparişi Detayları";
|
|
||||||
if (pathname === "/admin/crm/customers/new") return "Yeni Müşteri";
|
|
||||||
if (pathname.includes("/admin/crm/customers/edit/")) return "Müşteri Düzenle";
|
|
||||||
if (pathname.includes("/admin/crm/customers/") && !pathname.includes("edit") && !pathname.includes("new")) return "Müşteri Detayları";
|
|
||||||
return "CRM Yönetimi";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / CRM Yönetimi / Sayfanın Adı
|
|
||||||
const breadcrumbs = [
|
|
||||||
{ name: "CRM Yönetimi", href: "/admin/crm" }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader
|
|
||||||
title={pageTitle}
|
|
||||||
breadcrumbs={breadcrumbs}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CRMManagement;
|
|
||||||
|
|
@ -20,7 +20,6 @@ import {
|
||||||
getActivityStatusColor,
|
getActivityStatusColor,
|
||||||
getActivityStatusText,
|
getActivityStatusText,
|
||||||
getActivityTypeIcon,
|
getActivityTypeIcon,
|
||||||
getPsActivityTypeText,
|
|
||||||
getPriorityColor,
|
getPriorityColor,
|
||||||
getPriorityText,
|
getPriorityText,
|
||||||
getActivityTypeText,
|
getActivityTypeText,
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 */}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const HRManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (pathname === "/admin/hr" || pathname === "/admin/hr/") return "Personel Listesi";
|
|
||||||
if (pathname === "/admin/hr/departments") return "Departman Yönetimi";
|
|
||||||
if (pathname === "/admin/hr/cost-centers") return "Maliyet Merkezi Yönetimi";
|
|
||||||
if (pathname === "/admin/hr/organization") return "Organizasyon Şeması";
|
|
||||||
if (pathname === "/admin/hr/leave-management") return "İzin Yönetimi";
|
|
||||||
if (pathname === "/admin/hr/overtimes-management") return "Mesai Yönetimi";
|
|
||||||
if (pathname === "/admin/hr/payroll") return "Bordro Yönetimi";
|
|
||||||
if (pathname === "/admin/hr/job-positions") return "İş Pozisyonları";
|
|
||||||
if (pathname === "/admin/hr/employment-types") return "İstihdam Türleri";
|
|
||||||
if (pathname === "/admin/hr/badges") return "Rozet Yönetimi";
|
|
||||||
if (pathname === "/admin/hr/360-templates") return "360 Derece Şablonları";
|
|
||||||
if (pathname === "/admin/hr/360-evaluation") return "360 Derece Değerlendirme";
|
|
||||||
if (pathname === "/admin/hr/employees/new") return "Yeni Çalışan";
|
|
||||||
if (pathname.includes("/admin/hr/employees/edit/")) return "Çalışan Düzenle";
|
|
||||||
if (pathname.includes("/admin/hr/employees/") && !pathname.includes("edit") && !pathname.includes("new")) return "Çalışan Detayları";
|
|
||||||
return "İnsan Kaynakları";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / İnsan Kaynakları / Sayfanın Adı
|
|
||||||
const breadcrumbs = [
|
|
||||||
{ name: "İnsan Kaynakları", href: "/admin/hr" }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader
|
|
||||||
title={pageTitle}
|
|
||||||
breadcrumbs={breadcrumbs}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HRManagement;
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const MaintenanceManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (pathname === "/admin/maintenance" || pathname === "/admin/maintenance/" || pathname === "/admin/maintenance/equipment") return "İş Merkezleri";
|
|
||||||
if (pathname === "/admin/maintenance/workcenters") return "İş Merkezleri";
|
|
||||||
if (pathname === "/admin/maintenance/plans") return "Bakım Planları";
|
|
||||||
if (pathname === "/admin/maintenance/calendar") return "Bakım Takvimi";
|
|
||||||
if (pathname === "/admin/maintenance/teams") return "Bakım Takımları";
|
|
||||||
if (pathname === "/admin/maintenance/faults") return "Arıza Bildirimleri";
|
|
||||||
if (pathname === "/admin/maintenance/workorders") return "Bakım İş Emirleri";
|
|
||||||
return "Bakım Yönetimi";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / Bakım Yönetimi / Sayfanın Adı
|
|
||||||
const breadcrumbs = [
|
|
||||||
{ name: "Bakım Yönetimi", href: "/admin/maintenance" }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader
|
|
||||||
title={pageTitle}
|
|
||||||
breadcrumbs={breadcrumbs}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MaintenanceManagement;
|
|
||||||
|
|
@ -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 iş merkezlerini yönetin ve takip edin</p>
|
||||||
Tüm iş 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 iş merkezi ekleyin.
|
Arama kriterlerinizi değiştirin veya yeni bir iş 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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const MRPManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (pathname === "/admin/mrp/operation-types") return "Operasyon Türleri";
|
|
||||||
if (pathname === "/admin/mrp/workcenters") return "İş Merkezleri";
|
|
||||||
if (pathname === "/admin/mrp/operations") return "Operasyon Tanımları";
|
|
||||||
if (pathname === "/admin/mrp/bom") return "Ürün Ağaçları (BOM)";
|
|
||||||
if (pathname === "/admin/mrp/production-orders") return "Üretim Emirleri";
|
|
||||||
if (pathname === "/admin/mrp/work-orders") return "İş Emirleri";
|
|
||||||
if (pathname === "/admin/mrp/demand-planning") return "Talep Planlama";
|
|
||||||
if (pathname === "/admin/mrp/material-requirements") return "Malzeme İhtiyaçları";
|
|
||||||
if (pathname === "/admin/mrp/planning-gantt") return "Planlama Gantt";
|
|
||||||
return "MRP Yönetimi";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / MRP Yönetimi / Sayfanın Adı
|
|
||||||
const breadcrumbs = [{ name: "MRP Yönetimi", href: "/admin/mrp" }];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader
|
|
||||||
title={pageTitle}
|
|
||||||
breadcrumbs={breadcrumbs}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MRPManagement;
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const ProjectManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (pathname === "/admin/projects" || pathname === "/admin/projects/") return "Proje Listesi";
|
|
||||||
if (pathname === "/admin/projects/tasks") return "Görev Yönetimi";
|
|
||||||
if (pathname === "/admin/projects/phases") return "Proje Aşamaları";
|
|
||||||
if (pathname === "/admin/projects/activities") return "Aktivite Türleri";
|
|
||||||
if (pathname === "/admin/projects/cost-tracking") return "Maliyet Zaman Takibi";
|
|
||||||
if (pathname === "/admin/projects/workload") return "İş Yükü Gantt";
|
|
||||||
if (pathname === "/admin/projects/daily-updates") return "Görev Günlük Güncellemeler";
|
|
||||||
if (pathname === "/admin/projects/new") return "Yeni Proje";
|
|
||||||
if (pathname.includes("/admin/projects/edit/")) return "Proje Düzenle";
|
|
||||||
if (pathname.includes("/admin/projects/") && !pathname.includes("edit") && !pathname.includes("new")) return "Proje Detayları";
|
|
||||||
return "Proje Yönetimi";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / Proje Yönetimi / Sayfanın Adı
|
|
||||||
const breadcrumbs = [
|
|
||||||
{ name: "Proje Yönetimi", href: "/admin/projects" }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader
|
|
||||||
title={pageTitle}
|
|
||||||
breadcrumbs={breadcrumbs}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProjectManagement;
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const SupplyChainManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (pathname === "/admin/supplychain" || pathname === "/admin/supplychain/suppliers")
|
|
||||||
return "Tedarikçiler";
|
|
||||||
if (pathname === "/admin/supplychain/materials/groups") return "Malzeme Grupları";
|
|
||||||
if (pathname === "/admin/supplychain/materials/types") return "Malzeme Tipleri";
|
|
||||||
if (pathname === "/admin/supplychain/materials") return "Malzeme Listesi";
|
|
||||||
if (pathname === "/admin/supplychain/requests") return "Satınalma Talepleri";
|
|
||||||
if (pathname === "/admin/supplychain/requests/new")
|
|
||||||
return "Yeni Satınalma Talebi";
|
|
||||||
if (pathname.includes("/admin/supplychain/requests/edit/"))
|
|
||||||
return "Satınalma Talebi Düzenle";
|
|
||||||
if (pathname.includes("/admin/supplychain/requests/view/"))
|
|
||||||
return "Satınalma Talebi Detayları";
|
|
||||||
if (pathname === "/admin/supplychain/quotations") return "Teklif Yönetimi";
|
|
||||||
if (pathname === "/admin/supplychain/quotations/new") return "Yeni Teklif";
|
|
||||||
if (pathname.includes("/admin/supplychain/quotations/edit/"))
|
|
||||||
return "Teklif Düzenle";
|
|
||||||
if (pathname.includes("/admin/supplychain/quotations/view/"))
|
|
||||||
return "Teklif Detayları";
|
|
||||||
if (pathname === "/admin/supplychain/approvals") return "Onay İş Akışları";
|
|
||||||
if (pathname === "/admin/supplychain/orders") return "Sipariş Yönetimi";
|
|
||||||
if (pathname === "/admin/supplychain/orders/new") return "Yeni Sipariş";
|
|
||||||
if (pathname.includes("/admin/supplychain/orders/edit/"))
|
|
||||||
return "Sipariş Düzenle";
|
|
||||||
if (pathname.includes("/admin/supplychain/orders/view/"))
|
|
||||||
return "Sipariş Detayları";
|
|
||||||
if (pathname === "/admin/supplychain/delivery") return "Teslimat Takibi";
|
|
||||||
return "Satınalma Yönetimi";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / Satınalma Yönetimi / Sayfanın Adı
|
|
||||||
const breadcrumbs = [{ name: "Satınalma Yönetimi", href: "/admin/supplychain" }];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader title={pageTitle} breadcrumbs={breadcrumbs} />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SupplyChainManagement;
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
|
||||||
import ModuleHeader from "../../components/common/ModuleHeader";
|
|
||||||
|
|
||||||
const WarehouseManagement: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
// Define page mappings for breadcrumbs
|
|
||||||
const getPageTitle = (pathname: string) => {
|
|
||||||
if (
|
|
||||||
pathname === "/admin/warehouse" ||
|
|
||||||
pathname === "/admin/warehouse/" ||
|
|
||||||
pathname === "/admin/warehouse/definitions"
|
|
||||||
)
|
|
||||||
return "Depo Tanımları";
|
|
||||||
if (pathname === "/admin/warehouse/tracking") return "Lokasyon Takibi";
|
|
||||||
if (pathname === "/admin/warehouse/putaway") return "Yerleştirme Kuralları";
|
|
||||||
if (pathname === "/admin/warehouse/inventory") return "Stok Durumu";
|
|
||||||
if (pathname === "/admin/warehouse/receipt") return "Stok Giriş";
|
|
||||||
if (pathname === "/admin/warehouse/issue") return "Stok Çıkış";
|
|
||||||
if (pathname === "/admin/warehouse/transfer") return "Stok Transfer";
|
|
||||||
if (pathname === "/admin/warehouse/movements") return "Stok Hareketleri";
|
|
||||||
if (pathname === "/admin/warehouse/stocklevel") return "Envanter Takibi";
|
|
||||||
return "Depo Yönetimi";
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageTitle = getPageTitle(location.pathname);
|
|
||||||
|
|
||||||
// Create breadcrumbs: Anasayfa / Depo Yönetimi / Sayfanın Adı
|
|
||||||
const breadcrumbs = [{ name: "Depo Yönetimi", href: "/admin/warehouse" }];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<ModuleHeader title={pageTitle} breadcrumbs={breadcrumbs} />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WarehouseManagement;
|
|
||||||
Loading…
Reference in a new issue