color picker
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
Radio,
|
ColorPicker,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
||||||
import { validateRun } from '../../../../Utils/validate';
|
import { validateRun } from '../../../../Utils/validate';
|
||||||
@@ -18,18 +18,6 @@ import { createStatus, updateStatus } from '../../../../api/master-status';
|
|||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
// Daftar 8 warna yang tersedia
|
|
||||||
const COLOR_OPTIONS = [
|
|
||||||
{ label: 'Merah', value: '#EF4444', hex: '#EF4444' },
|
|
||||||
{ label: 'Biru', value: '#3B82F6', hex: '#3B82F6' },
|
|
||||||
{ label: 'Hijau', value: '#10B981', hex: '#10B981' },
|
|
||||||
{ label: 'Kuning', value: '#F59E0B', hex: '#F59E0B' },
|
|
||||||
{ label: 'Ungu', value: '#8B5CF6', hex: '#8B5CF6' },
|
|
||||||
{ label: 'Pink', value: '#EC4899', hex: '#EC4899' },
|
|
||||||
{ label: 'Orange', value: '#F97316', hex: '#F97316' },
|
|
||||||
{ label: 'Teal', value: '#14B8A6', hex: '#14B8A6' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const DetailStatus = (props) => {
|
const DetailStatus = (props) => {
|
||||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||||
|
|
||||||
@@ -57,8 +45,8 @@ const DetailStatus = (props) => {
|
|||||||
setFormData({ ...formData, is_active: checked });
|
setFormData({ ...formData, is_active: checked });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleColorChange = (e) => {
|
const handleColorChange = (color, hex) => {
|
||||||
setFormData({ ...formData, status_color: e.target.value });
|
setFormData({ ...formData, status_color: hex });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
@@ -213,38 +201,33 @@ const DetailStatus = (props) => {
|
|||||||
<Text strong>Status Color</Text>
|
<Text strong>Status Color</Text>
|
||||||
<Text style={{ color: 'red' }}> *</Text>
|
<Text style={{ color: 'red' }}> *</Text>
|
||||||
<div style={{ marginTop: '8px' }}>
|
<div style={{ marginTop: '8px' }}>
|
||||||
<Radio.Group
|
<ColorPicker
|
||||||
|
value={formData.status_color || '#000000'}
|
||||||
onChange={handleColorChange}
|
onChange={handleColorChange}
|
||||||
value={formData.status_color}
|
|
||||||
disabled={props.readOnly}
|
disabled={props.readOnly}
|
||||||
>
|
showText={(color) => `color hex: ${color.toHexString()}`}
|
||||||
<Row gutter={[8, 8]}>
|
allowClear={false}
|
||||||
{COLOR_OPTIONS.map((color) => (
|
format="hex"
|
||||||
<Col span={12} key={color.value}>
|
size="large"
|
||||||
<Radio value={color.value} style={{ width: '100%' }}>
|
style={{ width: '100%' }}
|
||||||
<div
|
presets={[
|
||||||
style={{
|
{
|
||||||
display: 'flex',
|
label: 'Recommended',
|
||||||
alignItems: 'center',
|
colors: [
|
||||||
gap: '8px',
|
'#EF4444', // Merah
|
||||||
}}
|
'#3B82F6', // Biru
|
||||||
>
|
'#10B981', // Hijau
|
||||||
<div
|
'#F59E0B', // Kuning
|
||||||
style={{
|
'#8B5CF6', // Ungu
|
||||||
width: '20px',
|
'#EC4899', // Pink
|
||||||
height: '20px',
|
'#F97316', // Orange
|
||||||
backgroundColor: color.hex,
|
'#14B8A6', // Teal
|
||||||
borderRadius: '4px',
|
'#6B7280', // Gray
|
||||||
border: '1px solid #d9d9d9',
|
'#000000', // Black
|
||||||
}}
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
<span>{color.label}</span>
|
|
||||||
</div>
|
|
||||||
</Radio>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
</Radio.Group>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { memo, useState, useEffect } from 'react';
|
import React, { memo, useState, useEffect } from 'react';
|
||||||
import { Space, Tag, ConfigProvider, Button, Row, Col, Card, Input } from 'antd';
|
import { Space, Tag, ConfigProvider, Button, Row, Col, Card, Input, Modal } from 'antd';
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
CheckOutlined,
|
CheckOutlined,
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../components/Global/ToastNotif';
|
import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../components/Global/ToastNotif';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
@@ -49,13 +50,7 @@ const getRoleColor = (role_name, role_level) => {
|
|||||||
return 'default';
|
return 'default';
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = (
|
const columns = (showPreviewModal, showEditModal, showDeleteDialog, showApprovalModal) => [
|
||||||
showPreviewModal,
|
|
||||||
showEditModal,
|
|
||||||
showDeleteDialog,
|
|
||||||
showApproveDialog,
|
|
||||||
showRejectDialog
|
|
||||||
) => [
|
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
dataIndex: 'user_id',
|
dataIndex: 'user_id',
|
||||||
@@ -116,32 +111,22 @@ const columns = (
|
|||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
// is_approve: 0 = Rejected, 1 = Pending, 2 = Approved
|
// is_approve: 0 = Rejected, 1 = Pending, 2 = Approved
|
||||||
if (record.is_approve === 1 || record.is_approve === '1') {
|
if (record.is_approve === 1 || record.is_approve === '1') {
|
||||||
// Pending - show both Approve and Reject buttons
|
// Pending - show single Pending button
|
||||||
return (
|
return (
|
||||||
<Space size="small" direction="vertical">
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="default"
|
||||||
size="small"
|
size="small"
|
||||||
icon={<CheckOutlined />}
|
icon={<ClockCircleOutlined />}
|
||||||
onClick={() => showApproveDialog(record)}
|
onClick={() => showApprovalModal(record)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: '#52c41a',
|
backgroundColor: '#faad14',
|
||||||
borderColor: '#52c41a',
|
borderColor: '#faad14',
|
||||||
|
color: 'white',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Approve
|
Pending
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
danger
|
|
||||||
size="small"
|
|
||||||
icon={<CloseOutlined />}
|
|
||||||
onClick={() => showRejectDialog(record)}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
>
|
|
||||||
Reject
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
);
|
);
|
||||||
} else if (record.is_approve === 0 || record.is_approve === '0') {
|
} else if (record.is_approve === 0 || record.is_approve === '0') {
|
||||||
// Rejected
|
// Rejected
|
||||||
@@ -233,6 +218,8 @@ const columns = (
|
|||||||
const ListUser = memo(function ListUser(props) {
|
const ListUser = memo(function ListUser(props) {
|
||||||
const [showFilter, setShowFilter] = useState(false);
|
const [showFilter, setShowFilter] = useState(false);
|
||||||
const [trigerFilter, setTrigerFilter] = useState(false);
|
const [trigerFilter, setTrigerFilter] = useState(false);
|
||||||
|
const [approvalModalVisible, setApprovalModalVisible] = useState(false);
|
||||||
|
const [selectedUser, setSelectedUser] = useState(null);
|
||||||
|
|
||||||
const defaultFilter = { criteria: '' };
|
const defaultFilter = { criteria: '' };
|
||||||
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
|
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
|
||||||
@@ -285,44 +272,30 @@ const ListUser = memo(function ListUser(props) {
|
|||||||
props.setActionMode('add');
|
props.setActionMode('add');
|
||||||
};
|
};
|
||||||
|
|
||||||
const showApproveDialog = (param) => {
|
const showApprovalModal = (param) => {
|
||||||
Swal.fire({
|
setSelectedUser(param);
|
||||||
icon: 'question',
|
setApprovalModalVisible(true);
|
||||||
title: 'Konfirmasi Approve User',
|
|
||||||
text: 'Apakah anda yakin approve user "' + param.user_fullname + '" ?',
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: '#d33',
|
|
||||||
cancelButtonText: 'Batal',
|
|
||||||
confirmButtonColor: '#23A55A',
|
|
||||||
confirmButtonText: 'Approve',
|
|
||||||
reverseButtons: true,
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
handleApprove(param.user_id);
|
|
||||||
} else if (result.dismiss) {
|
|
||||||
props.setSelectedData(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const showRejectDialog = (param) => {
|
const handleModalApprove = () => {
|
||||||
Swal.fire({
|
if (selectedUser) {
|
||||||
icon: 'warning',
|
handleApprove(selectedUser.user_id);
|
||||||
title: 'Konfirmasi Reject User',
|
setApprovalModalVisible(false);
|
||||||
text: 'Apakah anda yakin reject user "' + param.user_fullname + '" ?',
|
setSelectedUser(null);
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: '#23A55A',
|
|
||||||
cancelButtonText: 'Batal',
|
|
||||||
confirmButtonColor: '#d33',
|
|
||||||
confirmButtonText: 'Reject',
|
|
||||||
reverseButtons: true,
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
handleReject(param.user_id);
|
|
||||||
} else if (result.dismiss) {
|
|
||||||
props.setSelectedData(null);
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const handleModalReject = () => {
|
||||||
|
if (selectedUser) {
|
||||||
|
handleReject(selectedUser.user_id);
|
||||||
|
setApprovalModalVisible(false);
|
||||||
|
setSelectedUser(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalCancel = () => {
|
||||||
|
setApprovalModalVisible(false);
|
||||||
|
setSelectedUser(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const showDeleteDialog = (param) => {
|
const showDeleteDialog = (param) => {
|
||||||
@@ -470,14 +443,46 @@ const ListUser = memo(function ListUser(props) {
|
|||||||
showPreviewModal,
|
showPreviewModal,
|
||||||
showEditModal,
|
showEditModal,
|
||||||
showDeleteDialog,
|
showDeleteDialog,
|
||||||
showApproveDialog,
|
showApprovalModal
|
||||||
showRejectDialog
|
|
||||||
)}
|
)}
|
||||||
triger={trigerFilter}
|
triger={trigerFilter}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Approval Modal */}
|
||||||
|
<Modal
|
||||||
|
title="Konfirmasi Persetujuan User"
|
||||||
|
open={approvalModalVisible}
|
||||||
|
onCancel={handleModalCancel}
|
||||||
|
footer={[
|
||||||
|
<Button key="reject" danger onClick={handleModalReject}>
|
||||||
|
Reject User
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="approve"
|
||||||
|
type="primary"
|
||||||
|
style={{ backgroundColor: '#23A55A', borderColor: '#23A55A' }}
|
||||||
|
onClick={handleModalApprove}
|
||||||
|
>
|
||||||
|
Approve User
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
width={400}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: 'center', padding: '20px 0' }}>
|
||||||
|
<ClockCircleOutlined
|
||||||
|
style={{ fontSize: '48px', color: '#faad14', marginBottom: '16px' }}
|
||||||
|
/>
|
||||||
|
<p style={{ fontSize: '16px', margin: '16px 0' }}>
|
||||||
|
User: <strong>{selectedUser?.user_fullname}</strong>
|
||||||
|
</p>
|
||||||
|
<p style={{ color: '#666', margin: '8px 0' }}>
|
||||||
|
Apakah Anda ingin approve atau reject user ini?
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user