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 27, 2025
1 parent 55dafa1 commit e45c38e
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 138 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
2 changes: 1 addition & 1 deletion src/components/tab_label_with_badge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const LabelBadge = ({
alignItems: 'center',
height: '24px',
fontSize: '14px',
opacity: 1,
opacity: value === 0 ? 0 : 1,
transition: 'opacity 0.2s',
}}
>
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
162 changes: 99 additions & 63 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/tab_label_with_badge';

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: '70vh',
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 @@ -44,67 +130,17 @@ 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',
},
}}
>
{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>
</Box>
</>
<Box
sx={{
display: 'flex',
gap: '16px',
flexWrap: 'wrap',
justifyContent: 'space-between',
flexDirection: tabletDown ? 'column' : 'row',
}}
>
<Tabs tabs={tabs} actionComponent={actionComponent} />
</Box>
)}
</Drawer>
);
Expand Down
91 changes: 57 additions & 34 deletions src/features/meetings/my_assignments/useAssignments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,22 @@ const useMyAssignments = () => {
const now = getWeekDate();
const maxDate = addWeeks(now, displayRange);

let personAssignments = assignmentsHistory.filter(
(record) =>
(record.assignment.person === userUID ||
delegateMembers.includes(record.assignment.person)) &&
formatDate(new Date(record.weekOf), 'yyyy/MM/dd') >=
formatDate(now, 'yyyy/MM/dd') &&
formatDate(new Date(record.weekOf), 'yyyy/MM/dd') <=
formatDate(maxDate, 'yyyy/MM/dd')
);

if (exactDate) {
personAssignments = personAssignments.map((record) => {
const filterAssignments = (uid: string) => {
return assignmentsHistory.filter(
(record) =>
record.assignment.person === uid &&
formatDate(new Date(record.weekOf), 'yyyy/MM/dd') >=
formatDate(now, 'yyyy/MM/dd') &&
formatDate(new Date(record.weekOf), 'yyyy/MM/dd') <=
formatDate(maxDate, 'yyyy/MM/dd')
);
};

let ownAssignments = filterAssignments(userUID);
let delegateAssignments = delegateMembers.flatMap(filterAssignments);

const formatAssignments = (assignments: AssignmentHistoryType[]) => {
return assignments.map((record) => {
const isMidweek = record.assignment.key.startsWith('MM_');
const isWeekend = record.assignment.key.startsWith('WM_');

Expand Down Expand Up @@ -88,30 +92,49 @@ const useMyAssignments = () => {
weekOfFormatted: formatDate(meetingDate, shortDateFormat),
};
});
};

if (exactDate) {
ownAssignments = formatAssignments(ownAssignments);
delegateAssignments = formatAssignments(delegateAssignments);
}

const groupedByMonth = personAssignments.reduce<
Record<string, AssignmentHistoryType[]>
>((acc, obj) => {
const [year, month] = obj.weekOf.split('/').slice(0, 2);
const key = `${year}/${month}`;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});

const sortedGroups = Object.keys(groupedByMonth)
.sort()
.map((key) => ({
month: key,
children: groupedByMonth[key].sort((a, b) =>
a.weekOf.localeCompare(b.weekOf)
),
}));

return sortedGroups;
const groupAndSortAssignments = (assignments: AssignmentHistoryType[]) => {
const groupedByMonth = assignments.reduce<
Record<string, AssignmentHistoryType[]>
>((acc, obj) => {
const [year, month] = obj.weekOf.split('/').slice(0, 2);
const key = `${year}/${month}`;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});

return Object.keys(groupedByMonth)
.sort()
.map((key) => ({
month: key,
children: groupedByMonth[key].toSorted((a, b) =>
a.weekOf.localeCompare(b.weekOf)
),
}));
};

const sortedOwnAssignments = {
byDate: groupAndSortAssignments(ownAssignments),
total: ownAssignments.length,
};
const sortedDelegateAssignments = {
byDate: groupAndSortAssignments(delegateAssignments),
total: delegateAssignments.length,
};

return {
ownAssignments: sortedOwnAssignments,
delegateAssignments: sortedDelegateAssignments,
};
}, [
assignmentsHistory,
displayRange,
Expand Down
Loading

0 comments on commit e45c38e

Please sign in to comment.