feat: remove onboarding slide completely

This commit is contained in:
Shivam Mishra 2020-04-24 13:51:26 +05:30
parent 8e71c8d4a9
commit daed4ff49d
14 changed files with 2 additions and 708 deletions

View file

@ -1,75 +0,0 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Onboarding Slide', {
refresh: function(frm) {
frm.toggle_reqd('ref_doctype', (frm.doc.slide_type=='Create' || frm.doc.slide_type=='Settings'));
frm.toggle_reqd('slide_module', (frm.doc.slide_type=='Information' || frm.doc.slide_type=='Continue'));
if (frm.doc.ref_doctype) frm.trigger('ref_doctype');
},
ref_doctype: function(frm) {
frm.set_query('ref_doctype', function() {
if (frm.doc.slide_type === 'Create') {
return {
filters: {
'issingle': 0,
'istable': 0
}
};
} else if (frm.doc.slide_type === 'Settings') {
return {
filters: {
'issingle': 1,
'istable': 0
}
};
}
});
//fetch mandatory fields automatically
if (frm.doc.ref_doctype) {
const ref = frm.doc.ref_doctype
frappe.model.with_doctype(ref, function() {
const meta = frappe.get_meta(ref)
const fields = meta.fields.filter(
df => !frappe.model.no_value_type.includes(df.fieldtype)
).map(df => {
return { label: `${df.label} (${df.fieldtype})`, value: df.fieldname }
});
frappe.meta.get_docfield("Onboarding Slide Field", "fieldname", frm.doc.name).options = [""].concat(fields);
refresh_field('slide_fields');
});
}
}
});
frappe.ui.form.on("Onboarding Slide Field", {
fieldtype: function(frm, doctype, name) {
var doc = frappe.get_doc(doctype, name);
if (['Section Break', 'Column Break', 'Page Break'].includes(doc.fieldtype)) {
doc.fieldname = '';
frm.refresh_field("slide_fields");
}
},
fieldname: function(frm, doctype, name) {
var doc = frappe.get_doc(doctype, name);
const ref = frm.doc.ref_doctype;
frappe.model.with_doctype(ref, function() {
const meta = frappe.get_meta(ref)
const fields = meta.fields.filter(df => doc.fieldname == df.fieldname);
if (fields.length) {
let df = fields[0];
doc.label = df.label;
doc.reqd = df.reqd;
doc.options = df.options;
doc.fieldtype = df.fieldtype;
}
refresh_field('slide_fields');
});
}
});

View file

@ -1,169 +0,0 @@
{
"actions": [],
"autoname": "field:slide_title",
"creation": "2019-11-13 14:39:56.834658",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"slide_title",
"app",
"column_break_4",
"image_src",
"slide_module",
"description_section_break",
"slide_desc",
"action_section_break",
"slide_type",
"column_break_6",
"max_count",
"add_more_button",
"section_break_18",
"ref_doctype",
"slide_fields",
"section_break_10",
"domains",
"column_break_12",
"help_links"
],
"fields": [
{
"fieldname": "slide_title",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Slide Title",
"reqd": 1,
"unique": 1
},
{
"fieldname": "slide_desc",
"fieldtype": "HTML Editor",
"label": "Slide Description"
},
{
"default": "3",
"depends_on": "add_more_button",
"description": "The amount of times you want to repeat the set of fields (eg: if you want 3 customers in the slide, set this field to 3. Only the first set of fields is shown as mandatory in the slide)",
"fieldname": "max_count",
"fieldtype": "Int",
"label": "Max Count"
},
{
"default": "0",
"depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'",
"fieldname": "add_more_button",
"fieldtype": "Check",
"label": "Add More Button"
},
{
"depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'",
"fieldname": "slide_fields",
"fieldtype": "Table",
"label": "Slide Fields",
"options": "Onboarding Slide Field"
},
{
"fieldname": "section_break_10",
"fieldtype": "Section Break"
},
{
"description": "Specify in what all domains should the slides show up. If nothing is specified the slide is shown in all domains by default.",
"fieldname": "domains",
"fieldtype": "Table",
"label": "Domains",
"options": "Has Domain"
},
{
"fieldname": "column_break_12",
"fieldtype": "Column Break"
},
{
"description": "Add a help video link just in case user has no idea about what to fill in the slide.",
"fieldname": "help_links",
"fieldtype": "Table",
"label": "Help Links",
"options": "Onboarding Slide Help Link"
},
{
"fieldname": "action_section_break",
"fieldtype": "Section Break",
"label": "Action Settings"
},
{
"description": "If Slide Type is Create or Settings there should be a 'create_onboarding_docs' method in the {ref_doctype}.py file bound to be executed after the slide is completed.",
"fieldname": "slide_type",
"fieldtype": "Select",
"label": "Slide Type",
"options": "Information\nCreate\nSettings\nContinue",
"reqd": 1
},
{
"fieldname": "column_break_6",
"fieldtype": "Column Break"
},
{
"fieldname": "app",
"fieldtype": "Select",
"label": "App",
"options": "Frappe\nERPNext",
"reqd": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fieldname": "image_src",
"fieldtype": "Data",
"label": "Slide Image Source"
},
{
"fieldname": "description_section_break",
"fieldtype": "Section Break",
"label": "Description"
},
{
"depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'",
"fieldname": "ref_doctype",
"fieldtype": "Link",
"label": "Reference Document Type",
"options": "DocType"
},
{
"depends_on": "eval:doc.slide_type=='Information' || doc.slide_type=='Continue'",
"fieldname": "slide_module",
"fieldtype": "Link",
"label": "Module",
"options": "Module Def"
},
{
"collapsible_depends_on": "eval:doc.slide_type=='Create' || doc.slide_type=='Settings'",
"fieldname": "section_break_18",
"fieldtype": "Section Break",
"label": "Fields"
}
],
"links": [],
"modified": "2020-04-14 17:50:54.458942",
"modified_by": "Administrator",
"module": "Desk",
"name": "Onboarding Slide",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View file

