diff --git a/frontend/src/components/gantt/GanttRowBars.vue b/frontend/src/components/gantt/GanttRowBars.vue
index 9438e9ec6..bc6434d0e 100644
--- a/frontend/src/components/gantt/GanttRowBars.vue
+++ b/frontend/src/components/gantt/GanttRowBars.vue
@@ -16,6 +16,42 @@
:timeline-end="dateToDate"
:on-update="(id, start, end) => emit('updateTask', id, start, end)"
>
+
+
+
+
+
+
+
+
+
+
-
+
-
+
import {computed} from 'vue'
import dayjs from 'dayjs'
+import {useI18n} from 'vue-i18n'
import type {GanttBarModel} from '@/composables/useGanttBar'
import {getTextColor, LIGHT} from '@/helpers/color/getTextColor'
@@ -110,6 +144,8 @@ import {roundToNaturalDayBoundary} from '@/helpers/time/roundToNaturalDayBoundar
import GanttBarPrimitive from './primitives/GanttBarPrimitive.vue'
+const {t} = useI18n({useScope: 'global'})
+
const props = defineProps<{
bars: GanttBarModel[]
totalWidth: number
@@ -206,7 +242,23 @@ const getBarTextX = computed(() => (bar: GanttBarModel) => {
return getBarX.value(bar) + 8
})
+function isPartialDate(bar: GanttBarModel) {
+ return bar.meta?.dateType === 'startOnly' || bar.meta?.dateType === 'endOnly'
+}
+
+function isDateless(bar: GanttBarModel) {
+ return !bar.meta?.hasActualDates && !isPartialDate(bar)
+}
+
function getBarFill(bar: GanttBarModel) {
+ // Partial dates still have "actual" dates on one side — use the task color
+ if (isPartialDate(bar)) {
+ if (bar.meta?.color) {
+ return bar.meta.color
+ }
+ return 'var(--primary)'
+ }
+
if (bar.meta?.hasActualDates) {
if (bar.meta?.color) {
return bar.meta.color
@@ -217,22 +269,29 @@ function getBarFill(bar: GanttBarModel) {
return 'var(--grey-100)'
}
+function getBarFillAttr(bar: GanttBarModel): string {
+ if (isPartialDate(bar)) {
+ return `url(#gradient-${bar.id})`
+ }
+ return getBarFill(bar)
+}
+
function getBarStroke(bar: GanttBarModel) {
- if (!bar.meta?.hasActualDates) {
+ if (isDateless(bar)) {
return 'var(--grey-300)' // Gray for dashed border
}
return 'none'
}
function getBarStrokeWidth(bar: GanttBarModel) {
- if (!bar.meta?.hasActualDates) {
+ if (isDateless(bar)) {
return '2'
}
return '0'
}
function getBarTextColor(bar: GanttBarModel) {
- if (!bar.meta?.hasActualDates) {
+ if (isDateless(bar)) {
return 'var(--grey-800)'
}
@@ -243,6 +302,25 @@ function getBarTextColor(bar: GanttBarModel) {
return LIGHT
}
+function getBarAriaLabel(bar: GanttBarModel): string {
+ const task = bar.meta?.label || bar.id
+ const startDate = bar.start.toLocaleDateString()
+ const endDate = bar.end.toLocaleDateString()
+
+ let dateType: string
+ if (bar.meta?.dateType === 'startOnly') {
+ dateType = t('project.gantt.partialDatesStart')
+ } else if (bar.meta?.dateType === 'endOnly') {
+ dateType = t('project.gantt.partialDatesEnd')
+ } else if (bar.meta?.hasActualDates) {
+ dateType = t('project.gantt.scheduledDates')
+ } else {
+ dateType = t('project.gantt.estimatedDates')
+ }
+
+ return t('project.gantt.taskBarLabel', {task, startDate, endDate, dateType})
+}
+
function handleBarPointerDown(bar: GanttBarModel, event: PointerEvent) {
emit('barPointerDown', bar, event)
}