7

Splits segment dashboard view and set default table view

This commit is contained in:
Jasper Berghoef
2017-05-31 11:52:37 +02:00
committed by Michael van Tellingen
parent cc38634519
commit 45f2de62ea
28 changed files with 641 additions and 301 deletions

1
frontend/js/dashboard.js Normal file
View File

@ -0,0 +1 @@
import '../scss/dashboard.scss';

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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*/

View File

@ -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":""}

View File

@ -0,0 +1,2 @@
/*# sourceMappingURL=index.css.map*/

View File

@ -0,0 +1 @@
{"version":3,"sources":[],"names":[],"mappings":"","file":"../css/index.css","sourceRoot":""}

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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":""}

View File

@ -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

View File

@ -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":""}

View File

@ -14,6 +14,7 @@ from wagtail.wagtailcore.models import Page
from wagtail_personalisation.forms import AdminPersonalisablePageForm from wagtail_personalisation.forms import AdminPersonalisablePageForm
from wagtail_personalisation.rules import AbstractBaseRule from wagtail_personalisation.rules import AbstractBaseRule
from wagtail_personalisation.utils import count_active_days
@python_2_unicode_compatible @python_2_unicode_compatible
@ -65,6 +66,10 @@ class Segment(ClusterableModel):
"""Return a string with a slug for the segment.""" """Return a string with a slug for the segment."""
return slugify(self.name.lower()) 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): def get_rules(self):
"""Retrieve all rules in the segment.""" """Retrieve all rules in the segment."""
rules = AbstractBaseRule.__subclasses__() rules = AbstractBaseRule.__subclasses__()

View File

@ -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 %}
<header class="nice-padding hasform">
<div class="row">
<div class="left">
<div class="col">
{% block h1 %}<h1 {% if view.header_icon %}class="icon icon-{{ view.header_icon }}"{% endif %}>{{ view.get_page_title }}<span></span></h1>{% endblock %}
</div>
{% block search %}{% search_form %}{% endblock %}
</div>
{% block header_extra %}
{% if user_can_create %}
<div class="right">
<div class="addbutton">
{% include 'modeladmin/includes/button.html' with button=view.button_helper.add_button %}
</div>
</div>
{% endif %}
{% endblock %}
</div>
</header>
{% endblock %}

View File

@ -0,0 +1,111 @@
{% extends "modeladmin/wagtail_personalisation/segment/base.html" %}
{% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %}
{% block content_main %}
<div>
<div class="row">
{% block content_cols %}
{% block filters %}
{% if view.has_filters and all_count %}
<div class="changelist-filter col3">
<h2>{% trans 'Filter' %}</h2>
{% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %}
</div>
{% endif %}
{% endblock %}
<div>
{% block result_list %}
<div class="nice-padding block_container">
{% if all_count %}
{% for segment in object_list %}
<div class="block block--{{ segment.status }}" onclick="location.href = 'edit/{{ segment.pk }}'">
<h2>{{ segment }}</h2>
<div class="inspect_container">
<ul class="inspect segment_stats">
<li class="visit_stat">
{% trans "This segmented has been visited" %}
<span>{{ segment.visit_count|localize }} {% trans "time" %}{{ segment.visit_count|pluralize }}</span>
</li>
<li class="days_stat">
{% trans "This segment has been active for" %}
<span>{{ segment.enable_date|days_since:segment.disable_date }} {% trans "day" %}{{ segment.enable_date|days_since:segment.disable_date|pluralize }}</span>
</li>
</ul>
<hr />
<ul class="inspect segment_rules">
<li class="match_state {{ segment.match_any|yesno:"any,all" }}">
{% trans "The visitor must match" %}
{% if segment.match_any %}
<span>{% trans "Any rule" %}</span>
{% else %}
<span>{% trans "All rules" %}</span>
{% endif %}
</li>
<li class="persistent_state {{ segment.persistent|yesno:"persistent,fleeting" }}">
{% trans "The persistence of this segment is" %}
{% if segment.persistent %}
<span title="{% trans "This segment persists in between visits" %}">{% trans "Persistent" %}</span>
{% else %}
<span title="{% trans "This segment is reevaluated on every visit" %}">{% trans "Fleeting" %}</span>
{% endif %}
</li>
{% for rule in segment.get_rules %}
<li class="{{ rule.encoded_name }}">
{{ rule.description.title }}
{% if rule.description.code %}
<pre>{{ rule.description.value }}</pre>
{% else %}
<span>{{ rule.description.value }}</span>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% if user_can_create %}
<ul class="block_actions">
{% if segment.status == "disabled" %}
<li><a href="{% url 'segment:toggle' segment.pk %}" title="{% trans "Enable this segment" %}">enable</a></li>
{% elif segment.status == "enabled" %}
<li><a href="{% url 'segment:toggle' segment.pk %}" title="{% trans "Disable this segment" %}">disable</a></li>
{% endif %}
<li><a href="edit/{{ segment.pk }}" title="{% trans "Configure this segment" %}">configure this</a></li>
</ul>
{% endif %}
</div>
{% endfor %}
{% endif %}
{% if user_can_create %}
{% blocktrans with url=view.create_url name=view.verbose_name %}
<a class="block suggestion" href="{{ url }}">
<span class="suggestive_text">Add a new {{name}}</span>
</a>
{% endblocktrans %}
{% endif %}
</div>
{% endblock %}
</div>
{% block pagination %}
{% if paginator.num_pages > 1 %}
<div class="pagination {% if view.has_filters and all_count %}col9{% else %}col12{% endif %}">
<p>{% blocktrans with page_obj.number as current_page and paginator.num_pages as num_pages %}Page {{ current_page }} of {{ num_pages }}.{% endblocktrans %}</p>
<ul>
{% pagination_link_previous page_obj view %}
{% pagination_link_next page_obj view %}
</ul>
</div>
{% endif %}
{% endblock %}
{% endblock %}
</div>
</div>
{% endblock %}

