diff --git a/frontend/js/dashboard.js b/frontend/js/dashboard.js
new file mode 100644
index 0000000..657f371
--- /dev/null
+++ b/frontend/js/dashboard.js
@@ -0,0 +1 @@
+import '../scss/dashboard.scss';
diff --git a/frontend/scss/dashboard.scss b/frontend/scss/dashboard.scss
new file mode 100644
index 0000000..4f9c688
--- /dev/null
+++ b/frontend/scss/dashboard.scss
@@ -0,0 +1,183 @@
+.nice-padding {
+ padding-left: 50px;
+ padding-right: 50px;
+}
+
+.block_container {
+ display: block;
+ margin-top: 30px;
+}
+
+.block_container .block {
+ display: block;
+ float: left;
+ box-sizing: border-box;
+ position: relative;
+ width: calc(50% - 10px);
+ min-height: 216px;
+ padding: 10px 20px;
+ margin-bottom: 20px;
+ border: 1px solid #d9d9d9;
+ border-radius: 3px;
+ background-color: #fff;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00);
+ transition: box-shadow 0.3s cubic-bezier(.25,.8,.25,1), border 0.3s cubic-bezier(.25,.8,.25,1);
+ cursor: pointer;
+}
+
+.block_container .block--disabled h2,
+.block_container .block--disabled .inspect_container {
+ opacity: 0.5;
+}
+
+ .block_container .block h2 {
+ display: inline-block;
+ width: auto;
+ }
+
+.block_container .block:nth-child(odd) {
+ margin-right: 20px;
+}
+
+ .block_container .block .block_actions {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+
+ .block_container .block .block_actions li {
+ float: left;
+ margin-right: 10px;
+ }
+
+ .block_container .block .block_actions li:last-child {
+ margin-right: 0;
+ }
+
+.block_container .block.suggestion {
+ border: 1px dashed #d9d9d9;
+}
+
+.block_container .block:hover {
+ border: 1px solid #fff;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+}
+
+@media (max-width: 699px) {
+ .block_container .block {
+ width: 100%;
+ margin-right: 0;
+ }
+}
+
+.block_container .block .inspect_container {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ justify-content: space-between;
+ align-items: stretch;
+ margin-bottom: 10px;
+}
+
+.block_container .block .inspect_container .inspect {
+ display: block;
+ float: left;
+ width: calc(50% - 10px);
+ padding: 0;
+ margin: 0;
+ list-style: none;
+}
+
+ .block_container .block .inspect_container .inspect li {
+ display: inline-block;
+ margin-bottom: 5px;
+ }
+
+ .block_container .block .inspect_container .inspect li span {
+ display: block;
+ font-size: 20px;
+ font-weight: bold;
+ margin: 5px 0;
+ overflow-wrap: break-word;
+ }
+
+ .block_container .block .inspect_container .inspect li span::before {
+ display: inline-block;
+ content: "";
+ width: 16px;
+ height: 16px;
+ margin-right: 5px;
+ background-size: contain;
+ background-image: url("../img/ruler_icon.png");
+ }
+
+ .block_container .block .inspect_container .segment_stats .visit_stat span::before {
+ background-image: url("../img/rocket_icon.png");
+ }
+ .block_container .block .inspect_container .segment_stats .days_stat span::before {
+ background-image: url("../img/calendar_icon.png");
+ }
+
+ .block_container .block .inspect_container .segment_rules .persistent_state span::before {
+ background-image: url("../img/persistent_icon.png");
+ }
+ .block_container .block .inspect_container .segment_rules .persistent_state.fleeting span::before {
+ transform: rotate(45deg) translateY(-2px);
+ }
+ .block_container .block .inspect_container .segment_rules .time-rule span::before {
+ background-image: url("../img/time_icon.png");
+ }
+ .block_container .block .inspect_container .segment_rules .visit-count-rule span::before {
+ background-image: url("../img/visit_count_icon.png");
+ }
+ .block_container .block .inspect_container .segment_rules .logged-in-rule span::before {
+ background-image: url("../img/key_icon.png");
+ }
+ .block_container .block .inspect_container .segment_rules .day-rule span::before {
+ background-image: url("../img/calendar_icon.png");
+ }
+
+ .block_container .block .inspect_container .inspect li pre {
+ position: relative;
+ box-sizing: border-box;
+ width: auto;
+ background-color: #eee;
+ border: 1px solid #ccc;
+ margin: 5px 0 5px 21px;
+ padding: 2px 5px;
+ word-wrap: break-word;
+ word-break: break-all;
+ border-radius: 3px;
+ }
+
+ .block_container .block .inspect_container .inspect li pre::before {
+ display: inline-block;
+ position: absolute;
+ content: "";
+ left: -21px;
+ top: 6px;
+ width: 16px;
+ height: 16px;
+ margin-right: 5px;
+ background-size: contain;
+ background-image: url("../img/ruler_icon.png");
+ }
+
+ .block_container .block .inspect_container .segment_rules .referral-rule pre::before {
+ background-image: url("../img/referral_icon.png");
+ }
+ .block_container .block .inspect_container .segment_rules .query-rule pre::before {
+ background-image: url("../img/referral_icon.png");
+ }
+
+.block_container .block.suggestion .suggestive_text {
+ display: block;
+ position: absolute;
+ width: calc(100% - 40px);
+ text-align: center;
+ top: 50%;
+ transform: translateY(-50%);
+ color: #d9d9d9;
+ font-size: 20px;
+ font-weight: 100;
+}
diff --git a/frontend/scss/index.scss b/frontend/scss/index.scss
index 4f9c688..e69de29 100644
--- a/frontend/scss/index.scss
+++ b/frontend/scss/index.scss
@@ -1,183 +0,0 @@
-.nice-padding {
- padding-left: 50px;
- padding-right: 50px;
-}
-
-.block_container {
- display: block;
- margin-top: 30px;
-}
-
-.block_container .block {
- display: block;
- float: left;
- box-sizing: border-box;
- position: relative;
- width: calc(50% - 10px);
- min-height: 216px;
- padding: 10px 20px;
- margin-bottom: 20px;
- border: 1px solid #d9d9d9;
- border-radius: 3px;
- background-color: #fff;
- box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00);
- transition: box-shadow 0.3s cubic-bezier(.25,.8,.25,1), border 0.3s cubic-bezier(.25,.8,.25,1);
- cursor: pointer;
-}
-
-.block_container .block--disabled h2,
-.block_container .block--disabled .inspect_container {
- opacity: 0.5;
-}
-
- .block_container .block h2 {
- display: inline-block;
- width: auto;
- }
-
-.block_container .block:nth-child(odd) {
- margin-right: 20px;
-}
-
- .block_container .block .block_actions {
- list-style: none;
- margin: 0;
- padding: 0;
- }
-
- .block_container .block .block_actions li {
- float: left;
- margin-right: 10px;
- }
-
- .block_container .block .block_actions li:last-child {
- margin-right: 0;
- }
-
-.block_container .block.suggestion {
- border: 1px dashed #d9d9d9;
-}
-
-.block_container .block:hover {
- border: 1px solid #fff;
- box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
-}
-
-@media (max-width: 699px) {
- .block_container .block {
- width: 100%;
- margin-right: 0;
- }
-}
-
-.block_container .block .inspect_container {
- display: flex;
- flex-direction: row;
- flex-wrap: nowrap;
- justify-content: space-between;
- align-items: stretch;
- margin-bottom: 10px;
-}
-
-.block_container .block .inspect_container .inspect {
- display: block;
- float: left;
- width: calc(50% - 10px);
- padding: 0;
- margin: 0;
- list-style: none;
-}
-
- .block_container .block .inspect_container .inspect li {
- display: inline-block;
- margin-bottom: 5px;
- }
-
- .block_container .block .inspect_container .inspect li span {
- display: block;
- font-size: 20px;
- font-weight: bold;
- margin: 5px 0;
- overflow-wrap: break-word;
- }
-
- .block_container .block .inspect_container .inspect li span::before {
- display: inline-block;
- content: "";
- width: 16px;
- height: 16px;
- margin-right: 5px;
- background-size: contain;
- background-image: url("../img/ruler_icon.png");
- }
-
- .block_container .block .inspect_container .segment_stats .visit_stat span::before {
- background-image: url("../img/rocket_icon.png");
- }
- .block_container .block .inspect_container .segment_stats .days_stat span::before {
- background-image: url("../img/calendar_icon.png");
- }
-
- .block_container .block .inspect_container .segment_rules .persistent_state span::before {
- background-image: url("../img/persistent_icon.png");
- }
- .block_container .block .inspect_container .segment_rules .persistent_state.fleeting span::before {
- transform: rotate(45deg) translateY(-2px);
- }
- .block_container .block .inspect_container .segment_rules .time-rule span::before {
- background-image: url("../img/time_icon.png");
- }
- .block_container .block .inspect_container .segment_rules .visit-count-rule span::before {
- background-image: url("../img/visit_count_icon.png");
- }
- .block_container .block .inspect_container .segment_rules .logged-in-rule span::before {
- background-image: url("../img/key_icon.png");
- }
- .block_container .block .inspect_container .segment_rules .day-rule span::before {
- background-image: url("../img/calendar_icon.png");
- }
-
- .block_container .block .inspect_container .inspect li pre {
- position: relative;
- box-sizing: border-box;
- width: auto;
- background-color: #eee;
- border: 1px solid #ccc;
- margin: 5px 0 5px 21px;
- padding: 2px 5px;
- word-wrap: break-word;
- word-break: break-all;
- border-radius: 3px;
- }
-
- .block_container .block .inspect_container .inspect li pre::before {
- display: inline-block;
- position: absolute;
- content: "";
- left: -21px;
- top: 6px;
- width: 16px;
- height: 16px;
- margin-right: 5px;
- background-size: contain;
- background-image: url("../img/ruler_icon.png");
- }
-
- .block_container .block .inspect_container .segment_rules .referral-rule pre::before {
- background-image: url("../img/referral_icon.png");
- }
- .block_container .block .inspect_container .segment_rules .query-rule pre::before {
- background-image: url("../img/referral_icon.png");
- }
-
-.block_container .block.suggestion .suggestive_text {
- display: block;
- position: absolute;
- width: calc(100% - 40px);
- text-align: center;
- top: 50%;
- transform: translateY(-50%);
- color: #d9d9d9;
- font-size: 20px;
- font-weight: 100;
-}
diff --git a/src/personalisation/static/css/form.css b/src/personalisation/static/css/form.css
new file mode 100644
index 0000000..bf759f9
--- /dev/null
+++ b/src/personalisation/static/css/form.css
@@ -0,0 +1,2 @@
+.block_container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap}.block_container .block{display:block;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;width:calc(100% / 3 - 13.33px);padding:10px 20px;margin-bottom:20px;border:1px solid #d9d9d9;border-radius:3px;background-color:#fff;cursor:pointer;-webkit-box-shadow:0 1px 3px transparent,0 1px 2px transparent;box-shadow:0 1px 3px transparent,0 1px 2px transparent;-webkit-transition:border .3s cubic-bezier(.25,.8,.25,1),-webkit-box-shadow .3s cubic-bezier(.25,.8,.25,1);transition:border .3s cubic-bezier(.25,.8,.25,1),-webkit-box-shadow .3s cubic-bezier(.25,.8,.25,1);transition:box-shadow .3s cubic-bezier(.25,.8,.25,1),border .3s cubic-bezier(.25,.8,.25,1);transition:box-shadow .3s cubic-bezier(.25,.8,.25,1),border .3s cubic-bezier(.25,.8,.25,1),-webkit-box-shadow .3s cubic-bezier(.25,.8,.25,1)}.block_container .block:nth-child(3n+1),.block_container .block:nth-child(3n+2){margin-right:20px}.block_container .block:hover{border:1px solid #fff;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}.block_container .block.disabled{background-color:#eee;cursor:default}.block_container .block.disabled:hover{border:1px solid #d9d9d9;-webkit-box-shadow:0 1px 3px transparent,0 1px 2px transparent;box-shadow:0 1px 3px transparent,0 1px 2px transparent}@media (min-width:800px) and (max-width:999px){.block_container .block{width:calc(100% / 2 - 10px)}.block_container .block:nth-child(3n+1),.block_container .block:nth-child(3n+2){margin-right:0}.block_container .block:nth-child(odd){margin-right:20px}}@media (max-width:599px){.block_container .block{width:calc(100% / 2 - 10px)}.block_container .block:nth-child(3n+1),.block_container .block:nth-child(3n+2){margin-right:0}.block_container .block:nth-child(odd){margin-right:20px}}
+/*# sourceMappingURL=form.css.map*/
\ No newline at end of file
diff --git a/src/personalisation/static/css/form.css.map b/src/personalisation/static/css/form.css.map
new file mode 100644
index 0000000..31aeaf7
--- /dev/null
+++ b/src/personalisation/static/css/form.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["webpack:///./scss/form.scss"],"names":[],"mappings":"AAAA,iBACI,oBAAa,iCACb,8BAAmB,uEACnB,mBAAe,eAGf,wBACI,cACA,8BAAsB,sBACtB,kBACA,+BACA,kBACA,mBACA,yBACA,kBACA,sBACA,eACA,+DAAkE,uDAClE,2GAA8F,2UAGlG,gFAEI,iBAAkB,CAGtB,8BACI,sBACA,uEAAkE,+DAGtE,iCACI,sBACA,cAAe,CAGnB,uCACI,yBACA,+DAAkE,uDAG1E,+CACI,wBACI,2BAA4B,CAGhC,gFAEI,cAAe,CAGnB,uCACI,iBAAkB,CACrB,CAGL,yBACI,wBACI,2BAA4B,CAGhC,gFAEI,cAAe,CAGnB,uCACI,iBAAkB,CACrB","file":"../css/form.css","sourcesContent":[".block_container {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n}\n\n .block_container .block {\n display: block;\n box-sizing: border-box;\n position: relative;\n width: calc(100% / 3 - 13.33px);\n padding: 10px 20px;\n margin-bottom: 20px;\n border: 1px solid #d9d9d9;\n border-radius: 3px;\n background-color: #fff;\n cursor: pointer;\n box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00);\n transition: box-shadow 0.3s cubic-bezier(.25,.8,.25,1), border 0.3s cubic-bezier(.25,.8,.25,1);\n }\n\n .block_container .block:nth-child(3n+1),\n .block_container .block:nth-child(3n+2) {\n margin-right: 20px;\n }\n\n .block_container .block:hover {\n border: 1px solid #fff;\n box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);\n }\n\n .block_container .block.disabled {\n background-color: #eee;\n cursor: default;\n }\n\n .block_container .block.disabled:hover {\n border: 1px solid #d9d9d9;\n box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00);\n }\n\n@media (min-width: 800px) and (max-width: 999px) {\n .block_container .block {\n width: calc(100% / 2 - 10px);\n }\n\n .block_container .block:nth-child(3n+1),\n .block_container .block:nth-child(3n+2) {\n margin-right: 0;\n }\n\n .block_container .block:nth-child(2n+1) {\n margin-right: 20px;\n }\n}\n\n@media (max-width: 599px) {\n .block_container .block {\n width: calc(100% / 2 - 10px);\n }\n\n .block_container .block:nth-child(3n+1),\n .block_container .block:nth-child(3n+2) {\n margin-right: 0;\n }\n\n .block_container .block:nth-child(2n+1) {\n margin-right: 20px;\n }\n}\n\n\n// WEBPACK FOOTER //\n// ./scss/form.scss"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/personalisation/static/css/index.css b/src/personalisation/static/css/index.css
new file mode 100644
index 0000000..80c6e0e
--- /dev/null
+++ b/src/personalisation/static/css/index.css
@@ -0,0 +1,2 @@
+
+/*# sourceMappingURL=index.css.map*/
\ No newline at end of file
diff --git a/src/personalisation/static/css/index.css.map b/src/personalisation/static/css/index.css.map
new file mode 100644
index 0000000..9a1c86d
--- /dev/null
+++ b/src/personalisation/static/css/index.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":[],"names":[],"mappings":"","file":"../css/index.css","sourceRoot":""}
\ No newline at end of file
diff --git a/src/personalisation/static/img/calendar_icon.png b/src/personalisation/static/img/calendar_icon.png
new file mode 100644
index 0000000..a655cd3
Binary files /dev/null and b/src/personalisation/static/img/calendar_icon.png differ
diff --git a/src/personalisation/static/img/key_icon.png b/src/personalisation/static/img/key_icon.png
new file mode 100644
index 0000000..6d18648
Binary files /dev/null and b/src/personalisation/static/img/key_icon.png differ
diff --git a/src/personalisation/static/img/persistent_icon.png b/src/personalisation/static/img/persistent_icon.png
new file mode 100644
index 0000000..3805284
Binary files /dev/null and b/src/personalisation/static/img/persistent_icon.png differ
diff --git a/src/personalisation/static/img/referral_icon.png b/src/personalisation/static/img/referral_icon.png
new file mode 100644
index 0000000..4955b08
Binary files /dev/null and b/src/personalisation/static/img/referral_icon.png differ
diff --git a/src/personalisation/static/img/rocket_icon.png b/src/personalisation/static/img/rocket_icon.png
new file mode 100644
index 0000000..ab1b014
Binary files /dev/null and b/src/personalisation/static/img/rocket_icon.png differ
diff --git a/src/personalisation/static/img/ruler_icon.png b/src/personalisation/static/img/ruler_icon.png
new file mode 100644
index 0000000..bf9e209
Binary files /dev/null and b/src/personalisation/static/img/ruler_icon.png differ
diff --git a/src/personalisation/static/img/time_icon.png b/src/personalisation/static/img/time_icon.png
new file mode 100644
index 0000000..8e910a2
Binary files /dev/null and b/src/personalisation/static/img/time_icon.png differ
diff --git a/src/personalisation/static/img/visit_count_icon.png b/src/personalisation/static/img/visit_count_icon.png
new file mode 100644
index 0000000..b2397b6
Binary files /dev/null and b/src/personalisation/static/img/visit_count_icon.png differ
diff --git a/src/personalisation/static/js/commons.js b/src/personalisation/static/js/commons.js
new file mode 100644
index 0000000..1d07d4c
--- /dev/null
+++ b/src/personalisation/static/js/commons.js
@@ -0,0 +1,155 @@
+/******/ (function(modules) { // webpackBootstrap
+/******/ // install a JSONP callback for chunk loading
+/******/ var parentJsonpFunction = window["webpackJsonp"];
+/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
+/******/ // add "moreModules" to the modules object,
+/******/ // then flag all "chunkIds" as loaded and fire callback
+/******/ var moduleId, chunkId, i = 0, resolves = [], result;
+/******/ for(;i < chunkIds.length; i++) {
+/******/ chunkId = chunkIds[i];
+/******/ if(installedChunks[chunkId]) {
+/******/ resolves.push(installedChunks[chunkId][0]);
+/******/ }
+/******/ installedChunks[chunkId] = 0;
+/******/ }
+/******/ for(moduleId in moreModules) {
+/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
+/******/ modules[moduleId] = moreModules[moduleId];
+/******/ }
+/******/ }
+/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
+/******/ while(resolves.length) {
+/******/ resolves.shift()();
+/******/ }
+/******/ if(executeModules) {
+/******/ for(i=0; i < executeModules.length; i++) {
+/******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]);
+/******/ }
+/******/ }
+/******/ return result;
+/******/ };
+/******/
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // objects to store loaded and loading chunks
+/******/ var installedChunks = {
+/******/ 2: 0
+/******/ };
+/******/
+/******/ var resolvedPromise = new Promise(function(resolve) { resolve(); });
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/ // This file contains only the entry chunk.
+/******/ // The chunk loading function for additional chunks
+/******/ __webpack_require__.e = function requireEnsure(chunkId) {
+/******/ if(installedChunks[chunkId] === 0) {
+/******/ return resolvedPromise;
+/******/ }
+/******/
+/******/ // a Promise means "currently loading".
+/******/ if(installedChunks[chunkId]) {
+/******/ return installedChunks[chunkId][2];
+/******/ }
+/******/
+/******/ // setup Promise in chunk cache
+/******/ var promise = new Promise(function(resolve, reject) {
+/******/ installedChunks[chunkId] = [resolve, reject];
+/******/ });
+/******/ installedChunks[chunkId][2] = promise;
+/******/
+/******/ // start chunk loading
+/******/ var head = document.getElementsByTagName('head')[0];
+/******/ var script = document.createElement('script');
+/******/ script.type = 'text/javascript';
+/******/ script.charset = 'utf-8';
+/******/ script.async = true;
+/******/ script.timeout = 120000;
+/******/
+/******/ if (__webpack_require__.nc) {
+/******/ script.setAttribute("nonce", __webpack_require__.nc);
+/******/ }
+/******/ script.src = __webpack_require__.p + "" + chunkId + ".js";
+/******/ var timeout = setTimeout(onScriptComplete, 120000);
+/******/ script.onerror = script.onload = onScriptComplete;
+/******/ function onScriptComplete() {
+/******/ // avoid mem leaks in IE.
+/******/ script.onerror = script.onload = null;
+/******/ clearTimeout(timeout);
+/******/ var chunk = installedChunks[chunkId];
+/******/ if(chunk !== 0) {
+/******/ if(chunk) {
+/******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
+/******/ }
+/******/ installedChunks[chunkId] = undefined;
+/******/ }
+/******/ };
+/******/ head.appendChild(script);
+/******/
+/******/ return promise;
+/******/ };
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // identity function for calling harmony imports with the correct context
+/******/ __webpack_require__.i = function(value) { return value; };
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, {
+/******/ configurable: false,
+/******/ enumerable: true,
+/******/ get: getter
+/******/ });
+/******/ }
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/ // on error function for async loading
+/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
+/******/ })
+/************************************************************************/
+/******/ ([]);
+//# sourceMappingURL=commons.js.map
\ No newline at end of file
diff --git a/src/personalisation/static/js/commons.js.map b/src/personalisation/static/js/commons.js.map
new file mode 100644
index 0000000..c0c19ee
--- /dev/null
+++ b/src/personalisation/static/js/commons.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["webpack:///webpack/bootstrap e0eb5a8f691b9fda3313"],"names":[],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAQ,oBAAoB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAY,2BAA2B;AACvC;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA,+DAAuD,WAAW,EAAE;;AAEpE;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,YAAI;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA,mDAA2C,cAAc;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA,kDAA0C,oBAAoB,WAAW","file":"commons.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \tvar resolvedPromise = new Promise(function(resolve) { resolve(); });\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tif(installedChunks[chunkId] === 0) {\n \t\t\treturn resolvedPromise;\n \t\t}\n\n \t\t// a Promise means \"currently loading\".\n \t\tif(installedChunks[chunkId]) {\n \t\t\treturn installedChunks[chunkId][2];\n \t\t}\n\n \t\t// setup Promise in chunk cache\n \t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\tinstalledChunks[chunkId] = [resolve, reject];\n \t\t});\n \t\tinstalledChunks[chunkId][2] = promise;\n\n \t\t// start chunk loading\n \t\tvar head = document.getElementsByTagName('head')[0];\n \t\tvar script = document.createElement('script');\n \t\tscript.type = 'text/javascript';\n \t\tscript.charset = 'utf-8';\n \t\tscript.async = true;\n \t\tscript.timeout = 120000;\n\n \t\tif (__webpack_require__.nc) {\n \t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t}\n \t\tscript.src = __webpack_require__.p + \"\" + chunkId + \".js\";\n \t\tvar timeout = setTimeout(onScriptComplete, 120000);\n \t\tscript.onerror = script.onload = onScriptComplete;\n \t\tfunction onScriptComplete() {\n \t\t\t// avoid mem leaks in IE.\n \t\t\tscript.onerror = script.onload = null;\n \t\t\tclearTimeout(timeout);\n \t\t\tvar chunk = installedChunks[chunkId];\n \t\t\tif(chunk !== 0) {\n \t\t\t\tif(chunk) {\n \t\t\t\t\tchunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));\n \t\t\t\t}\n \t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t}\n \t\t};\n \t\thead.appendChild(script);\n\n \t\treturn promise;\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap e0eb5a8f691b9fda3313"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/personalisation/static/js/form.js b/src/personalisation/static/js/form.js
new file mode 100644
index 0000000..1d1c80e
--- /dev/null
+++ b/src/personalisation/static/js/form.js
@@ -0,0 +1,19 @@
+webpackJsonp([1],[
+/* 0 */
+/***/ (function(module, exports) {
+
+// removed by extract-text-webpack-plugin
+
+/***/ }),
+/* 1 */,
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+__webpack_require__(0);
+
+/***/ })
+],[2]);
+//# sourceMappingURL=form.js.map
\ No newline at end of file
diff --git a/src/personalisation/static/js/form.js.map b/src/personalisation/static/js/form.js.map
new file mode 100644
index 0000000..750b737
--- /dev/null
+++ b/src/personalisation/static/js/form.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["webpack:///./scss/form.scss?4a48","webpack:///./js/form.js"],"names":[],"mappings":";;;;AAAA,yC;;;;;;;;;;ACAA,uB","file":"form.js","sourcesContent":["// removed by extract-text-webpack-plugin\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./scss/form.scss\n// module id = 0\n// module chunks = 1","import '../scss/form.scss';\n\n\n\n// WEBPACK FOOTER //\n// ./js/form.js"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/personalisation/static/js/index.js b/src/personalisation/static/js/index.js
new file mode 100644
index 0000000..b60b616
--- /dev/null
+++ b/src/personalisation/static/js/index.js
@@ -0,0 +1,20 @@
+webpackJsonp([0],[
+/* 0 */,
+/* 1 */
+/***/ (function(module, exports) {
+
+// removed by extract-text-webpack-plugin
+
+/***/ }),
+/* 2 */,
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+__webpack_require__(1);
+
+/***/ })
+],[3]);
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/src/personalisation/static/js/index.js.map b/src/personalisation/static/js/index.js.map
new file mode 100644
index 0000000..ace065f
--- /dev/null
+++ b/src/personalisation/static/js/index.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["webpack:///./scss/index.scss","webpack:///./js/index.js"],"names":[],"mappings":";;;;;AAAA,yC;;;;;;;;;;ACAA,uB","file":"index.js","sourcesContent":["// removed by extract-text-webpack-plugin\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./scss/index.scss\n// module id = 1\n// module chunks = 0","import '../scss/index.scss';\n\n\n\n// WEBPACK FOOTER //\n// ./js/index.js"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/wagtail_personalisation/models.py b/src/wagtail_personalisation/models.py
index ea04e3f..34996a2 100644
--- a/src/wagtail_personalisation/models.py
+++ b/src/wagtail_personalisation/models.py
@@ -14,6 +14,7 @@ from wagtail.wagtailcore.models import Page
from wagtail_personalisation.forms import AdminPersonalisablePageForm
from wagtail_personalisation.rules import AbstractBaseRule
+from wagtail_personalisation.utils import count_active_days
@python_2_unicode_compatible
@@ -65,6 +66,10 @@ class Segment(ClusterableModel):
"""Return a string with a slug for the segment."""
return slugify(self.name.lower())
+ def get_active_days(self):
+ """Return the amount of days the segment has been active."""
+ return count_active_days(self.enable_date, self.disable_date)
+
def get_rules(self):
"""Retrieve all rules in the segment."""
rules = AbstractBaseRule.__subclasses__()
diff --git a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/base.html b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/base.html
new file mode 100644
index 0000000..020fa7f
--- /dev/null
+++ b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/base.html
@@ -0,0 +1,35 @@
+{% extends "modeladmin/index.html" %}
+{% load i18n l10n staticfiles modeladmin_tags %}
+
+{% block titletag %}{{ view.get_meta_title }}{% endblock %}
+
+{% block css %}
+ {{ block.super }}
+ {{ view.media.css }}
+{% endblock %}
+
+{% block extra_js %}
+ {{ view.media.js }}
+{% endblock %}
+
+{% block header %}
+ {{ view.get_page_title }}
{% endblock %}
+
{{ rule.description.value }}+ {% else %} + {{ rule.description.value }} + {% endif %} +
{% blocktrans with page_obj.number as current_page and paginator.num_pages as num_pages %}Page {{ current_page }} of {{ num_pages }}.{% endblocktrans %}
+{{ rule.description.value }}- {% else %} - {{ rule.description.value }} - {% endif %} -
{% blocktrans with view.verbose_name_plural as name %}No {{ name }} have been created yet. One of the following must be created before you can add any {{ name }}:{% endblocktrans %}
+{% blocktrans with view.verbose_name_plural as name %}No {{ name }} have been created yet.{% endblocktrans %} + {% if user_can_create %} + {% blocktrans with view.create_url as url %} + Why not add one? + {% endblocktrans %} + {% endif %}
{% endif %}{% blocktrans with page_obj.number as current_page and paginator.num_pages as num_pages %}Page {{ current_page }} of {{ num_pages }}.{% endblocktrans %}
{% if paginator.num_pages > 1 %} -{% blocktrans with page_obj.number as current_page and paginator.num_pages as num_pages %}Page {{ current_page }} of {{ num_pages }}.{% endblocktrans %}