feat: add image upload functionality and stock update feature in sparepart components

This commit is contained in:
2025-11-26 17:16:09 +07:00
parent 14f8a5d472
commit afcb85a323
4 changed files with 344 additions and 215 deletions

View File

@@ -1,7 +1,9 @@
import React, { useState, useEffect } from 'react';
import { Modal, Input, Divider, Typography, Switch, Button, ConfigProvider, message } from 'antd';
import { Modal, Input, Select, Divider, Typography, Switch, Button, ConfigProvider, Upload, message, Row, Col } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
import { createSparepart, updateSparepart } from '../../../../api/sparepart';
import { uploadFile } from '../../../../api/file-uploads';
import { validateRun } from '../../../../Utils/validate';
const { Text } = Typography;
@@ -9,6 +11,7 @@ const { TextArea } = Input;
const DetailSparepart = (props) => {
const [confirmLoading, setConfirmLoading] = useState(false);
const [imageFile, setImageFile] = useState(null);
const defaultData = {
sparepart_id: '',
@@ -18,7 +21,8 @@ const DetailSparepart = (props) => {
sparepart_item_type: '',
sparepart_unit: '',
sparepart_merk: '',
sparepart_stok: '',
sparepart_stok: '0',
image_url: '',
};
const [formData, setFormData] = useState(defaultData);
@@ -26,18 +30,14 @@ const DetailSparepart = (props) => {
const handleCancel = () => {
props.setSelectedData(null);
props.setActionMode('list');
setImageFile(null);
};
const handleSave = async () => {
setConfirmLoading(true);
// Daftar aturan validasi
const validationRules = [
{ field: 'sparepart_name', label: 'Sparepart Name', required: true },
{ field: 'sparepart_model', label: 'Sparepart Model', required: true },
{ field: 'sparepart_unit', label: 'Sparepart Unit', required: true },
{ field: 'sparepart_merk', label: 'Sparepart Merk', required: true },
{ field: 'sparepart_stok', label: 'Sparepart Stok', required: true },
];
if (
@@ -53,33 +53,42 @@ const DetailSparepart = (props) => {
return;
try {
let imageUrl = formData.image_url; // Keep existing image url if not changed
if (imageFile) {
const uploadResponse = await uploadFile(imageFile, 'images');
if (uploadResponse && uploadResponse.file_url) {
imageUrl = uploadResponse.file_url;
} else {
throw new Error('Image upload failed or did not return a URL.');
}
}
const payload = {
sparepart_name: formData.sparepart_name,
sparepart_description: formData.sparepart_description,
sparepart_model: formData.sparepart_model,
sparepart_item_type: formData.sparepart_item_type,
sparepart_unit: formData.sparepart_unit,
sparepart_stok: formData.sparepart_stok || '0',
image_url: imageUrl,
sparepart_merk: formData.sparepart_merk,
sparepart_stok: formData.sparepart_stok,
sparepart_model: formData.sparepart_model,
sparepart_description: formData.sparepart_description,
sparepart_unit: formData.sparepart_unit, // This field was in the old form, keep it
};
const response = formData.sparepart_id
? await updateSparepart(formData.sparepart_id, payload)
: await createSparepart(payload);
// Check if response is successful
if (response && (response.statusCode === 200 || response.statusCode === 201)) {
const sparepartName = response.data?.sparepart_name || formData.sparepart_name;
NotifOk({
icon: 'success',
title: 'Berhasil',
message: `Data Sparepart "${sparepartName}" berhasil ${
message: `Data Sparepart berhasil ${
formData.sparepart_id ? 'diubah' : 'ditambahkan'
}.`,
});
props.setActionMode('list');
setImageFile(null);
} else {
NotifAlert({
icon: 'error',
@@ -107,28 +116,44 @@ const DetailSparepart = (props) => {
});
};
const handleFieldChange = (name, value) => {
const handleSelectChange = (name, value) => {
setFormData({
...formData,
[name]: value,
});
};
const handleStatusToggle = (event) => {
const isChecked = event;
setFormData({
...formData,
is_active: isChecked ? true : false,
});
};
useEffect(() => {
if (props.selectedData) {
setFormData(props.selectedData);
} else {
setFormData(defaultData);
}
setImageFile(null);
}, [props.showModal, props.selectedData, props.actionMode]);
const uploadProps = {
beforeUpload: file => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
if(isJpgOrPng && isLt2M) {
setImageFile(file);
}
return false; // Prevent auto-upload
},
onRemove: () => {
setImageFile(null);
},
maxCount: 1,
};
return (
<Modal
@@ -187,99 +212,111 @@ const DetailSparepart = (props) => {
>
{formData && (
<div>
<div hidden>
<Text strong>Sparepart ID</Text>
<Input
name="sparepart_id"
value={formData.sparepart_id}
onChange={handleInputChange}
disabled
/>
</div>
<Row gutter={[16, 16]}>
<Col span={12}>
<Text strong>Sparepart Name</Text>
<Text style={{ color: 'red' }}> *</Text>
<Input
name="sparepart_name"
value={formData.sparepart_name}
onChange={handleInputChange}
placeholder="Enter Sparepart Name"
readOnly={props.readOnly}
/>
</Col>
<Col span={12}>
<Text strong>Item Type</Text>
<Select
name="sparepart_item_type"
value={formData.sparepart_item_type}
onChange={(value) => handleSelectChange('sparepart_item_type', value)}
placeholder="Select Item Type"
disabled={props.readOnly}
style={{ width: '100%' }}
>
<Select.Option value="air dryer">air dryer</Select.Option>
<Select.Option value="compressor">compressor</Select.Option>
</Select>
</Col>
</Row>
<div style={{ marginBottom: 12 }}>
<Text strong>Sparepart Name</Text>
<Text style={{ color: 'red' }}> *</Text>
<Input
name="sparepart_name"
value={formData.sparepart_name}
onChange={handleInputChange}
placeholder="Enter Sparepart Name"
readOnly={props.readOnly}
/>
</div>
<Row gutter={[16, 16]}>
<Col span={12}>
<Text strong>Stock</Text>
<Input
name="sparepart_stok"
value={formData.sparepart_stok}
onChange={handleInputChange}
placeholder="Initial stock quantity"
readOnly={props.readOnly}
type="number"
/>
</Col>
<Col span={12}>
<Text strong>Unit</Text>
<Input
name="sparepart_unit"
value={formData.sparepart_unit}
onChange={handleInputChange}
placeholder="e.g., pcs, box, roll"
readOnly={props.readOnly}
/>
</Col>
</Row>
<div style={{ marginBottom: 12 }}>
<Text strong>Sparepart Description</Text>
<TextArea
name="sparepart_description"
value={formData.sparepart_description}
onChange={handleInputChange}
placeholder="Enter Sparepart Description (Optional)"
readOnly={props.readOnly}
rows={4}
/>
</div>
<Row gutter={[16, 16]}>
<Col span={24}>
<Text strong>Foto</Text>
<div>
<Upload {...uploadProps}>
<Button icon={<UploadOutlined />} disabled={props.readOnly}>Select File</Button>
</Upload>
{formData.image_url && !imageFile && (
<Text type="secondary" style={{ display: 'block', marginTop: '8px' }}>
Current image: <a href={formData.image_url} target="_blank" rel="noopener noreferrer">{formData.image_url.split('/').pop()}</a>
</Text>
)}
</div>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Text strong>Brand</Text>
<Input
name="sparepart_merk"
value={formData.sparepart_merk}
onChange={handleInputChange}
placeholder="Enter Brand (Optional)"
readOnly={props.readOnly}
/>
</Col>
<Col span={12}>
<Text strong>Model</Text>
<Input
name="sparepart_model"
value={formData.sparepart_model}
onChange={handleInputChange}
placeholder="Enter Model (Optional)"
readOnly={props.readOnly}
/>
</Col>
</Row>
<div style={{ marginBottom: 12 }}>
<Text strong>Sparepart Model</Text>
<Text style={{ color: 'red' }}> *</Text>
<Input
name="sparepart_model"
value={formData.sparepart_model}
onChange={handleInputChange}
placeholder="Enter Sparepart Model"
readOnly={props.readOnly}
/>
</div>
<div style={{ marginBottom: 12 }}>
<Text strong>Sparepart Item Type</Text>
<Input
name="sparepart_item_type"
value={formData.sparepart_item_type}
onChange={handleInputChange}
placeholder="Enter Sparepart Item Type"
readOnly={props.readOnly}
/>
</div>
<div style={{ marginBottom: 12 }}>
<Text strong>Sparepart Unit</Text>
<Text style={{ color: 'red' }}> *</Text>
<Input
name="sparepart_unit"
value={formData.sparepart_unit}
onChange={handleInputChange}
placeholder="Enter Sparepart Unit"
readOnly={props.readOnly}
/>
</div>
<div style={{ marginBottom: 12 }}>
<Text strong>Sparepart Merk</Text>
<Text style={{ color: 'red' }}> *</Text>
<Input
name="sparepart_merk"
value={formData.sparepart_merk}
onChange={handleInputChange}
placeholder="Enter Sparepart Merk"
readOnly={props.readOnly}
/>
</div>
<div style={{ marginBottom: 12 }}>
<Text strong>Sparepart Stok</Text>
<Text style={{ color: 'red' }}> *</Text>
<Input
name="sparepart_stok"
value={formData.sparepart_stok}
onChange={handleInputChange}
placeholder="Enter Sparepart Stok"
readOnly={props.readOnly}
type="number"
/>
</div>
<Row gutter={[16, 16]}>
<Col span={24}>
<Text strong>Description</Text>
<TextArea
name="sparepart_description"
value={formData.sparepart_description}
onChange={handleInputChange}
placeholder="Enter Description (Optional)"
readOnly={props.readOnly}
rows={3}
/>
</Col>
</Row>
</div>
)}
</Modal>