refactor: enhance dataset management with auto-save functionality and improve dataset name editing process

This commit is contained in:
2025-10-19 21:25:54 +03:00
parent e99b5e2dc2
commit 11c876f74b
3 changed files with 160 additions and 1 deletions

View File

@@ -67,4 +67,5 @@ When implementing changes:
- Pay attention to the existing code base style and approaches and try to adhere to the existing style instead of bringing your own vision. - Pay attention to the existing code base style and approaches and try to adhere to the existing style instead of bringing your own vision.
- When updating documentation, do not record intermediate changes - write them always as a matter-of-fact information. - When updating documentation, do not record intermediate changes - write them always as a matter-of-fact information.
- When working on the code, if you notice any opportunities to better bring the project to the state above - bring this to user's attention and ask for approval to implement the suggested changes. - When working on the code, if you notice any opportunities to better bring the project to the state above - bring this to user's attention and ask for approval to implement the suggested changes.
- When attempting to implement a new feature, research whether a similar method or fuction already exists elsewhere. Propose to either enhance existing one, or create a new method in such cases.
- Testing: The user always tests changes manually. Do not start local servers or attempt to run the application. - Testing: The user always tests changes manually. Do not start local servers or attempt to run the application.

View File

@@ -200,7 +200,7 @@
<div class="dataset-details" id="dataset-details" style="display: none;"> <div class="dataset-details" id="dataset-details" style="display: none;">
<div class="dataset-detail-section"> <div class="dataset-detail-section">
<div class="dataset-actions"> <div class="dataset-actions">
<button class="btn btn-modal primary" id="edit-dataset-btn" title="Edit this dataset">Edit</button> <button class="btn btn-modal primary" id="edit-dataset-btn" title="Edit this dataset contents">Edit Contents</button>
<button class="btn btn-modal primary" id="new-snippet-btn" title="Create a new snippet using this dataset">New Snippet</button> <button class="btn btn-modal primary" id="new-snippet-btn" title="Create a new snippet using this dataset">New Snippet</button>
<button class="btn btn-modal" id="export-dataset-btn" title="Export this dataset to file">Export</button> <button class="btn btn-modal" id="export-dataset-btn" title="Export this dataset to file">Export</button>
<button class="btn btn-modal" id="copy-reference-btn" title="Copy dataset reference to clipboard">Copy Reference</button> <button class="btn btn-modal" id="copy-reference-btn" title="Copy dataset reference to clipboard">Copy Reference</button>

View File

