Container güncellemesi

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

View file

@ -82,7 +82,7 @@ define(['./workbox-a959eb95'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "/index.html",
"revision": "0.sbo84r6raa"
"revision": "0.daul0044rt"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), {

View file

@ -1,4 +1,3 @@
import { CrmSalesOrder } from "./admin/crm";
import { PmWorkCenter, WorkOrderStatusEnum } from "./pm";
import {
MmMaterial,
@ -7,6 +6,7 @@ import {
QualityStatusEnum,
} from "./mm";
import { PriorityEnum } from "./common";
import { CrmSalesOrder } from "./crm";
export interface MrpProductionOrder {
// Üretim Emri
@ -196,7 +196,7 @@ export interface MrpDemandForecast {
notes: string;
}
export interface MrpPurchaseSuggestionn extends MrpRecommendation {
export interface MrpPurchaseSuggestion extends MrpRecommendation {
// Satınalma Tavsiyesi
supplierId: string;
supplier?: MmMaterialSupplier;

View file

@ -1,42 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const AccountingManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (
pathname === "/admin/accounting" ||
pathname === "/admin/accounting/current-accounts"
)
return "Cari Hesap Yönetimi";
if (pathname === "/admin/accounting/waybills") return "İrsaliye Yönetimi";
if (pathname === "/admin/accounting/invoices") return "Fatura Yönetimi";
if (pathname === "/admin/accounting/cash") return "Kasa Yönetimi";
if (pathname === "/admin/accounting/bank") return "Banka Yönetimi";
if (pathname === "/admin/accounting/check-note") return "Çek & Senet Takibi";
return "Muhasebe Yönetimi";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / CRM Yönetimi / Sayfanın Adı
const breadcrumbs = [{ name: "CRM Yönetimi", href: "/admin/crm" }];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader
title={pageTitle}
breadcrumbs={breadcrumbs}
/>
<div>
<Outlet />
</div>
</div>
);
};
export default AccountingManagement;

View file

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

View file

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

View file

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

View file

@ -20,8 +20,12 @@ import CurrentAccountDetails from "./CurrentAccountDetails";
import CurrentAccountMovementForm from "./CurrentAccountMovementForm";
import Widget from "../../../components/common/Widget";
import {
getAccountTypeColor,
getAccountTypeText,
getFiDocumentTypeColor,
getFiDocumentTypeText,
getRiskGroupColor,
getRiskGroupText,
} from "../../../utils/erp";
interface CurrentAccountManagementProps {
@ -351,46 +355,6 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
}
});
const getAccountTypeLabel = (type: AccountTypeEnum) => {
const typeLabels = {
[AccountTypeEnum.Customer]: "Müşteri",
[AccountTypeEnum.Supplier]: "Tedarikçi",
[AccountTypeEnum.Both]: "Müşteri/Tedarikçi",
[AccountTypeEnum.Other]: "Diğer",
};
return typeLabels[type];
};
const getAccountTypeColor = (type: AccountTypeEnum) => {
const typeColors = {
[AccountTypeEnum.Customer]: "bg-blue-100 text-blue-800",
[AccountTypeEnum.Supplier]: "bg-green-100 text-green-800",
[AccountTypeEnum.Both]: "bg-purple-100 text-purple-800",
[AccountTypeEnum.Other]: "bg-gray-100 text-gray-800",
};
return typeColors[type];
};
const getRiskGroupLabel = (riskGroup: RiskGroupEnum) => {
const riskLabels = {
[RiskGroupEnum.Low]: "Düşük Risk",
[RiskGroupEnum.Medium]: "Orta Risk",
[RiskGroupEnum.High]: "Yüksek Risk",
[RiskGroupEnum.Blocked]: "Blokeli",
};
return riskLabels[riskGroup];
};
const getRiskGroupColor = (riskGroup: RiskGroupEnum) => {
const riskColors = {
[RiskGroupEnum.Low]: "bg-green-100 text-green-800",
[RiskGroupEnum.Medium]: "bg-yellow-100 text-yellow-800",
[RiskGroupEnum.High]: "bg-orange-100 text-orange-800",
[RiskGroupEnum.Blocked]: "bg-red-100 text-red-800",
};
return riskColors[riskGroup];
};
const formatBalance = (balance: number) => {
const isDebit = balance > 0;
const absBalance = Math.abs(balance);
@ -438,7 +402,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
account.type
)}`}
>
{getAccountTypeLabel(account.type)}
{getAccountTypeText(account.type)}
</span>
),
},
@ -486,7 +450,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
account.riskGroup
)}`}
>
{getRiskGroupLabel(account.riskGroup)}
{getRiskGroupText(account.riskGroup)}
</span>
),
},
@ -566,7 +530,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
);
return (
<div className="space-y-3 py-2">
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
@ -682,7 +646,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
<option value="all">Tüm Türler</option>
{Object.values(AccountTypeEnum).map((type) => (
<option key={type} value={type}>
{getAccountTypeLabel(type)}
{getAccountTypeText(type)}
</option>
))}
</select>
@ -699,7 +663,7 @@ const CurrentAccountManagement: React.FC<CurrentAccountManagementProps> = ({
<option value="all">Tüm Risk Grupları</option>
{Object.values(RiskGroupEnum).map((risk) => (
<option key={risk} value={risk}>
{getRiskGroupLabel(risk)}
{getRiskGroupText(risk)}
</option>
))}
</select>

View file

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

View file

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

View file

@ -1,49 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const CRMManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (pathname === "/admin/crm" || pathname === "/admin/crm/customers") return "Müşteriler";
if (pathname === "/admin/crm/sales-teams") return "Satış Takımları";
if (pathname === "/admin/crm/sales-teams/new") return "Yeni Satış Takımı";
if (pathname.includes("/admin/crm/sales-teams/edit/")) return "Satış Takımı Düzenle";
if (pathname.includes("/admin/crm/sales-teams/") && !pathname.includes("edit") && !pathname.includes("new")) return "Satış Takımı Detayları";
if (pathname === "/admin/crm/loss-reasons") return "Kayıp Nedenleri";
if (pathname === "/admin/crm/opportunities") return "Fırsatlar";
if (pathname === "/admin/crm/activities") return "Aktiviteler";
if (pathname === "/admin/crm/sales-orders") return "Satış Siparişleri";
if (pathname === "/admin/crm/sales-orders/new") return "Yeni Satış Siparişi";
if (pathname.includes("/admin/crm/sales-orders/edit/")) return "Satış Siparişi Düzenle";
if (pathname.includes("/admin/crm/sales-orders/") && !pathname.includes("edit") && !pathname.includes("new")) return "Satış Siparişi Detayları";
if (pathname === "/admin/crm/customers/new") return "Yeni Müşteri";
if (pathname.includes("/admin/crm/customers/edit/")) return "Müşteri Düzenle";
if (pathname.includes("/admin/crm/customers/") && !pathname.includes("edit") && !pathname.includes("new")) return "Müşteri Detayları";
return "CRM Yönetimi";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / CRM Yönetimi / Sayfanın Adı
const breadcrumbs = [
{ name: "CRM Yönetimi", href: "/admin/crm" }
];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader
title={pageTitle}
breadcrumbs={breadcrumbs}
/>
<div>
<Outlet />
</div>
</div>
);
};
export default CRMManagement;

View file

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

View file

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

View file

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

View file

@ -166,7 +166,7 @@ const CustomerEdit: React.FC = () => {
return (
<Container>
<div className="min-h-screen bg-gray-50">
<div className="space-y-2">
<form onSubmit={handleSubmit}>
{/* Header */}
<div className="bg-white border-b border-gray-200">
@ -196,8 +196,8 @@ const CustomerEdit: React.FC = () => {
</button>
<div className="flex items-center space-x-4">
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center text-white shadow-lg">
<FaBuilding className="w-6 h-6" />
<div className="w-12 h-12 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center text-white shadow-lg">
<FaBuilding className="w-8 h-8" />
</div>
<div>

View file

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

View file

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

View file

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

View file

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

View file

@ -128,7 +128,7 @@ const CustomerView: React.FC = () => {
return (
<Container>
<div className="min-h-screen bg-gray-50">
<div className="space-y-2">
<div className="bg-white border-b border-gray-200">
<div className="mx-auto px-4 py-3">
{/* Breadcrumb */}

View file

@ -221,7 +221,7 @@ const LossReasons: React.FC = () => {
return (
<Container>
<div className="space-y-3 pt-2">
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
@ -391,6 +391,7 @@ const LossReasons: React.FC = () => {
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
</div>
)}
</div>
{/* Modals */}
<LossReasonModal
@ -463,7 +464,6 @@ const LossReasons: React.FC = () => {
</div>
</div>
)}
</div>
</Container>
)
}

View file

@ -237,7 +237,7 @@ const OpportunityManagement: React.FC = () => {
return (
<Container>
<div className="space-y-3 pt-2">
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
@ -384,6 +384,7 @@ const OpportunityManagement: React.FC = () => {
<p className="text-gray-500">Arama kriterlerinizi değiştirmeyi deneyin.</p>
</div>
)}
</div>
{/* Modals */}
<OpportunityForm
@ -400,7 +401,6 @@ const OpportunityManagement: React.FC = () => {
onEdit={handleEditFromDetails}
opportunity={selectedOpportunity}
/>
</div>
</Container>
)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,49 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const HRManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (pathname === "/admin/hr" || pathname === "/admin/hr/") return "Personel Listesi";
if (pathname === "/admin/hr/departments") return "Departman Yönetimi";
if (pathname === "/admin/hr/cost-centers") return "Maliyet Merkezi Yönetimi";
if (pathname === "/admin/hr/organization") return "Organizasyon Şeması";
if (pathname === "/admin/hr/leave-management") return "İzin Yönetimi";
if (pathname === "/admin/hr/overtimes-management") return "Mesai Yönetimi";
if (pathname === "/admin/hr/payroll") return "Bordro Yönetimi";
if (pathname === "/admin/hr/job-positions") return "İş Pozisyonları";
if (pathname === "/admin/hr/employment-types") return "İstihdam Türleri";
if (pathname === "/admin/hr/badges") return "Rozet Yönetimi";
if (pathname === "/admin/hr/360-templates") return "360 Derece Şablonları";
if (pathname === "/admin/hr/360-evaluation") return "360 Derece Değerlendirme";
if (pathname === "/admin/hr/employees/new") return "Yeni Çalışan";
if (pathname.includes("/admin/hr/employees/edit/")) return "Çalışan Düzenle";
if (pathname.includes("/admin/hr/employees/") && !pathname.includes("edit") && !pathname.includes("new")) return "Çalışan Detayları";
return "İnsan Kaynakları";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / İnsan Kaynakları / Sayfanın Adı
const breadcrumbs = [
{ name: "İnsan Kaynakları", href: "/admin/hr" }
];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader
title={pageTitle}
breadcrumbs={breadcrumbs}
/>
<div>
<Outlet />
</div>
</div>
);
};
export default HRManagement;

View file

@ -1,41 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const MaintenanceManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (pathname === "/admin/maintenance" || pathname === "/admin/maintenance/" || pathname === "/admin/maintenance/equipment") return "İş Merkezleri";
if (pathname === "/admin/maintenance/workcenters") return "İş Merkezleri";
if (pathname === "/admin/maintenance/plans") return "Bakım Planları";
if (pathname === "/admin/maintenance/calendar") return "Bakım Takvimi";
if (pathname === "/admin/maintenance/teams") return "Bakım Takımları";
if (pathname === "/admin/maintenance/faults") return "Arıza Bildirimleri";
if (pathname === "/admin/maintenance/workorders") return "Bakım İş Emirleri";
return "Bakım Yönetimi";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / Bakım Yönetimi / Sayfanın Adı
const breadcrumbs = [
{ name: "Bakım Yönetimi", href: "/admin/maintenance" }
];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader
title={pageTitle}
breadcrumbs={breadcrumbs}
/>
<div>
<Outlet />
</div>
</div>
);
};
export default MaintenanceManagement;

View file

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

View file

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

View file

@ -27,6 +27,7 @@ import {
getCriticalityLevelColor,
getCriticalityLevelText,
} from '../../../utils/erp'
import { Container } from '@/components/shared'
const WorkCenterList: React.FC = () => {
const [searchTerm, setSearchTerm] = useState('')
@ -75,7 +76,8 @@ const WorkCenterList: React.FC = () => {
}
return (
<div className="space-y-3 pt-2">
<Container>
<div className="space-y-2">
{/* Header Actions */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div className="flex items-center space-x-3">
@ -193,8 +195,8 @@ const WorkCenterList: React.FC = () => {
<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}
{workCenter?.filter((e) => e.status === WorkCenterStatusEnum.Operational)
.length || 0}
</p>
</div>
<FaCheckCircle className="h-8 w-8 text-green-600" />
@ -399,6 +401,7 @@ const WorkCenterList: React.FC = () => {
)}
</div>
</div>
</Container>
)
}

View file

@ -1,41 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const MRPManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (pathname === "/admin/mrp/operation-types") return "Operasyon Türleri";
if (pathname === "/admin/mrp/workcenters") return "İş Merkezleri";
if (pathname === "/admin/mrp/operations") return "Operasyon Tanımları";
if (pathname === "/admin/mrp/bom") return "Ürün Ağaçları (BOM)";
if (pathname === "/admin/mrp/production-orders") return "Üretim Emirleri";
if (pathname === "/admin/mrp/work-orders") return "İş Emirleri";
if (pathname === "/admin/mrp/demand-planning") return "Talep Planlama";
if (pathname === "/admin/mrp/material-requirements") return "Malzeme İhtiyaçları";
if (pathname === "/admin/mrp/planning-gantt") return "Planlama Gantt";
return "MRP Yönetimi";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / MRP Yönetimi / Sayfanın Adı
const breadcrumbs = [{ name: "MRP Yönetimi", href: "/admin/mrp" }];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader
title={pageTitle}
breadcrumbs={breadcrumbs}
/>
<div>
<Outlet />
</div>
</div>
);
};
export default MRPManagement;

