Skip to content
Open
3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"dagre": "^0.8.5",
"html2canvas": "^1.4.1",
"jspdf": "^4.0.0",
"jspdf-autotable": "^5.0.7",
"monaco-editor": "0.54.0",
"monaco-yaml": "^5.4.0",
"open": "^10.1.0",
Expand Down
54 changes: 53 additions & 1 deletion src/layouts/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Menu, MenuButton, MenuItem, MenuItems} from '@headlessui/react';
import {FileSelectionModal} from '../components/ui/FileSelectionModal.jsx';
import Modal from '../components/ui/Modal.jsx';
import exampleYaml from '../example.yaml?raw';
import exportYamlToPdf from '../utils/exportYamlToPdf.js';

// Hamburger menu icon component
const HamburgerIcon = () => (
Expand Down Expand Up @@ -116,6 +117,46 @@ const Header = () => {
}
};

const handleSavePdf = async () => {
try {
// Parse YAML to check for required fields
let parsedYaml;
try {
parsedYaml = parseYaml(yaml);
} catch (parseError) {
alert('Cannot save: YAML is invalid. Please fix syntax errors first.');
return;
}

// Check for all required fundamental fields
const requiredFields = [
{ field: 'name', label: 'Name' },
{ field: 'version', label: 'Version' },
{ field: 'status', label: 'Status' },
{ field: 'id', label: 'ID' }
];

const missingFields = requiredFields.filter(({ field }) =>
!parsedYaml[field] || parsedYaml[field].trim() === ''
);

if (missingFields.length > 0) {
const missingFieldsList = missingFields.map(({ label }) => label).join(', ');
alert(`Cannot save: Missing required fields: ${missingFieldsList}\n\nPlease fill in all required fields in the Overview section.`);
return;
}

// Export Yaml as PDF
await exportYamlToPdf();
} catch (error) {
if (error.name === 'AbortError' || error.message === 'File selection cancelled') {
return;
}
console.error('Error saving PDF:', error);
alert(`Failed to save PDF: ${error.message}`);
}
};

const handleCancel = () => {
if (editorConfig?.onCancel) {
editorConfig.onCancel();
Expand Down Expand Up @@ -717,7 +758,18 @@ const Header = () => {
<path strokeLinecap="round" strokeLinejoin="round"
d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3"/>
</svg>
Save
Save YAML
</button>
</MenuItem>
<MenuItem>
<button
onClick={handleSavePdf}
className="group flex w-full items-center px-4 py-2 text-xs text-gray-700 data-[focus]:bg-gray-100 data-[focus]:text-gray-900 data-[focus]:outline-none"
>
<svg className="mr-3 h-5 w-5 text-gray-400 group-data-[focus]:text-gray-500" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
</svg>
Save PDF
</button>
</MenuItem>
</div>
Expand Down
19 changes: 16 additions & 3 deletions src/services/LocalFileStorageBackend.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FileStorageBackend } from './FileStorageBackend.js';
import yaml from 'js-yaml';

/**
* Local file storage backend that uses browser File APIs
Expand Down Expand Up @@ -34,10 +35,22 @@ export class LocalFileStorageBackend extends FileStorageBackend {
throw new Error('No content to save');
}

// Parse YAML to extract id and version for filename
let filename = suggestedName;
try {
const dataContract = yaml.load(yamlContent);
const id = dataContract.id ? `${dataContract.id}` : "data-contract";
const version = dataContract.version ? `version-${dataContract.version}` : "version_00";
filename = `${id}-${version}.yaml`;
} catch (e) {
// If parsing fails, use suggestedName
console.warn('Could not parse YAML for filename, using default:', e);
}

if (this.supportsFileSystemAccess) {
return this._saveWithFileSystemAccess(yamlContent, suggestedName);
return this._saveWithFileSystemAccess(yamlContent, filename);
} else {
return this._saveWithDownload(yamlContent, suggestedName);
return this._saveWithDownload(yamlContent, filename);
}
}

Expand Down Expand Up @@ -164,4 +177,4 @@ export class LocalFileStorageBackend extends FileStorageBackend {
file.name.endsWith('.yml')
);
}
}
}
Loading