feat(project-view): optimize component loading and enhance configuration

- Introduced lazy loading for project view components and chart components to reduce initial bundle size.
- Centralized Ant Design imports in a new `antd-imports.ts` file for better tree-shaking and maintainability.
- Updated project view header and task list components to utilize centralized imports, improving consistency and performance.
- Enhanced project view constants to streamline component rendering and improve user experience.
This commit is contained in:
chamikaJ
2025-06-25 13:05:38 +05:30
parent 680e84d19b
commit 7b326e8ff0
7 changed files with 389 additions and 79 deletions

View File

@@ -0,0 +1,25 @@
import React, { Suspense } from 'react';
import { Spin } from 'antd';
// Lazy load chart components to reduce initial bundle size
const LazyBar = React.lazy(() => import('react-chartjs-2').then(module => ({ default: module.Bar })));
const LazyDoughnut = React.lazy(() => import('react-chartjs-2').then(module => ({ default: module.Doughnut })));
interface ChartLoaderProps {
type: 'bar' | 'doughnut';
data: any;
options?: any;
[key: string]: any;
}
const ChartLoader: React.FC<ChartLoaderProps> = ({ type, ...props }) => {
const ChartComponent = type === 'bar' ? LazyBar : LazyDoughnut;
return (
<Suspense fallback={<Spin size="large" />}>
<ChartComponent {...props} />
</Suspense>
);
};
export default ChartLoader;

View File

