From f1e420f6865c57e69bb34fa6f55264d19640e11a Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 14 Apr 2017 16:01:25 -0500 Subject: Refactor time tracking --- .../components/time_tracking/collapsed_state.js | 57 ++++++++ .../components/time_tracking/comparison_pane.js | 78 +++++++++++ .../components/time_tracking/estimate_only_pane.js | 15 +++ .../components/time_tracking/help_state.js | 26 ++++ .../time_tracking/issuable_time_tracking.js | 59 ++++++++ .../components/time_tracking/no_tracking_pane.js | 8 ++ .../components/time_tracking/spent_only_pane.js | 15 +++ .../components/time_tracking/time_tracker.js | 148 +++++++++++++++++++++ app/assets/javascripts/issuable/event_hub.js | 3 + app/assets/javascripts/issuable/issuable_bundle.js | 16 ++- .../issuable/services/issuable_service.js | 14 ++ .../time_tracking/components/collapsed_state.js | 42 ------ .../time_tracking/components/comparison_pane.js | 70 ---------- .../time_tracking/components/estimate_only_pane.js | 14 -- .../time_tracking/components/help_state.js | 25 ---- .../time_tracking/components/no_tracking_pane.js | 12 -- .../time_tracking/components/spent_only_pane.js | 14 -- .../time_tracking/components/time_tracker.js | 117 ---------------- .../issuable/time_tracking/time_tracking_bundle.js | 66 --------- app/assets/javascripts/main.js | 1 - app/assets/javascripts/subbable_resource.js | 51 ------- app/views/shared/issuable/_sidebar.html.haml | 14 +- spec/javascripts/issuable_time_tracker_spec.js | 2 +- spec/javascripts/subbable_resource_spec.js | 63 --------- 24 files changed, 445 insertions(+), 485 deletions(-) create mode 100644 app/assets/javascripts/issuable/components/time_tracking/collapsed_state.js create mode 100644 app/assets/javascripts/issuable/components/time_tracking/comparison_pane.js create mode 100644 app/assets/javascripts/issuable/components/time_tracking/estimate_only_pane.js create mode 100644 app/assets/javascripts/issuable/components/time_tracking/help_state.js create mode 100644 app/assets/javascripts/issuable/components/time_tracking/issuable_time_tracking.js create mode 100644 app/assets/javascripts/issuable/components/time_tracking/no_tracking_pane.js create mode 100644 app/assets/javascripts/issuable/components/time_tracking/spent_only_pane.js create mode 100644 app/assets/javascripts/issuable/components/time_tracking/time_tracker.js create mode 100644 app/assets/javascripts/issuable/event_hub.js create mode 100644 app/assets/javascripts/issuable/services/issuable_service.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/components/comparison_pane.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/components/estimate_only_pane.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/components/help_state.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/components/no_tracking_pane.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/components/spent_only_pane.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/components/time_tracker.js delete mode 100644 app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js delete mode 100644 app/assets/javascripts/subbable_resource.js delete mode 100644 spec/javascripts/subbable_resource_spec.js diff --git a/app/assets/javascripts/issuable/components/time_tracking/collapsed_state.js b/app/assets/javascripts/issuable/components/time_tracking/collapsed_state.js new file mode 100644 index 00000000000..2101a4bb056 --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/collapsed_state.js @@ -0,0 +1,57 @@ +import stopwatchSvg from 'icons/_icon_stopwatch.svg'; + +import '../../../lib/utils/pretty_time'; + +export default { + name: 'time-tracking-collapsed-state', + props: { + showComparisonState: { + type: Boolean, + required: true, + }, + showSpentOnlyState: { + type: Boolean, + required: true, + }, + showEstimateOnlyState: { + type: Boolean, + required: true, + }, + showNoTimeTrackingState: { + type: Boolean, + required: true, + }, + timeSpentHumanReadable: { + type: String, + required: false, + }, + timeEstimateHumanReadable: { + type: String, + required: false, + }, + }, + methods: { + abbreviateTime(timeStr) { + return gl.utils.prettyTime.abbreviateTime(timeStr); + }, + }, + template: ` + + `, +}; diff --git a/app/assets/javascripts/issuable/components/time_tracking/comparison_pane.js b/app/assets/javascripts/issuable/components/time_tracking/comparison_pane.js new file mode 100644 index 00000000000..564f5cadcec --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/comparison_pane.js @@ -0,0 +1,78 @@ +import '../../../lib/utils/pretty_time'; + +const prettyTime = gl.utils.prettyTime; + +export default { + name: 'time-tracking-comparison-pane', + props: { + timeSpent: { + type: Number, + required: true, + }, + timeEstimate: { + type: Number, + required: true, + }, + timeSpentHumanReadable: { + type: String, + required: true, + }, + timeEstimateHumanReadable: { + type: String, + required: true, + }, + }, + computed: { + parsedRemaining() { + const diffSeconds = this.timeEstimate - this.timeSpent; + return prettyTime.parseSeconds(diffSeconds); + }, + timeRemainingHumanReadable() { + return prettyTime.stringifyTime(this.parsedRemaining); + }, + timeRemainingTooltip() { + const prefix = this.timeRemainingMinutes < 0 ? 'Over by' : 'Time remaining:'; + return `${prefix} ${this.timeRemainingHumanReadable}`; + }, + /* Diff values for comparison meter */ + timeRemainingMinutes() { + return this.timeEstimate - this.timeSpent; + }, + timeRemainingPercent() { + return `${Math.floor((this.timeSpent / this.timeEstimate) * 100)}%`; + }, + timeRemainingStatusClass() { + return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate'; + }, + /* Parsed time values */ + parsedEstimate() { + return prettyTime.parseSeconds(this.timeEstimate); + }, + parsedSpent() { + return prettyTime.parseSeconds(this.timeSpent); + }, + }, + template: ` +
+
+
+
+
+
+
+ Spent + {{ timeSpentHumanReadable }} +
+
+ Est + {{ timeEstimateHumanReadable }} +
+
+
+
+ `, +}; diff --git a/app/assets/javascripts/issuable/components/time_tracking/estimate_only_pane.js b/app/assets/javascripts/issuable/components/time_tracking/estimate_only_pane.js new file mode 100644 index 00000000000..d61fb26ed0d --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/estimate_only_pane.js @@ -0,0 +1,15 @@ +export default { + name: 'time-tracking-estimate-only-pane', + props: { + timeEstimateHumanReadable: { + type: String, + required: true, + }, + }, + template: ` +
+ Estimated: + {{ timeEstimateHumanReadable }} +
+ `, +}; diff --git a/app/assets/javascripts/issuable/components/time_tracking/help_state.js b/app/assets/javascripts/issuable/components/time_tracking/help_state.js new file mode 100644 index 00000000000..f955337b62b --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/help_state.js @@ -0,0 +1,26 @@ +export default { + name: 'time-tracking-help-state', + props: { + docsUrl: { + type: String, + required: true, + }, + }, + template: ` +
+
+

Track time with slash commands

+

Slash commands can be used in the issues description and comment boxes.

+

+ /estimate + will update the estimated time with the latest command. +

+

+ /spend + will update the sum of the time spent. +

+ Learn more +
+
+ `, +}; diff --git a/app/assets/javascripts/issuable/components/time_tracking/issuable_time_tracking.js b/app/assets/javascripts/issuable/components/time_tracking/issuable_time_tracking.js new file mode 100644 index 00000000000..62c05169666 --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/issuable_time_tracking.js @@ -0,0 +1,59 @@ +import '~/smart_interval'; + +import timeTracker from './time_tracker'; +import eventHub from '../../event_hub'; + +export default { + el: '#issuable-time-tracker', + data() { + const selector = this.$options.el; + const element = document.querySelector(selector); + + const docsUrl = element.dataset.docsUrl; + + return { + issuable: {}, + docsUrl, + }; + }, + components: { + 'issuable-time-tracker': timeTracker, + }, + methods: { + fetchIssuable() { + eventHub.$emit('fetchIssuable'); + }, + updateState(data) { + this.issuable = data; + }, + listenForSlashCommands() { + $(document).on('ajax:success', '.gfm-form', (e, data) => { + const subscribedCommands = ['spend_time', 'time_estimate']; + const changedCommands = data.commands_changes + ? Object.keys(data.commands_changes) + : []; + if (changedCommands && _.intersection(subscribedCommands, changedCommands).length) { + this.fetchIssuable(); + } + }); + }, + }, + created() { + eventHub.$on('receivedIssuable', data => this.updateState(data)); + }, + mounted() { + this.fetchIssuable(); + this.listenForSlashCommands(); + }, + template: ` +
+ +
+ `, +}; diff --git a/app/assets/javascripts/issuable/components/time_tracking/no_tracking_pane.js b/app/assets/javascripts/issuable/components/time_tracking/no_tracking_pane.js new file mode 100644 index 00000000000..79f1c5a6f27 --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/no_tracking_pane.js @@ -0,0 +1,8 @@ +export default { + name: 'time-tracking-no-tracking-pane', + template: ` +
+ No estimate or time spent +
+ `, +}; diff --git a/app/assets/javascripts/issuable/components/time_tracking/spent_only_pane.js b/app/assets/javascripts/issuable/components/time_tracking/spent_only_pane.js new file mode 100644 index 00000000000..c71d6cd270b --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/spent_only_pane.js @@ -0,0 +1,15 @@ +export default { + name: 'time-tracking-spent-only-pane', + props: { + timeSpentHumanReadable: { + type: String, + required: true, + }, + }, + template: ` +
+ Spent: + {{ timeSpentHumanReadable }} +
+ `, +}; diff --git a/app/assets/javascripts/issuable/components/time_tracking/time_tracker.js b/app/assets/javascripts/issuable/components/time_tracking/time_tracker.js new file mode 100644 index 00000000000..57c72f258c6 --- /dev/null +++ b/app/assets/javascripts/issuable/components/time_tracking/time_tracker.js @@ -0,0 +1,148 @@ +import timeTrackingHelpState from './help_state'; +import timeTrackingCollapsedState from './collapsed_state'; +import timeTrackingSpentOnlyPane from './spent_only_pane'; +import timeTrackingNoTrackingPane from './no_tracking_pane'; +import timeTrackingEstimateOnlyPane from './estimate_only_pane'; +import timeTrackingComparisonPane from './comparison_pane'; + +export default { + name: 'issuable-time-tracker', + props: { + time_estimate: { + type: Number, + required: true, + default: 0, + }, + time_spent: { + type: Number, + required: true, + default: 0, + }, + human_time_estimate: { + type: String, + required: false, + }, + human_time_spent: { + type: String, + required: false, + }, + docsUrl: { + type: String, + required: true, + }, + }, + data() { + return { + showHelp: false, + }; + }, + components: { + 'time-tracking-collapsed-state': timeTrackingCollapsedState, + 'time-tracking-estimate-only-pane': timeTrackingEstimateOnlyPane, + 'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane, + 'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane, + 'time-tracking-comparison-pane': timeTrackingComparisonPane, + 'time-tracking-help-state': timeTrackingHelpState, + }, + computed: { + timeSpent() { + return this.time_spent; + }, + timeEstimate() { + return this.time_estimate; + }, + timeEstimateHumanReadable() { + return this.human_time_estimate; + }, + timeSpentHumanReadable() { + return this.human_time_spent; + }, + hasTimeSpent() { + return !!this.timeSpent; + }, + hasTimeEstimate() { + return !!this.timeEstimate; + }, + showComparisonState() { + return this.hasTimeEstimate && this.hasTimeSpent; + }, + showEstimateOnlyState() { + return this.hasTimeEstimate && !this.hasTimeSpent; + }, + showSpentOnlyState() { + return this.hasTimeSpent && !this.hasTimeEstimate; + }, + showNoTimeTrackingState() { + return !this.hasTimeEstimate && !this.hasTimeSpent; + }, + showHelpState() { + return !!this.showHelp; + }, + }, + methods: { + toggleHelpState(show) { + this.showHelp = show; + }, + }, + template: ` +
+ +
+ Time tracking +
+