UNPKG

gentelella

Version:

Gentelella Admin is a free to use Bootstrap admin template

172 lines (148 loc) 143 kB
{"0": { "doc": "API Integration", "title": "API Integration Guide", "content": "Learn how to integrate Gentelella Admin Template with backend APIs and external services . ", "url": "/gentelella/api-integration/#api-integration-guide", "relUrl": "/api-integration/#api-integration-guide" },"1": { "doc": "API Integration", "title": "Table of contents", "content": ". | REST API Integration . | HTTP Client Setup . | Axios Configuration | . | API Service Layer . | Base Service Class | Specific Service Classes | . | . | Real-time Integration . | WebSocket Connection | Real-time Dashboard Updates | . | Data Management . | State Management | Data Caching | . | Authentication Integration . | JWT Token Management | . | Error Handling . | Global Error Handler | . | Performance Optimization . | Request Batching | . | Next Steps | . ", "url": "/gentelella/api-integration/#table-of-contents", "relUrl": "/api-integration/#table-of-contents" },"2": { "doc": "API Integration", "title": "REST API Integration", "content": "HTTP Client Setup . Axios Configuration . // src/js/api/http-client.js import axios from 'axios'; class HttpClient { constructor() { this.client = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8080/api', timeout: 10000, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } }); this.setupInterceptors(); } setupInterceptors() { // Request interceptor - add auth token this.client.interceptors.request.use( (config) => { const token = localStorage.getItem('auth_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Response interceptor - handle errors this.client.interceptors.response.use( (response) => response.data, (error) => { if (error.response?.status === 401) { this.handleUnauthorized(); } return Promise.reject(this.formatError(error)); } ); } handleUnauthorized() { localStorage.removeItem('auth_token'); localStorage.removeItem('user_data'); window.location.href = '/login.html'; } formatError(error) { if (error.response) { return { message: error.response.data?.message || 'Server error', status: error.response.status, data: error.response.data }; } else if (error.request) { return { message: 'Network error - please check your connection', status: 0 }; } else { return { message: error.message || 'Unknown error occurred', status: -1 }; } } // HTTP methods get(url, config = {}) { return this.client.get(url, config); } post(url, data = {}, config = {}) { return this.client.post(url, data, config); } put(url, data = {}, config = {}) { return this.client.put(url, data, config); } patch(url, data = {}, config = {}) { return this.client.patch(url, data, config); } delete(url, config = {}) { return this.client.delete(url, config); } // File upload upload(url, file, onProgress = null) { const formData = new FormData(); formData.append('file', file); return this.client.post(url, formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (progressEvent) => { if (onProgress) { const progress = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); onProgress(progress); } } }); } } // Create singleton instance export const httpClient = new HttpClient(); . API Service Layer . Base Service Class . // src/js/api/base-service.js import { httpClient } from './http-client.js'; export class BaseService { constructor(endpoint) { this.endpoint = endpoint; this.http = httpClient; } async getAll(params = {}) { try { const response = await this.http.get(this.endpoint, { params }); return { success: true, data: response.data, meta: response.meta }; } catch (error) { return { success: false, error: error.message, details: error }; } } async getById(id) { try { const response = await this.http.get(`${this.endpoint}/${id}`); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message, details: error }; } } async create(data) { try { const response = await this.http.post(this.endpoint, data); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message, details: error }; } } async update(id, data) { try { const response = await this.http.put(`${this.endpoint}/${id}`, data); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message, details: error }; } } async delete(id) { try { await this.http.delete(`${this.endpoint}/${id}`); return { success: true }; } catch (error) { return { success: false, error: error.message, details: error }; } } async search(query, params = {}) { try { const response = await this.http.get(`${this.endpoint}/search`, { params: { q: query, ...params } }); return { success: true, data: response.data, meta: response.meta }; } catch (error) { return { success: false, error: error.message, details: error }; } } } . Specific Service Classes . // src/js/api/user-service.js import { BaseService } from './base-service.js'; class UserService extends BaseService { constructor() { super('/users'); } async authenticate(credentials) { try { const response = await this.http.post('/auth/login', credentials); // Store auth token if (response.token) { localStorage.setItem('auth_token', response.token); localStorage.setItem('user_data', JSON.stringify(response.user)); } return { success: true, data: response }; } catch (error) { return { success: false, error: error.message }; } } async logout() { try { await this.http.post('/auth/logout'); } catch (error) { console.warn('Logout API call failed:', error.message); } finally { localStorage.removeItem('auth_token'); localStorage.removeItem('user_data'); window.location.href = '/login.html'; } } async getCurrentUser() { try { const response = await this.http.get('/auth/me'); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async updateProfile(data) { try { const response = await this.http.put('/auth/profile', data); // Update stored user data localStorage.setItem('user_data', JSON.stringify(response.data)); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async changePassword(passwordData) { try { const response = await this.http.post('/auth/change-password', passwordData); return { success: true, data: response }; } catch (error) { return { success: false, error: error.message }; } } async uploadAvatar(file, onProgress) { try { const response = await this.http.upload('/auth/avatar', file, onProgress); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } } export const userService = new UserService(); // src/js/api/dashboard-service.js import { BaseService } from './base-service.js'; class DashboardService extends BaseService { constructor() { super('/dashboard'); } async getStats(dateRange = '30d') { try { const response = await this.http.get('/dashboard/stats', { params: { range: dateRange } }); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async getChartData(chartType, params = {}) { try { const response = await this.http.get(`/dashboard/charts/${chartType}`, { params }); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } async getRecentActivity(limit = 10) { try { const response = await this.http.get('/dashboard/activity', { params: { limit } }); return { success: true, data: response.data }; } catch (error) { return { success: false, error: error.message }; } } } export const dashboardService = new DashboardService(); . ", "url": "/gentelella/api-integration/#rest-api-integration", "relUrl": "/api-integration/#rest-api-integration" },"3": { "doc": "API Integration", "title": "Real-time Integration", "content": "WebSocket Connection . // src/js/api/websocket-client.js class WebSocketClient { constructor() { this.ws = null; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectDelay = 1000; this.listeners = new Map(); this.isConnected = false; } connect() { const wsUrl = import.meta.env.VITE_WS_URL || 'ws://localhost:8080/ws'; const token = localStorage.getItem('auth_token'); this.ws = new WebSocket(`${wsUrl}?token=${token}`); this.ws.onopen = () => { console.log('WebSocket connected'); this.isConnected = true; this.reconnectAttempts = 0; this.emit('connected'); }; this.ws.onmessage = (event) => { try { const message = JSON.parse(event.data); this.handleMessage(message); } catch (error) { console.error('Failed to parse WebSocket message:', error); } }; this.ws.onclose = () => { console.log('WebSocket disconnected'); this.isConnected = false; this.emit('disconnected'); this.reconnect(); }; this.ws.onerror = (error) => { console.error('WebSocket error:', error); this.emit('error', error); }; } reconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.error('Max reconnection attempts reached'); return; } this.reconnectAttempts++; const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1); console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`); setTimeout(() => { this.connect(); }, delay); } handleMessage(message) { const { type, data } = message; this.emit(type, data); } send(type, data = {}) { if (!this.isConnected) { console.warn('WebSocket not connected'); return false; } const message = JSON.stringify({ type, data }); this.ws.send(message); return true; } on(event, callback) { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event).push(callback); } off(event, callback) { if (!this.listeners.has(event)) return; const callbacks = this.listeners.get(event); const index = callbacks.indexOf(callback); if (index > -1) { callbacks.splice(index, 1); } } emit(event, data) { if (!this.listeners.has(event)) return; this.listeners.get(event).forEach(callback => { try { callback(data); } catch (error) { console.error(`Error in WebSocket event handler for ${event}:`, error); } }); } disconnect() { if (this.ws) { this.ws.close(); this.ws = null; } this.isConnected = false; } } // Create singleton instance export const wsClient = new WebSocketClient(); // Auto-connect if user is authenticated if (localStorage.getItem('auth_token')) { wsClient.connect(); } . Real-time Dashboard Updates . // src/js/dashboard/real-time-dashboard.js import { wsClient } from '../api/websocket-client.js'; import { dashboardService } from '../api/dashboard-service.js'; class RealTimeDashboard { constructor() { this.charts = new Map(); this.stats = new Map(); this.init(); } init() { this.setupWebSocketListeners(); this.loadInitialData(); } setupWebSocketListeners() { // Listen for real-time stats updates wsClient.on('stats.update', (data) => { this.updateStats(data); }); // Listen for new chart data wsClient.on('chart.data', (data) => { this.updateChart(data.chartId, data.data); }); // Listen for new notifications wsClient.on('notification', (data) => { this.showNotification(data); }); // Listen for user activity wsClient.on('user.activity', (data) => { this.updateActivityFeed(data); }); } async loadInitialData() { try { // Load dashboard stats const statsResult = await dashboardService.getStats(); if (statsResult.success) { this.renderStats(statsResult.data); } // Load chart data const chartTypes = ['sales', 'users', 'revenue']; for (const chartType of chartTypes) { const chartResult = await dashboardService.getChartData(chartType); if (chartResult.success) { this.renderChart(chartType, chartResult.data); } } // Load recent activity const activityResult = await dashboardService.getRecentActivity(); if (activityResult.success) { this.renderActivity(activityResult.data); } } catch (error) { console.error('Failed to load dashboard data:', error); } } updateStats(data) { Object.entries(data).forEach(([key, value]) => { const element = document.querySelector(`[data-stat=\"${key}\"]`); if (element) { // Animate value change this.animateValue(element, value); } }); } animateValue(element, newValue) { const currentValue = parseFloat(element.textContent.replace(/[^0-9.-]/g, '')) || 0; const difference = newValue - currentValue; const steps = 30; const stepValue = difference / steps; let current = currentValue; const timer = setInterval(() => { current += stepValue; element.textContent = this.formatValue(current, element.dataset.format); if (--steps <= 0) { clearInterval(timer); element.textContent = this.formatValue(newValue, element.dataset.format); } }, 16); } formatValue(value, format) { switch (format) { case 'currency': return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value); case 'percentage': return `${value.toFixed(1)}%`; case 'number': return new Intl.NumberFormat('en-US').format(Math.round(value)); default: return value.toString(); } } updateChart(chartId, newData) { const chart = this.charts.get(chartId); if (!chart) return; // Update chart data chart.data = newData; chart.update('active'); } showNotification(data) { // Use notification plugin or create custom notification if (window.GentelellaPlugins && window.GentelellaPlugins.getPlugin('notifications')) { const notifications = window.GentelellaPlugins.getPlugin('notifications'); notifications.show(data.message, data.type); } } updateActivityFeed(activity) { const feedContainer = document.querySelector('#activity-feed'); if (!feedContainer) return; const activityItem = document.createElement('div'); activityItem.className = 'activity-item'; activityItem.innerHTML = ` <div class=\"activity-icon\"> <i class=\"fa fa-${activity.icon}\"></i> </div> <div class=\"activity-content\"> <div class=\"activity-text\">${activity.message}</div> <div class=\"activity-time\">${this.formatTime(activity.timestamp)}</div> </div> `; // Add to top of feed feedContainer.insertBefore(activityItem, feedContainer.firstChild); // Remove oldest items if feed is too long const items = feedContainer.querySelectorAll('.activity-item'); if (items.length > 10) { for (let i = 10; i < items.length; i++) { items[i].remove(); } } } formatTime(timestamp) { const date = new Date(timestamp); const now = new Date(); const diff = now - date; if (diff < 60000) return 'Just now'; if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; return date.toLocaleDateString(); } } // Initialize real-time dashboard new RealTimeDashboard(); . ", "url": "/gentelella/api-integration/#real-time-integration", "relUrl": "/api-integration/#real-time-integration" },"4": { "doc": "API Integration", "title": "Data Management", "content": "State Management . // src/js/store/app-store.js class AppStore { constructor() { this.state = { user: null, theme: 'light', sidebarCollapsed: false, notifications: [], loading: false, error: null }; this.listeners = new Map(); this.loadFromStorage(); } // Get current state getState() { return { ...this.state }; } // Update state setState(updates) { const prevState = { ...this.state }; this.state = { ...this.state, ...updates }; // Notify listeners this.notifyListeners(prevState, this.state); // Persist certain state to localStorage this.saveToStorage(); } // Subscribe to state changes subscribe(listener) { const id = Date.now() + Math.random(); this.listeners.set(id, listener); // Return unsubscribe function return () => { this.listeners.delete(id); }; } notifyListeners(prevState, newState) { this.listeners.forEach(listener => { try { listener(newState, prevState); } catch (error) { console.error('Error in state listener:', error); } }); } loadFromStorage() { try { const userData = localStorage.getItem('user_data'); if (userData) { this.state.user = JSON.parse(userData); } const theme = localStorage.getItem('theme'); if (theme) { this.state.theme = theme; } const sidebarCollapsed = localStorage.getItem('sidebar-collapsed'); if (sidebarCollapsed) { this.state.sidebarCollapsed = sidebarCollapsed === 'true'; } } catch (error) { console.error('Failed to load state from storage:', error); } } saveToStorage() { try { if (this.state.user) { localStorage.setItem('user_data', JSON.stringify(this.state.user)); } localStorage.setItem('theme', this.state.theme); localStorage.setItem('sidebar-collapsed', this.state.sidebarCollapsed.toString()); } catch (error) { console.error('Failed to save state to storage:', error); } } // Action methods setUser(user) { this.setState({ user }); } clearUser() { this.setState({ user: null }); localStorage.removeItem('user_data'); localStorage.removeItem('auth_token'); } setTheme(theme) { this.setState({ theme }); document.documentElement.setAttribute('data-theme', theme); } toggleSidebar() { this.setState({ sidebarCollapsed: !this.state.sidebarCollapsed }); } addNotification(notification) { const notifications = [...this.state.notifications, { id: Date.now(), timestamp: new Date(), ...notification }]; this.setState({ notifications }); } removeNotification(id) { const notifications = this.state.notifications.filter(n => n.id !== id); this.setState({ notifications }); } setLoading(loading) { this.setState({ loading }); } setError(error) { this.setState({ error }); } clearError() { this.setState({ error: null }); } } // Create singleton instance export const appStore = new AppStore(); // Helper hook for components export function useStore(selector) { const state = appStore.getState(); return selector ? selector(state) : state; } . Data Caching . // src/js/cache/data-cache.js class DataCache { constructor() { this.cache = new Map(); this.expiry = new Map(); this.defaultTTL = 5 * 60 * 1000; // 5 minutes } set(key, data, ttl = this.defaultTTL) { this.cache.set(key, data); this.expiry.set(key, Date.now() + ttl); } get(key) { if (!this.cache.has(key)) { return null; } const expiryTime = this.expiry.get(key); if (Date.now() > expiryTime) { this.delete(key); return null; } return this.cache.get(key); } has(key) { return this.get(key) !== null; } delete(key) { this.cache.delete(key); this.expiry.delete(key); } clear() { this.cache.clear(); this.expiry.clear(); } cleanup() { const now = Date.now(); for (const [key, expiryTime] of this.expiry.entries()) { if (now > expiryTime) { this.delete(key); } } } size() { return this.cache.size; } } // Create singleton instance export const dataCache = new DataCache(); // Auto cleanup every 5 minutes setInterval(() => { dataCache.cleanup(); }, 5 * 60 * 1000); . ", "url": "/gentelella/api-integration/#data-management", "relUrl": "/api-integration/#data-management" },"5": { "doc": "API Integration", "title": "Authentication Integration", "content": "JWT Token Management . // src/js/auth/auth-manager.js class AuthManager { constructor() { this.token = localStorage.getItem('auth_token'); this.refreshTimer = null; this.init(); } init() { if (this.token) { this.scheduleTokenRefresh(); } } async login(credentials) { try { const response = await userService.authenticate(credentials); if (response.success) { this.token = response.data.token; this.scheduleTokenRefresh(); // Update app state appStore.setUser(response.data.user); return response; } return response; } catch (error) { return { success: false, error: error.message }; } } logout() { this.clearTokenRefresh(); this.token = null; // Clear app state appStore.clearUser(); // Call logout service userService.logout(); } isAuthenticated() { return !!this.token && !this.isTokenExpired(); } isTokenExpired() { if (!this.token) return true; try { const payload = JSON.parse(atob(this.token.split('.')[1])); return payload.exp * 1000 < Date.now(); } catch (error) { return true; } } async refreshToken() { try { const response = await httpClient.post('/auth/refresh'); if (response.token) { this.token = response.token; localStorage.setItem('auth_token', this.token); this.scheduleTokenRefresh(); return true; } return false; } catch (error) { console.error('Token refresh failed:', error); this.logout(); return false; } } scheduleTokenRefresh() { this.clearTokenRefresh(); if (!this.token) return; try { const payload = JSON.parse(atob(this.token.split('.')[1])); const expiryTime = payload.exp * 1000; const refreshTime = expiryTime - (5 * 60 * 1000); // 5 minutes before expiry const timeUntilRefresh = refreshTime - Date.now(); if (timeUntilRefresh > 0) { this.refreshTimer = setTimeout(() => { this.refreshToken(); }, timeUntilRefresh); } else { // Token expired or will expire soon this.refreshToken(); } } catch (error) { console.error('Failed to schedule token refresh:', error); } } clearTokenRefresh() { if (this.refreshTimer) { clearTimeout(this.refreshTimer); this.refreshTimer = null; } } getToken() { return this.token; } getUser() { const userData = localStorage.getItem('user_data'); return userData ? JSON.parse(userData) : null; } } // Create singleton instance export const authManager = new AuthManager(); // Route protection export function requireAuth() { if (!authManager.isAuthenticated()) { window.location.href = '/login.html'; return false; } return true; } // Auto-redirect if not authenticated (for protected pages) if (document.querySelector('[data-require-auth]')) { requireAuth(); } . ", "url": "/gentelella/api-integration/#authentication-integration", "relUrl": "/api-integration/#authentication-integration" },"6": { "doc": "API Integration", "title": "Error Handling", "content": "Global Error Handler . // src/js/error/error-handler.js class ErrorHandler { constructor() { this.setupGlobalHandlers(); } setupGlobalHandlers() { // Handle unhandled promise rejections window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled promise rejection:', event.reason); this.handleError(event.reason, 'Promise Rejection'); event.preventDefault(); }); // Handle JavaScript errors window.addEventListener('error', (event) => { console.error('JavaScript error:', event.error); this.handleError(event.error, 'JavaScript Error'); }); // Handle API errors document.addEventListener('api-error', (event) => { this.handleApiError(event.detail); }); } handleError(error, context = 'Unknown') { const errorInfo = { message: error.message || 'Unknown error', stack: error.stack, context, timestamp: new Date(), userAgent: navigator.userAgent, url: window.location.href, user: authManager.getUser()?.id }; // Log to console console.error('Error handled:', errorInfo); // Send to error tracking service this.reportError(errorInfo); // Show user-friendly notification this.showErrorNotification(error); } handleApiError(error) { if (error.status === 401) { this.handleUnauthorized(); } else if (error.status >= 500) { this.showErrorNotification({ message: 'Server error occurred. Please try again later.' }); } else { this.showErrorNotification(error); } } handleUnauthorized() { // Clear auth data and redirect to login authManager.logout(); } showErrorNotification(error) { // Use notification plugin if available if (window.GentelellaPlugins && window.GentelellaPlugins.getPlugin('notifications')) { const notifications = window.GentelellaPlugins.getPlugin('notifications'); notifications.show(error.message || 'An error occurred', 'error'); } else { // Fallback to alert alert(error.message || 'An error occurred'); } } async reportError(errorInfo) { try { // Send error to monitoring service await httpClient.post('/errors/report', errorInfo); } catch (reportingError) { console.error('Failed to report error:', reportingError); } } } // Initialize global error handler new ErrorHandler(); . ", "url": "/gentelella/api-integration/#error-handling", "relUrl": "/api-integration/#error-handling" },"7": { "doc": "API Integration", "title": "Performance Optimization", "content": "Request Batching . // src/js/api/request-batcher.js class RequestBatcher { constructor() { this.batches = new Map(); this.batchDelay = 100; // ms } batch(endpoint, id, params = {}) { return new Promise((resolve, reject) => { if (!this.batches.has(endpoint)) { this.batches.set(endpoint, { requests: [], timer: null }); } const batch = this.batches.get(endpoint); batch.requests.push({ id, params, resolve, reject }); // Clear existing timer and set new one if (batch.timer) { clearTimeout(batch.timer); } batch.timer = setTimeout(() => { this.executeBatch(endpoint); }, this.batchDelay); }); } async executeBatch(endpoint) { const batch = this.batches.get(endpoint); if (!batch || batch.requests.length === 0) return; const requests = batch.requests.slice(); batch.requests = []; batch.timer = null; try { const ids = requests.map(req => req.id); const response = await httpClient.post(`${endpoint}/batch`, { ids }); // Resolve individual requests requests.forEach(request => { const result = response.data.find(item => item.id === request.id); if (result) { request.resolve(result); } else { request.reject(new Error('Item not found in batch response')); } }); } catch (error) { // Reject all requests requests.forEach(request => { request.reject(error); }); } } } export const requestBatcher = new RequestBatcher(); . ", "url": "/gentelella/api-integration/#performance-optimization", "relUrl": "/api-integration/#performance-optimization" },"8": { "doc": "API Integration", "title": "Next Steps", "content": ". | Security Guide - Implement security best practices | Testing Guide - Test your API integrations | Monitoring Guide - Monitor API performance | . 💡 Pro Tip: Always implement proper error handling and retry logic for API calls. Use caching strategically to reduce API load and improve user experience. ", "url": "/gentelella/api-integration/#next-steps", "relUrl": "/api-integration/#next-steps" },"9": { "doc": "API Integration", "title": "API Integration", "content": " ", "url": "/gentelella/api-integration/", "relUrl": "/api-integration/" },"10": { "doc": "Components Guide", "title": "Components Guide", "content": "Complete reference for all components available in Gentelella Admin Template . ", "url": "/gentelella/components/", "relUrl": "/components/" },"11": { "doc": "Components Guide", "title": "Table of contents", "content": ". | Dashboard Components . | Dashboard Layouts . | Main Dashboard (index.html) | Dashboard 2 (index2.html) | Dashboard 3 (index3.html) | . | Widget Cards . | Tile Widgets | Info Box Widgets | . | . | Chart Components . | Chart.js Integration . | Line Charts | Bar Charts | Pie Charts | . | Morris.js Charts . | Line Charts | Area Charts | . | Sparkline Charts | Gauge Charts | . | Form Components . | Basic Form Elements . | Input Fields | Select Dropdowns | . | Advanced Form Components . | Select2 Enhanced Dropdowns | Date/Time Pickers | Range Sliders | File Upload with Dropzone | Rich Text Editor | . | Form Validation . | Bootstrap Validation | Parsley.js Validation | . | . | Table Components . | Basic Tables . | Responsive Table | . | DataTables Integration . | Basic DataTable | Advanced DataTable Features | . | . | UI Elements . | Navigation Components . | Sidebar Navigation | Breadcrumbs | . | Modal Components . | Basic Modal | Large Modal with Form | . | Alert Components . | Bootstrap Alerts | PNotify Notifications | . | Progress Components . | Progress Bars | Animated Progress with JavaScript | . | . | Map Components . | jVectorMap Integration . | World Map | Regional Map | . | . | Calendar Components . | FullCalendar Integration | . | Media Components . | Image Gallery | . | Next Steps | . ", "url": "/gentelella/components/#table-of-contents", "relUrl": "/components/#table-of-contents" },"12": { "doc": "Components Guide", "title": "Dashboard Components", "content": "Dashboard Layouts . Gentelella includes three pre-designed dashboard layouts: . Main Dashboard (index.html) . | Revenue widgets with animated counters | Real-time charts showing trends and analytics | Activity timeline with user interactions | Quick stats cards with icons | To-do lists with progress tracking | . <!-- Revenue Widget Example --> <div class=\"col-md-3 col-sm-6\"> <div class=\"x_panel tile fixed_height_320\"> <div class=\"x_title\"> <h2>Total Revenue</h2> </div> <div class=\"x_content\"> <span class=\"chart\" data-percent=\"73\"> <span class=\"percent\">73</span> </span> <h3>$52,147</h3> <div class=\"sidebar-widget\"> <h4>Revenue breakdown</h4> <canvas id=\"revenue-chart\"></canvas> </div> </div> </div> </div> . Dashboard 2 (index2.html) . | Full-width charts for detailed analytics | Map integration with geographical data | Compact widgets for KPIs | News feed with updates | . Dashboard 3 (index3.html) . | Calendar integration with events | Weather widget with forecasts | Social media stats counters | Project timeline view | . Widget Cards . Tile Widgets . <div class=\"tile_count\"> <div class=\"col-md-2 col-sm-4 tile_stats_count\"> <span class=\"count_top\"><i class=\"fa fa-user\"></i> Total Users</span> <div class=\"count\">2500</div> <span class=\"count_bottom\"><i class=\"green\">4% </i> From last Week</span> </div> </div> . Info Box Widgets . <div class=\"col-md-4 col-sm-4\"> <div class=\"x_panel tile fixed_height_320 overflow_hidden\"> <div class=\"x_title\"> <h2>Network Activities</h2> </div> <div class=\"x_content\"> <table class=\"countries_list\"> <tbody> <tr> <td>United States</td> <td class=\"fs-15 fw-700 text-right\">2,371</td> </tr> </tbody> </table> </div> </div> </div> . ", "url": "/gentelella/components/#dashboard-components", "relUrl": "/components/#dashboard-components" },"13": { "doc": "Components Guide", "title": "Chart Components", "content": "Chart.js Integration . Line Charts . // Initialize line chart import Chart from 'chart.js/auto'; const ctx = document.getElementById('lineChart').getContext('2d'); const lineChart = new Chart(ctx, { type: 'line', data: { labels: ['January', 'February', 'March', 'April', 'May', 'June'], datasets: [{ label: 'Sales', data: [12, 19, 3, 5, 2, 3], borderColor: '#73879C', backgroundColor: 'rgba(115, 135, 156, 0.1)', tension: 0.4 }] }, options: { responsive: true, plugins: { legend: { position: 'bottom' } } } }); . Bar Charts . <div class=\"x_panel\"> <div class=\"x_title\"> <h2>Monthly Sales</h2> </div> <div class=\"x_content\"> <canvas id=\"barChart\" width=\"400\" height=\"200\"></canvas> </div> </div> . Pie Charts . const pieChart = new Chart(ctx, { type: 'pie', data: { labels: ['Desktop', 'Mobile', 'Tablet'], datasets: [{ data: [300, 50, 100], backgroundColor: ['#73879C', '#26B99A', '#3498DB'] }] } }); . Morris.js Charts . Line Charts . Morris.Line({ element: 'line-chart', data: [ { y: '2023-01', a: 100, b: 90 }, { y: '2023-02', a: 75, b: 65 }, { y: '2023-03', a: 50, b: 40 } ], xkey: 'y', ykeys: ['a', 'b'], labels: ['Series A', 'Series B'] }); . Area Charts . Morris.Area({ element: 'area-chart', data: [ { period: '2023-01', sales: 2666, downloads: 2647 }, { period: '2023-02', sales: 2778, downloads: 2294 } ], xkey: 'period', ykeys: ['sales', 'downloads'], labels: ['Sales', 'Downloads'] }); . Sparkline Charts . $('.sparkline').sparkline([5,6,7,2,0,-4,-2,4], { type: 'line', width: '100%', height: '30', lineColor: '#26B99A', fillColor: 'rgba(38, 185, 154, 0.3)' }); . Gauge Charts . import Gauge from 'gauge.js'; const gauge = new Gauge(document.getElementById('gauge')).setOptions({ angle: 0.15, lineWidth: 0.2, radiusScale: 1, pointer: { length: 0.6, strokeWidth: 0.035, color: '#000000' }, limitMax: false, limitMin: false, colorStart: '#6FADCF', colorStop: '#8FC0DA', strokeColor: '#E0E0E0', generateGradient: true, highDpiSupport: true }); gauge.maxValue = 100; gauge.setMinValue(0); gauge.animationSpeed = 32; gauge.set(67); . ", "url": "/gentelella/components/#chart-components", "relUrl": "/components/#chart-components" },"14": { "doc": "Components Guide", "title": "Form Components", "content": "Basic Form Elements . Input Fields . <div class=\"form-group row\"> <label class=\"col-form-label col-md-3 col-sm-3\">Email</label> <div class=\"col-md-6 col-sm-6\"> <input type=\"email\" class=\"form-control\" placeholder=\"Enter email\"> </div> </div> . Select Dropdowns . <div class=\"form-group row\"> <label class=\"col-form-label col-md-3 col-sm-3\">Country</label> <div class=\"col-md-6 col-sm-6\"> <select class=\"form-control\"> <option>Choose option</option> <option>United States</option> <option>United Kingdom</option> </select> </div> </div> . Advanced Form Components . Select2 Enhanced Dropdowns . <select class=\"form-control select2\" multiple=\"multiple\"> <option value=\"AK\">Alaska</option> <option value=\"HI\">Hawaii</option> <option value=\"CA\">California</option> </select> . // Initialize Select2 $('.select2').select2({ theme: 'bootstrap-5', width: '100%', placeholder: 'Select options...' }); . Date/Time Pickers . <div class=\"form-group\"> <label>Date Range:</label> <div> <input type=\"text\" class=\"form-control\" id=\"reservation\" placeholder=\"Select date range\"> </div> </div> . import { DateTime } from 'tempus-dominus'; new DateTime(document.getElementById('reservation'), { display: { components: { calendar: true, date: true, month: true, year: true, decades: true, clock: false } } }); . Range Sliders . <div class=\"form-group\"> <label>Price Range:</label> <input type=\"text\" id=\"range-slider\" value=\"\" name=\"range\"> </div> . $(\"#range-slider\").ionRangeSlider({ type: \"double\", min: 0, max: 1000, from: 200, to: 500, prefix: \"$\" }); . File Upload with Dropzone . <div class=\"dropzone\" id=\"file-dropzone\"> <div class=\"dz-message\"> <h3>Drop files here or click to upload</h3> </div> </div> . import Dropzone from 'dropzone'; new Dropzone(\"#file-dropzone\", { url: \"/upload\", maxFilesize: 10, acceptedFiles: \".jpeg,.jpg,.png,.gif\" }); . Rich Text Editor . <div class=\"form-group\"> <label>Content:</label> <div id=\"editor\" class=\"form-control\" style=\"height: 300px;\"> <p>Initial content...</p> </div> </div> . Form Validation . Bootstrap Validation . <form class=\"needs-validation\" novalidate> <div class=\"form-group\"> <label for=\"validationCustom01\">First name</label> <input type=\"text\" class=\"form-control\" id=\"validationCustom01\" placeholder=\"First name\" required> <div class=\"invalid-feedback\"> Please provide a valid first name. </div> </div> <button class=\"btn btn-primary\" type=\"submit\">Submit</button> </form> . Parsley.js Validation . <form data-parsley-validate> <div class=\"form-group\"> <label>Email *</label> <input type=\"email\" class=\"form-control\" data-parsley-type=\"email\" required> </div> <div class=\"form-group\"> <label>Password *</label> <input type=\"password\" class=\"form-control\" data-parsley-minlength=\"6\" required> </div> </form> . ", "url": "/gentelella/components/#form-components", "relUrl": "/components/#form-components" },"15": { "doc": "Components Guide", "title": "Table Components", "content": "Basic Tables . Responsive Table . <div class=\"table-responsive\"> <table class=\"table table-striped table-bordered\"> <thead> <tr> <th>Name</th> <th>Position</th> <th>Office</th> <th>Salary</th> </tr> </thead> <tbody> <tr> <td>Tiger Nixon</td> <td>System Architect</td> <td>Edinburgh</td> <td>$320,800</td> </tr> </tbody> </table> </div> . DataTables Integration . Basic DataTable . <table id=\"datatable\" class=\"table table-striped table-bordered\" style=\"width:100%\"> <thead> <tr> <th>Name</th> <th>Position</th> <th>Office</th> <th>Age</th> <th>Start date</th> <th>Salary</th> </tr> </thead> </table> . $('#datatable').DataTable({ ajax: '/api/employees', columns: [ { data: 'name' }, { data: 'position' }, { data: 'office' }, { data: 'age' }, { data: 'start_date' }, { data: 'salary' } ], responsive: true, pageLength: 25, dom: 'Bfrtip', buttons: ['copy', 'csv', 'excel', 'pdf', 'print'] }); . Advanced DataTable Features . $('#advanced-datatable').DataTable({ processing: true, serverSide: true, ajax: { url: '/api/data', type: 'POST' }, columns: [ { data: 'id', searchable: false }, { data: 'name' }, { data: 'email' }, { data: 'actions', orderable: false, searchable: false, render: function(data, type, row) { return ` <button class=\"btn btn-sm btn-primary edit-btn\" data-id=\"${row.id}\">Edit</button> <button class=\"btn btn-sm btn-danger delete-btn\" data-id=\"${row.id}\">Delete</button> `; } } ], order: [[0, 'desc']], pageLength: 50, lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, \"All\"]] }); . ", "url": "/gentelella/components/#table-components", "relUrl": "/components/#table-components" },"16": { "doc": "Components Guide", "title": "UI Elements", "content": "Navigation Components . Sidebar Navigation . <div class=\"col-md-3 left_col\"> <div class=\"left_col scroll-view\"> <div class=\"navbar nav_title\" style=\"border: 0;\"> <a href=\"index.html\" class=\"site_title\"> <i class=\"fa fa-paw\"></i> <span>Gentelella!</span> </a> </div> <div id=\"sidebar-menu\" class=\"main_menu_side hidden-print main_menu\"> <div class=\"menu_section\"> <h3>General</h3> <ul class=\"nav side-menu\"> <li><a><i class=\"fa fa-home\"></i> Home <span class=\"fa fa-chevron-down\"></span></a> <ul class=\"nav child_menu\"> <li><a href=\"index.html\">Dashboard</a></li> <li><a href=\"index2.html\">Dashboard2</a></li> </ul> </li> </ul> </div> </div> </div> </div> . Breadcrumbs . <div class=\"page-title\"> <div class=\"title_left\"> <h3>Form Elements</h3> </div> <div class=\"title_right\"> <div class=\"col-md-5 col-sm-5 form-group pull-right top_search\"> <div class=\"input-group\"> <input type=\"text\" class=\"form-control\" placeholder=\"Search for...\"> <span class=\"input-group-btn\"> <button class=\"btn btn-default\" type=\"button\">Go!</button> </span> </div> </div> </div> </div> . Modal Components . Basic Modal . <div class=\"modal fade\" id=\"exampleModal\" tabindex=\"-1\" role=\"dialog\"> <div class=\"modal-dialog\" role=\"document\"> <div class=\"modal-content\"> <div class=\"modal-header\"> <button type=\"button\" class=\"close\" data-dismiss=\"modal\"> <span aria-hidden=\"true\">×</span> </button> <h4 class=\"modal-title\">Modal title</h4> </div> <div class=\"modal-body\"> <p>Modal body content...</p> </div> <div class=\"modal-footer\"> <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Close</button> <button type=\"button\" class=\"btn btn-primary\">Save changes</button> </div> </div> </div> </div> . Large Modal with Form . <div class=\"modal fade bs-example-modal-lg\" tabindex=\"-1\" role=\"dialog\"> <div class=\"modal-dialog modal-lg\"> <div class=\"modal-content\"> <div class=\"modal-header\"> <h4 class=\"modal-title\">Large Modal</h4> </div> <div class=\"modal-body\"> <form> <!-- Form content --> </form> </div> </div> </div> </div> . Alert Components . Bootstrap Alerts . <div class=\"alert alert-success alert-dismissible\"> <button type=\"button\" class=\"close\" data-dismiss=\"alert\"> <span aria-hidden=\"true\">×</span> </button> <strong>Success!</strong> This is a success alert. </div> <div class=\"alert alert-danger alert-dismissible\"> <button type=\"button\" class=\"close\" data-dismiss=\"alert\"> <span aria-hidden=\"true\">×</span> </button> <strong>Error!</strong> Something went wrong. </div> . PNotify Notifications . import PNotify from 'pnotify'; // Success notification new PNotify({ title: 'Success!', text: 'Your changes have been saved.', type: 'success', styling: 'bootstrap4' }); // Error notification new PNotify({ title: 'Error!', text: 'An error occurred while processing your request.', type: 'error', styling: 'bootstrap4' }); . Progress Components . Progress Bars . <div class=\"progress\"> <div class=\"progress-bar progress-bar-success\" role=\"progressbar\" aria-valuenow=\"40\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"width:40%\"> 40% Complete (success) </div> </div> <div class=\"progress\"> <div class=\"progress-bar progress-bar-striped progress-bar-animated\" role=\"progressbar\" aria-valuenow=\"75\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"width:75%\"> 75% </div> </div> . Animated Progress with JavaScript . function animateProgress(selector, targetPercentage) { const progressBar = document.querySelector(selector); let currentPercentage = 0; const interval = setInterval(() => { if (currentPercentage >= targetPercentage) { clearInterval(interval); return; } currentPercentage++; progressBar.style.width = currentPercentage + '%'; progressBar.textContent = currentPercentage + '%'; }, 20); } // Usage animateProgress('.progress-bar', 85); . ", "url": "/gentelella/components/#ui-elements", "relUrl": "/components/#ui-elements" },"17": { "doc": "Components Guide", "title": "Map Components", "content": "jVectorMap Integration . World Map . <div id=\"world-map\" style=\"height: 400px;\"></div> . $('#world-map').vectorMap({ map: 'world_mill', backgroundColor: 'transparent', regionStyle: { initial: { fill: '#73879C', \"fill-opacity\": 1, stroke: '#fff', \"stroke-width\": 1, \"stroke-opacity\": 1 } }, series: { regions: [{ values: { \"US\": 298, \"SA\": 200, \"AU\": 760, \"IN\": 2000000, \"GB\": 120 }, scale: ['#26B99A', '#E74C3C'], normalizeFunction: 'polynomial' }] } }); . Regional Map . $('#usa-map').vectorMap({ map: 'us_aea', backgroundColor: 'transparent', regionsSelectable: true, series: { regions: [{ values: { \"US-CA\": 200, \"US-TX\": 300, \"US-NY\": 250 }, scale: ['#3498DB', '#E74C3C'] }] } }); . ", "url": "/gentelella/components/#map-components", "relUrl": "/components/#map-components" },"18": { "doc": "Components Guide", "title": "Calendar Components", "content": "FullCalendar Integration . <div id=\"calendar\"></div> . import { Calendar } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import timeGridPlugin from '@fullcalendar/timegrid'; import interactionPlugin from '@fullcalendar/interaction'; const calendarEl = document.getElementById('calendar'); const calendar = new Calendar(calendarEl, { plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin], headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay' }, initialDate: new Date(), navLinks: true, selectable: true, selectMirror: true, select: function(arg) { const title = prompt('Event Title:'); if (title) { calendar.addEvent({ title: title, start: arg.start, end: arg.end, allDay: arg.allDay }); } calendar.unselect(); }, eventClick: function(arg) { if (confirm('Are you sure you want to delete this event?')) { arg.event.remove(); } }, editable: true, dayMaxEvents: true, events: [ { title: 'All Day Event', start: '2023-01-01' }, { title: 'Long Event', start: '2023-01-07', end: '2023-01-10' } ] }); calendar.render(); . ", "url": "/gentelella/components/#calendar-components", "relUrl": "/components/#calendar-components" },"19": { "doc": "Components Guide", "title": "Media Components", "content": "Image Gallery . <div class=\"row\"> <div class=\"col-md-4\"> <a href=\"images/large1.jpg\" class=\"fancybox\" rel=\"gallery1\" title=\"Image 1\"> <img src=\"images/thumb1.jpg\" class=\"img-responsive\" alt=\"\"> </a> </div> <div class=\"col-md-4\"> <a href=\"images/large2.jpg\" class=\"fancybox\" rel=\"gallery1\" title=\"Image 2\"> <img src=\"images/thumb2.jpg\" class=\"img-responsive\" alt=\"\"> </a> </div> </div> . $('.fancybox').fancybox({ openEffect: 'elastic', closeEffect: 'elastic', helpers: { title: { type: 'inside' } } }); . ", "url": "/gentelella/components/#media-components", "relUrl": "/components/#media-components" },"20": { "doc": "Components Guide", "title": "Next Steps", "content": ". | Customization Guide - Learn how to customize these components | Performance Guide - Optimize component loading | API Reference - Detailed API documentation | Examples - See components in action | . 💡 Pro Tip: Use the smart loading system to load only the components you need on each page. This significantly improves performance while maintaining functionality. ", "url": "/gentelella/components/#next-steps", "relUrl": "/components/#next-steps" },"21": { "doc": "Configuration", "title": "Configuration Guide", "content": "Complete guide to configuring and customizing Gentelella Admin Template . ", "url": "/gentelella/configuration/#configuration-guide", "relUrl": "/configuration/#configuration-guide" },"22": { "doc": "Configuration", "title": "Table of contents", "content": ". | Vite Configuration . | Basic Configuration | Advanced Vite Options . | Development Optimizations | Production Optimizations | . | . | SASS Configuration . | Main SASS File | Bootstrap Customization | Custom Component Styles | . | Module Configuration . | Smart Loading System | Chart Module Configuration | Form Module Configuration | . | Environment Variables . | Development Environment | Production Environment | Using Environment Variables | . | Performance Configuration . | Bundle Optimization | Asset Optimization | . | Advanced Configuration . | TypeScript Support | ESLint Configuration | Prettier Configuration | . | Next Steps | . ", "url": "/gentelella/configuration/#table-of-contents", "relUrl": "/configuration/#table-of-contents" },"23": { "doc": "Configuration", "title": "Vite Configuration", "content": "Basic Configuration . The vite.config.js file contains optimized settings for both development and production builds: . import { defineConfig } from 'vite'; import { resolve } from 'path'; export default defineConfig({ // Development server configuration server: { port: 3000, host: true, open: true }, // Build configuration build: { outDir: 'dist', assetsDir: 'assets', rollupOptions: { input: { // All 42 HTML files are configured as entry points 'index': 'production/index.html', 'index2': 'production/index2.html', 'index3': 'production/index3.html', 'form': 'production/form.html', 'form_advanced': 'production/form_advanced.html', 'tables': 'production/tables.html', 'charts': 'production/chartjs.html', // ... and 35 more pages }, output: { // Manual chunk splitting for optimal loading manualChunks: { 'vendor-core': ['bootstrap', '@popperjs/core'], 'vendor-charts': ['chart.js', 'morris.js'], 'vendor-forms': ['select2', 'tempus-dominus'], 'vendor-tables': ['datatables.net'], 'vendor-utils': ['dayjs', 'nprogress'] } } }, // Asset optimization assetsInlineLimit: 4096, minify: 'terser', terserOptions: { compress: { drop_console: true, drop_debugger: true } } } }); . Advanced Vite Options . Development Optimizations . export default defineConfig({ server: { // Custom port port: 3001, // Enable HTTPS for local development https: true, // Proxy API requests proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, rewrite: (path) => path.replace(/^\\/api/, '') } } }, // Enable source maps in development css: { devSourcemap: true } }); . Production Optimizations . export default defineConfig({ build: { // Target modern browsers for smaller bundles target: 'es2018', // Enable CSS code splitting cssCodeSplit: true, // Generate source maps for production debugging sourcemap: true, // Optimize chunk size chunkSizeWarningLimit: 1000 } }); . ", "url": "/gentelella/configuration/#vite-configuration", "relUrl": "/configuration/#vite-configuration" },"24": { "doc": "Configuration", "title": "SASS Configuration", "content": "Main SASS F