#app
View Compiled
@import url('https://fonts.googleapis.com/css?family=Montserrat:700|Open+Sans');


$white: #ffffff;
$gray: #f2f3f5;
$light_gray: #fafafa;
$bluish_gray: #e5e8ea;
$dark_gray: #718592;
$smoke: #F6F7F8;
$text_gray: #b3b3b3;
$text_dark_gray: #83949e;
$red: #FF3366;
$yellow: #ffce33;
$dark: #011627;
$green: #2EC4B6;
$blue: #20A4F3;
$pink: #ff33d8;


$text_white: $white;
$text_night_white: $light_gray;

$bg_white: #ffffff;
$bg_night_white: #192935;

$bg_gray: #f2f3f5;
$bg_night_gray: #0e161d;

$border_gray: $bluish_gray;
$border_night_gray: #284254;



$spacer: 5px;
$spacer_m: $spacer*2;
$spacer_xm: $spacer*3;
$spacer_xxm: $spacer*4;
$spacer_xxxm: $spacer*5;
$spacer_l: $spacer*6;
$spacer_xl: $spacer*7;
$spacer_xxl: $spacer*8;
$spacer_xxxl: $spacer*9;

$btn_height: 38px;
$btn_width: 110px;


$shadow-l0: 0 1px 3px rgba(0,0,0,0.1);
$shadow-l1: 0 4px 8px rgba(0,0,0,0.2);
$shadow-l2: 0 6px 20px rgba(0, 0, 0, 0.3);

$header_height: 50px;
$aside_width: 200px;
$aside_actions_height: 170px;

$cart_width: 350px;

$product_min_height: 260px;
$product_image_height: 200px;
$product_details_image_height: 300px;
$product_details_media_width: 550px;
$discount_height: 22px;

$colorpicker_items_per_row: 5;
$colorpicker_spacing: $spacer / 2;
$colorpicker_size: (($aside_width - ($spacer_xxm * 2)) / $colorpicker_items_per_row) - ($colorpicker_spacing * 2) - 1;

$tablet_breakpoint: 1024px;
$tablet_s_breakpoint: 800px;
$tablet_xs_breakpoint: 600px;

$header_z: 9;
$z_l0: 9;
$z_l1: 99;

$counter_size: 20px;

$ph_height: 15px;
$ph_radius: 5px;

$time: 0.2s;


* {
  box-sizing: border-box;
  word-break: break-word;
  max-width: 100%;
  font-family: 'Open Sans', sans-serif;
}

.clearfix:before,
.clearfix:after{
  content: '';
  display:table;
  clear: both;
}

html,
body{
  color: $dark;
}


html,
body,
#app,
.app-inner,
.page,
.page > .content,
.a-side{
  margin: 0;
  padding: 0;
  height: 100%;
  min-height: 100%;
}

.no-decoration{
  text-decoration: none;
}

@mixin actionize($selector, $bgColor, $color: $white,$colorChangeStep: 5){
  #{$selector}{
    background-color: $bgColor;
    color: $color;
    cursor: pointer;
    
    &:hover,
    &:focus{
      @if $bgColor == $white {
        background-color: darken($bgColor, $colorChangeStep);  
      } @else {
        background-color: lighten($bgColor, $colorChangeStep);  
      }
      
    }
    
    &:active{
      @if $bgColor == $white {
        background-color: darken($bgColor, $colorChangeStep * 2);  
      } @else {
        background-color: darken($bgColor, $colorChangeStep);  
      }
    }
  }
}


@mixin actionizeBtn($selector, $bgColor, $color: $white,$colorChangeStep: 5){
  $btnSelector: '.btn.'+$selector;
  @include actionize($btnSelector, $bgColor, $color, $colorChangeStep);
}

.btn{
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  padding: $spacer_m;
  border-radius: 7px;
  font-size: 13px;
  
  &.wide{
    width: 100%;
  }
  
  .icon,
  .text{
    height: 18px;
    line-height: 18px;
  }
  
  .icon{
    font-size: 13px;    
  }
  
  .icon + .text{
    padding-left: $spacer;
  }
}


@mixin bg($color_name: white, $color: #fff, $borderize: false, $darken: 20){
    @if($borderize){
      .bg-#{$color_name}-bordered{
        background-color: $color;
        border-color: darken($color, $darken);
      }
  
      &.night-mode .bg-#{$color_name}-bordered{
        border-color: lighten($color, $darken);
      }
    } @else {
      .bg-#{$color_name}{
        background-color: $color;
      }
    }
  
}

$bg_colors: (white, $white),
            (gray, $gray), 
            (bluish-gray, $bluish_gray), 
            (yellow, $yellow), 
            (dark, $dark), 
            (black, $dark),
            (red, $red), 
            (blue, $blue), 
            (pink, $pink);