@ -1,127 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json
from frappe import _
from frappe.model.document import Document
from frappe.modules.export_file import export_to_files
class OnboardingSlide(Document):
def on_update(self):
if self.ref_doctype:
module = frappe.db.get_value('DocType', self.ref_doctype, 'module')
else:
module = self.slide_module
export_to_files(record_list=[['Onboarding Slide', self.name]], record_module=module)
def get_onboarding_slides_as_list():
slides = []
slide_docs = frappe.db.get_all('Onboarding Slide',
filters={'is_completed': 0},
or_filters={'slide_type': 'Continue'})
# to check if continue slide is required
first_slide = get_first_slide()
for entry in slide_docs:
# using get_doc because child table fields are not fetched in get_all
slide_doc = frappe.get_doc('Onboarding Slide', entry.name)
if frappe.scrub(slide_doc.app) in frappe.get_installed_apps():
slide = frappe._dict(
slide_type=slide_doc.slide_type,
title=slide_doc.slide_title,
help=slide_doc.slide_desc,
fields=slide_doc.slide_fields,
help_links=get_help_links(slide_doc),
add_more=slide_doc.add_more_button,
max_count=slide_doc.max_count,
image_src=get_slide_image(slide_doc),
ref_doctype=slide_doc.ref_doctype,
app=slide_doc.app
)
if slide.slide_type == 'Continue':
if is_continue_slide_required(first_slide):
slides.insert(0, slide)
else:
slides.append(slide)
return slides
@frappe.whitelist()
def get_onboarding_slides():
slides = []
slide_list = get_onboarding_slides_as_list()
active_domains = frappe.get_active_domains()
for slide in slide_list:
if not slide.domains or any(domain in active_domains for domain in slide.domains):
slides.append(slide)
return slides
def get_help_links(slide_doc):
links=[]
for link in slide_doc.help_links:
links.append({
'label': link.label,
'video_id': link.video_id
})
return links
def get_slide_image(slide_doc):
if slide_doc.image_src:
return slide_doc.image_src
return None
def is_continue_slide_required(first_slide):
# check if first slide itself is not completed
if not first_slide.is_completed:
return False
# check if there is any active slide which is not completed
return frappe.db.exists('Onboarding Slide', {
'is_completed': 0,
'slide_type': ('!=', 'Continue')
})
@frappe.whitelist()
def create_onboarding_docs(values, doctype=None, app=None, slide_type=None):
data = json.loads(values)
doc = frappe.new_doc(doctype)
try:
if hasattr(doc, 'create_onboarding_docs'):
doc.flags.ignore_validate = True
doc.flags.ignore_mandatory = True
doc.create_onboarding_docs(data)
else:
create_generic_onboarding_doc(data, doctype, slide_type)
except Exception:
pass
def create_generic_onboarding_doc(data, doctype, slide_type):
if slide_type == 'Settings':
doc = frappe.get_single(doctype)
for entry in data:
doc.set(entry, data.get(entry))
doc.save()
elif slide_type == 'Create':
doc = frappe.new_doc(doctype)
for entry in data:
doc.set(entry, data.get(entry))
doc.flags.ignore_validate = True
doc.flags.ignore_mandatory = True
doc.insert()
@frappe.whitelist()
def mark_slide_as_completed(slide_title):
frappe.db.set_value('Onboarding Slide', slide_title, 'is_completed', 1)
def get_first_slide():
slides = frappe.db.get_all('Onboarding Slide',
filters={'slide_type': ('!=', 'Continue')},
fields=['name', 'is_completed']
)
return slides[0]

