├── docs ├── detail.docx ├── Drawing1.pdf ├── Drawing1.vsdx ├── useful_notes.docx ├── DataDictionary_OnlineAuction.xls └── online_auction.sql ├── .gitignore ├── frontend ├── app │ ├── assets │ │ ├── img │ │ │ ├── logo.jpg │ │ │ └── logo_footer.jpg │ │ ├── styles │ │ │ ├── user │ │ │ │ ├── signin.css │ │ │ │ └── signup.css │ │ │ ├── sellItem.css │ │ │ ├── slideshow │ │ │ │ └── slideshow-style1.css │ │ │ ├── toppage │ │ │ │ ├── widgets │ │ │ │ │ ├── homeAppliances.css │ │ │ │ │ ├── laptopAndDesktops.css │ │ │ │ │ └── newArrivals.css │ │ │ │ └── homepage.css │ │ │ ├── cart │ │ │ │ └── viewCartItems.css │ │ │ └── item │ │ │ │ └── itemView.css │ │ └── styles-less │ │ │ ├── user │ │ │ ├── signin.less │ │ │ └── signup.less │ │ │ ├── slideshow │ │ │ └── slideshow-style1.less │ │ │ ├── sellItem.less │ │ │ ├── toppage │ │ │ ├── widgets │ │ │ │ ├── laptopAndDesktops.less │ │ │ │ ├── homeAppliances.less │ │ │ │ └── newArrivals.less │ │ │ └── homepage.less │ │ │ ├── cart │ │ │ └── viewCartItems.less │ │ │ └── item │ │ │ └── itemView.less │ ├── js │ │ ├── compiled-templates │ │ │ ├── searchpage.js │ │ │ ├── item.js │ │ │ ├── user.js │ │ │ ├── cart.js │ │ │ └── sell.js │ │ ├── constants │ │ │ └── modules │ │ │ │ └── constants.js │ │ ├── controllers │ │ │ ├── toppage │ │ │ │ └── homepageWidgetsCtrl.js │ │ │ ├── cart │ │ │ │ └── viewCartItemsCtrl.js │ │ │ └── item │ │ │ │ └── itemViewCtrl.js │ │ ├── utils │ │ │ └── modules │ │ │ │ └── loginUtils.js │ │ ├── sell │ │ │ └── modules │ │ │ │ └── sellItem.js │ │ ├── toppage │ │ │ └── modules │ │ │ │ ├── mainHeader.js │ │ │ │ ├── homepageBindings.js │ │ │ │ ├── homepage.js │ │ │ │ └── widgets │ │ │ │ ├── homeAppliancesWidget.js │ │ │ │ ├── newArrivalsWidget.js │ │ │ │ └── laptopsAndDesktopsWidget.js │ │ ├── appConfig.js │ │ ├── item │ │ │ └── modules │ │ │ │ └── itemView.js │ │ ├── cart │ │ │ └── modules │ │ │ │ └── viewCartItems.js │ │ └── module_config.js │ ├── templates │ │ ├── toppage │ │ │ ├── homepage.hbs │ │ │ ├── widgets │ │ │ │ ├── homepageWidgets.hbs │ │ │ │ ├── homeAppliances.hbs │ │ │ │ ├── newArrivals.hbs │ │ │ │ └── laptopsAndDesktops.hbs │ │ │ ├── footer.hbs │ │ │ ├── userMenu.hbs │ │ │ └── header.hbs │ │ ├── user │ │ │ ├── signin.hbs │ │ │ └── signup.hbs │ │ ├── cart │ │ │ └── viewCartItems.hbs │ │ ├── item │ │ │ └── itemView.hbs │ │ └── sell │ │ │ └── sellItem.hbs │ ├── jslib │ │ ├── utils │ │ │ ├── index.js │ │ │ ├── browser.js │ │ │ ├── number.js │ │ │ ├── api.js │ │ │ ├── storage.js │ │ │ └── common.js │ │ └── index.js │ └── index.html ├── README.md ├── .eslintrc.js ├── bower.json ├── package.json ├── Gruntfile.js └── yarn.lock ├── backend └── user │ └── isLoggedIn.php └── README.md /docs/detail.docx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | coverage/ 4 | .vscode 5 | .idea 6 | .DS_Store -------------------------------------------------------------------------------- /docs/Drawing1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3-b0ot/online-auction-system/HEAD/docs/Drawing1.pdf -------------------------------------------------------------------------------- /docs/Drawing1.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3-b0ot/online-auction-system/HEAD/docs/Drawing1.vsdx -------------------------------------------------------------------------------- /docs/useful_notes.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3-b0ot/online-auction-system/HEAD/docs/useful_notes.docx -------------------------------------------------------------------------------- /frontend/app/assets/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3-b0ot/online-auction-system/HEAD/frontend/app/assets/img/logo.jpg -------------------------------------------------------------------------------- /docs/DataDictionary_OnlineAuction.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3-b0ot/online-auction-system/HEAD/docs/DataDictionary_OnlineAuction.xls -------------------------------------------------------------------------------- /frontend/app/assets/img/logo_footer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3-b0ot/online-auction-system/HEAD/frontend/app/assets/img/logo_footer.jpg -------------------------------------------------------------------------------- /frontend/app/js/compiled-templates/searchpage.js: -------------------------------------------------------------------------------- 1 | define(['handlebars'], function (Handlebars) { 2 | var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {}; 3 | return templates; 4 | }); -------------------------------------------------------------------------------- /frontend/app/templates/toppage/homepage.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{> header}} 3 |
4 | 5 |
6 | {{> footer }} 7 | {{#loadModule 'homepageBindings'}} {{/loadModule}} -------------------------------------------------------------------------------- /frontend/app/templates/toppage/widgets/homepageWidgets.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> laptopsAndDesktopsWidget }} 4 | 5 | {{> homeAppliancesWidget }} 6 | 7 | {{> newArrivalsWidget }} 8 | 9 | -------------------------------------------------------------------------------- /frontend/app/assets/styles/user/signin.css: -------------------------------------------------------------------------------- 1 | .signin-form .panel-body { 2 | padding-top: 30px; 3 | } 4 | .signin-form .input-group { 5 | margin-bottom: 25px; 6 | } 7 | .signin-form .form-group { 8 | margin-top: 10px; 9 | } 10 | .signin-form .no-account { 11 | border-top: 1px solid#888; 12 | padding-top: 15px; 13 | font-size: 85%; 14 | } 15 | .signin-form .alert-danger { 16 | display: none; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/app/assets/styles/user/signup.css: -------------------------------------------------------------------------------- 1 | .signup-form .panel-body { 2 | padding-top: 30px; 3 | } 4 | .signup-form .input-group { 5 | margin-bottom: 25px; 6 | } 7 | .signup-form .form-group { 8 | margin-top: 10px; 9 | } 10 | .signup-form .no-account { 11 | border-top: 1px solid#888; 12 | padding-top: 15px; 13 | font-size: 85%; 14 | } 15 | .signup-form .alert-danger { 16 | display: none; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/app/jslib/utils/index.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['./browser', './common', './number', './api', './storage'], function utilsModule(browser, common, number, api, storage) { 3 | 'use strict'; 4 | 5 | return { 6 | browser : browser, 7 | commonUtils: common, 8 | numberUtils: number, 9 | apiUtils : api, 10 | Storage : storage, 11 | }; 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/user/signin.less: -------------------------------------------------------------------------------- 1 | .signin-form { 2 | 3 | .panel-body { 4 | padding-top: 30px; 5 | } 6 | 7 | .input-group { 8 | margin-bottom: 25px; 9 | } 10 | 11 | .form-group { 12 | margin-top: 10px; 13 | } 14 | 15 | .no-account { 16 | border-top: 1px solid#888; padding-top:15px; font-size:85% 17 | } 18 | 19 | .alert-danger { 20 | display: none; 21 | } 22 | } -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/user/signup.less: -------------------------------------------------------------------------------- 1 | .signup-form { 2 | 3 | .panel-body { 4 | padding-top: 30px; 5 | } 6 | 7 | .input-group { 8 | margin-bottom: 25px; 9 | } 10 | 11 | .form-group { 12 | margin-top: 10px; 13 | } 14 | 15 | .no-account { 16 | border-top: 1px solid#888; padding-top:15px; font-size:85% 17 | } 18 | 19 | .alert-danger { 20 | display: none; 21 | } 22 | } -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/slideshow/slideshow-style1.less: -------------------------------------------------------------------------------- 1 | .slideshow-type1-main { 2 | margin: 15px 0px 10px 0px; 3 | 4 | .slider-left-wrapper, .slider-right-wrapper { 5 | box-shadow: 1px 2px 10px -1px rgba(0,0,0,.3); 6 | background-color: hsla(0,0%,100%,.98); 7 | transition: box-shadow .2s ease; 8 | height: 50px; 9 | padding-top: 17px; 10 | padding-left: 9px; 11 | } 12 | 13 | .item { 14 | width: 150px; 15 | 16 | a { 17 | text-decoration: none; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /frontend/app/js/constants/modules/constants.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define('constants', [], function CONSTANTS() { 3 | 'use strict'; 4 | 5 | var constants = { 6 | API_URL : 'http://192.168.33.10/projects/online-auction-system/backend/', 7 | CART_ICON: { 8 | CART_ITEMS : 'cart_items', 9 | STORAGE_KEY: 'cart_icon_status', 10 | }, 11 | 12 | CUSTOM_EVENTS: { 13 | UPDATE_CART_ICON: 'updateCartItemsCount', 14 | }, 15 | }; 16 | 17 | return constants; 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /frontend/app/js/controllers/toppage/homepageWidgetsCtrl.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'handlebars', 'toppage'], function homepageWidgetsController($, Handlebars, toppage) { 3 | 'use strict'; 4 | 5 | return function callback() { 6 | Handlebars.registerPartial('laptopsAndDesktopsWidget', toppage.laptopsAndDesktops()); 7 | Handlebars.registerPartial('homeAppliancesWidget', toppage.homeAppliances()); 8 | Handlebars.registerPartial('newArrivalsWidget', toppage.newArrivals()); 9 | }; 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /frontend/app/js/utils/modules/loginUtils.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'constants'], function loginUtilsModule($, jscf, CONSTANTS) { 3 | 'use strict'; 4 | 5 | var loginUtils = {}; 6 | 7 | loginUtils.checkUserLoggedIn = function checkUserLoggedIn() { 8 | var options = { 9 | apiUrl : CONSTANTS.API_URL + 'user/isLoggedIn.php', 10 | callback: 'isLoggedInUser', 11 | }; 12 | return jscf.apiUtils.callApi(options); 13 | }; 14 | 15 | return loginUtils; 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/sellItem.less: -------------------------------------------------------------------------------- 1 | .sellitem-form { 2 | .upload-image-panel { 3 | .panel-heading { 4 | padding: 2px 10px; 5 | } 6 | } 7 | 8 | .category-panel .searched-category-dropdown ul { 9 | width: 91%; 10 | padding: 5px; 11 | } 12 | 13 | .pricing-info-panel { 14 | #sellingPrice, 15 | #salesCommission, 16 | #salesProfit { 17 | text-align: right; 18 | } 19 | 20 | #sellingPriceMask { 21 | display: none; 22 | position: absolute; 23 | top: 21%; 24 | right: 9%; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /frontend/app/assets/styles/sellItem.css: -------------------------------------------------------------------------------- 1 | .sellitem-form .upload-image-panel .panel-heading { 2 | padding: 2px 10px; 3 | } 4 | .sellitem-form .category-panel .searched-category-dropdown ul { 5 | width: 91%; 6 | padding: 5px; 7 | } 8 | .sellitem-form .pricing-info-panel #sellingPrice, 9 | .sellitem-form .pricing-info-panel #salesCommission, 10 | .sellitem-form .pricing-info-panel #salesProfit { 11 | text-align: right; 12 | } 13 | .sellitem-form .pricing-info-panel #sellingPriceMask { 14 | display: none; 15 | position: absolute; 16 | top: 21%; 17 | right: 9%; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/app/assets/styles/slideshow/slideshow-style1.css: -------------------------------------------------------------------------------- 1 | .slideshow-type1-main { 2 | margin: 15px 0px 10px 0px; 3 | } 4 | .slideshow-type1-main .slider-left-wrapper, 5 | .slideshow-type1-main .slider-right-wrapper { 6 | box-shadow: 1px 2px 10px -1px rgba(0, 0, 0, 0.3); 7 | background-color: rgba(255, 255, 255, 0.98); 8 | transition: box-shadow 0.2s ease; 9 | height: 50px; 10 | padding-top: 17px; 11 | padding-left: 9px; 12 | } 13 | .slideshow-type1-main .item { 14 | width: 150px; 15 | } 16 | .slideshow-type1-main .item a { 17 | text-decoration: none; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/app/templates/toppage/footer.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/user/isLoggedIn.php: -------------------------------------------------------------------------------- 1 | false); 9 | 10 | // check whether callback was passed or not, if not set up default 11 | if (isset($_GET["callback"])) { 12 | $callback = $_GET["callback"]; 13 | } else { 14 | $callback = "callback"; 15 | } 16 | 17 | // now check if loggedInUser is in session or not. if not, it means user is not logged in. 18 | if (isset($_SESSION["loggedInUser"])) { 19 | $response['isLoggedIn'] = true; 20 | } 21 | 22 | echo $callback . "(" . json_encode($response) . ");"; 23 | ?> -------------------------------------------------------------------------------- /frontend/app/js/controllers/cart/viewCartItemsCtrl.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'constants'], function viewCartItemsController($, jscf, CONSTANTS) { 3 | 'use strict'; 4 | 5 | return function callback() { 6 | var returnData = {}; 7 | var CartStorage; 8 | 9 | function run() { 10 | var items; 11 | 12 | CartStorage = new jscf.Storage(CONSTANTS.CART_ICON.STORAGE_KEY); 13 | items = CartStorage.getItem(CONSTANTS.CART_ICON.CART_ITEMS); 14 | 15 | if (!items) { 16 | returnData.items = []; 17 | } else { 18 | returnData.items = items; 19 | } 20 | } 21 | 22 | run(); 23 | 24 | return returnData; 25 | }; 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | ## About ## 2 | This is frontend part of application. It contains all front-end technologies which are intended to use and make development fast and easy. 3 | 4 | ## How to start with project ? ## 5 | For development purpose, you need to install `node.js` in your system. After that go to this project directory and use following commands: 6 | - `npm i -g yarn grunt-cli` 7 | - `yarn` 8 | 9 | **To build project:** 10 | - `grunt` 11 | 12 | **To run in browser:** 13 | Before you do so, you need to set your machine's IP address in `package.json` file, under `serverHost`. This will be automated soon but till that you need to do manually. After than execute following: 14 | - `grunt serve` 15 | 16 | **For production build:** 17 | - `grunt build` 18 | 19 | To run production build in browser: 20 | - `grunt serve --directory dist` -------------------------------------------------------------------------------- /frontend/app/jslib/utils/browser.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery'], function browserUtils($) { 3 | 'use strict'; 4 | 5 | var browser = {}; 6 | 7 | var userAgent = navigator.userAgent || navigator.vendor || window.opera; 8 | 9 | browser.prototype = {}; 10 | 11 | // detect specific smartphone device 12 | browser.isWindowsPhone = !!/windows phone/i.test(userAgent); 13 | browser.isiPhone = !!/iPhone/.test(userAgent) && !window.MSStream; 14 | browser.isiPad = !!/iPad/.test(userAgent) && !window.MSStream; 15 | browser.isiPod = !!/iPod/.test(userAgent) && !window.MSStream; 16 | browser.isAndroid = !!/android/i.test(userAgent) && !/windows phone/i.test(userAgent); 17 | 18 | // detect whether it is smartphone 19 | browser.isSp = browser.isiPhone || browser.isAndroid; 20 | 21 | return browser; 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/toppage/widgets/laptopAndDesktops.less: -------------------------------------------------------------------------------- 1 | @import "../../slideshow/slideshow-style1.less"; 2 | 3 | .laptops-and-desktops-random-widget { 4 | background-color: #fff; 5 | border-radius: 2px; 6 | box-shadow: 0 2px 4px 0 rgba(0,0,0,.08); 7 | padding: 0px 15px 5px 5px; 8 | margin-top: 10px; 9 | 10 | .slider-text { 11 | margin: 15px 0px 10px 0px; 12 | text-align: center; 13 | padding-bottom: 5px; 14 | 15 | .btn-viewall span { 16 | text-transform: uppercase; 17 | } 18 | 19 | .glyphicon-option-horizontal { 20 | top: 7px; 21 | } 22 | } 23 | 24 | #laptopsAndDesktopsSlider { 25 | .item-info { 26 | font-size: medium; 27 | } 28 | 29 | .item-name { 30 | font-weight: 400; 31 | color: #388e3c; 32 | } 33 | 34 | .item-price { 35 | color: black; 36 | font-weight: 500; 37 | text-align: center; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/toppage/widgets/homeAppliances.less: -------------------------------------------------------------------------------- 1 | @import "../../slideshow/slideshow-style1.less"; 2 | 3 | .home-appliances-widget { 4 | background-color: #fff; 5 | border-radius: 2px; 6 | box-shadow: 0 2px 4px 0 rgba(0,0,0,.08); 7 | padding: 0px 15px 5px 5px; 8 | margin-top: 10px; 9 | margin-bottom: 10px; 10 | 11 | .slider-text { 12 | margin: 15px 0px 10px 0px; 13 | text-align: center; 14 | padding-bottom: 5px; 15 | 16 | .btn-viewall span { 17 | text-transform: uppercase; 18 | } 19 | 20 | .glyphicon-option-horizontal { 21 | top: 7px; 22 | } 23 | } 24 | 25 | #homeAppliancesSlider { 26 | .item-info { 27 | font-size: medium; 28 | } 29 | 30 | .item-name { 31 | font-weight: 400; 32 | color: #388e3c; 33 | } 34 | 35 | .item-price { 36 | color: black; 37 | font-weight: 500; 38 | text-align: center; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /frontend/app/templates/toppage/userMenu.hbs: -------------------------------------------------------------------------------- 1 |
2 | 6 | 19 |
-------------------------------------------------------------------------------- /frontend/app/assets/styles-less/toppage/widgets/newArrivals.less: -------------------------------------------------------------------------------- 1 | @import "../../slideshow/slideshow-style1.less"; 2 | 3 | .new-arrivals-widget { 4 | background-color: #fff; 5 | border-radius: 2px; 6 | box-shadow: 0 2px 4px 0 rgba(0,0,0,.08); 7 | padding: 0px 15px 5px 5px; 8 | margin-top: 10px; 9 | margin-bottom: 10px; 10 | 11 | .slider-text { 12 | margin: 15px 0px 10px 0px; 13 | text-align: center; 14 | padding-bottom: 5px; 15 | 16 | .btn-viewall span { 17 | text-transform: uppercase; 18 | } 19 | 20 | .glyphicon-option-horizontal { 21 | top: 7px; 22 | } 23 | } 24 | 25 | #newArrivalsSlider { 26 | .item-info { 27 | font-size: medium; 28 | } 29 | 30 | .item-name { 31 | font-weight: 400; 32 | color: #388e3c; 33 | } 34 | 35 | .item-price { 36 | color: black; 37 | font-weight: 500; 38 | text-align: center; 39 | } 40 | 41 | .item-category { 42 | color: black; 43 | opacity: .6; 44 | white-space: nowrap; 45 | text-overflow: ellipsis; 46 | text-align: center; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /frontend/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Online Auction System 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/cart/viewCartItems.less: -------------------------------------------------------------------------------- 1 | .view-cart-items-wrap { 2 | 3 | .view-cart-panel-wrap { 4 | margin: 10px; 5 | background-color: #fff; 6 | border-radius: 2%; 7 | -moz-border-radius: 2%; 8 | -webkit-border-radius: 2%; 9 | 10 | .item-image { 11 | max-height: 120px; 12 | height: 100px; 13 | border: 1px solid #f0f0f0; 14 | } 15 | 16 | .btn-checkout-span { 17 | float: right; 18 | margin-top: -6px; 19 | } 20 | 21 | .no-items { 22 | .info { 23 | font-size: x-large; 24 | font-weight: 500; 25 | color: #000; 26 | } 27 | } 28 | 29 | .item { 30 | margin-top: 5px; 31 | border: 1px solid #f0f0f0; 32 | padding: 5px; 33 | } 34 | 35 | .item-info { 36 | margin: 5px; 37 | 38 | .item-name { 39 | font-size: medium; 40 | font-weight: 500; 41 | color: #333; 42 | } 43 | 44 | .category { 45 | color: #878787; 46 | font-weight: 400; 47 | font-size: small; 48 | } 49 | 50 | .item-price { 51 | font-size: medium; 52 | font-weight: 500; 53 | color: red; 54 | } 55 | } 56 | 57 | .item-remove, .item-remove-xs { 58 | font-size: x-large; 59 | color: darkred; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /frontend/app/assets/styles/toppage/widgets/homeAppliances.css: -------------------------------------------------------------------------------- 1 | .slideshow-type1-main { 2 | margin: 15px 0px 10px 0px; 3 | } 4 | .slideshow-type1-main .slider-left-wrapper, 5 | .slideshow-type1-main .slider-right-wrapper { 6 | box-shadow: 1px 2px 10px -1px rgba(0, 0, 0, 0.3); 7 | background-color: rgba(255, 255, 255, 0.98); 8 | transition: box-shadow 0.2s ease; 9 | height: 50px; 10 | padding-top: 17px; 11 | padding-left: 9px; 12 | } 13 | .slideshow-type1-main .item { 14 | width: 150px; 15 | } 16 | .slideshow-type1-main .item a { 17 | text-decoration: none; 18 | } 19 | .home-appliances-widget { 20 | background-color: #fff; 21 | border-radius: 2px; 22 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); 23 | padding: 0px 15px 5px 5px; 24 | margin-top: 10px; 25 | margin-bottom: 10px; 26 | } 27 | .home-appliances-widget .slider-text { 28 | margin: 15px 0px 10px 0px; 29 | text-align: center; 30 | padding-bottom: 5px; 31 | } 32 | .home-appliances-widget .slider-text .btn-viewall span { 33 | text-transform: uppercase; 34 | } 35 | .home-appliances-widget .slider-text .glyphicon-option-horizontal { 36 | top: 7px; 37 | } 38 | .home-appliances-widget #homeAppliancesSlider .item-info { 39 | font-size: medium; 40 | } 41 | .home-appliances-widget #homeAppliancesSlider .item-name { 42 | font-weight: 400; 43 | color: #388e3c; 44 | } 45 | .home-appliances-widget #homeAppliancesSlider .item-price { 46 | color: black; 47 | font-weight: 500; 48 | text-align: center; 49 | } 50 | -------------------------------------------------------------------------------- /frontend/app/assets/styles/cart/viewCartItems.css: -------------------------------------------------------------------------------- 1 | .view-cart-items-wrap .view-cart-panel-wrap { 2 | margin: 10px; 3 | background-color: #fff; 4 | border-radius: 2%; 5 | -moz-border-radius: 2%; 6 | -webkit-border-radius: 2%; 7 | } 8 | .view-cart-items-wrap .view-cart-panel-wrap .item-image { 9 | max-height: 120px; 10 | height: 100px; 11 | border: 1px solid #f0f0f0; 12 | } 13 | .view-cart-items-wrap .view-cart-panel-wrap .btn-checkout-span { 14 | float: right; 15 | margin-top: -6px; 16 | } 17 | .view-cart-items-wrap .view-cart-panel-wrap .no-items .info { 18 | font-size: x-large; 19 | font-weight: 500; 20 | color: #000; 21 | } 22 | .view-cart-items-wrap .view-cart-panel-wrap .item { 23 | margin-top: 5px; 24 | border: 1px solid #f0f0f0; 25 | padding: 5px; 26 | } 27 | .view-cart-items-wrap .view-cart-panel-wrap .item-info { 28 | margin: 5px; 29 | } 30 | .view-cart-items-wrap .view-cart-panel-wrap .item-info .item-name { 31 | font-size: medium; 32 | font-weight: 500; 33 | color: #333; 34 | } 35 | .view-cart-items-wrap .view-cart-panel-wrap .item-info .category { 36 | color: #878787; 37 | font-weight: 400; 38 | font-size: small; 39 | } 40 | .view-cart-items-wrap .view-cart-panel-wrap .item-info .item-price { 41 | font-size: medium; 42 | font-weight: 500; 43 | color: red; 44 | } 45 | .view-cart-items-wrap .view-cart-panel-wrap .item-remove, 46 | .view-cart-items-wrap .view-cart-panel-wrap .item-remove-xs { 47 | font-size: x-large; 48 | color: darkred; 49 | } 50 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "extends": "airbnb", 7 | "installedESLint": true, 8 | "plugins": [ 9 | "react" 10 | ], 11 | "parserOptions": { 12 | "sourceType" : "script", 13 | "globalReturn": true 14 | }, 15 | "globals": { 16 | "requirejs": {}, 17 | "require": {}, 18 | "define": {}, 19 | "window": {}, 20 | "document": {} 21 | }, 22 | "rules": { 23 | "key-spacing": ["error", { 24 | "align": "colon" 25 | }], 26 | "strict": ["error", "function"], 27 | "spaced-comment": ["error", "always", { 28 | "exceptions": ["/"] 29 | }], 30 | "max-len": ["error", { 31 | "code" : 150, 32 | "comments" : 150, 33 | "ignoreTrailingComments": true, 34 | "ignoreUrls" : true 35 | }], 36 | "no-unused-vars": ["error", { 37 | "args": "none", 38 | }], 39 | "prefer-rest-params": 0, 40 | "guard-for-in": 0, 41 | "no-restricted-syntax": 0, 42 | "prefer-template": 0, 43 | "no-throw-literal": 0, 44 | "global-require": 0, 45 | "import/no-dynamic-require": 0, 46 | "object-shorthand": 0, 47 | "no-plusplus": 0, 48 | "no-lonely-if": 0, 49 | "no-param-reassign": 0, 50 | "no-var": "off", // unless a transpiler is used 51 | "no-use-before-define": ["error", { 52 | "functions": false 53 | }], 54 | "prefer-arrow-callback": "off" // unless a transpiler is used 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /frontend/app/assets/styles/toppage/widgets/laptopAndDesktops.css: -------------------------------------------------------------------------------- 1 | .slideshow-type1-main { 2 | margin: 15px 0px 10px 0px; 3 | } 4 | .slideshow-type1-main .slider-left-wrapper, 5 | .slideshow-type1-main .slider-right-wrapper { 6 | box-shadow: 1px 2px 10px -1px rgba(0, 0, 0, 0.3); 7 | background-color: rgba(255, 255, 255, 0.98); 8 | transition: box-shadow 0.2s ease; 9 | height: 50px; 10 | padding-top: 17px; 11 | padding-left: 9px; 12 | } 13 | .slideshow-type1-main .item { 14 | width: 150px; 15 | } 16 | .slideshow-type1-main .item a { 17 | text-decoration: none; 18 | } 19 | .laptops-and-desktops-random-widget { 20 | background-color: #fff; 21 | border-radius: 2px; 22 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); 23 | padding: 0px 15px 5px 5px; 24 | margin-top: 10px; 25 | } 26 | .laptops-and-desktops-random-widget .slider-text { 27 | margin: 15px 0px 10px 0px; 28 | text-align: center; 29 | padding-bottom: 5px; 30 | } 31 | .laptops-and-desktops-random-widget .slider-text .btn-viewall span { 32 | text-transform: uppercase; 33 | } 34 | .laptops-and-desktops-random-widget .slider-text .glyphicon-option-horizontal { 35 | top: 7px; 36 | } 37 | .laptops-and-desktops-random-widget #laptopsAndDesktopsSlider .item-info { 38 | font-size: medium; 39 | } 40 | .laptops-and-desktops-random-widget #laptopsAndDesktopsSlider .item-name { 41 | font-weight: 400; 42 | color: #388e3c; 43 | } 44 | .laptops-and-desktops-random-widget #laptopsAndDesktopsSlider .item-price { 45 | color: black; 46 | font-weight: 500; 47 | text-align: center; 48 | } 49 | -------------------------------------------------------------------------------- /frontend/app/js/sell/modules/sellItem.js: -------------------------------------------------------------------------------- 1 | define(['jquery', 'handlebars', 'fileinput', 'jscf', 'explorertheme'], 2 | function sellItem($, Handlebars, fileinput, jscf) { 3 | 'use strict'; 4 | 5 | return function callback() { 6 | var fileinputOptions = { 7 | allowedFileExtensions: ['jpg', 'png'], 8 | browseOnZoneClick : false, 9 | browseLabel : 'Browse', 10 | maxFileCount : 5, 11 | dropZoneEnabled : false, 12 | uploadUrl : '/dfd/s', 13 | }; 14 | 15 | if (!jscf.browser.isSp) { 16 | fileinputOptions.theme = 'explorer'; 17 | } 18 | 19 | $('.sellitem-form #itemimages').fileinput(fileinputOptions); 20 | 21 | // put mask on itemprice input box to show formatted price 22 | $('.sellitem-form #sellingPrice').blur(function onSellingPriceBlur() { 23 | var $mask = $(this).next(); 24 | if (this.value === '' || parseInt(this.value, 10) === 0) { 25 | this.placeholder = '500 ~ 1000'; 26 | return; 27 | } 28 | $mask.text(jscf.numberUtils.currencyFormat(jscf.numberUtils.CURRENCY_BASE.INR, this.value)); 29 | this.value = ''; 30 | this.placeholder = ''; 31 | $mask.show(); 32 | }).focus(function onSellingPriceFocus() { 33 | var $mask = $(this).next(); 34 | var val = $mask.text(); 35 | $mask.hide(); 36 | if (val !== '') { 37 | this.value = parseInt(val.replace(/,/g, ''), 10); 38 | } 39 | $mask.text(''); 40 | }); 41 | }; 42 | }); 43 | -------------------------------------------------------------------------------- /frontend/app/assets/styles/toppage/widgets/newArrivals.css: -------------------------------------------------------------------------------- 1 | .slideshow-type1-main { 2 | margin: 15px 0px 10px 0px; 3 | } 4 | .slideshow-type1-main .slider-left-wrapper, 5 | .slideshow-type1-main .slider-right-wrapper { 6 | box-shadow: 1px 2px 10px -1px rgba(0, 0, 0, 0.3); 7 | background-color: rgba(255, 255, 255, 0.98); 8 | transition: box-shadow 0.2s ease; 9 | height: 50px; 10 | padding-top: 17px; 11 | padding-left: 9px; 12 | } 13 | .slideshow-type1-main .item { 14 | width: 150px; 15 | } 16 | .slideshow-type1-main .item a { 17 | text-decoration: none; 18 | } 19 | .new-arrivals-widget { 20 | background-color: #fff; 21 | border-radius: 2px; 22 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); 23 | padding: 0px 15px 5px 5px; 24 | margin-top: 10px; 25 | margin-bottom: 10px; 26 | } 27 | .new-arrivals-widget .slider-text { 28 | margin: 15px 0px 10px 0px; 29 | text-align: center; 30 | padding-bottom: 5px; 31 | } 32 | .new-arrivals-widget .slider-text .btn-viewall span { 33 | text-transform: uppercase; 34 | } 35 | .new-arrivals-widget .slider-text .glyphicon-option-horizontal { 36 | top: 7px; 37 | } 38 | .new-arrivals-widget #newArrivalsSlider .item-info { 39 | font-size: medium; 40 | } 41 | .new-arrivals-widget #newArrivalsSlider .item-name { 42 | font-weight: 400; 43 | color: #388e3c; 44 | } 45 | .new-arrivals-widget #newArrivalsSlider .item-price { 46 | color: black; 47 | font-weight: 500; 48 | text-align: center; 49 | } 50 | .new-arrivals-widget #newArrivalsSlider .item-category { 51 | color: black; 52 | opacity: .6; 53 | white-space: nowrap; 54 | text-overflow: ellipsis; 55 | text-align: center; 56 | } 57 | -------------------------------------------------------------------------------- /frontend/app/js/toppage/modules/mainHeader.js: -------------------------------------------------------------------------------- 1 | define(['jquery', 'handlebars'], function mainHeader($, Handlebars) { 2 | 'use strict'; 3 | 4 | return function callback() { 5 | function run() { 6 | var $mainHeader = $('.main-header-menubar'); 7 | $('.navbar a.dropdown-toggle', $mainHeader).on('click', function onDropdownToggleClick(e) { 8 | var $el = $(this); 9 | $el.parent('li').toggleClass('open'); 10 | 11 | $('.nav li.open').not($el.parents('li')).removeClass('open'); 12 | 13 | return false; 14 | }); 15 | 16 | $('.hidden-xs .nav .dropdown', $mainHeader).hover(function onDropdownHoverIn() { 17 | $(this).find('.dropdown-menu') 18 | .stop(true, true) 19 | .delay(100) 20 | .fadeIn(200); 21 | $(this).find('> a > .caret').css('transform', 'rotate(180deg)'); 22 | }, function onDropdownHoverOut() { 23 | $(this).find('.dropdown-menu') 24 | .stop(true, true) 25 | .delay(100) 26 | .fadeOut(200); 27 | $(this).find('> a > .caret').css('transform', 'rotate(0deg)'); 28 | }); 29 | 30 | $('.visible-xs .nav .dropdown a', $mainHeader).click(function onDropdownClick() { 31 | if ($(this).parent().hasClass('open')) { 32 | $(this).parent().parent() 33 | .find('.caret') 34 | .css('transform', 'rotate(0deg)'); 35 | $(this).find('.caret').css('transform', 'rotate(180deg)'); 36 | } else { 37 | $(this).parent().find('.caret').css('transform', 'rotate(0deg)'); 38 | } 39 | }); 40 | } // run 41 | 42 | $(run); 43 | }; 44 | }); 45 | -------------------------------------------------------------------------------- /frontend/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "online-auction-system", 3 | "description": "Online auction C2C type of system", 4 | "main": "index.html", 5 | "authors": [ 6 | "parth parmar" 7 | ], 8 | "license": "ISC", 9 | "homepage": "https://github.com/jaydeep987/online-auction-system", 10 | "private": true, 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | "requirejs": "^2.3.3", 20 | "handlebars": "^4.0.5", 21 | "bootstrap": "^3.3.7", 22 | "bullhorn-handlebars-helpers": "^0.5.0", 23 | "foundation-icon-fonts": "*", 24 | "components-font-awesome": "^4.7.0", 25 | "bootstrap-fileinput": "^4.3.9", 26 | "jquery-mask-plugin": "^1.14.10", 27 | "lightslider": "^1.1.6" 28 | }, 29 | "overrides": { 30 | "requirejs": { 31 | "main": [ 32 | "require.js" 33 | ] 34 | }, 35 | "bootstrap": { 36 | "main": [ 37 | "./dist/js/bootstrap.js", 38 | "./dist/css/*.min.*", 39 | "./dist/fonts/*.*" 40 | ] 41 | }, 42 | "handlebars": { 43 | "main": [ 44 | "handlebars.amd.js" 45 | ] 46 | }, 47 | "bullhorn-handlebars-helpers": { 48 | "main": [] 49 | }, 50 | "bootstrap-fileinput": { 51 | "main": [ 52 | "css/fileinput.min.css", 53 | "themes/explorer/theme.css" 54 | ] 55 | }, 56 | "jquery-mask-plugin": { 57 | "main": [] 58 | }, 59 | "lightslider": { 60 | "main": [ 61 | "dist/js/lightslider.js", 62 | "dist/css/lightslider.css" 63 | ] 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /frontend/app/js/controllers/item/itemViewCtrl.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf'], function itemViewController($, jscf) { 3 | 'use strict'; 4 | 5 | return function callback() { 6 | var returnData = {}; 7 | var urlHashRegEx = /^itemview\/([a-zA-Z0-9_\-&$#()]+)\/([0-9]+)/; 8 | var options = { 9 | data : {}, 10 | apiUrl: '/ite/get', 11 | }; 12 | 13 | options.mockData = '{"status":"Success","item":{"itemId":683404,"itemName":"item15","itemImageUrl":"http://item.image.url/15","itemPrice":14778,"itemUrl":"/item/item15","categoryName":"Electronics","deliveryBurden":"Paid by you","itemSource":"Gujarat","shipTime":"1~2 days","sellerName":"ABC co.","sellerId":"2323","description": "avsnsnf hfisnifn nsfn snfns fsninnns"}}'; 14 | 15 | function getItemData() { 16 | // call an api to get data 17 | jscf.apiUtils.callApi(options).done(function success(data) { 18 | returnData = data; 19 | }); 20 | } 21 | 22 | function checkURIHash() { 23 | var match; 24 | var hash = window.location.hash.substring(1); 25 | hash = decodeURIComponent(hash); 26 | 27 | match = new RegExp(urlHashRegEx).exec(hash); 28 | if (match != null && match.length === 3) { 29 | options.data.itemName = match[1]; 30 | options.data.itemId = parseInt(match[2], 10); 31 | 32 | getItemData(); 33 | } 34 | } 35 | 36 | function run() { 37 | // first check url hash and get itemname/id to call api 38 | checkURIHash(); 39 | } 40 | 41 | run(); 42 | 43 | return returnData; 44 | }; 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /frontend/app/js/toppage/modules/homepageBindings.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'constants'], function homepageBindingsModule($, jscf, CONSTANTS) { 3 | 'use strict'; 4 | 5 | return function callback() { 6 | var CartStorage; 7 | 8 | var settings = { 9 | css: { 10 | cartIconBtn : '.main-header .cart-icon', 11 | cartIconItemCount: '.main-header .cart-items-number', 12 | }, 13 | }; 14 | 15 | var $elems = { 16 | cartIconBtn : $(settings.css.cartIconBtn), 17 | cartIconItemCount: $(settings.css.cartIconItemCount), 18 | }; 19 | 20 | // custom event bind to cart icon. Can be triggered anytime from anywhere to update cart items count 21 | $elems.cartIconBtn.on(CONSTANTS.CUSTOM_EVENTS.UPDATE_CART_ICON, function onUpdateCartItemsCount() { 22 | var currentItems = CartStorage.getItem(CONSTANTS.CART_ICON.CART_ITEMS); 23 | var count = currentItems && currentItems.length ? currentItems.length : 0; 24 | $elems.cartIconItemCount.each(function eachItemCountElem(k, el) { 25 | $(el).text(count); 26 | }); 27 | if (!count || count === 0) { 28 | $elems.cartIconItemCount.hide(); 29 | } else { 30 | $elems.cartIconItemCount.show(); 31 | } 32 | }); 33 | 34 | function run() { 35 | CartStorage = new jscf.Storage(CONSTANTS.CART_ICON.STORAGE_KEY); 36 | 37 | setTimeout(function waitForSometime() { 38 | // initially also update cart icon items number 39 | $elems.cartIconBtn.trigger('updateCartItemsCount'); 40 | }, 0); 41 | } 42 | 43 | run(); 44 | }; 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /frontend/app/js/appConfig.js: -------------------------------------------------------------------------------- 1 | if (typeof require === 'function') { 2 | require(['jscf', 'bootstrap'], function appConfig(jscf) { 3 | 'use strict'; 4 | 5 | jscf.config(function setRoutes(routeProvider) { 6 | routeProvider 7 | .when('/homepage', { 8 | requireTpl: 'homepage', 9 | root : true, 10 | }) 11 | .when('/', { 12 | requireTpl : 'toppage', 13 | controller : 'homepageWidgetsCtrl', 14 | templateName: 'homepageWidgets', 15 | target : '.main-container', 16 | dependsOn : ['homepage'], 17 | }) 18 | .when('/signin', { 19 | requireTpl : 'user', 20 | templateName: 'signin', 21 | target : '#homepage', 22 | }) 23 | .when('/signup', { 24 | requireTpl : 'user', 25 | templateName: 'signup', 26 | target : '#homepage', 27 | }) 28 | .when('/itemview', { 29 | requireTpl : 'item', 30 | templateName: 'itemView', 31 | controller : 'itemViewCtrl', 32 | target : '.main-container', 33 | dependsOn : ['homepage'], 34 | }) 35 | .when('/sellItem', { 36 | requireTpl : 'sell', 37 | templateName: 'sellItem', 38 | target : '.main-container', 39 | dependsOn : ['homepage'], 40 | }) 41 | .when('/viewCartItems', { 42 | requireTpl : 'cart', 43 | templateName: 'viewCartItems', 44 | controller : 'viewCartItemsCtrl', 45 | target : '.main-container', 46 | dependsOn : ['homepage'], 47 | }) 48 | ; 49 | }); 50 | jscf.init('onlineauction', 'app'); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "online-auction-system", 3 | "version": "1.0.0", 4 | "description": "Online auction C2C type of system", 5 | "main": "index.js", 6 | "moduleConfigFile": "app/js/module_config.js", 7 | "serverPort": 8000, 8 | "openBrowser": false, 9 | "directories": { 10 | "doc": "docs" 11 | }, 12 | "release": "dev", 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1", 15 | "postinstall": "bower install" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/parthsparmar/online-auction-system.git" 20 | }, 21 | "author": "parth parmar", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/parthsparmar/online-auction-system/issues" 25 | }, 26 | "homepage": "https://github.com/parthsparmar/online-auction-system#readme", 27 | "devDependencies": { 28 | "bower": "^1.8.0", 29 | "del": "^2.2.2", 30 | "eslint": "^3.19.0", 31 | "eslint-config-airbnb": "^14.1.0", 32 | "eslint-plugin-import": "^2.2.0", 33 | "eslint-plugin-jsx-a11y": "^4.0.0", 34 | "eslint-plugin-react": "^6.10.3", 35 | "grunt": "^1.0.1", 36 | "grunt-contrib-clean": "^1.0.0", 37 | "grunt-contrib-connect": "^1.0.2", 38 | "grunt-contrib-copy": "^1.0.0", 39 | "grunt-contrib-handlebars": "^1.0.0", 40 | "grunt-contrib-imagemin": "^1.0.1", 41 | "grunt-contrib-less": "^1.4.1", 42 | "grunt-contrib-requirejs": "^1.0.0", 43 | "grunt-contrib-watch": "^1.0.0", 44 | "grunt-handlebars-compiler": "^0.3.0", 45 | "grunt-injector": "^1.1.0", 46 | "grunt-wiredep": "^3.0.1", 47 | "less-plugin-autoprefix": "^1.5.1", 48 | "os": "^0.1.1" 49 | }, 50 | "dependencies": { 51 | "wiredep": "^4.0.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /frontend/app/jslib/utils/number.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides Number related utility methods 3 | */ 4 | if (typeof define === 'function' && define.amd) { 5 | define(['jquery'], function numberUtilities($) { 6 | 'use strict'; 7 | 8 | var numberUtils = {}; 9 | var CURRENCY_SYMBOL = { 10 | USD: '$', 11 | INR: '₹', 12 | }; 13 | 14 | numberUtils.prototype = {}; 15 | numberUtils.CURRENCY_BASE = { 16 | USD: 'USD', 17 | INR: 'INR', 18 | }; 19 | 20 | /** 21 | * Formats given number to needed currency. 22 | * @name numberUtils.currencyFormat 23 | * 24 | * @param {String} currencyBase Currency base, e.g. USD 25 | * @param {Number} number number to format 26 | * 27 | * @return {String} Formatted number 28 | */ 29 | numberUtils.currencyFormat = function currencyFormat(currencyBase, number) { 30 | var cur; 31 | var lastThree; 32 | var otherNumbers; 33 | var formatted; 34 | var currencyBaseConsts = numberUtils.CURRENCY_BASE; 35 | 36 | switch (currencyBase) { 37 | case currencyBaseConsts.USD: 38 | // TODO: 39 | break; 40 | case currencyBaseConsts.INR: 41 | cur = number.toString(); 42 | lastThree = cur.substring(cur.length - 3); 43 | otherNumbers = cur.substring(0, cur.length - 3); 44 | 45 | if (otherNumbers !== '') { 46 | lastThree = ',' + lastThree; 47 | } 48 | 49 | formatted = otherNumbers.replace(/\B(?=(\d{2})+(?!\d))/g, ',') + lastThree + ' ' + CURRENCY_SYMBOL[currencyBase]; 50 | break; 51 | default: 52 | throw 'Invalid currency base was set.'; 53 | } 54 | 55 | return formatted; 56 | }; 57 | 58 | return numberUtils; 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Online auction system # 2 | 3 | This is my final semester project which I am developing by myself without any partner. 4 | I chose this topic because E-commerce is very interesting and there are lot of things to learn. Though this system is not fully ecommerce system, I wanted to develop system which can be used by everyone who want to sell their product unlike only merchants can sell in typical ecommerce system. So this is kind of C2C system, anyone can be seller and buyear at any time. 5 | 6 | ## Technology stack ## 7 | For front-end, normally html/css/js are used but I wanted to learn some new tech also as per my time availability. For back-end I used PHP. Following is detailed stack: 8 | 9 | - **Front-end technologies:** 10 | - **Core technologies:** 11 | - HTML5 12 | - CSS3 13 | - Javascript 14 | - **Libraries used:** 15 | - Bootstrap3 16 | - jQuery 17 | - requirejs 18 | - Handlebars - for templating 19 | - **Building & other tools:** 20 | - Bower - package manager 21 | - Grunt for building tool 22 | - ** Building & other tools: ** 23 | - Bower - package manager 24 | - Gulp for building tool 25 | - **Back-end technologies:** 26 | - PHP 27 | - MySQL database 28 | - Apache server 29 | 30 | ## How to start with project? ## 31 | **Development** 32 | For development purpose, you need to work with two components: frontend and backend. Both are separate and independent. 33 | For more details about how to work with each component, visit to that parituclar project. 34 | 35 | **Project execution** 36 | For frontend you need to make production build. After that upload that build and the backend to your server. 37 | Before running project, you need to do setup. For that visit `/setup/` and follow instructions. -------------------------------------------------------------------------------- /frontend/app/templates/user/signin.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 | 36 |
-------------------------------------------------------------------------------- /frontend/app/jslib/utils/api.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', './common'], function apiUtilities($, commonUtils) { 3 | 'use strict'; 4 | 5 | var apiUtils = {}; 6 | var apiAttrs = { 7 | mockData : [undefined, 'str'], 8 | mockUrl : [undefined, 'str'], 9 | mockTimeout: [undefined, 'int'], 10 | }; 11 | var API_TIMEOUT = 10000; 12 | 13 | /** 14 | * Read API settings from given element 15 | * @name readApiSettings 16 | * 17 | * @param {String|Object} elem Name of element with it's id/class prefix or jquery elemet itself 18 | * 19 | * @return {Object} attr:value pairs as of api attributes (predefined) 20 | */ 21 | apiUtils.readApiSettings = function readApiSettings(elem) { 22 | var $elem = typeof elem === 'string' ? $(elem) : elem; 23 | return commonUtils.readSettingsFromElem(apiAttrs, $elem); 24 | }; 25 | 26 | apiUtils.callApi = function callApi(options) { 27 | var def = $.Deferred(); 28 | var apiUrl; 29 | var type; 30 | 31 | if (options.mockData) { 32 | def.resolve(JSON.parse(options.mockData)); 33 | } else { 34 | apiUrl = options.mockUrl ? options.mockUrl : options.apiUrl; 35 | type = options.type ? options.type : 'POST'; 36 | 37 | $.ajax({ 38 | url : apiUrl, 39 | type : type, 40 | data : options.data, 41 | dataType: 'jsonp', 42 | timeout : options.apiTimeout || API_TIMEOUT, 43 | success : function callApiSuccess(data) { 44 | if (data) { 45 | def.resolve(data); 46 | } else { 47 | def.reject(data); 48 | } 49 | }, 50 | error: function callApiFailed(jqXhr, error) { 51 | def.reject(jqXhr); 52 | }, 53 | }); 54 | } 55 | 56 | return def.promise(); 57 | }; 58 | 59 | return apiUtils; 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /frontend/app/js/item/modules/itemView.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'constants'], function itemViewModule($, jscf, CONSTANTS) { 3 | 'use strict'; 4 | 5 | return function callback(params) { 6 | var CartStorage; 7 | var itemData = params[0]; 8 | 9 | var settings = { 10 | css: { 11 | addToCartBtn: '#addToCartBtn', 12 | cartIconBtn : '.main-header .cart-icon', 13 | }, 14 | }; 15 | 16 | var $elems = { 17 | addToCartBtn: $(settings.css.addToCartBtn), 18 | cartIconBtn : $(settings.css.cartIconBtn), 19 | }; 20 | 21 | function bindEvents() { 22 | // when click on add to cart 23 | $elems.addToCartBtn.on('click', function addTocartBtnClick() { 24 | var currentItems = CartStorage.getItem(CONSTANTS.CART_ICON.CART_ITEMS); 25 | var isAdded = false; 26 | var i; 27 | var n; 28 | var item; 29 | 30 | if (currentItems && currentItems.length) { 31 | n = currentItems.length; 32 | // check if item is already added ... 33 | for (i = 0; i < n; i++) { 34 | item = currentItems[i]; 35 | if (item.itemId === itemData.itemId) { 36 | isAdded = true; 37 | break; 38 | } 39 | } 40 | 41 | if (!isAdded) { 42 | currentItems.push(itemData); 43 | CartStorage.setItem(CONSTANTS.CART_ICON.CART_ITEMS, currentItems); 44 | } 45 | } else { 46 | CartStorage.setItem(CONSTANTS.CART_ICON.CART_ITEMS, [itemData]); 47 | } 48 | 49 | $elems.cartIconBtn.trigger(CONSTANTS.CUSTOM_EVENTS.UPDATE_CART_ICON); 50 | }); 51 | } 52 | 53 | function run() { 54 | CartStorage = new jscf.Storage(CONSTANTS.CART_ICON.STORAGE_KEY); 55 | 56 | bindEvents(); 57 | } 58 | 59 | run(); 60 | }; 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /frontend/app/js/cart/modules/viewCartItems.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'constants', 'loginUtils'], function viewCartItemsModule($, jscf, CONSTANTS, loginUtils) { 3 | 'use strict'; 4 | 5 | return function callback() { 6 | var css = { 7 | itemRow : '.view-cart-items-wrap .item-row-#ITEM_INDEX#', 8 | itemRemove : '.view-cart-items-wrap .item-remove', 9 | itemRemoveXs: '.view-cart-items-wrap .item-remove-xs', 10 | itemsCount : '.view-cart-items-wrap .items-count', 11 | noItems : '.view-cart-items-wrap .no-items', 12 | placeOrder : '.view-cart-items-wrap .btn-checkout-span', 13 | cartIcon : '.main-header .cart-icon', 14 | btnCheckout : '.view-cart-items-wrap .btn-checkout', 15 | }; 16 | var CartStorage; 17 | 18 | function bindEvents() { 19 | // bind events for remove button of each item in list 20 | $(document).on('click', css.itemRemove + ',' + css.itemRemoveXs, function onItemRemove(e) { 21 | var index = parseInt($(this).data('index'), 10); 22 | var items; 23 | 24 | e.preventDefault(); 25 | items = CartStorage.getItem(CONSTANTS.CART_ICON.CART_ITEMS); 26 | // remove item 27 | items.splice(index, 1); 28 | // update in localstorage 29 | CartStorage.setItem(CONSTANTS.CART_ICON.CART_ITEMS, items); 30 | $(css.cartIcon).trigger(CONSTANTS.CUSTOM_EVENTS.UPDATE_CART_ICON); 31 | $(jscf.commonUtils.replacePlaceholders(css.itemRow, { '#ITEM_INDEX#': index })).remove(); 32 | $(css.itemsCount).text(items.length); 33 | 34 | if (items.length === 0) { 35 | $(css.noItems).removeClass('hide'); 36 | $(css.placeOrder).hide(); 37 | } 38 | }); 39 | 40 | // place an order 41 | $(css.btnCheckout).on('click', function btnCheckoutClick() { 42 | // first need to check user is loggedin or not 43 | loginUtils.checkUserLoggedIn().done(function responseSuccess(data) { 44 | if (data && data.isLoggedIn) { 45 | alert('yes'); 46 | } else { 47 | alert('no'); 48 | } 49 | }); 50 | }); 51 | } 52 | 53 | function run() { 54 | CartStorage = new jscf.Storage(CONSTANTS.CART_ICON.STORAGE_KEY); 55 | bindEvents(); 56 | } 57 | run(); 58 | }; 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /frontend/app/js/module_config.js: -------------------------------------------------------------------------------- 1 | /* eslint quote-props:0, key-spacing:0 */ 2 | requirejs.config({ 3 | enforceDefine: false, 4 | shim : { 5 | bootstrap: { 6 | deps: ['jquery'], 7 | }, 8 | fileinput: { 9 | deps: ['jquery'], 10 | }, 11 | explorertheme: { 12 | deps : ['fileinput', 'jquery'], 13 | exports: 'explorertheme', 14 | }, 15 | lightslider: { 16 | deps: ['jquery'], 17 | }, 18 | }, 19 | paths: { 20 | 'hbdivide' : '/bower_components/bullhorn-handlebars-helpers/src/math/divide', 21 | 'fileinput' : '/bower_components/bootstrap-fileinput/js/fileinput.min', 22 | 'explorertheme': '/bower_components/bootstrap-fileinput/themes/explorer/theme', 23 | 'jscf' : 'jslib/index', 24 | 'jmask' : '/bower_components/jquery-mask-plugin/dist/jquery.mask', 25 | 26 | // 27 | 28 | 'handlebars': '/bower_components/handlebars/handlebars.amd', 29 | 'jquery': '/bower_components/jquery/dist/jquery', 30 | 'bootstrap': '/bower_components/bootstrap/dist/js/bootstrap', 31 | 'moment': '/bower_components/moment/moment', 32 | 'underscore': '/bower_components/underscore/underscore', 33 | 'lightslider': '/bower_components/lightslider/dist/js/lightslider', 34 | 'cart': 'js/compiled-templates/cart', 35 | 'item': 'js/compiled-templates/item', 36 | 'searchpage': 'js/compiled-templates/searchpage', 37 | 'sell': 'js/compiled-templates/sell', 38 | 'toppage': 'js/compiled-templates/toppage', 39 | 'user': 'js/compiled-templates/user', 40 | 'viewCartItems': 'js/cart/modules/viewCartItems', 41 | 'constants': 'js/constants/modules/constants', 42 | 'itemView': 'js/item/modules/itemView', 43 | 'sellItem': 'js/sell/modules/sellItem', 44 | 'homepage': 'js/toppage/modules/homepage', 45 | 'homepageBindings': 'js/toppage/modules/homepageBindings', 46 | 'mainHeader': 'js/toppage/modules/mainHeader', 47 | 'homeAppliancesWidget': 'js/toppage/modules/widgets/homeAppliancesWidget', 48 | 'laptopsAndDesktopsWidget': 'js/toppage/modules/widgets/laptopsAndDesktopsWidget', 49 | 'newArrivalsWidget': 'js/toppage/modules/widgets/newArrivalsWidget', 50 | 'loginUtils': 'js/utils/modules/loginUtils', 51 | 'viewCartItemsCtrl': 'js/controllers/cart/viewCartItemsCtrl', 52 | 'itemViewCtrl': 'js/controllers/item/itemViewCtrl', 53 | 'homepageWidgetsCtrl': 'js/controllers/toppage/homepageWidgetsCtrl', 54 | // 55 | }, 56 | }); 57 | -------------------------------------------------------------------------------- /frontend/app/jslib/utils/storage.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery'], function StorageModule($) { 3 | 'use strict'; 4 | 5 | var Storage; 6 | 7 | function checkLocalStorage(type) { 8 | var storage; 9 | try { 10 | storage = window[type]; 11 | storage.setItem('test', 'test'); 12 | storage.removeItem('test'); 13 | return true; 14 | } catch (e) { 15 | return false; 16 | } 17 | } 18 | 19 | Storage = function StorageFn(key) { 20 | if (!checkLocalStorage('localStorage')) { 21 | throw 'localStorage is not supportd in this browser'; 22 | } 23 | 24 | this.key = key; 25 | 26 | function setMainKey(skey) { 27 | if (localStorage.getItem(skey) == null) { 28 | localStorage.setItem(skey, undefined); 29 | } 30 | } 31 | 32 | setMainKey(this.key); 33 | 34 | this.setItem = function setValue(skey, value) { 35 | var curValue; 36 | var itemToSet = {}; 37 | 38 | setMainKey(this.key); 39 | 40 | curValue = localStorage.getItem(this.key); 41 | 42 | itemToSet[skey] = value; 43 | if (curValue && curValue !== 'undefined') { 44 | curValue = $.extend({}, JSON.parse(curValue), itemToSet); 45 | } else { 46 | curValue = itemToSet; 47 | } 48 | localStorage.setItem(this.key, JSON.stringify(curValue)); 49 | }; 50 | 51 | this.getItem = function getValue(skey) { 52 | var curValue = localStorage.getItem(this.key); 53 | if (curValue && curValue !== 'undefined') { 54 | return JSON.parse(curValue)[skey]; 55 | } 56 | return undefined; 57 | }; 58 | 59 | this.getAllItems = function getAllItems() { 60 | var curValue = localStorage.getItem(this.key); 61 | if (curValue && curValue !== 'undefined') { 62 | return JSON.parse(curValue); 63 | } 64 | return undefined; 65 | }; 66 | 67 | this.removeItem = function removeItem(skey) { 68 | var curValue = localStorage.getItem(this.key); 69 | if (curValue && curValue !== 'undefined') { 70 | curValue = JSON.parse(curValue); 71 | delete curValue[skey]; 72 | } 73 | curValue = JSON.stringify(curValue); 74 | localStorage.setItem(this.key, curValue === '{}' ? undefined : curValue); 75 | }; 76 | 77 | this.remove = function remove() { 78 | localStorage.removeItem(this.key); 79 | }; 80 | }; 81 | 82 | return Storage; 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /frontend/app/templates/cart/viewCartItems.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Total items in cart: {{items.length}} 11 | 12 | 15 | 16 |
17 |
18 |
19 | {{#if items.length}} 20 | {{#each items as |item itemIndex|}} 21 | {{#with item}} 22 |
23 |
24 |
25 | {{itemName}} 26 |
27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 |
35 |
36 |
37 | {{itemName}} 38 |
{{categoryName}}
39 |
{{itemPrice}}
40 |
41 |
42 | 49 |
50 | {{/with}} 51 | {{/each}} 52 | {{/if}} 53 |
54 |
Currently there are no items in cart.
55 | 56 |
57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 | {{#loadModule 'viewCartItems'}} {{/loadModule}} -------------------------------------------------------------------------------- /frontend/app/assets/styles-less/item/itemView.less: -------------------------------------------------------------------------------- 1 | @color-black: #000; 2 | @label-color: #878787; 3 | 4 | .info-label() { 5 | color: @label-color; 6 | } 7 | 8 | .panel-background() { 9 | background-color: #fff; 10 | margin: 10px; 11 | border-radius: 3px; 12 | box-sizing: border-box; 13 | } 14 | 15 | .item-overview-panel { 16 | .panel-background(); 17 | 18 | .item-image-box { 19 | min-height: 350px; 20 | border: 1px solid #f0f0f0; 21 | margin: 5px; 22 | } 23 | 24 | .detail-header { 25 | margin: 5px; 26 | font-size: x-large; 27 | font-weight: 500; 28 | color: @color-black; 29 | 30 | .category-info { 31 | font-size: small; 32 | color: @label-color; 33 | margin-left: 5px; 34 | 35 | i { 36 | font-size: 10px; 37 | } 38 | } 39 | 40 | .item-name-title { 41 | line-height: 1.4; 42 | padding-left: 5px; 43 | } 44 | } 45 | 46 | .item-price-detail { 47 | color: @color-black; 48 | 49 | .item-price { 50 | font-weight: 600; 51 | font-size: x-large; 52 | } 53 | } 54 | 55 | .add-to-cart-button { 56 | margin-top: 2px; 57 | margin-bottom: 7px; 58 | 59 | button { 60 | width: 200px; 61 | font-size: medium; 62 | } 63 | } 64 | 65 | .delivery-info { 66 | font-size: medium; 67 | margin-top: 15px; 68 | 69 | .info-detail-wrapper { 70 | margin-bottom: 10px; 71 | } 72 | 73 | .label-delivery-info { 74 | .info-label() 75 | } 76 | 77 | table { 78 | color: #388e3c; 79 | } 80 | 81 | .label { 82 | color: #212121; 83 | font-weight: 500; 84 | font-size: medium; 85 | } 86 | 87 | } 88 | 89 | .item-features { 90 | margin-top: 10px; 91 | font-size: medium; 92 | 93 | .label-item-features { 94 | .info-label() 95 | } 96 | 97 | .feature-list { 98 | li { 99 | list-style: none; 100 | position: relative; 101 | padding: 0 0 8px 16px; 102 | 103 | &:after { 104 | content: ""; 105 | height: 6px; 106 | width: 6px; 107 | position: absolute; 108 | top: 6px; 109 | left: 0; 110 | border-radius: 50%; 111 | background: #c2c2c2; 112 | } 113 | } 114 | } 115 | } 116 | 117 | .seller-info { 118 | font-size: medium; 119 | 120 | .label-seller-info { 121 | .info-label() 122 | } 123 | } 124 | } 125 | 126 | .item-description-panel { 127 | .panel-background(); 128 | 129 | .item-description-area { 130 | margin: 10px; 131 | padding: 5px; 132 | } 133 | 134 | .divider { 135 | border: 0.1px solid #878787; 136 | height: 1px; 137 | width: 98%; 138 | margin-left: 1%; 139 | opacity: 0.2; 140 | } 141 | 142 | .description-title { 143 | padding: 5px; 144 | color: #212121; 145 | font-size: x-large; 146 | font-weight: 600; 147 | } 148 | 149 | .details { 150 | margin: 10px 0px 10px 15px; 151 | font-size: medium; 152 | color: #332; 153 | } 154 | } -------------------------------------------------------------------------------- /frontend/app/assets/styles/item/itemView.css: -------------------------------------------------------------------------------- 1 | .item-overview-panel { 2 | background-color: #fff; 3 | margin: 10px; 4 | border-radius: 3px; 5 | box-sizing: border-box; 6 | } 7 | .item-overview-panel .item-image-box { 8 | min-height: 350px; 9 | border: 1px solid #f0f0f0; 10 | margin: 5px; 11 | } 12 | .item-overview-panel .detail-header { 13 | margin: 5px; 14 | font-size: x-large; 15 | font-weight: 500; 16 | color: #000; 17 | } 18 | .item-overview-panel .detail-header .category-info { 19 | font-size: small; 20 | color: #878787; 21 | margin-left: 5px; 22 | } 23 | .item-overview-panel .detail-header .category-info i { 24 | font-size: 10px; 25 | } 26 | .item-overview-panel .detail-header .item-name-title { 27 | line-height: 1.4; 28 | padding-left: 5px; 29 | } 30 | .item-overview-panel .item-price-detail { 31 | color: #000; 32 | } 33 | .item-overview-panel .item-price-detail .item-price { 34 | font-weight: 600; 35 | font-size: x-large; 36 | } 37 | .item-overview-panel .add-to-cart-button { 38 | margin-top: 2px; 39 | margin-bottom: 7px; 40 | } 41 | .item-overview-panel .add-to-cart-button button { 42 | width: 200px; 43 | font-size: medium; 44 | } 45 | .item-overview-panel .delivery-info { 46 | font-size: medium; 47 | margin-top: 15px; 48 | } 49 | .item-overview-panel .delivery-info .info-detail-wrapper { 50 | margin-bottom: 10px; 51 | } 52 | .item-overview-panel .delivery-info .label-delivery-info { 53 | color: #878787; 54 | } 55 | .item-overview-panel .delivery-info table { 56 | color: #388e3c; 57 | } 58 | .item-overview-panel .delivery-info .label { 59 | color: #212121; 60 | font-weight: 500; 61 | font-size: medium; 62 | } 63 | .item-overview-panel .item-features { 64 | margin-top: 10px; 65 | font-size: medium; 66 | } 67 | .item-overview-panel .item-features .label-item-features { 68 | color: #878787; 69 | } 70 | .item-overview-panel .item-features .feature-list li { 71 | list-style: none; 72 | position: relative; 73 | padding: 0 0 8px 16px; 74 | } 75 | .item-overview-panel .item-features .feature-list li:after { 76 | content: ""; 77 | height: 6px; 78 | width: 6px; 79 | position: absolute; 80 | top: 6px; 81 | left: 0; 82 | border-radius: 50%; 83 | background: #c2c2c2; 84 | } 85 | .item-overview-panel .seller-info { 86 | font-size: medium; 87 | } 88 | .item-overview-panel .seller-info .label-seller-info { 89 | color: #878787; 90 | } 91 | .item-description-panel { 92 | background-color: #fff; 93 | margin: 10px; 94 | border-radius: 3px; 95 | box-sizing: border-box; 96 | } 97 | .item-description-panel .item-description-area { 98 | margin: 10px; 99 | padding: 5px; 100 | } 101 | .item-description-panel .divider { 102 | border: 0.1px solid #878787; 103 | height: 1px; 104 | width: 98%; 105 | margin-left: 1%; 106 | opacity: 0.2; 107 | } 108 | .item-description-panel .description-title { 109 | padding: 5px; 110 | color: #212121; 111 | font-size: x-large; 112 | font-weight: 600; 113 | } 114 | .item-description-panel .details { 115 | margin: 10px 0px 10px 15px; 116 | font-size: medium; 117 | color: #332; 118 | } 119 | -------------------------------------------------------------------------------- /frontend/app/templates/toppage/widgets/homeAppliances.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |
7 |

Home appliances

8 | 9 | 12 | 13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 | 39 |
40 |
41 |
42 |
43 |
44 | {{#loadModule "homeAppliancesWidget"}} {{/loadModule}} -------------------------------------------------------------------------------- /frontend/app/js/toppage/modules/homepage.js: -------------------------------------------------------------------------------- 1 | define(['jquery', 'handlebars', 'toppage', 'mainHeader', 'hbdivide', 'jscf'], 2 | function homepage($, Handlebars, toppage, mainHeader, divide) { 3 | 'use strict'; 4 | 5 | return function callback() { 6 | var categories = [ 7 | { 8 | categoryId : 1, 9 | categoryName : 'Electronics', 10 | level : 1, 11 | subCategories: [ 12 | { 13 | categoryId : 3, 14 | categoryName : 'Mobile', 15 | isCategoryHead: true, 16 | level : 2, 17 | subCategories : [ 18 | { 19 | categoryId : 5, 20 | categoryName: 'LG', 21 | level : 3, 22 | }, 23 | { 24 | categoryId : 6, 25 | categoryName: 'Motorola', 26 | level : 3, 27 | }, 28 | { 29 | categoryId : 7, 30 | categoryName: 'Samsung', 31 | level : 3, 32 | }, 33 | ], 34 | }, 35 | { 36 | categoryId : 4, 37 | categoryName : 'Laptop', 38 | level : 2, 39 | isCategoryHead: true, 40 | subCategories : [ 41 | { 42 | categoryId : 8, 43 | categoryName: 'Apple', 44 | level : 3, 45 | }, 46 | { 47 | categoryId : 9, 48 | categoryName: 'Toshiba', 49 | level : 3, 50 | }, 51 | { 52 | categoryId : 10, 53 | categoryName: 'Lenovo', 54 | level : 3, 55 | }, 56 | ], 57 | }, 58 | { 59 | categoryId : 11, 60 | categoryName : 'Desktop PCs', 61 | level : 2, 62 | isCategoryHead: true, 63 | }, 64 | ], 65 | }, 66 | { 67 | categoryId : 2, 68 | categoryName: 'Appliances', 69 | level : 1, 70 | }, 71 | ]; 72 | 73 | /** 74 | * This helper will work to recursively iterate over categories and make nested menu for XS devices. 75 | * Similar like tree structure: 76 | * A 77 | * | 78 | * ---- B 79 | * | 80 | * ---- C 81 | * ---- D 82 | * ---- E 83 | * | 84 | * ---- F 85 | * | 86 | * ---- G 87 | * . 88 | * . 89 | * . 90 | */ 91 | Handlebars.registerHelper('generateSubCategories', function generateSubCategoriesPartial(mwrapper, mcategory, block) { 92 | return (function getCategories(category, wrapper) { 93 | var $wrapper = $(wrapper); 94 | var lis = ''; 95 | var subCategories = category.subCategories; 96 | if (subCategories && subCategories.length) { 97 | $.each(subCategories, function callbackForEachSubCat(key, cat) { 98 | lis += block.fn({ subCat: cat, moreSubCategories: getCategories(cat, wrapper) }); 99 | }); 100 | $wrapper.append(lis); 101 | return $wrapper[0].outerHTML; 102 | } 103 | return ''; 104 | }(mcategory, mwrapper)); 105 | }); 106 | 107 | // Register partials 108 | Handlebars.registerPartial('header', toppage.header({ categories: categories, userMenu: toppage.userMenu() })); 109 | Handlebars.registerPartial('footer', toppage.footer()); 110 | 111 | $('#homepage').html(toppage.homepage()); 112 | }; 113 | }); 114 | -------------------------------------------------------------------------------- /frontend/app/templates/toppage/widgets/newArrivals.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |
7 |

New arrivals

8 | 9 | 12 | 13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 | 42 |
43 |
44 |
45 |
46 |
47 | {{#loadModule "newArrivalsWidget"}} {{/loadModule}} -------------------------------------------------------------------------------- /frontend/app/templates/toppage/widgets/laptopsAndDesktops.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |
7 |

Laptops & Desktops

8 | 9 | 12 | 13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 | 39 |
40 |
41 |
42 |
43 |
44 | {{#loadModule "laptopsAndDesktopsWidget"}} {{/loadModule}} -------------------------------------------------------------------------------- /frontend/app/templates/user/signup.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 | 70 |
-------------------------------------------------------------------------------- /frontend/app/templates/item/itemView.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |
7 |
8 | 9 |
10 |
11 |
12 |
13 |
14 | {{item.categoryName}} 15 |
16 |
17 | {{item.itemName}} 18 |
19 |
20 |
21 |
22 |
23 | {{item.itemPrice}} 24 |
25 |
26 |
27 |
28 | 32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 | 42 | 43 | 46 | 50 | 51 | 52 | 55 | 59 | 60 | 61 | 64 | 68 | 69 |
44 | Burden of delivery: 45 | 47 | 48 | {{item.deliveryBurden}} 49 |
53 | Item is from: 54 | 56 | 57 | {{item.itemSource}} 58 |
62 | Can ship within: 63 | 65 | 66 | {{item.shipTime}} 67 |
70 |
71 |
72 |
73 |
74 |
75 | 76 |
77 |
78 |
    79 |
  • abc
  • 80 |
  • sfjs
  • 81 |
  • sehwr
  • 82 |
83 |
84 |
85 |
86 |
87 | 88 |
89 |
90 | 91 | {{item.sellerName}} 92 | 93 |
94 |
95 |
96 |
97 |
98 |
99 | 100 |
101 |
102 | Details 103 |
104 |
105 | {{item.description}} 106 |
107 |
108 |
109 |
110 | {{#loadModule 'itemView' item}} {{/loadModule}} -------------------------------------------------------------------------------- /frontend/app/js/toppage/modules/widgets/homeAppliancesWidget.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'lightslider'], function homeAppliancesWidgetModule($, jscf) { 3 | 'use strict'; 4 | 5 | return function homeAppliancesWidget() { 6 | var settings = { 7 | css: { 8 | sliderTpl : '#homeAppliancesSliderTpl', 9 | widget : '#homeAppliancesSlider', 10 | sliderItem : '.item', 11 | widgetSettings : '.widget-settings', 12 | widgetSettingsMock: '.widget-settings-mock', 13 | itemImage : '.item-image', 14 | }, 15 | 16 | widget: { 17 | enabled : [true, 'bool'], 18 | image_width : [110, 'int'], 19 | image_height: [110, 'int'], 20 | }, 21 | }; 22 | 23 | var $elems = { 24 | widget : $(settings.css.widget), 25 | sliderTplHtml: $(settings.css.sliderTpl).remove().html(), 26 | }; 27 | 28 | var options; 29 | 30 | /** 31 | * Initialize slider element using lightSlider plugin. 32 | * Set necessary options for slider 33 | */ 34 | function initializeSlideshow($contentSlider) { 35 | var sliderOptions = { 36 | loop : true, 37 | keyPress : true, 38 | enableDrag: false, 39 | autoWidth : true, 40 | prevHtml : '
', 41 | nextHtml : '
', 42 | }; 43 | 44 | if (jscf.browser.isSp) { 45 | sliderOptions.controls = false; 46 | sliderOptions.enableDrag = true; 47 | } 48 | $elems.widget.append($contentSlider); 49 | 50 | $contentSlider.lightSlider(sliderOptions); 51 | 52 | $elems.widget.show(); 53 | } 54 | 55 | /** 56 | * Prepare for rendering slideshow after calling api 57 | * @name prepareSlider 58 | * 59 | * @param {Object} data Data retrieved from api 60 | */ 61 | function prepareSlider(data) { 62 | var $sliderTpl = $($elems.sliderTplHtml); 63 | var css = settings.css; 64 | var $liItem = $sliderTpl.find(css.sliderItem).remove(); 65 | var items = data.items; 66 | var n = items.length; 67 | var sliderData = {}; 68 | var i; 69 | var $sliderItem; 70 | var item; 71 | 72 | for (i = 0; i < n; i++) { 73 | item = items[i]; 74 | sliderData['#ITEM_NAME#'] = item.itemName; 75 | sliderData['#ITEM_IMAGE_URL#'] = item.itemImageUrl; 76 | sliderData['#ITEM_URL#'] = item.itemUrl; 77 | sliderData['#ITEM_PRICE#'] = item.itemPrice; 78 | 79 | $sliderItem = $liItem.clone(); 80 | $sliderItem.html(jscf.commonUtils.replacePlaceholders($sliderItem.html(), sliderData)); 81 | $sliderTpl.append($sliderItem); 82 | 83 | // set css 84 | $sliderItem.find(css.itemImage).css({ width: options.widget.image_width + 'px', height: options.widget.image_height + 'px' }); 85 | } 86 | 87 | initializeSlideshow($sliderTpl); 88 | } 89 | 90 | /** 91 | * Execution starts after validating settings and if this widget is enabled. 92 | * @name run 93 | */ 94 | function run() { 95 | var apiOptions = { 96 | apiUril: '', 97 | data : { 98 | something: '', 99 | }, 100 | }; 101 | // call api to get items 102 | jscf.apiUtils.callApi($.extend({}, apiOptions, options.mock)) 103 | .done(prepareSlider); 104 | } 105 | 106 | /** 107 | * Initialize things and then run if this widget is enabled. 108 | */ 109 | function init() { 110 | var $widget = $elems.widget; 111 | var css = settings.css; 112 | 113 | $.extend($elems, { 114 | widgetSettings: $widget.find(css.widgetSettings), 115 | mockElem : $widget.find(css.widgetSettingsMock, css.widgetSettings), 116 | }); 117 | 118 | options = (function readSettings() { 119 | return { 120 | widget: jscf.commonUtils.readSettingsFromElem(settings.widget, $elems.widgetSettings), 121 | mock : jscf.apiUtils.readApiSettings($elems.mockElem), 122 | }; 123 | }()); 124 | 125 | if (!options || !(options.widget && options.widget.enabled)) { 126 | return; 127 | } 128 | 129 | run(); 130 | } 131 | 132 | // start execution by initing 133 | init(); 134 | }; // /homeAppliancesWidget 135 | }); // define 136 | } // /if 137 | -------------------------------------------------------------------------------- /frontend/app/js/toppage/modules/widgets/newArrivalsWidget.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'lightslider'], function newArrivalsWidgetModule($, jscf) { 3 | 'use strict'; 4 | 5 | return function newArrivalsWidget() { 6 | var settings = { 7 | css: { 8 | sliderTpl : '#newArrivalsSliderTpl', 9 | widget : '#newArrivalsSlider', 10 | sliderItem : '.item', 11 | widgetSettings : '.widget-settings', 12 | widgetSettingsMock: '.widget-settings-mock', 13 | itemImage : '.item-image', 14 | }, 15 | 16 | widget: { 17 | enabled : [true, 'bool'], 18 | image_width : [110, 'int'], 19 | image_height: [110, 'int'], 20 | }, 21 | }; 22 | 23 | var $elems = { 24 | widget : $(settings.css.widget), 25 | sliderTplHtml: $(settings.css.sliderTpl).remove().html(), 26 | }; 27 | 28 | var options; 29 | 30 | /** 31 | * Initialize slider element using lightSlider plugin. 32 | * Set necessary options for slider 33 | */ 34 | function initializeSlideshow($contentSlider) { 35 | var sliderOptions = { 36 | loop : true, 37 | keyPress : true, 38 | enableDrag: false, 39 | autoWidth : true, 40 | prevHtml : '
', 41 | nextHtml : '
', 42 | }; 43 | 44 | if (jscf.browser.isSp) { 45 | sliderOptions.controls = false; 46 | sliderOptions.enableDrag = true; 47 | } 48 | $elems.widget.append($contentSlider); 49 | 50 | $contentSlider.lightSlider(sliderOptions); 51 | 52 | $elems.widget.show(); 53 | } 54 | 55 | /** 56 | * Prepare for rendering slideshow after calling api 57 | * @name prepareSlider 58 | * 59 | * @param {Object} data Data retrieved from api 60 | */ 61 | function prepareSlider(data) { 62 | var $sliderTpl = $($elems.sliderTplHtml); 63 | var css = settings.css; 64 | var $liItem = $sliderTpl.find(css.sliderItem).remove(); 65 | var items = data.items; 66 | var n = items.length; 67 | var sliderData = {}; 68 | var i; 69 | var $sliderItem; 70 | var item; 71 | 72 | for (i = 0; i < n; i++) { 73 | item = items[i]; 74 | sliderData['#ITEM_NAME#'] = item.itemName; 75 | sliderData['#ITEM_IMAGE_URL#'] = item.itemImageUrl; 76 | sliderData['#ITEM_URL#'] = item.itemUrl; 77 | sliderData['#ITEM_PRICE#'] = item.itemPrice; 78 | sliderData['#ITEM_CATEGORY#'] = item.categoryName; 79 | 80 | $sliderItem = $liItem.clone(); 81 | $sliderItem.html(jscf.commonUtils.replacePlaceholders($sliderItem.html(), sliderData)); 82 | $sliderTpl.append($sliderItem); 83 | 84 | // set css 85 | $sliderItem.find(css.itemImage).css({ width: options.widget.image_width + 'px', height: options.widget.image_height + 'px' }); 86 | } 87 | 88 | initializeSlideshow($sliderTpl); 89 | } 90 | 91 | /** 92 | * Execution starts after validating settings and if this widget is enabled. 93 | * @name run 94 | */ 95 | function run() { 96 | var apiOptions = { 97 | apiUril: '', 98 | data : { 99 | something: '', 100 | }, 101 | }; 102 | // call api to get items 103 | jscf.apiUtils.callApi($.extend({}, apiOptions, options.mock)) 104 | .done(prepareSlider); 105 | } 106 | 107 | /** 108 | * Initialize things and then run if this widget is enabled. 109 | */ 110 | function init() { 111 | var $widget = $elems.widget; 112 | var css = settings.css; 113 | 114 | $.extend($elems, { 115 | widgetSettings: $widget.find(css.widgetSettings), 116 | mockElem : $widget.find(css.widgetSettingsMock, css.widgetSettings), 117 | }); 118 | 119 | options = (function readSettings() { 120 | return { 121 | widget: jscf.commonUtils.readSettingsFromElem(settings.widget, $elems.widgetSettings), 122 | mock : jscf.apiUtils.readApiSettings($elems.mockElem), 123 | }; 124 | }()); 125 | 126 | if (!options || !(options.widget && options.widget.enabled)) { 127 | return; 128 | } 129 | 130 | run(); 131 | } 132 | 133 | // start execution by initing 134 | init(); 135 | }; // /newArrivalsWidget 136 | }); // define 137 | } // /if 138 | -------------------------------------------------------------------------------- /frontend/app/jslib/utils/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides some common utility methods 3 | */ 4 | if (typeof define === 'function' && define.amd) { 5 | define(['jquery'], function commons($) { 6 | 'use strict'; 7 | 8 | var commonUtils = {}; 9 | var htmlEntityMap = { 10 | '&': '&', 11 | '<': '<', 12 | '>': '>', 13 | '"': '"', 14 | "'": ''', 15 | '/': '/', 16 | }; 17 | 18 | commonUtils.prototype = {}; 19 | 20 | function escapeHtml(str) { 21 | return String(str).replace(/&<>"'\//g, function replaceCallback(s) { 22 | return htmlEntityMap[s]; 23 | }); 24 | } 25 | 26 | function normalizeAttr(attrName) { 27 | var normalizedAttr; 28 | 29 | if (attrName) { 30 | attrName = attrName.replace(/^data-/, ''); 31 | normalizedAttr = (function findDash(attr) { 32 | var index; 33 | index = attr.indexOf('-'); 34 | if (index > 0) { 35 | attr = findDash(attr.substring(0, index) + attr.charAt(index + 1).toUpperCase() + attr.substring(index + 2)); 36 | } 37 | return attr; 38 | }(attrName)); 39 | } 40 | 41 | return normalizedAttr; 42 | } 43 | 44 | /** 45 | * This utility method can be used to replace placeholders with given data 46 | * @name commonUtils.replacePlaceholders 47 | * 48 | * @param {String} txt text containing placeholders 49 | * @param {Object} data object containing data to replace with each placeholder 50 | * @param {bool} escapeDataHtml whether to escape html or not 51 | * 52 | * @return {String} replaced placeholder string 53 | */ 54 | commonUtils.replacePlaceholders = function replacePlaceholders(txt, data, escapeDataHtml) { 55 | var i; 56 | var d; 57 | 58 | for (i in data) { 59 | d = escapeDataHtml === false ? data[i] : escapeHtml(data[i]); 60 | txt = txt.replace(new RegExp(i, 'g'), d); 61 | } 62 | 63 | return txt; 64 | }; 65 | 66 | /** 67 | * This function will read settings from given html element and return all attrs with respective values as plain object 68 | * 69 | * @name readSettingsFromElem 70 | * 71 | * @param {Object} settings Object of needed settings with value and type 72 | * @param {String|Object} elem Name of element with it's id/class prefix or jquery elemet itself 73 | * 74 | * @return {Object} attr:value pairs as of settings param 75 | */ 76 | commonUtils.readSettingsFromElem = function readSettingsFromElem(settings, elem) { 77 | var $elem = typeof elem === 'string' ? $(elem) : elem; 78 | var nAttrs = {}; 79 | var retAttrs = {}; 80 | 81 | function validateAttr(attr, attrVal) { 82 | var val = nAttrs[attr]; 83 | var type = attrVal[1]; 84 | 85 | if (val) { 86 | try { 87 | switch (type) { 88 | case 'bool': 89 | if (!(val === 'true' || val === 'yes' || val === '1' || val === '0' || val === 'false')) { 90 | throw 'invalid bool value for attr: ' + attr; 91 | } 92 | val = val === 'true' || val === 'yes' || val === '1'; 93 | break; 94 | case 'int': 95 | val = parseInt(val, 10); 96 | break; 97 | case 'float': 98 | val = parseFloat(val); 99 | break; 100 | default: 101 | val = String(val); 102 | } 103 | } catch (e) { 104 | if (attrVal[0] !== undefined) { 105 | val = attrVal[0]; 106 | } else { 107 | throw e; 108 | } 109 | } 110 | } else { 111 | val = attrVal[0]; 112 | } 113 | 114 | return val; 115 | } 116 | 117 | if ($elem.length) { 118 | $.each($elem[0].attributes, function eachAttrs() { 119 | if (this.specified) { 120 | nAttrs[normalizeAttr(this.name)] = this.value; 121 | } 122 | }); 123 | $.each(settings, function eachSettings(key, val) { 124 | retAttrs[key] = validateAttr(key, val); 125 | }); 126 | } 127 | 128 | return retAttrs; 129 | }; 130 | 131 | /** 132 | * Works as ES6 spread operator 133 | */ 134 | commonUtils.spread = function spread(obj) { 135 | var len; 136 | var key; 137 | var k; 138 | 139 | if (!obj.length) { 140 | return obj; 141 | } 142 | 143 | len = obj.length; 144 | key = Array(len); 145 | k = 0; 146 | 147 | for (; k < len; k++) { 148 | key[k] = obj[k]; 149 | } 150 | return key; 151 | }; 152 | 153 | return commonUtils; 154 | }); 155 | } 156 | -------------------------------------------------------------------------------- /frontend/app/js/toppage/modules/widgets/laptopsAndDesktopsWidget.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) { 2 | define(['jquery', 'jscf', 'lightslider'], function laptopsAndDesktopsWidgetModule($, jscf) { 3 | 'use strict'; 4 | 5 | return function laptopsAndDesktopsWidget(block) { 6 | var settings = { 7 | css: { 8 | sliderTpl : '#laptopsAndDesktopsSliderTpl', 9 | widget : '#laptopsAndDesktopsSlider', 10 | sliderItem : '.item', 11 | widgetSettings : '.widget-settings', 12 | widgetSettingsMock: '.widget-settings-mock', 13 | itemImage : '.item-image', 14 | }, 15 | 16 | widget: { 17 | enabled : [true, 'bool'], 18 | image_width : [110, 'int'], 19 | image_height: [110, 'int'], 20 | }, 21 | }; 22 | 23 | var $elems = { 24 | widget : $(settings.css.widget), 25 | sliderTplHtml: $(settings.css.sliderTpl).remove().html(), 26 | }; 27 | 28 | var options; 29 | 30 | /** 31 | * Initialize slider element using lightSlider plugin. 32 | * Set necessary options for slider 33 | */ 34 | function initializeSlideshow($contentSlider) { 35 | var sliderOptions = { 36 | loop : true, 37 | keyPress : true, 38 | enableDrag: false, 39 | autoWidth : true, 40 | prevHtml : '
', 41 | nextHtml : '
', 42 | }; 43 | 44 | if (jscf.browser.isSp) { 45 | sliderOptions.controls = false; 46 | sliderOptions.enableDrag = true; 47 | } 48 | $elems.widget.append($contentSlider); 49 | 50 | $contentSlider.lightSlider(sliderOptions); 51 | 52 | $elems.widget.show(); 53 | } 54 | 55 | /** 56 | * Prepare for rendering slideshow after calling api 57 | * @name prepareSlider 58 | * 59 | * @param {Object} data Data retrieved from api 60 | */ 61 | function prepareSlider(data) { 62 | var $sliderTpl = $($elems.sliderTplHtml); 63 | var css = settings.css; 64 | var $liItem = $sliderTpl.find(css.sliderItem).remove(); 65 | var items = data.items; 66 | var n = items.length; 67 | var sliderData = {}; 68 | var i; 69 | var $sliderItem; 70 | var item; 71 | 72 | for (i = 0; i < n; i++) { 73 | item = items[i]; 74 | sliderData['#ITEM_ID#'] = item.itemId; 75 | sliderData['#ITEM_NAME#'] = item.itemName; 76 | sliderData['#ITEM_IMAGE_URL#'] = item.itemImageUrl; 77 | sliderData['#ITEM_URL#'] = item.itemUrl; 78 | sliderData['#ITEM_PRICE#'] = item.itemPrice; 79 | 80 | $sliderItem = $liItem.clone(); 81 | $sliderItem.html(jscf.commonUtils.replacePlaceholders($sliderItem.html(), sliderData)); 82 | $sliderTpl.append($sliderItem); 83 | 84 | // set css 85 | $sliderItem.find(css.itemImage).css({ width: options.widget.image_width + 'px', height: options.widget.image_height + 'px' }); 86 | } 87 | 88 | initializeSlideshow($sliderTpl); 89 | } 90 | 91 | /** 92 | * Execution starts after validating settings and if this widget is enabled. 93 | * @name run 94 | */ 95 | function run() { 96 | var apiOptions = { 97 | apiUril: '', 98 | data : { 99 | something: '', 100 | }, 101 | }; 102 | // call api to get items 103 | jscf.apiUtils.callApi($.extend({}, apiOptions, options.mock)) 104 | .done(prepareSlider); 105 | } 106 | 107 | /** 108 | * Initialize things and then run if this widget is enabled. 109 | */ 110 | function init() { 111 | var $widget = $elems.widget; 112 | var css = settings.css; 113 | 114 | $.extend($elems, { 115 | widgetSettings: $widget.find(css.widgetSettings), 116 | mockElem : $widget.find(css.widgetSettingsMock, css.widgetSettings), 117 | }); 118 | 119 | options = (function readSettings() { 120 | return { 121 | widget: jscf.commonUtils.readSettingsFromElem(settings.widget, $elems.widgetSettings), 122 | mock : jscf.apiUtils.readApiSettings($elems.mockElem), 123 | }; 124 | }()); 125 | 126 | if (!options || !(options.widget && options.widget.enabled)) { 127 | return; 128 | } 129 | 130 | run(); 131 | } 132 | 133 | // start execution by initing 134 | init(); 135 | }; // /laptopsAndDesktopsWidget() 136 | }); // /define () 137 | } // /if 138 | -------------------------------------------------------------------------------- /frontend/app/assets/styles/toppage/homepage.css: -------------------------------------------------------------------------------- 1 | /*
*/ 2 | .main-header { 3 | background-color: #337ab7; 4 | padding-top: 10px; 5 | } 6 | .main-header .logo { 7 | background: url(../../img/logo.jpg) no-repeat; 8 | background-size: contain; 9 | width: 100%; 10 | height: 38px; 11 | } 12 | .main-header .navbar { 13 | min-height: 0px; 14 | margin-bottom: 0px; 15 | } 16 | .main-header .main-header-searchbar .search-text { 17 | width: 100%; 18 | } 19 | .main-header .navbar-top-links .cart-icon.btn-lg .cart-items-number { 20 | display: none; 21 | width: 22px; 22 | height: 21px; 23 | font-size: 15px; 24 | } 25 | @media (max-width: 1030px) { 26 | .main-header .navbar-right { 27 | margin-right: -5px; 28 | } 29 | .main-header .navbar-top-links { 30 | padding-left: 0px; 31 | padding-right: 0px; 32 | } 33 | } 34 | @media only screen and (min-device-width: 768px) and (max-device-width: 992px) { 35 | .main-header .navbar-right { 36 | margin-right: -12px; 37 | } 38 | .main-header .navbar-top-links { 39 | margin-left: -11px; 40 | } 41 | } 42 | @media only screen and (min-device-width: 972px) and (max-device-width: 1024px) { 43 | .main-header .navbar-top-links { 44 | padding-left: 0px; 45 | padding-right: 20px; 46 | } 47 | } 48 | @media (max-width: 375px) { 49 | .main-header .main-header-searchbar { 50 | padding-left: 0px; 51 | padding-right: 0px; 52 | } 53 | .main-header .navbar-top-links { 54 | padding-left: 0px; 55 | padding-right: 0px; 56 | } 57 | .main-header .navbar-top-links .navbar-right { 58 | margin-right: -6px; 59 | } 60 | .main-header .navbar-top-links .navbar-right .dropdown { 61 | margin-left: -3px; 62 | } 63 | } 64 | .main-header .navbar-top-links .navbar-right li { 65 | display: inline-block; 66 | } 67 | .main-header .xs-user-dropdown { 68 | float: right; 69 | } 70 | .main-header .xs-header .icons-right { 71 | float: right; 72 | } 73 | .main-header .xs-header .icons-right .cart-icon { 74 | margin-right: 5px; 75 | position: relative; 76 | } 77 | .main-header .cart-items-number { 78 | display: none; 79 | width: 23px; 80 | height: 20px; 81 | background: #d32f2f; 82 | border-radius: 50%; 83 | -moz-border-radius: 50%; 84 | -webkit-border-radius: 50%; 85 | color: #fff; 86 | top: 2px; 87 | position: absolute; 88 | right: 2px; 89 | font-size: 14px; 90 | font-weight: 900; 91 | border: 1px solid #fff; 92 | } 93 | .main-header .xs-searchbar .xs-searchbar-form { 94 | margin-bottom: 10px; 95 | margin-top: 5px; 96 | } 97 | .main-header .xs-searchbar .xs-searchbar-form #searchBtn { 98 | margin-top: -1px; 99 | padding-bottom: 8px; 100 | } 101 | .main-header .xs-searchbar .xs-searchbar-form #searchBtn .glyphicon { 102 | top: 3px; 103 | } 104 | /* */ 105 | /* */ 106 | .main-header-menubar .navbar-nav > li > a { 107 | color: white; 108 | } 109 | .main-header-menubar .navbar-nav li a:hover, 110 | .main-header-menubar .navbar-nav li a:focus { 111 | background-color: transparent; 112 | color: yellow; 113 | } 114 | .main-header-menubar .nav .open > a { 115 | background-color: transparent; 116 | } 117 | .main-header-menubar .nav .open > a:focus, 118 | .main-header-menubar .nav .open > a:hover { 119 | background-color: transparent; 120 | } 121 | .main-header-menubar .nav, 122 | .main-header-menubar .collapse, 123 | .main-header-menubar .dropup, 124 | .main-header-menubar .dropdown { 125 | position: static; 126 | } 127 | .main-header-menubar .container { 128 | position: relative; 129 | } 130 | .main-header-menubar .dropdown-menu { 131 | left: auto; 132 | } 133 | .main-header-menubar .dropdown-menu li a { 134 | color: black; 135 | text-decoration: none; 136 | } 137 | .main-header-menubar .dropdown-menu li a:hover { 138 | color: blue; 139 | } 140 | .main-header-menubar .navbar-nav .dropdown .dropdown-menu.top-category { 141 | left: 0; 142 | right: 0; 143 | } 144 | .main-header-menubar .navbar-nav .dropdown li { 145 | list-style: none; 146 | } 147 | .main-header-menubar .navbar-nav .dropdown li .category-head .caret { 148 | transform: rotate(270deg); 149 | } 150 | /* */ 151 | /* */ 152 | .main-header .usermenu-dropdown ul { 153 | padding-left: 5px; 154 | } 155 | .main-header .usermenu-dropdown li { 156 | width: 100%; 157 | list-style: none; 158 | } 159 | .main-header .usermenu-dropdown li a { 160 | text-decoration: none; 161 | font-size: 16px; 162 | padding-top: 3px; 163 | padding-bottom: 3px; 164 | color: black; 165 | } 166 | .main-header .usermenu-dropdown li a:hover { 167 | color: blue; 168 | } 169 | .main-header .usermenu-dropdown .sign-in { 170 | padding-left: 5px; 171 | } 172 | .main-header .usermenu-dropdown .sign-in button { 173 | width: 95%; 174 | } 175 | /* */ 176 | /*