Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion web/app/(all)/[workspaceSlug]/(projects)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
"use client";

import { CommandPalette } from "@/components/command-palette";
import dynamic from "next/dynamic";
import { AuthenticationWrapper } from "@/lib/wrappers";
// plane web components
import { WorkspaceAuthWrapper } from "@/plane-web/layouts/workspace-wrapper";
import { AppSidebar } from "./sidebar";

// Dynamically import heavy components
const CommandPalette = dynamic(
() => import("@/components/command-palette").then((module) => ({ default: module.CommandPalette })),
{
ssr: false, // Command palette doesn't need SSR
loading: () => null,
}
);

export default function WorkspaceLayout({ children }: { children: React.ReactNode }) {
return (
<AuthenticationWrapper>
Expand Down
21 changes: 10 additions & 11 deletions web/app/(all)/profile/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
"use client";

import { ReactNode } from "react";
// components
import { CommandPalette } from "@/components/command-palette";
// wrappers
import dynamic from "next/dynamic";
import { AuthenticationWrapper } from "@/lib/wrappers";
// layout
import { ProfileLayoutSidebar } from "./sidebar";

type Props = {
children: ReactNode;
};

export default function ProfileSettingsLayout(props: Props) {
const { children } = props;
// Dynamically import heavy components
const CommandPalette = dynamic(
() => import("@/components/command-palette").then((module) => ({ default: module.CommandPalette })),
{
ssr: false, // Command palette doesn't need SSR
loading: () => null,
}
);

export default function ProfileLayout({ children }: { children: React.ReactNode }) {
return (
<>
<CommandPalette />
Expand Down
5 changes: 0 additions & 5 deletions web/core/components/ui/graphs/index.ts

This file was deleted.

53 changes: 53 additions & 0 deletions web/core/components/ui/graphs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";
import dynamic from "next/dynamic";

// Shared LoadingSpinner component
const LoadingSpinner = () => (
<div className="flex h-96 w-full items-center justify-center">
<div className="h-8 w-8 animate-spin rounded-full border-2 border-custom-primary border-t-transparent" />
</div>
);

// Dynamically import heavy chart components (Nivo charts)
export const BarGraph = dynamic(
() => import("./bar-graph").then(module => ({ default: module.BarGraph })),
{
ssr: false, // Charts are client-side only
loading: () => <LoadingSpinner />,
}
);

export const PieGraph = dynamic(
() => import("./pie-graph").then(module => ({ default: module.PieGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);

export const LineGraph = dynamic(
() => import("./line-graph").then(module => ({ default: module.LineGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);

export const CalendarGraph = dynamic(
() => import("./calendar-graph").then(module => ({ default: module.CalendarGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);

export const ScatterPlotGraph = dynamic(
() => import("./scatter-plot-graph").then(module => ({ default: module.ScatterPlotGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);

Comment on lines +11 to +51
Copy link

Copilot AI May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Repeated dynamic import blocks for each graph component. Consider creating a helper function to reduce duplication and improve readability.

Suggested change
// Dynamically import heavy chart components (Nivo charts)
export const BarGraph = dynamic(
() => import("./bar-graph").then(module => ({ default: module.BarGraph })),
{
ssr: false, // Charts are client-side only
loading: () => <LoadingSpinner />,
}
);
export const PieGraph = dynamic(
() => import("./pie-graph").then(module => ({ default: module.PieGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);
export const LineGraph = dynamic(
() => import("./line-graph").then(module => ({ default: module.LineGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);
export const CalendarGraph = dynamic(
() => import("./calendar-graph").then(module => ({ default: module.CalendarGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);
export const ScatterPlotGraph = dynamic(
() => import("./scatter-plot-graph").then(module => ({ default: module.ScatterPlotGraph })),
{
ssr: false,
loading: () => <LoadingSpinner />,
}
);
// Helper function to dynamically import graph components
const createDynamicGraph = (modulePath: string, componentName: string) =>
dynamic(
() =>
import(modulePath).then(module => ({
default: module[componentName],
})),
{
ssr: false, // Charts are client-side only
loading: () => <LoadingSpinner />,
}
);
// Dynamically import heavy chart components (Nivo charts)
export const BarGraph = createDynamicGraph("./bar-graph", "BarGraph");
export const PieGraph = createDynamicGraph("./pie-graph", "PieGraph");
export const LineGraph = createDynamicGraph("./line-graph", "LineGraph");
export const CalendarGraph = createDynamicGraph("./calendar-graph", "CalendarGraph");
export const ScatterPlotGraph = createDynamicGraph("./scatter-plot-graph", "ScatterPlotGraph");

Copilot uses AI. Check for mistakes.
// Re-export types (no runtime impact)
export type * from "./types.d";
115 changes: 114 additions & 1 deletion web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
// eslint-disable-next-line @typescript-eslint/no-require-imports
require("dotenv").config({ path: ".env" });

// eslint-disable-next-line @typescript-eslint/no-require-imports
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});

const nextConfig = {
trailingSlash: true,
reactStrictMode: false,
Expand Down Expand Up @@ -37,7 +42,115 @@ const nextConfig = {
"lodash",
"clsx",
"tailwind-merge",
"recharts",
"axios",
"mobx",
"mobx-react",
],
// Enable modern bundling features
turbo: {
rules: {
"*.svg": {
loaders: ["@svgr/webpack"],
as: "*.js",
},
},
},
},
webpack: (config, { dev, isServer }) => {
// Apply aggressive optimizations only for client-side production builds
if (!dev && !isServer) {
// Enhanced tree shaking (client-side only)
config.optimization.usedExports = true;
config.optimization.sideEffects = false;
config.optimization.providedExports = true;
config.optimization.concatenateModules = true;

// Client-side chunk splitting strategy
config.optimization.splitChunks = {
chunks: "all",
minSize: 20000,
maxSize: 300000, // Increased from 200000 to reduce chunk fragmentation
maxAsyncRequests: 30,
maxInitialRequests: 25,
cacheGroups: {
// Framework chunks (React ecosystem)
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: "react",
chunks: "all",
priority: 40,
enforce: true,
},
// Large UI libraries
ui: {
test: /[\\/]node_modules[\\/](@headlessui|@radix-ui|@blueprintjs)[\\/]/,
name: "ui-libs",
chunks: "all",
priority: 35,
},
// Chart libraries (async loading for better performance)
charts: {
test: /[\\/]node_modules[\\/](@nivo|recharts)[\\/]/,
name: "charts",
chunks: "async", // Only load when needed
priority: 30,
enforce: true,
},
// Editor libraries (async loading)
editor: {
test: /[\\/]node_modules[\\/](@tiptap|prosemirror|@plane\/editor)[\\/]/,
name: "editor",
chunks: "async", // Only load when needed
priority: 30,
enforce: true,
},
// Date/time libraries
date: {
test: /[\\/]node_modules[\\/](date-fns|react-day-picker)[\\/]/,
name: "date-libs",
chunks: "all",
priority: 25,
},
// Utility libraries
utils: {
test: /[\\/]node_modules[\\/](lodash|clsx|tailwind-merge|uuid)[\\/]/,
name: "utils",
chunks: "all",
priority: 20,
},
// Vendor chunks for other libraries
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
priority: 10,
minChunks: 2,
},
// Common application code
common: {
name: "common",
minChunks: 2,
chunks: "all",
priority: 5,
reuseExistingChunk: true,
},
},
};

// Additional client-side optimizations
config.optimization.moduleIds = "deterministic";
config.optimization.chunkIds = "deterministic";
}

// Universal optimizations (both client and server)
config.resolve.alias = {
...config.resolve.alias,
// Reduce bundle size by aliasing to smaller alternatives where possible
"react/jsx-runtime": require.resolve("react/jsx-runtime"),
};

return config;
},
transpilePackages: [
"@plane/constants",
Expand Down Expand Up @@ -115,4 +228,4 @@ const nextConfig = {
},
};

module.exports = nextConfig;
module.exports = withBundleAnalyzer(nextConfig);
3 changes: 2 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@next/bundle-analyzer": "^15.3.2",
"@plane/eslint-config": "*",
"@plane/tailwind-config": "*",
"@plane/typescript-config": "*",
Expand All @@ -93,4 +94,4 @@
"prettier": "^3.2.5",
"typescript": "5.3.3"
}
}
}
Loading