diff --git a/src/components/Grid/GridView.jsx b/src/components/Grid/GridView.jsx
index ede933a73..d555490f0 100644
--- a/src/components/Grid/GridView.jsx
+++ b/src/components/Grid/GridView.jsx
@@ -35,20 +35,17 @@ const GridView = props => {
) : (
- {resultSet.length ? (
-
-
-
- {resultSet.map(renderItem)}
-
-
-
- ) : (
-
- {/* TODO replace this with proper style */}
-
No results
+
+
+
+ {resultSet.length ? (
+ resultSet.map(renderItem)
+ ) : (
+
No results
+ )}
- )}
+
+
)
)
@@ -77,6 +74,15 @@ const GridView = props => {
>
{[...resultSet, ...placeholders].map(renderItem)}
+ {totalCount === 0 &&
}
@@ -94,20 +100,6 @@ const GridView = props => {
)
}
- if (totalCount === 0) {
- return (
-
- )
- }
-
return (
{infiniteScroll ? renderGridWithInfiniteScroll() : renderGridWithPagination()}
diff --git a/src/components/TopBar/Filters.js b/src/components/TopBar/Filters.js
deleted file mode 100644
index 81033920f..000000000
--- a/src/components/TopBar/Filters.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import _ from 'lodash'
-import Select from '../../components/Select/Select'
-
-const Filters = ({ criteria, applyFilters, projectTypes }) => {
- const types = _.map(projectTypes, type => {
- return { value: type.key, label: type.displayName }
- })
-
- // TODO add segments list
- // const segments = _.map([], segment => {
- // return { value: segment.id, label: segment.value }
- // })
-
- return (
-
-
Filters
-
-
-
-
-
-
-
- {/*
-
-
-
-
*/}
-
-
-
- )
-}
-
-Filters.propTypes = {
- criteria: PropTypes.object.isRequired,
- applyFilters: PropTypes.func.isRequired,
- projectTypes: PropTypes.arrayOf(PropTypes.shape({
- key: PropTypes.string.isRequired,
- displayName: PropTypes.string.isRequired,
- }))
-}
-
-export default Filters
diff --git a/src/components/TopBar/ProjectsToolBar.js b/src/components/TopBar/ProjectsToolBar.js
index 4a2d860d1..4a84d2683 100644
--- a/src/components/TopBar/ProjectsToolBar.js
+++ b/src/components/TopBar/ProjectsToolBar.js
@@ -5,17 +5,13 @@ import PropTypes from 'prop-types'
import querystring from 'query-string'
import { withRouter, Prompt } from 'react-router-dom'
import { connect } from 'react-redux'
-import cn from 'classnames'
import _ from 'lodash'
import SearchBar from 'appirio-tech-react-components/components/SearchBar/SearchBar'
import MenuBar from 'appirio-tech-react-components/components/MenuBar/MenuBar'
-import Filters from './Filters'
import NotificationsDropdown from '../NotificationsDropdown/NotificationsDropdownContainer'
import NewProjectNavLink from './NewProjectNavLink'
import MobileMenu from '../MobileMenu/MobileMenu'
import MobileMenuToggle from '../MobileMenu/MobileMenuToggle'
-import SearchFilter from '../../assets/icons/ui-filters.svg'
-import SearchIcon from '../../assets/icons/ui-16px-1_zoom.svg'
import { projectSuggestions, loadProjects, setInfiniteAutoload } from '../../projects/actions/loadProjects'
import { loadProjectsMetadata } from '../../actions/templates'
import { getNewProjectLink } from '../../helpers/projectHelper'
@@ -26,17 +22,13 @@ class ProjectsToolBar extends Component {
super(props)
this.state = {
errorCreatingProject: false,
- isFilterVisible: false,
isMobileMenuOpen: false,
isMobileSearchVisible: false
}
- this.state.isFilterVisible = sessionStorage.getItem('isFilterVisible') === 'true'
this.applyFilters = this.applyFilters.bind(this)
- this.toggleFilter = this.toggleFilter.bind(this)
this.handleTermChange = this.handleTermChange.bind(this)
this.handleSearch = this.handleSearch.bind(this)
this.toggleMobileMenu = this.toggleMobileMenu.bind(this)
- this.toggleMobileSearch = this.toggleMobileSearch.bind(this)
this.onLeave = this.onLeave.bind(this)
}
@@ -74,10 +66,6 @@ class ProjectsToolBar extends Component {
}
componentDidMount() {
- const contentDiv = document.getElementById('wrapper-main')
- if (this.state.isFilterVisible) {
- contentDiv.classList.add('with-filters')
- }
// sets window unload hook to show unsaved changes alert and persist incomplete project
window.addEventListener('beforeunload', this.onLeave)
}
@@ -125,38 +113,10 @@ class ProjectsToolBar extends Component {
this.routeWithParams(criteria)
}
- toggleFilter() {
- const {isFilterVisible, isMobileSearchVisible} = this.state
- const contentDiv = document.getElementById('wrapper-main')
- this.setState({isFilterVisible: !isFilterVisible}, () => {
- sessionStorage.setItem('isFilterVisible', (!isFilterVisible).toString())
- if (this.state.isFilterVisible) {
- contentDiv.classList.add('with-filters')
- } else {
- contentDiv.classList.remove('with-filters')
- }
- })
- // if open filters, close search panel on mobile
- if (!isFilterVisible && isMobileSearchVisible) {
- this.toggleMobileSearch()
- }
- }
-
toggleMobileMenu() {
this.setState({ isMobileMenuOpen: !this.state.isMobileMenuOpen })
}
- toggleMobileSearch() {
- const { isFilterVisible, isMobileSearchVisible } = this.state
-
- // if open mobile search and filter is visible, then close filter
- if (!isMobileSearchVisible && isFilterVisible) {
- this.toggleFilter()
- }
-
- this.setState({ isMobileSearchVisible: !isMobileSearchVisible })
- }
-
routeWithParams(criteria) {
// because criteria is changed disable infinite autoload
this.props.setInfiniteAutoload(false)
@@ -171,7 +131,7 @@ class ProjectsToolBar extends Component {
shouldComponentUpdate(nextProps, nextState) {
const { user, criteria, creatingProject, projectCreationError, searchTermTag, projectTypes } = this.props
- const { errorCreatingProject, isFilterVisible, isMobileMenuOpen, isMobileSearchVisible } = this.state
+ const { errorCreatingProject, isMobileMenuOpen, isMobileSearchVisible } = this.state
return (nextProps.user || {}).handle !== (user || {}).handle
|| (nextProps.user || {}).photoURL !== (this.props.user || {}).photoURL
|| JSON.stringify(nextProps.criteria) !== JSON.stringify(criteria)
@@ -180,24 +140,15 @@ class ProjectsToolBar extends Component {
|| nextProps.searchTermTag !== searchTermTag
|| !!nextProps.projectTypes && !projectTypes
|| nextState.errorCreatingProject !== errorCreatingProject
- || nextState.isFilterVisible !== isFilterVisible
|| nextState.isMobileMenuOpen !== isMobileMenuOpen
|| nextState.isMobileSearchVisible !== isMobileSearchVisible
}
render() {
- const { renderLogoSection, userMenu, userRoles, criteria, isPowerUser, user, mobileMenu, location, projectTypes, orgConfig } = this.props
- const { isFilterVisible, isMobileMenuOpen, isMobileSearchVisible } = this.state
+ const { renderLogoSection, userMenu, userRoles, isPowerUser, user, mobileMenu, location, orgConfig } = this.props
+ const { isMobileMenuOpen, isMobileSearchVisible } = this.state
const isLoggedIn = !!(userRoles && userRoles.length)
- let excludedFiltersCount = 1 // 1 for default sort criteria
- if (criteria.memberOnly) {
- // https://github.com/appirio-tech/connect-app/issues/1319
- // The switch should not count as a filter in the menu!
- excludedFiltersCount++
- }
- // Ignore status from filters count
- const noOfFilters = _.keys(_.omit(criteria, ['status', 'keyword'])).length - excludedFiltersCount
const onLeaveMessage = this.onLeave() || ''
const primaryNavigationItems = [
@@ -240,20 +191,6 @@ class ProjectsToolBar extends Component {
onSearch={ this.handleSearch }
onClearSearch={ this.handleSearch }
/>
-
}
@@ -277,15 +214,6 @@ class ProjectsToolBar extends Component {
/>
}
- { !!projectTypes && isFilterVisible && isLoggedIn &&
-
-
-
- }
{isMobileMenuOpen && }
)
diff --git a/src/components/TopBar/ProjectsToolBar.scss b/src/components/TopBar/ProjectsToolBar.scss
index 1f8f15286..c74eadc23 100644
--- a/src/components/TopBar/ProjectsToolBar.scss
+++ b/src/components/TopBar/ProjectsToolBar.scss
@@ -172,104 +172,6 @@
}
}
- .search-filter {
- width: 97px;
- margin-left: 2 * $base-unit;
-
- @media screen and (max-width: $screen-md - 1px) {
- border: 1px solid $tc-gray-80;
- border-radius: 2px;
- display: flex;
- height: 32px;
- margin: 0 auto;
- width: auto;
- }
-
- .tc-btn {
- display: flex;
- align-items: center;
- // justify-content: center;
- padding: 0 2 * $base-unit 0 10px;
- text-align: left;
- color: $tc-gray-40;
- background-color: transparent;
- border-radius: 2px;
- border: 0;
- font-size: 12px;
- position: relative;
-
- @media screen and (max-width: $screen-md - 1px) {
- text-align: center;
- width: 46px;
-
- &:first-child {
- border-right: 1px solid $tc-gray-80;
- }
-
- > svg {
- margin: 0 auto;
- }
- }
-
- &.mobile-search-toggle {
- display: none;
-
- @media screen and (max-width: $screen-md - 1px) {
- display: flex;
- }
-
- > svg {
- fill: $tc-gray-50;
- }
-
- &.active > svg {
- fill: $tc-white;
- }
- }
-
- svg.icon-search-filter {
- margin-right: 10px;
-
- @media screen and (max-width: $screen-md - 1px) {
- margin-right: auto;
- }
- }
-
- &.active {
- background: $tc-gray-80;
- box-shadow: inset 0px 1px 3px 0px $tc-gray-80;
-
- svg.icon-search-filter g {
- fill: $tc-white;
- }
- }
- }
-
- .filter-text {
- @media screen and (max-width: $screen-md - 1px) {
- display: none;
- }
- }
-
- .filter-indicator {
- width: 15px;
- height: 15px;
- margin-left: $base-unit;
- background-color: $tc-dark-blue;
- border-radius: 26px;
- color: $tc-white;
- display: flex;
- justify-content: center;
- align-items: center;
-
- @media screen and (max-width: $screen-md - 1px) {
- right: -8px;
- position: absolute;
- top: 3px;
- }
- }
- }
-
/* .bar__search */
.bar__search {
align-items: center;
diff --git a/src/projects/list/components/Projects/ProjectListFilterColHeader.jsx b/src/projects/list/components/Projects/ProjectListFilterColHeader.jsx
new file mode 100644
index 000000000..5e060984d
--- /dev/null
+++ b/src/projects/list/components/Projects/ProjectListFilterColHeader.jsx
@@ -0,0 +1,61 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import _ from 'lodash'
+import Dropdown from 'appirio-tech-react-components/components/Dropdown/Dropdown'
+import IconCarretDownNormal from '../../../../assets/icons/arrow-6px-carret-down-normal.svg'
+
+class ProjectListFilterColHeader extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.state = { value: '' }
+ this.setFilter = this.setFilter.bind(this)
+ this.onChange = this.onChange.bind(this)
+ // debounced setFilter function to prevent polluting server with requests
+ this.debouncedSetFilterCheck = _.debounce(this.setFilter, 500)
+ }
+
+ setFilter(newfilter) {
+ this.props.setFilter(this.props.filterName, newfilter)
+ }
+
+ onChange(event) {
+ this.setState({ value: event.target.value }, () => {
+ this.debouncedSetFilterCheck(this.state.value)
+ })
+ }
+
+ componentDidMount() {
+ this.setState({
+ value: this.props.value || ''
+ })
+ }
+
+ render() {
+ const {
+ title
+ } = this.props
+
+ return (
+
+ )
+ }
+}
+
+ProjectListFilterColHeader.propTypes = {
+ title: PropTypes.string,
+ filterName: PropTypes.string,
+ setFilter: PropTypes.func,
+ value: PropTypes.string
+}
+export default ProjectListFilterColHeader
diff --git a/src/projects/list/components/Projects/Projects.jsx b/src/projects/list/components/Projects/Projects.jsx
index 73e4256e8..cea4a57b5 100755
--- a/src/projects/list/components/Projects/Projects.jsx
+++ b/src/projects/list/components/Projects/Projects.jsx
@@ -40,6 +40,7 @@ class Projects extends Component {
this.onPageChange = this.onPageChange.bind(this)
this.applyFilters = this.applyFilters.bind(this)
this.applySearchFilter = this.applySearchFilter.bind(this)
+ this.setFilter = this.setFilter.bind(this)
this.changeView = this.changeView.bind(this)
this.init = this.init.bind(this)
this.removeScrollPosition = this.removeScrollPosition.bind(this)
@@ -164,6 +165,19 @@ class Projects extends Component {
this.routeWithParams(criteria)
}
+ setFilter(name, filter) {
+ let criteria = _.assign({}, this.props.criteria)
+ if(filter && filter !== '') {
+ const temp = {}
+ temp[`${name}`] = `*${filter}*`
+ criteria = _.assign({}, criteria, temp)
+ } else if(_.has(criteria, name)){
+ criteria = _.omit(criteria, name)
+ }
+
+ this.props.loadProjects(criteria)
+ }
+
changeView(view) {
this.setState({selectedView : view})
}
@@ -198,6 +212,8 @@ class Projects extends Component {
onChangeStatus={this.onChangeStatus}
projectsStatus={getStatusCriteriaText(criteria)}
newProjectLink={getNewProjectLink(orgConfig)}
+ setFilter={this.setFilter}
+ criteria={criteria}
/>
)
const cardView = (
diff --git a/src/projects/list/components/Projects/ProjectsGridView.jsx b/src/projects/list/components/Projects/ProjectsGridView.jsx
index 9744225e7..3640e15cf 100644
--- a/src/projects/list/components/Projects/ProjectsGridView.jsx
+++ b/src/projects/list/components/Projects/ProjectsGridView.jsx
@@ -8,6 +8,7 @@ import {
filterNotificationsByProjectId,
} from '../../../../routes/notifications/helpers/notifications'
import ProjectListTimeSortColHeader from './ProjectListTimeSortColHeader'
+import ProjectListFilterColHeader from './ProjectListFilterColHeader'
import GridView from '../../../../components/Grid/GridView'
import UserTooltip from '../../../../components/User/UserTooltip'
import { PROJECTS_LIST_PER_PAGE, SORT_OPTIONS, PROJECT_STATUS_COMPLETED, DATE_TO_USER_FIELD_MAP } from '../../../../config/constants'
@@ -26,7 +27,7 @@ const EnhancedProjectStatus = editableProjectStatus(ProjectStatus)
const ProjectsGridView = props => {
const { projects, members, totalCount, criteria, pageNum, sortHandler, currentUser, onPageChange,
error, isLoading, infiniteAutoload, setInfiniteAutoload, projectsStatus, onChangeStatus,
- applyFilters, projectTemplates, notifications, newProjectLink } = props
+ applyFilters, projectTemplates, notifications, newProjectLink, setFilter } = props
const currentSortField = _.get(criteria, 'sort', '')
// This 'little' array is the heart of the list component.
@@ -70,12 +71,11 @@ const ProjectsGridView = props => {
}
}, {
id: 'projects',
- headerLabel: 'Project',
+ headerLabel: ,
classes: 'item-projects',
sortable: false,
renderText: item => {
const url = `/projects/${item.id}`
- const code = _.get(item, 'details.utm.code', '')
// project notifications
const notReadNotifications = filterReadNotifications(notifications)
const unreadProjectUpdate = filterNotificationsByProjectId(notReadNotifications, item.id)
@@ -85,7 +85,6 @@ const ProjectsGridView = props => {
{(recentlyCreated || unreadProjectUpdate.length > 0) && }
{_.unescape(item.name)}
- {code && { applyFilters({ keyword: code }) }} dangerouslySetInnerHTML={{ __html: code }} />}
{
)
}
+ }, {
+ id: 'reference',
+ headerLabel: ,
+ sortable: false,
+ classes: 'item-ref-code',
+ renderText: item => {
+ const code = _.get(item, 'details.utm.code', '')
+ return (
+
+ {code}
+
+ )
+ }
}, {
id: 'updatedAt',
headerLabel: ,
@@ -121,7 +133,7 @@ const ProjectsGridView = props => {
}
}, {
id: 'customer',
- headerLabel: 'Customer',
+ headerLabel: ,
sortable: false,
classes: 'item-customer',
renderText: item => {
@@ -166,7 +178,7 @@ const ProjectsGridView = props => {
}
}, {
id: 'managers',
- headerLabel: 'Managers',
+ headerLabel: ,
sortable: false,
classes: 'item-manager',
renderText: item => {
@@ -256,6 +268,7 @@ ProjectsGridView.propTypes = {
pageNum: PropTypes.number.isRequired,
criteria: PropTypes.object.isRequired,
projectTemplates: PropTypes.array.isRequired,
+ setFilter: PropTypes.func,
}
export default ProjectsGridView
diff --git a/src/projects/list/components/Projects/ProjectsGridView.scss b/src/projects/list/components/Projects/ProjectsGridView.scss
index a87fce816..a8b1382d4 100644
--- a/src/projects/list/components/Projects/ProjectsGridView.scss
+++ b/src/projects/list/components/Projects/ProjectsGridView.scss
@@ -21,7 +21,11 @@ $screen-one-column: 720px;
.item-projects {
flex: 8 1 505px;
min-width: 505px;
- overflow: hidden;
+ }
+
+ .item-ref-code {
+ flex: 1 3 142px;
+ min-width: 142px;
}
.item-status-date {
@@ -32,7 +36,6 @@ $screen-one-column: 720px;
.item-customer {
flex: 2 2 150px;
min-width: 150px;
-
.spacing {
width: 100%;
}
@@ -115,6 +118,17 @@ $screen-one-column: 720px;
}
}
}
+
+ .filter-drop-down {
+ a.dropdown-menu-header.txt-link {
+ font-size: 13px;
+ color: $tc-gray-50;
+
+ &:focus {
+ color: $tc-dark-blue;
+ }
+ }
+ }
}
}
}
@@ -179,19 +193,6 @@ $screen-one-column: 720px;
color: $tc-black;
font-size: 15px;
}
-
- .item-ref-code {
- flex: none;
- justify-content: flex-end;
- background: $tc-gray-10;
- @include roboto-medium;
- font-size: 13px;
- color: $tc-gray-50;
- border-radius: 2px;
- line-height: 20px;
- padding: 0px 2 * $base-unit;
- cursor: pointer;
- }
}
.project-description {
@@ -590,4 +591,52 @@ $screen-one-column: 720px;
}
}
}
+
+ .filter-drop-down {
+ position: relative;
+ display: inline-block;
+ margin-left: 0px;
+
+ a {
+ outline: 0;
+ }
+
+ .txt-link {
+ position: relative;
+ padding-right: 16px;
+ display: block;
+ z-index: 3;
+
+ &:focus {
+ color: $tc-dark-blue;
+ outline: 0;
+ z-index: 3;
+ }
+
+ .icon-carret-down-normal {
+ font-size: 0;
+ line-height: 0;
+ width: 10px;
+ height: 6px;
+ content: "";
+ display: block;
+ position: absolute;
+ top: 5px;
+ right: 0;
+ }
+ }
+
+ .down-layer {
+ background: $tc-white;
+ border-radius: 5px;
+ min-width: 130px;
+ min-height: 45px;
+ position: absolute;
+ top: -18px;
+ left: -20px;
+ box-shadow: 0 0 10px rgba($tc-black, 0.2);
+ z-index: 3;
+ padding: 32px 5px 10px;
+ }
+ }
}