Files
orion/static/admin/js/components.js
Samir Boulahtit ecd12b8667 feat: add Priority 3 demos to components showcase page
Add live demos for:
- Product Gallery with image navigation and thumbnails
- Variant Selector with size buttons and color swatches
- Product Info with title, price, rating, stock status
- Product Tabs with description, specifications, reviews

Add demoProductDetail state with full product data including
images, sizes, colors, specifications, and sample reviews.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 17:14:43 +01:00

485 lines
19 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: 'ecommerce', name: 'E-commerce', icon: 'shopping-cart' },
{ id: 'macros', name: 'Macros', icon: 'template' },
{ id: 'pagination', name: 'Pagination', icon: 'dots-horizontal' },
{ id: 'tabs', name: 'Tabs', icon: 'view-boards' },
{ 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' }
],
// Tab demo state
demoActiveTab: 'tab1',
// Number stepper demo state
demoQuantitySm: 3,
demoQuantityMd: 5,
demoQuantityLg: 500,
// E-commerce demo state
demoProducts: [
{
id: 1,
name: 'Premium Wireless Headphones',
url: '#',
image_url: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=300&h=300&fit=crop',
price: 149.99,
sale_price: 119.99,
rating: 4.5,
review_count: 127,
stock: 15,
is_new: false,
in_wishlist: false
},
{
id: 2,
name: 'Smart Watch Pro',
url: '#',
image_url: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=300&h=300&fit=crop',
price: 299.99,
sale_price: null,
rating: 4.8,
review_count: 89,
stock: 8,
is_new: true,
in_wishlist: true
},
{
id: 3,
name: 'Portable Bluetooth Speaker',
url: '#',
image_url: 'https://images.unsplash.com/photo-1608043152269-423dbba4e7e1?w=300&h=300&fit=crop',
price: 79.99,
sale_price: null,
rating: 4.2,
review_count: 45,
stock: 0,
is_new: false,
in_wishlist: false
}
],
demoCart: {
items: [
{
id: 1,
product_id: 1,
name: 'Premium Wireless Headphones',
url: '#',
image_url: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=300&h=300&fit=crop',
price: 119.99,
quantity: 1,
variant_name: 'Black',
max_quantity: 15
},
{
id: 2,
product_id: 2,
name: 'Smart Watch Pro',
url: '#',
image_url: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=300&h=300&fit=crop',
price: 299.99,
quantity: 2,
variant_name: 'Silver',
max_quantity: 8
}
],
item_count: 3,
subtotal: 719.97,
discount: 0,
shipping: 0,
tax: 0,
total: 719.97,
promo_code: null
},
showDemoCart: false,
demoQuantity: 1,
addingToCart: false,
addedToCart: false,
// E-commerce demo methods
demoAddToCart(product) {
this.addingToCart = true;
setTimeout(() => {
this.addingToCart = false;
this.addedToCart = true;
setTimeout(() => { this.addedToCart = false; }, 2000);
if (typeof Utils !== 'undefined' && Utils.showToast) {
Utils.showToast('Added to cart!', 'success');
}
}, 800);
},
demoToggleWishlist(product) {
product.in_wishlist = !product.in_wishlist;
const action = product.in_wishlist ? 'Added to' : 'Removed from';
if (typeof Utils !== 'undefined' && Utils.showToast) {
Utils.showToast(`${action} wishlist`, 'success');
}
},
demoRemoveFromCart(itemId) {
this.demoCart.items = this.demoCart.items.filter(i => i.id !== itemId);
this.demoCart.item_count = this.demoCart.items.reduce((sum, i) => sum + i.quantity, 0);
this.demoCart.subtotal = this.demoCart.items.reduce((sum, i) => sum + (i.price * i.quantity), 0);
this.demoCart.total = this.demoCart.subtotal;
},
// Product Detail Demo (Priority 3)
demoProductDetail: {
id: 1,
name: 'Premium Wireless Headphones',
sku: 'WH-1000XM5',
price: 349.99,
sale_price: 279.99,
rating: 4.7,
review_count: 1247,
stock: 23,
is_new: true,
short_description: 'Industry-leading noise cancellation with exceptional sound quality. 30-hour battery life.',
description: '<p>Experience the next level of silence with our Premium Wireless Headphones. Industry-leading noise cancellation technology lets you focus on what matters most.</p><p>With up to 30 hours of battery life, you can enjoy your favorite music all day long. The soft, pressure-relieving ear pads provide all-day comfort.</p>',
features: [
'Industry-leading noise cancellation',
'30-hour battery life',
'Hi-Res Audio certified',
'Multipoint connection',
'Speak-to-chat technology'
],
specifications: [
{ name: 'Driver Size', value: '40mm' },
{ name: 'Frequency Response', value: '4Hz - 40,000Hz' },
{ name: 'Battery Life', value: '30 hours' },
{ name: 'Charging Time', value: '3 hours' },
{ name: 'Weight', value: '250g' },
{ name: 'Connectivity', value: 'Bluetooth 5.2, 3.5mm' }
],
images: [
{ id: 1, url: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800&h=800&fit=crop', alt: 'Headphones front view' },
{ id: 2, url: 'https://images.unsplash.com/photo-1484704849700-f032a568e944?w=800&h=800&fit=crop', alt: 'Headphones side view' },
{ id: 3, url: 'https://images.unsplash.com/photo-1583394838336-acd977736f90?w=800&h=800&fit=crop', alt: 'Headphones with case' },
{ id: 4, url: 'https://images.unsplash.com/photo-1546435770-a3e426bf472b?w=800&h=800&fit=crop', alt: 'Person wearing headphones' }
],
sizes: [
{ id: 1, name: 'Standard', value: 'standard', stock: 23 },
{ id: 2, name: 'Compact', value: 'compact', stock: 8 }
],
colors: [
{ id: 1, name: 'Black', value: 'black', color_hex: '#1a1a1a', stock: 15 },
{ id: 2, name: 'Silver', value: 'silver', color_hex: '#C0C0C0', stock: 8 },
{ id: 3, name: 'Midnight Blue', value: 'blue', color_hex: '#191970', stock: 0 }
],
reviews: [
{
id: 1,
author_name: 'John D.',
rating: 5,
title: 'Best headphones I\'ve ever owned',
content: 'The noise cancellation is incredible. I use these for work calls and they block out everything. Battery life is amazing too.',
verified: true,
created_at: '2025-01-15',
helpful_count: 42
},
{
id: 2,
author_name: 'Sarah M.',
rating: 4,
title: 'Great sound, slightly tight fit',
content: 'Sound quality is phenomenal. My only complaint is they feel a bit tight after a few hours. Otherwise perfect.',
verified: true,
created_at: '2025-01-10',
helpful_count: 18
}
],
rating_distribution: { 5: 68, 4: 22, 3: 6, 2: 3, 1: 1 },
category: { name: 'Headphones', slug: 'headphones', url: '/category/headphones' },
vendor: { name: 'AudioTech', slug: 'audiotech', url: '/vendor/audiotech' }
},
selectedImage: 0,
selectedSize: null,
selectedColor: null,
activeProductTab: 'description',
// 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');