.app-inner {
  overflow-y: scroll;
  
  h1,h2,h3,.bold{
    font-family: 'Montserrat', sans-serif;
    letter-spacing: 1px;
  }
  
  &.night-mode{
    color: $text_night_white;
  }
  
  @each $color_name, $color in $bg_colors {
    @include bg($color_name, $color);
  }
  @each $color_name, $color in $bg_colors {
    @include bg($color_name, $color, true);
  }
  
  @include actionizeBtn('blue', $blue);
  @include actionizeBtn('red', $red);
  @include actionizeBtn('green', $green);
  @include actionizeBtn('dark', $dark);
  @include actionizeBtn('white', $white, $dark);
  
  &.night-mode {  
    @include actionizeBtn('white', $bg_night_white);
  }
  
  .header{
    display: flex;
    position: fixed;
    height: $header_height;
    background: $bg_white;
    width: calc(100% - #{$aside_width});
    margin-left: $aside_width;
    padding: 0 $spacer_xxxm;
    z-index: $header_z;
    border-bottom: solid 1px $gray;

    .cart-icon .counter{
      position: absolute;
      top: 8px;
      right: 8px;
    }
  }
  &.night-mode .header{
    background-color: $bg_night_white;  
    border-bottom-color: $bg_night_gray;
  }
  

  .header-navigation{
    flex-grow: 1;
    padding: 0;
    margin: 0;

    .link {
      display: inline-block;
      padding: 0 $spacer_xm;
      height: $header_height - 1;
      line-height: $header_height;
      text-decoration: none;

      &.active-link{
        font-weight: bold;
      }
    }  
  }
  
  
  @include actionize('.header-navigation .link', $white, $dark);  
  &.night-mode {
    @include actionize('.header-navigation .link', $bg_night_white, $white);  
  }

  


  .page {
    display: block;
    background: $bg_gray;
    height: auto;

    .a-side{
      position: fixed;
      width: $aside_width;
      min-width: $aside_width;
      background: $bg_white;
      border-right: solid 1px $gray;
      height: 100dvh;
      overflow-y: auto;
      padding-bottom: $aside_actions_height;
      
      .page-actions {
          position: fixed;
          width: $aside_width;
          left: 0;
          bottom: 0;
          padding: 20px;
          border-top: solid 1px $gray;
          background: inherit;
      }
    }

    .content{
      padding: 0 $spacer_xxm;
      margin-left: $aside_width;
      min-height: calc(100vh - #{$header_height});
    }

    &.cart-is-opened{
      margin-right: $cart_width;
    }

    .a-side,
    .content{
      padding-top: $header_height;
    }

    .content{
      & > .title{
        padding: 0 $spacer-m;
      }
    }
  }
  &.night-mode .page{
    background-color: $bg_night_gray;
    
    .a-side{
      background: $bg_night_white;
      border-right-color: $bg_night_gray;
      
      .page-actions {
        border-top-color: $bg_night_gray;
      }
    }
  }

  .products{
    display: flex;
    flex-wrap: wrap;
    margin: 0;
    padding: 0;

    .product{
      position: relative;
      margin: $spacer-m;
      background: $bg_white;
      list-style-type: none;
      flex-basis: calc(25% - (#{$spacer-m} * 2));
      min-height: $product_min_height;
      transition: box-shadow 0.15s ease-out;
      box-shadow: $shadow-l0;

      &:hover:not(.placeholder){
        box-shadow: $shadow-l1;
      }

      &:active:not(.placeholder){    
        box-shadow: $shadow-l0;
      }

      .details{
        display: flex;
        
        .name,
        .price{
          color: $dark;
        }
      }
    }
  }
  &.night-mode .products .product{
    background-color: $bg_night_white;
    
    .details{
      .name,
      .price{
        color: $white;
      }
    }
  }

  
  &.night-mode .product.page > .content{
    background-color: $bg_night_white;
  }
  
  .product{

    &.page > .content{
      background: $bg_white;
      height: 100vh;
      padding-top: $header_height + $spacer_xxm;
      display: flex;

      .media{
        flex-basis: $product_details_media_width;
        min-width: $product_details_media_width;

        .image{
          height: $product_details_image_height;
          background-size: auto 100%;
          background-repeat: no-repeat;
        }

      }

      .details{
        flex-grow: 1;
        padding: 0 $spacer_xxm $spacer_xxm;

        .title{
          margin: 0;
        }

        .price-wrapper{
            padding: $spacer_m 0 $spacer_xm;
            font-size: 16px;
        }

        .subtitle{
          color: $text_gray;
          text-transform: uppercase;
          font-size: 12px;
          margin-bottom: $spacer;
        }

        .actions-wrapper{
          padding-top: $spacer_xxm;
        }
      }
    }

    &.placeholder{
      position: relative;
      overflow: hidden;
      background: $light_gray;
    }

    &.placeholder:after{
      content:'';
      background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255,255,255,0.2) 50%, rgba(255, 255, 255, 0) 100%);
      position: absolute;
      top:0;
      left:0;
      bottom:0;
      right:0;
      transform: translateX(-100%);
      animation: placehodler 2.5s ease-in-out infinite;
    }

    .image{
      height: $product_image_height;
      width: 100%;
      background-size: cover;
      background-position: center;
      background-color: $bluish_gray;
    }

    .discount{
      position: absolute;
      background: $red;
      color: $white;
      transform: rotate(45deg);
      transform-origin: 10px 24px;
      height: $discount_height;
      right: 0;

      &:before,
      &:after{
        position: absolute;
        content: '';
        border: solid #{$discount_height / 2} transparent;
        border-right-color: $red;
        border-bottom-color: $red;
        left: -#{$discount_height};
        top: 0px;
      }


      &:after{
        border-left-color: $red;
        border-bottom-color: $red;
        border-right-color: transparent;
        right: -#{$discount_height};
        left: auto;
      }
    }


    .details{
      padding: $spacer_xxm $spacer_m;
      font-size: 14px;

      .price{
        margin-left: auto;
      }

      .old-price{
        text-decoration: line-through;
        color: $red;
      }
    }
  }



  @media screen and (max-width: #{$tablet_breakpoint}){
    .products .product{
      flex-basis: calc((100% / 3) - (#{$spacer-m} * 2));
    }
  }

  @media screen and (max-width: #{$tablet_s_breakpoint}){
    .products .product{
      flex-basis: calc((100% / 2) - (#{$spacer-m} * 2));
    }
  }

  @media screen and (max-width: #{$tablet_xs_breakpoint}){
    .products .product{
      flex-basis: calc(100% - (#{$spacer-m} * 2));
    }
  }

  @keyframes placehodler{
    0%, 100% {
        transform: translateX(-100%);
    }

    50%{
        transform: translateX(100%);
    }
  }

  .informer {
    padding: $spacer_m $spacer_xxm;
    background: $bg_white;
    box-shadow: #{$shadow-l0}; 

    .title{
      padding: $spacer_m 0;
      margin: 0;
      text-align: center;
    }
  }
  
  &.night-mode .informer{
    background: $bg_night_white;
  }


  .a-side{
    padding-left: $spacer_xxm;
    padding-right: $spacer_xxm;  
  }

  label {
      font-weight: 900;
      font-family: 'Montserrat';
      display: block;
      font-size: 12px;
      text-transform: uppercase;
  }

  select {
    display: block;
    width: 100%;
    margin-top: $spacer;
    padding: $spacer;
    height: 30px;
    background: $bluish_gray;
    color: $dark;
    border-color: transparent;
  }
  
  &.night-mode select{
    background: $bg_night_gray;
    color: $text_night_white;
  }


  .placeholder {
    .item{
      background: $bluish_gray;
      border-radius: $ph_radius;
      width: 100%;
      position: relative;
      overflow: hidden;
      margin-bottom: $spacer_m;

      &:after{
        content:'';
        background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255,255,255,0.2) 50%, rgba(255, 255, 255, 0) 100%);
        position: absolute;
        top:0;
        left:0;
        bottom:0;
        right:0;
        transform: translateX(-100%);
        animation: placehodler 2.5s ease-in-out infinite;
      }
    }

    .text{
      height: $ph_height;
    }

    .text + .text:last-of-type{
      width: 50%;
    }

    &.button .text{
      height: $btn_height;
      width: $btn_width;
    }

    &.price .text{
      margin: 0;
      height: 20px;
      width: 70px;
    }

    &.title .text{
      margin: 0;
      height: 34px;
      width: 40%;
    }

  }


  .counter {
      background: $red;
      color: $white;
      width: $counter_size;
      height: $counter_size;
      display: inline-block;
      text-align: center;
      line-height: $counter_size;
      border-radius: 50%;
      font-size: 11px;
  }


  .cart-icon {
    position: relative;
    line-height: $header_height;
    padding: 0 $spacer_xxxm;

    .counter,
    .icon{
      display: inline-block;
    }

    .icon{
      line-height: $header_height;
      transition: 0.3s ease-out;
    }

    .counter{
      &:not(.active){
        transform: scale(0);
      }
    }
  }

  @include actionize('.cart-icon', $white, $dark);
  
  &.night-mode {
    @include actionize('.cart-icon', $bg_night_white, $white);
  }
  
  &.night-mode .cart{
    background-color: $bg_night_white;  
    border-left-color: $bg_night_gray;
    
    .actions-wrapper{
      background-color: $bg_night_white;  
      border-top-color: $bg_night_gray;
    }
    
    .list .item{
      border-bottom-color: $bg_night_gray;
    }
  }
  
  .cart{
    position: fixed;
    top: 0;
    right: 0;
    width: $cart_width;
    height: 100vh;
    background: $bg_white;
    padding: ($header_height + $spacer_m) $spacer_xxm $spacer_m;
    box-shadow: $shadow-l2;
    z-index: $z-l1;
      
    .cart-topbar {
      display: flex;
      text-align: center;
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      height: $header_height;
      background: $white;
      border-bottom: solid 1px $border_gray;
      
      .cart-title{
        margin: auto;
      }
    }

    .empty{
      text-align: center;
      position: absolute;
      height: 105px;
      width: 100%;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      margin: auto;
      color: $text_gray;

      .icon{
         font-size: 48px;
      }

      .text{
        font-size: 13px;
      }
      
      .hint{
        padding: 0 $spacer_xxxm;
      }
    }

    .list {

      .item {
        display:flex;
        padding: $spacer 0;
        border-bottom: solid 1px $gray;

        .image{
          width: 36px;
          height: 36px;
        }

        .details{
          padding-top: 0;
          padding-bottom: 0;
        }

        .name{
          font-size: 13px;
        }

        .price{
          font-size: 13px;
        }



        .count{
          margin-left: auto;
          line-height: 36px;
        }
      }
    }

    .total{
      display: flex;
      padding: $spacer_xm 0;

      .amount{
        margin-left: auto;
      }
    }

    .actions-wrapper{
      position: fixed;
      bottom: 0;
      right: 0;
      width: $cart_width - 1;
      background: $bg_white;
      border-top: solid 1px $gray;
      padding: $spacer_xxm;
    }
  }


  .toggler{
    $status_icon_rotate: -270deg;
    
    position: relative;

    input[type="checkbox"]{
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      opacity: 0;
      width: 100%;
      height: 100%;
      margin: 0;
      z-index: 9;
      cursor: pointer;
    }

    .view{
      display: flex;
      
      .label{
        padding-right: $spacer_m;
        font-size: 13px;
        color: $text_dark_gray;
        height: 18px;
        line-height: 18px;
      }

      .state{
        position: absolute;
        width: 13px;
        height: 13px;
        top: -1px;
        left: 2px;
        right: 0;
        bottom: 0;
        margin: 0;
        padding: 0;
        line-height: initial;
        
        &.on{
          .icon{
            transform: rotate($status_icon_rotate);
            opacity: 0;
          }
        }
        &.off{
        }

        .icon{
          font-size: 13px;
          color: $text_gray;
          transform: rotate($status_icon_rotate);
          transition: $time ease-out;
        }
      }

      .body{
        position: relative;
        flex-grow: 1;

        background: $bg_gray;
        border-radius: 9px;
        height: 18px;
        border: solid 1px $border_gray;
        max-width: 40px;
        margin-left: auto;

        .indicator{
          position: absolute;
          width: 18px;
          height: 18px;
          left: -1px;
          top: -1px;
          border-radius: 50%;
          background: $bg_white;
          transform: translate3d(0,0,0);
          transition: $time ease-out;
          box-shadow: $shadow-l0;
        }
      }
    }

    input:checked ~ .view {
      .state.on .icon{
        transform: rotate(0deg);
        opacity: 1;
      }
      .state.off .icon{
        opacity: 0;
        transform: rotate($status_icon_rotate);
      }

      .indicator{
        left: calc(100% + 1px);
        transform: translateX(-100%);
      }
    } 
  }
  
  &.night-mode .toggler{
    .body {
      background: $bg_night_gray;
      border-color: $border_night_gray;
      
      .indicator{
        background: $border_night_gray;
      }
    }
  }
  
  .tabber {
      display: flex;
      border-radius: 7px;
      overflow: hidden;
      border: solid 1px $border_gray;
    
      .item {
          flex-grow: 1;
          text-align: center;
          background: $bg_gray;
          
          &:not(:last-of-type) {
              border-right: solid 1px $border_gray;
          }
        
          &.selected {
              background: $bg_white;
          }
      }
    
      
      @include actionize('.item:not(.selected)', $bg_gray);
  }
  
  &.night-mode .tabber{
    border-color: $border_night_gray;

    .item {
      background: $bg_night_gray;
      
      &:not(:last-of-type) {
        border-right: solid 1px $border_night_gray;
      }
      
      &.selected {
        background: $border_night_gray;
      }
    }
    
    @include actionize('.item:not(.selected)', $bg_night_gray);
  }
  
  
  .colorpicker {
    .list {
      display: flex;
      flex-flow: row wrap;
      
      .item{
        position: relative;
        width: $colorpicker_size;
        height: $colorpicker_size;
        flex-grow: 0;
        flex-shrink: 1;
        flex-basis: $colorpicker_size;
        border-radius: 50%;
        border-style: solid;
        border-width: 1px;
        margin: $colorpicker_spacing;
        transition: $time ease-out;
        
        &:not(.selected) {
          transform: scale(0.7);
        }
        
        &.active{
          cursor: pointer;
        }
        
        &.none{
          border-color: $border_gray;
          
          &:after {
            content: '';
            position: absolute;
            width: 100%;
            top: 50%;
            height: 1px;
            background: $border_gray;
            transform: rotate(-45deg);
          }
          
        }
        
        &.selected{
          border-width: 2px;
        }
      }
    }
  }
  
  &.night-mode .colorpicker .list .item .none{
    border-color: $border_night_gray;
    &:after {
      background: $border_night_gray;
    }
  }
  
}

.night-mode #devby{
  filter: contrast(0) brightness(2);
}
View Compiled
console.clear();


// Initialize Firebase
const config = {
  apiKey: "AIzaSyBOi39p524OJTY8KFo_vPUK0xK_aJX84hE",
  authDomain: "react-store-bc88f.firebaseapp.com",
  databaseURL: "https://react-store-bc88f.firebaseio.com",
  projectId: "react-store-bc88f",
  //storageBucket: "",
  messagingSenderId: "638463324339"
};
firebase.initializeApp(config);

const db = firebase.firestore();
const settings = {
  timestampsInSnapshots: true
};
db.settings(settings);

const productsRef = db.collection("products");
const statsRef = db.collection("stats");


const app = document.getElementById('app');
const dictionary = {
  'nav_home' : {
                  'eng' : 'Homepage',
                  'ua' : 'Домашня сторінка',
               },
  'nav_for_her' : {
                  'eng' : 'For her',
                  'ua' : 'Для неï',
               },
  'nav_for_him' : {
                  'eng' : 'For him',
                  'ua' : 'Для нього',
               },
  'stitle_for_her' : {
                  'eng' : 'For her',
                  'ua' : 'Для неï',
               },
  'stitle_for_him' : {
                  'eng' : 'For him',
                  'ua' : 'Для нього',
               },
  'ptitle_home' : {
                  'eng' : 'Shoes for everyone',
                  'ua' : 'Взуття для кожного',
               },
  'ptitle_for_her' : {
                  'eng' : 'Best shoes for her',
                  'ua' : 'Найкраще взуття для неï',
               },
  'ptitle_for_him' : {
                  'eng' : 'Strongest shoes for him',
                  'ua' : 'Наймiцнiше взуття для нього',
               },
  'ptitle_cart' : {
                  'eng' : 'Cart',
                  'ua' : 'Кошик',
               },
  'header_description' : {
                  'eng' : 'Description',
                  'ua' : 'Опис',
               },
  'header_total' : {
                  'eng' : 'Total',
                  'ua' : 'Усього',
               },
  'header_product_not_found' : {
                  'eng' : 'Oh, no!',
                  'ua' : 'O, нi!',
               },
  'text_product_not_found' : {
                  'eng' : 'We couldn\'t find such shoes... Maybe try different filters? 🤷‍',
                  'ua' : 'Ми не змогли знайти таке взуття... Може спробуєте iнщi фiльтри? 🤷‍',
               },
  'label_shoes_for' : {
                  'eng' : 'Shoes For',
                  'ua' : 'Взуття Для',
               },
  'label_brand' : {
                  'eng' : 'Brand',
                  'ua' : 'Бренд',
               },
  'label_color' : {
                  'eng' : 'Color',
                  'ua' : 'Колiр',
               },
  'action_reset_filters' : {
                  'eng' : 'Reset Filters',
                  'ua' : 'Скинути Фiльтри',
               },
  'action_add_to_cart' : {
                  'eng' : 'Add To Cart',
                  'ua' : 'Додати До Кошика',
               },
  'action_checkout' : {
                  'eng' : 'Checkout',
                  'ua' : 'Придбати',
               },
  'label_lightness_mode' : {
                  'eng' : 'Lightness mode',
                  'ua' : 'Режим яскравостi',
               },
  'text_empty_cart' : {
                  'eng' : 'You cart is empty right now.\nClick "Add To Cart" button to start shopping.',
                  'ua' : 'Ваш кошик зараз попрожнiй.\nТацнiть по кнопцi "Додати До Кошика" щоб розпочати шопитись.',
               },
  'placeholder_any' : {
                  'eng' : 'Any',
                  'ua' : 'Будь-що',
               },
  'placeholder_any_m' : {
                  'eng' : 'Any',
                  'ua' : 'Будь-який',
               },
  'placeholder_any_f' : {
                  'eng' : 'Any',
                  'ua' : 'Будь-якa',
               },
  'placeholder_any_p' : {
                  'eng' : 'Any',
                  'ua' : 'Будь-якi',
               },
  'placeholder_any_n' : {
                  'eng' : 'Any',
                  'ua' : 'Будь-яке',
               },
  'placeholder_any_s' : {
                  'eng' : 'Any',
                  'ua' : 'Будь-чого',
               },
  
 
};
const appData = {
  product: {
    fetchIsPending: false,
    fetchIsFulfilled: false,
    fetchIsRejected: false,
    
    details: {
      fetchIsPending: false,
      fetchIsFulfilled: false,
      fetchIsRejected: false,
      data: {},
      openedURL: '',
    },
    
    list: [],
    filters: {
      brands: [],
      categories: [],
      colors: []
    },
    appliedFilters: {
      gender: '',
      brand: '',
      category: '',
      color: '',
    },
    settings: {
      itemsPerPage: 12,
    },
    errors: {
      fetch: null,
    },
    cart: {
      list: {},
      isEmpty: true,
    },
  },
  ui: {
    cart: {
      isOpened: false,
    },
    nightModeEnabled: false,
    selectedLang: 'eng',
    langs: ['eng', 'ua'],  
    dictionary: dictionary,
  },
  navigation: {
    currentPageSlug : 'home',
    headerLinks : [
       {
         'url' : '/home',
         'name' : 'nav_home'
       },
       {
         'url' : '/category/women',
         'name' : 'nav_for_her'
       },
       {
         'url' : '/category/men',
         'name' : 'nav_for_him'
       }
     ],
     pages: {
        'home' : {
          title : 'ptitle_home'
        },
        'women' : {
          title : 'ptitle_for_her'
        },
        'men' : {
          title : 'ptitle_for_him'
        }
     },
  },
  pageData: {},
};


const {
  BrowserRouter,
  Switch,
  Route,
  Link,
  NavLink,
  Redirect,
  DefaultRoute,
  PropsRoute,
} = ReactRouterDOM;
const { withRouter } = ReactRouter;



Array.prototype.compare = (arrB) => {
  if(!(Array.isArray(this) && Array.isArray(arr))) return false;
  const str = (arr)=>{ return JSON.stringify(arr); };
  
  return str(this) === str(arrB);
};



/* Redux stuff */

const {connect, Provider} = ReactRedux;
const { applyMiddleware, combineReducers, createStore} = Redux;
const ReduxThunk = window.ReduxThunk.default;
const logger = reduxLogger.logger;

const Actions = {
  ui : {
    filters : {
      set: 'SET_UI_FILTERS'
    },
    cart : {
      open: 'OPEN_CART',
      close: 'CLOSE_CART',
    },
    toggleNightMode: 'TOGGLE_NIGHT_MODE',
    setLang: 'SET_LANGUAGE'
  },
  navigation : {
    to : 'NAVIGATE_TO'
  },
  product: {
    products: {
      fetch: {
        pending: 'PRODUCT_FETCH_PENDING',
        fulfilled: 'PRODUCT_FETCH_FULFILLED',
        rejected: 'PRODUCT_FETCH_REJECTED',
        store: 'PRODUCT_FETCH_STORE',
      },
    },
    details: {
      fetch: {
        pending: 'PRODUCT_DETAILS_FETCH_PENDING',
        fulfilled: 'PRODUCT_DETAILS_FETCH_FULFILLED',
        rejected: 'PRODUCT_DETAILS_FETCH_REJECTED',
        store: 'PRODUCT_DETAILS_FETCH_STORE',
      },
      set: {
        openedURL: 'PRODUCT_DETAILS_SET_OPENED_URL',
      },
      unset: {
        openedURL: 'PRODUCT_DETAILS_UNSET_OPENED_URL',
        data: 'PRODUCT_DETAILS_UNSET_DATA',
      }
    },
    filter: {
      fetch: {
        pending: 'PRODUCT_FILTER_FETCH_PENDING',
        fulfilled: 'PRODUCT_FILTER_FETCH_FULFILLED',
        rejected: 'PRODUCT_FILTER_FETCH_REJECTED',
        store: 'PRODUCT_FILTER_FETCH_STORE',
      },
      set: {
         gender: 'PRODUCT_SET_FILTER_GENDER',
         brand: 'PRODUCT_SET_FILTER_BRAND',
         category: 'PRODUCT_SET_FILTER_CATEGORY',
         color: 'PRODUCT_SET_FILTER_COLOR',
      },
      unset: {
        all: 'PRODUCT_RESET_ALL_FILTERS'
      }
    },
    cart: {
      add: {
        product: 'CART_ADD_PRODUCT',
      },
      remove: {
        product: 'CART_REMOVE_PRODUCT',
      },
    }
    
  }
};


//Everything about ui goes here
const uiReducer = (state=appData.ui, action) => {
  const actions = Actions.ui; 
  
  switch (action.type) {
    
    case actions.toggleNightMode:      
      return {
        ...state, 
        nightModeEnabled: !state.nightModeEnabled
      };
      break;
      
    case actions.setLang:
      console.log(action.payload, state.ui);
      return {
        ...state,
        selectedLang: action.payload
      };
      break;
      
    case actions.cart.open: 
      state = {...state, cart: {
        isOpened: true
      }};
      
      return state;
      break;
    
    case actions.cart.close: 
      state = {...state, cart: {
        isOpened: false
      }};
      return state;
      break;
      
    default:
      return state;
      break;
  }
};

function toggleNightModeAction(){
  return {
     type: Actions.ui.toggleNightMode
   };
}

function setLangAction(lang){
  return {
     type: Actions.ui.setLang,
     payload: lang
   };
}


function openCartAction(){
   return {
     type: Actions.ui.cart.open
   }; 
}

function closeCartAction(){
   return {
     type: Actions.ui.cart.close
   }; 
}


//Everything about navigation and current page goes here
const navigationReducer = (state=appData.navigation, action) => {
  const actions = Actions.navigation; 

  switch (action.type) {
    case actions.to: 
      return {
        ...state,
        currentPageSlug: action.payload
      };
      break;
    
    default:
      return state;
  }
};

function navigateAction(pageSlug){
 return {
   type: Actions.navigation.to,
   payload: pageSlug
 }; 
}
// store.dispatch(navigateAction('home'))


const productReducer = (state=appData.product, action) => {
  const actions = Actions.product; 
  
  switch (action.type) {
      
      //Fetch
      case actions.products.fetch.pending:
        
        //clear current list
        return {
          ...state,
          list: appData.product.list,
          fetchIsPending: true,
          fetchIsFulfilled: false,
          fetchIsRejected: false,
        };     
        break;
      
      case actions.products.fetch.fulfilled:
        return {
          ...state,
          list: appData.product.list,
          fetchIsPending: false,
          fetchIsFulfilled: true,
          fetchIsRejected: false,
        };       
        break;
      
      case actions.products.fetch.rejected:
        state = {
          ...state,
          list: appData.product.list,
          fetchIsPending: false,
          fetchIsFulfilled: false,
          fetchIsRejected: true,
        };
        state.errors.fetch = action.payload;
        
        return state;
        break;
      
      
      case actions.products.fetch.store:
        return {
          ...state,
          list: action.payload,
        };       
        break;
      
      
      
      
      //ProductDetails
      case actions.details.fetch.pending:
        state = {...state};
      
        state.details.data = appData.product.details.data;
        state.details.fetchIsPending = true;
        state.details.fetchIsFulfilled = false;
        state.details.fetchIsRejected = false;
      
        return state;     
        break;
      
      case actions.details.fetch.fulfilled:
        state = {...state};
      
        state.details.data = appData.product.details.data;
        state.details.fetchIsPending = false;
        state.details.fetchIsFulfilled = true;
        state.details.fetchIsRejected = false;
      
        return state;    
        break;
      
      case actions.details.fetch.rejected:
        state = {...state};
      
        state.details.data = appData.product.details.data;
        state.details.fetchIsPending = false;
        state.details.fetchIsFulfilled = false;
        state.details.fetchIsRejected = true;
      
        return state; 
        break;
      
      
      case actions.details.fetch.store:
        state = {...state};
      
        state.details.data = action.payload;
      
        return state; 
        break;
      
      
      case actions.details.set.openedURL:
        state = {...state};
      
        state.details.openedURL = action.payload;
      
        return state; 
        break;
      
      
      case actions.details.unset.openedURL:
        state = {...state};
      
        state.details.openedURL = null;
      
        return state; 
        break;
      
      
      case actions.details.unset.data:
        state = {...state};
      
        state.details.data = {};
      
        return state; 
        break;
      
      
      
      
      //Filters
      case actions.filter.fetch.pending:
        //update only if changed, so return current state
        return state;    
        break;
      
      case actions.filter.fetch.fulfilled:
        state = {...state};
      
        state.filters.brands = action.payload.brands;
        state.filters.categories = action.payload.categories;      
        state.filters.colors = action.payload.colors;      

        return state;       
        break;
      
      case actions.filter.fetch.rejected:
        state = {...state};
        state.errors.fetch = action.payload;
      
        return state;
        break;
      
      case actions.filter.set.gender:
        state = {...state};
        state.appliedFilters.gender=action.payload;
      
        return state;
        break;
      
      case actions.filter.set.brand:
        state = {...state};
        state.appliedFilters.brand=action.payload;
      
        return state;
        break;
      
      case actions.filter.set.category:
        state = {...state};
        state.appliedFilters.category=action.payload;
      
        return state;
        break;
      
      case actions.filter.set.color:
        state = {...state};
        state.appliedFilters.color=action.payload;
      
        return state;
        break;
      
      case actions.filter.unset.all:
        state = {...state};
        state.appliedFilters.color='';
        state.appliedFilters.category='';
        state.appliedFilters.brand='';
      
        return state;
        break;
      
      
      //Cart
      
      case actions.cart.add.product:
        const {product} = action.payload;
        const {url} = product;
      
        state = {...state};
        const list = {...state.cart.list};
      
        if(list[url]){
          list[url].count += 1;
        } else {
          list[url] = {
            ...product,
            count: 1
          };
        }
        
        state.cart.isEmpty = false;
        state.cart.list = list;
        
        return state;     
      
      default:
        return state;
  }  
};

function setProductsGenderAction(value){
  const actions = Actions.product.filter; 
  
  return {
    type: actions.set.gender, 
    payload: value
  };
}

function setProductsBrandAction(value){
  const actions = Actions.product.filter; 
  
  return {
    type: actions.set.brand, 
    payload: value
  };
}

function setProductsCategoryAction(value){
  const actions = Actions.product.filter; 
  
  return {
    type: actions.set.category, 
    payload: value
  };
}

function setProductsColorAction(value){
  const actions = Actions.product.filter; 
  
  return {
    type: actions.set.color, 
    payload: value
  };
}


function unsetProductsFilters(value){
  const actions = Actions.product.filter; 
  
  return {
    type: actions.unset.all, 
    payload: value
  };
}


function startFetchingProductsAction(){
  const actions = Actions.product.products; 
  
  return {
    type: actions.fetch.pending
  };
}

function storeProductsAction(products){
  const actions = Actions.product.products; 
  
  return {
    type: actions.fetch.store, 
    payload: products
  };
}

function fetchingProductsFulfilledAction(){
  const actions = Actions.product.products; 
  
  return {
    type: actions.fetch.fulfilled
  };
}


const fetchProducts = (dispatch) => {
  const state = store.getState();
  dispatch(startFetchingProductsAction());

  const {appliedFilters} = state.product;
  
  const genderKey = appliedFilters.gender ? 'gender' : '_'
  const genderQuery = appliedFilters.gender || true;
  
  const brandKey = appliedFilters.brand ? 'brand' : '_'
  const brandQuery = appliedFilters.brand || true;
  
  const catKey = appliedFilters.category ? 'category' : '_'
  const catQuery = appliedFilters.category || true;
  
  const colorKey = appliedFilters.color ? 'color' : '_'
  const colorQuery = appliedFilters.color || true;
  
    productsRef
      .where(genderKey,'==',genderQuery)
      .where(brandKey, '==',brandQuery)
      .where(catKey, '==',catQuery)
      .where(colorKey, '==',colorQuery)
      
      // .orderBy('posted')
      .limit(state.product.settings.itemsPerPage)
      .get()
      .then(querySnapshot=>{
        const state = store.getState();
        const products = [];
        
        querySnapshot.forEach(doc => {
            products.push(doc.data());
        });
      
        /*
           Note: Prevent usage of deprecated data
                 e.g. when user clicks to fast on
                 different links
        */
        if(state.product.fetchIsPending){
          dispatch(fetchingProductsFulfilledAction());
          dispatch(storeProductsAction(products));  
        }
        
      })
      .catch(err=>{
        console.error(err);
      });
};
//store.dispatch(fetchProducts);





function startFetchingProductDetailsAction(){
  const actions = Actions.product.details; 
  
  return {
    type: actions.fetch.pending
  };
}

function storeProductDetailsAction(productData){
  const actions = Actions.product.details; 
  
  return {
    type: actions.fetch.store, 
    payload: productData
  };
}

function storeOpenedProductDetailsAction(openedURL){
  const actions = Actions.product.details; 
  
  return {
    type: actions.set.openedURL, 
    payload: openedURL
  };
}



function unsetOpenedProductURLAction(openedURL){
  const actions = Actions.product.details; 
  
  return {
    type: actions.unset.openedURL
  };
}

function unsetProductDetailsDataAction(){
  const actions = Actions.product.details; 
  
  return {
    type: actions.unset.data
  };
}

function fetchingProductDetailsFulfilledAction(){
  const actions = Actions.product.details; 
  
  return {
    type: actions.fetch.fulfilled
  };
}

const fetchProductDetails = (dispatch) => {
  const state = store.getState();
  dispatch(startFetchingProductDetailsAction());

  const {details} = state.product;
  
  const urlKey = 'url'
  const urlQuery = details.openedURL;
  
    productsRef
      .where(urlKey,'==',urlQuery)
      .limit(1)
      .get()
      .then(querySnapshot=>{
        const state = store.getState();
        const products = [];
        
        querySnapshot.forEach(doc => {
            products.push(doc.data());
        });
      
        const productDetails = products[0];
        console.log(productDetails);
      
        /*
           Note: Prevent usage of deprecated data
                 e.g. when user clicks to fast on
                 different links
        */
        if(state.product.details.fetchIsPending){
          dispatch(fetchingProductDetailsFulfilledAction());
          dispatch(storeProductDetailsAction(productDetails));
        }
        
      })
      .catch(err=>{
        console.error(err);
      });
};




function startFetchingFiltersAction(){
  const actions = Actions.product.filter; 
  
  return {
    type: actions.fetch.pending
  };
}

function storeProductFiltersAction(filters){
  const actions = Actions.product.filter; 
  
  return {
    type: actions.fetch.fulfilled, 
    payload: filters
  };
}

const fetchProductFilters = (dispatch) => {
  const state = store.getState();
  
  dispatch(startFetchingFiltersAction());

  const {appliedFilters} = state.product;
  
  statsRef
    .doc('options')
    .get()
    .then(documentSnapshot=>{
    const filters = documentSnapshot.data();
    console.log(filters);
    dispatch(storeProductFiltersAction(filters));
  })
    .catch(err=>{
    console.error(err);
  });
};
//store.dispatch(fetchProductFilters);



function addProductToCart(product){
  const actions = Actions.product.cart; 
  
  return {
    type: actions.add.product, 
    payload: {
      product: product
    }
  };
}



const reducers = combineReducers({
  ui: uiReducer,
  navigation: navigationReducer,
  product: productReducer
});


const initialState = {
  someval: 1
};

const middleware = applyMiddleware(ReduxThunk, logger);
const store = createStore(reducers, appData, middleware);



store.subscribe(()=>{
});



const Icon = ({name}) => (<i className="icon material-icons">{name}</i>);

class Dictionary extends React.Component {
  handleText(str){
    return str.replace(/\\n/gm, '<br/>');
  }
  
  render(){
    const {lang, dictionary, tag} = this.props;
    console.log('))))', lang, dictionary, tag);
    return dictionary[tag] ? 
           this.handleText(dictionary[tag][lang]) : 
           '';
  }
};
const Dict = connect((state, ownProps)=>{
  return {
    lang: state.ui.selectedLang,
    dictionary: state.ui.dictionary,
    tag: ownProps.tag
  };
})(Dictionary);


class TextItem extends React.Component {
  handleText(str){
    return str.replace(/\\n/gm, '<br/>');
  }
  
  getText(){
    const {lang, dictionary, tag} = this.props;
    return dictionary[tag] ? 
           this.handleText(dictionary[tag][lang]) : 
           '';
  }
  
  render(){
     return (
        <span className="text">{this.getText()}</span>
     )
   }
};
const Text = connect((state, ownProps)=>{
  return {
    lang: state.ui.selectedLang,
    dictionary: state.ui.dictionary,
    tag: ownProps.tag
  };
})(TextItem);


const HeaderNavigationLink = (props) => {
  return (
    <NavLink 
      className="link" 
      to={props.link.url}
      activeClassName="active-link"
      >
      {props.link.name}
    </NavLink>
  );
};

const HeaderNavigationLinkWR = withRouter(HeaderNavigationLink);


class HeaderNavigation extends React.Component {
  render(){
    const links = this.props.headerLinks.map(link => {
      return (
        <NavLink 
          className="link"
          to={link.url}
          key={link.url}
          activeClassName="active-link"
          >
          <Text tag={link.name} />
        </NavLink>
      );
    });
    
    return (
      <ul className="header-navigation">
        {links}
      </ul>
    );
  }
}

const HeaderNavigationContainer = connect(state => {
  return {
    headerLinks: state.navigation.headerLinks 
  }
})(withRouter(HeaderNavigation));


function countItemsInCart(items={}){
  let count = 0;
  for (var url in items){
    if(items.hasOwnProperty(url)){
      count += items[url].count;
    }
  }
    
  return count;
}


function countPriceOfItemsInCart(items={}){
  let price = 0;
  let currency = '';
  
  for (var url in items){
    if(items.hasOwnProperty(url)){
      const item = items[url];
      price += (item.count * item.price);
      currency = item.price_currency;
    }
  }
    
  return prettifyCurrency(currency) + prettifyPrice(Math.round(price * 100) / 100);
}

const CartIcon = (props) => {
  const count = countItemsInCart(props.list);
  const counterDefaultClass = "counter bold";
  const counterActiveClass = count > 0 ? "active" : "";
  const counterClass = [
                          counterDefaultClass, 
                          counterActiveClass
                       ].join(" ");
  
        
  return (
    <div className="cart-icon" onClick={props.onClick}>
      <Icon name="shopping_basket" />
      <span className={counterClass}>
        {count}
      </span>
    </div>
  )
};

const CartItem = (props) => {
  return(
    <div className="product item">
      <ProductImage product={props.data} />
      <div className="details">
        <div className="name bold">
          {props.data.name}
        </div>
        <div className="price">
          {prettifyCurrency(props.data.price_currency)}
          {prettifyPrice(props.data.price)}
        </div>
      </div>
      <div className="count">
        {props.data.count}
      </div>
      
    </div>
  );
};


const CartEmpty = (props) => {
  return (
    <div className="empty">
      <Icon name="shopping_cart"/>
      <p className="hint text">
        <Text tag={'text_empty_cart'} />
      </p>
    </div>
  );
};

class Cart extends React.Component {
  renderCartItems(){
    const {list} = this.props.product.cart;
    let items = [];
    
    for(let itemKey in list){
      if(list.hasOwnProperty(itemKey)){
        items.push(<CartItem key={itemKey} data={list[itemKey]} />);
      }
    }
    
    return items;
  }
  
  renderActiveCart(){
    const {cart} = this.props.product;
    return (
      <div className="cart-content">
        <div className="list">
          {this.renderCartItems()}
        </div>
        <div className="total">
          <span className="text bold">        
            <Text tag={'header_total'} />
          </span>
          <span className="amount bold">
            {countPriceOfItemsInCart(cart.list)}
          </span>
        </div>
        <div className="actions-wrapper">
          <button className="btn red wide">
            <Text tag={'action_checkout'} />
          </button>
        </div>
      </div>
    );
  }
  
  renderContent(){
    const {cart} = this.props.product;
    
    return cart.isEmpty ?
           (<CartEmpty />) :
           (this.renderActiveCart());
  }
  
  render(){
    return (
      <div className="cart">
        <div className="cart-topbar">
          <a href="javascript:;" className="btn">
            <i className="icon material-icons">close</i>
          </a>
          <h3 className="cart-title">
            <i className="icon material-icons">shopping_basket</i>
            <Text tag="ptitle_cart"/>
          </h3>
        </div>
        {this.renderContent()}
      </div>
    );
  }
}

const CartContainer = connect(state=>(state))(Cart);


class Header extends React.Component {
  onCartIconClick(){
    const {dispatch, ui} = this.props;
    const {isOpened} = ui.cart;
    
    if(isOpened){
      dispatch(closeCartAction());  
    } else {
      dispatch(openCartAction());  
    }
    
  }
  
  render(){
    const {list} = this.props.product.cart;
    return (
      <header className="header">
        <HeaderNavigationContainer/>
        <CartIcon list={list} onClick={this.onCartIconClick.bind(this)}/>
      </header>
    );
  }
}

const HeaderContainer = connect(
  state=>{
    return state;
  }
)(Header);


class Selector extends React.Component {
   render(){
     let options = this.props.list.map((item, i)=>{
      return (
        <option 
          key={i} 
          value={item.url}
        >
          {item.name}
        </option>
      );
    });
    
    //insert placehodler option
    if(!this.props.disableDefault){
      options.unshift(<option key={'default'} value=''><Dict tag={this.props.placeholder || "placeholder_any"} /></option>);  
    }
    
    
    return (
      <div>
        <label htmlFor={this.props.id}>
           <Text tag={this.props.label} />
        </label>
        <select 
          id={this.props.id}
          name={this.props.name}
          onChange={this.props.onChange}
          value={this.props.selected}
        >
          {options}
        </select>
      </div>
    );
   }
}

class Toggler extends React.Component {
  constructor(props){
    super(props);
    this.state = this.props;
  }
  
  renderIcon(name){
    return (<Icon name={name} />);
  }
  
  renderIconOn(){
    return this.state.iconOn ? 
           this.renderIcon(this.state.iconOn) : 
           null;
  }
  
  renderIconOff(){
    return this.state.iconOn ? 
           this.renderIcon(this.state.iconOff) : 
           null;
  }
  
  // componentWillReceiveProps(nextProps){
  //   this.setState({isChecked: nextProps.isChecked});
  // }
  
  renderIndicator(){
    return (
      <div className="indicator">
        <div className="state on">
          {this.renderIconOn()}
        </div>
        <div className="state off">
          {this.renderIconOff()}
        </div>
      </div>
    );
  }
  
  handleChange(){
    this.setState({isChecked:!this.state.isChecked});
    if(this.state.onChange){this.state.onChange();}
  }
  
  renderCheckbox(){
    const {name, onChange, isChecked} = this.state;
    
    return (
      <input 
        type="checkbox" 
        name={name}
        onChange={this.handleChange.bind(this)}
        checked={isChecked ? 'true' : ''}
      />
    );
  }
  
  renderLabel(){
    const {label} = this.state;
    
    return label ? 
          (
            <div className="label">            
              <Text tag={label} />
            </div>
          ) : 
          null;
  }
  
  bakeClassName(){
    const {name, onChange, isChecked, label} = this.state;
    const mainClass = "toggler";
    const labelClass = label ? "with-label" : "";
    
    return [mainClass, labelClass].join(" ");
  }
  
  render(){
    const className=this.bakeClassName();
    
    return (
      <div className={className}>
        {this.renderCheckbox()}
        
        <div className="view">
          {this.renderLabel()}
          
          <div className="body">
            <div className="fill" />
            {this.renderIndicator()}
          </div>
         
        </div>
      </div>
    );
  }
};


const NightModeToggler = (props) =>{
  const {dispatch} = props;
  const label = "label_lightness_mode";
  
  return (
    <Toggler 
      name="night-mode"
      iconOn="brightness_3" 
      iconOff="brightness_7" 
      label={label}
      onChange={()=>{dispatch(toggleNightModeAction())}}
      isChecked={props.ui.nightModeEnabled}
    />
  );
};
const NightModeTogglerContainer = connect(state=>(state))(NightModeToggler);



class TabberOption extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      ...this.props,
      isSelected: this.props.selectedTab === this.props.value
    };
  }
  
  componentWillReceiveProps(newProps){
    if(this.props.selectedTab != newProps.selectedTab){
      this.setState({
        selectedTab: newProps.selectedTab,
        isSelected: newProps.selectedTab === newProps.value
      });
    }
  }
  
  handleClick(){
    const {onSelect, value} = this.state;
    onSelect(value);
  }
  
  bakeClassName(){
    const {isSelected} = this.state;
    
    const mainClass = "item"
    const selectedClass = isSelected ? 'selected' : '';
    
    return [mainClass, selectedClass].join(" ");
  }
  
  render(){
    return (
      <div 
        className={this.bakeClassName()} 
        onClick={this.handleClick.bind(this)}
      >
        {this.state.children}
      </div>
    );
  }
};

class Tabber extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      ...this.props,
      selectedTab: this.props.default || null,
    };
  }
  
  handleSelect(value){
    const {onSelect} = this.state;
    this.setState({selectedTab: value});
   
    if(onSelect) onSelect(value);
  }
  
  renderOptions(){
    const {selectedTab, children} = this.state;
    
    return React.Children.map(children, (child) =>
            React.cloneElement(child, {
              onSelect: this.handleSelect.bind(this),
              selectedTab: selectedTab,
            })
          );
  }
  
  render(){
    return (
      <div className="tabber">
        {
          this.renderOptions()
        }
      </div>
    );
  }
}



