From 3e66cfa3a9a40b228b27be102107aa0cbb8603d2 Mon Sep 17 00:00:00 2001 From: Mateusz Tylka Date: Tue, 4 Apr 2023 10:04:09 +0200 Subject: [PATCH] allentries on desktop --- src/App.js | 14 +- src/api/getAllEntries.js | 66 ++- .../AllEntries/AllEntries.js | 90 ++-- .../allEntriesSearchQueryHandler.js | 11 +- .../specific_challenge/Challenge.js | 14 +- .../specific_challenge/ColumnFilterIcon.js | 61 ++- .../DesktopChallengeMenu.js | 4 +- .../MyEntries/myEntriesSearchQueryHandler.js | 49 +- src/components/specific_challenge/Table.js | 432 ++++++++++-------- src/utils/containers.js | 2 + src/utils/globals.js | 2 +- 11 files changed, 446 insertions(+), 299 deletions(-) diff --git a/src/App.js b/src/App.js index f629170..0016df7 100644 --- a/src/App.js +++ b/src/App.js @@ -128,28 +128,28 @@ const App = () => { element={} /> } /> + } + /> } /> } - /> - } /> } /> } /> diff --git a/src/api/getAllEntries.js b/src/api/getAllEntries.js index 8ee069e..73573f8 100644 --- a/src/api/getAllEntries.js +++ b/src/api/getAllEntries.js @@ -1,16 +1,66 @@ import { API } from '../utils/globals'; +import KeyCloakService from '../services/KeyCloakService'; -const getAllEntries = async (setDataState, challengeName, setLoading) => { - await fetch(`${API}/challenge-all-submissions/${challengeName}`) +const getAllEntries = ( + challengeName, + setDataOriginalState, + setDataState, + setLoadingState, + setScoreSorted +) => { + fetch(`${API}/challenge-all-submissions/${challengeName}`, { + headers: { Authorization: `Bearer ${KeyCloakService.getToken()}` }, + }) .then((response) => response.json()) .then((data) => { - console.log(data); - setDataState(data.entries); + if (setDataOriginalState) setDataOriginalState(data); + let item = {}; + let result = []; + let initSetScoreSorted = []; + let tests = data.tests; + for (let submission of data.submissions) { + for (let evaluation of submission.evaluations) { + item = { + ...item, + evaluations: { + ...item.evaluations, + [`${evaluation.test.metric}.${evaluation.test.name}`]: + evaluation.score, + }, + }; + } + for (let test of tests) { + if (item.evaluations) { + if ( + !Object.hasOwn(item.evaluations, `${test.metric}.${test.name}`) + ) { + item = { + ...item, + evaluations: { + ...item.evaluations, + [`${test.metric}.${test.name}`]: '-1', + }, + }; + } + } + } + item = { + ...item, + id: submission.id, + submitter: submission.submitter, + when: submission.when, + }; + result.push(item); + item = {}; + } + // eslint-disable-next-line no-unused-vars + for (let _ of tests) { + initSetScoreSorted.push(false); + } + setDataState(result); + if (setScoreSorted) setScoreSorted(initSetScoreSorted); + if (setLoadingState) setLoadingState(false); }); - - if (setLoading) { - setLoading(false); - } }; export default getAllEntries; diff --git a/src/components/specific_challenge/AllEntries/AllEntries.js b/src/components/specific_challenge/AllEntries/AllEntries.js index d92a584..5050fc1 100644 --- a/src/components/specific_challenge/AllEntries/AllEntries.js +++ b/src/components/specific_challenge/AllEntries/AllEntries.js @@ -4,7 +4,7 @@ import Media from 'react-media'; import { FlexColumn } from '../../../utils/containers'; import { H2 } from '../../../utils/fonts'; import { - // CALC_PAGES, + CALC_PAGES, EVALUATIONS_FORMAT, RENDER_WHEN, } from '../../../utils/globals'; @@ -17,31 +17,39 @@ import getAllEntries from '../../../api/getAllEntries'; const AllEntries = (props) => { const [entriesFromApi, setEntriesFromApi] = React.useState([]); + const [entriesAll, setEntriesAll] = React.useState([]); const [entries, setEntries] = React.useState([]); const [pageNr, setPageNr] = React.useState(1); const [loading, setLoading] = React.useState(true); - // const [submitterSorted, setSubmitterSorted] = React.useState(false); - // const [entriesSorted, setEntriesSorted] = React.useState(false); - const [whenSorted, setWhenSorted] = React.useState(false); const [scoresSorted, setScoresSorted] = React.useState([]); + const [submitterSorted, setSubmitterSorted] = React.useState(false); + const [whenSorted, setWhenSorted] = React.useState(false); React.useEffect(() => { - challengeDataRequest(props.challengeName); + if (props.challengeName) challengeDataRequest(props.challengeName); }, [props.challengeName]); const challengeDataRequest = (challengeName) => { - getAllEntries(setEntriesFromApi, challengeName); - getAllEntries(setEntries, challengeName, setLoading); + getAllEntries(challengeName, setEntriesFromApi, setEntriesAll); + getAllEntries( + challengeName, + undefined, + setEntries, + setLoading, + setScoresSorted + ); }; const getPossibleMetrics = () => { let metrics = []; - // for (let test of entriesFromApi.tests) { - // let myEval = `${test.metric}.${test.name}`; - // if (myEval && !metrics.includes(myEval)) { - // metrics.push(myEval); - // } - // } + if (entriesFromApi.tests) { + for (let test of entriesFromApi.tests) { + let myEval = `${test.metric}.${test.name}`; + if (myEval && !metrics.includes(myEval)) { + metrics.push(myEval); + } + } + } return metrics; }; @@ -50,20 +58,19 @@ const AllEntries = (props) => { for (let metric of getPossibleMetrics()) { header.push(metric); } - header.push('entries'); header.push('when'); return header; }; const searchQueryHandler = (event) => { - allEntriesSearchQueryHandler(event, entriesFromApi, setPageNr, setEntries); + allEntriesSearchQueryHandler(event, entriesAll, setPageNr, setEntries); }; const nextPage = () => { - // if (pageNr !== CALC_PAGES(entries ? entries : [])) { - // let newPage = pageNr + 1; - // setPageNr(newPage); - // } + if (pageNr !== CALC_PAGES(entries ? entries : [])) { + let newPage = pageNr + 1; + setPageNr(newPage); + } }; const previousPage = () => { @@ -78,17 +85,38 @@ const AllEntries = (props) => { switch (elem) { case '#': break; + case 'submitter': + if (submitterSorted) { + setSubmitterSorted(false); + newEntries = newEntries.sort((a, b) => + a.submitter.toLowerCase() < b.submitter.toLowerCase() + ? 1 + : b.submitter.toLowerCase() < a.submitter.toLowerCase() + ? -1 + : 0 + ); + } else { + setSubmitterSorted(true); + newEntries = newEntries.sort((a, b) => + a.submitter.toLowerCase() > b.submitter.toLowerCase() + ? 1 + : b.submitter.toLowerCase() > a.submitter.toLowerCase() + ? -1 + : 0 + ); + } + break; case 'when': if (whenSorted) { + setWhenSorted(false); newEntries = newEntries.sort((a, b) => a.when < b.when ? 1 : b.when < a.when ? -1 : 0 ); - setWhenSorted(false); } else { + setWhenSorted(true); newEntries = newEntries.sort((a, b) => a.when > b.when ? 1 : b.when > a.when ? -1 : 0 ); - setWhenSorted(true); } break; default: @@ -124,7 +152,7 @@ const AllEntries = (props) => { const desktopRender = () => { return ( - +

All Entries

@@ -134,18 +162,14 @@ const AllEntries = (props) => { { align: 'left', }} pageNr={pageNr} - elements={[]} + elements={entries} sortByUpdate={sortByUpdate} /> ) : ( diff --git a/src/components/specific_challenge/AllEntries/allEntriesSearchQueryHandler.js b/src/components/specific_challenge/AllEntries/allEntriesSearchQueryHandler.js index 4f427b6..f572fff 100644 --- a/src/components/specific_challenge/AllEntries/allEntriesSearchQueryHandler.js +++ b/src/components/specific_challenge/AllEntries/allEntriesSearchQueryHandler.js @@ -10,15 +10,18 @@ const allEntriesSearchQueryHandler = ( if (searchQuery === '') setEntries(entriesFromApi); else { for (let entry of entriesFromApi) { - const { id, submitter, when, times } = entry; + const { id, when, submitter } = entry; + console.log(entry); let evaluations = ''; - for (let evaluation of entry.evaluations) { - evaluations += ` ${evaluation.score}`; + if (entry.evaluations) { + for (let evaluation of Object.values(entry.evaluations)) { + evaluations += ` ${evaluation}`; + } } const str = `${id} ${submitter} ${when.slice(11, 16)} ${when.slice( 0, 10 - )} ${evaluations} ${times}`; + )} ${evaluations}`; if (str.toLowerCase().includes(searchQuery.toLowerCase())) submissionsToRender.push(entry); } diff --git a/src/components/specific_challenge/Challenge.js b/src/components/specific_challenge/Challenge.js index e255ada..bf4510e 100644 --- a/src/components/specific_challenge/Challenge.js +++ b/src/components/specific_challenge/Challenge.js @@ -40,6 +40,10 @@ const Challenge = (props) => { /> ); case 1: + return ( + + ); + case 2: return ( { deadline={challenge.deadline} /> ); - case 2: + case 3: return ( { user={user} /> ); - case 3: - return ; case 4: - return ; + return ; case 5: - return ( - - ); + return ; default: return ( { - const renderSecondIcon = () => { - if (props.index === props.active) { - return ( - - ); - } else { - return ( - - ); - } - }; + const renderSecondIcon = () => { + if (props.index === props.active) { + return ( + + ); + } else { + return ( + + ); + } + }; - return ( - <> - - {renderSecondIcon()} - - ); + return ( + <> + + {renderSecondIcon()} + + ); }; -export default ColumnFilterIcon; \ No newline at end of file +export default ColumnFilterIcon; diff --git a/src/components/specific_challenge/DesktopChallengeMenu.js b/src/components/specific_challenge/DesktopChallengeMenu.js index 58dbfae..3db9579 100644 --- a/src/components/specific_challenge/DesktopChallengeMenu.js +++ b/src/components/specific_challenge/DesktopChallengeMenu.js @@ -38,15 +38,15 @@ const Option = styled(FlexColumn)` `; const DesktopChallengeMenu = (props) => { - let options = ['Leaderboard', 'Readme', 'How to']; + let options = ['Leaderboard', 'All entries', 'Readme', 'How to']; if (KeyCloakService.isLoggedIn()) options = [ 'Leaderboard', + 'All entries', 'Readme', 'How to', 'My entries', 'Submit', - 'All entries', ]; return ( diff --git a/src/components/specific_challenge/MyEntries/myEntriesSearchQueryHandler.js b/src/components/specific_challenge/MyEntries/myEntriesSearchQueryHandler.js index c31439a..ad073b8 100644 --- a/src/components/specific_challenge/MyEntries/myEntriesSearchQueryHandler.js +++ b/src/components/specific_challenge/MyEntries/myEntriesSearchQueryHandler.js @@ -1,24 +1,33 @@ -const myEntriesSearchQueryHandler = (event, entriesFromApi, setPageNr, setEntries) => { - let searchQuery = event.target.value; - let submissionsToRender = []; - setPageNr(1); - if (searchQuery === '') - setEntries(entriesFromApi); - else { - for (let entry of entriesFromApi) { - const {id, when} = entry; - let evaluations = ''; - for (let evaluation of Object.values(entry.evaluations)) { - evaluations += ` ${evaluation}`; - } - const str = `${id} ${when.slice(11, 16)} ${when.slice(0, 10)} ${evaluations}`; - console.log(entry); - console.log(str); - if (str.toLowerCase().includes(searchQuery.toLowerCase())) - submissionsToRender.push(entry); +const myEntriesSearchQueryHandler = ( + event, + entriesFromApi, + setPageNr, + setEntries +) => { + let searchQuery = event.target.value; + let submissionsToRender = []; + setPageNr(1); + if (searchQuery === '') setEntries(entriesFromApi); + else { + for (let entry of entriesFromApi) { + const { id, when } = entry; + let evaluations = ''; + if (entry.evaluations) { + for (let evaluation of Object.values(entry.evaluations)) { + evaluations += ` ${evaluation}`; } - setEntries(submissionsToRender); + } + const str = `${id} ${when.slice(11, 16)} ${when.slice( + 0, + 10 + )} ${evaluations}`; + console.log(entry); + console.log(str); + if (str.toLowerCase().includes(searchQuery.toLowerCase())) + submissionsToRender.push(entry); } + setEntries(submissionsToRender); + } }; -export default myEntriesSearchQueryHandler; \ No newline at end of file +export default myEntriesSearchQueryHandler; diff --git a/src/components/specific_challenge/Table.js b/src/components/specific_challenge/Table.js index 3ec5a84..1690229 100644 --- a/src/components/specific_challenge/Table.js +++ b/src/components/specific_challenge/Table.js @@ -1,20 +1,20 @@ import React from 'react'; -import {Container, FlexColumn, FlexRow, Grid} from '../../utils/containers'; +import { Container, FlexColumn, FlexRow, Grid } from '../../utils/containers'; import Media from 'react-media'; import theme from '../../utils/theme'; -import {ELEMENTS_PER_PAGE} from '../../utils/globals'; -import {Body, Medium} from '../../utils/fonts'; +import { ELEMENTS_PER_PAGE } from '../../utils/globals'; +import { Body, Medium } from '../../utils/fonts'; import styled from 'styled-components'; import ColumnFilterIcon from './ColumnFilterIcon'; const Line = styled(FlexRow)` position: absolute; - top: ${({top}) => top ? top : 'auto'}; - bottom: ${({bottom}) => bottom ? bottom : 'auto'}; + top: ${({ top }) => (top ? top : 'auto')}; + bottom: ${({ bottom }) => (bottom ? bottom : 'auto')}; left: 0; width: 100%; - background-color: ${({theme}) => theme.colors.dark04}; - height: ${({height}) => height ? height : '1px'}; + background-color: ${({ theme }) => theme.colors.dark04}; + height: ${({ height }) => (height ? height : '1px')}; `; const MobileTableStyle = styled(Container)` @@ -23,23 +23,27 @@ const MobileTableStyle = styled(Container)` margin: 32px 0; tr:nth-of-type(odd) { - background: ${({theme}) => theme.colors.dark03}; + background: ${({ theme }) => theme.colors.dark03}; } th { - background: ${({theme}) => theme.colors.dark05}; - color: ${({theme}) => theme.colors.white}; + background: ${({ theme }) => theme.colors.dark05}; + color: ${({ theme }) => theme.colors.white}; } - td, th { + td, + th { padding: 6px; - border: 1px solid ${({theme}) => theme.colors.white}; + border: 1px solid ${({ theme }) => theme.colors.white}; text-align: left; } display: block; - thead, tbody, th, td { + thead, + tbody, + th, + td { display: block; } @@ -51,7 +55,7 @@ const MobileTableStyle = styled(Container)` td { border: none; - border-bottom: 1px solid ${({theme}) => theme.colors.dark01}; + border-bottom: 1px solid ${({ theme }) => theme.colors.dark01}; position: relative; padding-left: 50%; @@ -67,222 +71,260 @@ const MobileTableStyle = styled(Container)` } td:nth-of-type(1):before { - content: ${({headerElements}) => headerElements[0] ? `'${headerElements[0]}'` : ''}; + content: ${({ headerElements }) => + headerElements[0] ? `'${headerElements[0]}'` : ''}; } td:nth-of-type(2):before { - content: ${({headerElements}) => headerElements[1] ? `'${headerElements[1]}'` : ''}; + content: ${({ headerElements }) => + headerElements[1] ? `'${headerElements[1]}'` : ''}; } td:nth-of-type(3):before { - content: ${({headerElements}) => headerElements[2] ? `'${headerElements[2]}'` : ''}; + content: ${({ headerElements }) => + headerElements[2] ? `'${headerElements[2]}'` : ''}; } td:nth-of-type(4):before { - content: ${({headerElements}) => headerElements[3] ? `'${headerElements[3]}'` : ''}; + content: ${({ headerElements }) => + headerElements[3] ? `'${headerElements[3]}'` : ''}; } td:nth-of-type(5):before { - content: ${({headerElements}) => headerElements[4] ? `'${headerElements[4]}'` : ''}; + content: ${({ headerElements }) => + headerElements[4] ? `'${headerElements[4]}'` : ''}; } td:nth-of-type(6):before { - content: ${({headerElements}) => headerElements[5] ? `'${headerElements[5]}'` : ''}; + content: ${({ headerElements }) => + headerElements[5] ? `'${headerElements[5]}'` : ''}; } td:nth-of-type(7):before { - content: ${({headerElements}) => headerElements[6] ? `'${headerElements[6]}'` : ''}; + content: ${({ headerElements }) => + headerElements[6] ? `'${headerElements[6]}'` : ''}; } td:nth-of-type(8):before { - content: ${({headerElements}) => headerElements[7] ? `'${headerElements[7]}'` : ''}; + content: ${({ headerElements }) => + headerElements[7] ? `'${headerElements[7]}'` : ''}; } td:nth-of-type(9):before { - content: ${({headerElements}) => headerElements[8] ? `'${headerElements[8]}'` : ''}; + content: ${({ headerElements }) => + headerElements[8] ? `'${headerElements[8]}'` : ''}; } td:nth-of-type(10):before { - content: ${({headerElements}) => headerElements[9] ? `'${headerElements[9]}'` : ''}; + content: ${({ headerElements }) => + headerElements[9] ? `'${headerElements[9]}'` : ''}; } `; const Table = (props) => { - const [, updateState] = React.useState(); - const forceUpdate = React.useCallback(() => updateState({}), []); - const [activeIcon, setActiveIcon] = React.useState(null); - const [rotateActiveIcon, setRotateActiveIcon] = React.useState(false); + const [, updateState] = React.useState(); + const forceUpdate = React.useCallback(() => updateState({}), []); + const [activeIcon, setActiveIcon] = React.useState(null); + const [rotateActiveIcon, setRotateActiveIcon] = React.useState(false); - const metricsRender = (elem) => { - if (!props.iterableColumnElement) - return <>; - if (Array.isArray(elem[props.iterableColumnElement.name])) - elem = elem[props.iterableColumnElement.name]; - else { - let newElem = []; - for (let metric of props.possibleMetrics) { - if (Object.hasOwn(elem, props.iterableColumnElement.name)) { - if (elem[props.iterableColumnElement.name][metric] === '-1') - newElem.push('N/A'); - else - newElem.push(elem[props.iterableColumnElement.name][metric]); - } else { - newElem.push('N/A'); - } - } - elem = newElem; - } - return ( - elem.map((iterableElem, i) => { - return ( - - {props.iterableColumnElement.format ? - props.iterableColumnElement.format(iterableElem) : iterableElem} - - ); - }) - ); - }; - - const rowRender = (elem) => { - if (elem.submitter === props.user) { - return ( - props.staticColumnElements.map((elemName, i) => { - return ( - - {elemName.format ? elemName.format(elem[elemName.name]) : elem[elemName.name]} - - ); - }) - ); - } - return ( - props.staticColumnElements.map((elemName, i) => { - return ( - - {elemName.format ? elemName.format(elem[elemName.name]) : elem[elemName.name]} - - ); - }) - ); - }; - - const desktopRender = () => { - const n = (props.pageNr - 1) * (ELEMENTS_PER_PAGE * 2); - let elementsToMap = props.elements.slice(n, n + (ELEMENTS_PER_PAGE * 2)); - if (elementsToMap.length > 0) { - return ( - - - - {props.headerElements.map((elem, i) => { - return ( - { - if (activeIcon === i) { - let newRotateActiveIcon = !rotateActiveIcon; - setRotateActiveIcon(newRotateActiveIcon); - } else { - setRotateActiveIcon(false); - } - setActiveIcon(i); - props.sortByUpdate(elem, i); - forceUpdate(); - }}> - - {elem} - - {elem !== '#' ? - - : ''} - - ); - })} - - - {elementsToMap.map((elem, index) => { - return ( - - {rowRender(elem)} - {props.headerElements ? metricsRender(elem) : ''} - - ); - })} - - - ); + const metricsRender = (elem) => { + if (!props.iterableColumnElement) return <>; + if (Array.isArray(elem[props.iterableColumnElement.name])) + elem = elem[props.iterableColumnElement.name]; + else { + let newElem = []; + for (let metric of props.possibleMetrics) { + if (Object.hasOwn(elem, props.iterableColumnElement.name)) { + if (elem[props.iterableColumnElement.name][metric] === '-1') + newElem.push('N/A'); + else newElem.push(elem[props.iterableColumnElement.name][metric]); } else { - return ( - - No results ;c - - ); + newElem.push('N/A'); } - }; + } + elem = newElem; + } + return elem.map((iterableElem, i) => { + return ( + + {props.iterableColumnElement.format + ? props.iterableColumnElement.format(iterableElem) + : iterableElem} + + ); + }); + }; - const mobileRender = () => { - const n = (props.pageNr - 1) * ELEMENTS_PER_PAGE; - let elementsToMap = props.elements.slice(n, n + ELEMENTS_PER_PAGE); - if (elementsToMap.length > 0) { - return ( - - - - {props.headerElements.map((elem, i) => { - return ( - - {elem} - - ); - })} - - - - {elementsToMap.map((elem, index) => { - return ( - - {rowRender(elem)} - {props.headerElements ? metricsRender(elem) : ''} - - ); - })} - - - ); - } else { - return ( - - No results ;c - - ); - } - }; + const rowRender = (elem) => { + if (elem.submitter === props.user) { + return props.staticColumnElements.map((elemName, i) => { + return ( + + {elemName.format + ? elemName.format(elem[elemName.name]) + : elem[elemName.name]} + + ); + }); + } + return props.staticColumnElements.map((elemName, i) => { + return ( + + {elemName.format + ? elemName.format(elem[elemName.name]) + : elem[elemName.name]} + + ); + }); + }; - return ( - <> - - {mobileRender()} - - - {desktopRender()} - - - ); + const desktopRender = () => { + const n = (props.pageNr - 1) * (ELEMENTS_PER_PAGE * 2); + let elementsToMap = props.elements.slice(n, n + ELEMENTS_PER_PAGE * 2); + if (elementsToMap.length > 0) { + return ( + + + + {props.headerElements.map((elem, i) => { + return ( + { + if (activeIcon === i) { + let newRotateActiveIcon = !rotateActiveIcon; + setRotateActiveIcon(newRotateActiveIcon); + } else { + setRotateActiveIcon(false); + } + setActiveIcon(i); + props.sortByUpdate(elem, i); + forceUpdate(); + }} + > + + {elem.replace('.', ' ')} + + {elem !== '#' ? ( + + ) : ( + '' + )} + + ); + })} + + + {elementsToMap.map((elem, index) => { + return ( + + {rowRender(elem)} + {props.headerElements ? metricsRender(elem) : ''} + + ); + })} + + + ); + } else { + return No results ;c; + } + }; + + const mobileRender = () => { + const n = (props.pageNr - 1) * ELEMENTS_PER_PAGE; + let elementsToMap = props.elements.slice(n, n + ELEMENTS_PER_PAGE); + if (elementsToMap.length > 0) { + return ( + + + + {props.headerElements.map((elem, i) => { + return ( + + {elem} + + ); + })} + + + + {elementsToMap.map((elem, index) => { + return ( + + {rowRender(elem)} + {props.headerElements ? metricsRender(elem) : ''} + + ); + })} + + + ); + } else { + return No results ;c; + } + }; + + return ( + <> + {mobileRender()} + {desktopRender()} + + ); }; -export default Table; \ No newline at end of file +export default Table; diff --git a/src/utils/containers.js b/src/utils/containers.js index bdb74fc..24c794a 100644 --- a/src/utils/containers.js +++ b/src/utils/containers.js @@ -35,6 +35,8 @@ const Container = styled.div` order: ${({ order }) => (order ? order : '0')}; z-index: ${({ zIndex }) => (zIndex ? zIndex : '0')}; list-style: ${({ listStyle }) => (listStyle ? listStyle : 'none')}; + overflow-wrap: ${({ overflowWrap }) => + overflowWrap ? overflowWrap : 'normal'}; `; const FlexRow = styled(Container)` diff --git a/src/utils/globals.js b/src/utils/globals.js index f839dd5..a885d87 100644 --- a/src/utils/globals.js +++ b/src/utils/globals.js @@ -16,7 +16,7 @@ const POLICY_PRIVACY_PAGE = '/policy-privacy'; const CSI_LINK = 'https://csi.amu.edu.pl/'; const ROOT_URL = window.location.origin; -const LOGIN_REQUIRED_PAGES = ['myentries', 'submit', 'howto']; +const LOGIN_REQUIRED_PAGES = ['myentries', 'submit']; const MINI_DESCRIPTION_RENDER = (description) => { if (description) {