1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

Dashboard API hook (#5990)

Data fetching for dashboard

https://linear.app/unleash/issue/1-1969/dashboard-users-chart-api-hook
This commit is contained in:
Tymoteusz Czech 2024-01-26 09:03:12 +01:00 committed by GitHub
parent 9ac1c88bd4
commit 00b3cbaa8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 118 additions and 338 deletions

View File

@ -3,6 +3,7 @@ import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { VFC } from 'react';
import { UsersChart } from './UsersChart/UsersChart';
import { FlagsChart } from './FlagsChart/FlagsChart';
import { useExecutiveDashboard } from 'hooks/api/getters/useExecutiveSummary/useExecutiveSummary';
import { UserStats } from './UserStats/UserStats';
const StyledGrid = styled(Box)(({ theme }) => ({
@ -13,6 +14,8 @@ const StyledGrid = styled(Box)(({ theme }) => ({
}));
export const ExecutiveDashboard: VFC = () => {
const { executiveDashboardData, loading, error } = useExecutiveDashboard();
return (
<>
<Box sx={(theme) => ({ paddingBottom: theme.spacing(4) })}>
@ -22,14 +25,17 @@ export const ExecutiveDashboard: VFC = () => {
Dashboard
</Typography>
}
// subtitle='Succesfully synchronized: 01 Sep 2023 - 07:05:07'
/>
</Box>
{/* Dashboard */}
<StyledGrid>
<UserStats />
<UsersChart />
<FlagsChart />
<UsersChart
userTrends={executiveDashboardData?.userTrends ?? []}
/>
<Paper>Stats</Paper>
<FlagsChart
flagsTrends={executiveDashboardData?.flagsTrends ?? []}
/>
</StyledGrid>
</>
);

View File

@ -1,4 +1,4 @@
import { type VFC } from 'react';
import { useMemo, type VFC } from 'react';
import {
Chart as ChartJS,
CategoryScale,
@ -18,40 +18,31 @@ import {
type ILocationSettings,
} from 'hooks/useLocationSettings';
import { formatDateYMD } from 'utils/formatDate';
import { mockData as usersMockData } from '../UsersChart/UsersChartComponent';
import { ExecutiveSummarySchema } from 'openapi';
type Data = {
date: string | Date;
total?: number;
active?: number;
archived?: number;
}[];
const mockData: Data = usersMockData.map((item) => ({
...item,
archived: item.inactive,
}));
const createData = (theme: Theme) => ({
labels: mockData.map((item) => item.date),
const createData = (
theme: Theme,
flagsTrends: ExecutiveSummarySchema['flagsTrends'] = [],
) => ({
labels: flagsTrends.map((item) => item.date),
datasets: [
{
label: 'Total flags',
data: mockData.map((item) => item.total),
data: flagsTrends.map((item) => item.total),
borderColor: theme.palette.primary.main,
backgroundColor: theme.palette.primary.main,
fill: true,
},
{
label: 'Archived flags',
data: mockData.map((item) => item.archived),
data: flagsTrends.map((item) => item.archived),
borderColor: theme.palette.error.main,
backgroundColor: theme.palette.error.main,
fill: true,
},
{
label: 'Active flags',
data: mockData.map((item) => item.active),
data: flagsTrends.map((item) => item.active),
borderColor: theme.palette.success.main,
backgroundColor: theme.palette.success.main,
fill: true,
@ -124,10 +115,19 @@ const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
},
}) as const;
const FlagsChartComponent: VFC = () => {
interface IFlagsChartComponentProps {
flagsTrends: ExecutiveSummarySchema['flagsTrends'];
}
const FlagsChartComponent: VFC<IFlagsChartComponentProps> = ({
flagsTrends,
}) => {
const theme = useTheme();
const { locationSettings } = useLocationSettings();
const data = createData(theme);
const data = useMemo(
() => createData(theme, flagsTrends),
[theme, flagsTrends],
);
const options = createOptions(theme, locationSettings);
return (

View File

@ -1,4 +1,4 @@
import { type VFC } from 'react';
import { useMemo, type VFC } from 'react';
import {
Chart as ChartJS,
CategoryScale,
@ -18,316 +18,7 @@ import {
type ILocationSettings,
} from 'hooks/useLocationSettings';
import { formatDateYMD } from 'utils/formatDate';
type Data = {
date: string | Date;
total?: number;
active?: number;
inactive?: number;
}[];
export const mockData: Data = [
{
date: '2023-01-21',
},
{
date: '2023-01-28',
},
{
date: '2023-02-04',
},
{
date: '2023-02-11',
},
{
date: '2023-02-18',
},
{
date: '2023-02-25',
},
{
date: '2023-03-04',
},
{
date: '2023-03-11',
},
{
date: '2023-03-18',
},
{
date: '2023-03-25',
},
{
date: '2023-04-01',
},
{
date: '2023-04-08',
},
{
date: '2023-04-15',
},
{
date: '2023-04-22',
total: 43,
active: 0,
inactive: 0,
},
{
date: '2023-04-29',
total: 54,
active: 54,
inactive: 0,
},
{
date: '2023-05-06',
total: 63,
active: 63,
inactive: 0,
},
{
date: '2023-05-13',
total: 81,
active: 81,
inactive: 0,
},
{
date: '2023-05-20',
total: 80,
active: 80,
inactive: 0,
},
{
date: '2023-05-27',
total: 95,
active: 95,
inactive: 0,
},
{
date: '2023-06-03',
total: 108,
active: 108,
inactive: 0,
},
{
date: '2023-06-10',
total: 101,
active: 101,
inactive: 0,
},
{
date: '2023-06-17',
total: 104,
active: 104,
inactive: 0,
},
{
date: '2023-06-24',
total: 114,
active: 114,
inactive: 0,
},
{
date: '2023-07-01',
total: 108,
active: 106,
inactive: 2,
},
{
date: '2023-07-08',
total: 103,
active: 102,
inactive: 1,
},
{
date: '2023-07-15',
total: 106,
active: 105,
inactive: 1,
},
{
date: '2023-07-22',
total: 112,
active: 106,
inactive: 6,
},
{
date: '2023-07-29',
total: 113,
active: 107,
inactive: 6,
},
{
date: '2023-08-05',
total: 109,
active: 98,
inactive: 11,
},
{
date: '2023-08-12',
total: 110,
active: 96,
inactive: 14,
},
{
date: '2023-08-19',
total: 127,
active: 111,
inactive: 16,
},
{
date: '2023-08-26',
total: 140,
active: 124,
inactive: 16,
},
{
date: '2023-09-02',
total: 150,
active: 130,
inactive: 20,
},
{
date: '2023-09-09',
total: 168,
active: 148,
inactive: 20,
},
{
date: '2023-09-16',
total: 171,
active: 154,
inactive: 17,
},
{
date: '2023-09-23',
total: 190,
active: 174,
inactive: 16,
},
{
date: '2023-09-30',
total: 186,
active: 169,
inactive: 17,
},
{
date: '2023-10-07',
total: 188,
active: 173,
inactive: 15,
},
{
date: '2023-10-14',
total: 181,
active: 166,
inactive: 15,
},
{
date: '2023-10-21',
total: 192,
active: 177,
inactive: 15,
},
{
date: '2023-10-28',
total: 183,
active: 164,
inactive: 19,
},
{
date: '2023-11-04',
total: 200,
active: 180,
inactive: 20,
},
{
date: '2023-11-11',
total: 212,
active: 189,
inactive: 23,
},
{
date: '2023-11-18',
total: 204,
active: 177,
inactive: 27,
},
{
date: '2023-11-25',
total: 200,
active: 173,
inactive: 27,
},
{
date: '2023-12-02',
total: 200,
active: 175,
inactive: 25,
},
{
date: '2023-12-09',
total: 200,
active: 176,
inactive: 24,
},
{
date: '2023-12-16',
total: 215,
active: 186,
inactive: 29,
},
{
date: '2023-12-23',
total: 221,
active: 195,
inactive: 26,
},
{
date: '2023-12-30',
total: 214,
active: 184,
inactive: 30,
},
{
date: '2024-01-06',
total: 204,
active: 173,
inactive: 31,
},
{
date: '2024-01-13',
total: 215,
active: 181,
inactive: 34,
},
];
const createData = (theme: Theme) => ({
labels: mockData.map((item) => item.date),
datasets: [
{
label: 'Total users',
data: mockData.map((item) => item.total),
borderColor: theme.palette.primary.main,
backgroundColor: theme.palette.primary.main,
fill: true,
},
{
label: 'Inactive users',
data: mockData.map((item) => item.inactive),
borderColor: theme.palette.error.main,
backgroundColor: theme.palette.error.main,
fill: true,
},
{
label: 'Active users',
data: mockData.map((item) => item.active),
borderColor: theme.palette.success.main,
backgroundColor: theme.palette.success.main,
fill: true,
},
],
});
import { ExecutiveSummarySchema } from 'openapi';
const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
({
@ -380,10 +71,50 @@ const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
},
}) as const;
const UsersChartComponent: VFC = () => {
interface IUsersChartComponentProps {
userTrends: ExecutiveSummarySchema['userTrends'];
}
const createData = (
theme: Theme,
userTrends: ExecutiveSummarySchema['userTrends'],
) => ({
labels: userTrends.map((item) => item.date),
datasets: [
{
label: 'Total users',
data: userTrends.map((item) => item.total),
borderColor: theme.palette.primary.main,
backgroundColor: theme.palette.primary.main,
fill: true,
},
{
label: 'Inactive users',
data: userTrends.map((item) => item.inactive),
borderColor: theme.palette.error.main,
backgroundColor: theme.palette.error.main,
fill: true,
},
{
label: 'Active users',
data: userTrends.map((item) => item.active),
borderColor: theme.palette.success.main,
backgroundColor: theme.palette.success.main,
fill: true,
},
],
});
const UsersChartComponent: VFC<IUsersChartComponentProps> = ({
userTrends,
}) => {
const theme = useTheme();
const { locationSettings } = useLocationSettings();
const data = createData(theme);
const data = useMemo(
() => createData(theme, userTrends),
[theme, userTrends],
);
const options = createOptions(theme, locationSettings);
return (

View File

@ -0,0 +1,43 @@
import useSWR, { mutate, SWRConfiguration } from 'swr';
import { useCallback } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
import { ExecutiveSummarySchema } from 'openapi';
interface IUseExecutiveDashboardDataOutput {
executiveDashboardData: ExecutiveSummarySchema | undefined;
refetchExecutiveDashboard: () => void;
loading: boolean;
error?: Error;
}
export const useExecutiveDashboard = (
options?: SWRConfiguration,
): IUseExecutiveDashboardDataOutput => {
const path = formatApiPath('api/admin/dashboard/executive');
const { data, error } = useSWR<ExecutiveSummarySchema>(
path,
fetchExecutiveDashboard,
options,
);
const refetchExecutiveDashboard = useCallback(() => {
mutate(path).catch(console.warn);
}, [path]);
return {
executiveDashboardData: data,
refetchExecutiveDashboard,
loading: !error && !data,
error,
};
};
const fetchExecutiveDashboard = (
path: string,
): Promise<ExecutiveSummarySchema> => {
return fetch(path)
.then(handleErrorResponses('Executive Dashboard Data'))
.then((res) => res.json());
};