class LanguageTabber extends React.Component {
  handleSelect(lang){
    const {dispatch} = this.props;
    
    dispatch(setLangAction(lang));
  }
  
  render(){
    return (
      <Tabber default={this.props.ui.selectedLang} className="lang" onSelect={this.handleSelect.bind(this)}>
        <TabberOption value="ua">
          <span className="flag-icon flag-icon-ua"></span>  
        </TabberOption>
        <TabberOption value="eng">
          <span className="flag-icon flag-icon-us"></span>  
        </TabberOption>
      </Tabber>
    );
  }
}
const LangTabber = connect(state=>(state))(LanguageTabber);



class ColorPicker extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      ...this.props,
      selected: this.props.selected || ""
    };
  }
  
  componentWillReceiveProps(nextProps){
    if(!this.state.list.compare(nextProps.list)){
      this.setState({list: nextProps.list});
    }
  }
  
  handleColorSelect(e){
    const colorURL = e.target.getAttribute('value') || '';
    this.setState({selected: colorURL});
    
    if(this.state.onChange) this.state.onChange(colorURL);
  }
  
  
  renderColorItem(color, key){
    const {selected} = this.state;
    const value = !color ? "" : color.url;
    const colorType = (!color && 'white') ||
                      (color.url === 'gray' && 'bluish-gray') || 
                      color.url;
    const mainClass = 'item color';
    const noneClass = !color ? 'none' : '';
    const colorClass = `bg-${colorType}-bordered`;
    const selectedClass = selected === value ? 'selected' : '';
    const activeClass = selected != value ? 'active' : '';
    const itemClass = [mainClass, noneClass, colorClass, selectedClass, activeClass].join(' ');

    return (
      <div 
        key={key}
        className={itemClass} 
        value={value}
        onClick={this.handleColorSelect.bind(this)}
      ></div>
    ); 
  }
  
  renderLabel(){
    const {label} = this.state;
    return label ? (<label className="label"><Text tag={label}/></label>) : null;
  }
  
  renderItems(){
    let list = this.state.list.map((color, key)=>{
       return this.renderColorItem(color, key);
    });
    list.unshift(this.renderColorItem(null, Math.random()));
    return list;
  }
  
  render(){
    return(
      <div 
        id={this.state.id || null}
        className="colorpicker"
      >
        {this.renderLabel()}
        <div className="list">
          {this.renderItems()}
        </div>
      </div>
    );
  }
  
}


