diff --git a/src/bar.js b/src/bar.js index e27a85b2c..edc0feefe 100644 --- a/src/bar.js +++ b/src/bar.js @@ -591,6 +591,28 @@ export default class Bar { } compute_duration() { + // For hour-level precision, calculate duration directly in the configured unit + if ( + ['Hour', 'Quarter Day', 'Half Day'].includes( + this.gantt.config.view_mode.name, + ) + ) { + this.duration = + date_utils.diff( + this.task._end, + this.task._start, + this.gantt.config.unit, + ) / this.gantt.config.step; + + // For hour-level views, we don't need to worry about ignored dates/weekends + this.actual_duration_raw = this.duration; + this.ignored_duration_raw = 0; + this.task.actual_duration = this.duration; + this.task.ignored_duration = 0; + return; + } + + // Original day-based calculation for day-level and above views let actual_duration_in_days = 0, duration_in_days = 0; for ( diff --git a/src/defaults.js b/src/defaults.js index e63e8ef5c..48f93a2c9 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -130,19 +130,31 @@ const DEFAULT_OPTIONS = { if (ctx.task.description) ctx.set_subtitle(ctx.task.description); else ctx.set_subtitle(''); + const is_hour_level = ['Hour', 'Quarter Day', 'Half Day'].includes( + ctx.chart.config.view_mode.name, + ); + const date_format = is_hour_level ? 'MMM D HH:mm' : 'MMM D'; + const start_date = date_utils.format( ctx.task._start, - 'MMM D', + date_format, ctx.chart.options.language, ); const end_date = date_utils.format( date_utils.add(ctx.task._end, -1, 'second'), - 'MMM D', + date_format, ctx.chart.options.language, ); + const duration_unit = is_hour_level ? 'hours' : 'days'; + const duration_value = is_hour_level + ? Math.round( + date_utils.diff(ctx.task._end, ctx.task._start, 'hour') * 100, + ) / 100 + : ctx.task.actual_duration; + ctx.set_details( - `${start_date} - ${end_date} (${ctx.task.actual_duration} days${ctx.task.ignored_duration ? ' + ' + ctx.task.ignored_duration + ' excluded' : ''})
Progress: ${Math.floor(ctx.task.progress * 100) / 100}%`, + `${start_date} - ${end_date} (${duration_value} ${duration_unit}${ctx.task.ignored_duration ? ' + ' + ctx.task.ignored_duration + ' excluded' : ''})
Progress: ${Math.floor(ctx.task.progress * 100) / 100}%`, ); }, popup_on: 'click', diff --git a/src/index.js b/src/index.js index 054d3e2ce..c6fe930b8 100644 --- a/src/index.js +++ b/src/index.js @@ -165,10 +165,18 @@ export default class Gantt { // cache index task._index = i; - // if hours is not set, assume the last day is full day - // e.g: 2018-09-09 becomes 2018-09-09 23:59:59 + // Only extend to full day if we're in day-level view modes and no time was specified + // For hour-level granularity, preserve the exact end time const task_end_values = date_utils.get_date_values(task._end); - if (task_end_values.slice(3).every((d) => d === 0)) { + const is_hour_level_view = [ + 'Hour', + 'Quarter Day', + 'Half Day', + ].includes(this.options.view_mode); + if ( + task_end_values.slice(3).every((d) => d === 0) && + !is_hour_level_view + ) { task._end = date_utils.add(task._end, 24, 'hour'); } @@ -323,7 +331,15 @@ export default class Gantt { } this.config.date_format = this.config.view_mode.date_format || this.options.date_format; - this.gantt_start.setHours(0, 0, 0, 0); + + // Only reset hours for day-level and above views + if ( + !['Hour', 'Quarter Day', 'Half Day'].includes( + this.config.view_mode.name, + ) + ) { + this.gantt_start.setHours(0, 0, 0, 0); + } } setup_date_values() {