Skip to content

Commit

Permalink
feat: add ability to 'star' a dashboard and persist it
Browse files Browse the repository at this point in the history
  • Loading branch information
belapferreira committed Nov 9, 2023
1 parent 9ab4564 commit a576361
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 22 deletions.
48 changes: 39 additions & 9 deletions src/app/components/Accordion/AccordionTrigger.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,59 @@
import { ComponentProps, ComponentRef, forwardRef } from 'react';
import { ComponentProps, ComponentRef, forwardRef, useCallback } from 'react';
import * as Accordion from '@radix-ui/react-accordion';

import { IconChevronDown24 } from '@dhis2/ui';
import {
Box,
IconChevronDown24,
IconStar24,
IconStarFilled24,
} from '@dhis2/ui';

type AccordionTriggerProps = ComponentProps<typeof Accordion.Trigger>;
import { Dashboard } from '@/hooks/queries/useGetDashboards';
import { useDashboards } from '@/hooks/useDashboards';

type AccordionTriggerProps = ComponentProps<typeof Accordion.Trigger> &
Dashboard;

export const AccordionTrigger = forwardRef<
ComponentRef<typeof Accordion.Trigger>,
AccordionTriggerProps
>((props, forwardedRef) => {
const { children, ...rest } = props;
const { id, displayName, starred, children, ...rest } = props;

const { handleChangeStarred } = useDashboards();

return (
<Accordion.Trigger
{...rest}
ref={forwardedRef}
className="group flex w-full"
>
<div className="flex w-full justify-between">
<Box className="flex w-full justify-between text-app-grey-900">
{children}

<div className="self-start transition-transform duration-500 ease-[cubic-bezier(0.87,_0,_0.13,_1)] group-data-[state=open]:rotate-180">
<IconChevronDown24 />
</div>
</div>
<Box className="flex gap-4">
<button
className="h-fit"
onClick={() =>
handleChangeStarred({
id,
displayName,
starred: !starred,
})
}
>
{starred ? (
<IconStarFilled24 color="#ffa902" />
) : (
<IconStar24 color="#404B5A" />
)}
</button>

<Box className="self-start transition-transform duration-500 ease-[cubic-bezier(0.87,_0,_0.13,_1)] group-data-[state=open]:rotate-180">
<IconChevronDown24 color="#404B5A" />
</Box>
</Box>
</Box>
</Accordion.Trigger>
);
});
Expand Down
10 changes: 4 additions & 6 deletions src/app/components/Accordion/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import { AccordionTrigger } from './AccordionTrigger';
import { AccordionContent } from './AccordionContent';

