From 9091392dfbd50b7570e1c9b94fc94c7b528c4f72 Mon Sep 17 00:00:00 2001 From: vinix Date: Tue, 21 Oct 2025 23:05:39 +0700 Subject: [PATCH] feat: enhance status management with validation and improved UI components --- src/pages/master/status/IndexStatus.jsx | 129 +-------- .../master/status/component/DetailStatus.jsx | 236 ++++++++------- .../master/status/component/ListStatus.jsx | 273 ++++++++++++------ .../master/unit/component/DetailUnit.jsx | 100 +++---- 4 files changed, 379 insertions(+), 359 deletions(-) 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!' }]} - > -