class SideA extends React.Component {
  
  navToBySelector(path){
    const {dispatch, history, location} = this.props;
    if(location.pathname === path){
      dispatch(fetchProducts);
    } else {
      history.push(path);
    }
  }
  
  handleSectorChange(){
    const {history, appliedFilters} = this.props;
    
    switch(appliedFilters.gender){
      case "male":
        this.navToBySelector('/category/men');
        break;
      case "female":
        this.navToBySelector('/category/women');
        break;
      default:
        this.navToBySelector('/home');
        break;
    }
    
  }
  
  handleBrandChange(event){
    const {dispatch} = this.props;
    const brandFilter = event.target.value;
    
    dispatch(setProductsBrandAction(brandFilter));
    this.handleSectorChange();
    // dispatch(fetchProducts);
  }
  
  handleCategoryChange(event){
    const {dispatch} = this.props;
    const catFilter = event.target.value;
    
    dispatch(setProductsCategoryAction(catFilter));
    this.handleSectorChange();
    // dispatch(fetchProducts);
  }
  
  handleColorChange(colorURL){
    const {dispatch} = this.props;
    const colorFilter = colorURL;
    
    console.log('change');
    
    dispatch(setProductsColorAction(colorFilter));
    // dispatch(fetchProducts);
    this.handleSectorChange();
  }
  
