415 lines
22 KiB
JavaScript
415 lines
22 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import { useNavigate, useParams, useLocation } from 'react-router-dom';
|
|
import { Typography, Card, Row, Col, Tag, Button, Space, Descriptions, Divider, Steps, Collapse, Switch, Spin, Modal, Empty } from 'antd';
|
|
import { ArrowLeftOutlined, FileTextOutlined, FilePdfOutlined, EyeOutlined } from '@ant-design/icons';
|
|
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
|
|
import { NotifOk, NotifAlert } from '../../../components/Global/ToastNotif';
|
|
import { getBrandById } from '../../../api/master-brand';
|
|
|
|
const { Title, Text } = Typography;
|
|
const { Step } = Steps;
|
|
const { Panel } = Collapse;
|
|
|
|
const ViewBrandDevice = () => {
|
|
const navigate = useNavigate();
|
|
const { id } = useParams();
|
|
const location = useLocation();
|
|
const { setBreadcrumbItems } = useBreadcrumb();
|
|
const [brandData, setBrandData] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [currentStep, setCurrentStep] = useState(0);
|
|
const [activeErrorKeys, setActiveErrorKeys] = useState([]);
|
|
|
|
useEffect(() => {
|
|
const fetchBrandData = async () => {
|
|
const token = localStorage.getItem('token');
|
|
if (token) {
|
|
const savedPhase = location.state?.phase || localStorage.getItem(`brand_device_${id}_last_phase`);
|
|
if (savedPhase) {
|
|
setCurrentStep(parseInt(savedPhase));
|
|
localStorage.removeItem(`brand_device_${id}_last_phase`);
|
|
}
|
|
|
|
setBreadcrumbItems([
|
|
{ title: <Text strong style={{ fontSize: '14px' }}>• Master</Text> },
|
|
{
|
|
title: <Text strong style={{ fontSize: '14px' }} onClick={() => navigate('/master/brand-device')}>Brand Device</Text>
|
|
},
|
|
{ title: <Text strong style={{ fontSize: '14px' }}>View Brand Device</Text> }
|
|
]);
|
|
|
|
try {
|
|
setLoading(true);
|
|
const response = await getBrandById(id);
|
|
|
|
if (response && response.statusCode === 200) {
|
|
setBrandData(response.data);
|
|
} else {
|
|
NotifAlert({
|
|
icon: 'error',
|
|
title: 'Error',
|
|
message: response?.message || 'Failed to fetch brand device data',
|
|
});
|
|
}
|
|
} catch (error) {
|
|
NotifAlert({
|
|
icon: 'error',
|
|
title: 'Error',
|
|
message: error.message || 'Failed to fetch brand device data',
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
} else {
|
|
navigate('/signin');
|
|
}
|
|
};
|
|
|
|
fetchBrandData();
|
|
}, [id, setBreadcrumbItems, navigate, location.state]);
|
|
|
|
const handleFileView = (fileName, fileType) => {
|
|
localStorage.setItem(`brand_device_${id}_last_phase`, currentStep.toString());
|
|
|
|
let actualFileName = fileName;
|
|
if (fileName && fileName.includes('/')) {
|
|
const parts = fileName.split('/');
|
|
actualFileName = parts[parts.length - 1];
|
|
}
|
|
|
|
const encodedFileName = encodeURIComponent(actualFileName);
|
|
const fileTypeParam = fileType === 'image' ? 'image' : 'pdf';
|
|
const navigationPath = `/master/brand-device/view/${id}/files/${fileTypeParam}/${encodedFileName}`;
|
|
navigate(navigationPath);
|
|
};
|
|
|
|
const renderStepContent = () => {
|
|
if (currentStep === 0) {
|
|
return (
|
|
<div>
|
|
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 16 }}>
|
|
<Text strong>Status</Text>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 24 }}>
|
|
<div style={{ width: 44, height: 24, backgroundColor: (brandData || {}).is_active ? '#23A55A' : '#bfbfbf', borderRadius: 12, marginRight: 8, position: 'relative' }}>
|
|
<div style={{
|
|
width: 20,
|
|
height: 20,
|
|
backgroundColor: 'white',
|
|
borderRadius: '50%',
|
|
position: 'absolute',
|
|
top: 2,
|
|
left: (brandData || {}).is_active ? 22 : 2,
|
|
transition: 'left 0.3s ease',
|
|
boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
|
|
}}></div>
|
|
</div>
|
|
<Text>{(brandData || {}).is_active ? 'Running' : 'Offline'}</Text>
|
|
</div>
|
|
|
|
<div style={{ marginBottom: 16 }}>
|
|
<Text strong>Brand Code</Text>
|
|
</div>
|
|
<div style={{ marginBottom: 24 }}>
|
|
<div style={{
|
|
padding: '8px 12px',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '6px',
|
|
backgroundColor: '#f5f5f5',
|
|
color: '#000000'
|
|
}}>
|
|
{brandData?.brand_code || (loading ? 'Loading...' : '-')}
|
|
</div>
|
|
</div>
|
|
|
|
<Row gutter={16}>
|
|
<Col span={12}>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<Text strong>Brand Name</Text>
|
|
</div>
|
|
<div style={{ marginBottom: 24 }}>
|
|
<div style={{
|
|
padding: '8px 12px',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '6px',
|
|
backgroundColor: 'white',
|
|
minHeight: '32px'
|
|
}}>
|
|
{brandData?.brand_name || (loading ? 'Loading...' : '-')}
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
<Col span={12}>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<Text strong>Manufacture</Text>
|
|
</div>
|
|
<div style={{ marginBottom: 24 }}>
|
|
<div style={{
|
|
padding: '8px 12px',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '6px',
|
|
backgroundColor: 'white',
|
|
minHeight: '32px'
|
|
}}>
|
|
{brandData?.brand_manufacture || (loading ? 'Loading...' : '-')}
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
|
|
<Row gutter={16}>
|
|
<Col span={12}>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<Text strong>Brand Type</Text>
|
|
</div>
|
|
<div style={{ marginBottom: 24 }}>
|
|
<div style={{
|
|
padding: '8px 12px',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '6px',
|
|
backgroundColor: 'white',
|
|
minHeight: '32px'
|
|
}}>
|
|
{brandData?.brand_type || (loading ? 'Loading...' : '-')}
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
<Col span={12}>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<Text strong>Model</Text>
|
|
</div>
|
|
<div style={{ marginBottom: 24 }}>
|
|
<div style={{
|
|
padding: '8px 12px',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '6px',
|
|
backgroundColor: 'white',
|
|
minHeight: '32px'
|
|
}}>
|
|
{brandData?.brand_model || (loading ? 'Loading...' : '-')}
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (currentStep === 1) {
|
|
const errorCodes = brandData?.error_code || [];
|
|
|
|
return (
|
|
<div>
|
|
<Title level={5} style={{ marginBottom: 16 }}>
|
|
Error Codes ({errorCodes.length})
|
|
</Title>
|
|
|
|
{errorCodes.length > 0 ? (
|
|
<Collapse
|
|
activeKey={activeErrorKeys}
|
|
onChange={setActiveErrorKeys}
|
|
style={{ marginBottom: 16 }}
|
|
>
|
|
{errorCodes.map((errorCode, index) => (
|
|
<Panel
|
|
key={index}
|
|
header={
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
|
|
<div>
|
|
<Text strong style={{ fontSize: '14px' }}>{errorCode.error_code}</Text>
|
|
<Text style={{ marginLeft: 8, fontSize: '12px', color: '#666' }}>
|
|
- {errorCode.error_code_name}
|
|
</Text>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
<Tag color={errorCode.is_active ? 'green' : 'red'} style={{ margin: 0 }}>
|
|
{errorCode.is_active ? 'Active' : 'Inactive'}
|
|
</Tag>
|
|
<Text style={{ fontSize: '12px', color: '#999' }}>
|
|
{errorCode.solution?.length || 0} solution(s)
|
|
</Text>
|
|
</div>
|
|
</div>
|
|
}
|
|
>
|
|
<div style={{ padding: '12px 0' }}>
|
|
<div style={{ marginBottom: 16 }}>
|
|
<Text type="secondary">Description:</Text>
|
|
<div style={{ marginTop: 4 }}>
|
|
<Text>{errorCode.error_code_description || 'No description'}</Text>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<Text strong>Solutions:</Text>
|
|
{errorCode.solution && errorCode.solution.length > 0 ? (
|
|
<div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
{errorCode.solution.map((solution) => (
|
|
<Card
|
|
key={solution.brand_code_solution_id}
|
|
size="small"
|
|
style={{
|
|
backgroundColor: '#fafafa',
|
|
border: '1px solid #f0f0f0'
|
|
}}
|
|
>
|
|
<Row justify="space-between" align="middle">
|
|
<Col>
|
|
<Space>
|
|
{solution.type_solution === 'pdf' ? (
|
|
<FilePdfOutlined style={{ color: '#ff4d4f', fontSize: '16px' }} />
|
|
) : solution.type_solution === 'image' ? (
|
|
<EyeOutlined style={{ color: '#1890ff', fontSize: '16px' }} />
|
|
) : (
|
|
<FileTextOutlined style={{ color: '#1890ff', fontSize: '16px' }} />
|
|
)}
|
|
<Text strong style={{ fontSize: '13px' }}>{solution.solution_name}</Text>
|
|
</Space>
|
|
</Col>
|
|
<Col>
|
|
<Tag
|
|
color={
|
|
solution.type_solution === 'pdf' ? 'red' :
|
|
solution.type_solution === 'image' ? 'blue' :
|
|
'default'
|
|
}
|
|
style={{ fontSize: '11px' }}
|
|
>
|
|
{solution.type_solution ? solution.type_solution.toUpperCase() : 'TEXT'}
|
|
</Tag>
|
|
</Col>
|
|
</Row>
|
|
|
|
<div style={{ marginTop: 8 }}>
|
|
{solution.type_solution === 'text' ? (
|
|
<Text style={{ fontSize: '12px', color: '#666' }}>
|
|
{solution.text_solution}
|
|
</Text>
|
|
) : (
|
|
<div>
|
|
<Text style={{ fontSize: '12px', color: '#666' }}>
|
|
File: {solution.path_document || solution.path_solution || 'Document'}
|
|
</Text>
|
|
{(solution.path_document || solution.path_solution) && (
|
|
<Button
|
|
type="link"
|
|
size="small"
|
|
onClick={() => {
|
|
handleFileView(
|
|
(solution.path_document || solution.path_solution || solution.file_upload_name || solution.solution_name || 'Document')?.toString(),
|
|
solution.type_solution || 'pdf'
|
|
);
|
|
}}
|
|
style={{ padding: 0, height: 'auto', fontSize: '12px', marginLeft: 8 }}
|
|
>
|
|
View
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div style={{ marginTop: 12 }}>
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>No solutions available</Text>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Panel>
|
|
))}
|
|
</Collapse>
|
|
) : (
|
|
!loading && (
|
|
<Empty
|
|
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
description={
|
|
<Text type="secondary">No error codes available</Text>
|
|
}
|
|
/>
|
|
)
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Card>
|
|
<Row justify="space-between" align="middle">
|
|
<Col>
|
|
<Title level={4} style={{ margin: 0 }}>View Brand Device</Title>
|
|
</Col>
|
|
<Col>
|
|
<Space>
|
|
<Button
|
|
icon={<ArrowLeftOutlined />}
|
|
onClick={() => navigate('/master/brand-device')}
|
|
>
|
|
Kembali
|
|
</Button>
|
|
</Space>
|
|
</Col>
|
|
</Row>
|
|
<Divider />
|
|
<Steps current={currentStep} style={{ marginBottom: 24 }}>
|
|
<Step title="Brand Device Details" />
|
|
<Step title="Error Codes & Solutions" />
|
|
</Steps>
|
|
|
|
<div style={{ position: 'relative', marginTop: 24 }}>
|
|
{loading && (
|
|
<div style={{
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundColor: 'rgba(255, 255, 255, 0.6)',
|
|
backdropFilter: 'blur(0.8px)',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
zIndex: 5,
|
|
borderRadius: '8px'
|
|
}}>
|
|
<Spin size="large" />
|
|
</div>
|
|
)}
|
|
|
|
<div style={{ filter: loading ? 'blur(0.5px)' : 'none', transition: 'filter 0.3s ease' }}>
|
|
{renderStepContent()}
|
|
</div>
|
|
</div>
|
|
|
|
<Divider />
|
|
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
|
|
{currentStep > 0 && (
|
|
<Button
|
|
onClick={() => setCurrentStep(currentStep - 1)}
|
|
style={{ marginRight: 8 }}
|
|
>
|
|
Kembali
|
|
</Button>
|
|
)}
|
|
{currentStep < 1 && (
|
|
<Button
|
|
type="primary"
|
|
onClick={() => setCurrentStep(currentStep + 1)}
|
|
style={{ backgroundColor: '#23a55a', borderColor: '#23a55a' }}
|
|
>
|
|
Lanjut
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
export default ViewBrandDevice; |