From b6fbfa4a615c89b7344ad8aa14232b12b12a56ee Mon Sep 17 00:00:00 2001 From: Artem Savchenko Date: Tue, 6 Oct 2026 16:31:06 +0700 Subject: [PATCH 1/4] Redesign Gant charts and fix issues Signed-off-by: Artem Savchenko --- .../gantt/src/__tests__/time-scale.test.ts | 26 ++ .../src/__tests__/toolbar-overflow.test.ts | 31 +- packages/gantt/src/time-scale.ts | 25 +- packages/gantt/src/toolbar-overflow.ts | 15 +- plugins/tracker-assets/assets/icons.svg | 6 +- plugins/tracker-assets/lang/cs.json | 13 +- plugins/tracker-assets/lang/de.json | 13 +- plugins/tracker-assets/lang/en.json | 13 +- plugins/tracker-assets/lang/es.json | 13 +- plugins/tracker-assets/lang/fr.json | 13 +- plugins/tracker-assets/lang/it.json | 13 +- plugins/tracker-assets/lang/ja.json | 13 +- plugins/tracker-assets/lang/ko.json | 13 +- plugins/tracker-assets/lang/pl.json | 13 +- plugins/tracker-assets/lang/pt-br.json | 13 +- plugins/tracker-assets/lang/pt.json | 13 +- plugins/tracker-assets/lang/ru.json | 13 +- plugins/tracker-assets/lang/tr.json | 13 +- plugins/tracker-assets/lang/zh.json | 13 +- .../components/LinkSubIssueActionPopup.svelte | 2 +- .../gantt/ConfirmCascadePopup.svelte | 8 +- .../src/components/gantt/GanttBar.svelte | 20 +- .../src/components/gantt/GanttCanvas.svelte | 139 +++++- .../gantt/GanttConfirmCommitPopup.svelte | 12 +- .../src/components/gantt/GanttHeader.svelte | 42 +- .../gantt/GanttResizeOverlay.svelte | 6 +- .../src/components/gantt/GanttSidebar.svelte | 135 ++++-- .../components/gantt/GanttTodayMarker.svelte | 7 +- .../components/gantt/GanttToolbarBar.svelte | 49 +- .../components/gantt/GanttToolbarTiers.svelte | 423 +++++++++++------- .../src/components/gantt/GanttView.svelte | 207 ++++++--- .../gantt/GanttViewSettingsPopup.svelte | 43 ++ .../src/components/gantt/ganttToolbarStore.ts | 8 +- .../gantt/lib/__tests__/bar-colors.test.ts | 18 +- .../__tests__/link-sub-issue-cycle.test.ts | 2 +- .../lib/__tests__/timeline-navigation.test.ts | 25 ++ .../components/gantt/lib/calendar-state.ts | 1 + .../src/components/gantt/lib/scheduler.ts | 2 +- .../gantt/lib/timeline-navigation.ts | 22 + .../src/components/issues/IssuesView.svelte | 5 +- .../issues/edit/SubIssueSelector.svelte | 2 +- .../issues/timereport/TimeSpendReport.svelte | 2 +- plugins/tracker-resources/src/plugin.ts | 11 + 43 files changed, 1120 insertions(+), 356 deletions(-) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttViewSettingsPopup.svelte create mode 100644 plugins/tracker-resources/src/components/gantt/lib/__tests__/timeline-navigation.test.ts create mode 100644 plugins/tracker-resources/src/components/gantt/lib/timeline-navigation.ts diff --git a/packages/gantt/src/__tests__/time-scale.test.ts b/packages/gantt/src/__tests__/time-scale.test.ts index bc3393f5e34..7b26550f0cb 100644 --- a/packages/gantt/src/__tests__/time-scale.test.ts +++ b/packages/gantt/src/__tests__/time-scale.test.ts @@ -141,3 +141,29 @@ describe('time-scale — secondary label (year/month supra row)', () => { void DAY }) }) + +describe('time-scale locale', () => { + const origin = Date.UTC(2026, 0, 1) + + it.each(['en', 'ru', 'de', 'ja'])('formats day and month labels using %s', (locale) => { + const expected = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' }).format(origin) + expect(createTimeScale('day', origin, undefined, { locale }).ticks([origin, origin])[0].secondaryLabel).toBe( + expected + ) + expect(createTimeScale('month', origin, undefined, { locale }).ticks([origin, origin])[0].label).toBe(expected) + }) + + it('accepts translated week and quarter labels without changing geometry', () => { + const labels = { + locale: 'ru', + weekLabel: (week: number) => `Нед. ${week}`, + quarterLabel: (quarter: number) => `Кв. ${quarter}` + } + const week = createTimeScale('week', origin, 14, labels) + const first = week.ticks([origin, origin + 7 * DAY_MS])[0] + expect(first.label).toBe('Нед. 2') + expect(first.date).toBe(Date.UTC(2026, 0, 5)) + expect(week.toX(first.date)).toBe(56) + expect(createTimeScale('quarter', origin, undefined, labels).ticks([origin, origin])[0].label).toBe('Кв. 1') + }) +}) diff --git a/packages/gantt/src/__tests__/toolbar-overflow.test.ts b/packages/gantt/src/__tests__/toolbar-overflow.test.ts index 59939b57e1e..b1176ae0a06 100644 --- a/packages/gantt/src/__tests__/toolbar-overflow.test.ts +++ b/packages/gantt/src/__tests__/toolbar-overflow.test.ts @@ -16,6 +16,7 @@ const ALL: ToolbarTier[] = [...TOOLBAR_TIERS] // One easy-to-reason-about width per tier. const W: Record = { group: 100, + color: 80, nav: 200, date: 150, zoom: 120, @@ -39,24 +40,31 @@ describe('computeToolbarOverflow', () => { }) it('keeps everything inline when it fits exactly', () => { - // 100+200+150+120+60+90 = 720, plus five gaps = 740 - const r = computeToolbarOverflow(ALL, W, 740, GAP, MORE) + // 100+80+200+150+120+60+90 = 800, plus six gaps = 824 + const r = computeToolbarOverflow(ALL, W, 824, GAP, MORE) expect(r.hidden).toEqual([]) expect(r.visible).toEqual(ALL) }) it('collapses the least important tier first', () => { - const r = computeToolbarOverflow(ALL, W, 739, GAP, MORE) + const r = computeToolbarOverflow(ALL, W, 823, GAP, MORE) expect(r.hidden).toEqual(['savedview']) - expect(r.visible).toEqual(['group', 'nav', 'date', 'zoom', 'undo']) + expect(r.visible).toEqual(['group', 'color', 'nav', 'date', 'zoom', 'undo']) }) it('collapses further tiers in TOOLBAR_COLLAPSE_ORDER as space shrinks', () => { - expect(computeToolbarOverflow(ALL, W, 600, GAP, MORE).hidden).toEqual(['date', 'savedview']) - expect(computeToolbarOverflow(ALL, W, 450, GAP, MORE).hidden).toEqual(['group', 'date', 'savedview']) - expect(computeToolbarOverflow(ALL, W, 400, GAP, MORE).hidden).toEqual(['group', 'date', 'undo', 'savedview']) + expect(computeToolbarOverflow(ALL, W, 600, GAP, MORE).hidden).toEqual(['color', 'date', 'savedview']) + expect(computeToolbarOverflow(ALL, W, 450, GAP, MORE).hidden).toEqual(['group', 'color', 'date', 'savedview']) + expect(computeToolbarOverflow(ALL, W, 400, GAP, MORE).hidden).toEqual([ + 'group', + 'color', + 'date', + 'undo', + 'savedview' + ]) expect(computeToolbarOverflow(ALL, W, 300, GAP, MORE).hidden).toEqual([ 'group', + 'color', 'date', 'zoom', 'undo', @@ -71,7 +79,7 @@ describe('computeToolbarOverflow', () => { }) it('never drops a tier — visible + hidden is always the input set', () => { - for (const available of [-1, 0, 30, 120, 300, 500, 740, 2000]) { + for (const available of [-1, 0, 30, 120, 300, 500, 824, 2000]) { const r = computeToolbarOverflow(ALL, W, available, GAP, MORE) expect([...r.visible, ...r.hidden].sort()).toEqual([...ALL].sort()) } @@ -83,6 +91,13 @@ describe('computeToolbarOverflow', () => { expect(r.hidden).toEqual(ALL.filter((t) => r.hidden.includes(t))) }) + it('keeps grouping inline when only one of the two view controls fits', () => { + const present: ToolbarTier[] = ['group', 'color', 'nav', 'zoom', 'undo'] + const r = computeToolbarOverflow(present, W, 510, GAP, 0) + expect(r.visible).toEqual(['group', 'nav', 'zoom', 'undo']) + expect(r.hidden).toEqual(['color']) + }) + it('ignores tiers that are not present', () => { const present: ToolbarTier[] = ['nav', 'zoom'] // nav(200) + zoom(120) + gap = 324 > 240; nav alone plus the "…" trigger diff --git a/packages/gantt/src/time-scale.ts b/packages/gantt/src/time-scale.ts index 94017e690eb..82ef40bd6ed 100644 --- a/packages/gantt/src/time-scale.ts +++ b/packages/gantt/src/time-scale.ts @@ -45,7 +45,22 @@ export interface TimeScale { ticks: (range: [number, number]) => Tick[] } -export function createTimeScale (zoom: ZoomLevel, origin: number, pxPerDayOverride?: number): TimeScale { +/** Presentation settings supplied by the host application's active language. */ +export interface TimeScaleLocale { + locale?: string + weekLabel?: (week: number) => string + quarterLabel?: (quarter: number) => string +} + +export function createTimeScale ( + zoom: ZoomLevel, + origin: number, + pxPerDayOverride?: number, + labels: TimeScaleLocale = {} +): TimeScale { + const locale = labels.locale ?? 'en' + const weekLabel = labels.weekLabel ?? ((week: number) => `W${week}`) + const quarterLabel = labels.quarterLabel ?? ((quarter: number) => `Q${quarter}`) const pxPerDay = pxPerDayOverride ?? PX_PER_DAY[zoom] const originSnapped = snapToUtcMidnight(origin) @@ -71,7 +86,7 @@ export function createTimeScale (zoom: ZoomLevel, origin: number, pxPerDayOverri const m = d.getUTCMonth() let secondary: string | undefined if (first || m !== lastMonth) { - secondary = d.toLocaleString(undefined, { month: 'short', timeZone: 'UTC' }) + secondary = d.toLocaleString(locale, { month: 'short', timeZone: 'UTC' }) lastMonth = m first = false } @@ -106,7 +121,7 @@ export function createTimeScale (zoom: ZoomLevel, origin: number, pxPerDayOverri } result.push({ date: cursor, - label: `W${isoWeekNumber(c)}`, + label: weekLabel(isoWeekNumber(c)), level: isFirstWeekOfMonth ? 'major' : 'minor', secondaryLabel: secondary }) @@ -135,7 +150,7 @@ export function createTimeScale (zoom: ZoomLevel, origin: number, pxPerDayOverri } result.push({ date: cursor, - label: c.toLocaleString(undefined, { month: 'short', timeZone: 'UTC' }), + label: c.toLocaleString(locale, { month: 'short', timeZone: 'UTC' }), level: c.getUTCMonth() === 0 ? 'major' : 'minor', secondaryLabel: secondary }) @@ -170,7 +185,7 @@ export function createTimeScale (zoom: ZoomLevel, origin: number, pxPerDayOverri } result.push({ date: cursor, - label: `Q${qNum}`, + label: quarterLabel(qNum), level: qNum === 1 ? 'major' : 'minor', secondaryLabel: secondary }) diff --git a/packages/gantt/src/toolbar-overflow.ts b/packages/gantt/src/toolbar-overflow.ts index 75011c7c6dc..7f7404bfdf4 100644 --- a/packages/gantt/src/toolbar-overflow.ts +++ b/packages/gantt/src/toolbar-overflow.ts @@ -28,7 +28,7 @@ * that collapses as a whole; splitting e.g. the date-navigation into single * buttons would leave a half-usable control group behind. */ -export const TOOLBAR_TIERS = ['group', 'nav', 'date', 'zoom', 'undo', 'savedview'] as const +export const TOOLBAR_TIERS = ['group', 'color', 'nav', 'date', 'zoom', 'undo', 'savedview'] as const export type ToolbarTier = (typeof TOOLBAR_TIERS)[number] /** @@ -37,12 +37,21 @@ export type ToolbarTier = (typeof TOOLBAR_TIERS)[number] * - `savedview` is a passive "modified" indicator plus its update button; the * same action is reachable from the More-actions menu. * - `date` (jump-to-date) duplicates what panning and the nav buttons do. - * - `group` (group-by / colour-by) is view configuration, not navigation. + * - `color` is cosmetic; keep `group` visible longer because it changes the + * layout of the issue list and chart. * - `undo` matters while editing, but the keyboard shortcuts stay live. * - `zoom` and `nav` are the two controls a Gantt is unusable without, so * they are the last to go. */ -export const TOOLBAR_COLLAPSE_ORDER: readonly ToolbarTier[] = ['savedview', 'date', 'group', 'undo', 'zoom', 'nav'] +export const TOOLBAR_COLLAPSE_ORDER: readonly ToolbarTier[] = [ + 'savedview', + 'date', + 'color', + 'group', + 'undo', + 'zoom', + 'nav' +] /** * Width assumed for a tier that has never been measured. Deliberately diff --git a/plugins/tracker-assets/assets/icons.svg b/plugins/tracker-assets/assets/icons.svg index eda12e190e5..9c44edfd781 100644 --- a/plugins/tracker-assets/assets/icons.svg +++ b/plugins/tracker-assets/assets/icons.svg @@ -124,10 +124,8 @@ - - - - + + diff --git a/plugins/tracker-assets/lang/cs.json b/plugins/tracker-assets/lang/cs.json index 4d785162d80..41f45e174d4 100644 --- a/plugins/tracker-assets/lang/cs.json +++ b/plugins/tracker-assets/lang/cs.json @@ -313,7 +313,18 @@ "SearchEmptyTitle": "Pro „{query}\" nebyly nalezeny žádné úkoly", "SearchEmptyActiveFilters": "Aktivní filtry: {filters}", "SearchEmptyClearFilters": "Hledat bez filtrů", - "SearchEmptyAllProjects": "Hledat ve všech projektech" + "SearchEmptyAllProjects": "Hledat ve všech projektech", + "GanttWeekLabel": "T{week}", + "GanttQuarterLabel": "Q{quarter}", + "GanttWithoutDates": "{count} bez dat", + "GanttDates": "Data", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/de.json b/plugins/tracker-assets/lang/de.json index 4886ccce28d..d02bc3df41f 100644 --- a/plugins/tracker-assets/lang/de.json +++ b/plugins/tracker-assets/lang/de.json @@ -358,6 +358,8 @@ "GanttJumpToDate": "Zu Datum springen", "GanttZoomDay": "Tag", "GanttZoomWeek": "Woche", + "GanttWeekLabel": "KW{week}", + "GanttQuarterLabel": "Q{quarter}", "GanttZoomMonth": "Monat", "GanttZoomQuarter": "Quartal", "GanttZoomCustom": "Benutzerdefiniert", @@ -561,7 +563,16 @@ "SearchEmptyTitle": "Keine Treffer für \"{query}\"", "SearchEmptyActiveFilters": "Aktive Filter: {filters}", "SearchEmptyClearFilters": "Suche ohne Filter", - "SearchEmptyAllProjects": "In allen Projekten suchen" + "SearchEmptyAllProjects": "In allen Projekten suchen", + "GanttWithoutDates": "{count} ohne Daten", + "GanttDates": "Termine", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index d9dc6a07ca5..8dfe03fdbc4 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -344,6 +344,8 @@ "GanttJumpToDate": "Jump to date", "GanttZoomDay": "Day", "GanttZoomWeek": "Week", + "GanttWeekLabel": "W{week}", + "GanttQuarterLabel": "Q{quarter}", "GanttZoomMonth": "Month", "GanttZoomQuarter": "Quarter", "GanttZoomCustom": "Custom", @@ -561,7 +563,16 @@ "SearchEmptyTitle": "No issues found for \"{query}\"", "SearchEmptyActiveFilters": "Active filters: {filters}", "SearchEmptyClearFilters": "Search without filters", - "SearchEmptyAllProjects": "Search in all projects" + "SearchEmptyAllProjects": "Search in all projects", + "GanttWithoutDates": "{count} without dates", + "GanttDates": "Dates", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/es.json b/plugins/tracker-assets/lang/es.json index 1ff5a8a77bc..e464659432d 100644 --- a/plugins/tracker-assets/lang/es.json +++ b/plugins/tracker-assets/lang/es.json @@ -306,7 +306,18 @@ "SearchEmptyTitle": "No se encontraron tareas para \"{query}\"", "SearchEmptyActiveFilters": "Filtros activos: {filters}", "SearchEmptyClearFilters": "Buscar sin filtros", - "SearchEmptyAllProjects": "Buscar en todos los proyectos" + "SearchEmptyAllProjects": "Buscar en todos los proyectos", + "GanttWeekLabel": "S{week}", + "GanttQuarterLabel": "T{quarter}", + "GanttWithoutDates": "{count} sin fechas", + "GanttDates": "Fechas", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/fr.json b/plugins/tracker-assets/lang/fr.json index df9e6094c23..17d5af55a2a 100644 --- a/plugins/tracker-assets/lang/fr.json +++ b/plugins/tracker-assets/lang/fr.json @@ -306,7 +306,18 @@ "SearchEmptyTitle": "Aucun problème trouvé pour \"{query}\"", "SearchEmptyActiveFilters": "Filtres actifs : {filters}", "SearchEmptyClearFilters": "Rechercher sans filtres", - "SearchEmptyAllProjects": "Rechercher dans tous les projets" + "SearchEmptyAllProjects": "Rechercher dans tous les projets", + "GanttWeekLabel": "S{week}", + "GanttQuarterLabel": "T{quarter}", + "GanttWithoutDates": "{count} sans dates", + "GanttDates": "Dates", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/it.json b/plugins/tracker-assets/lang/it.json index 318f9326f71..2cabd356b17 100644 --- a/plugins/tracker-assets/lang/it.json +++ b/plugins/tracker-assets/lang/it.json @@ -306,7 +306,18 @@ "SearchEmptyTitle": "Nessuna issue trovata per \"{query}\"", "SearchEmptyActiveFilters": "Filtri attivi: {filters}", "SearchEmptyClearFilters": "Cerca senza filtri", - "SearchEmptyAllProjects": "Cerca in tutti i progetti" + "SearchEmptyAllProjects": "Cerca in tutti i progetti", + "GanttWeekLabel": "S{week}", + "GanttQuarterLabel": "T{quarter}", + "GanttWithoutDates": "{count} senza date", + "GanttDates": "Date", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/ja.json b/plugins/tracker-assets/lang/ja.json index d060f6f83ea..b7016ec178d 100644 --- a/plugins/tracker-assets/lang/ja.json +++ b/plugins/tracker-assets/lang/ja.json @@ -306,7 +306,18 @@ "SearchEmptyTitle": "\"{query}\" に一致するイシューが見つかりません", "SearchEmptyActiveFilters": "アクティブなフィルター:{filters}", "SearchEmptyClearFilters": "フィルターなしで検索", - "SearchEmptyAllProjects": "すべてのプロジェクトを検索" + "SearchEmptyAllProjects": "すべてのプロジェクトを検索", + "GanttWeekLabel": "第{week}週", + "GanttQuarterLabel": "第{quarter}四半期", + "GanttWithoutDates": "日付なし: {count}", + "GanttDates": "日付", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/ko.json b/plugins/tracker-assets/lang/ko.json index 0d3bba943ee..3fc55d8e98c 100644 --- a/plugins/tracker-assets/lang/ko.json +++ b/plugins/tracker-assets/lang/ko.json @@ -306,7 +306,18 @@ "SearchEmptyTitle": "\"{query}\"에 대한 이슈를 찾을 수 없습니다", "SearchEmptyActiveFilters": "활성 필터: {filters}", "SearchEmptyClearFilters": "필터 없이 검색", - "SearchEmptyAllProjects": "모든 프로젝트에서 검색" + "SearchEmptyAllProjects": "모든 프로젝트에서 검색", + "GanttWeekLabel": "{week}주", + "GanttQuarterLabel": "{quarter}분기", + "GanttWithoutDates": "날짜 없음: {count}", + "GanttDates": "날짜", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/pl.json b/plugins/tracker-assets/lang/pl.json index fa2e8555275..0f9c0ddcc74 100644 --- a/plugins/tracker-assets/lang/pl.json +++ b/plugins/tracker-assets/lang/pl.json @@ -290,7 +290,18 @@ "UnsetParentIssue": "Wyczyść zagadnienie nadrzędne", "ForbidCreateProjectPermission": "Zakaż tworzenia projektów", "ForbidCreateProjectPermissionDescription": "Zakaż użytkownikom tworzenia nowych projektów.", - "AllowCreatingIssues": "Zezwól na tworzenie zadań" + "AllowCreatingIssues": "Zezwól na tworzenie zadań", + "GanttWeekLabel": "T{week}", + "GanttQuarterLabel": "K{quarter}", + "GanttWithoutDates": "{count} bez dat", + "GanttDates": "Daty", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/pt-br.json b/plugins/tracker-assets/lang/pt-br.json index fb3ea902261..03c508d23f3 100644 --- a/plugins/tracker-assets/lang/pt-br.json +++ b/plugins/tracker-assets/lang/pt-br.json @@ -306,7 +306,18 @@ "SearchEmptyTitle": "Nenhum problema encontrado para \"{query}\"", "SearchEmptyActiveFilters": "Filtros ativos: {filters}", "SearchEmptyClearFilters": "Procurar sem filtros", - "SearchEmptyAllProjects": "Procurar em todos os projetos" + "SearchEmptyAllProjects": "Procurar em todos os projetos", + "GanttWeekLabel": "S{week}", + "GanttQuarterLabel": "T{quarter}", + "GanttWithoutDates": "{count} sem datas", + "GanttDates": "Datas", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/pt.json b/plugins/tracker-assets/lang/pt.json index 4e5ffcb65a1..919a03c7c6a 100644 --- a/plugins/tracker-assets/lang/pt.json +++ b/plugins/tracker-assets/lang/pt.json @@ -306,7 +306,18 @@ "SearchEmptyTitle": "Nenhum problema encontrado para \"{query}\"", "SearchEmptyActiveFilters": "Filtros ativos: {filters}", "SearchEmptyClearFilters": "Procurar sem filtros", - "SearchEmptyAllProjects": "Procurar em todos os projetos" + "SearchEmptyAllProjects": "Procurar em todos os projetos", + "GanttWeekLabel": "S{week}", + "GanttQuarterLabel": "T{quarter}", + "GanttWithoutDates": "{count} sem datas", + "GanttDates": "Datas", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/ru.json b/plugins/tracker-assets/lang/ru.json index a3e3bacfa6d..76950873fa5 100644 --- a/plugins/tracker-assets/lang/ru.json +++ b/plugins/tracker-assets/lang/ru.json @@ -344,6 +344,8 @@ "GanttJumpToDate": "Перейти к дате", "GanttZoomDay": "День", "GanttZoomWeek": "Неделя", + "GanttWeekLabel": "Нед. {week}", + "GanttQuarterLabel": "Кв. {quarter}", "GanttZoomMonth": "Месяц", "GanttZoomQuarter": "Квартал", "GanttZoomCustom": "Другой", @@ -561,7 +563,16 @@ "SearchEmptyTitle": "Не найдено задач по запросу «{query}»", "SearchEmptyActiveFilters": "Активные фильтры: {filters}", "SearchEmptyClearFilters": "Искать без фильтров", - "SearchEmptyAllProjects": "Искать во всех проектах" + "SearchEmptyAllProjects": "Искать во всех проектах", + "GanttWithoutDates": "Без дат: {count}", + "GanttDates": "Даты", + "GanttSetDates": "Задать даты", + "GanttNoScheduledTasks": "Нет запланированных задач", + "GanttTodayAlreadyVisible": "Сегодня уже на экране", + "GanttVisibleRange": "Период", + "GanttNotScheduled": "Не запланировано", + "GanttMissingStartDates": "У {count} задач не задана дата начала.", + "GanttSetDatesHint": "Задайте дату начала и срок в карточке задачи, чтобы увидеть полосу на шкале." }, "status": {} } diff --git a/plugins/tracker-assets/lang/tr.json b/plugins/tracker-assets/lang/tr.json index 34aa6ad5d5d..678c92b9c08 100644 --- a/plugins/tracker-assets/lang/tr.json +++ b/plugins/tracker-assets/lang/tr.json @@ -327,6 +327,8 @@ "GanttJumpToDate": "Jump to date", "GanttZoomDay": "Gün", "GanttZoomWeek": "Hafta", + "GanttWeekLabel": "H{week}", + "GanttQuarterLabel": "Ç{quarter}", "GanttZoomMonth": "Ay", "GanttZoomQuarter": "Çeyrek", "GanttZoomCustom": "Özel", @@ -544,7 +546,16 @@ "SearchEmptyTitle": "\"{query}\" için sorun bulunamadı", "SearchEmptyActiveFilters": "Aktif filtreler: {filters}", "SearchEmptyClearFilters": "Filtresiz ara", - "SearchEmptyAllProjects": "Tüm projelerde ara" + "SearchEmptyAllProjects": "Tüm projelerde ara", + "GanttWithoutDates": "Tarihsiz: {count}", + "GanttDates": "Tarihler", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-assets/lang/zh.json b/plugins/tracker-assets/lang/zh.json index 09b80894b23..e66292e2deb 100644 --- a/plugins/tracker-assets/lang/zh.json +++ b/plugins/tracker-assets/lang/zh.json @@ -323,7 +323,18 @@ "SearchEmptyTitle": "未找到与 \"{query}\" 匹配的问题", "SearchEmptyActiveFilters": "活动筛选器:{filters}", "SearchEmptyClearFilters": "不使用筛选器搜索", - "SearchEmptyAllProjects": "在所有项目中搜索" + "SearchEmptyAllProjects": "在所有项目中搜索", + "GanttWeekLabel": "第{week}周", + "GanttQuarterLabel": "第{quarter}季度", + "GanttWithoutDates": "无日期:{count}", + "GanttDates": "日期", + "GanttSetDates": "Set dates", + "GanttNoScheduledTasks": "No scheduled tasks", + "GanttTodayAlreadyVisible": "Today is already in view", + "GanttVisibleRange": "Range", + "GanttNotScheduled": "Not scheduled", + "GanttMissingStartDates": "{count} issues have no start date.", + "GanttSetDatesHint": "Set start and due dates in an issue to show its timeline." }, "status": {} } diff --git a/plugins/tracker-resources/src/components/LinkSubIssueActionPopup.svelte b/plugins/tracker-resources/src/components/LinkSubIssueActionPopup.svelte index 1496874b292..68dc21b6244 100644 --- a/plugins/tracker-resources/src/components/LinkSubIssueActionPopup.svelte +++ b/plugins/tracker-resources/src/components/LinkSubIssueActionPopup.svelte @@ -67,7 +67,7 @@ // Descendants: BFS over parents[0].parentId pointers. const childrenByParent = new Map, Issue[]>() for (const i of all) { - const parentId = i.parents?.[0]?.parentId as Ref | undefined + const parentId = i.parents?.[0]?.parentId if (parentId === undefined) continue const bucket = childrenByParent.get(parentId) if (bucket === undefined) childrenByParent.set(parentId, [i]) diff --git a/plugins/tracker-resources/src/components/gantt/ConfirmCascadePopup.svelte b/plugins/tracker-resources/src/components/gantt/ConfirmCascadePopup.svelte index 46c2b4fa534..2a0e19b36a1 100644 --- a/plugins/tracker-resources/src/components/gantt/ConfirmCascadePopup.svelte +++ b/plugins/tracker-resources/src/components/gantt/ConfirmCascadePopup.svelte @@ -4,7 +4,7 @@ --> @@ -39,7 +39,11 @@
{:else} @@ -446,6 +444,10 @@ {row.groupLabel ?? ''} {row.groupCount ?? 0} + {#if row.groupKey !== undefined && groupRanges.has(row.groupKey)} + {@const range = groupRanges.get(row.groupKey)} + {formatDates(range?.start, range?.due, $themeStore.language) ?? ''} + {/if} {:else} {@const indent = row.depth * 20} @@ -581,6 +583,14 @@ {/if} {/if} + + {#if row.issue !== null} + {@const dates = formatDates(row.issue.startDate, row.issue.dueDate, $themeStore.language)} + {#if dates === null} {#if dir !== null && jumpX !== null} + + + + {:else if tier === 'date'} - - - {:else if tier === 'date'} - - {:else if tier === 'zoom'} -
+
+ {:else if tier === 'zoom'} +
+ {#each zoomPresets as preset (preset.id)} + + {/each} + {#if snap.zoomDropdownSelection === 'custom'} + + {/if} +
{:else if tier === 'undo'}