View file

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestOnboardingSlide(unittest.TestCase):
pass

View file

@ -1,75 +0,0 @@
{
"actions": [],
"creation": "2019-11-13 13:35:08.617909",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"fieldname",
"label",
"fieldtype",
"align",
"placeholder",
"reqd",
"column_break_4",
"options"
],
"fields": [
{
"fieldname": "label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Label"
},
{
"fieldname": "fieldtype",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Fieldtype",
"options": "Attach\nAttach Image\nCheck\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nInt\nRating\nSelect\nLink\nSmall Text\nText\nText Editor\nSection Break\nColumn Break"
},
{
"fieldname": "fieldname",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Fieldname"
},
{
"fieldname": "align",
"fieldtype": "Select",
"label": "Align",
"options": "\ncenter\nleft\nright"
},
{
"fieldname": "placeholder",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Placeholder"
},
{
"default": "0",
"fieldname": "reqd",
"fieldtype": "Check",
"label": "Mandatory"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fieldname": "options",
"fieldtype": "Text",
"label": "Options"
}
],
"istable": 1,
"links": [],
"modified": "2020-04-14 15:47:59.295428",
"modified_by": "Administrator",
"module": "Desk",
"name": "Onboarding Slide Field",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View file

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class OnboardingSlideField(Document):
pass

View file

@ -1,35 +0,0 @@
{
"creation": "2019-11-19 12:22:42.805741",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"label",
"video_id"
],
"fields": [
{
"fieldname": "label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Label"
},
{
"fieldname": "video_id",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Video"
}
],
"istable": 1,
"modified": "2019-11-19 13:39:57.716248",
"modified_by": "Administrator",
"module": "Desk",
"name": "Onboarding Slide Help Link",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View file

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class OnboardingSlideHelpLink(Document):
pass

View file

@ -44,9 +44,6 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe
("data_migration", "data_migration_mapping"),
("data_migration", "data_migration_plan_mapping"),
("data_migration", "data_migration_plan"),
("desk", "onboarding_slide_field"),
("desk", "onboarding_slide_help_link"),
("desk", "onboarding_slide"),
("desk", "desk_card"),
("desk", "desk_chart"),
("desk", "desk_shortcut"),
@ -79,7 +76,7 @@ def get_doc_files(files, start_path, force=0, sync_everything = False, verbose=F
# load in sequence - warning for devs
document_types = ['doctype', 'page', 'report', 'dashboard_chart_source', 'print_format',
'website_theme', 'web_form', 'notification', 'print_style',
'data_migration_mapping', 'data_migration_plan', 'onboarding_slide', 'desk_page']
'data_migration_mapping', 'data_migration_plan', 'desk_page']
for doctype in document_types:
doctype_path = os.path.join(start_path, doctype)
if os.path.exists(doctype_path):

View file

@ -44,17 +44,4 @@ class LetterHead(Document):
frappe.db.set_default("default_letter_head_content", self.content)
else:
frappe.defaults.clear_default('letter_head', self.name)
frappe.defaults.clear_default("default_letter_head_content", self.content)
def create_onboarding_docs(self, args):
letterhead = args.get('letterhead')
if letterhead:
try:
frappe.get_doc({
'doctype': self.doctype,
'image': letterhead,
'letter_head_name': _('Standard'),
'is_default': 1
}).insert()
except frappe.NameError:
pass
frappe.defaults.clear_default("default_letter_head_content", self.content)

View file

