187 lines
6.7 KiB
React
187 lines
6.7 KiB
React
import React, { useState } from 'react';
|
||
import { useReportData } from './DataContext';
|
||
import { useNomenclatureData } from '../../contexts/NomenclatureDataContext';
|
||
import './ConfigPanel.css';
|
||
|
||
function ConfigPanel({ apiEndpoint, onApiEndpointChange, isOpen, onClose }) {
|
||
const { reportData, isLoading, error, fetchData, setData, clearData } = useReportData();
|
||
const { definitions, entries } = useNomenclatureData();
|
||
const [localEndpoint, setLocalEndpoint] = useState(apiEndpoint || '');
|
||
const [manualDataInput, setManualDataInput] = useState('');
|
||
const [showManualInput, setShowManualInput] = useState(false);
|
||
|
||
const handleFetch = async () => {
|
||
if (localEndpoint.trim()) {
|
||
onApiEndpointChange(localEndpoint);
|
||
await fetchData(localEndpoint);
|
||
}
|
||
};
|
||
|
||
const handleSetManualData = () => {
|
||
const trimmedInput = manualDataInput.trim();
|
||
|
||
if (!trimmedInput) {
|
||
alert('Please enter some JSON data first');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const data = JSON.parse(trimmedInput);
|
||
setData(data);
|
||
setShowManualInput(false);
|
||
setManualDataInput('');
|
||
} catch (err) {
|
||
alert('Invalid JSON: ' + err.message);
|
||
}
|
||
};
|
||
|
||
const handleClear = () => {
|
||
clearData();
|
||
setLocalEndpoint('');
|
||
onApiEndpointChange('');
|
||
};
|
||
|
||
if (!isOpen) return null;
|
||
|
||
return (
|
||
<div className="config-panel-overlay" onClick={onClose}>
|
||
<div className="config-panel" onClick={(e) => e.stopPropagation()}>
|
||
<div className="config-panel-header">
|
||
<h2>Report Configuration</h2>
|
||
<button className="close-button" onClick={onClose}>×</button>
|
||
</div>
|
||
|
||
<div className="config-panel-content">
|
||
{/* API Endpoint Section */}
|
||
<div className="config-section">
|
||
<h3>API Endpoint</h3>
|
||
<div className="input-group">
|
||
<input
|
||
type="text"
|
||
className="config-input"
|
||
placeholder="http://localhost:8000/api/report-data"
|
||
value={localEndpoint}
|
||
onChange={(e) => setLocalEndpoint(e.target.value)}
|
||
disabled={isLoading}
|
||
/>
|
||
<button
|
||
className="config-button primary"
|
||
onClick={handleFetch}
|
||
disabled={isLoading || !localEndpoint.trim()}
|
||
>
|
||
{isLoading ? 'Fetching...' : 'Fetch Data'}
|
||
</button>
|
||
</div>
|
||
|
||
{error && (
|
||
<div className="config-error">
|
||
<strong>Error:</strong> {error}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Manual Data Input Section */}
|
||
<div className="config-section">
|
||
<button
|
||
className="config-button"
|
||
onClick={() => {
|
||
if (!showManualInput && !manualDataInput) {
|
||
// Build extra sample from actual nomenclature definitions
|
||
// Lookup fields become nested objects with all their sub-fields
|
||
const extra = {};
|
||
for (const def of Object.values(definitions)) {
|
||
if (def.kind === 'lookup') {
|
||
const activeEntries = (entries[def.code] || []).filter(e => e.is_active);
|
||
const firstEntry = activeEntries[0];
|
||
if (firstEntry?.data && Object.keys(firstEntry.data).length > 0) {
|
||
// Use actual field values from the first active entry
|
||
extra[def.name] = { ...firstEntry.data };
|
||
} else if (def.fields?.length > 0) {
|
||
// No entries yet — build placeholders from field definitions
|
||
const sample = {};
|
||
for (const field of def.fields) {
|
||
if (field.field_type === 'number') sample[field.key] = 0;
|
||
else if (field.field_type === 'bool') sample[field.key] = false;
|
||
else sample[field.key] = 'Sample';
|
||
}
|
||
extra[def.name] = sample;
|
||
} else {
|
||
extra[def.name] = {};
|
||
}
|
||
} else {
|
||
extra[def.name] = 'Sample Text';
|
||
}
|
||
}
|
||
setManualDataInput(JSON.stringify({
|
||
vehicle: {
|
||
vehicle_number: 'АА1234ВВ',
|
||
trailer1_number: 'ПВ5678ВВ',
|
||
trailer2_number: null,
|
||
driver_pid: '123456789',
|
||
tare: 8500,
|
||
tare_date: '2026-02-22T08:00:00Z',
|
||
tare_user_name: 'operator',
|
||
gross: 28500,
|
||
gross_date: '2026-02-22T10:30:00Z',
|
||
gross_user_name: 'operator',
|
||
net: 20000,
|
||
net_date: '2026-02-22T10:30:00Z',
|
||
net_user_name: 'operator',
|
||
doc_number: '2026-001',
|
||
extra,
|
||
}
|
||
}, null, 2));
|
||
}
|
||
setShowManualInput(!showManualInput);
|
||
}}
|
||
>
|
||
{showManualInput ? 'Hide' : 'Enter'} Manual Data (JSON)
|
||
</button>
|
||
|
||
{showManualInput && (
|
||
<div className="manual-input-section">
|
||
<textarea
|
||
className="manual-data-input"
|
||
placeholder='{"owner": {"name": "John Doe"}, "vessel": {"id": "ABC123"}}'
|
||
value={manualDataInput}
|
||
onChange={(e) => setManualDataInput(e.target.value)}
|
||
rows={8}
|
||
/>
|
||
<button
|
||
className="config-button primary"
|
||
onClick={handleSetManualData}
|
||
>
|
||
Set Data
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Data Preview Section */}
|
||
{reportData && (
|
||
<div className="config-section">
|
||
<div className="section-header">
|
||
<h3>Current Data</h3>
|
||
<button className="config-button small" onClick={handleClear}>
|
||
Clear Data
|
||
</button>
|
||
</div>
|
||
<div className="data-preview">
|
||
<pre>{JSON.stringify(reportData, null, 2)}</pre>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{!reportData && !isLoading && !error && (
|
||
<div className="config-info">
|
||
No data loaded. Enter an API endpoint to fetch data, or use manual input.
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default ConfigPanel;
|