export const Accordion = () => {
const { dashboardsData, dashboardActive, handleDashboardActiveChange } =
const { dashboards, dashboardActive, handleDashboardActiveChange } =
useDashboards();

const dashboards = dashboardsData?.dashboards;

if (!dashboardsData?.dashboards?.length) {
if (!dashboards?.length) {
return <Loader />;
}

Expand All @@ -24,9 +22,9 @@ export const Accordion = () => {
className="mt-6 flex w-full flex-col"
onValueChange={handleDashboardActiveChange}
>
{dashboards?.map(({ id, displayName }) => (
{dashboards?.map(({ id, displayName, starred }) => (
<AccordionItem key={id} value={id}>
<AccordionTrigger>
<AccordionTrigger id={id} displayName={displayName} starred={starred}>
<h3 className="text-lg font-semibold group-data-[state=open]:mb-5">
{displayName}
</h3>
Expand Down
101 changes: 94 additions & 7 deletions src/hooks/useDashboards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {

import { options, DashboardItemTypes } from '@/constants';

import { Dashboards } from '@/hooks/queries/useGetDashboards';
import { Dashboard } from '@/hooks/queries/useGetDashboards';
import { DashboardItems } from '@/hooks/queries/useGetDashboardItemsById';

import { useGetDashboards } from '@/hooks/queries/useGetDashboards';
Expand All @@ -24,33 +24,36 @@ type ProviderProps = { children: ReactNode };
type ContextProps = {
isLoadingItems: boolean;
dashboardActive: string;
dashboards: Dashboard[];
selectedOptions: DashboardItemTypes[];
dashboardsData: Dashboards | undefined;
dashboardItems: DashboardItems | undefined;
handleChangeStarred: (params: Dashboard) => void;
handleChangeSelectedOptions: (value: Value) => void;
handleDashboardActiveChange: (value: string) => void;
};

export const INITIAL_OPTIONS_VALUE = options[0].value;
export const DASHBOARDS_STARRED_KEY = '@Dhis2:recordStarredDashboards';

const DashboardsContext = createContext({} as ContextProps);

export const DashboardsProvider = ({ children }: ProviderProps) => {
const [dashboards, setDashboards] = useState<Dashboard[]>([]);
const [dashboardActive, setDashboardActive] = useState<string>('');
const [selectedOptions, setSelectedOptions] = useState([
INITIAL_OPTIONS_VALUE,
]);

const { data: dashboardsData } = useGetDashboards();
const { data: dashboardsResponse } = useGetDashboards();

const { data: dashboardItems, isLoading: isLoadingItems } =
useGetDashboardItemsById(dashboardActive as string, {
enabled: !!dashboardActive,
});

const dashboards = dashboardsData?.dashboards;
const dashboardsData = dashboardsResponse?.dashboards;

const firstDashboardId = dashboards && dashboards[0]?.id;
const firstDashboardId = dashboardsData && dashboardsData[0]?.id;

const handleDashboardActiveChange = useCallback((value: string) => {
setDashboardActive(value);
Expand Down Expand Up @@ -86,20 +89,104 @@ export const DashboardsProvider = ({ children }: ProviderProps) => {
}
}, []);

const handleGetDashboardsStored = useCallback(() => {
const storedDashboardsStarred = localStorage.getItem(
DASHBOARDS_STARRED_KEY,
);

if (!storedDashboardsStarred) {
return;
}

const storedDashboardsParsed: Dashboard[] = JSON.parse(
storedDashboardsStarred,
);

return storedDashboardsParsed;
}, []);

const handleChangeStarred = useCallback(
(params: Dashboard) => {
const { id, displayName, starred } = params;

const storedDashboardsData = handleGetDashboardsStored();

const dashboardsFiltered = dashboards?.filter(
(dashboard) => dashboard.id !== id,
);

if (storedDashboardsData) {
const storedDashboardsFitlered = storedDashboardsData?.filter(
(dashboard) => dashboard.id !== id,
);

const dashboardsUpdated = [
...storedDashboardsFitlered,
{ id, displayName, starred },
];

localStorage.setItem(
DASHBOARDS_STARRED_KEY,
JSON.stringify(dashboardsUpdated),
);
} else {
localStorage.setItem(
DASHBOARDS_STARRED_KEY,
JSON.stringify([{ id, displayName, starred }]),
);
}

setDashboards([...dashboardsFiltered, { id, displayName, starred }]);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[handleGetDashboardsStored],
);

useEffect(() => {
if (firstDashboardId) {
setDashboardActive(firstDashboardId);
}
}, [firstDashboardId]);

useEffect(() => {
const storedDashboardsData = handleGetDashboardsStored();

if (!storedDashboardsData && !dashboards.length) {
setDashboards(dashboardsData || []);
return;
}

const updatedDashboardsData = dashboardsData?.map((dashboard) => {
const starredStatusStored = storedDashboardsData?.find(
(storedDashboard) => storedDashboard?.id === dashboard?.id,
);

if (!starredStatusStored) {
return dashboard;
}

return starredStatusStored;
});

setDashboards(updatedDashboardsData as Dashboard[]);
}, [
dashboardsResponse,
setDashboards,
handleChangeStarred,
handleGetDashboardsStored,
dashboards?.length,
dashboardsData,
]);

return (
<DashboardsContext.Provider
value={{
dashboardActive,
dashboardsData,
dashboards,
dashboardItems,
isLoadingItems,
dashboardActive,
selectedOptions,
handleChangeStarred,
handleDashboardActiveChange,
handleChangeSelectedOptions,
}}
Expand Down

0 comments on commit a576361

Please sign in to comment.