@ -1,179 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.provide("frappe.setup");
frappe.provide("frappe.ui");
frappe.setup.OnboardingSlide = class OnboardingSlide extends frappe.ui.Slide {
constructor(slide = null) {
super(slide);
}
make() {
super.make();
this.$next_btn = this.slides_footer.find('.next-btn');
this.$complete_btn = this.slides_footer.find('.complete-btn');
this.$action_button = this.slides_footer.find('.next-btn');
if (this.help_links) {
this.$help_links = $(`<div class="text-center">
<div class="help-links"></div>
</div>`).appendTo(this.$body);
this.setup_help_links();
}
this.$skip_btn = this.slides_footer.find('.skip-btn').on('click', () => {
$('.onboarding-dialog').modal('toggle');
});
}
setup_form() {
super.setup_form();
const fields = this.get_atomic_fields();
// remove link indicator
fields.map((field) => {
if (field.fieldtype == 'Link') {
$('.link-btn').remove();
}
});
if (fields.length == 1) {
this.$form_wrapper.addClass("text-center");
} else {
this.$form_wrapper.removeClass("text-center");
}
}
before_show() {
if (this.id === 0) {
this.$next_btn.text(__('Let\'s Go'));
this.$skip_btn.removeClass('hide');
} else {
this.$next_btn.text(__('Next'));
this.$skip_btn.addClass('hide');
}
//last slide
if (this.is_last_slide()) {
this.$complete_btn.removeClass('hide').addClass('action primary');
this.$next_btn.removeClass('action primary');
this.$action_button = this.$complete_btn;
}
this.setup_action_button();
}
primary_action() {
if (this.set_values()) {
this.$action_button.addClass('disabled');
const primary_method = 'frappe.desk.doctype.onboarding_slide.onboarding_slide.create_onboarding_docs';
if (this.add_more) {
this.values.max_count = this.max_count;
}
frappe.call(primary_method, {
values: this.values,
doctype: this.ref_doctype,
app: this.app,
slide_type: this.slide_type
}).then(() => {
if (this.is_last_slide()) {
this.reset_is_first_startup();
$('.onboarding-dialog').modal('toggle');
frappe.msgprint({
message: __('You are all set up!'),
indicator: 'green',
title: __('Success')
});
}
});
}
}
unbind_primary_action() {
// unbind only action method as next button is same as create button in this setup wizard
this.$action_button.off('click.primary_action');
}
setup_help_links() {
this.help_links.map(link => {
let $link = $(
`<a target="_blank" class="small text-muted">${link.label || __("Need Help?")}</a>`
);
if (link.video_id) {
$link.on('click', () => {
frappe.help.show_video(link.video_id, link.label);
});
}
this.$help_links.append($link);
});
}
setup_action_button() {
if (this.slide_type === 'Create' || this.slide_type == 'Settings' || this.is_last_slide()) {
this.$action_button.addClass('primary');
} else {
this.$action_button.removeClass('primary');
}
this.$action_button.on('click', () => {
if (this.slide_type != 'Continue') {
this.mark_as_completed();
}
});
}
mark_as_completed() {
frappe.call({
method: 'frappe.desk.doctype.onboarding_slide.onboarding_slide.mark_slide_as_completed',
args: {slide_title: this.title},
callback: () => {},
freeze: true
});
}
reset_is_first_startup() {
frappe.call({
method: "frappe.desk.page.setup_wizard.setup_wizard.reset_is_first_startup",
args: {},
callback: () => {}
});
}
};
frappe.setup.OnboardingDialog = class OnboardingDialog {
constructor({
slides = []
}) {
this.slides = slides;
this.setup();
}
setup() {
this.dialog = new frappe.ui.Dialog({
static: true,
minimizable: false,
});
this.$wrapper = $(this.dialog.$wrapper).addClass('onboarding-dialog');
this.slide_container = new frappe.ui.Slides({
parent: this.dialog.body,
slides: this.slides,
slide_class: frappe.setup.OnboardingSlide,
unidirectional: 1,
before_load: ($footer) => {
$footer.find('.prev-btn').addClass('hide');
$footer.find('.next-btn').removeClass('btn-default').addClass('btn-primary action');
$footer.find('.prev-div').prepend(
$(`<a class="skip-btn text-muted btn btn-link btn-sm hide">
${__("Do It Later")}</a>`));
$footer.find('.next-div').prepend(
$(`<a class="complete-btn btn btn-primary btn-sm hide">
${__("Complete")}</a>`));
}
});
this.$wrapper.find('.modal-header').remove();
}
show() {
this.dialog.show();
}
};