Skip to content

Commit

Permalink
feat: implemented filtering users by coding language
Browse files Browse the repository at this point in the history
  • Loading branch information
Ekep-Obasi committed Dec 6, 2023
1 parent 26bf344 commit 04d9cd6
Show file tree
Hide file tree
Showing 3 changed files with 299 additions and 3 deletions.
5 changes: 5 additions & 0 deletions frontend/app/src/people/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ export interface BountyHeaderProps {
checkboxIdToSelectedMapLanguage: any;
}

export interface PeopleHeaderProps {
onChangeLanguage: (number) => void;
checkboxIdToSelectedMapLanguage: any;
}

export interface DeleteTicketModalProps {
closeModal: () => void;
confirmDelete: () => void;
Expand Down
50 changes: 47 additions & 3 deletions frontend/app/src/people/main/Body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { useEffect, useState } from 'react';
import { useHistory } from 'react-router';
import styled from 'styled-components';
import { EuiLoadingSpinner, EuiGlobalToastList } from '@elastic/eui';
import PeopleHeader from 'people/widgetViews/PeopleHeader';
import { Person as PersonType } from 'store/main';
import { SearchTextInput } from '../../components/common';
import { colors } from '../../config/colors';
import { useFuse, useIsMobile, usePageScroll, useScreenWidth } from '../../hooks';
Expand All @@ -24,6 +26,7 @@ const Body = styled.div<{ isMobile: boolean }>`
& > .header {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 10px 0;
}
& > .content {
Expand All @@ -50,6 +53,8 @@ function BodyComponent() {
const [loading, setLoading] = useState(true);
const screenWidth = useScreenWidth();
const [openStartUpModel, setOpenStartUpModel] = useState<boolean>(false);
const [checkboxIdToSelectedMapLanguage, setCheckboxIdToSelectedMapLanguage] = useState({});
const [filterResult, setFilterResult] = useState<PersonType[]>(main.people);
const closeModal = () => setOpenStartUpModel(false);
const { peoplePageNumber } = ui;
const history = useHistory();
Expand All @@ -71,6 +76,16 @@ function BodyComponent() {
const loadBackwardFunc = () => loadMore(-1);
const { loadingBottom, handleScroll } = usePageScroll(loadForwardFunc, loadBackwardFunc);

const onChangeLanguage = (optionId: any) => {
const newCheckboxIdToSelectedMapLanguage = {
...checkboxIdToSelectedMapLanguage,
...{
[optionId]: !checkboxIdToSelectedMapLanguage[optionId],
},
};
setCheckboxIdToSelectedMapLanguage(newCheckboxIdToSelectedMapLanguage);
};

const toastsEl = (
<EuiGlobalToastList
toasts={ui.toasts}
Expand All @@ -85,6 +100,30 @@ function BodyComponent() {
}
}, [main, ui.meInfo]);

interface CodingLanguage {
[language: string]: boolean;
}

const filterByCodingLanguage = (users: PersonType[], codingLanguages: CodingLanguage) => {
const requiredLanguages = Object.keys(codingLanguages).filter(
(key: string) => codingLanguages[key],
);

return users.filter((user: PersonType) => {
const userCodingLanguages = (user.extras.coding_languages ?? []).map(
(t: { [key: string]: string }) => t.value,
);
return requiredLanguages?.every((requiredLanguage: string) =>
userCodingLanguages.includes(requiredLanguage),
);
});
};

useEffect(() => {
setFilterResult(filterByCodingLanguage(main.people, checkboxIdToSelectedMapLanguage));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [checkboxIdToSelectedMapLanguage]);

// update search
useEffect(() => {
(async () => {
Expand Down Expand Up @@ -116,6 +155,11 @@ function BodyComponent() {
}}
>
<div className="header">
<PeopleHeader
onChangeLanguage={onChangeLanguage}
checkboxIdToSelectedMapLanguage={checkboxIdToSelectedMapLanguage}
/>

<SearchTextInput
small
name="search"
Expand All @@ -126,15 +170,15 @@ function BodyComponent() {
width: isMobile ? '95vw' : 240,
height: 40,
border: `1px solid ${color.grayish.G600}`,
background: color.grayish.G600
background: color.grayish.G600,
}}
onChange={(e: any) => {
ui.setSearchText(e);
}}
/>
</div>
<div className="content">
{(people ?? []).map((t: any) => (
{(ui.searchText ? people : filterResult).map((t: any) => (
<Person
{...t}
key={t.owner_pubkey}
Expand All @@ -144,7 +188,7 @@ function BodyComponent() {
select={selectPerson}
/>
))}
{!people.length && <NoResults />}
{!(ui.searchText ? people : filterResult)?.length && <NoResults />}
<PageLoadSpinner noAnimate show={loadingBottom} />
</div>

Expand Down
247 changes: 247 additions & 0 deletions frontend/app/src/people/widgetViews/PeopleHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import styled from 'styled-components';
import { PeopleHeaderProps } from 'people/interfaces';
import { observer } from 'mobx-react-lite';
import React, { useState, useEffect } from 'react';
import { EuiCheckboxGroup, EuiPopover, EuiText } from '@elastic/eui';
import MaterialIcon from '@material/react-material-icon';
import { colors } from 'config';
import { filterCount } from 'people/utils/ExtraFunctions';
import { GetValue, coding_languages } from '../utils/languageLabelStyle';

interface styledProps {
color?: any;
}

const FilterWrapper = styled.div`
display: flex;
align-items: center;
gap: 4px;
`;

const FilterTrigger = styled.div<styledProps>`
width: 78px;
height: 48px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-left: 19px;
cursor: pointer;
user-select: none;
.filterImageContainer {
display: flex;
justify-content: center;
align-items: center;
height: 48px;
width: 36px;
.materialIconImage {
color: ${(p: any) => p.color && p.color.grayish.G200};
cursor: pointer;
font-size: 18px;
margin-top: 4px;
}
}
.filterText {
font-family: 'Barlow';
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 19px;
display: flex;
align-items: center;
color: ${(p: any) => p.color && p.color.grayish.G200};
}
&:hover {
.filterImageContainer {
.materialIconImage {
color: ${(p: any) => p.color && p.color.grayish.G50} !important;
cursor: pointer;
font-size: 18px;
margin-top: 4px;
}
}
.filterText {
color: ${(p: any) => p.color && p.color.grayish.G50};
}
}
&:active {
.filterImageContainer {
.materialIconImage {
color: ${(p: any) => p.color && p.color.grayish.G10} !important;
cursor: pointer;
font-size: 18px;
margin-top: 4px;
}
}
.filterText {
color: ${(p: any) => p.color && p.color.grayish.G10};
}
}
`;

const FilterCount = styled.div<styledProps>`
height: 20px;
width: 20px;
border-radius: 50%;
margin-left: 4px;
display: flex;
justify-content: center;
align-items: center;
margin-top: -5px;
background: ${(p: any) => p?.color && p.color.blue1};
.filterCountText {
font-family: 'Barlow';
font-style: normal;
font-weight: 500;
font-size: 13px;
display: flex;
align-items: center;
text-align: center;
color: ${(p: any) => p.color && p.color.pureWhite};
}
`;

const PopOverBox = styled.div<styledProps>`
display: flex;
flex-direction: column;
max-height: 304px;
padding: 15px 0px 20px 21px;
.rightBoxHeading {
font-family: 'Barlow';
font-style: normal;
font-weight: 700;
font-size: 12px;
line-height: 32px;
text-transform: uppercase;
color: ${(p: any) => p.color && p.color.grayish.G100};
}
`;

const EuiPopOverCheckboxWrapper = styled.div<styledProps>`
min-width: 285px;
max-width: 285px;
height: 240px;
user-select: none;
&.CheckboxOuter > div {
height: 100%;
display: grid;
grid-template-columns: 1fr 1fr;
justify-content: center;
.euiCheckboxGroup__item {
.euiCheckbox__square {
top: 5px;
border: 1px solid ${(p: any) => p?.color && p?.color?.grayish.G500};
border-radius: 2px;
}
.euiCheckbox__input + .euiCheckbox__square {
background: ${(p: any) => p?.color && p?.color?.pureWhite} no-repeat center;
}
.euiCheckbox__input:checked + .euiCheckbox__square {
border: 1px solid ${(p: any) => p?.color && p?.color?.blue1};
background: ${(p: any) => p?.color && p?.color?.blue1} no-repeat center;
background-image: url('static/checkboxImage.svg');
}
.euiCheckbox__label {
font-family: 'Barlow';
font-style: normal;
font-weight: 500;
font-size: 13px;
line-height: 16px;
color: ${(p: any) => p?.color && p?.color?.grayish.G50};
&:hover {
color: ${(p: any) => p?.color && p?.color?.grayish.G05};
}
}
input.euiCheckbox__input:checked ~ label {
color: ${(p: any) => p?.color && p?.color?.blue1};
}
}
}
`;

const Coding_Languages = GetValue(coding_languages);

const PeopleHeader = ({ onChangeLanguage, checkboxIdToSelectedMapLanguage }: PeopleHeaderProps) => {
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
const [filterCountNumber, setFilterCountNumber] = useState<number>(0);
const onToggleButton = () => setIsPopoverOpen((prev: boolean) => !prev);
const closePopover = () => setIsPopoverOpen(false);
const color = colors['light'];

useEffect(() => {
setFilterCountNumber(filterCount(checkboxIdToSelectedMapLanguage));
}, [checkboxIdToSelectedMapLanguage]);

const panelStyles = {
border: 'none',
boxShadow: `0px 1px 20px ${color.black90}`,
background: `${color.pureWhite}`,
borderRadius: '6px',
minWidth: '300px',
minHeight: '304px',
marginTop: '0px',
marginLeft: '20px',
};

return (
<FilterWrapper>
<EuiPopover
button={
<FilterTrigger onClick={onToggleButton} color={color}>
<div className="filterImageContainer">
<MaterialIcon
className="materialIconImage"
icon="tune"
style={{
color: isPopoverOpen ? color.grayish.G10 : '',
}}
/>
</div>
<EuiText
className="filterText"
style={{
color: isPopoverOpen ? color.grayish.G10 : '',
}}
>
Filter
</EuiText>
</FilterTrigger>
}
panelStyle={panelStyles}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelClassName="yourClassNameHere"
panelPaddingSize="none"
anchorPosition="downLeft"
>
<div
style={{
display: 'flex',
flexDirection: 'row',
}}
>
<PopOverBox color={color}>
<EuiText className="rightBoxHeading">Skills</EuiText>
<EuiPopOverCheckboxWrapper className="CheckboxOuter" color={color}>
<EuiCheckboxGroup
options={Coding_Languages}
idToSelectedMap={checkboxIdToSelectedMapLanguage}
onChange={(id: any) => {
onChangeLanguage(id);
}}
/>
</EuiPopOverCheckboxWrapper>
</PopOverBox>
</div>
</EuiPopover>
{filterCountNumber > 0 && (
<FilterCount color={color}>
<EuiText className="filterCountText">{filterCountNumber}</EuiText>
</FilterCount>
)}
</FilterWrapper>
);
};

export default observer(PeopleHeader);

0 comments on commit 04d9cd6

Please sign in to comment.