View file

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState } from 'react'
import {
FaPlus,
FaSearch,
@ -10,81 +10,78 @@ import {
FaCopy,
FaExclamationTriangle,
FaClock,
} from "react-icons/fa";
import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from "../../../types/mrp";
import BOMFormModal from "./BOMFormModal";
import { getBOMTypeColor, getBOMTypeName } from "../../../utils/erp";
import { mockBOMs } from "../../../mocks/mockBOMs";
} from 'react-icons/fa'
import { MrpBOM, MrpBOMComponent, MrpBOMOperation } from '../../../types/mrp'
import BOMFormModal from './BOMFormModal'
import { getBOMTypeColor, getBOMTypeName } from '../../../utils/erp'
import { mockBOMs } from '../../../mocks/mockBOMs'
import { Container } from '@/components/shared'
const BOMManagement: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [showModal, setShowModal] = useState(false);
const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null);
const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null);
const [searchTerm, setSearchTerm] = useState('')
const [showModal, setShowModal] = useState(false)
const [editingBOM, setEditingBOM] = useState<MrpBOM | null>(null)
const [selectedBOM, setSelectedBOM] = useState<MrpBOM | null>(null)
// Mock data - replace with actual API calls
const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs);
const [boms, setBoms] = useState<MrpBOM[]>(mockBOMs)
const filteredBOMs = boms.filter(
(bom) =>
bom.bomCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
bom.materialId.toLowerCase().includes(searchTerm.toLowerCase()) ||
bom.version.toLowerCase().includes(searchTerm.toLowerCase())
);
bom.version.toLowerCase().includes(searchTerm.toLowerCase()),
)
const getTotalOperationTime = (operations: MrpBOMOperation[]) => {
return operations.reduce(
(total, op) => total + op.setupTime + op.runTime,
0
);
};
return operations.reduce((total, op) => total + op.setupTime + op.runTime, 0)
}
const getTotalComponents = (components: MrpBOMComponent[]) => {
return components.length;
};
return components.length
}
const getActiveComponents = (components: MrpBOMComponent[]) => {
return components.filter((comp) => comp.isActive).length;
};
return components.filter((comp) => comp.isActive).length
}
const handleEdit = (bom: MrpBOM) => {
setEditingBOM(bom);
setShowModal(true);
};
setEditingBOM(bom)
setShowModal(true)
}
const handleAddNew = () => {
setEditingBOM(null);
setShowModal(true);
};
setEditingBOM(null)
setShowModal(true)
}
const handleViewDetails = (bom: MrpBOM) => {
setSelectedBOM(bom);
setSelectedBOM(bom)
console.log(bom);
};
console.log(bom)
}
const handleCopy = (bom: MrpBOM) => {
console.log("Copying BOM:", bom.bomCode);
console.log('Copying BOM:', bom.bomCode)
// Implementation for copying BOM
};
}
const handleSave = (bom: MrpBOM) => {
setBoms((prev) => {
const existing = prev.find((b) => b.id === bom.id);
if (existing) return prev.map((b) => (b.id === bom.id ? bom : b));
return [...prev, { ...bom, id: bom.id || String(Date.now()) }];
});
setShowModal(false);
};
const existing = prev.find((b) => b.id === bom.id)
if (existing) return prev.map((b) => (b.id === bom.id ? bom : b))
return [...prev, { ...bom, id: bom.id || String(Date.now()) }]
})
setShowModal(false)
}
return (
<div className="space-y-3 pt-2">
<Container>
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-bold text-gray-900">
Ürün Ağaçları (BOM)
</h2>
<h2 className="text-xl font-bold text-gray-900">Ürün Ağaçları (BOM)</h2>
<p className="text-sm text-gray-600 mt-0.5">
Ürün bileşenlerini ve üretim operasyonlarını yönetin
</p>
@ -123,24 +120,19 @@ const BOMManagement: React.FC = () => {
<div className="flex-1">
<div className="flex items-center space-x-2 mb-2">
<FaCodeBranch className="w-4 h-4 text-gray-600" />
<h4 className="text-lg font-semibold text-gray-900">
{bom.bomCode}
</h4>
<h4 className="text-lg font-semibold text-gray-900">{bom.bomCode}</h4>
<span
className={`px-2 py-1 rounded-full text-xs font-medium ${getBOMTypeColor(
bom.bomType
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}
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
@ -180,8 +172,7 @@ const BOMManagement: React.FC = () => {
Bileşenler
</span>
<span className="font-medium">
{getActiveComponents(bom.components)}/
{getTotalComponents(bom.components)}
{getActiveComponents(bom.components)}/{getTotalComponents(bom.components)}
</span>
</div>
<div className="flex items-center justify-between">
@ -189,15 +180,11 @@ const BOMManagement: React.FC = () => {
<FaClock className="w-4 h-4 mr-1" />
Toplam Süre
</span>
<span className="font-medium">
{getTotalOperationTime(bom.operations)} dk
</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>
<span className="font-medium">{bom.validFrom.toLocaleDateString('tr-TR')}</span>
</div>
</div>
@ -206,12 +193,10 @@ const BOMManagement: React.FC = () => {
<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 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}
>
{bom.isActive ? "Aktif" : "Pasif"}
{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">
@ -221,7 +206,7 @@ const BOMManagement: React.FC = () => {
)}
</div>
<span className="text-xs text-gray-400">
{bom.lastModificationTime.toLocaleDateString("tr-TR")}
{bom.lastModificationTime.toLocaleDateString('tr-TR')}
</span>
</div>
</div>
@ -230,9 +215,7 @@ const BOMManagement: React.FC = () => {
{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>
<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>
@ -246,13 +229,10 @@ const BOMManagement: React.FC = () => {
{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>
<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}
Malzeme: {selectedBOM.material?.code} - {selectedBOM.material?.name}
</div>
<div>Versiyon: {selectedBOM.version}</div>
<div>Tip: {getBOMTypeName(selectedBOM.bomType)}</div>
@ -274,7 +254,7 @@ const BOMManagement: React.FC = () => {
>
<div className="flex items-center justify-between mb-1">
<span className="font-medium">
{component.position}. {component.material?.code} -{" "}
{component.position}. {component.material?.code} -{' '}
{component.material?.name}
</span>
<div className="flex space-x-2">
@ -286,17 +266,17 @@ const BOMManagement: React.FC = () => {
<span
className={`text-xs px-1 rounded ${
component.isActive
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}
>
{component.isActive ? "Aktif" : "Pasif"}
{component.isActive ? 'Aktif' : 'Pasif'}
</span>
</div>
</div>
<div className="text-gray-600">
Miktar: {component.quantity} {component.unitId} | Fire:
%{component.scrapPercentage}
Miktar: {component.quantity} {component.unitId} | Fire: %
{component.scrapPercentage}
</div>
</div>
))}
@ -317,23 +297,22 @@ const BOMManagement: React.FC = () => {
>
<div className="flex items-center justify-between mb-1">
<span className="font-medium">
{operation.sequence}. {operation.operation?.code} -{" "}
{operation.sequence}. {operation.operation?.code} -{' '}
{operation.operation?.name}
</span>
<span
className={`text-xs px-1 rounded ${
operation.isActive
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}
>
{operation.isActive ? "Aktif" : "Pasif"}
{operation.isActive ? 'Aktif' : 'Pasif'}
</span>
</div>
<div className="text-gray-600">
Hazırlık: {operation.setupTime}dk | İşlem:{" "}
{operation.runTime}dk | Kuyruk: {operation.queueTime}dk
| Taşıma: {operation.moveTime}dk
Hazırlık: {operation.setupTime}dk | İşlem: {operation.runTime}dk | Kuyruk:{' '}
{operation.queueTime}dk | Taşıma: {operation.moveTime}dk
</div>
</div>
))}
@ -346,13 +325,12 @@ const BOMManagement: React.FC = () => {
<h3 className="text-lg font-medium text-gray-900 mb-2">
BOM Detaylarını Görüntüle
</h3>
<p className="text-gray-500">
Detaylarını görmek için sol taraftan bir BOM seçin.
</p>
<p className="text-gray-500">Detaylarını görmek için sol taraftan bir BOM seçin.</p>
</div>
)}
</div>
</div>
</div>
<BOMFormModal
open={showModal}
@ -360,8 +338,8 @@ const BOMManagement: React.FC = () => {
onSave={handleSave}
onClose={() => setShowModal(false)}
/>
</div>
);
};
</Container>
)
}
export default BOMManagement;
export default BOMManagement

View file

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

View file

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState } from 'react'
import {
FaPlus,
FaSearch,
@ -11,62 +11,58 @@ import {
FaWrench,
FaTh,
FaList,
} from "react-icons/fa";
import { MrpOperation } from "../../../types/mrp";
import OperationFormModal from "./OperationFormModal";
import { mockOperations } from "../../../mocks/mockOperations";
} from 'react-icons/fa'
import { MrpOperation } from '../../../types/mrp'
import OperationFormModal from './OperationFormModal'
import { mockOperations } from '../../../mocks/mockOperations'
import { Container } from '@/components/shared'
const OperationDefinitions: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [showModal, setShowModal] = useState(false);
const [editingOperation, setEditingOperation] = useState<MrpOperation | null>(
null
);
const [viewMode, setViewMode] = useState<"card" | "list">("card");
const [searchTerm, setSearchTerm] = useState('')
const [showModal, setShowModal] = useState(false)
const [editingOperation, setEditingOperation] = useState<MrpOperation | null>(null)
const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
const getTotalCost = (operation: MrpOperation) => {
return operation.laborCost + operation.machineCost + operation.overheadCost;
};
return operation.laborCost + operation.machineCost + operation.overheadCost
}
// Mock data - replace with actual API calls
const [operations, setOperations] = useState<MrpOperation[]>(mockOperations);
const [operations, setOperations] = useState<MrpOperation[]>(mockOperations)
const filteredOperations = operations.filter(
(operation) =>
operation.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
operation.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
operation.workCenter?.name
.toLowerCase()
.includes(searchTerm.toLowerCase())
);
operation.workCenter?.name.toLowerCase().includes(searchTerm.toLowerCase()),
)
const handleEdit = (operation: MrpOperation) => {
setEditingOperation(operation);
setShowModal(true);
};
setEditingOperation(operation)
setShowModal(true)
}
const handleAddNew = () => {
setEditingOperation(null);
setShowModal(true);
};
setEditingOperation(null)
setShowModal(true)
}
const handleSave = (op: MrpOperation) => {
setOperations((prev) => {
const existing = prev.find((p) => p.id === op.id);
if (existing) return prev.map((p) => (p.id === op.id ? op : p));
return [...prev, { ...op, id: op.id || String(Date.now()) }];
});
setShowModal(false);
};
const existing = prev.find((p) => p.id === op.id)
if (existing) return prev.map((p) => (p.id === op.id ? op : p))
return [...prev, { ...op, id: op.id || String(Date.now()) }]
})
setShowModal(false)
}
return (
<div className="space-y-3 pt-2">
<Container>
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-bold text-gray-900">
Operasyon Tanımları
</h2>
<h2 className="text-xl font-bold text-gray-900">Operasyon Tanımları</h2>
<p className="text-sm text-gray-600 mt-0.5">
İş merkezlerinde gerçekleştirilen operasyonları tanımlayın
</p>
@ -75,21 +71,21 @@ const OperationDefinitions: React.FC = () => {
{/* View Toggle */}
<div className="flex bg-gray-100 rounded-lg p-0.5">
<button
onClick={() => setViewMode("card")}
onClick={() => setViewMode('card')}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
viewMode === "card"
? "bg-white text-blue-600 shadow-sm"
: "text-gray-600 hover:text-gray-900"
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")}
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"
viewMode === 'list'
? 'bg-white text-blue-600 shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
<FaList className="w-4 h-4" />
@ -118,7 +114,7 @@ const OperationDefinitions: React.FC = () => {
</div>
{/* Operations Display */}
{viewMode === "card" ? (
{viewMode === 'card' ? (
/* Card View */
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{filteredOperations.map((operation) => (
@ -129,18 +125,14 @@ const OperationDefinitions: React.FC = () => {
<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>
<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>
<p className="text-sm text-gray-500 mb-2">{operation.description}</p>
)}
</div>
<div className="flex space-x-1">
@ -162,12 +154,8 @@ const OperationDefinitions: React.FC = () => {
<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>
<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">
@ -177,9 +165,7 @@ const OperationDefinitions: React.FC = () => {
<FaClock className="w-4 h-4 mr-1" />
Standart Süre
</span>
<span className="font-medium">
{operation.standardTime} dk
</span>
<span className="font-medium">{operation.standardTime} dk</span>
</div>
<div className="flex items-center justify-between text-sm">
@ -187,9 +173,7 @@ const OperationDefinitions: React.FC = () => {
<FaCog className="w-4 h-4 mr-1" />
Hazırlık Süresi
</span>
<span className="font-medium">
{operation.setupTime} dk
</span>
<span className="font-medium">{operation.setupTime} dk</span>
</div>
<div className="flex items-center justify-between text-sm">
@ -204,16 +188,12 @@ const OperationDefinitions: React.FC = () => {
<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>
<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>
<span className="font-medium">{operation.overheadCost}</span>
</div>
<div className="flex items-center justify-between text-sm border-t pt-2">
@ -221,21 +201,15 @@ const OperationDefinitions: React.FC = () => {
<FaDollarSign className="w-4 h-4 mr-1" />
Toplam Maliyet
</span>
<span className="font-bold text-blue-600">
{getTotalCost(operation)}
</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>
<h4 className="text-sm font-medium text-blue-900 mb-1">Talimatlar</h4>
<p className="text-sm text-blue-700">{operation.instructions}</p>
</div>
)}
@ -244,26 +218,26 @@ const OperationDefinitions: React.FC = () => {
<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"
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}
>
{operation.isActive ? "Aktif" : "Pasif"}
{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"
? 'bg-purple-100 text-purple-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{operation.qualityCheckRequired
? "Kalite Kontrolü Gerekli"
: "Kalite Kontrolü İsteğe Bağlı"}
? 'Kalite Kontrolü Gerekli'
: 'Kalite Kontrolü İsteğe Bağlı'}
</span>
</div>
<span className="text-xs text-gray-400">
{operation.lastModificationTime.toLocaleDateString("tr-TR")}
{operation.lastModificationTime.toLocaleDateString('tr-TR')}
</span>
</div>
</div>
@ -303,31 +277,19 @@ const OperationDefinitions: React.FC = () => {
<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>
<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 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>
<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"}
>
<span className={'px-2 py-1 rounded-full text-xs font-medium'}>
{operation.operationType?.code}
</span>
</td>
@ -357,22 +319,22 @@ const OperationDefinitions: React.FC = () => {
<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"
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}
>
{operation.isActive ? "Aktif" : "Pasif"}
{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"
? 'bg-purple-100 text-purple-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{operation.qualityCheckRequired
? "Kalite Gerekli"
: "Kalite İsteğe Bağlı"}
? 'Kalite Gerekli'
: 'Kalite İsteğe Bağlı'}
</span>
</div>
</td>
@ -399,14 +361,13 @@ const OperationDefinitions: React.FC = () => {
{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>
<h3 className="text-lg font-medium text-gray-900 mb-2">Operasyon bulunamadı</h3>
<p className="text-gray-500">
Arama kriterlerinizi değiştirin veya yeni bir operasyon ekleyin.
</p>
</div>
)}
</div>
{/* Modal for Add/Edit */}
<OperationFormModal
@ -415,8 +376,8 @@ const OperationDefinitions: React.FC = () => {
onSave={handleSave}
onClose={() => setShowModal(false)}
/>
</div>
);
};
</Container>
)
}
export default OperationDefinitions;
export default OperationDefinitions

View file

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

View file

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

View file

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState } from 'react'
import {
FaPlus,
FaSearch,
@ -9,46 +9,46 @@ import {
FaCheckCircle,
FaTh,
FaList,
} from "react-icons/fa";
import { MrpOperationTypeDefinition } from "../../../types/mrp";
import OperationTypeFormModal from "./OperationTypeFormModal";
import { mockOperationTypes } from "../../../mocks/mockOperationTypes";
} from 'react-icons/fa'
import { MrpOperationTypeDefinition } from '../../../types/mrp'
import OperationTypeFormModal from './OperationTypeFormModal'
import { mockOperationTypes } from '../../../mocks/mockOperationTypes'
import {
getOperationCategoryColor,
getOperationTypeColor,
getOperationTypeText,
getSkillLevelText,
} from "../../../utils/erp";
} from '../../../utils/erp'
import { Container } from '@/components/shared'
const OperationTypes: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const [showModal, setShowModal] = useState(false);
const [editingType, setEditingType] =
useState<MrpOperationTypeDefinition | null>(null);
const [viewMode, setViewMode] = useState<"card" | "list">("card");
const [searchTerm, setSearchTerm] = useState('')
const [showModal, setShowModal] = useState(false)
const [editingType, setEditingType] = useState<MrpOperationTypeDefinition | null>(null)
const [viewMode, setViewMode] = useState<'card' | 'list'>('card')
const [operationTypes] =
useState<MrpOperationTypeDefinition[]>(mockOperationTypes);
const [operationTypes] = useState<MrpOperationTypeDefinition[]>(mockOperationTypes)
const filteredTypes = operationTypes.filter(
(type) =>
type.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
type.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
type.category.toLowerCase().includes(searchTerm.toLowerCase())
);
type.category.toLowerCase().includes(searchTerm.toLowerCase()),
)
const handleEdit = (type: MrpOperationTypeDefinition) => {
setEditingType(type);
setShowModal(true);
};
setEditingType(type)
setShowModal(true)
}
const handleAddNew = () => {
setEditingType(null);
setShowModal(true);
};
setEditingType(null)
setShowModal(true)
}
return (
<div className="space-y-3 pt-2">
<Container>
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
@ -61,21 +61,21 @@ const OperationTypes: React.FC = () => {
{/* View Toggle */}
<div className="flex bg-gray-100 rounded-lg p-0.5">
<button
onClick={() => setViewMode("card")}
onClick={() => setViewMode('card')}
className={`px-2 py-1.5 rounded-md flex items-center space-x-2 transition-colors text-sm ${
viewMode === "card"
? "bg-white text-blue-600 shadow-sm"
: "text-gray-600 hover:text-gray-900"
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")}
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"
viewMode === 'list'
? 'bg-white text-blue-600 shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
<FaList className="w-4 h-4" />
@ -104,7 +104,7 @@ const OperationTypes: React.FC = () => {
</div>
{/* Operation Types Display */}
{viewMode === "card" ? (
{viewMode === 'card' ? (
/* Card View */
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filteredTypes.map((type) => (
@ -115,12 +115,10 @@ const OperationTypes: React.FC = () => {
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<div className="flex items-center space-x-2 mb-2">
<h3 className="text-lg font-semibold text-gray-900">
{type.name}
</h3>
<h3 className="text-lg font-semibold text-gray-900">{type.name}</h3>
<span
className={`px-2 py-1 rounded-full text-xs font-medium ${getOperationTypeColor(
type.category
type.category,
)}`}
>
{getOperationTypeText(type.category)}
@ -128,9 +126,7 @@ const OperationTypes: React.FC = () => {
</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>
<p className="text-sm text-gray-500 mb-2">{type.description}</p>
)}
</div>
<div className="flex space-x-1">
@ -169,10 +165,10 @@ const OperationTypes: React.FC = () => {
</span>
<span
className={`font-medium ${
type.requiresSetup ? "text-orange-600" : "text-green-600"
type.requiresSetup ? 'text-orange-600' : 'text-green-600'
}`}
>
{type.requiresSetup ? "Evet" : "Hayır"}
{type.requiresSetup ? 'Evet' : 'Hayır'}
</span>
</div>
@ -180,12 +176,10 @@ const OperationTypes: React.FC = () => {
<span className="text-gray-500">Paralel İşlem</span>
<span
className={`font-medium ${
type.allowsParallelOperation
? "text-green-600"
: "text-gray-600"
type.allowsParallelOperation ? 'text-green-600' : 'text-gray-600'
}`}
>
{type.allowsParallelOperation ? "Evet" : "Hayır"}
{type.allowsParallelOperation ? 'Evet' : 'Hayır'}
</span>
</div>
@ -196,12 +190,10 @@ const OperationTypes: React.FC = () => {
</span>
<span
className={`font-medium ${
type.qualityCheckRequired
? "text-purple-600"
: "text-gray-600"
type.qualityCheckRequired ? 'text-purple-600' : 'text-gray-600'
}`}
>
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"}
{type.qualityCheckRequired ? 'Gerekli' : 'İsteğe Bağlı'}
</span>
</div>
</div>
@ -210,15 +202,13 @@ const OperationTypes: React.FC = () => {
<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 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}
>
{type.isActive ? "Aktif" : "Pasif"}
{type.isActive ? 'Aktif' : 'Pasif'}
</span>
<span className="text-xs text-gray-400">
{type.lastModificationTime.toLocaleDateString("tr-TR")}
{type.lastModificationTime.toLocaleDateString('tr-TR')}
</span>
</div>
</div>
@ -262,21 +252,17 @@ const OperationTypes: React.FC = () => {
<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 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 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,
)}`}
>
{type.category}
@ -295,33 +281,31 @@ const OperationTypes: React.FC = () => {
<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"
? 'bg-orange-100 text-orange-800'
: 'bg-green-100 text-green-800'
}`}
>
{type.requiresSetup ? "Gerekli" : "Gerekli Değil"}
{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"
? 'bg-purple-100 text-purple-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{type.qualityCheckRequired ? "Gerekli" : "İsteğe Bağlı"}
{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 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}
>
{type.isActive ? "Aktif" : "Pasif"}
{type.isActive ? 'Aktif' : 'Pasif'}
</span>
</td>
<td className="px-3 py-2 whitespace-nowrap text-right text-sm font-medium">
@ -347,15 +331,13 @@ const OperationTypes: React.FC = () => {
{filteredTypes.length === 0 && (
<div className="text-center py-12">
<FaCog className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">
Operasyon türü bulunamadı
</h3>
<h3 className="text-lg font-medium text-gray-900 mb-2">Operasyon türü bulunamadı</h3>
<p className="text-gray-500">
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü
ekleyin.
Arama kriterlerinizi değiştirin veya yeni bir operasyon türü ekleyin.
</p>
</div>
)}
</div>
{/* Modal for Add/Edit - Placeholder */}
<OperationTypeFormModal
@ -364,12 +346,12 @@ const OperationTypes: React.FC = () => {
onClose={() => setShowModal(false)}
onSave={(data) => {
// TODO: persist to API; for now just close modal
console.log("Saved operation type:", data);
setShowModal(false);
console.log('Saved operation type:', data)
setShowModal(false)
}}
/>
</div>
);
};
</Container>
)
}
export default OperationTypes;
export default OperationTypes

View file

@ -16,12 +16,13 @@ import {
getProductionOrderStatus,
getWorkOrderStatus,
} from "../../../utils/erp";
import { Container } from "@/components/shared";
interface PlanningGanttProps {
workCenterId?: string;
}
export const PlanningGantt: React.FC<PlanningGanttProps> = ({
const PlanningGantt: React.FC<PlanningGanttProps> = ({
workCenterId,
}) => {
const getInitialExpandedItems = () => {
@ -502,8 +503,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
};
return (
<>
<div className="mb-3 mt-2">
<Container>
<div className="mb-3">
<div className="flex items-center gap-3">
<div>
<h2 className="text-xl font-bold text-gray-900">
@ -618,6 +619,8 @@ export const PlanningGantt: React.FC<PlanningGanttProps> = ({
{filteredData.map((task) => renderTask(task))}
</div>
</div>
</>
</Container>
);
};
export default PlanningGantt;

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,8 @@
import React, { useMemo } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { mockProductionOrders } from "../../../mocks/mockProductionOrders";
import {
MrpProductionOrder,
MrpProductionOrderMaterial,
} from "../../../types/mrp";
import React, { useMemo } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import { mockProductionOrders } from '../../../mocks/mockProductionOrders'
import { MrpProductionOrder, MrpProductionOrderMaterial } from '../../../types/mrp'
import {
FaCog,
FaCalendarAlt,
@ -14,29 +11,28 @@ import {
FaMoneyBillWave,
FaArrowLeft,
FaExclamationTriangle,
} from "react-icons/fa";
import StatusBadge from "../../../components/common/StatusBadge";
import { getPriorityColor } from "../../../utils/erp";
} from 'react-icons/fa'
import StatusBadge from '../../../components/common/StatusBadge'
import { getPriorityColor } from '../../../utils/erp'
import { Container } from '@/components/shared'
const ProductionOrderView: React.FC = () => {
const { id } = useParams();
const navigate = useNavigate();
const { id } = useParams()
const navigate = useNavigate()
const { data: productionOrder, isLoading } = useQuery({
queryKey: ["production-order", id],
queryKey: ['production-order', id],
queryFn: async () => {
await new Promise((r) => setTimeout(r, 200));
return mockProductionOrders.find((p) => p.id === id) as
| MrpProductionOrder
| undefined;
await new Promise((r) => setTimeout(r, 200))
return mockProductionOrders.find((p) => p.id === id) as MrpProductionOrder | undefined
},
enabled: !!id,
});
})
const materials: MrpProductionOrderMaterial[] = useMemo(
() => productionOrder?.materials || [],
[productionOrder]
);
[productionOrder],
)
if (isLoading) {
return (
@ -46,7 +42,7 @@ const ProductionOrderView: React.FC = () => {
<p className="text-gray-600 font-medium">Üretim emri yükleniyor...</p>
</div>
</div>
);
)
}
if (!productionOrder) {
@ -56,14 +52,12 @@ const ProductionOrderView: React.FC = () => {
<div className="mb-6">
<FaExclamationTriangle className="mx-auto h-16 w-16 text-gray-400" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">
Üretim Emri Bulunamadı
</h2>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Üretim Emri Bulunamadı</h2>
<p className="text-gray-600 mb-6">
İstenen üretim emri mevcut değil veya silinmiş olabilir.
</p>
<button
onClick={() => navigate("/admin/mrp/production-orders")}
onClick={() => navigate('/admin/mrp/production-orders')}
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white font-medium rounded-lg hover:from-blue-700 hover:to-blue-800 transition-all duration-200 shadow-lg hover:shadow-xl"
>
<FaArrowLeft className="mr-2" />
@ -71,34 +65,30 @@ const ProductionOrderView: React.FC = () => {
</button>
</div>
</div>
);
)
}
const getProgressPercentage = () => {
if (productionOrder.plannedQuantity === 0) return 0;
return Math.round(
(productionOrder.confirmedQuantity / productionOrder.plannedQuantity) *
100
);
};
if (productionOrder.plannedQuantity === 0) return 0
return Math.round((productionOrder.confirmedQuantity / productionOrder.plannedQuantity) * 100)
}
return (
<div className="mx-auto mt-2">
<Container>
<div className="space-y-2">
{/* Header */}
<div className="mb-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<button
onClick={() => navigate("/admin/mrp/production-orders")}
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>
<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>
@ -114,15 +104,11 @@ const ProductionOrderView: React.FC = () => {
<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 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>
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">Durum</div>
<StatusBadge status={productionOrder.status} />
</div>
</div>
@ -134,7 +120,7 @@ const ProductionOrderView: React.FC = () => {
</div>
<span
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
productionOrder.priority
productionOrder.priority,
)}`}
>
{productionOrder.priority}
@ -154,21 +140,16 @@ const ProductionOrderView: React.FC = () => {
<div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
İlerleme
</div>
<div className="text-xl font-bold text-orange-900">
{getProgressPercentage()}%
</div>
<div className="text-xl font-bold text-orange-900">{getProgressPercentage()}%</div>
</div>
</div>
{/* Progress Bar */}
<div className="mb-6">
<div className="flex items-center justify-between mb-3">
<div className="text-sm font-medium text-gray-700">
Üretim İlerlemesi
</div>
<div className="text-sm font-medium text-gray-700">Üretim İlerlemesi</div>
<div className="text-sm text-gray-500">
{productionOrder.confirmedQuantity} /{" "}
{productionOrder.plannedQuantity} adet
{productionOrder.confirmedQuantity} / {productionOrder.plannedQuantity} adet
</div>
</div>
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
@ -187,9 +168,7 @@ const ProductionOrderView: React.FC = () => {
Müşteri Talebi
</div>
</div>
<p className="text-blue-700 leading-relaxed">
{productionOrder.customerRequirement}
</p>
<p className="text-blue-700 leading-relaxed">{productionOrder.customerRequirement}</p>
</div>
)}
</div>
@ -202,16 +181,12 @@ const ProductionOrderView: React.FC = () => {
<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>
<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>
<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>
@ -221,15 +196,11 @@ const ProductionOrderView: React.FC = () => {
</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>
<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>
<span className="font-bold text-red-600">{productionOrder.scrapQuantity}</span>
</div>
</div>
</div>
@ -248,15 +219,12 @@ const ProductionOrderView: React.FC = () => {
Plan Başlangıç
</div>
<div className="font-semibold text-gray-900">
{new Date(productionOrder.plannedStartDate).toLocaleDateString(
"tr-TR",
{
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
}
)}
{new Date(productionOrder.plannedStartDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
<div className="py-1.5 border-b border-gray-100">
@ -264,15 +232,12 @@ const ProductionOrderView: React.FC = () => {
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",
}
)}
{new Date(productionOrder.plannedEndDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
{productionOrder.actualStartDate && (
@ -281,15 +246,12 @@ const ProductionOrderView: React.FC = () => {
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",
}
)}
{new Date(productionOrder.actualStartDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
)}
@ -299,15 +261,12 @@ const ProductionOrderView: React.FC = () => {
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",
}
)}
{new Date(productionOrder.actualEndDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
)}
@ -320,9 +279,7 @@ const ProductionOrderView: React.FC = () => {
<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>
<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">
@ -330,8 +287,8 @@ const ProductionOrderView: React.FC = () => {
Planlanan Maliyet
</div>
<div className="text-lg font-bold text-gray-900">
{new Intl.NumberFormat("tr-TR", {
style: "currency",
{new Intl.NumberFormat('tr-TR', {
style: 'currency',
currency: productionOrder.currency,
}).format(productionOrder.plannedCost)}
</div>
@ -341,8 +298,8 @@ const ProductionOrderView: React.FC = () => {
Gerçekleşen Maliyet
</div>
<div className="text-lg font-bold text-green-600">
{new Intl.NumberFormat("tr-TR", {
style: "currency",
{new Intl.NumberFormat('tr-TR', {
style: 'currency',
currency: productionOrder.currency,
}).format(productionOrder.actualCost)}
</div>
@ -351,9 +308,7 @@ const ProductionOrderView: React.FC = () => {
<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 className="font-semibold text-gray-900">{productionOrder.currency}</div>
</div>
</div>
</div>
@ -364,9 +319,7 @@ const ProductionOrderView: React.FC = () => {
<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>
<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">
@ -374,10 +327,11 @@ const ProductionOrderView: React.FC = () => {
Oluşturulma
</div>
<div className="font-semibold text-gray-900">
{new Date(productionOrder.creationTime).toLocaleDateString(
"tr-TR",
{ year: "numeric", month: "short", day: "numeric" }
)}
{new Date(productionOrder.creationTime).toLocaleDateString('tr-TR', {
year: 'numeric',
month: 'short',
day: 'numeric',
})}
</div>
</div>
<div className="py-1.5 border-b border-gray-100">
@ -385,12 +339,10 @@ const ProductionOrderView: React.FC = () => {
Son Güncelleme
</div>
<div className="font-semibold text-gray-900">
{new Date(
productionOrder.lastModificationTime
).toLocaleDateString("tr-TR", {
year: "numeric",
month: "short",
day: "numeric",
{new Date(productionOrder.lastModificationTime).toLocaleDateString('tr-TR', {
year: 'numeric',
month: 'short',
day: 'numeric',
})}
</div>
</div>
@ -407,15 +359,11 @@ const ProductionOrderView: React.FC = () => {
</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 className="text-gray-600 mt-1 text-sm">Üretim emrine bağlı malzeme satırları</p>
</div>
</div>
<div className="text-right">
<div className="text-lg font-bold text-indigo-600">
{materials.length}
</div>
<div className="text-lg font-bold text-indigo-600">{materials.length}</div>
<div className="text-sm text-gray-500">Malzeme</div>
</div>
</div>
@ -445,8 +393,8 @@ const ProductionOrderView: React.FC = () => {
</div>
<div className="font-bold text-gray-900 text-base">
{m.material?.code || m.materialId}
{" - "}
{m.material?.name || "Malzeme adı bulunamadı"}
{' - '}
{m.material?.name || 'Malzeme adı bulunamadı'}
</div>
</div>
</div>
@ -462,9 +410,7 @@ const ProductionOrderView: React.FC = () => {
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Planlanan
</div>
<div className="font-bold text-gray-900 text-base">
{m.plannedQuantity}
</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">
@ -480,18 +426,14 @@ const ProductionOrderView: React.FC = () => {
<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 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 className="font-bold text-red-700 text-base">{m.scrapQuantity}</div>
</div>
</div>
</div>
@ -500,7 +442,8 @@ const ProductionOrderView: React.FC = () => {
)}
</div>
</div>
);
};
</Container>
)
}
export default ProductionOrderView;
export default ProductionOrderView

View file

@ -1,100 +1,88 @@
import React, { useState } from "react";
import { FaBox, FaIndustry, FaShoppingCart } from "react-icons/fa";
import React, { useState } from 'react'
import { FaBox, FaIndustry, FaShoppingCart } from 'react-icons/fa'
import {
MrpMaterialRequirement,
MrpProductionSuggestion,
MrpPurchaseSuggestion,
} from "../../../types/mrp";
import RunMrpModal from "./RunMrpModal";
import ProductionSuggestions from "./ProductionSuggestions";
import PurchaseSuggestions from "./PurchaseSuggestions";
import MaterialRequirements from "./MaterialRequirements";
} from '../../../types/mrp'
import RunMrpModal from './RunMrpModal'
import ProductionSuggestions from './ProductionSuggestions'
import PurchaseSuggestions from './PurchaseSuggestions'
import MaterialRequirements from './MaterialRequirements'
import {
mockMaterialRequirements,
mockProductionSuggestions,
mockPurchaseSuggestions,
} from "../../../mocks/mockMRP";
import Widget from "../../../components/common/Widget";
} from '../../../mocks/mockMRP'
import Widget from '../../../components/common/Widget'
import { Container } from '@/components/shared'
const Requirements: React.FC = () => {
const [materialRequirements, setMaterialRequirements] = useState<
MrpMaterialRequirement[]
>([]);
const [purchaseSuggestions, setPurchaseSuggestions] = useState<
MrpPurchaseSuggestion[]
>([]);
const [productionSuggestions, setProductionSuggestions] = useState<
MrpProductionSuggestion[]
>([]);
const [materialRequirements, setMaterialRequirements] = useState<MrpMaterialRequirement[]>([])
const [purchaseSuggestions, setPurchaseSuggestions] = useState<MrpPurchaseSuggestion[]>([])
const [productionSuggestions, setProductionSuggestions] = useState<MrpProductionSuggestion[]>([])
const runMrp = () => {
// This is where the actual MRP logic would run.
// For now, we'll just populate the state with mock data.
setMaterialRequirements(mockMaterialRequirements);
setPurchaseSuggestions(mockPurchaseSuggestions);
setProductionSuggestions(mockProductionSuggestions);
};
setMaterialRequirements(mockMaterialRequirements)
setPurchaseSuggestions(mockPurchaseSuggestions)
setProductionSuggestions(mockProductionSuggestions)
}
const [isMrpModalOpen, setIsMrpModalOpen] = useState(false);
const [activeTab, setActiveTab] = useState<
"requirements" | "production" | "purchase"
>("requirements");
const [isMrpModalOpen, setIsMrpModalOpen] = useState(false)
const [activeTab, setActiveTab] = useState<'requirements' | 'production' | 'purchase'>(
'requirements',
)
const getUrgencyLevel = (requirement: MrpMaterialRequirement) => {
const today = new Date();
const reqDate = new Date(requirement.requirementDate);
const today = new Date()
const reqDate = new Date(requirement.requirementDate)
const daysUntilRequired = Math.ceil(
(reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)
);
(reqDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24),
)
if (daysUntilRequired < 0)
return {
level: "overdue",
color: "bg-red-100 text-red-800",
label: "Gecikmiş",
};
level: 'overdue',
color: 'bg-red-100 text-red-800',
label: 'Gecikmiş',
}
if (daysUntilRequired <= 7)
return {
level: "urgent",
color: "bg-orange-100 text-orange-800",
label: "Acil",
};
level: 'urgent',
color: 'bg-orange-100 text-orange-800',
label: 'Acil',
}
if (daysUntilRequired <= 30)
return {
level: "soon",
color: "bg-yellow-100 text-yellow-800",
label: "Yakın",
};
level: 'soon',
color: 'bg-yellow-100 text-yellow-800',
label: 'Yakın',
}
return {
level: "normal",
color: "bg-green-100 text-green-800",
label: "Normal",
};
};
level: 'normal',
color: 'bg-green-100 text-green-800',
label: 'Normal',
}
}
// Calculate statistics
const totalRequirements = materialRequirements.length;
const shortageRequirements = materialRequirements.filter(
(r) => r.netRequirement > 0
).length;
const totalRequirements = materialRequirements.length
const shortageRequirements = materialRequirements.filter((r) => r.netRequirement > 0).length
const urgentRequirements = materialRequirements.filter(
(r) => getUrgencyLevel(r).level === "urgent"
).length;
const openProductionSuggestions = productionSuggestions.filter(
(s) => s.status === "OPEN"
).length;
const openPurchaseSuggestions = purchaseSuggestions.filter(
(s) => s.status === "OPEN"
).length;
(r) => getUrgencyLevel(r).level === 'urgent',
).length
const openProductionSuggestions = productionSuggestions.filter((s) => s.status === 'OPEN').length
const openPurchaseSuggestions = purchaseSuggestions.filter((s) => s.status === 'OPEN').length
return (
<div className="space-y-3 pt-2">
<Container>
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-bold text-gray-900">
Malzeme İhtiyaçları
</h2>
<h2 className="text-xl font-bold text-gray-900">Malzeme İhtiyaçları</h2>
<p className="text-sm text-gray-600">
Malzeme ihtiyaç hesaplama ve satın alma önerileri
</p>
@ -110,19 +98,9 @@ const Requirements: React.FC = () => {
{/* 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="Toplam İhtiyaç" value={totalRequirements} color="blue" icon="FaBox" />
<Widget
title="Eksik Stok"
value={shortageRequirements}
color="red"
icon="FaArrowDown"
/>
<Widget title="Eksik Stok" value={shortageRequirements} color="red" icon="FaArrowDown" />
<Widget
title="Acil İhtiyaç"
@ -142,11 +120,11 @@ const Requirements: React.FC = () => {
{/* Tabs */}
<div className="flex bg-gray-100 rounded-lg p-1">
<button
onClick={() => setActiveTab("requirements")}
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"
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">
@ -155,11 +133,11 @@ const Requirements: React.FC = () => {
</div>
</button>
<button
onClick={() => setActiveTab("production")}
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"
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">
@ -168,11 +146,11 @@ const Requirements: React.FC = () => {
</div>
</button>
<button
onClick={() => setActiveTab("purchase")}
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"
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">
@ -183,27 +161,28 @@ const Requirements: React.FC = () => {
</div>
{/* Data Table */}
{activeTab === "requirements" && (
{activeTab === 'requirements' && (
<MaterialRequirements materialRequirements={materialRequirements} />
)}
{activeTab === "production" && (
{activeTab === 'production' && (
<ProductionSuggestions productionSuggestions={productionSuggestions} />
)}
{activeTab === "purchase" && (
{activeTab === 'purchase' && (
<PurchaseSuggestions purchaseSuggestions={purchaseSuggestions} />
)}
</div>
<RunMrpModal
isOpen={isMrpModalOpen}
onClose={() => setIsMrpModalOpen(false)}
onRunComplete={() => {
runMrp();
setIsMrpModalOpen(false);
runMrp()
setIsMrpModalOpen(false)
}}
/>
</div>
);
};
</Container>
)
}
export default Requirements;
export default Requirements

View file

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

View file

@ -1,44 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const ProjectManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (pathname === "/admin/projects" || pathname === "/admin/projects/") return "Proje Listesi";
if (pathname === "/admin/projects/tasks") return "Görev Yönetimi";
if (pathname === "/admin/projects/phases") return "Proje Aşamaları";
if (pathname === "/admin/projects/activities") return "Aktivite Türleri";
if (pathname === "/admin/projects/cost-tracking") return "Maliyet Zaman Takibi";
if (pathname === "/admin/projects/workload") return "İş Yükü Gantt";
if (pathname === "/admin/projects/daily-updates") return "Görev Günlük Güncellemeler";
if (pathname === "/admin/projects/new") return "Yeni Proje";
if (pathname.includes("/admin/projects/edit/")) return "Proje Düzenle";
if (pathname.includes("/admin/projects/") && !pathname.includes("edit") && !pathname.includes("new")) return "Proje Detayları";
return "Proje Yönetimi";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / Proje Yönetimi / Sayfanın Adı
const breadcrumbs = [
{ name: "Proje Yönetimi", href: "/admin/projects" }
];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader
title={pageTitle}
breadcrumbs={breadcrumbs}
/>
<div>
<Outlet />
</div>
</div>
);
};
export default ProjectManagement;

View file

@ -1,55 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const SupplyChainManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (pathname === "/admin/supplychain" || pathname === "/admin/supplychain/suppliers")
return "Tedarikçiler";
if (pathname === "/admin/supplychain/materials/groups") return "Malzeme Grupları";
if (pathname === "/admin/supplychain/materials/types") return "Malzeme Tipleri";
if (pathname === "/admin/supplychain/materials") return "Malzeme Listesi";
if (pathname === "/admin/supplychain/requests") return "Satınalma Talepleri";
if (pathname === "/admin/supplychain/requests/new")
return "Yeni Satınalma Talebi";
if (pathname.includes("/admin/supplychain/requests/edit/"))
return "Satınalma Talebi Düzenle";
if (pathname.includes("/admin/supplychain/requests/view/"))
return "Satınalma Talebi Detayları";
if (pathname === "/admin/supplychain/quotations") return "Teklif Yönetimi";
if (pathname === "/admin/supplychain/quotations/new") return "Yeni Teklif";
if (pathname.includes("/admin/supplychain/quotations/edit/"))
return "Teklif Düzenle";
if (pathname.includes("/admin/supplychain/quotations/view/"))
return "Teklif Detayları";
if (pathname === "/admin/supplychain/approvals") return "Onay İş Akışları";
if (pathname === "/admin/supplychain/orders") return "Sipariş Yönetimi";
if (pathname === "/admin/supplychain/orders/new") return "Yeni Sipariş";
if (pathname.includes("/admin/supplychain/orders/edit/"))
return "Sipariş Düzenle";
if (pathname.includes("/admin/supplychain/orders/view/"))
return "Sipariş Detayları";
if (pathname === "/admin/supplychain/delivery") return "Teslimat Takibi";
return "Satınalma Yönetimi";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / Satınalma Yönetimi / Sayfanın Adı
const breadcrumbs = [{ name: "Satınalma Yönetimi", href: "/admin/supplychain" }];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader title={pageTitle} breadcrumbs={breadcrumbs} />
<div>
<Outlet />
</div>
</div>
);
};
export default SupplyChainManagement;

View file

@ -1,43 +0,0 @@
import React from "react";
import { Outlet, useLocation } from "react-router-dom";
import ModuleHeader from "../../components/common/ModuleHeader";
const WarehouseManagement: React.FC = () => {
const location = useLocation();
// Define page mappings for breadcrumbs
const getPageTitle = (pathname: string) => {
if (
pathname === "/admin/warehouse" ||
pathname === "/admin/warehouse/" ||
pathname === "/admin/warehouse/definitions"
)
return "Depo Tanımları";
if (pathname === "/admin/warehouse/tracking") return "Lokasyon Takibi";
if (pathname === "/admin/warehouse/putaway") return "Yerleştirme Kuralları";
if (pathname === "/admin/warehouse/inventory") return "Stok Durumu";
if (pathname === "/admin/warehouse/receipt") return "Stok Giriş";
if (pathname === "/admin/warehouse/issue") return "Stok Çıkış";
if (pathname === "/admin/warehouse/transfer") return "Stok Transfer";
if (pathname === "/admin/warehouse/movements") return "Stok Hareketleri";
if (pathname === "/admin/warehouse/stocklevel") return "Envanter Takibi";
return "Depo Yönetimi";
};
const pageTitle = getPageTitle(location.pathname);
// Create breadcrumbs: Anasayfa / Depo Yönetimi / Sayfanın Adı
const breadcrumbs = [{ name: "Depo Yönetimi", href: "/admin/warehouse" }];
return (
<div className="min-h-screen bg-gray-50">
<ModuleHeader title={pageTitle} breadcrumbs={breadcrumbs} />
<div>
<Outlet />
</div>
</div>
);
};
export default WarehouseManagement;