Skip to content

Commit

Permalink
Merge pull request #7 from CinCoders/feature/CSD-2
Browse files Browse the repository at this point in the history
Feature/csd 2
  • Loading branch information
dcruzb authored Aug 1, 2023
2 parents 6f8b11c + c9a5f90 commit c594a16
Show file tree
Hide file tree
Showing 9 changed files with 479 additions and 41 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"rules": {
"react/jsx-wrap-multilines": ["error", { "prop": "ignore" }],
"react/jsx-props-no-spreading": "off",
"react/no-array-index-key": "off",
"object-curly-newline": ["off"],
"implicit-arrow-linebreak": ["off"],
"max-len": ["error", 120],
Expand Down
331 changes: 296 additions & 35 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"@cincoders/cinnamon": "^1.0.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.1",
"@mui/lab": "^5.0.0-alpha.137",
"@mui/material": "^5.13.5",
"papaparse": "^5.4.1",
"react": "^18.1.0",
Expand Down
4 changes: 3 additions & 1 deletion src/components/Conference/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface DeadlineProps {
conference: string;
website: string;
conferenceDetail: string;
greatArea: string;
area: string;
conferenceDates: string;
location: string;
Expand All @@ -18,6 +19,7 @@ export function Conference({
conference,
website,
conferenceDetail,
greatArea,
area,
conferenceDates,
location,
Expand Down Expand Up @@ -91,7 +93,7 @@ export function Conference({
{conferenceDetail}
</Typography>
<Typography variant='body2' fontWeight='bold'>
{area}
{`${greatArea} - ${area}`}
</Typography>
</Stack>
</Stack>
Expand Down
13 changes: 11 additions & 2 deletions src/components/Filter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,29 @@ import { Conference, DeadlineProps } from '../Conference';

interface FilterProps {
deadlines: DeadlineProps[];
checkedValues: string[];
}

function FilterPage({ deadlines }: FilterProps) {
function FilterPage({ deadlines, checkedValues }: FilterProps) {
const [filterText, setFilterText] = useState('');

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setFilterText(event.target.value);
};

const filteredDeadlines: DeadlineProps[] = deadlines.filter(deadline =>
const getCheckedAreaNames = (): string[] => checkedValues.map(nodeId => nodeId.split('_')[1]);

const filteredDeadlinesByText: DeadlineProps[] = deadlines.filter(deadline =>
Object.values(deadline).some(
value => typeof value === 'string' && value.toLowerCase().includes(filterText.toLowerCase()),
),
);

const filteredDeadlines: DeadlineProps[] =
checkedValues.length > 0
? filteredDeadlinesByText.filter(deadline => getCheckedAreaNames().includes(deadline.area))
: filteredDeadlinesByText;

return (
<Box width='100%' height='100%' display='flex' flexDirection='column' flexGrow='1'>
<TextField
Expand Down
128 changes: 128 additions & 0 deletions src/components/FilterByArea/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React from 'react';
import { Typography } from '@mui/material';
import Checkbox from '@mui/material/Checkbox';
import TreeView from '@mui/lab/TreeView';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import TreeItem from '@mui/lab/TreeItem';
import { StyledTreeItemLabel } from './styles';

interface FilterByAreaDeadlineProps {
greatArea: string;
area: string;
}
interface FilterProps {
deadlines: FilterByAreaDeadlineProps[];
checkedValues: string[];
onCheckedChange: (checked: string[]) => void;
}

function FilterByArea({ deadlines, checkedValues, onCheckedChange }: FilterProps) {
const createTreeData = (deadlinesValue: FilterByAreaDeadlineProps[]) => {
const treeData = new Map<string, string[]>();
deadlinesValue.forEach(deadline => {
const { greatArea, area } = deadline;
if (!treeData.has(greatArea)) {
treeData.set(greatArea, []);
}
if (!treeData.get(greatArea)?.includes(area)) {
treeData.get(greatArea)?.push(area);
}
});
return treeData;
};
const treeData = createTreeData(deadlines);

const toggleChecked = (currentCheckedValues: string[], nodeId: string): string[] => {
const newCheckedValues = [...currentCheckedValues];
const currentIndex = currentCheckedValues.indexOf(nodeId);
if (currentIndex === -1) {
newCheckedValues.push(nodeId);
} else {
newCheckedValues.splice(currentIndex, 1);
}

if (treeData.has(nodeId)) {
treeData.get(nodeId)?.forEach(subarea => {
const childNodeId = `${nodeId}_${subarea}`;
if (!newCheckedValues.includes(childNodeId) && newCheckedValues.includes(nodeId)) {
newCheckedValues.push(childNodeId);
} else if (newCheckedValues.includes(childNodeId) && !newCheckedValues.includes(nodeId)) {
newCheckedValues.splice(newCheckedValues.indexOf(childNodeId), 1);
}
newCheckedValues.concat(toggleChecked(newCheckedValues, childNodeId));
});
}

const parentIndex = nodeId.lastIndexOf('_');
if (parentIndex !== -1) {
const parent = nodeId.substring(0, parentIndex);
const siblings = treeData.get(parent);
const siblingsChecked = siblings?.every(sibling => newCheckedValues.includes(sibling));

if (siblingsChecked && !newCheckedValues.includes(parent)) {
newCheckedValues.push(parent);
} else if (!siblingsChecked && newCheckedValues.includes(parent)) {
newCheckedValues.splice(newCheckedValues.indexOf(parent), 1);
}
}
return newCheckedValues;
};

const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>, nodeId: string) => {
const newCheckedValues = toggleChecked(checkedValues, nodeId);
onCheckedChange(newCheckedValues);
};

return (
<div>
<Typography variant='h6'>Filter by Areas</Typography>
<TreeView
aria-label='area-navigation system'
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
{treeData.size > 0 &&
Array.from(treeData)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([greatArea, areas]) => (
<TreeItem
key={greatArea}
nodeId={greatArea}
label={
<StyledTreeItemLabel>
<Checkbox
checked={checkedValues.includes(greatArea)}
onChange={e => handleCheckboxChange(e, greatArea)}
/>
{greatArea}
</StyledTreeItemLabel>
}
>
{areas.map(area => {
const childNodeId = `${greatArea}_${area}`;
return (
<TreeItem
key={childNodeId}
nodeId={childNodeId}
label={
<StyledTreeItemLabel>
<Checkbox
size='small'
checked={checkedValues.includes(childNodeId)}
onChange={e => handleCheckboxChange(e, childNodeId)}
/>
{area}
</StyledTreeItemLabel>
}
/>
);
})}
</TreeItem>
))}
</TreeView>
</div>
);
}

export default FilterByArea;
5 changes: 5 additions & 0 deletions src/components/FilterByArea/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { styled } from '@mui/material/styles';

export const StyledTreeItemLabel = styled('div')({
fontSize: '14px',
});
22 changes: 19 additions & 3 deletions src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Box, CircularProgress, Link, Typography } from '@mui/material';
import Papa from 'papaparse';
import { useEffect, useState } from 'react';
import { DeadlineProps } from '../../components/Conference';
import FilterPage from '../../components/Filter';
import FilterByArea from '../../components/FilterByArea';
import { FilterContainer, FilterByAreaContainer } from './styles';
import { DeadlineProps } from '../../components/Conference';

function compare(a: DeadlineProps, b: DeadlineProps) {
if (a.submissionDeadline < b.submissionDeadline) {
Expand All @@ -23,6 +25,7 @@ function compare(a: DeadlineProps, b: DeadlineProps) {
function Home() {
const [deadlines, setDeadlines] = useState<DeadlineProps[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [checkedValues, setCheckedValues] = useState<string[]>([]);

useEffect(() => {
const sheetUrl =
Expand All @@ -33,11 +36,12 @@ function Home() {
complete(results) {
const rawDeadlines: string[][] = results.data as string[][];
const parsedDeadlines = rawDeadlines.map((deadline: string[]) => ({
greatArea: deadline[rawDeadlines[0].indexOf('GreatArea')],
area: deadline[rawDeadlines[0].indexOf('Area')],
deadlineId: deadline[rawDeadlines[0].indexOf('DeadlineId')],
conference: deadline[rawDeadlines[0].indexOf('Conference')],
website: deadline[rawDeadlines[0].indexOf('WebSite')],
conferenceDetail: deadline[rawDeadlines[0].indexOf('ConferenceDetail')],
area: `${deadline[rawDeadlines[0].indexOf('GreatArea')]} - ${deadline[rawDeadlines[0].indexOf('Area')]}`,
conferenceDates: deadline[rawDeadlines[0].indexOf('ConferenceDates')],
location: deadline[rawDeadlines[0].indexOf('Location')],
submissionDeadline: new Date(deadline[rawDeadlines[0].indexOf('DeadlineISO')]),
Expand All @@ -52,6 +56,10 @@ function Home() {
});
}, []);

const handleCheckedChange = (checkedChangedValues: string[]) => {
setCheckedValues(checkedChangedValues);
};

return (
<>
<Typography variant='h6'>
Expand All @@ -65,7 +73,15 @@ function Home() {
<CircularProgress />
</Box>
)}
{!loading && <FilterPage deadlines={deadlines} />}
{!loading && (
<FilterContainer>
<FilterByAreaContainer>
<FilterByArea deadlines={deadlines} checkedValues={checkedValues} onCheckedChange={handleCheckedChange} />
</FilterByAreaContainer>

<FilterPage deadlines={deadlines} checkedValues={checkedValues} />
</FilterContainer>
)}
</>
);
}
Expand Down
14 changes: 14 additions & 0 deletions src/pages/Home/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { styled } from '@mui/material';

export const FilterContainer = styled('div')({
display: 'flex',
flexDirection: 'row',
flexGrow: 1,
marginTop: '20px',
width: '100%',
});

export const FilterByAreaContainer = styled('div')({
width: '20%',
marginRight: '20px',
});

0 comments on commit c594a16

Please sign in to comment.