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,79 +391,79 @@ const LossReasons: React.FC = () => {
|
||||||
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Modals */}
|
{/* Modals */}
|
||||||
<LossReasonModal
|
<LossReasonModal
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setIsModalOpen(false)
|
setIsModalOpen(false)
|
||||||
setEditingReason(null)
|
setEditingReason(null)
|
||||||
}}
|
}}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
editingReason={editingReason}
|
editingReason={editingReason}
|
||||||
mode={modalMode}
|
mode={modalMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Delete Confirmation Modal */}
|
{/* Delete Confirmation Modal */}
|
||||||
{isDeleteModalOpen && (
|
{isDeleteModalOpen && (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
|
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-3 border-b">
|
<div className="flex items-center justify-between p-3 border-b">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<FaExclamationTriangle className="w-6 h-6 text-red-600" />
|
<FaExclamationTriangle className="w-6 h-6 text-red-600" />
|
||||||
<h3 className="text-sm font-semibold text-gray-900">Silme Onayı</h3>
|
<h3 className="text-sm font-semibold text-gray-900">Silme Onayı</h3>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsDeleteModalOpen(false)
|
||||||
|
setReasonToDelete(null)
|
||||||
|
}}
|
||||||
|
className="text-gray-400 hover:text-gray-600"
|
||||||
|
>
|
||||||
|
<FaTimes className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="p-3">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="mx-auto flex items-center justify-center h-10 w-10 rounded-full bg-red-100 mb-3">
|
||||||
|
<FaTrash className="h-6 w-6 text-red-600" />
|
||||||
</div>
|
</div>
|
||||||
|
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||||
|
Kayıp nedeni silinsin mi?
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500 mb-6">
|
||||||
|
<span className="font-medium">"{reasonToDelete?.name}"</span> kayıp nedenini
|
||||||
|
silmek üzeresiniz. Bu işlem geri alınamaz.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Buttons */}
|
||||||
|
<div className="flex justify-end gap-1.5">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsDeleteModalOpen(false)
|
setIsDeleteModalOpen(false)
|
||||||
setReasonToDelete(null)
|
setReasonToDelete(null)
|
||||||
}}
|
}}
|
||||||
className="text-gray-400 hover:text-gray-600"
|
className="px-3 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors"
|
||||||
>
|
>
|
||||||
<FaTimes className="w-5 h-5" />
|
İptal
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={confirmDelete}
|
||||||
|
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors"
|
||||||
|
>
|
||||||
|
<FaTrash className="w-4 h-4" />
|
||||||
|
Sil
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
<div className="p-3">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="mx-auto flex items-center justify-center h-10 w-10 rounded-full bg-red-100 mb-3">
|
|
||||||
<FaTrash className="h-6 w-6 text-red-600" />
|
|
||||||
</div>
|
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
|
||||||
Kayıp nedeni silinsin mi?
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-gray-500 mb-6">
|
|
||||||
<span className="font-medium">"{reasonToDelete?.name}"</span> kayıp nedenini
|
|
||||||
silmek üzeresiniz. Bu işlem geri alınamaz.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Buttons */}
|
|
||||||
<div className="flex justify-end gap-1.5">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setIsDeleteModalOpen(false)
|
|
||||||
setReasonToDelete(null)
|
|
||||||
}}
|
|
||||||
className="px-3 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors"
|
|
||||||
>
|
|
||||||
İptal
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={confirmDelete}
|
|
||||||
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTrash className="w-4 h-4" />
|
|
||||||
Sil
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ const OpportunityManagement: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<div className="space-y-3 pt-2">
|
<div className="space-y-2">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -384,23 +384,23 @@ const OpportunityManagement: React.FC = () => {
|
||||||
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Modals */}
|
|
||||||
<OpportunityForm
|
|
||||||
isOpen={isFormOpen}
|
|
||||||
onClose={handleCloseForm}
|
|
||||||
onSave={handleSaveOpportunity}
|
|
||||||
opportunity={selectedOpportunity}
|
|
||||||
mode={formMode}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<OpportunityDetails
|
|
||||||
isOpen={isDetailsOpen}
|
|
||||||
onClose={handleCloseDetails}
|
|
||||||
onEdit={handleEditFromDetails}
|
|
||||||
opportunity={selectedOpportunity}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Modals */}
|
||||||
|
<OpportunityForm
|
||||||
|
isOpen={isFormOpen}
|
||||||
|
onClose={handleCloseForm}
|
||||||
|
onSave={handleSaveOpportunity}
|
||||||
|
opportunity={selectedOpportunity}
|
||||||
|
mode={formMode}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<OpportunityDetails
|
||||||
|
isOpen={isDetailsOpen}
|
||||||
|
onClose={handleCloseDetails}
|
||||||
|
onEdit={handleEditFromDetails}
|
||||||
|
opportunity={selectedOpportunity}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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,330 +76,332 @@ const WorkCenterList: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 pt-2">
|
<Container>
|
||||||
{/* Header Actions */}
|
<div className="space-y-2">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
{/* Header Actions */}
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||||
<div className="relative">
|
<div className="flex items-center space-x-3">
|
||||||
<FaSearch
|
<div className="relative">
|
||||||
size={20}
|
<FaSearch
|
||||||
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
|
size={20}
|
||||||
/>
|
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
|
||||||
<input
|
/>
|
||||||
type="text"
|
<input
|
||||||
placeholder="İş merkezi kodu veya adı..."
|
type="text"
|
||||||
value={searchTerm}
|
placeholder="İş merkezi kodu veya adı..."
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
value={searchTerm}
|
||||||
className="pl-10 pr-4 py-1.5 text-sm w-80 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
className="pl-10 pr-4 py-1.5 text-sm w-80 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setShowFilters(!showFilters)}
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center px-4 py-2 border rounded-lg transition-colors',
|
||||||
|
showFilters
|
||||||
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
||||||
|
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FaFilter size={16} className="mr-2" />
|
||||||
|
Filtreler
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<div className="flex items-center space-x-2">
|
||||||
onClick={() => setShowFilters(!showFilters)}
|
<button
|
||||||
className={classNames(
|
onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
|
||||||
'flex items-center px-4 py-2 border rounded-lg transition-colors',
|
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||||
showFilters
|
>
|
||||||
? 'border-blue-500 bg-blue-50 text-blue-700'
|
<FaDownload size={16} className="mr-2" />
|
||||||
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
|
Dışa Aktar
|
||||||
)}
|
</button>
|
||||||
>
|
|
||||||
<FaFilter size={16} className="mr-2" />
|
|
||||||
Filtreler
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<Link
|
||||||
<button
|
to="/admin/maintenance/equipment/new"
|
||||||
onClick={() => alert('Dışa aktarma özelliği yakında eklenecek')}
|
className="flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||||
className="flex items-center px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
>
|
||||||
>
|
<FaPlus size={16} className="mr-2" />
|
||||||
<FaDownload size={16} className="mr-2" />
|
Yeni İş Merkezi
|
||||||
Dışa Aktar
|
</Link>
|
||||||
</button>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
to="/admin/maintenance/equipment/new"
|
|
||||||
className="flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
|
||||||
>
|
|
||||||
<FaPlus size={16} className="mr-2" />
|
|
||||||
Yeni İş Merkezi
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Filters Panel */}
|
|
||||||
{showFilters && (
|
|
||||||
<div className="bg-white border border-gray-200 rounded-lg p-3">
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
|
||||||
<select
|
|
||||||
value={filterStatus}
|
|
||||||
onChange={(e) => setFilterStatus(e.target.value)}
|
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
>
|
|
||||||
<option value="all">Tümü</option>
|
|
||||||
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
|
||||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
|
||||||
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
|
||||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">Kritiklik</label>
|
|
||||||
<select
|
|
||||||
value={filterCriticality}
|
|
||||||
onChange={(e) => setFilterCriticality(e.target.value)}
|
|
||||||
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
>
|
|
||||||
<option value="all">Tümü</option>
|
|
||||||
<option value={CriticalityLevelEnum.Low}>Düşük</option>
|
|
||||||
<option value={CriticalityLevelEnum.Medium}>Orta</option>
|
|
||||||
<option value={CriticalityLevelEnum.High}>Yüksek</option>
|
|
||||||
<option value={CriticalityLevelEnum.Critical}>Kritik</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-end">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setFilterStatus('all')
|
|
||||||
setFilterCriticality('all')
|
|
||||||
setSearchTerm('')
|
|
||||||
}}
|
|
||||||
className="w-full px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
|
||||||
>
|
|
||||||
Filtreleri Temizle
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Statistics Cards */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-600">Toplam İş Merkezi</p>
|
|
||||||
<p className="text-xl font-bold text-gray-900">{workCenter?.length || 0}</p>
|
|
||||||
</div>
|
|
||||||
<FaCog className="h-8 w-8 text-blue-600" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
{/* Filters Panel */}
|
||||||
<div className="flex items-center justify-between">
|
{showFilters && (
|
||||||
<div>
|
<div className="bg-white border border-gray-200 rounded-lg p-3">
|
||||||
<p className="text-sm font-medium text-gray-600">Operasyonel</p>
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||||
<p className="text-xl font-bold text-green-600">
|
<div>
|
||||||
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational).length ||
|
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||||
0}
|
<select
|
||||||
</p>
|
value={filterStatus}
|
||||||
</div>
|
onChange={(e) => setFilterStatus(e.target.value)}
|
||||||
<FaCheckCircle className="h-8 w-8 text-green-600" />
|
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
</div>
|
>
|
||||||
</div>
|
<option value="all">Tümü</option>
|
||||||
|
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||||
|
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||||
|
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||||
|
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
<div>
|
||||||
<div className="flex items-center justify-between">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Kritiklik</label>
|
||||||
<div>
|
<select
|
||||||
<p className="text-sm font-medium text-gray-600">Bakımda</p>
|
value={filterCriticality}
|
||||||
<p className="text-xl font-bold text-yellow-600">
|
onChange={(e) => setFilterCriticality(e.target.value)}
|
||||||
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.UnderMaintenance)
|
className="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
.length || 0}
|
>
|
||||||
</p>
|
<option value="all">Tümü</option>
|
||||||
</div>
|
<option value={CriticalityLevelEnum.Low}>Düşük</option>
|
||||||
<FaWrench className="h-8 w-8 text-yellow-600" />
|
<option value={CriticalityLevelEnum.Medium}>Orta</option>
|
||||||
</div>
|
<option value={CriticalityLevelEnum.High}>Yüksek</option>
|
||||||
</div>
|
<option value={CriticalityLevelEnum.Critical}>Kritik</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
<div className="flex items-end">
|
||||||
<div className="flex items-center justify-between">
|
<button
|
||||||
<div>
|
onClick={() => {
|
||||||
<p className="text-sm font-medium text-gray-600">Kritik İş Merkezi</p>
|
setFilterStatus('all')
|
||||||
<p className="text-xl font-bold text-red-600">
|
setFilterCriticality('all')
|
||||||
{workCenter?.filter((e) => e.criticality === CriticalityLevelEnum.Critical)
|
setSearchTerm('')
|
||||||
.length || 0}
|
}}
|
||||||
</p>
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 bg-white text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||||
</div>
|
>
|
||||||
<FaExclamationTriangle className="h-8 w-8 text-red-600" />
|
Filtreleri Temizle
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* WorkCenter Table */}
|
|
||||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
|
||||||
<div className="px-4 py-3 border-b border-gray-200">
|
|
||||||
<h2 className="text-xl font-bold text-gray-900">WorkCenter Listesi</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="w-full">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
İş Merkezi Bilgileri
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Üretici / Model
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Lokasyon / Departman
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Durum / Kritiklik
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Kurulum / Garanti
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Performans
|
|
||||||
</th>
|
|
||||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
İşlemler
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{workCenter?.map((eq) => (
|
|
||||||
<tr key={eq.id} className="hover:bg-gray-50 transition-colors">
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-shrink-0 h-10 w-10">
|
|
||||||
<div className="h-10 w-10 rounded-lg bg-blue-100 flex items-center justify-center">
|
|
||||||
<FaCog className="h-5 w-5 text-blue-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-3">
|
|
||||||
<div className="text-sm font-medium text-gray-900">{eq.code}</div>
|
|
||||||
<div className="text-sm text-gray-500 max-w-xs truncate">{eq.name}</div>
|
|
||||||
{eq.serialNumber && (
|
|
||||||
<div className="text-xs text-gray-400 mt-1">S/N: {eq.serialNumber}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-gray-900">
|
|
||||||
{eq.manufacturer || '-'}
|
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">{eq.model || '-'}</div>
|
|
||||||
<div className="text-xs text-gray-400 mt-1">{eq.workCenterType?.name}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-gray-900">{eq.location}</div>
|
|
||||||
<div className="text-sm text-gray-500">{eq.departmentId}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<span
|
|
||||||
className={classNames(
|
|
||||||
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
|
|
||||||
getWorkCenterStatusColor(eq.status),
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{getWorkCenterStatusIcon(eq.status)}
|
|
||||||
<span className="ml-1">{getWorkCenterStatusText(eq.status)}</span>
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
'text-sm font-medium',
|
|
||||||
getCriticalityLevelColor(eq.criticality),
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{getCriticalityLevelText(eq.criticality)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div className="space-y-1">
|
|
||||||
<div className="flex items-center text-sm text-gray-900">
|
|
||||||
<FaCalendar size={14} className="mr-1" />
|
|
||||||
{dayjs(eq.installationDate).format('DD.MM.YYYY')}
|
|
||||||
</div>
|
|
||||||
{eq.warrantyExpiry && (
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
Garanti: {dayjs(eq.warrantyExpiry).format('DD.MM.YYYY')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div className="space-y-1">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<FaChartLine size={14} className="text-green-500 mr-1" />
|
|
||||||
<span className="text-sm text-green-600">98.5% Uptime</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<FaArrowUp size={14} className="text-blue-500 mr-1" />
|
|
||||||
<span className="text-sm text-blue-600">Verimli</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td className="px-4 py-3 text-right">
|
|
||||||
<div className="flex items-center justify-end space-x-1">
|
|
||||||
<Link
|
|
||||||
to={`/admin/maintenance/equipment/${eq.id}`}
|
|
||||||
className="p-1.5 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
|
|
||||||
title="Detayları Görüntüle"
|
|
||||||
>
|
|
||||||
<FaEye size={16} />
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
to={`/admin/maintenance/equipment/edit/${eq.id}`}
|
|
||||||
className="p-1.5 text-gray-600 hover:text-yellow-600 hover:bg-yellow-50 rounded-lg transition-colors"
|
|
||||||
title="Düzenle"
|
|
||||||
>
|
|
||||||
<FaEdit size={16} />
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => alert('Bakım emri oluşturma özelliği yakında eklenecek')}
|
|
||||||
className="p-1.5 text-gray-600 hover:text-green-600 hover:bg-green-50 rounded-lg transition-colors"
|
|
||||||
title="Bakım Emri Oluştur"
|
|
||||||
>
|
|
||||||
<FaWrench size={16} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{(!workCenter || workCenter.length === 0) && (
|
|
||||||
<div className="text-center py-10">
|
|
||||||
<FaCog className="mx-auto h-10 w-10 text-gray-400" />
|
|
||||||
<h3 className="mt-1.5 text-sm font-medium text-gray-900">İş Merkezi bulunamadı</h3>
|
|
||||||
<p className="mt-1 text-sm text-gray-500">Yeni iş merkezi ekleyerek başlayın.</p>
|
|
||||||
<div className="mt-4">
|
|
||||||
<Link
|
|
||||||
to="/admin/maintenance/equipment/new"
|
|
||||||
className="inline-flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
|
||||||
>
|
|
||||||
<FaPlus size={16} className="mr-2" />
|
|
||||||
Yeni İş Merkezi Ekle
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Statistics Cards */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-600">Toplam İş Merkezi</p>
|
||||||
|
<p className="text-xl font-bold text-gray-900">{workCenter?.length || 0}</p>
|
||||||
|
</div>
|
||||||
|
<FaCog className="h-8 w-8 text-blue-600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-600">Operasyonel</p>
|
||||||
|
<p className="text-xl font-bold text-green-600">
|
||||||
|
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational)
|
||||||
|
.length || 0}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<FaCheckCircle className="h-8 w-8 text-green-600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-600">Bakımda</p>
|
||||||
|
<p className="text-xl font-bold text-yellow-600">
|
||||||
|
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.UnderMaintenance)
|
||||||
|
.length || 0}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<FaWrench className="h-8 w-8 text-yellow-600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-600">Kritik İş Merkezi</p>
|
||||||
|
<p className="text-xl font-bold text-red-600">
|
||||||
|
{workCenter?.filter((e) => e.criticality === CriticalityLevelEnum.Critical)
|
||||||
|
.length || 0}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<FaExclamationTriangle className="h-8 w-8 text-red-600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* WorkCenter Table */}
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||||
|
<div className="px-4 py-3 border-b border-gray-200">
|
||||||
|
<h2 className="text-xl font-bold text-gray-900">WorkCenter Listesi</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full">
|
||||||
|
<thead className="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
İş Merkezi Bilgileri
|
||||||
|
</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Üretici / Model
|
||||||
|
</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Lokasyon / Departman
|
||||||
|
</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Durum / Kritiklik
|
||||||
|
</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Kurulum / Garanti
|
||||||
|
</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Performans
|
||||||
|
</th>
|
||||||
|
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
İşlemler
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
|
{workCenter?.map((eq) => (
|
||||||
|
<tr key={eq.id} className="hover:bg-gray-50 transition-colors">
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex-shrink-0 h-10 w-10">
|
||||||
|
<div className="h-10 w-10 rounded-lg bg-blue-100 flex items-center justify-center">
|
||||||
|
<FaCog className="h-5 w-5 text-blue-600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="ml-3">
|
||||||
|
<div className="text-sm font-medium text-gray-900">{eq.code}</div>
|
||||||
|
<div className="text-sm text-gray-500 max-w-xs truncate">{eq.name}</div>
|
||||||
|
{eq.serialNumber && (
|
||||||
|
<div className="text-xs text-gray-400 mt-1">S/N: {eq.serialNumber}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium text-gray-900">
|
||||||
|
{eq.manufacturer || '-'}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-500">{eq.model || '-'}</div>
|
||||||
|
<div className="text-xs text-gray-400 mt-1">{eq.workCenterType?.name}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium text-gray-900">{eq.location}</div>
|
||||||
|
<div className="text-sm text-gray-500">{eq.departmentId}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<span
|
||||||
|
className={classNames(
|
||||||
|
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
|
||||||
|
getWorkCenterStatusColor(eq.status),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{getWorkCenterStatusIcon(eq.status)}
|
||||||
|
<span className="ml-1">{getWorkCenterStatusText(eq.status)}</span>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'text-sm font-medium',
|
||||||
|
getCriticalityLevelColor(eq.criticality),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{getCriticalityLevelText(eq.criticality)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center text-sm text-gray-900">
|
||||||
|
<FaCalendar size={14} className="mr-1" />
|
||||||
|
{dayjs(eq.installationDate).format('DD.MM.YYYY')}
|
||||||
|
</div>
|
||||||
|
{eq.warrantyExpiry && (
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
Garanti: {dayjs(eq.warrantyExpiry).format('DD.MM.YYYY')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FaChartLine size={14} className="text-green-500 mr-1" />
|
||||||
|
<span className="text-sm text-green-600">98.5% Uptime</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FaArrowUp size={14} className="text-blue-500 mr-1" />
|
||||||
|
<span className="text-sm text-blue-600">Verimli</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-4 py-3 text-right">
|
||||||
|
<div className="flex items-center justify-end space-x-1">
|
||||||
|
<Link
|
||||||
|
to={`/admin/maintenance/equipment/${eq.id}`}
|
||||||
|
className="p-1.5 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
|
||||||
|
title="Detayları Görüntüle"
|
||||||
|
>
|
||||||
|
<FaEye size={16} />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to={`/admin/maintenance/equipment/edit/${eq.id}`}
|
||||||
|
className="p-1.5 text-gray-600 hover:text-yellow-600 hover:bg-yellow-50 rounded-lg transition-colors"
|
||||||
|
title="Düzenle"
|
||||||
|
>
|
||||||
|
<FaEdit size={16} />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => alert('Bakım emri oluşturma özelliği yakında eklenecek')}
|
||||||
|
className="p-1.5 text-gray-600 hover:text-green-600 hover:bg-green-50 rounded-lg transition-colors"
|
||||||
|
title="Bakım Emri Oluştur"
|
||||||
|
>
|
||||||
|
<FaWrench size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(!workCenter || workCenter.length === 0) && (
|
||||||
|
<div className="text-center py-10">
|
||||||
|
<FaCog className="mx-auto h-10 w-10 text-gray-400" />
|
||||||
|
<h3 className="mt-1.5 text-sm font-medium text-gray-900">İş Merkezi bulunamadı</h3>
|
||||||
|
<p className="mt-1 text-sm text-gray-500">Yeni iş merkezi ekleyerek başlayın.</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Link
|
||||||
|
to="/admin/maintenance/equipment/new"
|
||||||
|
className="inline-flex items-center px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||||
|
>
|
||||||
|
<FaPlus size={16} className="mr-2" />
|
||||||
|
Yeni İş Merkezi Ekle
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</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,347 +10,325 @@ import {
|
||||||
FaCopy,
|
FaCopy,
|
||||||
FaExclamationTriangle,
|
FaExclamationTriangle,
|
||||||
FaClock,
|
FaClock,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from "../../../types/mrp";
|
import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from '../../../types/mrp'
|
||||||
import BOMFormModal from "./BOMFormModal";
|
import BOMFormModal from './BOMFormModal'
|
||||||
import { getBOMTypeColor, getBOMTypeName } from "../../../utils/erp";
|
import { getBOMTypeColor, getBOMTypeName } from '../../../utils/erp'
|
||||||
import { mockBOMs } from "../../../mocks/mockBOMs";
|
import { mockBOMs } from '../../../mocks/mockBOMs'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const BOMManagement: React.FC = () => {
|
const BOMManagement: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null);
|
const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null)
|
||||||
const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null);
|
const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null)
|
||||||
|
|
||||||
// Mock data - replace with actual API calls
|
// Mock data - replace with actual API calls
|
||||||
const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs);
|
const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs)
|
||||||
|
|
||||||
const filteredBOMs = boms.filter(
|
const filteredBOMs = boms.filter(
|
||||||
(bom) =>
|
(bom) =>
|
||||||
bom.bomCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
bom.bomCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
bom.materialId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
bom.materialId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
bom.version.toLowerCase().includes(searchTerm.toLowerCase())
|
bom.version.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||||
);
|
)
|
||||||
|
|
||||||
const getTotalOperationTime = (operations: MrpBOMOperation[]) => {
|
const getTotalOperationTime = (operations: MrpBOMOperation[]) => {
|
||||||
return operations.reduce(
|
return operations.reduce((total, op) => total + op.setupTime + op.runTime, 0)
|
||||||
(total, op) => total + op.setupTime + op.runTime,
|
}
|
||||||
0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTotalComponents = (components: MrpBOMComponent[]) => {
|
const getTotalComponents = (components: MrpBOMComponent[]) => {
|
||||||
return components.length;
|
return components.length
|
||||||
};
|
}
|
||||||
|
|
||||||
const getActiveComponents = (components: MrpBOMComponent[]) => {
|
const getActiveComponents = (components: MrpBOMComponent[]) => {
|
||||||
return components.filter((comp) => comp.isActive).length;
|
return components.filter((comp) => comp.isActive).length
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEdit = (bom: MrpBOM) => {
|
const handleEdit = (bom: MrpBOM) => {
|
||||||
setEditingBOM(bom);
|
setEditingBOM(bom)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAddNew = () => {
|
const handleAddNew = () => {
|
||||||
setEditingBOM(null);
|
setEditingBOM(null)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleViewDetails = (bom: MrpBOM) => {
|
const handleViewDetails = (bom: MrpBOM) => {
|
||||||
setSelectedBOM(bom);
|
setSelectedBOM(bom)
|
||||||
|
|
||||||
console.log(bom);
|
console.log(bom)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCopy = (bom: MrpBOM) => {
|
const handleCopy = (bom: MrpBOM) => {
|
||||||
console.log("Copying BOM:", bom.bomCode);
|
console.log('Copying BOM:', bom.bomCode)
|
||||||
// Implementation for copying BOM
|
// Implementation for copying BOM
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = (bom: MrpBOM) => {
|
const handleSave = (bom: MrpBOM) => {
|
||||||
setBoms((prev) => {
|
setBoms((prev) => {
|
||||||
const existing = prev.find((b) => b.id === bom.id);
|
const existing = prev.find((b) => b.id === bom.id)
|
||||||
if (existing) return prev.map((b) => (b.id === bom.id ? bom : b));
|
if (existing) return prev.map((b) => (b.id === bom.id ? bom : b))
|
||||||
return [...prev, { ...bom, id: bom.id || String(Date.now()) }];
|
return [...prev, { ...bom, id: bom.id || String(Date.now()) }]
|
||||||
});
|
})
|
||||||
setShowModal(false);
|
setShowModal(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-bold text-gray-900">
|
<div>
|
||||||
Ürün Ağaçları (BOM)
|
<h2 className="text-xl font-bold text-gray-900">Ürün Ağaçları (BOM)</h2>
|
||||||
</h2>
|
<p className="text-sm text-gray-600 mt-0.5">
|
||||||
<p className="text-sm text-gray-600 mt-0.5">
|
Ürün bileşenlerini ve üretim operasyonlarını yönetin
|
||||||
Ürün bileşenlerini ve üretim operasyonlarını yönetin
|
</p>
|
||||||
</p>
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={handleAddNew}
|
||||||
|
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||||
|
>
|
||||||
|
<FaPlus className="w-4 h-4" />
|
||||||
|
<span>Yeni BOM</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
onClick={handleAddNew}
|
|
||||||
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
|
||||||
>
|
|
||||||
<FaPlus className="w-4 h-4" />
|
|
||||||
<span>Yeni BOM</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search Bar */}
|
{/* Search Bar */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="BOM ara..."
|
placeholder="BOM ara..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="w-full pl-10 pr-4 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
className="w-full pl-10 pr-4 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
{/* BOM List */}
|
{/* BOM List */}
|
||||||
<div className="space-y-3 pt-2">
|
<div className="space-y-3 pt-2">
|
||||||
<h3 className="text-lg font-semibold text-gray-900">BOM Listesi</h3>
|
<h3 className="text-lg font-semibold text-gray-900">BOM Listesi</h3>
|
||||||
{filteredBOMs.map((bom) => (
|
{filteredBOMs.map((bom) => (
|
||||||
<div
|
<div
|
||||||
key={bom.id}
|
key={bom.id}
|
||||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between mb-2">
|
<div className="flex items-start justify-between mb-2">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
<FaCodeBranch className="w-4 h-4 text-gray-600" />
|
<FaCodeBranch className="w-4 h-4 text-gray-600" />
|
||||||
<h4 className="text-lg font-semibold text-gray-900">
|
<h4 className="text-lg font-semibold text-gray-900">{bom.bomCode}</h4>
|
||||||
{bom.bomCode}
|
<span
|
||||||
</h4>
|
className={`px-2 py-1 rounded-full text-xs font-medium ${getBOMTypeColor(
|
||||||
|
bom.bomType,
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
{getBOMTypeName(bom.bomType)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-600 mb-0.5">
|
||||||
|
Malzeme: {bom.material?.code} - {bom.material?.name} - v{bom.version}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-gray-500">Temel Miktar: {bom.baseQuantity}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-1">
|
||||||
|
<button
|
||||||
|
onClick={() => handleViewDetails(bom)}
|
||||||
|
className="p-2 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
||||||
|
title="Detayları Görüntüle"
|
||||||
|
>
|
||||||
|
<FaEye className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCopy(bom)}
|
||||||
|
className="p-2 text-gray-400 hover:text-purple-600 hover:bg-purple-50 rounded-md transition-colors"
|
||||||
|
title="Kopyala"
|
||||||
|
>
|
||||||
|
<FaCopy className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleEdit(bom)}
|
||||||
|
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||||
|
title="Düzenle"
|
||||||
|
>
|
||||||
|
<FaEdit className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors"
|
||||||
|
title="Sil"
|
||||||
|
>
|
||||||
|
<FaTrash className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-3 text-sm mb-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaBox className="w-4 h-4 mr-1" />
|
||||||
|
Bileşenler
|
||||||
|
</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{getActiveComponents(bom.components)}/{getTotalComponents(bom.components)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaClock className="w-4 h-4 mr-1" />
|
||||||
|
Toplam Süre
|
||||||
|
</span>
|
||||||
|
<span className="font-medium">{getTotalOperationTime(bom.operations)} dk</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-500">Geçerlilik</span>
|
||||||
|
<span className="font-medium">{bom.validFrom.toLocaleDateString('tr-TR')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status and Warnings */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex space-x-2">
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getBOMTypeColor(
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
bom.bomType
|
bom.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||||
)}`}
|
}`}
|
||||||
>
|
>
|
||||||
{getBOMTypeName(bom.bomType)}
|
{bom.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</span>
|
</span>
|
||||||
|
{bom.validTo && new Date() > bom.validTo && (
|
||||||
|
<span className="px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 flex items-center">
|
||||||
|
<FaExclamationTriangle className="w-3 h-3 mr-1" />
|
||||||
|
Süresi Dolmuş
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 mb-0.5">
|
<span className="text-xs text-gray-400">
|
||||||
Malzeme: {bom.material?.code} - {bom.material?.name} - v
|
{bom.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||||
{bom.version}
|
</span>
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-500">
|
|
||||||
Temel Miktar: {bom.baseQuantity}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex space-x-1">
|
|
||||||
<button
|
|
||||||
onClick={() => handleViewDetails(bom)}
|
|
||||||
className="p-2 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
|
||||||
title="Detayları Görüntüle"
|
|
||||||
>
|
|
||||||
<FaEye className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleCopy(bom)}
|
|
||||||
className="p-2 text-gray-400 hover:text-purple-600 hover:bg-purple-50 rounded-md transition-colors"
|
|
||||||
title="Kopyala"
|
|
||||||
>
|
|
||||||
<FaCopy className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(bom)}
|
|
||||||
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
|
||||||
title="Düzenle"
|
|
||||||
>
|
|
||||||
<FaEdit className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors"
|
|
||||||
title="Sil"
|
|
||||||
>
|
|
||||||
<FaTrash className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3 text-sm mb-2">
|
{filteredBOMs.length === 0 && (
|
||||||
<div className="flex items-center justify-between">
|
<div className="text-center py-8">
|
||||||
<span className="text-gray-500 flex items-center">
|
<FaCodeBranch className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 mb-2">BOM bulunamadı</h3>
|
||||||
|
<p className="text-gray-500">
|
||||||
|
Arama kriterlerinizi değiştirin veya yeni bir BOM ekleyin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* BOM Details */}
|
||||||
|
<div className="space-y-4 pt-2">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900">BOM Detayları</h3>
|
||||||
|
{selectedBOM ? (
|
||||||
|
<div className="bg-white rounded-lg shadow-md border border-gray-200 p-3">
|
||||||
|
<div className="mb-3">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-2">{selectedBOM.bomCode}</h4>
|
||||||
|
<div className="grid grid-cols-2 gap-1 text-sm text-gray-600">
|
||||||
|
<div>
|
||||||
|
Malzeme: {selectedBOM.material?.code} - {selectedBOM.material?.name}
|
||||||
|
</div>
|
||||||
|
<div>Versiyon: {selectedBOM.version}</div>
|
||||||
|
<div>Tip: {getBOMTypeName(selectedBOM.bomType)}</div>
|
||||||
|
<div>Temel Miktar: {selectedBOM.baseQuantity}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Components */}
|
||||||
|
<div className="mb-3">
|
||||||
|
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
||||||
<FaBox className="w-4 h-4 mr-1" />
|
<FaBox className="w-4 h-4 mr-1" />
|
||||||
Bileşenler
|
Bileşenler ({selectedBOM.components.length})
|
||||||
</span>
|
</h5>
|
||||||
<span className="font-medium">
|
<div className="space-y-2">
|
||||||
{getActiveComponents(bom.components)}/
|
{selectedBOM.components.map((component) => (
|
||||||
{getTotalComponents(bom.components)}
|
<div
|
||||||
</span>
|
key={component.id}
|
||||||
</div>
|
className="border border-gray-200 rounded p-1.5 text-sm"
|
||||||
<div className="flex items-center justify-between">
|
>
|
||||||
<span className="text-gray-500 flex items-center">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<FaClock className="w-4 h-4 mr-1" />
|
<span className="font-medium">
|
||||||
Toplam Süre
|
{component.position}. {component.material?.code} -{' '}
|
||||||
</span>
|
{component.material?.name}
|
||||||
<span className="font-medium">
|
|
||||||
{getTotalOperationTime(bom.operations)} dk
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-gray-500">Geçerlilik</span>
|
|
||||||
<span className="font-medium">
|
|
||||||
{bom.validFrom.toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Status and Warnings */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex space-x-2">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
bom.isActive
|
|
||||||
? "bg-green-100 text-green-800"
|
|
||||||
: "bg-red-100 text-red-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{bom.isActive ? "Aktif" : "Pasif"}
|
|
||||||
</span>
|
|
||||||
{bom.validTo && new Date() > bom.validTo && (
|
|
||||||
<span className="px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 flex items-center">
|
|
||||||
<FaExclamationTriangle className="w-3 h-3 mr-1" />
|
|
||||||
Süresi Dolmuş
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<span className="text-xs text-gray-400">
|
|
||||||
{bom.lastModificationTime.toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{filteredBOMs.length === 0 && (
|
|
||||||
<div className="text-center py-8">
|
|
||||||
<FaCodeBranch className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
|
||||||
BOM bulunamadı
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-500">
|
|
||||||
Arama kriterlerinizi değiştirin veya yeni bir BOM ekleyin.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* BOM Details */}
|
|
||||||
<div className="space-y-4 pt-2">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-900">BOM Detayları</h3>
|
|
||||||
{selectedBOM ? (
|
|
||||||
<div className="bg-white rounded-lg shadow-md border border-gray-200 p-3">
|
|
||||||
<div className="mb-3">
|
|
||||||
<h4 className="font-semibold text-gray-900 mb-2">
|
|
||||||
{selectedBOM.bomCode}
|
|
||||||
</h4>
|
|
||||||
<div className="grid grid-cols-2 gap-1 text-sm text-gray-600">
|
|
||||||
<div>
|
|
||||||
Malzeme: {selectedBOM.material?.code} -{" "}
|
|
||||||
{selectedBOM.material?.name}
|
|
||||||
</div>
|
|
||||||
<div>Versiyon: {selectedBOM.version}</div>
|
|
||||||
<div>Tip: {getBOMTypeName(selectedBOM.bomType)}</div>
|
|
||||||
<div>Temel Miktar: {selectedBOM.baseQuantity}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Components */}
|
|
||||||
<div className="mb-3">
|
|
||||||
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
|
||||||
<FaBox className="w-4 h-4 mr-1" />
|
|
||||||
Bileşenler ({selectedBOM.components.length})
|
|
||||||
</h5>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{selectedBOM.components.map((component) => (
|
|
||||||
<div
|
|
||||||
key={component.id}
|
|
||||||
className="border border-gray-200 rounded p-1.5 text-sm"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between mb-1">
|
|
||||||
<span className="font-medium">
|
|
||||||
{component.position}. {component.material?.code} -{" "}
|
|
||||||
{component.material?.name}
|
|
||||||
</span>
|
|
||||||
<div className="flex space-x-2">
|
|
||||||
{component.isPhantom && (
|
|
||||||
<span className="bg-purple-100 text-purple-800 text-xs px-1 rounded-lg">
|
|
||||||
Phantom
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span
|
|
||||||
className={`text-xs px-1 rounded ${
|
|
||||||
component.isActive
|
|
||||||
? "bg-green-100 text-green-800"
|
|
||||||
: "bg-red-100 text-red-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{component.isActive ? "Aktif" : "Pasif"}
|
|
||||||
</span>
|
</span>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
{component.isPhantom && (
|
||||||
|
<span className="bg-purple-100 text-purple-800 text-xs px-1 rounded-lg">
|
||||||
|
Phantom
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={`text-xs px-1 rounded ${
|
||||||
|
component.isActive
|
||||||
|
? 'bg-green-100 text-green-800'
|
||||||
|
: 'bg-red-100 text-red-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{component.isActive ? 'Aktif' : 'Pasif'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-gray-600">
|
||||||
|
Miktar: {component.quantity} {component.unitId} | Fire: %
|
||||||
|
{component.scrapPercentage}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-gray-600">
|
))}
|
||||||
Miktar: {component.quantity} {component.unitId} | Fire:
|
</div>
|
||||||
%{component.scrapPercentage}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Operations */}
|
{/* Operations */}
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
<h5 className="font-medium text-gray-900 mb-2 flex items-center">
|
||||||
<FaCodeBranch className="w-4 h-4 mr-1" />
|
<FaCodeBranch className="w-4 h-4 mr-1" />
|
||||||
Operasyonlar ({selectedBOM.operations.length})
|
Operasyonlar ({selectedBOM.operations.length})
|
||||||
</h5>
|
</h5>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{selectedBOM.operations.map((operation) => (
|
{selectedBOM.operations.map((operation) => (
|
||||||
<div
|
<div
|
||||||
key={operation.id}
|
key={operation.id}
|
||||||
className="border border-gray-200 rounded p-2 text-sm"
|
className="border border-gray-200 rounded p-2 text-sm"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{operation.sequence}. {operation.operation?.code} -{" "}
|
{operation.sequence}. {operation.operation?.code} -{' '}
|
||||||
{operation.operation?.name}
|
{operation.operation?.name}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`text-xs px-1 rounded ${
|
className={`text-xs px-1 rounded ${
|
||||||
operation.isActive
|
operation.isActive
|
||||||
? "bg-green-100 text-green-800"
|
? 'bg-green-100 text-green-800'
|
||||||
: "bg-red-100 text-red-800"
|
: 'bg-red-100 text-red-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{operation.isActive ? "Aktif" : "Pasif"}
|
{operation.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-gray-600">
|
||||||
|
Hazırlık: {operation.setupTime}dk | İşlem: {operation.runTime}dk | Kuyruk:{' '}
|
||||||
|
{operation.queueTime}dk | Taşıma: {operation.moveTime}dk
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-gray-600">
|
))}
|
||||||
Hazırlık: {operation.setupTime}dk | İşlem:{" "}
|
</div>
|
||||||
{operation.runTime}dk | Kuyruk: {operation.queueTime}dk
|
|
||||||
| Taşıma: {operation.moveTime}dk
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
) : (
|
<div className="bg-gray-50 rounded-lg p-6 text-center">
|
||||||
<div className="bg-gray-50 rounded-lg p-6 text-center">
|
<FaBox className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||||
<FaBox className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
BOM Detaylarını Görüntüle
|
||||||
BOM Detaylarını Görüntüle
|
</h3>
|
||||||
</h3>
|
<p className="text-gray-500">Detaylarını görmek için sol taraftan bir BOM seçin.</p>
|
||||||
<p className="text-gray-500">
|
</div>
|
||||||
Detaylarını görmek için sol taraftan bir BOM seçin.
|
)}
|
||||||
</p>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -360,8 +338,8 @@ const BOMManagement: React.FC = () => {
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onClose={() => setShowModal(false)}
|
onClose={() => setShowModal(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default BOMManagement;
|
export default BOMManagement
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaSearch,
|
FaSearch,
|
||||||
|
|
@ -11,402 +11,363 @@ import {
|
||||||
FaWrench,
|
FaWrench,
|
||||||
FaTh,
|
FaTh,
|
||||||
FaList,
|
FaList,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { MrpOperation } from "../../../types/mrp";
|
import { MrpOperation } from '../../../types/mrp'
|
||||||
import OperationFormModal from "./OperationFormModal";
|
import OperationFormModal from './OperationFormModal'
|
||||||
import { mockOperations } from "../../../mocks/mockOperations";
|
import { mockOperations } from '../../../mocks/mockOperations'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const OperationDefinitions: React.FC = () => {
|
const OperationDefinitions: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [editingOperation, setEditingOperation] = useState<MrpOperation | null>(
|
const [editingOperation, setEditingOperation] = useState<MrpOperation | null>(null)
|
||||||
null
|
const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
|
||||||
);
|
|
||||||
const [viewMode, setViewMode] = useState<"card" | "list">("card");
|
|
||||||
|
|
||||||
const getTotalCost = (operation: MrpOperation) => {
|
const getTotalCost = (operation: MrpOperation) => {
|
||||||
return operation.laborCost + operation.machineCost + operation.overheadCost;
|
return operation.laborCost + operation.machineCost + operation.overheadCost
|
||||||
};
|
}
|
||||||
|
|
||||||
// Mock data - replace with actual API calls
|
// Mock data - replace with actual API calls
|
||||||
const [operations, setOperations] = useState<MrpOperation[]>(mockOperations);
|
const [operations, setOperations] = useState<MrpOperation[]>(mockOperations)
|
||||||
|
|
||||||
const filteredOperations = operations.filter(
|
const filteredOperations = operations.filter(
|
||||||
(operation) =>
|
(operation) =>
|
||||||
operation.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
operation.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
operation.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
operation.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
operation.workCenter?.name
|
operation.workCenter?.name.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||||
.toLowerCase()
|
)
|
||||||
.includes(searchTerm.toLowerCase())
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleEdit = (operation: MrpOperation) => {
|
const handleEdit = (operation: MrpOperation) => {
|
||||||
setEditingOperation(operation);
|
setEditingOperation(operation)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAddNew = () => {
|
const handleAddNew = () => {
|
||||||
setEditingOperation(null);
|
setEditingOperation(null)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = (op: MrpOperation) => {
|
const handleSave = (op: MrpOperation) => {
|
||||||
setOperations((prev) => {
|
setOperations((prev) => {
|
||||||
const existing = prev.find((p) => p.id === op.id);
|
const existing = prev.find((p) => p.id === op.id)
|
||||||
if (existing) return prev.map((p) => (p.id === op.id ? op : p));
|
if (existing) return prev.map((p) => (p.id === op.id ? op : p))
|
||||||
return [...prev, { ...op, id: op.id || String(Date.now()) }];
|
return [...prev, { ...op, id: op.id || String(Date.now()) }]
|
||||||
});
|
})
|
||||||
setShowModal(false);
|
setShowModal(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-bold text-gray-900">
|
<div>
|
||||||
Operasyon Tanımları
|
<h2 className="text-xl font-bold text-gray-900">Operasyon Tanımları</h2>
|
||||||
</h2>
|
<p className="text-sm text-gray-600 mt-0.5">
|
||||||
<p className="text-sm text-gray-600 mt-0.5">
|
İş merkezlerinde gerçekleştirilen operasyonları tanımlayın
|
||||||
İş merkezlerinde gerçekleştirilen operasyonları tanımlayın
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex items-center space-x-2">
|
{/* View Toggle */}
|
||||||
{/* View Toggle */}
|
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
||||||
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
<button
|
||||||
|
onClick={() => setViewMode('card')}
|
||||||
|
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||||
|
viewMode === 'card'
|
||||||
|
? 'bg-white text-blue-600 shadow-sm'
|
||||||
|
: 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<FaTh className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setViewMode('list')}
|
||||||
|
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||||
|
viewMode === 'list'
|
||||||
|
? 'bg-white text-blue-600 shadow-sm'
|
||||||
|
: 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<FaList className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("card")}
|
onClick={handleAddNew}
|
||||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
className="bg-blue-600 text-white px-2 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||||
viewMode === "card"
|
|
||||||
? "bg-white text-blue-600 shadow-sm"
|
|
||||||
: "text-gray-600 hover:text-gray-900"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<FaTh className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
</button>
|
<span>Yeni Operasyon</span>
|
||||||
<button
|
|
||||||
onClick={() => setViewMode("list")}
|
|
||||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
|
||||||
viewMode === "list"
|
|
||||||
? "bg-white text-blue-600 shadow-sm"
|
|
||||||
: "text-gray-600 hover:text-gray-900"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<FaList className="w-4 h-4" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
onClick={handleAddNew}
|
|
||||||
className="bg-blue-600 text-white px-2 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
|
||||||
>
|
|
||||||
<FaPlus className="w-4 h-4" />
|
|
||||||
<span>Yeni Operasyon</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search Bar */}
|
{/* Search Bar */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Operasyon ara..."
|
placeholder="Operasyon ara..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Operations Display */}
|
|
||||||
{viewMode === "card" ? (
|
|
||||||
/* Card View */
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
||||||
{filteredOperations.map((operation) => (
|
|
||||||
<div
|
|
||||||
key={operation.id}
|
|
||||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
|
||||||
>
|
|
||||||
<div className="flex items-start justify-between mb-2">
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-center space-x-2 mb-1">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
|
||||||
{operation.name}
|
|
||||||
</h3>
|
|
||||||
<span className="px-2 py-1 rounded-full text-xs font-medium">
|
|
||||||
{operation.operationType?.code}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-600 mb-1">{operation.code}</p>
|
|
||||||
{operation.description && (
|
|
||||||
<p className="text-sm text-gray-500 mb-2">
|
|
||||||
{operation.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex space-x-1">
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(operation)}
|
|
||||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
|
||||||
>
|
|
||||||
<FaEdit className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
|
||||||
<FaTrash className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Work Center Info */}
|
|
||||||
<div className="bg-gray-50 rounded-lg p-2 mb-2">
|
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
|
||||||
<FaWrench className="w-4 h-4 text-gray-600" />
|
|
||||||
<span className="font-medium text-gray-900">İş Merkezi</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-700">
|
|
||||||
{operation.workCenter?.name}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-600">
|
|
||||||
{operation.workCenter?.code}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3 mb-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span className="text-gray-500 flex items-center">
|
|
||||||
<FaClock className="w-4 h-4 mr-1" />
|
|
||||||
Standart Süre
|
|
||||||
</span>
|
|
||||||
<span className="font-medium">
|
|
||||||
{operation.standardTime} dk
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span className="text-gray-500 flex items-center">
|
|
||||||
<FaCog className="w-4 h-4 mr-1" />
|
|
||||||
Hazırlık Süresi
|
|
||||||
</span>
|
|
||||||
<span className="font-medium">
|
|
||||||
{operation.setupTime} dk
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span className="text-gray-500 flex items-center">
|
|
||||||
<FaUser className="w-4 h-4 mr-1" />
|
|
||||||
İşçilik
|
|
||||||
</span>
|
|
||||||
<span className="font-medium">₺{operation.laborCost}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span className="text-gray-500">Makine Maliyeti</span>
|
|
||||||
<span className="font-medium">
|
|
||||||
₺{operation.machineCost}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span className="text-gray-500">Genel Gider</span>
|
|
||||||
<span className="font-medium">
|
|
||||||
₺{operation.overheadCost}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm border-t pt-2">
|
|
||||||
<span className="text-gray-700 font-medium flex items-center">
|
|
||||||
<FaDollarSign className="w-4 h-4 mr-1" />
|
|
||||||
Toplam Maliyet
|
|
||||||
</span>
|
|
||||||
<span className="font-bold text-blue-600">
|
|
||||||
₺{getTotalCost(operation)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{operation.instructions && (
|
|
||||||
<div className="bg-blue-50 rounded-lg p-2 mb-2">
|
|
||||||
<h4 className="text-sm font-medium text-blue-900 mb-1">
|
|
||||||
Talimatlar
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-blue-700">
|
|
||||||
{operation.instructions}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between mt-2 pt-2 border-t">
|
|
||||||
<div className="flex space-x-3">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
operation.isActive
|
|
||||||
? "bg-green-100 text-green-800"
|
|
||||||
: "bg-red-100 text-red-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{operation.isActive ? "Aktif" : "Pasif"}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
operation.qualityCheckRequired
|
|
||||||
? "bg-purple-100 text-purple-800"
|
|
||||||
: "bg-gray-100 text-gray-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{operation.qualityCheckRequired
|
|
||||||
? "Kalite Kontrolü Gerekli"
|
|
||||||
: "Kalite Kontrolü İsteğe Bağlı"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span className="text-xs text-gray-400">
|
|
||||||
{operation.lastModificationTime.toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
/* List View */
|
{/* Operations Display */}
|
||||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
{viewMode === 'card' ? (
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
/* Card View */
|
||||||
<thead className="bg-gray-50">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
<tr>
|
{filteredOperations.map((operation) => (
|
||||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<div
|
||||||
Operasyon
|
key={operation.id}
|
||||||
</th>
|
className="bg-white rounded-lg shadow-md border border-gray-200 p-3 hover:shadow-lg transition-shadow"
|
||||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
>
|
||||||
İş Merkezi
|
<div className="flex items-start justify-between mb-2">
|
||||||
</th>
|
<div className="flex-1">
|
||||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<div className="flex items-center space-x-2 mb-1">
|
||||||
Tür
|
<h3 className="text-lg font-semibold text-gray-900">{operation.name}</h3>
|
||||||
</th>
|
<span className="px-2 py-1 rounded-full text-xs font-medium">
|
||||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
{operation.operationType?.code}
|
||||||
Süre (dk)
|
</span>
|
||||||
</th>
|
|
||||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Maliyet (₺)
|
|
||||||
</th>
|
|
||||||
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Durum
|
|
||||||
</th>
|
|
||||||
<th className="px-2 py-1.5 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
İşlemler
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{filteredOperations.map((operation) => (
|
|
||||||
<tr key={operation.id} className="hover:bg-gray-50 text-sm">
|
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-gray-900">
|
|
||||||
{operation.name}
|
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
{operation.code}
|
|
||||||
</div>
|
|
||||||
{operation.description && (
|
|
||||||
<div className="text-xs text-gray-400 mt-1">
|
|
||||||
{operation.description}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<p className="text-sm text-gray-600 mb-1">{operation.code}</p>
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
{operation.description && (
|
||||||
<div className="text-sm text-gray-900">
|
<p className="text-sm text-gray-500 mb-2">{operation.description}</p>
|
||||||
{operation.workCenter?.name}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="flex space-x-1">
|
||||||
{operation.workCenter?.code}
|
<button
|
||||||
</div>
|
onClick={() => handleEdit(operation)}
|
||||||
</td>
|
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
className={"px-2 py-1 rounded-full text-xs font-medium"}
|
|
||||||
>
|
>
|
||||||
{operation.operationType?.code}
|
<FaEdit className="w-4 h-4" />
|
||||||
</span>
|
</button>
|
||||||
</td>
|
<button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
<FaTrash className="w-4 h-4" />
|
||||||
<div className="space-y-1">
|
</button>
|
||||||
<div className="flex items-center">
|
</div>
|
||||||
<FaClock className="w-3 h-3 mr-1 text-gray-400" />
|
</div>
|
||||||
<span>Standart: {operation.standardTime}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<FaCog className="w-3 h-3 mr-1 text-gray-400" />
|
|
||||||
<span>Hazırlık: {operation.setupTime}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
|
||||||
<div className="space-y-1">
|
|
||||||
<div>İşçilik: ₺{operation.laborCost}</div>
|
|
||||||
<div>Makine: ₺{operation.machineCost}</div>
|
|
||||||
<div className="font-medium text-blue-600">
|
|
||||||
Toplam: ₺{getTotalCost(operation)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap">
|
|
||||||
<div className="flex flex-col space-y-1">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
operation.isActive
|
|
||||||
? "bg-green-100 text-green-800"
|
|
||||||
: "bg-red-100 text-red-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{operation.isActive ? "Aktif" : "Pasif"}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
operation.qualityCheckRequired
|
|
||||||
? "bg-purple-100 text-purple-800"
|
|
||||||
: "bg-gray-100 text-gray-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{operation.qualityCheckRequired
|
|
||||||
? "Kalite Gerekli"
|
|
||||||
: "Kalite İsteğe Bağlı"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-2 py-1.5 whitespace-nowrap text-right text-sm font-medium">
|
|
||||||
<div className="flex justify-end space-x-2">
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(operation)}
|
|
||||||
className="text-blue-600 hover:text-blue-900"
|
|
||||||
>
|
|
||||||
<FaEdit className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button className="text-red-600 hover:text-red-900">
|
|
||||||
<FaTrash className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{filteredOperations.length === 0 && (
|
{/* Work Center Info */}
|
||||||
<div className="text-center py-12">
|
<div className="bg-gray-50 rounded-lg p-2 mb-2">
|
||||||
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
<FaWrench className="w-4 h-4 text-gray-600" />
|
||||||
Operasyon bulunamadı
|
<span className="font-medium text-gray-900">İş Merkezi</span>
|
||||||
</h3>
|
</div>
|
||||||
<p className="text-gray-500">
|
<p className="text-sm text-gray-700">{operation.workCenter?.name}</p>
|
||||||
Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin.
|
<p className="text-sm text-gray-600">{operation.workCenter?.code}</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
<div className="grid grid-cols-2 gap-3 mb-2">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaClock className="w-4 h-4 mr-1" />
|
||||||
|
Standart Süre
|
||||||
|
</span>
|
||||||
|
<span className="font-medium">{operation.standardTime} dk</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaCog className="w-4 h-4 mr-1" />
|
||||||
|
Hazırlık Süresi
|
||||||
|
</span>
|
||||||
|
<span className="font-medium">{operation.setupTime} dk</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaUser className="w-4 h-4 mr-1" />
|
||||||
|
İşçilik
|
||||||
|
</span>
|
||||||
|
<span className="font-medium">₺{operation.laborCost}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500">Makine Maliyeti</span>
|
||||||
|
<span className="font-medium">₺{operation.machineCost}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500">Genel Gider</span>
|
||||||
|
<span className="font-medium">₺{operation.overheadCost}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm border-t pt-2">
|
||||||
|
<span className="text-gray-700 font-medium flex items-center">
|
||||||
|
<FaDollarSign className="w-4 h-4 mr-1" />
|
||||||
|
Toplam Maliyet
|
||||||
|
</span>
|
||||||
|
<span className="font-bold text-blue-600">₺{getTotalCost(operation)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{operation.instructions && (
|
||||||
|
<div className="bg-blue-50 rounded-lg p-2 mb-2">
|
||||||
|
<h4 className="text-sm font-medium text-blue-900 mb-1">Talimatlar</h4>
|
||||||
|
<p className="text-sm text-blue-700">{operation.instructions}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between mt-2 pt-2 border-t">
|
||||||
|
<div className="flex space-x-3">
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
operation.isActive
|
||||||
|
? 'bg-green-100 text-green-800'
|
||||||
|
: 'bg-red-100 text-red-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{operation.isActive ? 'Aktif' : 'Pasif'}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
operation.qualityCheckRequired
|
||||||
|
? 'bg-purple-100 text-purple-800'
|
||||||
|
: 'bg-gray-100 text-gray-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{operation.qualityCheckRequired
|
||||||
|
? 'Kalite Kontrolü Gerekli'
|
||||||
|
: 'Kalite Kontrolü İsteğe Bağlı'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-400">
|
||||||
|
{operation.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
/* List View */
|
||||||
|
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||||
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
|
<thead className="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Operasyon
|
||||||
|
</th>
|
||||||
|
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
İş Merkezi
|
||||||
|
</th>
|
||||||
|
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Tür
|
||||||
|
</th>
|
||||||
|
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Süre (dk)
|
||||||
|
</th>
|
||||||
|
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Maliyet (₺)
|
||||||
|
</th>
|
||||||
|
<th className="px-2 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Durum
|
||||||
|
</th>
|
||||||
|
<th className="px-2 py-1.5 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
İşlemler
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
|
{filteredOperations.map((operation) => (
|
||||||
|
<tr key={operation.id} className="hover:bg-gray-50 text-sm">
|
||||||
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium text-gray-900">{operation.name}</div>
|
||||||
|
<div className="text-sm text-gray-500">{operation.code}</div>
|
||||||
|
{operation.description && (
|
||||||
|
<div className="text-xs text-gray-400 mt-1">{operation.description}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
|
<div className="text-sm text-gray-900">{operation.workCenter?.name}</div>
|
||||||
|
<div className="text-sm text-gray-500">{operation.workCenter?.code}</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
|
<span className={'px-2 py-1 rounded-full text-xs font-medium'}>
|
||||||
|
{operation.operationType?.code}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FaClock className="w-3 h-3 mr-1 text-gray-400" />
|
||||||
|
<span>Standart: {operation.standardTime}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FaCog className="w-3 h-3 mr-1 text-gray-400" />
|
||||||
|
<span>Hazırlık: {operation.setupTime}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1.5 whitespace-nowrap text-sm text-gray-900">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div>İşçilik: ₺{operation.laborCost}</div>
|
||||||
|
<div>Makine: ₺{operation.machineCost}</div>
|
||||||
|
<div className="font-medium text-blue-600">
|
||||||
|
Toplam: ₺{getTotalCost(operation)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1.5 whitespace-nowrap">
|
||||||
|
<div className="flex flex-col space-y-1">
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
operation.isActive
|
||||||
|
? 'bg-green-100 text-green-800'
|
||||||
|
: 'bg-red-100 text-red-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{operation.isActive ? 'Aktif' : 'Pasif'}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
operation.qualityCheckRequired
|
||||||
|
? 'bg-purple-100 text-purple-800'
|
||||||
|
: 'bg-gray-100 text-gray-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{operation.qualityCheckRequired
|
||||||
|
? 'Kalite Gerekli'
|
||||||
|
: 'Kalite İsteğe Bağlı'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-2 py-1.5 whitespace-nowrap text-right text-sm font-medium">
|
||||||
|
<div className="flex justify-end space-x-2">
|
||||||
|
<button
|
||||||
|
onClick={() => handleEdit(operation)}
|
||||||
|
className="text-blue-600 hover:text-blue-900"
|
||||||
|
>
|
||||||
|
<FaEdit className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button className="text-red-600 hover:text-red-900">
|
||||||
|
<FaTrash className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{filteredOperations.length === 0 && (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 mb-2">Operasyon bulunamadı</h3>
|
||||||
|
<p className="text-gray-500">
|
||||||
|
Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Modal for Add/Edit */}
|
{/* Modal for Add/Edit */}
|
||||||
<OperationFormModal
|
<OperationFormModal
|
||||||
|
|
@ -415,8 +376,8 @@ const OperationDefinitions: React.FC = () => {
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onClose={() => setShowModal(false)}
|
onClose={() => setShowModal(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default OperationDefinitions;
|
export default OperationDefinitions
|
||||||
|
|
|
||||||
|
|
@ -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,353 +9,335 @@ import {
|
||||||
FaCheckCircle,
|
FaCheckCircle,
|
||||||
FaTh,
|
FaTh,
|
||||||
FaList,
|
FaList,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { MrpOperationTypeDefinition } from "../../../types/mrp";
|
import { MrpOperationTypeDefinition } from '../../../types/mrp'
|
||||||
import OperationTypeFormModal from "./OperationTypeFormModal";
|
import OperationTypeFormModal from './OperationTypeFormModal'
|
||||||
import { mockOperationTypes } from "../../../mocks/mockOperationTypes";
|
import { mockOperationTypes } from '../../../mocks/mockOperationTypes'
|
||||||
import {
|
import {
|
||||||
getOperationCategoryColor,
|
getOperationCategoryColor,
|
||||||
getOperationTypeColor,
|
getOperationTypeColor,
|
||||||
getOperationTypeText,
|
getOperationTypeText,
|
||||||
getSkillLevelText,
|
getSkillLevelText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const OperationTypes: React.FC = () => {
|
const OperationTypes: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [editingType, setEditingType] =
|
const [editingType, setEditingType] = useState<MrpOperationTypeDefinition | null>(null)
|
||||||
useState<MrpOperationTypeDefinition | null>(null);
|
const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
|
||||||
const [viewMode, setViewMode] = useState<"card" | "list">("card");
|
|
||||||
|
|
||||||
const [operationTypes] =
|
const [operationTypes] = useState<MrpOperationTypeDefinition[]>(mockOperationTypes)
|
||||||
useState<MrpOperationTypeDefinition[]>(mockOperationTypes);
|
|
||||||
|
|
||||||
const filteredTypes = operationTypes.filter(
|
const filteredTypes = operationTypes.filter(
|
||||||
(type) =>
|
(type) =>
|
||||||
type.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
type.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
type.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
type.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
type.category.toLowerCase().includes(searchTerm.toLowerCase())
|
type.category.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||||
);
|
)
|
||||||
|
|
||||||
const handleEdit = (type: MrpOperationTypeDefinition) => {
|
const handleEdit = (type: MrpOperationTypeDefinition) => {
|
||||||
setEditingType(type);
|
setEditingType(type)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAddNew = () => {
|
const handleAddNew = () => {
|
||||||
setEditingType(null);
|
setEditingType(null)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-bold text-gray-900">Operasyon Türleri</h2>
|
<div>
|
||||||
<p className="text-sm text-gray-600 mt-0.5">
|
<h2 className="text-xl font-bold text-gray-900">Operasyon Türleri</h2>
|
||||||
İş merkezlerinde kullanılacak operasyon türlerini tanımlayın
|
<p className="text-sm text-gray-600 mt-0.5">
|
||||||
</p>
|
İş merkezlerinde kullanılacak operasyon türlerini tanımlayın
|
||||||
</div>
|
</p>
|
||||||
<div className="flex items-center space-x-2">
|
</div>
|
||||||
{/* View Toggle */}
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
{/* View Toggle */}
|
||||||
|
<div className="flex bg-gray-100 rounded-lg p-0.5">
|
||||||
|
<button
|
||||||
|
onClick={() => setViewMode('card')}
|
||||||
|
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||||
|
viewMode === 'card'
|
||||||
|
? 'bg-white text-blue-600 shadow-sm'
|
||||||
|
: 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<FaTh className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setViewMode('list')}
|
||||||
|
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
||||||
|
viewMode === 'list'
|
||||||
|
? 'bg-white text-blue-600 shadow-sm'
|
||||||
|
: 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<FaList className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode("card")}
|
onClick={handleAddNew}
|
||||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
||||||
viewMode === "card"
|
|
||||||
? "bg-white text-blue-600 shadow-sm"
|
|
||||||
: "text-gray-600 hover:text-gray-900"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<FaTh className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
</button>
|
<span>Yeni Operasyon Türü</span>
|
||||||
<button
|
|
||||||
onClick={() => setViewMode("list")}
|
|
||||||
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
|
|
||||||
viewMode === "list"
|
|
||||||
? "bg-white text-blue-600 shadow-sm"
|
|
||||||
: "text-gray-600 hover:text-gray-900"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<FaList className="w-4 h-4" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
onClick={handleAddNew}
|
|
||||||
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 flex items-center space-x-2"
|
|
||||||
>
|
|
||||||
<FaPlus className="w-4 h-4" />
|
|
||||||
<span>Yeni Operasyon Türü</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search Bar */}
|
{/* Search Bar */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Operasyon türü ara..."
|
placeholder="Operasyon türü ara..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Operation Types Display */}
|
{/* Operation Types Display */}
|
||||||
{viewMode === "card" ? (
|
{viewMode === 'card' ? (
|
||||||
/* Card View */
|
/* Card View */
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{filteredTypes.map((type) => (
|
{filteredTypes.map((type) => (
|
||||||
<div
|
<div
|
||||||
key={type.id}
|
key={type.id}
|
||||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-4 hover:shadow-lg transition-shadow"
|
className="bg-white rounded-lg shadow-md border border-gray-200 p-4 hover:shadow-lg transition-shadow"
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between mb-3">
|
<div className="flex items-start justify-between mb-3">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-lg font-semibold text-gray-900">{type.name}</h3>
|
||||||
{type.name}
|
<span
|
||||||
</h3>
|
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor(
|
||||||
<span
|
type.category,
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor(
|
)}`}
|
||||||
type.category
|
>
|
||||||
)}`}
|
{getOperationTypeText(type.category)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-600 mb-2">{type.code}</p>
|
||||||
|
{type.description && (
|
||||||
|
<p className="text-sm text-gray-500 mb-2">{type.description}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-1">
|
||||||
|
<button
|
||||||
|
onClick={() => handleEdit(type)}
|
||||||
|
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||||
>
|
>
|
||||||
{getOperationTypeText(type.category)}
|
<FaEdit className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||||
|
<FaTrash className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaClock className="w-4 h-4 mr-1" />
|
||||||
|
Varsayılan Süre
|
||||||
|
</span>
|
||||||
|
<span className="font-medium">{type.defaultDuration} dk</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500">Beceri Seviyesi</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{getSkillLevelText(type.skillLevelRequired)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaCog className="w-4 h-4 mr-1" />
|
||||||
|
Hazırlık Gerekli
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`font-medium ${
|
||||||
|
type.requiresSetup ? 'text-orange-600' : 'text-green-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{type.requiresSetup ? 'Evet' : 'Hayır'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500">Paralel İşlem</span>
|
||||||
|
<span
|
||||||
|
className={`font-medium ${
|
||||||
|
type.allowsParallelOperation ? 'text-green-600' : 'text-gray-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{type.allowsParallelOperation ? 'Evet' : 'Hayır'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-500 flex items-center">
|
||||||
|
<FaCheckCircle className="w-4 h-4 mr-1" />
|
||||||
|
Kalite Kontrolü
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`font-medium ${
|
||||||
|
type.qualityCheckRequired ? 'text-purple-600' : 'text-gray-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{type.qualityCheckRequired ? 'Gerekli' : 'İsteğe Bağlı'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 mb-2">{type.code}</p>
|
|
||||||
{type.description && (
|
|
||||||
<p className="text-sm text-gray-500 mb-2">
|
|
||||||
{type.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-1">
|
|
||||||
<button
|
<div className="mt-3 pt-3 border-t border-gray-100">
|
||||||
onClick={() => handleEdit(type)}
|
<div className="flex items-center justify-between">
|
||||||
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
<span
|
||||||
>
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
<FaEdit className="w-4 h-4" />
|
type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||||
</button>
|
}`}
|
||||||
<button className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
>
|
||||||
<FaTrash className="w-4 h-4" />
|
{type.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</button>
|
</span>
|
||||||
|
<span className="text-xs text-gray-400">
|
||||||
|
{type.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
<div className="space-y-1.5">
|
</div>
|
||||||
<div className="flex items-center justify-between text-sm">
|
) : (
|
||||||
<span className="text-gray-500 flex items-center">
|
/* List View */
|
||||||
<FaClock className="w-4 h-4 mr-1" />
|
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||||
Varsayılan Süre
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
</span>
|
<thead className="bg-gray-50">
|
||||||
<span className="font-medium">{type.defaultDuration} dk</span>
|
<tr>
|
||||||
</div>
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Operasyon Türü
|
||||||
<div className="flex items-center justify-between text-sm">
|
</th>
|
||||||
<span className="text-gray-500">Beceri Seviyesi</span>
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
<span className="font-medium">
|
Kategori
|
||||||
{getSkillLevelText(type.skillLevelRequired)}
|
</th>
|
||||||
</span>
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
</div>
|
Süre
|
||||||
|
</th>
|
||||||
<div className="flex items-center justify-between text-sm">
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
<span className="text-gray-500 flex items-center">
|
Beceri Seviyesi
|
||||||
<FaCog className="w-4 h-4 mr-1" />
|
</th>
|
||||||
Hazırlık Gerekli
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
</span>
|
Hazırlık
|
||||||
<span
|
</th>
|
||||||
className={`font-medium ${
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
type.requiresSetup ? "text-orange-600" : "text-green-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.requiresSetup ? "Evet" : "Hayır"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span className="text-gray-500">Paralel İşlem</span>
|
|
||||||
<span
|
|
||||||
className={`font-medium ${
|
|
||||||
type.allowsParallelOperation
|
|
||||||
? "text-green-600"
|
|
||||||
: "text-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.allowsParallelOperation ? "Evet" : "Hayır"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<span className="text-gray-500 flex items-center">
|
|
||||||
<FaCheckCircle className="w-4 h-4 mr-1" />
|
|
||||||
Kalite Kontrolü
|
Kalite Kontrolü
|
||||||
</span>
|
</th>
|
||||||
<span
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
className={`font-medium ${
|
Durum
|
||||||
type.qualityCheckRequired
|
</th>
|
||||||
? "text-purple-600"
|
<th className="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
: "text-gray-600"
|
İşlemler
|
||||||
}`}
|
</th>
|
||||||
>
|
|
||||||
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-3 pt-3 border-t border-gray-100">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
type.isActive
|
|
||||||
? "bg-green-100 text-green-800"
|
|
||||||
: "bg-red-100 text-red-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.isActive ? "Aktif" : "Pasif"}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs text-gray-400">
|
|
||||||
{type.lastModificationTime.toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
/* List View */
|
|
||||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Operasyon Türü
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Kategori
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Süre
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Beceri Seviyesi
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Hazırlık
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Kalite Kontrolü
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Durum
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
İşlemler
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{filteredTypes.map((type) => (
|
|
||||||
<tr key={type.id} className="hover:bg-gray-50 text-sm">
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-gray-900">
|
|
||||||
{type.name}
|
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">{type.code}</div>
|
|
||||||
{type.description && (
|
|
||||||
<div className="text-xs text-gray-400 mt-0.5">
|
|
||||||
{type.description}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationCategoryColor(
|
|
||||||
type.category
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
{type.category}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<FaClock className="w-4 h-4 mr-1 text-gray-400" />
|
|
||||||
{type.defaultDuration} dk
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
|
||||||
{getSkillLevelText(type.skillLevelRequired)}
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
type.requiresSetup
|
|
||||||
? "bg-orange-100 text-orange-800"
|
|
||||||
: "bg-green-100 text-green-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.requiresSetup ? "Gerekli" : "Gerekli Değil"}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
type.qualityCheckRequired
|
|
||||||
? "bg-purple-100 text-purple-800"
|
|
||||||
: "bg-gray-100 text-gray-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
||||||
type.isActive
|
|
||||||
? "bg-green-100 text-green-800"
|
|
||||||
: "bg-red-100 text-red-800"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.isActive ? "Aktif" : "Pasif"}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
|
||||||
<div className="flex justify-end space-x-2">
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(type)}
|
|
||||||
className="text-blue-600 hover:text-blue-900"
|
|
||||||
>
|
|
||||||
<FaEdit className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button className="text-red-600 hover:text-red-900">
|
|
||||||
<FaTrash className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
</table>
|
{filteredTypes.map((type) => (
|
||||||
</div>
|
<tr key={type.id} className="hover:bg-gray-50 text-sm">
|
||||||
)}
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-medium text-gray-900">{type.name}</div>
|
||||||
|
<div className="text-sm text-gray-500">{type.code}</div>
|
||||||
|
{type.description && (
|
||||||
|
<div className="text-xs text-gray-400 mt-0.5">{type.description}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationCategoryColor(
|
||||||
|
type.category,
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
{type.category}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FaClock className="w-4 h-4 mr-1 text-gray-400" />
|
||||||
|
{type.defaultDuration} dk
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
||||||
|
{getSkillLevelText(type.skillLevelRequired)}
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
type.requiresSetup
|
||||||
|
? 'bg-orange-100 text-orange-800'
|
||||||
|
: 'bg-green-100 text-green-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{type.requiresSetup ? 'Gerekli' : 'Gerekli Değil'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
type.qualityCheckRequired
|
||||||
|
? 'bg-purple-100 text-purple-800'
|
||||||
|
: 'bg-gray-100 text-gray-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{type.qualityCheckRequired ? 'Gerekli' : 'İsteğe Bağlı'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
<span
|
||||||
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
|
type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{type.isActive ? 'Aktif' : 'Pasif'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
|
||||||
|
<div className="flex justify-end space-x-2">
|
||||||
|
<button
|
||||||
|
onClick={() => handleEdit(type)}
|
||||||
|
className="text-blue-600 hover:text-blue-900"
|
||||||
|
>
|
||||||
|
<FaEdit className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button className="text-red-600 hover:text-red-900">
|
||||||
|
<FaTrash className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{filteredTypes.length === 0 && (
|
{filteredTypes.length === 0 && (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
<h3 className="text-lg font-medium text-gray-900 mb-2">Operasyon türü bulunamadı</h3>
|
||||||
Operasyon türü bulunamadı
|
<p className="text-gray-500">
|
||||||
</h3>
|
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü ekleyin.
|
||||||
<p className="text-gray-500">
|
</p>
|
||||||
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü
|
</div>
|
||||||
ekleyin.
|
)}
|
||||||
</p>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Modal for Add/Edit - Placeholder */}
|
{/* Modal for Add/Edit - Placeholder */}
|
||||||
<OperationTypeFormModal
|
<OperationTypeFormModal
|
||||||
|
|
@ -364,12 +346,12 @@ const OperationTypes: React.FC = () => {
|
||||||
onClose={() => setShowModal(false)}
|
onClose={() => setShowModal(false)}
|
||||||
onSave={(data) => {
|
onSave={(data) => {
|
||||||
// TODO: persist to API; for now just close modal
|
// TODO: persist to API; for now just close modal
|
||||||
console.log("Saved operation type:", data);
|
console.log('Saved operation type:', data)
|
||||||
setShowModal(false);
|
setShowModal(false)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default OperationTypes;
|
export default OperationTypes
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,13 @@ import {
|
||||||
getProductionOrderStatus,
|
getProductionOrderStatus,
|
||||||
getWorkOrderStatus,
|
getWorkOrderStatus,
|
||||||
} from "../../../utils/erp";
|
} from "../../../utils/erp";
|
||||||
|
import { Container } from "@/components/shared";
|
||||||
|
|
||||||
interface PlanningGanttProps {
|
interface PlanningGanttProps {
|
||||||
workCenterId?: string;
|
workCenterId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
||||||
workCenterId,
|
workCenterId,
|
||||||
}) => {
|
}) => {
|
||||||
const getInitialExpandedItems = () => {
|
const getInitialExpandedItems = () => {
|
||||||
|
|
@ -502,8 +503,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container>
|
||||||
<div className="mb-3 mt-2">
|
<div className="mb-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-bold text-gray-900">
|
<h2 className="text-xl font-bold text-gray-900">
|
||||||
|
|
@ -618,6 +619,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
|
||||||
{filteredData.map((task) => renderTask(task))}
|
{filteredData.map((task) => renderTask(task))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default PlanningGantt;
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,8 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from 'react'
|
||||||
import { useParams, useNavigate } from "react-router-dom";
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { mockProductionOrders } from "../../../mocks/mockProductionOrders";
|
import { mockProductionOrders } from '../../../mocks/mockProductionOrders'
|
||||||
import {
|
import { MrpProductionOrder, MrpProductionOrderMaterial } from '../../../types/mrp'
|
||||||
MrpProductionOrder,
|
|
||||||
MrpProductionOrderMaterial,
|
|
||||||
} from "../../../types/mrp";
|
|
||||||
import {
|
import {
|
||||||
FaCog,
|
FaCog,
|
||||||
FaCalendarAlt,
|
FaCalendarAlt,
|
||||||
|
|
@ -14,29 +11,28 @@ import {
|
||||||
FaMoneyBillWave,
|
FaMoneyBillWave,
|
||||||
FaArrowLeft,
|
FaArrowLeft,
|
||||||
FaExclamationTriangle,
|
FaExclamationTriangle,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import StatusBadge from "../../../components/common/StatusBadge";
|
import StatusBadge from '../../../components/common/StatusBadge'
|
||||||
import { getPriorityColor } from "../../../utils/erp";
|
import { getPriorityColor } from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const ProductionOrderView: React.FC = () => {
|
const ProductionOrderView: React.FC = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams()
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const { data: productionOrder, isLoading } = useQuery({
|
const { data: productionOrder, isLoading } = useQuery({
|
||||||
queryKey: ["production-order", id],
|
queryKey: ['production-order', id],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
await new Promise((r) => setTimeout(r, 200));
|
await new Promise((r) => setTimeout(r, 200))
|
||||||
return mockProductionOrders.find((p) => p.id === id) as
|
return mockProductionOrders.find((p) => p.id === id) as MrpProductionOrder | undefined
|
||||||
| MrpProductionOrder
|
|
||||||
| undefined;
|
|
||||||
},
|
},
|
||||||
enabled: !!id,
|
enabled: !!id,
|
||||||
});
|
})
|
||||||
|
|
||||||
const materials: MrpProductionOrderMaterial[] = useMemo(
|
const materials: MrpProductionOrderMaterial[] = useMemo(
|
||||||
() => productionOrder?.materials || [],
|
() => productionOrder?.materials || [],
|
||||||
[productionOrder]
|
[productionOrder],
|
||||||
);
|
)
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -46,7 +42,7 @@ const ProductionOrderView: React.FC = () => {
|
||||||
<p className="text-gray-600 font-medium">Üretim emri yükleniyor...</p>
|
<p className="text-gray-600 font-medium">Üretim emri yükleniyor...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!productionOrder) {
|
if (!productionOrder) {
|
||||||
|
|
@ -56,14 +52,12 @@ const ProductionOrderView: React.FC = () => {
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<FaExclamationTriangle className="mx-auto h-16 w-16 text-gray-400" />
|
<FaExclamationTriangle className="mx-auto h-16 w-16 text-gray-400" />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
<h2 className="text-2xl font-bold text-gray-900 mb-2">Üretim Emri Bulunamadı</h2>
|
||||||
Üretim Emri Bulunamadı
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600 mb-6">
|
<p className="text-gray-600 mb-6">
|
||||||
İstenen üretim emri mevcut değil veya silinmiş olabilir.
|
İstenen üretim emri mevcut değil veya silinmiş olabilir.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate("/admin/mrp/production-orders")}
|
onClick={() => navigate('/admin/mrp/production-orders')}
|
||||||
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white font-medium rounded-lg hover:from-blue-700 hover:to-blue-800 transition-all duration-200 shadow-lg hover:shadow-xl"
|
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white font-medium rounded-lg hover:from-blue-700 hover:to-blue-800 transition-all duration-200 shadow-lg hover:shadow-xl"
|
||||||
>
|
>
|
||||||
<FaArrowLeft className="mr-2" />
|
<FaArrowLeft className="mr-2" />
|
||||||
|
|
@ -71,436 +65,385 @@ const ProductionOrderView: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getProgressPercentage = () => {
|
const getProgressPercentage = () => {
|
||||||
if (productionOrder.plannedQuantity === 0) return 0;
|
if (productionOrder.plannedQuantity === 0) return 0
|
||||||
return Math.round(
|
return Math.round((productionOrder.confirmedQuantity / productionOrder.plannedQuantity) * 100)
|
||||||
(productionOrder.confirmedQuantity / productionOrder.plannedQuantity) *
|
}
|
||||||
100
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto mt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="mb-6">
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center space-x-3">
|
|
||||||
<button
|
|
||||||
onClick={() => navigate("/admin/mrp/production-orders")}
|
|
||||||
className="inline-flex items-center px-3 py-2 bg-white text-gray-700 font-medium rounded-lg hover:bg-gray-50 transition-all duration-200 shadow-sm border border-gray-200"
|
|
||||||
>
|
|
||||||
<FaArrowLeft className="mr-2 text-sm" />
|
|
||||||
Geri
|
|
||||||
</button>
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-bold text-gray-900">
|
|
||||||
Üretim Emri Detayları
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 mt-1 text-sm">
|
|
||||||
{productionOrder.orderNumber} - Detaylı bilgiler
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Order Header Card */}
|
|
||||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 mb-4">
|
|
||||||
<div className="flex flex-wrap justify-between items-start mb-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
|
|
||||||
Üretim Emri No
|
|
||||||
</div>
|
|
||||||
<div className="text-xl font-bold text-gray-900">
|
|
||||||
{productionOrder.orderNumber}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2 text-right">
|
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
|
|
||||||
Durum
|
|
||||||
</div>
|
|
||||||
<StatusBadge status={productionOrder.status} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
||||||
<div className="bg-gradient-to-br from-blue-50 to-blue-100 p-4 rounded-xl border border-blue-200">
|
|
||||||
<div className="text-sm font-medium text-blue-700 mb-2 uppercase tracking-wide">
|
|
||||||
Öncelik
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
|
|
||||||
productionOrder.priority
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
{productionOrder.priority}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-gradient-to-br from-purple-50 to-purple-100 p-4 rounded-xl border border-purple-200">
|
|
||||||
<div className="text-sm font-medium text-purple-700 mb-2 uppercase tracking-wide">
|
|
||||||
Tür
|
|
||||||
</div>
|
|
||||||
<div className="text-base font-semibold text-purple-900">
|
|
||||||
{productionOrder.orderType}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-gradient-to-br from-orange-50 to-orange-100 p-4 rounded-xl border border-orange-200">
|
|
||||||
<div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
|
|
||||||
İlerleme
|
|
||||||
</div>
|
|
||||||
<div className="text-xl font-bold text-orange-900">
|
|
||||||
{getProgressPercentage()}%
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Progress Bar */}
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between">
|
||||||
<div className="text-sm font-medium text-gray-700">
|
<div className="flex items-center space-x-3">
|
||||||
Üretim İlerlemesi
|
<button
|
||||||
</div>
|
onClick={() => navigate('/admin/mrp/production-orders')}
|
||||||
<div className="text-sm text-gray-500">
|
className="inline-flex items-center px-3 py-2 bg-white text-gray-700 font-medium rounded-lg hover:bg-gray-50 transition-all duration-200 shadow-sm border border-gray-200"
|
||||||
{productionOrder.confirmedQuantity} /{" "}
|
>
|
||||||
{productionOrder.plannedQuantity} adet
|
<FaArrowLeft className="mr-2 text-sm" />
|
||||||
</div>
|
Geri
|
||||||
</div>
|
</button>
|
||||||
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
|
<div>
|
||||||
<div
|
<h1 className="text-2xl font-bold text-gray-900">Üretim Emri Detayları</h1>
|
||||||
className="bg-gradient-to-r from-blue-500 to-blue-600 h-3 rounded-full transition-all duration-500 ease-out"
|
<p className="text-gray-600 mt-1 text-sm">
|
||||||
style={{ width: `${getProgressPercentage()}%` }}
|
{productionOrder.orderNumber} - Detaylı bilgiler
|
||||||
></div>
|
</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{productionOrder.customerRequirement && (
|
|
||||||
<div className="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200">
|
|
||||||
<div className="flex items-center mb-3">
|
|
||||||
<FaClipboardList className="text-blue-600 mr-2" />
|
|
||||||
<div className="text-sm font-semibold text-blue-800 uppercase tracking-wide">
|
|
||||||
Müşteri Talebi
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-blue-700 leading-relaxed">
|
|
||||||
{productionOrder.customerRequirement}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Info Cards Grid */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
|
|
||||||
{/* Quantities Card */}
|
|
||||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<div className="p-1.5 bg-blue-100 rounded-lg">
|
|
||||||
<FaCog className="text-blue-600 text-base" />
|
|
||||||
</div>
|
|
||||||
<h3 className="text-base font-bold text-gray-900">
|
|
||||||
Miktar Bilgileri
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
|
||||||
<span className="text-gray-600 font-medium">Planlanan:</span>
|
|
||||||
<span className="font-bold text-gray-900">
|
|
||||||
{productionOrder.plannedQuantity}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
|
||||||
<span className="text-gray-600 font-medium">Üretilen:</span>
|
|
||||||
<span className="font-bold text-green-600">
|
|
||||||
{productionOrder.confirmedQuantity}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
|
||||||
<span className="text-gray-600 font-medium">Gereken:</span>
|
|
||||||
<span className="font-bold text-blue-600">
|
|
||||||
{productionOrder.requiredQuantity}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between items-center py-2">
|
|
||||||
<span className="text-gray-600 font-medium">Fire:</span>
|
|
||||||
<span className="font-bold text-red-600">
|
|
||||||
{productionOrder.scrapQuantity}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Schedule Card */}
|
{/* Order Header Card */}
|
||||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 mb-4">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex flex-wrap justify-between items-start mb-4">
|
||||||
<div className="p-1.5 bg-purple-100 rounded-lg">
|
<div className="space-y-2">
|
||||||
<FaCalendarAlt className="text-purple-600 text-base" />
|
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
|
||||||
|
Üretim Emri No
|
||||||
|
</div>
|
||||||
|
<div className="text-xl font-bold text-gray-900">{productionOrder.orderNumber}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2 text-right">
|
||||||
|
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">Durum</div>
|
||||||
|
<StatusBadge status={productionOrder.status} />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-base font-bold text-gray-900">Zamanlama</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="py-1.5 border-b border-gray-100">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
<div className="bg-gradient-to-br from-blue-50 to-blue-100 p-4 rounded-xl border border-blue-200">
|
||||||
Plan Başlangıç
|
<div className="text-sm font-medium text-blue-700 mb-2 uppercase tracking-wide">
|
||||||
|
Öncelik
|
||||||
</div>
|
</div>
|
||||||
<div className="font-semibold text-gray-900">
|
<span
|
||||||
{new Date(productionOrder.plannedStartDate).toLocaleDateString(
|
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
|
||||||
"tr-TR",
|
productionOrder.priority,
|
||||||
{
|
)}`}
|
||||||
weekday: "long",
|
>
|
||||||
year: "numeric",
|
{productionOrder.priority}
|
||||||
month: "long",
|
</span>
|
||||||
day: "numeric",
|
</div>
|
||||||
}
|
|
||||||
)}
|
<div className="bg-gradient-to-br from-purple-50 to-purple-100 p-4 rounded-xl border border-purple-200">
|
||||||
|
<div className="text-sm font-medium text-purple-700 mb-2 uppercase tracking-wide">
|
||||||
|
Tür
|
||||||
|
</div>
|
||||||
|
<div className="text-base font-semibold text-purple-900">
|
||||||
|
{productionOrder.orderType}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-1.5 border-b border-gray-100">
|
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
<div className="bg-gradient-to-br from-orange-50 to-orange-100 p-4 rounded-xl border border-orange-200">
|
||||||
Plan Bitiş
|
<div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
|
||||||
|
İlerleme
|
||||||
</div>
|
</div>
|
||||||
<div className="font-semibold text-gray-900">
|
<div className="text-xl font-bold text-orange-900">{getProgressPercentage()}%</div>
|
||||||
{new Date(productionOrder.plannedEndDate).toLocaleDateString(
|
</div>
|
||||||
"tr-TR",
|
</div>
|
||||||
{
|
|
||||||
weekday: "long",
|
{/* Progress Bar */}
|
||||||
year: "numeric",
|
<div className="mb-6">
|
||||||
month: "long",
|
<div className="flex items-center justify-between mb-3">
|
||||||
day: "numeric",
|
<div className="text-sm font-medium text-gray-700">Üretim İlerlemesi</div>
|
||||||
}
|
<div className="text-sm text-gray-500">
|
||||||
)}
|
{productionOrder.confirmedQuantity} / {productionOrder.plannedQuantity} adet
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{productionOrder.actualStartDate && (
|
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="bg-gradient-to-r from-blue-500 to-blue-600 h-3 rounded-full transition-all duration-500 ease-out"
|
||||||
|
style={{ width: `${getProgressPercentage()}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{productionOrder.customerRequirement && (
|
||||||
|
<div className="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200">
|
||||||
|
<div className="flex items-center mb-3">
|
||||||
|
<FaClipboardList className="text-blue-600 mr-2" />
|
||||||
|
<div className="text-sm font-semibold text-blue-800 uppercase tracking-wide">
|
||||||
|
Müşteri Talebi
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-blue-700 leading-relaxed">{productionOrder.customerRequirement}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Info Cards Grid */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
|
||||||
|
{/* Quantities Card */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<div className="p-1.5 bg-blue-100 rounded-lg">
|
||||||
|
<FaCog className="text-blue-600 text-base" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-base font-bold text-gray-900">Miktar Bilgileri</h3>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||||
|
<span className="text-gray-600 font-medium">Planlanan:</span>
|
||||||
|
<span className="font-bold text-gray-900">{productionOrder.plannedQuantity}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||||
|
<span className="text-gray-600 font-medium">Üretilen:</span>
|
||||||
|
<span className="font-bold text-green-600">
|
||||||
|
{productionOrder.confirmedQuantity}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
|
||||||
|
<span className="text-gray-600 font-medium">Gereken:</span>
|
||||||
|
<span className="font-bold text-blue-600">{productionOrder.requiredQuantity}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center py-2">
|
||||||
|
<span className="text-gray-600 font-medium">Fire:</span>
|
||||||
|
<span className="font-bold text-red-600">{productionOrder.scrapQuantity}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Schedule Card */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<div className="p-1.5 bg-purple-100 rounded-lg">
|
||||||
|
<FaCalendarAlt className="text-purple-600 text-base" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-base font-bold text-gray-900">Zamanlama</h3>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
<div className="py-1.5 border-b border-gray-100">
|
<div className="py-1.5 border-b border-gray-100">
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
Gerçek Başlangıç
|
Plan Başlangıç
|
||||||
</div>
|
</div>
|
||||||
<div className="font-semibold text-green-600">
|
<div className="font-semibold text-gray-900">
|
||||||
{new Date(productionOrder.actualStartDate).toLocaleDateString(
|
{new Date(productionOrder.plannedStartDate).toLocaleDateString('tr-TR', {
|
||||||
"tr-TR",
|
weekday: 'long',
|
||||||
{
|
year: 'numeric',
|
||||||
weekday: "long",
|
month: 'long',
|
||||||
year: "numeric",
|
day: 'numeric',
|
||||||
month: "long",
|
})}
|
||||||
day: "numeric",
|
</div>
|
||||||
}
|
</div>
|
||||||
)}
|
<div className="py-1.5 border-b border-gray-100">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
|
Plan Bitiş
|
||||||
|
</div>
|
||||||
|
<div className="font-semibold text-gray-900">
|
||||||
|
{new Date(productionOrder.plannedEndDate).toLocaleDateString('tr-TR', {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{productionOrder.actualStartDate && (
|
||||||
|
<div className="py-1.5 border-b border-gray-100">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
|
Gerçek Başlangıç
|
||||||
|
</div>
|
||||||
|
<div className="font-semibold text-green-600">
|
||||||
|
{new Date(productionOrder.actualStartDate).toLocaleDateString('tr-TR', {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{productionOrder.actualEndDate && (
|
||||||
|
<div className="py-1.5">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
|
Gerçek Bitiş
|
||||||
|
</div>
|
||||||
|
<div className="font-semibold text-green-600">
|
||||||
|
{new Date(productionOrder.actualEndDate).toLocaleDateString('tr-TR', {
|
||||||
|
weekday: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Costs Card */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<div className="p-1.5 bg-green-100 rounded-lg">
|
||||||
|
<FaMoneyBillWave className="text-green-600 text-base" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-base font-bold text-gray-900">Maliyet Bilgileri</h3>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="py-1.5 border-b border-gray-100">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
|
Planlanan Maliyet
|
||||||
|
</div>
|
||||||
|
<div className="text-lg font-bold text-gray-900">
|
||||||
|
{new Intl.NumberFormat('tr-TR', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: productionOrder.currency,
|
||||||
|
}).format(productionOrder.plannedCost)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="py-1.5 border-b border-gray-100">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
|
Gerçekleşen Maliyet
|
||||||
|
</div>
|
||||||
|
<div className="text-lg font-bold text-green-600">
|
||||||
|
{new Intl.NumberFormat('tr-TR', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: productionOrder.currency,
|
||||||
|
}).format(productionOrder.actualCost)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{productionOrder.actualEndDate && (
|
|
||||||
<div className="py-1.5">
|
<div className="py-1.5">
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
Gerçek Bitiş
|
Para Birimi
|
||||||
</div>
|
</div>
|
||||||
<div className="font-semibold text-green-600">
|
<div className="font-semibold text-gray-900">{productionOrder.currency}</div>
|
||||||
{new Date(productionOrder.actualEndDate).toLocaleDateString(
|
</div>
|
||||||
"tr-TR",
|
</div>
|
||||||
{
|
</div>
|
||||||
weekday: "long",
|
|
||||||
year: "numeric",
|
{/* Metadata Card */}
|
||||||
month: "long",
|
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
||||||
day: "numeric",
|
<div className="flex items-center gap-2 mb-2">
|
||||||
}
|
<div className="p-1.5 bg-gray-100 rounded-lg">
|
||||||
)}
|
<FaClipboardList className="text-gray-600 text-base" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-base font-bold text-gray-900">Sistem Bilgileri</h3>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="py-1.5 border-b border-gray-100">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
|
Oluşturulma
|
||||||
|
</div>
|
||||||
|
<div className="font-semibold text-gray-900">
|
||||||
|
{new Date(productionOrder.creationTime).toLocaleDateString('tr-TR', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="py-1.5 border-b border-gray-100">
|
||||||
</div>
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
</div>
|
Son Güncelleme
|
||||||
|
</div>
|
||||||
{/* Costs Card */}
|
<div className="font-semibold text-gray-900">
|
||||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
{new Date(productionOrder.lastModificationTime).toLocaleDateString('tr-TR', {
|
||||||
<div className="flex items-center gap-2 mb-2">
|
year: 'numeric',
|
||||||
<div className="p-1.5 bg-green-100 rounded-lg">
|
month: 'short',
|
||||||
<FaMoneyBillWave className="text-green-600 text-base" />
|
day: 'numeric',
|
||||||
</div>
|
})}
|
||||||
<h3 className="text-base font-bold text-gray-900">
|
</div>
|
||||||
Maliyet Bilgileri
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="py-1.5 border-b border-gray-100">
|
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
|
||||||
Planlanan Maliyet
|
|
||||||
</div>
|
|
||||||
<div className="text-lg font-bold text-gray-900">
|
|
||||||
{new Intl.NumberFormat("tr-TR", {
|
|
||||||
style: "currency",
|
|
||||||
currency: productionOrder.currency,
|
|
||||||
}).format(productionOrder.plannedCost)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="py-1.5 border-b border-gray-100">
|
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
|
||||||
Gerçekleşen Maliyet
|
|
||||||
</div>
|
|
||||||
<div className="text-lg font-bold text-green-600">
|
|
||||||
{new Intl.NumberFormat("tr-TR", {
|
|
||||||
style: "currency",
|
|
||||||
currency: productionOrder.currency,
|
|
||||||
}).format(productionOrder.actualCost)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="py-1.5">
|
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
|
||||||
Para Birimi
|
|
||||||
</div>
|
|
||||||
<div className="font-semibold text-gray-900">
|
|
||||||
{productionOrder.currency}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Metadata Card */}
|
{/* Materials List */}
|
||||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
|
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="p-1.5 bg-gray-100 rounded-lg">
|
<div className="flex items-center gap-2">
|
||||||
<FaClipboardList className="text-gray-600 text-base" />
|
<div className="p-1.5 bg-indigo-100 rounded-lg">
|
||||||
</div>
|
<FaTruck className="text-indigo-600 text-base" />
|
||||||
<h3 className="text-base font-bold text-gray-900">
|
|
||||||
Sistem Bilgileri
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="py-1.5 border-b border-gray-100">
|
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
|
||||||
Oluşturulma
|
|
||||||
</div>
|
</div>
|
||||||
<div className="font-semibold text-gray-900">
|
<div>
|
||||||
{new Date(productionOrder.creationTime).toLocaleDateString(
|
<h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
|
||||||
"tr-TR",
|
<p className="text-gray-600 mt-1 text-sm">Üretim emrine bağlı malzeme satırları</p>
|
||||||
{ year: "numeric", month: "short", day: "numeric" }
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-1.5 border-b border-gray-100">
|
<div className="text-right">
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
<div className="text-lg font-bold text-indigo-600">{materials.length}</div>
|
||||||
Son Güncelleme
|
<div className="text-sm text-gray-500">Malzeme</div>
|
||||||
</div>
|
|
||||||
<div className="font-semibold text-gray-900">
|
|
||||||
{new Date(
|
|
||||||
productionOrder.lastModificationTime
|
|
||||||
).toLocaleDateString("tr-TR", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Materials List */}
|
{materials.length === 0 ? (
|
||||||
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4">
|
<div className="p-8 text-center bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border-2 border-dashed border-gray-300">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<FaTruck className="mx-auto h-16 w-16 text-gray-400 mb-4" />
|
||||||
<div className="flex items-center gap-2">
|
<p className="text-gray-500 text-base font-medium">
|
||||||
<div className="p-1.5 bg-indigo-100 rounded-lg">
|
Bu üretim emrine bağlı malzeme bulunmamaktadır.
|
||||||
<FaTruck className="text-indigo-600 text-base" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
|
|
||||||
<p className="text-gray-600 mt-1 text-sm">
|
|
||||||
Üretim emrine bağlı malzeme satırları
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
<div className="text-right">
|
<div className="space-y-3 max-h-96 overflow-y-auto">
|
||||||
<div className="text-lg font-bold text-indigo-600">
|
{materials.map((m: MrpProductionOrderMaterial) => (
|
||||||
{materials.length}
|
<div
|
||||||
|
key={m.id}
|
||||||
|
className="flex flex-col lg:flex-row lg:items-center justify-between p-4 border border-gray-200 rounded-xl hover:bg-gradient-to-r hover:from-blue-50 hover:to-indigo-50 hover:border-blue-300 transition-all duration-200 group"
|
||||||
|
>
|
||||||
|
<div className="mb-4 lg:mb-0 flex-1">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<div className="p-1.5 bg-blue-100 rounded-lg group-hover:bg-blue-200 transition-colors">
|
||||||
|
<FaTruck className="text-blue-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-gray-600">
|
||||||
|
{m.salesOrder?.orderNumber || m.salesOrder?.id}
|
||||||
|
</div>
|
||||||
|
<div className="font-bold text-gray-900 text-base">
|
||||||
|
{m.material?.code || m.materialId}
|
||||||
|
{' - '}
|
||||||
|
{m.material?.name || 'Malzeme adı bulunamadı'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{m.customerRequirement && (
|
||||||
|
<div className="text-sm text-blue-600 italic rounded-lg mt-3">
|
||||||
|
"{m.customerRequirement}"
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4">
|
||||||
|
<div className="text-center p-2 bg-gray-50 rounded-lg">
|
||||||
|
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
||||||
|
Planlanan
|
||||||
|
</div>
|
||||||
|
<div className="font-bold text-gray-900 text-base">{m.plannedQuantity}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center p-2 bg-green-50 rounded-lg">
|
||||||
|
<div className="text-xs font-medium text-green-600 uppercase tracking-wide mb-1">
|
||||||
|
Üretilen
|
||||||
|
</div>
|
||||||
|
<div className="font-bold text-green-700 text-base">
|
||||||
|
{m.confirmedQuantity}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center p-2 bg-blue-50 rounded-lg">
|
||||||
|
<div className="text-xs font-medium text-blue-600 uppercase tracking-wide mb-1">
|
||||||
|
Gereken
|
||||||
|
</div>
|
||||||
|
<div className="font-bold text-blue-700 text-base">{m.requiredQuantity}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center p-2 bg-red-50 rounded-lg">
|
||||||
|
<div className="text-xs font-medium text-red-600 uppercase tracking-wide mb-1">
|
||||||
|
Fire
|
||||||
|
</div>
|
||||||
|
<div className="font-bold text-red-700 text-base">{m.scrapQuantity}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-500">Malzeme</div>
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{materials.length === 0 ? (
|
|
||||||
<div className="p-8 text-center bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border-2 border-dashed border-gray-300">
|
|
||||||
<FaTruck className="mx-auto h-16 w-16 text-gray-400 mb-4" />
|
|
||||||
<p className="text-gray-500 text-base font-medium">
|
|
||||||
Bu üretim emrine bağlı malzeme bulunmamaktadır.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-3 max-h-96 overflow-y-auto">
|
|
||||||
{materials.map((m: MrpProductionOrderMaterial) => (
|
|
||||||
<div
|
|
||||||
key={m.id}
|
|
||||||
className="flex flex-col lg:flex-row lg:items-center justify-between p-4 border border-gray-200 rounded-xl hover:bg-gradient-to-r hover:from-blue-50 hover:to-indigo-50 hover:border-blue-300 transition-all duration-200 group"
|
|
||||||
>
|
|
||||||
<div className="mb-4 lg:mb-0 flex-1">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<div className="p-1.5 bg-blue-100 rounded-lg group-hover:bg-blue-200 transition-colors">
|
|
||||||
<FaTruck className="text-blue-600" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-gray-600">
|
|
||||||
{m.salesOrder?.orderNumber || m.salesOrder?.id}
|
|
||||||
</div>
|
|
||||||
<div className="font-bold text-gray-900 text-base">
|
|
||||||
{m.material?.code || m.materialId}
|
|
||||||
{" - "}
|
|
||||||
{m.material?.name || "Malzeme adı bulunamadı"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{m.customerRequirement && (
|
|
||||||
<div className="text-sm text-blue-600 italic rounded-lg mt-3">
|
|
||||||
"{m.customerRequirement}"
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4">
|
|
||||||
<div className="text-center p-2 bg-gray-50 rounded-lg">
|
|
||||||
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
|
|
||||||
Planlanan
|
|
||||||
</div>
|
|
||||||
<div className="font-bold text-gray-900 text-base">
|
|
||||||
{m.plannedQuantity}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-center p-2 bg-green-50 rounded-lg">
|
|
||||||
<div className="text-xs font-medium text-green-600 uppercase tracking-wide mb-1">
|
|
||||||
Üretilen
|
|
||||||
</div>
|
|
||||||
<div className="font-bold text-green-700 text-base">
|
|
||||||
{m.confirmedQuantity}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-center p-2 bg-blue-50 rounded-lg">
|
|
||||||
<div className="text-xs font-medium text-blue-600 uppercase tracking-wide mb-1">
|
|
||||||
Gereken
|
|
||||||
</div>
|
|
||||||
<div className="font-bold text-blue-700 text-base">
|
|
||||||
{m.requiredQuantity}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-center p-2 bg-red-50 rounded-lg">
|
|
||||||
<div className="text-xs font-medium text-red-600 uppercase tracking-wide mb-1">
|
|
||||||
Fire
|
|
||||||
</div>
|
|
||||||
<div className="font-bold text-red-700 text-base">
|
|
||||||
{m.scrapQuantity}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ProductionOrderView;
|
export default ProductionOrderView
|
||||||
|
|
|
||||||
|
|
@ -1,209 +1,188 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaBox, FaIndustry, FaShoppingCart } from "react-icons/fa";
|
import { FaBox, FaIndustry, FaShoppingCart } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
MrpMaterialRequirement,
|
MrpMaterialRequirement,
|
||||||
MrpProductionSuggestion,
|
MrpProductionSuggestion,
|
||||||
MrpPurchaseSuggestion,
|
MrpPurchaseSuggestion,
|
||||||
} from "../../../types/mrp";
|
} from '../../../types/mrp'
|
||||||
import RunMrpModal from "./RunMrpModal";
|
import RunMrpModal from './RunMrpModal'
|
||||||
import ProductionSuggestions from "./ProductionSuggestions";
|
import ProductionSuggestions from './ProductionSuggestions'
|
||||||
import PurchaseSuggestions from "./PurchaseSuggestions";
|
import PurchaseSuggestions from './PurchaseSuggestions'
|
||||||
import MaterialRequirements from "./MaterialRequirements";
|
import MaterialRequirements from './MaterialRequirements'
|
||||||
import {
|
import {
|
||||||
mockMaterialRequirements,
|
mockMaterialRequirements,
|
||||||
mockProductionSuggestions,
|
mockProductionSuggestions,
|
||||||
mockPurchaseSuggestions,
|
mockPurchaseSuggestions,
|
||||||
} from "../../../mocks/mockMRP";
|
} from '../../../mocks/mockMRP'
|
||||||
import Widget from "../../../components/common/Widget";
|
import Widget from '../../../components/common/Widget'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const Requirements: React.FC = () => {
|
const Requirements: React.FC = () => {
|
||||||
const [materialRequirements, setMaterialRequirements] = useState<
|
const [materialRequirements, setMaterialRequirements] = useState<MrpMaterialRequirement[]>([])
|
||||||
MrpMaterialRequirement[]
|
const [purchaseSuggestions, setPurchaseSuggestions] = useState<MrpPurchaseSuggestion[]>([])
|
||||||
>([]);
|
const [productionSuggestions, setProductionSuggestions] = useState<MrpProductionSuggestion[]>([])
|
||||||
const [purchaseSuggestions, setPurchaseSuggestions] = useState<
|
|
||||||
MrpPurchaseSuggestion[]
|
|
||||||
>([]);
|
|
||||||
const [productionSuggestions, setProductionSuggestions] = useState<
|
|
||||||
MrpProductionSuggestion[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const runMrp = () => {
|
const runMrp = () => {
|
||||||
// This is where the actual MRP logic would run.
|
// This is where the actual MRP logic would run.
|
||||||
// For now, we'll just populate the state with mock data.
|
// For now, we'll just populate the state with mock data.
|
||||||
setMaterialRequirements(mockMaterialRequirements);
|
setMaterialRequirements(mockMaterialRequirements)
|
||||||
setPurchaseSuggestions(mockPurchaseSuggestions);
|
setPurchaseSuggestions(mockPurchaseSuggestions)
|
||||||
setProductionSuggestions(mockProductionSuggestions);
|
setProductionSuggestions(mockProductionSuggestions)
|
||||||
};
|
}
|
||||||
|
|
||||||
const [isMrpModalOpen, setIsMrpModalOpen] = useState(false);
|
const [isMrpModalOpen, setIsMrpModalOpen] = useState(false)
|
||||||
const [activeTab, setActiveTab] = useState<
|
const [activeTab, setActiveTab] = useState<'requirements' | 'production' | 'purchase'>(
|
||||||
"requirements" | "production" | "purchase"
|
'requirements',
|
||||||
>("requirements");
|
)
|
||||||
|
|
||||||
const getUrgencyLevel = (requirement: MrpMaterialRequirement) => {
|
const getUrgencyLevel = (requirement: MrpMaterialRequirement) => {
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
const reqDate = new Date(requirement.requirementDate);
|
const reqDate = new Date(requirement.requirementDate)
|
||||||
const daysUntilRequired = Math.ceil(
|
const daysUntilRequired = Math.ceil(
|
||||||
(reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)
|
(reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24),
|
||||||
);
|
)
|
||||||
|
|
||||||
if (daysUntilRequired < 0)
|
if (daysUntilRequired < 0)
|
||||||
return {
|
return {
|
||||||
level: "overdue",
|
level: 'overdue',
|
||||||
color: "bg-red-100 text-red-800",
|
color: 'bg-red-100 text-red-800',
|
||||||
label: "Gecikmiş",
|
label: 'Gecikmiş',
|
||||||
};
|
}
|
||||||
if (daysUntilRequired <= 7)
|
if (daysUntilRequired <= 7)
|
||||||
return {
|
return {
|
||||||
level: "urgent",
|
level: 'urgent',
|
||||||
color: "bg-orange-100 text-orange-800",
|
color: 'bg-orange-100 text-orange-800',
|
||||||
label: "Acil",
|
label: 'Acil',
|
||||||
};
|
}
|
||||||
if (daysUntilRequired <= 30)
|
if (daysUntilRequired <= 30)
|
||||||
return {
|
return {
|
||||||
level: "soon",
|
level: 'soon',
|
||||||
color: "bg-yellow-100 text-yellow-800",
|
color: 'bg-yellow-100 text-yellow-800',
|
||||||
label: "Yakın",
|
label: 'Yakın',
|
||||||
};
|
}
|
||||||
return {
|
return {
|
||||||
level: "normal",
|
level: 'normal',
|
||||||
color: "bg-green-100 text-green-800",
|
color: 'bg-green-100 text-green-800',
|
||||||
label: "Normal",
|
label: 'Normal',
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Calculate statistics
|
// Calculate statistics
|
||||||
const totalRequirements = materialRequirements.length;
|
const totalRequirements = materialRequirements.length
|
||||||
const shortageRequirements = materialRequirements.filter(
|
const shortageRequirements = materialRequirements.filter((r) => r.netRequirement > 0).length
|
||||||
(r) => r.netRequirement > 0
|
|
||||||
).length;
|
|
||||||
const urgentRequirements = materialRequirements.filter(
|
const urgentRequirements = materialRequirements.filter(
|
||||||
(r) => getUrgencyLevel(r).level === "urgent"
|
(r) => getUrgencyLevel(r).level === 'urgent',
|
||||||
).length;
|
).length
|
||||||
const openProductionSuggestions = productionSuggestions.filter(
|
const openProductionSuggestions = productionSuggestions.filter((s) => s.status === 'OPEN').length
|
||||||
(s) => s.status === "OPEN"
|
const openPurchaseSuggestions = purchaseSuggestions.filter((s) => s.status === 'OPEN').length
|
||||||
).length;
|
|
||||||
const openPurchaseSuggestions = purchaseSuggestions.filter(
|
|
||||||
(s) => s.status === "OPEN"
|
|
||||||
).length;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-bold text-gray-900">
|
<div>
|
||||||
Malzeme İhtiyaçları
|
<h2 className="text-xl font-bold text-gray-900">Malzeme İhtiyaçları</h2>
|
||||||
</h2>
|
<p className="text-sm text-gray-600">
|
||||||
<p className="text-sm text-gray-600">
|
Malzeme ihtiyaç hesaplama ve satın alma önerileri
|
||||||
Malzeme ihtiyaç hesaplama ve satın alma önerileri
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
<button
|
||||||
<button
|
onClick={() => setIsMrpModalOpen(true)}
|
||||||
onClick={() => setIsMrpModalOpen(true)}
|
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||||
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
>
|
||||||
>
|
|
||||||
<FaBox className="w-4 h-4" />
|
|
||||||
MRP Çalıştır
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Stats Cards */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
||||||
<Widget
|
|
||||||
title="Toplam İhtiyaç"
|
|
||||||
value={totalRequirements}
|
|
||||||
color="blue"
|
|
||||||
icon="FaBox"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
|
||||||
title="Eksik Stok"
|
|
||||||
value={shortageRequirements}
|
|
||||||
color="red"
|
|
||||||
icon="FaArrowDown"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
|
||||||
title="Acil İhtiyaç"
|
|
||||||
value={urgentRequirements}
|
|
||||||
color="orange"
|
|
||||||
icon="FaExclamationTriangle"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
|
||||||
title="Açık Öneri"
|
|
||||||
value={openProductionSuggestions + openPurchaseSuggestions}
|
|
||||||
color="purple"
|
|
||||||
icon="FaBullseye"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Tabs */}
|
|
||||||
<div className="flex bg-gray-100 rounded-lg p-1">
|
|
||||||
<button
|
|
||||||
onClick={() => setActiveTab("requirements")}
|
|
||||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
|
||||||
activeTab === "requirements"
|
|
||||||
? "bg-white text-gray-900 shadow-sm"
|
|
||||||
: "text-gray-600 hover:text-gray-900"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-center gap-2">
|
|
||||||
<FaBox className="w-4 h-4" />
|
<FaBox className="w-4 h-4" />
|
||||||
Malzeme İhtiyaçları ({totalRequirements})
|
MRP Çalıştır
|
||||||
</div>
|
</button>
|
||||||
</button>
|
</div>
|
||||||
<button
|
|
||||||
onClick={() => setActiveTab("production")}
|
{/* Stats Cards */}
|
||||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
activeTab === "production"
|
<Widget title="Toplam İhtiyaç" value={totalRequirements} color="blue" icon="FaBox" />
|
||||||
? "bg-white text-gray-900 shadow-sm"
|
|
||||||
: "text-gray-600 hover:text-gray-900"
|
<Widget title="Eksik Stok" value={shortageRequirements} color="red" icon="FaArrowDown" />
|
||||||
}`}
|
|
||||||
>
|
<Widget
|
||||||
<div className="flex items-center justify-center gap-2">
|
title="Acil İhtiyaç"
|
||||||
<FaIndustry className="w-4 h-4" />
|
value={urgentRequirements}
|
||||||
Üretim Önerileri ({openProductionSuggestions})
|
color="orange"
|
||||||
</div>
|
icon="FaExclamationTriangle"
|
||||||
</button>
|
/>
|
||||||
<button
|
|
||||||
onClick={() => setActiveTab("purchase")}
|
<Widget
|
||||||
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
title="Açık Öneri"
|
||||||
activeTab === "purchase"
|
value={openProductionSuggestions + openPurchaseSuggestions}
|
||||||
? "bg-white text-gray-900 shadow-sm"
|
color="purple"
|
||||||
: "text-gray-600 hover:text-gray-900"
|
icon="FaBullseye"
|
||||||
}`}
|
/>
|
||||||
>
|
</div>
|
||||||
<div className="flex items-center justify-center gap-2">
|
|
||||||
<FaShoppingCart className="w-4 h-4" />
|
{/* Tabs */}
|
||||||
Satınalma Önerileri ({openPurchaseSuggestions})
|
<div className="flex bg-gray-100 rounded-lg p-1">
|
||||||
</div>
|
<button
|
||||||
</button>
|
onClick={() => setActiveTab('requirements')}
|
||||||
|
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||||
|
activeTab === 'requirements'
|
||||||
|
? 'bg-white text-gray-900 shadow-sm'
|
||||||
|
: 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<FaBox className="w-4 h-4" />
|
||||||
|
Malzeme İhtiyaçları ({totalRequirements})
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab('production')}
|
||||||
|
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||||
|
activeTab === 'production'
|
||||||
|
? 'bg-white text-gray-900 shadow-sm'
|
||||||
|
: 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<FaIndustry className="w-4 h-4" />
|
||||||
|
Üretim Önerileri ({openProductionSuggestions})
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab('purchase')}
|
||||||
|
className={`flex-1 py-2 px-3 rounded-md text-xs font-medium transition-colors ${
|
||||||
|
activeTab === 'purchase'
|
||||||
|
? 'bg-white text-gray-900 shadow-sm'
|
||||||
|
: 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<FaShoppingCart className="w-4 h-4" />
|
||||||
|
Satınalma Önerileri ({openPurchaseSuggestions})
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Data Table */}
|
||||||
|
{activeTab === 'requirements' && (
|
||||||
|
<MaterialRequirements materialRequirements={materialRequirements} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{activeTab === 'production' && (
|
||||||
|
<ProductionSuggestions productionSuggestions={productionSuggestions} />
|
||||||
|
)}
|
||||||
|
{activeTab === 'purchase' && (
|
||||||
|
<PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Data Table */}
|
|
||||||
{activeTab === "requirements" && (
|
|
||||||
<MaterialRequirements materialRequirements={materialRequirements} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeTab === "production" && (
|
|
||||||
<ProductionSuggestions productionSuggestions={productionSuggestions} />
|
|
||||||
)}
|
|
||||||
{activeTab === "purchase" && (
|
|
||||||
<PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<RunMrpModal
|
<RunMrpModal
|
||||||
isOpen={isMrpModalOpen}
|
isOpen={isMrpModalOpen}
|
||||||
onClose={() => setIsMrpModalOpen(false)}
|
onClose={() => setIsMrpModalOpen(false)}
|
||||||
onRunComplete={() => {
|
onRunComplete={() => {
|
||||||
runMrp();
|
runMrp()
|
||||||
setIsMrpModalOpen(false);
|
setIsMrpModalOpen(false)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Requirements;
|
export default Requirements
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,52 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import { FaCog, FaPlus, FaEdit, FaTrash, FaClock, FaCheckCircle, FaPlay } from 'react-icons/fa'
|
||||||
FaCog,
|
import { MrpWorkOrder } from '../../../types/mrp'
|
||||||
FaPlus,
|
import DataTable, { Column } from '../../../components/common/DataTable'
|
||||||
FaEdit,
|
import { mockWorkOrders } from '../../../mocks/mockWorkOrders'
|
||||||
FaTrash,
|
import NewWorkOrderForm from './NewWorkOrderForm'
|
||||||
FaClock,
|
import EditWorkOrderForm from './EditWorkOrderForm'
|
||||||
FaCheckCircle,
|
import ViewWorkOrderModal from './ViewWorkOrderModal'
|
||||||
FaPlay,
|
import CompleteWorkOrderModal from './CompleteWorkOrderModal'
|
||||||
} from "react-icons/fa";
|
import Widget from '../../../components/common/Widget'
|
||||||
import { MrpWorkOrder } from "../../../types/mrp";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
import DataTable, { Column } from "../../../components/common/DataTable";
|
|
||||||
import { mockWorkOrders } from "../../../mocks/mockWorkOrders";
|
|
||||||
import NewWorkOrderForm from "./NewWorkOrderForm";
|
|
||||||
import EditWorkOrderForm from "./EditWorkOrderForm";
|
|
||||||
import ViewWorkOrderModal from "./ViewWorkOrderModal";
|
|
||||||
import CompleteWorkOrderModal from "./CompleteWorkOrderModal";
|
|
||||||
import Widget from "../../../components/common/Widget";
|
|
||||||
import { PriorityEnum } from "../../../types/common";
|
|
||||||
import {
|
import {
|
||||||
getPriorityColor,
|
getPriorityColor,
|
||||||
getPriorityText,
|
getPriorityText,
|
||||||
getWorkOrderStatusColor,
|
getWorkOrderStatusColor,
|
||||||
getWorkOrderStatusText,
|
getWorkOrderStatusText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
import { WorkOrderStatusEnum } from "../../../types/pm";
|
import { WorkOrderStatusEnum } from '../../../types/pm'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const WorkOrders: React.FC = () => {
|
const WorkOrders: React.FC = () => {
|
||||||
const [workOrders, setWorkOrders] = useState<MrpWorkOrder[]>(mockWorkOrders);
|
const [workOrders, setWorkOrders] = useState<MrpWorkOrder[]>(mockWorkOrders)
|
||||||
|
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [selectedStatus, setSelectedStatus] = useState<
|
const [selectedStatus, setSelectedStatus] = useState<WorkOrderStatusEnum | 'all'>('all')
|
||||||
WorkOrderStatusEnum | "all"
|
const [selectedPriority, setSelectedPriority] = useState<PriorityEnum | 'all'>('all')
|
||||||
>("all");
|
|
||||||
const [selectedPriority, setSelectedPriority] = useState<
|
|
||||||
PriorityEnum | "all"
|
|
||||||
>("all");
|
|
||||||
|
|
||||||
// Modal states
|
// Modal states
|
||||||
const [isNewWorkOrderOpen, setIsNewWorkOrderOpen] = useState(false);
|
const [isNewWorkOrderOpen, setIsNewWorkOrderOpen] = useState(false)
|
||||||
const [isEditWorkOrderOpen, setIsEditWorkOrderOpen] = useState(false);
|
const [isEditWorkOrderOpen, setIsEditWorkOrderOpen] = useState(false)
|
||||||
const [isViewWorkOrderOpen, setIsViewWorkOrderOpen] = useState(false);
|
const [isViewWorkOrderOpen, setIsViewWorkOrderOpen] = useState(false)
|
||||||
const [isCompleteWorkOrderOpen, setIsCompleteWorkOrderOpen] = useState(false);
|
const [isCompleteWorkOrderOpen, setIsCompleteWorkOrderOpen] = useState(false)
|
||||||
const [selectedWorkOrder, setSelectedWorkOrder] =
|
const [selectedWorkOrder, setSelectedWorkOrder] = useState<MrpWorkOrder | null>(null)
|
||||||
useState<MrpWorkOrder | null>(null);
|
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
setIsNewWorkOrderOpen(true);
|
setIsNewWorkOrderOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEdit = (workOrder: MrpWorkOrder) => {
|
const handleEdit = (workOrder: MrpWorkOrder) => {
|
||||||
setSelectedWorkOrder(workOrder);
|
setSelectedWorkOrder(workOrder)
|
||||||
setIsEditWorkOrderOpen(true);
|
setIsEditWorkOrderOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleDelete = (id: string) => {
|
const handleDelete = (id: string) => {
|
||||||
if (window.confirm("Bu iş emrini silmek istediğinizden emin misiniz?")) {
|
if (window.confirm('Bu iş emrini silmek istediğinizden emin misiniz?')) {
|
||||||
setWorkOrders((prev) => prev.filter((wo) => wo.id !== id));
|
setWorkOrders((prev) => prev.filter((wo) => wo.id !== id))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleStart = (workOrder: MrpWorkOrder) => {
|
const handleStart = (workOrder: MrpWorkOrder) => {
|
||||||
setWorkOrders((prev) =>
|
setWorkOrders((prev) =>
|
||||||
|
|
@ -69,57 +57,54 @@ const WorkOrders: React.FC = () => {
|
||||||
status: WorkOrderStatusEnum.InProgress,
|
status: WorkOrderStatusEnum.InProgress,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
}
|
}
|
||||||
: wo
|
: wo,
|
||||||
)
|
),
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleComplete = (workOrder: MrpWorkOrder) => {
|
const handleComplete = (workOrder: MrpWorkOrder) => {
|
||||||
setSelectedWorkOrder(workOrder);
|
setSelectedWorkOrder(workOrder)
|
||||||
setIsCompleteWorkOrderOpen(true);
|
setIsCompleteWorkOrderOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleViewDetails = (workOrder: MrpWorkOrder) => {
|
const handleViewDetails = (workOrder: MrpWorkOrder) => {
|
||||||
setSelectedWorkOrder(workOrder);
|
setSelectedWorkOrder(workOrder)
|
||||||
setIsViewWorkOrderOpen(true);
|
setIsViewWorkOrderOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
// Modal handlers
|
// Modal handlers
|
||||||
const handleNewWorkOrderSave = (
|
const handleNewWorkOrderSave = (
|
||||||
newWorkOrderData: Omit<
|
newWorkOrderData: Omit<MrpWorkOrder, 'id' | 'creationTime' | 'lastModificationTime'>,
|
||||||
MrpWorkOrder,
|
|
||||||
"id" | "creationTime" | "lastModificationTime"
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
const newWorkOrder: MrpWorkOrder = {
|
const newWorkOrder: MrpWorkOrder = {
|
||||||
...newWorkOrderData,
|
...newWorkOrderData,
|
||||||
id: `WO-${Date.now()}`,
|
id: `WO-${Date.now()}`,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
setWorkOrders((prev) => [...prev, newWorkOrder]);
|
setWorkOrders((prev) => [...prev, newWorkOrder])
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEditWorkOrderSave = (updatedWorkOrder: MrpWorkOrder) => {
|
const handleEditWorkOrderSave = (updatedWorkOrder: MrpWorkOrder) => {
|
||||||
setWorkOrders((prev) =>
|
setWorkOrders((prev) =>
|
||||||
prev.map((wo) => (wo.id === updatedWorkOrder.id ? updatedWorkOrder : wo))
|
prev.map((wo) => (wo.id === updatedWorkOrder.id ? updatedWorkOrder : wo)),
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCompleteWorkOrder = (
|
const handleCompleteWorkOrder = (
|
||||||
workOrderId: string,
|
workOrderId: string,
|
||||||
confirmedQuantity: number,
|
confirmedQuantity: number,
|
||||||
scrapQuantity: number
|
scrapQuantity: number,
|
||||||
) => {
|
) => {
|
||||||
setWorkOrders((prev) =>
|
setWorkOrders((prev) =>
|
||||||
prev.map((wo) => {
|
prev.map((wo) => {
|
||||||
if (wo.id === workOrderId) {
|
if (wo.id === workOrderId) {
|
||||||
const newConfirmedQuantity = wo.confirmedQuantity + confirmedQuantity;
|
const newConfirmedQuantity = wo.confirmedQuantity + confirmedQuantity
|
||||||
const newScrapQuantity = wo.scrapQuantity + scrapQuantity;
|
const newScrapQuantity = wo.scrapQuantity + scrapQuantity
|
||||||
const newStatus =
|
const newStatus =
|
||||||
newConfirmedQuantity + newScrapQuantity >= wo.plannedQuantity
|
newConfirmedQuantity + newScrapQuantity >= wo.plannedQuantity
|
||||||
? WorkOrderStatusEnum.Completed
|
? WorkOrderStatusEnum.Completed
|
||||||
: wo.status;
|
: wo.status
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...wo,
|
...wo,
|
||||||
|
|
@ -127,80 +112,69 @@ const WorkOrders: React.FC = () => {
|
||||||
scrapQuantity: newScrapQuantity,
|
scrapQuantity: newScrapQuantity,
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
return wo;
|
return wo
|
||||||
})
|
}),
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const filteredWorkOrders = workOrders.filter((workOrder) => {
|
const filteredWorkOrders = workOrders.filter((workOrder) => {
|
||||||
if (
|
if (
|
||||||
searchTerm &&
|
searchTerm &&
|
||||||
!workOrder.workOrderNumber
|
!workOrder.workOrderNumber.toLowerCase().includes(searchTerm.toLowerCase()) &&
|
||||||
.toLowerCase()
|
!workOrder.productionOrder?.orderNumber?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
.includes(searchTerm.toLowerCase()) &&
|
|
||||||
!workOrder.productionOrder?.orderNumber
|
|
||||||
?.toLowerCase()
|
|
||||||
.includes(searchTerm.toLowerCase())
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
if (selectedStatus !== "all" && workOrder.status !== selectedStatus) {
|
if (selectedStatus !== 'all' && workOrder.status !== selectedStatus) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
if (
|
if (selectedPriority !== 'all' && workOrder.productionOrder?.priority !== selectedPriority) {
|
||||||
selectedPriority !== "all" &&
|
return false
|
||||||
workOrder.productionOrder?.priority !== selectedPriority
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true
|
||||||
});
|
})
|
||||||
|
|
||||||
const columns: Column<MrpWorkOrder>[] = [
|
const columns: Column<MrpWorkOrder>[] = [
|
||||||
{
|
{
|
||||||
key: "workOrderNumber",
|
key: 'workOrderNumber',
|
||||||
header: "İş Emri No",
|
header: 'İş Emri No',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-gray-900">
|
<div className="font-medium text-gray-900">{workOrder.workOrderNumber}</div>
|
||||||
{workOrder.workOrderNumber}
|
<div className="text-sm text-gray-500">Sıra: {workOrder.sequence}</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
Sıra: {workOrder.sequence}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "productionOrder",
|
key: 'productionOrder',
|
||||||
header: "Üretim Emri",
|
header: 'Üretim Emri',
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-gray-900">
|
<div className="font-medium text-gray-900">
|
||||||
{workOrder.productionOrder?.orderNumber || "N/A"}
|
{workOrder.productionOrder?.orderNumber || 'N/A'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "material",
|
key: 'material',
|
||||||
header: "Malzeme",
|
header: 'Malzeme',
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<div>
|
<div>
|
||||||
<div className="text-gray-900">
|
<div className="text-gray-900">
|
||||||
{workOrder.material?.code}
|
{workOrder.material?.code}
|
||||||
{" - "}
|
{' - '}
|
||||||
{workOrder.material?.name || workOrder.materialId}
|
{workOrder.material?.name || workOrder.materialId}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "workCenter",
|
key: 'workCenter',
|
||||||
header: "İş Merkezi",
|
header: 'İş Merkezi',
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-gray-900">
|
<div className="font-medium text-gray-900">
|
||||||
|
|
@ -209,15 +183,13 @@ const WorkOrders: React.FC = () => {
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span>{workOrder.operation?.name || workOrder.operationId}</span>
|
<span>{workOrder.operation?.name || workOrder.operationId}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">{workOrder.assignedOperators.length} Operatör</div>
|
||||||
{workOrder.assignedOperators.length} Operatör
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "quantities",
|
key: 'quantities',
|
||||||
header: "Miktarlar",
|
header: 'Miktarlar',
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<div>Plan: {workOrder.plannedQuantity}</div>
|
<div>Plan: {workOrder.plannedQuantity}</div>
|
||||||
|
|
@ -229,50 +201,46 @@ const WorkOrders: React.FC = () => {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "schedule",
|
key: 'schedule',
|
||||||
header: "Planlama",
|
header: 'Planlama',
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<FaClock className="w-3 h-3 text-gray-400" />
|
<FaClock className="w-3 h-3 text-gray-400" />
|
||||||
<span>
|
<span>
|
||||||
Başlangıç:{" "}
|
Başlangıç: {new Date(workOrder.plannedStartDate).toLocaleDateString('tr-TR')}
|
||||||
{new Date(workOrder.plannedStartDate).toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<FaClock className="w-3 h-3 text-gray-400" />
|
<FaClock className="w-3 h-3 text-gray-400" />
|
||||||
<span>
|
<span>Bitiş: {new Date(workOrder.plannedEndDate).toLocaleDateString('tr-TR')}</span>
|
||||||
Bitiş:{" "}
|
|
||||||
{new Date(workOrder.plannedEndDate).toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "priority",
|
key: 'priority',
|
||||||
header: "Öncelik",
|
header: 'Öncelik',
|
||||||
render: (workOrder: MrpWorkOrder) =>
|
render: (workOrder: MrpWorkOrder) =>
|
||||||
workOrder.productionOrder?.priority ? (
|
workOrder.productionOrder?.priority ? (
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 text-xs font-medium rounded-full ${getPriorityColor(
|
className={`px-2 py-1 text-xs font-medium rounded-full ${getPriorityColor(
|
||||||
workOrder.productionOrder.priority
|
workOrder.productionOrder.priority,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getPriorityText(workOrder.productionOrder.priority)}
|
{getPriorityText(workOrder.productionOrder.priority)}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
"-"
|
'-'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "status",
|
key: 'status',
|
||||||
header: "Durum",
|
header: 'Durum',
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 text-xs font-medium rounded-full ${getWorkOrderStatusColor(
|
className={`px-2 py-1 text-xs font-medium rounded-full ${getWorkOrderStatusColor(
|
||||||
workOrder.status
|
workOrder.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWorkOrderStatusText(workOrder.status)}
|
{getWorkOrderStatusText(workOrder.status)}
|
||||||
|
|
@ -280,8 +248,8 @@ const WorkOrders: React.FC = () => {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "actions",
|
key: 'actions',
|
||||||
header: "İşlemler",
|
header: 'İşlemler',
|
||||||
render: (workOrder: MrpWorkOrder) => (
|
render: (workOrder: MrpWorkOrder) => (
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
{workOrder.status === WorkOrderStatusEnum.Released && (
|
{workOrder.status === WorkOrderStatusEnum.Released && (
|
||||||
|
|
@ -326,167 +294,129 @@ const WorkOrders: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
// Calculate statistics
|
// Calculate statistics
|
||||||
const totalWorkOrders = workOrders.length;
|
const totalWorkOrders = workOrders.length
|
||||||
const inProgressOrders = workOrders.filter(
|
const inProgressOrders = workOrders.filter(
|
||||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress
|
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
|
||||||
).length;
|
).length
|
||||||
const completedOrders = workOrders.filter(
|
const completedOrders = workOrders.filter(
|
||||||
(wo) => wo.status === WorkOrderStatusEnum.Completed
|
(wo) => wo.status === WorkOrderStatusEnum.Completed,
|
||||||
).length;
|
).length
|
||||||
const delayedOrders = workOrders.filter(
|
const delayedOrders = workOrders.filter(
|
||||||
(wo) =>
|
(wo) => wo.status !== WorkOrderStatusEnum.Completed && new Date(wo.plannedEndDate) < new Date(),
|
||||||
wo.status !== WorkOrderStatusEnum.Completed &&
|
).length
|
||||||
new Date(wo.plannedEndDate) < new Date()
|
|
||||||
).length;
|
|
||||||
|
|
||||||
// Status distribution
|
// Status distribution
|
||||||
const statusDistribution = Object.values(WorkOrderStatusEnum).map(
|
const statusDistribution = Object.values(WorkOrderStatusEnum).map((status) => ({
|
||||||
(status) => ({
|
status,
|
||||||
status,
|
count: workOrders.filter((wo) => wo.status === status).length,
|
||||||
count: workOrders.filter((wo) => wo.status === status).length,
|
percentage:
|
||||||
percentage:
|
(workOrders.filter((wo) => wo.status === status).length / workOrders.length) * 100 || 0,
|
||||||
(workOrders.filter((wo) => wo.status === status).length /
|
}))
|
||||||
workOrders.length) *
|
|
||||||
100 || 0,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2>
|
<div>
|
||||||
<p className="text-sm text-gray-600">
|
<h2 className="text-xl font-bold text-gray-900">İş Emirleri</h2>
|
||||||
Üretim operasyonlarının detaylı takibi
|
<p className="text-sm text-gray-600">Üretim operasyonlarının detaylı takibi</p>
|
||||||
</p>
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={handleAdd}
|
||||||
|
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
||||||
|
>
|
||||||
|
<FaPlus className="w-4 h-4" />
|
||||||
|
Yeni İş Emri
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
onClick={handleAdd}
|
|
||||||
className="flex items-center gap-2 px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
|
|
||||||
>
|
|
||||||
<FaPlus className="w-4 h-4" />
|
|
||||||
Yeni İş Emri
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||||
<Widget
|
<Widget title="Toplam İş Emri" value={totalWorkOrders} color="blue" icon="FaCog" />
|
||||||
title="Toplam İş Emri"
|
|
||||||
value={totalWorkOrders}
|
|
||||||
color="blue"
|
|
||||||
icon="FaCog"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
<Widget title="İşlemde" value={inProgressOrders} color="yellow" icon="FaPlay" />
|
||||||
title="İşlemde"
|
|
||||||
value={inProgressOrders}
|
|
||||||
color="yellow"
|
|
||||||
icon="FaPlay"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
<Widget title="Tamamlanan" value={completedOrders} color="green" icon="FaCheckCircle" />
|
||||||
title="Tamamlanan"
|
|
||||||
value={completedOrders}
|
|
||||||
color="green"
|
|
||||||
icon="FaCheckCircle"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
<Widget title="Geciken" value={delayedOrders} color="red" icon="FaExclamationCircle" />
|
||||||
title="Geciken"
|
</div>
|
||||||
value={delayedOrders}
|
|
||||||
color="red"
|
|
||||||
icon="FaExclamationCircle"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Status Distribution */}
|
{/* Status Distribution */}
|
||||||
<div className="bg-white rounded-lg shadow-sm border p-3">
|
<div className="bg-white rounded-lg shadow-sm border p-3">
|
||||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">
|
<h3 className="text-sm font-semibold text-gray-900 mb-2">Durum Dağılımı</h3>
|
||||||
Durum Dağılımı
|
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
|
||||||
</h3>
|
{statusDistribution.map(({ status, count, percentage }) => (
|
||||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
|
<div key={status} className="text-center p-2 border rounded-lg">
|
||||||
{statusDistribution.map(({ status, count, percentage }) => (
|
<div
|
||||||
<div key={status} className="text-center p-2 border rounded-lg">
|
className={`inline-block px-1.5 py-0.5 text-xs font-medium rounded-full mb-1 ${getWorkOrderStatusColor(
|
||||||
<div
|
status,
|
||||||
className={`inline-block px-1.5 py-0.5 text-xs font-medium rounded-full mb-1 ${getWorkOrderStatusColor(
|
)}`}
|
||||||
status
|
>
|
||||||
)}`}
|
{getWorkOrderStatusText(status)}
|
||||||
>
|
</div>
|
||||||
|
<div className="text-xl font-bold text-gray-900">{count}</div>
|
||||||
|
<div className="text-xs text-gray-500">{percentage.toFixed(1)}%</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Filters */}
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<div className="flex-1">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="İş emri numarası ara..."
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
|
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<select
|
||||||
|
value={selectedStatus}
|
||||||
|
onChange={(e) => setSelectedStatus(e.target.value as WorkOrderStatusEnum | 'all')}
|
||||||
|
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<option value="all">Tüm Durumlar</option>
|
||||||
|
{Object.values(WorkOrderStatusEnum).map((status) => (
|
||||||
|
<option key={status} value={status}>
|
||||||
{getWorkOrderStatusText(status)}
|
{getWorkOrderStatusText(status)}
|
||||||
</div>
|
</option>
|
||||||
<div className="text-xl font-bold text-gray-900">{count}</div>
|
))}
|
||||||
<div className="text-xs text-gray-500">
|
</select>
|
||||||
{percentage.toFixed(1)}%
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Filters */}
|
<select
|
||||||
<div className="flex gap-2 items-center">
|
value={selectedPriority}
|
||||||
<div className="flex-1">
|
onChange={(e) => setSelectedPriority(e.target.value as PriorityEnum | 'all')}
|
||||||
<input
|
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
type="text"
|
>
|
||||||
placeholder="İş emri numarası ara..."
|
<option value="all">Tüm Öncelikler</option>
|
||||||
value={searchTerm}
|
{Object.values(PriorityEnum).map((priority) => (
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
<option key={priority} value={priority}>
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
{getPriorityText(priority)}
|
||||||
/>
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select
|
{/* Data Table */}
|
||||||
value={selectedStatus}
|
<div className="bg-white rounded-lg shadow-sm border">
|
||||||
onChange={(e) =>
|
<DataTable data={filteredWorkOrders} columns={columns} />
|
||||||
setSelectedStatus(e.target.value as WorkOrderStatusEnum | "all")
|
|
||||||
}
|
|
||||||
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
<option value="all">Tüm Durumlar</option>
|
|
||||||
{Object.values(WorkOrderStatusEnum).map((status) => (
|
|
||||||
<option key={status} value={status}>
|
|
||||||
{getWorkOrderStatusText(status)}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select
|
|
||||||
value={selectedPriority}
|
|
||||||
onChange={(e) =>
|
|
||||||
setSelectedPriority(e.target.value as PriorityEnum | "all")
|
|
||||||
}
|
|
||||||
className="px-2 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
<option value="all">Tüm Öncelikler</option>
|
|
||||||
{Object.values(PriorityEnum).map((priority) => (
|
|
||||||
<option key={priority} value={priority}>
|
|
||||||
{getPriorityText(priority)}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Data Table */}
|
|
||||||
<div className="bg-white rounded-lg shadow-sm border">
|
|
||||||
<DataTable data={filteredWorkOrders} columns={columns} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{filteredWorkOrders.length === 0 && (
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
|
||||||
İş emri bulunamadı
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-500">
|
|
||||||
Arama kriterlerinizi değiştirmeyi deneyin.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
{filteredWorkOrders.length === 0 && (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 mb-2">İş emri bulunamadı</h3>
|
||||||
|
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Modals */}
|
{/* Modals */}
|
||||||
<NewWorkOrderForm
|
<NewWorkOrderForm
|
||||||
|
|
@ -514,8 +444,8 @@ const WorkOrders: React.FC = () => {
|
||||||
onConfirm={handleCompleteWorkOrder}
|
onConfirm={handleCompleteWorkOrder}
|
||||||
workOrder={selectedWorkOrder}
|
workOrder={selectedWorkOrder}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default WorkOrders;
|
export default WorkOrders
|
||||||
|
|
|
||||||
|
|
@ -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