update ui brand device
This commit is contained in:
483
src/pages/master/brandDevice/component/CustomSparepartCard.jsx
Normal file
483
src/pages/master/brandDevice/component/CustomSparepartCard.jsx
Normal file
@@ -0,0 +1,483 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card, Typography, Tag, Button, Modal, Row, Col, Space } from 'antd';
|
||||
import { EyeOutlined, DeleteOutlined, CheckOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
|
||||
const CustomSparepartCard = ({
|
||||
sparepart,
|
||||
isSelected = false,
|
||||
isReadOnly = false,
|
||||
showPreview = true,
|
||||
showDelete = false,
|
||||
onPreview,
|
||||
onDelete,
|
||||
onCardClick,
|
||||
loading = false,
|
||||
size = 'small',
|
||||
style = {},
|
||||
}) => {
|
||||
const [previewModalVisible, setPreviewModalVisible] = useState(false);
|
||||
|
||||
// Construct image source with proper fallback
|
||||
const getImageSrc = () => {
|
||||
if (sparepart.sparepart_foto) {
|
||||
if (sparepart.sparepart_foto.startsWith('http')) {
|
||||
return sparepart.sparepart_foto;
|
||||
} else {
|
||||
const fileName = sparepart.sparepart_foto.split('/').pop();
|
||||
if (fileName === 'defaultSparepartImg.jpg') {
|
||||
return `/assets/defaultSparepartImg.jpg`;
|
||||
} else {
|
||||
const token = localStorage.getItem('token');
|
||||
const baseURL = import.meta.env.VITE_API_SERVER || '';
|
||||
return `${baseURL}/file-uploads/images/${encodeURIComponent(fileName)}${token ? `?token=${encodeURIComponent(token)}` : ''}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'https://via.placeholder.com/150';
|
||||
};
|
||||
|
||||
const handlePreview = () => {
|
||||
if (onPreview) {
|
||||
onPreview(sparepart);
|
||||
} else {
|
||||
setPreviewModalVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCardClick = () => {
|
||||
if (!isReadOnly && onCardClick) {
|
||||
onCardClick(sparepart);
|
||||
}
|
||||
};
|
||||
|
||||
const getCardActions = () => {
|
||||
const actions = [];
|
||||
|
||||
// Preview button
|
||||
if (showPreview) {
|
||||
actions.push(
|
||||
<Button
|
||||
key="preview"
|
||||
type="text"
|
||||
icon={<EyeOutlined />}
|
||||
title="Lihat Detail"
|
||||
style={{ color: '#1890ff' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePreview();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Delete button without confirmation
|
||||
if (showDelete && !isReadOnly) {
|
||||
actions.push(
|
||||
<Button
|
||||
key="delete"
|
||||
type="text"
|
||||
icon={<DeleteOutlined />}
|
||||
title="Hapus"
|
||||
style={{ color: '#ff4d4f' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete?.(sparepart);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
||||
// Get card styling based on size
|
||||
const getCardStyle = () => {
|
||||
const baseStyle = {
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden',
|
||||
border: isSelected ? '2px solid #1890ff' : '1px solid #E0E0E0',
|
||||
cursor: isReadOnly ? 'default' : 'pointer',
|
||||
position: 'relative',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.06)',
|
||||
transition: 'all 0.3s ease'
|
||||
};
|
||||
|
||||
switch (size) {
|
||||
case 'small':
|
||||
return {
|
||||
...baseStyle,
|
||||
height: '180px',
|
||||
minHeight: '180px'
|
||||
};
|
||||
case 'large':
|
||||
return {
|
||||
...baseStyle,
|
||||
height: '280px',
|
||||
minHeight: '280px'
|
||||
};
|
||||
default:
|
||||
return {
|
||||
...baseStyle,
|
||||
height: '220px',
|
||||
minHeight: '220px'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card
|
||||
hoverable={!!onCardClick && !isReadOnly}
|
||||
style={getCardStyle()}
|
||||
bodyStyle={{
|
||||
padding: 0,
|
||||
height: 'calc(100% - 48px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}}
|
||||
actions={getCardActions()}
|
||||
onClick={handleCardClick}
|
||||
>
|
||||
<div style={{ display: 'flex', height: '100%' }}>
|
||||
{/* Image Section */}
|
||||
<div style={{
|
||||
width: size === 'small' ? '90px' : '110px',
|
||||
flexShrink: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: size === 'small' ? '12px' : '16px',
|
||||
backgroundColor: '#fafafa',
|
||||
borderRight: '1px solid #f0f0f0',
|
||||
position: 'relative'
|
||||
}}>
|
||||
{sparepart.sparepart_item_type && (
|
||||
<Tag
|
||||
color="blue"
|
||||
style={{
|
||||
marginBottom: '8px',
|
||||
fontSize: '10px',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
{sparepart.sparepart_item_type}
|
||||
</Tag>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
width: size === 'small' ? '65px' : '75px',
|
||||
height: size === 'small' ? '65px' : '75px',
|
||||
backgroundColor: '#f0f0f0',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '1px solid #e8e8e8'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={getImageSrc()}
|
||||
alt={sparepart.sparepart_name || 'Sparepart'}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover'
|
||||
}}
|
||||
onError={(e) => {
|
||||
e.target.src = 'https://via.placeholder.com/75';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Selection Indicator */}
|
||||
{isSelected && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '8px',
|
||||
right: '8px',
|
||||
backgroundColor: '#52c41a',
|
||||
borderRadius: '50%',
|
||||
width: '18px',
|
||||
height: '18px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.15)',
|
||||
zIndex: 2
|
||||
}}
|
||||
>
|
||||
<CheckOutlined style={{ color: 'white', fontSize: '10px' }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content Section */}
|
||||
<div style={{
|
||||
flex: 1,
|
||||
padding: size === 'small' ? '12px' : '16px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
<div style={{ flex: 1, overflow: 'hidden' }}>
|
||||
<Title
|
||||
level={size === 'small' ? 5 : 4}
|
||||
style={{
|
||||
margin: `0 0 ${size === 'small' ? '6px' : '8px'} 0`,
|
||||
fontSize: size === 'small' ? '12px' : '14px',
|
||||
fontWeight: 600,
|
||||
lineHeight: '1.4',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: size === 'small' ? 2 : 3,
|
||||
WebkitBoxOrient: 'vertical'
|
||||
}}
|
||||
>
|
||||
{sparepart.sparepart_name || sparepart.name || 'Unnamed'}
|
||||
</Title>
|
||||
|
||||
{size !== 'small' && (
|
||||
<>
|
||||
<Text
|
||||
type="secondary"
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
display: 'block',
|
||||
marginBottom: '6px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}}
|
||||
>
|
||||
Stock: {sparepart.sparepart_stock || sparepart.sparepart_stok || '0'} {sparepart.sparepart_unit || 'pcs'}
|
||||
</Text>
|
||||
|
||||
<div style={{ marginBottom: '6px' }}>
|
||||
<Text
|
||||
code
|
||||
style={{
|
||||
fontSize: '11px',
|
||||
backgroundColor: '#f5f5f5',
|
||||
padding: '2px 6px',
|
||||
borderRadius: '3px'
|
||||
}}
|
||||
>
|
||||
{sparepart.sparepart_code || 'No code'}
|
||||
</Text>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{size === 'small' && (
|
||||
<Text
|
||||
code
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
backgroundColor: '#f5f5f5',
|
||||
display: 'block',
|
||||
marginBottom: '4px'
|
||||
}}
|
||||
>
|
||||
{sparepart.sparepart_code || 'No code'}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{(sparepart.sparepart_merk || sparepart.sparepart_model) && (
|
||||
<div style={{
|
||||
fontSize: size === 'small' ? '10px' : '11px',
|
||||
color: '#666',
|
||||
lineHeight: '1.4',
|
||||
marginBottom: '4px'
|
||||
}}>
|
||||
{sparepart.sparepart_merk && (
|
||||
<div>Brand: {sparepart.sparepart_merk}</div>
|
||||
)}
|
||||
{sparepart.sparepart_model && (
|
||||
<div>Model: {sparepart.sparepart_model}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Text
|
||||
type="secondary"
|
||||
style={{
|
||||
fontSize: size === 'small' ? '9px' : '10px',
|
||||
marginTop: 'auto',
|
||||
paddingTop: '4px',
|
||||
borderTop: '1px solid #f0f0f0'
|
||||
}}
|
||||
>
|
||||
{sparepart.updated_at && dayjs(sparepart.updated_at).format('DD MMM YYYY')}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Preview Modal */}
|
||||
<Modal
|
||||
title="Sparepart Details"
|
||||
open={previewModalVisible}
|
||||
onCancel={() => setPreviewModalVisible(false)}
|
||||
footer={[
|
||||
<Button key="close" onClick={() => setPreviewModalVisible(false)}>
|
||||
Close
|
||||
</Button>
|
||||
]}
|
||||
width={700}
|
||||
centered
|
||||
bodyStyle={{ padding: '24px' }}
|
||||
>
|
||||
<Row gutter={[24, 24]}>
|
||||
<Col span={8}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: '#f0f0f0',
|
||||
width: '200px',
|
||||
height: '200px',
|
||||
margin: '0 auto 16px',
|
||||
position: 'relative',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
border: '1px solid #E0E0E0',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={getImageSrc()}
|
||||
alt={sparepart.sparepart_name || 'Sparepart'}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover'
|
||||
}}
|
||||
onError={(e) => {
|
||||
e.target.src = 'https://via.placeholder.com/200x200/d9d9d9/666666?text=No+Image';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{sparepart.sparepart_item_type && (
|
||||
<Tag color="blue" style={{ marginTop: '12px' }}>
|
||||
{sparepart.sparepart_item_type}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col span={16}>
|
||||
<div>
|
||||
<Title level={3} style={{ marginBottom: '16px' }}>
|
||||
{sparepart.sparepart_name || 'Unnamed'}
|
||||
</Title>
|
||||
|
||||
<Row gutter={[16, 16]} style={{ marginBottom: '16px' }}>
|
||||
<Col span={12}>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
<Text strong style={{ fontSize: '16px', color: '#262626' }}>
|
||||
Code:
|
||||
</Text>
|
||||
<Text style={{ fontSize: '16px', marginLeft: '8px' }}>
|
||||
{sparepart.sparepart_code || 'N/A'}
|
||||
</Text>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
<Text strong style={{ fontSize: '16px', color: '#262626' }}>
|
||||
Status:
|
||||
</Text>
|
||||
<Tag
|
||||
color={sparepart.is_active ? 'green' : 'red'}
|
||||
style={{ marginLeft: '8px', fontSize: '14px' }}
|
||||
>
|
||||
{sparepart.is_active ? 'Active' : 'Inactive'}
|
||||
</Tag>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{sparepart.sparepart_description && (
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Text strong style={{ fontSize: '16px', color: '#262626' }}>
|
||||
Description:
|
||||
</Text>
|
||||
<Text style={{ fontSize: '16px', marginLeft: '8px' }}>
|
||||
{sparepart.sparepart_description}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Text strong style={{ fontSize: '16px', color: '#262626' }}>
|
||||
Stock:
|
||||
</Text>
|
||||
<Text style={{ fontSize: '16px', marginLeft: '8px' }}>
|
||||
{sparepart.sparepart_stock || sparepart.sparepart_stok || '0'}
|
||||
{sparepart.sparepart_unit ? ` ${sparepart.sparepart_unit}` : ' units'}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Row gutter={[16, 16]} style={{ marginBottom: '16px' }}>
|
||||
{sparepart.sparepart_merk && (
|
||||
<Col span={8}>
|
||||
<div>
|
||||
<Text strong style={{ fontSize: '14px', color: '#262626' }}>
|
||||
Brand:
|
||||
</Text>
|
||||
<Text style={{ fontSize: '14px', marginLeft: '8px' }}>
|
||||
{sparepart.sparepart_merk}
|
||||
</Text>
|
||||
</div>
|
||||
</Col>
|
||||
)}
|
||||
{sparepart.sparepart_model && (
|
||||
<Col span={8}>
|
||||
<div>
|
||||
<Text strong style={{ fontSize: '14px', color: '#262626' }}>
|
||||
Model:
|
||||
</Text>
|
||||
<Text style={{ fontSize: '14px', marginLeft: '8px' }}>
|
||||
{sparepart.sparepart_model}
|
||||
</Text>
|
||||
</div>
|
||||
</Col>
|
||||
)}
|
||||
{sparepart.sparepart_unit && (
|
||||
<Col span={8}>
|
||||
<div>
|
||||
<Text strong style={{ fontSize: '14px', color: '#262626' }}>
|
||||
Unit:
|
||||
</Text>
|
||||
<Text style={{ fontSize: '14px', marginLeft: '8px' }}>
|
||||
{sparepart.sparepart_unit}
|
||||
</Text>
|
||||
</div>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
{sparepart.updated_at && (
|
||||
<div style={{ marginTop: '24px', paddingTop: '16px', borderTop: '1px solid #f0f0f0' }}>
|
||||
<Text type="secondary" style={{ fontSize: '14px' }}>
|
||||
Last updated: {dayjs(sparepart.updated_at).format('DD MMMM YYYY, HH:mm')}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomSparepartCard;
|
||||
Reference in New Issue
Block a user