feat: integrate sparepart management into AddBrandDevice and EditBrandDevice components

This commit is contained in:
2025-11-24 21:25:24 +07:00
parent b05e3fe5d9
commit 3e384f89b1
5 changed files with 440 additions and 322 deletions

View File

@@ -1,18 +1,7 @@
import React, { useState, useEffect } from 'react';
import {
Form,
Input,
Button,
Divider,
Typography,
Switch,
Space,
Card,
Upload,
message,
} from 'antd';
import { PlusOutlined, DeleteOutlined, UploadOutlined } from '@ant-design/icons';
import { uploadFile } from '../../../../api/file-uploads';
import React, { useState } from 'react';
import { Form, Card, Typography, Divider, Button } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import SparepartField from './SparepartField';
const { Text } = Typography;
@@ -21,238 +10,67 @@ const SparepartForm = ({
sparepartFields,
onAddSparepartField,
onRemoveSparepartField,
onSparepartTypeChange,
onSparepartStatusChange,
onSparepartImageUpload,
onSparepartImageRemove,
sparepartImages = {},
isReadOnly = false,
spareparts = [],
onSparepartChange
}) => {
const [fieldStatuses, setFieldStatuses] = useState({});
const [sparepartList, setSparepartList] = useState([]);
// Watch form values for each field
const getFieldValue = (fieldName) => {
try {
const values = sparepartForm?.getFieldsValue();
return values?.sparepart_items?.[fieldName]?.status ?? true;
} catch {
return true;
const handleSparepartChange = (list) => {
setSparepartList(list);
if (onSparepartChange) {
onSparepartChange(list);
}
};
useEffect(() => {
// Update field statuses when form changes
const newStatuses = {};
sparepartFields.forEach((field) => {
newStatuses[field.key] = getFieldValue(field.key);
});
setFieldStatuses(newStatuses);
}, [sparepartFields, sparepartForm]);
const handleImageUpload = async (fieldKey, file) => {
// Check if file is an image
const isImage = file.type.startsWith('image/');
if (!isImage) {
message.error('You can only upload image files!');
return Upload.LIST_IGNORE;
}
// Check file size (max 2MB)
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must be smaller than 2MB!');
return Upload.LIST_IGNORE;
}
try {
const fileExtension = file.name.split('.').pop().toLowerCase();
const isImageFile = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'].includes(
fileExtension
);
const fileType = isImageFile ? 'image' : 'pdf';
const folder = 'images';
const uploadResponse = await uploadFile(file, folder);
const imagePath =
uploadResponse.data?.path_icon || uploadResponse.data?.path_solution || '';
if (imagePath) {
onSparepartImageUpload &&
onSparepartImageUpload(fieldKey, {
name: file.name,
uploadPath: imagePath,
fileExtension,
isImage: isImageFile,
size: file.size,
});
message.success(`${file.name} uploaded successfully!`);
} else {
message.error(`Failed to upload ${file.name}`);
}
} catch (error) {
console.error('Error uploading image:', error);
message.error(`Failed to upload ${file.name}`);
}
};
const handleImageRemove = (fieldKey) => {
onSparepartImageRemove && onSparepartImageRemove(fieldKey);
};
return (
<div>
<Text strong style={{ marginBottom: 16, display: 'block' }}>
{isReadOnly ? 'Sparepart Details' : 'Tambah Sparepart'}
</Text>
<Form
form={sparepartForm}
layout="vertical"
initialValues={{
sparepart_status_0: true,
sparepart_type_0: 'required',
}}
>
{/* Dynamic Sparepart Fields */}
<Divider orientation="left">Sparepart Items</Divider>
{sparepartFields.map((field, index) => (
<Card
<SparepartField
key={field.key}
size="small"
style={{ marginBottom: 16 }}
title={
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Text strong>Sparepart {index + 1}</Text>
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
{!isReadOnly && sparepartFields.length > 1 && (
<div style={{ textAlign: 'right' }}>
<Button
type="text"
danger
icon={<DeleteOutlined />}
onClick={() => onRemoveSparepartField(field.key)}
/>
</div>
)}
</div>
</div>
}
>
<Form layout="vertical" style={{ border: 'none' }}>
{/* Sparepart Name */}
<Form.Item
name={[field.name, 'name']}
rules={[{ required: true, message: 'Sparepart name wajib diisi!' }]}
>
<Input placeholder="Enter sparepart name" disabled={isReadOnly} />
</Form.Item>
{/* Description */}
<Form.Item name={[field.name, 'description']}>
<Input.TextArea
placeholder="Enter sparepart description (optional)"
rows={2}
disabled={isReadOnly}
/>
</Form.Item>
{/* Image Upload */}
<Form.Item label="Sparepart Image">
{!isReadOnly ? (
<Upload
beforeUpload={(file) => handleImageUpload(field.key, file)}
showUploadList={false}
accept="image/*"
style={{ width: '100%' }}
>
<Button icon={<UploadOutlined />} style={{ width: '100%' }}>
Upload Sparepart Image
</Button>
</Upload>
) : (
<div
style={{
padding: '8px 12px',
border: '1px solid #d9d9d9',
borderRadius: 4,
}}
>
<Text type="secondary">No upload allowed</Text>
</div>
)}
{sparepartImages[field.key] && (
<div style={{ marginTop: 8 }}>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
}}
>
<img
src={sparepartImages[field.key].uploadPath}
alt="Sparepart Image"
style={{
width: 50,
height: 50,
objectFit: 'cover',
border: '1px solid #d9d9d9',
borderRadius: 4,
}}
/>
<div>
<Text style={{ fontSize: 12 }}>
{sparepartImages[field.key].name}
</Text>
<br />
<Text type="secondary" style={{ fontSize: 10 }}>
Size:{' '}
{(
sparepartImages[field.key].size / 1024
).toFixed(1)}{' '}
KB
</Text>
</div>
{!isReadOnly && (
<Button
type="text"
danger
size="small"
onClick={() => handleImageRemove(field.key)}
>
Remove
</Button>
)}
</div>
</div>
)}
</Form.Item>
</Form>
</Card>
fieldKey={field.key}
fieldName={field.name}
index={index}
sparepartStatus={field.status}
onRemove={() => onRemoveSparepartField(field.key)}
isReadOnly={isReadOnly}
canRemove={sparepartFields.length > 1}
spareparts={sparepartList}
onSparepartChange={handleSparepartChange}
/>
))}
{!isReadOnly && (
<Form.Item>
<Button
type="dashed"
onClick={() => onAddSparepartField()}
icon={<PlusOutlined />}
style={{ width: '100%' }}
>
+ Add Sparepart
</Button>
</Form.Item>
<>
<Form.Item>
<Button
type="dashed"
onClick={onAddSparepartField}
icon={<PlusOutlined />}
style={{ width: '100%' }}
>
+ Add Sparepart
</Button>
</Form.Item>
<div style={{ marginTop: 16 }}>
<Text type="secondary">
* Sparepart is optional and can be added for each error code if needed.
</Text>
</div>
</>
)}
</Form>
</div>
);
};
export default SparepartForm;
export default SparepartForm;