fixing ui master

This commit is contained in:
2025-10-25 16:08:42 +07:00
parent a3e5fdd138
commit a86795fdf6
15 changed files with 183 additions and 184 deletions

View File

@@ -15,7 +15,6 @@ import IndexTag from './pages/master/tag/IndexTag';
import IndexUnit from './pages/master/unit/IndexUnit';
import IndexBrandDevice from './pages/master/brandDevice/IndexBrandDevice';
import AddBrandDevice from './pages/master/brandDevice/AddBrandDevice';
import IndexPlantSection from './pages/master/plantSection/IndexPlantSection';
import IndexStatus from './pages/master/status/IndexStatus';
import IndexShift from './pages/master/shift/IndexShift';
@@ -41,6 +40,7 @@ import SvgAirDryerB from './pages/home/SvgAirDryerB';
import SvgAirDryerC from './pages/home/SvgAirDryerC';
import IndexHistoryAlarm from './pages/history/alarm/IndexHistoryAlarm';
import IndexHistoryEvent from './pages/history/event/IndexHistoryEvent';
import IndexPlantSubSection from './pages/master/plantSubSection/IndexPlantSubSection';
const App = () => {
return (
@@ -74,7 +74,7 @@ const App = () => {
<Route path="unit" element={<IndexUnit />} />
<Route path="brand-device" element={<IndexBrandDevice />} />
<Route path="brand-device/add" element={<AddBrandDevice />} />
<Route path="plant-section" element={<IndexPlantSection />} />
<Route path="plant-sub-section" element={<IndexPlantSubSection />} />
<Route path="shift" element={<IndexShift />} />
<Route path="status" element={<IndexStatus />} />
</Route>

View File

@@ -93,9 +93,9 @@ const allItems = [
label: 'Master',
children: [
{
key: 'master-plant-section',
key: 'master-plant-sub-section',
icon: <ProductOutlined style={{ fontSize: '19px' }} />,
label: <Link to="/master/plant-section">Plant Sub Section</Link>,
label: <Link to="/master/plant-sub-section">Plant Sub Section</Link>,
},
{
key: 'master-brand-device',

View File

@@ -15,6 +15,13 @@ import { deleteDevice, getAllDevice } from '../../../../api/master-device';
import TableList from '../../../../components/Global/TableList';
const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
{
title: 'No',
key: 'no',
width: '5%',
align: 'center',
render: (_, __, index) => index + 1,
},
{
title: 'ID',
dataIndex: 'device_id',
@@ -27,6 +34,7 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
dataIndex: 'device_code',
key: 'device_code',
width: '10%',
hidden: true,
},
{
title: 'Device Name',

View File

@@ -1,13 +1,13 @@
import React, { memo, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import ListPlantSection from './component/ListPlantSection';
import DetailPlantSection from './component/DetailPlantSection';
import ListPlantSection from './component/ListPlantSubSection';
import DetailPlantSection from './component/DetailPlantSubSection';
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
import { Typography } from 'antd';
const { Text } = Typography;
const IndexPlantSection = memo(function IndexPlantSection() {
const IndexPlantSubSection = memo(function IndexPlantSubSection() {
const navigate = useNavigate();
const { setBreadcrumbItems } = useBreadcrumb();
@@ -71,4 +71,4 @@ const IndexPlantSection = memo(function IndexPlantSection() {
);
});
export default IndexPlantSection;
export default IndexPlantSubSection;

View File

@@ -7,7 +7,7 @@ import TextArea from 'antd/es/input/TextArea';
const { Text } = Typography;
const DetailPlantSection = (props) => {
const DetailPlantSubSection = (props) => {
const [confirmLoading, setConfirmLoading] = useState(false);
const defaultData = {
@@ -265,4 +265,4 @@ const DetailPlantSection = (props) => {
);
};
export default DetailPlantSection;
export default DetailPlantSubSection;

View File

@@ -24,27 +24,21 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
title: 'Plant Sub Section Code',
dataIndex: 'plant_sub_section_code',
key: 'plant_sub_section_code',
width: '15%',
width: '10%',
align: 'center',
hidden: true,
},
{
title: 'Plant Sub Section Name',
dataIndex: 'plant_sub_section_name',
key: 'plant_sub_section_name',
width: '25%',
},
{
title: 'Table Name Value',
dataIndex: 'table_name_value',
key: 'table_name_value',
width: '20%',
render: (text) => text || '-',
width: '15%',
},
{
title: 'Description',
dataIndex: 'plant_sub_section_description',
key: 'plant_sub_section_description',
width: '20%',
width: '30%',
render: (text) => text || '-',
},
{
@@ -101,7 +95,7 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
},
];
const ListPlantSection = memo(function ListPlantSection(props) {
const ListPlantSubSection = memo(function ListPlantSubSection(props) {
const [trigerFilter, setTrigerFilter] = useState(false);
const defaultFilter = { criteria: '' };
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
@@ -259,4 +253,4 @@ const ListPlantSection = memo(function ListPlantSection(props) {
);
});
export default ListPlantSection;
export default ListPlantSubSection;

View File

@@ -28,6 +28,13 @@ const formatTime = (timeValue) => {
};
const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
{
title: 'No',
key: 'no',
width: '5%',
align: 'center',
render: (_, __, index) => index + 1,
},
{
title: 'Shift Name',
dataIndex: 'shift_name',

View File

@@ -197,39 +197,43 @@ const DetailStatus = (props) => {
</div>
</Col>
</Row>
<div style={{ marginBottom: 12 }}>
<Text strong>Status Color</Text>
<Text style={{ color: 'red' }}> *</Text>
<div style={{ marginTop: '8px' }}>
<ColorPicker
value={formData.status_color || '#000000'}
onChange={handleColorChange}
disabled={props.readOnly}
showText={(color) => `color hex: ${color.toHexString()}`}
allowClear={false}
format="hex"
size="large"
style={{ width: '100%' }}
presets={[
{
label: 'Recommended',
colors: [
'#EF4444', // Merah
'#3B82F6', // Biru
'#10B981', // Hijau
'#F59E0B', // Kuning
'#8B5CF6', // Ungu
'#EC4899', // Pink
'#F97316', // Orange
'#14B8A6', // Teal
'#6B7280', // Gray
'#000000', // Black
],
},
]}
/>
</div>
</div>
<Row gutter={16}>
<Col span={12}>
<div style={{ marginBottom: 12 }}>
<Text strong>Status Color</Text>
<Text style={{ color: 'red' }}> *</Text>
<div style={{ marginTop: '8px' }}>
<ColorPicker
value={formData.status_color || '#000000'}
onChange={handleColorChange}
disabled={props.readOnly}
showText={(color) => `color hex: ${color.toHexString()}`}
allowClear={false}
format="hex"
style={{ width: '100%' }}
presets={[
{
label: 'Recommended',
colors: [
'#EF4444', // Merah
'#3B82F6', // Biru
'#10B981', // Hijau
'#F59E0B', // Kuning
'#8B5CF6', // Ungu
'#EC4899', // Pink
'#F97316', // Orange
'#14B8A6', // Teal
'#6B7280', // Gray
'#000000', // Black
],
},
]}
/>
</div>
</div>
</Col>
</Row>
<div style={{ marginBottom: 12 }}>
<Text strong>Description</Text>
<TextArea

View File

@@ -13,8 +13,29 @@ import { deleteStatus, getAllStatuss } from '../../../../api/master-status';
import TableList from '../../../../components/Global/TableList';
const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
{ title: 'Number', dataIndex: 'status_number', key: 'status_number', width: '15%' },
{
title: 'No',
key: 'no',
width: '5%',
align: 'center',
render: (_, __, index) => index + 1,
},
{ title: 'Status Number', dataIndex: 'status_number', key: 'status_number', width: '15%' },
{ title: 'Name', dataIndex: 'status_name', key: 'status_name', width: '25%' },
{
title: 'Color',
dataIndex: 'status_color',
key: 'status_color',
align: 'center',
width: '10%',
render: (_, record) => (
<Button
type="text"
style={{ backgroundColor: record.status_color }}
onClick={() => showPreviewModal(record)}
/>
),
},
{
title: 'Description',
dataIndex: 'status_description',

View File

@@ -68,7 +68,7 @@ const DetailTag = (props) => {
return;
// Validasi format number untuk tag_number
const tagNumberInt = parseInt(formData.tag_number);
const tagNumberInt = Number(formData.tag_number);
if (isNaN(tagNumberInt)) {
NotifOk({
icon: 'warning',
@@ -88,7 +88,7 @@ const DetailTag = (props) => {
const existingTags = response.data.data;
const isDuplicate = existingTags.some((tag) => {
const isSameNumber = parseInt(tag.tag_number) === tagNumberInt;
const isSameNumber = Number(tag.tag_number) === tagNumberInt;
const isDifferentTag = formData.tag_id ? tag.tag_id !== formData.tag_id : true;
return isSameNumber && isDifferentTag;
});
@@ -131,7 +131,7 @@ const DetailTag = (props) => {
// Prepare payload berdasarkan backend validation schema
const payload = {
tag_name: formData.tag_name.trim(),
tag_number: parseInt(formData.tag_number),
tag_number: Number(formData.tag_number),
is_active: formData.is_active,
is_alarm: formData.is_alarm,
is_report: formData.is_report,
@@ -155,26 +155,26 @@ const DetailTag = (props) => {
// Add device_id only if it has a value
if (formData.device_id) {
payload.device_id = parseInt(formData.device_id);
payload.device_id = Number(formData.device_id);
}
// Add limit fields only if they have values
if (formData.lim_low_crash !== '' && formData.lim_low_crash !== null) {
payload.lim_low_crash = parseFloat(formData.lim_low_crash);
payload.lim_low_crash = Number(formData.lim_low_crash);
}
if (formData.lim_low !== '' && formData.lim_low !== null) {
payload.lim_low = parseFloat(formData.lim_low);
payload.lim_low = Number(formData.lim_low);
}
if (formData.lim_high !== '' && formData.lim_high !== null) {
payload.lim_high = parseFloat(formData.lim_high);
payload.lim_high = Number(formData.lim_high);
}
if (formData.lim_high_crash !== '' && formData.lim_high_crash !== null) {
payload.lim_high_crash = parseFloat(formData.lim_high_crash);
payload.lim_high_crash = Number(formData.lim_high_crash);
}
// Add plant_sub_section_id only if it has a value
if (formData.plant_sub_section_id) {
payload.plant_sub_section_id = parseInt(formData.plant_sub_section_id);
payload.plant_sub_section_id = Number(formData.plant_sub_section_id);
}
try {
@@ -433,37 +433,47 @@ const DetailTag = (props) => {
}}
/>
</div>
{/* Alarm Checkbox */}
<div style={{ flex: 1 }}>
<Text strong>Alarm</Text>
<div style={{ marginTop: '8px' }}>
<Checkbox
disabled={props.readOnly}
checked={formData.is_alarm === true}
onChange={handleAlarmToggle}
/>
</div>
</div>
{/* Report Checkbox */}
<div style={{ flex: 1 }}>
<Text strong>Report</Text>
<div style={{ marginTop: '8px' }}>
<Checkbox
disabled={props.readOnly}
checked={formData.is_report === true}
onChange={handleReportToggle}
/>
</div>
</div>
{/* History Checkbox */}
<div style={{ flex: 1 }}>
<Text strong>History</Text>
<div>
<Checkbox
disabled={props.readOnly}
checked={formData.is_history === true}
onChange={handleHistoryToggle}
/>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
gap: '20px',
}}
>
{/* Alarm Checkbox */}
<div style={{ flex: 1 }}>
<Text strong>Alarm</Text>
<div style={{ marginTop: '8px' }}>
<Checkbox
disabled={props.readOnly}
checked={formData.is_alarm === true}
onChange={handleAlarmToggle}
/>
</div>
</div>
{/* Report Checkbox */}
<div style={{ flex: 1 }}>
<Text strong>Report</Text>
<div style={{ marginTop: '8px' }}>
<Checkbox
disabled={props.readOnly}
checked={formData.is_report === true}
onChange={handleReportToggle}
/>
</div>
</div>
{/* History Checkbox */}
<div style={{ flex: 1 }}>
<Text strong>History</Text>
<div>
<Checkbox
disabled={props.readOnly}
checked={formData.is_history === true}
onChange={handleHistoryToggle}
/>
</div>
</div>
</div>
</div>
</div>
@@ -639,14 +649,14 @@ const DetailTag = (props) => {
gap: '12px',
}}
>
{/* Limit Low Crash */}
{/* Limit Low Low */}
<div style={{ flex: 1 }}>
<Text strong>Limit Low Crash</Text>
<Text strong>Limit Low Low</Text>
<Input
name="lim_low_crash"
value={formData.lim_low_crash}
onChange={handleInputChange}
placeholder="Enter Limit Low Crash"
placeholder="Enter Limit Low Low"
readOnly={props.readOnly}
type="number"
step="any"
@@ -678,14 +688,14 @@ const DetailTag = (props) => {
step="any"
/>
</div>
{/* Limit High Crash */}
{/* Limit High High */}
<div style={{ flex: 1 }}>
<Text strong>Limit High Crash</Text>
<Text strong>Limit High High</Text>
<Input
name="lim_high_crash"
value={formData.lim_high_crash}
onChange={handleInputChange}
placeholder="Enter Limit High Crash"
placeholder="Enter Limit High High"
readOnly={props.readOnly}
type="number"
step="any"

View File

@@ -13,6 +13,13 @@ import TableList from '../../../../components/Global/TableList';
import { getAllTag, deleteTag } from '../../../../api/master-tag';
const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
{
title: 'No',
key: 'no',
width: '5%',
align: 'center',
render: (_, __, index) => index + 1,
},
{
title: 'ID',
dataIndex: 'tag_id',
@@ -25,12 +32,7 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
dataIndex: 'tag_code',
key: 'tag_code',
width: '10%',
},
{
title: 'Tag Name',
dataIndex: 'tag_name',
key: 'tag_name',
width: '15%',
hidden: true,
},
{
title: 'Tag Number',
@@ -40,7 +42,14 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
align: 'center',
},
{
title: 'Data Type',
title: 'Tag Name',
dataIndex: 'tag_name',
key: 'tag_name',
width: '20%',
},
{
title: 'Type',
dataIndex: 'data_type',
key: 'data_type',
width: '8%',
@@ -66,19 +75,13 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
key: 'device_name',
width: '12%',
render: (text) => text || '-',
},
{
title: 'Description',
dataIndex: 'tag_description',
key: 'tag_description',
width: '12%',
render: (text) => text || '-',
hidden: true,
},
{
title: 'Status',
dataIndex: 'is_active',
key: 'is_active',
width: '8%',
width: '5%',
align: 'center',
render: (_, { is_active }) => (
<>

View File

@@ -2,15 +2,12 @@ import React, { useEffect, useState } from 'react';
import { Modal, Input, Typography, Switch, Button, ConfigProvider, Divider, Select } from 'antd';
import { NotifOk } from '../../../../components/Global/ToastNotif';
import { createUnit, updateUnit } from '../../../../api/master-unit';
import { getAllTag } from '../../../../api/master-tag'; // Import API untuk Tag
import { validateRun } from '../../../../Utils/validate';
const { Text } = Typography;
const DetailUnit = (props) => {
const [confirmLoading, setConfirmLoading] = useState(false);
const [tagList, setTagList] = useState([]);
const [loadingTags, setLoadingTags] = useState(false);
const defaultData = {
unit_id: '',
@@ -18,28 +15,10 @@ const DetailUnit = (props) => {
unit_name: '',
unit_description: '',
is_active: true,
tag_id: null, // Tambahkan tag_id
};
const [formData, setFormData] = useState(defaultData);
// Fungsi untuk mengambil data Tag
const loadTags = async () => {
setLoadingTags(true);
try {
const params = new URLSearchParams({ limit: 1000, criteria: '' });
const response = await getAllTag(params);
if (response && response.data) {
const activeTags = response.data.filter((tag) => tag.is_active === true);
setTagList(activeTags);
}
} catch (error) {
console.error('Error loading tags:', error);
} finally {
setLoadingTags(false);
}
};
const handleCancel = () => {
props.setSelectedData(null);
props.setActionMode('list');
@@ -48,10 +27,7 @@ const DetailUnit = (props) => {
const handleSave = async () => {
setConfirmLoading(true);
const validationRules = [
{ field: 'unit_name', label: 'Unit Name', required: true },
{ field: 'tag_id', label: 'Tag', required: true }, // Tambah validasi untuk tag_id
];
const validationRules = [{ field: 'unit_name', label: 'Unit Name', required: true }];
if (
validateRun(formData, validationRules, (errorMessages) => {
@@ -71,7 +47,6 @@ const DetailUnit = (props) => {
unit_name: formData.unit_name,
unit_description: formData.unit_description,
is_active: formData.is_active,
tag_id: formData.tag_id, // Tambahkan tag_id ke payload
};
const response =
@@ -115,13 +90,6 @@ const DetailUnit = (props) => {
});
};
const handleSelectChange = (name, value) => {
setFormData({
...formData,
[name]: value,
});
};
const handleStatusToggle = (checked) => {
setFormData({
...formData,
@@ -130,10 +98,6 @@ const DetailUnit = (props) => {
};
useEffect(() => {
if (props.showModal) {
loadTags(); // Panggil fungsi loadTags saat modal muncul
}
if (props.selectedData) {
setFormData(props.selectedData);
} else {
@@ -227,33 +191,6 @@ const DetailUnit = (props) => {
/>
</div>
<div style={{ marginBottom: 12 }}>
<Text strong>Tag</Text>
<Text style={{ color: 'red' }}> *</Text>
<Select
style={{ width: '100%' }}
placeholder="Pilih Tag"
value={formData.tag_id || undefined}
onChange={(value) => handleSelectChange('tag_id', value)}
disabled={props.readOnly}
loading={loadingTags}
showSearch
allowClear
optionFilterProp="children"
filterOption={(input, option) => {
const text = option.children;
if (!text) return false;
return text.toLowerCase().includes(input.toLowerCase());
}}
>
{tagList.map((tag) => (
<Select.Option key={tag.tag_id} value={tag.tag_id}>
{`${tag.tag_code || ''} - ${tag.tag_name || ''}`}
</Select.Option>
))}
</Select>
</div>
<div style={{ marginBottom: 12 }}>
<Text strong>Unit Name</Text>
<Text style={{ color: 'red' }}> *</Text>
@@ -284,4 +221,4 @@ const DetailUnit = (props) => {
);
};
export default DetailUnit;
export default DetailUnit;

View File

@@ -24,19 +24,20 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
title: 'Unit Code',
dataIndex: 'unit_code',
key: 'unit_code',
width: '20%',
width: '10%',
hidden: true,
},
{
title: 'Name',
dataIndex: 'unit_name',
key: 'unit_name',
width: '20%',
width: '15%',
},
{
title: 'Description',
dataIndex: 'unit_description',
key: 'unit_description',
width: '25%',
width: '30%',
render: (text) => text || '-',
},
{

View File

@@ -13,6 +13,13 @@ import { getAllRole, deleteRole } from '../../../api/role';
import TableList from '../../../components/Global/TableList';
const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [
{
title: 'No',
key: 'no',
width: '5%',
align: 'center',
render: (_, __, index) => index + 1,
},
{
title: 'ID',
dataIndex: 'role_id',

View File

@@ -51,6 +51,13 @@ const getRoleColor = (role_name, role_level) => {
};
const columns = (showPreviewModal, showEditModal, showDeleteDialog, showApprovalModal) => [
{
title: 'No',
key: 'no',
width: '5%',
align: 'center',
render: (_, __, index) => index + 1,
},
{
title: 'ID',
dataIndex: 'user_id',