diff --git a/src/pages/master/status/IndexStatus.jsx b/src/pages/master/status/IndexStatus.jsx
index 86e1421..61cc608 100644
--- a/src/pages/master/status/IndexStatus.jsx
+++ b/src/pages/master/status/IndexStatus.jsx
@@ -5,87 +5,23 @@ import { Typography } from 'antd';
import ListStatus from './component/ListStatus';
import DetailStatus from './component/DetailStatus';
-import { NotifConfirmDialog, NotifAlert } from '../../../components/Global/ToastNotif';
-
const { Text } = Typography;
-// Mock Data
-const initialData = [
- {
- key: '3',
- statusCode: 3,
- statusName: 'Done',
- description: 'Indicates that the process is complete.',
- },
- {
- key: '1',
- statusCode: 1,
- statusName: 'Warning',
- description: 'Indicates a warning condition.',
- },
- {
- key: '2',
- statusCode: 2,
- statusName: 'Alarm',
- description: 'Indicates an alarm condition.',
- },
- {
- key: '4',
- statusCode: 4,
- statusName: 'Critical',
- description: 'Indicates a critical condition.',
- },
-];
-
const IndexStatus = memo(function IndexStatus() {
const navigate = useNavigate();
const { setBreadcrumbItems } = useBreadcrumb();
- const [data, setData] = useState(initialData);
const [actionMode, setActionMode] = useState('list');
const [selectedData, setSelectedData] = useState(null);
const [isModalVisible, setIsModalVisible] = useState(false);
const [readOnly, setReadOnly] = useState(false);
- // Mock API function
- const getAllStatus = async (params) => {
- const { page = 1, limit = 10, search = '' } = Object.fromEntries(params.entries());
-
- let filteredData = data;
- if (search) {
- filteredData = data.filter(item =>
- item.statusName.toLowerCase().includes(search.toLowerCase())
- );
- }
-
- const start = (page - 1) * limit;
- const end = start + limit;
- const paginatedData = filteredData.slice(start, end);
-
- return new Promise(resolve => {
- setTimeout(() => {
- resolve({
- status: 200,
- data: {
- data: paginatedData,
- total: filteredData.length,
- paging: {
- page: parseInt(page),
- limit: parseInt(limit),
- total: filteredData.length,
- },
- },
- });
- }, 500);
- });
- };
-
useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
setBreadcrumbItems([
- { title: • Master },
- { title: Status }
+ { title: • Master },
+ { title: Status }
]);
} else {
navigate('/signin');
@@ -101,67 +37,24 @@ const IndexStatus = memo(function IndexStatus() {
}
}, [actionMode]);
- const handleDataSaved = (values) => {
- let newData = [...data];
- if (values.key) { // Editing
- const index = newData.findIndex((item) => values.key === item.key);
- if (index > -1) {
- newData.splice(index, 1, values);
- }
- } else { // Adding
- const newKey = (Math.max(...data.map(item => parseInt(item.key))) + 1).toString();
- newData = [{ key: newKey, ...values }, ...newData];
- }
- setData(newData);
- };
-
- const handleEdit = (record) => {
- setSelectedData(record);
- setActionMode('edit');
- };
-
- const handlePreview = (record) => {
- setSelectedData(record);
- setActionMode('preview');
- };
-
- const handleDelete = (record) => {
- NotifConfirmDialog({
- icon: 'question',
- title: 'Konfirmasi',
- message: `Apakah anda yakin ingin menghapus status "${record.statusName}"?`,
- onConfirm: () => {
- const newData = data.filter((item) => item.key !== record.key);
- setData(newData);
- NotifAlert({
- icon: 'success',
- title: 'Berhasil',
- message: `Status "${record.statusName}" berhasil dihapus.`,
- });
- },
- });
- };
-
return (
-
+ {actionMode === 'list' &&
+
+ }
);
});
-export default IndexStatus;
\ No newline at end of file
+export default IndexStatus;
diff --git a/src/pages/master/status/component/DetailStatus.jsx b/src/pages/master/status/component/DetailStatus.jsx
index a5a8274..85d3410 100644
--- a/src/pages/master/status/component/DetailStatus.jsx
+++ b/src/pages/master/status/component/DetailStatus.jsx
@@ -1,66 +1,113 @@
import React, { useEffect, useState } from 'react';
-import { Modal, Input, Divider, Typography, Button, ConfigProvider, InputNumber, Form } from 'antd';
+import { Modal, Input, Divider, Typography, Button, ConfigProvider, InputNumber, Switch } from 'antd';
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
+import { validateRun } from '../../../../Utils/validate';
+import { createStatus, updateStatus } from '../../../../api/master-status';
const { Text } = Typography;
const { TextArea } = Input;
const DetailStatus = (props) => {
- const [form] = Form.useForm();
const [confirmLoading, setConfirmLoading] = useState(false);
const defaultData = {
- key: '',
- statusCode: '',
- statusName: '',
- description: '',
+ status_id: '',
+ status_number: null,
+ status_name: '',
+ status_color: '',
+ status_description: '',
+ is_active: true,
};
- const [FormData, setFormData] = useState(defaultData);
+ const [formData, setFormData] = useState(defaultData);
+
+ const handleInputChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({ ...formData, [name]: value });
+ };
+
+ const handleInputNumberChange = (value) => {
+ setFormData({ ...formData, status_number: value });
+ };
+
+ const handleStatusToggle = (checked) => {
+ setFormData({ ...formData, is_active: checked });
+ };
const handleCancel = () => {
props.setSelectedData(null);
props.setActionMode('list');
- form.resetFields();
};
const handleSave = async () => {
- try {
- const values = await form.validateFields();
- setConfirmLoading(true);
+ setConfirmLoading(true);
+ const validationRules = [
+ { field: 'status_number', label: 'Status Number', required: true },
+ { field: 'status_name', label: 'Status Name', required: true },
+ { field: 'status_color', label: 'Status Color', required: true },
+ { field: 'status_description', label: 'Description', required: true },
+ ];
+
+ if (
+ validateRun(formData, validationRules, (errorMessages) => {
+ NotifOk({
+ icon: 'warning',
+ title: 'Peringatan',
+ message: errorMessages,
+ });
+ setConfirmLoading(false);
+ })
+ ) {
+ return;
+ }
+
+ try {
const payload = {
- key: FormData.key,
- ...values,
+ status_number: formData.status_number,
+ status_name: formData.status_name,
+ status_color: formData.status_color,
+ status_description: formData.status_description,
+ is_active: formData.is_active,
};
- props.onDataSaved(payload);
-
- NotifOk({
- icon: 'success',
- title: 'Berhasil',
- message: `Data Status "${payload.statusName}" berhasil ${
- payload.key ? 'diubah' : 'ditambahkan'
- }.`,
- });
+ const response = formData.status_id
+ ? await updateStatus(formData.status_id, payload)
+ : await createStatus(payload);
+ if (response && (response.statusCode === 200 || response.statusCode === 201)) {
+ const action = formData.status_id ? 'diubah' : 'ditambahkan';
+ NotifOk({
+ icon: 'success',
+ title: 'Berhasil',
+ message: `Data Status "${payload.status_name}" berhasil ${action}.`,
+ });
+ props.setActionMode('list');
+ } else {
+ NotifAlert({
+ icon: 'error',
+ title: 'Gagal',
+ message: response?.message || 'Gagal menyimpan data.',
+ });
+ }
+ } catch (error) {
+ NotifAlert({
+ icon: 'error',
+ title: 'Error',
+ message: error.message || 'Terjadi kesalahan pada server.',
+ });
+ } finally {
setConfirmLoading(false);
- props.setActionMode('list');
- form.resetFields();
- } catch (errorInfo) {
- console.log('Failed:', errorInfo);
}
};
useEffect(() => {
if (props.selectedData) {
- setFormData(props.selectedData);
- form.setFieldsValue(props.selectedData);
+ setFormData({ ...defaultData, ...props.selectedData });
} else {
setFormData(defaultData);
- form.resetFields();
}
- }, [props.showModal, props.selectedData, form]);
+ }, [props.showModal, props.selectedData]);
return (
{
footer={
!props.readOnly && (
-
-
-
-
-
-
+
+
)
}
>
- Status Code}
- rules={[{ required: true, message: 'Silakan masukkan kode status!' }]}
- >
-
+ Status
+
+
-
-
Status Name}
- rules={[{ required: true, message: 'Silakan masukkan nama status!' }]}
- >
-
-
- Description}
- rules={[{ required: true, message: 'Silakan masukkan deskripsi!' }]}
- >
-
-
-
+ {formData.is_active ? 'Active' : 'Inactive'}
+
+
+
+ Status Number
+ *
+
+
+
+ Status Name
+ *
+
+
+
+ Status Color
+ *
+
+
+
+ Description
+ *
+
+
);
};
diff --git a/src/pages/master/status/component/ListStatus.jsx b/src/pages/master/status/component/ListStatus.jsx
index 945f778..ce5dd42 100644
--- a/src/pages/master/status/component/ListStatus.jsx
+++ b/src/pages/master/status/component/ListStatus.jsx
@@ -1,68 +1,152 @@
-import React from 'react';
-import { Card, Button, Row, Col, Typography, Space, ConfigProvider } from 'antd';
-import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
+import React, { memo, useState, useEffect } from 'react';
+import { Space, ConfigProvider, Button, Row, Col, Card, Input, Segmented, Table, Pagination } from 'antd';
+import {
+ PlusOutlined,
+ EditOutlined,
+ DeleteOutlined,
+ EyeOutlined,
+ SearchOutlined,
+ AppstoreOutlined,
+ TableOutlined,
+} from '@ant-design/icons';
+import { NotifAlert, NotifConfirmDialog } from '../../../../components/Global/ToastNotif';
+import { useNavigate } from 'react-router-dom';
+import { deleteStatus, getAllStatuss } from '../../../../api/master-status';
-const { Title, Text } = Typography;
+const ListStatus = memo(function ListStatus(props) {
+ const [data, setData] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [viewMode, setViewMode] = useState('card');
+ const [trigerFilter, setTrigerFilter] = useState(false);
+ const [searchValue, setSearchValue] = useState('');
+ const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
+ const navigate = useNavigate();
-const ListStatus = ({
- setActionMode,
- handleEdit,
- handleDelete,
- handlePreview,
- data,
-}) => {
-
- const getCardStyle = (statusName) => {
- let color;
- switch (statusName.toLowerCase()) {
- case 'done':
- color = '#52c41a'; // green
- break;
- case 'warning':
- color = '#faad14'; // orange
- break;
- case 'alarm':
- color = '#f5222d'; // red
- break;
- case 'critical':
- color = '#000000'; // black
- break;
- default:
- color = '#d9d9d9'; // default antd border color
+ const fetchData = async (page = 1, pageSize = 10) => {
+ setLoading(true);
+ try {
+ const params = new URLSearchParams();
+ params.append('page', page);
+ params.append('limit', pageSize);
+ if (searchValue) {
+ params.append('search', searchValue);
+ }
+ const response = await getAllStatuss(params);
+ setData(response.data || []);
+ setPagination(prev => ({ ...prev, total: response.paging?.total || 0, current: page, pageSize: pageSize }));
+ } catch (error) {
+ console.error("Failed to fetch status data:", error);
+ setData([]);
+ } finally {
+ setLoading(false);
}
- return { border: `2px solid ${color}` };
};
- const getTitleStyle = (statusName) => {
- let backgroundColor;
- switch (statusName.toLowerCase()) {
- case 'done':
- backgroundColor = '#52c41a'; // green
- break;
- case 'warning':
- backgroundColor = '#faad14'; // orange
- break;
- case 'alarm':
- backgroundColor = '#f5222d'; // red
- break;
- case 'critical':
- backgroundColor = '#000000'; // black
- break;
- default:
- backgroundColor = 'transparent';
+ useEffect(() => {
+ const token = localStorage.getItem('token');
+ if (!token) {
+ navigate('/signin');
+ return;
}
- return {
- backgroundColor,
- color: '#fff',
- padding: '2px 8px',
- borderRadius: '4px',
- display: 'inline-block'
- };
+ fetchData(pagination.current, pagination.pageSize);
+ }, [props.actionMode, trigerFilter, navigate]);
+
+ const doFilter = () => {
+ setTrigerFilter(prev => !prev);
+ };
+
+ const handleSearch = (value) => {
+ setSearchValue(value);
+ setPagination(prev => ({ ...prev, current: 1 })); // Reset to first page on search
+ doFilter();
+ };
+
+ const handlePaginationChange = (page, pageSize) => {
+ fetchData(page, pageSize);
+ };
+
+ const showPreviewModal = (record) => {
+ props.setSelectedData(record);
+ props.setActionMode('preview');
+ };
+
+ const showEditModal = (record) => {
+ props.setSelectedData(record);
+ props.setActionMode('edit');
+ };
+
+ const showAddModal = () => {
+ props.setSelectedData(null);
+ props.setActionMode('add');
+ };
+
+ const showDeleteDialog = (record) => {
+ NotifConfirmDialog({
+ icon: 'question',
+ title: 'Konfirmasi Hapus',
+ message: `Status "${record.status_name}" akan dihapus?`,
+ onConfirm: () => handleDelete(record.status_id),
+ });
+ };
+
+ const handleDelete = async (status_id) => {
+ try {
+ const response = await deleteStatus(status_id);
+ if (response.statusCode === 200) {
+ NotifAlert({ icon: 'success', title: 'Berhasil', message: 'Data Status berhasil dihapus.' });
+ doFilter();
+ } else {
+ NotifAlert({ icon: 'error', title: 'Gagal', message: response?.message || 'Gagal Menghapus Data' });
+ }
+ } catch (error) {
+ NotifAlert({ icon: 'error', title: 'Error', message: error.message });
+ }
+ };
+
+ const columns = [
+ { title: 'Number', dataIndex: 'status_number', key: 'status_number', width: '15%' },
+ { title: 'Name', dataIndex: 'status_name', key: 'status_name', width: '25%' },
+ { title: 'Description', dataIndex: 'status_description', key: 'status_description', width: '40%' },
+ {
+ title: 'Aksi', key: 'aksi', align: 'center', width: '20%',
+ render: (_, record) => (
+
+ } onClick={() => showPreviewModal(record)} />
+ } onClick={() => showEditModal(record)} />
+ } onClick={() => showDeleteDialog(record)} />
+
+ ),
+ },
+ ];
+
+ const getCardStyle = (color) => {
+ return { border: `2px solid ${color || '#d9d9d9'}`, height: '100%' };
+ };
+
+ const getTitleStyle = (color) => {
+ return { backgroundColor: color || 'transparent', color: '#fff', padding: '2px 8px', borderRadius: '4px', display: 'inline-block' };
};
return (
-
-
+
+
+
+ }
+ style={{ backgroundColor: '#23A55A', borderColor: '#23A55A' }}
+ >
+ Search
+
+ }
+ size="large"
+ />
+
- }
- onClick={() => setActionMode('add')}
- >
+ } onClick={showAddModal} size="large">
Tambah Data
-
- {data.map(item => (
-
- {item.statusName}}
- style={getCardStyle(item.statusName)}
- actions={[
-
- } onClick={() => handlePreview(item)} />
- } onClick={() => handleEdit(item)} />
- } onClick={() => handleDelete(item)} />
-
- ]}
- >
- Code: {item.statusCode}
- Description: {item.description}
-
-
- ))}
+
+
+
+ }, { value: 'table', icon: }]}
+ value={viewMode}
+ onChange={setViewMode}
+ />
+
-
+
+
+ {viewMode === 'card' ? (
+
+ {data.map(item => (
+
+ {item.status_name}}
+ style={getCardStyle(item.status_color)}
+ actions={[
+ showPreviewModal(item)} />,
+ showEditModal(item)} />,
+ showDeleteDialog(item)} />,
+ ]}
+ >
+ Number: {item.status_number}
+ Description: {item.status_description}
+
+
+ ))}
+
+ ) : (
+ <>
+
({ ...item, key: item.status_id }))}
+ pagination={false}
+ loading={loading}
+ />
+
+ >
+ )}
+
+
);
-};
+});
export default ListStatus;
\ No newline at end of file
diff --git a/src/pages/master/unit/component/DetailUnit.jsx b/src/pages/master/unit/component/DetailUnit.jsx
index 7d5f922..aa2c1fe 100644
--- a/src/pages/master/unit/component/DetailUnit.jsx
+++ b/src/pages/master/unit/component/DetailUnit.jsx
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import { Modal, Input, Typography, Button, ConfigProvider, Switch } from 'antd';
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
import { createUnit, updateUnit, getAllUnit } from '../../../../api/master-unit';
+import { validateRun } from '../../../../Utils/validate';
const { Text } = Typography;
@@ -26,70 +27,47 @@ const DetailUnit = (props) => {
const handleSave = async () => {
setConfirmLoading(true);
- // Validasi required fields
- if (!FormData.unit_name || FormData.unit_name.trim() === '') {
- NotifOk({
- icon: 'warning',
- title: 'Peringatan',
- message: 'Kolom Name Tidak Boleh Kosong',
- });
- setConfirmLoading(false);
+ const validationRules = [
+ { field: 'unit_name', label: 'Name', required: true },
+ ];
+
+ if (
+ validateRun(FormData, validationRules, (errorMessages) => {
+ NotifOk({
+ icon: 'warning',
+ title: 'Peringatan',
+ message: errorMessages,
+ });
+ setConfirmLoading(false);
+ })
+ )
return;
- }
try {
- if (FormData.unit_id) {
- // Update existing unit
- const payload = {
- name: FormData.unit_name,
- is_active: FormData.is_active,
- };
+ const payload = {
+ unit_name: FormData.unit_name,
+ is_active: FormData.is_active,
+ };
- const response = await updateUnit(FormData.unit_id, payload);
- console.log('updateUnit response:', response);
+ const response = FormData.unit_id
+ ? await updateUnit(FormData.unit_id, payload)
+ : await createUnit(payload);
- if (response.statusCode === 200) {
- // Get updated data to show unit_code in notification
- const unitCode = response.data?.unit_code || FormData.unit_code;
- NotifOk({
- icon: 'success',
- title: 'Berhasil',
- message: `Data Unit "${unitCode} - ${FormData.unit_name}" berhasil diubah.`,
- });
- props.setActionMode('list');
- } else {
- NotifAlert({
- icon: 'error',
- title: 'Gagal',
- message: response.message || 'Gagal mengubah data Unit.',
- });
- }
+ if (response.statusCode === 200 || response.statusCode === 201) {
+ const unitCode = response.data?.unit_code || FormData.unit_code || 'N/A';
+ const action = FormData.unit_id ? 'diubah' : 'ditambahkan';
+ NotifOk({
+ icon: 'success',
+ title: 'Berhasil',
+ message: `Data Unit "${unitCode} - ${payload.unit_name}" berhasil ${action}.`,
+ });
+ props.setActionMode('list');
} else {
- // Create new unit
- const payload = {
- name: FormData.unit_name,
- is_active: FormData.is_active,
- };
-
- const response = await createUnit(payload);
- console.log('createUnit response:', response);
-
- if (response.statusCode === 200 || response.statusCode === 201) {
- // Get unit_code from response
- const unitCode = response.data?.unit_code || 'N/A';
- NotifOk({
- icon: 'success',
- title: 'Berhasil',
- message: `Data Unit "${unitCode} - ${FormData.unit_name}" berhasil ditambahkan.`,
- });
- props.setActionMode('list');
- } else {
- NotifAlert({
- icon: 'error',
- title: 'Gagal',
- message: response.message || 'Gagal menambahkan data Unit.',
- });
- }
+ NotifAlert({
+ icon: 'error',
+ title: 'Gagal',
+ message: response.message || 'Gagal menyimpan data Unit.',
+ });
}
} catch (error) {
console.error('Save Unit Error:', error);
@@ -98,9 +76,9 @@ const DetailUnit = (props) => {
title: 'Error',
message: error.message || 'Terjadi kesalahan saat menyimpan data.',
});
+ } finally {
+ setConfirmLoading(false);
}
-
- setConfirmLoading(false);
};
const handleInputChange = (e) => {
@@ -298,4 +276,4 @@ const DetailUnit = (props) => {
);
};
-export default DetailUnit;
+export default DetailUnit;
\ No newline at end of file