diff --git a/config/dev.js b/config/dev.js
index 04cc1f2..ea415de 100644
--- a/config/dev.js
+++ b/config/dev.js
@@ -1,6 +1,8 @@
 module.exports = {
   API: {
+    V3: "https://api.topcoder-dev.com/v3",
     V5: "https://api.topcoder-dev.com/v5",
   },
   PLATFORM_WEBSITE_URL: "https://platform.topcoder-dev.com",
+  TOPCODER_WEBSITE_URL: "https://www.topcoder-dev.com",
 };
diff --git a/config/index.js b/config/index.js
index 8511e16..ba0ff0d 100644
--- a/config/index.js
+++ b/config/index.js
@@ -2,9 +2,5 @@
 
 module.exports = (() => {
   const appEnv = process.env.APPENV === "prod" ? "prod" : "dev";
-
-  // eslint-disable-next-line no-console
-  console.log(`APPENV: "${appEnv}"`);
-
   return require(`./${appEnv}`);
 })();
diff --git a/config/prod.js b/config/prod.js
index 7d0f7c4..e539e99 100644
--- a/config/prod.js
+++ b/config/prod.js
@@ -1,6 +1,8 @@
 module.exports = {
   API: {
+    V3: "https://api.topcoder.com/v3",
     V5: "https://api.topcoder.com/v5",
   },
   PLATFORM_WEBSITE_URL: "https://platform.topcoder.com",
+  TOPCODER_WEBSITE_URL: "https://www.topcoder.com",
 };
diff --git a/src/assets/images/icon-computer.svg b/src/assets/images/icon-computer.svg
new file mode 100644
index 0000000..2ac6647
--- /dev/null
+++ b/src/assets/images/icon-computer.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="21px" height="24px" viewBox="0 0 21 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02E-Job-Details" transform="translate(-331.000000, -213.000000)" fill="#555555" fill-rule="nonzero">
+            <g id="Group" transform="translate(305.000000, 182.000000)">
+                <g id="info-copy" transform="translate(24.000000, 24.000000)">
+                    <g id="desktop-computer" transform="translate(2.000000, 7.000000)">
+                        <g>
+                            <path d="M2.25,15 C1.00781635,14.9988973 0.00110269364,13.9921836 0,12.75 L0,2.25 C0,1.01 1.009,0 2.25,0 L18.75,0 C19.9921836,0.00110269364 20.9988973,1.00781635 21,2.25 L21,12.75 C20.9988973,13.9921836 19.9921836,14.9988973 18.75,15 L2.25,15 Z M1.5,12.75 C1.5,13.164 1.836,13.5 2.25,13.5 L18.75,13.5 C19.1642136,13.5 19.5,13.1642136 19.5,12.75 L19.5,12 L1.5,12 L1.5,12.75 Z M19.5,10.5 L19.5,2.25 C19.5,1.83578644 19.1642136,1.5 18.75,1.5 L2.25,1.5 C1.83578644,1.5 1.5,1.83578644 1.5,2.25 L1.5,10.5 L19.5,10.5 Z" id="Shape"></path>
+                            <path d="M2.46989185,24.0000019 C2.0824202,23.9978148 1.71179541,23.8300418 1.44030147,23.533929 C1.06505536,23.1372438 0.915224003,22.5566605 1.04698599,22.0098663 L2.16718829,17.2126415 C2.33099615,16.499659 2.928474,15.9988832 3.6139918,16.0000019 L17.3840163,16.0000019 C18.0690829,16.0002351 18.6664173,16.4989802 18.8338071,17.2105084 L19.9540094,22.0109328 C20.0515914,22.4236776 19.992843,22.8534867 19.7877215,23.220371 C19.5194216,23.7041349 19.0325398,24.0001624 18.5072059,23.9989335 L2.46989185,24.0000019 L2.46989185,24.0000019 Z M2.49478523,22.4002141 L18.5052144,22.4002141 L17.3850121,17.6008562 L3.61498753,17.5997897 L2.49478523,22.4002141 Z" id="Shape"></path>
+                            <path d="M4.75,21 C4.33578644,21 4,20.5522847 4,20 C4,19.4477153 4.33578644,19 4.75,19 L6.25,19 C6.66421356,19 7,19.4477153 7,20 C7,20.5522847 6.66421356,21 6.25,21 L4.75,21 Z" id="Path"></path>
+                            <path d="M13.75,21 C13.3357864,21 13,20.5522847 13,20 C13,19.4477153 13.3357864,19 13.75,19 L15.25,19 C15.6642136,19 16,19.4477153 16,20 C16,20.5522847 15.6642136,21 15.25,21 L13.75,21 Z" id="Path"></path>
+                            <path d="M9.75,21 C9.33578644,21 9,20.5522847 9,20 C9,19.4477153 9.33578644,19 9.75,19 L11.25,19 C11.6642136,19 12,19.4477153 12,20 C12,20.5522847 11.6642136,21 11.25,21 L9.75,21 Z" id="Path"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/assets/images/icon-copy.png b/src/assets/images/icon-copy.png
new file mode 100644
index 0000000..95064c0
Binary files /dev/null and b/src/assets/images/icon-copy.png differ
diff --git a/src/assets/images/icon-dollar-circled.png b/src/assets/images/icon-dollar-circled.png
new file mode 100644
index 0000000..46af102
Binary files /dev/null and b/src/assets/images/icon-dollar-circled.png differ
diff --git a/src/assets/images/icon-link.png b/src/assets/images/icon-link.png
new file mode 100644
index 0000000..3e09cca
Binary files /dev/null and b/src/assets/images/icon-link.png differ
diff --git a/src/assets/images/icon-open-outside.png b/src/assets/images/icon-open-outside.png
new file mode 100644
index 0000000..743c8e0
Binary files /dev/null and b/src/assets/images/icon-open-outside.png differ
diff --git a/src/components/Button/index.jsx b/src/components/Button/index.jsx
index f0919ac..a9ca78b 100644
--- a/src/components/Button/index.jsx
+++ b/src/components/Button/index.jsx
@@ -10,6 +10,7 @@ import styles from "./styles.module.scss";
  * @param {Object} props.children button text
  * @param {string} [props.className] class name added to root element
  * @param {'primary'|'primary-dark'|'primary-light'} [props.color] button color
+ * @param {boolean} [props.isDisabled] if button is disabled
  * @param {boolean} [props.isSelected] if button is selected
  * @param {string} [props.name] button name
  * @param {(e: any) => void} props.onClick function called when button is clicked
