From c84b1fdcfb94b6d3d4565a50118a0a5c9a8a9785 Mon Sep 17 00:00:00 2001 From: Oleh Omelchenko Date: Sat, 18 Oct 2025 01:49:30 +0300 Subject: [PATCH] feat: integrate generic storage UI and refactor dataset/snippet item handling --- index.html | 1 + src/js/dataset-manager.js | 68 ++++++++++++++------------------------ src/js/snippet-manager.js | 69 +++++++++++++++------------------------ 3 files changed, 53 insertions(+), 85 deletions(-) diff --git a/index.html b/index.html index 0a00777..a372f9f 100644 --- a/index.html +++ b/index.html @@ -700,6 +700,7 @@ + diff --git a/src/js/dataset-manager.js b/src/js/dataset-manager.js index 820fb33..e990bb8 100644 --- a/src/js/dataset-manager.js +++ b/src/js/dataset-manager.js @@ -288,7 +288,7 @@ async function renderDatasetList() { // Sort by modified date (most recent first) datasets.sort((a, b) => new Date(b.modified) - new Date(a.modified)); - const html = datasets.map(dataset => { + const formatDatasetItem = (dataset) => { let metaText; if (dataset.source === 'url') { // Show metadata if available, otherwise just URL and format @@ -308,7 +308,7 @@ async function renderDatasetList() { : ''; return ` -
+
${dataset.name}
${metaText}
@@ -316,14 +316,15 @@ async function renderDatasetList() { ${usageBadge}
`; - }).join(''); + }; + const html = datasets.map(formatDatasetItem).join(''); listContainer.innerHTML = html; // Attach click handlers document.querySelectorAll('.dataset-item').forEach(item => { item.addEventListener('click', function() { - const datasetId = parseFloat(this.dataset.datasetId); + const datasetId = parseFloat(this.dataset.itemId); selectDataset(datasetId); }); }); @@ -338,7 +339,10 @@ async function selectDataset(datasetId, updateURL = true) { document.querySelectorAll('.dataset-item').forEach(item => { item.classList.remove('selected'); }); - document.querySelector(`[data-dataset-id="${datasetId}"]`).classList.add('selected'); + const selectedItem = document.querySelector(`[data-item-id="${datasetId}"]`); + if (selectedItem) { + selectedItem.classList.add('selected'); + } // Show details panel const detailsPanel = document.getElementById('dataset-details'); @@ -722,46 +726,28 @@ function showTablePreview(dataset) { // Update linked snippets display in dataset details panel function updateLinkedSnippets(dataset) { - const snippetsSection = document.getElementById('dataset-snippets-section'); - const snippetsContainer = document.getElementById('dataset-snippets'); - - if (!snippetsSection || !snippetsContainer) return; - // Find all snippets that reference this dataset const snippets = SnippetStorage.loadSnippets(); const linkedSnippets = snippets.filter(snippet => snippet.datasetRefs && snippet.datasetRefs.includes(dataset.name) ); - if (linkedSnippets.length === 0) { - snippetsSection.style.display = 'none'; - return; - } - - // Show section and populate with snippet links - snippetsSection.style.display = 'block'; - - const snippetItems = linkedSnippets.map(snippet => { - return ` + updateGenericLinkedItems( + linkedSnippets, + 'dataset-snippets', + 'dataset-snippets-section', + (snippet) => ` - `; - }).join(''); - - snippetsContainer.innerHTML = snippetItems; - - // Attach click handlers to snippet links - snippetsContainer.querySelectorAll('.snippet-link').forEach(link => { - link.addEventListener('click', function(e) { - e.preventDefault(); - const snippetId = parseFloat(this.dataset.snippetId); + `, + (snippetId) => { openSnippetFromDataset(snippetId); - }); - }); + } + ); } // Close dataset manager and open snippet @@ -1157,15 +1143,11 @@ async function deleteCurrentDataset() { // Check if dataset is in use const usageCount = countSnippetUsage(dataset.name); - let confirmMessage = `Delete dataset "${dataset.name}"?`; + const warningMessage = usageCount > 0 + ? `⚠️ Warning: Dataset "${dataset.name}" is currently used by ${usageCount} snippet${usageCount !== 1 ? 's' : ''}.\n\nDeleting this dataset will break those visualizations.` + : null; - if (usageCount > 0) { - confirmMessage = `⚠️ Warning: Dataset "${dataset.name}" is currently used by ${usageCount} snippet${usageCount !== 1 ? 's' : ''}.\n\nDeleting this dataset will break those visualizations. Are you sure you want to delete it?`; - } else { - confirmMessage += ' This action cannot be undone.'; - } - - if (confirm(confirmMessage)) { + confirmGenericDeletion(dataset.name, warningMessage, async () => { await DatasetStorage.deleteDataset(dataset.id); document.getElementById('dataset-details').style.display = 'none'; window.currentDatasetId = null; @@ -1175,8 +1157,8 @@ async function deleteCurrentDataset() { Toast.success('Dataset deleted'); // Track event - Analytics.track('dataset-delete', 'Delete dataset'); - } + trackEventIfAvailable('dataset-delete', 'Delete dataset'); + }); } // Copy dataset reference to clipboard diff --git a/src/js/snippet-manager.js b/src/js/snippet-manager.js index 0f3f6db..92429da 100644 --- a/src/js/snippet-manager.js +++ b/src/js/snippet-manager.js @@ -341,7 +341,9 @@ function renderSnippetList(searchQuery = null) { `; const currentSort = AppSettings.get('sortBy'); - const snippetItems = snippets.map(snippet => { + + // Use generic formatter + const formatSnippetItem = (snippet) => { // Show appropriate date based on current sort let dateText; if (currentSort === 'created') { @@ -364,7 +366,7 @@ function renderSnippetList(searchQuery = null) { const datasetIconHTML = usesDatasets ? '📁' : ''; return ` -
  • +
  • ${snippet.name}${datasetIconHTML}
    ${dateText}
    @@ -373,8 +375,9 @@ function renderSnippetList(searchQuery = null) {
  • `; - }).join(''); + }; + const snippetItems = snippets.map(formatSnippetItem).join(''); snippetList.innerHTML = ghostCard + snippetItems; // Re-attach event listeners for snippet selection @@ -484,7 +487,7 @@ function performSearch() { // Clear selection if current snippet is no longer visible if (window.currentSnippetId) { - const selectedItem = document.querySelector(`[data-snippet-id="${window.currentSnippetId}"]`); + const selectedItem = document.querySelector(`[data-item-id="${window.currentSnippetId}"]`); if (!selectedItem) { clearSelection(); } else { @@ -522,7 +525,7 @@ function getCurrentSnippet() { // Helper: Restore visual selection state for current snippet function restoreSnippetSelection() { if (window.currentSnippetId) { - const item = document.querySelector(`[data-snippet-id="${window.currentSnippetId}"]`); + const item = document.querySelector(`[data-item-id="${window.currentSnippetId}"]`); if (item) { item.classList.add('selected'); return item; @@ -569,7 +572,7 @@ function attachSnippetEventListeners() { // Left click to select item.addEventListener('click', function () { - const snippetId = parseFloat(this.dataset.snippetId); + const snippetId = parseFloat(this.dataset.itemId); selectSnippet(snippetId); }); }); @@ -584,7 +587,10 @@ function selectSnippet(snippetId, updateURL = true) { document.querySelectorAll('.snippet-item').forEach(item => { item.classList.remove('selected'); }); - document.querySelector(`[data-snippet-id="${snippetId}"]`).classList.add('selected'); + const selectedItem = document.querySelector(`[data-item-id="${snippetId}"]`); + if (selectedItem) { + selectedItem.classList.add('selected'); + } // Load spec based on current view mode loadSnippetIntoEditor(snippet); @@ -631,43 +637,24 @@ function selectSnippet(snippetId, updateURL = true) { // Update linked datasets display in metadata panel function updateLinkedDatasets(snippet) { - const datasetsSection = document.getElementById('snippet-datasets-section'); - const datasetsContainer = document.getElementById('snippet-datasets'); - - if (!datasetsSection || !datasetsContainer) return; - - // Get dataset references from snippet const datasetRefs = snippet.datasetRefs || []; - if (datasetRefs.length === 0) { - datasetsSection.style.display = 'none'; - return; - } - - // Show section and populate with dataset references - datasetsSection.style.display = 'block'; - - const datasetItems = datasetRefs.map(datasetName => { - return ` + updateGenericLinkedItems( + datasetRefs, + 'snippet-datasets', + 'snippet-datasets-section', + (datasetName) => ` - `; - }).join(''); - - datasetsContainer.innerHTML = datasetItems; - - // Attach click handlers to dataset links - datasetsContainer.querySelectorAll('.dataset-link').forEach(link => { - link.addEventListener('click', async function(e) { - e.preventDefault(); - const datasetName = this.dataset.datasetName; + `, + async (datasetName) => { await openDatasetByName(datasetName); - }); - }); + } + ); } // Open dataset manager and select dataset by name @@ -1048,7 +1035,7 @@ function deleteSnippet(snippetId) { const snippet = SnippetStorage.getSnippet(snippetId); if (!snippet) return; - if (confirm(`Delete snippet "${snippet.name}"? This action cannot be undone.`)) { + const confirmed = confirmGenericDeletion(snippet.name, null, () => { SnippetStorage.deleteSnippet(snippetId); // If we deleted the currently selected snippet, clear selection @@ -1063,12 +1050,10 @@ function deleteSnippet(snippetId) { Toast.success('Snippet deleted'); // Track event - Analytics.track('snippet-delete', 'Delete snippet'); + trackEventIfAvailable('snippet-delete', 'Delete snippet'); + }); - return true; - } - - return false; + return confirmed; } // Load snippet into editor based on view mode