feat: integrate GoatCounter analytics for event tracking across modals and snippets

This commit is contained in:
2025-10-16 22:59:54 +03:00
parent 8b056b66e7
commit e7d3669772
5 changed files with 65 additions and 0 deletions

View File

@@ -534,6 +534,11 @@
<script src="src/js/panel-manager.js"></script> <script src="src/js/panel-manager.js"></script>
<script src="src/js/editor.js"></script> <script src="src/js/editor.js"></script>
<script src="src/js/app.js"></script> <script src="src/js/app.js"></script>
<!-- GoatCounter Analytics -->
<script data-goatcounter="https://astrolabe.goatcounter.com/count"
data-goatcounter-settings='{"allow_local": true}'
async src="//gc.zgo.at/count.js"></script>
</body> </body>
</html> </html>

View File

@@ -468,6 +468,8 @@ function openHelpModal() {
const modal = document.getElementById('help-modal'); const modal = document.getElementById('help-modal');
if (modal) { if (modal) {
modal.style.display = 'flex'; modal.style.display = 'flex';
// Track event
Analytics.track('modal-help', 'Open Help modal');
} }
} }
@@ -483,6 +485,8 @@ function openDonateModal() {
const modal = document.getElementById('donate-modal'); const modal = document.getElementById('donate-modal');
if (modal) { if (modal) {
modal.style.display = 'flex'; modal.style.display = 'flex';
// Track event
Analytics.track('modal-donate', 'Open Donate modal');
} }
} }

View File

