Update admin components page with documentation for: - New Macros section listing all available shared macros with imports - Pagination section with live interactive examples - Copy-to-clipboard functionality for code snippets - Dark mode support for all new sections This serves as a living style guide for developers implementing new admin pages using the shared component library. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
295 lines
11 KiB
JavaScript
295 lines
11 KiB
JavaScript
// static/admin/js/components.js
|
|
|
|
// ✅ Use centralized logger - ONE LINE!
|
|
// Create custom logger for components page
|
|
const componentsLog = window.LogConfig.createLogger('COMPONENTS');
|
|
|
|
/**
|
|
* Components Library Alpine.js Component
|
|
* UI components reference with live examples
|
|
*/
|
|
function adminComponents() {
|
|
return {
|
|
// ✅ CRITICAL: Inherit base layout functionality
|
|
...data(),
|
|
|
|
// ✅ CRITICAL: Set page identifier
|
|
currentPage: 'components',
|
|
|
|
// Active section for navigation
|
|
activeSection: 'forms',
|
|
|
|
// Component sections
|
|
sections: [
|
|
{ id: 'macros', name: 'Macros', icon: 'template' },
|
|
{ id: 'pagination', name: 'Pagination', icon: 'dots-horizontal' },
|
|
{ id: 'forms', name: 'Forms', icon: 'clipboard-list' },
|
|
{ id: 'buttons', name: 'Buttons', icon: 'cursor-click' },
|
|
{ id: 'cards', name: 'Cards', icon: 'collection' },
|
|
{ id: 'badges', name: 'Badges', icon: 'tag' },
|
|
{ id: 'tables', name: 'Tables', icon: 'table' },
|
|
{ id: 'modals', name: 'Modals', icon: 'view-grid-add' },
|
|
{ id: 'alerts', name: 'Alerts', icon: 'exclamation' },
|
|
{ id: 'charts', name: 'Charts', icon: 'chart-pie' }
|
|
],
|
|
|
|
// Sample form data for examples
|
|
exampleForm: {
|
|
textInput: 'Sample text',
|
|
email: 'user@example.com',
|
|
textarea: 'Sample description text...',
|
|
select: 'option1',
|
|
checkbox: true,
|
|
radio: 'option1',
|
|
disabled: 'Read-only value'
|
|
},
|
|
|
|
// Sample errors for validation examples
|
|
exampleErrors: {
|
|
email: 'Please enter a valid email address',
|
|
required: 'This field is required'
|
|
},
|
|
|
|
// Modal state variables for examples
|
|
showExampleModal: false,
|
|
showFormModal: false,
|
|
|
|
// ✅ CRITICAL: Proper initialization with guard
|
|
async init() {
|
|
componentsLog.info('=== COMPONENTS PAGE INITIALIZING ===');
|
|
|
|
// Prevent multiple initializations
|
|
if (window._componentsInitialized) {
|
|
componentsLog.warn('Components page already initialized, skipping...');
|
|
return;
|
|
}
|
|
window._componentsInitialized = true;
|
|
|
|
// Set active section from URL hash if present
|
|
this.setActiveSectionFromHash();
|
|
|
|
// Listen for hash changes
|
|
window.addEventListener('hashchange', () => {
|
|
this.setActiveSectionFromHash();
|
|
});
|
|
|
|
// Initialize charts after DOM is ready
|
|
this.$nextTick(() => {
|
|
this.initializeCharts();
|
|
});
|
|
|
|
componentsLog.info('=== COMPONENTS PAGE INITIALIZATION COMPLETE ===');
|
|
},
|
|
|
|
/**
|
|
* Set active section from URL hash
|
|
*/
|
|
setActiveSectionFromHash() {
|
|
const hash = window.location.hash.replace('#', '');
|
|
if (hash && this.sections.find(s => s.id === hash)) {
|
|
this.activeSection = hash;
|
|
componentsLog.debug('Set active section from hash:', hash);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Navigate to section
|
|
*/
|
|
goToSection(sectionId) {
|
|
componentsLog.info('Navigating to section:', sectionId);
|
|
this.activeSection = sectionId;
|
|
window.location.hash = sectionId;
|
|
|
|
// Smooth scroll to section
|
|
const element = document.getElementById(sectionId);
|
|
if (element) {
|
|
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if section is active
|
|
*/
|
|
isSectionActive(sectionId) {
|
|
return this.activeSection === sectionId;
|
|
},
|
|
|
|
/**
|
|
* Copy code to clipboard
|
|
*/
|
|
async copyCode(code) {
|
|
try {
|
|
await navigator.clipboard.writeText(code);
|
|
// Use the global Utils.showToast function
|
|
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
|
Utils.showToast('Code copied to clipboard!', 'success');
|
|
} else {
|
|
componentsLog.warn('Utils.showToast not available');
|
|
}
|
|
componentsLog.debug('Code copied to clipboard');
|
|
} catch (error) {
|
|
window.LogConfig.logError(error, 'Copy Code');
|
|
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
|
Utils.showToast('Failed to copy code', 'error');
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Show toast example
|
|
*/
|
|
showToastExample(type) {
|
|
const messages = {
|
|
success: 'Operation completed successfully!',
|
|
error: 'An error occurred!',
|
|
warning: 'Please review your input.',
|
|
info: 'Here is some information.'
|
|
};
|
|
|
|
componentsLog.info('Showing toast example:', type);
|
|
|
|
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
|
Utils.showToast(messages[type] || messages.info, type);
|
|
} else {
|
|
componentsLog.error('Utils.showToast not available');
|
|
alert(messages[type] || messages.info); // Fallback to alert
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Initialize Chart.js charts
|
|
*/
|
|
initializeCharts() {
|
|
try {
|
|
// Check if Chart.js is loaded
|
|
if (typeof Chart === 'undefined') {
|
|
componentsLog.warn('Chart.js not loaded, skipping chart initialization');
|
|
return;
|
|
}
|
|
|
|
componentsLog.info('Initializing charts...');
|
|
componentsLog.time('Chart Initialization');
|
|
|
|
// Pie Chart
|
|
const pieCanvas = document.getElementById('examplePieChart');
|
|
if (pieCanvas) {
|
|
const pieConfig = {
|
|
type: 'doughnut',
|
|
data: {
|
|
datasets: [{
|
|
data: [33, 33, 33],
|
|
backgroundColor: ['#0694a2', '#7e3af2', '#1c64f2'],
|
|
label: 'Dataset 1',
|
|
}],
|
|
labels: ['Shoes', 'Shirts', 'Bags'],
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
cutoutPercentage: 80,
|
|
legend: {
|
|
display: false,
|
|
},
|
|
},
|
|
};
|
|
new Chart(pieCanvas, pieConfig);
|
|
componentsLog.debug('Pie chart initialized');
|
|
}
|
|
|
|
// Line Chart
|
|
const lineCanvas = document.getElementById('exampleLineChart');
|
|
if (lineCanvas) {
|
|
const lineConfig = {
|
|
type: 'line',
|
|
data: {
|
|
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
|
|
datasets: [{
|
|
label: 'Organic',
|
|
backgroundColor: '#0694a2',
|
|
borderColor: '#0694a2',
|
|
data: [43, 48, 40, 54, 67, 73, 70],
|
|
fill: false,
|
|
}, {
|
|
label: 'Paid',
|
|
fill: false,
|
|
backgroundColor: '#7e3af2',
|
|
borderColor: '#7e3af2',
|
|
data: [24, 50, 64, 74, 52, 51, 65],
|
|
}],
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
legend: {
|
|
display: false,
|
|
},
|
|
tooltips: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
},
|
|
hover: {
|
|
mode: 'nearest',
|
|
intersect: true,
|
|
},
|
|
scales: {
|
|
x: {
|
|
display: true,
|
|
scaleLabel: {
|
|
display: true,
|
|
labelString: 'Month',
|
|
},
|
|
},
|
|
y: {
|
|
display: true,
|
|
scaleLabel: {
|
|
display: true,
|
|
labelString: 'Value',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
new Chart(lineCanvas, lineConfig);
|
|
componentsLog.debug('Line chart initialized');
|
|
}
|
|
|
|
// Bar Chart
|
|
const barCanvas = document.getElementById('exampleBarChart');
|
|
if (barCanvas) {
|
|
const barConfig = {
|
|
type: 'bar',
|
|
data: {
|
|
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
|
|
datasets: [{
|
|
label: 'Shoes',
|
|
backgroundColor: '#0694a2',
|
|
borderColor: '#0694a2',
|
|
borderWidth: 1,
|
|
data: [43, 48, 40, 54, 67, 73, 70],
|
|
}, {
|
|
label: 'Bags',
|
|
backgroundColor: '#7e3af2',
|
|
borderColor: '#7e3af2',
|
|
borderWidth: 1,
|
|
data: [24, 50, 64, 74, 52, 51, 65],
|
|
}],
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
legend: {
|
|
display: false,
|
|
},
|
|
},
|
|
};
|
|
new Chart(barCanvas, barConfig);
|
|
componentsLog.debug('Bar chart initialized');
|
|
}
|
|
|
|
componentsLog.timeEnd('Chart Initialization');
|
|
componentsLog.info('All charts initialized successfully');
|
|
} catch (error) {
|
|
window.LogConfig.logError(error, 'Initialize Charts');
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
componentsLog.info('Components module loaded'); |