diff --git a/src/app/loading.js b/src/app/loading.js deleted file mode 100644 index afba6f4..0000000 --- a/src/app/loading.js +++ /dev/null @@ -1,5 +0,0 @@ -import LoadingComponent from '@/components/Loading' - -export default function Loading() { - return () -} \ No newline at end of file diff --git a/src/components/Charts/Bar.jsx b/src/components/Charts/Bar.jsx index e09a415..0cd734a 100644 --- a/src/components/Charts/Bar.jsx +++ b/src/components/Charts/Bar.jsx @@ -1,6 +1,5 @@ 'use client'; import React, { useRef, useState } from 'react'; -import ReactECharts from 'echarts-for-react'; import isEqual from 'lodash/isEqual'; import Loading from '../Loading'; import Link from 'next/link'; @@ -8,6 +7,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function Bar({ data, stack, onSelect, link, metabaseLink }) { const [loading, setLoading] = useState(true); @@ -115,7 +121,8 @@ export default function Bar({ data, stack, onSelect, link, metabaseLink }) { }; const onMouseOver = () => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; echartsInstance.dispatchAction({ type: 'takeGlobalCursor', key: 'brush', @@ -127,7 +134,8 @@ export default function Bar({ data, stack, onSelect, link, metabaseLink }) { const onBrushEnd = (params) => { if (params.areas.length > 0) { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; let start = echartsInstance.convertFromPixel( { xAxisIndex: 0 }, params.areas[0].range[0] diff --git a/src/components/Charts/CountryMap.jsx b/src/components/Charts/CountryMap.jsx index 66f0b8d..c867822 100644 --- a/src/components/Charts/CountryMap.jsx +++ b/src/components/Charts/CountryMap.jsx @@ -1,5 +1,4 @@ 'use client'; -import ReactECharts from 'echarts-for-react'; import 'echarts-countries-js/echarts-countries-js/world'; import isEqual from 'lodash/isEqual'; import Loading from '../Loading'; @@ -9,6 +8,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function CountryMap({ data, selected, onClick, link, metabaseLink }) { const [loading, setLoading] = useState(true); diff --git a/src/components/Charts/Gauge.jsx b/src/components/Charts/Gauge.jsx index d50df77..a45711f 100644 --- a/src/components/Charts/Gauge.jsx +++ b/src/components/Charts/Gauge.jsx @@ -1,8 +1,14 @@ 'use client' import React, { useState } from 'react' -import ReactECharts from 'echarts-for-react'; import isEqual from 'lodash/isEqual' import Loading from '../Loading' +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function Gauge({ data }) { const [loading, setLoading] = useState(true) diff --git a/src/components/Charts/HeatMap.jsx b/src/components/Charts/HeatMap.jsx index cb3185c..d46ae9a 100644 --- a/src/components/Charts/HeatMap.jsx +++ b/src/components/Charts/HeatMap.jsx @@ -1,7 +1,6 @@ 'use client'; import React, { useRef, useState } from 'react'; import isEqual from 'lodash/isEqual'; -import ReactECharts from 'echarts-for-react'; import styles from './styles.module.css'; import Loading from '../Loading'; import Link from 'next/link'; @@ -9,6 +8,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function HeatMap({ data, title, subtitle, onClick, link, metabaseLink }) { const chartRef = useRef(); @@ -96,7 +102,8 @@ export default function HeatMap({ data, title, subtitle, onClick, link, metabase }, extraCssText: 'visibility: hidden;padding:0px;', position: (point, params, dom, rect, size) => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; const pos = echartsInstance.convertToPixel({ seriesIndex: 0 }, [ params.value[0], params.value[1] diff --git a/src/components/Charts/HorizontalBar.jsx b/src/components/Charts/HorizontalBar.jsx index bd94034..5ea3dbf 100644 --- a/src/components/Charts/HorizontalBar.jsx +++ b/src/components/Charts/HorizontalBar.jsx @@ -1,7 +1,6 @@ 'use client'; import React, { useRef, useState } from 'react'; import isEqual from 'lodash/isEqual'; -import ReactECharts from 'echarts-for-react'; import styles from './styles.module.css'; import { formatNumber, toValidStyleName } from '@/utils/utils'; import Loading from '../Loading'; @@ -10,6 +9,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function HorizontalBar({ data, @@ -170,7 +176,8 @@ export default function HorizontalBar({ }; const onMouseOver = () => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; }; const onChartReady = (echarts) => { diff --git a/src/components/Charts/Line.jsx b/src/components/Charts/Line.jsx index 19ede2e..8170c3c 100644 --- a/src/components/Charts/Line.jsx +++ b/src/components/Charts/Line.jsx @@ -1,8 +1,6 @@ 'use client'; import React, { useRef, useState } from 'react'; import isEqual from 'lodash/isEqual'; -import ReactECharts from 'echarts-for-react'; - import styles from './styles.module.css'; import Loading from '../Loading'; import Link from 'next/link'; @@ -10,6 +8,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function Line({ data, onSelect, onClear, link, metabaseLink }) { const [loading, setLoading] = useState(true); @@ -17,7 +22,8 @@ export default function Line({ data, onSelect, onClear, link, metabaseLink }) { const chartRef = useRef(); const xAxis = data.map((p) => p.x); const onMouseOver = () => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; const newOptions = echartsInstance.getOption(); newOptions.series[0].lineStyle.opacity = 1; newOptions.series[0].lineStyle.shadowColor = '#FAFF69'; @@ -111,7 +117,8 @@ export default function Line({ data, onSelect, onClear, link, metabaseLink }) { }, extraCssText: 'visibility: hidden;padding:0px;', position: (point, params, dom, rect, size) => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; const pos = echartsInstance.convertToPixel({ seriesIndex: 0 }, [ params[0].axisValue, params[0].value @@ -132,13 +139,15 @@ export default function Line({ data, onSelect, onClear, link, metabaseLink }) { } const onMouseOut = () => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; echartsInstance.setOption(options); }; const onBrushEnd = (params) => { if (params.areas.length > 0) { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; let start = echartsInstance.convertFromPixel( { xAxisIndex: 0 }, params.areas[0].range[0] diff --git a/src/components/Charts/MultiLine.jsx b/src/components/Charts/MultiLine.jsx index 2f76078..e46e854 100644 --- a/src/components/Charts/MultiLine.jsx +++ b/src/components/Charts/MultiLine.jsx @@ -1,6 +1,5 @@ 'use client'; import React, { useRef, useState } from 'react'; -import ReactECharts from 'echarts-for-react'; import isEqual from 'lodash/isEqual'; import Loading from '../Loading'; import Link from 'next/link'; @@ -8,6 +7,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function MultiLine({ data, stack, fill, onSelect, link, metabaseLink }) { const [loading, setLoading] = useState(true); @@ -117,7 +123,8 @@ export default function MultiLine({ data, stack, fill, onSelect, link, metabaseL }; const onMouseOver = () => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; echartsInstance.dispatchAction({ type: 'takeGlobalCursor', key: 'brush', @@ -129,7 +136,8 @@ export default function MultiLine({ data, stack, fill, onSelect, link, metabaseL const onBrushEnd = (params) => { if (params.areas.length > 0) { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; let start = echartsInstance.convertFromPixel( { xAxisIndex: 0 }, params.areas[0].range[0] diff --git a/src/components/Charts/Pie.jsx b/src/components/Charts/Pie.jsx index 47ed393..ab4ca3e 100644 --- a/src/components/Charts/Pie.jsx +++ b/src/components/Charts/Pie.jsx @@ -1,4 +1,3 @@ -import ReactECharts from 'echarts-for-react'; import Loading from '../Loading'; import isEqual from 'lodash/isEqual'; import { useState } from 'react'; @@ -7,6 +6,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function Pie({ data, onClick, link, metabaseLink }) { const [loading, setLoading] = useState(true); diff --git a/src/components/Charts/PunchCard.jsx b/src/components/Charts/PunchCard.jsx index 9afc666..f133936 100644 --- a/src/components/Charts/PunchCard.jsx +++ b/src/components/Charts/PunchCard.jsx @@ -1,6 +1,5 @@ 'use client'; import React, { useState } from 'react'; -import ReactECharts from 'echarts-for-react'; import isEqual from 'lodash/isEqual'; import Loading from '../Loading'; import Link from 'next/link'; @@ -8,6 +7,13 @@ import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function PunchCard({ data, diff --git a/src/components/Charts/Radar.jsx b/src/components/Charts/Radar.jsx index 5d2b064..5b318c0 100644 --- a/src/components/Charts/Radar.jsx +++ b/src/components/Charts/Radar.jsx @@ -1,5 +1,4 @@ 'use client'; -import ReactECharts from 'echarts-for-react'; import isEqual from 'lodash/isEqual'; import Loading from '../Loading'; import { useState } from 'react'; @@ -7,6 +6,13 @@ import Link from 'next/link'; import { ArrowTopRightOnSquareIcon, } from '@heroicons/react/20/solid'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function Radar({ data, onClick, link }) { const [loading, setLoading] = useState(true); diff --git a/src/components/Charts/Spark.jsx b/src/components/Charts/Spark.jsx index 0ccc418..e5d3592 100644 --- a/src/components/Charts/Spark.jsx +++ b/src/components/Charts/Spark.jsx @@ -1,12 +1,18 @@ 'use client'; import React, { useRef, useState } from 'react'; -import ReactECharts from 'echarts-for-react'; import { ArrowTopRightOnSquareIcon } from '@heroicons/react/20/solid'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import isEqual from 'lodash/isEqual'; import Image from 'next/image'; import CopyDropdown from '../CopyDropdown'; +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function Spark({ name, data, link, metabaseLink, type='bar' }) { const chartRef = useRef(); @@ -20,7 +26,8 @@ export default function Spark({ name, data, link, metabaseLink, type='bar' }) { }; const onMouseOver = () => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; const newOptions = echartsInstance.getOption(); newOptions.series[0].lineStyle.opacity = 0.8; newOptions.series[0].lineStyle.shadowColor = '#262626'; @@ -59,8 +66,6 @@ export default function Spark({ name, data, link, metabaseLink, type='bar' }) { top: '10px' }, xAxis: { - type: 'category', - data: data.map((p) => p.x), show: true, type: 'category', data: xAxis, @@ -116,7 +121,8 @@ export default function Spark({ name, data, link, metabaseLink, type='bar' }) { }; const onMouseOut = () => { - const echartsInstance = chartRef.current.getEchartsInstance(); + const echartsInstance = chartRef.current?.getEchartsInstance(); + if (!echartsInstance) return; echartsInstance.setOption(options); setSelected(false); }; diff --git a/src/components/Charts/treemap.js b/src/components/Charts/treemap.js index 9aebaf6..bc3a680 100644 --- a/src/components/Charts/treemap.js +++ b/src/components/Charts/treemap.js @@ -1,6 +1,12 @@ 'use client' import React, { useRef } from 'react' -import ReactECharts from 'echarts-for-react' +import dynamic from 'next/dynamic' + +// ECharts depends on browser APIs (window/document), so it breaks during Next.js SSR. +// We load it dynamically on the client only. +const ReactECharts = dynamic(() => import('echarts-for-react'), { + ssr: false, +}) export default function TreeMap ({data, onSelect}) { diff --git a/src/components/DownloadList.jsx b/src/components/DownloadList.jsx index b3dc62e..fc78c8c 100644 --- a/src/components/DownloadList.jsx +++ b/src/components/DownloadList.jsx @@ -34,7 +34,7 @@ async function DownloadList({ } const { total, last_day, last_week, last_month } = data[0]; return ( -
+
@@ -47,29 +47,29 @@ async function DownloadList({ width={16} height={16} /> -
-

Downloads

-

by period

-
-
-
-
-

{formatNumber(last_day)}

-

last day

-
-
-

{formatNumber(last_week)}

-

last week

-
-
-

{formatNumber(last_month)}

-

last month

-
-
-

{formatNumber(total)}

-

total

-
+

+ Downloads + by period +

+
    +
  • + {formatNumber(last_day)} + last day +
  • +
  • + {formatNumber(last_week)} + last week +
  • +
  • + {formatNumber(last_month)} + last month +
  • +
  • + {formatNumber(total)} + total +
  • +
@@ -81,7 +81,7 @@ async function DownloadList({
- + ); } diff --git a/src/components/PackageDetails.jsx b/src/components/PackageDetails.jsx index 7e9a95f..61fda0a 100644 --- a/src/components/PackageDetails.jsx +++ b/src/components/PackageDetails.jsx @@ -1,5 +1,5 @@ -import { ArrowTopRightOnSquareIcon } from '@heroicons/react/20/solid'; import Image from 'next/image'; +import Link from 'next/link'; export default function PackageDetails({ name, @@ -7,95 +7,66 @@ export default function PackageDetails({ author_email, licenses, summary, - home_page, repo_name }) { return (
-

{name}

- {home_page && ( - - - - ) - } - { - repo_name && repo_name != '' && ( - - - - ) - } +

{name}

+ {repo_name && String(repo_name).trim().length > 0 && ( + + {`${name} + + )}
- { authors || author_email || licenses || summary ? ( -
+ {(authors || author_email || licenses || summary) ? ( +
    {authors && ( -
    - Authors: - - {authors.split(',').length > 3 - ? ( - <> - {authors.split(',').slice(0, 3).join(',').trim()}... - - (+{authors.split(',').length - 3} more) - - - ) - : authors - } - -
    +
  • + Authors: + {authors.split(',').length > 3 ? ( + <> + {authors.split(',').slice(0, 3).join(',').trim()}... + + (+{authors.split(',').length - 3} more) + + + ) : authors} +
  • )} {author_email && ( -
    - Author Email: - {author_email} -
    +
  • + Author Email: + {author_email} +
  • )} {licenses && ( -
    - License: - - {licenses.replace(/^[-\s]+|[-\s]+$/g, '').split(' ').slice(0, 10).join(' ')}{licenses.split(' ').length > 10 && '...'} - -
    +
  • + License: + {licenses.replace(/^[-\s]+|[-\s]+$/g, '').split(' ').slice(0, 10).join(' ')}{licenses.split(' ').length > 10 && '...'} +
  • )} {summary && ( -
    - Summary: - - {summary.split(' ').length > 25 - ? `${summary.split(' ').slice(0, 25).join(' ')}...` - : summary - } - -
    +
  • + Summary: + {summary.split(' ').length > 25 ? `${summary.split(' ').slice(0, 25).join(' ')}...` : summary} +
  • )} -
+ ) : (
No package details available
- ) - } + )}
); }