mirror of
https://github.com/olehomelchenko/astrolabe-nvc.git
synced 2025-12-21 13:12:23 +00:00
feat: integrate generic storage UI and refactor dataset/snippet item handling
This commit is contained in:
@@ -700,6 +700,7 @@
|
||||
|
||||
<script src="src/js/user-settings.js"></script>
|
||||
<script src="src/js/config.js"></script>
|
||||
<script src="src/js/generic-storage-ui.js"></script>
|
||||
<script src="src/js/snippet-manager.js"></script>
|
||||
<script src="src/js/dataset-manager.js"></script>
|
||||
<script src="src/js/panel-manager.js"></script>
|
||||
|
||||
@@ -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 `
|
||||
<div class="dataset-item" data-dataset-id="${dataset.id}">
|
||||
<div class="dataset-item" data-item-id="${dataset.id}">
|
||||
<div class="dataset-info">
|
||||
<div class="dataset-name">${dataset.name}</div>
|
||||
<div class="dataset-meta">${metaText}</div>
|
||||
@@ -316,14 +316,15 @@ async function renderDatasetList() {
|
||||
${usageBadge}
|
||||
</div>
|
||||
`;
|
||||
}).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) => `
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">📄</span>
|
||||
<span>
|
||||
<a href="#" class="snippet-link" data-snippet-id="${snippet.id}">${snippet.name}</a>
|
||||
<a href="#" class="snippet-link" data-linked-item-id="${snippet.id}">${snippet.name}</a>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}).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
|
||||
|
||||
@@ -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 ? '<span class="snippet-dataset-icon" title="Uses external dataset">📁</span>' : '';
|
||||
|
||||
return `
|
||||
<li class="snippet-item" data-snippet-id="${snippet.id}">
|
||||
<li class="snippet-item" data-item-id="${snippet.id}">
|
||||
<div class="snippet-info">
|
||||
<div class="snippet-name">${snippet.name}${datasetIconHTML}</div>
|
||||
<div class="snippet-date">${dateText}</div>
|
||||
@@ -373,8 +375,9 @@ function renderSnippetList(searchQuery = null) {
|
||||
<div class="snippet-status ${statusClass}"></div>
|
||||
</li>
|
||||
`;
|
||||
}).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) => `
|
||||
<div class="meta-info-item">
|
||||
<span class="meta-info-label">📁</span>
|
||||
<span class="meta-info-value">
|
||||
<a href="#" class="dataset-link" data-dataset-name="${datasetName}">${datasetName}</a>
|
||||
<a href="#" class="dataset-link" data-linked-item-id="${datasetName}">${datasetName}</a>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}).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
|
||||
|
||||
Reference in New Issue
Block a user