Sync DNS2 production changes - removed obsolete test suite and refactored structure

This commit is contained in:
Krystie
2026-03-23 10:47:15 +01:00
parent 1ac50918ab
commit d76644d948
288 changed files with 8965 additions and 15731 deletions

View File

@@ -0,0 +1,337 @@
/**
* Tooltip Definitions
* Defines all tooltip content, positioning, and behavior for the onboarding system
*/
(function(window) {
'use strict';
/**
* Validate a tooltip definition
* @param {Object} tooltip - The tooltip definition to validate
* @returns {Object} { valid: boolean, errors: string[] }
*/
function validateTooltipDefinition(tooltip) {
const errors = [];
// Required fields
if (!tooltip.id || typeof tooltip.id !== 'string') {
errors.push('Tooltip must have a valid string id');
}
if (!tooltip.element) {
errors.push('Tooltip must have an element selector or HTMLElement');
}
if (!tooltip.popover || typeof tooltip.popover !== 'object') {
errors.push('Tooltip must have a popover object');
} else {
// Validate popover fields
if (!tooltip.popover.title || typeof tooltip.popover.title !== 'string') {
errors.push('Tooltip popover must have a valid string title');
}
if (!tooltip.popover.description || typeof tooltip.popover.description !== 'string') {
errors.push('Tooltip popover must have a valid string description');
}
// Validate position if provided
if (tooltip.popover.position) {
const validPositions = ['top', 'bottom', 'left', 'right', 'center'];
if (!validPositions.includes(tooltip.popover.position)) {
errors.push(`Invalid position: ${tooltip.popover.position}. Must be one of: ${validPositions.join(', ')}`);
}
}
// Validate align if provided
if (tooltip.popover.align) {
const validAligns = ['start', 'center', 'end'];
if (!validAligns.includes(tooltip.popover.align)) {
errors.push(`Invalid align: ${tooltip.popover.align}. Must be one of: ${validAligns.join(', ')}`);
}
}
// Validate showButtons if provided
if (tooltip.popover.showButtons && !Array.isArray(tooltip.popover.showButtons)) {
errors.push('showButtons must be an array');
}
// Validate callbacks if provided
const callbacks = ['onNext', 'onPrevious', 'onClose', 'onSetupNow', 'onLater'];
callbacks.forEach(callback => {
if (tooltip.popover[callback] && typeof tooltip.popover[callback] !== 'function') {
errors.push(`${callback} must be a function`);
}
});
}
// Validate condition if provided
if (tooltip.condition && typeof tooltip.condition !== 'function') {
errors.push('condition must be a function');
}
// Validate priority if provided
if (tooltip.priority !== undefined && typeof tooltip.priority !== 'number') {
errors.push('priority must be a number');
}
return {
valid: errors.length === 0,
errors
};
}
/**
* Validate an array of tooltip definitions
* @param {Array} tooltips - Array of tooltip definitions
* @returns {Object} { valid: boolean, errors: Object[] }
*/
function validateTooltipDefinitions(tooltips) {
if (!Array.isArray(tooltips)) {
return {
valid: false,
errors: [{ tooltip: null, errors: ['tooltips must be an array'] }]
};
}
const allErrors = [];
const ids = new Set();
tooltips.forEach((tooltip, index) => {
const validation = validateTooltipDefinition(tooltip);
if (!validation.valid) {
allErrors.push({
tooltip: tooltip.id || `index ${index}`,
errors: validation.errors
});
}
// Check for duplicate IDs
if (tooltip.id) {
if (ids.has(tooltip.id)) {
allErrors.push({
tooltip: tooltip.id,
errors: [`Duplicate tooltip ID: ${tooltip.id}`]
});
}
ids.add(tooltip.id);
}
});
return {
valid: allErrors.length === 0,
errors: allErrors
};
}
/**
* Error handler for tooltip system
*/
class TooltipError extends Error {
constructor(message, tooltipId = null) {
super(message);
this.name = 'TooltipError';
this.tooltipId = tooltipId;
}
}
/**
* Handle tooltip definition errors
* @param {Object} validation - Validation result
* @throws {TooltipError} If validation fails
*/
function handleValidationErrors(validation) {
if (!validation.valid) {
const errorMessages = validation.errors.map(e =>
`${e.tooltip}: ${e.errors.join(', ')}`
).join('\n');
console.error('[TooltipDefinitions] Validation errors:', errorMessages);
throw new TooltipError(`Tooltip validation failed:\n${errorMessages}`);
}
}
// Export to global scope
window.TooltipValidation = {
validateTooltipDefinition,
validateTooltipDefinitions,
handleValidationErrors,
TooltipError
};
console.log('[TooltipDefinitions] Validation module loaded');
})(window);
/**
* Tooltip Definitions Array
* Defines all tooltips for the onboarding tour
*/
const TOOLTIP_DEFINITIONS = [
// 1. Welcome tooltip pointing to logo
{
id: 'welcome',
element: '#brand',
popover: {
title: 'Welcome to DashCaddy!',
description: `
<p>Your personal dashboard for managing services with Caddy reverse proxy.</p>
<p>Let's take a quick tour to help you get started.</p>
<p style="margin-top: 8px; font-size: 0.85rem; opacity: 0.8;">Tip: You can customize this logo in Settings.</p>
`,
position: 'bottom',
align: 'start',
showButtons: ['next'],
showProgress: true
},
priority: 1,
isNewFeature: false
},
// 2. Add Service button
{
id: 'add-service',
element: '#add-service-btn',
popover: {
title: 'Adding New Services',
description: `
<p>Click <strong>+ Add Service</strong> to deploy new apps or add existing services to your dashboard.</p>
<p>Choose from 50+ templates including:</p>
<ul>
<li>Media servers (Plex, Jellyfin, Emby)</li>
<li>Download managers (qBittorrent, Transmission)</li>
<li>DNS servers (Technitium, Pi-hole)</li>
</ul>
`,
position: 'bottom',
showButtons: ['previous', 'next'],
showProgress: true
},
priority: 2,
isNewFeature: false,
condition: () => {
return document.getElementById('add-service-btn') !== null;
}
},
// 3. App Grid explanation
{
id: 'app-grid',
element: '#cards',
popover: {
title: 'Your Services',
description: `
<p>This is your service grid where all your deployed applications appear.</p>
<p>Each card shows:</p>
<ul>
<li>Service status (online/offline)</li>
<li>Response time</li>
<li>Quick actions (restart, open, logs, settings)</li>
</ul>
`,
position: 'top',
showButtons: ['previous', 'next'],
showProgress: true
},
priority: 3,
isNewFeature: false
},
// 4. Theme selector
{
id: 'theme-selector',
element: '#theme',
popover: {
title: 'Customize Your Theme',
description: `
<p>DashCaddy comes with 7 themes. Click here to switch between them.</p>
<p>Your preference is saved automatically.</p>
`,
position: 'bottom',
showButtons: ['previous', 'close'],
showProgress: true
},
priority: 4,
isNewFeature: false,
condition: () => {
return document.getElementById('theme') !== null;
}
}
];
/**
* Get tooltip definitions
* @returns {Array} Array of tooltip definitions
*/
function getTooltipDefinitions() {
return TOOLTIP_DEFINITIONS;
}
/**
* Get a specific tooltip by ID
* @param {string} id - Tooltip ID
* @returns {Object|null} Tooltip definition or null if not found
*/
function getTooltipById(id) {
return TOOLTIP_DEFINITIONS.find(t => t.id === id) || null;
}
/**
* Get tooltips filtered by condition
* @returns {Array} Array of tooltips that pass their condition check
*/
function getActiveTooltips() {
return TOOLTIP_DEFINITIONS.filter(tooltip => {
if (tooltip.condition && typeof tooltip.condition === 'function') {
try {
return tooltip.condition();
} catch (error) {
console.error(`[TooltipDefinitions] Error evaluating condition for ${tooltip.id}:`, error);
return false;
}
}
return true;
});
}
/**
* Get tooltips sorted by priority
* @returns {Array} Array of tooltips sorted by priority (ascending)
*/
function getSortedTooltips() {
const tooltips = getActiveTooltips();
return tooltips.sort((a, b) => {
const priorityA = a.priority || 999;
const priorityB = b.priority || 999;
return priorityA - priorityB;
});
}
/**
* Get tooltips marked as new features
* @returns {Array} Array of tooltips marked with isNewFeature flag
*/
function getNewFeatureTooltips() {
const tooltips = getActiveTooltips();
return tooltips.filter(tooltip => tooltip.isNewFeature === true)
.sort((a, b) => {
const priorityA = a.priority || 999;
const priorityB = b.priority || 999;
return priorityA - priorityB;
});
}
// Export to global scope
window.TooltipDefinitions = {
TOOLTIP_DEFINITIONS,
getTooltipDefinitions,
getTooltipById,
getActiveTooltips,
getSortedTooltips,
getNewFeatureTooltips
};
console.log('[TooltipDefinitions] Definitions loaded:', TOOLTIP_DEFINITIONS.length, 'tooltips');