  handleResetButtonClick(){
    const {dispatch} = this.props;
    
    dispatch(unsetProductsFilters());
    // dispatch(fetchProducts);
    this.handleSectorChange();
  }
  
  renderBrands(){
    const selected = this.props.selectedBrand;
    return (
      <Selector
        id="side-brand"
        label="label_brand"
        name="brand"
        placeholder="placeholder_any_m"
        list={this.props.brands}
        selected={selected}
        onChange={this.handleBrandChange.bind(this)}
        disableDefault={false}
      />
    );
  }
  
  renderCategories(){
    const selected = this.props.selectedCategory;
    return (
      <Selector
        id="side-category"
        label="label_shoes_for"
        name="category"
        placeholder="placeholder_any_s"
        list={this.props.categories}
        selected={selected}
        onChange={this.handleCategoryChange.bind(this)}
        disableDefault={false}
      />
    );
  }
  
  renderColors(){
    const selected = this.props.selectedColor;
    return (
      <ColorPicker 
        id="side-color"
        label="label_color"
        list={this.props.colors}
        selected={selected}
        onChange={this.handleColorChange.bind(this)}
      />
      // <Selector
      //   id="side-color"
      //   label="label_color"
      //   placeholder="placeholder_any_m"
      //   name="color"
      //   list={this.props.colors}
      //   selected={selected}
      //   onChange={this.handleColorChange.bind(this)}
      //   disableDefault={false}
      // />
    );
  }
  
