repair: sollution brand-device
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Input, Button, Switch, Radio, Typography, Space } from 'antd';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { Form, Input, Button, Switch, Radio, Typography, Space, Card } from 'antd';
|
||||
import { DeleteOutlined, EyeOutlined, FileOutlined } from '@ant-design/icons';
|
||||
import FileUploadHandler from './FileUploadHandler';
|
||||
import { NotifAlert } from '../../../../components/Global/ToastNotif';
|
||||
import { getFileUrl, getFolderFromFileType } from '../../../../api/file-uploads';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { TextArea } = Input;
|
||||
@@ -20,50 +21,89 @@ const SolutionFieldNew = ({
|
||||
onRemove,
|
||||
onFileUpload,
|
||||
onFileView,
|
||||
fileList = []
|
||||
fileList = [],
|
||||
originalSolutionData = null
|
||||
}) => {
|
||||
const form = Form.useFormInstance();
|
||||
const existingFile = Form.useWatch([`solution_items,${fieldKey}`, 'fileUpload'], form) ||
|
||||
Form.useWatch([`solution_items,${fieldKey}`, 'file'], form);
|
||||
const [currentFile, setCurrentFile] = useState(null);
|
||||
const [isDeleted, setIsDeleted] = useState(false);
|
||||
|
||||
// Get form values for debugging and file data extraction
|
||||
const allFormValues = form.getFieldsValue(true);
|
||||
const solutionData = allFormValues[`solution_items,${fieldKey}`] || {};
|
||||
const fileUpload = Form.useWatch(['solution_items', fieldKey, 'fileUpload'], form);
|
||||
const file = Form.useWatch(['solution_items', fieldKey, 'file'], form);
|
||||
|
||||
// Extract file data from form values for preview
|
||||
const getFileFromFormValues = () => {
|
||||
if (solutionData.fileUpload && typeof solutionData.fileUpload === 'object' && Object.keys(solutionData.fileUpload).length > 0) {
|
||||
return solutionData.fileUpload;
|
||||
const getSolutionData = () => {
|
||||
try {
|
||||
return form.getFieldValue(['solution_items', fieldKey]) || {};
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
if (solutionData.file && typeof solutionData.file === 'object' && Object.keys(solutionData.file).length > 0) {
|
||||
return solutionData.file;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const fileFromForm = getFileFromFormValues();
|
||||
const displayFile = existingFile || fileFromForm;
|
||||
const nameValue = Form.useWatch(['solution_items', fieldKey, 'name'], form);
|
||||
const typeValue = Form.useWatch(['solution_items', fieldKey, 'type'], form);
|
||||
const textValue = Form.useWatch(['solution_items', fieldKey, 'text'], form);
|
||||
const fileNameValue = Form.useWatch(['solution_items', fieldKey, 'fileName'], form);
|
||||
|
||||
const pathSolution = Form.useWatch(['solution_items', fieldKey, 'path_solution'], form);
|
||||
|
||||
const [deleteCounter, setDeleteCounter] = useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const getFileFromFormValues = () => {
|
||||
const hasValidFileUpload = fileUpload && typeof fileUpload === 'object' && Object.keys(fileUpload).length > 0;
|
||||
const hasValidFile = file && typeof file === 'object' && Object.keys(file).length > 0;
|
||||
const hasValidPath = pathSolution && pathSolution.trim() !== '';
|
||||
|
||||
const wasExplicitlyDeleted =
|
||||
(fileUpload === null || file === null || pathSolution === null) &&
|
||||
!hasValidFileUpload &&
|
||||
!hasValidFile &&
|
||||
!hasValidPath;
|
||||
|
||||
if (wasExplicitlyDeleted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (solutionType === 'text') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (hasValidFileUpload) {
|
||||
return fileUpload;
|
||||
}
|
||||
if (hasValidFile) {
|
||||
return file;
|
||||
}
|
||||
if (hasValidPath) {
|
||||
return {
|
||||
name: fileNameValue || pathSolution.split('/').pop() || 'File',
|
||||
uploadPath: pathSolution,
|
||||
url: pathSolution,
|
||||
path: pathSolution
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const fileFromForm = getFileFromFormValues();
|
||||
|
||||
if (JSON.stringify(currentFile) !== JSON.stringify(fileFromForm)) {
|
||||
setCurrentFile(fileFromForm);
|
||||
}
|
||||
}, [fileUpload, file, pathSolution, solutionType, deleteCounter, fileNameValue, fieldKey]);
|
||||
|
||||
console.log(`🔍 SolutionField ${fieldKey}:`, {
|
||||
solutionType,
|
||||
hasPathSolution: !!solutionData.path_solution,
|
||||
pathSolution: solutionData.path_solution,
|
||||
fileFromForm,
|
||||
existingFile,
|
||||
displayFile,
|
||||
shouldRenderPreview: !!displayFile
|
||||
});
|
||||
|
||||
const renderSolutionContent = () => {
|
||||
if (solutionType === 'text') {
|
||||
return (
|
||||
<Form.Item
|
||||
name={[`solution_items,${fieldKey}`, 'text']}
|
||||
name={['solution_items', fieldKey, 'text']}
|
||||
rules={[{ required: true, message: 'Text solution wajib diisi!' }]}
|
||||
>
|
||||
<TextArea
|
||||
placeholder="Enter solution text"
|
||||
rows={2}
|
||||
rows={3}
|
||||
disabled={isReadOnly}
|
||||
style={{ fontSize: 12 }}
|
||||
/>
|
||||
@@ -72,46 +112,251 @@ const SolutionFieldNew = ({
|
||||
}
|
||||
|
||||
if (solutionType === 'file') {
|
||||
return (
|
||||
<div>
|
||||
const hasOriginalFile = originalSolutionData && (
|
||||
originalSolutionData.path_solution ||
|
||||
originalSolutionData.path_document
|
||||
);
|
||||
|
||||
let displayFile = null;
|
||||
|
||||
if (currentFile && Object.keys(currentFile).length > 0) {
|
||||
displayFile = currentFile;
|
||||
}
|
||||
else if (hasOriginalFile && !isDeleted) {
|
||||
displayFile = {
|
||||
name: originalSolutionData.file_upload_name ||
|
||||
(originalSolutionData.path_solution || originalSolutionData.path_document)?.split('/').pop() ||
|
||||
'File',
|
||||
uploadPath: originalSolutionData.path_solution || originalSolutionData.path_document,
|
||||
url: originalSolutionData.path_solution || originalSolutionData.path_document,
|
||||
path: originalSolutionData.path_solution || originalSolutionData.path_document,
|
||||
isExisting: true
|
||||
};
|
||||
}
|
||||
else if (fileUpload && typeof fileUpload === 'object' && Object.keys(fileUpload).length > 0) {
|
||||
displayFile = fileUpload;
|
||||
}
|
||||
else if (file && typeof file === 'object' && Object.keys(file).length > 0) {
|
||||
displayFile = file;
|
||||
}
|
||||
else if (pathSolution && pathSolution.trim() !== '') {
|
||||
displayFile = {
|
||||
name: pathSolution.split('/').pop() || 'File',
|
||||
uploadPath: pathSolution,
|
||||
url: pathSolution,
|
||||
path: pathSolution
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (displayFile) {
|
||||
const getFileNameFromPath = () => {
|
||||
const filePath = displayFile.uploadPath || displayFile.url || displayFile.path || '';
|
||||
if (filePath) {
|
||||
const fileName = filePath.split('/').pop();
|
||||
return fileName || 'Uploaded File';
|
||||
}
|
||||
return displayFile.name || 'Uploaded File';
|
||||
};
|
||||
|
||||
const displayFileName = getFileNameFromPath();
|
||||
|
||||
return (
|
||||
<Card
|
||||
style={{
|
||||
marginBottom: 8,
|
||||
borderRadius: 8,
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||
border: '1px solid #e8e8e8'
|
||||
}}
|
||||
styles={{ body: { padding: '16px' } }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 8,
|
||||
backgroundColor: '#f0f5ff',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<FileOutlined style={{ fontSize: 24, color: '#1890ff' }} />
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
color: '#262626',
|
||||
marginBottom: 4,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}}>
|
||||
{displayFileName}
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: '#8c8c8c' }}>
|
||||
{displayFile.size ? `${(displayFile.size / 1024).toFixed(1)} KB` : 'File uploaded'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="middle"
|
||||
icon={<EyeOutlined />}
|
||||
style={{
|
||||
fontSize: 12,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4
|
||||
}}
|
||||
onClick={() => {
|
||||
try {
|
||||
let fileUrl = '';
|
||||
let actualFileName = '';
|
||||
|
||||
const filePath = displayFile.uploadPath || displayFile.url || displayFile.path || '';
|
||||
|
||||
if (filePath) {
|
||||
actualFileName = filePath.split('/').pop();
|
||||
|
||||
if (actualFileName) {
|
||||
const fileExtension = actualFileName.split('.').pop()?.toLowerCase();
|
||||
const folder = getFolderFromFileType(fileExtension);
|
||||
|
||||
fileUrl = getFileUrl(folder, actualFileName);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileUrl && filePath) {
|
||||
fileUrl = filePath.startsWith('http') ? filePath : `${import.meta.env.VITE_API_SERVER}/${filePath}`;
|
||||
}
|
||||
|
||||
if (fileUrl && actualFileName) {
|
||||
const fileExtension = actualFileName.split('.').pop()?.toLowerCase();
|
||||
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
|
||||
|
||||
if (imageExtensions.includes(fileExtension)) {
|
||||
const viewerUrl = `/image-viewer/${encodeURIComponent(actualFileName)}`;
|
||||
window.open(viewerUrl, '_blank', 'noopener,noreferrer');
|
||||
} else {
|
||||
window.open(fileUrl, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
} else {
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
message: 'File URL not found'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
message: 'Failed to open file preview'
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
size="middle"
|
||||
icon={<DeleteOutlined />}
|
||||
style={{
|
||||
fontSize: 12,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
onClick={() => {
|
||||
setIsDeleted(true);
|
||||
|
||||
form.setFieldValue(['solution_items', fieldKey, 'fileUpload'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'file'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'path_solution'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'fileName'], null);
|
||||
|
||||
setCurrentFile(null);
|
||||
|
||||
if (onFileUpload && typeof onFileUpload === 'function') {
|
||||
onFileUpload(null);
|
||||
}
|
||||
|
||||
setDeleteCounter(prev => prev + 1);
|
||||
|
||||
setTimeout(() => {
|
||||
form.validateFields(['solution_items', fieldKey]);
|
||||
}, 50);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<FileUploadHandler
|
||||
type="solution"
|
||||
existingFile={displayFile}
|
||||
existingFile={null}
|
||||
clearSignal={deleteCounter}
|
||||
debugProps={{
|
||||
currentFile: !!currentFile,
|
||||
deleteCounter,
|
||||
shouldClear: !currentFile && deleteCounter > 0
|
||||
}}
|
||||
onFileUpload={(fileObject) => {
|
||||
setIsDeleted(false);
|
||||
|
||||
const filePath = fileObject.path_solution || fileObject.uploadPath || fileObject.path || fileObject.url;
|
||||
|
||||
const fileWithKey = {
|
||||
...fileObject,
|
||||
solutionId: fieldKey
|
||||
solutionId: fieldKey,
|
||||
path_solution: filePath,
|
||||
uploadPath: filePath
|
||||
};
|
||||
|
||||
if (onFileUpload && typeof onFileUpload === 'function') {
|
||||
onFileUpload(fileWithKey);
|
||||
}
|
||||
|
||||
form.setFieldValue([`solution_items,${fieldKey}`, 'fileUpload'], fileWithKey);
|
||||
form.setFieldValue([`solution_items,${fieldKey}`, 'file'], fileWithKey);
|
||||
form.setFieldValue([`solution_items,${fieldKey}`, 'type'], 'file');
|
||||
form.setFieldValue(['solution_items', fieldKey, 'fileUpload'], fileWithKey);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'file'], fileWithKey);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'type'], 'file');
|
||||
form.setFieldValue(['solution_items', fieldKey, 'path_solution'], filePath);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'fileName'], fileObject.name);
|
||||
|
||||
setTimeout(() => {
|
||||
const values = form.getFieldValue(['solution_items', fieldKey]);
|
||||
const pathSolutionValue = form.getFieldValue(['solution_items', fieldKey, 'path_solution']);
|
||||
}, 100);
|
||||
|
||||
setCurrentFile(fileWithKey);
|
||||
}}
|
||||
onFileRemove={() => {
|
||||
console.log(`🗑️ Removing file from solution ${fieldKey}`);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'fileUpload'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'file'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'path_solution'], null);
|
||||
|
||||
// Clear file form values only, keep type as file and status active
|
||||
form.setFieldValue([`solution_items,${fieldKey}`, 'fileUpload'], null);
|
||||
form.setFieldValue([`solution_items,${fieldKey}`, 'file'], null);
|
||||
setCurrentFile(null);
|
||||
|
||||
// Call parent callback if exists
|
||||
if (onFileUpload && typeof onFileUpload === 'function') {
|
||||
onFileUpload(null);
|
||||
}
|
||||
|
||||
console.log(`✅ File removed from solution ${fieldKey} - type and status preserved`);
|
||||
setDeleteCounter(prev => prev + 1);
|
||||
}}
|
||||
disabled={isReadOnly}
|
||||
buttonText={displayFile ? 'Replace File' : 'Upload File'}
|
||||
buttonText="Upload File"
|
||||
buttonStyle={{ width: '100%', fontSize: 12 }}
|
||||
uploadText="Upload solution file"
|
||||
uploadText="Upload solution file (includes images, PDF, documents)"
|
||||
acceptFileTypes="*"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -140,7 +385,7 @@ const SolutionFieldNew = ({
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Form.Item name={[`solution_items,${fieldKey}`, 'status']} valuePropName="checked" noStyle>
|
||||
<Form.Item name={['solution_items', fieldKey, 'status']} valuePropName="checked" noStyle>
|
||||
<Switch
|
||||
size="small"
|
||||
disabled={isReadOnly}
|
||||
@@ -180,7 +425,7 @@ const SolutionFieldNew = ({
|
||||
</div>
|
||||
|
||||
<Form.Item
|
||||
name={[`solution_items,${fieldKey}`, 'name']}
|
||||
name={['solution_items', fieldKey, 'name']}
|
||||
rules={[{ required: true, message: 'Solution name wajib diisi!' }]}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
@@ -192,16 +437,36 @@ const SolutionFieldNew = ({
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Form.Item
|
||||
name={[`solution_items,${fieldKey}`, 'type']}
|
||||
name={['solution_items', fieldKey, 'type']}
|
||||
rules={[{ required: true, message: 'Solution type wajib diisi!' }]}
|
||||
style={{ marginBottom: 8 }}
|
||||
initialValue={solutionType || 'text'}
|
||||
>
|
||||
<Radio.Group
|
||||
onChange={(e) => onTypeChange(fieldKey, e.target.value)}
|
||||
onChange={(e) => {
|
||||
const newType = e.target.value;
|
||||
|
||||
if (newType === 'text') {
|
||||
form.setFieldValue(['solution_items', fieldKey, 'fileUpload'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'file'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'path_solution'], null);
|
||||
form.setFieldValue(['solution_items', fieldKey, 'fileName'], null);
|
||||
setCurrentFile(null);
|
||||
setIsDeleted(true);
|
||||
|
||||
if (onFileUpload && typeof onFileUpload === 'function') {
|
||||
onFileUpload(null);
|
||||
}
|
||||
} else if (newType === 'file') {
|
||||
form.setFieldValue(['solution_items', fieldKey, 'text'], null);
|
||||
setIsDeleted(false);
|
||||
}
|
||||
|
||||
onTypeChange(fieldKey, newType);
|
||||
}}
|
||||
disabled={isReadOnly}
|
||||
size="small"
|
||||
>
|
||||
@@ -211,7 +476,7 @@ const SolutionFieldNew = ({
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[`solution_items,${fieldKey}`, 'status']}
|
||||
name={['solution_items', fieldKey, 'status']}
|
||||
initialValue={solutionStatus !== false ? true : false}
|
||||
noStyle
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user