225 lines
8.8 KiB
JavaScript
225 lines
8.8 KiB
JavaScript
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 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 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);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const token = localStorage.getItem('token');
|
|
if (!token) {
|
|
navigate('/signin');
|
|
return;
|
|
}
|
|
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) => (
|
|
<Space>
|
|
<Button type="text" icon={<EyeOutlined />} onClick={() => showPreviewModal(record)} />
|
|
<Button type="text" icon={<EditOutlined />} onClick={() => showEditModal(record)} />
|
|
<Button danger type="text" icon={<DeleteOutlined />} onClick={() => showDeleteDialog(record)} />
|
|
</Space>
|
|
),
|
|
},
|
|
];
|
|
|
|
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 (
|
|
<Card>
|
|
<Row justify="space-between" align="middle" gutter={[16, 16]}>
|
|
<Col xs={24} sm={24} md={12} lg={12}>
|
|
<Input.Search
|
|
placeholder="Search by status name..."
|
|
onSearch={handleSearch}
|
|
allowClear
|
|
enterButton={
|
|
<Button
|
|
type="primary"
|
|
icon={<SearchOutlined />}
|
|
style={{ backgroundColor: '#23A55A', borderColor: '#23A55A' }}
|
|
>
|
|
Search
|
|
</Button>
|
|
}
|
|
size="large"
|
|
/>
|
|
</Col>
|
|
<Col>
|
|
<ConfigProvider
|
|
theme={{
|
|
token: { colorBgContainer: '#E9F6EF' },
|
|
components: {
|
|
Button: {
|
|
defaultBg: 'white',
|
|
defaultColor: '#23A55A',
|
|
defaultBorderColor: '#23A55A',
|
|
defaultHoverColor: '#23A55A',
|
|
defaultHoverBorderColor: '#23A55A',
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<Button icon={<PlusOutlined />} onClick={showAddModal} size="large">
|
|
Tambah Data
|
|
</Button>
|
|
</ConfigProvider>
|
|
</Col>
|
|
</Row>
|
|
|
|
<Row style={{ marginTop: 16 }}>
|
|
<Col>
|
|
<Segmented
|
|
options={[{ value: 'card', icon: <AppstoreOutlined /> }, { value: 'table', icon: <TableOutlined /> }]}
|
|
value={viewMode}
|
|
onChange={setViewMode}
|
|
/>
|
|
</Col>
|
|
</Row>
|
|
|
|
<div style={{ marginTop: 24 }}>
|
|
{viewMode === 'card' ? (
|
|
<Row gutter={[16, 16]}>
|
|
{data.map(item => (
|
|
<Col xs={24} sm={12} md={8} lg={6} key={item.status_id}>
|
|
<Card
|
|
title={<span style={getTitleStyle(item.status_color)}>{item.status_name}</span>}
|
|
style={getCardStyle(item.status_color)}
|
|
actions={[
|
|
<EyeOutlined key="preview" onClick={() => showPreviewModal(item)} />,
|
|
<EditOutlined key="edit" onClick={() => showEditModal(item)} />,
|
|
<DeleteOutlined key="delete" onClick={() => showDeleteDialog(item)} />,
|
|
]}
|
|
>
|
|
<p><b>Number:</b> {item.status_number}</p>
|
|
<p><b>Description:</b> {item.status_description}</p>
|
|
</Card>
|
|
</Col>
|
|
))}
|
|
</Row>
|
|
) : (
|
|
<>
|
|
<Table
|
|
columns={columns}
|
|
dataSource={data.map(item => ({ ...item, key: item.status_id }))}
|
|
pagination={false}
|
|
loading={loading}
|
|
/>
|
|
<Pagination
|
|
style={{ marginTop: 16, textAlign: 'right' }}
|
|
current={pagination.current}
|
|
pageSize={pagination.pageSize}
|
|
total={pagination.total}
|
|
onChange={handlePaginationChange}
|
|
showSizeChanger
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
);
|
|
});
|
|
|
|
export default ListStatus; |