  renderResetButton(){
    const {ui} = this.props;
    
    const btnMainClass = "btn";
    const btnType = "wide";
    const btnColor = "white";
    const btbClass = [btnMainClass,btnType,btnColor].join(" ");
    
    return (
      <button 
        className={btbClass}
        onClick={this.handleResetButtonClick.bind(this)}
      >
        <Icon name="replay" />
        <Text tag="action_reset_filters" />
      </button>
    );
  }
  
  toggleNightMode(){
    const {dispatch} = this.props;
    
    dispatch(toggleNightModeAction());
  }
  
  renderNightModeToggler(){
    const {ui} = this.props;
    
    return (
      <Toggler 
        iconOn="brightness_3" 
        iconOff="brightness_7" 
        onChange={this.toggleNightMode.bind(this)} 
        setAsChecked={this.props.ui.nightModeEnabled}
      />
    );
  }
  
  
  componentDidMount(){
    this.props.dispatch(fetchProductFilters);
  }
  

  
  
  render(){
    return (
      <aside id="a-side" className="a-side">
        {this.renderCategories()}
        <br/>
        {this.renderBrands()}
        <br/>
        {this.renderColors()}
        <br/>
        {this.renderResetButton()}
        
        <div className="page-actions">        
          <NightModeTogglerContainer />
          <br/>
          <LangTabber />
          <br/>
          <DevBy />
        </div>
      </aside>
    );
  }
}

