feat: integrate sparepart management into AddBrandDevice and EditBrandDevice components
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user