Skip to content

Commit c332731

Browse files
authored
Merge pull request #5327 from nursoltan-s/feature/poc-recommender-nursoltan-s
final fixes for recommender POC.
2 parents 295ce04 + ef32202 commit c332731

File tree

11 files changed

+141
-42
lines changed

11 files changed

+141
-42
lines changed

__tests__/shared/components/challenge-listing/Filters/__snapshots__/FiltersPanel.jsx.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ exports[`Matches shallow shapshot 2`] = `
184184
</div>
185185
</div>
186186
</div>
187+
<hr />
187188
<div
188189
className="src-shared-components-challenge-listing-Filters-FiltersPanel-___style__buttons___2r3xW"
189190
>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import PT from 'prop-types';
2+
import React from 'react';
3+
4+
import './style.scss';
5+
6+
export default function MatchScore({ score }) {
7+
return (
8+
<span styleName="match-score">
9+
{score}% match
10+
</span>
11+
);
12+
}
13+
14+
MatchScore.defaultProps = {
15+
score: 0,
16+
};
17+
18+
MatchScore.propTypes = {
19+
score: PT.number,
20+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@import "~styles/mixins";
2+
3+
.match-score {
4+
background-color: $tc-light-blue-30;
5+
color: $tc-dark-blue-100;
6+
font-size: 10px;
7+
line-height: 12px;
8+
padding: 3px;
9+
border-radius: 2px;
10+
margin-left: 5px;
11+
margin-top: 1px;
12+
}

src/shared/components/challenge-listing/ChallengeCard/index.jsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import Tags from '../Tags';
1313

1414
import ChallengeStatus from './Status';
1515
import TrackAbbreviationTooltip from '../Tooltips/TrackAbbreviationTooltip';
16+
import MatchScore from './MatchScore';
17+
import { calculateScore } from '../../../utils/challenge-listing/helper';
1618
import './style.scss';
1719

1820
/* TODO: Note that this component uses a dirty trick to cheat linter and to be
@@ -67,13 +69,19 @@ function ChallengeCard({
6769
</div>
6870

6971
<div styleName={isRegistrationOpen ? 'challenge-details with-register-button' : 'challenge-details'}>
70-
<Link
71-
onClick={() => selectChallengeDetailsTab(DETAIL_TABS.DETAILS)}
72-
to={challengeDetailLink}
73-
styleName="challenge-title"
74-
openNewTab={openChallengesInNewTabs}
75-
><p>{challenge.name}</p>
76-
</Link>
72+
<div styleName="challenge-detail-header">
73+
<Link
74+
onClick={() => selectChallengeDetailsTab(DETAIL_TABS.DETAILS)}
75+
to={challengeDetailLink}
76+
styleName="challenge-title"
77+
openNewTab={openChallengesInNewTabs}
78+
><p>{challenge.name}</p>
79+
</Link>
80+
{
81+
challenge.matchScore
82+
&& <MatchScore score={calculateScore(challenge.matchScore)} />
83+
}
84+
</div>
7785
<div styleName="details-footer">
7886
<span styleName="date">
7987
{challenge.status === 'Active' ? 'Ends ' : 'Ended '}

src/shared/components/challenge-listing/ChallengeCard/style.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ $challenge-radius-4: $corner-radius * 2;
106106
display: inline-block;
107107
}
108108
}
109+
110+
.challenge-detail-header {
111+
display: flex;
112+
}
109113
}
110114
// date and technologies
111115
.details-footer {

src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx

Lines changed: 64 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/* eslint-disable jsx-a11y/label-has-for */
2323

2424
import _ from 'lodash';
25-
import React from 'react';
25+
import React, { useState, useEffect } from 'react';
2626
import PT from 'prop-types';
2727
import Select from 'components/Select';
2828
import DateRangePicker from 'components/DateRangePicker';
@@ -250,8 +250,30 @@ export default function FiltersPanel({
250250
const past = isPastBucket(activeBucket);
251251
const disableClearFilterButtons = isFilterEmpty(filterState, past ? 'past' : '', activeBucket);
252252

253-
const availableTypes = activeBucket === 'openForRegistration'
254-
? validTypes : validTypes.filter(item => item.abbreviation !== 'REC');
253+
const availableTypes = validTypes.filter(item => item.abbreviation !== 'REC');
254+
const isRecommendedChallengesVisible = activeBucket === 'openForRegistration';
255+
const [recommendedToggle, setRecommendedToggle] = useState(false);
256+
257+
useEffect(() => {
258+
if (recommendedToggle) {
259+
const types = _.union(filterState.types, ['REC']);
260+
setFilterState({ ..._.clone(filterState), types });
261+
}
262+
}, []);
263+
264+
const onSwitchRecommendedChallenge = (on) => {
265+
const { types } = filterState;
266+
types.push('REC');
267+
setRecommendedToggle(on);
268+
269+
if (on) {
270+
setSort('openForRegistration', 'updatedBy');
271+
setFilterState({ ..._.clone(filterState), types });
272+
} else {
273+
setFilterState({ ..._.clone(filterState), types: ['TSK', 'CH', 'F2F'] });
274+
setSort('openForRegistration', 'startDate');
275+
}
276+
};
255277

256278
const handleTypeChange = (option, e) => {
257279
let { types } = filterState;
@@ -261,20 +283,14 @@ export default function FiltersPanel({
261283
types = types.filter(type => type !== option.value);
262284
}
263285

264-
if (option.label === 'Recommended') {
265-
types = types.filter(type => type === 'REC');
266-
if (!e.target.checked) {
267-
setFilterState({ ..._.clone(filterState), types: ['TSK', 'CH', 'F2F'] });
268-
setSort('openForRegistration', 'startDate');
269-
} else {
270-
setSort('openForRegistration', 'updatedBy');
271-
setFilterState({ ..._.clone(filterState), types });
272-
}
286+
if (recommendedToggle) {
287+
types = [...types, 'REC'];
273288
} else {
274289
types = types.filter(type => type !== 'REC');
275-
setFilterState({ ..._.clone(filterState), types });
276-
setSort('openForRegistration', 'startDate');
277290
}
291+
292+
setFilterState({ ..._.clone(filterState), types });
293+
setSort('openForRegistration', 'startDate');
278294
};
279295

280296
const recommendedCheckboxTip = (
@@ -462,24 +478,7 @@ export default function FiltersPanel({
462478
checked={filterState.types.includes(option.value)}
463479
onChange={e => handleTypeChange(option, e)}
464480
/>
465-
{
466-
option.label === 'Recommended'
467-
? (
468-
<label styleName="checkbox-label" htmlFor={option.label}>
469-
<Tooltip
470-
id="recommended-tip"
471-
content={recommendedCheckboxTip}
472-
className={style['tooltip-overlay']}
473-
trigger={['hover', 'focus']}
474-
>
475-
{option.label}
476-
</Tooltip>
477-
478-
</label>
479-
)
480-
481-
: <label styleName="checkbox-label" htmlFor={option.label}>{option.label}</label>
482-
}
481+
<label styleName="checkbox-label" htmlFor={option.label}>{option.label}</label>
483482
</span>
484483
))
485484
}
@@ -579,8 +578,41 @@ export default function FiltersPanel({
579578
</div>
580579
)
581580
}
581+
582+
{
583+
isRecommendedChallengesVisible
584+
&& (
585+
<div styleName="filter-row recommended-challenges-filter">
586+
<span
587+
styleName="filter-switch-with-label"
588+
aria-label={`Recommended challenge toggle button pressed ${recommendedToggle ? 'On' : 'Off'}`}
589+
role="switch"
590+
aria-checked={recommendedToggle}
591+
>
592+
<SwitchWithLabel
593+
enabled={recommendedToggle}
594+
labelAfter="Recommended Challenges"
595+
onSwitch={onSwitchRecommendedChallenge}
596+
/>
597+
</span>
598+
599+
<div styleName="recommended-challenge-tooltip">
600+
<Tooltip
601+
id="recommended-tip"
602+
content={recommendedCheckboxTip}
603+
className={style['tooltip-overlay']}
604+
trigger={['hover', 'focus']}
605+
>
606+
<i className="fa fa-info-cirle" aria-hidden="true" />
607+
</Tooltip>
608+
</div>
609+
</div>
610+
)
611+
}
582612
</div>
583613

614+
<hr />
615+
584616
<div styleName="buttons">
585617
<Button
586618
composeContextTheme={COMPOSE.SOFT}

src/shared/components/challenge-listing/Filters/FiltersPanel/style.scss

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@import "../../../../../styles/awesome.css";
12
@import '~styles/mixins';
23

34
.uppercase {
@@ -285,7 +286,7 @@
285286
display: inline-block;
286287
line-height: 30px;
287288
flex: 1 0 auto;
288-
min-width: calc(11% - 10px);
289+
min-width: calc(33% - 10px);
289290

290291
&:not(:last-child) {
291292
padding-right: 10px;
@@ -422,6 +423,15 @@
422423
}
423424
}
424425
}
426+
427+
.recommended-challenges-filter {
428+
display: flex;
429+
margin-bottom: 25px;
430+
431+
.recommended-challenge-tooltip {
432+
margin: 3px 0 0 3px;
433+
}
434+
}
425435
}
426436

427437
.tctooltiptext {

src/shared/reducers/challenge-listing/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ function onGetRecommendedChallengesDone(state, { error, payload }) {
660660
allRecommendedChallengesLoaded: challenges.length >= payload.meta.allRecommendedChallengesCount,
661661
meta: {
662662
...state.meta,
663-
allRecommendedChallengesCount: payload.meta.allRecommendedChallengesCount,
663+
openChallengesCount: payload.meta.allRecommendedChallengesCount,
664664
},
665665
};
666666
}

src/shared/utils/challenge-listing/buckets.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export function isPastBucket(bucket) {
275275
* @param {Object} filterState current filter state
276276
*/
277277
export function isRecommendedChallengeType(bucket, filterState) {
278-
return bucket === 'openForRegistration' && filterState.types.length === 1 && filterState.types[0] === 'REC';
278+
return bucket === 'openForRegistration' && filterState.types.includes('REC');
279279
}
280280

281281
export default undefined;

src/shared/utils/challenge-listing/helper.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,11 @@ export function phaseStartDate(phase) {
3333
// For all other cases, take the `actualStartDate` as phase is already started
3434
return new Date(phase.actualStartDate);
3535
}
36+
37+
/**
38+
* Calculate match percentage.
39+
* @param {Float} score
40+
*/
41+
export function calculateScore(score) {
42+
return Math.trunc(Math.abs(score + 1.0) * 100.0 / 2.0);
43+
}

src/styles/awesome.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@
6565
content: "\f110";
6666
}
6767

68+
.fa-info-cirle::before {
69+
content: "\0024D8";
70+
}
71+
6872
@keyframes fa-spin {
6973
0% {
7074
-webkit-transform: rotate(0deg);

0 commit comments

Comments
 (0)