const SideAContainer = connect(state=>{
  const {filters, appliedFilters} = state.product;
  return {
    appliedFilters: appliedFilters,
    selectedBrand: appliedFilters.brand,
    brands: filters.brands,
    selectedCategory: appliedFilters.category,
    categories: filters.categories,
    selectedColor: appliedFilters.color,
    colors: filters.colors,
    ui: state.ui,
  };
})(withRouter(SideA));


const ProductImage = (props) => {
  const {product} = props;
  const styles = {
    backgroundImage: `url('${product.image}')`
  };
  return (
    <div className="image" style={styles}></div>
  );
};


function prettifyCurrency(currencyName){
  if(currencyName && currencyName.toLowerCase() === 'usd'){
    return '$';
  } else {
    return '';
  }
};

function prettifyPrice(price=''){
  return price.toString().indexOf('.') > -1 ? price : price + '.00';
}

const ProductPricePlaceholder = ()=>{
  return (
    <div className="price placeholder">
      <div className="item text"></div>
    </div>
  );
};

const ProductPrice = (props) => {
  const prettyCurrency = prettifyCurrency(props.currency);
  const prettyPrice = prettifyPrice(props.price);
  const price = prettyCurrency + prettyPrice;

  return (
    <span className="price bold">
      {" "}

      { props.old_price && (
        <span className="old-price">
          {prettyCurrency}
          {props.old_price}
          {" "}
        </span>
      )}

      {price}
    </span>
  );
};


class Product extends React.Component {
  constructor(props){
    super(props);
    this.state = props;
  }
  
  renderDiscount(){
    const product = this.state.data;
    if(!product.old_price){ return; }
    
    const percents = Math.floor(100/product.old_price * product.price) + '%';
    
    return (
      <div className="discount">
        {'-'}
        {percents}
        {' Off!'}
      </div>
    );
     
  }
  
  renderProduct(){
    const product = this.state.data;
    
    return (
      <div className="product-content">
        {this.renderDiscount()}
        
        <ProductImage product={product} />
        <div className="details">
          <span className="name bold">
            {product.name}
          </span>

          <ProductPrice price={product.price} currency={product.price_currency} />
        </div>
        
      </div>
    );
  }
  
  render(){
    const product = this.state.data;
    const productURL = `/product/${product.url}`;
    
    return (
      <li className="product">
        <NavLink 
        className="no-decoration" 
        to={productURL}
        activeClassName="active-link"
        >
          {this.renderProduct()}
        </NavLink>
      </li>
    );
  }
  
};


class ProductPlaceholder extends React.Component {
  constructor(props){
    super(props);
    this.state = props;
  }
  
  render(){
    return (
      <li className='product placeholder'>
        <div className='image'></div>
      </li>
    );
  }
}



class Products extends React.Component {  
  renderItem(product, index){
    return (<Product key={index} data={product}/>);
  }
  
  renderPlaceholder(data, index){
    return (<ProductPlaceholder key={index}/>);
  }
  
  renderProductsList(){
    const { product } = this.props;
    
    return product.fetchIsFulfilled && product.list.length > 0 ?
              product.list.map(this.renderItem) : 
              null;
  }
  
  renderPlaceholderList(){
    const { product } = this.props;
    const itemsPerPage = product.settings.itemsPerPage;
    
    return product.fetchIsPending ?
              Array.from({ length: itemsPerPage }).map(this.renderPlaceholder) :
              null;
  }
  
  renderEmptyList(){
    const { product } = this.props;
    
    return product.fetchIsFulfilled && product.list.length === 0 ? 
      (
        <div className="informer">
          <h3 className="title">
             <Text tag="header_product_not_found" />
          </h3>
          <div className="text">
             <Text tag="text_product_not_found" />
          </div>
        </div>
      ) :
      null;
  }

  render(){
    const { product } = this.props;

    return (
      <div className="products-wrapper" data-filter={product.appliedFilters.gender}>
        <ul className="products">
          { this.renderProductsList() }
          { this.renderPlaceholderList() }
          { this.renderEmptyList() } 
        </ul>
        <div className="clearfix"></div>
      </div>
    );
  };
}

const ProductsContainer = connect(
  state=>{
    return {product: state.product};
  }
)(Products);


class ProductsView extends React.Component {
  renderCart(){
    const cart = this.props.cart;
    
    return cart.isOpened ?
           (<CartContainer />) :
           null;
  }
  
  render(){
    const cart = this.props.cart;
    const pageDefaultClass = "page";
    const cartOpenedClass = cart.isOpened ? "cart-is-opened" : "";
    const pageClass = [
                        pageDefaultClass,
                        cartOpenedClass,
                      ].join(" ");
    return (
      <div id="homepage" className={pageClass}>
        <SideAContainer />
        <div className="content">
          <h1 className="title">
            <Text tag={this.props.pageData.title} />
          </h1>
          
          <ProductsContainer />
        </div>
        {this.renderCart()}
      </div>
    );
  }
}


const ProductsViewContainer = connect(state=>{
  return {
    pageData: state.navigation.pages[state.navigation.currentPageSlug],
    cart : state.ui.cart,
  }
})(ProductsView);


const ProductDescriptionPlaceholder = (props) => {
  const linesSize = (props.lines || 5);
  const placeholderLines = Array.from(Array(linesSize).keys()).map((l,i)=>{
    return (<div className="item text" key={i}></div>)  ;
  });
  
  return (
    <div className="description placeholder">
      {placeholderLines}
    </div>
  );
};


