Skip to content

Commit

Permalink
feat(my_assigments): separate all assignments into "My own" and "Dele…
Browse files Browse the repository at this point in the history
…gated" tabs
  • Loading branch information
ild0tt0re committed Feb 26, 2025
1 parent b48096f commit 08c32a8
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 130 deletions.
11 changes: 11 additions & 0 deletions src/RootWrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ const theme = createTheme({
},
},
breakpoints: {
keys: [
'mobile',
'mobile400',
'tablet',
'tablet500',
'tablet600',
'tablet688',
'laptop',
'desktop',
'desktopLarge',
],
values: {
mobile: 0,
mobile400: 400,
Expand Down
20 changes: 16 additions & 4 deletions src/components/tabs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ReactNode, SyntheticEvent, useEffect, useState } from 'react';
import { Tabs as MUITabs, Tab, Box } from '@mui/material';
import { TabsPanelProps, CustomTabProps } from './index.types';
import useBreakpoints from '@hooks/useBreakpoints';

/**
* A custom tab panel component.
Expand All @@ -11,15 +12,16 @@ export const CustomTabPanel = (props: TabsPanelProps) => {
const { children, value, index, ...other } = props;

return (
<div
<Box
sx={{ height: '100%' }}
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && <Box sx={{ padding: '24px 0' }}>{children}</Box>}
</div>
</Box>
);
};

Expand All @@ -41,8 +43,9 @@ const a11yProps = (index: number) => {
*
* @param tabs An array of tabs with label and corresponding component.
*/
const Tabs = ({ tabs, value, onChange }: CustomTabProps) => {
const Tabs = ({ tabs, value, onChange, actionComponent }: CustomTabProps) => {
const [valueOfActivePanel, setValueOfActivePanel] = useState(value || 0);
const { tabletDown } = useBreakpoints();

/**
* Handle tab change event.
Expand All @@ -63,7 +66,15 @@ const Tabs = ({ tabs, value, onChange }: CustomTabProps) => {

return (
<Box sx={{ width: '100%' }}>
<Box>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: tabletDown ? 'stretch' : 'center',
flexDirection: tabletDown ? 'column' : 'row',
rowGap: tabletDown ? '16px' : '0px',
}}
>
<MUITabs
value={valueOfActivePanel}
onChange={handleChange}
Expand Down Expand Up @@ -94,6 +105,7 @@ const Tabs = ({ tabs, value, onChange }: CustomTabProps) => {
)
)}
</MUITabs>
{actionComponent}
</Box>

{tabs.map(
Expand Down
5 changes: 5 additions & 0 deletions src/components/tabs/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ export interface CustomTabProps extends TabOwnProps {
*/
tabsCountOnScreen?: number;

/**
* The action component to be displayed with the tab.
*/
actionComponent?: ReactNode;

/**
* Custom styling applied to the tab component using MUI's `sx` prop.
*/
Expand Down
39 changes: 39 additions & 0 deletions src/components/tabs/tab_label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Box } from '@mui/material';

const TabLabel = ({ label, count }: { label: string; count: number }) => {
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: '8px',
transform: count === 0 && 'translateX(12px)',
transition: 'transform 0.2s',
}}
>
{label}
<LabelBadge value={count} />
</Box>
);
};

const LabelBadge = ({ value }: { value: number }) => (
<Box
sx={{
backgroundColor: 'var(--accent-150)',
borderRadius: 'var(--radius-s)',
width: '24px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '24px',
fontSize: '14px',
opacity: value === 0 ? 0 : 1,
transition: 'opacity 0.2s',
}}
>
{value.toString()}
</Box>
);

export default TabLabel;
150 changes: 94 additions & 56 deletions src/features/meetings/my_assignments/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Stack } from '@mui/material';
import { useAppTranslation } from '@hooks/index';
import { useAppTranslation, useBreakpoints } from '@hooks/index';
import { IconInfo } from '@components/icons';
import { DisplayRange } from './indextypes';
import useMyAssignments from './useAssignments';
Expand All @@ -10,6 +10,8 @@ import MonthContainer from './month_container';
import Select from '@components/select';
import Typography from '@components/typography';
import NoAssigmentsImg from '@assets/img/illustration_no_assigments.svg?component';
import Tabs from '@components/tabs';
import TabLabel from '@components/tabs/tab_label';

