Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

613 linhas
17 KiB

  1. frappe.provide("frappe.setup");
  2. frappe.provide("frappe.setup.events");
  3. frappe.provide("frappe.ui");
  4. frappe.setup = {
  5. slides: [],
  6. events: {},
  7. data: {},
  8. utils: {},
  9. domains: [],
  10. on: function(event, fn) {
  11. if(!frappe.setup.events[event]) {
  12. frappe.setup.events[event] = [];
  13. }
  14. frappe.setup.events[event].push(fn);
  15. },
  16. add_slide: function(slide) {
  17. frappe.setup.slides.push(slide);
  18. },
  19. run_event: function(event) {
  20. $.each(frappe.setup.events[event] || [], function(i, fn) {
  21. fn();
  22. });
  23. }
  24. }
  25. frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
  26. let requires = (frappe.boot.setup_wizard_requires || []);
  27. frappe.require(requires, function() {
  28. frappe.call({
  29. method: "frappe.desk.page.setup_wizard.setup_wizard.load_languages",
  30. freeze: true,
  31. callback: function(r) {
  32. frappe.setup.data.lang = r.message;
  33. frappe.setup.run_event("before_load");
  34. var wizard_settings = {
  35. parent: wrapper,
  36. slides: frappe.setup.slides,
  37. slide_class: frappe.setup.SetupWizardSlide,
  38. unidirectional: 1,
  39. before_load: ($footer) => {
  40. $footer.find('.next-btn').removeClass('btn-default')
  41. .addClass('btn-primary');
  42. $footer.find('.text-right').prepend(
  43. $(`<a class="complete-btn btn btn-sm primary">
  44. ${__("Complete Setup")}</a>`));
  45. }
  46. }
  47. frappe.wizard = new frappe.setup.SetupWizard(wizard_settings);
  48. frappe.setup.run_event("after_load");
  49. // frappe.wizard.values = test_values_edu;
  50. let route = frappe.get_route();
  51. if(route) {
  52. frappe.wizard.show_slide(route[1]);
  53. }
  54. }
  55. });
  56. });
  57. };
  58. frappe.pages['setup-wizard'].on_page_show = function(wrapper) {
  59. if(frappe.get_route()[1]) {
  60. frappe.wizard && frappe.wizard.show_slide(frappe.get_route()[1]);
  61. }
  62. };
  63. frappe.setup.on("before_load", function() {
  64. // load slides
  65. frappe.setup.slides_settings.forEach((s) => {
  66. if(!(s.name==='user' && frappe.boot.developer_mode)) {
  67. // if not user slide with developer mode
  68. frappe.setup.add_slide(s);
  69. }
  70. });
  71. });
  72. frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {
  73. constructor(args = {}) {
  74. super(args);
  75. $.extend(this, args);
  76. this.welcomed = true;
  77. this.page_name = "setup-wizard";
  78. frappe.set_route("setup-wizard/0");
  79. }
  80. make() {
  81. super.make();
  82. this.container.addClass("container setup-wizard-slide with-form");
  83. this.$next_btn.addClass('action');
  84. this.$complete_btn = this.$footer.find('.complete-btn').addClass('action');
  85. this.setup_keyboard_nav();
  86. }
  87. setup_keyboard_nav() {
  88. $('body').on('keydown', this.handle_enter_press.bind(this));
  89. }
  90. disable_keyboard_nav() {
  91. $('body').off('keydown', this.handle_enter_press.bind(this));
  92. }
  93. handle_enter_press(e) {
  94. if (e.which === frappe.ui.keyCode.ENTER) {
  95. var $target = $(e.target);
  96. if($target.hasClass('prev-btn')) {
  97. $target.trigger('click');
  98. } else {
  99. this.container.find('.next-btn').trigger('click');
  100. e.preventDefault();
  101. }
  102. }
  103. }
  104. before_show_slide() {
  105. if(!this.welcomed) {
  106. frappe.set_route(this.page_name);
  107. return false;
  108. }
  109. return true;
  110. }
  111. show_slide(id) {
  112. if (id === this.slides.length) {
  113. // show_slide called on last slide
  114. this.action_on_complete();
  115. return;
  116. }
  117. super.show_slide(id);
  118. frappe.set_route(this.page_name, id + "");
  119. }
  120. show_hide_prev_next(id) {
  121. super.show_hide_prev_next(id);
  122. if (id + 1 === this.slides.length){
  123. this.$next_btn.removeClass("btn-primary").hide();
  124. this.$complete_btn.addClass("btn-primary").show()
  125. .on('click', this.action_on_complete.bind(this));
  126. } else {
  127. this.$next_btn.addClass("btn-primary").show();
  128. this.$complete_btn.removeClass("btn-primary").hide();
  129. }
  130. }
  131. refresh_slides() {
  132. // For Translations, etc.
  133. if(this.in_refresh_slides || !this.current_slide.set_values()) {
  134. return;
  135. }
  136. this.in_refresh_slides = true;
  137. this.update_values();
  138. frappe.setup.slides = [];
  139. frappe.setup.run_event("before_load");
  140. frappe.setup.slides = this.get_setup_slides_filtered_by_domain();
  141. this.slides = frappe.setup.slides;
  142. frappe.setup.run_event("after_load");
  143. // re-render all slide, only remake made slides
  144. $.each(this.slide_dict, (id, slide) => {
  145. if(slide.made) {
  146. this.made_slide_ids.push(id);
  147. }
  148. });
  149. this.made_slide_ids.push(this.current_id);
  150. this.setup();
  151. this.show_slide(this.current_id);
  152. setTimeout(() => {
  153. this.container.find('.form-control').first().focus();
  154. }, 200);
  155. this.in_refresh_slides = false;
  156. }
  157. action_on_complete() {
  158. if (!this.current_slide.set_values()) return;
  159. this.update_values();
  160. this.show_working_state();
  161. this.disable_keyboard_nav();
  162. this.listen_for_setup_stages();
  163. return frappe.call({
  164. method: "frappe.desk.page.setup_wizard.setup_wizard.setup_complete",
  165. args: {args: this.values},
  166. callback: (r) => {
  167. if(r.message.status === 'ok') {
  168. this.post_setup_success();
  169. } else if(r.message.fail !== undefined) {
  170. this.abort_setup(r.message.fail);
  171. }
  172. },
  173. error: this.abort_setup.bind(this, "Error in setup", true)
  174. });
  175. }
  176. post_setup_success() {
  177. this.set_setup_complete_message(__("Setup Complete"), __("Refreshing..."));
  178. if(frappe.setup.welcome_page) {
  179. localStorage.setItem("session_last_route", frappe.setup.welcome_page);
  180. }
  181. setTimeout(function() {
  182. // Reload
  183. window.location.href = '';
  184. }, 2000);
  185. }
  186. abort_setup(fail_msg, error=false) {
  187. this.$working_state.find('.state-icon-container').html('');
  188. fail_msg = fail_msg ? fail_msg : __("Failed to complete setup");
  189. if(error && !frappe.boot.developer_mode) {
  190. frappe.msgprint(`Don't worry. It's not you, it's us. We've
  191. received the issue details and will get back to you on the solution.
  192. Please feel free to contact us on support@erpnext.com in the meantime.`);
  193. }
  194. this.update_setup_message('Could not start up: ' + fail_msg);
  195. this.$working_state.find('.title').html('Setup failed');
  196. this.$abort_btn.show();
  197. }
  198. listen_for_setup_stages() {
  199. frappe.realtime.on("setup_task", (data) => {
  200. // console.log('data', data);
  201. if(data.stage_status) {
  202. // .html('Process '+ data.progress[0] + ' of ' + data.progress[1] + ': ' + data.stage_status);
  203. this.update_setup_message(data.stage_status);
  204. this.set_setup_load_percent((data.progress[0]+1)/data.progress[1] * 100);
  205. }
  206. if(data.fail_msg) {
  207. this.abort_setup(data.fail_msg);
  208. }
  209. })
  210. }
  211. update_setup_message(message) {
  212. this.$working_state.find('.setup-message').html(message);
  213. }
  214. get_setup_slides_filtered_by_domain() {
  215. var filtered_slides = [];
  216. frappe.setup.slides.forEach(function(slide) {
  217. if(frappe.setup.domains) {
  218. let active_domains = frappe.setup.domains;
  219. if (!slide.domains ||
  220. slide.domains.filter(d => active_domains.includes(d)).length > 0) {
  221. filtered_slides.push(slide);
  222. }
  223. } else {
  224. filtered_slides.push(slide);
  225. }
  226. })
  227. return filtered_slides;
  228. }
  229. show_working_state() {
  230. this.container.hide();
  231. frappe.set_route(this.page_name);
  232. this.$working_state = this.get_message(
  233. __("Setting up your system"),
  234. __("Starting Frappé ...")).appendTo(this.parent);
  235. this.attach_abort_button();
  236. this.current_id = this.slides.length;
  237. this.current_slide = null;
  238. }
  239. attach_abort_button() {
  240. this.$abort_btn = $(`<button class='btn btn-default btn-xs text-muted'
  241. style="margin-bottom: 30px;">${__('Retry')}</button>`);
  242. this.$working_state.find('.content').append(this.$abort_btn);
  243. this.$abort_btn.on('click', () => {
  244. $(this.parent).find('.setup-in-progress').remove();
  245. this.container.show();
  246. frappe.set_route(this.page_name, this.slides.length - 1);
  247. });
  248. this.$abort_btn.hide();
  249. }
  250. get_message(title, message="") {
  251. const loading_html = `<div class="progress-chart" style ="width: 150px;">
  252. <div class="progress" style="margin-top: 70px; margin-bottom: 0px">
  253. <div class="progress-bar" style="width: 2%; background-color: #5e64ff;"></div>
  254. </div>
  255. </div>`;
  256. return $(`<div class="slides-wrapper setup-wizard-slide setup-in-progress">
  257. <div class="content text-center">
  258. <p class="title lead">${title}</p>
  259. <div class="state-icon-container">${loading_html}</div>
  260. <p class="setup-message text-muted" style="margin: 30px 0px;">${message}</p>
  261. </div>
  262. </div>`);
  263. }
  264. set_setup_complete_message(title, message) {
  265. this.$working_state.find('.title').html(title);
  266. this.$working_state.find('.setup-message').html(message);
  267. }
  268. set_setup_load_percent(percent) {
  269. this.$working_state.find('.progress-bar').css({"width": percent + "%"});
  270. }
  271. };
  272. frappe.setup.SetupWizardSlide = class SetupWizardSlide extends frappe.ui.Slide {
  273. constructor(slide = null) {
  274. super(slide);
  275. }
  276. make() {
  277. super.make();
  278. this.set_init_values();
  279. this.reset_action_button_state();
  280. }
  281. set_init_values () {
  282. var me = this;
  283. // set values from frappe.setup.values
  284. if(frappe.wizard.values && this.fields) {
  285. this.fields.forEach(function(f) {
  286. var value = frappe.wizard.values[f.fieldname];
  287. if(value) {
  288. me.get_field(f.fieldname).set_input(value);
  289. }
  290. });
  291. }
  292. }
  293. };
  294. // Frappe slides settings
  295. // ======================================================
  296. frappe.setup.slides_settings = [
  297. {
  298. // Welcome (language) slide
  299. name: "welcome",
  300. title: __("Hello!"),
  301. icon: "fa fa-world",
  302. // help: __("Let's prepare the system for first use."),
  303. fields: [
  304. { fieldname: "language", label: __("Your Language"),
  305. fieldtype: "Select", reqd: 1}
  306. ],
  307. onload: function(slide) {
  308. this.setup_fields(slide);
  309. var language_field = slide.get_field("language");
  310. language_field.set_input(frappe.setup.data.default_language || "English");
  311. if (!frappe.setup._from_load_messages) {
  312. language_field.$input.trigger("change");
  313. }
  314. delete frappe.setup._from_load_messages;
  315. moment.locale("en");
  316. },
  317. setup_fields: function(slide) {
  318. frappe.setup.utils.setup_language_field(slide);
  319. frappe.setup.utils.bind_language_events(slide);
  320. },
  321. },
  322. {
  323. // Region slide
  324. name: 'region',
  325. title: __("Select Your Region"),
  326. icon: "fa fa-flag",
  327. // help: __("Select your Country, Time Zone and Currency"),
  328. fields: [
  329. { fieldname: "country", label: __("Your Country"), reqd:1,
  330. fieldtype: "Select" },
  331. { fieldtype: "Section Break" },
  332. { fieldname: "timezone", label: __("Time Zone"), reqd:1,
  333. fieldtype: "Select" },
  334. { fieldtype: "Column Break" },
  335. { fieldname: "currency", label: __("Currency"), reqd:1,
  336. fieldtype: "Select" }
  337. ],
  338. onload: function(slide) {
  339. if(frappe.setup.data.regional_data) {
  340. this.setup_fields(slide);
  341. } else {
  342. frappe.setup.utils.load_regional_data(slide, this.setup_fields);
  343. }
  344. },
  345. setup_fields: function(slide) {
  346. frappe.setup.utils.setup_region_fields(slide);
  347. frappe.setup.utils.bind_region_events(slide);
  348. }
  349. },
  350. {
  351. // Profile slide
  352. name: 'user',
  353. title: __("The First User: You"),
  354. icon: "fa fa-user",
  355. fields: [
  356. { "fieldtype":"Attach Image", "fieldname":"attach_user_image",
  357. label: __("Attach Your Picture"), is_private: 0, align: 'center'},
  358. { "fieldname": "full_name", "label": __("Full Name"), "fieldtype": "Data",
  359. reqd:1},
  360. { "fieldname": "email", "label": __("Email Address") + ' (' + __("Will be your login ID") + ')',
  361. "fieldtype": "Data", "options":"Email"},
  362. { "fieldname": "password", "label": __("Password"), "fieldtype": "Password" }
  363. ],
  364. // help: __('The first user will become the System Manager (you can change this later).'),
  365. onload: function(slide) {
  366. if(frappe.session.user!=="Administrator") {
  367. slide.form.fields_dict.email.$wrapper.toggle(false);
  368. slide.form.fields_dict.password.$wrapper.toggle(false);
  369. // remove password field
  370. delete slide.form.fields_dict.password;
  371. if(frappe.boot.user.first_name || frappe.boot.user.last_name) {
  372. slide.form.fields_dict.full_name.set_input(
  373. [frappe.boot.user.first_name, frappe.boot.user.last_name].join(' ').trim());
  374. }
  375. var user_image = frappe.get_cookie("user_image");
  376. var $attach_user_image = slide.form.fields_dict.attach_user_image.$wrapper;
  377. if(user_image) {
  378. $attach_user_image.find(".missing-image").toggle(false);
  379. $attach_user_image.find("img").attr("src", decodeURIComponent(user_image));
  380. $attach_user_image.find(".img-container").toggle(true);
  381. }
  382. delete slide.form.fields_dict.email;
  383. } else {
  384. slide.form.fields_dict.email.df.reqd = 1;
  385. slide.form.fields_dict.email.refresh();
  386. slide.form.fields_dict.password.df.reqd = 1;
  387. slide.form.fields_dict.password.refresh();
  388. frappe.setup.utils.load_user_details(slide, this.setup_fields);
  389. }
  390. },
  391. setup_fields: function(slide) {
  392. if(frappe.setup.data.full_name) {
  393. slide.form.fields_dict.full_name.set_input(frappe.setup.data.full_name);
  394. }
  395. if(frappe.setup.data.email) {
  396. let email = frappe.setup.data.email;
  397. slide.form.fields_dict.email.set_input(email);
  398. if (frappe.get_gravatar(email, 200)) {
  399. var $attach_user_image = slide.form.fields_dict.attach_user_image.$wrapper;
  400. $attach_user_image.find(".missing-image").toggle(false);
  401. $attach_user_image.find("img").attr("src", frappe.get_gravatar(email, 200));
  402. $attach_user_image.find(".img-container").toggle(true);
  403. }
  404. }
  405. },
  406. }
  407. ];
  408. frappe.setup.utils = {
  409. load_regional_data: function(slide, callback) {
  410. frappe.call({
  411. method:"frappe.geo.country_info.get_country_timezone_info",
  412. callback: function(data) {
  413. frappe.setup.data.regional_data = data.message;
  414. callback(slide);
  415. }
  416. });
  417. },
  418. load_user_details: function(slide, callback) {
  419. frappe.call({
  420. method: "frappe.desk.page.setup_wizard.setup_wizard.load_user_details",
  421. freeze: true,
  422. callback: function(r) {
  423. frappe.setup.data.full_name = r.message.full_name;
  424. frappe.setup.data.email = r.message.email;
  425. callback(slide);
  426. }
  427. })
  428. },
  429. setup_language_field: function(slide) {
  430. var language_field = slide.get_field("language");
  431. language_field.df.options = frappe.setup.data.lang.languages;
  432. language_field.refresh();
  433. },
  434. setup_region_fields: function(slide) {
  435. /*
  436. Set a slide's country, timezone and currency fields
  437. */
  438. var data = frappe.setup.data.regional_data;
  439. var country_field = slide.get_field('country');
  440. slide.get_input("country").empty()
  441. .add_options([""].concat(Object.keys(data.country_info).sort()));
  442. slide.get_input("currency").empty()
  443. .add_options(frappe.utils.unique([""].concat($.map(data.country_info,
  444. function(opts, country) { return opts.currency; }))).sort());
  445. slide.get_input("timezone").empty()
  446. .add_options([""].concat(data.all_timezones));
  447. // set values if present
  448. if(frappe.wizard.values.country) {
  449. country_field.set_input(frappe.wizard.values.country);
  450. } else if (data.default_country) {
  451. country_field.set_input(data.default_country);
  452. }
  453. if(frappe.wizard.values.currency) {
  454. slide.get_field("currency").set_input(frappe.wizard.values.currency);
  455. }
  456. if(frappe.wizard.values.timezone) {
  457. slide.get_field("timezone").set_input(frappe.wizard.values.timezone);
  458. }
  459. },
  460. bind_language_events: function(slide) {
  461. slide.get_input("language").unbind("change").on("change", function() {
  462. clearTimeout (slide.language_call_timeout);
  463. slide.language_call_timeout = setTimeout (() => {
  464. var lang = $(this).val() || "English";
  465. frappe._messages = {};
  466. frappe.call({
  467. method: "frappe.desk.page.setup_wizard.setup_wizard.load_messages",
  468. freeze: true,
  469. args: {
  470. language: lang
  471. },
  472. callback: function(r) {
  473. frappe.setup._from_load_messages = true;
  474. frappe.wizard.refresh_slides();
  475. }
  476. });
  477. }, 500);
  478. });
  479. },
  480. bind_region_events: function(slide) {
  481. /*
  482. Bind a slide's country, timezone and currency fields
  483. */
  484. slide.get_input("country").on("change", function() {
  485. var country = slide.get_input("country").val();
  486. var $timezone = slide.get_input("timezone");
  487. var data = frappe.setup.data.regional_data;
  488. $timezone.empty();
  489. // add country specific timezones first
  490. if(country) {
  491. var timezone_list = data.country_info[country].timezones || [];
  492. $timezone.add_options(timezone_list.sort());
  493. slide.get_field("currency").set_input(data.country_info[country].currency);
  494. slide.get_field("currency").$input.trigger("change");
  495. }
  496. // add all timezones at the end, so that user has the option to change it to any timezone
  497. $timezone.add_options([""].concat(data.all_timezones));
  498. slide.get_field("timezone").set_input($timezone.val());
  499. // temporarily set date format
  500. frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format
  501. || "dd-mm-yyyy");
  502. });
  503. slide.get_input("currency").on("change", function() {
  504. var currency = slide.get_input("currency").val();
  505. if (!currency) return;
  506. frappe.model.with_doc("Currency", currency, function() {
  507. frappe.provide("locals.:Currency." + currency);
  508. var currency_doc = frappe.model.get_doc("Currency", currency);
  509. var number_format = currency_doc.number_format;
  510. if (number_format==="#.###") {
  511. number_format = "#.###,##";
  512. } else if (number_format==="#,###") {
  513. number_format = "#,###.##"
  514. }
  515. frappe.boot.sysdefaults.number_format = number_format;
  516. locals[":Currency"][currency] = $.extend({}, currency_doc);
  517. });
  518. });
  519. },
  520. };