diff --git a/backend/app/plugins/wiaas/includes/admin/class-wiaas-admin-organization.php b/backend/app/plugins/wiaas/includes/admin/class-wiaas-admin-organization.php index c063411..b711632 100644 --- a/backend/app/plugins/wiaas/includes/admin/class-wiaas-admin-organization.php +++ b/backend/app/plugins/wiaas/includes/admin/class-wiaas-admin-organization.php @@ -141,7 +141,7 @@ class Wiaas_Admin_Organization { * @param $admin_bar */ public static function add_role_switcher_menu($admin_bar) { - if (is_super_admin()) { + if (get_current_user_id() === Wiaas_Authentication::SUPER_ADMIN_USER_ID) { $roles = array( 'administrator' ); } else { $organization_id = wiaas_get_current_user_organization_id(); diff --git a/frontend/src/actions/coMarket/coMarketPackagesActions.js b/frontend/src/actions/coMarket/coMarketPackagesActions.js index 45b1a6e..17be580 100644 --- a/frontend/src/actions/coMarket/coMarketPackagesActions.js +++ b/frontend/src/actions/coMarket/coMarketPackagesActions.js @@ -5,9 +5,12 @@ import HtmlClient from '../../helpers/HtmlClient'; import { REQUEST_SHOP_PACKAGES, RECIEVE_SHOP_PACKAGES, + SEARCH_SHOP_PACKAGES_REQUEST, + SEARCH_SHOP_PACKAGES_RESULT, REQUEST_SHOPS, RECEIVE_SHOPS, - SELECT_SHOP + SELECT_SHOP, + SHOP_PAGE_SIZE } from '../../constants/coMarketConstants'; import { fromWCPackage } from '../../helpers/PackageHelper'; @@ -17,30 +20,60 @@ const requestShopPackages = () => ({ type: REQUEST_SHOP_PACKAGES, isLoading: true }); -const recieveShopPackages = (json) => ({ +const recieveShopPackages = (packages, page = 1) => ({ type: RECIEVE_SHOP_PACKAGES, isLoading: false, - shopPackages: json + shopPackages: packages, + page: page, }); -export const fetchShopPackages = (shop, search) => { +export const fetchShopPackages = (shop, page = 1) => { return dispatch => { dispatch(requestShopPackages()); - let searchParam = search ? '?search=' +search : '' return client.fetch({ - url: `${API_SERVER}/wp-json/wc/v2/products?shop_id=${shop.id}` + searchParam, + url: `${API_SERVER}/wp-json/wc/v2/products?shop_id=${shop.id}&page=${page}&per_page=${SHOP_PAGE_SIZE + 1}`, }) .then(response => { if (response.data) { - dispatch(recieveShopPackages(response.data.map(wcPackage => fromWCPackage(wcPackage)))) + const packages = response.data.map(wcPackage => fromWCPackage(wcPackage)); + dispatch(recieveShopPackages(packages, page)) } }) .catch(error => { client.onError(error, dispatch); }); } -} +}; + +const searchShopPackagesRequest = () => ({ + type: SEARCH_SHOP_PACKAGES_REQUEST, + isLoading: true +}); +const searchShopPackagesResult = (packages) => ({ + type: SEARCH_SHOP_PACKAGES_RESULT, + isLoading: false, + shopPackages: packages, +}); + +export const searchShopPackages = (shop, search) => { + return dispatch => { + dispatch(searchShopPackagesRequest()); + + return client.fetch({ + url: `${API_SERVER}/wp-json/wc/v2/products?shop_id=${shop.id}&search=${search}`, + }) + .then(response => { + if (response.data) { + const packages = response.data.map(wcPackage => fromWCPackage(wcPackage)); + dispatch(searchShopPackagesResult(packages)) + } + }) + .catch(error => { + client.onError(error, dispatch); + }); + } +}; const requestShops = () => ({ type: REQUEST_SHOPS diff --git a/frontend/src/constants/coMarketConstants.js b/frontend/src/constants/coMarketConstants.js index 57719df..e2e64e6 100644 --- a/frontend/src/constants/coMarketConstants.js +++ b/frontend/src/constants/coMarketConstants.js @@ -2,6 +2,9 @@ const MODULE = 'CO_MARKET_'; export const REQUEST_SHOP_PACKAGES = MODULE + 'REQUEST_SHOP_PACKAGES'; export const RECIEVE_SHOP_PACKAGES = MODULE + 'RECIEVE_SHOP_PACKAGES'; +export const SEARCH_SHOP_PACKAGES_REQUEST = MODULE + 'SEARCH_SHOP_PACKAGES_REQUEST'; +export const SEARCH_SHOP_PACKAGES_RESULT = MODULE + 'SEARCH_SHOP_PACKAGES_RESULT'; + export const REQUEST_SHOPS = MODULE + 'REQUEST_SHOPS'; export const RECEIVE_SHOPS = MODULE + 'RECEIVE_SHOPS'; export const SELECT_SHOP = MODULE + 'SELECT_SHOP'; @@ -105,6 +108,9 @@ export const coMarketTexts = { }, buttons: { ADD_TO_CART: 'Add to cart', - DETAILS: 'Details' + DETAILS: 'Details', + LOAD_MORE: 'Load more' } } + +export const SHOP_PAGE_SIZE = 4; diff --git a/frontend/src/containers/coMarket/CoMarketNavContainer.jsx b/frontend/src/containers/coMarket/CoMarketNavContainer.jsx index ab74e99..f41ddbe 100644 --- a/frontend/src/containers/coMarket/CoMarketNavContainer.jsx +++ b/frontend/src/containers/coMarket/CoMarketNavContainer.jsx @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {Row, Col, Input} from 'reactstrap'; -import {fetchShopPackages} from '../../actions/coMarket/coMarketPackagesActions'; +import {searchShopPackages, fetchShopPackages} from '../../actions/coMarket/coMarketPackagesActions'; import {coMarketTexts} from '../../constants/coMarketConstants'; class CoMarketNavContainer extends Component { @@ -15,9 +15,12 @@ class CoMarketNavContainer extends Component { } handleSearchChange(event) { - this.setState({searchValue: event.target.value}); - if (this.props.selectedShop) { - this.props.dispatch(fetchShopPackages(this.props.selectedShop, event.target.value)); + const s = event.target.value; + this.setState({searchValue: s}); + if (this.props.selectedShop && s) { + this.props.dispatch(searchShopPackages(this.props.selectedShop, s)); + } else if (this.props.selectedShop) { + this.props.dispatch(fetchShopPackages(this.props.selectedShop)); } } @@ -42,7 +45,8 @@ class CoMarketNavContainer extends Component { } const mapStateToProps = (state) => ({ - selectedShop: state.coMarketPackagesReducer.selectedShop + selectedShop: state.coMarketPackagesReducer.selectedShop, + shopPage: state.coMarketPackagesReducer.shopPage, }); export default connect(mapStateToProps)(CoMarketNavContainer); diff --git a/frontend/src/containers/coMarket/CoMarketPackagesContainer.jsx b/frontend/src/containers/coMarket/CoMarketPackagesContainer.jsx index fc334ad..f648445 100644 --- a/frontend/src/containers/coMarket/CoMarketPackagesContainer.jsx +++ b/frontend/src/containers/coMarket/CoMarketPackagesContainer.jsx @@ -3,12 +3,14 @@ import {connect} from 'react-redux'; import { Navbar, Row, - Col + Col, + Button } from 'reactstrap'; import ShopItem from './components/ShopItem.jsx'; import WiaasBox from '../../mainComponents/box/WiaasBox.jsx'; import CoMarketNavContainer from './CoMarketNavContainer.jsx'; import {fetchShopPackages} from '../../actions/coMarket/coMarketPackagesActions'; +import {coMarketTexts} from "../../constants/coMarketConstants"; class CoMarketPackagesContainer extends Component { componentDidMount() { @@ -17,8 +19,14 @@ class CoMarketPackagesContainer extends Component { } } + onLoadMore() { + if (this.props.selectedShop) { + this.props.dispatch(fetchShopPackages(this.props.selectedShop, this.props.shopPage + 1)); + } + } + render() { - const {shopPackages, selectedShop, isLoading} = this.props; + const {shopPackages, selectedShop, isLoading, shopHasMorePackages, shopSearch} = this.props; return (
@@ -41,22 +49,34 @@ class CoMarketPackagesContainer extends Component { + { + (shopPackages) && + shopPackages.map((shopPackage, mapKey) => ) + } { isLoading && } - { - (shopPackages && !isLoading) && - shopPackages.map((shopPackage, mapKey) => ) - } + { + shopHasMorePackages && !isLoading && !shopSearch && ( + + + + + ) + }
); } @@ -64,6 +84,9 @@ class CoMarketPackagesContainer extends Component { const mapStateToProps = (state) => ({ shopPackages: state.coMarketPackagesReducer.shopPackages, + shopHasMorePackages: state.coMarketPackagesReducer.shopHasMorePackages, + shopPage: state.coMarketPackagesReducer.shopPage, + shopSearch: state.coMarketPackagesReducer.shopSearch, selectedShop: state.coMarketPackagesReducer.selectedShop, isLoading: state.coMarketPackagesReducer.isLoading }); diff --git a/frontend/src/containers/coMarket/components/CoMarketCatalogSelect.jsx b/frontend/src/containers/coMarket/components/CoMarketCatalogSelect.jsx index 19c63fc..f0bfdab 100644 --- a/frontend/src/containers/coMarket/components/CoMarketCatalogSelect.jsx +++ b/frontend/src/containers/coMarket/components/CoMarketCatalogSelect.jsx @@ -9,7 +9,6 @@ class CoMarketCatalogSelect extends Component { super(props); this.handleShopChange = this.handleShopChange.bind(this); - this.handleSearchChange = this.handleSearchChange.bind(this); this.state = { searchValue : '' }; @@ -41,14 +40,6 @@ class CoMarketCatalogSelect extends Component { this.props.dispatch(fetchShopPackages(shop)); } - handleSearchChange(event) { - this.setState({searchValue: event.target.value}); - - if (this.props.selectedShop) { - this.props.dispatch(fetchShopPackages(this.props.selectedShop, event.target.value)); - } - } - render() { const {shops, selectedShop, idPackage, activeSubmodule} = this.props; const isDisabled = (idPackage || this.props.activeModule === 'cart') ? true : false; diff --git a/frontend/src/containers/coMarket/style/CoMarket.scss b/frontend/src/containers/coMarket/style/CoMarket.scss index 4dd73d3..bf5bec9 100644 --- a/frontend/src/containers/coMarket/style/CoMarket.scss +++ b/frontend/src/containers/coMarket/style/CoMarket.scss @@ -89,6 +89,18 @@ font-weight: $font-weight; } + .shop-package-load-more-btn { + border-radius: 0; + color: #e25c56; + font-size: 1rem; + border: 1px solid rgba(0, 0, 0, 0.125); + + &:hover, &:active, &:focus { + background: $hoverColor; + box-shadow: none !important; + } + } + .search-layer{ display:flex; align-items:center; diff --git a/frontend/src/reducers/coMarket/coMarketPackagesReducers.js b/frontend/src/reducers/coMarket/coMarketPackagesReducers.js index 57a48ad..97a2db9 100644 --- a/frontend/src/reducers/coMarket/coMarketPackagesReducers.js +++ b/frontend/src/reducers/coMarket/coMarketPackagesReducers.js @@ -2,7 +2,10 @@ import { RECIEVE_SHOP_PACKAGES, RECEIVE_SHOPS, SELECT_SHOP, - REQUEST_SHOP_PACKAGES + REQUEST_SHOP_PACKAGES, + SHOP_PAGE_SIZE, + SEARCH_SHOP_PACKAGES_REQUEST, + SEARCH_SHOP_PACKAGES_RESULT, } from '../../constants/coMarketConstants'; const moduleReducers = {}; @@ -13,9 +16,59 @@ moduleReducers[REQUEST_SHOP_PACKAGES] = (state, action) => { }); }; -moduleReducers[RECIEVE_SHOP_PACKAGES] = (state, action) => { +moduleReducers[RECIEVE_SHOP_PACKAGES] = (state = {}, action) => { + + // implement paging + // paging is implemented in a way that with every request one more package is requested on top of + // page size number + // this means that if retrieved number of packages is greater than page size there may be more packages + + const shopPage = action.page || 1; + let shopPackages = []; + let shopPackagesDiff = []; + let retrievedShopPackages = action.shopPackages || []; + + // append newly retrieved packages to existing ones if more packages are loaded + if (shopPage > state.shopPage) { + shopPackages = state.loadedShopPackages || []; + + // get ignored packages from previous request + const oldShopPackagesDiff = state.shopPackagesDiff || []; + // append packages ignored previous time to the beginning + retrievedShopPackages = oldShopPackagesDiff.concat(retrievedShopPackages); + } + + // if number of packages is greater than page size there may be more of them to retrieve + const hasMorePages = retrievedShopPackages.length > SHOP_PAGE_SIZE; + + // ignore all packages over limit of page size (they will be displayed at the beginning of the next request) + while (retrievedShopPackages.length > SHOP_PAGE_SIZE) { + shopPackagesDiff.push(retrievedShopPackages.pop()); + } + // append packages from this page to existing ones + shopPackages = shopPackages.concat(retrievedShopPackages); + return Object.assign({}, state, { - shopPackages: action.shopPackages, + shopPackages: shopPackages, + loadedShopPackages: shopPackages, + shopPackagesDiff: shopPackagesDiff, + shopPage: shopPage, + shopSearch: false, + shopHasMorePackages: hasMorePages, + isLoading: action.isLoading + }); +}; + +moduleReducers[SEARCH_SHOP_PACKAGES_REQUEST] = (state, action) => { + return Object.assign({}, state, { + isLoading: action.isLoading + }); +}; + +moduleReducers[SEARCH_SHOP_PACKAGES_RESULT] = (state, action) => { + return Object.assign({}, state, { + shopPackages: action.shopPackages || [], + shopSearch: true, isLoading: action.isLoading }); };