View File

@ -1,111 +1,57 @@
{% extends "modeladmin/index.html" %} {% extends "modeladmin/wagtail_personalisation/segment/base.html" %}
{% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %} {% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %}
{% block content_main %} {% block content_main %}
<div> <div>
<div class="row"> <div class="row">
{% block content_cols %} {% block content_cols %}
{% block filters %} {% block filters %}
{% if view.has_filters and all_count %} {% if view.has_filters and all_count %}
<div class="changelist-filter col3"> <div class="changelist-filter col3">
<h2>{% trans 'Filter' %}</h2> <h2>{% trans 'Filter' %}</h2>
{% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %} {% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %}
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
<div> <div class="result-list {% if view.has_filters and all_count %}col9{% else %}col12{% endif %}">
{% block result_list %} {% block result_list %}
<div class="nice-padding block_container"> {% if not all_count %}
{% if all_count %} <div class="nice-padding" style="margin-top:30px;">
{% for segment in object_list %} {% if no_valid_parents %}
<div class="block block--{{ segment.status }}" onclick="location.href = 'edit/{{ segment.pk }}'"> <p>{% 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 %}</p>
<h2>{{ segment }}</h2> <ul>
<div class="inspect_container"> {% for type in required_parent_types %}<li><b>{{ type|title }}</b></li>{% endfor %}
<ul class="inspect segment_stats"> </ul>
<li class="visit_stat"> {% else %}
{% trans "This segmented has been visited" %} <p>{% blocktrans with view.verbose_name_plural as name %}No {{ name }} have been created yet.{% endblocktrans %}
<span>{{ segment.visit_count|localize }} {% trans "time" %}{{ segment.visit_count|pluralize }}</span> {% if user_can_create %}
</li> {% blocktrans with view.create_url as url %}
<li class="days_stat"> Why not <a href="{{ url }}">add one</a>?
{% trans "This segment has been active for" %} {% endblocktrans %}
<span>{{ segment.enable_date|days_since:segment.disable_date }} {% trans "day" %}{{ segment.enable_date|days_since:segment.disable_date|pluralize }}</span> {% endif %}</p>
</li>
</ul>
<hr />
<ul class="inspect segment_rules">
<li class="match_state {{ segment.match_any|yesno:"any,all" }}">
{% trans "The visitor must match" %}
{% if segment.match_any %}
<span>{% trans "Any rule" %}</span>
{% else %}
<span>{% trans "All rules" %}</span>
{% endif %}
</li>
<li class="persistent_state {{ segment.persistent|yesno:"persistent,fleeting" }}">
{% trans "The persistence of this segment is" %}
{% if segment.persistent %}
<span title="{% trans "This segment persists in between visits" %}">{% trans "Persistent" %}</span>
{% else %}
<span title="{% trans "This segment is reevaluated on every visit" %}">{% trans "Fleeting" %}</span>
{% endif %}
</li>
{% for rule in segment.get_rules %}
<li class="{{ rule.encoded_name }}">
{{ rule.description.title }}
{% if rule.description.code %}
<pre>{{ rule.description.value }}</pre>
{% else %}
<span>{{ rule.description.value }}</span>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% if user_can_create %}
<ul class="block_actions">
{% if segment.status == "disabled" %}
<li><a href="{% url 'segment:toggle' segment.pk %}" title="{% trans "Enable this segment" %}">enable</a></li>
{% elif segment.status == "enabled" %}
<li><a href="{% url 'segment:toggle' segment.pk %}" title="{% trans "Disable this segment" %}">disable</a></li>
{% endif %}
<li><a href="edit/{{ segment.pk }}" title="{% trans "Configure this segment" %}">configure this</a></li>
</ul>
{% endif %}
</div>
{% endfor %}
{% endif %}
{% if user_can_create %}
{% blocktrans with url=view.create_url name=view.verbose_name %}
<a class="block suggestion" href="{{ url }}">
<span class="suggestive_text">Add a new {{name}}</span>
</a>
{% endblocktrans %}
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% else %}
</div> {% result_list %}
{% endif %}
{% endblock %}
</div>
{% block pagination %} {% block pagination %}
<div class="pagination {% if view.has_filters and all_count %}col9{% else %}col12{% endif %}">
<p>{% blocktrans with page_obj.number as current_page and paginator.num_pages as num_pages %}Page {{ current_page }} of {{ num_pages }}.{% endblocktrans %}</p>
{% if paginator.num_pages > 1 %} {% if paginator.num_pages > 1 %}
<div class="pagination {% if view.has_filters and all_count %}col9{% else %}col12{% endif %}">
<p>{% blocktrans with page_obj.number as current_page and paginator.num_pages as num_pages %}Page {{ current_page }} of {{ num_pages }}.{% endblocktrans %}</p>
<ul> <ul>
{% pagination_link_previous page_obj view %} {% pagination_link_previous page_obj view %}
{% pagination_link_next page_obj view %} {% pagination_link_next page_obj view %}
</ul> </ul>
</div>
{% endif %} {% endif %}
{% endblock %} </div>
{% endblock %} {% endblock %}
</div>
{% endblock %}
</div> </div>
{% endblock %} </div>
{% endblock %}