const MyAssignments = () => {
const { t } = useAppTranslation();
Expand All @@ -21,9 +23,93 @@ const MyAssignments = () => {
isSetup,
displayRange,
handleRangeChange,
personAssignments,
personAssignments: { ownAssignments, delegateAssignments },
} = useMyAssignments();

const { tabletDown } = useBreakpoints();

const actionComponent = (
<Box
sx={{
width: tabletDown ? '100%' : '240px',
}}
>
<Select
label={t('tr_display')}
value={displayRange}
onChange={(e) => {
handleRangeChange(+e.target.value);
}}
>
<MenuItem value={DisplayRange.MONTHS_3}>
<Typography>{t('tr_next3MonthsLabel')}</Typography>
</MenuItem>
<MenuItem value={DisplayRange.MONTHS_6}>
<Typography>{t('tr_next6MonthsLabel')}</Typography>
</MenuItem>
<MenuItem value={DisplayRange.MONTHS_12}>
<Typography>{t('tr_next12MonthsLabel')}</Typography>
</MenuItem>
</Select>
</Box>
);

const renderAssignments = (assignments) => (
<Box
sx={{
height: '75vh',
overflowY: 'scroll',
'&::-webkit-scrollbar': {
width: '4px',
},
}}
>
{assignments.length === 0 ? (
<Box
sx={{
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '24px',
}}
>
<NoAssigmentsImg viewBox="0 0 128 128" />
<Stack spacing="8px">
<Typography className="h2">{t('tr_noAssignmentsYet')}</Typography>
<Typography
color="var(--grey-400)"
sx={{
maxWidth: '350px',
}}
>
{t('tr_noAssignmentsYetDesc')}
</Typography>
</Stack>
</Box>
) : (
<Stack spacing={2.3}>
{assignments.map((month) => (
<MonthContainer key={month.month} monthData={month} />
))}
</Stack>
)}
</Box>
);

const tabs = [
{
label: <TabLabel count={ownAssignments.total} label={t('tr_myOwn')} />,
Component: renderAssignments(ownAssignments.byDate),
},
{
label: (
<TabLabel count={delegateAssignments.total} label={t('tr_delegated')} />
),
Component: renderAssignments(delegateAssignments.byDate),
},
];

return (
<Drawer
anchor={'left'}
Expand All @@ -45,64 +131,16 @@ const MyAssignments = () => {

{!isSetup && (
<>
<Select
label={t('tr_display')}
value={displayRange}
onChange={(e) => handleRangeChange(+e.target.value)}
>
<MenuItem value={DisplayRange.MONTHS_3}>
<Typography>{t('tr_next3MonthsLabel')}</Typography>
</MenuItem>
<MenuItem value={DisplayRange.MONTHS_6}>
<Typography>{t('tr_next6MonthsLabel')}</Typography>
</MenuItem>
<MenuItem value={DisplayRange.MONTHS_12}>
<Typography>{t('tr_next12MonthsLabel')}</Typography>
</MenuItem>
</Select>

<Box
sx={{
marginTop: '16px',
height: '100%',
overflow: 'auto',
'&::-webkit-scrollbar': {
width: '4px',
},
display: 'flex',
gap: '16px',
flexWrap: 'wrap',
justifyContent: 'space-between',
flexDirection: tabletDown ? 'column' : 'row',
}}
>
{personAssignments.length === 0 && (
<Box
sx={{
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '24px',
}}
>
<NoAssigmentsImg viewBox="0 0 128 128" />
<Stack spacing="8px">
<Typography className="h2">
{t('tr_noAssignmentsYet')}
</Typography>
<Typography
color="var(--grey-400)"
sx={{
maxWidth: '350px',
}}
>
{t('tr_noAssignmentsYetDesc')}
</Typography>
</Stack>
</Box>
)}

<Stack spacing={2.3}>
{personAssignments.map((month) => (
<MonthContainer key={month.month} monthData={month} />
))}
</Stack>
<Tabs tabs={tabs} actionComponent={actionComponent} />
</Box>
</>
)}
Expand Down
Loading

0 comments on commit 08c32a8

Please sign in to comment.