diff --git a/src/pages/role/IndexRole.jsx b/src/pages/role/IndexRole.jsx index 0829656..c446522 100644 --- a/src/pages/role/IndexRole.jsx +++ b/src/pages/role/IndexRole.jsx @@ -1,144 +1,70 @@ import React, { memo, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useBreadcrumb } from '../../layout/LayoutBreadcrumb'; -import { Form, Typography } from 'antd'; import ListRole from './component/ListRole'; import DetailRole from './component/DetailRole'; -import { createRole, updateRole } from '../../api/role'; -import { NotifAlert, NotifOk } from '../../components/Global/ToastNotif'; +import { useBreadcrumb } from '../../layout/LayoutBreadcrumb'; +import { Typography } from 'antd'; const { Text } = Typography; const IndexRole = memo(function IndexRole() { const navigate = useNavigate(); const { setBreadcrumbItems } = useBreadcrumb(); - const [form] = Form.useForm(); const [actionMode, setActionMode] = useState('list'); const [selectedData, setSelectedData] = useState(null); - const [isModalVisible, setIsModalVisible] = useState(false); const [readOnly, setReadOnly] = useState(false); + const [showModal, setShowModal] = useState(false); + + const setMode = (param) => { + setShowModal(true); + switch (param) { + case 'add': + setReadOnly(false); + break; + case 'edit': + setReadOnly(false); + break; + case 'preview': + setReadOnly(true); + break; + default: + setShowModal(false); + break; + } + setActionMode(param); + }; useEffect(() => { const token = localStorage.getItem('token'); if (token) { setBreadcrumbItems([ - { - title: ( - - • Role - - ), - }, + { title: • Role }, ]); } else { navigate('/signin'); } }, [navigate, setBreadcrumbItems]); - useEffect(() => { - if (actionMode === 'add' || actionMode === 'edit' || actionMode === 'preview') { - setIsModalVisible(true); - setReadOnly(actionMode === 'preview'); - - if (actionMode === 'add') { - form.resetFields(); - } else if (selectedData) { - form.setFieldsValue(selectedData); - } - } else { - setIsModalVisible(false); - form.resetFields(); - } - }, [actionMode, selectedData, form]); - - const handleCancel = () => { - setActionMode('list'); - setSelectedData(null); - form.resetFields(); - }; - - const handleOk = () => { - if (readOnly) { - handleCancel(); - return; - } - - form.validateFields() - .then(async (values) => { - try { - let response; - if (actionMode === 'edit') { - response = await updateRole(selectedData.role_id, values); - console.log('Update Response:', response); - - const isSuccess = response.statusCode === 200 || response.statusCode === 201; - if (isSuccess) { - NotifAlert({ - icon: 'success', - title: 'Berhasil', - message: `Data Role "${values.role_name}" berhasil diubah.`, - }); - handleCancel(); - } else { - NotifOk({ - icon: 'error', - title: 'Gagal', - message: response.message || 'Gagal mengubah data Role', - }); - } - } else if (actionMode === 'add') { - response = await createRole(values); - console.log('Create Response:', response); - - const isSuccess = response.statusCode === 200 || response.statusCode === 201; - if (isSuccess) { - NotifAlert({ - icon: 'success', - title: 'Berhasil', - message: `Data Role "${values.role_name}" berhasil ditambahkan.`, - }); - handleCancel(); - } else { - NotifOk({ - icon: 'error', - title: 'Gagal', - message: response.message || 'Gagal menambahkan data Role', - }); - } - } - } catch (error) { - console.error('Error:', error); - NotifOk({ - icon: 'error', - title: 'Error', - message: 'Terjadi kesalahan saat menyimpan data', - }); - } - }) - .catch((info) => { - console.log('Validate Failed:', info); - }); - }; - return ( ); }); -export default IndexRole; +export default IndexRole; \ No newline at end of file diff --git a/src/pages/role/component/DetailRole.jsx b/src/pages/role/component/DetailRole.jsx index 8d930a8..1c24dbf 100644 --- a/src/pages/role/component/DetailRole.jsx +++ b/src/pages/role/component/DetailRole.jsx @@ -1,72 +1,213 @@ -import React from 'react'; -import { Modal, Form, Input, InputNumber, Switch, Row, Col, Typography, Divider } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { Modal, Input, Divider, Typography, Switch, Button, ConfigProvider, InputNumber, Row, Col } from 'antd'; +import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif'; +import { validateRun } from '../../../Utils/validate'; +import { createRole, updateRole } from '../../../api/role'; const { Text } = Typography; +const { TextArea } = Input; -const DetailRole = ({ visible, onCancel, onOk, form, editingKey, readOnly }) => { - const modalTitle = editingKey ? (readOnly ? 'Preview Role' : 'Edit Role') : 'Tambah Role'; +const DetailRole = (props) => { + const [confirmLoading, setConfirmLoading] = useState(false); + + const defaultData = { + role_id: '', + role_name: '', + role_level: null, + role_description: '', + is_active: true, + }; + + const [formData, setFormData] = useState(defaultData); + + const handleInputChange = (e) => { + const { name, value } = e.target; + setFormData({ ...formData, [name]: value }); + }; + + const handleInputNumberChange = (value) => { + setFormData({ ...formData, role_level: value }); + }; + + const handleStatusToggle = (checked) => { + setFormData({ ...formData, is_active: checked }); + }; + + const handleCancel = () => { + props.setSelectedData(null); + props.setActionMode('list'); + }; + + const handleSave = async () => { + setConfirmLoading(true); + + const validationRules = [ + { field: 'role_name', label: 'Nama Role', required: true }, + { field: 'role_level', label: 'Level', required: true }, + ]; + + if ( + validateRun(formData, validationRules, (errorMessages) => { + NotifOk({ + icon: 'warning', + title: 'Peringatan', + message: errorMessages, + }); + setConfirmLoading(false); + }) + ) { + return; + } + + try { + const payload = { + role_name: formData.role_name, + role_level: formData.role_level, + role_description: formData.role_description, + is_active: formData.is_active, + }; + + const response = formData.role_id + ? await updateRole(formData.role_id, payload) + : await createRole(payload); + + if (response && (response.statusCode === 200 || response.statusCode === 201)) { + const action = formData.role_id ? 'diubah' : 'ditambahkan'; + NotifOk({ + icon: 'success', + title: 'Berhasil', + message: `Data Role "${payload.role_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); + } + }; + + useEffect(() => { + if (props.selectedData) { + setFormData({ ...defaultData, ...props.selectedData }); + } else { + setFormData(defaultData); + } + }, [props.showModal, props.selectedData]); return ( {modalTitle}} - open={visible} - onCancel={onCancel} - onOk={onOk} - okText="Simpan" - cancelText="Batal" - okButtonProps={{ disabled: readOnly }} - destroyOnClose - + title={ + + {props.actionMode === 'add' + ? 'Tambah Role' + : props.actionMode === 'preview' + ? 'Preview Role' + : 'Edit Role'} + + } + open={props.showModal} + onCancel={handleCancel} + footer={[ + + + {props.readOnly ? 'Tutup' : 'Batal'} + + + {!props.readOnly && ( + + Simpan + + )} + + , + ]} > - - Status} - valuePropName="checked" - initialValue={true} - > - - - - - - Nama Role} - rules={[{ required: true, message: 'Nama Role wajib diisi!' }]} - > - - - - - Level} - rules={[{ required: true, message: 'Level wajib diisi!' }]} - > - - - - - - Deskripsi Role} - > - + Status + + - - + {formData.is_active ? 'Active' : 'Inactive'} + + + + + + Nama Role + * + + + + + + Level + * + + + + + + Deskripsi Role + + ); }; -export default DetailRole; \ No newline at end of file +export default DetailRole; diff --git a/src/pages/role/component/ListRole.jsx b/src/pages/role/component/ListRole.jsx index 03d9631..855f746 100644 --- a/src/pages/role/component/ListRole.jsx +++ b/src/pages/role/component/ListRole.jsx @@ -1,5 +1,5 @@ import React, { memo, useState, useEffect } from 'react'; -import { Space, ConfigProvider, Button, Row, Col, Card, Input } from 'antd'; +import { Space, Tag, ConfigProvider, Button, Row, Col, Card, Input } from 'antd'; import { PlusOutlined, EditOutlined, @@ -14,17 +14,17 @@ import TableList from '../../../components/Global/TableList'; const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ { - title: 'No', - key: 'no', + title: 'ID', + dataIndex: 'role_id', + key: 'role_id', width: '5%', - align: 'center', - render: (_, __, index) => index + 1, + hidden: true, }, { title: 'Nama Role', dataIndex: 'role_name', key: 'role_name', - width: '20%', + width: '25%', }, { title: 'Level', @@ -37,7 +37,19 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ title: 'Deskripsi', dataIndex: 'role_description', key: 'role_description', - width: '40%', + width: '35%', + }, + { + title: 'Status', + dataIndex: 'is_active', + key: 'is_active', + width: '10%', + align: 'center', + render: (_, { is_active }) => ( + + {is_active ? 'Active' : 'Inactive'} + + ), }, { title: 'Aksi', @@ -47,28 +59,23 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ render: (_, record) => ( } + type="text" + style={{ borderColor: '#1890ff' }} + icon={} onClick={() => showPreviewModal(record)} - style={{ - color: '#1890ff', - borderColor: '#1890ff', - }} /> } + type="text" + style={{ borderColor: '#faad14' }} + icon={} onClick={() => showEditModal(record)} - style={{ - color: '#faad14', - borderColor: '#faad14', - }} /> } onClick={() => showDeleteDialog(record)} - style={{ - borderColor: '#ff4d4f', - }} /> ), @@ -76,19 +83,16 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ ]; const ListRole = memo(function ListRole(props) { - const [showFilter, setShowFilter] = useState(false); const [trigerFilter, setTrigerFilter] = useState(false); - const defaultFilter = { criteria: '' }; const [formDataFilter, setFormDataFilter] = useState(defaultFilter); const [searchValue, setSearchValue] = useState(''); - const navigate = useNavigate(); useEffect(() => { const token = localStorage.getItem('token'); if (token) { - if (props.actionMode == 'list') { + if (props.actionMode === 'list') { setFormDataFilter(defaultFilter); doFilter(); } @@ -97,11 +101,6 @@ const ListRole = memo(function ListRole(props) { } }, [props.actionMode]); - const toggleFilter = () => { - setFormDataFilter(defaultFilter); - setShowFilter((prev) => !prev); - }; - const doFilter = () => { setTrigerFilter((prev) => !prev); }; @@ -135,8 +134,8 @@ const ListRole = memo(function ListRole(props) { const showDeleteDialog = (param) => { NotifConfirmDialog({ icon: 'question', - title: 'Konfirmasi', - message: 'Apakah anda yakin hapus data "' + param.role_name + '" ?', + title: 'Konfirmasi Hapus', + message: 'Role "' + param.role_name + '" akan dihapus?', onConfirm: () => handleDelete(param.role_id, param.role_name), onCancel: () => props.setSelectedData(null), }); @@ -144,13 +143,11 @@ const ListRole = memo(function ListRole(props) { const handleDelete = async (role_id, role_name) => { const response = await deleteRole(role_id); - console.log('Delete Role Response:', response); - - if (response.statusCode == 200) { + if (response.statusCode === 200) { NotifAlert({ icon: 'success', title: 'Berhasil', - message: 'Data Role "' + role_name + '" berhasil dihapus.', + message: `Data Role "${role_name}" berhasil dihapus.`, }); doFilter(); } else { @@ -175,7 +172,6 @@ const ListRole = memo(function ListRole(props) { onChange={(e) => { const value = e.target.value; setSearchValue(value); - // Auto search when clearing by backspace/delete if (value === '') { setFormDataFilter({ criteria: '' }); setTrigerFilter((prev) => !prev); @@ -204,14 +200,11 @@ const ListRole = memo(function ListRole(props) {