From 899695f54823e2002c3511e87cfdea8cd97d4a5e Mon Sep 17 00:00:00 2001 From: vinix Date: Mon, 24 Nov 2025 12:17:31 +0700 Subject: [PATCH] feat: add sparepart management page and update routing in App component --- src/App.jsx | 38 +- src/layout/LayoutMenu.jsx | 7 + src/pages/master/sparepart/IndexSparepart.jsx | 379 ++++++++++++++++++ 3 files changed, 413 insertions(+), 11 deletions(-) create mode 100644 src/pages/master/sparepart/IndexSparepart.jsx diff --git a/src/App.jsx b/src/App.jsx index 099dd71..8493d06 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,13 +10,15 @@ import Home from './pages/home/Home'; import Blank from './pages/blank/Blank'; // Master -import IndexDevice from './pages/master/device/IndexDevice'; -import IndexTag from './pages/master/tag/IndexTag'; -import IndexUnit from './pages/master/unit/IndexUnit'; +import IndexPlantSubSection from './pages/master/plantSubSection/IndexPlantSubSection'; import IndexBrandDevice from './pages/master/brandDevice/IndexBrandDevice'; +import IndexDevice from './pages/master/device/IndexDevice'; +import IndexUnit from './pages/master/unit/IndexUnit'; +import IndexTag from './pages/master/tag/IndexTag'; import IndexStatus from './pages/master/status/IndexStatus'; +import IndexSparepart from './pages/master/sparepart/IndexSparepart'; import IndexShift from './pages/master/shift/IndexShift'; -// Brand device +// Brand device import AddBrandDevice from './pages/master/brandDevice/AddBrandDevice'; import EditBrandDevice from './pages/master/brandDevice/EditBrandDevice'; import ViewBrandDevice from './pages/master/brandDevice/ViewBrandDevice'; @@ -48,7 +50,6 @@ import SvgAirDryerB from './pages/home/SvgAirDryerB'; import SvgAirDryerC from './pages/home/SvgAirDryerC'; import IndexHistoryAlarm from './pages/history/alarm/IndexHistoryAlarm'; import IndexHistoryEvent from './pages/history/event/IndexHistoryEvent'; -import IndexPlantSubSection from './pages/master/plantSubSection/IndexPlantSubSection'; const App = () => { return ( @@ -59,9 +60,14 @@ const App = () => { } /> } /> } /> - } /> - } /> - + } + /> + } + /> {/* Protected Routes */} }> @@ -84,13 +90,23 @@ const App = () => { } /> } /> } /> + } /> } /> } /> } /> } /> - } /> - } /> - } /> + } + /> + } + /> + } + /> } /> } /> } /> diff --git a/src/layout/LayoutMenu.jsx b/src/layout/LayoutMenu.jsx index a0dfd5a..b475b86 100644 --- a/src/layout/LayoutMenu.jsx +++ b/src/layout/LayoutMenu.jsx @@ -32,6 +32,7 @@ import { SlidersOutlined, SnippetsOutlined, ContactsOutlined, + ToolOutlined, } from '@ant-design/icons'; const { Text } = Typography; @@ -142,6 +143,11 @@ const allItems = [ icon: , label: Status, }, + { + key: 'master-sparepart', + icon: , + label: sparepart, + }, // { // key: 'master-shift', // icon: , @@ -259,6 +265,7 @@ const LayoutMenu = () => { unit: 'master-unit', tag: 'master-tag', status: 'master-status', + sparepart: 'master-sparepart', shift: 'master-shift', }; return masterKeyMap[subPath] || `master-${subPath}`; diff --git a/src/pages/master/sparepart/IndexSparepart.jsx b/src/pages/master/sparepart/IndexSparepart.jsx new file mode 100644 index 0000000..c6e55fe --- /dev/null +++ b/src/pages/master/sparepart/IndexSparepart.jsx @@ -0,0 +1,379 @@ +import React, { memo, useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Button, Col, Row, Space, Input, ConfigProvider, Card, Tag, Spin, Table, Modal } from 'antd'; +import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, EyeOutlined } from '@ant-design/icons'; +import { NotifAlert, NotifConfirmDialog, NotifOk } from '../../../components/Global/ToastNotif'; +import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; +import { Typography } from 'antd'; + +const { Text, Title } = Typography; + +// Mock data untuk sparepart +const mockSpareparts = [ + { + sparepart_id: 1, + sparepart_name: 'Compressor Oil', + sparepart_code: 'SP-COMP-001', + sparepart_description: 'Oil for industrial compressor maintenance', + brand_name: 'Sullair', + model: '750H', + type: 'consumable', + is_active: true, + created_date: '2024-01-15', + updated_date: '2024-11-20' + }, + { + sparepart_id: 2, + sparepart_name: 'Air Filter Element', + sparepart_code: 'SP-AF-002', + sparepart_description: 'High efficiency air filter for compressor', + brand_name: 'Ingersoll Rand', + model: 'X7', + type: 'filter', + is_active: true, + created_date: '2024-02-20', + updated_date: '2024-10-15' + }, + { + sparepart_id: 3, + sparepart_name: 'Oil Filter', + sparepart_code: 'SP-OF-003', + sparepart_description: 'Oil filtration system component', + brand_name: 'Atlas Copco', + model: 'GA 55', + type: 'filter', + is_active: false, + created_date: '2024-03-10', + updated_date: '2024-09-05' + }, + { + sparepart_id: 4, + sparepart_name: 'Cooling Fan Motor', + sparepart_code: 'SP-CFM-004', + sparepart_description: 'Motor for cooling system in compressor', + brand_name: 'Kobelco', + model: 'SRE-110', + type: 'electrical', + is_active: true, + created_date: '2024-04-05', + updated_date: '2024-11-10' + }, + { + sparepart_id: 5, + sparepart_name: 'Coupling Elastomer', + sparepart_code: 'SP-CE-005', + sparepart_description: 'Elastomer coupling for drive system', + brand_name: 'Kaesar', + model: 'Comprex', + type: 'mechanical', + is_active: true, + created_date: '2024-05-12', + updated_date: '2024-11-18' + } +]; + +const IndexSparepart = memo(function IndexSparepart() { + const navigate = useNavigate(); + const { setBreadcrumbItems } = useBreadcrumb(); + const [spareparts, setSpareparts] = useState([]); + const [filteredSpareparts, setFilteredSpareparts] = useState([]); + const [loading, setLoading] = useState(true); + const [searchValue, setSearchValue] = useState(''); + + useEffect(() => { + const token = localStorage.getItem('token'); + if (token) { + // Simulasi loading data + setTimeout(() => { + setSpareparts(mockSpareparts); + setFilteredSpareparts(mockSpareparts); + setLoading(false); + }, 1000); + + setBreadcrumbItems([ + { title: • Master }, + { title: Sparepart } + ]); + } else { + navigate('/signin'); + } + }, [navigate, setBreadcrumbItems]); + + // Fungsi untuk pencarian + const handleSearch = (value) => { + if (!value) { + setFilteredSpareparts(spareparts); + } else { + const filtered = spareparts.filter(item => + item.sparepart_name.toLowerCase().includes(value.toLowerCase()) || + item.sparepart_code.toLowerCase().includes(value.toLowerCase()) || + item.brand_name.toLowerCase().includes(value.toLowerCase()) || + item.model.toLowerCase().includes(value.toLowerCase()) + ); + setFilteredSpareparts(filtered); + } + }; + + const handleSearchChange = (e) => { + const value = e.target.value; + setSearchValue(value); + handleSearch(value); + }; + + const handleSearchClear = () => { + setSearchValue(''); + setFilteredSpareparts(spareparts); + }; + + const handleAddSparepart = () => { + // Navigasi ke halaman tambah sparepart (akan dibuat nanti) + NotifAlert({ + icon: 'info', + title: 'Info', + message: 'Fitur tambah sparepart akan segera dibuat', + }); + }; + + const handleEditSparepart = (record) => { + // Navigasi ke halaman edit sparepart (akan dibuat nanti) + NotifAlert({ + icon: 'info', + title: 'Info', + message: `Edit sparepart: ${record.sparepart_name}`, + }); + }; + + const handleViewSparepart = (record) => { + // Tampilkan detail sparepart (akan dibuat nanti) + NotifOk({ + icon: 'success', + title: 'Info', + message: `View sparepart: ${record.sparepart_name}`, + }); + }; + + const handleDeleteSparepart = (record) => { + NotifConfirmDialog({ + icon: 'question', + title: 'Konfirmasi', + message: 'Apakah anda yakin hapus sparepart "' + record.sparepart_name + '" ?', + onConfirm: () => { + const updatedSpareparts = spareparts.filter(item => item.sparepart_id !== record.sparepart_id); + setSpareparts(updatedSpareparts); + setFilteredSpareparts(updatedSpareparts); + NotifOk({ + icon: 'success', + title: 'Berhasil', + message: `Sparepart ${record.sparepart_name} deleted successfully.`, + }); + }, + onCancel: () => {}, + }); + }; + + // Kolom-kolom tabel + const columns = [ + { + title: 'No', + key: 'no', + width: '5%', + align: 'center', + render: (_, __, index) => index + 1, + }, + { + title: 'Sparepart Code', + dataIndex: 'sparepart_code', + key: 'sparepart_code', + width: '15%', + }, + { + title: 'Sparepart Name', + dataIndex: 'sparepart_name', + key: 'sparepart_name', + width: '20%', + }, + { + title: 'Description', + dataIndex: 'sparepart_description', + key: 'sparepart_description', + width: '20%', + render: (text) => text || '-', + }, + { + title: 'Brand', + dataIndex: 'brand_name', + key: 'brand_name', + width: '12%', + }, + { + title: 'Model', + dataIndex: 'model', + key: 'model', + width: '10%', + }, + { + title: 'Type', + dataIndex: 'type', + key: 'type', + width: '10%', + render: (text) => ( + + {text?.toUpperCase()} + + ), + }, + { + title: 'Status', + dataIndex: 'is_active', + key: 'is_active', + width: '8%', + align: 'center', + render: (_, { is_active }) => ( + <> + {is_active === true ? ( + + Active + + ) : ( + + Inactive + + )} + + ), + }, + { + title: 'Action', + key: 'action', + align: 'center', + width: '15%', + render: (_, record) => ( + + + } + size="large" + /> + + + + + + + + + + + + {loading ? ( +
+ +
+ ) : ( + `${range[0]}-${range[1]} of ${total} items`, + }} + scroll={{ x: 1000 }} + size="middle" + /> + )} + + + + + ); +}); + +export default IndexSparepart; \ No newline at end of file