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) => (
+
+ }
+ onClick={() => handleViewSparepart(record)}
+ style={{
+ color: '#1890ff',
+ borderColor: '#1890ff',
+ }}
+ size="small"
+ />
+ }
+ onClick={() => handleEditSparepart(record)}
+ style={{
+ color: '#faad14',
+ borderColor: '#faad14',
+ }}
+ size="small"
+ />
+ }
+ onClick={() => handleDeleteSparepart(record)}
+ style={{
+ borderColor: '#ff4d4f',
+ }}
+ size="small"
+ />
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+ ✕,
+ }}
+ enterButton={
+ }
+ style={{
+ backgroundColor: '#23A55A',
+ borderColor: '#23A55A',
+ }}
+ >
+ Search
+
+ }
+ size="large"
+ />
+
+
+
+
+ }
+ onClick={handleAddSparepart}
+ size="large"
+ >
+ Add Sparepart
+
+
+
+
+
+
+
+ {loading ? (
+
+
+
+ ) : (
+ `${range[0]}-${range[1]} of ${total} items`,
+ }}
+ scroll={{ x: 1000 }}
+ size="middle"
+ />
+ )}
+
+
+
+
+ );
+});
+
+export default IndexSparepart;
\ No newline at end of file