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 %} +
+
+
+
+ {% block h1 %}

{{ view.get_page_title }}

{% endblock %} +
+ {% block search %}{% search_form %}{% endblock %} +
+ {% block header_extra %} + {% if user_can_create %} +
+
+ {% include 'modeladmin/includes/button.html' with button=view.button_helper.add_button %} +
+
+ {% endif %} + {% endblock %} +
+
+{% endblock %} diff --git a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/dashboard.html b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/dashboard.html new file mode 100644 index 0000000..0e61334 --- /dev/null +++ b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/dashboard.html @@ -0,0 +1,111 @@ +{% extends "modeladmin/wagtail_personalisation/segment/base.html" %} +{% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %} + +{% block content_main %} +
+
+ {% block content_cols %} + + {% block filters %} + {% if view.has_filters and all_count %} +
+

{% trans 'Filter' %}

+ {% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %} +
+ {% endif %} + {% endblock %} + +
+ {% block result_list %} +
+ {% if all_count %} + {% for segment in object_list %} +
+

{{ segment }}

+
+
    +
  • + {% trans "This segmented has been visited" %} + {{ segment.visit_count|localize }} {% trans "time" %}{{ segment.visit_count|pluralize }} +
  • +
  • + {% trans "This segment has been active for" %} + {{ segment.enable_date|days_since:segment.disable_date }} {% trans "day" %}{{ segment.enable_date|days_since:segment.disable_date|pluralize }} +
  • +
+ +
+ +
    +
  • + {% trans "The visitor must match" %} + {% if segment.match_any %} + {% trans "Any rule" %} + {% else %} + {% trans "All rules" %} + {% endif %} +
  • + +
  • + {% trans "The persistence of this segment is" %} + {% if segment.persistent %} + {% trans "Persistent" %} + {% else %} + {% trans "Fleeting" %} + {% endif %} +
  • + + {% for rule in segment.get_rules %} +
  • + {{ rule.description.title }} + {% if rule.description.code %} +
    {{ rule.description.value }}
    + {% else %} + {{ rule.description.value }} + {% endif %} +
  • + {% endfor %} +
+
+ + {% if user_can_create %} + + {% endif %} +
+ {% endfor %} + {% endif %} + + {% if user_can_create %} + {% blocktrans with url=view.create_url name=view.verbose_name %} + + Add a new {{name}} + + {% endblocktrans %} + {% endif %} +
+ {% endblock %} +
+ + {% block pagination %} + {% if paginator.num_pages > 1 %} + + {% endif %} + {% endblock %} + + {% endblock %} +
+
+{% endblock %} \ No newline at end of file diff --git a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html index 6813d94..b1c0d9e 100644 --- a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html +++ b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html @@ -1,111 +1,57 @@ -{% extends "modeladmin/index.html" %} +{% extends "modeladmin/wagtail_personalisation/segment/base.html" %} {% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %} {% block content_main %} -
-
- {% block content_cols %} +
+
+ {% block content_cols %} - {% block filters %} - {% if view.has_filters and all_count %} -
-

{% trans 'Filter' %}

- {% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %} -
- {% endif %} - {% endblock %} + {% block filters %} + {% if view.has_filters and all_count %} +
+

{% trans 'Filter' %}

+ {% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %} +
+ {% endif %} + {% endblock %} -
- {% block result_list %} -
- {% if all_count %} - {% for segment in object_list %} -
-

{{ segment }}

-
-
    -
  • - {% trans "This segmented has been visited" %} - {{ segment.visit_count|localize }} {% trans "time" %}{{ segment.visit_count|pluralize }} -
  • -
  • - {% trans "This segment has been active for" %} - {{ segment.enable_date|days_since:segment.disable_date }} {% trans "day" %}{{ segment.enable_date|days_since:segment.disable_date|pluralize }} -
  • -
- -
- -
    -
  • - {% trans "The visitor must match" %} - {% if segment.match_any %} - {% trans "Any rule" %} - {% else %} - {% trans "All rules" %} - {% endif %} -
  • - -
  • - {% trans "The persistence of this segment is" %} - {% if segment.persistent %} - {% trans "Persistent" %} - {% else %} - {% trans "Fleeting" %} - {% endif %} -
  • - - {% for rule in segment.get_rules %} -
  • - {{ rule.description.title }} - {% if rule.description.code %} -
    {{ rule.description.value }}
    - {% else %} - {{ rule.description.value }} - {% endif %} -
  • - {% endfor %} -
-
- - {% if user_can_create %} - - {% endif %} -
- {% endfor %} - {% endif %} - - {% if user_can_create %} - {% blocktrans with url=view.create_url name=view.verbose_name %} - - Add a new {{name}} - - {% endblocktrans %} +
+ {% block result_list %} + {% if not all_count %} +
+ {% if no_valid_parents %} +

