added users management and permissions

This commit is contained in:
2026-02-23 15:01:58 +02:00
parent a993f8944d
commit 574de2c32d
11 changed files with 440 additions and 82 deletions
@@ -4,9 +4,9 @@ import { DataProvider, useReportData } from './DataContext';
import { BandProvider } from './BandContext';
import Toolbar from './Toolbar';
import EditorCanvas from './EditorCanvas';
import ConfigPanel from './ConfigPanel';
import CharacterPalette from './CharacterPalette';
import ObjectInspector from './ObjectInspector';
import { useNomenclatureData } from '../../contexts/NomenclatureDataContext';
import api from '../../services/api';
import './ReportEditor.css';
@@ -23,7 +23,6 @@ function ReportEditorContent() {
const [toolMode, setToolMode] = useState('select');
const [borderStyle, setBorderStyle] = useState('single');
const [previewMode, setPreviewMode] = useState(false);
const [showConfigPanel, setShowConfigPanel] = useState(false);
const [selectedChar, setSelectedChar] = useState('─');
const [showCharacterPalette, setShowCharacterPalette] = useState(false);
@@ -37,6 +36,7 @@ function ReportEditorContent() {
// Auto-load report and vehicle data passed via router state
const location = useLocation();
const { setData } = useReportData();
const { definitions, entries, isLoaded } = useNomenclatureData();
useEffect(() => {
const { reportName, vehicleData } = location.state || {};
@@ -66,6 +66,55 @@ function ReportEditorContent() {
});
}, []);
// When no vehicle data was passed (direct navigation), auto-set sample data
// once nomenclatures have loaded so field bindings can be designed
useEffect(() => {
if (!isLoaded || location.state?.vehicleData) return;
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) {
extra[def.name] = { ...firstEntry.data };
} else if (def.fields?.length > 0) {
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';
}
}
setData({
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,
}
});
}, [isLoaded]);
const handleAddElement = (element) => {
setReport(prev => {
// Check if the new element is positioned inside a band
@@ -332,9 +381,6 @@ function ReportEditorContent() {
setSelectedElementIds([]);
setToolMode('select');
setShowCharacterPalette(false);
if (showConfigPanel) {
setShowConfigPanel(false);
}
} else if (e.key.toLowerCase() === 'd' && !e.ctrlKey && !e.metaKey && e.target.tagName !== 'INPUT') {
handleToolChange('drawTable');
} else if (e.key === 'Delete' && selectedElementIds.length > 0) {
@@ -347,7 +393,7 @@ function ReportEditorContent() {
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [showConfigPanel, selectedElementIds, toolMode, report.elements]);
}, [selectedElementIds, toolMode, report.elements]);
// Get the selected element for the Object Inspector
const selectedElement = selectedElementIds.length === 1
@@ -363,7 +409,6 @@ function ReportEditorContent() {
onBorderStyleChange={setBorderStyle}
previewMode={previewMode}
onTogglePreview={handleTogglePreview}
onConfigureAPI={() => setShowConfigPanel(true)}
onSave={handleSave}
onLoad={handleLoadClick}
reportName={report.name}
@@ -393,12 +438,6 @@ function ReportEditorContent() {
onUpdate={(updates) => handleElementUpdate(selectedElementIds[0], updates)}
/>
)}
<ConfigPanel
apiEndpoint={report.apiEndpoint}
onApiEndpointChange={handleApiEndpointChange}
isOpen={showConfigPanel}
onClose={() => setShowConfigPanel(false)}
/>
{showCharacterPalette && (
<CharacterPalette
selectedChar={selectedChar}