Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

447 righe
12 KiB

  1. import BaseTimeline from "../../../public/js/frappe/form/footer/base_timeline";
  2. frappe.provide('frappe.energy_points');
  3. class UserProfile {
  4. constructor(wrapper) {
  5. this.wrapper = $(wrapper);
  6. this.page = frappe.ui.make_app_page({
  7. parent: wrapper,
  8. });
  9. this.sidebar = this.wrapper.find('.layout-side-section');
  10. this.main_section = this.wrapper.find('.layout-main-section');
  11. this.wrapper.bind('show', () => {
  12. this.show();
  13. });
  14. }
  15. show() {
  16. let route = frappe.get_route();
  17. this.user_id = route[1] || frappe.session.user;
  18. frappe.dom.freeze(__('Loading user profile') + '...');
  19. frappe.db.exists('User', this.user_id).then(exists => {
  20. frappe.dom.unfreeze();
  21. if (exists) {
  22. this.make_user_profile();
  23. } else {
  24. frappe.msgprint(__('User does not exist'));
  25. }
  26. });
  27. }
  28. make_user_profile() {
  29. this.user = frappe.user_info(this.user_id);
  30. this.page.set_title(this.user.fullname);
  31. this.setup_user_search();
  32. this.main_section.empty().append(frappe.render_template('user_profile'));
  33. this.energy_points = 0;
  34. this.review_points = 0;
  35. this.rank = 0;
  36. this.month_rank = 0;
  37. this.render_user_details();
  38. this.render_points_and_rank();
  39. this.render_heatmap();
  40. this.render_line_chart();
  41. this.render_percentage_chart('type', 'Type Distribution');
  42. this.create_percentage_chart_filters();
  43. this.setup_user_activity_timeline();
  44. }
  45. setup_user_search() {
  46. this.$user_search_button = this.page.set_secondary_action(
  47. __('Change User'),
  48. () => this.show_user_search_dialog(),
  49. { icon: 'change', size: 'sm' }
  50. );
  51. }
  52. show_user_search_dialog() {
  53. let dialog = new frappe.ui.Dialog({
  54. title: __('Change User'),
  55. fields: [
  56. {
  57. fieldtype: 'Link',
  58. fieldname: 'user',
  59. options: 'User',
  60. label: __('User'),
  61. }
  62. ],
  63. primary_action_label: __('Go'),
  64. primary_action: ({ user }) => {
  65. dialog.hide();
  66. frappe.set_route('user-profile', user);
  67. }
  68. });
  69. dialog.show();
  70. }
  71. render_heatmap() {
  72. this.heatmap = new frappe.Chart('.performance-heatmap', {
  73. type: 'heatmap',
  74. countLabel: 'Energy Points',
  75. data: {},
  76. discreteDomains: 1,
  77. radius: 3,
  78. height: 150
  79. });
  80. this.update_heatmap_data();
  81. this.create_heatmap_chart_filters();
  82. }
  83. update_heatmap_data(date_from) {
  84. frappe.xcall('frappe.desk.page.user_profile.user_profile.get_energy_points_heatmap_data', {
  85. user: this.user_id,
  86. date: date_from || frappe.datetime.year_start(),
  87. }).then((r) => {
  88. this.heatmap.update({ dataPoints: r });
  89. });
  90. }
  91. render_line_chart() {
  92. this.line_chart_filters = [
  93. ['Energy Point Log', 'user', '=', this.user_id, false],
  94. ['Energy Point Log', 'type', '!=', 'Review', false]
  95. ];
  96. this.line_chart_config = {
  97. timespan: 'Last Month',
  98. time_interval: 'Daily',
  99. type: 'Line',
  100. value_based_on: 'points',
  101. chart_type: 'Sum',
  102. document_type: 'Energy Point Log',
  103. name: 'Energy Points',
  104. width: 'half',
  105. based_on: 'creation'
  106. };
  107. this.line_chart = new frappe.Chart('.performance-line-chart', {
  108. type: 'line',
  109. height: 200,
  110. data: {
  111. labels: [],
  112. datasets: [{}]
  113. },
  114. colors: ['purple'],
  115. axisOptions: {
  116. xIsSeries: 1
  117. }
  118. });
  119. this.update_line_chart_data();
  120. this.create_line_chart_filters();
  121. }
  122. update_line_chart_data() {
  123. this.line_chart_config.filters_json = JSON.stringify(this.line_chart_filters);
  124. frappe.xcall('frappe.desk.doctype.dashboard_chart.dashboard_chart.get', {
  125. chart: this.line_chart_config,
  126. no_cache: 1,
  127. }).then(chart => {
  128. this.line_chart.update(chart);
  129. });
  130. }
  131. // eslint-disable-next-line no-unused-vars
  132. render_percentage_chart(field, title) {
  133. frappe.xcall('frappe.desk.page.user_profile.user_profile.get_energy_points_percentage_chart_data', {
  134. user: this.user_id,
  135. field: field
  136. }).then(chart => {
  137. if (chart.labels.length) {
  138. this.percentage_chart = new frappe.Chart('.performance-percentage-chart', {
  139. type: 'percentage',
  140. data: {
  141. labels: chart.labels,
  142. datasets: chart.datasets
  143. },
  144. truncateLegends: 1,
  145. barOptions: {
  146. height: 11,
  147. depth: 1
  148. },
  149. height: 200,
  150. maxSlices: 8,
  151. colors: ['purple', 'blue', 'cyan', 'teal', 'pink', 'red', 'orange', 'yellow'],
  152. });
  153. } else {
  154. this.wrapper.find('.percentage-chart-container').hide();
  155. }
  156. });
  157. }
  158. create_line_chart_filters() {
  159. let filters = [
  160. {
  161. label: 'All',
  162. options: ['All', 'Auto', 'Criticism', 'Appreciation', 'Revert'],
  163. action: (selected_item) => {
  164. if (selected_item === 'All') {
  165. this.line_chart_filters = [
  166. ['Energy Point Log', 'user', '=', this.user_id, false],
  167. ['Energy Point Log', 'type', '!=', 'Review', false]
  168. ];
  169. } else {
  170. this.line_chart_filters[1] = ['Energy Point Log', 'type', '=', selected_item, false];
  171. }
  172. this.update_line_chart_data();
  173. }
  174. },
  175. {
  176. label: 'Last Month',
  177. options: ['Last Week', 'Last Month', 'Last Quarter', 'Last Year'],
  178. action: (selected_item) => {
  179. this.line_chart_config.timespan = selected_item;
  180. this.update_line_chart_data();
  181. }
  182. },
  183. {
  184. label: 'Daily',
  185. options: ['Daily', 'Weekly', 'Monthly'],
  186. action: (selected_item) => {
  187. this.line_chart_config.time_interval = selected_item;
  188. this.update_line_chart_data();
  189. }
  190. },
  191. ];
  192. frappe.dashboard_utils.render_chart_filters(filters, 'chart-filter', '.line-chart-options', 1);
  193. }
  194. create_percentage_chart_filters() {
  195. let filters = [
  196. {
  197. label: 'Type',
  198. options: ['Type', 'Reference Doctype', 'Rule'],
  199. fieldnames: ['type', 'reference_doctype', 'rule'],
  200. action: (selected_item, fieldname) => {
  201. let title = selected_item + ' Distribution';
  202. this.render_percentage_chart(fieldname, title);
  203. }
  204. },
  205. ];
  206. frappe.dashboard_utils.render_chart_filters(filters, 'chart-filter', '.percentage-chart-options');
  207. }
  208. create_heatmap_chart_filters() {
  209. let filters = [
  210. {
  211. label: frappe.dashboard_utils.get_year(frappe.datetime.now_date()),
  212. options: frappe.dashboard_utils.get_years_since_creation(frappe.boot.user.creation),
  213. action: (selected_item) => {
  214. this.update_heatmap_data(frappe.datetime.obj_to_str(selected_item));
  215. }
  216. },
  217. ];
  218. frappe.dashboard_utils.render_chart_filters(filters, 'chart-filter', '.heatmap-options');
  219. }
  220. edit_profile() {
  221. let edit_profile_dialog = new frappe.ui.Dialog({
  222. title: __('Edit Profile'),
  223. fields: [
  224. {
  225. fieldtype: 'Attach Image',
  226. fieldname: 'user_image',
  227. label: 'Profile Image',
  228. },
  229. {
  230. fieldtype: 'Data',
  231. fieldname: 'interest',
  232. label: 'Interests',
  233. },
  234. {
  235. fieldtype: 'Column Break'
  236. },
  237. {
  238. fieldtype: 'Data',
  239. fieldname: 'location',
  240. label: 'Location',
  241. },
  242. {
  243. fieldtype: 'Section Break',
  244. fieldname: 'Interest',
  245. },
  246. {
  247. fieldtype: 'Small Text',
  248. fieldname: 'bio',
  249. label: 'Bio',
  250. }
  251. ],
  252. primary_action: values => {
  253. edit_profile_dialog.disable_primary_action();
  254. frappe.xcall('frappe.desk.page.user_profile.user_profile.update_profile_info', {
  255. profile_info: values
  256. }).then(user => {
  257. user.image = user.user_image;
  258. this.user = Object.assign(values, user);
  259. edit_profile_dialog.hide();
  260. this.render_user_details();
  261. }).finally(() => {
  262. edit_profile_dialog.enable_primary_action();
  263. });
  264. },
  265. primary_action_label: __('Save')
  266. });
  267. edit_profile_dialog.set_values({
  268. user_image: this.user.image,
  269. location: this.user.location,
  270. interest: this.user.interest,
  271. bio: this.user.bio
  272. });
  273. edit_profile_dialog.show();
  274. }
  275. render_user_details() {
  276. this.sidebar.empty().append(frappe.render_template('user_profile_sidebar', {
  277. user_image: this.user.image,
  278. user_abbr: this.user.abbr,
  279. user_location: this.user.location,
  280. user_interest: this.user.interest,
  281. user_bio: this.user.bio,
  282. }));
  283. this.setup_user_profile_links();
  284. }
  285. setup_user_profile_links() {
  286. if (this.user_id !== frappe.session.user) {
  287. this.wrapper.find('.profile-links').hide();
  288. } else {
  289. this.wrapper.find('.edit-profile-link').on('click', () => {
  290. this.edit_profile();
  291. });
  292. this.wrapper.find('.user-settings-link').on('click', () => {
  293. this.go_to_user_settings();
  294. });
  295. }
  296. }
  297. get_user_rank() {
  298. return frappe.xcall('frappe.desk.page.user_profile.user_profile.get_user_rank', {
  299. user: this.user_id,
  300. }).then(r => {
  301. if (r.monthly_rank.length) this.month_rank = r.monthly_rank[0];
  302. if (r.all_time_rank.length) this.rank = r.all_time_rank[0];
  303. });
  304. }
  305. get_user_points() {
  306. return frappe.xcall(
  307. 'frappe.social.doctype.energy_point_log.energy_point_log.get_user_energy_and_review_points',
  308. {
  309. user: this.user_id,
  310. }
  311. ).then(r => {
  312. if (r[this.user_id]) {
  313. this.energy_points = r[this.user_id].energy_points;
  314. this.review_points = r[this.user_id].review_points;
  315. }
  316. });
  317. }
  318. render_points_and_rank() {
  319. let $profile_details = this.wrapper.find('.user-stats');
  320. let $profile_details_wrapper = this.wrapper.find('.user-stats-detail');
  321. const _get_stat_dom = (value, label, icon) => {
  322. return `<div class="user-stats-item mt-4">
  323. ${frappe.utils.icon(icon, "lg", "no-stroke")}
  324. <div>
  325. <div class="stat-value">${value}</div>
  326. <div class="stat-label">${label}</div>
  327. </div>
  328. </div>`;
  329. };
  330. this.get_user_rank().then(() => {
  331. this.get_user_points().then(() => {
  332. let html = $(`
  333. ${_get_stat_dom(this.energy_points, __('Energy Points'), "color-energy-points")}
  334. ${_get_stat_dom(this.review_points, __('Review Points'), "color-review-points")}
  335. ${_get_stat_dom(this.rank, __('Rank'), "color-rank")}
  336. ${_get_stat_dom(this.month_rank, __('Monthly Rank'), "color-monthly-rank")}
  337. `);
  338. $profile_details.append(html);
  339. $profile_details_wrapper.removeClass("hide");
  340. });
  341. });
  342. }
  343. go_to_user_settings() {
  344. frappe.set_route('Form', 'User', this.user_id);
  345. }
  346. setup_user_activity_timeline() {
  347. this.user_activity_timeline = new UserProfileTimeline({
  348. parent: this.wrapper.find('.recent-activity-list'),
  349. footer: this.wrapper.find('.recent-activity-footer'),
  350. user: this.user_id
  351. });
  352. this.user_activity_timeline.refresh();
  353. }
  354. }
  355. class UserProfileTimeline extends BaseTimeline {
  356. make() {
  357. super.make();
  358. this.activity_start = 0;
  359. this.activity_limit = 20;
  360. this.setup_show_more_activity();
  361. }
  362. prepare_timeline_contents() {
  363. return this.get_user_activity_data().then((activities) => {
  364. if (!activities.length) {
  365. this.show_more_button.hide();
  366. this.timeline_wrapper.html(`<div>${__('No activities to show')}</div>`);
  367. return;
  368. }
  369. this.show_more_button.toggle(activities.length === this.activity_limit);
  370. this.timeline_items = activities.map((activity) => this.get_activity_timeline_item(activity));
  371. });
  372. }
  373. get_user_activity_data() {
  374. return frappe.xcall('frappe.desk.page.user_profile.user_profile.get_energy_points_list', {
  375. start: this.activity_start,
  376. limit: this.activity_limit,
  377. user: this.user
  378. });
  379. }
  380. get_activity_timeline_item(data) {
  381. let icon = data.type == 'Appreciation' ? 'clap': data.type == 'Criticism' ? 'criticize': null;
  382. return {
  383. icon: icon,
  384. creation: data.creation,
  385. is_card: true,
  386. content: frappe.energy_points.format_history_log(data),
  387. };
  388. }
  389. setup_show_more_activity() {
  390. this.show_more_button = $(`<a class="show-more-activity-btn">${__('Show More Activity')}</a>`);
  391. this.show_more_button.hide();
  392. this.footer.append(this.show_more_button);
  393. this.show_more_button.on('click', () => this.show_more_activity());
  394. }
  395. show_more_activity() {
  396. this.activity_start += this.activity_limit;
  397. this.get_user_activity_data().then(activities => {
  398. if (!activities.length || activities.length < this.activity_limit) {
  399. this.show_more_button.hide();
  400. }
  401. let timeline_items = activities.map((activity) => this.get_activity_timeline_item(activity));
  402. timeline_items.map((item) => this.add_timeline_item(item, true));
  403. });
  404. }
  405. }
  406. frappe.provide('frappe.ui');
  407. frappe.ui.UserProfile = UserProfile;