Initial commit: DashCaddy v1.0
Full codebase including API server (32 modules + routes), dashboard frontend, DashCA certificate distribution, installer script, and deployment skills.
This commit is contained in:
282
status/js/progress-tracker.js
Normal file
282
status/js/progress-tracker.js
Normal file
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* Progress Tracker
|
||||
* Manages persistent storage of user progress through the onboarding flow
|
||||
* using browser local storage.
|
||||
*
|
||||
* Storage Schema:
|
||||
* {
|
||||
* "version": "1.0",
|
||||
* "tourCompleted": false,
|
||||
* "completedTooltips": ["welcome", "dns-priority", ...],
|
||||
* "currentStep": 3,
|
||||
* "completionTimestamp": "2024-01-15T10:30:00Z",
|
||||
* "dnsSetupDeferred": false,
|
||||
* "lastVisit": "2024-01-15T10:30:00Z"
|
||||
* }
|
||||
*/
|
||||
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* ProgressTracker class
|
||||
* Manages persistent storage of onboarding progress
|
||||
*
|
||||
* @class
|
||||
* @param {string} storageKey - The key to use for local storage (default: 'dashcaddy_onboarding')
|
||||
*/
|
||||
class ProgressTracker {
|
||||
constructor(storageKey = 'dashcaddy_onboarding') {
|
||||
this.storageKey = storageKey;
|
||||
this.storageVersion = '1.0';
|
||||
|
||||
// Initialize storage if it doesn't exist
|
||||
this._initializeStorage();
|
||||
|
||||
// Update last visit timestamp
|
||||
this._updateLastVisit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize storage with default values if it doesn't exist
|
||||
* @private
|
||||
*/
|
||||
_initializeStorage() {
|
||||
const existing = this._getStorage();
|
||||
if (!existing || existing.version !== this.storageVersion) {
|
||||
const defaultState = {
|
||||
version: this.storageVersion,
|
||||
tourCompleted: false,
|
||||
completedTooltips: [],
|
||||
currentStep: 0,
|
||||
completionTimestamp: null,
|
||||
dnsSetupDeferred: false,
|
||||
lastVisit: new Date().toISOString()
|
||||
};
|
||||
this._setStorage(defaultState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current storage state
|
||||
* @private
|
||||
* @returns {Object|null} The storage state or null if unavailable
|
||||
*/
|
||||
_getStorage() {
|
||||
try {
|
||||
const data = localStorage.getItem(this.storageKey);
|
||||
return data ? JSON.parse(data) : null;
|
||||
} catch (error) {
|
||||
console.error('[ProgressTracker] Error reading from storage:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the storage state
|
||||
* @private
|
||||
* @param {Object} state - The state to save
|
||||
*/
|
||||
_setStorage(state) {
|
||||
try {
|
||||
localStorage.setItem(this.storageKey, JSON.stringify(state));
|
||||
} catch (error) {
|
||||
console.error('[ProgressTracker] Error writing to storage:', error);
|
||||
// Handle quota exceeded or storage unavailable
|
||||
// Fall back to session storage or in-memory storage
|
||||
this._handleStorageError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle storage errors (quota exceeded, unavailable, etc.)
|
||||
* @private
|
||||
* @param {Error} error - The error that occurred
|
||||
*/
|
||||
_handleStorageError(error) {
|
||||
// Try session storage as fallback
|
||||
try {
|
||||
sessionStorage.setItem(this.storageKey, JSON.stringify(this._getStorage()));
|
||||
console.warn('[ProgressTracker] Falling back to session storage');
|
||||
} catch (sessionError) {
|
||||
console.error('[ProgressTracker] Session storage also unavailable:', sessionError);
|
||||
// Could implement in-memory fallback here if needed
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the last visit timestamp
|
||||
* @private
|
||||
*/
|
||||
_updateLastVisit() {
|
||||
const state = this._getStorage();
|
||||
if (state) {
|
||||
state.lastVisit = new Date().toISOString();
|
||||
this._setStorage(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific tooltip has been completed
|
||||
* @param {string} tooltipId - The ID of the tooltip to check
|
||||
* @returns {boolean} True if the tooltip has been completed
|
||||
*/
|
||||
isTooltipCompleted(tooltipId) {
|
||||
const state = this._getStorage();
|
||||
if (!state) return false;
|
||||
return state.completedTooltips.includes(tooltipId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a tooltip as completed with timestamp
|
||||
* @param {string} tooltipId - The ID of the tooltip to mark as completed
|
||||
*/
|
||||
markTooltipCompleted(tooltipId) {
|
||||
const state = this._getStorage();
|
||||
if (!state) return;
|
||||
|
||||
// Add tooltip to completed list if not already there
|
||||
if (!state.completedTooltips.includes(tooltipId)) {
|
||||
state.completedTooltips.push(tooltipId);
|
||||
|
||||
// Store timestamp for this specific tooltip
|
||||
if (!state.tooltipTimestamps) {
|
||||
state.tooltipTimestamps = {};
|
||||
}
|
||||
state.tooltipTimestamps[tooltipId] = new Date().toISOString();
|
||||
|
||||
this._setStorage(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the entire tour has been completed
|
||||
* @returns {boolean} True if the tour is completed
|
||||
*/
|
||||
isTourCompleted() {
|
||||
const state = this._getStorage();
|
||||
if (!state) return false;
|
||||
return state.tourCompleted === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the entire tour as completed
|
||||
*/
|
||||
markTourCompleted() {
|
||||
const state = this._getStorage();
|
||||
if (!state) return;
|
||||
|
||||
state.tourCompleted = true;
|
||||
state.completionTimestamp = new Date().toISOString();
|
||||
this._setStorage(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current step index
|
||||
* @returns {number} The current step index (0-based)
|
||||
*/
|
||||
getCurrentStep() {
|
||||
const state = this._getStorage();
|
||||
if (!state) return 0;
|
||||
return state.currentStep || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current step index
|
||||
* @param {number} stepIndex - The step index to set (0-based)
|
||||
*/
|
||||
setCurrentStep(stepIndex) {
|
||||
const state = this._getStorage();
|
||||
if (!state) return;
|
||||
|
||||
state.currentStep = stepIndex;
|
||||
this._setStorage(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all progress and clear storage
|
||||
*/
|
||||
resetProgress() {
|
||||
const defaultState = {
|
||||
version: this.storageVersion,
|
||||
tourCompleted: false,
|
||||
completedTooltips: [],
|
||||
currentStep: 0,
|
||||
completionTimestamp: null,
|
||||
dnsSetupDeferred: false,
|
||||
lastVisit: new Date().toISOString()
|
||||
};
|
||||
this._setStorage(defaultState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the completion timestamp
|
||||
* @returns {Date|null} The completion timestamp or null if not completed
|
||||
*/
|
||||
getCompletionTimestamp() {
|
||||
const state = this._getStorage();
|
||||
if (!state || !state.completionTimestamp) return null;
|
||||
return new Date(state.completionTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if DNS setup was deferred
|
||||
* @returns {boolean} True if DNS setup was deferred
|
||||
*/
|
||||
isDnsSetupDeferred() {
|
||||
const state = this._getStorage();
|
||||
if (!state) return false;
|
||||
return state.dnsSetupDeferred === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark DNS setup as deferred
|
||||
*/
|
||||
markDnsSetupDeferred() {
|
||||
const state = this._getStorage();
|
||||
if (!state) return;
|
||||
|
||||
state.dnsSetupDeferred = true;
|
||||
this._setStorage(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp for a specific tooltip completion
|
||||
* @param {string} tooltipId - The ID of the tooltip
|
||||
* @returns {Date|null} The timestamp or null if not completed
|
||||
*/
|
||||
getTooltipTimestamp(tooltipId) {
|
||||
const state = this._getStorage();
|
||||
if (!state || !state.tooltipTimestamps || !state.tooltipTimestamps[tooltipId]) {
|
||||
return null;
|
||||
}
|
||||
return new Date(state.tooltipTimestamps[tooltipId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all completed tooltip IDs
|
||||
* @returns {string[]} Array of completed tooltip IDs
|
||||
*/
|
||||
getCompletedTooltips() {
|
||||
const state = this._getStorage();
|
||||
if (!state) return [];
|
||||
return state.completedTooltips || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last visit timestamp
|
||||
* @returns {Date|null} The last visit timestamp
|
||||
*/
|
||||
getLastVisit() {
|
||||
const state = this._getStorage();
|
||||
if (!state || !state.lastVisit) return null;
|
||||
return new Date(state.lastVisit);
|
||||
}
|
||||
}
|
||||
|
||||
// Export to global scope
|
||||
window.ProgressTracker = ProgressTracker;
|
||||
|
||||
console.log('[ProgressTracker] Module loaded');
|
||||
|
||||
})(window);
|
||||
Reference in New Issue
Block a user