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:
parent
9ac1c88bd4
commit
00b3cbaa8b
@ -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>
|
||||
</>
|
||||
);
|
||||
|
@ -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 (
|
||||
|
@ -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 (
|
||||
|
@ -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());
|
||||
};
|
Loading…
Reference in New Issue
Block a user