repair: sparepart select brand-device

This commit is contained in:
2025-12-19 12:43:19 +07:00
parent d8a1878ab1
commit 1d7253f9a1
2 changed files with 85 additions and 206 deletions

View File

@@ -46,6 +46,11 @@ const CustomSparepartCard = ({
}
};
const truncateText = (text, maxLength = 15) => {
if (!text) return 'Unnamed';
return text.length > maxLength ? `${text.substring(0, maxLength)}...` : text;
};
const handleCardClick = () => {
if (!isReadOnly && onCardClick) {
onCardClick(sparepart);
@@ -125,210 +130,88 @@ const CustomSparepartCard = ({
return (
<>
<Card
hoverable={!!onCardClick && !isReadOnly}
style={getCardStyle()}
styles={{
body: {
padding: 0,
height: 'calc(100% - 48px)',
display: 'flex',
flexDirection: 'column'
}
<div
style={{
border: '1px solid #f0f0f0',
borderRadius: '6px',
padding: '12px 16px',
marginBottom: '8px',
backgroundColor: 'white',
cursor: onCardClick && !isReadOnly ? 'pointer' : 'default',
transition: 'all 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}
actions={getCardActions()}
onClick={handleCardClick}
>
<div style={{ display: 'flex', height: '100%' }}>
<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
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '4px' }}>
<Text
strong
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'
fontSize: '14px',
color: '#262626',
marginRight: '12px'
}}
title={sparepart.sparepart_name || sparepart.name || 'Unnamed'}
>
<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>
{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>
)}
{truncateText(sparepart.sparepart_name || sparepart.name || 'Unnamed')}
</Text>
<Tag
color={sparepart.sparepart_stok === 'Available' ? 'green' : 'red'}
style={{ fontSize: '11px', margin: 0 }}
>
{sparepart.sparepart_stok || 'Not Available'}
</Tag>
</div>
<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}
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ fontSize: '12px', color: '#666', marginRight: '4px' }}>
qty:
</Text>
<Text
style={{
margin: `0 0 ${size === 'small' ? '6px' : '8px'} 0`,
fontSize: size === 'small' ? '12px' : '14px',
fontSize: '12px',
fontWeight: 600,
lineHeight: '1.4',
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitLineClamp: size === 'small' ? 2 : 3,
WebkitBoxOrient: 'vertical'
color: '#262626'
}}
>
{sparepart.sparepart_name || sparepart.name || 'Unnamed'}
</Title>
<div style={{ marginBottom: size === 'small' ? '6px' : '8px' }}>
<div style={{ marginBottom: '4px' }}>
<Tag
color={sparepart.sparepart_stok === 'Available' ? 'green' : 'red'}
style={{
fontSize: size === 'small' ? '9px' : '10px',
padding: '0 4px',
margin: 0,
height: 'auto'
}}
>
{sparepart.sparepart_stok || 'Not Available'}
</Tag>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text
style={{
fontSize: size === 'small' ? '10px' : '11px',
fontWeight: 500,
color: '#262626',
marginRight: '4px'
}}
>
qty:
</Text>
<Text
style={{
fontSize: size === 'small' ? '10px' : '11px',
color: sparepart.sparepart_qty > 0 ? '#52c41a' : '#ff4d4f',
fontWeight: 600
}}
>
{sparepart.sparepart_qty || 0}
{sparepart.sparepart_unit ? ` ${sparepart.sparepart_unit}` : ''}
</Text>
</div>
</div>
<div style={{ marginBottom: size === 'small' ? '4px' : '6px' }}>
<Text
code
style={{
fontSize: size === 'small' ? '10px' : '11px',
backgroundColor: '#f5f5f5',
padding: '2px 6px',
borderRadius: '3px'
}}
>
{sparepart.sparepart_code || 'No code'}
</Text>
</div>
{(sparepart.sparepart_merk || sparepart.sparepart_model) && (
<div style={{
fontSize: size === 'small' ? '9px' : '10px',
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>
)}
{sparepart.sparepart_qty || 0}
</Text>
</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>
<Space size="small">
{showPreview && (
<Button
type="text"
icon={<EyeOutlined />}
size="small"
onClick={(e) => {
e.stopPropagation();
handlePreview();
}}
title="Preview"
/>
)}
{showDelete && !isReadOnly && (
<Button
type="text"
icon={<DeleteOutlined />}
size="small"
danger
onClick={(e) => {
e.stopPropagation();
onDelete?.(sparepart);
}}
title="Remove"
/>
)}
</Space>
</div>
<Modal

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import { Select, Typography, Tag, Spin, Empty, Button, Row, Col } from 'antd';
import { Select, Typography, Tag, Spin, Empty, Button } from 'antd';
import { PlusOutlined, DeleteOutlined, CheckOutlined, EyeOutlined, InfoCircleOutlined } from '@ant-design/icons';
import { getAllSparepart } from '../../../../api/sparepart';
import CustomSparepartCard from './CustomSparepartCard';
@@ -96,20 +96,16 @@ const SparepartSelect = ({
const isAlreadySelected = selectedSpareparts.some(sp => sp.sparepart_id === sparepart.sparepart_id);
return (
<Col xs={24} sm={24} md={12} lg={12} key={sparepart.sparepart_id}>
<CustomSparepartCard
sparepart={sparepart}
isSelected={isSelected}
isReadOnly={isReadOnly}
showPreview={true}
showDelete={isAlreadySelected && !isReadOnly}
onCardClick={!isAlreadySelected && !isReadOnly ? () => handleSparepartSelect(sparepart.sparepart_id) : undefined}
onDelete={() => handleRemoveSparepart(sparepart.sparepart_id)}
style={{
border: isAlreadySelected ? '2px solid #52c41a' : undefined,
}}
/>
</Col>
<CustomSparepartCard
key={sparepart.sparepart_id}
sparepart={sparepart}
isSelected={isSelected}
isReadOnly={isReadOnly}
showPreview={true}
showDelete={isAlreadySelected && !isReadOnly}
onCardClick={!isAlreadySelected && !isReadOnly ? () => handleSparepartSelect(sparepart.sparepart_id) : undefined}
onDelete={() => handleRemoveSparepart(sparepart.sparepart_id)}
/>
);
};
@@ -163,9 +159,9 @@ const SparepartSelect = ({
<Title level={5} style={{ marginBottom: 16 }}>
Selected Spareparts ({selectedSpareparts.length})
</Title>
<Row gutter={[16, 16]}>
<div>
{selectedSpareparts.map(sparepart => renderSparepartCard(sparepart, true))}
</Row>
</div>
</div>
) : (
<Empty