- Added primary_keyword, secondary_keywords, tags, and categories fields to Tasks model - Updated generate_content function to handle full JSON response with all SEO fields - Improved progress bar animation: smooth 1% increments every 300ms - Enhanced step detection for content generation vs clustering vs ideas - Fixed progress modal to show correct messages for each function type - Added comprehensive logging to Keywords and Tasks pages for AI functions - Fixed error handling to show meaningful error messages instead of generic failures
103 lines
6.5 KiB
TypeScript
103 lines
6.5 KiB
TypeScript
/**
|
|
* Lazy Icon Loader
|
|
*
|
|
* This module provides lazy-loaded icons to reduce initial bundle size.
|
|
* Icons are only loaded when actually used, not on initial page load.
|
|
*
|
|
* Usage:
|
|
* import { lazyIcon } from '@/icons/lazy';
|
|
* const PlusIcon = lazyIcon('plus');
|
|
*/
|
|
|
|
import React, { lazy, ComponentType } from 'react';
|
|
|
|
// Icon name to component mapping
|
|
const iconMap: Record<string, () => Promise<{ default: ComponentType<any> }>> = {
|
|
'plus': () => import('./plus.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'close': () => import('./close.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'box': () => import('./box.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'check-circle': () => import('./check-circle.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'alert': () => import('./alert.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'info': () => import('./info.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'error': () => import('./info-error.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'bolt': () => import('./bolt.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'arrow-up': () => import('./arrow-up.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'arrow-down': () => import('./arrow-down.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'folder': () => import('./folder.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'videos': () => import('./videos.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'audio': () => import('./audio.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'grid': () => import('./grid.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'file': () => import('./file.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'download': () => import('./download.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'arrow-right': () => import('./arrow-right.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'group': () => import('./group.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'box-line': () => import('./box-line.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'shooting-star': () => import('./shooting-star.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'dollar-line': () => import('./dollar-line.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'trash': () => import('./trash.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'angle-up': () => import('./angle-up.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'angle-down': () => import('./angle-down.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'angle-left': () => import('./angle-left.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'angle-right': () => import('./angle-right.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'pencil': () => import('./pencil.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'check-line': () => import('./check-line.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'close-line': () => import('./close-line.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'chevron-down': () => import('./chevron-down.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'chevron-up': () => import('./chevron-up.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'paper-plane': () => import('./paper-plane.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'lock': () => import('./lock.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'envelope': () => import('./envelope.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'user-line': () => import('./user-line.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'calender-line': () => import('./calender-line.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'eye': () => import('./eye.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'eye-close': () => import('./eye-close.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'time': () => import('./time.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'copy': () => import('./copy.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'chevron-left': () => import('./chevron-left.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'user-circle': () => import('./user-circle.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'task-icon': () => import('./task-icon.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'list': () => import('./list.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'table': () => import('./table.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'page': () => import('./page.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'pie-chart': () => import('./pie-chart.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'box-cube': () => import('./box-cube.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'plug-in': () => import('./plug-in.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'docs': () => import('./docs.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'mail-line': () => import('./mail-line.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'horizontal-dots': () => import('./horizontal-dots.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'chat': () => import('./chat.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'moredot': () => import('./moredot.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'alert-hexa': () => import('./alert-hexa.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
'info-hexa': () => import('./info-hexa.svg?react').then(m => ({ default: m.ReactComponent })),
|
|
};
|
|
|
|
/**
|
|
* Get a lazy-loaded icon component
|
|
* @param iconName - Name of the icon (without .svg extension)
|
|
* @returns Lazy-loaded React component
|
|
*/
|
|
export function lazyIcon(iconName: string): ComponentType<any> {
|
|
const loader = iconMap[iconName];
|
|
if (!loader) {
|
|
console.warn(`Icon "${iconName}" not found. Available icons: ${Object.keys(iconMap).join(', ')}`);
|
|
// Return a placeholder component
|
|
return () => React.createElement('span', null, '?');
|
|
}
|
|
return lazy(loader);
|
|
}
|
|
|
|
/**
|
|
* Preload commonly used icons (optional optimization)
|
|
* Call this early in your app to preload icons that are used on every page
|
|
*/
|
|
export function preloadCommonIcons() {
|
|
const commonIcons = ['plus', 'close', 'chevron-down', 'chevron-up', 'chevron-left', 'chevron-right'];
|
|
commonIcons.forEach(iconName => {
|
|
const loader = iconMap[iconName];
|
|
if (loader) {
|
|
loader(); // Start loading but don't await
|
|
}
|
|
});
|
|
}
|
|
|