@@ -210,6 +210,20 @@ const Toast = {
} }
}; };
// Analytics utility: Track events with GoatCounter
const Analytics = {
track(eventName, title) {
// Only track if GoatCounter is loaded
if (window.goatcounter && window.goatcounter.count) {
window.goatcounter.count({
path: eventName,
title: title || eventName,
event: true,
});
}
}
};
// Shared utility: Format bytes for display // Shared utility: Format bytes for display
function formatBytes(bytes) { function formatBytes(bytes) {
if (bytes === null || bytes === undefined) return 'N/A'; if (bytes === null || bytes === undefined) return 'N/A';

View File

@@ -785,6 +785,9 @@ function openDatasetManager(updateURL = true) {
if (updateURL) { if (updateURL) {
URLState.update({ view: 'datasets', snippetId: null, datasetId: null }); URLState.update({ view: 'datasets', snippetId: null, datasetId: null });
} }
// Track event
Analytics.track('modal-datasets', 'Open Dataset Manager');
} }
// Close dataset manager modal // Close dataset manager modal
@@ -1139,6 +1142,9 @@ async function saveNewDataset() {
hideNewDatasetForm(); hideNewDatasetForm();
await renderDatasetList(); await renderDatasetList();
// Track event
Analytics.track('dataset-create', `Create dataset (${source})`);
} catch (error) { } catch (error) {
errorEl.textContent = `Failed to save dataset: ${error.message}`; errorEl.textContent = `Failed to save dataset: ${error.message}`;
} }
@@ -1167,6 +1173,9 @@ async function deleteCurrentDataset() {
// Show success message // Show success message
Toast.success('Dataset deleted'); Toast.success('Dataset deleted');
// Track event
Analytics.track('dataset-delete', 'Delete dataset');
} }
} }
@@ -1295,6 +1304,9 @@ async function exportCurrentDataset() {
// Show success message // Show success message
Toast.success(`Dataset "${dataset.name}" exported successfully`); Toast.success(`Dataset "${dataset.name}" exported successfully`);
// Track event
Analytics.track('dataset-export', `Export dataset (${dataset.format})`);
} catch (error) { } catch (error) {
Toast.error(`Failed to export dataset: ${error.message}`); Toast.error(`Failed to export dataset: ${error.message}`);
} }
@@ -1394,6 +1406,9 @@ async function importDatasetFromFile(fileInput) {
Toast.success(`Dataset "${datasetName}" imported successfully!`); Toast.success(`Dataset "${datasetName}" imported successfully!`);
} }
// Track event
Analytics.track('dataset-import', `Import dataset (${format})`);
} catch (error) { } catch (error) {
Toast.error(`Failed to import dataset: ${error.message}`); Toast.error(`Failed to import dataset: ${error.message}`);
} finally { } finally {

View File

@@ -835,6 +835,9 @@ function createNewSnippet() {
renderSnippetList(); renderSnippetList();
selectSnippet(newSnippet.id); selectSnippet(newSnippet.id);
// Track event
Analytics.track('snippet-create', 'Create new snippet');
return newSnippet; return newSnippet;
} }
@@ -859,6 +862,9 @@ function duplicateSnippet(snippetId) {
// Show success message // Show success message
Toast.success('Snippet duplicated successfully'); Toast.success('Snippet duplicated successfully');
// Track event
Analytics.track('snippet-duplicate', 'Duplicate snippet');
return newSnippet; return newSnippet;
} }
@@ -881,6 +887,9 @@ function createSnippetFromDataset(datasetName) {
renderSnippetList(); renderSnippetList();
selectSnippet(newSnippet.id); selectSnippet(newSnippet.id);
// Track event
Analytics.track('snippet-from-dataset', 'Create snippet from dataset');
return newSnippet; return newSnippet;
} }
@@ -992,6 +1001,9 @@ async function extractToDataset() {
// Show success message // Show success message
Toast.success(`Dataset "${datasetName}" created successfully!`); Toast.success(`Dataset "${datasetName}" created successfully!`);
// Track event
Analytics.track('dataset-extract', 'Extract inline data to dataset');
} catch (error) { } catch (error) {
errorEl.textContent = `Failed to create dataset: ${error.message}`; errorEl.textContent = `Failed to create dataset: ${error.message}`;
} }
@@ -1032,6 +1044,9 @@ function deleteSnippet(snippetId) {
// Show success message // Show success message
Toast.success('Snippet deleted'); Toast.success('Snippet deleted');
// Track event
Analytics.track('snippet-delete', 'Delete snippet');
return true; return true;
} }
@@ -1121,6 +1136,9 @@ function publishDraft() {
// Show success message // Show success message
Toast.success('Snippet published successfully!'); Toast.success('Snippet published successfully!');
// Track event
Analytics.track('snippet-publish', 'Publish draft');
} }
// Revert draft to published spec // Revert draft to published spec
@@ -1146,6 +1164,9 @@ function revertDraft() {
// Show success message // Show success message
Toast.success('Draft reverted to published version'); Toast.success('Draft reverted to published version');
// Track event
Analytics.track('snippet-revert', 'Revert draft');
} }
} }
@@ -1212,6 +1233,9 @@ function exportSnippets() {
// Show success message // Show success message
Toast.success(`Exported ${snippets.length} snippet${snippets.length !== 1 ? 's' : ''}`); Toast.success(`Exported ${snippets.length} snippet${snippets.length !== 1 ? 's' : ''}`);
// Track event
Analytics.track('snippets-export', `Export ${snippets.length} snippets`);
} }
// Normalize external snippet format to Astrolabe format // Normalize external snippet format to Astrolabe format
@@ -1296,6 +1320,9 @@ function importSnippets(fileInput) {
if (SnippetStorage.saveSnippets(existingSnippets)) { if (SnippetStorage.saveSnippets(existingSnippets)) {
Toast.success(`Successfully imported ${importedCount} snippet${importedCount !== 1 ? 's' : ''}`); Toast.success(`Successfully imported ${importedCount} snippet${importedCount !== 1 ? 's' : ''}`);
renderSnippetList(); renderSnippetList();
// Track event
Analytics.track('snippets-import', `Import ${importedCount} snippets`);
} }
} catch (error) { } catch (error) {