Skip to content

Commit

Permalink
Implemented Date Range Input. (#2328)
Browse files Browse the repository at this point in the history
* Added: date range input component using ShadCN's date picker component

* Changes: applied to necessary files with suggested code

* Changes: Applied to take a try with just onChange as a prop.

* Changes: Updated the files with necessary props

* Updated: Applied suggested changes

* fix(backoffice-v2): addressed pr comments

* refactor(backoffice-v2): simplified undefined values

* Changes: Applied to seperate the business logic annd presentation data within home-page

* Applied: Suggested changes to useHomeLogic

* Updated: Fixed the active state of tabs using defaultTabValue

* Changes: Applied suggested file changes

* updated useHomeLogic exports

---------

Co-authored-by: Omri Levy <[email protected]>
  • Loading branch information
shashankvish0010 and Omri-Levy authored May 19, 2024
1 parent 1509300 commit fec51d2
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 80 deletions.
1 change: 1 addition & 0 deletions apps/backoffice-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"msw": "^1.0.0",
"qs": "^6.11.2",
"react": "^18.2.0",
"react-day-picker": "^8.10.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-i18next": "^12.1.4",
Expand Down
2 changes: 1 addition & 1 deletion apps/backoffice-v2/src/Router/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { NotFoundRedirect } from '@/pages/NotFound/NotFound';
import { TransactionMonitoringAlerts } from '@/pages/TransactionMonitoringAlerts/TransactionMonitoringAlerts.page';
import { TransactionMonitoring } from '@/pages/TransactionMonitoring/TransactionMonitoring';
import { TransactionMonitoringAlertsAnalysisPage } from '@/pages/TransactionMonitoringAlertsAnalysis/TransactionMonitoringAlertsAnalysis.page';
import { Home } from '../common/components/atoms/Home/Home';
import { Home } from '@/pages/Home/Home.page';
import { Statistics } from '@/pages/Statistics/Statistics.page';
import { Workflows } from '@/pages/Workflows/Workflows.page';

Expand Down
60 changes: 0 additions & 60 deletions apps/backoffice-v2/src/common/components/atoms/Home/Home.tsx

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { ComponentProps } from 'react';
import { CalendarIcon } from '@radix-ui/react-icons';
import { formatDate } from '@/common/utils/format-date';
import { ctw } from '@/common/utils/ctw/ctw';
import { Button } from '../../atoms/Button/Button';
import { Calendar } from '../../organisms/Calendar/Calendar';
import { Popover, PopoverContent, PopoverTrigger } from '@ballerine/ui';

type TDateRangePickerProps = {
onChange: NonNullable<ComponentProps<typeof Calendar>['onSelect']>;
value: NonNullable<ComponentProps<typeof Calendar>['selected']>;
className?: ComponentProps<'div'>['className'];
};

export const DateRangePicker = ({ onChange, value, className }: TDateRangePickerProps) => {
return (
<div className={ctw('grid gap-2', className)}>
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant={'outline'}
className={ctw('w-[300px] justify-start text-left font-normal', {
'text-muted-foreground': !value,
})}
>
<CalendarIcon className="size-4 mr-2" />
{value?.from && value?.to && (
<>
{formatDate(value.from, 'LLL dd, y')} - {formatDate(value.to, 'LLL dd, y')}
</>
)}
{value?.from && !value?.to && formatDate(value.from, 'LLL dd, y')}
{!value?.from && !value?.to && <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
initialFocus
mode="range"
selected={value}
onSelect={onChange}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { ChevronLeftIcon, ChevronRightIcon } from '@radix-ui/react-icons';
import { ctw } from '@/common/utils/ctw/ctw';
import { DayPicker, DayPickerRangeProps } from 'react-day-picker';
import { buttonVariants } from '../../atoms/Button/Button';

export type CalendarProps = DayPickerRangeProps;
export const Calendar = ({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) => {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={ctw('p-3', className)}
classNames={{
months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
month: 'space-y-4',
caption: 'flex justify-center pt-1 relative items-center',
caption_label: 'text-sm font-medium',
nav: 'space-x-1 flex items-center',
nav_button: ctw(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
),
nav_button_previous: 'absolute left-1',
nav_button_next: 'absolute right-1',
table: 'w-full border-collapse space-y-1',
head_row: 'flex',
head_cell: 'text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]',
row: 'flex w-full mt-2',
cell: ctw(
'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md',
props.mode === 'range'
? '[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md'
: '[&:has([aria-selected])]:rounded-md',
),
day: ctw(
buttonVariants({ variant: 'ghost' }),
'h-8 w-8 p-0 font-normal aria-selected:opacity-100',
),
day_range_start: 'day-range-start',
day_range_end: 'day-range-end',
day_selected:
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
day_today: 'bg-accent text-accent-foreground',
day_outside:
'day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30',
day_disabled: 'text-muted-foreground opacity-50',
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
day_hidden: 'invisible',
...classNames,
}}
components={{
IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />,
IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />,
}}
{...props}
/>
);
};
Calendar.displayName = 'Calendar';
46 changes: 46 additions & 0 deletions apps/backoffice-v2/src/common/hooks/useHomeLogic/useHomeLogic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ComponentProps, useEffect } from 'react';
import { DateRangePicker } from '@/common/components/molecules/DateRangePicker/DateRangePicker';
import { useZodSearchParams } from '@/common/hooks/useZodSearchParams/useZodSearchParams';
import { HomeSearchSchema } from '@/pages/Home/home-search-schema';
import { useAuthenticatedUserQuery } from '@/domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery';
import { useLocale } from '@/common/hooks/useLocale/useLocale';
import { useLocation, useNavigate } from 'react-router-dom';

export const useHomeLogic = () => {
const locale = useLocale();
const { pathname, search } = useLocation();
const navigate = useNavigate();
const [{ from, to }, setSearchParams] = useZodSearchParams(HomeSearchSchema);
const { data: session } = useAuthenticatedUserQuery();
const { firstName, fullName, avatarUrl } = session?.user || {};
const statisticsLink = `/${locale}/home/statistics${search}`;
const workflowsLink = `/${locale}/home/workflows${search}`;
const defaultTabValue = `${pathname}${search}`;

useEffect(() => {
if (pathname !== `/${locale}` && pathname !== `/${locale}/home`) {
return;
}

navigate(`/${locale}/home/statistics`);
}, [pathname, locale, navigate]);

const onDateRangeChange: ComponentProps<typeof DateRangePicker>['onChange'] = range => {
const from = range?.from?.toISOString();
const to = range?.to?.toISOString();

setSearchParams({ from, to });
};

return {
from,
to,
firstName,
fullName,
avatarUrl,
statisticsLink,
workflowsLink,
defaultTabValue,
onDateRangeChange,
};
};
61 changes: 61 additions & 0 deletions apps/backoffice-v2/src/pages/Home/Home.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { FunctionComponent } from 'react';
import { NavLink, Outlet } from 'react-router-dom';
import { UserAvatar } from '@/common/components/atoms/UserAvatar/UserAvatar';
import { Tabs } from '@/common/components/organisms/Tabs/Tabs';
import { TabsList } from '@/common/components/organisms/Tabs/Tabs.List';
import { TabsTrigger } from '@/common/components/organisms/Tabs/Tabs.Trigger';
import { TabsContent } from '@/common/components/organisms/Tabs/Tabs.Content';
import { DateRangePicker } from '@/common/components/molecules/DateRangePicker/DateRangePicker';
import { useHomeLogic } from '@/common/hooks/useHomeLogic/useHomeLogic';
import { t } from 'i18next';

export const Home: FunctionComponent = () => {
const {
onDateRangeChange,
from,
to,
firstName,
fullName,
avatarUrl,
statisticsLink,
workflowsLink,
defaultTabValue,
} = useHomeLogic();

return (
<div className={`flex flex-col gap-10 p-10`}>
<div className={`flex items-center justify-between`}>
<div className={`flex items-center`}>
<UserAvatar
fullName={fullName ?? ''}
className={`mr-2 d-6`}
avatarUrl={avatarUrl ?? undefined}
/>
<h3 className={`flex max-w-[45ch] break-all text-2xl font-semibold`}>
{t(`home.greeting`)}
{firstName && ` ${firstName}`}
</h3>
</div>
<DateRangePicker
onChange={onDateRangeChange}
value={{ from: from ? new Date(from) : undefined, to: to ? new Date(to) : undefined }}
/>
</div>
<div>
<Tabs defaultValue={defaultTabValue} key={defaultTabValue}>
<TabsList>
<TabsTrigger asChild value={statisticsLink}>
<NavLink to={statisticsLink}>Statistics</NavLink>
</TabsTrigger>
<TabsTrigger asChild value={workflowsLink}>
<NavLink to={workflowsLink}>Workflows</NavLink>
</TabsTrigger>
</TabsList>
<TabsContent value={defaultTabValue}>
<Outlet />
</TabsContent>
</Tabs>
</div>
</div>
);
};
8 changes: 8 additions & 0 deletions apps/backoffice-v2/src/pages/Home/home-search-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from 'zod';

export const HomeSearchSchema = z.object({
from: z.string().datetime().optional(),
to: z.string().datetime().optional(),
});

export type DateRange = z.infer<typeof HomeSearchSchema>;
21 changes: 17 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion services/workflows-service/prisma/data-migrations

0 comments on commit fec51d2

Please sign in to comment.