const ProductDescription = ({description = ""}) => {
  const descriptionHTML = description.split('\\n').map((item, key) => {
    return (<span key={key}>{item}<br/></span>);
  });
  
  return (
    <div className="description">
      {descriptionHTML}
    </div>
  );
};

const ProductTitlePlaceholder = (props)=>{
  return (
    <div className="title placeholder">
      <div className="item text"></div>
    </div>
  );
}


const ProductTitle = (props)=>{
  return (
    <h1 className="title">
      {props.title}
    </h1>
  );
}


const ProductBuyButtonPlaceholder = (props)=>{
  return (
    <div className="button placeholder">
      <div className="item text"></div>
    </div>
  );
}


class ProductDetails extends React.Component {
  componentDidMount(){
    const {dispatch} = this.props;
    const productURL = this.props.openedURL;
    
    dispatch(storeOpenedProductDetailsAction(productURL));
    dispatch(fetchProductDetails);
  }
  
  componentWillUnmount() {
    const {dispatch} = this.props;
    
    dispatch(unsetOpenedProductURLAction());
    dispatch(unsetProductDetailsDataAction());
  }
  
  renderPrice(){
    const productData = this.props.product.details.data;
    
    return this.props.product.details.fetchIsPending ? 
            (<ProductPricePlaceholder/>) : 
            (
              <ProductPrice 
                price={productData.price} 
                currency={productData.price_currency}
                old_price={productData.old_price}
              />
            );
        
  }
  
  renderDescription(){
    const details = this.props.product.details;

    return details.fetchIsPending ?
            (<ProductDescriptionPlaceholder lines={5}/>) :
            (<ProductDescription description={details.data.description}/>);
    
  }
  
  renderTitle(){
    const details = this.props.product.details;

    return details.fetchIsPending ?
            (<ProductTitlePlaceholder/>) :
            (<ProductTitle title={details.data.name}/>);
  }
  
  handleButtonClick(){
    const {dispatch} = this.props;
    const productData = this.props.product.details.data;
    
    dispatch(addProductToCart(productData));
  }
  
  renderCart(){
    const cart = this.props.ui.cart;
    
    return cart.isOpened ?
           (<CartContainer />) :
           null;
  }
  
  renderButton(){
    const details = this.props.product.details;

    return details.fetchIsPending ?
           (<ProductBuyButtonPlaceholder/>) :
           (
            <button 
              className="btn red"
              onClick={this.handleButtonClick.bind(this)}
              >
              <Text tag="action_add_to_cart" />
            </button>
           );
  }
  
  renderGender(){
    const details = this.props.product.details;
    const productData = details.data;
    const genderText = 'stitle_for_' +
                        (
                          (productData.gender === 'male' && 'him') ||
                          (productData.gender === 'female' && 'her') ||
                          'everyone'
                        );
    return details.fetchIsPending ?
           (
            <div className="gender placeholder">
              <span className="item text"></span>
            </div>
           ) : 
           (<span className="gender">
              <Text tag={genderText} />
            </span>);
  }
  
  render(){
    const productData = this.props.product.details.data;
    const cart = this.props.ui.cart;
    const pageDefaultClass = "page product product-details";
    const cartOpenedClass = cart.isOpened ? "cart-is-opened" : "";
    const genderClass = `product-${productData.gender}`;
    const pageClass = [
                        pageDefaultClass,
                        cartOpenedClass,
                        genderClass
                      ].join(" ");
    
    return (
      <div id="details" className={pageClass}>
        
        <SideAContainer />
        
        <div className="content">
          <div className="media">
            <ProductImage product={productData} />
          </div>
          
          <div className="details">
            <div className="title-wrapper">
              {this.renderTitle()}
            </div>
            
            <div className="gender-wrapper">
              {this.renderGender()}
            </div>
            
            <div className="price-wrapper">
              {this.renderPrice()}
            </div>
            
            <div className="description-wrapper">
              <div className="subtitle">
                <span className="text bold">
                  <Text tag="header_description"/>
                </span>
              </div>
              {this.renderDescription()}
            </div>
            
            <div className="actions-wrapper">
              {this.renderButton()}
            </div>
          </div>
          
        </div>
        
        {this.renderCart()}
      </div>
    );
   
  }
}

const ProductDetailsContainer = connect(store => (store))(ProductDetails);


class Homepage extends React.Component {
  componentDidMount(){
    const {dispatch} = this.props;
    
    dispatch(navigateAction('home'));
    dispatch(setProductsGenderAction(''));
    dispatch(fetchProducts);
  }
  
  render(){
    return (
       <ProductsViewContainer />
    );
  }
};
const HomepageContainer = connect(store=>(store))(Homepage);


const DevBy = () => (
  <a
    id="devby"
    href="https://levchenkod.com?utm_source=old_react_store&utm_medium=devby&utm_campaign=codepen"
    target="_blank"
    style={{
      opacity: 0.8,
      backgroundColor: "transparent",
      color: "rgb(25, 25, 25)",
      height: 20,
      fontSize: 13,
      padding: "0.2rem 0.4rem 0.2rem 0.6rem",
      gap: "0.2rem",
      display: "flex",
      margin: "0px auto",
      maxWidth: "max-content",
      placeContent: "center",
      placeItems: "center",
      borderRadius: "2rem",
      fontFamily:
        "__Inter_aaf875, __Inter_Fallback_aaf875, Geneva, Verdana, sans-serif",
      textDecoration: "none",
      boxSizing: "content-box"
    }}
>
  <span>Developed by</span>
  <img
    title="Dmytro Levchenko"
    alt="Levchenko Dmytro Monogram Logo"
    fetchpriority="high"
    width={18}
    height={18}
    decoding="async"
    data-nimg={1}
    className="transition duration-500"
    style={{ color: "transparent", width: 18, height: 18 }}
    src="https://levchenkod.com/img/levchenkod-logo-symbol-32-dark.svg"
  />
</a>
);


class WomenPage extends React.Component {
  componentDidMount(){
    const {dispatch} = this.props;
    
    dispatch(navigateAction('women'));
    dispatch(setProductsGenderAction('female'));
    dispatch(fetchProducts);
    
  }
  
  render(){
    return (
        <ProductsViewContainer />
    );
  }
};
const WomenPageContainer = connect(store=>(store))(WomenPage);

class MenPage extends React.Component {
  componentDidMount(){
    const {dispatch} = this.props;
    
    dispatch(navigateAction('men'));
    dispatch(setProductsGenderAction('male'));
    dispatch(fetchProducts);
  }
  
  render(){
    return (
        <ProductsViewContainer />
    );
  }
};
const MenPageContainer = connect(store=>(store))(MenPage);




class Roster extends React.Component {
  render(){
    return (
      <Switch>
        <Route 
          exact path='/home' 
          render={
            ()=>{
              return (<HomepageContainer/>);
            }
          }             
         />

        <Route 
          exact path='/category/women' 
          render={
            ()=>{
              return (<WomenPageContainer/>);
            }
          }                
         />
        
        <Route 
          exact path='/category/men' 
          render={
            ()=>{
              return (<MenPageContainer/>);
            }
          }                
         />
        
        <Route 
          path='/product/:product_url' 
          render={
            (data)=>{
              const openedURL = data.match.params.product_url;
              return (<ProductDetailsContainer openedURL={openedURL}/>);
            }
          }             
         />


        <Redirect from='/' to="/home"/>
      </Switch>
    
    );
  } 
}


class App extends React.Component {
  render(){
    const innerClass = "app-inner";
    const nightModeClass = this.props.ui.nightModeEnabled ? "night-mode" : "";
    const appClassName = [innerClass, nightModeClass].join(" ");
    
    return (
      <BrowserRouter>
        <div className={appClassName}>
          <HeaderContainer />
          <Roster />
        </div>
      </BrowserRouter>
    );
  }
}

const AppContainer = connect(state=>(state))(App);



ReactDOM.render((

  <Provider store={store}>
    <AppContainer />
  </Provider>

    
), app);
View Compiled
Run Pen

External CSS

  1. https://fonts.googleapis.com/icon?family=Material+Icons
  2. https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/css/flag-icon.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js
  3. https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/4.2.2/react-router-dom.js
  4. https://www.gstatic.com/firebasejs/5.0.4/firebase.js
  5. https://www.gstatic.com/firebasejs/5.0.4/firebase-app.js
  6. https://www.gstatic.com/firebasejs/5.0.4/firebase-firestore.js
  7. https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.0/redux.min.js
  8. https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js
  9. https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.3.0/redux-thunk.min.js
  10. https://cdn.jsdelivr.net/npm/redux-logger@3.0.6/dist/redux-logger.min.js
  11. https://cdnjs.cloudflare.com/ajax/libs/react-router/4.2.0/react-router.min.js