diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py
index 583878a26e..07725423f4 100644
--- a/frappe/desk/notifications.py
+++ b/frappe/desk/notifications.py
@@ -30,7 +30,6 @@ def get_notifications():
"open_count_module": get_notifications_for_modules(config, notification_count),
"open_count_other": get_notifications_for_other(config, notification_count),
"targets": get_notifications_for_targets(config, notification_percent),
- "user_progress": get_user_progress_status(config),
"new_messages": get_new_messages()
}
@@ -157,19 +156,6 @@ def get_notifications_for_targets(config, notification_percent):
return doc_target_percents
-def get_user_progress_status(config):
- "User Progress status based on predefined setup slides"
- user_progress_status = {}
- for key, val in config.user_progress.iteritems():
- if "default" in val and val["default"] in frappe.defaults.get_defaults():
- doc_name = frappe.defaults.get_defaults()[val["default"]]
- field_value = frappe.db.get_value(val["doctype"], doc_name, val["field"])
- user_progress_status[key] = int(field_value > val["min_value"])
- elif "min_count" in val:
- user_progress_status[key] = int(frappe.db.count(val["doctype"]) > val["min_count"])
-
- return user_progress_status
-
def clear_notifications(user=None):
if frappe.flags.in_install:
return
@@ -222,7 +208,7 @@ def get_notification_config():
config = frappe._dict()
for notification_config in frappe.get_hooks().notification_config:
nc = frappe.get_attr(notification_config)()
- for key in ("for_doctype", "for_module", "for_other", "targets", "user_progress"):
+ for key in ("for_doctype", "for_module", "for_other", "targets"):
config.setdefault(key, {})
config[key].update(nc.get(key, {}))
return config
diff --git a/frappe/desk/user_progress.py b/frappe/desk/user_progress.py
index f22adf8890..15877dc767 100644
--- a/frappe/desk/user_progress.py
+++ b/frappe/desk/user_progress.py
@@ -15,3 +15,14 @@ def get_user_progress_slides():
slides += frappe.get_attr(fn)()
return slides
+
+@frappe.whitelist()
+def update_and_get_user_progress():
+ '''
+ Return setup progress action states (called via `update_and_get_user_progress` hook)
+ '''
+ states = {}
+ for fn in frappe.get_hooks('update_and_get_user_progress'):
+ states.update(frappe.get_attr(fn)())
+
+ return states
diff --git a/frappe/public/css/desk.css b/frappe/public/css/desk.css
index 2049e0a170..f8893f1f66 100644
--- a/frappe/public/css/desk.css
+++ b/frappe/public/css/desk.css
@@ -441,7 +441,7 @@ fieldset[disabled] .form-control {
}
}
@media (min-width: 768px) {
- .video-modal {
+ .video-modal .modal-dialog {
width: 700px;
}
}
@@ -1093,7 +1093,7 @@ input[type="checkbox"]:checked:before {
cursor: pointer;
}
.slides-wrapper .form {
- margin-top: 15px;
+ margin-top: 30px;
}
.slides-wrapper .form .form-layout {
margin-top: 0px;
@@ -1121,6 +1121,12 @@ input[type="checkbox"]:checked:before {
margin-top: 15px;
padding: 0px 7px;
}
+.slides-wrapper .footer .btn:not(:last-child) {
+ margin-right: 3px;
+}
+.slides-wrapper .footer a.btn.make-btn {
+ margin-right: 7px;
+}
.slides-wrapper .footer a.make-btn.disabled {
background-color: #b1bdca;
color: #fff;
@@ -1132,6 +1138,7 @@ input[type="checkbox"]:checked:before {
border: 1px solid #e5e5e5;
border-radius: 3px;
display: flex;
+ position: relative;
}
.cards-container .card-container.done {
background-color: #fafbfc;
@@ -1142,12 +1149,35 @@ input[type="checkbox"]:checked:before {
.cards-container .card-container.single_action {
cursor: pointer;
}
+.cards-container .card-container.single_action .image-overlay {
+ opacity: 0.1;
+}
.cards-container .title {
margin-top: 0px;
}
.cards-container .content {
font-size: 12px;
}
+.cards-container .img-container {
+ position: relative;
+}
+.cards-container .img-container .image-overlay {
+ position: absolute;
+ top: 0;
+ background-color: #fff;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+}
+.cards-container .img-container .fa-play-circle {
+ position: absolute;
+ font-size: 42px;
+ left: calc(33%);
+ top: 25%;
+ color: #fff;
+ opacity: 0.8;
+ text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.35);
+}
.cards-container img {
width: 195px;
}
@@ -1157,11 +1187,34 @@ input[type="checkbox"]:checked:before {
}
.cards-container .content-container {
padding: 10px;
- width: 516px;
+ width: 92%;
+ position: relative;
}
-.cards-container .actions {
+.cards-container .action-area {
margin-top: 15px;
+ font-size: 12px;
}
-.cards-container .actions button:not(:first-child) {
+.cards-container .action-area button:not(:first-child),
+.cards-container .action-area a:not(:first-child) {
margin-left: 15px;
}
+.cards-container .check {
+ position: absolute;
+ font-size: 24px;
+ right: 10px;
+ top: calc(40%);
+}
+.user-progress-dialog .modal-title {
+ display: inline-block;
+}
+.user-progress-dialog .progress-chart {
+ display: inline-block;
+ margin-left: 15px;
+ width: 60px;
+}
+.user-progress-dialog .progress {
+ margin-bottom: 0px;
+}
+.user-progress-dialog .progress-bar {
+ background-color: #98d85b;
+}
diff --git a/frappe/public/js/frappe/misc/help.js b/frappe/public/js/frappe/misc/help.js
index 754bc25185..1d3402c216 100644
--- a/frappe/public/js/frappe/misc/help.js
+++ b/frappe/public/js/frappe/misc/help.js
@@ -26,7 +26,7 @@ frappe.help.show_video = function(youtube_id, title) {
frameborder="0" allowfullscreen>' + (frappe.help_feedback_link || ""),
title || __("Help"));
- dialog.$wrapper.find(".modal-content").addClass("video-modal");
+ dialog.$wrapper.addClass("video-modal");
}
$("body").on("click", "a.help-link", function() {
diff --git a/frappe/public/js/frappe/ui/slides.js b/frappe/public/js/frappe/ui/slides.js
index a14702c111..71cbdf0d7b 100644
--- a/frappe/public/js/frappe/ui/slides.js
+++ b/frappe/public/js/frappe/ui/slides.js
@@ -25,7 +25,7 @@ frappe.ui.Slide = class Slide {
`);
this.property_components = [
- { card_property: 'content', component_name: '$content', class_name: 'content' },
- { card_property: 'image', component_name: '$img_container', class_name: 'img-container'},
- { card_property: 'done', component_name: '$check', class_name: 'check' },
- { card_property: 'actions', component_name: '$actions', class_name: 'actions' },
- { card_property: 'help_links', component_name: '$help_links', class_name: 'help-links' },
+ { card_properties: ['content'], component_name: '$content', class_name: 'content' },
+ { card_properties: ['image'], component_name: '$img_container', class_name: 'img-container'},
+ { card_properties: ['done'], component_name: '$check', class_name: 'check' },
+ { card_properties: ['actions', 'help_links'], component_name: '$action_area', class_name: 'action-area' }
];
}
setup() {
this.property_components.map(d => {
- this[d.component_name] = this.container.find('.' + d.class_name);
+ if(!this[d.component_name]) {
+ this[d.component_name] = this.container.find('.' + d.class_name);
+ }
});
if(this.data.video_id) {
- this.data.image = `http://img.youtube.com/vi/${this.data.video_id}/1.jpg`;
+ this.data.image = `http://img.youtube.com/vi/${this.data.video_id}/0.jpg`;
this.$img_container.find('.image-overlay').removeClass('hide');
+ this.$img_container.find('.fa-play-circle').removeClass('hide');
}
this.refresh();
@@ -59,9 +58,13 @@ frappe.ui.ActionCard = class {
refresh() {
// render according to props
- this.property_components.map(d => {
- if(!this.data[d.card_property]) {
- this[d.component_name].hide();
+ this.property_components.map(comp => {
+ let visible = 0;
+ comp.card_properties.map(d => {
+ if(this.data[d]) visible = 1;
+ });
+ if(!visible) {
+ this[comp.component_name].hide();
}
});
@@ -82,7 +85,7 @@ frappe.ui.ActionCard = class {
if(this.data.actions) {
this.data.actions.map(action => {
let $btn = $(``);
- this.$actions.append($btn);
+ this.$action_area.append($btn);
if(action.route) {
$btn.on('click', () => {
frappe.set_route(action.route);
@@ -97,7 +100,7 @@ frappe.ui.ActionCard = class {
if(this.data.help_links) {
this.data.help_links.map(link => {
let $link = $(`${link.label}`);
- this.$help_links.append($link);
+ this.$action_area.append($link);
});
}
}
@@ -127,8 +130,11 @@ frappe.setup.UserProgressSlide = class UserProgressSlide extends frappe.ui.Slide
}
setup_done_state() {
- this.$body.find(".form-wrapper").hide();
this.$body.find(".slide-help").hide();
+ this.$body.find(".form-wrapper").hide();
+ this.slides_footer.find('.next-btn').addClass('btn-primary');
+ this.slides_footer.find('.done-btn').hide();
+ this.$primary_btn.hide();
this.make_action_cards();
}
@@ -154,8 +160,10 @@ frappe.setup.UserProgressSlide = class UserProgressSlide extends frappe.ui.Slide
before_show() {
if(this.done) {
this.slides_footer.find('.next-btn').addClass('btn-primary');
+ this.slides_footer.find('.done-btn').hide();
} else {
this.slides_footer.find('.next-btn').removeClass('btn-primary');
+ this.slides_footer.find('.done-btn').show();
}
}
@@ -163,23 +171,16 @@ frappe.setup.UserProgressSlide = class UserProgressSlide extends frappe.ui.Slide
var me = this;
if(this.set_values()) {
frappe.call({
- method: me.method,
+ method: me.submit_method,
args: {args_data: me.values},
callback: function() {
me.done = 1;
- // hide Create button immediately, or show_slide again
- me.slides_footer.find('.next-btn').addClass('btn-primary');
- me.$primary_btn.hide();
me.refresh();
},
freeze: true
});
}
}
-
- mark_as_done() {
- // most hard
- }
};
frappe.setup.UserProgressDialog = class UserProgressDialog {
@@ -187,47 +188,120 @@ frappe.setup.UserProgressDialog = class UserProgressDialog {
slides = []
}) {
this.slides = slides;
- // Add a progress bar
- // show the last visited slide
- // Add a mark as done button
- // this.progress_state_dict = this.slides.map();
-
+ this.progress_state_dict = {};
+ this.slides.map(slide => {
+ this.progress_state_dict[slide.action_name] = slide.done || 0;
+ });
+ this.progress_percent = 0;
this.setup();
}
setup() {
this.dialog = new frappe.ui.Dialog({title: __("Complete Setup")});
+ this.$wrapper = $(this.dialog.$wrapper).addClass('user-progress-dialog');
+ this.$progress = $(``);
+ this.dialog.header.find('.col-xs-7').append(this.$progress);
this.slide_container = new frappe.ui.Slides({
parent: this.dialog.body,
slides: this.slides,
slide_class: frappe.setup.UserProgressSlide,
done_state: 1,
before_load: ($footer) => {
- $footer.find('.text-right').prepend(
- $(`
- ${__("Create")}`));
+ $footer.find('.text-right')
+ .prepend($(`
+ ${__("Mark as Done")}`))
+ .prepend($(`
+ ${__("Create")}`));
},
on_update: (completed, total) => {
let percent = completed * 100 / total;
+ this.$wrapper.find('.progress-bar').css({'width': percent + '%'});
$('.user-progress .progress-bar').css({'width': percent + '%'});
if(percent === 100) {
$(document).trigger("user-initial-setup-complete");
}
}
});
+
+ this.$wrapper.find('.done-btn').on('click', () => {
+ this.mark_as_done();
+ });
+
this.make_dismiss_button();
+ this.get_and_update_progress_state();
+ this.check_for_updates();
}
- listen_for_updates() {
- // on every notif 30 sec event
- this.update_progress_state();
+ mark_as_done() {
+ let me = this;
+ let current_slide = this.slide_container.current_slide;
+ frappe.call({
+ method: current_slide.mark_as_done_method,
+ args: {action_name: current_slide.action_name},
+ callback: function() {
+ current_slide.done = 1;
+ current_slide.refresh();
+ },
+ freeze: true
+ });
}
- update_progress_state() {
- // update states of slides and cards and refresh them
- // Update the progress bar in both the toolbar and the dialog
+ check_for_updates() {
+ this.updater = setInterval(() => {
+ this.get_and_update_progress_state();
+ }, 60000);
+ }
- // remove on_update from original slides container
+ get_and_update_progress_state() {
+ var me = this;
+ frappe.call({
+ method: "frappe.desk.user_progress.update_and_get_user_progress",
+ callback: function(r) {
+ // console.log("states", r.message);
+ let states = r.message;
+ let changed = 0;
+ let completed = 0;
+ Object.keys(states).map(action_name => {
+ if(states[action_name]) {
+ completed ++;
+ }
+ if(me.progress_state_dict[action_name] != states[action_name]) {
+ changed = 1;
+ me.progress_state_dict[action_name] = states[action_name];
+ }
+ });
+
+ if(changed) {
+ Object.keys(me.slide_container.slide_dict).map((id) => {
+ let slide = me.slide_container.slide_dict[id];
+ if(me.progress_state_dict[slide.action_name]) {
+ if(!slide.done) {
+ slide.done = 1;
+ slide.refresh();
+ }
+ }
+ });
+
+ }
+ me.progress_percent = completed / Object.keys(states).length * 100;
+ me.update_progress();
+ },
+ freeze: false
+ });
+ }
+
+ update_progress() {
+ this.update_progress_bars();
+ }
+
+ update_progress_bars() {
+ this.$wrapper.find('.progress-bar').css({'width': this.progress_percent + '%'});
+ $('.user-progress .progress-bar').css({'width': this.progress_percent + '%'});
+ if(this.progress_percent === 100) {
+ $(document).trigger("user-initial-setup-complete");
+ }
}
make_dismiss_button() {
@@ -245,6 +319,7 @@ frappe.setup.UserProgressDialog = class UserProgressDialog {
}
add_finish_slide_and_make_dismissable() {
+ clearInterval(this.updater);
this.$dismiss_button.removeClass('hide');
}
diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less
index 1ed397a3bf..08e223915a 100644
--- a/frappe/public/less/desk.less
+++ b/frappe/public/less/desk.less
@@ -246,7 +246,7 @@ textarea.form-control {
}
@media (min-width: 768px) {
- .video-modal {
+ .video-modal .modal-dialog {
width: 700px;
}
}
@@ -1036,7 +1036,7 @@ input[type="checkbox"] {
}
}
.form {
- margin-top: 15px;
+ margin-top: 30px;
.form-layout {
margin-top: 0px;
margin-bottom: 0px;
@@ -1067,6 +1067,14 @@ input[type="checkbox"] {
margin-top: 15px;
padding: 0px 7px;
+ .btn:not(:last-child) {
+ margin-right: 3px;
+ }
+
+ a.btn.make-btn {
+ margin-right: 7px;
+ }
+
a.make-btn.disabled {
background-color: #b1bdca;
color: #fff;
@@ -1083,6 +1091,7 @@ input[type="checkbox"] {
border: 1px solid #e5e5e5;
border-radius: 3px;
display: flex;
+ position: relative;
&.done {
background-color: #fafbfc;
@@ -1094,12 +1103,12 @@ input[type="checkbox"] {
&.single_action {
cursor: pointer;
-
- // .title {
- // color: blue;
+ .image-overlay {
+ opacity: 0.1;
+ }
+ // .fa-play-circle{
+ // opacity: 1;
// }
-
- // emphasize image overlay
}
}
@@ -1110,9 +1119,28 @@ input[type="checkbox"] {
.content {
font-size: 12px;
}
- // .img-container {
+ .img-container {
+ position: relative;
- // }
+ .image-overlay {
+ position: absolute;
+ top: 0;
+ background-color: #fff;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ }
+
+ .fa-play-circle {
+ position: absolute;
+ font-size: 42px;
+ left: calc(50% - 17px);
+ top: 25%;
+ color: #fff;
+ opacity: 0.8;
+ text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.35);
+ }
+ }
img {
width: 195px;
@@ -1125,14 +1153,41 @@ input[type="checkbox"] {
.content-container {
padding: 10px;
- width: 516px;
+ width: 92%;
+ position: relative;
}
- .actions {
+ .action-area {
margin-top: 15px;
+ font-size: 12px;
- button:not(:first-child) {
+ button:not(:first-child), a:not(:first-child) {
margin-left: 15px;
}
}
+
+ .check {
+ position: absolute;
+ font-size: 24px;
+ right: 10px;
+ top: calc(50% - 10px);
+ }
}
+
+// User Progress Dialog
+.user-progress-dialog {
+ .modal-title {
+ display: inline-block;
+ }
+ .progress-chart {
+ display: inline-block;
+ margin-left: 15px;
+ width: 60px;
+ }
+ .progress {
+ margin-bottom: 0px;
+ }
+ .progress-bar {
+ background-color: #98d85b;
+ }
+}
\ No newline at end of file