@@ -24,6 +25,7 @@ const Button = ({
   children,
   className,
   color = "primary",
+  isDisabled = false,
   isSelected = false,
   name,
   onClick,
@@ -35,6 +37,7 @@ const Button = ({
 }) => (
   <button
     data-value={value}
+    disabled={isDisabled}
     name={name || ""}
     type={type}
     className={cn(
@@ -58,6 +61,7 @@ Button.propTypes = {
   children: PT.node,
   className: PT.string,
   color: PT.oneOf(["primary"]),
+  isDisabled: PT.bool,
   isSelected: PT.bool,
   name: PT.string,
   onClick: PT.func,
diff --git a/src/components/Button/styles.module.scss b/src/components/Button/styles.module.scss
index 14164c5..2ae6cd1 100644
--- a/src/components/Button/styles.module.scss
+++ b/src/components/Button/styles.module.scss
@@ -10,6 +10,11 @@
   text-transform: uppercase;
   outline: none;
   cursor: pointer;
+
+  &:disabled {
+    opacity: 1;
+    cursor: not-allowed;
+  }
 }
 
 .medium {
@@ -55,6 +60,12 @@
     border-color: $primary-dark-color;
     color: $primary-dark-text-color;
   }
+
+  &:disabled {
+    border-color: $control-disabled-border-color;
+    background-color: $control-disabled-bg-color;
+    color: $control-disabled-text-color;
+  }
 }
 
 .contained {
@@ -76,6 +87,12 @@
     border-color: $primary-dark-color;
     background-color: $primary-dark-color;
   }
+
+  &:disabled {
+    border-color: $control-disabled-border-color;
+    background-color: $control-disabled-bg-color;
+    color: $control-disabled-text-color;
+  }
 }
 
 .circle,
diff --git a/src/components/Checkbox/index.jsx b/src/components/Checkbox/index.jsx
index 67ac06c..da21e84 100644
--- a/src/components/Checkbox/index.jsx
+++ b/src/components/Checkbox/index.jsx
@@ -1,6 +1,7 @@
 import React from "react";
 import PT from "prop-types";
 import cn from "classnames";
+import { stopPropagation } from "utils/misc";
 import styles from "./styles.module.scss";
 
 /**
@@ -9,20 +10,25 @@ import styles from "./styles.module.scss";
  * @param {Object} props component properties
  * @param {boolean} props.checked whether checkbox is checked
  * @param {string} [props.className] class name added to root element
+ * @param {boolean} [props.isDisabled] if checkbox is disabled
  * @param {string} props.name name for input element
  * @param {() => void} props.onChange function called when checkbox changes state
  * @param {Object} [props.option] object { value, label }
  * @param {'medium'|'small'} [props.size] checkbox size
+ * @param {boolean} [props.stopClickPropagation] whether to stop click event propagation
  * @returns {JSX.Element}
  */
 const Checkbox = ({
   checked,
   className,
+  isDisabled = false,
   name,
   onChange,
   option,
   size = "medium",
+  stopClickPropagation = false,
 }) => (
+  // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
   <label
     className={cn(
       styles.container,
@@ -30,9 +36,11 @@ const Checkbox = ({
       { [styles.single]: !option || !option.label },
       className
     )}
+    onClick={stopClickPropagation ? stopPropagation : null}
   >
     <input
       type="checkbox"
+      disabled={isDisabled}
       className={styles.checkbox}
       name={name}
       onChange={onChange}
@@ -49,6 +57,7 @@ const Checkbox = ({
 Checkbox.propTypes = {
   checked: PT.bool,
   className: PT.string,
+  isDisabled: PT.bool,
   name: PT.string.isRequired,
   size: PT.oneOf(["medium", "small"]),
   onChange: PT.func.isRequired,
@@ -56,6 +65,7 @@ Checkbox.propTypes = {
     value: PT.string.isRequired,
     label: PT.string,
   }),
+  stopClickPropagation: PT.bool,
 };
 
 export default Checkbox;
diff --git a/src/components/Checkbox/styles.module.scss b/src/components/Checkbox/styles.module.scss
index 10ff101..e07c408 100644
--- a/src/components/Checkbox/styles.module.scss
+++ b/src/components/Checkbox/styles.module.scss
@@ -36,6 +36,13 @@ input.checkbox {
       }
     }
   }
+
+  &:disabled {
+    + .impostor {
+      background-color: $control-disabled-bg-color;
+      cursor: not-allowed;
+    }
+  }
 }
 
 .impostor {
diff --git a/src/components/IntegerField/index.jsx b/src/components/IntegerField/index.jsx
index c7a0684..0af814f 100644
--- a/src/components/IntegerField/index.jsx
+++ b/src/components/IntegerField/index.jsx
@@ -8,6 +8,7 @@ import styles from "./styles.module.scss";
  *
  * @param {Object} props component properties
  * @param {string} [props.className] class name to be added to root element
+ * @param {boolean} [props.isDisabled] if the field is disabled
  * @param {string} props.name field's name
  * @param {number} props.value field's value
  * @param {number} [props.maxValue] maximum allowed value
@@ -17,6 +18,7 @@ import styles from "./styles.module.scss";
  */
 const IntegerField = ({
   className,
+  isDisabled = false,
   name,
   onChange,
   value,
@@ -24,20 +26,37 @@ const IntegerField = ({
   minValue = -Infinity,
 }) => (
   <div className={cn(styles.container, className)}>
+    <input
+      disabled={isDisabled}
+      readOnly
+      className={styles.input}
+      name={name}
+      value={value}
+    />
     <button
       className={styles.btnMinus}
-      onClick={() => onChange(Math.max(value - 1, minValue))}
+      onClick={(event) => {
+        event.stopPropagation();
+        if (!isDisabled) {
+          onChange(Math.max(value - 1, minValue));
+        }
+      }}
     />
     <button
       className={styles.btnPlus}
-      onClick={() => onChange(Math.min(+value + 1, maxValue))}
+      onClick={(event) => {
+        event.stopPropagation();
+        if (!isDisabled) {
+          onChange(Math.min(+value + 1, maxValue));
+        }
+      }}
     />
-    <input readOnly className={styles.input} name={name} value={value} />
   </div>
 );
 
 IntegerField.propTypes = {
   className: PT.string,
+  isDisabled: PT.bool,
   name: PT.string.isRequired,
   maxValue: PT.number,
   minValue: PT.number,
diff --git a/src/components/IntegerField/styles.module.scss b/src/components/IntegerField/styles.module.scss
index da7da79..32cd095 100644
--- a/src/components/IntegerField/styles.module.scss
+++ b/src/components/IntegerField/styles.module.scss
@@ -19,6 +19,18 @@ input.input {
   outline: none !important;
   box-shadow: none !important;
   text-align: center;
+
+  &:disabled {
+    border-color: $control-disabled-border-color;
+    background-color: $control-disabled-bg-color;
+    color: $control-disabled-text-color;
+    cursor: not-allowed;
+
+    ~ .btnMinus,
+    ~ .btnPlus {
+      cursor: not-allowed;
+    }
+  }
 }
 
 .btnMinus,
diff --git a/src/components/Page/index.jsx b/src/components/Page/index.jsx
index 4e681d9..2a85959 100644
--- a/src/components/Page/index.jsx
+++ b/src/components/Page/index.jsx
@@ -1,6 +1,7 @@
 import React from "react";
 import PT from "prop-types";
 import cn from "classnames";
+import ReduxToastr from "react-redux-toastr";
 import styles from "./styles.module.scss";
 
 /**
@@ -12,7 +13,15 @@ import styles from "./styles.module.scss";
  * @returns {Object}
  */
 const Page = ({ className, children }) => (
-  <div className={cn(styles.container, className)}>{children}</div>
+  <div className={cn(styles.container, className)}>
+    {children}
+    <ReduxToastr
+      timeOut={60000}
+      position="top-right"
+      transitionIn="fadeIn"
+      transitionOut="fadeOut"
+    />
+  </div>
 );
 
 Page.propTypes = {
diff --git a/src/components/SearchHandleField/index.jsx b/src/components/SearchHandleField/index.jsx
new file mode 100644
index 0000000..38a2168
--- /dev/null
+++ b/src/components/SearchHandleField/index.jsx
@@ -0,0 +1,102 @@
+import React, { useCallback, useState } from "react";
+import PT from "prop-types";
+import cn from "classnames";
+import _ from "lodash";
+import AsyncSelect from "react-select/async";
+import { getMemberSuggestions } from "services/teams";
+// import { getOptionByValue } from "utils/misc";
+import styles from "./styles.module.scss";
+
+const selectComponents = {
+  DropdownIndicator: () => null,
+  IndicatorSeparator: () => null,
+};
+
+/**
+ * Displays search input field.
+ *
+ * @param {Object} props component properties
+ * @param {string} [props.className] class name added to root element
+ * @param {string} props.id id for input element
+ * @param {string} props.placeholder placeholder text
+ * @param {string} props.name name for input element
+ * @param {'medium'|'small'} [props.size] field size
+ * @param {function} props.onChange function called when input value changes
+ * @param {string} props.value input value
+ * @returns {JSX.Element}
+ */
+const SearchAutocomplete = ({
+  className,
+  id,
+  size = "medium",
+  onChange,
+  placeholder,
+  value,
+}) => {
+  // const option = getOptionByValue(options, value);
+  const [savedInput, setSavedInput] = useState("");
+
+  const onValueChange = useCallback(
+    (option) => {
+      onChange(option.value);
+    },
+    [onChange]
+  );
+
+  return (
+    <div className={cn(styles.container, styles[size], className)}>
+      <span className={styles.icon} />
+      <AsyncSelect
+        className={styles.select}
+        classNamePrefix="custom"
+        components={selectComponents}
+        id={id}
+        isSearchable={true}
+        // menuIsOpen={true} // for debugging
+        // onChange={onOptionChange}
+        // onMenuOpen={onMenuOpen}
+        // onMenuClose={onMenuClose}
+        value={{ value, label: value }}
+        onInputChange={setSavedInput}
+        onFocus={() => {
+          setSavedInput("");
+          onChange(savedInput);
+        }}
+        placeholder={placeholder}
+        onChange={onValueChange}
+        noOptionsMessage={() => "No options"}
+        loadingMessage={() => "Loading..."}
+        loadOptions={loadSuggestions}
+        blurInputOnSelect
+      />
+    </div>
+  );
+};
+
+const loadSuggestions = (inputVal) => {
+  return getMemberSuggestions(inputVal)
+    .then((res) => {
+      const users = _.get(res, "data.result.content", []);
+      return users.map((user) => ({
+        label: user.handle,
+        value: user.handle,
+      }));
+    })
+    .catch(() => {
+      console.warn("could not get suggestions");
+      return [];
+    });
+};
+
+SearchAutocomplete.propTypes = {
+  className: PT.string,
+  id: PT.string.isRequired,
+  size: PT.oneOf(["medium", "small"]),
+  name: PT.string.isRequired,
+  onChange: PT.func.isRequired,
+  options: PT.array,
+  placeholder: PT.string,
+  value: PT.oneOfType([PT.number, PT.string]),
+};
+
+export default SearchAutocomplete;
diff --git a/src/components/SearchHandleField/styles.module.scss b/src/components/SearchHandleField/styles.module.scss
new file mode 100644
index 0000000..e6614cc
--- /dev/null
+++ b/src/components/SearchHandleField/styles.module.scss
@@ -0,0 +1,148 @@
+@import "styles/variables";
+@import "styles/mixins";
+
+.container {
+  display: flex;
+  align-items: center;
+  border: 1px solid $control-border-color;
+  border-radius: 6px;
+  background-color: #fff;
+
+  &.medium {
+    height: $control-height-medium;
+  }
+
+  &.small {
+    height: $control-height-small;
+  }
+}
+
+.icon {
+  margin: auto 10px;
+  width: 16px;
+  height: 16px;
+  background-size: 16px 16px;
+  background-position: center;
+  background-repeat: no-repeat;
+  background-image: url("./../../assets/images/icon-magnifier.svg");
+}
+
+input.input {
+  &::placeholder {
+    text-transform: none;
+    color: #aaa;
+  }
+}
+
+.select {
+  z-index: 1;
+  position: relative;
+  flex: 1 1 0;
+  align-self: stretch;
+  display: flex;
+  margin: 0;
+  border: none !important;
+  padding: 8px 16px 8px 0;
+  line-height: 22px;
+  background: none;
+  outline: none !important;
+  box-shadow: none !important;
+
+  :global(.custom__control) {
+    flex: 1 1 0;
+    display: flex;
+    border: none;
+    // border: 1px solid $control-border-color;
+    // border-radius: 6px;
+    min-height: 0;
+  }
+
+  :global(.custom__control--is-focused) {
+    box-shadow: none;
+  }
+
+  :global(.custom__value-container) {
+    position: relative;
+    flex: 1 1 0;
+    align-self: stretch;
+    margin: 0;
+    border: none;
+    padding: 0;
+
+    > * {
+      display: flex;
+      position: absolute;
+      left: 0;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      margin: 0;
+      border: none;
+      padding: 0;
+    }
+
+    input {
+      flex: 1 1 0;
+      margin: 0 !important;
+      padding: 0 !important;
+      border: none !important;
+      width: auto !important;
+      height: 22px !important;
+      outline: none !important;
+      box-shadow: none !important;
+      line-height: 22px;
+      color: inherit;
+    }
+  }
+
+  :global(.custom__single-value) {
+    line-height: 22px;
+    transform: none;
+    color: inherit;
+  }
+
+  :global(.custom__input) {
+    flex: 1 1 0;
+    display: flex;
+  }
+
+  :global(.custom__placeholder) {
+    + div {
+      margin: 0;
+      padding: 0;
+      color: inherit;
+    }
+  }
+
+  :global(.custom__menu) {
+    margin: 1px 0 0;
+    border: 1px solid $control-border-color;
+    border-radius: 0;
+    box-shadow: none;
+  }
+
+  :global(.custom__menu-list) {
+    padding: 9px 0;
+  }
+
+  :global(.custom__option) {
+    padding: 0 20px 1px;
+    font-size: 16px;
+    line-height: 26px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    background-color: #fff;
+    cursor: pointer;
+    @include roboto-regular;
+
+    &:hover {
+      background-color: #f5f5f5;
+    }
+  }
+
+  :global(.custom__option--is-selected) {
+    background-color: #229174 !important;
+    color: #fff;
+  }
+}
diff --git a/src/components/SelectField/index.jsx b/src/components/SelectField/index.jsx
index 31d8587..99c1e26 100644
--- a/src/components/SelectField/index.jsx
+++ b/src/components/SelectField/index.jsx
@@ -23,6 +23,7 @@ const selectComponents = { DropdownIndicator, IndicatorSeparator: () => null };
  * @param {Object} props component properties
  * @param {string} [props.className] class name to be added to root element
  * @param {string} props.id control's id
+ * @param {boolean} [props.isDisabled] whether the control should be disabled
  * @param {string} [props.label] control's label
  * @param {(v: string) => void} props.onChange on change handler
  * @param {Object} props.options options for dropdown
@@ -33,6 +34,7 @@ const selectComponents = { DropdownIndicator, IndicatorSeparator: () => null };
 const SelectField = ({
   className,
   id,
+  isDisabled = false,
   label,
   onChange,
   options,
@@ -76,6 +78,7 @@ const SelectField = ({
         classNamePrefix="custom"
         components={selectComponents}
         id={id}
+        isDisabled={isDisabled}
         isSearchable={false}
         // menuIsOpen={true} // for debugging
         onChange={onOptionChange}
@@ -91,6 +94,7 @@ const SelectField = ({
 SelectField.propTypes = {
   className: PT.string,
   id: PT.string.isRequired,
+  isDisabled: PT.bool,
   label: PT.string,
   size: PT.oneOf(["medium", "small"]),
   onChange: PT.func.isRequired,
diff --git a/src/components/ToastrMessage/index.jsx b/src/components/ToastrMessage/index.jsx
new file mode 100644
index 0000000..32aa210
--- /dev/null
+++ b/src/components/ToastrMessage/index.jsx
@@ -0,0 +1,40 @@
+import React from "react";
+import PT from "prop-types";
+import cn from "classnames";
+import styles from "./styles.module.scss";
+
+/**
+ * Displays toastr message.
+ *
+ * @param {Object} props component properties
+ * @returns {JSX.Element}
+ */
+const ToastrMessage = ({
+  children,
+  className,
+  message,
+  remove,
+  type = "info",
+}) => {
+  return (
+    <div className={cn(styles.container, styles[type], className)}>
+      {message && <span className={styles.message}>{message}</span>}
+      {children}
+      <button
+        type="button"
+        className={styles.btnClose}
+        onClick={remove}
+      ></button>
+    </div>
+  );
+};
+
+ToastrMessage.propTypes = {
+  children: PT.node,
+  className: PT.string,
+  message: PT.string,
+  remove: PT.func,
+  type: PT.oneOf(["info", "success", "warning", "error"]),
+};
+
+export default ToastrMessage;
diff --git a/src/components/ToastrMessage/styles.module.scss b/src/components/ToastrMessage/styles.module.scss
new file mode 100644
index 0000000..606e378
--- /dev/null
+++ b/src/components/ToastrMessage/styles.module.scss
@@ -0,0 +1,72 @@
+.container {
+  display: block;
+  position: relative;
+  border-radius: 8px;
+  padding: 14px 27px 15px 64px;
+  font-size: 16px;
+  line-height: 26px;
+  text-align: center;
+  color: #fff;
+
+  a {
+    color: #fff;
+  }
+}
+
+.message {
+  text-align: center;
+}
+
+.btnClose {
+  position: absolute;
+  top: 22px;
+  right: 27px;
+  margin: 0;
+  border: none;
+  padding: 0;
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  background: transparent;
+  outline: none !important;
+  cursor: pointer;
+
+  &::before,
+  &::after {
+    content: "";
+    display: block;
+    position: absolute;
+    top: 50%;
+    left: 0;
+    right: 0;
+    margin: -1px -1px auto;
+    width: 12px;
+    height: 2px;
+    background: #fff;
+    transform-origin: 50% 50%;
+  }
+
+  &::before {
+    transform: rotate(45deg);
+  }
+
+  &::after {
+    transform: rotate(-45deg);
+  }
+}
+
+.info {
+  background: #1e94a3;
+}
+
+.success {
+  background: #229174;
+}
+
+.warning {
+  background: #ef476f;
+}
+
+.error {
+  background: #e90c5a;
+}
diff --git a/src/components/Toggle/index.jsx b/src/components/Toggle/index.jsx
new file mode 100644
index 0000000..59dc92c
--- /dev/null
+++ b/src/components/Toggle/index.jsx
@@ -0,0 +1,51 @@
+import React, { useCallback } from "react";
+import PT from "prop-types";
+import cn from "classnames";
+import styles from "./styles.module.scss";
+
+/**
+ * Displays a toggle.
+ *
+ * @param {Object} props component properties
+ * @param {string} [props.className] class name to be added to root element
+ * @param {string} [props.id] id for toggle's input element
+ * @param {'medium'|'small'} [props.size] toggle size
+ * @param {boolean} props.isOn whether toggle is on or off
+ * @param {string} props.name name for toggle's input element
+ * @param {(v: boolean) => void} props.onChange function called with toggle's state changes
+ * @returns {JSX.Element}
+ */
+const Toggle = ({ className, id, isOn, name, onChange, size = "medium" }) => {
+  id = id || name;
+
+  const onToggleChange = useCallback(
+    (event) => {
+      onChange(event.currentTarget.checked);
+    },
+    [onChange]
+  );
+
+  return (
+    <label htmlFor={id} className={cn(styles.toggle, styles[size], className)}>
+      <input
+        type="checkbox"
+        id={id}
+        name={name}
+        checked={isOn}
+        onChange={onToggleChange}
+      />
+      <span />
+    </label>
+  );
+};
+
+Toggle.propTypes = {
+  className: PT.string,
+  id: PT.string,
+  isOn: PT.bool.isRequired,
+  name: PT.string.isRequired,
+  onChange: PT.func.isRequired,
+  size: PT.oneOf(["medium", "small"]),
+};
+
+export default Toggle;
diff --git a/src/components/Toggle/styles.module.scss b/src/components/Toggle/styles.module.scss
new file mode 100644
index 0000000..a789aaf
--- /dev/null
+++ b/src/components/Toggle/styles.module.scss
@@ -0,0 +1,105 @@
+@import "styles/variables";
+
+.toggle {
+  $toggle-width: 42px;
+  $toggle-height: 27px;
+  $handle-height: 21px;
+  $handle-width: $handle-height;
+  $handle-margin: ($toggle-height - $handle-height) / 2;
+  $handle-shift: $toggle-width - 2 * $handle-margin - $handle-width;
+
+  position: relative;
+  display: inline-block;
+  width: $toggle-width;
+  height: $toggle-height;
+  border-radius: 500rem;
+  box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.15);
+  overflow: hidden;
+
+  input {
+    display: block;
+    z-index: 1;
+    position: relative;
+    margin: 0;
+    border: none;
+    padding: 0;
+    width: 100%;
+    height: 100%;
+    -webkit-appearance: none;
+    -moz-appearance: none;
+    appearance: none;
+
+    &:checked {
+      + span {
+        background-color: $primary-light-color;
+
+        &::after {
+          transform: translateX($handle-shift);
+        }
+      }
+    }
+  }
+
+  span {
+    display: block;
+    z-index: 2;
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    width: 100%;
+    height: 100%;
+    background-color: #aaa;
+    transition: background-color 0.3s ease-in-out;
+    cursor: pointer;
+
+    &::after {
+      content: "";
+      display: block;
+      position: absolute;
+      left: $handle-margin;
+      top: 0;
+      bottom: 0;
+      margin: auto 0;
+      width: $handle-width;
+      height: $handle-height;
+      border-radius: 500rem;
+      background-color: #fff;
+      box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.35);
+      transform: translateX(0);
+      transition: transform 0.3s ease-in-out;
+      // overflow: hidden;
+    }
+  }
+
+  &.small {
+    $toggle-width: 34px;
+    $toggle-height: 20px;
+    $handle-height: 14px;
+    $handle-width: $handle-height;
+    $handle-margin: ($toggle-height - $handle-height) / 2;
+    $handle-shift: $toggle-width - 2 * $handle-margin - $handle-width;
+
+    width: $toggle-width;
+    height: $toggle-height;
+
+    input {
+      &:checked {
+        + span {
+          &::after {
+            transform: translateX($handle-shift);
+          }
+        }
+      }
+    }
+
+    span {
+      &::after {
+        left: $handle-margin;
+        width: $handle-width;
+        height: $handle-height;
+      }
+    }
+  }
+}
diff --git a/src/constants/index.js b/src/constants/index.js
index 94326c5..51f91e6 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -1,6 +1,6 @@
-import { PLATFORM_WEBSITE_URL } from "../../config";
+import { PLATFORM_WEBSITE_URL, TOPCODER_WEBSITE_URL } from "../../config";
 
-export { PLATFORM_WEBSITE_URL };
+export { PLATFORM_WEBSITE_URL, TOPCODER_WEBSITE_URL };
 
 export const APP_BASE_PATH = "/taas-admin";
 
diff --git a/src/constants/workPeriods.js b/src/constants/workPeriods.js
index fc1492c..5de195d 100644
--- a/src/constants/workPeriods.js
+++ b/src/constants/workPeriods.js
@@ -8,15 +8,22 @@ import * as PAYMENT_STATUS from "./workPeriods/paymentStatus";
 
 export { API_PAYMENT_STATUS, API_SORT_BY, SORT_BY, SORT_ORDER, PAYMENT_STATUS };
 
-// challenges API url
-export const API_URL = `${API.V5}/resourceBookings`;
+// resource bookings API url
+export const RB_API_URL = `${API.V5}/resourceBookings`;
+export const JOBS_API_URL = `${API.V5}/jobs`;
+export const PAYMENTS_API_URL = `${API.V5}/work-period-payments`;
+export const PROJECTS_API_URL = `${API.V5}/projects`;
+export const WORK_PERIODS_API_URL = `${API.V5}/work-periods`;
 
-export const DATE_FORMAT = "YYYY-MM-DD";
+export const DATE_FORMAT_API = "YYYY-MM-DD";
+export const DATE_FORMAT_UI = "MMM DD, YYYY";
 
-// Field names that are required to be retrieved for display, filtering and soring.
+// Field names that are required to be retrieved for display, filtering and sorting.
 export const REQUIRED_FIELDS = [
   "id",
+  "jobId",
   "projectId",
+  "billingAccountId",
   "startDate",
   "endDate",
   "memberRate",
@@ -59,7 +66,7 @@ export const PAYMENT_STATUS_LABELS = {
   [PAYMENT_STATUS.PAID]: "Paid",
   [PAYMENT_STATUS.PENDING]: "Pending",
   [PAYMENT_STATUS.IN_PROGRESS]: "In Progress",
-  [PAYMENT_STATUS.UNDEFINED]: "Undefined",
+  [PAYMENT_STATUS.UNDEFINED]: "NA",
 };
 
 export const PAYMENT_STATUS_MAP = {
diff --git a/src/root.component.jsx b/src/root.component.jsx
index 6be922c..9bec738 100644
--- a/src/root.component.jsx
+++ b/src/root.component.jsx
@@ -1,7 +1,6 @@
 import React, { useLayoutEffect } from "react";
 import { Provider } from "react-redux";
 import { Router, Redirect } from "@reach/router";
-// import ReduxToastr from "react-redux-toastr";
 import store from "store";
 import { disableSidebarForRoute } from "@topcoder/micro-frontends-navbar-app";
 import WorkPeriods from "routes/WorkPeriods";
@@ -25,12 +24,6 @@ export default function Root() {
         <WorkPeriods path={`${APP_BASE_PATH}/work-periods`} />
         <Freelancers path={`${APP_BASE_PATH}/freelancers`} />
       </Router>
-      {/* <ReduxToastr
-        timeOut={4000}
-        position="bottom-left"
-        transitionIn="fadeIn"
-        transitionOut="fadeOut"
-      /> */}
     </Provider>
   );
 }
diff --git a/src/routes/WorkPeriods/components/PaymentStatus/styles.module.scss b/src/routes/WorkPeriods/components/PaymentStatus/styles.module.scss
index 139f939..a232c95 100644
--- a/src/routes/WorkPeriods/components/PaymentStatus/styles.module.scss
+++ b/src/routes/WorkPeriods/components/PaymentStatus/styles.module.scss
@@ -30,5 +30,10 @@
 }
 
 .undefined {
-  background: #2a2a2a;
+  padding: 0;
+  @include roboto-regular;
+  font-size: 14px;
+  line-height: 20px;
+  letter-spacing: normal;
+  background: transparent;
 }
diff --git a/src/routes/WorkPeriods/components/PaymentsListItem/index.jsx b/src/routes/WorkPeriods/components/PaymentsListItem/index.jsx
new file mode 100644
index 0000000..1914614
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PaymentsListItem/index.jsx
@@ -0,0 +1,48 @@
+// @ts-nocheck
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+import React, { useCallback, useRef } from "react";
+import PT from "prop-types";
+import cn from "classnames";
+import styles from "./styles.module.scss";
+import { formatChallengeUrl } from "utils/formatters";
+
+const PaymentsListItem = ({ className, item }) => {
+  const inputRef = useRef();
+
+  const onCopyLinkClick = useCallback(() => {
+    inputRef.current?.focus();
+    inputRef.current?.select();
+    document.execCommand("copy");
+  }, []);
+
+  return (
+    <div className={cn(styles.container, className)}>
+      <span className={styles.iconLink}></span>
+      <input
+        readOnly
+        ref={inputRef}
+        type="text"
+        value={item.challengeId || "0"}
+      />
+      <span className={styles.iconCopyLink} onClick={onCopyLinkClick}></span>
+      <a
+        className={styles.iconOpenLink}
+        href={formatChallengeUrl(item.challengeId)}
+        target="_blank"
+        rel="noreferrer"
+      >
+        <span className={styles.hidden}>{item.id}</span>
+      </a>
+    </div>
+  );
+};
+
+PaymentsListItem.propTypes = {
+  className: PT.string,
+  item: PT.shape({
+    id: PT.oneOfType([PT.string, PT.number]),
+    challengeId: PT.oneOfType([PT.string, PT.number]),
+  }),
+};
+
+export default PaymentsListItem;
diff --git a/src/routes/WorkPeriods/components/PaymentsListItem/styles.module.scss b/src/routes/WorkPeriods/components/PaymentsListItem/styles.module.scss
new file mode 100644
index 0000000..b71ea11
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PaymentsListItem/styles.module.scss
@@ -0,0 +1,62 @@
+@import "styles/mixins";
+
+.container {
+  display: flex;
+  align-items: center;
+
+  input {
+    display: block;
+    margin: 0;
+    border: none;
+    padding: 0;
+    max-width: 70px;
+    height: 22px;
+    background: #fff;
+    @include roboto-medium;
+    line-height: 22px;
+    outline: none !important;
+    color: #0d61bf;
+  }
+}
+
+.iconLink {
+  flex: 0 0 auto;
+  display: inline-block;
+  margin-right: 5px;
+  width: 16px;
+  height: 16px;
+  background-repeat: no-repeat;
+  background-size: contain;
+  background-position: center;
+  background-image: url("./../../../../assets/images/icon-link.png");
+}
+
+.iconCopyLink {
+  flex: 0 0 auto;
+  display: inline-block;
+  margin-left: 18px;
+  width: 16px;
+  height: 16px;
+  background-repeat: no-repeat;
+  background-size: contain;
+  background-position: center;
+  background-image: url("./../../../../assets/images/icon-copy.png");
+  cursor: pointer;
+}
+
+.iconOpenLink {
+  flex: 0 0 auto;
+  display: inline-block;
+  margin-left: 16px;
+  width: 16px;
+  height: 16px;
+  text-decoration: none;
+  background-repeat: no-repeat;
+  background-size: contain;
+  background-position: center;
+  background-image: url("./../../../../assets/images/icon-open-outside.png");
+}
+
+.hidden {
+  display: none;
+}
diff --git a/src/routes/WorkPeriods/components/PaymentsPopup/index.jsx b/src/routes/WorkPeriods/components/PaymentsPopup/index.jsx
new file mode 100644
index 0000000..93e8324
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PaymentsPopup/index.jsx
@@ -0,0 +1,36 @@
+import React from "react";
+import PT from "prop-types";
+import cn from "classnames";
+import styles from "./styles.module.scss";
+import PaymentsListItem from "../PaymentsListItem";
+
+/**
+ * Displays popup with payments.
+ *
+ * @param {Object} props component properties
+ * @returns {JSX.Element}
+ */
+const PaymentsPopup = ({ className, payments }) => {
+  return (
+    <form className={cn(styles.container, className)} action="#">
+      <div className={styles.title}>Challenges for Payments</div>
+      <div className={styles.paymentsList}>
+        {payments.map((payment) => (
+          <PaymentsListItem key={payment.id} item={payment} />
+        ))}
+      </div>
+    </form>
+  );
+};
+
+PaymentsPopup.propTypes = {
+  className: PT.string,
+  payments: PT.arrayOf(
+    PT.shape({
+      id: PT.oneOfType([PT.string, PT.number]),
+      challengeId: PT.oneOfType([PT.string, PT.number]),
+    })
+  ),
+};
+
+export default PaymentsPopup;
diff --git a/src/routes/WorkPeriods/components/PaymentsPopup/styles.module.scss b/src/routes/WorkPeriods/components/PaymentsPopup/styles.module.scss
new file mode 100644
index 0000000..a29e703
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PaymentsPopup/styles.module.scss
@@ -0,0 +1,20 @@
+@import "styles/mixins";
+
+.container {
+  position: relative;
+  border-radius: 8px;
+  padding: 25px 30px 25px 23px;
+  box-shadow: 0 5px 35px 5px rgba(21, 21, 22, 0.1),
+    0 10px 14px 0 rgba(21, 21, 22, 0.3);
+  background: #fff;
+}
+
+.title {
+  @include roboto-medium;
+  line-height: 20px;
+  white-space: nowrap;
+}
+
+.paymentsList {
+  margin-top: 5px;
+}
diff --git a/src/routes/WorkPeriods/components/PeriodDetails/index.jsx b/src/routes/WorkPeriods/components/PeriodDetails/index.jsx
new file mode 100644
index 0000000..bf0ce72
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodDetails/index.jsx
@@ -0,0 +1,204 @@
+import React, { memo, useCallback } from "react";
+import { useDispatch } from "react-redux";
+import PT from "prop-types";
+import cn from "classnames";
+import debounce from "lodash/debounce";
+import Button from "components/Button";
+import Toggle from "components/Toggle";
+import SelectField from "components/SelectField";
+import PeriodsHistory from "../PeriodsHistory";
+import IconComputer from "../../../../assets/images/icon-computer.svg";
+import {
+  hideWorkPeriodDetails,
+  setBillingAccount,
+  setDetailsHidePastPeriods,
+  setDetailsLockWorkingDays,
+} from "store/actions/workPeriods";
+import styles from "./styles.module.scss";
+import { updateWorkPeriodBillingAccount } from "store/thunks/workPeriods";
+import { useUpdateEffect } from "utils/hooks";
+
+/**
+ * Displays working period details.
+ *
+ * @param {Object} props component properties
+ * @param {string} [props.className] class name to be added to root element
+ * @param {Object} props.details working period details object
+ * @param {boolean} props.isDisabled whether the details are disabled
+ * @returns {JSX.Element}
+ */
+const PeriodDetails = ({ className, details, isDisabled }) => {
+  const dispatch = useDispatch();
+  const {
+    periodId,
+    rbId,
+    jobName,
+    jobNameIsLoading,
+    billingAccountId,
+    billingAccounts,
+    billingAccountsIsLoading,
+    periodsVisible,
+    periodsIsLoading,
+    hidePastPeriods,
+    lockWorkingDays,
+  } = details;
+
+  const onHideDetailsBtnClick = useCallback(() => {
+    dispatch(hideWorkPeriodDetails(periodId));
+  }, [dispatch, periodId]);
+
+  const onChangeHidePastPeriods = useCallback(
+    (hide) => {
+      dispatch(setDetailsHidePastPeriods(periodId, hide));
+    },
+    [dispatch, periodId]
+  );
+
+  const onChangeLockWorkingDays = useCallback(
+    (lock) => {
+      dispatch(setDetailsLockWorkingDays(periodId, lock));
+    },
+    [dispatch, periodId]
+  );
+
+  const onChangeBillingAccount = useCallback(
+    (value) => {
+      dispatch(setBillingAccount(periodId, value));
+    },
+    [dispatch, periodId]
+  );
+
+  const updateBillingAccount = useCallback(
+    debounce(
+      (billingAccountId) => {
+        dispatch(updateWorkPeriodBillingAccount(rbId, billingAccountId));
+      },
+      300,
+      { leading: false }
+    ),
+    [dispatch, rbId]
+  );
+
+  useUpdateEffect(() => {
+    updateBillingAccount(billingAccountId);
+  }, [billingAccountId]);
+
+  const isFailedLoadingJobName = !jobNameIsLoading && jobName === "Error";
+  const isFailedLoadingBilAccs =
+    !billingAccountsIsLoading &&
+    billingAccounts.length === 1 &&
+    billingAccounts[0].value === 0;
+  const isDisabledBilAccs =
+    !billingAccountsIsLoading &&
+    billingAccounts.length === 1 &&
+    billingAccounts[0].value === -1;
+
+  return (
+    <tr className={cn(styles.container, className)}>
+      {periodsIsLoading ? (
+        <td colSpan={8}>
+          <div className={styles.loadingIndicator}>Loading...</div>
+        </td>
+      ) : (
+        <>
+          <td colSpan={3} className={styles.periodInfo}>
+            <div className={styles.jobNameSection}>
+              <IconComputer className={styles.jobNameIcon} />
+              <div className={styles.sectionField}>
+                <div className={styles.label}>Job Name</div>
+                <div
+                  className={cn(styles.jobName, {
+                    [styles.jobNameError]: isFailedLoadingJobName,
+                  })}
+                >
+                  {jobNameIsLoading ? "Loading..." : jobName}
+                </div>
+              </div>
+            </div>
+            <div className={styles.lockWorkingDaysSection}>
+              <div className={styles.sectionLabel}>Lock Working Days</div>
+              <Toggle
+                size="small"
+                className={styles.lockWorkingDaysToggle}
+                name={`rb_lck_wd_${periodId}`}
+                onChange={onChangeLockWorkingDays}
+                isOn={lockWorkingDays}
+              />
+            </div>
+            <div className={styles.billingAccountSection}>
+              <div className={styles.sectionLabel}>Billing Account</div>
+              <SelectField
+                className={
+                  isFailedLoadingBilAccs ? styles.billingAccountError : ""
+                }
+                id={`rb_bil_acc_${periodId}`}
+                isDisabled={isDisabledBilAccs}
+                size="small"
+                onChange={onChangeBillingAccount}
+                options={billingAccounts}
+                value={billingAccountId}
+              />
+            </div>
+            <div className={styles.detailsControls}>
+              <Button size="small" onClick={onHideDetailsBtnClick}>
+                Hide Details
+              </Button>
+            </div>
+          </td>
+          <td colSpan={5} className={styles.periodHistory}>
+            <div className={styles.periodsContainer}>
+              <div className={styles.periodsHeader}>
+                <span className={styles.periodsHeaderTitle}>History</span>
+                <span className={styles.hidePastPeriods}>
+                  <label
+                    htmlFor={`hide_past_wp_${periodId}`}
+                    className={styles.hidePastPeriodsLabel}
+                  >
+                    Current &amp; Future Only
+                  </label>
+                  <Toggle
+                    className={styles.hidePastPeriodsToggle}
+                    name={`hide_past_wp_${periodId}`}
+                    onChange={onChangeHidePastPeriods}
+                    size="small"
+                    isOn={hidePastPeriods}
+                  />
+                </span>
+              </div>
+              <PeriodsHistory
+                isDisabled={isDisabled}
+                periodId={periodId}
+                periods={periodsVisible}
+              />
+            </div>
+          </td>
+        </>
+      )}
+    </tr>
+  );
+};
+
+PeriodDetails.propTypes = {
+  className: PT.string,
+  details: PT.shape({
+    periodId: PT.string.isRequired,
+    rbId: PT.string.isRequired,
+    jobName: PT.string,
+    jobNameIsLoading: PT.bool.isRequired,
+    billingAccountId: PT.number.isRequired,
+    billingAccounts: PT.arrayOf(
+      PT.shape({
+        label: PT.string.isRequired,
+        value: PT.string.isRequired,
+      })
+    ),
+    billingAccountsIsLoading: PT.bool.isRequired,
+    periodsVisible: PT.array.isRequired,
+    periodsIsLoading: PT.bool.isRequired,
+    hidePastPeriods: PT.bool.isRequired,
+    lockWorkingDays: PT.bool.isRequired,
+  }).isRequired,
+  isDisabled: PT.bool.isRequired,
+};
+
+export default memo(PeriodDetails);
diff --git a/src/routes/WorkPeriods/components/PeriodDetails/styles.module.scss b/src/routes/WorkPeriods/components/PeriodDetails/styles.module.scss
new file mode 100644
index 0000000..6349a41
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodDetails/styles.module.scss
@@ -0,0 +1,131 @@
+@import "styles/mixins";
+
+.container {
+  position: relative;
+}
+
+.loadingIndicator {
+  border: 1px solid #d6d6d6;
+  border-top: none;
+  padding: 31px 0 35px;
+  text-align: center;
+  @include roboto-regular;
+  font-size: 24px;
+  background-color: #fff;
+  color: #aaa;
+}
+
+.periodInfo {
+  vertical-align: top;
+  border: 1px solid #d6d6d6;
+  border-top: none;
+  border-right: none;
+  padding: 16px 22px 13px;
+}
+
+.label {
+  font-size: 12px;
+  line-height: 16px;
+  letter-spacing: 0.5px;
+  color: #555;
+}
+
+.sectionLabel {
+  @include roboto-medium;
+  font-size: 14px;
+  line-height: 26px;
+}
+
+.jobNameSection {
+  display: flex;
+  align-items: center;
+}
+
+.jobNameIcon {
+  flex: 0 0 auto;
+  margin-right: 16px;
+  width: 21px;
+  height: auto;
+}
+
+.sectionField {
+  flex: 1 1 auto;
+}
+
+.jobName {
+  margin-top: 2px;
+  @include roboto-medium;
+  line-height: 22px;
+}
+
+.jobNameError {
+  color: #e90c5a;
+}
+
+.lockWorkingDaysSection {
+  margin-top: 19px;
+}
+
+.lockWorkingDaysToggle {
+  margin-top: 6px;
+}
+
+.billingAccountSection {
+  margin-top: 13px;
+}
+
+.billingAccountError {
+  color: #e90c5a;
+}
+
+.detailsControls {
+  margin-top: 20px;
+}
+
+.periodHistory {
+  vertical-align: top;
+  border: 1px solid #d6d6d6;
+  border-top: none;
+  border-left: none;
+}
+
+.periodsContainer {
+  border: 1px solid #d6d6d6;
+  border-right: none;
+}
+
+.periodsHeader {
+  position: relative;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 7px 12px;
+  background: #f4f4f4;
+
+  &::after {
+    content: "";
+    display: block;
+    position: absolute;
+    left: 12px;
+    right: 12px;
+    bottom: 0;
+    height: 1px;
+    background: #e9e9e9;
+  }
+}
+
+.periodsHeaderTitle {
+  @include roboto-medium;
+  line-height: 26px;
+}
+
+.hidePastPeriods {
+  display: flex;
+  align-items: center;
+  @include roboto-medium;
+  line-height: 26px;
+}
+
+.hidePastPeriodsLabel {
+  margin-right: 7px;
+}
diff --git a/src/routes/WorkPeriods/components/PeriodFilters/index.jsx b/src/routes/WorkPeriods/components/PeriodFilters/index.jsx
index fff1eb6..695ed57 100644
--- a/src/routes/WorkPeriods/components/PeriodFilters/index.jsx
+++ b/src/routes/WorkPeriods/components/PeriodFilters/index.jsx
@@ -5,6 +5,7 @@ import PT from "prop-types";
 import cn from "classnames";
 // import SidebarSection from "components/SidebarSection";
 import Button from "components/Button";
+import SearchHandleField from "components/SearchHandleField";
 // import CheckboxList from "components/CheckboxList";
 // import { PAYMENT_STATUS } from "constants/workPeriods";
 import { getWorkPeriodsFilters } from "store/selectors/workPeriods";
@@ -16,7 +17,6 @@ import {
 import { loadWorkPeriodsPage as loadWorkingPeriodsPage } from "store/thunks/workPeriods";
 import { useUpdateEffect } from "utils/hooks";
 import styles from "./styles.module.scss";
-import SearchField from "components/SearchField";
 
 /**
  * Displays working periods' filters like user handle search control or
@@ -66,7 +66,7 @@ const PeriodFilters = ({ className }) => {
   return (
     <form className={cn(styles.container, className)} action="#">
       <div className={styles.handleSection}>
-        <SearchField
+        <SearchHandleField
           id="topcoder-handle"
           name="topcoder_handle"
           placeholder="Search Topcoder Handle"
diff --git a/src/routes/WorkPeriods/components/PeriodItem/index.jsx b/src/routes/WorkPeriods/components/PeriodItem/index.jsx
index 4b1a943..92ca032 100644
--- a/src/routes/WorkPeriods/components/PeriodItem/index.jsx
+++ b/src/routes/WorkPeriods/components/PeriodItem/index.jsx
@@ -1,82 +1,136 @@
 import React, { memo, useCallback } from "react";
+import { useDispatch } from "react-redux";
 import PT from "prop-types";
+import cn from "classnames";
+import debounce from "lodash/debounce";
 import Checkbox from "components/Checkbox";
 import IntegerField from "components/IntegerField";
 import PaymentStatus from "../PaymentStatus";
+import PeriodDetails from "../PeriodDetails";
+import {
+  setWorkPeriodWorkingDays,
+  toggleWorkPeriod,
+} from "store/actions/workPeriods";
+import {
+  toggleWorkPeriodDetails,
+  updateWorkPeriodWorkingDays,
+} from "store/thunks/workPeriods";
+import { useUpdateEffect } from "utils/hooks";
 import { formatUserHandleLink, formatWeeklyRate } from "utils/formatters";
 import styles from "./styles.module.scss";
 
+/**
+ * @param {(v: string) => void} props.onToggle function called when working period checkbox is clicked
+ * @param {(id: object) => void} props.onToggleDetails function called when item row is clicked
+ * @param {(v: { periodId: string, workingDays: number }) => void} props.onWorkingDaysChange
+ * function called when the number of working days is changed
+ */
+
 /**
  * Displays the working period data row to be used in PeriodList component.
  *
  * @param {Object} props component properties
+ * @param {boolean} [props.isDisabled] whether the item is disabled
  * @param {boolean} props.isSelected whether the item is selected
  * @param {Object} props.item object describing a working period
- * @param {(v: string) => void} props.onToggle function called when working period checkbox is clicked
- * @param {(v: { periodId: string, workingDays: number }) => void} props.onWorkingDaysChange
- * function called when the number of working days is changed
+ * @param {Object} [props.details] object with working period details
  * @returns {JSX.Element}
  */
-const PeriodItem = ({ isSelected, item, onToggle, onWorkingDaysChange }) => {
+const PeriodItem = ({ isDisabled = false, isSelected, item, details }) => {
+  const dispatch = useDispatch();
+
   const onToggleItem = useCallback(
     (event) => {
-      onToggle(event.target.value);
+      dispatch(toggleWorkPeriod(event.target.value));
     },
-    [onToggle]
+    [dispatch]
   );
-  const onDaysChange = useCallback(
+
+  const onToggleItemDetails = useCallback(() => {
+    dispatch(toggleWorkPeriodDetails(item));
+  }, [dispatch, item]);
+
+  const onWorkingDaysChange = useCallback(
     (workingDays) => {
-      onWorkingDaysChange({ periodId: item.id, workingDays });
+      dispatch(setWorkPeriodWorkingDays({ periodId: item.id, workingDays }));
     },
-    [item, onWorkingDaysChange]
+    [dispatch, item.id]
   );
+
+  const updateWorkingDays = useCallback(
+    debounce(
+      (workingDays) => {
+        dispatch(updateWorkPeriodWorkingDays(item.id, workingDays));
+      },
+      300,
+      { leading: false }
+    ),
+    [dispatch, item.id]
+  );
+
+  // Update working days on server if working days change.
+  useUpdateEffect(() => {
+    updateWorkingDays(item.workingDays);
+  }, [item.workingDays]);
+
   return (
-    <tr className={styles.container}>
-      <td className={styles.toggle}>
-        <Checkbox
-          size="small"
-          checked={isSelected}
-          name={`res_chb_${item.id}`}
-          onChange={onToggleItem}
-          option={{ value: item.id }}
-        />
-      </td>
-      <td className={styles.userHandle}>
-        <span>
-          <a
-            href={formatUserHandleLink(item.projectId, item.rbId)}
-            target="_blank"
-            rel="noreferrer"
-          >
-            {item.userHandle}
-          </a>
-        </span>
-      </td>
-      <td className={styles.teamName}>{item.projectId}</td>
-      <td className={styles.startDate}>{item.startDate}</td>
-      <td className={styles.endDate}>{item.endDate}</td>
-      <td className={styles.weeklyRate}>
-        <span>{formatWeeklyRate(item.weeklyRate)}</span>
-      </td>
-      <td>
-        <PaymentStatus status={item.paymentStatus} />
-      </td>
-      <td className={styles.workingDays}>
-        <IntegerField
-          className={styles.workingDaysControl}
-          name={`res_wrk_days_${item.id}`}
-          onChange={onDaysChange}
-          maxValue={7}
-          minValue={0}
-          value={item.workingDays}
-        />
-      </td>
-    </tr>
+    <>
+      <tr
+        className={cn(styles.container, { [styles.hasDetails]: !!details })}
+        onClick={onToggleItemDetails}
+      >
+        <td className={styles.toggle}>
+          <Checkbox
+            size="small"
+            isDisabled={isDisabled}
+            checked={isSelected}
+            name={`wp_chb_${item.id}`}
+            onChange={onToggleItem}
+            option={{ value: item.id }}
+            stopClickPropagation={true}
+          />
+        </td>
+        <td className={styles.userHandle}>
+          <span>
+            <a
+              href={formatUserHandleLink(item.projectId, item.rbId)}
+              onClick={stopPropagation}
+              target="_blank"
+              rel="noreferrer"
+            >
+              {item.userHandle}
+            </a>
+          </span>
+        </td>
+        <td className={styles.teamName}>{item.projectId}</td>
+        <td className={styles.startDate}>{item.startDate}</td>
+        <td className={styles.endDate}>{item.endDate}</td>
+        <td className={styles.weeklyRate}>
+          <span>{formatWeeklyRate(item.weeklyRate)}</span>
+        </td>
+        <td>
+          <PaymentStatus status={item.paymentStatus} />
+        </td>
+        <td className={styles.workingDays}>
+          <IntegerField
+            className={styles.workingDaysControl}
+            isDisabled={isDisabled}
+            name={`wp_wrk_days_${item.id}`}
+            onChange={onWorkingDaysChange}
+            maxValue={5}
+            minValue={0}
+            value={item.workingDays}
+          />
+        </td>
+      </tr>
+      {details && <PeriodDetails details={details} isDisabled={isDisabled} />}
+    </>
   );
 };
 
 PeriodItem.propTypes = {
   className: PT.string,
+  isDisabled: PT.bool,
   isSelected: PT.bool.isRequired,
   item: PT.shape({
     id: PT.oneOfType([PT.number, PT.string]).isRequired,
@@ -90,8 +144,33 @@ PeriodItem.propTypes = {
     paymentStatus: PT.string.isRequired,
     workingDays: PT.number.isRequired,
   }),
-  onToggle: PT.func.isRequired,
-  onWorkingDaysChange: PT.func.isRequired,
+  details: PT.shape({
+    periodId: PT.string.isRequired,
+    rbId: PT.string.isRequired,
+    jobName: PT.string.isRequired,
+    jobNameIsLoading: PT.bool.isRequired,
+    billingAccountId: PT.number.isRequired,
+    billingAccounts: PT.arrayOf(
+      PT.shape({
+        value: PT.string.isRequired,
+        label: PT.string.isRequired,
+      })
+    ),
+    billingAccountsIsLoading: PT.bool.isRequired,
+    periods: PT.arrayOf(
+      PT.shape({
+        id: PT.string.isRequired,
+      })
+    ),
+    periodsIsLoading: PT.bool.isRequired,
+  }),
+  // onToggle: PT.func.isRequired,
+  // onToggleDetails: PT.func.isRequired,
+  // onWorkingDaysChange: PT.func.isRequired,
 };
 
+function stopPropagation(event) {
+  event.stopPropagation();
+}
+
 export default memo(PeriodItem);
diff --git a/src/routes/WorkPeriods/components/PeriodItem/styles.module.scss b/src/routes/WorkPeriods/components/PeriodItem/styles.module.scss
index 18dd70c..98a4561 100644
--- a/src/routes/WorkPeriods/components/PeriodItem/styles.module.scss
+++ b/src/routes/WorkPeriods/components/PeriodItem/styles.module.scss
@@ -12,6 +12,25 @@
       background: #f9f9f9;
     }
   }
+
+  &.hasDetails {
+    td {
+      border-top: 1px solid #d6d6d6;
+      background: #fff;
+
+      &.toggle {
+        border-left: 1px solid #d6d6d6;
+        padding-top: 11px;
+        padding-left: 14px;
+      }
+
+      &.workingDays {
+        border-right: 1px solid #d6d6d6;
+        padding-top: 4px;
+        padding-right: 9px;
+      }
+    }
+  }
 }
 
 td.toggle {
diff --git a/src/routes/WorkPeriods/components/PeriodList/index.jsx b/src/routes/WorkPeriods/components/PeriodList/index.jsx
index 5cb6f88..e925a27 100644
--- a/src/routes/WorkPeriods/components/PeriodList/index.jsx
+++ b/src/routes/WorkPeriods/components/PeriodList/index.jsx
@@ -1,17 +1,15 @@
-import React, { useCallback } from "react";
-import { useDispatch, useSelector } from "react-redux";
+import React from "react";
+import { useSelector } from "react-redux";
 import PT from "prop-types";
 import cn from "classnames";
 import PeriodItem from "../PeriodItem";
 import PeriodListHead from "../PeriodListHead";
 import {
   getWorkPeriods,
+  getWorkPeriodsDetails,
+  getWorkPeriodsIsProcessingPayments,
   getWorkPeriodsSelected,
 } from "store/selectors/workPeriods";
-import {
-  setWorkPeriodWorkingDays,
-  toggleWorkPeriod,
-} from "store/actions/workPeriods";
 import styles from "./styles.module.scss";
 
 /**
@@ -23,22 +21,9 @@ import styles from "./styles.module.scss";
  */
 const PeriodList = ({ className }) => {
   const periods = useSelector(getWorkPeriods);
+  const periodsDetails = useSelector(getWorkPeriodsDetails);
   const periodsSelected = useSelector(getWorkPeriodsSelected);
-  const dispatch = useDispatch();
-
-  const onTogglePeriod = useCallback(
-    (periodId) => {
-      dispatch(toggleWorkPeriod(periodId));
-    },
-    [dispatch]
-  );
-
-  const onWorkingDaysChange = useCallback(
-    (payload) => {
-      dispatch(setWorkPeriodWorkingDays(payload));
-    },
-    [dispatch]
-  );
+  const isProcessingPayments = useSelector(getWorkPeriodsIsProcessingPayments);
 
   return (
     <div className={cn(styles.container, className)}>
@@ -53,10 +38,10 @@ const PeriodList = ({ className }) => {
           {periods.map((period) => (
             <PeriodItem
               key={period.id}
+              isDisabled={isProcessingPayments}
               isSelected={period.id in periodsSelected}
               item={period}
-              onToggle={onTogglePeriod}
-              onWorkingDaysChange={onWorkingDaysChange}
+              details={periodsDetails[period.id]}
             />
           ))}
         </tbody>
diff --git a/src/routes/WorkPeriods/components/PeriodsContentHeader/index.jsx b/src/routes/WorkPeriods/components/PeriodsContentHeader/index.jsx
new file mode 100644
index 0000000..36c4417
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsContentHeader/index.jsx
@@ -0,0 +1,36 @@
+import React, { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import ContentHeader from "components/ContentHeader";
+import Button from "components/Button";
+import PageTitle from "components/PageTitle";
+import {
+  getWorkPeriodsHasSelectedItems,
+  getWorkPeriodsIsProcessingPayments,
+} from "store/selectors/workPeriods";
+import { processPayments } from "store/thunks/workPeriods";
+import styles from "./styles.module.scss";
+
+const PeriodsContentHeader = () => {
+  const hasSelectedItems = useSelector(getWorkPeriodsHasSelectedItems);
+  const isProcessingPayments = useSelector(getWorkPeriodsIsProcessingPayments);
+  const dispatch = useDispatch();
+
+  const onProcessPaymentsClick = useCallback(() => {
+    dispatch(processPayments);
+  }, [dispatch]);
+
+  return (
+    <ContentHeader className={styles.container}>
+      <PageTitle text="Working Periods" />
+      <Button
+        variant="contained"
+        isDisabled={!hasSelectedItems || isProcessingPayments}
+        onClick={onProcessPaymentsClick}
+      >
+        Process Payment
+      </Button>
+    </ContentHeader>
+  );
+};
+
+export default PeriodsContentHeader;
diff --git a/src/routes/WorkPeriods/components/PeriodsContentHeader/styles.module.scss b/src/routes/WorkPeriods/components/PeriodsContentHeader/styles.module.scss
new file mode 100644
index 0000000..b5709b1
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsContentHeader/styles.module.scss
@@ -0,0 +1,6 @@
+.container {
+  position: relative;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
diff --git a/src/routes/WorkPeriods/components/PeriodsHistory/index.jsx b/src/routes/WorkPeriods/components/PeriodsHistory/index.jsx
new file mode 100644
index 0000000..ad283bf
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsHistory/index.jsx
@@ -0,0 +1,41 @@
+import React from "react";
+import { useSelector } from "react-redux";
+import PT from "prop-types";
+import cn from "classnames";
+import PeriodHistoryItem from "../PeriodsHistoryItem";
+import { getWorkPeriodsDateRange } from "store/selectors/workPeriods";
+import styles from "./styles.module.scss";
+
+/**
+ * Displays all working periods' history.
+ *
+ * @param {Object} props component properties
+ * @returns {JSX.Element}
+ */
+const PeriodsHistory = ({ className, isDisabled, periodId, periods }) => {
+  const [startDate] = useSelector(getWorkPeriodsDateRange);
+  return (
+    <div className={cn(styles.container, className)}>
+      <table>
+        {periods.map((period) => (
+          <PeriodHistoryItem
+            key={period.id}
+            periodId={periodId}
+            isDisabled={isDisabled}
+            item={period}
+            currentStartDate={startDate}
+          />
+        ))}
+      </table>
+    </div>
+  );
+};
+
+PeriodsHistory.propTypes = {
+  className: PT.string,
+  isDisabled: PT.bool.isRequired,
+  periodId: PT.string.isRequired,
+  periods: PT.arrayOf(PT.object),
+};
+
+export default PeriodsHistory;
diff --git a/src/routes/WorkPeriods/components/PeriodsHistory/styles.module.scss b/src/routes/WorkPeriods/components/PeriodsHistory/styles.module.scss
new file mode 100644
index 0000000..4f96674
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsHistory/styles.module.scss
@@ -0,0 +1,11 @@
+.container {
+  position: relative;
+  height: 279px;
+  overflow-x: hidden;
+  overflow-y: auto;
+
+  > table {
+    width: 100%;
+    border-collapse: collapse;
+  }
+}
diff --git a/src/routes/WorkPeriods/components/PeriodsHistoryItem/index.jsx b/src/routes/WorkPeriods/components/PeriodsHistoryItem/index.jsx
new file mode 100644
index 0000000..8934dff
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsHistoryItem/index.jsx
@@ -0,0 +1,114 @@
+import React, { memo, useCallback } from "react";
+import { useDispatch } from "react-redux";
+import PT from "prop-types";
+import cn from "classnames";
+import debounce from "lodash/debounce";
+import IntegerField from "components/IntegerField";
+import PaymentStatus from "../PaymentStatus";
+import { PAYMENT_STATUS } from "constants/workPeriods";
+import { setDetailsWorkingDays } from "store/actions/workPeriods";
+import { updateWorkPeriodWorkingDays } from "store/thunks/workPeriods";
+import { useUpdateEffect } from "utils/hooks";
+import {
+  formatDateLabel,
+  formatDateRange,
+  formatWeeklyRate,
+} from "utils/formatters";
+import styles from "./styles.module.scss";
+import PeriodsHistoryWeeklyRate from "../PeriodsHistoryWeeklyRate";
+
+/**
+ * Displays working period row in history table in details view.
+ *
+ * @param {Object} props component properties
+ * @returns {JSX.Element}
+ */
+const PeriodsHistoryItem = ({
+  periodId,
+  isDisabled,
+  item,
+  currentStartDate,
+}) => {
+  const dispatch = useDispatch();
+
+  const dateLabel = formatDateLabel(item.startDate, currentStartDate);
+  const workingDays = item.workingDays;
+
+  const onWorkingDaysChange = useCallback(
+    (workingDays) => {
+      dispatch(setDetailsWorkingDays(periodId, item.id, workingDays));
+    },
+    [dispatch, periodId, item.id]
+  );
+
+  const updateWorkingDays = useCallback(
+    debounce(
+      (workingDays) => {
+        dispatch(updateWorkPeriodWorkingDays(item.id, workingDays));
+      },
+      300,
+      { leading: false }
+    ),
+    [dispatch, item.id]
+  );
+
+  // Update working days on server if working days change.
+  useUpdateEffect(() => {
+    updateWorkingDays(item.workingDays);
+  }, [item.workingDays]);
+
+  return (
+    <tr
+      className={cn(styles.container, {
+        [styles.current]: dateLabel === "Current Period",
+      })}
+    >
+      <td className={styles.dateRange}>
+        {formatDateRange(item.startDate, item.endDate)}
+      </td>
+      <td className={styles.dateLabel}>{dateLabel}</td>
+      <td className={styles.weeklyRate}>
+        <PeriodsHistoryWeeklyRate
+          className={styles.weeklyRateContainer}
+          payments={item.payments}
+          weeklyRate={formatWeeklyRate(item.weeklyRate)}
+        />
+      </td>
+      <td className={styles.paymentStatus}>
+        <PaymentStatus status={item.paymentStatus} />
+      </td>
+      <td className={styles.workingDays}>
+        {item.paymentStatus === PAYMENT_STATUS.PAID ? (
+          `${workingDays} ${workingDays === 1 ? "Day" : "Days"}`
+        ) : (
+          <IntegerField
+            className={styles.workingDaysControl}
+            name={`wp_det_wd_${item.id}`}
+            isDisabled={isDisabled}
+            onChange={onWorkingDaysChange}
+            value={workingDays}
+            maxValue={5}
+            minValue={0}
+          />
+        )}
+      </td>
+    </tr>
+  );
+};
+
+PeriodsHistoryItem.propTypes = {
+  periodId: PT.string.isRequired,
+  isDisabled: PT.bool.isRequired,
+  item: PT.shape({
+    id: PT.string.isRequired,
+    startDate: PT.oneOfType([PT.string, PT.number]).isRequired,
+    endDate: PT.oneOfType([PT.string, PT.number]).isRequired,
+    paymentStatus: PT.string.isRequired,
+    payments: PT.array,
+    weeklyRate: PT.number.isRequired,
+    workingDays: PT.number.isRequired,
+  }).isRequired,
+  currentStartDate: PT.oneOfType([PT.string, PT.number, PT.object]).isRequired,
+};
+
+export default memo(PeriodsHistoryItem);
diff --git a/src/routes/WorkPeriods/components/PeriodsHistoryItem/styles.module.scss b/src/routes/WorkPeriods/components/PeriodsHistoryItem/styles.module.scss
new file mode 100644
index 0000000..27d50dc
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsHistoryItem/styles.module.scss
@@ -0,0 +1,46 @@
+.container {
+  position: relative;
+
+  td {
+    border-top: 1px solid #e9e9e9;
+  }
+
+  &:first-child {
+    td {
+      border-top: none;
+    }
+  }
+}
+
+.dateRange {
+  padding: 8px 12px;
+}
+
+.dateLabel {
+  padding: 8px 12px;
+}
+
+.current {
+  .dateRange,
+  .dateLabel {
+    font-weight: 700;
+  }
+}
+
+.weeklyRate {
+  padding: 6px 12px;
+  line-height: 26px;
+}
+
+.weeklyRateContainer {
+  position: relative;
+}
+
+.workingDays {
+  padding: 4px 10px;
+}
+
+.workingDaysControl {
+  display: block;
+  width: 100px;
+}
diff --git a/src/routes/WorkPeriods/components/PeriodsHistoryWeeklyRate/index.jsx b/src/routes/WorkPeriods/components/PeriodsHistoryWeeklyRate/index.jsx
new file mode 100644
index 0000000..8383ecc
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsHistoryWeeklyRate/index.jsx
@@ -0,0 +1,78 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+import React, { useCallback, useRef, useState } from "react";
+import { usePopper } from "react-popper";
+import PT from "prop-types";
+import cn from "classnames";
+import ChallengePopup from "../PaymentsPopup";
+import compStyles from "./styles.module.scss";
+import { useClickOutside } from "utils/hooks";
+
+const PeriodsHistoryWeeklyRate = ({ className, payments, weeklyRate }) => {
+  const [isShowPopup, setIsShowPopup] = useState(false);
+  const containerRef = useRef();
+
+  const [referenceElement, setReferenceElement] = useState(null);
+  const [popperElement, setPopperElement] = useState(null);
+  const [arrowElement, setArrowElement] = useState(null);
+  const { styles, attributes } = usePopper(referenceElement, popperElement, {
+    placement: "bottom",
+    modifiers: [
+      { name: "arrow", options: { element: arrowElement, padding: 10 } },
+      { name: "offset", options: { offset: [0, 5] } },
+      { name: "preventOverflow", options: { padding: 15 } },
+    ],
+  });
+
+  const onWeeklyRateClick = useCallback(() => {
+    setIsShowPopup(negate);
+  }, []);
+
+  const onClickOutside = useCallback(() => {
+    setIsShowPopup(false);
+  }, []);
+
+  useClickOutside(containerRef, onClickOutside, []);
+
+  const hasPayments = !!payments && !!payments.length;
+
+  return (
+    <div className={cn(compStyles.container, className)} ref={containerRef}>
+      <span
+        className={cn(compStyles.weeklyRateValue, {
+          [compStyles.hasPayments]: hasPayments,
+        })}
+        ref={setReferenceElement}
+        onClick={onWeeklyRateClick}
+      >
+        {weeklyRate}
+      </span>
+      {hasPayments && isShowPopup && (
+        <div
+          className={compStyles.popup}
+          ref={setPopperElement}
+          style={styles.popper}
+          {...attributes.popper}
+        >
+          <ChallengePopup payments={payments} />
+          <div
+            className="dropdown-arrow"
+            ref={setArrowElement}
+            style={styles.arrow}
+          />
+        </div>
+      )}
+    </div>
+  );
+};
+
+PeriodsHistoryWeeklyRate.propTypes = {
+  className: PT.string,
+  payments: PT.array,
+  weeklyRate: PT.string.isRequired,
+};
+
+function negate(value) {
+  return !value;
+}
+
+export default PeriodsHistoryWeeklyRate;
diff --git a/src/routes/WorkPeriods/components/PeriodsHistoryWeeklyRate/styles.module.scss b/src/routes/WorkPeriods/components/PeriodsHistoryWeeklyRate/styles.module.scss
new file mode 100644
index 0000000..8ab6350
--- /dev/null
+++ b/src/routes/WorkPeriods/components/PeriodsHistoryWeeklyRate/styles.module.scss
@@ -0,0 +1,21 @@
+.container {
+  position: relative;
+
+  .dropdown-arrow {
+    display: none;
+  }
+}
+
+.weeklyRateValue {
+  display: inline-block;
+  width: 70px;
+  text-align: right;
+}
+
+.hasPayments {
+  cursor: pointer;
+}
+
+.popup {
+  z-index: 1;
+}
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsError/index.jsx b/src/routes/WorkPeriods/components/ToastPaymentsError/index.jsx
new file mode 100644
index 0000000..03578a8
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsError/index.jsx
@@ -0,0 +1,30 @@
+import React from "react";
+import PT from "prop-types";
+import ToastMessage from "components/ToastrMessage";
+
+/**
+ * Displays a toastr message with info about the number of resources payments
+ * for which have been failed to be scheduled.
+ *
+ * @param {Object} props
+ * @returns {JSX.Element}
+ */
+const ToastPaymentsSuccess = ({ periods, remove }) => {
+  return (
+    <ToastMessage type="error" remove={remove}>
+      Failed to schedule payments for {periods.length} resources
+    </ToastMessage>
+  );
+};
+
+ToastPaymentsSuccess.propTypes = {
+  periods: PT.arrayOf(
+    PT.shape({
+      workPeriodId: PT.string.isRequired,
+      amount: PT.number.isRequired,
+    })
+  ),
+  remove: PT.func,
+};
+
+export default ToastPaymentsSuccess;
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsError/styles.module.scss b/src/routes/WorkPeriods/components/ToastPaymentsError/styles.module.scss
new file mode 100644
index 0000000..4867abe
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsError/styles.module.scss
@@ -0,0 +1,3 @@
+.container {
+  position: relative;
+}
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsProcessing/index.jsx b/src/routes/WorkPeriods/components/ToastPaymentsProcessing/index.jsx
new file mode 100644
index 0000000..78387db
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsProcessing/index.jsx
@@ -0,0 +1,32 @@
+import React from "react";
+import PT from "prop-types";
+import ToastMessage from "components/ToastrMessage";
+import styles from "./styles.module.scss";
+
+/**
+ * Displays a toastr message with info about the number of resources being
+ * processed.
+ *
+ * @param {Object} props
+ * @returns {JSX.Element}
+ */
+const ToastPaymentsProcessing = ({ periods, remove }) => {
+  return (
+    <ToastMessage className={styles.container} remove={remove}>
+      <span className={styles.icon}></span>
+      Payment in progress for {periods.length} resources
+    </ToastMessage>
+  );
+};
+
+ToastPaymentsProcessing.propTypes = {
+  periods: PT.arrayOf(
+    PT.shape({
+      workPeriodId: PT.string.isRequired,
+      amount: PT.number.isRequired,
+    })
+  ),
+  remove: PT.func,
+};
+
+export default ToastPaymentsProcessing;
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsProcessing/styles.module.scss b/src/routes/WorkPeriods/components/ToastPaymentsProcessing/styles.module.scss
new file mode 100644
index 0000000..b934fda
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsProcessing/styles.module.scss
@@ -0,0 +1,16 @@
+.container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.icon {
+  display: inline-block;
+  margin-right: 7px;
+  width: 24px;
+  height: 24px;
+  background-repeat: no-repeat;
+  background-size: contain;
+  background-position: center;
+  background-image: url("./../../../../assets/images/icon-dollar-circled.png");
+}
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsSuccess/index.jsx b/src/routes/WorkPeriods/components/ToastPaymentsSuccess/index.jsx
new file mode 100644
index 0000000..61279aa
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsSuccess/index.jsx
@@ -0,0 +1,30 @@
+import React from "react";
+import PT from "prop-types";
+import ToastMessage from "components/ToastrMessage";
+
+/**
+ * Displays a toastr message with info about the number of resources payments
+ * for which have been scheduled.
+ *
+ * @param {Object} props
+ * @returns {JSX.Element}
+ */
+const ToastPaymentsSuccess = ({ periods, remove }) => {
+  return (
+    <ToastMessage type="success" remove={remove}>
+      Payment scheduled for {periods.length} resources
+    </ToastMessage>
+  );
+};
+
+ToastPaymentsSuccess.propTypes = {
+  periods: PT.arrayOf(
+    PT.shape({
+      workPeriodId: PT.string.isRequired,
+      amount: PT.number.isRequired,
+    })
+  ),
+  remove: PT.func,
+};
+
+export default ToastPaymentsSuccess;
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsSuccess/styles.module.scss b/src/routes/WorkPeriods/components/ToastPaymentsSuccess/styles.module.scss
new file mode 100644
index 0000000..4867abe
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsSuccess/styles.module.scss
@@ -0,0 +1,3 @@
+.container {
+  position: relative;
+}
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsWarning/index.jsx b/src/routes/WorkPeriods/components/ToastPaymentsWarning/index.jsx
new file mode 100644
index 0000000..0297d34
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsWarning/index.jsx
@@ -0,0 +1,58 @@
+import React from "react";
+import PT from "prop-types";
+import ToastMessage from "components/ToastrMessage";
+import styles from "./styles.module.scss";
+
+/**
+ * Displays a toastr message with info about the number of resources for which
+ * payments have been scheduled or failed to schedule.
+ *
+ * @param {Object} props
+ * @returns {JSX.Element}
+ */
+const ToastPaymentsWarning = ({ periodsSucceeded, periodsFailed, remove }) => {
+  return (
+    <ToastMessage type="warning" remove={remove}>
+      Payment scheduled for {periodsSucceeded.length} resources
+      <br />
+      <div className={styles.periodsSucceeded}>
+        {periodsSucceeded.map((period) => (
+          <div key={period.workPeriodId} className={styles.periodSucceeded}>
+            {period.workPeriodId}
+          </div>
+        ))}
+      </div>
+      Failed to schedule payment for {periodsFailed.length} resources:
+      <br />
+      <div className={styles.periodsFailed}>
+        {periodsFailed.map((period) => (
+          <div key={period.workPeriodId} className={styles.periodFailed}>
+            {period.workPeriodId}: ({period.error.code}) {period.error.message}
+          </div>
+        ))}
+      </div>
+    </ToastMessage>
+  );
+};
+
+ToastPaymentsWarning.propTypes = {
+  periodsSucceeded: PT.arrayOf(
+    PT.shape({
+      workPeriodId: PT.string.isRequired,
+      amount: PT.number.isRequired,
+    })
+  ),
+  periodsFailed: PT.arrayOf(
+    PT.shape({
+      workPeriodId: PT.string.isRequired,
+      amount: PT.number.isRequired,
+      error: PT.shape({
+        message: PT.string.isRequired,
+        code: PT.number.isRequired,
+      }),
+    })
+  ),
+  remove: PT.func,
+};
+
+export default ToastPaymentsWarning;
diff --git a/src/routes/WorkPeriods/components/ToastPaymentsWarning/styles.module.scss b/src/routes/WorkPeriods/components/ToastPaymentsWarning/styles.module.scss
new file mode 100644
index 0000000..d0ceca9
--- /dev/null
+++ b/src/routes/WorkPeriods/components/ToastPaymentsWarning/styles.module.scss
@@ -0,0 +1,11 @@
+.container {
+  position: relative;
+}
+
+.periodsSucceeded {
+  margin-bottom: 10px;
+}
+
+.periodFailed {
+  background: #ff7b7b;
+}
diff --git a/src/routes/WorkPeriods/index.jsx b/src/routes/WorkPeriods/index.jsx
index 88a4a18..30f15c9 100644
--- a/src/routes/WorkPeriods/index.jsx
+++ b/src/routes/WorkPeriods/index.jsx
@@ -2,11 +2,9 @@ import React from "react";
 import withAuthentication from "hoc/withAuthentication";
 import Sidebar from "components/Sidebar";
 import Content from "components/Content";
-import ContentHeader from "components/ContentHeader";
 import ContentBlock from "components/ContentBlock";
-import Button from "components/Button";
 import Page from "components/Page";
-import PageTitle from "components/PageTitle";
+import PeriodsContentHeader from "./components/PeriodsContentHeader";
 import PeriodFilters from "./components/PeriodFilters";
 import Periods from "./components/Periods";
 import PeriodCount from "./components/PeriodCount";
@@ -26,12 +24,7 @@ const WorkPeriods = () => (
       <PeriodFilters />
     </Sidebar>
     <Content>
-      <ContentHeader className={styles.contentHeader}>
-        <PageTitle text="Working Periods" />
-        <Button variant="contained" onClick={() => {}}>
-          Process Payment
-        </Button>
-      </ContentHeader>
+      <PeriodsContentHeader />
       <ContentBlock>
         <div className={styles.periodsHeader}>
           <PeriodCount className={styles.periodCount} />
diff --git a/src/routes/WorkPeriods/styles.module.scss b/src/routes/WorkPeriods/styles.module.scss
index bedfd27..3a46a12 100644
--- a/src/routes/WorkPeriods/styles.module.scss
+++ b/src/routes/WorkPeriods/styles.module.scss
@@ -1,12 +1,6 @@
 .container {
 }
 
-.contentHeader {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
 .periodsHeader {
   display: flex;
   justify-content: space-between;
diff --git a/src/services/axios.js b/src/services/axios.js
index 5a7cc78..d89223f 100644
--- a/src/services/axios.js
+++ b/src/services/axios.js
@@ -2,6 +2,8 @@ import axios from "axios";
 import get from "lodash/get";
 import { getAuthUserTokens } from "@topcoder/micro-frontends-navbar-app";
 
+export const CancelToken = axios.CancelToken;
+
 const axiosInstance = axios.create({
   headers: { "Content-Type": "application/json" },
 });
diff --git a/src/services/teams.js b/src/services/teams.js
new file mode 100644
index 0000000..1ffede2
--- /dev/null
+++ b/src/services/teams.js
@@ -0,0 +1,13 @@
+import axios from "./axios";
+import config from "../../config";
+
+/**
+ * Get member suggestions
+ *
+ * @param {string} fragment text for suggestions
+ *
+ * @returns {Promise}
+ */
+export const getMemberSuggestions = (fragment) => {
+  return axios.get(`${config.API.V3}/members/_suggest/${fragment}`);
+};
diff --git a/src/services/workPeriods.js b/src/services/workPeriods.js
index 1bffc8c..9a85245 100644
--- a/src/services/workPeriods.js
+++ b/src/services/workPeriods.js
@@ -1,9 +1,76 @@
-import axiosDefault from "axios";
-import axios from "./axios";
-import { API_URL, QUERY_PARAM_NAMES } from "constants/workPeriods";
-import { buildRequestQuery } from "utils/misc";
+import axios, { CancelToken } from "./axios";
+import {
+  RB_API_URL,
+  JOBS_API_URL,
+  PAYMENTS_API_URL,
+  PROJECTS_API_URL,
+  QUERY_PARAM_NAMES,
+  WORK_PERIODS_API_URL,
+} from "constants/workPeriods";
+import { buildRequestQuery, extractResponseData } from "utils/misc";
 
-const CancelToken = axiosDefault.CancelToken;
+/**
+ * Fetches job name by job id.
+ *
+ * @param {number|string} jobId job id
+ * @param {Object} [source] axios cancel token source
+ * @returns {[Promise, Object]}
+ */
+export const fetchJob = (jobId, source) => {
+  if (!source) {
+    source = CancelToken.source();
+  }
+  return [
+    axios
+      .get(`${JOBS_API_URL}/${jobId}`, {
+        cancelToken: source.token,
+      })
+      .then(extractResponseData),
+    source,
+  ];
+};
+
+/**
+ * Fetches billing accounts for specific project id.
+ *
+ * @param {number|string} projectId resource booking's project id
+ * @param {Object} [source] axios cancel token source
+ * @returns {[Promise, Object]}
+ */
+export const fetchBillingAccounts = (projectId, source) => {
+  if (!source) {
+    source = CancelToken.source();
+  }
+  return [
+    axios
+      .get(`${PROJECTS_API_URL}/${projectId}/billingAccounts`, {
+        cancelToken: source.token,
+      })
+      .then(extractResponseData),
+    source,
+  ];
+};
+
+/**
+ * Fetches working periods for specific resource booking.
+ *
+ * @param {string} rbId ResourceBooking id
+ * @param {Object} [source] optioinal cancel token source
+ * @returns {[Promise, Object]}
+ */
+export const fetchWorkPeriods = (rbId, source) => {
+  if (!source) {
+    source = CancelToken.source();
+  }
+  return [
+    axios
+      .get(`${WORK_PERIODS_API_URL}/?resourceBookingIds=${rbId}`, {
+        cancelToken: source.token,
+      })
+      .then(extractResponseData),
+    source,
+  ];
+};
 
 /**
  * Fetches working periods using provided parameters.
@@ -14,9 +81,42 @@ const CancelToken = axiosDefault.CancelToken;
 export const fetchResourceBookings = (params) => {
   const source = CancelToken.source();
   return [
-    axios.get(`${API_URL}?${buildRequestQuery(params, QUERY_PARAM_NAMES)}`, {
+    axios.get(`${RB_API_URL}?${buildRequestQuery(params, QUERY_PARAM_NAMES)}`, {
       cancelToken: source.token,
     }),
     source,
   ];
 };
+
+/**
+ * Updates working period's working days.
+ *
+ * @param {string} periodId working period id
+ * @param {number} daysWorked new number of working days
+ * @returns {Promise}
+ */
+export const patchWorkPeriodWorkingDays = (periodId, daysWorked) => {
+  return axios.patch(`${WORK_PERIODS_API_URL}/${periodId}`, { daysWorked });
+};
+
+/**
+ * Updates billing account id for resource booking with the specified id.
+ *
+ * @param {string} rbId resource booking id
+ * @param {number} billingAccountId billing account id
+ * @returns {Promise}
+ */
+export const patchWorkPeriodBillingAccount = (rbId, billingAccountId) => {
+  return axios.patch(`${RB_API_URL}/${rbId}`, { billingAccountId });
+};
+
+/**
+ * Sends request to queue payments for specific working periods and amounts
+ * inside the provided array.
+ *
+ * @param {Array} payments
+ * @returns {Promise}
+ */
+export const postWorkPeriodsPayments = (payments) => {
+  return axios.post(`${PAYMENTS_API_URL}`, payments).then(extractResponseData);
+};
diff --git a/src/store/actionTypes/workPeriods.js b/src/store/actionTypes/workPeriods.js
index 4f38629..064a7ae 100644
--- a/src/store/actionTypes/workPeriods.js
+++ b/src/store/actionTypes/workPeriods.js
@@ -1,7 +1,23 @@
 export const WP_LOAD_PAGE_PENDING = "WP_LOAD_PAGE_PENDING";
 export const WP_LOAD_PAGE_SUCCESS = "WP_LOAD_PAGE_SUCCESS";
 export const WP_LOAD_PAGE_ERROR = "WP_LOAD_PAGE_ERROR";
+export const WP_HIDE_PERIOD_DETAILS = "WP_HIDE_PERIOD_DETAILS";
+export const WP_LOAD_PERIOD_DETAILS_PENDING = "WP_LOAD_PERIOD_DETAILS_PENDING";
+export const WP_LOAD_PERIOD_DETAILS_ERROR = "WP_LOAD_PERIOD_DETAILS_ERROR";
+export const WP_LOAD_PERIOD_DETAILS_SUCCESS = "WP_LOAD_PERIOD_DETAILS_SUCCESS";
+export const WP_LOAD_JOB_NAME_ERROR = "WP_LOAD_JOB_NAME_ERROR";
+export const WP_LOAD_JOB_NAME_SUCCESS = "WP_LOAD_JOB_NAME_SUCCESS";
+export const WP_LOAD_BILLING_ACCOUNTS_ERROR = "WP_LOAD_BILLING_ACCOUNTS_ERROR";
+export const WP_LOAD_BILLING_ACCOUNTS_SUCCESS =
+  "WP_LOAD_BILLING_ACCOUNTS_SUCCESS";
 export const WP_RESET_FILTERS = "WP_RESET_FILTERS";
+export const WP_SELECT_PERIODS = "WP_SELECT_PERIODS";
+export const WP_SET_BILLING_ACCOUNT = "WP_SET_BILLING_ACCOUNT";
+export const WP_SET_DETAILS_WORKING_DAYS = "WP_SET_DETAILS_WORKING_DAYS";
+export const WP_SET_DETAILS_HIDE_PAST_PERIODS =
+  "WP_SET_DETAILS_HIDE_PAST_PERIODS";
+export const WP_SET_DETAILS_LOCK_WORKING_DAYS =
+  "WP_SET_DETAILS_LOCK_WORKING_DAYS";
 export const WP_SET_PAGE_NUMBER = "WP_SET_PAGE_NUMBER";
 export const WP_SET_PAGE_SIZE = "WP_SET_PAGE_SIZE";
 export const WP_SET_DATE_RANGE = "WP_SET_DATE_RANGE";
@@ -14,3 +30,4 @@ export const WP_SET_WORKING_DAYS = "WP_SET_WORKING_DAYS";
 export const WP_TOGGLE_PERIOD = "WP_TOGGLE_PERIOD";
 export const WP_TOGGLE_PERIODS_ALL = "WP_TOGGLE_PERIODS_ALL";
 export const WP_TOGGLE_PERIODS_VISIBLE = "WP_TOGGLE_PERIODS_VISIBLE";
+export const WP_TOGGLE_PROCESSING_PAYMENTS = "WP_TOGGLE_PROCESSING_PAYMENTS";
diff --git a/src/store/actions/workPeriods.js b/src/store/actions/workPeriods.js
index dcfd5e3..a57ad21 100644
--- a/src/store/actions/workPeriods.js
+++ b/src/store/actions/workPeriods.js
@@ -16,7 +16,7 @@ export const loadWorkPeriodsPagePending = (cancelSource, pageNumber) => ({
 });
 
 /**
- * Creates an action denoting the saving of fetched challenge page.
+ * Creates an action denoting the saving of fetched working periods' page.
  *
  * @param {Array} periods array of challenge objects
  * @param {number} totalCount total number of periods for current filters' state
@@ -29,14 +29,165 @@ export const loadWorkPeriodsPageSuccess = (periods, totalCount, pageCount) => ({
 });
 
 /**
- * Creates an action denoting the occurrence of an error while loading challenges.
+ * Creates an action denoting the occurrence of an error while loading working
+ * periods.
  *
  * @param {string} message error message
  * @returns {Object}
  */
 export const loadWorkPeriodsPageError = (message) => ({
   type: ACTION_TYPE.WP_LOAD_PAGE_ERROR,
-  payload: { id: nextErrorId++, message },
+  payload: { message, id: nextErrorId++ },
+});
+
+/**
+ * Creates an action to hide specific working period details.
+ *
+ * @param {string} periodId working period id
+ * @returns {Object}
+ */
+export const hideWorkPeriodDetails = (periodId) => ({
+  type: ACTION_TYPE.WP_HIDE_PERIOD_DETAILS,
+  payload: periodId,
+});
+
+/**
+ * Creates an action denoting the loading of working period's details.
+ *
+ * @param {string} periodId working period id
+ * @param {string} rbId resource booking id
+ * @param {number} billingAccountId billing account id
+ * @param {Object} cancelSource axios cancel token source
+ * @returns {Object}
+ */
+export const loadWorkPeriodDetailsPending = (
+  periodId,
+  rbId,
+  billingAccountId,
+  cancelSource
+) => ({
+  type: ACTION_TYPE.WP_LOAD_PERIOD_DETAILS_PENDING,
+  payload: { periodId, rbId, billingAccountId, cancelSource },
+});
+
+/**
+ * Creates an action denoting successful loading of working period details.
+ *
+ * @param {string} periodId working period id
+ * @param {Object} details working period details object
+ * @returns {Object}
+ */
+export const loadWorkPeriodDetailsSuccess = (periodId, details) => ({
+  type: ACTION_TYPE.WP_LOAD_PERIOD_DETAILS_SUCCESS,
+  payload: { periodId, details },
+});
+
+/**
+ * Creates an action denoting the occurrence of an error while loading
+ * working period details.
+ *
+ * @param {string} periodId work period id
+ * @param {string} message error message
+ * @returns {Object}
+ */
+export const loadWorkPeriodDetailsError = (periodId, message) => ({
+  type: ACTION_TYPE.WP_LOAD_PERIOD_DETAILS_ERROR,
+  payload: { periodId, message, id: nextErrorId++ },
+});
+
+/**
+ * Creates an action denoting successful loading of resource booking's job name.
+ *
+ * @param {string} periodId working period id
+ * @param {string} jobName working period job name
+ * @returns {Object}
+ */
+export const loadJobNameSuccess = (periodId, jobName) => ({
+  type: ACTION_TYPE.WP_LOAD_JOB_NAME_SUCCESS,
+  payload: { periodId, jobName },
+});
+
+/**
+ * Creates an action denoting an error for loading resource booking's job name.
+ *
+ * @param {string} periodId working period id
+ * @param {string} message error message
+ * @returns {Object}
+ */
+export const loadJobNameError = (periodId, message) => ({
+  type: ACTION_TYPE.WP_LOAD_JOB_NAME_ERROR,
+  payload: { periodId, message, id: nextErrorId++ },
+});
+
+/**
+ * Creates an action denoting successful load of billing accounts.
+ *
+ * @param {string} periodId working period id
+ * @param {Array} accounts billing accounts
+ * @returns {Object}
+ */
+export const loadBillingAccountsSuccess = (periodId, accounts) => ({
+  type: ACTION_TYPE.WP_LOAD_BILLING_ACCOUNTS_SUCCESS,
+  payload: { periodId, accounts },
+});
+
+/**
+ * Creates an action denoting an error while loading billing accounts.
+ *
+ * @param {string} periodId working period id
+ * @param {string} message error message
+ * @returns {Object}
+ */
+export const loadBillingAccountsError = (periodId, message) => ({
+  type: ACTION_TYPE.WP_LOAD_BILLING_ACCOUNTS_ERROR,
+  payload: { periodId, message, id: nextErrorId++ },
+});
+
+/**
+ * Creates an action denoting the change of billing account.
+ *
+ * @param {string} periodId working period id
+ * @param {number|string} accountId billing account id
+ * @returns {Object}
+ */
+export const setBillingAccount = (periodId, accountId) => ({
+  type: ACTION_TYPE.WP_SET_BILLING_ACCOUNT,
+  payload: { periodId, accountId },
+});
+
+/**
+ * Creates an action denoting the change of working period's working days in
+ * details view.
+ *
+ * @param {string} parentPeriodId parent working period id
+ * @param {string} periodId working period id
+ * @param {number} workingDays number of working days
+ * @returns {Object}
+ */
+export const setDetailsWorkingDays = (
+  parentPeriodId,
+  periodId,
+  workingDays
+) => ({
+  type: ACTION_TYPE.WP_SET_DETAILS_WORKING_DAYS,
+  payload: { parentPeriodId, periodId, workingDays },
+});
+
+/**
+ * Creates an action denoting the hiding or showing past working periods.
+ *
+ * @param {string} periodId working period id
+ * @param {boolean} hide whether to hide or show past working periods
+ * @returns {Object}
+ */
+export const setDetailsHidePastPeriods = (periodId, hide) => ({
+  type: ACTION_TYPE.WP_SET_DETAILS_HIDE_PAST_PERIODS,
+  payload: { periodId, hide },
+});
+
+export const setDetailsLockWorkingDays = (periodId, lock) => ({
+  type: ACTION_TYPE.WP_SET_DETAILS_LOCK_WORKING_DAYS,
+  payload: { periodId, lock },
 });
 
 /**
@@ -48,6 +199,18 @@ export const resetWorkPeriodsFilters = () => ({
   type: ACTION_TYPE.WP_RESET_FILTERS,
 });
 
+/**
+ * Creates an action denoting the selection/deselection of specified
+ * working periods.
+ *
+ * @param {Object} periods object with period ids as keys and booleans as values
+ * @returns {Object}
+ */
+export const selectWorkPeriods = (periods) => ({
+  type: ACTION_TYPE.WP_SELECT_PERIODS,
+  payload: periods,
+});
+
 /**
  * Creates an action denoting the changing of working periods' page number.
  *
@@ -181,3 +344,14 @@ export const toggleWorkingPeriodsAll = () => ({
 export const toggleWorkingPeriodsVisible = () => ({
   type: ACTION_TYPE.WP_TOGGLE_PERIODS_VISIBLE,
 });
+
+/**
+ * Creates an action denoting the change of processing-payments state.
+ *
+ * @param {?boolean} on whether to turn processing-payments state on or off
+ * @returns {Object}
+ */
+export const toggleWorkPeriodsProcessingPeyments = (on = null) => ({
+  type: ACTION_TYPE.WP_TOGGLE_PROCESSING_PAYMENTS,
+  payload: on,
+});
diff --git a/src/store/reducers/workPeriods.js b/src/store/reducers/workPeriods.js
index 25b1089..891c876 100644
--- a/src/store/reducers/workPeriods.js
+++ b/src/store/reducers/workPeriods.js
@@ -5,7 +5,11 @@ import {
   SORT_ORDER_DEFAULT,
   PAYMENT_STATUS,
 } from "constants/workPeriods";
-import { getWeekByDate, updateOptionMap } from "utils/misc";
+import {
+  filterPeriodsByStartDate,
+  getWeekByDate,
+  updateOptionMap,
+} from "utils/misc";
 
 const initPagination = () => ({
   totalCount: 0,
@@ -24,13 +28,38 @@ const initFilters = () => ({
   userHandle: "",
 });
 
+const cancelSourceDummy = { cancel: () => {} };
+
+const initPeriodDetails = (
+  periodId,
+  rbId,
+  billingAccountId = 0,
+  cancelSource = cancelSourceDummy
+) => ({
+  periodId,
+  rbId,
+  cancelSource,
+  jobName: "Loading...",
+  jobNameIsLoading: true,
+  billingAccountId,
+  billingAccounts: [{ value: billingAccountId, label: "Loading..." }],
+  billingAccountsIsLoading: true,
+  periods: [],
+  periodsVisible: [],
+  periodsIsLoading: true,
+  hidePastPeriods: false,
+  lockWorkingDays: false,
+});
+
 const initialState = {
   error: null,
-  cancelSource: { cancel: () => {} },
+  cancelSource: cancelSourceDummy,
   periods: [],
+  periodsDetails: {},
   periodsSelected: {},
   isSelectedPeriodsAll: false,
   isSelectedPeriodsVisible: false,
+  isProcessingPayments: false,
   pagination: initPagination(),
   sorting: {
     criteria: SORT_BY_DEFAULT,
@@ -55,6 +84,8 @@ const actionHandlers = {
     cancelSource,
     error: null,
     periods: [],
+    periodsDetails: {},
+    periodsSelected: {},
     pagination:
       pageNumber === state.pagination.pageNumber
         ? state.pagination
@@ -87,6 +118,255 @@ const actionHandlers = {
       periods: [],
     };
   },
+  [ACTION_TYPE.WP_HIDE_PERIOD_DETAILS]: (state, periodId) => {
+    const periodsDetails = { ...state.periodsDetails };
+    delete periodsDetails[periodId];
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_LOAD_PERIOD_DETAILS_PENDING]: (
+    state,
+    { periodId, rbId, billingAccountId, cancelSource }
+  ) => {
+    const periodsDetails = { ...state.periodsDetails };
+    periodsDetails[periodId] = initPeriodDetails(
+      periodId,
+      rbId,
+      billingAccountId,
+      cancelSource
+    );
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_LOAD_PERIOD_DETAILS_SUCCESS]: (
+    state,
+    { periodId, details }
+  ) => {
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[periodId];
+    // period details object must already be initialized
+    if (!periodDetails) {
+      // This branch should not be reachable but just in case.
+      return state;
+    }
+    periodDetails = {
+      ...periodDetails,
+      periods: details.periods,
+      periodsIsLoading: false,
+    };
+    if (periodDetails.hidePastPeriods) {
+      periodDetails.periodsVisible = filterPeriodsByStartDate(
+        periodDetails.periods,
+        state.filters.dateRange[0]
+      );
+    } else {
+      periodDetails.periodsVisible = periodDetails.periods;
+    }
+    periodsDetails[periodId] = periodDetails;
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_LOAD_PERIOD_DETAILS_ERROR]: (
+    state,
+    { periodId, message }
+  ) => {
+    const periodsDetails = { ...state.periodsDetails };
+    // No periods to show so we just hide period details.
+    delete periodsDetails[periodId];
+    console.error(message);
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_LOAD_JOB_NAME_SUCCESS]: (state, { periodId, jobName }) => {
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[periodId];
+    if (!periodDetails) {
+      // Period details may be removed at this point so we must handle this case.
+      return state;
+    }
+    periodDetails = { ...periodDetails, jobName, jobNameIsLoading: false };
+    if (!periodDetails.billingAccountsIsLoading) {
+      periodDetails.cancelSource = null;
+    }
+    periodsDetails[periodId] = periodDetails;
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_LOAD_JOB_NAME_ERROR]: (state, { periodId, message }) => {
+    console.error(message);
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[periodId];
+    if (!periodDetails) {
+      return state;
+    }
+    periodDetails = {
+      ...periodDetails,
+      jobName: "Error",
+      jobNameIsLoading: false,
+    };
+    if (!periodDetails.billingAccountsIsLoading) {
+      periodDetails.cancelSource = null;
+    }
+    periodsDetails[periodId] = periodDetails;
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_LOAD_BILLING_ACCOUNTS_SUCCESS]: (
+    state,
+    { periodId, accounts }
+  ) => {
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[periodId];
+    if (!periodDetails) {
+      // Period details may be removed at this point so we must handle this case.
+      return state;
+    }
+    let billingAccountId = periodDetails.billingAccountId;
+    if (!accounts.length) {
+      accounts.push({ value: -1, label: "No Accounts Available" });
+      billingAccountId = -1;
+    }
+    periodDetails = {
+      ...periodDetails,
+      billingAccountId,
+      billingAccounts: accounts,
+      billingAccountsIsLoading: false,
+    };
+    if (!periodDetails.jobNameIsLoading) {
+      periodDetails.cancelSource = null;
+    }
+    periodsDetails[periodId] = periodDetails;
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_LOAD_BILLING_ACCOUNTS_ERROR]: (
+    state,
+    { periodId, message }
+  ) => {
+    console.error(message);
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[periodId];
+    if (!periodDetails) {
+      return state;
+    }
+    periodDetails = {
+      ...periodDetails,
+      billingAccounts: [
+        { value: periodDetails.billingAccountId, label: "Error" },
+      ],
+      billingAccountsIsLoading: false,
+    };
+    if (!periodDetails.jobNameIsLoading) {
+      periodDetails.cancelSource = null;
+    }
+    periodsDetails[periodId] = periodDetails;
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_SET_BILLING_ACCOUNT]: (state, { periodId, accountId }) => {
+    const periodsDetails = { ...state.periodsDetails };
+    const periodDetails = periodsDetails[periodId];
+    if (!periodDetails) {
+      return state;
+    }
+    periodsDetails[periodId] = {
+      ...periodDetails,
+      billingAccountId: accountId,
+    };
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_SET_DETAILS_HIDE_PAST_PERIODS]: (
+    state,
+    { periodId, hide }
+  ) => {
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[periodId];
+    if (!periodDetails) {
+      return state;
+    }
+    periodDetails = { ...periodDetails, hidePastPeriods: hide };
+    if (hide) {
+      periodDetails.periodsVisible = filterPeriodsByStartDate(
+        periodDetails.periods,
+        state.filters.dateRange[0]
+      );
+    } else {
+      periodDetails.periodsVisible = periodDetails.periods;
+    }
+    periodsDetails[periodId] = periodDetails;
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_SET_DETAILS_LOCK_WORKING_DAYS]: (
+    state,
+    { periodId, lock }
+  ) => {
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[periodId];
+    if (!periodDetails) {
+      return state;
+    }
+    periodsDetails[periodId] = { ...periodDetails, lockWorkingDays: lock };
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
+  [ACTION_TYPE.WP_SET_DETAILS_WORKING_DAYS]: (
+    state,
+    { parentPeriodId, periodId, workingDays }
+  ) => {
+    const periodsDetails = { ...state.periodsDetails };
+    let periodDetails = periodsDetails[parentPeriodId];
+    if (!periodDetails) {
+      return state;
+    }
+    workingDays = Math.min(Math.max(workingDays, 0), 5);
+    const periods = [];
+    for (let period of periodDetails.periods) {
+      if (period.id === periodId) {
+        period = { ...period, workingDays };
+      }
+      periods.push(period);
+    }
+    const periodsVisible = [];
+    for (let period of periodDetails.periodsVisible) {
+      if (period.id === periodId) {
+        period = { ...period, workingDays };
+      }
+      periodsVisible.push(period);
+    }
+    periodsDetails[parentPeriodId] = {
+      ...periodDetails,
+      periods,
+      periodsVisible,
+    };
+    return {
+      ...state,
+      periodsDetails,
+    };
+  },
   [ACTION_TYPE.WP_RESET_FILTERS]: (state) => ({
     ...state,
     filters: initFilters(),
@@ -105,6 +385,20 @@ const actionHandlers = {
       },
     };
   },
+  [ACTION_TYPE.WP_SELECT_PERIODS]: (state, periods) => {
+    let periodsSelected = { ...state.periodsSelected };
+    for (let periodId in periods) {
+      if (periods[periodId] === true) {
+        periodsSelected[periodId] = true;
+      } else {
+        delete periodsSelected[periodId];
+      }
+    }
+    return {
+      ...state,
+      periodsSelected,
+    };
+  },
   [ACTION_TYPE.WP_SET_PAGE_NUMBER]: (state, pageNumber) => ({
     ...state,
     pagination:
@@ -226,6 +520,10 @@ const actionHandlers = {
       isSelectedPeriodsVisible: isSelected,
     };
   },
+  [ACTION_TYPE.WP_TOGGLE_PROCESSING_PAYMENTS]: (state, on) => ({
+    ...state,
+    isProcessingPayments: on === null ? !state.isProcessingPayments : on,
+  }),
 };
 
 export default reducer;
diff --git a/src/store/selectors/workPeriods.js b/src/store/selectors/workPeriods.js
index 9ffcfa0..3719420 100644
--- a/src/store/selectors/workPeriods.js
+++ b/src/store/selectors/workPeriods.js
@@ -14,6 +14,15 @@ export const getWorkPeriodsStateSlice = (state) => state.workPeriods;
  */
 export const getWorkPeriods = (state) => state.workPeriods.periods;
 
+/**
+ * Returns working periods' details.
+ *
+ * @param {Object} state redux root state
+ * @returns {Object}
+ */
+export const getWorkPeriodsDetails = (state) =>
+  state.workPeriods.periodsDetails;
+
 /**
  * Returns an object with working periods' ids as keys and booleans showing
  * whether the period is selected as values.
@@ -46,9 +55,20 @@ export const getWorkPeriodsCount = (state) => state.workPeriods.periods.length;
 export const getWorkPeriodsTotalCount = (state) =>
   state.workPeriods.pagination.totalCount;
 
+export const getWorkPeriodsHasSelectedItems = (state) => {
+  const periodsSelected = state.workPeriods.periodsSelected;
+  for (let id in periodsSelected) {
+    return true;
+  }
+  return false;
+};
+
 export const getWorkPeriodsIsLoading = (state) =>
   !!state.workPeriods.cancelSource;
 
+export const getWorkPeriodsIsProcessingPayments = (state) =>
+  state.workPeriods.isProcessingPayments;
+
 export const getWorkPeriodsIsSelectedAll = (state) =>
   state.workPeriods.isSelectedPeriodsAll;
 
diff --git a/src/store/thunks/workPeriods.js b/src/store/thunks/workPeriods.js
deleted file mode 100644
index 29c0c38..0000000
--- a/src/store/thunks/workPeriods.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import axios from "axios";
-import * as actions from "store/actions/workPeriods";
-import * as selectors from "store/selectors/workPeriods";
-import * as services from "services/workPeriods";
-import {
-  SORT_BY_MAP,
-  API_SORT_BY,
-  DATE_FORMAT,
-  PAYMENT_STATUS_MAP,
-  FIELDS_QUERY,
-} from "constants/workPeriods";
-import {
-  extractResponseData,
-  extractResponsePagination,
-  replaceItems,
-} from "utils/misc";
-import { normalizePeriodItems } from "utils/workPeriods";
-import { RESOURCE_BOOKING_STATUS } from "constants/index.js";
-
-/**
- * Thunk that loads the specified working periods' page. If page number is not
- * provided the current page number from current state is used. All relevant
- * working period filters are loaded from the current state to construct
- * a request query.
- *
- * @param {number} [pageNumber] page number to load
- * @returns {function}
- */
-export const loadWorkPeriodsPage =
-  (pageNumber) => async (dispatch, getState) => {
-    const workPeriods = selectors.getWorkPeriodsStateSlice(getState());
-    if (workPeriods.cancelSource) {
-      // If there's an ongoing request we just cancel it since the data that comes
-      // with its response will not correspond to application's current state,
-      // namely filters and sorting.
-      workPeriods.cancelSource.cancel();
-    }
-    const { filters, sorting, pagination } = workPeriods;
-
-    // If page number is not specified get it from current state.
-    pageNumber = pageNumber || pagination.pageNumber;
-
-    const sortOrder = sorting.order;
-    const sortBy = SORT_BY_MAP[sorting.criteria] || API_SORT_BY.USER_HANDLE;
-
-    const [startDate] = filters.dateRange;
-    const paymentStatuses = replaceItems(
-      Object.keys(filters.paymentStatuses),
-      PAYMENT_STATUS_MAP
-    );
-
-    // For parameter description see:
-    // https://topcoder-platform.github.io/taas-apis/#/ResourceBookings/get_resourceBookings
-    const [promise, cancelSource] = services.fetchResourceBookings({
-      fields: FIELDS_QUERY,
-      page: pageNumber,
-      perPage: pagination.pageSize,
-      sortBy,
-      sortOrder,
-      // we only want to show Resource Bookings with status "placed"
-      status: RESOURCE_BOOKING_STATUS.PLACED,
-      ["workPeriods.userHandle"]: filters.userHandle,
-      ["workPeriods.startDate"]: startDate.format(DATE_FORMAT),
-      ["workPeriods.paymentStatus"]:
-        // Currently resourceBookings API does not support multiple payment statuses.
-        // When the support is implemented remove the next line and uncomment
-        // the following line.
-        paymentStatuses.length === 1 ? paymentStatuses[0] : null,
-      // paymentStatuses,
-    });
-    dispatch(actions.loadWorkPeriodsPagePending(cancelSource, pageNumber));
-    let totalCount, periods, pageCount;
-    try {
-      const response = await promise;
-      ({ totalCount, pageNumber, pageCount } =
-        extractResponsePagination(response));
-      const data = extractResponseData(response);
-      periods = normalizePeriodItems(data);
-    } catch (error) {
-      // If request was cancelled by the next call to loadWorkPeriodsPage
-      // there's nothing more to do.
-      if (!axios.isCancel(error)) {
-        dispatch(actions.loadWorkPeriodsPageError(error.toString()));
-      }
-      return;
-    }
-    dispatch(
-      actions.loadWorkPeriodsPageSuccess(periods, totalCount, pageCount)
-    );
-  };
diff --git a/src/store/thunks/workPeriods.jsx b/src/store/thunks/workPeriods.jsx
new file mode 100644
index 0000000..2db85ff
--- /dev/null
+++ b/src/store/thunks/workPeriods.jsx
@@ -0,0 +1,336 @@
+import React from "react";
+import axios from "axios";
+import { toastr } from "react-redux-toastr";
+import ToastrMessage from "components/ToastrMessage";
+import ToastPaymentsProcessing from "routes/WorkPeriods/components/ToastPaymentsProcessing";
+import ToastPaymentsSuccess from "routes/WorkPeriods/components/ToastPaymentsSuccess";
+import ToastPaymentsWarning from "routes/WorkPeriods/components/ToastPaymentsWarning";
+import ToastPaymentsError from "routes/WorkPeriods/components/ToastPaymentsError";
+import * as actions from "store/actions/workPeriods";
+import * as selectors from "store/selectors/workPeriods";
+import * as services from "services/workPeriods";
+import {
+  SORT_BY_MAP,
+  API_SORT_BY,
+  DATE_FORMAT_API,
+  PAYMENT_STATUS_MAP,
+  FIELDS_QUERY,
+} from "constants/workPeriods";
+import {
+  extractJobName,
+  extractResponseData,
+  extractResponsePagination,
+  replaceItems,
+} from "utils/misc";
+import {
+  normalizeBillingAccounts,
+  normalizeDetailsPeriodItems,
+  normalizePeriodItems,
+} from "utils/workPeriods";
+import { RESOURCE_BOOKING_STATUS } from "constants/index.js";
+
+/**
+ * Thunk that loads the specified working periods' page. If page number is not
+ * provided the current page number from current state is used. All relevant
+ * working period filters are loaded from the current state to construct
+ * a request query.
+ *
+ * @param {number} [pageNumber] page number to load
+ * @returns {function}
+ */
+export const loadWorkPeriodsPage =
+  (pageNumber) => async (dispatch, getState) => {
+    const workPeriods = selectors.getWorkPeriodsStateSlice(getState());
+    if (workPeriods.cancelSource) {
+      // If there's an ongoing request we just cancel it since the data that comes
+      // with its response will not correspond to application's current state,
+      // namely filters and sorting.
+      workPeriods.cancelSource.cancel();
+    }
+    const { filters, sorting, pagination } = workPeriods;
+
+    // If page number is not specified get it from current state.
+    pageNumber = pageNumber || pagination.pageNumber;
+
+    const sortOrder = sorting.order;
+    const sortBy = SORT_BY_MAP[sorting.criteria] || API_SORT_BY.USER_HANDLE;
+
+    const [startDate] = filters.dateRange;
+    const paymentStatuses = replaceItems(
+      Object.keys(filters.paymentStatuses),
+      PAYMENT_STATUS_MAP
+    );
+
+    // For parameter description see:
+    // https://topcoder-platform.github.io/taas-apis/#/ResourceBookings/get_resourceBookings
+    const [promise, cancelSource] = services.fetchResourceBookings({
+      fields: FIELDS_QUERY,
+      page: pageNumber,
+      perPage: pagination.pageSize,
+      sortBy,
+      sortOrder,
+      // we only want to show Resource Bookings with status "placed"
+      status: RESOURCE_BOOKING_STATUS.PLACED,
+      ["workPeriods.userHandle"]: filters.userHandle,
+      ["workPeriods.startDate"]: startDate.format(DATE_FORMAT_API),
+      ["workPeriods.paymentStatus"]:
+        // Currently resourceBookings API does not support multiple payment statuses.
+        // When the support is implemented remove the next line and uncomment
+        // the following line.
+        paymentStatuses.length === 1 ? paymentStatuses[0] : null,
+      // paymentStatuses,
+    });
+    dispatch(actions.loadWorkPeriodsPagePending(cancelSource, pageNumber));
+    let totalCount, periods, pageCount;
+    try {
+      const response = await promise;
+      ({ totalCount, pageNumber, pageCount } =
+        extractResponsePagination(response));
+      const data = extractResponseData(response);
+      periods = normalizePeriodItems(data);
+    } catch (error) {
+      // If request was cancelled by the next call to loadWorkPeriodsPage
+      // there's nothing more to do.
+      if (!axios.isCancel(error)) {
+        dispatch(actions.loadWorkPeriodsPageError(error.toString()));
+      }
+      return;
+    }
+    dispatch(
+      actions.loadWorkPeriodsPageSuccess(periods, totalCount, pageCount)
+    );
+  };
+
+/**
+ * Thunk that either loads and displays or hides the details of the specified
+ * working period.
+ *
+ * @param {Object} period working period object
+ * @param {string} period.id working period id
+ * @param {string} period.rbId resource booking id
+ * @param {number|string} period.projectId resource booking's project id
+ * @param {number|string} period.jobId resource booking's job id
+ * @param {number} period.billingAccountId billing account id
+ * @param {?boolean} [show] whether to show or hide working period details
+ * @returns {function}
+ */
+export const toggleWorkPeriodDetails =
+  (period, show = null) =>
+  async (dispatch, getState) => {
+    const periodsDetails = selectors.getWorkPeriodsDetails(getState());
+    const periodDetails = periodsDetails[period.id];
+    // If there's an ongoing request to load details for specified working
+    // period we cancel this request because
+    // 1. If we show details the data that will come with its response will not
+    // correspond to the current state.
+    // 2. If we hide details we don't need details data anyway.
+    periodDetails?.cancelSource?.cancel();
+    show = show === null ? !periodDetails : show;
+    if (show) {
+      if (periodDetails) {
+        // reload details?
+      } else {
+        const source = axios.CancelToken.source();
+        dispatch(
+          actions.loadWorkPeriodDetailsPending(
+            period.id,
+            period.rbId,
+            period.billingAccountId,
+            source
+          )
+        );
+        const [rbPromise] = services.fetchWorkPeriods(period.rbId, source);
+        const [jobNamePromise] = services.fetchJob(period.jobId, source);
+        const [bilAccsPromise] = services.fetchBillingAccounts(
+          period.projectId,
+          source
+        );
+        let details = null;
+        let errorMessage = null;
+        try {
+          const data = await rbPromise;
+          const periods = normalizeDetailsPeriodItems(data);
+          details = { periods };
+        } catch (error) {
+          if (!axios.isCancel(error)) {
+            errorMessage = error.toString();
+          }
+        }
+        if (details) {
+          dispatch(actions.loadWorkPeriodDetailsSuccess(period.id, details));
+        } else if (errorMessage) {
+          dispatch(actions.loadWorkPeriodDetailsError(period.id, errorMessage));
+          makeToast(errorMessage);
+        }
+        let jobName = null;
+        errorMessage = null;
+        try {
+          const data = await jobNamePromise;
+          jobName = extractJobName(data);
+        } catch (error) {
+          if (!axios.isCancel(error)) {
+            errorMessage = error.toString();
+          }
+        }
+        if (jobName) {
+          dispatch(actions.loadJobNameSuccess(period.id, jobName));
+        } else if (errorMessage) {
+          dispatch(actions.loadJobNameError(period.id, errorMessage));
+          makeToast(errorMessage);
+        }
+        let accounts = null;
+        errorMessage = null;
+        try {
+          const data = await bilAccsPromise;
+          const periodsDetails = selectors.getWorkPeriodsDetails(getState());
+          const periodDetails = periodsDetails[period.id];
+          const billingAccountId =
+            (periodDetails && periodDetails.billingAccountId) ||
+            period.billingAccountId;
+          accounts = normalizeBillingAccounts(data, billingAccountId);
+        } catch (error) {
+          if (!axios.isCancel(error)) {
+            errorMessage = error.toString();
+          }
+        }
+        if (accounts) {
+          dispatch(actions.loadBillingAccountsSuccess(period.id, accounts));
+        } else if (errorMessage) {
+          dispatch(actions.loadBillingAccountsError(period.id, errorMessage));
+          makeToast(errorMessage);
+        }
+      }
+    } else {
+      dispatch(actions.hideWorkPeriodDetails(period.id));
+    }
+  };
+
+/**
+ *
+ * @param {string} rbId
+ * @param {number} billingAccountId
+ * @returns {function}
+ */
+export const updateWorkPeriodBillingAccount =
+  (rbId, billingAccountId) => async () => {
+    try {
+      await services.patchWorkPeriodBillingAccount(rbId, billingAccountId);
+    } catch (error) {
+      makeToast(
+        `Failed to update billing account for resource booking ${rbId}.\n` +
+          error.toString()
+      );
+    }
+  };
+
+/**
+ *
+ * @param {string} periodId
+ * @param {number} workingDays
+ * @returns {function}
+ */
+export const updateWorkPeriodWorkingDays =
+  (periodId, workingDays) => async () => {
+    try {
+      await services.patchWorkPeriodWorkingDays(periodId, workingDays);
+    } catch (error) {
+      makeToast(
+        `Failed to update working days for working period ${periodId}.\n` +
+          error.toString()
+      );
+    }
+  };
+
+/**
+ * Sends request to process payments for selected working periods.
+ *
+ * @param {function} dispatch redux store dispatch function
+ * @param {function} getState function returning redux store root state
+ */
+export const processPayments = async (dispatch, getState) => {
+  dispatch(actions.toggleWorkPeriodsProcessingPeyments(true));
+  const state = getState();
+  const periods = selectors.getWorkPeriods(state);
+  const periodsSelected = selectors.getWorkPeriodsSelected(state);
+  const payments = [];
+  for (let period of periods) {
+    if (period.id in periodsSelected) {
+      payments.push({ workPeriodId: period.id, amount: period.weeklyRate });
+    }
+  }
+  makeProcessingToast(payments);
+  let results = null;
+  let errorMessage = null;
+  try {
+    results = await services.postWorkPeriodsPayments(payments);
+  } catch (error) {
+    errorMessage = error.toString();
+  }
+  if (results) {
+    const periodsToDeselect = {};
+    const periodsSucceeded = [];
+    const periodsFailed = [];
+    for (let result of results) {
+      if ("error" in result) {
+        periodsFailed.push(result);
+      } else {
+        periodsToDeselect[result.workPeriodId] = false;
+        periodsSucceeded.push(result);
+      }
+    }
+    dispatch(actions.selectWorkPeriods(periodsToDeselect));
+    if (periodsSucceeded.length) {
+      if (periodsFailed.length) {
+        makeWarningToast(periodsSucceeded, periodsFailed);
+      } else {
+        makeSuccessToast(periodsSucceeded);
+      }
+    } else {
+      makeErrorToast(periodsFailed);
+    }
+  } else {
+    makeToast(errorMessage);
+  }
+  dispatch(actions.toggleWorkPeriodsProcessingPeyments(false));
+};
+
+/**
+ *
+ * @param {string} message
+ * @param {'info'|'success'|'warning'|'error'} type
+ * @returns {Object}
+ */
+function makeToast(message, type = "error") {
+  const component =
+    typeof message === "string" ? (
+      <ToastrMessage message={message} type={type} />
+    ) : (
+      <ToastrMessage type={type}>{message}</ToastrMessage>
+    );
+  toastr[type]("", { component });
+}
+
+function makeProcessingToast(periods) {
+  const component = <ToastPaymentsProcessing periods={periods} />;
+  toastr.info("", { component });
+}
+
+function makeSuccessToast(periods) {
+  const component = <ToastPaymentsSuccess periods={periods} />;
+  toastr.success("", { component });
+}
+
+function makeWarningToast(periodsSucceeded, periodsFailed) {
+  const component = (
+    <ToastPaymentsWarning
+      periodsSucceeded={periodsSucceeded}
+      periodsFailed={periodsFailed}
+    />
+  );
+  toastr.warning("", { component });
+}
+
+function makeErrorToast(periods) {
+  const component = <ToastPaymentsError periods={periods} />;
+  toastr.error("", { component });
+}
diff --git a/src/styles/toastr.scss b/src/styles/toastr.scss
index 8367c56..0dc3193 100644
--- a/src/styles/toastr.scss
+++ b/src/styles/toastr.scss
@@ -1,3 +1,49 @@
+@import "mixins";
+@import "variables";
+
 .redux-toastr {
   position: absolute;
+  left: $sidebar-width;
+  top: 0;
+  right: 0;
+  margin: 0;
+  border: none;
+  padding: 0;
+  height: auto;
+  background: transparent;
+
+  > div {
+    position: absolute;
+    left: 22px;
+    top: 24px;
+    right: 14px;
+    margin: 0;
+    border: none;
+    padding: 0;
+    height: auto;
+    background: transparent;
+  }
+
+  .top-right {
+    z-index: 1000;
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+
+    > div {
+      margin-top: 10px;
+
+      &:first-child {
+        margin-top: 0;
+      }
+    }
+  }
+
+  .toastr {
+    .rrt-left-container,
+    .rrt-right-container {
+      display: none;
+    }
+  }
 }
diff --git a/src/styles/variables/_colors.scss b/src/styles/variables/_colors.scss
index 8a9b503..9e8c6ba 100644
--- a/src/styles/variables/_colors.scss
+++ b/src/styles/variables/_colors.scss
@@ -1,11 +1,25 @@
 $primary-color: #137d60;
 $primary-text-color: #229174;
 $primary-light-color: #0ab88a;
-$primary-light-text-color: #0ab88a; // currently not used, cen be changed
-$primary-dark-color: #137d60; // currently not used, cen be changed
-$primary-dark-text-color: #137d60; // currently not used, cen be changed
+$primary-light-text-color: #0ab88a; // currently not used, can be changed
+$primary-dark-color: #137d60; // currently not used, can be changed
+$primary-dark-text-color: #137d60; // currently not used, can be changed
 $text-color: #2a2a2a;
 $page-bg-color: #f4f5f6;
 
 $control-border-color: #aaa;
+$control-disabled-border-color: lighten(
+  $color: $control-border-color,
+  $amount: 5%,
+);
+$control-disabled-bg-color: lighten(
+  $color: $control-border-color,
+  $amount: 10%,
+);
+$control-disabled-text-color: lighten(
+  $color: $text-color,
+  $amount: 10%,
+);
+
 $checkbox-bg-color: $primary-light-color;
+$toggle-active-bg-color: $primary-light-color;
diff --git a/src/utils/formatters.js b/src/utils/formatters.js
index bd67d1f..d720727 100644
--- a/src/utils/formatters.js
+++ b/src/utils/formatters.js
@@ -1,9 +1,57 @@
+import moment from "moment";
 import isNumber from "lodash/isNumber";
 import { PAYMENT_STATUS_LABELS } from "constants/workPeriods";
-import { PLATFORM_WEBSITE_URL, TAAS_BASE_PATH } from "../constants";
+import {
+  PLATFORM_WEBSITE_URL,
+  TAAS_BASE_PATH,
+  TOPCODER_WEBSITE_URL,
+} from "../constants";
 
 const rxWhitespace = /\s+/;
 
+/**
+ * Creates a challenge URL using challenge id.
+ *
+ * @param {number} challengeId challenge id
+ * @returns {string}
+ */
+export function formatChallengeUrl(challengeId) {
+  return `${TOPCODER_WEBSITE_URL}/challenges/${challengeId}`;
+}
+
+/**
+ * Returns a string denoting whether the specified start date corresponds to the
+ * current period or future period.
+ *
+ * @param {*} startDate start date
+ * @param {*} currentStartDate start date of currently selected period
+ * @returns {string}
+ */
+export function formatDateLabel(startDate, currentStartDate) {
+  let start = moment(startDate);
+  let currentStart = moment(currentStartDate);
+  if (start.isSame(currentStart, "date")) {
+    return "Current Period";
+  }
+  if (start.isAfter(currentStart, "date")) {
+    return "Future Period";
+  }
+  return "";
+}
+
+/**
+ * Formats working period's date range.
+ *
+ * @param {number|string} startDate working period start date
+ * @param {number|string} endDate working period end date
+ * @returns {string}
+ */
+export function formatDateRange(startDate, endDate) {
+  let start = moment(startDate);
+  let end = moment(endDate);
+  return `${start.format("DD MMM, YYYY")} to ${end.format("DD MMM, YYYY")}`;
+}
+
 /**
  * Formats payment status.
  *
diff --git a/src/utils/hooks.js b/src/utils/hooks.js
index 82e138f..717943d 100644
--- a/src/utils/hooks.js
+++ b/src/utils/hooks.js
@@ -1,5 +1,32 @@
 import { useEffect, useRef } from "react";
 
+/**
+ * By "click" it is implied "mousedown" or "touchstart"
+ *
+ * @param {Object} ref element reference obtained with useRef
+ * @param {function} listener function with stable identity
+ * that will be executed on click outside
+ * @param {Array} deps dependencies
+ * when click happens outside the element referred by ref
+ */
+export const useClickOutside = (ref, listener, deps) => {
+  useEffect(() => {
+    const onClick = (event) => {
+      let elem = ref.current;
+      if (elem && !elem.contains(event.target)) {
+        listener();
+      }
+    };
+    document.addEventListener("mousedown", onClick);
+    document.addEventListener("touchstart", onClick);
+    return () => {
+      document.removeEventListener("touchstart", onClick);
+      document.removeEventListener("mousedown", onClick);
+    };
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, deps);
+};
+
 /**
  * A hook that calls effect only if dependencies or effect itself change.
  *
diff --git a/src/utils/misc.js b/src/utils/misc.js
index f931c62..97d8215 100644
--- a/src/utils/misc.js
+++ b/src/utils/misc.js
@@ -1,5 +1,27 @@
 import moment from "moment";
 
+/**
+ * Returns working periods filtered by start date.
+ *
+ * @param {Array} periods array of working period items that contain startDate
+ * @param {string|Object} startDate value denoting start date
+ * that can be accepted by momentjs
+ * @returns {Array}
+ */
+export function filterPeriodsByStartDate(periods, startDate) {
+  if (!startDate) {
+    return periods;
+  }
+  const items = [];
+  startDate = moment(startDate);
+  for (let period of periods) {
+    if (moment(period.startDate).isSameOrAfter(startDate, "date")) {
+      items.push(period);
+    }
+  }
+  return items;
+}
+
 /**
  * Returns the option which matches the provided value or null.
  *
@@ -109,4 +131,10 @@ export const extractResponsePagination = ({ headers }) => ({
   pageSize: +headers["x-per-page"] || 10,
 });
 
+export const extractJobName = (data) => data.title;
+
 export const extractResponseData = (response) => response.data;
+
+export function stopPropagation(event) {
+  event.stopPropagation();
+}
diff --git a/src/utils/workPeriods.js b/src/utils/workPeriods.js
index 2258b73..253d7fb 100644
--- a/src/utils/workPeriods.js
+++ b/src/utils/workPeriods.js
@@ -1,19 +1,23 @@
 import moment from "moment";
-import { API_PAYMENT_STATUS_MAP, PAYMENT_STATUS } from "constants/workPeriods";
-
-const DATE_FORMAT_UI = "MMM DD, YYYY";
+import {
+  API_PAYMENT_STATUS_MAP,
+  DATE_FORMAT_UI,
+  PAYMENT_STATUS,
+} from "constants/workPeriods";
 
 export function normalizePeriodItems(items) {
   const empty = {};
   const periods = [];
   for (let item of items) {
     const workPeriod = item.workPeriods?.[0] || empty;
-    const paymentStatus = workPeriod.paymentStatus;
+    const billingAccountId = item.billingAccountId;
     const daysWorked = workPeriod.daysWorked;
     periods.push({
       id: workPeriod.id || item.id,
       rbId: item.id,
+      jobId: item.jobId,
       projectId: item.projectId,
+      billingAccountId: billingAccountId === null ? 0 : billingAccountId,
       teamName: "",
       userHandle: workPeriod.userHandle || "",
       startDate: item.startDate
@@ -21,11 +25,66 @@ export function normalizePeriodItems(items) {
         : "",
       endDate: item.endDate ? moment(item.endDate).format(DATE_FORMAT_UI) : "",
       weeklyRate: item.memberRate,
-      paymentStatus: paymentStatus
-        ? API_PAYMENT_STATUS_MAP[paymentStatus] || paymentStatus.toUpperCase()
-        : PAYMENT_STATUS.UNDEFINED,
+      paymentStatus: normalizePaymentStatus(workPeriod.paymentStatus),
       workingDays: daysWorked === null ? 5 : +daysWorked || 0,
     });
   }
   return periods;
 }
+
+/**
+ * Creates options to be used in dropdown selecting working period's
+ * billing account.
+ *
+ * @param {Array} accounts array of billing accounts received for specific project
+ * @param {number} accountId resource booking's billing account id
+ * @returns {Array}
+ */
+export function normalizeBillingAccounts(accounts, accountId = -1) {
+  const accs = [];
+  let hasSelectedAccount = false;
+  for (let acc of accounts) {
+    const value = +acc.tcBillingAccountId;
+    hasSelectedAccount = hasSelectedAccount || value === accountId;
+    const endDate = acc.endDate
+      ? moment(acc.endDate).format("DD MMM YYYY")
+      : "";
+    accs.push({
+      value,
+      label: `${acc.name} (${value})` + (endDate ? ` - ${endDate}` : ""),
+    });
+  }
+  if (!hasSelectedAccount && accountId > 0) {
+    accs.unshift({
+      value: accountId,
+      label: `<Assigned Account> (${accountId})`,
+    });
+  }
+  return accs;
+}
+
+export function normalizeDetailsPeriodItems(items) {
+  const periods = [];
+  for (let item of items) {
+    const daysWorked = item.daysWorked;
+    periods.push({
+      id: item.id,
+      startDate: item.startDate ? moment(item.startDate).valueOf() : 0,
+      endDate: item.endDate ? moment(item.endDate).valueOf() : 0,
+      paymentStatus: normalizePaymentStatus(item.paymentStatus),
+      payments: item.payments || [],
+      weeklyRate: item.memberRate,
+      workingDays: daysWorked === null ? 5 : +daysWorked || 0,
+    });
+  }
+  periods.sort(sortByStartDate);
+  return periods;
+}
+
+export function normalizePaymentStatus(paymentStatus) {
+  return API_PAYMENT_STATUS_MAP[paymentStatus] || PAYMENT_STATUS.UNDEFINED;
+}
+
+export function sortByStartDate(itemA, itemB) {
+  return itemA.startDate - itemB.startDate;
+}