@@ -1,12 +1,12 @@
import React, { ReactNode } from 'react';
import ProjectViewInsights from '@/pages/projects/projectView/insights/project-view-insights';
import ProjectViewFiles from '@/pages/projects/projectView/files/project-view-files';
import ProjectViewMembers from '@/pages/projects/projectView/members/project-view-members';
import ProjectViewUpdates from '@/pages/projects/project-view-1/updates/project-view-updates';
import ProjectViewTaskList from '@/pages/projects/projectView/taskList/project-view-task-list';
import ProjectViewBoard from '@/pages/projects/projectView/board/project-view-board';
import ProjectViewEnhancedTasks from '@/pages/projects/projectView/enhancedTasks/project-view-enhanced-tasks';
import ProjectViewEnhancedBoard from '@/pages/projects/projectView/enhancedBoard/project-view-enhanced-board';
// Lazy load all project view components to reduce initial bundle size
const ProjectViewInsights = React.lazy(() => import('@/pages/projects/projectView/insights/project-view-insights'));
const ProjectViewFiles = React.lazy(() => import('@/pages/projects/projectView/files/project-view-files'));
const ProjectViewMembers = React.lazy(() => import('@/pages/projects/projectView/members/project-view-members'));
const ProjectViewUpdates = React.lazy(() => import('@/pages/projects/project-view-1/updates/project-view-updates'));
const ProjectViewEnhancedTasks = React.lazy(() => import('@/pages/projects/projectView/enhancedTasks/project-view-enhanced-tasks'));
const ProjectViewEnhancedBoard = React.lazy(() => import('@/pages/projects/projectView/enhancedBoard/project-view-enhanced-board'));
// type of a tab items
type TabItems = {
@@ -28,57 +28,31 @@ export const tabItems: TabItems[] = [
},
{
index: 1,
key: 'task-list-v1',
label: 'Task List v1',
isPinned: true,
element: React.createElement(ProjectViewTaskList),
},
{
index: 2,
key: 'board',
label: 'Board',
isPinned: true,
element: React.createElement(ProjectViewEnhancedBoard),
},
{
index: 3,
key: 'board-v1',
label: 'Board v1',
isPinned: true,
element: React.createElement(ProjectViewBoard),
},
// {
// index: 3,
// key: 'workload',
// label: 'Workload',
// element: React.createElement(ProjectViewWorkload),
// },
// {
// index: 4,
// key: 'roadmap',
// label: 'Roadmap',
// element: React.createElement(ProjectViewRoadmap),
// },
{
index: 5,
index: 2,
key: 'project-insights-member-overview',
label: 'Insights',
element: React.createElement(ProjectViewInsights),
},
{
index: 6,
index: 3,
key: 'all-attachments',
label: 'Files',
element: React.createElement(ProjectViewFiles),
},
{
index: 7,
index: 4,
key: 'members',
label: 'Members',
element: React.createElement(ProjectViewMembers),
},
{
index: 8,
index: 5,
key: 'updates',
label: 'Updates',
element: React.createElement(ProjectViewUpdates),

View File

@@ -1,4 +1,10 @@
import {
import {
Button,
Dropdown,
Flex,
Tag,
Tooltip,
Typography,
ArrowLeftOutlined,
BellFilled,
BellOutlined,
@@ -9,10 +15,9 @@ import {
SaveOutlined,
SettingOutlined,
SyncOutlined,
UsergroupAddOutlined,
} from '@ant-design/icons';
UsergroupAddOutlined
} from '@/shared/antd-imports';
import { PageHeader } from '@ant-design/pro-components';
import { Button, Dropdown, Flex, Tag, Tooltip, Typography } from 'antd';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';

View File

@@ -1,9 +1,20 @@
import React, { useEffect, useState, useMemo, useCallback } from 'react';
import { PushpinFilled, PushpinOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import { Badge, Button, ConfigProvider, Flex, Tabs, TabsProps, Tooltip } from 'antd';
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { createPortal } from 'react-dom';
// Centralized Ant Design imports
import {
Button,
ConfigProvider,
Flex,
Tooltip,
Badge,
Tabs,
PushpinFilled,
PushpinOutlined,
type TabsProps
} from '@/shared/antd-imports';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { useAppSelector } from '@/hooks/useAppSelector';
import { getProject, setProjectId, setProjectView } from '@/features/project/project.slice';

View File

@@ -1,4 +1,5 @@
import { useEffect, useState, useMemo, lazy, Suspense } from 'react';
import { Empty } from '@/shared/antd-imports';
import Flex from 'antd/es/flex';
import Skeleton from 'antd/es/skeleton';
import { useSearchParams } from 'react-router-dom';
@@ -10,7 +11,6 @@ import { useAppDispatch } from '@/hooks/useAppDispatch';
import { fetchTaskGroups, fetchTaskListColumns } from '@/features/tasks/tasks.slice';
import { fetchStatusesCategories } from '@/features/taskAttributes/taskStatusSlice';
import { fetchPhasesByProjectId } from '@/features/projects/singleProject/phase/phases.slice';
import { Empty } from 'antd';
import useTabSearchParam from '@/hooks/useTabSearchParam';
const ProjectViewTaskList = () => {

View File

@@ -0,0 +1,198 @@
/**
* Optimized Ant Design imports for maximum tree-shaking
*
* This file provides:
* - Granular imports from antd/es for better tree-shaking
* - Only commonly used components to reduce bundle size
* - Separate icon imports to avoid loading entire icon set
*/
// Core Components - Import as default exports for better tree-shaking
import Button from 'antd/es/button';
import Input from 'antd/es/input';
import Select from 'antd/es/select';
import Typography from 'antd/es/typography';
import Card from 'antd/es/card';
import Spin from 'antd/es/spin';
import Empty from 'antd/es/empty';
import Space from 'antd/es/space';
import Tooltip from 'antd/es/tooltip';
import Badge from 'antd/es/badge';
import Popconfirm from 'antd/es/popconfirm';
import Checkbox from 'antd/es/checkbox';
import Dropdown from 'antd/es/dropdown';
import Menu from 'antd/es/menu';
import Modal from 'antd/es/modal';
import Tag from 'antd/es/tag';
import Avatar from 'antd/es/avatar';
import List from 'antd/es/list';
import Table from 'antd/es/table';
import Flex from 'antd/es/flex';
import Divider from 'antd/es/divider';
import Progress from 'antd/es/progress';
import Result from 'antd/es/result';
import Skeleton from 'antd/es/skeleton';
import Alert from 'antd/es/alert';
import Tabs from 'antd/es/tabs';
import ConfigProvider from 'antd/es/config-provider';
// Date & Time Components
import DatePicker from 'antd/es/date-picker';
import TimePicker from 'antd/es/time-picker';
// Form Components
import Form from 'antd/es/form';
import InputNumber from 'antd/es/input-number';
// Layout Components
import Row from 'antd/es/row';
import Col from 'antd/es/col';
import Layout from 'antd/es/layout';
import Drawer from 'antd/es/drawer';
// Message and Notification - Import separately
import message from 'antd/es/message';
import notification from 'antd/es/notification';
// Theme
import theme from 'antd/es/theme';
// Re-export all components
export {
Button,
Input,
Select,
Typography,
Card,
Spin,
Empty,
Space,
Tooltip,
Badge,
Popconfirm,
Checkbox,
Dropdown,
Menu,
Modal,
Tag,
Avatar,
List,
Table,
Flex,
Divider,
Progress,
Result,
Skeleton,
Alert,
Tabs,
ConfigProvider,
DatePicker,
TimePicker,
Form,
InputNumber,
Row,
Col,
Layout,
Drawer,
message,
notification,
theme
};
// Icons - Import only commonly used ones
export {
EditOutlined,
DeleteOutlined,
PlusOutlined,
MoreOutlined,
CheckOutlined,
CloseOutlined,
CalendarOutlined,
ClockCircleOutlined,
UserOutlined,
TeamOutlined,
TagOutlined,
BarsOutlined,
AppstoreOutlined,
FilterOutlined,
SearchOutlined,
SettingOutlined,
DownOutlined,
RightOutlined,
LeftOutlined,
UpOutlined,
ArrowLeftOutlined,
BellFilled,
BellOutlined,
SaveOutlined,
SyncOutlined,
PushpinFilled,
PushpinOutlined,
UsergroupAddOutlined,
ImportOutlined
} from '@ant-design/icons';
// TypeScript Types - Import only commonly used ones
export type {
ButtonProps,
InputProps,
InputRef,
SelectProps,
TypographyProps,
CardProps,
TooltipProps,
DropdownProps,
MenuProps,
DatePickerProps,
FormProps,
FormInstance,
FlexProps,
TabsProps,
TableProps,
TableColumnsType
} from 'antd';
// Dayjs
export { default as dayjs } from 'dayjs';
export type { Dayjs } from 'dayjs';
// Optimized message utilities
export const appMessage = {
success: (content: string) => message.success(content),
error: (content: string) => message.error(content),
warning: (content: string) => message.warning(content),
info: (content: string) => message.info(content),
loading: (content: string) => message.loading(content),
};
export const appNotification = {
success: (config: any) => notification.success(config),
error: (config: any) => notification.error(config),
warning: (config: any) => notification.warning(config),
info: (config: any) => notification.info(config),
};
// Default configurations
export const antdConfig = {
datePickerDefaults: {
format: 'MMM DD, YYYY',
placeholder: 'Set Date',
size: 'small' as const,
},
buttonDefaults: {
size: 'small' as const,
},
inputDefaults: {
size: 'small' as const,
},
selectDefaults: {
size: 'small' as const,
showSearch: true,
},
};
export default {
config: antdConfig,
message: appMessage,
notification: appNotification,
};

View File

@@ -20,16 +20,34 @@ export default defineConfig(({ command, mode }) => {
{ find: '@features', replacement: path.resolve(__dirname, './src/features') },
{ find: '@assets', replacement: path.resolve(__dirname, './src/assets') },
{ find: '@utils', replacement: path.resolve(__dirname, './src/utils') },
{ find: '@services', replacement: path.resolve(__dirname, './src/services') },
{ find: '@hooks', replacement: path.resolve(__dirname, './src/hooks') },
{ find: '@pages', replacement: path.resolve(__dirname, './src/pages') },
{ find: '@api', replacement: path.resolve(__dirname, './src/api') },
{ find: '@types', replacement: path.resolve(__dirname, './src/types') },
{ find: '@shared', replacement: path.resolve(__dirname, './src/shared') },
{ find: '@layouts', replacement: path.resolve(__dirname, './src/layouts') },
{ find: '@services', replacement: path.resolve(__dirname, './src/services') },
],
// **Ensure single React instance**
dedupe: ['react', 'react-dom'],
},
// **CSS Configuration**
css: {
preprocessorOptions: {
less: {
modifyVars: {
'@primary-color': '#1890ff',
},
javascriptEnabled: true,
},
},
},
// **Development Server**
server: {
port: 5173,
host: true,
open: true,
hmr: {
overlay: false,
@@ -47,43 +65,114 @@ export default defineConfig(({ command, mode }) => {
cssCodeSplit: true,
// **Sourcemaps**
sourcemap: !isProduction ? 'inline' : false, // Disable sourcemaps in production for smaller bundles
sourcemap: false,
// **Minification**
minify: isProduction ? 'terser' : false,
terserOptions: isProduction ? {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log', 'console.info', 'console.debug'],
passes: 2, // Multiple passes for better compression
drop_console: isProduction,
drop_debugger: isProduction,
},
mangle: {
safari10: true,
},
format: {
comments: false,
},
} : undefined,
},
// **Chunk Size Warnings**
chunkSizeWarningLimit: 1000,
// **Rollup Options**
rollupOptions: {
output: {
// **Simplified Chunking Strategy to avoid React context issues**
manualChunks: {
// Keep React and all React-dependent libraries together
'react-vendor': ['react', 'react-dom', 'react/jsx-runtime'],
// **Rollup Options**
rollupOptions: {
output: {
// **Manual Chunking Strategy**
manualChunks: (id) => {
// Vendor libraries
if (id.includes('node_modules')) {
// Ant Design - Split into smaller chunks
if (id.includes('antd/es')) {
// Split antd by component types for better caching
if (id.includes('date-picker') || id.includes('time-picker') || id.includes('calendar')) {
return 'antd-date';
}
if (id.includes('table') || id.includes('list') || id.includes('tree')) {
return 'antd-data';
}
if (id.includes('form') || id.includes('input') || id.includes('select') || id.includes('checkbox')) {
return 'antd-form';
}
if (id.includes('button') || id.includes('tooltip') || id.includes('dropdown') || id.includes('menu')) {
return 'antd-basic';
}
if (id.includes('modal') || id.includes('drawer') || id.includes('popconfirm')) {
return 'antd-overlay';
}
// Catch remaining antd components
return 'antd-misc';
}
// Separate chunk for router
'react-router': ['react-router-dom'],
// Icons
if (id.includes('@ant-design/icons')) {
return 'antd-icons';
}
// Keep Ant Design separate but ensure React is available
'antd': ['antd', '@ant-design/icons'],
},
// Chart.js and related
if (id.includes('chart.js') || id.includes('react-chartjs-2')) {
return 'charts';
}
// Redux and related state management
if (id.includes('@reduxjs/toolkit') || id.includes('react-redux')) {
return 'redux';
}
// React Router
if (id.includes('react-router')) {
return 'router';
}
// i18n
if (id.includes('react-i18next') || id.includes('i18next')) {
return 'i18n';
}
// Other large libraries
if (id.includes('lodash')) {
return 'lodash';
}
if (id.includes('dayjs')) {
return 'dayjs';
}
// Remaining vendor code
return 'vendor';
}
// Application code chunking
// Project view components
if (id.includes('src/pages/projects/projectView')) {
return 'project-view';
}
// Other project components
if (id.includes('src/pages/projects')) {
return 'projects';
}
// Task management components
if (id.includes('src/components/task-') || id.includes('src/features/tasks')) {
return 'tasks';
}
// Settings and admin components
if (id.includes('src/pages/settings') || id.includes('src/components/admin')) {
return 'admin';
}
// Common components
if (id.includes('src/components/common') || id.includes('src/shared')) {
return 'common';
}
},
// **File Naming Strategies**
chunkFileNames: (chunkInfo) => {
const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId.split('/').pop() : 'chunk';
@@ -103,6 +192,12 @@ export default defineConfig(({ command, mode }) => {
},
},
// **Tree shaking optimization**
treeshake: {
moduleSideEffects: false,
unknownGlobalSideEffects: false,
},
// **External dependencies (if any should be externalized)**
external: [],
@@ -122,15 +217,17 @@ export default defineConfig(({ command, mode }) => {
include: [
'react',
'react-dom',
'react/jsx-runtime',
'react-router-dom',
'@reduxjs/toolkit',
'react-redux',
'react-i18next',
'dayjs',
],
exclude: [
// Exclude antd from pre-bundling to allow better tree-shaking
'antd',
'@ant-design/icons',
],
exclude: [
// Add any packages that should not be pre-bundled
],
// Force pre-bundling to avoid runtime issues
force: true,
},
// **Define global constants**