@@ -35,6 +35,33 @@ function generateDatasetId() {
return Date.now() + Math.random() * 1000; return Date.now() + Math.random() * 1000;
} }
// Replace dataset name references in a Vega-Lite spec
function replaceDatasetNameInSpec(spec, oldName, newName) {
if (!spec || typeof spec !== 'object') return spec;
// Clone the spec to avoid mutation
const newSpec = JSON.parse(JSON.stringify(spec));
// Recursively traverse and replace dataset name references
(function traverse(obj) {
if (!obj || typeof obj !== 'object') return;
// Check if this is a data object with a name property matching oldName
if (obj.data && typeof obj.data === 'object' && obj.data.name === oldName) {
obj.data.name = newName;
}
// Recursively check all properties
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
traverse(obj[key]);
}
}
})(newSpec);
return newSpec;
}
// Calculate dataset statistics // Calculate dataset statistics
function calculateDatasetStats(data, format, source) { function calculateDatasetStats(data, format, source) {
let rowCount = 0; let rowCount = 0;
@@ -465,6 +492,9 @@ async function selectDataset(datasetId, updateURL = true) {
// Update linked snippets display // Update linked snippets display
updateLinkedSnippets(dataset); updateLinkedSnippets(dataset);
// Initialize auto-save for detail fields
initializeDatasetDetailAutoSave();
// Update URL state (URLState.update will add 'dataset-' prefix) // Update URL state (URLState.update will add 'dataset-' prefix)
if (updateURL) { if (updateURL) {
URLState.update({ view: 'datasets', snippetId: null, datasetId: datasetId }); URLState.update({ view: 'datasets', snippetId: null, datasetId: datasetId });
@@ -1707,3 +1737,131 @@ async function importDatasetFromFile(fileInput) {
fileInput.value = ''; fileInput.value = '';
} }
} }
// Auto-save dataset metadata (name and comment)
async function autoSaveDatasetMeta() {
const nameField = document.getElementById('dataset-detail-name');
const commentField = document.getElementById('dataset-detail-comment');
if (!nameField || !commentField) return;
const dataset = await getCurrentDataset();
if (!dataset) return;
const newName = nameField.value.trim();
const newComment = commentField.value;
// Store old name for snippet reference updates
const oldName = dataset.name;
const nameChanged = newName !== dataset.name;
// Check if name has changed
if (nameChanged) {
// Validate name is not empty
if (!newName) {
Toast.error('Dataset name cannot be empty');
nameField.value = dataset.name; // Restore previous value
return;
}
// Check if new name already exists
if (await DatasetStorage.nameExists(newName)) {
Toast.error(`Dataset name "${newName}" already exists`);
nameField.value = dataset.name; // Restore previous value
return;
}
// Check if any snippets reference this dataset
const snippets = SnippetStorage.loadSnippets();
const affectedSnippets = snippets.filter(snippet =>
snippet.datasetRefs && snippet.datasetRefs.includes(dataset.name)
);
// Show warning if there are affected snippets
if (affectedSnippets.length > 0) {
const confirmed = confirm(
`⚠️ Warning: Renaming this dataset will update ${affectedSnippets.length} snippet${affectedSnippets.length > 1 ? 's' : ''} that reference it.\n\n` +
`We'll attempt to update all references automatically, but complex specs with nested data sources may require manual review.\n\n` +
`Affected snippets:\n${affectedSnippets.map(s => `${s.name}`).join('\n')}\n\n` +
`Continue with rename?`
);
if (!confirmed) {
nameField.value = dataset.name; // Restore previous value
return;
}
}
}
// Update dataset
const updatedDataset = await DatasetStorage.updateDataset(dataset.id, {
name: newName || dataset.name,
comment: newComment
});
// If name changed, update all snippets that reference this dataset
if (nameChanged && newName) {
const snippets = SnippetStorage.loadSnippets();
const affectedSnippets = snippets.filter(snippet =>
snippet.datasetRefs && snippet.datasetRefs.includes(oldName)
);
affectedSnippets.forEach(snippet => {
// Update spec and draftSpec by replacing dataset name references
snippet.spec = replaceDatasetNameInSpec(snippet.spec, oldName, newName);
snippet.draftSpec = replaceDatasetNameInSpec(snippet.draftSpec, oldName, newName);
// Re-extract dataset references from updated specs
// extractDatasetRefs is a global function from snippet-manager.js
if (typeof extractDatasetRefs === 'function') {
snippet.datasetRefs = extractDatasetRefs(snippet.spec);
}
SnippetStorage.saveSnippet(snippet);
});
// Show success message
if (affectedSnippets.length > 0) {
Toast.success(`Updated ${affectedSnippets.length} snippet${affectedSnippets.length > 1 ? 's' : ''}`);
}
// Refresh linked snippets display
updateLinkedSnippets(updatedDataset);
}
// Update the modified timestamp in the UI
document.getElementById('dataset-detail-modified').textContent = new Date(updatedDataset.modified).toLocaleString();
// Update the dataset list display to reflect the new name
await renderDatasetList();
// Restore selection after re-render
const selectedItem = document.querySelector(`[data-item-id="${dataset.id}"]`);
if (selectedItem) {
selectedItem.classList.add('selected');
}
}
// Debounced auto-save for dataset metadata
let datasetMetaAutoSaveTimeout;
function debouncedAutoSaveDatasetMeta() {
clearTimeout(datasetMetaAutoSaveTimeout);
datasetMetaAutoSaveTimeout = setTimeout(autoSaveDatasetMeta, 1000);
}
// Initialize auto-save for dataset detail fields
function initializeDatasetDetailAutoSave() {
const nameField = document.getElementById('dataset-detail-name');
const commentField = document.getElementById('dataset-detail-comment');
if (nameField) {
// Remove any existing event listener to prevent duplicates
nameField.removeEventListener('input', debouncedAutoSaveDatasetMeta);
nameField.addEventListener('input', debouncedAutoSaveDatasetMeta);
}
if (commentField) {
// Remove any existing event listener to prevent duplicates
commentField.removeEventListener('input', debouncedAutoSaveDatasetMeta);
commentField.addEventListener('input', debouncedAutoSaveDatasetMeta);
}
}