{% 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 %}

+
    + {% for type in required_parent_types %}
  • {{ type|title }}
  • {% endfor %} +
+ {% else %} +

{% 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 %}
- {% endblock %} -
+ {% else %} + {% result_list %} + {% endif %} + {% endblock %} +
- {% block pagination %} + {% block pagination %} + {% endblock %} -
+ + {% endblock %}
- {% endblock %} +
+{% endblock %} diff --git a/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py b/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py index 1cc8fdf..d408aea 100644 --- a/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py +++ b/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py @@ -1,29 +1,10 @@ from django.template import Library -from django.utils import timezone + +from wagtail_personalisation.utils import count_active_days register = Library() @register.filter(name='days_since') def active_days(enable_date, disable_date): - """Return the number of days the segment has been active. - - :param enable_date: The date the segment was enabled - :type enable_date: timezone.datetime - :param disable_date: The date the segment was disabled - :type disable_date: timezone.datetime - :returns: The amount of days a segment is/has been active - :rtype: int - - """ - if enable_date is not None: - if disable_date is None or disable_date <= enable_date: - # There is no disable date, or it is not relevant. - delta = timezone.now() - enable_date - return delta.days - if disable_date > enable_date: - # There is a disable date and it is relevant. - delta = disable_date - enable_date - return delta.days - - return 0 + return count_active_days(enable_date, disable_date) diff --git a/src/wagtail_personalisation/utils.py b/src/wagtail_personalisation/utils.py index 5d420bb..d42876f 100644 --- a/src/wagtail_personalisation/utils.py +++ b/src/wagtail_personalisation/utils.py @@ -1,5 +1,7 @@ import time +from django.utils import timezone + def impersonate_other_page(page, other_page): """Function to change the page metadata so the user gets to see the @@ -32,3 +34,27 @@ def create_segment_dictionary(segment): "timestamp": int(time.time()), "persistent": segment.persistent } + + +def count_active_days(enable_date, disable_date): + """Return the number of days the segment has been active. + + :param enable_date: The date the segment was enabled + :type enable_date: timezone.datetime + :param disable_date: The date the segment was disabled + :type disable_date: timezone.datetime + :returns: The amount of days a segment is/has been active + :rtype: int + + """ + if enable_date is not None: + if disable_date is None or disable_date <= enable_date: + # There is no disable date, or it is not relevant. + delta = timezone.now() - enable_date + return delta.days + if disable_date > enable_date: + # There is a disable date and it is relevant. + delta = disable_date - enable_date + return delta.days + + return 0 diff --git a/src/wagtail_personalisation/views.py b/src/wagtail_personalisation/views.py index a31e884..f5134c2 100644 --- a/src/wagtail_personalisation/views.py +++ b/src/wagtail_personalisation/views.py @@ -1,7 +1,10 @@ from __future__ import absolute_import, unicode_literals +from django import forms + from django.http import HttpResponseForbidden, HttpResponseRedirect from django.shortcuts import get_object_or_404, reverse +from django.utils.translation import ugettext_lazy as _ from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register from wagtail.contrib.modeladmin.views import IndexView @@ -13,19 +16,48 @@ class SegmentModelIndexView(IndexView): pass +class SegmentModelDashboardView(IndexView): + """Placeholder for additional dashboard functionality.""" + def media(self): + return forms.Media( + css={'all': ['css/dashboard.css']}, + js=['js/commons.js', 'js/dashboard.js'] + ) + + def get_template_names(self): + return [ + 'modeladmin/wagtail_personalisation/segment/dashboard.html', + 'modeladmin/index.html' + ] + + @modeladmin_register class SegmentModelAdmin(ModelAdmin): """The model admin for the Segments administration interface.""" model = Segment index_view_class = SegmentModelIndexView + dashboard_view_class = SegmentModelDashboardView menu_icon = 'group' add_to_settings_menu = False - list_display = ('status', 'name', 'create_date', 'edit_date') + list_display = ('name', 'visits', 'active_days', 'status') index_view_extra_js = ['js/commons.js', 'js/index.js'] index_view_extra_css = ['css/index.css'] form_view_extra_js = ['js/commons.js', 'js/form.js'] form_view_extra_css = ['css/form.css'] + def index_view(self, request): + kwargs = {'model_admin': self} + view_class = self.index_view_class + return view_class.as_view(**kwargs)(request) + + def visits(self, obj): + return _("{visits} visits").format( + visits=obj.visit_count) + + def active_days(self, obj): + return _("{days} days").format( + days=obj.get_active_days()) + def toggle(request, segment_id): """Toggle the status of the selected segment.