1// SPDX-License-Identifier: GPL-2-0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
4import FileDownloadIcon from '@mui/icons-material/FileDownload';
5import FileUploadIcon from '@mui/icons-material/FileUpload';
6import { IconButton, Tooltip } from '@mui/material';
7import { invoke } from '@tauri-apps/api/core';
8import { dirname, join } from '@tauri-apps/api/path';
9import { open, save } from '@tauri-apps/plugin-dialog';
10import { writeTextFile } from '@tauri-apps/plugin-fs';
11import { useState } from 'react';
12import { useScenarioStore } from '@/stores/scenarioStore';
13import ConfirmDialog from './ConfirmDialog';
15export default function ScenarioIO() {
16 const loadScenario = useScenarioStore((state) => state.loadScenario);
17 const isDirty = useScenarioStore((state) => state.isDirty);
18 const resetScenario = useScenarioStore((state) => state.resetScenario);
19 const showError = useScenarioStore((state) => state.showError);
20 const showWarning = useScenarioStore((state) => state.showWarning);
21 const setScenarioFilePath = useScenarioStore(
22 (state) => state.setScenarioFilePath
24 const scenarioFilePath = useScenarioStore(
25 (state) => state.scenarioFilePath
28 const [isConfirmOpen, setConfirmOpen] = useState(false);
30 const handleExport = async () => {
32 const state = useScenarioStore.getState();
33 await state.syncBackend();
35 const xmlContent = await invoke<string>('get_scenario_as_xml');
37 // 1. Determine default directory
39 if (scenarioFilePath) {
40 defaultDir = await dirname(scenarioFilePath);
43 // 2. Determine suggested filename
45 state.globalParameters.simulation_name || 'scenario';
46 const suggestedFileName = `${simName.replace(/[^a-z0-9]/gi, '_')}.fersxml`;
48 // 3. Combine for dialog
49 const defaultPath = await join(defaultDir, suggestedFileName);
51 const filePath = await save({
52 title: 'Export Scenario',
53 defaultPath: defaultPath,
56 name: 'FERS XML Scenario',
57 extensions: ['fersxml', 'xml'],
63 await writeTextFile(filePath, xmlContent);
64 setScenarioFilePath(filePath);
65 console.log('Scenario exported successfully to:', filePath);
69 error instanceof Error ? error.message : String(error);
70 console.error('Failed to export scenario:', errorMessage);
71 showError(`Export failed: ${errorMessage}`);
75 const performImport = async () => {
77 const selectedPath = await open({
78 title: 'Import Scenario',
82 name: 'FERS XML Scenario',
83 extensions: ['xml', 'fersxml'],
88 if (typeof selectedPath === 'string') {
89 // Load the XML file into the C++ core
90 const warnings = await invoke<string[]>(
91 'load_scenario_from_xml_file',
93 filepath: selectedPath,
97 // Fetch the new state as JSON from the C++ core
98 const jsonState = await invoke<string>('get_scenario_as_json');
99 const scenarioData = JSON.parse(jsonState);
101 // Update the UI's Zustand store with the new state after resetting the current state
103 loadScenario(scenarioData);
104 setScenarioFilePath(selectedPath);
105 warnings.forEach((warning) => showWarning(warning));
108 'Scenario imported and synchronized successfully from:',
114 error instanceof Error ? error.message : String(error);
115 console.error('Failed to import scenario:', errorMessage);
116 showError(`Import failed: ${errorMessage}`);
120 const handleImport = () => {
122 setConfirmOpen(true);
124 void performImport();
128 const handleConfirmImport = () => {
129 setConfirmOpen(false);
130 void performImport();
133 const handleCancelImport = () => {
134 setConfirmOpen(false);
139 <Tooltip title="Import Scenario (XML)">
140 <IconButton size="small" onClick={handleImport}>
141 <FileUploadIcon fontSize="inherit" />
144 <Tooltip title="Export Scenario (XML)">
145 <IconButton size="small" onClick={handleExport}>
146 <FileDownloadIcon fontSize="inherit" />
151 onConfirm={handleConfirmImport}
152 onCancel={handleCancelImport}
153 title="Overwrite Current Scenario?"
154 message="Importing a new scenario will discard all unsaved changes. Are you sure you want to proceed?"