View File

@ -1,29 +1,10 @@
from django.template import Library from django.template import Library
from django.utils import timezone
from wagtail_personalisation.utils import count_active_days
register = Library() register = Library()
@register.filter(name='days_since') @register.filter(name='days_since')
def active_days(enable_date, disable_date): def active_days(enable_date, disable_date):
"""Return the number of days the segment has been active. return count_active_days(enable_date, disable_date)
: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

View File

@ -1,5 +1,7 @@
import time import time
from django.utils import timezone
def impersonate_other_page(page, other_page): def impersonate_other_page(page, other_page):
"""Function to change the page metadata so the user gets to see the """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()), "timestamp": int(time.time()),
"persistent": segment.persistent "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

View File

@ -1,7 +1,10 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from django import forms
from django.http import HttpResponseForbidden, HttpResponseRedirect from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.shortcuts import get_object_or_404, reverse 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.options import ModelAdmin, modeladmin_register
from wagtail.contrib.modeladmin.views import IndexView from wagtail.contrib.modeladmin.views import IndexView
@ -13,19 +16,48 @@ class SegmentModelIndexView(IndexView):
pass 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 @modeladmin_register
class SegmentModelAdmin(ModelAdmin): class SegmentModelAdmin(ModelAdmin):
"""The model admin for the Segments administration interface.""" """The model admin for the Segments administration interface."""
model = Segment model = Segment
index_view_class = SegmentModelIndexView index_view_class = SegmentModelIndexView
dashboard_view_class = SegmentModelDashboardView
menu_icon = 'group' menu_icon = 'group'
add_to_settings_menu = False 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_js = ['js/commons.js', 'js/index.js']
index_view_extra_css = ['css/index.css'] index_view_extra_css = ['css/index.css']
form_view_extra_js = ['js/commons.js', 'js/form.js'] form_view_extra_js = ['js/commons.js', 'js/form.js']
form_view_extra_css = ['css/form.css'] 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): def toggle(request, segment_id):
"""Toggle the status of the selected segment. """Toggle the status of the selected segment.