Add component

This commit is contained in:
2025-09-17 12:17:14 +07:00
parent b25aaf124c
commit 4a3ae20b84
19 changed files with 2074 additions and 0 deletions

View File

@@ -0,0 +1,499 @@
import React, { useState } from 'react';
import {
Button,
Modal,
Divider,
Card,
Tag,
ConfigProvider,
Typography,
message,
Input,
Radio,
} from 'antd';
import { getStatusHistory, approvalPermit } from '../../api/status-history';
import { NotifAlert, NotifOk, NotifConfirmDialog } from './ToastNotif';
import { getSessionData } from './Formatter';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);
const { Text } = Typography;
const { TextArea } = Input;
const StatusButton = (props) => {
const {
color,
name,
style,
canApprove = true,
canReject = true,
refreshData = (e) => {},
} = props;
const session = getSessionData();
const isVendor = session?.user?.role_id == `${import.meta.env.VITE_ROLE_VENDOR}`;
const [isModalVisible, setIsModalVisible] = useState(false);
const [confirmLoading, setConfirmLoading] = useState(false);
const [historyData, setHistoryData] = useState([]);
const [showConfirmModal, setShowConfirmModal] = useState(false);
const [showPermitSelesai, setShowPermitSelesai] = useState(false);
const [actionType, setActionType] = useState(null);
const [deskripsi, setDeskripsi] = useState('');
const [closeType, setCloseType] = useState('1');
const pengajuanPermitSelesai = 4;
const permitSelesai = 7;
const fetchHistory = async () => {
const permitId = props.permitId;
const id = parseInt(permitId);
if (!permitId || isNaN(id)) {
console.error('Permit ID tidak valid:', permitId);
message.error('Permit ID tidak valid atau kosong');
return;
}
try {
const res = await getStatusHistory(id);
const mapped =
res?.data?.data?.map((item) => ({
name: item.name,
color: item.status_permit_color,
text: item.status_permit_name,
deskripsi: item.deskripsi,
date: item.created_at,
closed: item.close_type !== null ? item.close_type_name : null,
})) ?? [];
setHistoryData(mapped);
} catch (err) {
console.error('API ERROR:', err);
message.error('Gagal mengambil riwayat status dari server');
}
};
const showModal = () => {
fetchHistory();
setIsModalVisible(true);
};
const handleCancel = () => {
setIsModalVisible(false);
};
const handleSelesai = () => {
setShowPermitSelesai(true);
};
const handleApprove = () => {
setActionType('approve');
setShowConfirmModal(true);
};
const handleReject = () => {
setActionType('reject');
setShowConfirmModal(true);
};
const submitSelesai = async () => {
const payload = {
status_permit: true,
close_type: closeType,
};
try {
setConfirmLoading(true);
const response = await approvalPermit(props.permitId, payload);
if (response?.status === 200) {
NotifOk({
icon: 'success',
title: 'Pengajuan Selesai',
message: `Permit berhasil diajukan sebagai ${
closeType === '1' ? 'selesai' : 'belum selesai'
}.`,
});
setIsModalVisible(false);
setShowPermitSelesai(false);
setCloseType('');
setTimeout(() => {
refreshData();
}, 500);
} else {
throw new Error(response?.data?.message || 'Proses gagal');
}
} catch (err) {
console.error('Error saat mengajukan permit:', err);
NotifAlert({
icon: 'error',
title: 'Gagal',
message: err.message || 'Terjadi kesalahan saat memproses permit.',
});
} finally {
setConfirmLoading(false);
setShowPermitSelesai(false);
setCloseType('');
}
};
const submitApproval = async () => {
const payload = {
status_permit: actionType === 'approve' ? true : false,
deskripsi: deskripsi.trim(),
};
try {
setConfirmLoading(true);
const response = await approvalPermit(props.permitId, payload);
if (response?.status === 200) {
NotifOk({
icon: 'success',
title: actionType === 'approve' ? 'Disetujui' : 'Ditolak',
message:
actionType === 'approve'
? 'Permit berhasil disetujui.'
: 'Permit berhasil ditolak.',
});
setIsModalVisible(false);
setShowConfirmModal(false);
setDeskripsi('');
setTimeout(() => {
refreshData();
}, 500);
} else {
throw new Error(response?.data?.message || 'Proses gagal');
}
} catch (err) {
console.error('Error saat menyetujui permit:', err);
NotifAlert({
icon: 'error',
title: 'Gagal',
message: err.message || 'Terjadi kesalahan saat memproses permit.',
});
} finally {
setConfirmLoading(false);
setShowConfirmModal(false);
setDeskripsi('');
}
};
return (
<>
<Button
size="middle"
onClick={showModal}
style={{
backgroundColor: '#fff',
color: color,
borderColor: color,
borderRadius: '8px',
padding: '5px 16px',
fontWeight: 500,
...style?.button,
}}
>
{name || 'N/A'}
</Button>
<Modal
title={
<Tag
style={{
backgroundColor: '#fff',
color: color || '#999',
border: `1px solid ${color || '#ddd'}`,
borderRadius: 8,
padding: '4px 12px',
fontSize: 16,
display: 'inline-block',
}}
>
{name ?? 'Belum ada status'}
</Tag>
}
open={isModalVisible}
onCancel={handleCancel}
footer={[
<>
<ConfigProvider
theme={{
token: { colorBgContainer: '#E9F6EF' },
components: {
Button: {
defaultBg: 'white',
defaultColor: '#23A55A',
defaultBorderColor: '#23A55A',
defaultHoverColor: '#23A55A',
defaultHoverBorderColor: '#23A55A',
},
},
}}
>
<Button onClick={handleCancel}>Batal</Button>
</ConfigProvider>
{props.status_permit === pengajuanPermitSelesai &&
historyData.length >= 0 &&
isVendor && (
<>
<ConfigProvider
theme={{
token: { colorBgContainer: '#23a55ade' },
components: {
Button: {
defaultBg: '#23a55a',
defaultColor: '#FFFFFF',
defaultBorderColor: '#23a55a',
defaultHoverColor: '#FFFFFF',
defaultHoverBorderColor: '#23a55a',
},
},
}}
>
<Button loading={confirmLoading} onClick={handleSelesai}>
Selesai
</Button>
</ConfigProvider>
</>
)}
{props.status_permit !== 0 &&
props.status_permit !== pengajuanPermitSelesai &&
props.status_permit !== permitSelesai &&
historyData.length >= 0 && (
<>
{canReject && (
<ConfigProvider
theme={{
token: { colorBgContainer: '#FF4D4Fde' },
components: {
Button: {
defaultBg: '#FF4D4F',
defaultColor: '#FFFFFF',
defaultBorderColor: '#FF4D4F',
defaultHoverColor: '#FFFFFF',
defaultHoverBorderColor: '#FF4D4F',
},
},
}}
>
<Button onClick={handleReject}>Reject</Button>
</ConfigProvider>
)}
{canApprove && (
<ConfigProvider
theme={{
token: { colorBgContainer: '#23a55ade' },
components: {
Button: {
defaultBg: '#23a55a',
defaultColor: '#FFFFFF',
defaultBorderColor: '#23a55a',
defaultHoverColor: '#FFFFFF',
defaultHoverBorderColor: '#23a55a',
},
},
}}
>
<Button
loading={confirmLoading}
onClick={handleApprove}
>
Approve
</Button>
</ConfigProvider>
)}
</>
)}
</>,
]}
>
<Divider style={{ margin: '16px 0', borderTop: '2px solid #D9D9D9' }} />
{historyData.length > 0 ? (
[...historyData]
.sort((a, b) => new Date(b.date) - new Date(a.date))
.map((item, index) => (
<Card
key={index}
style={{
marginBottom: 16,
color: item.color,
backgroundColor: '#FFFFFF',
border: `1.5px solid ${item.color}`,
borderRadius: 8,
}}
>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
marginBottom: 8,
}}
>
<Tag
style={{
backgroundColor: '#FFFFFF',
color: item.color,
border: `1.5px solid ${item.color}`,
fontSize: 13,
padding: '4px 10px',
borderRadius: 6,
fontWeight: '600',
}}
>
{item.text}
</Tag>
{item.date != null && (
<Text type="secondary" style={{ fontSize: 12 }}>
{dayjs.utc(item.date).format('YYYY-MM-DD HH:mm:ss')}
</Text>
)}
</div>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 4,
}}
>
<div style={{ fontSize: 13, color: '#333' }}>{item.name}</div>
{item.closed && (
<Tag
style={{
backgroundColor: '#f9f9f9',
color: '#000',
border: '1px dashed #999',
fontSize: 12,
fontStyle: 'italic',
marginRight: 0,
}}
>
Closed: {item.closed}
</Tag>
)}
</div>
{item.deskripsi && (
<p style={{ fontSize: 12, color: '#333', margin: 0 }}>
{item.deskripsi}
</p>
)}
</Card>
))
) : (
<Text type="secondary">Belum ada riwayat status.</Text>
)}
</Modal>
<Modal
open={showConfirmModal}
title={actionType === 'approve' ? 'Konfirmasi Persetujuan' : 'Konfirmasi Penolakan'}
onCancel={() => {
setShowConfirmModal(false);
setDeskripsi('');
}}
confirmLoading={confirmLoading}
footer={[
<Button
key="cancel"
onClick={() => {
setShowConfirmModal(false);
setDeskripsi('');
}}
>
Batal
</Button>,
<ConfigProvider
key="action"
theme={{
token: {
colorBgContainer:
actionType === 'approve' ? '#23a55ade' : '#FF4D4Fde',
},
components: {
Button: {
defaultBg: actionType === 'approve' ? '#23a55a' : '#FF4D4F',
defaultColor: '#FFFFFF',
defaultBorderColor:
actionType === 'approve' ? '#23a55a' : '#FF4D4F',
defaultHoverColor: '#FFFFFF',
defaultHoverBorderColor:
actionType === 'approve' ? '#23a55a' : '#FF4D4F',
},
},
}}
>
<Button key="submit" loading={confirmLoading} onClick={submitApproval}>
{actionType === 'approve' ? 'Approve' : 'Reject'}
</Button>
</ConfigProvider>,
]}
>
<p>Silakan isi deskripsi:</p>
<TextArea
rows={4}
value={deskripsi}
onChange={(e) => setDeskripsi(e.target.value)}
placeholder="Contoh: Disetujui karena dokumen lengkap atau ditolak karena dokumen tidak lengkap."
/>
</Modal>
<Modal
open={showPermitSelesai}
title={'Konfirmasi Pengajuan Selesai'}
onCancel={() => {
setShowPermitSelesai(false);
setCloseType('');
}}
confirmLoading={confirmLoading}
footer={[
<Button
key="cancel"
onClick={() => {
setShowPermitSelesai(false);
setCloseType('');
}}
>
Batal
</Button>,
<ConfigProvider
key="action"
theme={{
token: { colorBgContainer: '#23a55ade' },
components: {
Button: {
defaultBg: '#23a55a',
defaultColor: '#FFFFFF',
defaultBorderColor: '#23a55a',
defaultHoverColor: '#FFFFFF',
defaultHoverBorderColor: '#23a55a',
},
},
}}
>
<Button key="submit" loading={confirmLoading} onClick={submitSelesai}>
Ajukan Permit Selesai
</Button>
</ConfigProvider>,
]}
>
<p>Status Permit saat ini :</p>
<Radio.Group onChange={(e) => setCloseType(e.target.value)} value={closeType}>
<Radio value="0">Belum Selesai</Radio>
<Radio value="1">Selesai</Radio>
</Radio.Group>
</Modal>
</>
);
};
export default StatusButton;