From 7d0829a2f4d01f96203e759bbd01a6cb6a12dc10 Mon Sep 17 00:00:00 2001 From: alicendeh <alicendeh16@gmail.com> Date: Wed, 30 Mar 2022 18:18:30 +0100 Subject: [PATCH 1/5] a --- zubhub_frontend/zubhub/src/App.js | 1 + zubhub_frontend/zubhub/src/views/projects/Projects.jsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zubhub_frontend/zubhub/src/App.js b/zubhub_frontend/zubhub/src/App.js index 8b1ef4f96..2c44ebdeb 100644 --- a/zubhub_frontend/zubhub/src/App.js +++ b/zubhub_frontend/zubhub/src/App.js @@ -72,6 +72,7 @@ const LazyImport = props => { }; function App(props) { + console.log("hey alice"); return ( <Router> <Switch> diff --git a/zubhub_frontend/zubhub/src/views/projects/Projects.jsx b/zubhub_frontend/zubhub/src/views/projects/Projects.jsx index c0474702b..057d0bc44 100644 --- a/zubhub_frontend/zubhub/src/views/projects/Projects.jsx +++ b/zubhub_frontend/zubhub/src/views/projects/Projects.jsx @@ -263,6 +263,6 @@ const mapDispatchToProps = dispatch => { return dispatch(ProjectActions.setStaffPicks(args)); }, }; -}; +};Projects export default connect(mapStateToProps, mapDispatchToProps)(Projects); From 7ee432e4aed8f48b605bb0fba3f2753233e1ffad Mon Sep 17 00:00:00 2001 From: alicendeh <alicendeh16@gmail.com> Date: Sun, 3 Apr 2022 13:35:02 +0100 Subject: [PATCH 2/5] Done resolving issue 211 fixed failed to load api specification error (#363) fixed failed to load api specification error added style to fix text wrap (#373) Add hot reloading to media container (#382) Add new search features (#362) * Finish creators and projects search migration * Finish creators and projects search migration * Add tag search * Add search for projects with tags * Fix search bar on mobile * use 0 rather than 0 with units in css * Remove print * Default search type to projects * Disable scroll lock Revert package-lock (#383) improveConsistency: fixed title text small, extra vertical space ... (#369) * improveConsistency: fixed title text small, extra vertical space and inconsitent padding of buttons fix text overflow issue in signup page phone number field (#378) removed spacing between icons --- zubhub_backend/docker-compose.yml | 1 + zubhub_backend/zubhub/creators/views.py | 11 +- zubhub_backend/zubhub/projects/utils.py | 38 ++- zubhub_backend/zubhub/projects/views.py | 11 +- zubhub_frontend/zubhub/package-lock.json | 6 +- .../zubhub/public/locales/en/translation.json | 4 +- zubhub_frontend/zubhub/src/App.js | 1 - zubhub_frontend/zubhub/src/api/api.js | 22 +- .../zubhub/src/assets/css/index.css | 4 + .../js/styles/views/login/loginStyles.js | 1 + .../views/page_wrapper/pageWrapperStyles.js | 27 +- .../project_details/projectDetailsStyles.js | 4 + .../components/input_select/InputSelect.jsx | 64 ++++ .../src/store/actions/projectActions.js | 25 ++ .../zubhub/src/views/PageWrapper.jsx | 90 +++-- .../views/create_project/CreateProject.jsx | 7 +- .../zubhub/src/views/pageWrapperScripts.js | 11 +- .../views/project_details/ProjectDetails.jsx | 29 +- .../zubhub/src/views/projects/Projects.jsx | 2 +- .../views/search_results/SearchResults.jsx | 322 ++++++------------ .../search_results/searchResultsScripts.js | 47 +-- 21 files changed, 395 insertions(+), 332 deletions(-) create mode 100644 zubhub_frontend/zubhub/src/components/input_select/InputSelect.jsx diff --git a/zubhub_backend/docker-compose.yml b/zubhub_backend/docker-compose.yml index bc10ea2ed..e378e447f 100644 --- a/zubhub_backend/docker-compose.yml +++ b/zubhub_backend/docker-compose.yml @@ -25,6 +25,7 @@ services: dockerfile: ./compose/media/dev/Dockerfile restart: on-failure volumes: + - ./media:/home/media - media_data:/home/media/media_store - ./compose/media/requirements.txt:/home/requirements.txt:ro ports: diff --git a/zubhub_backend/zubhub/creators/views.py b/zubhub_backend/zubhub/creators/views.py index 2016f64f9..c99ebe158 100644 --- a/zubhub_backend/zubhub/creators/views.py +++ b/zubhub_backend/zubhub/creators/views.py @@ -80,9 +80,12 @@ def get(self, request, format=None): class UserProfileAPIView(RetrieveAPIView): """ Fetch Profile of user with given username. - Requires username of user. Returns user profile. + + Note that this schema returns the full user profile, but the api sometimes + returns a minimal version of the user profile, omitting certain fields that + are not neccessary or are sensitive. """ queryset = Creator.objects.filter(is_active=True) @@ -91,10 +94,10 @@ class UserProfileAPIView(RetrieveAPIView): throttle_classes = [GetUserRateThrottle, SustainedRateThrottle] def get_serializer_class(self): - if self.kwargs.get("username") == self.request.user.username: - return CreatorSerializer - else: + if self.request and self.kwargs.get("username") != self.request.user.username: return CreatorMinimalSerializer + else: + return CreatorSerializer class RegisterCreatorAPIView(RegisterView): diff --git a/zubhub_backend/zubhub/projects/utils.py b/zubhub_backend/zubhub/projects/utils.py index 028a8365e..81c8e68f2 100644 --- a/zubhub_backend/zubhub/projects/utils.py +++ b/zubhub_backend/zubhub/projects/utils.py @@ -1,4 +1,6 @@ +from enum import IntEnum import re +from typing import Optional, Set from akismet import Akismet from lxml.html.clean import Cleaner from lxml.html import document_fromstring @@ -366,8 +368,14 @@ def clean_project_desc(string): return cleaner.clean_html(string) +class ProjectSearchCriteria(IntEnum): + CATGEORY = 0 + TAG = 1 + TITLE_DESCRIPTION = 2 -def perform_project_search(user, query_string): + +default_search_criteria = {ProjectSearchCriteria.CATGEORY, ProjectSearchCriteria.TAG, ProjectSearchCriteria.TITLE_DESCRIPTION} +def perform_project_search(user, query_string, search_criteria: Optional[Set[ProjectSearchCriteria]] = None): """ Perform search for projects matching query. @@ -397,25 +405,23 @@ def perform_project_search(user, query_string): else: result_categories_tree = Category.get_tree(parent=category) - if result_categories_tree: + if search_criteria is None: + search_criteria = default_search_criteria + + result_projects = Project.objects.none() + if ProjectSearchCriteria.CATGEORY in search_criteria and result_categories_tree: prefetch_related_objects(result_categories_tree, 'projects') for category in result_categories_tree: - if result_projects: - result_projects |= category.projects.all().annotate(rank=rank).order_by('-rank') - else: - result_projects = category.projects.all().annotate(rank=rank).order_by('-rank') + result_projects |= category.projects.all().annotate(rank=rank).order_by('-rank') ################################################################# # fetch all projects whose tag(s) matches the search query result_tags = Tag.objects.filter( search_vector=query).prefetch_related("projects") - - for tag in result_tags: - if result_projects: + if ProjectSearchCriteria.TAG in search_criteria: + for tag in result_tags: result_projects |= tag.projects.all().annotate(rank=rank).order_by('-rank') - else: - result_projects = tag.projects.all().annotate(rank=rank).order_by('-rank') ############################################################ @@ -436,16 +442,12 @@ def perform_project_search(user, query_string): # ############################################################ # fetch all projects that matches the search term - if result_projects: - result_projects |= Project.objects.annotate(rank=rank).filter( - search_vector=query ).order_by('-rank') - else: - result_projects = Project.objects.annotate(rank=rank).filter( - search_vector=query ).order_by('-rank') + if ProjectSearchCriteria.TITLE_DESCRIPTION in search_criteria: + result_projects |= Project.objects.annotate(rank=rank).filter(search_vector=query ).order_by('-rank') ############################################################## result = [] - + """ make sure that user can view all projects in search result """ for project in result_projects: if can_view(user, project): diff --git a/zubhub_backend/zubhub/projects/views.py b/zubhub_backend/zubhub/projects/views.py index d49b9ceb1..b45476d7b 100644 --- a/zubhub_backend/zubhub/projects/views.py +++ b/zubhub_backend/zubhub/projects/views.py @@ -14,7 +14,7 @@ from projects.permissions import (IsOwner, IsStaffOrModerator, SustainedRateThrottle, PostUserRateThrottle, GetUserRateThrottle, CustomUserRateThrottle) from .models import Project, Comment, StaffPick, Category, Tag, PublishingRule -from .utils import (project_changed, detect_mentions, +from .utils import (ProjectSearchCriteria, project_changed, detect_mentions, perform_project_search, can_view, get_published_projects_for_user) from creators.utils import activity_notification from .serializers import (ProjectSerializer, ProjectListSerializer, @@ -155,7 +155,8 @@ def get_queryset(self): query_string = self.request.GET.get('q') query = SearchQuery(query_string) rank = SearchRank(F('search_vector'), query) - return Tag.objects.annotate(rank=rank).filter(search_vector=query).order_by('-rank') + tags = Tag.objects.annotate(rank=rank).filter(search_vector=query).order_by('-rank') + return tags class ProjectSearchAPIView(ListAPIView): @@ -172,7 +173,11 @@ class ProjectSearchAPIView(ListAPIView): pagination_class = ProjectNumberPagination def get_queryset(self): - return perform_project_search(self.request.user, self.request.GET.get("q")) + try: + search_criteria = {ProjectSearchCriteria(int(self.request.GET.get('criteria', '')))} + except (KeyError, ValueError): + search_criteria = None + return perform_project_search(self.request.user, self.request.GET.get("q"), search_criteria) class ProjectDetailsAPIView(RetrieveAPIView): diff --git a/zubhub_frontend/zubhub/package-lock.json b/zubhub_frontend/zubhub/package-lock.json index 082d96f12..33c236238 100644 --- a/zubhub_frontend/zubhub/package-lock.json +++ b/zubhub_frontend/zubhub/package-lock.json @@ -4203,9 +4203,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001263", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001263.tgz", - "integrity": "sha512-doiV5dft6yzWO1WwU19kt8Qz8R0/8DgEziz6/9n2FxUasteZNwNNYSmJO3GLBH8lCVE73AB1RPDPAeYbcO5Cvw==" + "version": "1.0.30001322", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz", + "integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==" }, "capture-exit": { "version": "2.0.0", diff --git a/zubhub_frontend/zubhub/public/locales/en/translation.json b/zubhub_frontend/zubhub/public/locales/en/translation.json index 5afe07a39..c29e0f182 100644 --- a/zubhub_frontend/zubhub/public/locales/en/translation.json +++ b/zubhub_frontend/zubhub/public/locales/en/translation.json @@ -379,8 +379,8 @@ "searchResults": { "projects": "Projects", "creators": "Creators", - "resultFound": "Result found", - "resultsFound": "Results found", + "resultFound": "Result for", + "resultsFound": "Results for", "prev": "Prev", "next": "Next", "ariaLabels": { diff --git a/zubhub_frontend/zubhub/src/App.js b/zubhub_frontend/zubhub/src/App.js index 78954e60d..cad6b4a87 100644 --- a/zubhub_frontend/zubhub/src/App.js +++ b/zubhub_frontend/zubhub/src/App.js @@ -75,7 +75,6 @@ const LazyImport = props => { }; function App(props) { - console.log("hey alice"); return ( <Router> <Switch> diff --git a/zubhub_frontend/zubhub/src/api/api.js b/zubhub_frontend/zubhub/src/api/api.js index 682c9be6d..f130dd898 100644 --- a/zubhub_frontend/zubhub/src/api/api.js +++ b/zubhub_frontend/zubhub/src/api/api.js @@ -282,14 +282,15 @@ class API { * * @todo - describe method's signature */ - searchProjects = ({ page, token, query_string }) => { - let url; + searchProjects = ({ page, token, query_string, criteria }) => { + const params = { q: query_string, criteria }; if (page) { - url = `projects/search/?q=${query_string}&page=${page}`; - } else { - url = `projects/search/?q=${query_string}`; + params[page] = page; } + const searchParams = new URLSearchParams(params); + const url = `projects/search/?${searchParams.toString()}`; + return this.request({ url, token }).then(res => res.json()); }; @@ -310,6 +311,17 @@ class API { return this.request({ url, token }).then(res => res.json()); }; + searchTags = ({ page, token, query_string }) => { + let url; + if (page) { + url = `projects/tags/search/?q=${query_string}&page=${page}`; + } else { + url = `projects/tags/search/?q=${query_string}`; + } + + return this.request({ url, token }).then(res => res.json()); + }; + /** * @method getFollowers - get a list of users that a username is following * @author Raymond Ndibe <ndiberaymond1@gmail.com> diff --git a/zubhub_frontend/zubhub/src/assets/css/index.css b/zubhub_frontend/zubhub/src/assets/css/index.css index 53b82c450..f586d3b14 100644 --- a/zubhub_frontend/zubhub/src/assets/css/index.css +++ b/zubhub_frontend/zubhub/src/assets/css/index.css @@ -595,3 +595,7 @@ html, .display-none { display: none !important; } + +.MuiInputBase-root #phone{ + width: 80%; +} \ No newline at end of file diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js index d42465e83..8f70b778d 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js @@ -77,6 +77,7 @@ const styles = theme => ({ alignItems: 'center', }, dividerText: { + whiteSpace: 'nowrap', [theme.breakpoints.up('1600')]: { fontSize: '1.2rem', }, diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/views/page_wrapper/pageWrapperStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/views/page_wrapper/pageWrapperStyles.js index 808d9866c..be6fa72a6 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/views/page_wrapper/pageWrapperStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/views/page_wrapper/pageWrapperStyles.js @@ -23,16 +23,17 @@ const styles = theme => ({ searchFormStyle: { display: 'inline-block', position: 'relative', - marginLeft: '1em', + marginLeft: '2em', + verticalAlign: 'bottom', '& .search-form-input': { height: '2.5em', - position: 'absolute', - top: '-1em', maxWidth: '40em', - width: '40em', + width: '35em', backgroundColor: 'rgba(255,255,255,0.2)', color: 'black', borderRadius: '50px', + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, '&:hover': { backgroundColor: 'rgba(255,255,255,0.8)', '& .MuiInputAdornment-root .MuiButtonBase-root': { @@ -61,22 +62,20 @@ const styles = theme => ({ }, }, }, - smallSearchFormStyle: { - height: '2.5em', + height: '4em', width: '100%', - '& .MuiFormControl-root': { - width: '100%', - }, - + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + justifyContent: 'center', '& .search-form-input': { - height: '2em', - position: 'absolute', - top: '-0.3em', - width: '100%', + height: '2.5em', backgroundColor: 'rgba(255,255,255,0.2)', color: 'black', borderRadius: '50px', + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, '&:hover': { backgroundColor: 'rgba(255,255,255,0.8)', '& .MuiInputAdornment-root .MuiButtonBase-root': { diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js index e66f9e8f0..4e2fefcd2 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js @@ -98,6 +98,7 @@ const styles = theme => ({ flexDirection: 'column', alignItems: 'center', justifyContent: 'center', + boxShadow: '0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)', [theme.breakpoints.down('1080')]: { @@ -253,6 +254,9 @@ const styles = theme => ({ error: { color: '#a94442', }, + dialogButtonContainer: { + padding: '16px 24px', + }, }); export const sliderSettings = images_num => ({ diff --git a/zubhub_frontend/zubhub/src/components/input_select/InputSelect.jsx b/zubhub_frontend/zubhub/src/components/input_select/InputSelect.jsx new file mode 100644 index 000000000..4bca266f7 --- /dev/null +++ b/zubhub_frontend/zubhub/src/components/input_select/InputSelect.jsx @@ -0,0 +1,64 @@ +import { InputBase, Select, withStyles } from '@material-ui/core'; +import React from 'react'; + +const BootstrapInput = withStyles(theme => ({ + input: { + borderRadius: 0, + borderTopLeftRadius: 250, + borderBottomLeftRadius: 250, + position: 'relative', + fontSize: 16, + padding: '10px 26px 10px 18px', + backgroundColor: '#00B8C4', + color: 'white', + transition: 'background-color 250ms ease', + textAlign: 'center', + '& ~ svg': { + color: 'white', + }, + '&:focus': { + borderTopLeftRadius: 250, + borderBottomLeftRadius: 250, + backgroundColor: '#00B8C4', + }, + '&[aria-expanded]': { + borderRadius: 0, + borderTopLeftRadius: 250, + borderBottomLeftRadius: 250, + backgroundColor: 'white', + color: '#00B8C4', + '& ~ svg': { + color: '#00B8C4', + }, + }, + }, +}))(InputBase); + +const InputSelect = ({ + searchType, + onSearchTypeChange, + children, + ...selectProps +}) => { + return ( + <Select + labelId="demo-customized-select-label" + id="demo-customized-select" + value={searchType} + onChange={({ target: { value } }) => onSearchTypeChange(value)} + input={<BootstrapInput />} + style={{ minWidth: '115px' }} + MenuProps={{ + getContentAnchorEl: null, + anchorOrigin: { vertical: 'bottom', horizontal: 'center' }, + transformOrigin: { vertical: 'top', horizontal: 'center' }, + disableScrollLock: true, + }} + {...selectProps} + > + {children} + </Select> + ); +}; + +export default InputSelect; diff --git a/zubhub_frontend/zubhub/src/store/actions/projectActions.js b/zubhub_frontend/zubhub/src/store/actions/projectActions.js index 589d6e321..ac7bea70a 100644 --- a/zubhub_frontend/zubhub/src/store/actions/projectActions.js +++ b/zubhub_frontend/zubhub/src/store/actions/projectActions.js @@ -268,6 +268,31 @@ export const searchProjects = args => { }; }; +export const searchTags = args => { + return () => { + return API.searchTags(args) + .then(res => { + if (Array.isArray(res.results)) { + return { ...res, loading: false, tab: args.tab }; + } else { + res = Object.keys(res) + .map(key => res[key]) + .join('\n'); + throw new Error(res); + } + }) + .catch(error => { + console.error(error); + if (error.message.startsWith('Unexpected')) { + toast.warning(args.t('projects.errors.unexpected')); + } else { + toast.warning(error.message); + } + return { loading: false, tab: args.tab }; + }); + }; +}; + /** * @function suggestTags * @author Raymond Ndibe <ndiberaymond1@gmail.com> diff --git a/zubhub_frontend/zubhub/src/views/PageWrapper.jsx b/zubhub_frontend/zubhub/src/views/PageWrapper.jsx index 507afdb99..c6bfc2482 100644 --- a/zubhub_frontend/zubhub/src/views/PageWrapper.jsx +++ b/zubhub_frontend/zubhub/src/views/PageWrapper.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import clsx from 'clsx'; @@ -33,6 +33,8 @@ import { MenuItem, Avatar, Select, + FormGroup, + InputBase, } from '@material-ui/core'; import { @@ -47,8 +49,9 @@ import { } from './pageWrapperScripts'; import { - getQueryParams -} from './search_results/searchResultsScripts' + getQueryParams, + SearchType, +} from './search_results/searchResultsScripts'; import CustomButton from '../components/button/Button.js'; import LoadingPage from './loading/LoadingPage'; @@ -60,6 +63,7 @@ import styles from '../assets/js/styles/views/page_wrapper/pageWrapperStyles'; import commonStyles from '../assets/js/styles'; import languageMap from '../assets/js/languageMap.json'; +import InputSelect from '../components/input_select/InputSelect'; const useStyles = makeStyles(styles); const useCommonStyles = makeStyles(commonStyles); @@ -74,6 +78,9 @@ function PageWrapper(props) { const backToTopEl = React.useRef(null); const classes = useStyles(); const common_classes = useCommonStyles(); + const [searchType, setSearchType] = useState( + getQueryParams(window.location.href).get('type') || SearchType.PROJECTS, + ); const [state, setState] = React.useState({ username: null, @@ -175,29 +182,49 @@ function PageWrapper(props) { > {t('pageWrapper.inputs.search.label')} </InputLabel> - <OutlinedInput - name="q" - id="q" - type="search" - defaultValue={getQueryParams(window.location.href).get('q')} - className={clsx( - classes.searchFormInputStyle, - 'search-form-input', - )} - placeholder={`${t('pageWrapper.inputs.search.label')}...`} - pattern="(.|\s)*\S(.|\s)*" - endAdornment={ - <InputAdornment position="end"> - <IconButton - type="submit" - className={classes.searchFormSubmitStyle} - aria-label={t('pageWrapper.inputs.search.label')} - > - <SearchIcon /> - </IconButton> - </InputAdornment> - } - /> + <FormGroup row> + <FormControl variant="outlined"> + <InputSelect + searchType={searchType} + onSearchTypeChange={setSearchType} + name="type" + > + <MenuItem value={SearchType.PROJECTS}> + Projects + </MenuItem> + <MenuItem value={SearchType.CREATORS}> + Creators + </MenuItem> + <MenuItem value={SearchType.TAGS}>Tags</MenuItem> + </InputSelect> + </FormControl> + <OutlinedInput + name="q" + id="q" + type="search" + defaultValue={ + props.location.search && + getQueryParams(window.location.href).get('q') + } + className={clsx( + classes.searchFormInputStyle, + 'search-form-input', + )} + placeholder={`${t('pageWrapper.inputs.search.label')}...`} + pattern="(.|\s)*\S(.|\s)*" + endAdornment={ + <InputAdornment position="end"> + <IconButton + type="submit" + className={classes.searchFormSubmitStyle} + aria-label={t('pageWrapper.inputs.search.label')} + > + <SearchIcon /> + </IconButton> + </InputAdornment> + } + /> + </FormGroup> </FormControl> </form> </Box> @@ -470,6 +497,17 @@ function PageWrapper(props) { className={clsx(classes.smallSearchFormStyle, classes.addOn894)} role="search" > + <FormControl variant="outlined"> + <InputSelect + searchType={searchType} + onSearchTypeChange={setSearchType} + name="type" + > + <MenuItem value={SearchType.PROJECTS}>Projects</MenuItem> + <MenuItem value={SearchType.CREATORS}>Creators</MenuItem> + <MenuItem value={SearchType.TAGS}>Tags</MenuItem> + </InputSelect> + </FormControl> <FormControl variant="outlined"> <InputLabel htmlFor="q" diff --git a/zubhub_frontend/zubhub/src/views/create_project/CreateProject.jsx b/zubhub_frontend/zubhub/src/views/create_project/CreateProject.jsx index aff221a3a..2363337b8 100644 --- a/zubhub_frontend/zubhub/src/views/create_project/CreateProject.jsx +++ b/zubhub_frontend/zubhub/src/views/create_project/CreateProject.jsx @@ -127,6 +127,9 @@ const buildMaterialUsedNodes = ({ props, refs, classes, common_classes }) => { * @todo - describe function's signature */ function CreateProject(props) { + console.log(props.values.category); + const [category, setCategory] = React.useState([]); + const classes = useStyles(); const common_classes = useCommonStyles(); @@ -660,9 +663,11 @@ function CreateProject(props) { labelId="category" id="category" name="category" + multiple={true} className={classes.customInputStyle} value={ - props.values.category ? props.values.category : '' + [...category] + // props.values.category ? props.values.category : '' } onChange={props.handleChange} onBlur={props.handleBlur} diff --git a/zubhub_frontend/zubhub/src/views/pageWrapperScripts.js b/zubhub_frontend/zubhub/src/views/pageWrapperScripts.js index b40622520..36aa9a93f 100644 --- a/zubhub_frontend/zubhub/src/views/pageWrapperScripts.js +++ b/zubhub_frontend/zubhub/src/views/pageWrapperScripts.js @@ -93,11 +93,8 @@ export const handleCloseSearchForm = () => { * @todo - describe function's signature */ export const closeSearchFormOrIgnore = e => { - let is_toggle_search_button; - e.path.forEach(el => { - if (el.id === 'toggle-search') { - is_toggle_search_button = true; - } - }); - if (!is_toggle_search_button) return handleCloseSearchForm(); + const form = e.target.closest('form'); + if (!form || form.getAttribute('role') !== 'search') { + handleCloseSearchForm(); + } }; diff --git a/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx b/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx index da2e38e14..bf2de064a 100644 --- a/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx +++ b/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx @@ -196,7 +196,7 @@ function ProjectDetails(props) { </Typography> </Link> {project.creator.id === props.auth.id ? ( - <> + <Grid container justify="flex-end"> <Link className={classes.textDecorationNone} to={`/projects/${project.id}/edit`} @@ -219,7 +219,7 @@ function ProjectDetails(props) { > {t('projectDetails.project.delete.label')} </CustomButton> - </> + </Grid> ) : ( <CustomButton className={common_classes.marginLeft1em} @@ -246,10 +246,7 @@ function ProjectDetails(props) { xs={12} sm={12} md={12} - className={clsx( - classes.positionRelative, - classes.marginBottom1em, - )} + className={clsx(classes.positionRelative)} > <Grid item @@ -306,6 +303,7 @@ function ProjectDetails(props) { </a> ) : null} </Grid> + {/* box style here */} <Box className={classes.actionBoxStyle}> <CustomButton className={classes.actionBoxButtonStyle} @@ -491,24 +489,25 @@ function ProjectDetails(props) { aria-labelledby={t('projectDetails.ariaLabels.deleteProject')} > <DialogTitle id="delete-project"> + <Typography variant="h4"> {t('projectDetails.project.delete.dialog.primary')} + </Typography> </DialogTitle> - <Box + {delete_project_dialog_error !== null && (<Box component="p" - className={delete_project_dialog_error !== null && classes.errorBox} + className={classes.errorBox} > - {delete_project_dialog_error !== null && ( - <Box component="span" className={classes.error}> - {delete_project_dialog_error} - </Box> - )} - </Box>{' '} + <Box component="span" className={classes.error}> + {delete_project_dialog_error} + </Box> + + </Box>)} <DialogContent> <Typography> {t('projectDetails.project.delete.dialog.secondary')} </Typography> </DialogContent> - <DialogActions> + <DialogActions className={classes.dialogButtonContainer}> <CustomButton variant="outlined" onClick={() => diff --git a/zubhub_frontend/zubhub/src/views/projects/Projects.jsx b/zubhub_frontend/zubhub/src/views/projects/Projects.jsx index 1a8f7dcd8..67cc798b0 100644 --- a/zubhub_frontend/zubhub/src/views/projects/Projects.jsx +++ b/zubhub_frontend/zubhub/src/views/projects/Projects.jsx @@ -267,6 +267,6 @@ const mapDispatchToProps = dispatch => { return dispatch(ProjectActions.setStaffPicks(args)); }, }; -};Projects +}; export default connect(mapStateToProps, mapDispatchToProps)(Projects); diff --git a/zubhub_frontend/zubhub/src/views/search_results/SearchResults.jsx b/zubhub_frontend/zubhub/src/views/search_results/SearchResults.jsx index 2cb44aadf..dbc57acf4 100644 --- a/zubhub_frontend/zubhub/src/views/search_results/SearchResults.jsx +++ b/zubhub_frontend/zubhub/src/views/search_results/SearchResults.jsx @@ -22,10 +22,10 @@ import { import { getQueryParams, - switchTab, fetchPage, updateProjects, toggleFollow, + SearchType, } from './searchResultsScripts'; import * as ProjectActions from '../../store/actions/projectActions'; @@ -115,17 +115,12 @@ function SearchResults(props) { previous: null, next: null, loading: true, - tab: 'projects', }); React.useEffect(() => { const params = getQueryParams(window.location.href); - if (!['creators', 'projects'].includes(params.get('tab'))) { - switchTab('projects', props, window.location.href); - } - - handleSetState(fetchPage(null, props, params.get('q'), params.get('tab'))); + handleSetState(fetchPage(null, props, params.get('q'), params.get('type'))); }, []); const handleSetState = obj => { @@ -136,13 +131,44 @@ function SearchResults(props) { } }; + const getResults = (type, results) => { + if (type === SearchType.CREATORS) { + return buildCreatorProfiles( + results, + { classes, common_classes }, + props, + state, + handleSetState, + ); + } else { + return results.map(project => ( + <Grid + item + xs={12} + sm={6} + md={4} + className={classes.projectGridStyle} + align="center" + > + <Project + project={project} + key={project.id} + updateProjects={res => + handleSetState(updateProjects(res, state, props, toast)) + } + {...props} + /> + </Grid> + )); + } + }; + const { count, results, previous: prev_page, next: next_page, loading, - tab, } = state; const { t } = props; if (loading) { @@ -150,211 +176,81 @@ function SearchResults(props) { } else { return ( <Box className={classes.root}> - <Box className={classes.searchSectionStyle}> - <Button - className={clsx( - tab === 'projects' ? classes.selectedTabStyle : null, - classes.tabStyle, - )} - onClick={() => { - switchTab('projects', props, window.location.href); - const params = getQueryParams(window.location.href); - handleSetState({ loading: true }); - - handleSetState( - fetchPage(null, props, params.get('q'), params.get('tab')), - ); - }} - > - {t('searchResults.projects')} - </Button> - - <Button - className={clsx( - tab === 'creators' ? classes.selectedTabStyle : null, - classes.tabStyle, - )} - onClick={() => { - switchTab('creators', props, window.location.href); - const params = getQueryParams(window.location.href); - handleSetState({ loading: true }); - - handleSetState( - fetchPage(null, props, params.get('q'), params.get('tab')), - ); - }} - > - {t('searchResults.creators')} - </Button> - </Box> {results && results.length > 0 ? ( - tab === 'projects' ? ( - <Container className={classes.mainContainerStyle}> - <Grid container> - <Grid item xs={12}> - <Typography - className={classes.pageHeaderStyle} - variant="h3" - gutterBottom - > - {count}{' '} - {count > 1 - ? t('searchResults.resultsFound') - : t('searchResults.resultFound')} - </Typography> - </Grid> - {results.map(project => ( - <Grid - item - xs={12} - sm={6} - md={4} - className={classes.projectGridStyle} - align="center" - > - <Project - project={project} - key={project.id} - updateProjects={res => - handleSetState(updateProjects(res, state, props, toast)) - } - {...props} - /> - </Grid> - ))} + <Container className={classes.mainContainerStyle}> + <Grid container> + <Grid item xs={12}> + <Typography + className={classes.pageHeaderStyle} + variant="h3" + gutterBottom + > + {count}{' '} + {count > 1 + ? t('searchResults.resultsFound') + : t('searchResults.resultFound')}{' '} + "{getQueryParams(window.location.href).get('q')}" + </Typography> </Grid> - <ButtonGroup - aria-label={t('searchResults.ariaLabels.prevNxtButtons')} - className={classes.buttonGroupStyle} - > - {prev_page ? ( - <CustomButton - className={classes.floatLeft} - size="large" - startIcon={<NavigateBeforeIcon />} - onClick={( - _, - page = getQueryParams(prev_page).get('page'), - ) => { - handleSetState({ loading: true }); - handleSetState( - fetchPage( - page, - props, - getQueryParams(prev_page).get('q'), - tab, - ), - ); - }} - primaryButtonStyle - > - {t('searchResults.prev')} - </CustomButton> - ) : null} - {next_page ? ( - <CustomButton - className={classes.floatRight} - size="large" - endIcon={<NavigateNextIcon />} - onClick={( - _, - page = getQueryParams(next_page, tab).get('page'), - ) => { - handleSetState({ loading: true }); - handleSetState( - fetchPage( - page, - props, - getQueryParams(next_page, tab).get('q'), - tab, - ), - ); - }} - primaryButtonStyle - > - {t('searchResults.next')} - </CustomButton> - ) : null} - </ButtonGroup> - </Container> - ) : ( - <Container className={classes.mainContainerStyle}> - <Grid container> - <Grid item xs={12}> - <Typography - className={classes.pageHeaderStyle} - variant="h3" - gutterBottom - > - {count}{' '} - {count > 1 - ? t('searchResults.resultsFound') - : t('searchResults.resultFound')} - </Typography> - </Grid> - {buildCreatorProfiles( - results, - { classes, common_classes }, - props, - state, - handleSetState, - )} - </Grid> - <ButtonGroup - aria-label={t('searchResults.ariaLabels.prevNxtButtons')} - className={classes.buttonGroupStyle} - > - {prev_page ? ( - <CustomButton - className={classes.floatLeft} - size="large" - startIcon={<NavigateBeforeIcon />} - onClick={( - _, - page = getQueryParams(prev_page, tab).get('page'), - ) => { - handleSetState({ loading: true }); - handleSetState( - fetchPage( - page, - props, - getQueryParams(prev_page, tab).get('q'), - tab, - ), - ); - }} - primaryButtonStyle - > - {t('searchResults.prev')} - </CustomButton> - ) : null} - {next_page ? ( - <CustomButton - className={classes.floatRight} - size="large" - endIcon={<NavigateNextIcon />} - onClick={( - _, - page = getQueryParams(next_page, tab).get('page'), - ) => { - handleSetState({ loading: true }); - handleSetState( - fetchPage( - page, - props, - getQueryParams(next_page, tab).get('q'), - tab, - ), - ); - }} - primaryButtonStyle - > - {t('searchResults.next')} - </CustomButton> - ) : null} - </ButtonGroup> - </Container> - ) + {getResults( + getQueryParams(window.location.href).get('type'), + results, + )} + </Grid> + <ButtonGroup + aria-label={t('searchResults.ariaLabels.prevNxtButtons')} + className={classes.buttonGroupStyle} + > + {prev_page ? ( + <CustomButton + className={classes.floatLeft} + size="large" + startIcon={<NavigateBeforeIcon />} + onClick={( + _, + page = getQueryParams(prev_page).get('page'), + ) => { + handleSetState({ loading: true }); + handleSetState( + fetchPage( + page, + props, + getQueryParams(prev_page).get('q'), + getQueryParams(window.location.href).get('type'), + ), + ); + }} + primaryButtonStyle + > + {t('searchResults.prev')} + </CustomButton> + ) : null} + {next_page ? ( + <CustomButton + className={classes.floatRight} + size="large" + endIcon={<NavigateNextIcon />} + onClick={( + _, + page = getQueryParams(next_page).get('page'), + ) => { + handleSetState({ loading: true }); + handleSetState( + fetchPage( + page, + props, + getQueryParams(next_page).get('q'), + getQueryParams(window.location.href).get('type'), + ), + ); + }} + primaryButtonStyle + > + {t('searchResults.next')} + </CustomButton> + ) : null} + </ButtonGroup> + </Container> ) : ( <ErrorPage style={{ width: '100vw' }} @@ -370,6 +266,7 @@ SearchResults.propTypes = { auth: PropTypes.object.isRequired, searchProjects: PropTypes.func.isRequired, searchCreators: PropTypes.func.isRequired, + searchTags: PropTypes.func.isRequired, toggleFollow: PropTypes.func.isRequired, toggleLike: PropTypes.func.isRequired, toggleSave: PropTypes.func.isRequired, @@ -389,6 +286,9 @@ const mapDispatchToProps = dispatch => { searchCreators: args => { return dispatch(CreatorActions.searchCreators(args)); }, + searchTags: args => { + return dispatch(ProjectActions.searchTags(args)); + }, toggleFollow: args => { return dispatch(CreatorActions.toggleFollow(args)); }, diff --git a/zubhub_frontend/zubhub/src/views/search_results/searchResultsScripts.js b/zubhub_frontend/zubhub/src/views/search_results/searchResultsScripts.js index b41b6985a..c3a2f48a5 100644 --- a/zubhub_frontend/zubhub/src/views/search_results/searchResultsScripts.js +++ b/zubhub_frontend/zubhub/src/views/search_results/searchResultsScripts.js @@ -1,40 +1,36 @@ +export const SearchType = { + CREATORS: 'creators', + PROJECTS: 'projects', + TAGS: 'tags', +}; + +export const ProjectSearchCriteria = { + CATEGORY: 0, + TAG: 1, + TITLE_DESCRIPTION: 2, +} + /** * @function getQueryParams * @author Raymond Ndibe <ndiberaymond1@gmail.com> * * @todo - describe function's signature */ -export const getQueryParams = (url, tab) => { +export const getQueryParams = url => { let params = new URL(url); params = new URLSearchParams(params.search); - if (tab) { - params.set('tab', tab); - } return params; }; -/** - * @function switchTab - * @author Raymond Ndibe <ndiberaymond1@gmail.com> - * - * @todo - describe function's signature - */ -export const switchTab = (tab, props, url) => { - props.history.push({ - pathname: props.location.pathname, - search: getQueryParams(url, tab).toString(), - }); -}; - /** * @function fetchPage * @author Raymond Ndibe <ndiberaymond1@gmail.com> * * @todo - describe function's signature */ -export const fetchPage = (page, props, query_string, tab) => { - if (!tab || tab === 'projects' || tab !== 'creators') { +export const fetchPage = (page, props, query_string, type) => { + if (type === SearchType.PROJECTS) { return props.searchProjects({ page, query_string, @@ -42,13 +38,22 @@ export const fetchPage = (page, props, query_string, tab) => { token: props.auth.token, tab: 'projects', }); - } else if (tab === 'creators') { + } else if (type === SearchType.CREATORS) { return props.searchCreators({ page, query_string, t: props.t, token: props.auth.token, - tab, + tab: 'creators', + }); + } else { + return props.searchProjects({ + page, + query_string, + t: props.t, + token: props.auth.token, + criteria: ProjectSearchCriteria.TAG, + tab: 'tags', }); } }; From db8fc998288cb3ff9cde1b33225f5f8bb7e25293 Mon Sep 17 00:00:00 2001 From: alicendeh <alicendeh16@gmail.com> Date: Mon, 11 Apr 2022 08:04:17 +0100 Subject: [PATCH 3/5] fixed issue with Clap-bookmark-views --- .../project_details/projectDetailsStyles.js | 13 ++- .../views/project_details/ProjectDetails.jsx | 85 ++++++++++--------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js index f06d07d48..cf8690751 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js @@ -105,9 +105,17 @@ const styles = theme => ({ height: '3.5em', width: '100%', flexDirection: 'row', - justifyContent: 'flex-start', + justifyContent: 'flex-end', + paddingRight: 12, }, }, + iconsBoxStyle: { + [theme.breakpoints.down('1080')]: { + display: 'flex', + paddingRight: 4, + }, + }, + actionBoxButtonStyle: { margin: '0.5em', display: 'flex', @@ -117,7 +125,8 @@ const styles = theme => ({ '& span': { display: 'flex', flexDirection: 'column' }, [theme.breakpoints.down('1080')]: { flexDirection: 'row', - marginBottom: '1em', + margin: '0.2em', + padding: '7px', '& span': { flexDirection: 'row', }, diff --git a/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx b/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx index 57aa7fd61..f3a00b54d 100644 --- a/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx +++ b/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx @@ -196,7 +196,7 @@ function ProjectDetails(props) { </Typography> </Link> {project.creator.id === props.auth.id ? ( - <> + <Grid container justify="flex-end"> <Link className={classes.textDecorationNone} to={`/projects/${project.id}/edit`} @@ -219,7 +219,7 @@ function ProjectDetails(props) { > {t('projectDetails.project.delete.label')} </CustomButton> - </> + </Grid> ) : ( <CustomButton className={common_classes.marginLeft1em} @@ -246,10 +246,7 @@ function ProjectDetails(props) { xs={12} sm={12} md={12} - className={clsx( - classes.positionRelative, - classes.marginBottom1em, - )} + className={clsx(classes.positionRelative)} > <Grid item @@ -318,19 +315,21 @@ function ProjectDetails(props) { handleSetState(toggleLike(e, props, project.id)) } > - {project.likes.includes(props.auth.id) ? ( - <ClapIcon - arial-label={t( - 'projectDetails.ariaLabels.likeButton.unlilke', - )} - /> - ) : ( - <ClapBorderIcon - arial-label={t( - 'projectDetails.ariaLabels.likeButton.like', - )} - /> - )} + <Box className={classes.iconsBoxStyle}> + {project.likes.includes(props.auth.id) ? ( + <ClapIcon + arial-label={t( + 'projectDetails.ariaLabels.likeButton.unlilke', + )} + /> + ) : ( + <ClapBorderIcon + arial-label={t( + 'projectDetails.ariaLabels.likeButton.like', + )} + /> + )} + </Box> <Typography> {nFormatter(project.likes.length)} </Typography> @@ -345,19 +344,21 @@ function ProjectDetails(props) { handleSetState(toggleSave(e, props, project.id)) } > - {project.saved_by.includes(props.auth.id) ? ( - <BookmarkIcon - aria-label={t( - 'projectDetails.ariaLabels.saveButton.unsave', - )} - /> - ) : ( - <BookmarkBorderIcon - aria-label={t( - 'projectDetails.ariaLabels.saveButton.save', - )} - /> - )} + <Box className={classes.iconsBoxStyle}> + {project.saved_by.includes(props.auth.id) ? ( + <BookmarkIcon + aria-label={t( + 'projectDetails.ariaLabels.saveButton.unsave', + )} + /> + ) : ( + <BookmarkBorderIcon + aria-label={t( + 'projectDetails.ariaLabels.saveButton.save', + )} + /> + )} + </Box> </CustomButton> <Typography color="textSecondary" @@ -365,7 +366,9 @@ function ProjectDetails(props) { component="span" className={classes.actionBoxButtonStyle} > - <VisibilityIcon /> + <Box className={classes.iconsBoxStyle}> + <VisibilityIcon /> + </Box> <Typography>{project.views_count}</Typography> </Typography> </Box> @@ -492,18 +495,16 @@ function ProjectDetails(props) { > <DialogTitle id="delete-project"> <Typography variant="h4"> - {t('projectDetails.project.delete.dialog.primary')} + {t('projectDetails.project.delete.dialog.primary')} </Typography> </DialogTitle> - {delete_project_dialog_error !== null && (<Box - component="p" - className={classes.errorBox} - > - <Box component="span" className={classes.error}> - {delete_project_dialog_error} + {delete_project_dialog_error !== null && ( + <Box component="p" className={classes.errorBox}> + <Box component="span" className={classes.error}> + {delete_project_dialog_error} + </Box> </Box> - - </Box>)} + )} <DialogContent> <Typography> {t('projectDetails.project.delete.dialog.secondary')} From 4220277012f3a0aacfdf610238a41f8979e49808 Mon Sep 17 00:00:00 2001 From: alicendeh <alicendeh16@gmail.com> Date: Wed, 13 Apr 2022 07:24:17 +0100 Subject: [PATCH 4/5] Reduced the width of clap-bookmark-view icons --- .../project_details/projectDetailsStyles.js | 2 +- .../views/project_details/ProjectDetails.jsx | 135 +++++++++--------- 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js index a5be98443..8296f6f38 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/views/project_details/projectDetailsStyles.js @@ -104,7 +104,7 @@ const styles = theme => ({ [theme.breakpoints.down('1080')]: { position: 'static', height: '3.5em', - width: '100%', + width: 'fit-content', flexDirection: 'row', justifyContent: 'flex-end', paddingRight: 12, diff --git a/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx b/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx index f7bcdf0ce..5f69a5a1b 100644 --- a/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx +++ b/zubhub_frontend/zubhub/src/views/project_details/ProjectDetails.jsx @@ -303,75 +303,76 @@ function ProjectDetails(props) { </a> ) : null} </Grid> - {/* box style here */} - <Box className={classes.actionBoxStyle}> - <CustomButton - className={classes.actionBoxButtonStyle} - size="small" - aria-label={t( - 'projectDetails.ariaLabels.likeButton.label', - )} - variant="extended" - onClick={e => - handleSetState(toggleLike(e, props, project.id)) - } - > - <Box className={classes.iconsBoxStyle}> - {project.likes.includes(props.auth.id) ? ( - <ClapIcon - arial-label={t( - 'projectDetails.ariaLabels.likeButton.unlilke', - )} - /> - ) : ( - <ClapBorderIcon - arial-label={t( - 'projectDetails.ariaLabels.likeButton.like', - )} - /> + <Box display="flex" justifyContent="flex-end"> + <Box className={classes.actionBoxStyle}> + <CustomButton + className={classes.actionBoxButtonStyle} + size="small" + aria-label={t( + 'projectDetails.ariaLabels.likeButton.label', )} - </Box> - <Typography> - {nFormatter(project.likes.length)} - </Typography> - </CustomButton> - <CustomButton - className={classes.actionBoxButtonStyle} - size="small" - aria-label={t( - 'projectDetails.ariaLabels.saveButton.label', - )} - onClick={e => - handleSetState(toggleSave(e, props, project.id)) - } - > - <Box className={classes.iconsBoxStyle}> - {project.saved_by.includes(props.auth.id) ? ( - <BookmarkIcon - aria-label={t( - 'projectDetails.ariaLabels.saveButton.unsave', - )} - /> - ) : ( - <BookmarkBorderIcon - aria-label={t( - 'projectDetails.ariaLabels.saveButton.save', - )} - /> + variant="extended" + onClick={e => + handleSetState(toggleLike(e, props, project.id)) + } + > + <Box className={classes.iconsBoxStyle}> + {project.likes.includes(props.auth.id) ? ( + <ClapIcon + arial-label={t( + 'projectDetails.ariaLabels.likeButton.unlilke', + )} + /> + ) : ( + <ClapBorderIcon + arial-label={t( + 'projectDetails.ariaLabels.likeButton.like', + )} + /> + )} + </Box> + <Typography> + {nFormatter(project.likes.length)} + </Typography> + </CustomButton> + <CustomButton + className={classes.actionBoxButtonStyle} + size="small" + aria-label={t( + 'projectDetails.ariaLabels.saveButton.label', )} - </Box> - </CustomButton> - <Typography - color="textSecondary" - variant="caption" - component="span" - className={classes.actionBoxButtonStyle} - > - <Box className={classes.iconsBoxStyle}> - <VisibilityIcon /> - </Box> - <Typography>{project.views_count}</Typography> - </Typography> + onClick={e => + handleSetState(toggleSave(e, props, project.id)) + } + > + <Box className={classes.iconsBoxStyle}> + {project.saved_by.includes(props.auth.id) ? ( + <BookmarkIcon + aria-label={t( + 'projectDetails.ariaLabels.saveButton.unsave', + )} + /> + ) : ( + <BookmarkBorderIcon + aria-label={t( + 'projectDetails.ariaLabels.saveButton.save', + )} + /> + )} + </Box> + </CustomButton> + <Typography + color="textSecondary" + variant="caption" + component="span" + className={classes.actionBoxButtonStyle} + > + <Box className={classes.iconsBoxStyle}> + <VisibilityIcon /> + </Box> + <Typography>{project.views_count}</Typography> + </Typography> + </Box> </Box> </Grid> {project.images && project.images.length > 0 ? ( From 8f69cb836c3ac7be7e7d52dc8db9f4e1800cf1f3 Mon Sep 17 00:00:00 2001 From: alicendeh <alicendeh16@gmail.com> Date: Fri, 15 Apr 2022 18:15:51 +0100 Subject: [PATCH 5/5] a --- zubhub_frontend/zubhub/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zubhub_frontend/zubhub/package-lock.json b/zubhub_frontend/zubhub/package-lock.json index 33c236238..082d96f12 100644 --- a/zubhub_frontend/zubhub/package-lock.json +++ b/zubhub_frontend/zubhub/package-lock.json @@ -4203,9 +4203,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001322", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz", - "integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==" + "version": "1.0.30001263", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001263.tgz", + "integrity": "sha512-doiV5dft6yzWO1WwU19kt8Qz8R0/8DgEziz6/9n2FxUasteZNwNNYSmJO3GLBH8lCVE73AB1RPDPAeYbcO5Cvw==" }, "capture-exit": { "version": "2.0.0",