diff --git a/package-lock.json b/package-lock.json index 6df18277..8337cdfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.15", "@mui/material": "^5.14.15", - "@mui/x-charts": "^6.19.3", "@mui/x-date-pickers": "^5.0.0-beta.4", "@testing-library/react": "^14.1.0", "@vercel/speed-insights": "^1.0.1", @@ -2308,41 +2307,6 @@ } } }, - "node_modules/@mui/x-charts": { - "version": "6.19.5", - "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-6.19.5.tgz", - "integrity": "sha512-BBRGLup5gpaLkhECv+J2ahFbDDgqK4BgLyLXLHKUASoWSU3YRCyDt9ifBREspEPfTZXgrcqNkybAl5b+l6baFQ==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/base": "^5.0.0-beta.22", - "@react-spring/rafz": "^9.7.3", - "@react-spring/web": "^9.7.3", - "clsx": "^2.0.0", - "d3-color": "^3.1.0", - "d3-scale": "^4.0.2", - "d3-shape": "^3.2.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.4.1", - "@mui/system": "^5.4.1", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, "node_modules/@mui/x-date-pickers": { "version": "5.0.20", "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.20.tgz", @@ -2462,71 +2426,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@react-spring/animated": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", - "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", - "dependencies": { - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/core": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", - "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", - "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/rafz": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz", - "integrity": "sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ==" - }, - "node_modules/@react-spring/shared": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", - "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", - "dependencies": { - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/types": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", - "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" - }, - "node_modules/@react-spring/web": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", - "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", - "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/core": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/@remix-run/router": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", @@ -4487,25 +4386,6 @@ "node": ">=12" } }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape/node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "engines": { - "node": ">=12" - } - }, "node_modules/d3-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", diff --git a/package.json b/package.json index e90534d7..18cd123a 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.15", "@mui/material": "^5.14.15", - "@mui/x-charts": "^6.19.3", "@mui/x-date-pickers": "^5.0.0-beta.4", "@testing-library/react": "^14.1.0", "@vercel/speed-insights": "^1.0.1", diff --git a/src/components/Base/Charts/ApexPieChart.component.tsx b/src/components/Base/Charts/ApexPieChart.component.tsx index 036cc021..867b5b6b 100644 --- a/src/components/Base/Charts/ApexPieChart.component.tsx +++ b/src/components/Base/Charts/ApexPieChart.component.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTheme, alpha, hexToRgb } from '@mui/material'; import Chart, { type Props } from 'react-apexcharts'; import { formatBalance } from '@/utils'; -import { type TPieChartData } from './PieChart.component'; +import { type TPieChartData } from './index'; export type TApexPieChartProps = Omit & { data: TPieChartData[]; diff --git a/src/components/Base/Charts/MuiPieChart.component.tsx b/src/components/Base/Charts/MuiPieChart.component.tsx deleted file mode 100644 index 2c65800b..00000000 --- a/src/components/Base/Charts/MuiPieChart.component.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import { alpha, hexToRgb, useTheme } from '@mui/material'; -import { PieChart, type PieValueType, type PieChartProps } from '@mui/x-charts'; -import { type MakeOptional } from '@mui/x-date-pickers/internals/models/helpers'; -import { PieCenterLabel } from './PieCenterLabel.component'; -import { formatBalance } from '@/utils'; - -export type TMuiChartData = MakeOptional[]; - -export type TMuiChartProps = Omit & { - data: TMuiChartData; - formatAsCurrency?: boolean; - showTotalSum?: boolean; -}; - -export const MuiPieChart: React.FC = ({ - data, - formatAsCurrency = false, - showTotalSum = false, - ...props -}) => { - const theme = useTheme(); - - const sum = React.useMemo(() => { - return data.reduce((acc, { value }) => acc + value, 0); - }, [data]); - - const colorRange: string[] = React.useMemo(() => { - return data - .map((_, idx, arr) => { - return data.length > 1 - ? alpha(hexToRgb(theme.palette.primary.main), (1 / arr.length) * (idx + 1)) - : alpha(hexToRgb(theme.palette.primary.main), 1); - }) - .reverse(); - }, [data]); - - const formatNumber = React.useCallback( - (num: number) => { - return formatAsCurrency ? formatBalance(num) : num; - }, - [formatAsCurrency] - ); - - return ( - `${item.label}`, - arcLabelMinAngle: 10, - paddingAngle: 0.25, - sortingValues: (a, b) => b - a, - valueFormatter: ({ value }) => formatBalance(value), - }, - ]} - margin={{ right: 0 }} - slotProps={{ legend: { hidden: true } }} - {...props} - > - {showTotalSum && {formatNumber(sum)}} - - ); -}; diff --git a/src/components/Base/Charts/PieCenterLabel.component.tsx b/src/components/Base/Charts/PieCenterLabel.component.tsx deleted file mode 100644 index ca8746ed..00000000 --- a/src/components/Base/Charts/PieCenterLabel.component.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { styled } from '@mui/material'; -import { useDrawingArea } from '@mui/x-charts'; - -const StyledText = styled('text')(({ theme }) => ({ - fill: theme.palette.text.primary, - textAnchor: 'middle', - fontWeight: 'bolder', - dominantBaseline: 'central', - fontSize: 28, -})); - -export const PieCenterLabel: React.FC = ({ children }) => { - const { width, height, left, top } = useDrawingArea(); - return ( - - {children} - - ); -}; diff --git a/src/components/Base/Charts/PieChart.component.tsx b/src/components/Base/Charts/PieChart.component.tsx deleted file mode 100644 index 5006054c..00000000 --- a/src/components/Base/Charts/PieChart.component.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React from 'react'; -import { Tooltip } from '@mui/material'; -import { alpha, hexToRgb, useTheme } from '@mui/material'; -import { formatBalance } from '@/utils'; -import { useScreenSize } from '@/hooks'; -import Pie, { PieArcDatum } from '@visx/shape/lib/shapes/Pie'; -import { NoResults } from '../NoResults.component'; -import { scaleOrdinal } from '@visx/scale'; -import { Group } from '@visx/group'; - -export type TPieChartData = { - label: string; - value: number; -}; - -export type TPieChartProps = { - data: TPieChartData[]; - width: number; - height: number; - margin?: { top: number; right: number; bottom: number; left: number }; - formatAsCurrency?: boolean; - showTotalSum?: boolean; -}; - -function getAbsoluteAmount(data: TPieChartData) { - return Math.abs(data.value); -} - -function formatValue(amount: number, formatAsCurrency: boolean = false) { - return formatAsCurrency ? formatBalance(amount) : amount; -} - -function hasSpaceForLabel( - screenSize: ReturnType, - arc: PieArcDatum -) { - return screenSize === 'small' - ? arc.endAngle - arc.startAngle >= 0.15 - : arc.endAngle - arc.startAngle >= 0.2; -} - -export const PieChart: React.FC = ({ - data, - width, - height, - margin = { top: 0, right: 0, bottom: 0, left: 0 }, - formatAsCurrency, - showTotalSum = false, -}) => { - // Hooks - const theme = useTheme(); - const screenSize = useScreenSize(); - // Component variables - const inner = React.useMemo(() => { - return { - width: width - margin.left - margin.right, - height: height - margin.top - margin.bottom, - }; - }, [width, height, margin]); - const axis = React.useMemo(() => { - return { - y: inner.height / 2, - x: inner.width / 2, - }; - }, [inner]); - const radius = React.useMemo(() => { - return Math.min(inner.width, inner.height) / 2; - }, [inner]); - const donut_thickness = screenSize === 'small' ? 75 : 100; - // Data driven variables - const displayedData = React.useMemo(() => { - return data.filter((entry) => entry.value !== 0); - }, [data]); - const category_names = Object.entries(displayedData).map(([_key, value]) => value.label); - const color_range = displayedData.map((_expense, index) => { - return displayedData.length > 1 - ? alpha(hexToRgb(theme.palette.primary.main), (1 / displayedData.length) * (index + 1)) - : alpha(hexToRgb(theme.palette.primary.main), 1); - }); - const sum = displayedData.map(getAbsoluteAmount).reduce((prev, cur) => prev + cur, 0); - const getCategoryColor = scaleOrdinal({ - domain: category_names, - range: color_range, - }); - - if (displayedData.length === 0) { - return ; - } - return ( - - - - {(pie) => - pie.arcs.map((arc, i: number) => { - const [centroidX, centroidY] = pie.path.centroid(arc); - return ( - - - - - - {hasSpaceForLabel(screenSize, arc) && ( - - - {arc.data.label} - - - {formatValue(Math.abs(arc.data.value), formatAsCurrency)} - - - {Math.abs((arc.data.value * 100) / sum).toFixed(2)} % - - - )} - - - {/* Total sum */} - {showTotalSum && ( - - - {formatValue(sum, formatAsCurrency)} - - - )} - - ); - }) - } - - - - ); -}; diff --git a/src/components/Base/Charts/index.ts b/src/components/Base/Charts/index.ts index 92bb37c7..a02fc1f6 100644 --- a/src/components/Base/Charts/index.ts +++ b/src/components/Base/Charts/index.ts @@ -1,4 +1,7 @@ -export * from './PieChart.component'; +export type TPieChartData = { + label: string; + value: number; +}; + +export * from './ApexPieChart.component'; export * from './BarChart.component'; -export * from './MuiPieChart.component'; -export * from './PieCenterLabel.component'; diff --git a/src/components/Base/SparklineWidget.component.tsx b/src/components/Base/SparklineWidget.component.tsx index 57a8d565..4325811b 100644 --- a/src/components/Base/SparklineWidget.component.tsx +++ b/src/components/Base/SparklineWidget.component.tsx @@ -1,108 +1,20 @@ import React from 'react'; -import Card from './Card.component'; import { Box, CardProps, useTheme } from '@mui/material'; -import { - AxisConfig, - CardinalDirections, - ChartsAxisHighlightProps, - ChartsTooltipProps, - LineSeriesType, - ResponsiveChartContainerProps, - SparkLineChart, -} from '@mui/x-charts'; -import { - SparkLineChartSlotComponentProps, - SparkLineChartSlotsComponent, -} from 'node_modules/@mui/x-charts/SparkLineChart/SparkLineChart'; -import { type MakeOptional } from '@mui/x-date-pickers/internals/models/helpers'; - -/** - * Currently not exported by the @mui/x-charts library, therefore we need to redeclare it here. - * @description The props for the SparkLineChart component. - */ -export interface SparkLineChartProps - extends Omit { - /** - * The xAxis configuration. - * Notice it is a single configuration object, not an array of configuration. - */ - xAxis?: MakeOptional; - tooltip?: ChartsTooltipProps; - axisHighlight?: ChartsAxisHighlightProps; - /** - * Type of plot used. - * @default 'line' - */ - plotType?: 'line' | 'bar'; - /** - * Data to plot. - */ - data: number[]; - /** - * Formatter used by the tooltip. - * @param {number} value The value to format. - * @returns {string} the formatted value. - * @default (v: number) => v.toString() - */ - valueFormatter?: (value: number) => string; - /** - * Set to `true` to enable the tooltip in the sparkline. - * @default false - */ - showTooltip?: boolean; - /** - * Set to `true` to highlight the value. - * With line, it shows a point. - * With bar, it shows a highlight band. - * @default false - */ - showHighlight?: boolean; - /** - * Set to `true` to fill spark line area. - * Has no effect if plotType='bar'. - * @default false - */ - area?: LineSeriesType['area']; - /** - * @default 'linear' - */ - curve?: LineSeriesType['curve']; - /** - * The margin between the SVG and the drawing area. - * It's used for leaving some space for extra information such as the x- and y-axis or legend. - * Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`. - * @default { - * top: 5, - * bottom: 5, - * left: 5, - * right: 5, - * } - */ - margin?: Partial>; - /** - * Overridable component slots. - * @default {} - */ - slots?: SparkLineChartSlotsComponent; - /** - * The props used for each component slot. - * @default {} - */ - slotProps?: SparkLineChartSlotComponentProps; -} +import Chart from 'react-apexcharts'; +import Card from './Card.component'; export type TSparklineWidget = { title: string; subtitle?: string; cardProps?: CardProps; - sparklineProps: SparkLineChartProps; + data: number[]; }; export const SparklineWidget: React.FC = ({ title, subtitle, cardProps, - sparklineProps, + data, }) => { const theme = useTheme(); return ( @@ -114,14 +26,36 @@ export const SparklineWidget: React.FC = ({ - diff --git a/src/components/Drawer/FormDrawer/FormDrawer.component.tsx b/src/components/Drawer/FormDrawer/FormDrawer.component.tsx index da37a763..2159d1c3 100644 --- a/src/components/Drawer/FormDrawer/FormDrawer.component.tsx +++ b/src/components/Drawer/FormDrawer/FormDrawer.component.tsx @@ -9,10 +9,9 @@ import { IconButton, Alert, CircularProgress, - ButtonProps, } from '@mui/material'; import React from 'react'; -import { ActionPaper } from '../../Base'; +import { ActionPaper } from '@/components/Base'; import { CloseRounded, DoneRounded, ErrorRounded } from '@mui/icons-material'; import { type TFormDrawerState } from './FormDrawer.reducer'; @@ -105,35 +104,27 @@ export const FormDrawer: React.FC> = ( Cancel - + ); }; - -const SaveButton: React.FC<{ state?: TFormDrawerState } & Pick> = - React.forwardRef(({ state }, ref) => { - return ( - - ); - }); diff --git a/src/components/Stocks/Stock.service.ts b/src/components/Stocks/Stock.service.ts index 4d565dcf..aa89d444 100644 --- a/src/components/Stocks/Stock.service.ts +++ b/src/components/Stocks/Stock.service.ts @@ -329,7 +329,7 @@ export class StockService { * @returns A promise that resolves to a tuple containing the stock exchanges data and any potential error. */ static async getExchanges( - authOptions: IAuthContext['authOptions'] + _authOptions: IAuthContext['authOptions'] ): Promise> { return [null, new Error('Not implemented')]; // try { diff --git a/src/components/Transaction/MonthlyBalance/MonthlyBalanceWidget.component.tsx b/src/components/Transaction/MonthlyBalance/MonthlyBalanceWidget.component.tsx index 84ee1160..e1153432 100644 --- a/src/components/Transaction/MonthlyBalance/MonthlyBalanceWidget.component.tsx +++ b/src/components/Transaction/MonthlyBalance/MonthlyBalanceWidget.component.tsx @@ -1,9 +1,6 @@ import React from 'react'; import { SparklineWidget, type TSparklineWidget } from '@/components/Base'; import { useFetchMonthlyBalance } from './useFetchMonthlyBalance.hook'; -import { DateService } from '@/services'; -import { isSameYear } from 'date-fns'; -import { formatBalance } from '@/utils'; export type TMonthlyBalanceWidget = { cardPros?: TSparklineWidget['cardProps']; @@ -25,17 +22,18 @@ export const MonthlyBalanceWidget: React.FC = ({ cardPros - `${DateService.shortMonthName(value)} ${ - isSameYear(value, new Date()) ? '' : value.getFullYear() - }`, - }, - }} + data={data} + // sparklineProps={{ + // data: data, + // valueFormatter: formatBalance, + // xAxis: { + // data: months, + // valueFormatter: (value) => + // `${DateService.shortMonthName(value)} ${ + // isSameYear(value, new Date()) ? '' : value.getFullYear() + // }`, + // }, + // }} cardProps={cardPros} /> );