refactor(js): move components.js to dev_tools module
components.js is a UI components library reference for developers, so it belongs in dev_tools alongside icons-page.js, testing-*.js, and code-quality-*.js. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,708 +0,0 @@
|
||||
// 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 (matches first section on page)
|
||||
activeSection: 'ecommerce',
|
||||
|
||||
// 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: 'dropdowns', name: 'Dropdowns', icon: 'chevron-down' },
|
||||
{ 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',
|
||||
|
||||
// Navigation & Discovery Demo (Priority 4)
|
||||
demoCategories: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Electronics',
|
||||
slug: 'electronics',
|
||||
url: '/category/electronics',
|
||||
product_count: 245,
|
||||
children: [
|
||||
{ id: 11, name: 'Audio', slug: 'audio', url: '/category/audio', product_count: 89, children: [
|
||||
{ id: 111, name: 'Headphones', slug: 'headphones', url: '/category/headphones', product_count: 45 },
|
||||
{ id: 112, name: 'Speakers', slug: 'speakers', url: '/category/speakers', product_count: 32 }
|
||||
]},
|
||||
{ id: 12, name: 'Wearables', slug: 'wearables', url: '/category/wearables', product_count: 67 },
|
||||
{ id: 13, name: 'Accessories', slug: 'accessories', url: '/category/accessories', product_count: 89 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Clothing',
|
||||
slug: 'clothing',
|
||||
url: '/category/clothing',
|
||||
product_count: 512,
|
||||
children: [
|
||||
{ id: 21, name: 'Men', slug: 'men', url: '/category/men', product_count: 234 },
|
||||
{ id: 22, name: 'Women', slug: 'women', url: '/category/women', product_count: 278 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Home & Garden',
|
||||
slug: 'home-garden',
|
||||
url: '/category/home-garden',
|
||||
product_count: 189
|
||||
}
|
||||
],
|
||||
currentCategory: { id: 111, name: 'Headphones', slug: 'headphones' },
|
||||
demoBreadcrumbs: [
|
||||
{ label: 'Electronics', url: '/category/electronics' },
|
||||
{ label: 'Audio', url: '/category/audio' },
|
||||
{ label: 'Headphones' }
|
||||
],
|
||||
|
||||
// Filter Demo State
|
||||
demoFilters: {
|
||||
categories: [
|
||||
{ id: 'headphones', name: 'Headphones', count: 45 },
|
||||
{ id: 'earbuds', name: 'Earbuds', count: 32 },
|
||||
{ id: 'speakers', name: 'Speakers', count: 28 }
|
||||
],
|
||||
brands: [
|
||||
{ id: 'audiotech', name: 'AudioTech', count: 24 },
|
||||
{ id: 'soundmax', name: 'SoundMax', count: 18 },
|
||||
{ id: 'beatspro', name: 'BeatsPro', count: 15 },
|
||||
{ id: 'sonicwave', name: 'SonicWave', count: 12 }
|
||||
],
|
||||
priceRange: { min: 0, max: 500 },
|
||||
attributes: {
|
||||
color: [
|
||||
{ value: 'black', label: 'Black', hex: '#1a1a1a', count: 35 },
|
||||
{ value: 'white', label: 'White', hex: '#ffffff', count: 28 },
|
||||
{ value: 'silver', label: 'Silver', hex: '#C0C0C0', count: 22 },
|
||||
{ value: 'blue', label: 'Blue', hex: '#3B82F6', count: 15 }
|
||||
],
|
||||
size: [
|
||||
{ value: 'compact', label: 'Compact', count: 18 },
|
||||
{ value: 'standard', label: 'Standard', count: 42 },
|
||||
{ value: 'over-ear', label: 'Over-ear', count: 25 }
|
||||
]
|
||||
},
|
||||
ratings: [
|
||||
{ value: 5, count: 12 },
|
||||
{ value: 4, count: 28 },
|
||||
{ value: 3, count: 15 },
|
||||
{ value: 2, count: 5 },
|
||||
{ value: 1, count: 2 }
|
||||
]
|
||||
},
|
||||
demoActiveFilters: {
|
||||
categories: [],
|
||||
brands: [],
|
||||
priceMin: undefined,
|
||||
priceMax: undefined,
|
||||
rating: undefined,
|
||||
attributes: {},
|
||||
inStock: false
|
||||
},
|
||||
demoSortBy: 'relevance',
|
||||
showMobileSearch: false,
|
||||
showMobileFilters: false,
|
||||
showCategoryDrawer: false,
|
||||
|
||||
// Search Demo Methods
|
||||
demoFilterProducts() {
|
||||
componentsLog.debug('Filtering products with:', this.demoActiveFilters);
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Filters applied!', 'info');
|
||||
}
|
||||
},
|
||||
demoSortProducts() {
|
||||
componentsLog.debug('Sorting by:', this.demoSortBy);
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Sorted by: ' + this.demoSortBy, 'info');
|
||||
}
|
||||
},
|
||||
|
||||
// Social Proof Demo (Priority 5)
|
||||
demoRating: 4.5,
|
||||
demoReviewCount: 127,
|
||||
demoRatingDistribution: { 5: 68, 4: 35, 3: 15, 2: 6, 3: 3 },
|
||||
demoUserRating: 0,
|
||||
demoReviews: [
|
||||
{
|
||||
id: 1,
|
||||
author_name: 'John D.',
|
||||
author_avatar: null,
|
||||
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. Would highly recommend to anyone looking for premium audio quality.',
|
||||
verified: true,
|
||||
created_at: '2025-01-15',
|
||||
helpful_count: 42,
|
||||
images: []
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
author_name: 'Sarah M.',
|
||||
author_avatar: null,
|
||||
rating: 4,
|
||||
title: 'Great sound, slightly tight fit',
|
||||
content: 'Sound quality is phenomenal and the build quality feels premium. My only complaint is they feel a bit tight after a few hours of continuous use. Otherwise perfect for music lovers.',
|
||||
verified: true,
|
||||
created_at: '2025-01-10',
|
||||
helpful_count: 18,
|
||||
images: []
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
author_name: 'Mike R.',
|
||||
author_avatar: null,
|
||||
rating: 5,
|
||||
title: 'Worth every penny',
|
||||
content: 'These headphones are absolutely worth the investment. The sound stage is wide, bass is punchy but not overwhelming, and the noise cancellation is top-notch.',
|
||||
verified: false,
|
||||
created_at: '2025-01-05',
|
||||
helpful_count: 7,
|
||||
images: []
|
||||
}
|
||||
],
|
||||
demoReviewSort: 'newest',
|
||||
demoNewReview: {
|
||||
rating: 0,
|
||||
title: '',
|
||||
content: '',
|
||||
images: []
|
||||
},
|
||||
submittingReview: false,
|
||||
showReviewForm: false,
|
||||
|
||||
// Review Demo Methods
|
||||
demoSubmitReview() {
|
||||
this.submittingReview = true;
|
||||
setTimeout(() => {
|
||||
this.submittingReview = false;
|
||||
this.showReviewForm = false;
|
||||
this.demoNewReview = { rating: 0, title: '', content: '', images: [] };
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Review submitted successfully!', 'success');
|
||||
}
|
||||
}, 1500);
|
||||
},
|
||||
demoMarkHelpful(reviewId) {
|
||||
const review = this.demoReviews.find(r => r.id === reviewId);
|
||||
if (review) {
|
||||
review.helpful_count++;
|
||||
}
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Thanks for your feedback!', 'info');
|
||||
}
|
||||
},
|
||||
|
||||
// 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,
|
||||
showDetailsModal: false,
|
||||
showLogModal: false,
|
||||
exampleLog: null,
|
||||
|
||||
// Confirm modal demo state
|
||||
showConfirmModalDemo: false,
|
||||
confirmModalVariant: 'danger',
|
||||
showDynamicConfirmDemo: false,
|
||||
dynamicItemName: '',
|
||||
|
||||
// Example log data for demo
|
||||
showErrorLogDemo() {
|
||||
this.exampleLog = {
|
||||
id: 1,
|
||||
level: 'ERROR',
|
||||
timestamp: new Date().toISOString(),
|
||||
logger_name: 'app.services.import',
|
||||
module: 'import_service',
|
||||
message: 'Failed to import product: Invalid GTIN format',
|
||||
exception_type: 'ValidationError',
|
||||
exception_message: 'GTIN must be 13 digits',
|
||||
stack_trace: 'Traceback (most recent call last):\n File "/app/services/import.py", line 42, in process\n validate_gtin(product.gtin)\n File "/app/validators.py", line 15, in validate_gtin\n raise ValidationError("GTIN must be 13 digits")\nValidationError: GTIN must be 13 digits'
|
||||
};
|
||||
this.showLogModal = true;
|
||||
},
|
||||
|
||||
showWarningLogDemo() {
|
||||
this.exampleLog = {
|
||||
id: 2,
|
||||
level: 'WARNING',
|
||||
timestamp: new Date().toISOString(),
|
||||
logger_name: 'app.utils.cache',
|
||||
module: 'cache',
|
||||
message: 'Cache miss for key: product_list_vendor_5. Fetching from database.',
|
||||
exception_type: null,
|
||||
exception_message: null,
|
||||
stack_trace: null
|
||||
};
|
||||
this.showLogModal = true;
|
||||
},
|
||||
|
||||
// ✅ 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');
|
||||
// noqa: JS-009 - Fallback for component showcase when Utils not loaded
|
||||
alert(messages[type] || messages.info);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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');
|
||||
Reference in New Issue
Block a user