From 0e6d0dfc7ff0de0551c7c64a532971eb77ccde56 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:27:37 +0000 Subject: [PATCH 001/248] feat(tracker): add Gantt scheduling schema (startDate + IssueRelation) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schema-only foundation for the upcoming Gantt-chart view in tracker. No UI in this PR. Changes: - Issue.startDate: Timestamp | null (interface + IssueDraft + @Prop with @Index) - Milestone.startDate: Timestamp | null (interface + @Prop, reusing the existing tracker.string.StartDate IntlString) - New DependencyKind type ('finish-to-start' | 'start-to-start' | 'finish-to-finish' | 'start-to-finish') - New IssueRelation AttachedDoc class with kind: DependencyKind, signed lag: number — registered in models/tracker via TIssueRelation - 7 new IntlString keys: IssueStartDate, GanttDependency, GanttDependency{FinishToStart,StartToStart,FinishToFinish,StartToFinish}, GanttLag — all 13 locales updated - Cross-plugin literal updates in importer + github sync to satisfy the new required Issue.startDate / Milestone.startDate fields: - packages/importer/src/importer/importer.ts: AttachedData literal - services/github/pod-github/src/sync/issueBase.ts: 'startDate' added to GithubIssueData Omit list (github sync does not own scheduling) - services/github/pod-github/src/sync/issues.ts + pullrequests.ts: AttachedData literals Out of scope (deferred to follow-up PRs): - UI for Gantt view, drag/resize, dependency editor, critical path - blockedBy → IssueRelation migration (ships atomically with the writer redirect in the dependency-UI PR) - LinkIssues permission (tracker uses forbid-style permissions; needs maintainer discussion) - Activity-feed wiring for IssueRelation (needs a producer to test against) - IssueTemplate.startDate (template propagation semantics undecided) Signed-off-by: Michael Uray --- models/tracker/src/index.ts | 2 + models/tracker/src/types.ts | 29 ++++++++++++ packages/importer/src/importer/importer.ts | 1 + plugins/tracker-assets/lang/cs.json | 7 +++ plugins/tracker-assets/lang/de.json | 7 +++ plugins/tracker-assets/lang/en.json | 7 +++ plugins/tracker-assets/lang/es.json | 7 +++ plugins/tracker-assets/lang/fr.json | 7 +++ plugins/tracker-assets/lang/it.json | 7 +++ plugins/tracker-assets/lang/ja.json | 7 +++ plugins/tracker-assets/lang/ko.json | 7 +++ plugins/tracker-assets/lang/pt-br.json | 7 +++ plugins/tracker-assets/lang/pt.json | 7 +++ plugins/tracker-assets/lang/ru.json | 7 +++ plugins/tracker-assets/lang/tr.json | 7 +++ plugins/tracker-assets/lang/zh.json | 7 +++ .../src/components/CreateIssue.svelte | 3 ++ .../src/components/SubIssues.svelte | 1 + .../components/milestones/NewMilestone.svelte | 1 + .../templates/DraftIssueChildEditor.svelte | 1 + plugins/tracker/src/index.ts | 44 +++++++++++++++++++ .../github/pod-github/src/sync/issueBase.ts | 1 + services/github/pod-github/src/sync/issues.ts | 1 + .../pod-github/src/sync/pullrequests.ts | 1 + 24 files changed, 176 insertions(+) diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index 44d1b97b506..baabea1381a 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -39,6 +39,7 @@ import { TClassicProjectTypeData, TComponent, TIssue, + TIssueRelation, TIssueStatus, TIssueTemplate, TIssueTypeData, @@ -441,6 +442,7 @@ export function createModel (builder: Builder): void { TProject, TComponent, TIssue, + TIssueRelation, TIssueTemplate, TIssueStatus, TTypeIssuePriority, diff --git a/models/tracker/src/types.ts b/models/tracker/src/types.ts index 09d3421f302..369493111b8 100644 --- a/models/tracker/src/types.ts +++ b/models/tracker/src/types.ts @@ -58,10 +58,12 @@ import time, { type ToDo } from '@hcengineering/time' import { type ProjectTargetPreference, type Component, + type DependencyKind, type Issue, type IssueChildInfo, type IssueParentInfo, type IssuePriority, + type IssueRelation, type IssueStatus, type IssueTemplate, type IssueTemplateChild, @@ -233,6 +235,10 @@ export class TIssue extends TTask implements Issue { @ReadOnly() declare space: Ref + @Prop(TypeDate(DateRangeMode.DATETIME), tracker.string.IssueStartDate) + @Index(IndexKind.Indexed) + declare startDate: Timestamp | null + @Prop(TypeDate(DateRangeMode.DATETIME), tracker.string.DueDate) declare dueDate: Timestamp | null @@ -340,6 +346,26 @@ export class TTimeSpendReport extends TAttachedDoc implements TimeSpendReport { @Prop(TypeString(), tracker.string.TimeSpendReportDescription) description!: string } + +/** + * @public + */ +@Model(tracker.class.IssueRelation, core.class.AttachedDoc, DOMAIN_TRACKER) +@UX(tracker.string.GanttDependency, tracker.icon.Issue) +export class TIssueRelation extends TAttachedDoc implements IssueRelation { + @Prop(TypeRef(tracker.class.Issue), tracker.string.Issue) + declare attachedTo: Ref + + @Prop(TypeRef(tracker.class.Issue), tracker.string.Issue) + @Index(IndexKind.Indexed) + target!: Ref + + @Prop(TypeString(), tracker.string.GanttDependency) + kind!: DependencyKind + + @Prop(TypeNumber(), tracker.string.GanttLag) + lag!: number +} /** * @public */ @@ -389,6 +415,9 @@ export class TMilestone extends TDoc implements Milestone { @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) attachments?: number + @Prop(TypeDate(), tracker.string.StartDate) + startDate!: Timestamp | null + @Prop(TypeDate(), tracker.string.TargetDate) targetDate!: Timestamp diff --git a/packages/importer/src/importer/importer.ts b/packages/importer/src/importer/importer.ts index 73bdd7bb3b0..5c5d30b7b5b 100644 --- a/packages/importer/src/importer/importer.ts +++ b/packages/importer/src/importer/importer.ts @@ -591,6 +591,7 @@ export class WorkspaceImporter { rank, comments: issue.comments?.length ?? 0, subIssues: issue.subdocs.length, + startDate: null, dueDate: null, parents: parentsInfo, remainingTime, diff --git a/plugins/tracker-assets/lang/cs.json b/plugins/tracker-assets/lang/cs.json index f1e9d23d05c..27764f0806e 100644 --- a/plugins/tracker-assets/lang/cs.json +++ b/plugins/tracker-assets/lang/cs.json @@ -115,6 +115,13 @@ "NoAssignee": "Bez přiřazení", "LastUpdated": "Poslední aktualizace", "DueDate": "Datum splnění", + "IssueStartDate": "Datum zahájení", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manuální", "All": "Vše", "PastWeek": "Minulý týden", diff --git a/plugins/tracker-assets/lang/de.json b/plugins/tracker-assets/lang/de.json index 99fe946c9d4..ae1645b8649 100644 --- a/plugins/tracker-assets/lang/de.json +++ b/plugins/tracker-assets/lang/de.json @@ -125,6 +125,13 @@ "NoAssignee": "Nicht zugewiesen", "LastUpdated": "Zuletzt aktualisiert", "DueDate": "Fälligkeitsdatum", + "IssueStartDate": "Startdatum", + "GanttDependency": "Abhängigkeit", + "GanttDependencyFinishToFinish": "Ende zu Ende", + "GanttDependencyFinishToStart": "Ende zu Anfang", + "GanttDependencyStartToFinish": "Anfang zu Ende", + "GanttDependencyStartToStart": "Anfang zu Anfang", + "GanttLag": "Verzögerung", "Manual": "Manuell", "All": "Alle", "PastWeek": "Letzte Woche", diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index 4345dc5a0aa..6b19e6e47f2 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -125,6 +125,13 @@ "NoAssignee": "No assignee", "LastUpdated": "Last updated", "DueDate": "Due date", + "IssueStartDate": "Start date", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manual", "All": "All", "PastWeek": "Past week", diff --git a/plugins/tracker-assets/lang/es.json b/plugins/tracker-assets/lang/es.json index 2699c92764c..909cd5d8aa5 100644 --- a/plugins/tracker-assets/lang/es.json +++ b/plugins/tracker-assets/lang/es.json @@ -123,6 +123,13 @@ "NoAssignee": "Sin asignar", "LastUpdated": "Última actualización", "DueDate": "Fecha de vencimiento", + "IssueStartDate": "Fecha de inicio", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manual", "All": "Todos", "PastWeek": "Semana pasada", diff --git a/plugins/tracker-assets/lang/fr.json b/plugins/tracker-assets/lang/fr.json index 7b1f0c94667..de5343c7651 100644 --- a/plugins/tracker-assets/lang/fr.json +++ b/plugins/tracker-assets/lang/fr.json @@ -123,6 +123,13 @@ "NoAssignee": "Non assigné", "LastUpdated": "Dernière mise à jour", "DueDate": "Date d'échéance", + "IssueStartDate": "Date de début", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manuel", "All": "Tous", "PastWeek": "La semaine passée", diff --git a/plugins/tracker-assets/lang/it.json b/plugins/tracker-assets/lang/it.json index 7113d551116..80024447846 100644 --- a/plugins/tracker-assets/lang/it.json +++ b/plugins/tracker-assets/lang/it.json @@ -123,6 +123,13 @@ "NoAssignee": "Nessun assegnatario", "LastUpdated": "Ultimo aggiornamento", "DueDate": "Data di scadenza", + "IssueStartDate": "Data di inizio", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manuale", "All": "Tutti", "PastWeek": "Settimana scorsa", diff --git a/plugins/tracker-assets/lang/ja.json b/plugins/tracker-assets/lang/ja.json index 58f184c7493..28744b81c50 100644 --- a/plugins/tracker-assets/lang/ja.json +++ b/plugins/tracker-assets/lang/ja.json @@ -123,6 +123,13 @@ "NoAssignee": "担当者なし", "LastUpdated": "最終更新日", "DueDate": "期日", + "IssueStartDate": "開始日", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "手動", "All": "すべて", "PastWeek": "先週", diff --git a/plugins/tracker-assets/lang/ko.json b/plugins/tracker-assets/lang/ko.json index b89719af619..4d8b0f91063 100644 --- a/plugins/tracker-assets/lang/ko.json +++ b/plugins/tracker-assets/lang/ko.json @@ -123,6 +123,13 @@ "NoAssignee": "담당자 없음", "LastUpdated": "최근 업데이트", "DueDate": "마감일", + "IssueStartDate": "시작일", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "수동", "All": "전체", "PastWeek": "지난주", diff --git a/plugins/tracker-assets/lang/pt-br.json b/plugins/tracker-assets/lang/pt-br.json index 828c9ca50b6..9a61364dee1 100644 --- a/plugins/tracker-assets/lang/pt-br.json +++ b/plugins/tracker-assets/lang/pt-br.json @@ -123,6 +123,13 @@ "NoAssignee": "Sem atribuição", "LastUpdated": "Última atualização", "DueDate": "Data de vencimento", + "IssueStartDate": "Data de início", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manual", "All": "Todos", "PastWeek": "Semana passada", diff --git a/plugins/tracker-assets/lang/pt.json b/plugins/tracker-assets/lang/pt.json index da5a3e85e10..37d6f04e6cc 100644 --- a/plugins/tracker-assets/lang/pt.json +++ b/plugins/tracker-assets/lang/pt.json @@ -123,6 +123,13 @@ "NoAssignee": "Sem atribuição", "LastUpdated": "Última atualização", "DueDate": "Data de vencimento", + "IssueStartDate": "Data de início", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manual", "All": "Todos", "PastWeek": "Semana passada", diff --git a/plugins/tracker-assets/lang/ru.json b/plugins/tracker-assets/lang/ru.json index 642877b385a..e3f5c470dba 100644 --- a/plugins/tracker-assets/lang/ru.json +++ b/plugins/tracker-assets/lang/ru.json @@ -125,6 +125,13 @@ "NoAssignee": "Нет исполнителя", "LastUpdated": "Последнее обновление", "DueDate": "Срок", + "IssueStartDate": "Дата начала", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Пользовательский", "All": "Все", "PastWeek": "Предыдущая неделя", diff --git a/plugins/tracker-assets/lang/tr.json b/plugins/tracker-assets/lang/tr.json index 9f66c50ea5f..cc146a55017 100644 --- a/plugins/tracker-assets/lang/tr.json +++ b/plugins/tracker-assets/lang/tr.json @@ -123,6 +123,13 @@ "NoAssignee": "Atanan yok", "LastUpdated": "Son güncelleme", "DueDate": "Bitiş tarihi", + "IssueStartDate": "Başlangıç tarihi", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "Manuel", "All": "Tümü", "PastWeek": "Geçen hafta", diff --git a/plugins/tracker-assets/lang/zh.json b/plugins/tracker-assets/lang/zh.json index 8e05a889e18..50c62441988 100644 --- a/plugins/tracker-assets/lang/zh.json +++ b/plugins/tracker-assets/lang/zh.json @@ -125,6 +125,13 @@ "NoAssignee": "无受理人", "LastUpdated": "最后更新", "DueDate": "截止日期", + "IssueStartDate": "开始日期", + "GanttDependency": "Dependency", + "GanttDependencyFinishToFinish": "Finish to finish", + "GanttDependencyFinishToStart": "Finish to start", + "GanttDependencyStartToFinish": "Start to finish", + "GanttDependencyStartToStart": "Start to start", + "GanttLag": "Lag", "Manual": "手动", "All": "全部", "PastWeek": "过去一周", diff --git a/plugins/tracker-resources/src/components/CreateIssue.svelte b/plugins/tracker-resources/src/components/CreateIssue.svelte index 7878ea9d750..224ce28c9ce 100644 --- a/plugins/tracker-resources/src/components/CreateIssue.svelte +++ b/plugins/tracker-resources/src/components/CreateIssue.svelte @@ -186,6 +186,7 @@ priority: priority ?? IssuePriority.NoPriority, space: _space as Ref, component: component ?? $activeComponent ?? null, + startDate: null, dueDate: null, attachments: 0, estimation: 0, @@ -312,6 +313,7 @@ _id: generateId(), space: _space as Ref, subIssues: [], + startDate: null, dueDate: null, labels: p.labels !== undefined @@ -488,6 +490,7 @@ rank: '', comments: 0, subIssues: 0, + startDate: object.startDate, dueDate: object.dueDate, parents: parentIssue != null diff --git a/plugins/tracker-resources/src/components/SubIssues.svelte b/plugins/tracker-resources/src/components/SubIssues.svelte index d0e4bf0835a..86b8b2264ed 100644 --- a/plugins/tracker-resources/src/components/SubIssues.svelte +++ b/plugins/tracker-resources/src/components/SubIssues.svelte @@ -86,6 +86,7 @@ rank: '', comments: 0, subIssues: 0, + startDate: subIssue.startDate ?? null, dueDate: null, parents, reportedTime: 0, diff --git a/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte b/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte index 6139262bc37..31b1fd37902 100644 --- a/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte +++ b/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte @@ -34,6 +34,7 @@ status: MilestoneStatus.Planned, comments: 0, attachments: 0, + startDate: null, targetDate: Date.now() + 14 * 24 * 60 * 60 * 1000 } diff --git a/plugins/tracker-resources/src/components/templates/DraftIssueChildEditor.svelte b/plugins/tracker-resources/src/components/templates/DraftIssueChildEditor.svelte index 899f5748ba4..64fc96254ac 100644 --- a/plugins/tracker-resources/src/components/templates/DraftIssueChildEditor.svelte +++ b/plugins/tracker-resources/src/components/templates/DraftIssueChildEditor.svelte @@ -71,6 +71,7 @@ assignee: project.defaultAssignee ?? null, status: project.defaultIssueStatus, space: project._id, + startDate: null, dueDate: null, subIssues: [], attachments: 0, diff --git a/plugins/tracker/src/index.ts b/plugins/tracker/src/index.ts index bc1cba1152c..78a4dcae8df 100644 --- a/plugins/tracker/src/index.ts +++ b/plugins/tracker/src/index.ts @@ -119,6 +119,22 @@ export enum IssuePriority { Low } +/** + * Dependency kind between two Issues for Gantt scheduling. + * + * - `finish-to-start` (FS): A must finish before B can start. Most common. + * - `start-to-start` (SS): A must start before B can start. + * - `finish-to-finish` (FF): A must finish before B can finish. + * - `start-to-finish` (SF): A must start before B can finish. Rare. + * + * @public + */ +export type DependencyKind = + | 'finish-to-start' + | 'start-to-start' + | 'finish-to-finish' + | 'start-to-finish' + /** * @public */ @@ -175,6 +191,7 @@ export interface Milestone extends Doc { comments: number attachments?: number + startDate: Timestamp | null // null = open-ended begin marker targetDate: Timestamp } @@ -196,6 +213,8 @@ export interface Issue extends Task { relations?: RelatedDocument[] parents: IssueParentInfo[] + startDate: Timestamp | null // for Gantt scheduling; null = unscheduled + space: Ref milestone?: Ref | null @@ -236,6 +255,7 @@ export interface IssueDraft { assignee: Ref | null component: Ref | null space: Ref + startDate: Timestamp | null dueDate: Timestamp | null milestone?: Ref | null @@ -323,6 +343,22 @@ export interface IssueParentInfo { space: Ref } +/** + * Typed dependency between two Issues, used by the Gantt view to compute + * cascade scheduling and critical path. + * + * Persisted as an AttachedDoc collection 'relations' on the source Issue. + * + * @public + */ +export interface IssueRelation extends AttachedDoc { + attachedTo: Ref // source / predecessor + target: Ref // successor + kind: DependencyKind + /** Lag in schedule days; can be negative (overlap). */ + lag: number +} + /** * @public */ @@ -366,6 +402,7 @@ const pluginState = plugin(trackerId, { class: { Project: '' as Ref>, Issue: '' as Ref>, + IssueRelation: '' as Ref>, IssueTemplate: '' as Ref>, Component: '' as Ref>, IssueStatus: '' as Ref>, @@ -520,6 +557,13 @@ const pluginState = plugin(trackerId, { Project: '' as IntlString, RelatedIssues: '' as IntlString, Issue: '' as IntlString, + IssueStartDate: '' as IntlString, + GanttDependency: '' as IntlString, + GanttDependencyFinishToFinish: '' as IntlString, + GanttDependencyFinishToStart: '' as IntlString, + GanttDependencyStartToFinish: '' as IntlString, + GanttDependencyStartToStart: '' as IntlString, + GanttLag: '' as IntlString, NewProject: '' as IntlString, UnsetParentIssue: '' as IntlString, ForbidCreateProjectPermission: '' as IntlString, diff --git a/services/github/pod-github/src/sync/issueBase.ts b/services/github/pod-github/src/sync/issueBase.ts index bf4ce7535fa..ff181599b66 100644 --- a/services/github/pod-github/src/sync/issueBase.ts +++ b/services/github/pod-github/src/sync/issueBase.ts @@ -69,6 +69,7 @@ WithMarkup, | 'reports' | 'childInfo' | 'dueDate' +| 'startDate' | 'kind' | 'reviews' | 'reviewThreads' diff --git a/services/github/pod-github/src/sync/issues.ts b/services/github/pod-github/src/sync/issues.ts index d4121953a8f..07d7b2cea98 100644 --- a/services/github/pod-github/src/sync/issues.ts +++ b/services/github/pod-github/src/sync/issues.ts @@ -868,6 +868,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan rank: calcRank(lastOne, undefined), comments: 0, subIssues: 0, + startDate: null, dueDate: null, parents: [], reportedTime: 0, diff --git a/services/github/pod-github/src/sync/pullrequests.ts b/services/github/pod-github/src/sync/pullrequests.ts index 0d574222057..ebc6e9054d4 100644 --- a/services/github/pod-github/src/sync/pullrequests.ts +++ b/services/github/pod-github/src/sync/pullrequests.ts @@ -1169,6 +1169,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS rank: calcRank(lastOne, undefined), comments: 0, subIssues: 0, + startDate: null, dueDate: null, parents: [], reportedTime: 0, From 5c9faf7564cae3048ccdf6344e1ae5132e92e328 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:27:37 +0000 Subject: [PATCH 002/248] test(model-tracker): add migrateAddStartDate jest tests 3 tests covering migrateAddStartDate: - writes startDate=null to Issues in DOMAIN_TASK with the right filter - writes startDate=null to Milestones in DOMAIN_TRACKER with the right filter - issues exactly two update calls (one per class) Follows the MigrationClient mock pattern from models/chat/src/__tests__/migration.test.ts. Signed-off-by: Michael Uray --- .../tracker/src/__tests__/migration.test.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 models/tracker/src/__tests__/migration.test.ts diff --git a/models/tracker/src/__tests__/migration.test.ts b/models/tracker/src/__tests__/migration.test.ts new file mode 100644 index 00000000000..68f452b2415 --- /dev/null +++ b/models/tracker/src/__tests__/migration.test.ts @@ -0,0 +1,49 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// + +import { DOMAIN_TASK } from '@hcengineering/model-task' +import tracker from '@hcengineering/tracker' + +import { DOMAIN_TRACKER } from '../types' +import { migrateAddStartDate } from '../migration' + +describe('migrateAddStartDate', () => { + it('sets startDate=null on every Issue lacking the field (DOMAIN_TASK)', async () => { + const update = jest.fn().mockResolvedValue(undefined) + const client: any = { update } + + await migrateAddStartDate(client) + + expect(update).toHaveBeenCalledWith( + DOMAIN_TASK, + { _class: tracker.class.Issue, startDate: { $exists: false } }, + { startDate: null } + ) + }) + + it('sets startDate=null on every Milestone lacking the field (DOMAIN_TRACKER)', async () => { + const update = jest.fn().mockResolvedValue(undefined) + const client: any = { update } + + await migrateAddStartDate(client) + + expect(update).toHaveBeenCalledWith( + DOMAIN_TRACKER, + { _class: tracker.class.Milestone, startDate: { $exists: false } }, + { startDate: null } + ) + }) + + it('issues exactly two update calls (one per class)', async () => { + const update = jest.fn().mockResolvedValue(undefined) + const client: any = { update } + + await migrateAddStartDate(client) + + expect(update).toHaveBeenCalledTimes(2) + }) +}) From 86414230c0e4ae97962a964a2f63e934a440ef43 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:27:37 +0000 Subject: [PATCH 003/248] feat(model-tracker): add migrateAddStartDate + wire into trackerOperation Backfills startDate=null on existing Issues (DOMAIN_TASK) and Milestones (DOMAIN_TRACKER) so the new schema field has a defined value on every pre-existing document. Idempotent via the standard tryMigrate state-key mechanism (state: 'gantt-add-startdate'). Verified domain choices against existing migration helpers: - migrateIdentifiers / passIdentifierToParentInfo use DOMAIN_TASK for Issues (lines 145, 161 in this file). - TMilestone @Model decorator confirms DOMAIN_TRACKER for Milestones (models/tracker/src/types.ts:372). Signed-off-by: Michael Uray --- models/tracker/src/migration.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/models/tracker/src/migration.ts b/models/tracker/src/migration.ts index d07660e56c6..a6a119bdb72 100644 --- a/models/tracker/src/migration.ts +++ b/models/tracker/src/migration.ts @@ -47,6 +47,7 @@ import tracker, { } from '@hcengineering/tracker' import { classicIssueTaskStatuses } from '.' +import { DOMAIN_TRACKER } from './types' async function createDefaultProject (tx: TxOperations): Promise { const current = await tx.findOne(tracker.class.Project, { @@ -170,6 +171,22 @@ async function migrateIdentifiers (client: MigrationClient): Promise { } } +export async function migrateAddStartDate (client: MigrationClient): Promise { + // Issues live in DOMAIN_TASK (verified against migrateIdentifiers and + // passIdentifierToParentInfo at lines 144 and 159) + await client.update( + DOMAIN_TASK, + { _class: tracker.class.Issue, startDate: { $exists: false } }, + { startDate: null } + ) + // Milestones live in DOMAIN_TRACKER (verified against TMilestone @Model decorator) + await client.update( + DOMAIN_TRACKER, + { _class: tracker.class.Milestone, startDate: { $exists: false } }, + { startDate: null } + ) +} + async function migrateDefaultStatuses (client: MigrationClient, logger: ModelLogger): Promise { const defaultTypeId = tracker.ids.ClassingProjectType const typeDescriptor = tracker.descriptors.ProjectType @@ -398,6 +415,11 @@ export const trackerOperation: MigrateOperation = { state: 'migrateDefaultTypeMixins', mode: 'upgrade', func: migrateDefaultTypeMixins + }, + { + state: 'gantt-add-startdate', + mode: 'upgrade', + func: migrateAddStartDate } ]) }, From 8d932e8758a74e29779f9704aae9b39a1eeb947b Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:27:37 +0000 Subject: [PATCH 004/248] feat(tracker): expose Issue.startDate / Milestone.startDate in UI; tighten typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI changes (so the new schema fields are actually editable, in chronological order Start → Due/Target): - New StartDateEditor.svelte (mirrors DueDateEditor.svelte for startDate) - ControlPanel: render Start Date row above Due Date row in the issue side panel; both always-visible (no `!== null` guard) so users can set them on issues that don't have a date yet - NewMilestone form: Start Date input above Target Date input - Milestone list view: Start Date column before Target Date column - TIssueRelation: tighten interface to `extends AttachedDoc` so attachedTo + collection are statically typed. The model class re-declares `collection: 'relations'` to match the narrower base. - Drop 4 unused Dependency-kind IntlString keys (FinishToFinish, FinishToStart, StartToFinish, StartToStart) — they had no consumer in PR 1; will be re-introduced in PR 4 (dependency editor). - Simplify migration.ts comments — drop ageing line-references. Signed-off-by: Michael Uray --- models/tracker/src/migration.ts | 4 +- models/tracker/src/types.ts | 2 + models/tracker/src/viewlets.ts | 8 ++- plugins/tracker-assets/lang/cs.json | 4 -- plugins/tracker-assets/lang/de.json | 4 -- plugins/tracker-assets/lang/en.json | 4 -- plugins/tracker-assets/lang/es.json | 4 -- plugins/tracker-assets/lang/fr.json | 4 -- plugins/tracker-assets/lang/it.json | 4 -- plugins/tracker-assets/lang/ja.json | 4 -- plugins/tracker-assets/lang/ko.json | 4 -- plugins/tracker-assets/lang/pt-br.json | 4 -- plugins/tracker-assets/lang/pt.json | 4 -- plugins/tracker-assets/lang/ru.json | 4 -- plugins/tracker-assets/lang/tr.json | 4 -- plugins/tracker-assets/lang/zh.json | 4 -- .../components/issues/StartDateEditor.svelte | 53 +++++++++++++++++++ .../issues/edit/ControlPanel.svelte | 19 ++++--- .../components/milestones/NewMilestone.svelte | 8 +++ plugins/tracker/src/index.ts | 7 +-- 20 files changed, 84 insertions(+), 69 deletions(-) create mode 100644 plugins/tracker-resources/src/components/issues/StartDateEditor.svelte diff --git a/models/tracker/src/migration.ts b/models/tracker/src/migration.ts index a6a119bdb72..558674f7206 100644 --- a/models/tracker/src/migration.ts +++ b/models/tracker/src/migration.ts @@ -172,14 +172,12 @@ async function migrateIdentifiers (client: MigrationClient): Promise { } export async function migrateAddStartDate (client: MigrationClient): Promise { - // Issues live in DOMAIN_TASK (verified against migrateIdentifiers and - // passIdentifierToParentInfo at lines 144 and 159) + // Issues live in DOMAIN_TASK; Milestones live in DOMAIN_TRACKER. await client.update( DOMAIN_TASK, { _class: tracker.class.Issue, startDate: { $exists: false } }, { startDate: null } ) - // Milestones live in DOMAIN_TRACKER (verified against TMilestone @Model decorator) await client.update( DOMAIN_TRACKER, { _class: tracker.class.Milestone, startDate: { $exists: false } }, diff --git a/models/tracker/src/types.ts b/models/tracker/src/types.ts index 369493111b8..249a592ef6d 100644 --- a/models/tracker/src/types.ts +++ b/models/tracker/src/types.ts @@ -356,6 +356,8 @@ export class TIssueRelation extends TAttachedDoc implements IssueRelation { @Prop(TypeRef(tracker.class.Issue), tracker.string.Issue) declare attachedTo: Ref + declare collection: 'relations' + @Prop(TypeRef(tracker.class.Issue), tracker.string.Issue) @Index(IndexKind.Indexed) target!: Ref diff --git a/models/tracker/src/viewlets.ts b/models/tracker/src/viewlets.ts index 8cb2463fc2a..509e00d7d0e 100644 --- a/models/tracker/src/viewlets.ts +++ b/models/tracker/src/viewlets.ts @@ -663,7 +663,7 @@ export function defineViewlets (builder: Builder): void { viewOptions: milestoneOptions, configOptions: { strict: true, - hiddenKeys: ['targetDate', 'label', 'description'] + hiddenKeys: ['startDate', 'targetDate', 'label', 'description'] }, config: [ { @@ -672,6 +672,12 @@ export function defineViewlets (builder: Builder): void { }, { key: '', presenter: tracker.component.MilestonePresenter, props: { shouldUseMargin: true } }, { key: '', displayProps: { grow: true } }, + { + key: '', + label: tracker.string.StartDate, + presenter: tracker.component.MilestoneDatePresenter, + props: { field: 'startDate' } + }, { key: '', label: tracker.string.TargetDate, diff --git a/plugins/tracker-assets/lang/cs.json b/plugins/tracker-assets/lang/cs.json index 27764f0806e..dea411668af 100644 --- a/plugins/tracker-assets/lang/cs.json +++ b/plugins/tracker-assets/lang/cs.json @@ -117,10 +117,6 @@ "DueDate": "Datum splnění", "IssueStartDate": "Datum zahájení", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manuální", "All": "Vše", diff --git a/plugins/tracker-assets/lang/de.json b/plugins/tracker-assets/lang/de.json index ae1645b8649..d1e698c9c9a 100644 --- a/plugins/tracker-assets/lang/de.json +++ b/plugins/tracker-assets/lang/de.json @@ -127,10 +127,6 @@ "DueDate": "Fälligkeitsdatum", "IssueStartDate": "Startdatum", "GanttDependency": "Abhängigkeit", - "GanttDependencyFinishToFinish": "Ende zu Ende", - "GanttDependencyFinishToStart": "Ende zu Anfang", - "GanttDependencyStartToFinish": "Anfang zu Ende", - "GanttDependencyStartToStart": "Anfang zu Anfang", "GanttLag": "Verzögerung", "Manual": "Manuell", "All": "Alle", diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index 6b19e6e47f2..3d856a20031 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -127,10 +127,6 @@ "DueDate": "Due date", "IssueStartDate": "Start date", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manual", "All": "All", diff --git a/plugins/tracker-assets/lang/es.json b/plugins/tracker-assets/lang/es.json index 909cd5d8aa5..ed9795ef41b 100644 --- a/plugins/tracker-assets/lang/es.json +++ b/plugins/tracker-assets/lang/es.json @@ -125,10 +125,6 @@ "DueDate": "Fecha de vencimiento", "IssueStartDate": "Fecha de inicio", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manual", "All": "Todos", diff --git a/plugins/tracker-assets/lang/fr.json b/plugins/tracker-assets/lang/fr.json index de5343c7651..daa7f4eaaba 100644 --- a/plugins/tracker-assets/lang/fr.json +++ b/plugins/tracker-assets/lang/fr.json @@ -125,10 +125,6 @@ "DueDate": "Date d'échéance", "IssueStartDate": "Date de début", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manuel", "All": "Tous", diff --git a/plugins/tracker-assets/lang/it.json b/plugins/tracker-assets/lang/it.json index 80024447846..7fe2285f261 100644 --- a/plugins/tracker-assets/lang/it.json +++ b/plugins/tracker-assets/lang/it.json @@ -125,10 +125,6 @@ "DueDate": "Data di scadenza", "IssueStartDate": "Data di inizio", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manuale", "All": "Tutti", diff --git a/plugins/tracker-assets/lang/ja.json b/plugins/tracker-assets/lang/ja.json index 28744b81c50..34426b09875 100644 --- a/plugins/tracker-assets/lang/ja.json +++ b/plugins/tracker-assets/lang/ja.json @@ -125,10 +125,6 @@ "DueDate": "期日", "IssueStartDate": "開始日", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "手動", "All": "すべて", diff --git a/plugins/tracker-assets/lang/ko.json b/plugins/tracker-assets/lang/ko.json index 4d8b0f91063..32b4b4c75c6 100644 --- a/plugins/tracker-assets/lang/ko.json +++ b/plugins/tracker-assets/lang/ko.json @@ -125,10 +125,6 @@ "DueDate": "마감일", "IssueStartDate": "시작일", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "수동", "All": "전체", diff --git a/plugins/tracker-assets/lang/pt-br.json b/plugins/tracker-assets/lang/pt-br.json index 9a61364dee1..9d8ec0214e2 100644 --- a/plugins/tracker-assets/lang/pt-br.json +++ b/plugins/tracker-assets/lang/pt-br.json @@ -125,10 +125,6 @@ "DueDate": "Data de vencimento", "IssueStartDate": "Data de início", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manual", "All": "Todos", diff --git a/plugins/tracker-assets/lang/pt.json b/plugins/tracker-assets/lang/pt.json index 37d6f04e6cc..82a640e2c59 100644 --- a/plugins/tracker-assets/lang/pt.json +++ b/plugins/tracker-assets/lang/pt.json @@ -125,10 +125,6 @@ "DueDate": "Data de vencimento", "IssueStartDate": "Data de início", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manual", "All": "Todos", diff --git a/plugins/tracker-assets/lang/ru.json b/plugins/tracker-assets/lang/ru.json index e3f5c470dba..7ccd4cb5ccd 100644 --- a/plugins/tracker-assets/lang/ru.json +++ b/plugins/tracker-assets/lang/ru.json @@ -127,10 +127,6 @@ "DueDate": "Срок", "IssueStartDate": "Дата начала", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Пользовательский", "All": "Все", diff --git a/plugins/tracker-assets/lang/tr.json b/plugins/tracker-assets/lang/tr.json index cc146a55017..ec118014359 100644 --- a/plugins/tracker-assets/lang/tr.json +++ b/plugins/tracker-assets/lang/tr.json @@ -125,10 +125,6 @@ "DueDate": "Bitiş tarihi", "IssueStartDate": "Başlangıç tarihi", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "Manuel", "All": "Tümü", diff --git a/plugins/tracker-assets/lang/zh.json b/plugins/tracker-assets/lang/zh.json index 50c62441988..a424d856cac 100644 --- a/plugins/tracker-assets/lang/zh.json +++ b/plugins/tracker-assets/lang/zh.json @@ -127,10 +127,6 @@ "DueDate": "截止日期", "IssueStartDate": "开始日期", "GanttDependency": "Dependency", - "GanttDependencyFinishToFinish": "Finish to finish", - "GanttDependencyFinishToStart": "Finish to start", - "GanttDependencyStartToFinish": "Start to finish", - "GanttDependencyStartToStart": "Start to start", "GanttLag": "Lag", "Manual": "手动", "All": "全部", diff --git a/plugins/tracker-resources/src/components/issues/StartDateEditor.svelte b/plugins/tracker-resources/src/components/issues/StartDateEditor.svelte new file mode 100644 index 00000000000..b042f9d15db --- /dev/null +++ b/plugins/tracker-resources/src/components/issues/StartDateEditor.svelte @@ -0,0 +1,53 @@ + + + +{#if value} + handleStartDateChanged(e)} + shouldIgnoreOverdue={true} + /> +{/if} diff --git a/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte b/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte index 33f131bc571..9aa91228e29 100644 --- a/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte +++ b/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte @@ -35,6 +35,7 @@ import DueDateEditor from '../DueDateEditor.svelte' import PriorityEditor from '../PriorityEditor.svelte' import RelationEditor from '../RelationEditor.svelte' + import StartDateEditor from '../StartDateEditor.svelte' import StatusEditor from '../StatusEditor.svelte' import notification from '@hcengineering/notification' @@ -60,6 +61,7 @@ 'number', 'assignee', 'component', + 'startDate', 'dueDate', 'milestone', 'relations', @@ -202,14 +204,17 @@ - {#if issue.dueDate !== null} -
+
- - - - {/if} + + + + + + + {#if keys.length > 0}
diff --git a/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte b/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte index 31b1fd37902..a74c93fa508 100644 --- a/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte +++ b/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte @@ -77,6 +77,14 @@ /> + // source / predecessor +export interface IssueRelation extends AttachedDoc { target: Ref // successor kind: DependencyKind /** Lag in schedule days; can be negative (overlap). */ @@ -559,10 +558,6 @@ const pluginState = plugin(trackerId, { Issue: '' as IntlString, IssueStartDate: '' as IntlString, GanttDependency: '' as IntlString, - GanttDependencyFinishToFinish: '' as IntlString, - GanttDependencyFinishToStart: '' as IntlString, - GanttDependencyStartToFinish: '' as IntlString, - GanttDependencyStartToStart: '' as IntlString, GanttLag: '' as IntlString, NewProject: '' as IntlString, UnsetParentIssue: '' as IntlString, From c94a5cc73a06b701ff6120779f093e74705e6747 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:27:37 +0000 Subject: [PATCH 005/248] fix(tracker): set explicit @Prop ranks for Milestone date fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DocAttributeBar side panel sorts attributes by attr.rank ?? toRank(_id) (see plugins/view-resources/src/components/ClassAttributeBar.svelte:42-47), so without explicit ranks the visible order on a Milestone was hash-based (startDate before Status, breaking the chronological flow the user expects). Set ranks so the side panel renders Status → Start date → Target date. Comments and attachments stay where they are (they're collections, filtered out of the attribute panel by categorizeFields). Issues are unaffected — the Issue side panel is the custom ControlPanel.svelte which renders Start date / Due date in explicit slots (see PR 1's UI commit). Signed-off-by: Michael Uray --- models/tracker/src/types.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/models/tracker/src/types.ts b/models/tracker/src/types.ts index 249a592ef6d..962d2fd4424 100644 --- a/models/tracker/src/types.ts +++ b/models/tracker/src/types.ts @@ -407,7 +407,12 @@ export class TMilestone extends TDoc implements Milestone { @Prop(TypeMarkup(), tracker.string.Description) description?: Markup - @Prop(TypeMilestoneStatus(), tracker.string.Status) + // Rank-based ordering for the auto-generated DocAttributeBar side panel: + // Status → Start date → Target date. Without explicit ranks the panel + // sorts by toRank(attr._id) (a hash-derived rank), which would put + // startDate before status. See plugins/view-resources/src/components/ + // ClassAttributeBar.svelte:42-47. + @Prop(TypeMilestoneStatus(), tracker.string.Status, { rank: '0|a0001:' }) @Index(IndexKind.Indexed) status!: MilestoneStatus @@ -417,10 +422,10 @@ export class TMilestone extends TDoc implements Milestone { @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) attachments?: number - @Prop(TypeDate(), tracker.string.StartDate) + @Prop(TypeDate(), tracker.string.StartDate, { rank: '0|a0002:' }) startDate!: Timestamp | null - @Prop(TypeDate(), tracker.string.TargetDate) + @Prop(TypeDate(), tracker.string.TargetDate, { rank: '0|a0003:' }) targetDate!: Timestamp declare space: Ref From f84e497a083c45e0e489f41e3ad7675f077c3501 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:27:37 +0000 Subject: [PATCH 006/248] fix(tracker-resources): EditMilestone renders Status/Start/Target in body in chronological order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The right-side DocAttributeBar sorts attributes by attr.rank ?? toRank(_id), giving startDate before status (toRank('startDate') < toRank('status') lexicographically). Setting an explicit rank via @Prop's third arg did not propagate through the workspace upgrade for existing Attribute documents in the model TX log — the rank made it into the bundled txes but the existing Attribute creation TXes are not replaced on upgrade-workspace. Pivot: render Status, Start date, Target date in the EditMilestone body in explicit chronological order, and add 'status', 'startDate', 'targetDate' to ignoreKeys so they don't appear duplicated in the side panel. This mirrors how Issue's ControlPanel.svelte handles its date fields. Reverts the no-op @Prop rank attempt. Signed-off-by: Michael Uray --- models/tracker/src/types.ts | 11 +-- .../milestones/EditMilestone.svelte | 67 ++++++++++++++++++- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/models/tracker/src/types.ts b/models/tracker/src/types.ts index 962d2fd4424..249a592ef6d 100644 --- a/models/tracker/src/types.ts +++ b/models/tracker/src/types.ts @@ -407,12 +407,7 @@ export class TMilestone extends TDoc implements Milestone { @Prop(TypeMarkup(), tracker.string.Description) description?: Markup - // Rank-based ordering for the auto-generated DocAttributeBar side panel: - // Status → Start date → Target date. Without explicit ranks the panel - // sorts by toRank(attr._id) (a hash-derived rank), which would put - // startDate before status. See plugins/view-resources/src/components/ - // ClassAttributeBar.svelte:42-47. - @Prop(TypeMilestoneStatus(), tracker.string.Status, { rank: '0|a0001:' }) + @Prop(TypeMilestoneStatus(), tracker.string.Status) @Index(IndexKind.Indexed) status!: MilestoneStatus @@ -422,10 +417,10 @@ export class TMilestone extends TDoc implements Milestone { @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) attachments?: number - @Prop(TypeDate(), tracker.string.StartDate, { rank: '0|a0002:' }) + @Prop(TypeDate(), tracker.string.StartDate) startDate!: Timestamp | null - @Prop(TypeDate(), tracker.string.TargetDate, { rank: '0|a0003:' }) + @Prop(TypeDate(), tracker.string.TargetDate) targetDate!: Timestamp declare space: Ref diff --git a/plugins/tracker-resources/src/components/milestones/EditMilestone.svelte b/plugins/tracker-resources/src/components/milestones/EditMilestone.svelte index 1f8d90e19b3..2d86efef092 100644 --- a/plugins/tracker-resources/src/components/milestones/EditMilestone.svelte +++ b/plugins/tracker-resources/src/components/milestones/EditMilestone.svelte @@ -16,9 +16,10 @@ import { AttachmentStyleBoxEditor } from '@hcengineering/attachment-resources' import { getClient } from '@hcengineering/presentation' import { Milestone } from '@hcengineering/tracker' - import { EditBox, Label } from '@hcengineering/ui' + import { DatePresenter, EditBox, Label } from '@hcengineering/ui' import { createEventDispatcher, onMount } from 'svelte' import tracker from '../../plugin' + import MilestoneStatusEditor from './MilestoneStatusEditor.svelte' import QueryIssuesList from '../issues/edit/QueryIssuesList.svelte' export let object: Milestone @@ -33,12 +34,27 @@ await client.update(object, { [field]: value }) } + async function changeStartDate (value: number | null | undefined): Promise { + await client.update(object, { startDate: value ?? null }) + } + async function changeTargetDate (value: number | null | undefined): Promise { + if (value === null || value === undefined) return + await client.update(object, { targetDate: value }) + } + $: if (oldLabel !== object.label) { oldLabel = object.label rawLabel = object.label } - onMount(() => dispatch('open', { ignoreKeys: ['label', 'description', 'attachments'] })) + // status / startDate / targetDate are rendered in this component's body in + // chronological order (Status → Start → Target). Hide them from the + // auto-generated side panel so they don't appear twice. + onMount(() => + dispatch('open', { + ignoreKeys: ['label', 'description', 'attachments', 'status', 'startDate', 'targetDate'] + }) + ) $: descriptionKey = client.getHierarchy().getAttribute(tracker.class.Component, 'description') let descriptionBox: AttachmentStyleBoxEditor @@ -58,6 +74,33 @@ }} /> +
+
+ + +
+
+ + { void changeStartDate(e.detail) }} + /> +
+
+ + { void changeTargetDate(e.detail) }} + /> +
+
+
+ +
Date: Sun, 17 May 2026 10:47:13 +0000 Subject: [PATCH 007/248] fix(fulltext): bump model version to 0.7.423 to match deployed workspaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fulltext-pod's compiled model version (baked into bundle/model.json via common/scripts/version.txt at build time) lags whenever the workspaces have been migrated to a newer patch but the pod was not rebuilt. In that state the indexer rejects every incoming Tx with a `wrong version` warning, new issues silently fail to land in Elasticsearch, and search returns empty results for any document created after the migration. Bumping `version.txt` aligns the compiled model with the workspaces. All future builds (front, transactor, workspace, tool, fulltext) will emit 0.7.423, the indexer accepts the Tx stream again, and the deferred backlog gets consumed automatically — no manual reindex needed. This commit is the build-side companion to the schema migration in this same PR. Without it the fulltext-pod cannot consume the migrated workspace's Tx events. Signed-off-by: Michael Uray --- common/scripts/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/scripts/version.txt b/common/scripts/version.txt index b8caacae731..af1137434f7 100644 --- a/common/scripts/version.txt +++ b/common/scripts/version.txt @@ -1 +1 @@ -"0.7.422" +"0.7.423" From 32cef1e02e67a446e523d553c0cfe704375e8c57 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 008/248] feat(tracker): register Gantt view tab (placeholder UI) Signed-off-by: Michael Uray --- models/tracker/src/viewlets.ts | 48 +++++++++++++++++++ plugins/tracker-assets/lang/cs.json | 7 ++- plugins/tracker-assets/lang/de.json | 7 ++- plugins/tracker-assets/lang/en.json | 7 ++- plugins/tracker-assets/lang/es.json | 7 ++- plugins/tracker-assets/lang/fr.json | 7 ++- plugins/tracker-assets/lang/it.json | 7 ++- plugins/tracker-assets/lang/ja.json | 7 ++- plugins/tracker-assets/lang/ko.json | 7 ++- plugins/tracker-assets/lang/pt-br.json | 7 ++- plugins/tracker-assets/lang/pt.json | 7 ++- plugins/tracker-assets/lang/ru.json | 7 ++- plugins/tracker-assets/lang/tr.json | 7 ++- plugins/tracker-assets/lang/zh.json | 7 ++- plugins/tracker-resources/package.json | 4 +- .../src/components/gantt/GanttView.svelte | 41 ++++++++++++++++ plugins/tracker-resources/src/index.ts | 2 + plugins/tracker-resources/src/plugin.ts | 11 ++++- 18 files changed, 182 insertions(+), 15 deletions(-) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttView.svelte diff --git a/models/tracker/src/viewlets.ts b/models/tracker/src/viewlets.ts index 509e00d7d0e..194a5a7554d 100644 --- a/models/tracker/src/viewlets.ts +++ b/models/tracker/src/viewlets.ts @@ -212,6 +212,30 @@ export function issueConfig ( ] } +export function ganttViewOptions (): ViewOptionsModel { + // PR 2 ships a minimal read-only Gantt. Group-by / dropdown ViewOptions + // are intentionally NOT advertised here — the canvas does not honour them + // yet. They will be added in PR 3 alongside drag/edit + Component swimlanes + // so users never see options that have no effect. + return { + groupBy: [], + orderBy: [ + ['startDate', SortingOrder.Ascending], + ['rank', SortingOrder.Ascending], + ['dueDate', SortingOrder.Ascending] + ], + other: [showColorsViewOption] + } +} + +export function ganttConfig (): BuildModelKey[] { + // Minimal config — Gantt drives its own column layout. + return [ + { key: '', presenter: tracker.component.PriorityEditor, label: tracker.string.Priority, props: { kind: 'list', size: 'small' } }, + { key: '', presenter: tracker.component.IssuePresenter, label: tracker.string.Issue } + ] +} + export function defineViewlets (builder: Builder): void { builder.createDoc( view.class.ViewletDescriptor, @@ -224,6 +248,30 @@ export function defineViewlets (builder: Builder): void { tracker.viewlet.Kanban ) + builder.createDoc( + view.class.ViewletDescriptor, + core.space.Model, + { + label: tracker.string.Gantt, + icon: tracker.icon.Issues, + component: tracker.component.GanttView + }, + tracker.viewlet.Gantt + ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: tracker.class.Issue, + descriptor: tracker.viewlet.Gantt, + viewOptions: ganttViewOptions(), + configOptions: { strict: true, hiddenKeys: ['title'] }, + config: ganttConfig() + }, + tracker.viewlet.IssueGantt + ) + builder.createDoc( view.class.Viewlet, core.space.Model, diff --git a/plugins/tracker-assets/lang/cs.json b/plugins/tracker-assets/lang/cs.json index dea411668af..9f87e86aff4 100644 --- a/plugins/tracker-assets/lang/cs.json +++ b/plugins/tracker-assets/lang/cs.json @@ -283,7 +283,12 @@ "UnsetParentIssue": "Odebrat nadřazený úkol", "ForbidCreateProjectPermission": "Zakázat vytvoření projektu", "ForbidCreateProjectPermissionDescription": "Zakazuje uživatelům vytvářet nové projekty", - "AllowCreatingIssues": "Povolit vytváření úkolů" + "AllowCreatingIssues": "Povolit vytváření úkolů", + "Day": "Den", + "Gantt": "Gantt", + "Month": "Měsíc", + "Quarter": "Čtvrtletí", + "Week": "Týden" }, "status": {} } diff --git a/plugins/tracker-assets/lang/de.json b/plugins/tracker-assets/lang/de.json index d1e698c9c9a..51aa73d1518 100644 --- a/plugins/tracker-assets/lang/de.json +++ b/plugins/tracker-assets/lang/de.json @@ -293,7 +293,12 @@ "UnsetParentIssue": "Übergeordnete Aufgabe entfernen", "ForbidCreateProjectPermission": "Projekterstellung verbieten", "ForbidCreateProjectPermissionDescription": "Verbietet Benutzern das Erstellen neuer Projekte", - "AllowCreatingIssues": "Erstellen von Aufgaben erlauben" + "AllowCreatingIssues": "Erstellen von Aufgaben erlauben", + "Day": "Tag", + "Gantt": "Gantt", + "Month": "Monat", + "Quarter": "Quartal", + "Week": "Woche" }, "status": {} } diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index 3d856a20031..7dad04bee41 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -293,7 +293,12 @@ "UnsetParentIssue": "Unset parent issue", "ForbidCreateProjectPermission": "Forbid create project", "ForbidCreateProjectPermissionDescription": "Forbid users creating new projects", - "AllowCreatingIssues": "Allow creating issues" + "AllowCreatingIssues": "Allow creating issues", + "Day": "Day", + "Gantt": "Gantt", + "Month": "Month", + "Quarter": "Quarter", + "Week": "Week" }, "status": {} } diff --git a/plugins/tracker-assets/lang/es.json b/plugins/tracker-assets/lang/es.json index ed9795ef41b..127dc0a4f63 100644 --- a/plugins/tracker-assets/lang/es.json +++ b/plugins/tracker-assets/lang/es.json @@ -276,7 +276,12 @@ "UnsetParentIssue": "Unset parent issue", "ForbidCreateProjectPermission": "Prohibir crear proyecto", "ForbidCreateProjectPermissionDescription": "Prohíbe a los usuarios crear nuevos proyectos", - "AllowCreatingIssues": "Permitir crear incidencias" + "AllowCreatingIssues": "Permitir crear incidencias", + "Day": "Día", + "Gantt": "Gantt", + "Month": "Mes", + "Quarter": "Trimestre", + "Week": "Semana" }, "status": {} } diff --git a/plugins/tracker-assets/lang/fr.json b/plugins/tracker-assets/lang/fr.json index daa7f4eaaba..4bf83349e83 100644 --- a/plugins/tracker-assets/lang/fr.json +++ b/plugins/tracker-assets/lang/fr.json @@ -276,7 +276,12 @@ "UnsetParentIssue": "Désélectionner l'issue parent", "ForbidCreateProjectPermission": "Interdire la création de projet", "ForbidCreateProjectPermissionDescription": "Interdit aux utilisateurs de créer de nouveaux projets", - "AllowCreatingIssues": "Autoriser la création d'issues" + "AllowCreatingIssues": "Autoriser la création d'issues", + "Day": "Jour", + "Gantt": "Gantt", + "Month": "Mois", + "Quarter": "Trimestre", + "Week": "Semaine" }, "status": {} } diff --git a/plugins/tracker-assets/lang/it.json b/plugins/tracker-assets/lang/it.json index 7fe2285f261..02d01f2850c 100644 --- a/plugins/tracker-assets/lang/it.json +++ b/plugins/tracker-assets/lang/it.json @@ -276,7 +276,12 @@ "UnsetParentIssue": "Annulla l'issue genitore", "ForbidCreateProjectPermission": "Vieta creazione progetto", "ForbidCreateProjectPermissionDescription": "Vieta agli utenti di creare nuovi progetti", - "AllowCreatingIssues": "Consenti la creazione di issue" + "AllowCreatingIssues": "Consenti la creazione di issue", + "Day": "Giorno", + "Gantt": "Gantt", + "Month": "Mese", + "Quarter": "Trimestre", + "Week": "Settimana" }, "status": {} } diff --git a/plugins/tracker-assets/lang/ja.json b/plugins/tracker-assets/lang/ja.json index 34426b09875..61b13474fd4 100644 --- a/plugins/tracker-assets/lang/ja.json +++ b/plugins/tracker-assets/lang/ja.json @@ -276,7 +276,12 @@ "UnsetParentIssue": "親イシューの設定を解除", "ForbidCreateProjectPermission": "プロジェクト作成禁止", "ForbidCreateProjectPermissionDescription": "ユーザーが新しいプロジェクトを作成することを禁止します", - "AllowCreatingIssues": "イシューの作成を許可" + "AllowCreatingIssues": "イシューの作成を許可", + "Day": "日", + "Gantt": "Gantt", + "Month": "月", + "Quarter": "四半期", + "Week": "週" }, "status": {} } diff --git a/plugins/tracker-assets/lang/ko.json b/plugins/tracker-assets/lang/ko.json index 32b4b4c75c6..5d8c5ca23b6 100644 --- a/plugins/tracker-assets/lang/ko.json +++ b/plugins/tracker-assets/lang/ko.json @@ -276,7 +276,12 @@ "UnsetParentIssue": "상위 이슈 설정 해제", "ForbidCreateProjectPermission": "프로젝트 생성 금지", "ForbidCreateProjectPermissionDescription": "사용자의 새 프로젝트 생성을 금지", - "AllowCreatingIssues": "이슈 생성 허용" + "AllowCreatingIssues": "이슈 생성 허용", + "Day": "일", + "Gantt": "Gantt", + "Month": "월", + "Quarter": "분기", + "Week": "주" }, "status": {} } diff --git a/plugins/tracker-assets/lang/pt-br.json b/plugins/tracker-assets/lang/pt-br.json index 9d8ec0214e2..c32447c979d 100644 --- a/plugins/tracker-assets/lang/pt-br.json +++ b/plugins/tracker-assets/lang/pt-br.json @@ -276,7 +276,12 @@ "UnsetParentIssue": "Desmarcar problema pai", "ForbidCreateProjectPermission": "Proibir criação de projeto", "ForbidCreateProjectPermissionDescription": "Proíbe os usuários de criar novos projetos", - "AllowCreatingIssues": "Permitir criar problemas" + "AllowCreatingIssues": "Permitir criar problemas", + "Day": "Dia", + "Gantt": "Gantt", + "Month": "Mês", + "Quarter": "Trimestre", + "Week": "Semana" }, "status": {} } diff --git a/plugins/tracker-assets/lang/pt.json b/plugins/tracker-assets/lang/pt.json index 82a640e2c59..d83e2745a75 100644 --- a/plugins/tracker-assets/lang/pt.json +++ b/plugins/tracker-assets/lang/pt.json @@ -276,7 +276,12 @@ "UnsetParentIssue": "Desmarcar problema pai", "ForbidCreateProjectPermission": "Proibir criação de projeto", "ForbidCreateProjectPermissionDescription": "Proíbe os usuários de criar novos projetos", - "AllowCreatingIssues": "Permitir criar problemas" + "AllowCreatingIssues": "Permitir criar problemas", + "Day": "Dia", + "Gantt": "Gantt", + "Month": "Mês", + "Quarter": "Trimestre", + "Week": "Semana" }, "status": {} } diff --git a/plugins/tracker-assets/lang/ru.json b/plugins/tracker-assets/lang/ru.json index 7ccd4cb5ccd..142a3eec2b3 100644 --- a/plugins/tracker-assets/lang/ru.json +++ b/plugins/tracker-assets/lang/ru.json @@ -293,7 +293,12 @@ "UnsetParentIssue": "Снять родительскую задачу", "ForbidCreateProjectPermission": "Запретить создание проекта", "ForbidCreateProjectPermissionDescription": "Запрещает пользователям создавать новые проекты", - "AllowCreatingIssues": "Разрешить создание задач" + "AllowCreatingIssues": "Разрешить создание задач", + "Day": "День", + "Gantt": "Gantt", + "Month": "Месяц", + "Quarter": "Квартал", + "Week": "Неделя" }, "status": {} } diff --git a/plugins/tracker-assets/lang/tr.json b/plugins/tracker-assets/lang/tr.json index ec118014359..0e040c303d7 100644 --- a/plugins/tracker-assets/lang/tr.json +++ b/plugins/tracker-assets/lang/tr.json @@ -274,7 +274,12 @@ "IssueStatus": "Durum", "Extensions": "Uzantılar", "UnsetParentIssue": "Üst sorunu kaldır", - "AllowCreatingIssues": "Sorun oluşturmaya izin ver" + "AllowCreatingIssues": "Sorun oluşturmaya izin ver", + "Day": "Gün", + "Gantt": "Gantt", + "Month": "Ay", + "Quarter": "Çeyrek", + "Week": "Hafta" }, "status": {} } diff --git a/plugins/tracker-assets/lang/zh.json b/plugins/tracker-assets/lang/zh.json index a424d856cac..7bf3bc34d7c 100644 --- a/plugins/tracker-assets/lang/zh.json +++ b/plugins/tracker-assets/lang/zh.json @@ -293,7 +293,12 @@ "UnsetParentIssue": "取消父问题", "ForbidCreateProjectPermission": "禁止创建项目", "ForbidCreateProjectPermissionDescription": "禁止用户创建新项目", - "AllowCreatingIssues": "允许创建问题" + "AllowCreatingIssues": "允许创建问题", + "Day": "日", + "Gantt": "Gantt", + "Month": "月", + "Quarter": "季度", + "Week": "周" }, "status": {} } diff --git a/plugins/tracker-resources/package.json b/plugins/tracker-resources/package.json index 7afa566df43..7ae0b97d0a3 100644 --- a/plugins/tracker-resources/package.json +++ b/plugins/tracker-resources/package.json @@ -8,12 +8,14 @@ "build": "compile ui", "build:docs": "api-extractor run --local", "svelte-check": "do-svelte-check", + "test": "jest --passWithNoTests --silent", "_phase:svelte-check": "do-svelte-check", "format": "format src", "build:watch": "compile ui", "_phase:build": "compile ui", "_phase:format": "format src", - "_phase:validate": "compile validate" + "_phase:validate": "compile validate", + "_phase:test": "jest --passWithNoTests --silent" }, "devDependencies": { "svelte-loader": "^3.2.0", diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte new file mode 100644 index 00000000000..5a951fd57ab --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -0,0 +1,41 @@ + + + +
+
+
+
+ + diff --git a/plugins/tracker-resources/src/index.ts b/plugins/tracker-resources/src/index.ts index 071a13ac73f..58cec18fb0c 100644 --- a/plugins/tracker-resources/src/index.ts +++ b/plugins/tracker-resources/src/index.ts @@ -52,6 +52,7 @@ import ProjectComponents from './components/components/ProjectComponents.svelte' import CreateIssue from './components/CreateIssue.svelte' import EditRelatedTargets from './components/EditRelatedTargets.svelte' import EditRelatedTargetsPopup from './components/EditRelatedTargetsPopup.svelte' +import GanttView from './components/gantt/GanttView.svelte' import AssigneeEditor from './components/issues/AssigneeEditor.svelte' import DueDatePresenter from './components/issues/DueDatePresenter.svelte' import EditIssue from './components/issues/edit/EditIssue.svelte' @@ -434,6 +435,7 @@ export default async (): Promise => ({ EditComponent, IssuesView, KanbanView, + GanttView, ProjectComponents, IssuePreview, RelationsPopup, diff --git a/plugins/tracker-resources/src/plugin.ts b/plugins/tracker-resources/src/plugin.ts index eed088c59d5..fc1f85177ad 100644 --- a/plugins/tracker-resources/src/plugin.ts +++ b/plugins/tracker-resources/src/plugin.ts @@ -36,6 +36,8 @@ export default mergeIds(trackerId, tracker, { SubIssues: '' as Ref, List: '' as Ref, Kanban: '' as Ref, + Gantt: '' as Ref, + IssueGantt: '' as Ref, MilestoneIssuesList: '' as Ref, ComponentIssuesList: '' as Ref }, @@ -307,7 +309,13 @@ export default mergeIds(trackerId, tracker, { UnsetParent: '' as IntlString, PreviousAssigned: '' as IntlString, EditRelatedTargets: '' as IntlString, - RelatedIssueTargetDescription: '' as IntlString + RelatedIssueTargetDescription: '' as IntlString, + + Day: '' as IntlString, + Gantt: '' as IntlString, + Month: '' as IntlString, + Quarter: '' as IntlString, + Week: '' as IntlString }, component: { NopeComponent: '' as AnyComponent, @@ -347,6 +355,7 @@ export default mergeIds(trackerId, tracker, { EditComponent: '' as AnyComponent, IssuesView: '' as AnyComponent, KanbanView: '' as AnyComponent, + GanttView: '' as AnyComponent, ProjectComponents: '' as AnyComponent, IssuePreview: '' as AnyComponent, RelationsPopup: '' as AnyComponent, From cd7a58ab70455b28ccd0af1d8e7b8b58f55eb43d Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 009/248] feat(tracker-resources): add Gantt lib types Signed-off-by: Michael Uray --- .../src/components/gantt/.gitignore | 3 ++ .../src/components/gantt/lib/types.ts | 50 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/.gitignore create mode 100644 plugins/tracker-resources/src/components/gantt/lib/types.ts diff --git a/plugins/tracker-resources/src/components/gantt/.gitignore b/plugins/tracker-resources/src/components/gantt/.gitignore new file mode 100644 index 00000000000..176a11c6ff4 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/.gitignore @@ -0,0 +1,3 @@ +# Override root-level lib/ ignore — this lib/ is source code, not build output. +!lib/ +!lib/** diff --git a/plugins/tracker-resources/src/components/gantt/lib/types.ts b/plugins/tracker-resources/src/components/gantt/lib/types.ts new file mode 100644 index 00000000000..485ecabf5ae --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/lib/types.ts @@ -0,0 +1,50 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// SPDX-License-Identifier: EPL-2.0 +// + +import { type Ref } from '@hcengineering/core' +import { type Issue, type Component as TrackerComponent, type Milestone } from '@hcengineering/tracker' + +/** Zoom presets — controls pxPerDay and header tick density. */ +export type ZoomLevel = 'day' | 'week' | 'month' | 'quarter' + +/** A single tick on the time-axis header (vertical gridline + label). */ +export interface Tick { + date: number // UTC ms + label: string // pre-formatted, locale-aware + level: 'major' | 'minor' // major ticks render thicker + with text label +} + +/** A row in the flattened layout. May be an issue or a component-swimlane header. */ +export interface LayoutRow { + kind: 'issue' | 'component-swimlane' + /** Y-coord top-edge of the row in canvas pixels. */ + y: number + /** Row height in pixels. */ + height: number + /** Tree depth — 0 for top-level issues. */ + depth: number + /** Whether this row is currently rendered (vs virtually skipped). */ + visible: boolean + /** The issue this row represents — null for swimlane headers. */ + issue: Issue | null + /** The component this swimlane represents — null for issue rows. */ + component: Ref | null + /** True iff this issue has children (renders as summary "claw" bar). */ + isSummary: boolean +} + +/** Cached aggregate dates of a parent issue's children, for summary-bar rendering. */ +export interface SummaryRange { + startDate: number | null + dueDate: number | null +} + +/** Compact view of a Milestone for the canvas overlay. */ +export interface MilestoneMarker { + _id: Ref + label: string + startDate: number | null + targetDate: number +} From e9dd999b94d5e712081455a1e81b001188c5cbeb Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 010/248] test(tracker-resources): add failing Gantt time-scale tests Signed-off-by: Michael Uray --- .../gantt/lib/__tests__/time-scale.test.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts diff --git a/plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts b/plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts new file mode 100644 index 00000000000..e0b3b8262d2 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts @@ -0,0 +1,78 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// SPDX-License-Identifier: EPL-2.0 +// + +import { createTimeScale, snapToUtcMidnight } from '../time-scale' + +const DAY_MS = 86_400_000 + +describe('snapToUtcMidnight', () => { + it('returns 0 unchanged', () => { + expect(snapToUtcMidnight(0)).toBe(0) + }) + + it('rounds down to UTC midnight', () => { + const t = Date.UTC(2026, 4, 15, 17, 30, 45) // 2026-05-15 17:30:45 UTC + expect(snapToUtcMidnight(t)).toBe(Date.UTC(2026, 4, 15)) + }) + + it('is idempotent', () => { + const t = Date.UTC(2026, 0, 1) + expect(snapToUtcMidnight(snapToUtcMidnight(t))).toBe(t) + }) +}) + +describe('createTimeScale', () => { + const origin = Date.UTC(2026, 0, 1) // 2026-01-01 UTC + + it('week zoom: pxPerDay = 14', () => { + const ts = createTimeScale('week', origin) + expect(ts.pxPerDay).toBe(14) + }) + + it('day/month/quarter zoom values match preset', () => { + expect(createTimeScale('day', origin).pxPerDay).toBe(32) + expect(createTimeScale('month', origin).pxPerDay).toBe(4) + expect(createTimeScale('quarter', origin).pxPerDay).toBe(1.5) + }) + + it('toX(origin) === 0', () => { + const ts = createTimeScale('week', origin) + expect(ts.toX(origin)).toBe(0) + }) + + it('toX(origin + 7d) === 7 * pxPerDay', () => { + const ts = createTimeScale('week', origin) + expect(ts.toX(origin + 7 * DAY_MS)).toBe(7 * 14) + }) + + it('fromX is inverse of toX (snapped)', () => { + const ts = createTimeScale('week', origin) + const t = origin + 5 * DAY_MS + expect(ts.fromX(ts.toX(t))).toBe(t) + }) + + it('week zoom emits weekly ticks aligned to Monday', () => { + const ts = createTimeScale('week', origin) + const ticks = ts.ticks([origin, origin + 30 * DAY_MS]) + expect(ticks.length).toBeGreaterThanOrEqual(4) + expect(ticks.length).toBeLessThanOrEqual(6) + // First tick is the first Monday on or after origin + expect(ticks[0].date).toBeGreaterThanOrEqual(origin) + expect(ticks[0].date).toBeLessThan(origin + 7 * DAY_MS) + // Every tick is on a Monday. + for (const t of ticks) { + expect(new Date(t.date).getUTCDay()).toBe(1) + } + expect(ticks.every(t => Number.isInteger(t.date))).toBe(true) + }) + + it('all tick dates are UTC midnights', () => { + const ts = createTimeScale('week', origin) + const ticks = ts.ticks([origin, origin + 30 * DAY_MS]) + for (const t of ticks) { + expect(snapToUtcMidnight(t.date)).toBe(t.date) + } + }) +}) From 7d5b84f9d052e95a0fdc2e217102b2dea739bc52 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 011/248] feat(tracker-resources): implement Gantt time-scale lib Signed-off-by: Michael Uray --- .../src/components/gantt/lib/time-scale.ts | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/lib/time-scale.ts diff --git a/plugins/tracker-resources/src/components/gantt/lib/time-scale.ts b/plugins/tracker-resources/src/components/gantt/lib/time-scale.ts new file mode 100644 index 00000000000..da290d0389b --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/lib/time-scale.ts @@ -0,0 +1,131 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// SPDX-License-Identifier: EPL-2.0 +// + +import { type Tick, type ZoomLevel } from './types' + +const DAY_MS = 86_400_000 + +const PX_PER_DAY: Record = { + day: 32, + week: 14, + month: 4, + quarter: 1.5 +} + +/** Snap any Timestamp (ms) to the start of its UTC day. */ +export function snapToUtcMidnight (t: number): number { + const d = new Date(t) + return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()) +} + +export interface TimeScale { + /** Pixel width of one calendar day at the current zoom. */ + pxPerDay: number + /** Convert a Timestamp to its X coordinate (relative to origin). */ + toX: (t: number) => number + /** Convert an X coordinate back to a snapped Timestamp. */ + fromX: (x: number) => number + /** Generate header ticks across [from, to]. */ + ticks: (range: [number, number]) => Tick[] +} + +export function createTimeScale (zoom: ZoomLevel, origin: number): TimeScale { + const pxPerDay = PX_PER_DAY[zoom] + const originSnapped = snapToUtcMidnight(origin) + + const toX = (t: number): number => ((t - originSnapped) / DAY_MS) * pxPerDay + const fromX = (x: number): number => snapToUtcMidnight(originSnapped + (x / pxPerDay) * DAY_MS) + + const ticks = (range: [number, number]): Tick[] => { + const [from, to] = range + const fromDay = snapToUtcMidnight(from) + const result: Tick[] = [] + let cursor = fromDay + + switch (zoom) { + case 'day': { + while (cursor <= to) { + const d = new Date(cursor) + const isMonday = d.getUTCDay() === 1 + result.push({ + date: cursor, + label: d.getUTCDate().toString(), + level: isMonday ? 'major' : 'minor' + }) + cursor += DAY_MS + } + break + } + case 'week': { + const d = new Date(cursor) + const dow = d.getUTCDay() // 0=Sun + const offsetToMonday = ((1 - dow) + 7) % 7 + cursor += offsetToMonday * DAY_MS + while (cursor <= to) { + const c = new Date(cursor) + const isFirstWeekOfMonth = c.getUTCDate() <= 7 + result.push({ + date: cursor, + label: `W${isoWeekNumber(c)}`, + level: isFirstWeekOfMonth ? 'major' : 'minor' + }) + cursor += 7 * DAY_MS + } + break + } + case 'month': { + const start = new Date(cursor) + let y = start.getUTCFullYear() + let m = start.getUTCMonth() + cursor = Date.UTC(y, m, 1) + while (cursor <= to) { + const c = new Date(cursor) + result.push({ + date: cursor, + label: c.toLocaleString(undefined, { month: 'short', timeZone: 'UTC' }), + level: c.getUTCMonth() === 0 ? 'major' : 'minor' + }) + m += 1 + if (m > 11) { m = 0; y += 1 } + cursor = Date.UTC(y, m, 1) + } + break + } + case 'quarter': { + const start = new Date(cursor) + let y = start.getUTCFullYear() + let q = Math.floor(start.getUTCMonth() / 3) + cursor = Date.UTC(y, q * 3, 1) + while (cursor <= to) { + const c = new Date(cursor) + const qNum = Math.floor(c.getUTCMonth() / 3) + 1 + result.push({ + date: cursor, + label: `Q${qNum} ${c.getUTCFullYear()}`, + level: qNum === 1 ? 'major' : 'minor' + }) + q += 1 + if (q > 3) { q = 0; y += 1 } + cursor = Date.UTC(y, q * 3, 1) + } + break + } + } + + return result + } + + return { pxPerDay, toX, fromX, ticks } +} + +/** ISO 8601 week number for a UTC date. */ +function isoWeekNumber (d: Date): number { + const target = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())) + const dayNum = (target.getUTCDay() + 6) % 7 + target.setUTCDate(target.getUTCDate() - dayNum + 3) + const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4)) + const diff = target.getTime() - firstThursday.getTime() + return 1 + Math.round(diff / (7 * DAY_MS)) +} From a8e88a3cc53eec1a837826972f4c60732ef32111 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 012/248] test(tracker-resources): add failing Gantt layout tests Signed-off-by: Michael Uray --- .../gantt/lib/__tests__/layout.test.ts | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts diff --git a/plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts b/plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts new file mode 100644 index 00000000000..a58bd4ad8b4 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts @@ -0,0 +1,99 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// SPDX-License-Identifier: EPL-2.0 +// + +import type { Issue } from '@hcengineering/tracker' +import { buildLayout, filterVisibleRows } from '../layout' +import { type LayoutRow } from '../types' + +function fakeIssue (id: string, parentId?: string, hasChildren = false): Issue { + return { + _id: id as any, + _class: 'tracker:class:Issue' as any, + title: `Issue ${id}`, + space: 'project-1' as any, + component: null, + milestone: null, + startDate: null, + dueDate: null, + parents: parentId !== undefined ? [{ parentId: parentId as any }] : [], + childInfo: hasChildren ? [{ childId: 'fake-child' as any, count: 0, category: '' }] : [], + estimation: 0, + remainingTime: 0, + reportedTime: 0, + reports: 0, + subIssues: 0, + priority: 0, + status: '' as any, + attachedTo: 'tracker:ids:NoParent' as any + } as unknown as Issue +} + +const ROW_H = 28 + +describe('buildLayout (no grouping)', () => { + it('flattens a flat list of root issues', () => { + const issues = [fakeIssue('a'), fakeIssue('b')] + const rows = buildLayout(issues, 'none', ROW_H) + expect(rows).toHaveLength(2) + expect(rows[0].issue?._id).toBe('a') + expect(rows[1].issue?._id).toBe('b') + expect(rows[0].depth).toBe(0) + expect(rows[0].y).toBe(0) + expect(rows[1].y).toBe(ROW_H) + }) + + it('places children below parent with depth+1', () => { + const a = fakeIssue('a', undefined, true) + const child = fakeIssue('a.1', 'a') + const rows = buildLayout([a, child], 'none', ROW_H) + expect(rows.find(r => r.issue?._id === 'a.1')?.depth).toBe(1) + }) + + it('marks parent issues as summary rows', () => { + const a = fakeIssue('a', undefined, true) + const child = fakeIssue('a.1', 'a') + const rows = buildLayout([a, child], 'none', ROW_H) + const parentRow = rows.find(r => r.issue?._id === 'a')! + expect(parentRow.isSummary).toBe(true) + expect(rows.find(r => r.issue?._id === 'a.1')?.isSummary).toBe(false) + }) + + it('row Y coordinates are sequential multiples of rowHeight', () => { + const issues = [fakeIssue('a'), fakeIssue('b'), fakeIssue('c')] + const rows = buildLayout(issues, 'none', ROW_H) + expect(rows.map(r => r.y)).toEqual([0, ROW_H, 2 * ROW_H]) + }) +}) + +describe('filterVisibleRows', () => { + it('returns only rows whose Y range intersects the viewport (overscan=0)', () => { + const all: LayoutRow[] = [ + { kind: 'issue', y: 0, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, + { kind: 'issue', y: 100, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, + { kind: 'issue', y: 5000, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false } + ] + const visible = filterVisibleRows(all, 80, 60, 0) + expect(visible.map(r => r.y)).toEqual([100]) + }) + + it('default overscan brings adjacent rows into the visible set', () => { + const all: LayoutRow[] = [ + { kind: 'issue', y: 0, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, + { kind: 'issue', y: 100, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, + { kind: 'issue', y: 5000, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false } + ] + const visible = filterVisibleRows(all, 80, 60) + expect(visible.map(r => r.y).sort((a, b) => a - b)).toEqual([0, 100]) + }) + + it('honours an explicit overscan', () => { + const all: LayoutRow[] = [ + { kind: 'issue', y: 0, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, + { kind: 'issue', y: 1000, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false } + ] + const visible = filterVisibleRows(all, 950, 50, 200) + expect(visible.map(r => r.y)).toEqual([1000]) + }) +}) From 1c63a84d30d84d591a148568b0818105b042e9ca Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 013/248] feat(tracker-resources): implement Gantt layout lib Signed-off-by: Michael Uray --- .../src/components/gantt/lib/layout.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/lib/layout.ts diff --git a/plugins/tracker-resources/src/components/gantt/lib/layout.ts b/plugins/tracker-resources/src/components/gantt/lib/layout.ts new file mode 100644 index 00000000000..48fa27a84a7 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/lib/layout.ts @@ -0,0 +1,72 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// SPDX-License-Identifier: EPL-2.0 +// + +import { type Issue } from '@hcengineering/tracker' +import { type LayoutRow } from './types' + +export type GroupBy = 'none' | 'component' | 'milestone' + +const DEFAULT_OVERSCAN_PX = 240 + +/** + * Flatten the issues array into rows ordered by (parent → child) DFS, + * computing y-coords from rowHeight. + */ +export function buildLayout (issues: Issue[], _group: GroupBy, rowHeight: number): LayoutRow[] { + const childrenOf = new Map() + const roots: Issue[] = [] + for (const i of issues) { + const parentId = i.parents?.[0]?.parentId + if (parentId !== undefined && parentId !== null) { + const list = childrenOf.get(parentId as unknown as string) ?? [] + list.push(i) + childrenOf.set(parentId as unknown as string, list) + } else { + roots.push(i) + } + } + + const rows: LayoutRow[] = [] + let y = 0 + + function emit (i: Issue, depth: number): void { + const kids = childrenOf.get(i._id as unknown as string) ?? [] + rows.push({ + kind: 'issue', + y, + height: rowHeight, + depth, + visible: true, + issue: i, + component: null, + isSummary: kids.length > 0 + }) + y += rowHeight + for (const c of kids) { + emit(c, depth + 1) + } + } + + for (const r of roots) { + emit(r, 0) + } + + return rows +} + +/** + * Return the subset of rows whose [y, y+height] intersects + * [viewportTop - overscan, viewportTop + viewportHeight + overscan]. + */ +export function filterVisibleRows ( + rows: LayoutRow[], + viewportTop: number, + viewportHeight: number, + overscan: number = DEFAULT_OVERSCAN_PX +): LayoutRow[] { + const min = viewportTop - overscan + const max = viewportTop + viewportHeight + overscan + return rows.filter(r => r.y + r.height >= min && r.y <= max) +} From bd9a07c2e0730227bde976ff1e4b9d3108c67fdd Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 014/248] feat(tracker-resources): add GanttHeader.svelte Signed-off-by: Michael Uray --- .../src/components/gantt/GanttHeader.svelte | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttHeader.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte b/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte new file mode 100644 index 00000000000..eb4d4245ff6 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte @@ -0,0 +1,63 @@ + + + + + {#each ticks as tick (tick.date)} + {@const x = timeScale.toX(tick.date)} + + + {tick.label} + + {/each} + + + From dcfd75015b9302de7c1df67707e15cde673febde Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 015/248] feat(tracker-resources): add GanttBar.svelte (regular + summary claws) Signed-off-by: Michael Uray --- .../src/components/gantt/GanttBar.svelte | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttBar.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte new file mode 100644 index 00000000000..20f23a11029 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte @@ -0,0 +1,73 @@ + + + +{#if visible} + {@const barY = row.y + 6} + {@const barH = row.height - 12} + {#if isSummary} + + + + + {:else} + + {tooltipText} + {/if} +{/if} + + From e48b9409e7b234ff2e269efe2723ac0b99964efc Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 016/248] feat(tracker-resources): add GanttTodayMarker.svelte Signed-off-by: Michael Uray --- .../components/gantt/GanttTodayMarker.svelte | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte b/plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte new file mode 100644 index 00000000000..8a88f6789f7 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte @@ -0,0 +1,34 @@ + + + +{#if visible} + +{/if} + + From 291e5241425be877321c6d1c42917ea51c0e443b Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 017/248] feat(tracker-resources): add GanttMilestoneFlag.svelte Signed-off-by: Michael Uray --- .../gantt/GanttMilestoneFlag.svelte | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte b/plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte new file mode 100644 index 00000000000..1e5539fbd56 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte @@ -0,0 +1,53 @@ + + + +{#if visible} + + + {milestone.label} + + + {milestone.label} + +{/if} + + From 51c81c79a125d713d27386b0b42b9a79c3c2b0ea Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 018/248] feat(tracker-resources): add GanttCanvas.svelte Signed-off-by: Michael Uray --- .../src/components/gantt/GanttCanvas.svelte | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte new file mode 100644 index 00000000000..12f0289b591 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte @@ -0,0 +1,75 @@ + + + + + + {#each visibleRows as row (rowKey(row))} + {#if row.issue !== null} + + {/if} + {/each} + + + + {#each milestones as ms (ms._id)} + + {/each} + + + + + + From c74ea95fb389058d700157b2aee8bcfef047985d Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 019/248] feat(tracker-resources): add GanttSidebar.svelte Signed-off-by: Michael Uray --- .../src/components/gantt/GanttSidebar.svelte | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte new file mode 100644 index 00000000000..8644b47e1df --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte @@ -0,0 +1,57 @@ + + + +
+ +
+ + From e5f4e0d58b3870f69c853d8f8e5601f99a247731 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:14 +0000 Subject: [PATCH 020/248] feat(tracker-resources): add GanttToolbar.svelte (zoom buttons) Signed-off-by: Michael Uray --- .../src/components/gantt/GanttToolbar.svelte | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte b/plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte new file mode 100644 index 00000000000..8589cdd6af5 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte @@ -0,0 +1,46 @@ + + + +
+ {#each order as z} +
+ + From a91cf345f0aeff85bd78213db628d1761d02e1eb Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 021/248] feat(tracker-resources): GanttView wires Sidebar+Canvas+Toolbar with reactive queries PR 2 read-only Gantt is now end-to-end functional: - Two reactive queries (issues, milestones) - buildLayout(issues) -> rows with depth + isSummary - createTimeScale(zoom, dateRange.from) -> date<->px math - Summary ranges computed per parent issue from children's date span - Local zoom state (Day/Week/Month/Quarter) - no ViewletPreference plumbing - Horizontal scroll via canvas-scroller; vertical scroll moves Sidebar via translate Signed-off-by: Michael Uray --- .../src/components/gantt/GanttView.svelte | 202 ++++++++++++++++-- 1 file changed, 183 insertions(+), 19 deletions(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index 5a951fd57ab..a682fd03fff 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -1,41 +1,205 @@ -
-
-
+
+ + {#if loading} + + {:else} +
+ +
+
+ + +
+
+
+ {/if}
From d0f67442f59e2283f47b8528c69566348b253503 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 022/248] - layout.ts: orphan child issues (parent filtered out) now emit as roots - viewlets.ts: register IssueGantt AFTER IssueKanban so List stays default - viewlets.ts: drop showColorsViewOption (canvas does not honour it yet) - GanttBar.svelte: normalise reversed startDate>dueDate ranges UX: - GanttSidebar adds Title column with sticky two-column header - Sticky time-scale header decoupled from milestone strip (layout fix) - Per-row jump-to-bar arrow (Plane-style) when bar is offscreen - ResizeObserver initialises viewport on mount and on resize Milestones as Gantt rows + collapse: - Milestones group their issues as nested children (depth+1) - Collapsible toggle per parent row, local collapsedIds Set - Milestone summary bar uses existing GanttBar with aggregated range Icon: - Register tracker.icon.Gantt to #timeline svg (Gantt pictogram) Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray --- models/tracker/src/viewlets.ts | 42 ++-- plugins/tracker-assets/src/index.ts | 3 +- .../src/components/gantt/GanttBar.svelte | 16 +- .../src/components/gantt/GanttCanvas.svelte | 31 ++- .../src/components/gantt/GanttSidebar.svelte | 205 ++++++++++++++++-- .../src/components/gantt/GanttView.svelte | 116 ++++++++-- .../gantt/lib/__tests__/layout.test.ts | 138 ++++++++++-- .../src/components/gantt/lib/layout.ts | 120 ++++++++-- .../src/components/gantt/lib/types.ts | 20 +- plugins/tracker/src/index.ts | 1 + 10 files changed, 586 insertions(+), 106 deletions(-) diff --git a/models/tracker/src/viewlets.ts b/models/tracker/src/viewlets.ts index 194a5a7554d..ce6e058e535 100644 --- a/models/tracker/src/viewlets.ts +++ b/models/tracker/src/viewlets.ts @@ -213,10 +213,10 @@ export function issueConfig ( } export function ganttViewOptions (): ViewOptionsModel { - // PR 2 ships a minimal read-only Gantt. Group-by / dropdown ViewOptions - // are intentionally NOT advertised here — the canvas does not honour them - // yet. They will be added in PR 3 alongside drag/edit + Component swimlanes - // so users never see options that have no effect. + // PR 2 ships a minimal read-only Gantt. Group-by, "Show colors" and other + // dropdown ViewOptions are intentionally NOT advertised — the canvas does + // not honour them yet. They will be added in PR 3 alongside drag/edit + + // Component swimlanes so users never see options that have no effect. return { groupBy: [], orderBy: [ @@ -224,7 +224,7 @@ export function ganttViewOptions (): ViewOptionsModel { ['rank', SortingOrder.Ascending], ['dueDate', SortingOrder.Ascending] ], - other: [showColorsViewOption] + other: [] } } @@ -253,25 +253,12 @@ export function defineViewlets (builder: Builder): void { core.space.Model, { label: tracker.string.Gantt, - icon: tracker.icon.Issues, + icon: tracker.icon.Gantt, component: tracker.component.GanttView }, tracker.viewlet.Gantt ) - builder.createDoc( - view.class.Viewlet, - core.space.Model, - { - attachTo: tracker.class.Issue, - descriptor: tracker.viewlet.Gantt, - viewOptions: ganttViewOptions(), - configOptions: { strict: true, hiddenKeys: ['title'] }, - config: ganttConfig() - }, - tracker.viewlet.IssueGantt - ) - builder.createDoc( view.class.Viewlet, core.space.Model, @@ -549,6 +536,23 @@ export function defineViewlets (builder: Builder): void { tracker.viewlet.IssueKanban ) + // Gantt is registered AFTER List + Kanban so List remains the default + // viewlet (ViewletSelector falls back to viewlets[0] when no preference + // is saved). Putting Gantt last avoids surprising users with an empty + // canvas on first visit. + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: tracker.class.Issue, + descriptor: tracker.viewlet.Gantt, + viewOptions: ganttViewOptions(), + configOptions: { strict: true, hiddenKeys: ['title'] }, + config: ganttConfig() + }, + tracker.viewlet.IssueGantt + ) + const componentListViewOptions: ViewOptionsModel = { groupBy: ['lead', 'createdBy', 'modifiedBy'], orderBy: [ diff --git a/plugins/tracker-assets/src/index.ts b/plugins/tracker-assets/src/index.ts index 07aaf1badab..4c64fa48f9e 100644 --- a/plugins/tracker-assets/src/index.ts +++ b/plugins/tracker-assets/src/index.ts @@ -64,5 +64,6 @@ loadMetadata(tracker.icon, { CopyBranch: `${icons}#copyBranch`, Duplicate: `${icons}#duplicate`, TimeReport: `${icons}#timeReport`, - Estimation: `${icons}#estimation` + Estimation: `${icons}#estimation`, + Gantt: `${icons}#timeline` }) diff --git a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte index 20f23a11029..f179d163c18 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte @@ -3,10 +3,11 @@ // SPDX-License-Identifier: EPL-2.0 -->
+
@@ -37,21 +121,110 @@ overflow: hidden; background: var(--theme-comp-header-color); } + .sidebar-header { + display: flex; + align-items: center; + gap: 8px; + padding: 0 8px; + border-bottom: 1px solid var(--theme-divider-color); + background: var(--theme-comp-header-color); + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + color: var(--theme-darker-color); + letter-spacing: 0.05em; + box-sizing: border-box; + } + .col-toggle { + flex: 0 0 18px; + display: flex; + align-items: center; + justify-content: center; + } + .col-id { + flex: 0 0 88px; + } + .col-title { + flex: 1 1 auto; + } + .col-jump { + flex: 0 0 28px; + } .sidebar-rows { will-change: transform; } .sidebar-row { display: flex; align-items: center; + gap: 8px; padding-right: 8px; border-bottom: 1px solid var(--theme-divider-color); color: var(--theme-content-color); font-size: 13px; overflow: hidden; - text-overflow: ellipsis; white-space: nowrap; + box-sizing: border-box; + } + .cell-id { + flex: 0 0 88px; + overflow: hidden; + text-overflow: ellipsis; + } + .cell-id.ms-icon { + text-align: center; + color: var(--theme-state-info-color, #6366f1); + font-size: 14px; + } + .cell-title { + flex: 1 1 auto; + overflow: hidden; + text-overflow: ellipsis; + } + .cell-jump { + flex: 0 0 28px; + display: flex; + align-items: center; + justify-content: center; + } + .toggle-btn { + width: 18px; + height: 18px; + padding: 0; + border: none; + background: transparent; + color: var(--theme-darker-color); + font-size: 10px; + line-height: 1; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + } + .toggle-btn:hover { + color: var(--theme-content-color); + } + .jump-btn { + width: 22px; + height: 22px; + padding: 0; + border: 1px solid var(--theme-button-border); + border-radius: 3px; + background: var(--theme-button-default); + color: var(--theme-content-color); + font-size: 13px; + line-height: 1; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + } + .jump-btn:hover { + filter: brightness(1.1); } .sidebar-row.summary { font-weight: 600; } + .sidebar-row.milestone { + background: color-mix(in srgb, var(--theme-state-info-color, #6366f1) 6%, transparent); + } diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index a682fd03fff..de1a0690842 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -8,6 +8,7 @@ import { type Issue, type Milestone } from '@hcengineering/tracker' import { Loading } from '@hcengineering/ui' import { type Viewlet, type ViewOptions } from '@hcengineering/view' + import { onDestroy, onMount } from 'svelte' import tracker from '../../plugin' import GanttCanvas from './GanttCanvas.svelte' import GanttHeader from './GanttHeader.svelte' @@ -24,8 +25,9 @@ export let viewOptions: ViewOptions const ROW_HEIGHT = 28 - const SIDEBAR_WIDTH = 320 - const HEADER_HEIGHT = 32 + const SIDEBAR_WIDTH = 360 + const HEADER_HEIGHT = 56 + const MILESTONE_STRIP_HEIGHT = 22 let issues: Issue[] = [] let milestones: Milestone[] = [] @@ -100,9 +102,26 @@ } $: timeScale = createTimeScale(zoom, dateRange.from) - $: rows = buildLayout(issues, 'none', ROW_HEIGHT) + $: milestoneMarkers = milestones.map(m => ({ + _id: m._id, + label: m.label, + startDate: (m as Milestone & { startDate: number | null }).startDate ?? null, + targetDate: m.targetDate + })) + + // Set of row ids currently collapsed (children hidden). Local state in PR 2. + let collapsedIds: Set = new Set() + function onToggle (e: CustomEvent<{ id: string }>): void { + const next = new Set(collapsedIds) + if (next.has(e.detail.id)) next.delete(e.detail.id) + else next.add(e.detail.id) + collapsedIds = next + } + + $: rows = buildLayout(issues, milestoneMarkers, 'none', { rowHeight: ROW_HEIGHT, collapsedIds }) - // Compute summary ranges for parent issues — aggregate of children's startDate/dueDate + // Compute summary ranges for parent issues + milestones — aggregate of children's + // startDate/dueDate. $: summaryRanges = computeSummaryRanges(rows, issues) function computeSummaryRanges ( @@ -111,6 +130,7 @@ ): Map { const result = new Map() const childrenOf = new Map() + const issuesByMilestone = new Map() for (const i of allIssues) { const p = i.parents?.[0]?.parentId if (p !== undefined && p !== null) { @@ -119,9 +139,27 @@ list.push(i) childrenOf.set(k, list) } + const ms = (i as unknown as { milestone?: string | null }).milestone + if (ms != null) { + const list = issuesByMilestone.get(ms) ?? [] + list.push(i) + issuesByMilestone.set(ms, list) + } } for (const row of layoutRows) { - if (!row.isSummary || row.issue === null) continue + if (!row.isSummary) continue + if (row.kind === 'milestone' && row.milestone !== null) { + const msId = row.milestone._id as unknown as string + const kids = issuesByMilestone.get(msId) ?? [] + const starts = kids.map(k => k.startDate).filter((v): v is number => v !== null && v !== undefined) + const dues = kids.map(k => k.dueDate).filter((v): v is number => v !== null && v !== undefined) + result.set(row.id, { + startDate: starts.length > 0 ? Math.min(...starts) : null, + dueDate: dues.length > 0 ? Math.max(...dues) : null + }) + continue + } + if (row.issue === null) continue const id = (row.issue as Issue)._id as unknown as string const kids = childrenOf.get(id) ?? [] const starts = kids.map(k => k.startDate).filter((v): v is number => v !== null && v !== undefined) @@ -134,13 +172,6 @@ return result } - $: milestoneMarkers = milestones.map(m => ({ - _id: m._id, - label: m.label, - startDate: (m as Milestone & { startDate: number | null }).startDate ?? null, - targetDate: m.targetDate - })) - $: totalCanvasWidth = Math.max(viewportWidth(), timeScale.toX(dateRange.to) - timeScale.toX(dateRange.from)) function viewportWidth (): number { @@ -155,6 +186,34 @@ viewportHeight = t.clientHeight } + function onJump (e: CustomEvent<{ x: number }>): void { + if (scrollerEl !== undefined) { + scrollerEl.scrollTo({ left: Math.max(0, e.detail.x - 80), behavior: 'smooth' }) + } + } + + // Initialise viewport dimensions after mount and on resize so jump-buttons / + // virtualisation start with correct values instead of waiting for the first + // user-driven scroll event. + let resizeObs: ResizeObserver | undefined + function syncViewport (): void { + if (scrollerEl === undefined) return + canvasViewportLeft = scrollerEl.scrollLeft + canvasViewportWidth = scrollerEl.clientWidth + scrollTop = scrollerEl.scrollTop + viewportHeight = scrollerEl.clientHeight + } + onMount(() => { + syncViewport() + if (typeof ResizeObserver !== 'undefined' && scrollerEl !== undefined) { + resizeObs = new ResizeObserver(() => syncViewport()) + resizeObs.observe(scrollerEl) + } + }) + onDestroy(() => { + resizeObs?.disconnect() + }) + $: viewport = { left: canvasViewportLeft, right: canvasViewportLeft + canvasViewportWidth } $: loading = loadingIssues || loadingMilestones @@ -166,10 +225,23 @@ {:else}
- +
-
- +
+
+ +
@@ -197,9 +270,22 @@ display: flex; flex: 1 1 auto; overflow: hidden; + min-height: 0; } .canvas-scroller { flex: 1 1 auto; overflow: auto; + min-width: 0; + min-height: 0; + } + .canvas-stack { + position: relative; + } + .header-sticky { + position: sticky; + top: 0; + z-index: 2; + background: var(--theme-comp-header-color); + border-bottom: 1px solid var(--theme-divider-color); } diff --git a/plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts b/plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts index a58bd4ad8b4..4e18eb4e931 100644 --- a/plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts +++ b/plugins/tracker-resources/src/components/gantt/lib/__tests__/layout.test.ts @@ -5,16 +5,16 @@ import type { Issue } from '@hcengineering/tracker' import { buildLayout, filterVisibleRows } from '../layout' -import { type LayoutRow } from '../types' +import { type LayoutRow, type MilestoneMarker } from '../types' -function fakeIssue (id: string, parentId?: string, hasChildren = false): Issue { +function fakeIssue (id: string, parentId?: string, hasChildren = false, milestone?: string): Issue { return { _id: id as any, _class: 'tracker:class:Issue' as any, title: `Issue ${id}`, space: 'project-1' as any, component: null, - milestone: null, + milestone: milestone ?? null, startDate: null, dueDate: null, parents: parentId !== undefined ? [{ parentId: parentId as any }] : [], @@ -30,12 +30,16 @@ function fakeIssue (id: string, parentId?: string, hasChildren = false): Issue { } as unknown as Issue } +function fakeMilestone (id: string, label = `MS ${id}`): MilestoneMarker { + return { _id: id as any, label, startDate: null, targetDate: 1_700_000_000_000 } +} + const ROW_H = 28 describe('buildLayout (no grouping)', () => { it('flattens a flat list of root issues', () => { const issues = [fakeIssue('a'), fakeIssue('b')] - const rows = buildLayout(issues, 'none', ROW_H) + const rows = buildLayout(issues, [], 'none', ROW_H) expect(rows).toHaveLength(2) expect(rows[0].issue?._id).toBe('a') expect(rows[1].issue?._id).toBe('b') @@ -47,14 +51,14 @@ describe('buildLayout (no grouping)', () => { it('places children below parent with depth+1', () => { const a = fakeIssue('a', undefined, true) const child = fakeIssue('a.1', 'a') - const rows = buildLayout([a, child], 'none', ROW_H) + const rows = buildLayout([a, child], [], 'none', ROW_H) expect(rows.find(r => r.issue?._id === 'a.1')?.depth).toBe(1) }) it('marks parent issues as summary rows', () => { const a = fakeIssue('a', undefined, true) const child = fakeIssue('a.1', 'a') - const rows = buildLayout([a, child], 'none', ROW_H) + const rows = buildLayout([a, child], [], 'none', ROW_H) const parentRow = rows.find(r => r.issue?._id === 'a')! expect(parentRow.isSummary).toBe(true) expect(rows.find(r => r.issue?._id === 'a.1')?.isSummary).toBe(false) @@ -62,37 +66,129 @@ describe('buildLayout (no grouping)', () => { it('row Y coordinates are sequential multiples of rowHeight', () => { const issues = [fakeIssue('a'), fakeIssue('b'), fakeIssue('c')] - const rows = buildLayout(issues, 'none', ROW_H) + const rows = buildLayout(issues, [], 'none', ROW_H) expect(rows.map(r => r.y)).toEqual([0, ROW_H, 2 * ROW_H]) }) + + it('emits orphan children as roots when their parent is not in the input set', () => { + const a = fakeIssue('a', 'p') + const b = fakeIssue('b', 'p') + const rows = buildLayout([a, b], [], 'none', ROW_H) + expect(rows.map(r => r.issue?._id)).toEqual(['a', 'b']) + expect(rows.every(r => r.depth === 0)).toBe(true) + }) + + it('keeps real parent/child nesting when parent IS in the input set', () => { + const parent = fakeIssue('p', undefined, true) + const childA = fakeIssue('a', 'p') + const childB = fakeIssue('b', 'p') + const rows = buildLayout([parent, childA, childB], [], 'none', ROW_H) + expect(rows.map(r => r.issue?._id)).toEqual(['p', 'a', 'b']) + expect(rows[0].depth).toBe(0) + expect(rows[1].depth).toBe(1) + expect(rows[2].depth).toBe(1) + }) +}) + +describe('buildLayout — milestones', () => { + it('emits milestone parent rows above their issues', () => { + const ms = fakeMilestone('m1') + const i1 = fakeIssue('a', undefined, false, 'm1') + const i2 = fakeIssue('b', undefined, false, 'm1') + const rows = buildLayout([i1, i2], [ms], 'none', ROW_H) + expect(rows.map(r => r.id)).toEqual(['milestone:m1', 'issue:a', 'issue:b']) + expect(rows[0].kind).toBe('milestone') + expect(rows[0].milestone?.label).toBe('MS m1') + expect(rows[1].depth).toBe(1) + expect(rows[2].depth).toBe(1) + }) + + it('places issues without a known milestone as top-level roots', () => { + const i1 = fakeIssue('a', undefined, false, 'unknown-ms') + const i2 = fakeIssue('b') + const rows = buildLayout([i1, i2], [], 'none', ROW_H) + expect(rows.map(r => r.id)).toEqual(['issue:a', 'issue:b']) + expect(rows.every(r => r.depth === 0)).toBe(true) + }) + + it('mixes milestone groups with bare issues (milestones first)', () => { + const ms = fakeMilestone('m1') + const inGroup = fakeIssue('a', undefined, false, 'm1') + const ungrouped = fakeIssue('b') + const rows = buildLayout([inGroup, ungrouped], [ms], 'none', ROW_H) + expect(rows.map(r => r.id)).toEqual(['milestone:m1', 'issue:a', 'issue:b']) + }) +}) + +describe('buildLayout — collapse', () => { + it('hides children of a collapsed milestone row', () => { + const ms = fakeMilestone('m1') + const i1 = fakeIssue('a', undefined, false, 'm1') + const i2 = fakeIssue('b', undefined, false, 'm1') + const rows = buildLayout([i1, i2], [ms], 'none', { + rowHeight: ROW_H, + collapsedIds: new Set(['milestone:m1']) + }) + expect(rows.map(r => r.id)).toEqual(['milestone:m1']) + expect(rows[0].collapsed).toBe(true) + }) + + it('hides sub-issues of a collapsed parent issue', () => { + const parent = fakeIssue('p', undefined, true) + const child = fakeIssue('a', 'p') + const rows = buildLayout([parent, child], [], 'none', { + rowHeight: ROW_H, + collapsedIds: new Set(['issue:p']) + }) + expect(rows.map(r => r.id)).toEqual(['issue:p']) + expect(rows[0].collapsed).toBe(true) + expect(rows[0].collapsible).toBe(true) + }) + + it('expanded parents render their children', () => { + const parent = fakeIssue('p', undefined, true) + const child = fakeIssue('a', 'p') + const rows = buildLayout([parent, child], [], 'none', { + rowHeight: ROW_H, + collapsedIds: new Set() + }) + expect(rows.map(r => r.id)).toEqual(['issue:p', 'issue:a']) + expect(rows[0].collapsed).toBe(false) + }) }) describe('filterVisibleRows', () => { + function row (y: number): LayoutRow { + return { + kind: 'issue', + id: `r-${y}`, + y, + height: ROW_H, + depth: 0, + visible: true, + issue: null, + milestone: null, + component: null, + isSummary: false, + collapsible: false, + collapsed: false + } + } + it('returns only rows whose Y range intersects the viewport (overscan=0)', () => { - const all: LayoutRow[] = [ - { kind: 'issue', y: 0, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, - { kind: 'issue', y: 100, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, - { kind: 'issue', y: 5000, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false } - ] + const all: LayoutRow[] = [row(0), row(100), row(5000)] const visible = filterVisibleRows(all, 80, 60, 0) expect(visible.map(r => r.y)).toEqual([100]) }) it('default overscan brings adjacent rows into the visible set', () => { - const all: LayoutRow[] = [ - { kind: 'issue', y: 0, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, - { kind: 'issue', y: 100, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, - { kind: 'issue', y: 5000, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false } - ] + const all: LayoutRow[] = [row(0), row(100), row(5000)] const visible = filterVisibleRows(all, 80, 60) expect(visible.map(r => r.y).sort((a, b) => a - b)).toEqual([0, 100]) }) it('honours an explicit overscan', () => { - const all: LayoutRow[] = [ - { kind: 'issue', y: 0, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false }, - { kind: 'issue', y: 1000, height: ROW_H, depth: 0, visible: true, issue: null, component: null, isSummary: false } - ] + const all: LayoutRow[] = [row(0), row(1000)] const visible = filterVisibleRows(all, 950, 50, 200) expect(visible.map(r => r.y)).toEqual([1000]) }) diff --git a/plugins/tracker-resources/src/components/gantt/lib/layout.ts b/plugins/tracker-resources/src/components/gantt/lib/layout.ts index 48fa27a84a7..dbf490dc7a7 100644 --- a/plugins/tracker-resources/src/components/gantt/lib/layout.ts +++ b/plugins/tracker-resources/src/components/gantt/lib/layout.ts @@ -4,55 +4,137 @@ // import { type Issue } from '@hcengineering/tracker' -import { type LayoutRow } from './types' +import { type LayoutRow, type MilestoneMarker } from './types' export type GroupBy = 'none' | 'component' | 'milestone' const DEFAULT_OVERSCAN_PX = 240 +/** Stable id used for keyed iteration AND collapse-state lookup. */ +export function rowId (row: LayoutRow): string { + return row.id +} + +export interface BuildLayoutOptions { + rowHeight: number + /** Set of row ids that are currently collapsed (children hidden). */ + collapsedIds?: Set +} + /** - * Flatten the issues array into rows ordered by (parent → child) DFS, - * computing y-coords from rowHeight. + * Build the flattened row layout. + * + * Hierarchy from top to bottom: + * Milestone-row (if any issues belong to that milestone) + * └── Issue-root + * └── Issue-child (sub-issue) + * + * Issues without a milestone are emitted as roots after the milestone groups. + * Issues whose parent is filtered out are promoted to roots so they don't + * silently disappear from the Gantt. */ -export function buildLayout (issues: Issue[], _group: GroupBy, rowHeight: number): LayoutRow[] { - const childrenOf = new Map() - const roots: Issue[] = [] +export function buildLayout ( + issues: Issue[], + milestones: MilestoneMarker[], + _group: GroupBy, + rowHeightOrOpts: number | BuildLayoutOptions +): LayoutRow[] { + const opts: BuildLayoutOptions = + typeof rowHeightOrOpts === 'number' ? { rowHeight: rowHeightOrOpts } : rowHeightOrOpts + const rowHeight = opts.rowHeight + const collapsedIds = opts.collapsedIds ?? new Set() + + // 1) Build issue parent/child map, dropping orphan parent refs. + const visibleIssueIds = new Set(issues.map(i => i._id as unknown as string)) + const issueChildrenOf = new Map() + const issueRoots: Issue[] = [] for (const i of issues) { - const parentId = i.parents?.[0]?.parentId - if (parentId !== undefined && parentId !== null) { - const list = childrenOf.get(parentId as unknown as string) ?? [] + const parentId = i.parents?.[0]?.parentId as unknown as string | undefined + if (parentId != null && visibleIssueIds.has(parentId)) { + const list = issueChildrenOf.get(parentId) ?? [] list.push(i) - childrenOf.set(parentId as unknown as string, list) + issueChildrenOf.set(parentId, list) + } else { + issueRoots.push(i) + } + } + + // 2) Group root-issues by milestone. + const milestoneById = new Map() + for (const m of milestones) { + milestoneById.set(m._id as unknown as string, m) + } + const issuesByMilestone = new Map() + const issuesWithoutMilestone: Issue[] = [] + for (const root of issueRoots) { + const ms = (root as unknown as { milestone?: string | null }).milestone + if (ms != null && milestoneById.has(ms)) { + const list = issuesByMilestone.get(ms) ?? [] + list.push(root) + issuesByMilestone.set(ms, list) } else { - roots.push(i) + issuesWithoutMilestone.push(root) } } const rows: LayoutRow[] = [] let y = 0 - function emit (i: Issue, depth: number): void { - const kids = childrenOf.get(i._id as unknown as string) ?? [] + function emitIssue (issue: Issue, depth: number): void { + const issueId = issue._id as unknown as string + const kids = issueChildrenOf.get(issueId) ?? [] + const id = `issue:${issueId}` + const collapsible = kids.length > 0 + const collapsed = collapsedIds.has(id) rows.push({ kind: 'issue', + id, y, height: rowHeight, depth, visible: true, - issue: i, + issue, + milestone: null, component: null, - isSummary: kids.length > 0 + isSummary: kids.length > 0, + collapsible, + collapsed }) y += rowHeight - for (const c of kids) { - emit(c, depth + 1) + if (!collapsed) { + for (const c of kids) emitIssue(c, depth + 1) } } - for (const r of roots) { - emit(r, 0) + // 3a) Milestone groups first. + for (const [msId, msIssues] of issuesByMilestone) { + const ms = milestoneById.get(msId) + if (ms === undefined) continue + const id = `milestone:${msId}` + const collapsed = collapsedIds.has(id) + rows.push({ + kind: 'milestone', + id, + y, + height: rowHeight, + depth: 0, + visible: true, + issue: null, + milestone: ms, + component: null, + isSummary: true, + collapsible: true, + collapsed + }) + y += rowHeight + if (!collapsed) { + for (const i of msIssues) emitIssue(i, 1) + } } + // 3b) Issues without milestone. + for (const r of issuesWithoutMilestone) emitIssue(r, 0) + return rows } diff --git a/plugins/tracker-resources/src/components/gantt/lib/types.ts b/plugins/tracker-resources/src/components/gantt/lib/types.ts index 485ecabf5ae..8c2b8e2a1a0 100644 --- a/plugins/tracker-resources/src/components/gantt/lib/types.ts +++ b/plugins/tracker-resources/src/components/gantt/lib/types.ts @@ -16,23 +16,31 @@ export interface Tick { level: 'major' | 'minor' // major ticks render thicker + with text label } -/** A row in the flattened layout. May be an issue or a component-swimlane header. */ +/** A row in the flattened layout. May be an issue, milestone, or swimlane header. */ export interface LayoutRow { - kind: 'issue' | 'component-swimlane' + kind: 'issue' | 'milestone' | 'component-swimlane' + /** Stable key for keyed each-blocks and the collapsed-set. */ + id: string /** Y-coord top-edge of the row in canvas pixels. */ y: number /** Row height in pixels. */ height: number - /** Tree depth — 0 for top-level issues. */ + /** Tree depth — 0 for top-level rows. */ depth: number /** Whether this row is currently rendered (vs virtually skipped). */ visible: boolean - /** The issue this row represents — null for swimlane headers. */ + /** The issue this row represents — null for milestone/swimlane rows. */ issue: Issue | null - /** The component this swimlane represents — null for issue rows. */ + /** The milestone this row represents — null for issue/swimlane rows. */ + milestone: MilestoneMarker | null + /** The component this swimlane represents — null otherwise. */ component: Ref | null - /** True iff this issue has children (renders as summary "claw" bar). */ + /** True iff this row has children (renders as summary "claw" bar). */ isSummary: boolean + /** True iff this row has children and the user can collapse/expand it. */ + collapsible: boolean + /** True iff currently collapsed (children hidden). */ + collapsed: boolean } /** Cached aggregate dates of a parent issue's children, for summary-bar rendering. */ diff --git a/plugins/tracker/src/index.ts b/plugins/tracker/src/index.ts index b762afefc79..ca53fb92050 100644 --- a/plugins/tracker/src/index.ts +++ b/plugins/tracker/src/index.ts @@ -500,6 +500,7 @@ const pluginState = plugin(trackerId, { TimeReport: '' as Asset, Estimation: '' as Asset, + Gantt: '' as Asset, // Project icons Home: '' as Asset, From fa3b39c9c1e4b75e06afc11d5f4ff35a9b21960b Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 023/248] =?UTF-8?q?Toolbar=20(replaces=20previous=20floati?= =?UTF-8?q?ng=20zoom):=20-=20Visible=20top=20row=20with=20[=C2=AB]=20[Toda?= =?UTF-8?q?y]=20[=C2=BB]=20|=20Day=20Week=20Month=20Quarter=20|=20?= =?UTF-8?q?=E2=9A=99=20-=20Today=20scrolls=20to=20current=20date,=20prev/n?= =?UTF-8?q?ext=20page-scroll=20by=2080%=20viewport=20-=20Settings=20popove?= =?UTF-8?q?r=20toggles=20Issue-Code=20and=20Title=20columns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layout: - Bigger row height (28→36) and bar font (11→13px) for readability - Sidebar reworked into flex-column with separate clipped rows region so scrolled rows can never paint over the sticky header(layout fix) - Drag handle between sidebar and canvas (120–600px range) - ResizeObserver re-syncs viewport on drag/resize/zoom changes Interaction: - Vertical gridlines aligned to time-scale ticks (Plane-style) - Row hover highlights both sidebar and canvas, with rich HTML tooltip (issue title + start + due + duration) - Title click in sidebar dispatches openIssue → showPanel(EditIssue) - Double-click on canvas bar dispatches openIssue - Wheel forwarding from sidebar to canvas-scroller (vertical scroll works while hovering issue list) - Pointerdown + drag on empty canvas pans both axes - Jump-to-bar buttons now use the bar's true left-edge target Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttBar.svelte | 33 +- .../src/components/gantt/GanttCanvas.svelte | 156 +++++-- .../src/components/gantt/GanttSidebar.svelte | 198 ++++++--- .../src/components/gantt/GanttView.svelte | 388 ++++++++++++++++-- 4 files changed, 643 insertions(+), 132 deletions(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte index f179d163c18..a54c310d91e 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte @@ -1,6 +1,5 @@ {#if visible} @@ -52,6 +57,15 @@ points="{x + w - 6},{barY + barH / 2 - 1} {x + w},{barY + barH / 2 - 1} {x + w - 3},{barY + barH / 2 + 5}" fill="var(--theme-content-color)" /> + {#if barLabel !== ''} + {barLabel} + {/if} + {tooltipText} {:else} + {#if barLabel !== ''} + {barLabel} + {/if} {tooltipText} {/if} {/if} @@ -76,4 +98,13 @@ .bar:hover { filter: brightness(1.1); } + .bar-label { + font-size: 13px; + user-select: none; + pointer-events: none; + dominant-baseline: alphabetic; + } + .summary-label { + font-weight: 600; + } diff --git a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte index 772b944a294..66dc7990b1a 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte @@ -1,15 +1,22 @@
- diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index de1a0690842..514014be8dc 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -1,6 +1,5 @@
- {#if loading} {:else} +
+
+ + + +
+
+ {#each ZOOM_LEVELS as z (z)} + + {/each} +
+
+ + {#if showSettings} +
+ + +
+ {/if} +
+
+
- + +
+ +
-
+ +
@@ -251,10 +417,44 @@ {viewportHeight} {viewport} milestoneStripHeight={MILESTONE_STRIP_HEIGHT} + {hoveredRowId} + on:openIssue={onIssueOpen} + on:hoverRow={onRowHover} />
+ {#if tooltipState.visible && tooltipState.row !== null} + {@const row = tooltipState.row} + {@const issue = row.issue} + {@const ms = row.milestone} +
+ {#if row.kind === 'milestone' && ms !== null} +
◆ Milestone
+
{ms.label}
+ {#if ms.startDate !== null} +
Start: {new Date(ms.startDate).toISOString().slice(0, 10)}
+ {/if} +
Target: {new Date(ms.targetDate).toISOString().slice(0, 10)}
+ {:else if issue !== null} +
Issue
+
{issue.title}
+ {#if issue.startDate !== null} +
Start: {new Date(issue.startDate).toISOString().slice(0, 10)}
+ {/if} + {#if issue.dueDate !== null} +
Due: {new Date(issue.dueDate).toISOString().slice(0, 10)}
+ {/if} + {#if issue.startDate !== null && issue.dueDate !== null} + {@const days = Math.round((Math.max(issue.dueDate, issue.startDate) - Math.min(issue.dueDate, issue.startDate)) / 86_400_000) + 1} +
Duration: {days} day{days === 1 ? '' : 's'}
+ {/if} + {/if} +
+ {/if} {/if}
@@ -266,21 +466,111 @@ width: 100%; overflow: hidden; } + .gantt-toolbar { + flex: 0 0 auto; + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + padding: 0 12px; + border-bottom: 1px solid var(--theme-divider-color); + background: var(--theme-comp-header-color); + } + .toolbar-left { display: flex; gap: 4px; } + .toolbar-center { display: flex; gap: 2px; justify-self: center; } + .toolbar-right { display: flex; gap: 4px; justify-self: end; position: relative; } + .nav-btn { + height: 26px; + padding: 0 10px; + border: 1px solid var(--theme-divider-color); + background: var(--theme-button-default); + color: var(--theme-content-color); + font-size: 12px; + border-radius: 4px; + cursor: pointer; + } + .nav-btn:hover { + background: var(--theme-button-hovered); + } + .today-btn { + font-weight: 600; + } + .zoom-btn { + height: 26px; + padding: 0 12px; + border: 1px solid var(--theme-divider-color); + background: var(--theme-button-default); + color: var(--theme-content-color); + font-size: 12px; + cursor: pointer; + } + .zoom-btn:first-child { border-radius: 4px 0 0 4px; } + .zoom-btn:last-child { border-radius: 0 4px 4px 0; } + .zoom-btn:not(:first-child) { border-left: none; } + .zoom-btn:hover { background: var(--theme-button-hovered); } + .zoom-btn.active { + background: var(--theme-button-pressed); + font-weight: 600; + } + .settings-btn { + width: 26px; + height: 26px; + padding: 0; + border: 1px solid var(--theme-divider-color); + background: var(--theme-button-default); + color: var(--theme-content-color); + font-size: 13px; + cursor: pointer; + border-radius: 4px; + } + .settings-btn:hover { background: var(--theme-button-hovered); } + .settings-popover { + position: absolute; + top: 32px; + right: 0; + background: var(--theme-popup-color, var(--theme-comp-header-color)); + border: 1px solid var(--theme-divider-color); + border-radius: 4px; + padding: 8px; + display: flex; + flex-direction: column; + gap: 6px; + font-size: 12px; + color: var(--theme-content-color); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + z-index: 5; + } + .settings-popover label { + display: flex; align-items: center; gap: 6px; cursor: pointer; white-space: nowrap; + } .gantt-body { display: flex; flex: 1 1 auto; overflow: hidden; min-height: 0; } + .sidebar-host { flex: 0 0 auto; min-height: 0; } + .resize-handle { + flex: 0 0 5px; + cursor: col-resize; + background: var(--theme-divider-color); + transition: background 80ms ease; + user-select: none; + touch-action: none; + } + .resize-handle:hover, .resize-handle.active { + background: var(--theme-state-info-color, #6366f1); + } .canvas-scroller { flex: 1 1 auto; overflow: auto; min-width: 0; min-height: 0; + cursor: grab; } - .canvas-stack { - position: relative; + .canvas-scroller.panning { + cursor: grabbing; } + .canvas-stack { position: relative; } .header-sticky { position: sticky; top: 0; @@ -288,4 +578,34 @@ background: var(--theme-comp-header-color); border-bottom: 1px solid var(--theme-divider-color); } + .hover-tooltip { + position: fixed; + z-index: 100; + background: var(--theme-popup-color, var(--theme-comp-header-color)); + border: 1px solid var(--theme-divider-color); + border-radius: 4px; + padding: 8px 10px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18); + pointer-events: none; + font-size: 12px; + color: var(--theme-content-color); + max-width: 320px; + } + .tt-head { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + color: var(--theme-darker-color); + letter-spacing: 0.05em; + margin-bottom: 2px; + } + .tt-title { + font-size: 13px; + font-weight: 600; + margin-bottom: 4px; + } + .tt-line { + font-size: 12px; + line-height: 1.5; + } From 83feba5f4102d34475fcc66e2450cb3e2b9a4aef Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 024/248] - SVG horizontal-scroll bug: GanttHeader and GanttCanvas now render at totalCanvasWidth (was viewport width), with viewBox in scroll-content coordinates. The sticky header lives in the same coordinate system as the canvas-stack so horizontal scroll no longer clips the time-axis. - showPanel uses tracker.component.EditIssue instead of the hardcoded string + 'as any' cast. - Removed unused GanttToolbar.svelte and GanttMilestoneFlag.svelte (dead files since toolbar moved into GanttView and milestone flags became rows). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Toolbar: - Time-navigation cluster: ⏮ « Today » ⏭ + native date picker that jumps to a specific date. Replaces the lone Today button. Interaction polish: - Sidebar wheel forwarding now uses direct scrollTop/scrollLeft mutation with deltaMode scaling — same speed as native canvas scroll. - TodayMarker no longer hides when scrolled offscreen (SVG width handles clipping); milestone reference lines render unconditionally for the same reason. Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttCanvas.svelte | 44 +++++++-------- .../src/components/gantt/GanttHeader.svelte | 21 ++++---- .../gantt/GanttMilestoneFlag.svelte | 53 ------------------- .../components/gantt/GanttTodayMarker.svelte | 8 ++- .../src/components/gantt/GanttToolbar.svelte | 46 ---------------- .../src/components/gantt/GanttView.svelte | 52 ++++++++++++++++-- 6 files changed, 88 insertions(+), 136 deletions(-) delete mode 100644 plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte delete mode 100644 plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte index 66dc7990b1a..500c41a8d9c 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte @@ -24,13 +24,17 @@ export let scrollTop: number = 0 export let viewportHeight: number = 600 export let viewport: { left: number; right: number } + export let totalWidth: number export let milestoneStripHeight: number = 0 export let hoveredRowId: string | null = null $: visibleRows = filterVisibleRows(rows, scrollTop, viewportHeight) $: rowsHeight = rows.length > 0 ? rows[rows.length - 1].y + rows[rows.length - 1].height : 0 $: totalHeight = rowsHeight + milestoneStripHeight - $: ticks = timeScale.ticks([timeScale.fromX(viewport.left), timeScale.fromX(viewport.right)]) + $: ticks = timeScale.ticks([ + timeScale.fromX(Math.max(0, viewport.left - 100)), + timeScale.fromX(viewport.right + 100) + ]) function rowKey (row: LayoutRow): string { return row.id @@ -48,9 +52,9 @@ @@ -75,9 +79,9 @@ {#each visibleRows as row (rowKey(row))} {@const isHover = hoveredRowId === row.id} @@ -140,20 +144,18 @@ {#each milestones as ms (ms._id)} {@const x = timeScale.toX(ms.targetDate)} - {#if x >= viewport.left - 16 && x <= viewport.right + 16} - - {ms.label} - - {/if} + + {ms.label} + {/each} diff --git a/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte b/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte index eb4d4245ff6..dc52767c335 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte @@ -1,27 +1,30 @@ {#each ticks as tick (tick.date)} @@ -36,7 +39,7 @@ /> @@ -53,7 +56,7 @@ } .tick-label { font-family: var(--mono-font, monospace); - font-size: 11px; + font-size: 12px; user-select: none; pointer-events: none; } diff --git a/plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte b/plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte deleted file mode 100644 index 1e5539fbd56..00000000000 --- a/plugins/tracker-resources/src/components/gantt/GanttMilestoneFlag.svelte +++ /dev/null @@ -1,53 +0,0 @@ - - - -{#if visible} - - - {milestone.label} - - - {milestone.label} - -{/if} - - diff --git a/plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte b/plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte index 8a88f6789f7..ab1bac75106 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttTodayMarker.svelte @@ -7,11 +7,15 @@ export let timeScale: TimeScale export let canvasHeight: number - export let viewport: { left: number; right: number } + // Viewport accepted for API compatibility but no longer needed for visibility + // (the SVG itself spans the whole canvas, scrollbar clips off-viewport). + // eslint-disable-next-line @typescript-eslint/no-unused-vars + export let viewport: { left: number; right: number } = { left: 0, right: 0 } + $: void viewport $: today = Date.now() $: x = timeScale.toX(today) - $: visible = x >= viewport.left && x <= viewport.right + $: visible = true {#if visible} diff --git a/plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte b/plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte deleted file mode 100644 index 8589cdd6af5..00000000000 --- a/plugins/tracker-resources/src/components/gantt/GanttToolbar.svelte +++ /dev/null @@ -1,46 +0,0 @@ - - - -
- {#each order as z} -
- - diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index 514014be8dc..f48f2b05343 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -217,7 +217,7 @@ function onIssueOpen (e: CustomEvent<{ issue: { _id: string, _class: string } }>): void { showPanel( - 'tracker:component:EditIssue' as any, + tracker.component.EditIssue, e.detail.issue._id as Ref, e.detail.issue._class as Ref>, 'content' @@ -233,14 +233,34 @@ if (scrollerEl === undefined) return scrollerEl.scrollBy({ left: dir * canvasViewportWidth * 0.8, behavior: 'smooth' }) } + function jumpToStart (): void { + if (scrollerEl === undefined) return + scrollerEl.scrollTo({ left: 0, behavior: 'smooth' }) + } + function jumpToEnd (): void { + if (scrollerEl === undefined) return + scrollerEl.scrollTo({ left: scrollerEl.scrollWidth, behavior: 'smooth' }) + } + function jumpToDate (iso: string): void { + if (scrollerEl === undefined || iso === '') return + const t = Date.parse(iso) + if (isNaN(t)) return + const x = timeScale.toX(t) + scrollerEl.scrollTo({ left: Math.max(0, x - canvasViewportWidth / 2), behavior: 'smooth' }) + } + let datePickerValue: string = '' // Forward wheel events from the sidebar to the canvas-scroller so users can - // scroll vertically while hovering the issue list (the sidebar itself uses a - // transform-based layout and has no native scrollbar). + // scroll vertically while hovering the issue list. Direct scrollTop/scrollLeft + // assignment matches the browser's native wheel-to-scroll speed; smooth + // scrolling would feel laggy against the canvas. function forwardSidebarWheel (e: WheelEvent): void { if (scrollerEl === undefined) return e.preventDefault() - scrollerEl.scrollBy({ top: e.deltaY, left: e.deltaX, behavior: 'auto' }) + // deltaMode 1 = lines, 2 = pages; scale to pixels. + const factor = e.deltaMode === 1 ? 16 : (e.deltaMode === 2 ? scrollerEl.clientHeight : 1) + scrollerEl.scrollTop += e.deltaY * factor + scrollerEl.scrollLeft += e.deltaX * factor } // Click-and-drag panning across empty canvas area. @@ -324,9 +344,18 @@ {:else}
+ + + jumpToDate(datePickerValue)} + />
{#each ZOOM_LEVELS as z (z)} @@ -406,7 +435,7 @@ >
- +
Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 025/248] feat(tracker-resources): wire Gantt sidebar toggles into Customize-View - Register two ToggleViewOptions on the Gantt viewlet: ganttShowIssueCode (default OFF) and ganttShowTitle (default ON). These show up in the standard Customize-View dropdown. - Drop the per-component settings popover from GanttView; sidebar column visibility now reads viewOptions directly. - Hover tooltip always surfaces the issue code (e.g. OSTRO-31), even when the issue-code column is hidden. - IntlStrings + en/de/es/fr/it/ja/pt/ru/zh/cs translations added. Signed-off-by: Michael Uray --- models/tracker/src/viewlets.ts | 24 +- plugins/tracker-assets/lang/cs.json | 573 +++++++++-------- plugins/tracker-assets/lang/de.json | 591 +++++++++--------- plugins/tracker-assets/lang/en.json | 4 +- plugins/tracker-assets/lang/es.json | 574 ++++++++--------- plugins/tracker-assets/lang/fr.json | 574 ++++++++--------- plugins/tracker-assets/lang/it.json | 574 ++++++++--------- plugins/tracker-assets/lang/ja.json | 574 ++++++++--------- plugins/tracker-assets/lang/pt.json | 574 ++++++++--------- plugins/tracker-assets/lang/ru.json | 591 +++++++++--------- plugins/tracker-assets/lang/zh.json | 591 +++++++++--------- .../src/components/gantt/GanttView.svelte | 37 +- plugins/tracker-resources/src/plugin.ts | 4 +- 13 files changed, 2622 insertions(+), 2663 deletions(-) diff --git a/models/tracker/src/viewlets.ts b/models/tracker/src/viewlets.ts index ce6e058e535..45a9d0406e8 100644 --- a/models/tracker/src/viewlets.ts +++ b/models/tracker/src/viewlets.ts @@ -213,10 +213,9 @@ export function issueConfig ( } export function ganttViewOptions (): ViewOptionsModel { - // PR 2 ships a minimal read-only Gantt. Group-by, "Show colors" and other - // dropdown ViewOptions are intentionally NOT advertised — the canvas does - // not honour them yet. They will be added in PR 3 alongside drag/edit + - // Component swimlanes so users never see options that have no effect. + // PR 2 ships a minimal read-only Gantt. Group-by + Show-colors are + // intentionally NOT advertised — the canvas does not honour them yet. + // The two sidebar-column toggles below ARE wired up to GanttSidebar. return { groupBy: [], orderBy: [ @@ -224,7 +223,22 @@ export function ganttViewOptions (): ViewOptionsModel { ['rank', SortingOrder.Ascending], ['dueDate', SortingOrder.Ascending] ], - other: [] + other: [ + { + key: 'ganttShowIssueCode', + type: 'toggle', + defaultValue: false, + actionTarget: 'display', + label: tracker.string.GanttShowIssueCode + }, + { + key: 'ganttShowTitle', + type: 'toggle', + defaultValue: true, + actionTarget: 'display', + label: tracker.string.GanttShowTitle + } + ] } } diff --git a/plugins/tracker-assets/lang/cs.json b/plugins/tracker-assets/lang/cs.json index 9f87e86aff4..03e0177a161 100644 --- a/plugins/tracker-assets/lang/cs.json +++ b/plugins/tracker-assets/lang/cs.json @@ -1,294 +1,281 @@ { - "string": { - "TrackerApplication": "Přehled", - "Projects": "Vaše projekty", - "More": "Více", - "Default": "Výchozí", - "MakeDefault": "Nastavit jako výchozí", - "Delete": "Smazat", - "Open": "Otevřít", - "Members": "Členové", - "Inbox": "Doručená pošta", - "MyIssues": "Mé úkoly", - "ViewIssue": "Zobrazit úkol", - "IssueCreated": "Úkol vytvořen", - "Issues": "Úkoly", - "Views": "Zobrazení", - "Active": "Aktivní", - "AllIssues": "Všechny úkoly", - "ActiveIssues": "Aktivní úkoly", - "BacklogIssues": "Návrhy", - "Backlog": "Návrhy", - "Board": "Tabule", - "Components": "Komponenty", - "AllComponents": "Vše", - "BacklogComponents": "Návrhy", - "ActiveComponents": "Aktivní", - "ClosedComponents": "Uzavřené", - "NewComponent": "Nová komponenta", - "CreateComponent": "Vytvořit komponentu", - "ComponentNamePlaceholder": "Název komponenty", - "ComponentDescriptionPlaceholder": "Popis (volitelné)", - "ComponentLead": "Vedoucí", - "ComponentMembers": "Členové", - "StartDate": "Datum zahájení", - "TargetDate": "Cílové datum", - "Planned": "Plánováno", - "InProgress": "Probíhá", - "Paused": "Pozastaveno", - "Completed": "Dokončeno", - "Canceled": "Zrušeno", - "CreateProject": "Vytvořit projekt", - "NewProject": "Nový projekt", - "ProjectTitle": "Název projektu", - "ProjectTitlePlaceholder": "Nový projekt", - "UsedInIssueIDs": "Použito v ID úkolů", - "Identifier": "Identifikátor", - "Import": "Importovat", - "ProjectIdentifier": "Identifikátor projektu", - "IdentifierExists": "Identifikátor projektu již existuje", - "ProjectIdentifierPlaceholder": "PROJ", - "ChooseIcon": "Vybrat ikonu", - "AddIssue": "Přidat úkol", - "NewIssue": "Nový úkol", - "NewIssuePlaceholder": "Nový", - "ResumeDraft": "Pokračovat v konceptu", - "SaveIssue": "Vytvořit úkol", - "SetPriority": "Nastavit prioritu…", - "SetStatus": "Nastavit stav…", - "SelectIssue": "Vybrat úkol", - "Priority": "Priorita", - "NoPriority": "Bez priority", - "Urgent": "Naléhavé", - "High": "Vysoká", - "Medium": "Střední", - "Low": "Nízká", - "Unassigned": "Nepřiřazeno", - "Back": "Zpět", - "List": "Seznam", - "NumberLabels": "{count, plural, =0 {žádné štítky} =1 {1 štítek} other {# štítků}}", - "CategoryBacklog": "Návrhy", - "CategoryUnstarted": "Nezahájeno", - "CategoryStarted": "Zahájeno", - "CategoryCompleted": "Dokončeno", - "CategoryCanceled": "Zrušeno", - "Title": "Název", - "Name": "Jméno", - "Description": "Popis", - "Status": "Stav", - "Number": "Číslo", - "Assignee": "Přiřazený", - "AssignTo": "Přiřadit…", - "AssignedTo": "Přiřazeno k {value}", - "Parent": "Nadřazený úkol", - "SetParent": "Nastavit nadřazený úkol…", - "ChangeParent": "Změnit nadřazený úkol…", - "RemoveParent": "Odebrat nadřazený úkol", - "OpenParent": "Otevřít nadřazený úkol", - "SubIssues": "Podúkoly", - "SubIssuesList": "Podúkoly ({subIssues})", - "OpenSubIssues": "Otevřít podúkoly", - "AddSubIssues": "Přidat podúkol", - "BlockedBy": "Blokováno", - "RelatedTo": "Související s", - "Comments": "Komentáře", - "Attachments": "Přílohy", - "Labels": "Štítky", - "Component": "Komponenta", - "SetDueDate": "Nastavit datum splnění…", - "ChangeDueDate": "Změnit datum splnění…", - "ModificationDate": "Aktualizováno {value}", - "Project": "Projekt", - "Issue": "Úkol", - "SubIssue": "Podúkol", - "Rank": "Hodnost", - "TypeIssuePriority": "Priorita úkolu", - "IssueTitlePlaceholder": "Název úkolu", - "SubIssueTitlePlaceholder": "Název podúkolu", - "IssueDescriptionPlaceholder": "Přidat popis…", - "SubIssueDescriptionPlaceholder": "Přidat popis podúkolu", - "AddIssueTooltip": "Přidat úkol…", - "Grouping": "Seskupení", - "Ordering": "Řazení", - "CompletedIssues": "Dokončené úkoly", - "NoGrouping": "Bez seskupení", - "NoAssignee": "Bez přiřazení", - "LastUpdated": "Poslední aktualizace", - "DueDate": "Datum splnění", - "IssueStartDate": "Datum zahájení", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "Manuální", - "All": "Vše", - "PastWeek": "Minulý týden", - "PastMonth": "Minulý měsíc", - "CopyIssueUrl": "Zkopírovat URL úkolu do schránky", - "CopyIssueId": "Zkopírovat ID úkolu do schránky", - "CopyIssueBranch": "Zkopírovat název větve Git do schránky", - "CopyIssueTitle": "Zkopírovat název úkolu do schránky", - "AssetLabel": "Mějte přehled", - "AddToComponent": "Přidat do komponenty…", - "MoveToComponent": "Přesunout do komponenty…", - "NoComponent": "Bez komponenty", - "ComponentLeadTitle": "Vedoucí komponenty", - "ComponentMembersTitle": "Členové komponenty", - "ComponentLeadSearchPlaceholder": "Nastavit vedoucího komponenty…", - "ComponentMembersSearchPlaceholder": "Změnit členy komponenty…", - "MoveToProject": "Přesunout do projektu", - "Duplicate": "Duplikovat", - - "GotoIssues": "Přejít na úkoly", - "GotoActive": "Přejít na aktivní úkoly", - "GotoBacklog": "Přejít na návrhy", - "GotoComponents": "Přejít na komponenty", - "GotoMyIssues": "Přejít na mé úkoly", - "GotoTrackerApplication": "Přepnout na aplikaci Přehled", - - "CreatedOne": "Vytvořeno", - "MoveIssues": "Přesunout úkoly", - "MoveIssuesDescription": "Vyberte projekt, do kterého chcete úkoly přesunout", - "ManageAttributes": "Spravovat atributy", - "KeepOriginalAttributes": "Zachovat původní atributy", - "KeepOriginalAttributesTooltip": "Původní stavy a komponenty úkolů budou zachovány v novém projektu", - "SelectReplacement": "Následující položky nejsou dostupné v novém projektu. Vyberte náhradu.", - "MissingItem": "CHYBĚJÍCÍ POLOŽKA", - "Replacement": "NÁHRADA", - "Original": "PŮVODNÍ", - "OriginalDescription": "Položky z této sekce budou vytvořeny v novém projektu", - - "Relations": "Vztahy", - "RemoveRelation": "Odebrat vztah...", - "AddBlockedBy": "Označit jako blokováno...", - "AddIsBlocking": "Označit jako blokující...", - "AddRelatedIssue": "Odkázat na jiný úkol...", - "RelatedIssue": "Související úkol {id} - {title}", - "BlockedIssue": "Blokovaný úkol {id} - {title}", - "BlockingIssue": "Blokující úkol {id} - {title}", - "BlockedBySearchPlaceholder": "Vyhledat úkol pro označení jako blokováno...", - "IsBlockingSearchPlaceholder": "Vyhledat úkol pro označení jako blokující...", - "RelatedIssueSearchPlaceholder": "Vyhledat úkol pro odkazování...", - "Blocks": "Blokuje", - "Related": "Související", - "RelatedIssues": "Související úkoly", - - "EditIssue": "Upravit {title}", - "EditWorkflowStatuses": "Upravit stavy úkolů", - "EditProject": "Upravit projekt", - "DeleteProject": "Smazat projekt", - "ArchiveProjectName": "Archivovat projekt {name}?", - "ArchiveProjectConfirm": "Chcete tento projekt archivovat?", - "DeleteProjectConfirm": "Chcete tento projekt a všechny jeho úkoly smazat?", - "ProjectHasIssues": "Tento projekt obsahuje úkoly. Opravdu jej chcete archivovat?", - "ManageWorkflowStatuses": "Spravovat typy projektů", - "AddWorkflowStatus": "Přidat stav úkolu", - "EditWorkflowStatus": "Upravit stav úkolu", - "DeleteWorkflowStatus": "Smazat stav úkolu", - "DeleteWorkflowStatusConfirm": "Chcete smazat stav \"{status}\"?", - "DeleteWorkflowStatusErrorDescription": "Stav \"{status}\" má přiřazeno {count, plural, =1 {1 úkol} other {# úkolů}}. Vyberte stav pro přesun", - - "Save": "Uložit", - "IncludeItemsThatMatch": "Zahrnout položky, které odpovídají", - "AnyFilter": "jakémukoli filtru", - "AllFilters": "všem filtrům", - "NoDescription": "Žádný popis", - "SearchIssue": "Hledat úkol...", - - "StatusHistory": "Historie stavů", - "NewSubIssue": "Přidat podúkol...", - "AddLabel": "Přidat štítek", - - "DeleteIssue": "Smazat {issueCount, plural, =1 {úkol} other {# úkolů}}", - "DeleteIssueConfirm": "Chcete smazat {issueCount, plural, =1 {úkol} other {úkoly}}{subIssueCount, plural, =0 {?} =1 { a podúkol?} other { a podúkoly?}}", - - "Milestone": "Milník", - "NoMilestone": "Žádný milník", - "MoveToMilestone": "Vybrat milník", - "Milestones": "Milníky", - "AllMilestones": "Vše", - "PlannedMilestones": "Plánované", - "ActiveMilestones": "Aktivní", - "ClosedMilestones": "Dokončené", - "AddToMilestone": "Přidat do milníku", - "MilestoneNamePlaceholder": "Název milníku", - - "NewMilestone": "Nový milník", - "CreateMilestone": "Vytvořit", - - "MoveAndDeleteMilestone": "Přesunout úkoly do {newMilestone} a smazat {deleteMilestone}", - "MoveAndDeleteMilestoneConfirm": "Chcete smazat milník a přesunout úkoly do jiného milníku?", - - "Estimation": "Odhad", - "ReportedTime": "Strávený čas", - "RemainingTime": "Zbývající čas", - "TimeSpendReports": "Zprávy o stráveném čase", - "TimeSpendReport": "Čas", - "TimeSpendReportAdd": "Přidat zprávu o čase", - "TimeSpendReportDate": "Datum", - "TimeSpendReportValue": "Strávený čas", - "TimeSpendReportValueTooltip": "Strávený čas v hodinách", - "TimeSpendReportDescription": "Popis", - "TimeSpendDays": "{value}d", - "TimeSpendHours": "{value}h", - "TimeSpendMinutes": "{value}m", - "ChildEstimation": "Odhad podúkolů", - "ChildReportedTime": "Čas podúkolů", - "CapacityValue": "z {value}d", - "NewRelatedIssue": "Nový související úkol", - "RelatedIssuesNotFound": "Související úkoly nenalezeny", - - "AddedReference": "Přidán odkaz", - "AddedAsBlocked": "Označeno jako blokované", - "AddedAsBlocking": "Označeno jako blokující", - - "IssueTemplate": "Šablona", - "IssueTemplates": "Šablony", - "NewProcess": "Nová šablona", - "SaveProcess": "Uložit šablonu", - "NoIssueTemplate": "Žádná šablona", - "TemplateReplace": "Chcete použít novou šablonu?", - "TemplateReplaceConfirm": "Všechna pole budou přepsána hodnotami nové šablony", - "Apply": "Použít", - - "CurrentWorkDay": "Aktuální pracovní den", - "PreviousWorkDay": "Předchozí pracovní den", - "TimeReportDayTypeLabel": "Vyberte typ pracovního dne", - "DefaultAssignee": "Výchozí přiřazení úkolů", - - "SevenHoursLength": "Sedm hodin", - "EightHoursLength": "Osm hodin", - "HourLabel": "h", - "MinuteLabel": "m", - "Saved": "Uloženo...", - "CreatedIssue": "Úkol vytvořen", - "CreatedSubIssue": "Podúkol vytvořen", - "ChangeStatus": "Změnit stav", - "ConfigLabel": "Přehled", - "ConfigDescription": "Rozšíření pro správu pracovních úkolů a projektů.", - "NoStatusFound": "Nebyl nalezen odpovídající stav", - "CreateMissingStatus": "Vytvořit chybějící stav", - "UnsetParent": "Nadřazený úkol bude odebrán", - "AllProjects": "Všechny projekty", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "Aktualizováno uživatelem {senderName}", - "IssueNotificationChanged": "{senderName} změnil {property}", - "IssueNotificationChangedProperty": "{senderName} změnil {property} na \"{newValue}\"", - "IssueNotificationMessage": "{senderName}: {message}", - "PreviousAssigned": "Původně přiřazeno", - "IssueAssignedToYou": "Přiřazeno vám", - "RelatedIssueTargetDescription": "Cílový projekt pro související úkol", - "MapRelatedIssues": "Nastavit výchozí projekty pro související úkoly", - "DefaultIssueStatus": "Výchozí stav úkolu", - "IssueStatus": "Stav", - "Extensions": "Rozšíření", - "UnsetParentIssue": "Odebrat nadřazený úkol", - "ForbidCreateProjectPermission": "Zakázat vytvoření projektu", - "ForbidCreateProjectPermissionDescription": "Zakazuje uživatelům vytvářet nové projekty", - "AllowCreatingIssues": "Povolit vytváření úkolů", - "Day": "Den", - "Gantt": "Gantt", - "Month": "Měsíc", - "Quarter": "Čtvrtletí", - "Week": "Týden" - }, - "status": {} -} + "string": { + "TrackerApplication": "Přehled", + "Projects": "Vaše projekty", + "More": "Více", + "Default": "Výchozí", + "MakeDefault": "Nastavit jako výchozí", + "Delete": "Smazat", + "Open": "Otevřít", + "Members": "Členové", + "Inbox": "Doručená pošta", + "MyIssues": "Mé úkoly", + "ViewIssue": "Zobrazit úkol", + "IssueCreated": "Úkol vytvořen", + "Issues": "Úkoly", + "Views": "Zobrazení", + "Active": "Aktivní", + "AllIssues": "Všechny úkoly", + "ActiveIssues": "Aktivní úkoly", + "BacklogIssues": "Návrhy", + "Backlog": "Návrhy", + "Board": "Tabule", + "Components": "Komponenty", + "AllComponents": "Vše", + "BacklogComponents": "Návrhy", + "ActiveComponents": "Aktivní", + "ClosedComponents": "Uzavřené", + "NewComponent": "Nová komponenta", + "CreateComponent": "Vytvořit komponentu", + "ComponentNamePlaceholder": "Název komponenty", + "ComponentDescriptionPlaceholder": "Popis (volitelné)", + "ComponentLead": "Vedoucí", + "ComponentMembers": "Členové", + "StartDate": "Datum zahájení", + "TargetDate": "Cílové datum", + "Planned": "Plánováno", + "InProgress": "Probíhá", + "Paused": "Pozastaveno", + "Completed": "Dokončeno", + "Canceled": "Zrušeno", + "CreateProject": "Vytvořit projekt", + "NewProject": "Nový projekt", + "ProjectTitle": "Název projektu", + "ProjectTitlePlaceholder": "Nový projekt", + "UsedInIssueIDs": "Použito v ID úkolů", + "Identifier": "Identifikátor", + "Import": "Importovat", + "ProjectIdentifier": "Identifikátor projektu", + "IdentifierExists": "Identifikátor projektu již existuje", + "ProjectIdentifierPlaceholder": "PROJ", + "ChooseIcon": "Vybrat ikonu", + "AddIssue": "Přidat úkol", + "NewIssue": "Nový úkol", + "NewIssuePlaceholder": "Nový", + "ResumeDraft": "Pokračovat v konceptu", + "SaveIssue": "Vytvořit úkol", + "SetPriority": "Nastavit prioritu…", + "SetStatus": "Nastavit stav…", + "SelectIssue": "Vybrat úkol", + "Priority": "Priorita", + "NoPriority": "Bez priority", + "Urgent": "Naléhavé", + "High": "Vysoká", + "Medium": "Střední", + "Low": "Nízká", + "Unassigned": "Nepřiřazeno", + "Back": "Zpět", + "List": "Seznam", + "NumberLabels": "{count, plural, =0 {žádné štítky} =1 {1 štítek} other {# štítků}}", + "CategoryBacklog": "Návrhy", + "CategoryUnstarted": "Nezahájeno", + "CategoryStarted": "Zahájeno", + "CategoryCompleted": "Dokončeno", + "CategoryCanceled": "Zrušeno", + "Title": "Název", + "Name": "Jméno", + "Description": "Popis", + "Status": "Stav", + "Number": "Číslo", + "Assignee": "Přiřazený", + "AssignTo": "Přiřadit…", + "AssignedTo": "Přiřazeno k {value}", + "Parent": "Nadřazený úkol", + "SetParent": "Nastavit nadřazený úkol…", + "ChangeParent": "Změnit nadřazený úkol…", + "RemoveParent": "Odebrat nadřazený úkol", + "OpenParent": "Otevřít nadřazený úkol", + "SubIssues": "Podúkoly", + "SubIssuesList": "Podúkoly ({subIssues})", + "OpenSubIssues": "Otevřít podúkoly", + "AddSubIssues": "Přidat podúkol", + "BlockedBy": "Blokováno", + "RelatedTo": "Související s", + "Comments": "Komentáře", + "Attachments": "Přílohy", + "Labels": "Štítky", + "Component": "Komponenta", + "SetDueDate": "Nastavit datum splnění…", + "ChangeDueDate": "Změnit datum splnění…", + "ModificationDate": "Aktualizováno {value}", + "Project": "Projekt", + "Issue": "Úkol", + "SubIssue": "Podúkol", + "Rank": "Hodnost", + "TypeIssuePriority": "Priorita úkolu", + "IssueTitlePlaceholder": "Název úkolu", + "SubIssueTitlePlaceholder": "Název podúkolu", + "IssueDescriptionPlaceholder": "Přidat popis…", + "SubIssueDescriptionPlaceholder": "Přidat popis podúkolu", + "AddIssueTooltip": "Přidat úkol…", + "Grouping": "Seskupení", + "Ordering": "Řazení", + "CompletedIssues": "Dokončené úkoly", + "NoGrouping": "Bez seskupení", + "NoAssignee": "Bez přiřazení", + "LastUpdated": "Poslední aktualizace", + "DueDate": "Datum splnění", + "IssueStartDate": "Datum zahájení", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "Manuální", + "All": "Vše", + "PastWeek": "Minulý týden", + "PastMonth": "Minulý měsíc", + "CopyIssueUrl": "Zkopírovat URL úkolu do schránky", + "CopyIssueId": "Zkopírovat ID úkolu do schránky", + "CopyIssueBranch": "Zkopírovat název větve Git do schránky", + "CopyIssueTitle": "Zkopírovat název úkolu do schránky", + "AssetLabel": "Mějte přehled", + "AddToComponent": "Přidat do komponenty…", + "MoveToComponent": "Přesunout do komponenty…", + "NoComponent": "Bez komponenty", + "ComponentLeadTitle": "Vedoucí komponenty", + "ComponentMembersTitle": "Členové komponenty", + "ComponentLeadSearchPlaceholder": "Nastavit vedoucího komponenty…", + "ComponentMembersSearchPlaceholder": "Změnit členy komponenty…", + "MoveToProject": "Přesunout do projektu", + "Duplicate": "Duplikovat", + "GotoIssues": "Přejít na úkoly", + "GotoActive": "Přejít na aktivní úkoly", + "GotoBacklog": "Přejít na návrhy", + "GotoComponents": "Přejít na komponenty", + "GotoMyIssues": "Přejít na mé úkoly", + "GotoTrackerApplication": "Přepnout na aplikaci Přehled", + "CreatedOne": "Vytvořeno", + "MoveIssues": "Přesunout úkoly", + "MoveIssuesDescription": "Vyberte projekt, do kterého chcete úkoly přesunout", + "ManageAttributes": "Spravovat atributy", + "KeepOriginalAttributes": "Zachovat původní atributy", + "KeepOriginalAttributesTooltip": "Původní stavy a komponenty úkolů budou zachovány v novém projektu", + "SelectReplacement": "Následující položky nejsou dostupné v novém projektu. Vyberte náhradu.", + "MissingItem": "CHYBĚJÍCÍ POLOŽKA", + "Replacement": "NÁHRADA", + "Original": "PŮVODNÍ", + "OriginalDescription": "Položky z této sekce budou vytvořeny v novém projektu", + "Relations": "Vztahy", + "RemoveRelation": "Odebrat vztah...", + "AddBlockedBy": "Označit jako blokováno...", + "AddIsBlocking": "Označit jako blokující...", + "AddRelatedIssue": "Odkázat na jiný úkol...", + "RelatedIssue": "Související úkol {id} - {title}", + "BlockedIssue": "Blokovaný úkol {id} - {title}", + "BlockingIssue": "Blokující úkol {id} - {title}", + "BlockedBySearchPlaceholder": "Vyhledat úkol pro označení jako blokováno...", + "IsBlockingSearchPlaceholder": "Vyhledat úkol pro označení jako blokující...", + "RelatedIssueSearchPlaceholder": "Vyhledat úkol pro odkazování...", + "Blocks": "Blokuje", + "Related": "Související", + "RelatedIssues": "Související úkoly", + "EditIssue": "Upravit {title}", + "EditWorkflowStatuses": "Upravit stavy úkolů", + "EditProject": "Upravit projekt", + "DeleteProject": "Smazat projekt", + "ArchiveProjectName": "Archivovat projekt {name}?", + "ArchiveProjectConfirm": "Chcete tento projekt archivovat?", + "DeleteProjectConfirm": "Chcete tento projekt a všechny jeho úkoly smazat?", + "ProjectHasIssues": "Tento projekt obsahuje úkoly. Opravdu jej chcete archivovat?", + "ManageWorkflowStatuses": "Spravovat typy projektů", + "AddWorkflowStatus": "Přidat stav úkolu", + "EditWorkflowStatus": "Upravit stav úkolu", + "DeleteWorkflowStatus": "Smazat stav úkolu", + "DeleteWorkflowStatusConfirm": "Chcete smazat stav \"{status}\"?", + "DeleteWorkflowStatusErrorDescription": "Stav \"{status}\" má přiřazeno {count, plural, =1 {1 úkol} other {# úkolů}}. Vyberte stav pro přesun", + "Save": "Uložit", + "IncludeItemsThatMatch": "Zahrnout položky, které odpovídají", + "AnyFilter": "jakémukoli filtru", + "AllFilters": "všem filtrům", + "NoDescription": "Žádný popis", + "SearchIssue": "Hledat úkol...", + "StatusHistory": "Historie stavů", + "NewSubIssue": "Přidat podúkol...", + "AddLabel": "Přidat štítek", + "DeleteIssue": "Smazat {issueCount, plural, =1 {úkol} other {# úkolů}}", + "DeleteIssueConfirm": "Chcete smazat {issueCount, plural, =1 {úkol} other {úkoly}}{subIssueCount, plural, =0 {?} =1 { a podúkol?} other { a podúkoly?}}", + "Milestone": "Milník", + "NoMilestone": "Žádný milník", + "MoveToMilestone": "Vybrat milník", + "Milestones": "Milníky", + "AllMilestones": "Vše", + "PlannedMilestones": "Plánované", + "ActiveMilestones": "Aktivní", + "ClosedMilestones": "Dokončené", + "AddToMilestone": "Přidat do milníku", + "MilestoneNamePlaceholder": "Název milníku", + "NewMilestone": "Nový milník", + "CreateMilestone": "Vytvořit", + "MoveAndDeleteMilestone": "Přesunout úkoly do {newMilestone} a smazat {deleteMilestone}", + "MoveAndDeleteMilestoneConfirm": "Chcete smazat milník a přesunout úkoly do jiného milníku?", + "Estimation": "Odhad", + "ReportedTime": "Strávený čas", + "RemainingTime": "Zbývající čas", + "TimeSpendReports": "Zprávy o stráveném čase", + "TimeSpendReport": "Čas", + "TimeSpendReportAdd": "Přidat zprávu o čase", + "TimeSpendReportDate": "Datum", + "TimeSpendReportValue": "Strávený čas", + "TimeSpendReportValueTooltip": "Strávený čas v hodinách", + "TimeSpendReportDescription": "Popis", + "TimeSpendDays": "{value}d", + "TimeSpendHours": "{value}h", + "TimeSpendMinutes": "{value}m", + "ChildEstimation": "Odhad podúkolů", + "ChildReportedTime": "Čas podúkolů", + "CapacityValue": "z {value}d", + "NewRelatedIssue": "Nový související úkol", + "RelatedIssuesNotFound": "Související úkoly nenalezeny", + "AddedReference": "Přidán odkaz", + "AddedAsBlocked": "Označeno jako blokované", + "AddedAsBlocking": "Označeno jako blokující", + "IssueTemplate": "Šablona", + "IssueTemplates": "Šablony", + "NewProcess": "Nová šablona", + "SaveProcess": "Uložit šablonu", + "NoIssueTemplate": "Žádná šablona", + "TemplateReplace": "Chcete použít novou šablonu?", + "TemplateReplaceConfirm": "Všechna pole budou přepsána hodnotami nové šablony", + "Apply": "Použít", + "CurrentWorkDay": "Aktuální pracovní den", + "PreviousWorkDay": "Předchozí pracovní den", + "TimeReportDayTypeLabel": "Vyberte typ pracovního dne", + "DefaultAssignee": "Výchozí přiřazení úkolů", + "SevenHoursLength": "Sedm hodin", + "EightHoursLength": "Osm hodin", + "HourLabel": "h", + "MinuteLabel": "m", + "Saved": "Uloženo...", + "CreatedIssue": "Úkol vytvořen", + "CreatedSubIssue": "Podúkol vytvořen", + "ChangeStatus": "Změnit stav", + "ConfigLabel": "Přehled", + "ConfigDescription": "Rozšíření pro správu pracovních úkolů a projektů.", + "NoStatusFound": "Nebyl nalezen odpovídající stav", + "CreateMissingStatus": "Vytvořit chybějící stav", + "UnsetParent": "Nadřazený úkol bude odebrán", + "AllProjects": "Všechny projekty", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "Aktualizováno uživatelem {senderName}", + "IssueNotificationChanged": "{senderName} změnil {property}", + "IssueNotificationChangedProperty": "{senderName} změnil {property} na \"{newValue}\"", + "IssueNotificationMessage": "{senderName}: {message}", + "PreviousAssigned": "Původně přiřazeno", + "IssueAssignedToYou": "Přiřazeno vám", + "RelatedIssueTargetDescription": "Cílový projekt pro související úkol", + "MapRelatedIssues": "Nastavit výchozí projekty pro související úkoly", + "DefaultIssueStatus": "Výchozí stav úkolu", + "IssueStatus": "Stav", + "Extensions": "Rozšíření", + "UnsetParentIssue": "Odebrat nadřazený úkol", + "ForbidCreateProjectPermission": "Zakázat vytvoření projektu", + "ForbidCreateProjectPermissionDescription": "Zakazuje uživatelům vytvářet nové projekty", + "AllowCreatingIssues": "Povolit vytváření úkolů", + "Day": "Den", + "Gantt": "Gantt", + "Month": "Měsíc", + "Quarter": "Čtvrtletí", + "Week": "Týden", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/de.json b/plugins/tracker-assets/lang/de.json index 51aa73d1518..eafe7e7cc44 100644 --- a/plugins/tracker-assets/lang/de.json +++ b/plugins/tracker-assets/lang/de.json @@ -1,304 +1,289 @@ { - "string": { - "TrackerApplication": "Tracker", - "Projects": "Deine Projekte", - "More": "Mehr", - "Default": "Standard", - "MakeDefault": "Als Standard festlegen", - "Delete": "Löschen", - "Open": "Öffnen", - "Members": "Mitglieder", - "Inbox": "Posteingang", - "MyIssues": "Meine Aufgaben", - "ViewIssue": "Aufgabe anzeigen", - "IssueCreated": "Aufgabe erstellt", - "Issues": "Aufgaben", - "Views": "Ansichten", - "Active": "Aktiv", - "AllIssues": "Alle Aufgaben", - "ActiveIssues": "Aktive Aufgaben", - "BacklogIssues": "Backlog", - "Backlog": "Backlog", - "Board": "Board", - "Components": "Komponenten", - "AllComponents": "Alle", - "BacklogComponents": "Backlog", - "ActiveComponents": "Aktive", - "ClosedComponents": "Geschlossen", - "NewComponent": "Neue Komponente", - "CreateComponent": "Komponente erstellen", - "ComponentNamePlaceholder": "Komponentenname", - "ComponentDescriptionPlaceholder": "Beschreibung (optional)", - "ComponentLead": "Leitung", - "ComponentMembers": "Mitglieder", - "StartDate": "Startdatum", - "TargetDate": "Zieldatum", - "Planned": "Geplant", - "InProgress": "In Bearbeitung", - "Paused": "Pausiert", - "Completed": "Abgeschlossen", - "Canceled": "Abgebrochen", - "CreateProject": "Projekt erstellen", - "NewProject": "Neues Projekt", - "ProjectTitle": "Projekttitel", - "ProjectTitlePlaceholder": "Neues Projekt", - "UsedInIssueIDs": "Verwendet in Aufgaben-IDs", - "Identifier": "Kennung", - "Import": "Importieren", - "ProjectIdentifier": "Projektkennung", - "IdentifierExists": "Projektkennung existiert bereits", - "ProjectIdentifierPlaceholder": "PRJKT", - "ChooseIcon": "Symbol wählen", - "AddIssue": "Aufgabe hinzufügen", - "NewIssue": "Neue Aufgabe", - "NewIssuePlaceholder": "Neu", - "ResumeDraft": "Entwurf fortsetzen", - "SaveIssue": "Aufgabe erstellen", - "SetPriority": "Priorität setzen...", - "SetStatus": "Status setzen...", - "SelectIssue": "Aufgabe auswählen", - "Priority": "Priorität", - "NoPriority": "Keine Priorität", - "Urgent": "Dringend", - "High": "Hoch", - "Medium": "Mittel", - "Low": "Niedrig", - "Unassigned": "Nicht zugewiesen", - "Back": "Zurück", - "List": "Liste", - "NumberLabels": "{count, plural, =0 {keine Labels} =1 {1 Label} other {# Labels}}", - - "CategoryBacklog": "Backlog", - "CategoryUnstarted": "Nicht gestartet", - "CategoryStarted": "Gestartet", - "CategoryCompleted": "Abgeschlossen", - "CategoryCanceled": "Abgebrochen", - - "Title": "Titel", - "Name": "Name", - "Description": "Beschreibung", - "Status": "Status", - "Number": "Nummer", - "Assignee": "Zugewiesen an", - "AssignTo": "Zuweisen an...", - "AssignedTo": "Zugewiesen an {value}", - "Parent": "Übergeordnete Aufgabe", - "SetParent": "Übergeordnete Aufgabe setzen...", - "ChangeParent": "Übergeordnete Aufgabe ändern...", - "RemoveParent": "Übergeordnete Aufgabe entfernen", - "OpenParent": "Übergeordnete Aufgabe öffnen", - "SubIssues": "Unteraufgaben", - "SubIssuesList": "Unteraufgaben ({subIssues})", - "OpenSubIssues": "Unteraufgaben öffnen", - "AddSubIssues": "Unteraufgabe hinzufügen", - "BlockedBy": "Blockiert durch", - "RelatedTo": "Verwandt mit", - "Comments": "Kommentare", - "Attachments": "Anhänge", - "Labels": "Labels", - "Component": "Komponente", - "Space": "", - "SetDueDate": "Fälligkeitsdatum setzen...", - "ChangeDueDate": "Fälligkeitsdatum ändern...", - "ModificationDate": "Aktualisiert am {value}", - "Project": "Projekt", - "Issue": "Aufgabe", - "SubIssue": "Unteraufgabe", - "Document": "Dokument", - "DocumentIcon": "Dokumentsymbol", - "DocumentColor": "Dokumentfarbe", - "Rank": "Rang", - "TypeIssuePriority": "Aufgabenpriorität", - "IssueTitlePlaceholder": "Aufgabentitel", - "SubIssueTitlePlaceholder": "Unteraufgabentitel", - "IssueDescriptionPlaceholder": "Beschreibung hinzufügen...", - "SubIssueDescriptionPlaceholder": "Unteraufgabenbeschreibung hinzufügen", - "AddIssueTooltip": "Aufgabe hinzufügen...", - "NewIssueDialogClose": "Möchten Sie diesen Dialog schließen?", - "NewIssueDialogCloseNote": "Alle Änderungen gehen verloren", - "RemoveComponentDialogClose": "Komponente löschen?", - "RemoveComponentDialogCloseNote": "Sind Sie sicher, dass Sie diese Komponente löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden", - "Grouping": "Gruppierung", - "Ordering": "Sortierung", - "CompletedIssues": "Abgeschlossene Aufgaben", - "NoGrouping": "Keine Gruppierung", - "NoAssignee": "Nicht zugewiesen", - "LastUpdated": "Zuletzt aktualisiert", - "DueDate": "Fälligkeitsdatum", - "IssueStartDate": "Startdatum", - "GanttDependency": "Abhängigkeit", - "GanttLag": "Verzögerung", - "Manual": "Manuell", - "All": "Alle", - "PastWeek": "Letzte Woche", - "PastMonth": "Letzter Monat", - "CopyIssueUrl": "Aufgaben-URL in Zwischenablage kopieren", - "CopyIssueId": "Aufgaben-ID in Zwischenablage kopieren", - "CopyIssueBranch": "Git-Branch-Namen in Zwischenablage kopieren", - "CopyIssueTitle": "Aufgabentitel in Zwischenablage kopieren", - "AssetLabel": "Asset", - "AddToComponent": "Zu Komponente hinzufügen...", - "MoveToComponent": "Zu Komponente verschieben...", - "NoComponent": "Keine Komponente", - "ComponentLeadTitle": "Komponentenleitung", - "ComponentMembersTitle": "Komponentenmitglieder", - "ComponentLeadSearchPlaceholder": "Komponentenleitung festlegen...", - "ComponentMembersSearchPlaceholder": "Komponentenmitglieder ändern...", - "MoveToProject": "Zu Projekt verschieben", - "Duplicate": "Duplizieren", - - "GotoIssues": "Zu Aufgaben", - "GotoActive": "Zu aktiven Aufgaben", - "GotoBacklog": "Zum Backlog", - "GotoComponents": "Zu Komponenten", - "GotoMyIssues": "Zu meinen Aufgaben", - "GotoTrackerApplication": "Zum Tracker wechseln", - - "CreatedOne": "Erstellt", - "MoveIssues": "Aufgaben verschieben", - "MoveIssuesDescription": "Wählen Sie das Zielprojekt aus", - "ManageAttributes": "Attribute verwalten", - "KeepOriginalAttributes": "Ursprüngliche Attribute beibehalten", - "KeepOriginalAttributesTooltip": "Ursprüngliche Aufgabenstatus und Komponenten werden im neuen Projekt beibehalten", - "SelectReplacement": "Die folgenden Elemente sind im neuen Projekt nicht verfügbar. Wählen Sie einen Ersatz.", - "MissingItem": "FEHLENDES ELEMENT", - "Replacement": "ERSATZ", - "Original": "ORIGINAL", - "OriginalDescription": "Elemente aus diesem Bereich werden im neuen Projekt erstellt", - - "Relations": "Beziehungen", - "RemoveRelation": "Beziehung entfernen...", - "AddBlockedBy": "Als blockiert markieren durch...", - "AddIsBlocking": "Als blockierend markieren...", - "AddRelatedIssue": "Andere Aufgabe referenzieren...", - "RelatedIssue": "Verwandte Aufgabe {id} - {title}", - "BlockedIssue": "Blockierte Aufgabe {id} - {title}", - "BlockingIssue": "Blockierende Aufgabe {id} - {title}", - "BlockedBySearchPlaceholder": "Nach blockierender Aufgabe suchen...", - "IsBlockingSearchPlaceholder": "Nach zu blockierender Aufgabe suchen...", - "RelatedIssueSearchPlaceholder": "Nach zu referenzierender Aufgabe suchen...", - "Blocks": "Blockiert", - "Related": "Verwandt", - "RelatedIssues": "Verwandte Aufgaben", - - "EditIssue": "{title} bearbeiten", - "EditWorkflowStatuses": "Aufgabenstatus bearbeiten", - "EditProject": "Projekt bearbeiten", - "DeleteProject": "Projekt löschen", - "ArchiveProjectName": "Projekt {name} archivieren?", - "ArchiveProjectConfirm": "Möchten Sie dieses Projekt archivieren?", - "DeleteProjectConfirm": "Möchten Sie dieses Projekt und alle Aufgaben löschen?", - "ProjectHasIssues": "Es gibt bestehende Aufgaben in diesem Projekt. Sind Sie sicher, dass Sie archivieren möchten?", - "ManageWorkflowStatuses": "Projekttypen verwalten", - "AddWorkflowStatus": "Aufgabenstatus hinzufügen", - "EditWorkflowStatus": "Aufgabenstatus bearbeiten", - "DeleteWorkflowStatus": "Aufgabenstatus löschen", - "DeleteWorkflowStatusConfirm": "Möchten Sie den Status \"{status}\" löschen?", - "DeleteWorkflowStatusErrorDescription": "Dem Status \"{status}\" sind {count, plural, =1 {1 Aufgabe} other {# Aufgaben}} zugewiesen. Bitte wählen Sie einen neuen Status", - - "Save": "Speichern", - "IncludeItemsThatMatch": "Elemente einschließen, die übereinstimmen mit", - "AnyFilter": "einem Filter", - "AllFilters": "allen Filtern", - "NoDescription": "Keine Beschreibung", - "SearchIssue": "Nach Aufgabe suchen...", - - "StatusHistory": "Statusverlauf", - "NewSubIssue": "Unteraufgabe hinzufügen...", - "AddLabel": "Label hinzufügen", - - "DeleteIssue": "{issueCount, plural, =1 {Aufgabe} other {# Aufgaben}} löschen", - "DeleteIssueConfirm": "Möchten Sie {issueCount, plural, =1 {die Aufgabe} other {die Aufgaben}}{subIssueCount, plural, =0 {} =1 { und Unteraufgabe} other { und Unteraufgaben}} löschen?", - - "Milestone": "Meilenstein", - "NoMilestone": "Kein Meilenstein", - "MoveToMilestone": "Meilenstein auswählen", - "Milestones": "Meilensteine", - "AllMilestones": "Alle", - "PlannedMilestones": "Geplant", - "ActiveMilestones": "Aktiv", - "ClosedMilestones": "Abgeschlossen", - "AddToMilestone": "Zu Meilenstein hinzufügen", - "MilestoneNamePlaceholder": "Meilensteinname", - - "NewMilestone": "Neuer Meilenstein", - "CreateMilestone": "Erstellen", - - "MoveAndDeleteMilestone": "Aufgaben zu {newMilestone} verschieben und {deleteMilestone} löschen", - "MoveAndDeleteMilestoneConfirm": "Möchten Sie den Meilenstein löschen und die Aufgaben zu einem anderen Meilenstein verschieben?", - - "Estimation": "Schätzung", - "ReportedTime": "Aufgewendete Zeit", - "RemainingTime": "Verbleibende Zeit", - "TimeSpendReports": "Zeiterfassungsberichte", - "TimeSpendReport": "Zeit", - "TimeSpendReportAdd": "Zeiterfassung hinzufügen", - "TimeSpendReportDate": "Datum", - "TimeSpendReportValue": "Aufgewendete Zeit", - "TimeSpendReportValueTooltip": "Aufgewendete Zeit in Stunden", - "TimeSpendReportDescription": "Beschreibung", - "TimeSpendDays": "{value}T", - "TimeSpendHours": "{value}Std", - "TimeSpendMinutes": "{value}Min", - "ChildEstimation": "Schätzung Unteraufgaben", - "ChildReportedTime": "Zeit Unteraufgaben", - "CapacityValue": "von {value}T", - "NewRelatedIssue": "Neue verwandte Aufgabe", - "RelatedIssuesNotFound": "Keine verwandten Aufgaben gefunden", - - "AddedReference": "Referenz hinzugefügt", - "AddedAsBlocked": "Als blockiert markiert", - "AddedAsBlocking": "Als blockierend markiert", - - "IssueTemplate": "Vorlage", - "IssueTemplates": "Vorlagen", - "NewProcess": "Neue Vorlage", - "SaveProcess": "Vorlage speichern", - "NoIssueTemplate": "Keine Vorlage", - "TemplateReplace": "Möchten Sie die neue Vorlage anwenden?", - "TemplateReplaceConfirm": "Alle Felder werden mit den Werten der neuen Vorlage überschrieben", - "Apply": "Anwenden", - - "CurrentWorkDay": "Aktueller Arbeitstag", - "PreviousWorkDay": "Vorheriger Arbeitstag", - "TimeReportDayTypeLabel": "Art des Zeiterfassungstags auswählen", - "DefaultAssignee": "Standardzuweisung für Aufgaben", - - "SevenHoursLength": "Sieben Stunden", - "EightHoursLength": "Acht Stunden", - "HourLabel": "Std", - "MinuteLabel": "Min", - "Saved": "Gespeichert...", - "CreatedIssue": "Aufgabe erstellt", - "CreatedSubIssue": "Unteraufgabe erstellt", - "ChangeStatus": "Status ändern", - "ConfigLabel": "Tracker", - "ConfigDescription": "Erweiterung zur Verwaltung von Arbeitsaufgaben und deren Erledigung.", - "NoStatusFound": "Kein passender Status gefunden", - "CreateMissingStatus": "Fehlenden Status erstellen", - "UnsetParent": "Übergeordnete Aufgabe wird entfernt", - "AllProjects": "Alle Projekte", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "Aktualisiert von {senderName}", - "IssueNotificationChanged": "{senderName} hat {property} geändert", - "IssueNotificationChangedProperty": "{senderName} hat {property} zu \"{newValue}\" geändert", - "IssueNotificationMessage": "{senderName}: {message}", - "PreviousAssigned": "Zuvor zugewiesen", - "IssueAssignedToYou": "Ihnen zugewiesen", - "RelatedIssueTargetDescription": "Zielprojekt für verwandte Aufgaben für Klasse oder Space", - "MapRelatedIssues": "Standard-Projekte für verwandte Aufgaben konfigurieren", - "DefaultIssueStatus": "Standard-Aufgabenstatus", - "IssueStatus": "Status", - "Extensions": "Erweiterungen", - "UnsetParentIssue": "Übergeordnete Aufgabe entfernen", - "ForbidCreateProjectPermission": "Projekterstellung verbieten", - "ForbidCreateProjectPermissionDescription": "Verbietet Benutzern das Erstellen neuer Projekte", - "AllowCreatingIssues": "Erstellen von Aufgaben erlauben", - "Day": "Tag", - "Gantt": "Gantt", - "Month": "Monat", - "Quarter": "Quartal", - "Week": "Woche" - }, - "status": {} -} + "string": { + "TrackerApplication": "Tracker", + "Projects": "Deine Projekte", + "More": "Mehr", + "Default": "Standard", + "MakeDefault": "Als Standard festlegen", + "Delete": "Löschen", + "Open": "Öffnen", + "Members": "Mitglieder", + "Inbox": "Posteingang", + "MyIssues": "Meine Aufgaben", + "ViewIssue": "Aufgabe anzeigen", + "IssueCreated": "Aufgabe erstellt", + "Issues": "Aufgaben", + "Views": "Ansichten", + "Active": "Aktiv", + "AllIssues": "Alle Aufgaben", + "ActiveIssues": "Aktive Aufgaben", + "BacklogIssues": "Backlog", + "Backlog": "Backlog", + "Board": "Board", + "Components": "Komponenten", + "AllComponents": "Alle", + "BacklogComponents": "Backlog", + "ActiveComponents": "Aktive", + "ClosedComponents": "Geschlossen", + "NewComponent": "Neue Komponente", + "CreateComponent": "Komponente erstellen", + "ComponentNamePlaceholder": "Komponentenname", + "ComponentDescriptionPlaceholder": "Beschreibung (optional)", + "ComponentLead": "Leitung", + "ComponentMembers": "Mitglieder", + "StartDate": "Startdatum", + "TargetDate": "Zieldatum", + "Planned": "Geplant", + "InProgress": "In Bearbeitung", + "Paused": "Pausiert", + "Completed": "Abgeschlossen", + "Canceled": "Abgebrochen", + "CreateProject": "Projekt erstellen", + "NewProject": "Neues Projekt", + "ProjectTitle": "Projekttitel", + "ProjectTitlePlaceholder": "Neues Projekt", + "UsedInIssueIDs": "Verwendet in Aufgaben-IDs", + "Identifier": "Kennung", + "Import": "Importieren", + "ProjectIdentifier": "Projektkennung", + "IdentifierExists": "Projektkennung existiert bereits", + "ProjectIdentifierPlaceholder": "PRJKT", + "ChooseIcon": "Symbol wählen", + "AddIssue": "Aufgabe hinzufügen", + "NewIssue": "Neue Aufgabe", + "NewIssuePlaceholder": "Neu", + "ResumeDraft": "Entwurf fortsetzen", + "SaveIssue": "Aufgabe erstellen", + "SetPriority": "Priorität setzen...", + "SetStatus": "Status setzen...", + "SelectIssue": "Aufgabe auswählen", + "Priority": "Priorität", + "NoPriority": "Keine Priorität", + "Urgent": "Dringend", + "High": "Hoch", + "Medium": "Mittel", + "Low": "Niedrig", + "Unassigned": "Nicht zugewiesen", + "Back": "Zurück", + "List": "Liste", + "NumberLabels": "{count, plural, =0 {keine Labels} =1 {1 Label} other {# Labels}}", + "CategoryBacklog": "Backlog", + "CategoryUnstarted": "Nicht gestartet", + "CategoryStarted": "Gestartet", + "CategoryCompleted": "Abgeschlossen", + "CategoryCanceled": "Abgebrochen", + "Title": "Titel", + "Name": "Name", + "Description": "Beschreibung", + "Status": "Status", + "Number": "Nummer", + "Assignee": "Zugewiesen an", + "AssignTo": "Zuweisen an...", + "AssignedTo": "Zugewiesen an {value}", + "Parent": "Übergeordnete Aufgabe", + "SetParent": "Übergeordnete Aufgabe setzen...", + "ChangeParent": "Übergeordnete Aufgabe ändern...", + "RemoveParent": "Übergeordnete Aufgabe entfernen", + "OpenParent": "Übergeordnete Aufgabe öffnen", + "SubIssues": "Unteraufgaben", + "SubIssuesList": "Unteraufgaben ({subIssues})", + "OpenSubIssues": "Unteraufgaben öffnen", + "AddSubIssues": "Unteraufgabe hinzufügen", + "BlockedBy": "Blockiert durch", + "RelatedTo": "Verwandt mit", + "Comments": "Kommentare", + "Attachments": "Anhänge", + "Labels": "Labels", + "Component": "Komponente", + "Space": "", + "SetDueDate": "Fälligkeitsdatum setzen...", + "ChangeDueDate": "Fälligkeitsdatum ändern...", + "ModificationDate": "Aktualisiert am {value}", + "Project": "Projekt", + "Issue": "Aufgabe", + "SubIssue": "Unteraufgabe", + "Document": "Dokument", + "DocumentIcon": "Dokumentsymbol", + "DocumentColor": "Dokumentfarbe", + "Rank": "Rang", + "TypeIssuePriority": "Aufgabenpriorität", + "IssueTitlePlaceholder": "Aufgabentitel", + "SubIssueTitlePlaceholder": "Unteraufgabentitel", + "IssueDescriptionPlaceholder": "Beschreibung hinzufügen...", + "SubIssueDescriptionPlaceholder": "Unteraufgabenbeschreibung hinzufügen", + "AddIssueTooltip": "Aufgabe hinzufügen...", + "NewIssueDialogClose": "Möchten Sie diesen Dialog schließen?", + "NewIssueDialogCloseNote": "Alle Änderungen gehen verloren", + "RemoveComponentDialogClose": "Komponente löschen?", + "RemoveComponentDialogCloseNote": "Sind Sie sicher, dass Sie diese Komponente löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden", + "Grouping": "Gruppierung", + "Ordering": "Sortierung", + "CompletedIssues": "Abgeschlossene Aufgaben", + "NoGrouping": "Keine Gruppierung", + "NoAssignee": "Nicht zugewiesen", + "LastUpdated": "Zuletzt aktualisiert", + "DueDate": "Fälligkeitsdatum", + "IssueStartDate": "Startdatum", + "GanttDependency": "Abhängigkeit", + "GanttLag": "Verzögerung", + "Manual": "Manuell", + "All": "Alle", + "PastWeek": "Letzte Woche", + "PastMonth": "Letzter Monat", + "CopyIssueUrl": "Aufgaben-URL in Zwischenablage kopieren", + "CopyIssueId": "Aufgaben-ID in Zwischenablage kopieren", + "CopyIssueBranch": "Git-Branch-Namen in Zwischenablage kopieren", + "CopyIssueTitle": "Aufgabentitel in Zwischenablage kopieren", + "AssetLabel": "Asset", + "AddToComponent": "Zu Komponente hinzufügen...", + "MoveToComponent": "Zu Komponente verschieben...", + "NoComponent": "Keine Komponente", + "ComponentLeadTitle": "Komponentenleitung", + "ComponentMembersTitle": "Komponentenmitglieder", + "ComponentLeadSearchPlaceholder": "Komponentenleitung festlegen...", + "ComponentMembersSearchPlaceholder": "Komponentenmitglieder ändern...", + "MoveToProject": "Zu Projekt verschieben", + "Duplicate": "Duplizieren", + "GotoIssues": "Zu Aufgaben", + "GotoActive": "Zu aktiven Aufgaben", + "GotoBacklog": "Zum Backlog", + "GotoComponents": "Zu Komponenten", + "GotoMyIssues": "Zu meinen Aufgaben", + "GotoTrackerApplication": "Zum Tracker wechseln", + "CreatedOne": "Erstellt", + "MoveIssues": "Aufgaben verschieben", + "MoveIssuesDescription": "Wählen Sie das Zielprojekt aus", + "ManageAttributes": "Attribute verwalten", + "KeepOriginalAttributes": "Ursprüngliche Attribute beibehalten", + "KeepOriginalAttributesTooltip": "Ursprüngliche Aufgabenstatus und Komponenten werden im neuen Projekt beibehalten", + "SelectReplacement": "Die folgenden Elemente sind im neuen Projekt nicht verfügbar. Wählen Sie einen Ersatz.", + "MissingItem": "FEHLENDES ELEMENT", + "Replacement": "ERSATZ", + "Original": "ORIGINAL", + "OriginalDescription": "Elemente aus diesem Bereich werden im neuen Projekt erstellt", + "Relations": "Beziehungen", + "RemoveRelation": "Beziehung entfernen...", + "AddBlockedBy": "Als blockiert markieren durch...", + "AddIsBlocking": "Als blockierend markieren...", + "AddRelatedIssue": "Andere Aufgabe referenzieren...", + "RelatedIssue": "Verwandte Aufgabe {id} - {title}", + "BlockedIssue": "Blockierte Aufgabe {id} - {title}", + "BlockingIssue": "Blockierende Aufgabe {id} - {title}", + "BlockedBySearchPlaceholder": "Nach blockierender Aufgabe suchen...", + "IsBlockingSearchPlaceholder": "Nach zu blockierender Aufgabe suchen...", + "RelatedIssueSearchPlaceholder": "Nach zu referenzierender Aufgabe suchen...", + "Blocks": "Blockiert", + "Related": "Verwandt", + "RelatedIssues": "Verwandte Aufgaben", + "EditIssue": "{title} bearbeiten", + "EditWorkflowStatuses": "Aufgabenstatus bearbeiten", + "EditProject": "Projekt bearbeiten", + "DeleteProject": "Projekt löschen", + "ArchiveProjectName": "Projekt {name} archivieren?", + "ArchiveProjectConfirm": "Möchten Sie dieses Projekt archivieren?", + "DeleteProjectConfirm": "Möchten Sie dieses Projekt und alle Aufgaben löschen?", + "ProjectHasIssues": "Es gibt bestehende Aufgaben in diesem Projekt. Sind Sie sicher, dass Sie archivieren möchten?", + "ManageWorkflowStatuses": "Projekttypen verwalten", + "AddWorkflowStatus": "Aufgabenstatus hinzufügen", + "EditWorkflowStatus": "Aufgabenstatus bearbeiten", + "DeleteWorkflowStatus": "Aufgabenstatus löschen", + "DeleteWorkflowStatusConfirm": "Möchten Sie den Status \"{status}\" löschen?", + "DeleteWorkflowStatusErrorDescription": "Dem Status \"{status}\" sind {count, plural, =1 {1 Aufgabe} other {# Aufgaben}} zugewiesen. Bitte wählen Sie einen neuen Status", + "Save": "Speichern", + "IncludeItemsThatMatch": "Elemente einschließen, die übereinstimmen mit", + "AnyFilter": "einem Filter", + "AllFilters": "allen Filtern", + "NoDescription": "Keine Beschreibung", + "SearchIssue": "Nach Aufgabe suchen...", + "StatusHistory": "Statusverlauf", + "NewSubIssue": "Unteraufgabe hinzufügen...", + "AddLabel": "Label hinzufügen", + "DeleteIssue": "{issueCount, plural, =1 {Aufgabe} other {# Aufgaben}} löschen", + "DeleteIssueConfirm": "Möchten Sie {issueCount, plural, =1 {die Aufgabe} other {die Aufgaben}}{subIssueCount, plural, =0 {} =1 { und Unteraufgabe} other { und Unteraufgaben}} löschen?", + "Milestone": "Meilenstein", + "NoMilestone": "Kein Meilenstein", + "MoveToMilestone": "Meilenstein auswählen", + "Milestones": "Meilensteine", + "AllMilestones": "Alle", + "PlannedMilestones": "Geplant", + "ActiveMilestones": "Aktiv", + "ClosedMilestones": "Abgeschlossen", + "AddToMilestone": "Zu Meilenstein hinzufügen", + "MilestoneNamePlaceholder": "Meilensteinname", + "NewMilestone": "Neuer Meilenstein", + "CreateMilestone": "Erstellen", + "MoveAndDeleteMilestone": "Aufgaben zu {newMilestone} verschieben und {deleteMilestone} löschen", + "MoveAndDeleteMilestoneConfirm": "Möchten Sie den Meilenstein löschen und die Aufgaben zu einem anderen Meilenstein verschieben?", + "Estimation": "Schätzung", + "ReportedTime": "Aufgewendete Zeit", + "RemainingTime": "Verbleibende Zeit", + "TimeSpendReports": "Zeiterfassungsberichte", + "TimeSpendReport": "Zeit", + "TimeSpendReportAdd": "Zeiterfassung hinzufügen", + "TimeSpendReportDate": "Datum", + "TimeSpendReportValue": "Aufgewendete Zeit", + "TimeSpendReportValueTooltip": "Aufgewendete Zeit in Stunden", + "TimeSpendReportDescription": "Beschreibung", + "TimeSpendDays": "{value}T", + "TimeSpendHours": "{value}Std", + "TimeSpendMinutes": "{value}Min", + "ChildEstimation": "Schätzung Unteraufgaben", + "ChildReportedTime": "Zeit Unteraufgaben", + "CapacityValue": "von {value}T", + "NewRelatedIssue": "Neue verwandte Aufgabe", + "RelatedIssuesNotFound": "Keine verwandten Aufgaben gefunden", + "AddedReference": "Referenz hinzugefügt", + "AddedAsBlocked": "Als blockiert markiert", + "AddedAsBlocking": "Als blockierend markiert", + "IssueTemplate": "Vorlage", + "IssueTemplates": "Vorlagen", + "NewProcess": "Neue Vorlage", + "SaveProcess": "Vorlage speichern", + "NoIssueTemplate": "Keine Vorlage", + "TemplateReplace": "Möchten Sie die neue Vorlage anwenden?", + "TemplateReplaceConfirm": "Alle Felder werden mit den Werten der neuen Vorlage überschrieben", + "Apply": "Anwenden", + "CurrentWorkDay": "Aktueller Arbeitstag", + "PreviousWorkDay": "Vorheriger Arbeitstag", + "TimeReportDayTypeLabel": "Art des Zeiterfassungstags auswählen", + "DefaultAssignee": "Standardzuweisung für Aufgaben", + "SevenHoursLength": "Sieben Stunden", + "EightHoursLength": "Acht Stunden", + "HourLabel": "Std", + "MinuteLabel": "Min", + "Saved": "Gespeichert...", + "CreatedIssue": "Aufgabe erstellt", + "CreatedSubIssue": "Unteraufgabe erstellt", + "ChangeStatus": "Status ändern", + "ConfigLabel": "Tracker", + "ConfigDescription": "Erweiterung zur Verwaltung von Arbeitsaufgaben und deren Erledigung.", + "NoStatusFound": "Kein passender Status gefunden", + "CreateMissingStatus": "Fehlenden Status erstellen", + "UnsetParent": "Übergeordnete Aufgabe wird entfernt", + "AllProjects": "Alle Projekte", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "Aktualisiert von {senderName}", + "IssueNotificationChanged": "{senderName} hat {property} geändert", + "IssueNotificationChangedProperty": "{senderName} hat {property} zu \"{newValue}\" geändert", + "IssueNotificationMessage": "{senderName}: {message}", + "PreviousAssigned": "Zuvor zugewiesen", + "IssueAssignedToYou": "Ihnen zugewiesen", + "RelatedIssueTargetDescription": "Zielprojekt für verwandte Aufgaben für Klasse oder Space", + "MapRelatedIssues": "Standard-Projekte für verwandte Aufgaben konfigurieren", + "DefaultIssueStatus": "Standard-Aufgabenstatus", + "IssueStatus": "Status", + "Extensions": "Erweiterungen", + "UnsetParentIssue": "Übergeordnete Aufgabe entfernen", + "ForbidCreateProjectPermission": "Projekterstellung verbieten", + "ForbidCreateProjectPermissionDescription": "Verbietet Benutzern das Erstellen neuer Projekte", + "AllowCreatingIssues": "Erstellen von Aufgaben erlauben", + "Day": "Tag", + "Gantt": "Gantt", + "Month": "Monat", + "Quarter": "Quartal", + "Week": "Woche", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index 7dad04bee41..e47d9b284c9 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -298,7 +298,9 @@ "Gantt": "Gantt", "Month": "Month", "Quarter": "Quarter", - "Week": "Week" + "Week": "Week", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" }, "status": {} } diff --git a/plugins/tracker-assets/lang/es.json b/plugins/tracker-assets/lang/es.json index 127dc0a4f63..35cd60a1e00 100644 --- a/plugins/tracker-assets/lang/es.json +++ b/plugins/tracker-assets/lang/es.json @@ -1,287 +1,289 @@ { - "string": { - "TrackerApplication": "Seguimiento", - "Projects": "Tus proyectos", - "More": "Más", - "Default": "Por defecto", - "MakeDefault": "Establecer como por defecto", - "Delete": "Eliminar", - "Open": "Abrir", - "Members": "Miembros", - "Inbox": "Bandeja de entrada", - "MyIssues": "Mis tareas", - "ViewIssue": "Ver tarea", - "IssueCreated": "Tarea creada", - "Issues": "Tareas", - "Views": "Vistas", - "Active": "Activo", - "AllIssues": "Todas las tareas", - "ActiveIssues": "Tareas activas", - "BacklogIssues": "Atrasadas", - "Backlog": "Atrasadas", - "Board": "Tablero", - "Components": "Componentes", - "AllComponents": "Todos", - "BacklogComponents": "Atrasados", - "ActiveComponents": "Activos", - "ClosedComponents": "Cerrados", - "NewComponent": "Nuevo componente", - "CreateComponent": "Crear componente", - "ComponentNamePlaceholder": "Nombre del componente", - "ComponentDescriptionPlaceholder": "Descripción (opcional)", - "ComponentLead": "Responsable del componente", - "ComponentMembers": "Miembros del componente", - "StartDate": "Fecha de inicio", - "TargetDate": "Fecha objetivo", - "Planned": "Planificado", - "InProgress": "En progreso", - "Paused": "Pausado", - "Completed": "Completado", - "Canceled": "Cancelado", - "CreateProject": "Crear proyecto", - "NewProject": "Nuevo proyecto", - "ProjectTitle": "Título del proyecto", - "ProjectTitlePlaceholder": "Nuevo proyecto", - "UsedInIssueIDs": "Utilizado en IDs de tareas", - "Identifier": "Identificador", - "Import": "Importar", - "ProjectIdentifier": "Identificador del proyecto", - "IdentifierExists": "El identificador del proyecto ya existe", - "ProjectIdentifierPlaceholder": "PROJ", - "ChooseIcon": "Seleccionar icono", - "AddIssue": "Añadir tarea", - "NewIssue": "Nueva tarea", - "NewIssuePlaceholder": "Nueva", - "ResumeDraft": "Continuar borrador", - "SaveIssue": "Crear tarea", - "SetPriority": "Establecer prioridad\u2026", - "SetStatus": "Establecer estado\u2026", - "SelectIssue": "Seleccionar tarea", - "Priority": "Prioridad", - "NoPriority": "Sin prioridad", - "Urgent": "Urgente", - "High": "Alta", - "Medium": "Media", - "Low": "Baja", - "Unassigned": "Sin asignar", - "Back": "Atrás", - "List": "Lista", - "NumberLabels": "{count, plural, =0 {sin etiquetas} =1 {1 etiqueta} other {# etiquetas}}", - "CategoryBacklog": "Atraso", - "CategoryUnstarted": "Por Iniciar", - "CategoryStarted": "Iniciado", - "CategoryCompleted": "Completado", - "CategoryCanceled": "Cancelado", - "Title": "Título", - "Name": "Nombre", - "Description": "Descripción", - "Status": "Estado", - "Number": "Número", - "Assignee": "Asignado a", - "AssignTo": "Asignar a...", - "AssignedTo": "Asignado a {value}", - "Parent": "Tarea principal", - "SetParent": "Establecer tarea principal...", - "ChangeParent": "Cambiar tarea principal...", - "RemoveParent": "Eliminar tarea principal", - "OpenParent": "Abrir tarea principal", - "SubIssues": "Subtareas", - "SubIssuesList": "Subtareas ({subIssues})", - "OpenSubIssues": "Abrir subtareas", - "AddSubIssues": "Añadir subtarea", - "BlockedBy": "Bloqueado por", - "RelatedTo": "Relacionado con", - "Comments": "Comentarios", - "Attachments": "Adjuntos", - "Labels": "Etiquetas", - "Component": "Componente", - "Space": "", - "SetDueDate": "Establecer fecha de vencimiento...", - "ChangeDueDate": "Cambiar fecha de vencimiento...", - "ModificationDate": "Actualizado {value}", - "Project": "Proyecto", - "Issue": "Tarea", - "SubIssue": "Subtarea", - "Document": "", - "DocumentIcon": "", - "DocumentColor": "", - "Rank": "Posición", - "TypeIssuePriority": "Prioridad de la tarea", - "IssueTitlePlaceholder": "Título de la tarea", - "SubIssueTitlePlaceholder": "Título de la subtarea", - "IssueDescriptionPlaceholder": "Añadir descripción...", - "SubIssueDescriptionPlaceholder": "Añadir descripción de la subtarea", - "AddIssueTooltip": "Añadir tarea...", - "NewIssueDialogClose": "¿Quieres cerrar este diálogo?", - "NewIssueDialogCloseNote": "Se perderán todos los cambios", - "RemoveComponentDialogClose": "¿Eliminar el componente?", - "RemoveComponentDialogCloseNote": "¿Estás seguro de que quieres eliminar este componente? Esta operación no se puede deshacer", - "Grouping": "Agrupación", - "Ordering": "Ordenación", - "CompletedIssues": "Tareas completadas", - "NoGrouping": "Sin agrupación", - "NoAssignee": "Sin asignar", - "LastUpdated": "Última actualización", - "DueDate": "Fecha de vencimiento", - "IssueStartDate": "Fecha de inicio", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "Manual", - "All": "Todos", - "PastWeek": "Semana pasada", - "PastMonth": "Mes pasado", - "CopyIssueUrl": "Copiar URL de la tarea al portapapeles", - "CopyIssueId": "Copiar ID de la tarea al portapapeles", - "CopyIssueBranch": "Copiar nombre de la rama Git al portapapeles", - "CopyIssueTitle": "Copiar título de la tarea al portapapeles", - "AssetLabel": "Activo", - "AddToComponent": "Añadir al componente...", - "MoveToComponent": "Mover al componente...", - "NoComponent": "Sin componente", - "ComponentLeadTitle": "Responsable del componente", - "ComponentMembersTitle": "Miembros del componente", - "ComponentLeadSearchPlaceholder": "Establecer responsable del componente...", - "ComponentMembersSearchPlaceholder": "Cambiar miembros del componente...", - "MoveToProject": "Mover al proyecto", - "Duplicate": "Duplicar", - "GotoIssues": "Ir a tareas", - "GotoActive": "Ir a tareas activas", - "GotoBacklog": "Ir al backlog", - "GotoComponents": "Ir a componentes", - "GotoMyIssues": "Ir a mis tareas", - "GotoTrackerApplication": "Cambiar a la aplicación de seguimiento", - "CreatedOne": "Creada", - "MoveIssues": "Mover tareas", - "MoveIssuesDescription": "Selecciona el proyecto al que deseas mover las tareas", - "ManageAttributes": "Gestionar atributos", - "KeepOriginalAttributes": "Mantener atributos originales", - "KeepOriginalAttributesTooltip": "Se mantendrán los estados y componentes originales en el nuevo proyecto", - "SelectReplacement": "Los siguientes elementos no están disponibles en el nuevo proyecto. Seleccione un reemplazo.", - "MissingItem": "SIN ELEMENTO", - "Replacement": "REEMPLAZO", - "Original": "ORIGINAL", - "OriginalDescription": "Los elementos de esta sección se crearán en el nuevo proyecto", - "Relations": "Relaciones", - "RemoveRelation": "Eliminar relación...", - "AddBlockedBy": "Marcar como bloqueado por...", - "AddIsBlocking": "Marcar como bloqueador...", - "AddRelatedIssue": "Referenciar otro problema...", - "RelatedIssue": "Problema relacionado {id} - {title}", - "BlockedIssue": "Problema bloqueado {id} - {title}", - "BlockingIssue": "Problema bloqueador {id} - {title}", - "BlockedBySearchPlaceholder": "Buscar problema para marcar como bloqueado por...", - "IsBlockingSearchPlaceholder": "Buscar problema para marcar como bloqueador...", - "RelatedIssueSearchPlaceholder": "Buscar problema para referenciar...", - "Blocks": "Bloquea", - "Related": "Relacionado", - "RelatedIssues": "Problemas relacionados", - "EditIssue": "Editar {title}", - "EditWorkflowStatuses": "Editar estados de problema", - "EditProject": "Editar proyecto", - "DeleteProject": "Eliminar proyecto", - "ArchiveProjectName": "¿Archivar proyecto {name}?", - "ArchiveProjectConfirm": "¿Desea archivar este proyecto?", - "DeleteProjectConfirm": "¿Desea eliminar este proyecto y todos los problemas?", - "ProjectHasIssues": "Hay problemas existentes en este proyecto, ¿está seguro de que desea archivarlos?", - "ManageWorkflowStatuses": "Administrar tipos de proyecto", - "AddWorkflowStatus": "Agregar estado de problema", - "EditWorkflowStatus": "Editar estado de problema", - "DeleteWorkflowStatus": "Eliminar estado de problema", - "DeleteWorkflowStatusConfirm": "¿Desea eliminar el estado \"{status}\"?", - "DeleteWorkflowStatusErrorDescription": "El estado \"{status}\" tiene {count, plural, =1 {1 problema} other {# problemas}} asignados. Por favor seleccione un estado al que moverlos", - "Save": "Guardar", - "IncludeItemsThatMatch": "Incluir elementos que coincidan", - "AnyFilter": "cualquier filtro", - "AllFilters": "todos los filtros", - "NoDescription": "Sin descripción", - "SearchIssue": "Buscar tarea...", - "StatusHistory": "Historial de estado", - "NewSubIssue": "Agregar subproblema...", - "AddLabel": "Agregar etiqueta", - "DeleteIssue": "Eliminar {issueCount, plural, =1 {problema} other {# problemas}}", - "DeleteIssueConfirm": "¿Desea eliminar {issueCount, plural, =1 {problema} other {problemas}}{subIssueCount, plural, =0 {?} =1 { y subproblema?} other { y subproblemas?}}", - "Milestone": "Hitos", - "NoMilestone": "Sin hito", - "MoveToMilestone": "Seleccionar hito", - "Milestones": "Hitos", - "AllMilestones": "Todos", - "PlannedMilestones": "Planificados", - "ActiveMilestones": "Activos", - "ClosedMilestones": "Completados", - "AddToMilestone": "Agregar a hito", - "MilestoneNamePlaceholder": "Nombre del hito", - "NewMilestone": "Nuevo hito", - "CreateMilestone": "Crear", - "MoveAndDeleteMilestone": "Mover problemas a {newMilestone} y eliminar {deleteMilestone}", - "MoveAndDeleteMilestoneConfirm": "¿Desea eliminar el hito y mover los problemas a otro hito?", - "Estimation": "Estimación", - "ReportedTime": "Tiempo empleado", - "RemainingTime": "Tiempo restante", - "TimeSpendReports": "Informes de tiempo empleado", - "TimeSpendReport": "Tiempo", - "TimeSpendReportAdd": "Agregar informe de tiempo", - "TimeSpendReportDate": "Fecha", - "TimeSpendReportValue": "Tiempo empleado", - "TimeSpendReportValueTooltip": "Tiempo empleado en horas", - "TimeSpendReportDescription": "Descripción", - "TimeSpendDays": "{value}d", - "TimeSpendHours": "{value}h", - "TimeSpendMinutes": "{value}m", - "ChildEstimation": "Estimación de subtemas", - "ChildReportedTime": "Tiempo de subtemas", - "CapacityValue": "de {value}d", - "NewRelatedIssue": "Nuevo problema relacionado", - "RelatedIssuesNotFound": "Problemas relacionados no encontrados", - "AddedReference": "Referencia añadida", - "AddedAsBlocked": "Marcado como bloqueado", - "AddedAsBlocking": "Marcado como bloqueante", - "IssueTemplate": "Plantilla", - "IssueTemplates": "Plantillas", - "NewProcess": "Nueva plantilla", - "SaveProcess": "Guardar plantilla", - "NoIssueTemplate": "Sin plantilla", - "TemplateReplace": "¿Desea aplicar la nueva plantilla?", - "TemplateReplaceConfirm": "Todos los campos serán sobrescritos por los valores de la nueva plantilla", - "Apply": "Aplicar", - "CurrentWorkDay": "Día laboral actual", - "PreviousWorkDay": "Día laboral anterior", - "TimeReportDayTypeLabel": "Seleccionar tipo de día para reporte de tiempo", - "DefaultAssignee": "Asignado por defecto para problemas", - "SevenHoursLength": "Siete horas", - "EightHoursLength": "Ocho horas", - "HourLabel": "h", - "MinuteLabel": "m", - "Saved": "Guardado...", - "CreatedIssue": "Problema creado", - "CreatedSubIssue": "Sub-problema creado", - "ChangeStatus": "Cambiar estado", - "ConfigLabel": "Rastreador", - "ConfigDescription": "Extensión para gestionar elementos de trabajo y realizar todas las tareas realizadas.", - "NoStatusFound": "No se encontró ningún estado coincidente", - "CreateMissingStatus": "Crear estado faltante", - "UnsetParent": "El problema padre será desmarcado", - "AllProjects": "Todos los proyectos", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "Actualizado por {senderName}", - "IssueNotificationChanged": "{senderName} cambió {property}", - "IssueNotificationChangedProperty": "{senderName} cambió {property} a \"{newValue}\"", - "IssueNotificationMessage": "{senderName}: {message}", - "PreviousAssigned": "Anteriormente asignado", - "IssueAssignedToYou": "Asignado a ti", - "RelatedIssueTargetDescription": "Proyecto de destino de problema relacionado para Clase o Espacio", - "MapRelatedIssues": "Configurar proyectos predeterminados de problemas relacionados", - "DefaultIssueStatus": "Estado de problema predeterminado", - "IssueStatus": "Estado", - "Extensions": "Extensions", - "UnsetParentIssue": "Unset parent issue", - "ForbidCreateProjectPermission": "Prohibir crear proyecto", - "ForbidCreateProjectPermissionDescription": "Prohíbe a los usuarios crear nuevos proyectos", - "AllowCreatingIssues": "Permitir crear incidencias", - "Day": "Día", - "Gantt": "Gantt", - "Month": "Mes", - "Quarter": "Trimestre", - "Week": "Semana" - }, - "status": {} -} + "string": { + "TrackerApplication": "Seguimiento", + "Projects": "Tus proyectos", + "More": "Más", + "Default": "Por defecto", + "MakeDefault": "Establecer como por defecto", + "Delete": "Eliminar", + "Open": "Abrir", + "Members": "Miembros", + "Inbox": "Bandeja de entrada", + "MyIssues": "Mis tareas", + "ViewIssue": "Ver tarea", + "IssueCreated": "Tarea creada", + "Issues": "Tareas", + "Views": "Vistas", + "Active": "Activo", + "AllIssues": "Todas las tareas", + "ActiveIssues": "Tareas activas", + "BacklogIssues": "Atrasadas", + "Backlog": "Atrasadas", + "Board": "Tablero", + "Components": "Componentes", + "AllComponents": "Todos", + "BacklogComponents": "Atrasados", + "ActiveComponents": "Activos", + "ClosedComponents": "Cerrados", + "NewComponent": "Nuevo componente", + "CreateComponent": "Crear componente", + "ComponentNamePlaceholder": "Nombre del componente", + "ComponentDescriptionPlaceholder": "Descripción (opcional)", + "ComponentLead": "Responsable del componente", + "ComponentMembers": "Miembros del componente", + "StartDate": "Fecha de inicio", + "TargetDate": "Fecha objetivo", + "Planned": "Planificado", + "InProgress": "En progreso", + "Paused": "Pausado", + "Completed": "Completado", + "Canceled": "Cancelado", + "CreateProject": "Crear proyecto", + "NewProject": "Nuevo proyecto", + "ProjectTitle": "Título del proyecto", + "ProjectTitlePlaceholder": "Nuevo proyecto", + "UsedInIssueIDs": "Utilizado en IDs de tareas", + "Identifier": "Identificador", + "Import": "Importar", + "ProjectIdentifier": "Identificador del proyecto", + "IdentifierExists": "El identificador del proyecto ya existe", + "ProjectIdentifierPlaceholder": "PROJ", + "ChooseIcon": "Seleccionar icono", + "AddIssue": "Añadir tarea", + "NewIssue": "Nueva tarea", + "NewIssuePlaceholder": "Nueva", + "ResumeDraft": "Continuar borrador", + "SaveIssue": "Crear tarea", + "SetPriority": "Establecer prioridad…", + "SetStatus": "Establecer estado…", + "SelectIssue": "Seleccionar tarea", + "Priority": "Prioridad", + "NoPriority": "Sin prioridad", + "Urgent": "Urgente", + "High": "Alta", + "Medium": "Media", + "Low": "Baja", + "Unassigned": "Sin asignar", + "Back": "Atrás", + "List": "Lista", + "NumberLabels": "{count, plural, =0 {sin etiquetas} =1 {1 etiqueta} other {# etiquetas}}", + "CategoryBacklog": "Atraso", + "CategoryUnstarted": "Por Iniciar", + "CategoryStarted": "Iniciado", + "CategoryCompleted": "Completado", + "CategoryCanceled": "Cancelado", + "Title": "Título", + "Name": "Nombre", + "Description": "Descripción", + "Status": "Estado", + "Number": "Número", + "Assignee": "Asignado a", + "AssignTo": "Asignar a...", + "AssignedTo": "Asignado a {value}", + "Parent": "Tarea principal", + "SetParent": "Establecer tarea principal...", + "ChangeParent": "Cambiar tarea principal...", + "RemoveParent": "Eliminar tarea principal", + "OpenParent": "Abrir tarea principal", + "SubIssues": "Subtareas", + "SubIssuesList": "Subtareas ({subIssues})", + "OpenSubIssues": "Abrir subtareas", + "AddSubIssues": "Añadir subtarea", + "BlockedBy": "Bloqueado por", + "RelatedTo": "Relacionado con", + "Comments": "Comentarios", + "Attachments": "Adjuntos", + "Labels": "Etiquetas", + "Component": "Componente", + "Space": "", + "SetDueDate": "Establecer fecha de vencimiento...", + "ChangeDueDate": "Cambiar fecha de vencimiento...", + "ModificationDate": "Actualizado {value}", + "Project": "Proyecto", + "Issue": "Tarea", + "SubIssue": "Subtarea", + "Document": "", + "DocumentIcon": "", + "DocumentColor": "", + "Rank": "Posición", + "TypeIssuePriority": "Prioridad de la tarea", + "IssueTitlePlaceholder": "Título de la tarea", + "SubIssueTitlePlaceholder": "Título de la subtarea", + "IssueDescriptionPlaceholder": "Añadir descripción...", + "SubIssueDescriptionPlaceholder": "Añadir descripción de la subtarea", + "AddIssueTooltip": "Añadir tarea...", + "NewIssueDialogClose": "¿Quieres cerrar este diálogo?", + "NewIssueDialogCloseNote": "Se perderán todos los cambios", + "RemoveComponentDialogClose": "¿Eliminar el componente?", + "RemoveComponentDialogCloseNote": "¿Estás seguro de que quieres eliminar este componente? Esta operación no se puede deshacer", + "Grouping": "Agrupación", + "Ordering": "Ordenación", + "CompletedIssues": "Tareas completadas", + "NoGrouping": "Sin agrupación", + "NoAssignee": "Sin asignar", + "LastUpdated": "Última actualización", + "DueDate": "Fecha de vencimiento", + "IssueStartDate": "Fecha de inicio", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "Manual", + "All": "Todos", + "PastWeek": "Semana pasada", + "PastMonth": "Mes pasado", + "CopyIssueUrl": "Copiar URL de la tarea al portapapeles", + "CopyIssueId": "Copiar ID de la tarea al portapapeles", + "CopyIssueBranch": "Copiar nombre de la rama Git al portapapeles", + "CopyIssueTitle": "Copiar título de la tarea al portapapeles", + "AssetLabel": "Activo", + "AddToComponent": "Añadir al componente...", + "MoveToComponent": "Mover al componente...", + "NoComponent": "Sin componente", + "ComponentLeadTitle": "Responsable del componente", + "ComponentMembersTitle": "Miembros del componente", + "ComponentLeadSearchPlaceholder": "Establecer responsable del componente...", + "ComponentMembersSearchPlaceholder": "Cambiar miembros del componente...", + "MoveToProject": "Mover al proyecto", + "Duplicate": "Duplicar", + "GotoIssues": "Ir a tareas", + "GotoActive": "Ir a tareas activas", + "GotoBacklog": "Ir al backlog", + "GotoComponents": "Ir a componentes", + "GotoMyIssues": "Ir a mis tareas", + "GotoTrackerApplication": "Cambiar a la aplicación de seguimiento", + "CreatedOne": "Creada", + "MoveIssues": "Mover tareas", + "MoveIssuesDescription": "Selecciona el proyecto al que deseas mover las tareas", + "ManageAttributes": "Gestionar atributos", + "KeepOriginalAttributes": "Mantener atributos originales", + "KeepOriginalAttributesTooltip": "Se mantendrán los estados y componentes originales en el nuevo proyecto", + "SelectReplacement": "Los siguientes elementos no están disponibles en el nuevo proyecto. Seleccione un reemplazo.", + "MissingItem": "SIN ELEMENTO", + "Replacement": "REEMPLAZO", + "Original": "ORIGINAL", + "OriginalDescription": "Los elementos de esta sección se crearán en el nuevo proyecto", + "Relations": "Relaciones", + "RemoveRelation": "Eliminar relación...", + "AddBlockedBy": "Marcar como bloqueado por...", + "AddIsBlocking": "Marcar como bloqueador...", + "AddRelatedIssue": "Referenciar otro problema...", + "RelatedIssue": "Problema relacionado {id} - {title}", + "BlockedIssue": "Problema bloqueado {id} - {title}", + "BlockingIssue": "Problema bloqueador {id} - {title}", + "BlockedBySearchPlaceholder": "Buscar problema para marcar como bloqueado por...", + "IsBlockingSearchPlaceholder": "Buscar problema para marcar como bloqueador...", + "RelatedIssueSearchPlaceholder": "Buscar problema para referenciar...", + "Blocks": "Bloquea", + "Related": "Relacionado", + "RelatedIssues": "Problemas relacionados", + "EditIssue": "Editar {title}", + "EditWorkflowStatuses": "Editar estados de problema", + "EditProject": "Editar proyecto", + "DeleteProject": "Eliminar proyecto", + "ArchiveProjectName": "¿Archivar proyecto {name}?", + "ArchiveProjectConfirm": "¿Desea archivar este proyecto?", + "DeleteProjectConfirm": "¿Desea eliminar este proyecto y todos los problemas?", + "ProjectHasIssues": "Hay problemas existentes en este proyecto, ¿está seguro de que desea archivarlos?", + "ManageWorkflowStatuses": "Administrar tipos de proyecto", + "AddWorkflowStatus": "Agregar estado de problema", + "EditWorkflowStatus": "Editar estado de problema", + "DeleteWorkflowStatus": "Eliminar estado de problema", + "DeleteWorkflowStatusConfirm": "¿Desea eliminar el estado \"{status}\"?", + "DeleteWorkflowStatusErrorDescription": "El estado \"{status}\" tiene {count, plural, =1 {1 problema} other {# problemas}} asignados. Por favor seleccione un estado al que moverlos", + "Save": "Guardar", + "IncludeItemsThatMatch": "Incluir elementos que coincidan", + "AnyFilter": "cualquier filtro", + "AllFilters": "todos los filtros", + "NoDescription": "Sin descripción", + "SearchIssue": "Buscar tarea...", + "StatusHistory": "Historial de estado", + "NewSubIssue": "Agregar subproblema...", + "AddLabel": "Agregar etiqueta", + "DeleteIssue": "Eliminar {issueCount, plural, =1 {problema} other {# problemas}}", + "DeleteIssueConfirm": "¿Desea eliminar {issueCount, plural, =1 {problema} other {problemas}}{subIssueCount, plural, =0 {?} =1 { y subproblema?} other { y subproblemas?}}", + "Milestone": "Hitos", + "NoMilestone": "Sin hito", + "MoveToMilestone": "Seleccionar hito", + "Milestones": "Hitos", + "AllMilestones": "Todos", + "PlannedMilestones": "Planificados", + "ActiveMilestones": "Activos", + "ClosedMilestones": "Completados", + "AddToMilestone": "Agregar a hito", + "MilestoneNamePlaceholder": "Nombre del hito", + "NewMilestone": "Nuevo hito", + "CreateMilestone": "Crear", + "MoveAndDeleteMilestone": "Mover problemas a {newMilestone} y eliminar {deleteMilestone}", + "MoveAndDeleteMilestoneConfirm": "¿Desea eliminar el hito y mover los problemas a otro hito?", + "Estimation": "Estimación", + "ReportedTime": "Tiempo empleado", + "RemainingTime": "Tiempo restante", + "TimeSpendReports": "Informes de tiempo empleado", + "TimeSpendReport": "Tiempo", + "TimeSpendReportAdd": "Agregar informe de tiempo", + "TimeSpendReportDate": "Fecha", + "TimeSpendReportValue": "Tiempo empleado", + "TimeSpendReportValueTooltip": "Tiempo empleado en horas", + "TimeSpendReportDescription": "Descripción", + "TimeSpendDays": "{value}d", + "TimeSpendHours": "{value}h", + "TimeSpendMinutes": "{value}m", + "ChildEstimation": "Estimación de subtemas", + "ChildReportedTime": "Tiempo de subtemas", + "CapacityValue": "de {value}d", + "NewRelatedIssue": "Nuevo problema relacionado", + "RelatedIssuesNotFound": "Problemas relacionados no encontrados", + "AddedReference": "Referencia añadida", + "AddedAsBlocked": "Marcado como bloqueado", + "AddedAsBlocking": "Marcado como bloqueante", + "IssueTemplate": "Plantilla", + "IssueTemplates": "Plantillas", + "NewProcess": "Nueva plantilla", + "SaveProcess": "Guardar plantilla", + "NoIssueTemplate": "Sin plantilla", + "TemplateReplace": "¿Desea aplicar la nueva plantilla?", + "TemplateReplaceConfirm": "Todos los campos serán sobrescritos por los valores de la nueva plantilla", + "Apply": "Aplicar", + "CurrentWorkDay": "Día laboral actual", + "PreviousWorkDay": "Día laboral anterior", + "TimeReportDayTypeLabel": "Seleccionar tipo de día para reporte de tiempo", + "DefaultAssignee": "Asignado por defecto para problemas", + "SevenHoursLength": "Siete horas", + "EightHoursLength": "Ocho horas", + "HourLabel": "h", + "MinuteLabel": "m", + "Saved": "Guardado...", + "CreatedIssue": "Problema creado", + "CreatedSubIssue": "Sub-problema creado", + "ChangeStatus": "Cambiar estado", + "ConfigLabel": "Rastreador", + "ConfigDescription": "Extensión para gestionar elementos de trabajo y realizar todas las tareas realizadas.", + "NoStatusFound": "No se encontró ningún estado coincidente", + "CreateMissingStatus": "Crear estado faltante", + "UnsetParent": "El problema padre será desmarcado", + "AllProjects": "Todos los proyectos", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "Actualizado por {senderName}", + "IssueNotificationChanged": "{senderName} cambió {property}", + "IssueNotificationChangedProperty": "{senderName} cambió {property} a \"{newValue}\"", + "IssueNotificationMessage": "{senderName}: {message}", + "PreviousAssigned": "Anteriormente asignado", + "IssueAssignedToYou": "Asignado a ti", + "RelatedIssueTargetDescription": "Proyecto de destino de problema relacionado para Clase o Espacio", + "MapRelatedIssues": "Configurar proyectos predeterminados de problemas relacionados", + "DefaultIssueStatus": "Estado de problema predeterminado", + "IssueStatus": "Estado", + "Extensions": "Extensions", + "UnsetParentIssue": "Unset parent issue", + "ForbidCreateProjectPermission": "Prohibir crear proyecto", + "ForbidCreateProjectPermissionDescription": "Prohíbe a los usuarios crear nuevos proyectos", + "AllowCreatingIssues": "Permitir crear incidencias", + "Day": "Día", + "Gantt": "Gantt", + "Month": "Mes", + "Quarter": "Trimestre", + "Week": "Semana", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/fr.json b/plugins/tracker-assets/lang/fr.json index 4bf83349e83..ebbc675b081 100644 --- a/plugins/tracker-assets/lang/fr.json +++ b/plugins/tracker-assets/lang/fr.json @@ -1,287 +1,289 @@ { - "string": { - "TrackerApplication": "Suivi", - "Projects": "Vos projets", - "More": "Plus", - "Default": "Par défaut", - "MakeDefault": "Définir par défaut", - "Delete": "Supprimer", - "Open": "Ouvrir", - "Members": "Membres", - "Inbox": "Boîte de réception", - "MyIssues": "Mes issues", - "ViewIssue": "Voir l'issue", - "IssueCreated": "Issue créées", - "Issues": "Issues", - "Views": "Vues", - "Active": "Actif", - "AllIssues": "Toutes les issues", - "ActiveIssues": "Issues actives", - "BacklogIssues": "Backlog", - "Backlog": "Backlog", - "Board": "Tableau", - "Components": "Composants", - "AllComponents": "Tous", - "BacklogComponents": "Backlog", - "ActiveComponents": "Actif", - "ClosedComponents": "Fermé", - "NewComponent": "Nouveau composant", - "CreateComponent": "Créer un composant", - "ComponentNamePlaceholder": "Nom du composant", - "ComponentDescriptionPlaceholder": "Description (facultatif)", - "ComponentLead": "Responsable", - "ComponentMembers": "Membres", - "StartDate": "Date de début", - "TargetDate": "Date cible", - "Planned": "Planifié", - "InProgress": "En cours", - "Paused": "En pause", - "Completed": "Terminé", - "Canceled": "Annulé", - "CreateProject": "Créer un projet", - "NewProject": "Nouveau projet", - "ProjectTitle": "Titre du projet", - "ProjectTitlePlaceholder": "Nouveau projet", - "UsedInIssueIDs": "Utilisé dans les ID d'une issue", - "Identifier": "Identifiant", - "Import": "Importer", - "ProjectIdentifier": "Identifiant du projet", - "IdentifierExists": "L'identifiant du projet existe déjà", - "ProjectIdentifierPlaceholder": "PRJCT", - "ChooseIcon": "Choisir une icône", - "AddIssue": "Ajouter une issue", - "NewIssue": "Nouvelle issue", - "NewIssuePlaceholder": "Nouveau", - "ResumeDraft": "Reprendre le brouillon", - "SaveIssue": "Créer une issue", - "SetPriority": "Définir la priorité...", - "SetStatus": "Définir le statut...", - "SelectIssue": "Sélectionner une issue", - "Priority": "Priorité", - "NoPriority": "Aucune priorité", - "Urgent": "Urgent", - "High": "Haute", - "Medium": "Moyenne", - "Low": "Basse", - "Unassigned": "Non assigné", - "Back": "Retour", - "List": "Liste", - "NumberLabels": "{count, plural, =0 {aucune étiquette} =1 {1 étiquette} other {# étiquettes}}", - "CategoryBacklog": "Backlog", - "CategoryUnstarted": "Non commencé", - "CategoryStarted": "Commencé", - "CategoryCompleted": "Terminé", - "CategoryCanceled": "Annulé", - "Title": "Titre", - "Name": "Nom", - "Description": "Description", - "Status": "Statut", - "Number": "Numéro", - "Assignee": "Assigné à", - "AssignTo": "Assigner à...", - "AssignedTo": "Assigné à {value}", - "Parent": "Problème parent", - "SetParent": "Définir le problème parent...", - "ChangeParent": "Changer le problème parent...", - "RemoveParent": "Supprimer le problème parent", - "OpenParent": "Ouvrir le problème parent", - "SubIssues": "Sub-issue", - "SubIssuesList": "Sub-issue ({subIssues})", - "OpenSubIssues": "Ouvrir les sub-issue", - "AddSubIssues": "Ajouter un sub-issue", - "BlockedBy": "Bloqué par", - "RelatedTo": "Lié à", - "Comments": "Commentaires", - "Attachments": "Pièces jointes", - "Labels": "Étiquettes", - "Component": "Composant", - "Space": "", - "SetDueDate": "Définir la date d'échéance...", - "ChangeDueDate": "Changer la date d'échéance...", - "ModificationDate": "Mis à jour {value}", - "Project": "Projet", - "Issue": "Issue", - "SubIssue": "Sub-issue", - "Document": "", - "DocumentIcon": "", - "DocumentColor": "", - "Rank": "Rang", - "TypeIssuePriority": "Priorité de l'issue", - "IssueTitlePlaceholder": "Titre de l'issue", - "SubIssueTitlePlaceholder": "Titre de la sub-issue", - "IssueDescriptionPlaceholder": "Ajouter une description...", - "SubIssueDescriptionPlaceholder": "Ajouter une description de la sub-issue", - "AddIssueTooltip": "Ajouter une issue...", - "NewIssueDialogClose": "Voulez-vous fermer cette fenêtre ?", - "NewIssueDialogCloseNote": "Tous les changements seront perdus", - "RemoveComponentDialogClose": "Supprimer le composant ?", - "RemoveComponentDialogCloseNote": "Êtes-vous sûr de vouloir supprimer ce composant ? Cette opération est irréversible", - "Grouping": "Regroupement", - "Ordering": "Classement", - "CompletedIssues": "Issues terminées", - "NoGrouping": "Aucun regroupement", - "NoAssignee": "Non assigné", - "LastUpdated": "Dernière mise à jour", - "DueDate": "Date d'échéance", - "IssueStartDate": "Date de début", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "Manuel", - "All": "Tous", - "PastWeek": "La semaine passée", - "PastMonth": "Le mois passé", - "CopyIssueUrl": "Copier l'URL de l'issue dans le presse-papiers", - "CopyIssueId": "Copier l'ID de l'issue dans le presse-papiers", - "CopyIssueBranch": "Copier le nom de la branche Git dans le presse-papiers", - "CopyIssueTitle": "Copier le titre de l'issue dans le presse-papiers", - "AssetLabel": "Actif", - "AddToComponent": "Ajouter au composant...", - "MoveToComponent": "Déplacer vers le composant...", - "NoComponent": "Aucun composant", - "ComponentLeadTitle": "Responsable du composant", - "ComponentMembersTitle": "Membres du composant", - "ComponentLeadSearchPlaceholder": "Définir le responsable du composant...", - "ComponentMembersSearchPlaceholder": "Modifier les membres du composant...", - "MoveToProject": "Déplacer vers le projet", - "Duplicate": "Dupliquer", - "GotoIssues": "Aller aux issues", - "GotoActive": "Aller aux problèmes actifs", - "GotoBacklog": "Aller au backlog", - "GotoComponents": "Aller aux composants", - "GotoMyIssues": "Aller à mes issues", - "GotoTrackerApplication": "Passer à l'application de suivi", - "CreatedOne": "Créé", - "MoveIssues": "Déplacer les issues", - "MoveIssuesDescription": "Sélectionnez le projet vers lequel vous souhaitez déplacer les issues", - "ManageAttributes": "Gérer les attributs", - "KeepOriginalAttributes": "Conserver les attributs d'origine", - "KeepOriginalAttributesTooltip": "Les statuts et composants des problèmes d'origine seront conservés dans le nouveau projet", - "SelectReplacement": "Les éléments suivants ne sont pas disponibles dans le nouveau projet. Sélectionnez un remplacement.", - "MissingItem": "ÉLÉMENT MANQUANT", - "Replacement": "REMPLACEMENT", - "Original": "ORIGINAL", - "OriginalDescription": "Les éléments de cette section seront créés dans le nouveau projet", - "Relations": "Relations", - "RemoveRelation": "Supprimer la relation...", - "AddBlockedBy": "Marquer comme bloqué par...", - "AddIsBlocking": "Marquer comme bloquant...", - "AddRelatedIssue": "Référencer une autre issue...", - "RelatedIssue": "Issue liée {id} - {title}", - "BlockedIssue": "Issue bloquée {id} - {title}", - "BlockingIssue": "Issue bloquante {id} - {title}", - "BlockedBySearchPlaceholder": "Rechercher un problème à marquer comme bloqué par...", - "IsBlockingSearchPlaceholder": "Rechercher un problème à marquer comme bloquant...", - "RelatedIssueSearchPlaceholder": "Rechercher une issue à référencer...", - "Blocks": "Bloque", - "Related": "Lié", - "RelatedIssues": "Issues liées", - "EditIssue": "Modifier {title}", - "EditWorkflowStatuses": "Modifier les statuts des problèmes", - "EditProject": "Modifier le projet", - "DeleteProject": "Supprimer le projet", - "ArchiveProjectName": "Archiver le projet {name} ?", - "ArchiveProjectConfirm": "Voulez-vous archiver ce projet ?", - "DeleteProjectConfirm": "Voulez-vous supprimer ce projet et tous les problèmes ?", - "ProjectHasIssues": "Il y a des issues existantes dans ce projet, êtes-vous sûr de vouloir archiver ?", - "ManageWorkflowStatuses": "Gérer les types de projet", - "AddWorkflowStatus": "Ajouter un statut de problème", - "EditWorkflowStatus": "Modifier le statut du problème", - "DeleteWorkflowStatus": "Supprimer le statut du problème", - "DeleteWorkflowStatusConfirm": "Voulez-vous supprimer le statut \"{status}\" ?", - "DeleteWorkflowStatusErrorDescription": "Le statut \"{status}\" a {count, plural, =1 {1 problème} other {# problèmes}} assigné(s). Veuillez sélectionner un statut pour le déplacer", - "Save": "Enregistrer", - "IncludeItemsThatMatch": "Inclure les éléments qui correspondent", - "AnyFilter": "n'importe quel filtre", - "AllFilters": "tous les filtres", - "NoDescription": "Pas de description", - "SearchIssue": "Rechercher une tâche...", - "StatusHistory": "Historique des statuts", - "NewSubIssue": "Ajouter un sub-ssue...", - "AddLabel": "Ajouter une étiquette", - "DeleteIssue": "Supprimer {issueCount, plural, =1 {problème} other {# problèmes}}", - "DeleteIssueConfirm": "Voulez-vous supprimer {issueCount, plural, =1 {le problème} other {les problèmes}}{subIssueCount, plural, =0 {?} =1 { et le sous-problème ?} other { et les sous-problèmes ?}}", - "Milestone": "Jalon", - "NoMilestone": "Pas de jalon", - "MoveToMilestone": "Sélectionner un jalon", - "Milestones": "Jalons", - "AllMilestones": "Tous", - "PlannedMilestones": "Prévu", - "ActiveMilestones": "Actif", - "ClosedMilestones": "Terminé", - "AddToMilestone": "Ajouter au jalon", - "MilestoneNamePlaceholder": "Nom du jalon", - "NewMilestone": "Nouveau jalon", - "CreateMilestone": "Créer", - "MoveAndDeleteMilestone": "Déplacer les problèmes vers {newMilestone} et supprimer {deleteMilestone}", - "MoveAndDeleteMilestoneConfirm": "Voulez-vous supprimer le jalon et déplacer les problèmes vers un autre jalon ?", - "Estimation": "Estimation", - "ReportedTime": "Temps passé", - "RemainingTime": "Temps restant", - "TimeSpendReports": "Rapports de temps passé", - "TimeSpendReport": "Temps", - "TimeSpendReportAdd": "Ajouter un rapport de temps", - "TimeSpendReportDate": "Date", - "TimeSpendReportValue": "Temps passé", - "TimeSpendReportValueTooltip": "Temps passé en heures", - "TimeSpendReportDescription": "Description", - "TimeSpendDays": "{value}j", - "TimeSpendHours": "{value}h", - "TimeSpendMinutes": "{value}m", - "ChildEstimation": "Estimation des sous-problèmes", - "ChildReportedTime": "Temps des sous-problèmes", - "CapacityValue": "de {value}j", - "NewRelatedIssue": "Nouvelle issue liée", - "RelatedIssuesNotFound": "Issues liées non trouvés", - "AddedReference": "Référence ajoutée", - "AddedAsBlocked": "Marqué comme bloqué", - "AddedAsBlocking": "Marqué comme bloquant", - "IssueTemplate": "Modèle", - "IssueTemplates": "Modèles", - "NewProcess": "Nouveau modèle", - "SaveProcess": "Enregistrer le modèle", - "NoIssueTemplate": "Aucun modèle", - "TemplateReplace": "Voulez-vous appliquer le nouveau modèle ?", - "TemplateReplaceConfirm": "Tous les champs seront remplacés par les valeurs du nouveau modèle", - "Apply": "Appliquer", - "CurrentWorkDay": "Journée de travail actuelle", - "PreviousWorkDay": "Journée de travail précédente", - "TimeReportDayTypeLabel": "Sélectionnez le type de journée de rapport de temps", - "DefaultAssignee": "Assigné par défaut pour les problèmes", - "SevenHoursLength": "Sept heures", - "EightHoursLength": "Huit heures", - "HourLabel": "h", - "MinuteLabel": "m", - "Saved": "Enregistré...", - "CreatedIssue": "Issue créée", - "CreatedSubIssue": "Sub-issue créée", - "ChangeStatus": "Changer le statut", - "ConfigLabel": "Suivi", - "ConfigDescription": "Extension pour gérer les éléments de travail et accomplir toutes les tâches.", - "NoStatusFound": "Aucun statut correspondant trouvé", - "CreateMissingStatus": "Créer un statut manquant", - "UnsetParent": "Le problème parent sera désélectionné", - "AllProjects": "Tous les projets", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "Mis à jour par {senderName}", - "IssueNotificationChanged": "{senderName} a modifié {property}", - "IssueNotificationChangedProperty": "{senderName} a modifié {property} en \"{newValue}\"", - "IssueNotificationMessage": "{senderName} : {message}", - "PreviousAssigned": "Assigné précédemment", - "IssueAssignedToYou": "Assigné à vous", - "RelatedIssueTargetDescription": "Projet cible de l'issue liée pour la classe ou l'espace", - "MapRelatedIssues": "Configurer les projets par défaut des issues liées", - "DefaultIssueStatus": "Statut par défaut de l'issue", - "IssueStatus": "Statut", - "Extensions": "Extensions", - "UnsetParentIssue": "Désélectionner l'issue parent", - "ForbidCreateProjectPermission": "Interdire la création de projet", - "ForbidCreateProjectPermissionDescription": "Interdit aux utilisateurs de créer de nouveaux projets", - "AllowCreatingIssues": "Autoriser la création d'issues", - "Day": "Jour", - "Gantt": "Gantt", - "Month": "Mois", - "Quarter": "Trimestre", - "Week": "Semaine" - }, - "status": {} -} + "string": { + "TrackerApplication": "Suivi", + "Projects": "Vos projets", + "More": "Plus", + "Default": "Par défaut", + "MakeDefault": "Définir par défaut", + "Delete": "Supprimer", + "Open": "Ouvrir", + "Members": "Membres", + "Inbox": "Boîte de réception", + "MyIssues": "Mes issues", + "ViewIssue": "Voir l'issue", + "IssueCreated": "Issue créées", + "Issues": "Issues", + "Views": "Vues", + "Active": "Actif", + "AllIssues": "Toutes les issues", + "ActiveIssues": "Issues actives", + "BacklogIssues": "Backlog", + "Backlog": "Backlog", + "Board": "Tableau", + "Components": "Composants", + "AllComponents": "Tous", + "BacklogComponents": "Backlog", + "ActiveComponents": "Actif", + "ClosedComponents": "Fermé", + "NewComponent": "Nouveau composant", + "CreateComponent": "Créer un composant", + "ComponentNamePlaceholder": "Nom du composant", + "ComponentDescriptionPlaceholder": "Description (facultatif)", + "ComponentLead": "Responsable", + "ComponentMembers": "Membres", + "StartDate": "Date de début", + "TargetDate": "Date cible", + "Planned": "Planifié", + "InProgress": "En cours", + "Paused": "En pause", + "Completed": "Terminé", + "Canceled": "Annulé", + "CreateProject": "Créer un projet", + "NewProject": "Nouveau projet", + "ProjectTitle": "Titre du projet", + "ProjectTitlePlaceholder": "Nouveau projet", + "UsedInIssueIDs": "Utilisé dans les ID d'une issue", + "Identifier": "Identifiant", + "Import": "Importer", + "ProjectIdentifier": "Identifiant du projet", + "IdentifierExists": "L'identifiant du projet existe déjà", + "ProjectIdentifierPlaceholder": "PRJCT", + "ChooseIcon": "Choisir une icône", + "AddIssue": "Ajouter une issue", + "NewIssue": "Nouvelle issue", + "NewIssuePlaceholder": "Nouveau", + "ResumeDraft": "Reprendre le brouillon", + "SaveIssue": "Créer une issue", + "SetPriority": "Définir la priorité...", + "SetStatus": "Définir le statut...", + "SelectIssue": "Sélectionner une issue", + "Priority": "Priorité", + "NoPriority": "Aucune priorité", + "Urgent": "Urgent", + "High": "Haute", + "Medium": "Moyenne", + "Low": "Basse", + "Unassigned": "Non assigné", + "Back": "Retour", + "List": "Liste", + "NumberLabels": "{count, plural, =0 {aucune étiquette} =1 {1 étiquette} other {# étiquettes}}", + "CategoryBacklog": "Backlog", + "CategoryUnstarted": "Non commencé", + "CategoryStarted": "Commencé", + "CategoryCompleted": "Terminé", + "CategoryCanceled": "Annulé", + "Title": "Titre", + "Name": "Nom", + "Description": "Description", + "Status": "Statut", + "Number": "Numéro", + "Assignee": "Assigné à", + "AssignTo": "Assigner à...", + "AssignedTo": "Assigné à {value}", + "Parent": "Problème parent", + "SetParent": "Définir le problème parent...", + "ChangeParent": "Changer le problème parent...", + "RemoveParent": "Supprimer le problème parent", + "OpenParent": "Ouvrir le problème parent", + "SubIssues": "Sub-issue", + "SubIssuesList": "Sub-issue ({subIssues})", + "OpenSubIssues": "Ouvrir les sub-issue", + "AddSubIssues": "Ajouter un sub-issue", + "BlockedBy": "Bloqué par", + "RelatedTo": "Lié à", + "Comments": "Commentaires", + "Attachments": "Pièces jointes", + "Labels": "Étiquettes", + "Component": "Composant", + "Space": "", + "SetDueDate": "Définir la date d'échéance...", + "ChangeDueDate": "Changer la date d'échéance...", + "ModificationDate": "Mis à jour {value}", + "Project": "Projet", + "Issue": "Issue", + "SubIssue": "Sub-issue", + "Document": "", + "DocumentIcon": "", + "DocumentColor": "", + "Rank": "Rang", + "TypeIssuePriority": "Priorité de l'issue", + "IssueTitlePlaceholder": "Titre de l'issue", + "SubIssueTitlePlaceholder": "Titre de la sub-issue", + "IssueDescriptionPlaceholder": "Ajouter une description...", + "SubIssueDescriptionPlaceholder": "Ajouter une description de la sub-issue", + "AddIssueTooltip": "Ajouter une issue...", + "NewIssueDialogClose": "Voulez-vous fermer cette fenêtre ?", + "NewIssueDialogCloseNote": "Tous les changements seront perdus", + "RemoveComponentDialogClose": "Supprimer le composant ?", + "RemoveComponentDialogCloseNote": "Êtes-vous sûr de vouloir supprimer ce composant ? Cette opération est irréversible", + "Grouping": "Regroupement", + "Ordering": "Classement", + "CompletedIssues": "Issues terminées", + "NoGrouping": "Aucun regroupement", + "NoAssignee": "Non assigné", + "LastUpdated": "Dernière mise à jour", + "DueDate": "Date d'échéance", + "IssueStartDate": "Date de début", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "Manuel", + "All": "Tous", + "PastWeek": "La semaine passée", + "PastMonth": "Le mois passé", + "CopyIssueUrl": "Copier l'URL de l'issue dans le presse-papiers", + "CopyIssueId": "Copier l'ID de l'issue dans le presse-papiers", + "CopyIssueBranch": "Copier le nom de la branche Git dans le presse-papiers", + "CopyIssueTitle": "Copier le titre de l'issue dans le presse-papiers", + "AssetLabel": "Actif", + "AddToComponent": "Ajouter au composant...", + "MoveToComponent": "Déplacer vers le composant...", + "NoComponent": "Aucun composant", + "ComponentLeadTitle": "Responsable du composant", + "ComponentMembersTitle": "Membres du composant", + "ComponentLeadSearchPlaceholder": "Définir le responsable du composant...", + "ComponentMembersSearchPlaceholder": "Modifier les membres du composant...", + "MoveToProject": "Déplacer vers le projet", + "Duplicate": "Dupliquer", + "GotoIssues": "Aller aux issues", + "GotoActive": "Aller aux problèmes actifs", + "GotoBacklog": "Aller au backlog", + "GotoComponents": "Aller aux composants", + "GotoMyIssues": "Aller à mes issues", + "GotoTrackerApplication": "Passer à l'application de suivi", + "CreatedOne": "Créé", + "MoveIssues": "Déplacer les issues", + "MoveIssuesDescription": "Sélectionnez le projet vers lequel vous souhaitez déplacer les issues", + "ManageAttributes": "Gérer les attributs", + "KeepOriginalAttributes": "Conserver les attributs d'origine", + "KeepOriginalAttributesTooltip": "Les statuts et composants des problèmes d'origine seront conservés dans le nouveau projet", + "SelectReplacement": "Les éléments suivants ne sont pas disponibles dans le nouveau projet. Sélectionnez un remplacement.", + "MissingItem": "ÉLÉMENT MANQUANT", + "Replacement": "REMPLACEMENT", + "Original": "ORIGINAL", + "OriginalDescription": "Les éléments de cette section seront créés dans le nouveau projet", + "Relations": "Relations", + "RemoveRelation": "Supprimer la relation...", + "AddBlockedBy": "Marquer comme bloqué par...", + "AddIsBlocking": "Marquer comme bloquant...", + "AddRelatedIssue": "Référencer une autre issue...", + "RelatedIssue": "Issue liée {id} - {title}", + "BlockedIssue": "Issue bloquée {id} - {title}", + "BlockingIssue": "Issue bloquante {id} - {title}", + "BlockedBySearchPlaceholder": "Rechercher un problème à marquer comme bloqué par...", + "IsBlockingSearchPlaceholder": "Rechercher un problème à marquer comme bloquant...", + "RelatedIssueSearchPlaceholder": "Rechercher une issue à référencer...", + "Blocks": "Bloque", + "Related": "Lié", + "RelatedIssues": "Issues liées", + "EditIssue": "Modifier {title}", + "EditWorkflowStatuses": "Modifier les statuts des problèmes", + "EditProject": "Modifier le projet", + "DeleteProject": "Supprimer le projet", + "ArchiveProjectName": "Archiver le projet {name} ?", + "ArchiveProjectConfirm": "Voulez-vous archiver ce projet ?", + "DeleteProjectConfirm": "Voulez-vous supprimer ce projet et tous les problèmes ?", + "ProjectHasIssues": "Il y a des issues existantes dans ce projet, êtes-vous sûr de vouloir archiver ?", + "ManageWorkflowStatuses": "Gérer les types de projet", + "AddWorkflowStatus": "Ajouter un statut de problème", + "EditWorkflowStatus": "Modifier le statut du problème", + "DeleteWorkflowStatus": "Supprimer le statut du problème", + "DeleteWorkflowStatusConfirm": "Voulez-vous supprimer le statut \"{status}\" ?", + "DeleteWorkflowStatusErrorDescription": "Le statut \"{status}\" a {count, plural, =1 {1 problème} other {# problèmes}} assigné(s). Veuillez sélectionner un statut pour le déplacer", + "Save": "Enregistrer", + "IncludeItemsThatMatch": "Inclure les éléments qui correspondent", + "AnyFilter": "n'importe quel filtre", + "AllFilters": "tous les filtres", + "NoDescription": "Pas de description", + "SearchIssue": "Rechercher une tâche...", + "StatusHistory": "Historique des statuts", + "NewSubIssue": "Ajouter un sub-ssue...", + "AddLabel": "Ajouter une étiquette", + "DeleteIssue": "Supprimer {issueCount, plural, =1 {problème} other {# problèmes}}", + "DeleteIssueConfirm": "Voulez-vous supprimer {issueCount, plural, =1 {le problème} other {les problèmes}}{subIssueCount, plural, =0 {?} =1 { et le sous-problème ?} other { et les sous-problèmes ?}}", + "Milestone": "Jalon", + "NoMilestone": "Pas de jalon", + "MoveToMilestone": "Sélectionner un jalon", + "Milestones": "Jalons", + "AllMilestones": "Tous", + "PlannedMilestones": "Prévu", + "ActiveMilestones": "Actif", + "ClosedMilestones": "Terminé", + "AddToMilestone": "Ajouter au jalon", + "MilestoneNamePlaceholder": "Nom du jalon", + "NewMilestone": "Nouveau jalon", + "CreateMilestone": "Créer", + "MoveAndDeleteMilestone": "Déplacer les problèmes vers {newMilestone} et supprimer {deleteMilestone}", + "MoveAndDeleteMilestoneConfirm": "Voulez-vous supprimer le jalon et déplacer les problèmes vers un autre jalon ?", + "Estimation": "Estimation", + "ReportedTime": "Temps passé", + "RemainingTime": "Temps restant", + "TimeSpendReports": "Rapports de temps passé", + "TimeSpendReport": "Temps", + "TimeSpendReportAdd": "Ajouter un rapport de temps", + "TimeSpendReportDate": "Date", + "TimeSpendReportValue": "Temps passé", + "TimeSpendReportValueTooltip": "Temps passé en heures", + "TimeSpendReportDescription": "Description", + "TimeSpendDays": "{value}j", + "TimeSpendHours": "{value}h", + "TimeSpendMinutes": "{value}m", + "ChildEstimation": "Estimation des sous-problèmes", + "ChildReportedTime": "Temps des sous-problèmes", + "CapacityValue": "de {value}j", + "NewRelatedIssue": "Nouvelle issue liée", + "RelatedIssuesNotFound": "Issues liées non trouvés", + "AddedReference": "Référence ajoutée", + "AddedAsBlocked": "Marqué comme bloqué", + "AddedAsBlocking": "Marqué comme bloquant", + "IssueTemplate": "Modèle", + "IssueTemplates": "Modèles", + "NewProcess": "Nouveau modèle", + "SaveProcess": "Enregistrer le modèle", + "NoIssueTemplate": "Aucun modèle", + "TemplateReplace": "Voulez-vous appliquer le nouveau modèle ?", + "TemplateReplaceConfirm": "Tous les champs seront remplacés par les valeurs du nouveau modèle", + "Apply": "Appliquer", + "CurrentWorkDay": "Journée de travail actuelle", + "PreviousWorkDay": "Journée de travail précédente", + "TimeReportDayTypeLabel": "Sélectionnez le type de journée de rapport de temps", + "DefaultAssignee": "Assigné par défaut pour les problèmes", + "SevenHoursLength": "Sept heures", + "EightHoursLength": "Huit heures", + "HourLabel": "h", + "MinuteLabel": "m", + "Saved": "Enregistré...", + "CreatedIssue": "Issue créée", + "CreatedSubIssue": "Sub-issue créée", + "ChangeStatus": "Changer le statut", + "ConfigLabel": "Suivi", + "ConfigDescription": "Extension pour gérer les éléments de travail et accomplir toutes les tâches.", + "NoStatusFound": "Aucun statut correspondant trouvé", + "CreateMissingStatus": "Créer un statut manquant", + "UnsetParent": "Le problème parent sera désélectionné", + "AllProjects": "Tous les projets", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "Mis à jour par {senderName}", + "IssueNotificationChanged": "{senderName} a modifié {property}", + "IssueNotificationChangedProperty": "{senderName} a modifié {property} en \"{newValue}\"", + "IssueNotificationMessage": "{senderName} : {message}", + "PreviousAssigned": "Assigné précédemment", + "IssueAssignedToYou": "Assigné à vous", + "RelatedIssueTargetDescription": "Projet cible de l'issue liée pour la classe ou l'espace", + "MapRelatedIssues": "Configurer les projets par défaut des issues liées", + "DefaultIssueStatus": "Statut par défaut de l'issue", + "IssueStatus": "Statut", + "Extensions": "Extensions", + "UnsetParentIssue": "Désélectionner l'issue parent", + "ForbidCreateProjectPermission": "Interdire la création de projet", + "ForbidCreateProjectPermissionDescription": "Interdit aux utilisateurs de créer de nouveaux projets", + "AllowCreatingIssues": "Autoriser la création d'issues", + "Day": "Jour", + "Gantt": "Gantt", + "Month": "Mois", + "Quarter": "Trimestre", + "Week": "Semaine", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/it.json b/plugins/tracker-assets/lang/it.json index 02d01f2850c..d85bdae0d9e 100644 --- a/plugins/tracker-assets/lang/it.json +++ b/plugins/tracker-assets/lang/it.json @@ -1,287 +1,289 @@ { - "string": { - "TrackerApplication": "Tracker", - "Projects": "I tuoi progetti", - "More": "Altro", - "Default": "Predefinito", - "MakeDefault": "Imposta come predefinito", - "Delete": "Elimina", - "Open": "Apri", - "Members": "Membri", - "Inbox": "Posta in arrivo", - "MyIssues": "Le mie issues", - "ViewIssue": "Visualizza issue", - "IssueCreated": "Issue creata", - "Issues": "Issue", - "Views": "Visualizzazioni", - "Active": "Attivo", - "AllIssues": "Tutte le issue", - "ActiveIssues": "Issue attive", - "BacklogIssues": "Backlog", - "Backlog": "Backlog", - "Board": "Bacheca", - "Components": "Componenti", - "AllComponents": "Tutti", - "BacklogComponents": "Backlog", - "ActiveComponents": "Attivi", - "ClosedComponents": "Chiuso", - "NewComponent": "Nuovo componente", - "CreateComponent": "Crea componente", - "ComponentNamePlaceholder": "Nome del componente", - "ComponentDescriptionPlaceholder": "Descrizione (facoltativo)", - "ComponentLead": "Responsabile", - "ComponentMembers": "Membri", - "StartDate": "Data di inizio", - "TargetDate": "Data obiettivo", - "Planned": "Pianificato", - "InProgress": "In corso", - "Paused": "In pausa", - "Completed": "Completato", - "Canceled": "Annullato", - "CreateProject": "Crea progetto", - "NewProject": "Nuovo progetto", - "ProjectTitle": "Titolo del progetto", - "ProjectTitlePlaceholder": "Nuovo progetto", - "UsedInIssueIDs": "Utilizzato negli ID delle issue", - "Identifier": "Identificatore", - "Import": "Importa", - "ProjectIdentifier": "Identificatore del progetto", - "IdentifierExists": "L'identificatore del progetto esiste già", - "ProjectIdentifierPlaceholder": "PRJCT", - "ChooseIcon": "Scegli icona", - "AddIssue": "Aggiungi issue", - "NewIssue": "Nuova issue", - "NewIssuePlaceholder": "Nuovo", - "ResumeDraft": "Riprendi bozza", - "SaveIssue": "Crea issue", - "SetPriority": "Imposta priorità…", - "SetStatus": "Imposta stato…", - "SelectIssue": "Seleziona issue", - "Priority": "Priorità", - "NoPriority": "Nessuna priorità", - "Urgent": "Urgente", - "High": "Alto", - "Medium": "Medio", - "Low": "Basso", - "Unassigned": "Non assegnato", - "Back": "Indietro", - "List": "Elenco", - "NumberLabels": "{count, plural, =0 {nessuna etichetta} =1 {1 etichetta} other {# etichette}}", - "CategoryBacklog": "Backlog", - "CategoryUnstarted": "Non iniziato", - "CategoryStarted": "Iniziato", - "CategoryCompleted": "Completato", - "CategoryCanceled": "Annullato", - "Title": "Titolo", - "Name": "Nome", - "Description": "Descrizione", - "Status": "Stato", - "Number": "Numero", - "Assignee": "Assegnatario", - "AssignTo": "Assegna a...", - "AssignedTo": "Assegnato a {value}", - "Parent": "Issue genitore", - "SetParent": "Imposta issue genitore…", - "ChangeParent": "Cambia issue genitore…", - "RemoveParent": "Rimuovi issue genitore", - "OpenParent": "Apri issue genitore", - "SubIssues": "Subissue", - "SubIssuesList": "Subissue ({subIssues})", - "OpenSubIssues": "Apri subissue", - "AddSubIssues": "Aggiungi subissue", - "BlockedBy": "Bloccato da", - "RelatedTo": "Correlato a", - "Comments": "Commenti", - "Attachments": "Allegati", - "Labels": "Etichette", - "Component": "Componente", - "Space": "", - "SetDueDate": "Imposta data di scadenza…", - "ChangeDueDate": "Cambia data di scadenza…", - "ModificationDate": "Aggiornato {value}", - "Project": "Progetto", - "Issue": "Issue", - "SubIssue": "Subissue", - "Document": "", - "DocumentIcon": "", - "DocumentColor": "", - "Rank": "Classifica", - "TypeIssuePriority": "Priorità della issue", - "IssueTitlePlaceholder": "Titolo della issue", - "SubIssueTitlePlaceholder": "Titolo della subissue", - "IssueDescriptionPlaceholder": "Aggiungi descrizione…", - "SubIssueDescriptionPlaceholder": "Aggiungi descrizione della subissue", - "AddIssueTooltip": "Aggiungi issue...", - "NewIssueDialogClose": "Vuoi chiudere questo dialogo?", - "NewIssueDialogCloseNote": "Tutte le modifiche andranno perse", - "RemoveComponentDialogClose": "Eliminare il componente?", - "RemoveComponentDialogCloseNote": "Sei sicuro di voler eliminare questo componente? Questa operazione non può essere annullata", - "Grouping": "Raggruppamento", - "Ordering": "Ordinamento", - "CompletedIssues": "Issue completate", - "NoGrouping": "Nessun raggruppamento", - "NoAssignee": "Nessun assegnatario", - "LastUpdated": "Ultimo aggiornamento", - "DueDate": "Data di scadenza", - "IssueStartDate": "Data di inizio", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "Manuale", - "All": "Tutti", - "PastWeek": "Settimana scorsa", - "PastMonth": "Mese scorso", - "CopyIssueUrl": "Copia URL della issue negli appunti", - "CopyIssueId": "Copia ID della issue negli appunti", - "CopyIssueBranch": "Copia nome del ramo Git negli appunti", - "CopyIssueTitle": "Copia titolo della issue negli appunti", - "AssetLabel": "Risorsa", - "AddToComponent": "Aggiungi al componente…", - "MoveToComponent": "Sposta nel componente…", - "NoComponent": "Nessun componente", - "ComponentLeadTitle": "Responsabile del componente", - "ComponentMembersTitle": "Membri del componente", - "ComponentLeadSearchPlaceholder": "Imposta responsabile del componente…", - "ComponentMembersSearchPlaceholder": "Cambia membri del componente…", - "MoveToProject": "Sposta nel progetto", - "Duplicate": "Duplicato", - "GotoIssues": "Vai alle issue", - "GotoActive": "Vai alle issue attive", - "GotoBacklog": "Vai al backlog", - "GotoComponents": "Vai ai componenti", - "GotoMyIssues": "Vai alle mie issue", - "GotoTrackerApplication": "Passa all'applicazione Tracker", - "CreatedOne": "Creato", - "MoveIssues": "Sposta issue", - "MoveIssuesDescription": "Seleziona il progetto in cui desideri spostare le issue", - "ManageAttributes": "Gestisci attributi", - "KeepOriginalAttributes": "Mantieni attributi originali", - "KeepOriginalAttributesTooltip": "Gli stati e i componenti originali delle issue saranno mantenuti nel nuovo progetto", - "SelectReplacement": "I seguenti elementi non sono disponibili nel nuovo progetto. Seleziona un sostituto.", - "MissingItem": "ELEMENTO MANCANTE", - "Replacement": "SOSTITUTO", - "Original": "ORIGINALE", - "OriginalDescription": "Gli elementi di questa sezione verranno creati nel nuovo progetto", - "Relations": "Relazioni", - "RemoveRelation": "Rimuovi relazione...", - "AddBlockedBy": "Segna come bloccato da...", - "AddIsBlocking": "Segna come bloccante...", - "AddRelatedIssue": "Riferisci un'altra issue...", - "RelatedIssue": "Issue correlato {id} - {title}", - "BlockedIssue": "Issue bloccato {id} - {title}", - "BlockingIssue": "Issue bloccante {id} - {title}", - "BlockedBySearchPlaceholder": "Cerca una issue da contrassegnare come bloccato da...", - "IsBlockingSearchPlaceholder": "Cerca una issue da contrassegnare come bloccante...", - "RelatedIssueSearchPlaceholder": "Cerca una issue da riferire...", - "Blocks": "Blocca", - "Related": "Correlato", - "RelatedIssues": "Issue correlate", - "EditIssue": "Modifica {title}", - "EditWorkflowStatuses": "Modifica stati della issue", - "EditProject": "Modifica progetto", - "DeleteProject": "Elimina progetto", - "ArchiveProjectName": "Archivia progetto {name}?", - "ArchiveProjectConfirm": "Vuoi archiviare questo progetto?", - "DeleteProjectConfirm": "Vuoi eliminare questo progetto e tutte le issue?", - "ProjectHasIssues": "Ci sono issue esistenti in questo progetto, sei sicuro di voler archiviare?", - "ManageWorkflowStatuses": "Gestisci tipi di progetto", - "AddWorkflowStatus": "Aggiungi stato della issue", - "EditWorkflowStatus": "Modifica stato della issue", - "DeleteWorkflowStatus": "Elimina stato della issue", - "DeleteWorkflowStatusConfirm": "Vuoi eliminare lo stato \"{status}\"?", - "DeleteWorkflowStatusErrorDescription": "Lo stato \"{status}\" ha {count, plural, =1 {1 issue} other {# issue}} assegnate. Seleziona uno stato per spostare", - "Save": "Salva", - "IncludeItemsThatMatch": "Includi elementi che corrispondono", - "AnyFilter": "qualunque filtro", - "AllFilters": "tutti i filtri", - "NoDescription": "Nessuna descrizione", - "SearchIssue": "Cerca attività...", - "StatusHistory": "Storia degli stati", - "NewSubIssue": "Aggiungi subissue...", - "AddLabel": "Aggiungi etichetta", - "DeleteIssue": "Elimina {issueCount, plural, =1 {issue} other {# issue}}", - "DeleteIssueConfirm": "Vuoi eliminare {issueCount, plural, =1 {issue} other {issue}}{subIssueCount, plural, =0 {?} =1 { e subissue?} other { e subissue?}}", - "Milestone": "Traguardo", - "NoMilestone": "Nessun traguardo", - "MoveToMilestone": "Seleziona traguardo", - "Milestones": "Traguardi", - "AllMilestones": "Tutti", - "PlannedMilestones": "Pianificati", - "ActiveMilestones": "Attivi", - "ClosedMilestones": "Fatto", - "AddToMilestone": "Aggiungi al traguardo", - "MilestoneNamePlaceholder": "Nome del traguardo", - "NewMilestone": "Nuovo traguardo", - "CreateMilestone": "Crea", - "MoveAndDeleteMilestone": "Sposta problemi in {newMilestone} ed elimina {deleteMilestone}", - "MoveAndDeleteMilestoneConfirm": "Vuoi eliminare il traguardo e spostare i problemi in un altro traguardo?", - "Estimation": "Stima", - "ReportedTime": "Tempo trascorso", - "RemainingTime": "Tempo rimanente", - "TimeSpendReports": "Rapporti sul tempo trascorso", - "TimeSpendReport": "Tempo", - "TimeSpendReportAdd": "Aggiungi rapporto sul tempo", - "TimeSpendReportDate": "Data", - "TimeSpendReportValue": "Tempo trascorso", - "TimeSpendReportValueTooltip": "Tempo trascorso in ore", - "TimeSpendReportDescription": "Descrizione", - "TimeSpendDays": "{value}d", - "TimeSpendHours": "{value}h", - "TimeSpendMinutes": "{value}m", - "ChildEstimation": "Stima delle subissue", - "ChildReportedTime": "Tempo delle subissue", - "CapacityValue": "di {value}d", - "NewRelatedIssue": "Nuova issue correlata", - "RelatedIssuesNotFound": "Issue correlate non trovate", - "AddedReference": "Riferimento aggiunto", - "AddedAsBlocked": "Contrassegnato come bloccato", - "AddedAsBlocking": "Contrassegnato come bloccante", - "IssueTemplate": "Modello", - "IssueTemplates": "Modelli", - "NewProcess": "Nuovo modello", - "SaveProcess": "Salva modello", - "NoIssueTemplate": "Nessun modello", - "TemplateReplace": "Vuoi applicare il nuovo modello?", - "TemplateReplaceConfirm": "Tutti i campi saranno sovrascritti dai valori del nuovo modello", - "Apply": "Applica", - "CurrentWorkDay": "Giorno lavorativo corrente", - "PreviousWorkDay": "Giorno lavorativo precedente", - "TimeReportDayTypeLabel": "Seleziona il tipo di giorno", - "DefaultAssignee": "Assegnatario predefinito per le issue", - "SevenHoursLength": "Sette ore", - "EightHoursLength": "Otto ore", - "HourLabel": "h", - "MinuteLabel": "m", - "Saved": "Salvato...", - "CreatedIssue": "Issue creata", - "CreatedSubIssue": "Subissue creata", - "ChangeStatus": "Cambia stato", - "ConfigLabel": "Tracker", - "ConfigDescription": "Estensione per gestire elementi di lavoro e completare tutte le attività.", - "NoStatusFound": "Nessuno stato corrispondente trovato", - "CreateMissingStatus": "Crea stato mancante", - "UnsetParent": "La issue genitore sarà annullato", - "AllProjects": "Tutti i progetti", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "Aggiornato da {senderName}", - "IssueNotificationChanged": "{senderName} ha modificato {property}", - "IssueNotificationChangedProperty": "{senderName} ha modificato {property} in \"{newValue}\"", - "IssueNotificationMessage": "{senderName}: {message}", - "PreviousAssigned": "Assegnato in precedenza", - "IssueAssignedToYou": "Assegnato a te", - "RelatedIssueTargetDescription": "Obiettivo del progetto del problema correlato per Classe o Spazio", - "MapRelatedIssues": "Configura i progetti predefiniti per issue correlate", - "DefaultIssueStatus": "Stato predefinito della issue", - "IssueStatus": "Stato", - "Extensions": "Estensioni", - "UnsetParentIssue": "Annulla l'issue genitore", - "ForbidCreateProjectPermission": "Vieta creazione progetto", - "ForbidCreateProjectPermissionDescription": "Vieta agli utenti di creare nuovi progetti", - "AllowCreatingIssues": "Consenti la creazione di issue", - "Day": "Giorno", - "Gantt": "Gantt", - "Month": "Mese", - "Quarter": "Trimestre", - "Week": "Settimana" - }, - "status": {} -} + "string": { + "TrackerApplication": "Tracker", + "Projects": "I tuoi progetti", + "More": "Altro", + "Default": "Predefinito", + "MakeDefault": "Imposta come predefinito", + "Delete": "Elimina", + "Open": "Apri", + "Members": "Membri", + "Inbox": "Posta in arrivo", + "MyIssues": "Le mie issues", + "ViewIssue": "Visualizza issue", + "IssueCreated": "Issue creata", + "Issues": "Issue", + "Views": "Visualizzazioni", + "Active": "Attivo", + "AllIssues": "Tutte le issue", + "ActiveIssues": "Issue attive", + "BacklogIssues": "Backlog", + "Backlog": "Backlog", + "Board": "Bacheca", + "Components": "Componenti", + "AllComponents": "Tutti", + "BacklogComponents": "Backlog", + "ActiveComponents": "Attivi", + "ClosedComponents": "Chiuso", + "NewComponent": "Nuovo componente", + "CreateComponent": "Crea componente", + "ComponentNamePlaceholder": "Nome del componente", + "ComponentDescriptionPlaceholder": "Descrizione (facoltativo)", + "ComponentLead": "Responsabile", + "ComponentMembers": "Membri", + "StartDate": "Data di inizio", + "TargetDate": "Data obiettivo", + "Planned": "Pianificato", + "InProgress": "In corso", + "Paused": "In pausa", + "Completed": "Completato", + "Canceled": "Annullato", + "CreateProject": "Crea progetto", + "NewProject": "Nuovo progetto", + "ProjectTitle": "Titolo del progetto", + "ProjectTitlePlaceholder": "Nuovo progetto", + "UsedInIssueIDs": "Utilizzato negli ID delle issue", + "Identifier": "Identificatore", + "Import": "Importa", + "ProjectIdentifier": "Identificatore del progetto", + "IdentifierExists": "L'identificatore del progetto esiste già", + "ProjectIdentifierPlaceholder": "PRJCT", + "ChooseIcon": "Scegli icona", + "AddIssue": "Aggiungi issue", + "NewIssue": "Nuova issue", + "NewIssuePlaceholder": "Nuovo", + "ResumeDraft": "Riprendi bozza", + "SaveIssue": "Crea issue", + "SetPriority": "Imposta priorità…", + "SetStatus": "Imposta stato…", + "SelectIssue": "Seleziona issue", + "Priority": "Priorità", + "NoPriority": "Nessuna priorità", + "Urgent": "Urgente", + "High": "Alto", + "Medium": "Medio", + "Low": "Basso", + "Unassigned": "Non assegnato", + "Back": "Indietro", + "List": "Elenco", + "NumberLabels": "{count, plural, =0 {nessuna etichetta} =1 {1 etichetta} other {# etichette}}", + "CategoryBacklog": "Backlog", + "CategoryUnstarted": "Non iniziato", + "CategoryStarted": "Iniziato", + "CategoryCompleted": "Completato", + "CategoryCanceled": "Annullato", + "Title": "Titolo", + "Name": "Nome", + "Description": "Descrizione", + "Status": "Stato", + "Number": "Numero", + "Assignee": "Assegnatario", + "AssignTo": "Assegna a...", + "AssignedTo": "Assegnato a {value}", + "Parent": "Issue genitore", + "SetParent": "Imposta issue genitore…", + "ChangeParent": "Cambia issue genitore…", + "RemoveParent": "Rimuovi issue genitore", + "OpenParent": "Apri issue genitore", + "SubIssues": "Subissue", + "SubIssuesList": "Subissue ({subIssues})", + "OpenSubIssues": "Apri subissue", + "AddSubIssues": "Aggiungi subissue", + "BlockedBy": "Bloccato da", + "RelatedTo": "Correlato a", + "Comments": "Commenti", + "Attachments": "Allegati", + "Labels": "Etichette", + "Component": "Componente", + "Space": "", + "SetDueDate": "Imposta data di scadenza…", + "ChangeDueDate": "Cambia data di scadenza…", + "ModificationDate": "Aggiornato {value}", + "Project": "Progetto", + "Issue": "Issue", + "SubIssue": "Subissue", + "Document": "", + "DocumentIcon": "", + "DocumentColor": "", + "Rank": "Classifica", + "TypeIssuePriority": "Priorità della issue", + "IssueTitlePlaceholder": "Titolo della issue", + "SubIssueTitlePlaceholder": "Titolo della subissue", + "IssueDescriptionPlaceholder": "Aggiungi descrizione…", + "SubIssueDescriptionPlaceholder": "Aggiungi descrizione della subissue", + "AddIssueTooltip": "Aggiungi issue...", + "NewIssueDialogClose": "Vuoi chiudere questo dialogo?", + "NewIssueDialogCloseNote": "Tutte le modifiche andranno perse", + "RemoveComponentDialogClose": "Eliminare il componente?", + "RemoveComponentDialogCloseNote": "Sei sicuro di voler eliminare questo componente? Questa operazione non può essere annullata", + "Grouping": "Raggruppamento", + "Ordering": "Ordinamento", + "CompletedIssues": "Issue completate", + "NoGrouping": "Nessun raggruppamento", + "NoAssignee": "Nessun assegnatario", + "LastUpdated": "Ultimo aggiornamento", + "DueDate": "Data di scadenza", + "IssueStartDate": "Data di inizio", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "Manuale", + "All": "Tutti", + "PastWeek": "Settimana scorsa", + "PastMonth": "Mese scorso", + "CopyIssueUrl": "Copia URL della issue negli appunti", + "CopyIssueId": "Copia ID della issue negli appunti", + "CopyIssueBranch": "Copia nome del ramo Git negli appunti", + "CopyIssueTitle": "Copia titolo della issue negli appunti", + "AssetLabel": "Risorsa", + "AddToComponent": "Aggiungi al componente…", + "MoveToComponent": "Sposta nel componente…", + "NoComponent": "Nessun componente", + "ComponentLeadTitle": "Responsabile del componente", + "ComponentMembersTitle": "Membri del componente", + "ComponentLeadSearchPlaceholder": "Imposta responsabile del componente…", + "ComponentMembersSearchPlaceholder": "Cambia membri del componente…", + "MoveToProject": "Sposta nel progetto", + "Duplicate": "Duplicato", + "GotoIssues": "Vai alle issue", + "GotoActive": "Vai alle issue attive", + "GotoBacklog": "Vai al backlog", + "GotoComponents": "Vai ai componenti", + "GotoMyIssues": "Vai alle mie issue", + "GotoTrackerApplication": "Passa all'applicazione Tracker", + "CreatedOne": "Creato", + "MoveIssues": "Sposta issue", + "MoveIssuesDescription": "Seleziona il progetto in cui desideri spostare le issue", + "ManageAttributes": "Gestisci attributi", + "KeepOriginalAttributes": "Mantieni attributi originali", + "KeepOriginalAttributesTooltip": "Gli stati e i componenti originali delle issue saranno mantenuti nel nuovo progetto", + "SelectReplacement": "I seguenti elementi non sono disponibili nel nuovo progetto. Seleziona un sostituto.", + "MissingItem": "ELEMENTO MANCANTE", + "Replacement": "SOSTITUTO", + "Original": "ORIGINALE", + "OriginalDescription": "Gli elementi di questa sezione verranno creati nel nuovo progetto", + "Relations": "Relazioni", + "RemoveRelation": "Rimuovi relazione...", + "AddBlockedBy": "Segna come bloccato da...", + "AddIsBlocking": "Segna come bloccante...", + "AddRelatedIssue": "Riferisci un'altra issue...", + "RelatedIssue": "Issue correlato {id} - {title}", + "BlockedIssue": "Issue bloccato {id} - {title}", + "BlockingIssue": "Issue bloccante {id} - {title}", + "BlockedBySearchPlaceholder": "Cerca una issue da contrassegnare come bloccato da...", + "IsBlockingSearchPlaceholder": "Cerca una issue da contrassegnare come bloccante...", + "RelatedIssueSearchPlaceholder": "Cerca una issue da riferire...", + "Blocks": "Blocca", + "Related": "Correlato", + "RelatedIssues": "Issue correlate", + "EditIssue": "Modifica {title}", + "EditWorkflowStatuses": "Modifica stati della issue", + "EditProject": "Modifica progetto", + "DeleteProject": "Elimina progetto", + "ArchiveProjectName": "Archivia progetto {name}?", + "ArchiveProjectConfirm": "Vuoi archiviare questo progetto?", + "DeleteProjectConfirm": "Vuoi eliminare questo progetto e tutte le issue?", + "ProjectHasIssues": "Ci sono issue esistenti in questo progetto, sei sicuro di voler archiviare?", + "ManageWorkflowStatuses": "Gestisci tipi di progetto", + "AddWorkflowStatus": "Aggiungi stato della issue", + "EditWorkflowStatus": "Modifica stato della issue", + "DeleteWorkflowStatus": "Elimina stato della issue", + "DeleteWorkflowStatusConfirm": "Vuoi eliminare lo stato \"{status}\"?", + "DeleteWorkflowStatusErrorDescription": "Lo stato \"{status}\" ha {count, plural, =1 {1 issue} other {# issue}} assegnate. Seleziona uno stato per spostare", + "Save": "Salva", + "IncludeItemsThatMatch": "Includi elementi che corrispondono", + "AnyFilter": "qualunque filtro", + "AllFilters": "tutti i filtri", + "NoDescription": "Nessuna descrizione", + "SearchIssue": "Cerca attività...", + "StatusHistory": "Storia degli stati", + "NewSubIssue": "Aggiungi subissue...", + "AddLabel": "Aggiungi etichetta", + "DeleteIssue": "Elimina {issueCount, plural, =1 {issue} other {# issue}}", + "DeleteIssueConfirm": "Vuoi eliminare {issueCount, plural, =1 {issue} other {issue}}{subIssueCount, plural, =0 {?} =1 { e subissue?} other { e subissue?}}", + "Milestone": "Traguardo", + "NoMilestone": "Nessun traguardo", + "MoveToMilestone": "Seleziona traguardo", + "Milestones": "Traguardi", + "AllMilestones": "Tutti", + "PlannedMilestones": "Pianificati", + "ActiveMilestones": "Attivi", + "ClosedMilestones": "Fatto", + "AddToMilestone": "Aggiungi al traguardo", + "MilestoneNamePlaceholder": "Nome del traguardo", + "NewMilestone": "Nuovo traguardo", + "CreateMilestone": "Crea", + "MoveAndDeleteMilestone": "Sposta problemi in {newMilestone} ed elimina {deleteMilestone}", + "MoveAndDeleteMilestoneConfirm": "Vuoi eliminare il traguardo e spostare i problemi in un altro traguardo?", + "Estimation": "Stima", + "ReportedTime": "Tempo trascorso", + "RemainingTime": "Tempo rimanente", + "TimeSpendReports": "Rapporti sul tempo trascorso", + "TimeSpendReport": "Tempo", + "TimeSpendReportAdd": "Aggiungi rapporto sul tempo", + "TimeSpendReportDate": "Data", + "TimeSpendReportValue": "Tempo trascorso", + "TimeSpendReportValueTooltip": "Tempo trascorso in ore", + "TimeSpendReportDescription": "Descrizione", + "TimeSpendDays": "{value}d", + "TimeSpendHours": "{value}h", + "TimeSpendMinutes": "{value}m", + "ChildEstimation": "Stima delle subissue", + "ChildReportedTime": "Tempo delle subissue", + "CapacityValue": "di {value}d", + "NewRelatedIssue": "Nuova issue correlata", + "RelatedIssuesNotFound": "Issue correlate non trovate", + "AddedReference": "Riferimento aggiunto", + "AddedAsBlocked": "Contrassegnato come bloccato", + "AddedAsBlocking": "Contrassegnato come bloccante", + "IssueTemplate": "Modello", + "IssueTemplates": "Modelli", + "NewProcess": "Nuovo modello", + "SaveProcess": "Salva modello", + "NoIssueTemplate": "Nessun modello", + "TemplateReplace": "Vuoi applicare il nuovo modello?", + "TemplateReplaceConfirm": "Tutti i campi saranno sovrascritti dai valori del nuovo modello", + "Apply": "Applica", + "CurrentWorkDay": "Giorno lavorativo corrente", + "PreviousWorkDay": "Giorno lavorativo precedente", + "TimeReportDayTypeLabel": "Seleziona il tipo di giorno", + "DefaultAssignee": "Assegnatario predefinito per le issue", + "SevenHoursLength": "Sette ore", + "EightHoursLength": "Otto ore", + "HourLabel": "h", + "MinuteLabel": "m", + "Saved": "Salvato...", + "CreatedIssue": "Issue creata", + "CreatedSubIssue": "Subissue creata", + "ChangeStatus": "Cambia stato", + "ConfigLabel": "Tracker", + "ConfigDescription": "Estensione per gestire elementi di lavoro e completare tutte le attività.", + "NoStatusFound": "Nessuno stato corrispondente trovato", + "CreateMissingStatus": "Crea stato mancante", + "UnsetParent": "La issue genitore sarà annullato", + "AllProjects": "Tutti i progetti", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "Aggiornato da {senderName}", + "IssueNotificationChanged": "{senderName} ha modificato {property}", + "IssueNotificationChangedProperty": "{senderName} ha modificato {property} in \"{newValue}\"", + "IssueNotificationMessage": "{senderName}: {message}", + "PreviousAssigned": "Assegnato in precedenza", + "IssueAssignedToYou": "Assegnato a te", + "RelatedIssueTargetDescription": "Obiettivo del progetto del problema correlato per Classe o Spazio", + "MapRelatedIssues": "Configura i progetti predefiniti per issue correlate", + "DefaultIssueStatus": "Stato predefinito della issue", + "IssueStatus": "Stato", + "Extensions": "Estensioni", + "UnsetParentIssue": "Annulla l'issue genitore", + "ForbidCreateProjectPermission": "Vieta creazione progetto", + "ForbidCreateProjectPermissionDescription": "Vieta agli utenti di creare nuovi progetti", + "AllowCreatingIssues": "Consenti la creazione di issue", + "Day": "Giorno", + "Gantt": "Gantt", + "Month": "Mese", + "Quarter": "Trimestre", + "Week": "Settimana", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/ja.json b/plugins/tracker-assets/lang/ja.json index 61b13474fd4..d522504f663 100644 --- a/plugins/tracker-assets/lang/ja.json +++ b/plugins/tracker-assets/lang/ja.json @@ -1,287 +1,289 @@ { - "string": { - "TrackerApplication": "トラッカー", - "Projects": "あなたのプロジェクト", - "More": "その他", - "Default": "デフォルト", - "MakeDefault": "デフォルトに設定", - "Delete": "削除", - "Open": "開く", - "Members": "メンバー", - "Inbox": "受信箱", - "MyIssues": "自分のイシュー", - "ViewIssue": "イシューを表示", - "IssueCreated": "イシューが作成されました", - "Issues": "イシュー", - "Views": "ビュー", - "Active": "アクティブ", - "AllIssues": "すべてのイシュー", - "ActiveIssues": "アクティブなイシュー", - "BacklogIssues": "バックログ", - "Backlog": "バックログ", - "Board": "ボード", - "Components": "コンポーネント", - "AllComponents": "すべて", - "BacklogComponents": "バックログ", - "ActiveComponents": "アクティブ", - "ClosedComponents": "完了", - "NewComponent": "新しいコンポーネント", - "CreateComponent": "コンポーネントを作成", - "ComponentNamePlaceholder": "コンポーネント名", - "ComponentDescriptionPlaceholder": "説明 (オプション)", - "ComponentLead": "リーダー", - "ComponentMembers": "メンバー", - "StartDate": "開始日", - "TargetDate": "目標日", - "Planned": "計画済み", - "InProgress": "進行中", - "Paused": "一時停止", - "Completed": "完了", - "Canceled": "キャンセル", - "CreateProject": "プロジェクトを作成", - "NewProject": "新しいプロジェクト", - "ProjectTitle": "プロジェクトタイトル", - "ProjectTitlePlaceholder": "新しいプロジェクト", - "UsedInIssueIDs": "イシューIDで使用", - "Identifier": "識別子", - "Import": "インポート", - "ProjectIdentifier": "プロジェクト識別子", - "IdentifierExists": "プロジェクト識別子はすでに存在します", - "ProjectIdentifierPlaceholder": "PRJCT", - "ChooseIcon": "アイコンを選択", - "AddIssue": "イシューを追加", - "NewIssue": "新しいイシュー", - "NewIssuePlaceholder": "新規", - "ResumeDraft": "下書きを再開", - "SaveIssue": "イシューを作成", - "SetPriority": "優先度を設定\u2026", - "SetStatus": "ステータスを設定\u2026", - "SelectIssue": "イシューを選択", - "Priority": "優先度", - "NoPriority": "優先度なし", - "Urgent": "緊急", - "High": "高", - "Medium": "中", - "Low": "低", - "Unassigned": "未割り当て", - "Back": "戻る", - "List": "リスト", - "NumberLabels": "{count, plural, =0 {ラベルなし} =1 {1 ラベル} other {# ラベル}}", - "CategoryBacklog": "バックログ", - "CategoryUnstarted": "未着手", - "CategoryStarted": "開始", - "CategoryCompleted": "完了", - "CategoryCanceled": "キャンセル", - "Title": "タイトル", - "Name": "名前", - "Description": "説明", - "Status": "ステータス", - "Number": "番号", - "Assignee": "担当者", - "AssignTo": "担当者を設定...", - "AssignedTo": "{value} に割り当て", - "Parent": "親イシュー", - "SetParent": "親イシューを設定\u2026", - "ChangeParent": "親イシューを変更\u2026", - "RemoveParent": "親イシューを削除", - "OpenParent": "親イシューを開く", - "SubIssues": "子イシュー", - "SubIssuesList": "子イシュー ({subIssues})", - "OpenSubIssues": "子イシューを開く", - "AddSubIssues": "子イシューを追加", - "BlockedBy": "ブロックされている", - "RelatedTo": "関連する", - "Comments": "コメント", - "Attachments": "添付ファイル", - "Labels": "ラベル", - "Component": "コンポーネント", - "Space": "", - "SetDueDate": "期日を設定\u2026", - "ChangeDueDate": "期日を変更\u2026", - "ModificationDate": "更新日 {value}", - "Project": "プロジェクト", - "Issue": "イシュー", - "SubIssue": "子イシュー", - "Document": "", - "DocumentIcon": "", - "DocumentColor": "", - "Rank": "ランク", - "TypeIssuePriority": "イシューの優先度", - "IssueTitlePlaceholder": "イシューのタイトル", - "SubIssueTitlePlaceholder": "子イシューのタイトル", - "IssueDescriptionPlaceholder": "説明を追加\u2026", - "SubIssueDescriptionPlaceholder": "子イシューの説明を追加", - "AddIssueTooltip": "イシューを追加...", - "NewIssueDialogClose": "このダイアログを閉じますか?", - "NewIssueDialogCloseNote": "すべての変更は失われます", - "RemoveComponentDialogClose": "コンポーネントを削除しますか?", - "RemoveComponentDialogCloseNote": "本当にこのコンポーネントを削除しますか?この操作は元に戻せません", - "Grouping": "グループ化", - "Ordering": "順序", - "CompletedIssues": "完了したイシュー", - "NoGrouping": "グループ化なし", - "NoAssignee": "担当者なし", - "LastUpdated": "最終更新日", - "DueDate": "期日", - "IssueStartDate": "開始日", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "手動", - "All": "すべて", - "PastWeek": "先週", - "PastMonth": "先月", - "CopyIssueUrl": "イシューのURLをクリップボードにコピー", - "CopyIssueId": "イシューIDをクリップボードにコピー", - "CopyIssueBranch": "Gitブランチ名をクリップボードにコピー", - "CopyIssueTitle": "イシューのタイトルをクリップボードにコピー", - "AssetLabel": "アセット", - "AddToComponent": "コンポーネントに追加\u2026", - "MoveToComponent": "コンポーネントに移動\u2026", - "NoComponent": "コンポーネントなし", - "ComponentLeadTitle": "コンポーネントリーダー", - "ComponentMembersTitle": "コンポーネントメンバー", - "ComponentLeadSearchPlaceholder": "コンポーネントリーダーを設定\u2026", - "ComponentMembersSearchPlaceholder": "コンポーネントメンバーを変更\u2026", - "MoveToProject": "プロジェクトに移動", - "Duplicate": "複製", - "GotoIssues": "イシューへ移動", - "GotoActive": "アクティブなイシューへ移動", - "GotoBacklog": "バックログへ移動", - "GotoComponents": "コンポーネントへ移動", - "GotoMyIssues": "自分のイシューへ移動", - "GotoTrackerApplication": "トラッカーアプリケーションに切り替え", - "CreatedOne": "作成済み", - "MoveIssues": "イシューを移動", - "MoveIssuesDescription": "イシューを移動するプロジェクトを選択します", - "ManageAttributes": "属性を管理", - "KeepOriginalAttributes": "元の属性を保持", - "KeepOriginalAttributesTooltip": "元のイシューステータスとコンポーネントは新しいプロジェクトに保持されます", - "SelectReplacement": "次のアイテムは新しいプロジェクトで利用できません。代替を選択してください。", - "MissingItem": "不足しているアイテム", - "Replacement": "代替", - "Original": "オリジナル", - "OriginalDescription": "このセクションのアイテムは新しいプロジェクトで作成されます", - "Relations": "関連", - "RemoveRelation": "関連を削除...", - "AddBlockedBy": "ブロックされているとしてマーク...", - "AddIsBlocking": "ブロックしているとしてマーク...", - "AddRelatedIssue": "別のイシューを参照...", - "RelatedIssue": "関連するイシュー {id} - {title}", - "BlockedIssue": "ブロックされているイシュー {id} - {title}", - "BlockingIssue": "ブロックしているイシュー {id} - {title}", - "BlockedBySearchPlaceholder": "ブロックされているとしてマークするイシューを検索...", - "IsBlockingSearchPlaceholder": "ブロックしているとしてマークするイシューを検索...", - "RelatedIssueSearchPlaceholder": "参照するイシューを検索...", - "Blocks": "ブロック", - "Related": "関連", - "RelatedIssues": "関連するイシュー", - "EditIssue": "{title} を編集", - "EditWorkflowStatuses": "イシューステータスを編集", - "EditProject": "プロジェクトを編集", - "DeleteProject": "プロジェクトを削除", - "ArchiveProjectName": "プロジェクト {name} をアーカイブしますか?", - "ArchiveProjectConfirm": "このプロジェクトをアーカイブしますか?", - "DeleteProjectConfirm": "このプロジェクトとすべてのイシューを削除しますか?", - "ProjectHasIssues": "このプロジェクトには既存のイシューがあります。アーカイブしてもよろしいですか?", - "ManageWorkflowStatuses": "プロジェクトタイプを管理", - "AddWorkflowStatus": "イシューステータスを追加", - "EditWorkflowStatus": "イシューステータスを編集", - "DeleteWorkflowStatus": "イシューステータスを削除", - "DeleteWorkflowStatusConfirm": "\"{status}\" ステータスを削除しますか?", - "DeleteWorkflowStatusErrorDescription": "\"{status}\" ステータスには {count, plural, =1 {1 イシュー} other {# イシュー}} が割り当てられています。移動先のステータスを選択してください", - "Save": "保存", - "IncludeItemsThatMatch": "一致するアイテムを含める", - "AnyFilter": "いずれかのフィルター", - "AllFilters": "すべてのフィルター", - "NoDescription": "説明なし", - "SearchIssue": "タスクを検索...", - "StatusHistory": "状態履歴", - "NewSubIssue": "子イシューを追加...", - "AddLabel": "ラベルを追加", - "DeleteIssue": "{issueCount, plural, =1 {イシュー} other {イシュー}} を削除", - "DeleteIssueConfirm": "{issueCount, plural, =1 {イシュー} other {イシュー}}{subIssueCount, plural, =0 {?} =1 { と子イシュー?} other { と子イシュー?}} を削除しますか?", - "Milestone": "マイルストーン", - "NoMilestone": "マイルストーンなし", - "MoveToMilestone": "マイルストーンを選択", - "Milestones": "マイルストーン", - "AllMilestones": "すべて", - "PlannedMilestones": "計画済み", - "ActiveMilestones": "アクティブ", - "ClosedMilestones": "完了", - "AddToMilestone": "マイルストーンに追加", - "MilestoneNamePlaceholder": "マイルストーン名", - "NewMilestone": "新しいマイルストーン", - "CreateMilestone": "作成", - "MoveAndDeleteMilestone": "イシューを {newMilestone} に移動し、{deleteMilestone} を削除", - "MoveAndDeleteMilestoneConfirm": "マイルストーンを削除し、イシューを別のマイルストーンに移動しますか?", - "Estimation": "見積もり", - "ReportedTime": "消費時間", - "RemainingTime": "残り時間", - "TimeSpendReports": "時間消費レポート", - "TimeSpendReport": "時間", - "TimeSpendReportAdd": "時間レポートを追加", - "TimeSpendReportDate": "日付", - "TimeSpendReportValue": "消費時間", - "TimeSpendReportValueTooltip": "消費時間 (時間)", - "TimeSpendReportDescription": "説明", - "TimeSpendDays": "{value}日", - "TimeSpendHours": "{value}時間", - "TimeSpendMinutes": "{value}分", - "ChildEstimation": "子イシューの見積もり", - "ChildReportedTime": "子イシューの時間", - "CapacityValue": "うち {value}日", - "NewRelatedIssue": "新しい関連イシュー", - "RelatedIssuesNotFound": "関連イシューが見つかりません", - "AddedReference": "参照を追加", - "AddedAsBlocked": "ブロックされているとしてマーク", - "AddedAsBlocking": "ブロックしているとしてマーク", - "IssueTemplate": "テンプレート", - "IssueTemplates": "テンプレート", - "NewProcess": "新しいテンプレート", - "SaveProcess": "テンプレートを保存", - "NoIssueTemplate": "テンプレートなし", - "TemplateReplace": "新しいテンプレートを適用しますか?", - "TemplateReplaceConfirm": "すべてのフィールドが新しいテンプレートの値で上書きされます", - "Apply": "適用", - "CurrentWorkDay": "現在の営業日", - "PreviousWorkDay": "前の営業日", - "TimeReportDayTypeLabel": "時間レポートの日種別を選択", - "DefaultAssignee": "イシューのデフォルトの担当者", - "SevenHoursLength": "7時間", - "EightHoursLength": "8時間", - "HourLabel": "時間", - "MinuteLabel": "分", - "Saved": "保存済み...", - "CreatedIssue": "イシューを作成しました", - "CreatedSubIssue": "子イシューを作成しました", - "ChangeStatus": "ステータスを変更", - "ConfigLabel": "トラッカー", - "ConfigDescription": "作業項目を管理し、すべてのジョブを完了するための拡張機能。", - "NoStatusFound": "一致するステータスが見つかりません", - "CreateMissingStatus": "不足しているステータスを作成", - "UnsetParent": "親イシューが設定されなくなります", - "AllProjects": "すべてのプロジェクト", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "{senderName} が更新しました", - "IssueNotificationChanged": "{senderName} が {property} を変更しました", - "IssueNotificationChangedProperty": "{senderName} が {property} を \"{newValue}\" に変更しました", - "IssueNotificationMessage": "{senderName}: {message}", - "PreviousAssigned": "以前に割り当てられた", - "IssueAssignedToYou": "あなたに割り当てられました", - "RelatedIssueTargetDescription": "クラスまたはスペースの関連イシュープロジェクトターゲット", - "MapRelatedIssues": "関連イシューのデフォルトプロジェクトを構成", - "DefaultIssueStatus": "デフォルトのイシューステータス", - "IssueStatus": "ステータス", - "Extensions": "拡張機能", - "UnsetParentIssue": "親イシューの設定を解除", - "ForbidCreateProjectPermission": "プロジェクト作成禁止", - "ForbidCreateProjectPermissionDescription": "ユーザーが新しいプロジェクトを作成することを禁止します", - "AllowCreatingIssues": "イシューの作成を許可", - "Day": "日", - "Gantt": "Gantt", - "Month": "月", - "Quarter": "四半期", - "Week": "週" - }, - "status": {} -} + "string": { + "TrackerApplication": "トラッカー", + "Projects": "あなたのプロジェクト", + "More": "その他", + "Default": "デフォルト", + "MakeDefault": "デフォルトに設定", + "Delete": "削除", + "Open": "開く", + "Members": "メンバー", + "Inbox": "受信箱", + "MyIssues": "自分のイシュー", + "ViewIssue": "イシューを表示", + "IssueCreated": "イシューが作成されました", + "Issues": "イシュー", + "Views": "ビュー", + "Active": "アクティブ", + "AllIssues": "すべてのイシュー", + "ActiveIssues": "アクティブなイシュー", + "BacklogIssues": "バックログ", + "Backlog": "バックログ", + "Board": "ボード", + "Components": "コンポーネント", + "AllComponents": "すべて", + "BacklogComponents": "バックログ", + "ActiveComponents": "アクティブ", + "ClosedComponents": "完了", + "NewComponent": "新しいコンポーネント", + "CreateComponent": "コンポーネントを作成", + "ComponentNamePlaceholder": "コンポーネント名", + "ComponentDescriptionPlaceholder": "説明 (オプション)", + "ComponentLead": "リーダー", + "ComponentMembers": "メンバー", + "StartDate": "開始日", + "TargetDate": "目標日", + "Planned": "計画済み", + "InProgress": "進行中", + "Paused": "一時停止", + "Completed": "完了", + "Canceled": "キャンセル", + "CreateProject": "プロジェクトを作成", + "NewProject": "新しいプロジェクト", + "ProjectTitle": "プロジェクトタイトル", + "ProjectTitlePlaceholder": "新しいプロジェクト", + "UsedInIssueIDs": "イシューIDで使用", + "Identifier": "識別子", + "Import": "インポート", + "ProjectIdentifier": "プロジェクト識別子", + "IdentifierExists": "プロジェクト識別子はすでに存在します", + "ProjectIdentifierPlaceholder": "PRJCT", + "ChooseIcon": "アイコンを選択", + "AddIssue": "イシューを追加", + "NewIssue": "新しいイシュー", + "NewIssuePlaceholder": "新規", + "ResumeDraft": "下書きを再開", + "SaveIssue": "イシューを作成", + "SetPriority": "優先度を設定…", + "SetStatus": "ステータスを設定…", + "SelectIssue": "イシューを選択", + "Priority": "優先度", + "NoPriority": "優先度なし", + "Urgent": "緊急", + "High": "高", + "Medium": "中", + "Low": "低", + "Unassigned": "未割り当て", + "Back": "戻る", + "List": "リスト", + "NumberLabels": "{count, plural, =0 {ラベルなし} =1 {1 ラベル} other {# ラベル}}", + "CategoryBacklog": "バックログ", + "CategoryUnstarted": "未着手", + "CategoryStarted": "開始", + "CategoryCompleted": "完了", + "CategoryCanceled": "キャンセル", + "Title": "タイトル", + "Name": "名前", + "Description": "説明", + "Status": "ステータス", + "Number": "番号", + "Assignee": "担当者", + "AssignTo": "担当者を設定...", + "AssignedTo": "{value} に割り当て", + "Parent": "親イシュー", + "SetParent": "親イシューを設定…", + "ChangeParent": "親イシューを変更…", + "RemoveParent": "親イシューを削除", + "OpenParent": "親イシューを開く", + "SubIssues": "子イシュー", + "SubIssuesList": "子イシュー ({subIssues})", + "OpenSubIssues": "子イシューを開く", + "AddSubIssues": "子イシューを追加", + "BlockedBy": "ブロックされている", + "RelatedTo": "関連する", + "Comments": "コメント", + "Attachments": "添付ファイル", + "Labels": "ラベル", + "Component": "コンポーネント", + "Space": "", + "SetDueDate": "期日を設定…", + "ChangeDueDate": "期日を変更…", + "ModificationDate": "更新日 {value}", + "Project": "プロジェクト", + "Issue": "イシュー", + "SubIssue": "子イシュー", + "Document": "", + "DocumentIcon": "", + "DocumentColor": "", + "Rank": "ランク", + "TypeIssuePriority": "イシューの優先度", + "IssueTitlePlaceholder": "イシューのタイトル", + "SubIssueTitlePlaceholder": "子イシューのタイトル", + "IssueDescriptionPlaceholder": "説明を追加…", + "SubIssueDescriptionPlaceholder": "子イシューの説明を追加", + "AddIssueTooltip": "イシューを追加...", + "NewIssueDialogClose": "このダイアログを閉じますか?", + "NewIssueDialogCloseNote": "すべての変更は失われます", + "RemoveComponentDialogClose": "コンポーネントを削除しますか?", + "RemoveComponentDialogCloseNote": "本当にこのコンポーネントを削除しますか?この操作は元に戻せません", + "Grouping": "グループ化", + "Ordering": "順序", + "CompletedIssues": "完了したイシュー", + "NoGrouping": "グループ化なし", + "NoAssignee": "担当者なし", + "LastUpdated": "最終更新日", + "DueDate": "期日", + "IssueStartDate": "開始日", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "手動", + "All": "すべて", + "PastWeek": "先週", + "PastMonth": "先月", + "CopyIssueUrl": "イシューのURLをクリップボードにコピー", + "CopyIssueId": "イシューIDをクリップボードにコピー", + "CopyIssueBranch": "Gitブランチ名をクリップボードにコピー", + "CopyIssueTitle": "イシューのタイトルをクリップボードにコピー", + "AssetLabel": "アセット", + "AddToComponent": "コンポーネントに追加…", + "MoveToComponent": "コンポーネントに移動…", + "NoComponent": "コンポーネントなし", + "ComponentLeadTitle": "コンポーネントリーダー", + "ComponentMembersTitle": "コンポーネントメンバー", + "ComponentLeadSearchPlaceholder": "コンポーネントリーダーを設定…", + "ComponentMembersSearchPlaceholder": "コンポーネントメンバーを変更…", + "MoveToProject": "プロジェクトに移動", + "Duplicate": "複製", + "GotoIssues": "イシューへ移動", + "GotoActive": "アクティブなイシューへ移動", + "GotoBacklog": "バックログへ移動", + "GotoComponents": "コンポーネントへ移動", + "GotoMyIssues": "自分のイシューへ移動", + "GotoTrackerApplication": "トラッカーアプリケーションに切り替え", + "CreatedOne": "作成済み", + "MoveIssues": "イシューを移動", + "MoveIssuesDescription": "イシューを移動するプロジェクトを選択します", + "ManageAttributes": "属性を管理", + "KeepOriginalAttributes": "元の属性を保持", + "KeepOriginalAttributesTooltip": "元のイシューステータスとコンポーネントは新しいプロジェクトに保持されます", + "SelectReplacement": "次のアイテムは新しいプロジェクトで利用できません。代替を選択してください。", + "MissingItem": "不足しているアイテム", + "Replacement": "代替", + "Original": "オリジナル", + "OriginalDescription": "このセクションのアイテムは新しいプロジェクトで作成されます", + "Relations": "関連", + "RemoveRelation": "関連を削除...", + "AddBlockedBy": "ブロックされているとしてマーク...", + "AddIsBlocking": "ブロックしているとしてマーク...", + "AddRelatedIssue": "別のイシューを参照...", + "RelatedIssue": "関連するイシュー {id} - {title}", + "BlockedIssue": "ブロックされているイシュー {id} - {title}", + "BlockingIssue": "ブロックしているイシュー {id} - {title}", + "BlockedBySearchPlaceholder": "ブロックされているとしてマークするイシューを検索...", + "IsBlockingSearchPlaceholder": "ブロックしているとしてマークするイシューを検索...", + "RelatedIssueSearchPlaceholder": "参照するイシューを検索...", + "Blocks": "ブロック", + "Related": "関連", + "RelatedIssues": "関連するイシュー", + "EditIssue": "{title} を編集", + "EditWorkflowStatuses": "イシューステータスを編集", + "EditProject": "プロジェクトを編集", + "DeleteProject": "プロジェクトを削除", + "ArchiveProjectName": "プロジェクト {name} をアーカイブしますか?", + "ArchiveProjectConfirm": "このプロジェクトをアーカイブしますか?", + "DeleteProjectConfirm": "このプロジェクトとすべてのイシューを削除しますか?", + "ProjectHasIssues": "このプロジェクトには既存のイシューがあります。アーカイブしてもよろしいですか?", + "ManageWorkflowStatuses": "プロジェクトタイプを管理", + "AddWorkflowStatus": "イシューステータスを追加", + "EditWorkflowStatus": "イシューステータスを編集", + "DeleteWorkflowStatus": "イシューステータスを削除", + "DeleteWorkflowStatusConfirm": "\"{status}\" ステータスを削除しますか?", + "DeleteWorkflowStatusErrorDescription": "\"{status}\" ステータスには {count, plural, =1 {1 イシュー} other {# イシュー}} が割り当てられています。移動先のステータスを選択してください", + "Save": "保存", + "IncludeItemsThatMatch": "一致するアイテムを含める", + "AnyFilter": "いずれかのフィルター", + "AllFilters": "すべてのフィルター", + "NoDescription": "説明なし", + "SearchIssue": "タスクを検索...", + "StatusHistory": "状態履歴", + "NewSubIssue": "子イシューを追加...", + "AddLabel": "ラベルを追加", + "DeleteIssue": "{issueCount, plural, =1 {イシュー} other {イシュー}} を削除", + "DeleteIssueConfirm": "{issueCount, plural, =1 {イシュー} other {イシュー}}{subIssueCount, plural, =0 {?} =1 { と子イシュー?} other { と子イシュー?}} を削除しますか?", + "Milestone": "マイルストーン", + "NoMilestone": "マイルストーンなし", + "MoveToMilestone": "マイルストーンを選択", + "Milestones": "マイルストーン", + "AllMilestones": "すべて", + "PlannedMilestones": "計画済み", + "ActiveMilestones": "アクティブ", + "ClosedMilestones": "完了", + "AddToMilestone": "マイルストーンに追加", + "MilestoneNamePlaceholder": "マイルストーン名", + "NewMilestone": "新しいマイルストーン", + "CreateMilestone": "作成", + "MoveAndDeleteMilestone": "イシューを {newMilestone} に移動し、{deleteMilestone} を削除", + "MoveAndDeleteMilestoneConfirm": "マイルストーンを削除し、イシューを別のマイルストーンに移動しますか?", + "Estimation": "見積もり", + "ReportedTime": "消費時間", + "RemainingTime": "残り時間", + "TimeSpendReports": "時間消費レポート", + "TimeSpendReport": "時間", + "TimeSpendReportAdd": "時間レポートを追加", + "TimeSpendReportDate": "日付", + "TimeSpendReportValue": "消費時間", + "TimeSpendReportValueTooltip": "消費時間 (時間)", + "TimeSpendReportDescription": "説明", + "TimeSpendDays": "{value}日", + "TimeSpendHours": "{value}時間", + "TimeSpendMinutes": "{value}分", + "ChildEstimation": "子イシューの見積もり", + "ChildReportedTime": "子イシューの時間", + "CapacityValue": "うち {value}日", + "NewRelatedIssue": "新しい関連イシュー", + "RelatedIssuesNotFound": "関連イシューが見つかりません", + "AddedReference": "参照を追加", + "AddedAsBlocked": "ブロックされているとしてマーク", + "AddedAsBlocking": "ブロックしているとしてマーク", + "IssueTemplate": "テンプレート", + "IssueTemplates": "テンプレート", + "NewProcess": "新しいテンプレート", + "SaveProcess": "テンプレートを保存", + "NoIssueTemplate": "テンプレートなし", + "TemplateReplace": "新しいテンプレートを適用しますか?", + "TemplateReplaceConfirm": "すべてのフィールドが新しいテンプレートの値で上書きされます", + "Apply": "適用", + "CurrentWorkDay": "現在の営業日", + "PreviousWorkDay": "前の営業日", + "TimeReportDayTypeLabel": "時間レポートの日種別を選択", + "DefaultAssignee": "イシューのデフォルトの担当者", + "SevenHoursLength": "7時間", + "EightHoursLength": "8時間", + "HourLabel": "時間", + "MinuteLabel": "分", + "Saved": "保存済み...", + "CreatedIssue": "イシューを作成しました", + "CreatedSubIssue": "子イシューを作成しました", + "ChangeStatus": "ステータスを変更", + "ConfigLabel": "トラッカー", + "ConfigDescription": "作業項目を管理し、すべてのジョブを完了するための拡張機能。", + "NoStatusFound": "一致するステータスが見つかりません", + "CreateMissingStatus": "不足しているステータスを作成", + "UnsetParent": "親イシューが設定されなくなります", + "AllProjects": "すべてのプロジェクト", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "{senderName} が更新しました", + "IssueNotificationChanged": "{senderName} が {property} を変更しました", + "IssueNotificationChangedProperty": "{senderName} が {property} を \"{newValue}\" に変更しました", + "IssueNotificationMessage": "{senderName}: {message}", + "PreviousAssigned": "以前に割り当てられた", + "IssueAssignedToYou": "あなたに割り当てられました", + "RelatedIssueTargetDescription": "クラスまたはスペースの関連イシュープロジェクトターゲット", + "MapRelatedIssues": "関連イシューのデフォルトプロジェクトを構成", + "DefaultIssueStatus": "デフォルトのイシューステータス", + "IssueStatus": "ステータス", + "Extensions": "拡張機能", + "UnsetParentIssue": "親イシューの設定を解除", + "ForbidCreateProjectPermission": "プロジェクト作成禁止", + "ForbidCreateProjectPermissionDescription": "ユーザーが新しいプロジェクトを作成することを禁止します", + "AllowCreatingIssues": "イシューの作成を許可", + "Day": "日", + "Gantt": "Gantt", + "Month": "月", + "Quarter": "四半期", + "Week": "週", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/pt.json b/plugins/tracker-assets/lang/pt.json index d83e2745a75..463e20de583 100644 --- a/plugins/tracker-assets/lang/pt.json +++ b/plugins/tracker-assets/lang/pt.json @@ -1,287 +1,289 @@ { - "string": { - "TrackerApplication": "Seguimento", - "Projects": "Os teus projetos", - "More": "Mais", - "Default": "Padrão", - "MakeDefault": "Tornar padrão", - "Delete": "Apagar", - "Open": "Abrir", - "Members": "Membros", - "Inbox": "Caixa de entrada", - "MyIssues": "Os meus problemas", - "ViewIssue": "Ver problema", - "IssueCreated": "Problema criado", - "Issues": "Problemas", - "Views": "Vistas", - "Active": "Ativo", - "AllIssues": "Todos os problemas", - "ActiveIssues": "Problemas ativos", - "BacklogIssues": "Atraso", - "Backlog": "Atraso", - "Board": "Quadro", - "Components": "Componentes", - "AllComponents": "Todos", - "BacklogComponents": "Atraso", - "ActiveComponents": "Ativo", - "ClosedComponents": "Fechado", - "NewComponent": "Novo componente", - "CreateComponent": "Criar componente", - "ComponentNamePlaceholder": "Nome do componente", - "ComponentDescriptionPlaceholder": "Descrição (opcional)", - "ComponentLead": "Líder", - "ComponentMembers": "Membros", - "StartDate": "Data de início", - "TargetDate": "Data alvo", - "Planned": "Planeado", - "InProgress": "Em progresso", - "Paused": "Pausado", - "Completed": "Concluído", - "Canceled": "Cancelado", - "CreateProject": "Criar projeto", - "NewProject": "Novo projeto", - "ProjectTitle": "Título do projeto", - "ProjectTitlePlaceholder": "Novo projeto", - "UsedInIssueIDs": "Usado em IDs de problemas", - "Identifier": "Identificador", - "Import": "Importar", - "ProjectIdentifier": "Identificador do projeto", - "IdentifierExists": "O identificador do projeto já existe", - "ProjectIdentifierPlaceholder": "PROJ", - "ChooseIcon": "Escolher ícone", - "AddIssue": "Adicionar problema", - "NewIssue": "Novo problema", - "NewIssuePlaceholder": "Novo", - "ResumeDraft": "Retomar rascunho", - "SaveIssue": "Criar problema", - "SetPriority": "Definir prioridade\u2026", - "SetStatus": "Definir estado\u2026", - "SelectIssue": "Selecionar problema", - "Priority": "Prioridade", - "NoPriority": "Sem prioridade", - "Urgent": "Urgente", - "High": "Alta", - "Medium": "Média", - "Low": "Baixa", - "Unassigned": "Não atribuído", - "Back": "Voltar", - "List": "Lista", - "NumberLabels": "{count, plural, =0 {sem etiquetas} =1 {1 etiqueta} other {# etiquetas}}", - "CategoryBacklog": "Atraso", - "CategoryUnstarted": "Não iniciado", - "CategoryStarted": "Iniciado", - "CategoryCompleted": "Concluído", - "CategoryCanceled": "Cancelado", - "Title": "Título", - "Name": "Nome", - "Description": "Descrição", - "Status": "Estado", - "Number": "Número", - "Assignee": "Atribuído a", - "AssignTo": "Atribuir a...", - "AssignedTo": "Atribuído a {value}", - "Parent": "Problema superior", - "SetParent": "Definir problema superior\u2026", - "ChangeParent": "Alterar problema superior\u2026", - "RemoveParent": "Remover problema superior", - "OpenParent": "Abrir problema superior", - "SubIssues": "Sub-problemas", - "SubIssuesList": "Sub-problemas ({subIssues})", - "OpenSubIssues": "Abrir sub-problemas", - "AddSubIssues": "Adicionar sub-problema", - "BlockedBy": "Bloqueado por", - "RelatedTo": "Relacionado com", - "Comments": "Comentários", - "Attachments": "Anexos", - "Labels": "Etiquetas", - "Component": "Componente", - "Space": "", - "SetDueDate": "Definir data de vencimento\u2026", - "ChangeDueDate": "Alterar data de vencimento", - "ModificationDate": "Atualizado {value}", - "Project": "Projeto", - "Issue": "Tarefa", - "SubIssue": "Sub-Tarefa", - "Document": "", - "DocumentIcon": "", - "DocumentColor": "", - "Rank": "Classificação", - "TypeIssuePriority": "Prioridade da Tarefa", - "IssueTitlePlaceholder": "Título da Tarefa", - "SubIssueTitlePlaceholder": "Título da Sub-Tarefa", - "IssueDescriptionPlaceholder": "Adicionar descrição...", - "SubIssueDescriptionPlaceholder": "Adicionar Descrição da Sub-Tarefa", - "AddIssueTooltip": "Adicionar Tarefa...", - "NewIssueDialogClose": "Deseja fechar este diálogo?", - "NewIssueDialogCloseNote": "Todas as alterações serão perdidas", - "RemoveComponentDialogClose": "Eliminar o componente?", - "RemoveComponentDialogCloseNote": "Tem a certeza de que deseja eliminar este componente? Esta operação não pode ser desfeita", - "Grouping": "Agrupamento", - "Ordering": "Ordenação", - "CompletedIssues": "Tarefas Concluídas", - "NoGrouping": "Sem agrupamento", - "NoAssignee": "Sem atribuição", - "LastUpdated": "Última atualização", - "DueDate": "Data de vencimento", - "IssueStartDate": "Data de início", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "Manual", - "All": "Todos", - "PastWeek": "Semana passada", - "PastMonth": "Mês passado", - "CopyIssueUrl": "Copiar URL da questão para a área de transferência", - "CopyIssueId": "Copiar ID da questão para a área de transferência", - "CopyIssueBranch": "Copiar nome do branch Git para a área de transferência", - "CopyIssueTitle": "Copiar título da questão para a área de transferência", - "AssetLabel": "Ativo", - "AddToComponent": "Adicionar ao componente...", - "MoveToComponent": "Mover para o componente...", - "NoComponent": "Sem componente", - "ComponentLeadTitle": "Responsável pelo componente", - "ComponentMembersTitle": "Membros do componente", - "ComponentLeadSearchPlaceholder": "Definir responsável pelo componente...", - "ComponentMembersSearchPlaceholder": "Alterar membros do componente...", - "MoveToProject": "Mover para o projeto", - "Duplicate": "Duplicar", - "GotoIssues": "Ir para as questões", - "GotoActive": "Ir para as questões ativas", - "GotoBacklog": "Ir para a lista de espera", - "GotoComponents": "Ir para os componentes", - "GotoMyIssues": "Ir para as minhas questões", - "GotoTrackerApplication": "Alternar para a Aplicação de Acompanhamento", - "CreatedOne": "Criado", - "MoveIssues": "Mover questões", - "MoveIssuesDescription": "Selecione o projeto para mover as questões", - "ManageAttributes": "Gerir atributos", - "KeepOriginalAttributes": "Manter atributos originais", - "KeepOriginalAttributesTooltip": "Os estados e componentes originais da questão serão mantidos no novo projeto", - "SelectReplacement": "Os seguintes itens não estão disponíveis no novo projeto. Selecione uma substituição.", - "MissingItem": "ITEM EM FALTA", - "Replacement": "SUBSTITUIÇÃO", - "Original": "ORIGINAL", - "OriginalDescription": "Os itens desta secção serão criados no novo projeto", - "Relations": "Relações", - "RemoveRelation": "Remover relação...", - "AddBlockedBy": "Marcar como bloqueado por...", - "AddIsBlocking": "Marcar como bloqueador...", - "AddRelatedIssue": "Referenciar outra questão...", - "RelatedIssue": "Questão relacionada {id} - {title}", - "BlockedIssue": "Questão bloqueada {id} - {title}", - "BlockingIssue": "Questão bloqueadora {id} - {title}", - "BlockedBySearchPlaceholder": "Procurar questão para marcar como bloqueada por...", - "IsBlockingSearchPlaceholder": "Procurar questão para marcar como bloqueadora...", - "RelatedIssueSearchPlaceholder": "Procurar questão para referenciar...", - "Blocks": "Bloqueia", - "Related": "Relacionado", - "RelatedIssues": "Questões relacionadas", - "EditIssue": "Editar {title}", - "EditWorkflowStatuses": "Editar estados de tarefa", - "EditProject": "Editar projeto", - "DeleteProject": "Apagar projeto", - "ArchiveProjectName": "Arquivar projeto {name}?", - "ArchiveProjectConfirm": "Deseja arquivar este projeto?", - "DeleteProjectConfirm": "Deseja apagar este projeto e todas as tarefas?", - "ProjectHasIssues": "Existem tarefas neste projeto, tem a certeza de que deseja arquivar?", - "ManageWorkflowStatuses": "Gerir tipos de projeto", - "AddWorkflowStatus": "Adicionar estado de tarefa", - "EditWorkflowStatus": "Editar estado de tarefa", - "DeleteWorkflowStatus": "Apagar estado de tarefa", - "DeleteWorkflowStatusConfirm": "Deseja apagar o estado \"{status}\"?", - "DeleteWorkflowStatusErrorDescription": "O estado \"{status}\" tem {count, plural, =1 {1 tarefa} other {# tarefas}} atribuída. Por favor, selecione um estado para mover", - "Save": "Guardar", - "IncludeItemsThatMatch": "Incluir itens que coincidam", - "AnyFilter": "qualquer filtro", - "AllFilters": "todos os filtros", - "NoDescription": "Sem descrição", - "SearchIssue": "Procurar tarefa...", - "StatusHistory": "Histórico de estado", - "NewSubIssue": "Adicionar sub-tarefa...", - "AddLabel": "Adicionar etiqueta", - "DeleteIssue": "Apagar {issueCount, plural, =1 {tarefa} other {# tarefas}}", - "DeleteIssueConfirm": "Deseja apagar {issueCount, plural, =1 {tarefa} other {tarefas}}{subIssueCount, plural, =0 {?} =1 { e sub-tarefa?} other { e sub-tarefas?}}", - "Milestone": "Objetivo", - "NoMilestone": "Sem objetivo", - "MoveToMilestone": "Selecionar Objetivo", - "Milestones": "Objetivos", - "AllMilestones": "Todos", - "PlannedMilestones": "Planeados", - "ActiveMilestones": "Ativos", - "ClosedMilestones": "Concluídos", - "AddToMilestone": "Adicionar ao Objetivo", - "MilestoneNamePlaceholder": "Nome do objetivo", - "NewMilestone": "Novo Objetivo", - "CreateMilestone": "Criar", - "MoveAndDeleteMilestone": "Mover tarefas para {newMilestone} e Apagar {deleteMilestone}", - "MoveAndDeleteMilestoneConfirm": "Deseja apagar o objetivo e mover tarefas para outro objetivo?", - "Estimation": "Estimativa", - "ReportedTime": "Tempo gasto", - "RemainingTime": "Tempo restante", - "TimeSpendReports": "Relatórios de tempo gasto", - "TimeSpendReport": "Tempo", - "TimeSpendReportAdd": "Adicionar relatório de tempo", - "TimeSpendReportDate": "Data", - "TimeSpendReportValue": "Tempo gasto", - "TimeSpendReportValueTooltip": "Tempo gasto em horas", - "TimeSpendReportDescription": "Descrição", - "TimeSpendDays": "{value}d", - "TimeSpendHours": "{value}h", - "TimeSpendMinutes": "{value}m", - "ChildEstimation": "Estimativa de sub-tarefas", - "ChildReportedTime": "Tempo de sub-tarefas", - "CapacityValue": "de {value}d", - "NewRelatedIssue": "Nova tarefa relacionada", - "RelatedIssuesNotFound": "Tarefas relacionadas não encontradas", - "AddedReference": "Referência adicionada", - "AddedAsBlocked": "Marcado como bloqueado", - "AddedAsBlocking": "Marcado como bloqueador", - "IssueTemplate": "Modelo", - "IssueTemplates": "Modelos", - "NewProcess": "Novo Modelo", - "SaveProcess": "Guardar Modelo", - "NoIssueTemplate": "Sem Modelo", - "TemplateReplace": "Deseja aplicar o novo modelo?", - "TemplateReplaceConfirm": "Todos os campos serão sobrescritos pelos valores do novo modelo", - "Apply": "Aplicar", - "CurrentWorkDay": "Dia de trabalho atual", - "PreviousWorkDay": "Dia de trabalho anterior", - "TimeReportDayTypeLabel": "Selecione o tipo de dia do relatório de tempo", - "DefaultAssignee": "Responsável padrão para problemas", - "SevenHoursLength": "Sete Horas", - "EightHoursLength": "Oito Horas", - "HourLabel": "h", - "MinuteLabel": "m", - "Saved": "Guardado...", - "CreatedIssue": "Problema criado", - "CreatedSubIssue": "Sub-problema criado", - "ChangeStatus": "Alterar estado", - "ConfigLabel": "Rastreador", - "ConfigDescription": "Extensão para gerir itens de trabalho e fazer todas as tarefas necessárias.", - "NoStatusFound": "Nenhum estado correspondente encontrado", - "CreateMissingStatus": "Criar estado em falta", - "UnsetParent": "O problema pai será desassociado", - "AllProjects": "Todos os projetos", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "Atualizado por {senderName}", - "IssueNotificationChanged": "{senderName} alterou {property}", - "IssueNotificationChangedProperty": "{senderName} alterou {property} para \"{newValue}\"", - "IssueNotificationMessage": "{senderName}: {message}", - "PreviousAssigned": "Anteriormente atribuído", - "IssueAssignedToYou": "Atribuído a si", - "RelatedIssueTargetDescription": "Projeto alvo de problemas relacionados para Classe ou Espaço", - "MapRelatedIssues": "Configurar projetos padrão de problemas relacionados", - "DefaultIssueStatus": "Estado padrão do problema", - "IssueStatus": "Estado", - "Extensions": "Extensions", - "UnsetParentIssue": "Desmarcar problema pai", - "ForbidCreateProjectPermission": "Proibir criação de projeto", - "ForbidCreateProjectPermissionDescription": "Proíbe os usuários de criar novos projetos", - "AllowCreatingIssues": "Permitir criar problemas", - "Day": "Dia", - "Gantt": "Gantt", - "Month": "Mês", - "Quarter": "Trimestre", - "Week": "Semana" - }, - "status": {} -} + "string": { + "TrackerApplication": "Seguimento", + "Projects": "Os teus projetos", + "More": "Mais", + "Default": "Padrão", + "MakeDefault": "Tornar padrão", + "Delete": "Apagar", + "Open": "Abrir", + "Members": "Membros", + "Inbox": "Caixa de entrada", + "MyIssues": "Os meus problemas", + "ViewIssue": "Ver problema", + "IssueCreated": "Problema criado", + "Issues": "Problemas", + "Views": "Vistas", + "Active": "Ativo", + "AllIssues": "Todos os problemas", + "ActiveIssues": "Problemas ativos", + "BacklogIssues": "Atraso", + "Backlog": "Atraso", + "Board": "Quadro", + "Components": "Componentes", + "AllComponents": "Todos", + "BacklogComponents": "Atraso", + "ActiveComponents": "Ativo", + "ClosedComponents": "Fechado", + "NewComponent": "Novo componente", + "CreateComponent": "Criar componente", + "ComponentNamePlaceholder": "Nome do componente", + "ComponentDescriptionPlaceholder": "Descrição (opcional)", + "ComponentLead": "Líder", + "ComponentMembers": "Membros", + "StartDate": "Data de início", + "TargetDate": "Data alvo", + "Planned": "Planeado", + "InProgress": "Em progresso", + "Paused": "Pausado", + "Completed": "Concluído", + "Canceled": "Cancelado", + "CreateProject": "Criar projeto", + "NewProject": "Novo projeto", + "ProjectTitle": "Título do projeto", + "ProjectTitlePlaceholder": "Novo projeto", + "UsedInIssueIDs": "Usado em IDs de problemas", + "Identifier": "Identificador", + "Import": "Importar", + "ProjectIdentifier": "Identificador do projeto", + "IdentifierExists": "O identificador do projeto já existe", + "ProjectIdentifierPlaceholder": "PROJ", + "ChooseIcon": "Escolher ícone", + "AddIssue": "Adicionar problema", + "NewIssue": "Novo problema", + "NewIssuePlaceholder": "Novo", + "ResumeDraft": "Retomar rascunho", + "SaveIssue": "Criar problema", + "SetPriority": "Definir prioridade…", + "SetStatus": "Definir estado…", + "SelectIssue": "Selecionar problema", + "Priority": "Prioridade", + "NoPriority": "Sem prioridade", + "Urgent": "Urgente", + "High": "Alta", + "Medium": "Média", + "Low": "Baixa", + "Unassigned": "Não atribuído", + "Back": "Voltar", + "List": "Lista", + "NumberLabels": "{count, plural, =0 {sem etiquetas} =1 {1 etiqueta} other {# etiquetas}}", + "CategoryBacklog": "Atraso", + "CategoryUnstarted": "Não iniciado", + "CategoryStarted": "Iniciado", + "CategoryCompleted": "Concluído", + "CategoryCanceled": "Cancelado", + "Title": "Título", + "Name": "Nome", + "Description": "Descrição", + "Status": "Estado", + "Number": "Número", + "Assignee": "Atribuído a", + "AssignTo": "Atribuir a...", + "AssignedTo": "Atribuído a {value}", + "Parent": "Problema superior", + "SetParent": "Definir problema superior…", + "ChangeParent": "Alterar problema superior…", + "RemoveParent": "Remover problema superior", + "OpenParent": "Abrir problema superior", + "SubIssues": "Sub-problemas", + "SubIssuesList": "Sub-problemas ({subIssues})", + "OpenSubIssues": "Abrir sub-problemas", + "AddSubIssues": "Adicionar sub-problema", + "BlockedBy": "Bloqueado por", + "RelatedTo": "Relacionado com", + "Comments": "Comentários", + "Attachments": "Anexos", + "Labels": "Etiquetas", + "Component": "Componente", + "Space": "", + "SetDueDate": "Definir data de vencimento…", + "ChangeDueDate": "Alterar data de vencimento", + "ModificationDate": "Atualizado {value}", + "Project": "Projeto", + "Issue": "Tarefa", + "SubIssue": "Sub-Tarefa", + "Document": "", + "DocumentIcon": "", + "DocumentColor": "", + "Rank": "Classificação", + "TypeIssuePriority": "Prioridade da Tarefa", + "IssueTitlePlaceholder": "Título da Tarefa", + "SubIssueTitlePlaceholder": "Título da Sub-Tarefa", + "IssueDescriptionPlaceholder": "Adicionar descrição...", + "SubIssueDescriptionPlaceholder": "Adicionar Descrição da Sub-Tarefa", + "AddIssueTooltip": "Adicionar Tarefa...", + "NewIssueDialogClose": "Deseja fechar este diálogo?", + "NewIssueDialogCloseNote": "Todas as alterações serão perdidas", + "RemoveComponentDialogClose": "Eliminar o componente?", + "RemoveComponentDialogCloseNote": "Tem a certeza de que deseja eliminar este componente? Esta operação não pode ser desfeita", + "Grouping": "Agrupamento", + "Ordering": "Ordenação", + "CompletedIssues": "Tarefas Concluídas", + "NoGrouping": "Sem agrupamento", + "NoAssignee": "Sem atribuição", + "LastUpdated": "Última atualização", + "DueDate": "Data de vencimento", + "IssueStartDate": "Data de início", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "Manual", + "All": "Todos", + "PastWeek": "Semana passada", + "PastMonth": "Mês passado", + "CopyIssueUrl": "Copiar URL da questão para a área de transferência", + "CopyIssueId": "Copiar ID da questão para a área de transferência", + "CopyIssueBranch": "Copiar nome do branch Git para a área de transferência", + "CopyIssueTitle": "Copiar título da questão para a área de transferência", + "AssetLabel": "Ativo", + "AddToComponent": "Adicionar ao componente...", + "MoveToComponent": "Mover para o componente...", + "NoComponent": "Sem componente", + "ComponentLeadTitle": "Responsável pelo componente", + "ComponentMembersTitle": "Membros do componente", + "ComponentLeadSearchPlaceholder": "Definir responsável pelo componente...", + "ComponentMembersSearchPlaceholder": "Alterar membros do componente...", + "MoveToProject": "Mover para o projeto", + "Duplicate": "Duplicar", + "GotoIssues": "Ir para as questões", + "GotoActive": "Ir para as questões ativas", + "GotoBacklog": "Ir para a lista de espera", + "GotoComponents": "Ir para os componentes", + "GotoMyIssues": "Ir para as minhas questões", + "GotoTrackerApplication": "Alternar para a Aplicação de Acompanhamento", + "CreatedOne": "Criado", + "MoveIssues": "Mover questões", + "MoveIssuesDescription": "Selecione o projeto para mover as questões", + "ManageAttributes": "Gerir atributos", + "KeepOriginalAttributes": "Manter atributos originais", + "KeepOriginalAttributesTooltip": "Os estados e componentes originais da questão serão mantidos no novo projeto", + "SelectReplacement": "Os seguintes itens não estão disponíveis no novo projeto. Selecione uma substituição.", + "MissingItem": "ITEM EM FALTA", + "Replacement": "SUBSTITUIÇÃO", + "Original": "ORIGINAL", + "OriginalDescription": "Os itens desta secção serão criados no novo projeto", + "Relations": "Relações", + "RemoveRelation": "Remover relação...", + "AddBlockedBy": "Marcar como bloqueado por...", + "AddIsBlocking": "Marcar como bloqueador...", + "AddRelatedIssue": "Referenciar outra questão...", + "RelatedIssue": "Questão relacionada {id} - {title}", + "BlockedIssue": "Questão bloqueada {id} - {title}", + "BlockingIssue": "Questão bloqueadora {id} - {title}", + "BlockedBySearchPlaceholder": "Procurar questão para marcar como bloqueada por...", + "IsBlockingSearchPlaceholder": "Procurar questão para marcar como bloqueadora...", + "RelatedIssueSearchPlaceholder": "Procurar questão para referenciar...", + "Blocks": "Bloqueia", + "Related": "Relacionado", + "RelatedIssues": "Questões relacionadas", + "EditIssue": "Editar {title}", + "EditWorkflowStatuses": "Editar estados de tarefa", + "EditProject": "Editar projeto", + "DeleteProject": "Apagar projeto", + "ArchiveProjectName": "Arquivar projeto {name}?", + "ArchiveProjectConfirm": "Deseja arquivar este projeto?", + "DeleteProjectConfirm": "Deseja apagar este projeto e todas as tarefas?", + "ProjectHasIssues": "Existem tarefas neste projeto, tem a certeza de que deseja arquivar?", + "ManageWorkflowStatuses": "Gerir tipos de projeto", + "AddWorkflowStatus": "Adicionar estado de tarefa", + "EditWorkflowStatus": "Editar estado de tarefa", + "DeleteWorkflowStatus": "Apagar estado de tarefa", + "DeleteWorkflowStatusConfirm": "Deseja apagar o estado \"{status}\"?", + "DeleteWorkflowStatusErrorDescription": "O estado \"{status}\" tem {count, plural, =1 {1 tarefa} other {# tarefas}} atribuída. Por favor, selecione um estado para mover", + "Save": "Guardar", + "IncludeItemsThatMatch": "Incluir itens que coincidam", + "AnyFilter": "qualquer filtro", + "AllFilters": "todos os filtros", + "NoDescription": "Sem descrição", + "SearchIssue": "Procurar tarefa...", + "StatusHistory": "Histórico de estado", + "NewSubIssue": "Adicionar sub-tarefa...", + "AddLabel": "Adicionar etiqueta", + "DeleteIssue": "Apagar {issueCount, plural, =1 {tarefa} other {# tarefas}}", + "DeleteIssueConfirm": "Deseja apagar {issueCount, plural, =1 {tarefa} other {tarefas}}{subIssueCount, plural, =0 {?} =1 { e sub-tarefa?} other { e sub-tarefas?}}", + "Milestone": "Objetivo", + "NoMilestone": "Sem objetivo", + "MoveToMilestone": "Selecionar Objetivo", + "Milestones": "Objetivos", + "AllMilestones": "Todos", + "PlannedMilestones": "Planeados", + "ActiveMilestones": "Ativos", + "ClosedMilestones": "Concluídos", + "AddToMilestone": "Adicionar ao Objetivo", + "MilestoneNamePlaceholder": "Nome do objetivo", + "NewMilestone": "Novo Objetivo", + "CreateMilestone": "Criar", + "MoveAndDeleteMilestone": "Mover tarefas para {newMilestone} e Apagar {deleteMilestone}", + "MoveAndDeleteMilestoneConfirm": "Deseja apagar o objetivo e mover tarefas para outro objetivo?", + "Estimation": "Estimativa", + "ReportedTime": "Tempo gasto", + "RemainingTime": "Tempo restante", + "TimeSpendReports": "Relatórios de tempo gasto", + "TimeSpendReport": "Tempo", + "TimeSpendReportAdd": "Adicionar relatório de tempo", + "TimeSpendReportDate": "Data", + "TimeSpendReportValue": "Tempo gasto", + "TimeSpendReportValueTooltip": "Tempo gasto em horas", + "TimeSpendReportDescription": "Descrição", + "TimeSpendDays": "{value}d", + "TimeSpendHours": "{value}h", + "TimeSpendMinutes": "{value}m", + "ChildEstimation": "Estimativa de sub-tarefas", + "ChildReportedTime": "Tempo de sub-tarefas", + "CapacityValue": "de {value}d", + "NewRelatedIssue": "Nova tarefa relacionada", + "RelatedIssuesNotFound": "Tarefas relacionadas não encontradas", + "AddedReference": "Referência adicionada", + "AddedAsBlocked": "Marcado como bloqueado", + "AddedAsBlocking": "Marcado como bloqueador", + "IssueTemplate": "Modelo", + "IssueTemplates": "Modelos", + "NewProcess": "Novo Modelo", + "SaveProcess": "Guardar Modelo", + "NoIssueTemplate": "Sem Modelo", + "TemplateReplace": "Deseja aplicar o novo modelo?", + "TemplateReplaceConfirm": "Todos os campos serão sobrescritos pelos valores do novo modelo", + "Apply": "Aplicar", + "CurrentWorkDay": "Dia de trabalho atual", + "PreviousWorkDay": "Dia de trabalho anterior", + "TimeReportDayTypeLabel": "Selecione o tipo de dia do relatório de tempo", + "DefaultAssignee": "Responsável padrão para problemas", + "SevenHoursLength": "Sete Horas", + "EightHoursLength": "Oito Horas", + "HourLabel": "h", + "MinuteLabel": "m", + "Saved": "Guardado...", + "CreatedIssue": "Problema criado", + "CreatedSubIssue": "Sub-problema criado", + "ChangeStatus": "Alterar estado", + "ConfigLabel": "Rastreador", + "ConfigDescription": "Extensão para gerir itens de trabalho e fazer todas as tarefas necessárias.", + "NoStatusFound": "Nenhum estado correspondente encontrado", + "CreateMissingStatus": "Criar estado em falta", + "UnsetParent": "O problema pai será desassociado", + "AllProjects": "Todos os projetos", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "Atualizado por {senderName}", + "IssueNotificationChanged": "{senderName} alterou {property}", + "IssueNotificationChangedProperty": "{senderName} alterou {property} para \"{newValue}\"", + "IssueNotificationMessage": "{senderName}: {message}", + "PreviousAssigned": "Anteriormente atribuído", + "IssueAssignedToYou": "Atribuído a si", + "RelatedIssueTargetDescription": "Projeto alvo de problemas relacionados para Classe ou Espaço", + "MapRelatedIssues": "Configurar projetos padrão de problemas relacionados", + "DefaultIssueStatus": "Estado padrão do problema", + "IssueStatus": "Estado", + "Extensions": "Extensions", + "UnsetParentIssue": "Desmarcar problema pai", + "ForbidCreateProjectPermission": "Proibir criação de projeto", + "ForbidCreateProjectPermissionDescription": "Proíbe os usuários de criar novos projetos", + "AllowCreatingIssues": "Permitir criar problemas", + "Day": "Dia", + "Gantt": "Gantt", + "Month": "Mês", + "Quarter": "Trimestre", + "Week": "Semana", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/ru.json b/plugins/tracker-assets/lang/ru.json index 142a3eec2b3..ccaa4f22c99 100644 --- a/plugins/tracker-assets/lang/ru.json +++ b/plugins/tracker-assets/lang/ru.json @@ -1,304 +1,289 @@ { - "string": { - "TrackerApplication": "Трекер", - "Projects": "Проекты", - "More": "Больше", - "Default": "По умолчанию", - "MakeDefault": "Установить по умолчанию", - "Delete": "Удалить", - "Open": "Открыть", - "Members": "Участники", - "Inbox": "Входящие", - "ViewIssue": "Открыть задачу", - "IssueCreated": "Задача создана", - "MyIssues": "Мои задачи", - "Issues": "Задачи", - "Views": "Отображения", - "Active": "Активные", - "AllIssues": "Все задачи", - "ActiveIssues": "Активные задачи", - "BacklogIssues": "Пул задач", - "Backlog": "Пул задач", - "Board": "Канбан", - "Components": "Компоненты", - "AllComponents": "Все", - "BacklogComponents": "Пул задач", - "ActiveComponents": "Активные", - "ClosedComponents": "Закрытые", - "NewComponent": "Новый компонент", - "CreateComponent": "Создать компонент", - "ComponentNamePlaceholder": "Название компонента", - "ComponentDescriptionPlaceholder": "Описание (необязательно)", - "ComponentLead": "Руководитель", - "ComponentMembers": "Участники", - "StartDate": "Дата начала", - "TargetDate": "Дата завершения", - "Planned": "Запланирован", - "InProgress": "В работе", - "Paused": "Приостановлен", - "Completed": "Завершен", - "Canceled": "Отменено", - "CreateProject": "Новый проект", - "NewProject": "Новый проект", - "ProjectTitle": "Название проекта", - "ProjectTitlePlaceholder": "Новый проект", - "UsedInIssueIDs": "Используется в идентификаторах задач", - "Identifier": "Идентификатор", - "Import": "Импорт", - "ProjectIdentifier": "Идентификатор проекта", - "IdentifierExists": "Идентификатор уже существует проекта", - "ProjectIdentifierPlaceholder": "ПКТ", - "ChooseIcon": "Выбрать иконку", - "AddIssue": "Добавить задачу", - "NewIssue": "Новая задача", - "NewIssuePlaceholder": "Новая", - "ResumeDraft": "Восстановить черновик", - "SaveIssue": "Создать задачу", - "SetPriority": "Установить приоритет\u2026", - "SetStatus": "Установить статус\u2026", - "SelectIssue": "Выбрать задачу", - "Priority": "Приоритет", - "NoPriority": "Нет приоритета", - "Urgent": "Наивысший", - "High": "Высокий", - "Medium": "Средний", - "Low": "Низкий", - "Unassigned": "Не назначен", - "Back": "Назад", - "List": "Список", - "NumberLabels": "{count, plural, =0 {нет меток} one {# метка} few {# метки} other {# меток}}", - - "CategoryBacklog": "Пул", - "CategoryUnstarted": "Не запущенные", - "CategoryStarted": "Запущенный", - "CategoryCompleted": "Завершенные", - "CategoryCanceled": "Отменённые", - - "Title": "Заголовок", - "Name": "Имя", - "Description": "Описание", - "Status": "Статус", - "Number": "Номер", - "Assignee": "Исполнитель", - "AssignTo": "Назначить...", - "AssignedTo": "Назначен на {value}", - "Parent": "Родительская задача", - "SetParent": "Задать родительскую задачу\u2026", - "ChangeParent": "Изменить родительскую задачу\u2026", - "RemoveParent": "Удалить родительскую задачу", - "OpenParent": "Открыть родительскую задачу", - "SubIssues": "Подзадачи", - "SubIssuesList": "Подзадачи ({subIssues})", - "OpenSubIssues": "Открыть подзадачи", - "AddSubIssues": "Добавить подзадачу", - "BlockedBy": "Блокируется", - "RelatedTo": "Связано", - "Comments": "Комментарии", - "Attachments": "Вложения", - "Labels": "Метки", - "Component": "Компонент", - "Space": "", - "SetDueDate": "Указать срок выполнения\u2026", - "ChangeDueDate": "Изменить срок выполнения\u2026", - "ModificationDate": "Изменено {value}", - "Project": "Проект", - "Issue": "Задача", - "SubIssue": "Подзадача", - "Document": "", - "DocumentIcon": "", - "DocumentColor": "", - "Rank": "Ранг", - "TypeIssuePriority": "Приоритет задачи", - "IssueTitlePlaceholder": "Имя задачи", - "SubIssueTitlePlaceholder": "Имя подзадачи", - "IssueDescriptionPlaceholder": "Добавить описание\u2026", - "SubIssueDescriptionPlaceholder": "Описание подзадачи", - "AddIssueTooltip": "Добавить задачу\u2026", - "NewIssueDialogClose": "Вы действительно хотите закрыть окно?", - "NewIssueDialogCloseNote": "Все внесенные изменения будут потеряны", - "RemoveComponentDialogClose": "Удалить компонент?", - "RemoveComponentDialogCloseNote": "Уверены, что хотите удалить этот компонент? Эта операция не может быть отменена.", - "Grouping": "Группировка", - "Ordering": "Сортировка", - "CompletedIssues": "Завершённые задачи", - "NoGrouping": "Нет группировки", - "NoAssignee": "Нет исполнителя", - "LastUpdated": "Последнее обновление", - "DueDate": "Срок", - "IssueStartDate": "Дата начала", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "Пользовательский", - "All": "Все", - "PastWeek": "Предыдущая неделя", - "PastMonth": "Предыдущий месяц", - "CopyIssueUrl": "Копировать URL-адрес задачи в буфер обмена", - "CopyIssueId": "Копировать ID задачи в буфер обмена", - "CopyIssueBranch": "Копировать имя ветки Git в буфер обмена", - "CopyIssueTitle": "Копировать имя задачи в буфер обмена", - "AssetLabel": "Asset", - "AddToComponent": "Добавить в компонент\u2026", - "MoveToComponent": "Переместить в компонент\u2026", - "NoComponent": "Без компонента", - "ComponentLeadTitle": "Руководитель компонента", - "ComponentMembersTitle": "Участники компонента", - "ComponentLeadSearchPlaceholder": "Назначьте руководителя компонента\u2026", - "ComponentMembersSearchPlaceholder": "Изменить участников компонента\u2026", - "MoveToProject": "Изменить проект", - "Duplicate": "Дублировать", - - "GotoIssues": "Перейти к задачам", - "GotoActive": "Перейти к активным задачам", - "GotoBacklog": "Перейти к пулу задач", - "GotoComponents": "Перейти к компоненту", - "GotoMyIssues": "Перейти к моим задачам", - "GotoTrackerApplication": "Перейти к приложению Трекер", - - "CreatedOne": "Создана", - "MoveIssues": "Переместить задачи", - "MoveIssuesDescription": "Выберите проект, в который вы хотите переместить задачи", - "ManageAttributes": "Управление атрибутами", - "KeepOriginalAttributes": "Оставить оригинальные аттрибуты", - "KeepOriginalAttributesTooltip": "Оригинальные статусы и компоненты будут сохранены в новом проекте", - "SelectReplacement": "Следующие элементы не доступны в новом проекте. Выберите замену.", - "MissingItem": "ОТСУТСТВУЮЩИЙ ЭЛЕМЕНТ", - "Replacement": "ЗАМЕНА", - "Original": "ОРИГИНАЛ", - "OriginalDescription": "Элементы из этой секции будут созданы в новом проекте", - - "Relations": "Зависимости", - "RemoveRelation": "Удалить зависимость...", - "AddBlockedBy": "Отметить как блокируемую...", - "AddIsBlocking": "Отметить как блокирующую...", - "AddRelatedIssue": "Связать с задачей...", - "RelatedIssue": "Связанная задача {id} - {title}", - "BlockedIssue": "Блокируемая задача {id} - {title}", - "BlockingIssue": "Блокирующая {id} - {title}", - "BlockedBySearchPlaceholder": "Поиск блокирующей задачи...", - "IsBlockingSearchPlaceholder": "Поиск блокируемой задачи...", - "RelatedIssueSearchPlaceholder": "Поиск связанной задачи...", - "Blocks": "Блокирует", - "Related": "Связан", - "RelatedIssues": "Связанные задачи", - - "EditIssue": "Редактирование {title}", - "EditWorkflowStatuses": "Редактировать статусы задач", - "EditProject": "Редактировать проект", - "DeleteProject": "Удалить проект", - "ArchiveProjectName": "Архивировать проект {name}?", - "ArchiveProjectConfirm": "Вы действительно хотите заархивировать этот проект?", - "DeleteProjectConfirm": "Вы действительно хотите удалить этот проект со всем содержимым?", - "ProjectHasIssues": "Для данного проекта существуют задачи, уверены, что хотите заархивировать?", - "ManageWorkflowStatuses": "Управлять типами проекта", - "AddWorkflowStatus": "Добавить статус задачи", - "EditWorkflowStatus": "Редактировать статус задачи", - "DeleteWorkflowStatus": "Удалить статус задачи", - "DeleteWorkflowStatusConfirm": "Вы действительно хотите удалить \"{status}\" статус?", - "DeleteWorkflowStatusErrorDescription": "Статус \"{status}\" {count, plural, one {имеет # задача} few {имеет $ задачи} other {имеют # задач}}. Пожалуйста, выберите статус для перемещения.", - - "Save": "Сохранить", - "IncludeItemsThatMatch": "Включить элементы, которые соответствуют", - "AnyFilter": "любому фильтру", - "AllFilters": "всем фильтрам", - "NoDescription": "Нет описания", - "SearchIssue": "Поиск задачи...", - - "StatusHistory": "История состояний", - "NewSubIssue": "Добавить подзадачу...", - "AddLabel": "Добавить метку", - - "DeleteIssue": "Удалить {issueCount, plural, =1 {задачу} one {# задачу} few {# задачи} other {задач}}", - "DeleteIssueConfirm": "Вы действительно хотите удалить {issueCount, plural, =1 {задачу} one {# задачу} few {# задачи} other {задач}}{subIssueCount, plural, =0 {?} =1 { и подзадачу?} one { и # подзадачу?} other { и # подзадач?}}", - - "Milestone": "Этап", - "NoMilestone": "Без Этапа", - "MoveToMilestone": "Выбрать Этап", - "Milestones": "Этапы", - "AllMilestones": "Все", - "PlannedMilestones": "Запланировано", - "ActiveMilestones": "Активно", - "ClosedMilestones": "Завершено", - "AddToMilestone": "Добавить в Этап", - "MilestoneNamePlaceholder": "Название Этапа", - - "NewMilestone": "Новый Этап", - "CreateMilestone": "Создать", - - "MoveAndDeleteMilestone": "Переместить Задачи в {newMilestone} и Удалить {deleteMilestone}", - "MoveAndDeleteMilestoneConfirm": "Вы действительно хотите удалить этап и перенести задачи в другой?", - - "Estimation": "Оценка", - "ReportedTime": "Потраченное времени", - "RemainingTime": "Осталось времени", - "TimeSpendReports": "Отчеты по времени", - "TimeSpendReport": "Время", - "TimeSpendReportAdd": "Добавить затраченное время", - "TimeSpendReportDate": "Дата", - "TimeSpendReportValue": "Затраченное время", - "TimeSpendReportValueTooltip": "Затраченное время в человеко днях", - "TimeSpendReportDescription": "Описание", - "TimeSpendDays": "{value} д", - "TimeSpendHours": "{value} ч", - "TimeSpendMinutes": "{value} м", - "ChildEstimation": "Оценка подзадач", - "ChildReportedTime": "Время подзадач", - "CapacityValue": "из {value} д", - "NewRelatedIssue": "Завести связанную задачу", - "RelatedIssuesNotFound": "Связанные задачи не найдены", - - "AddedReference": "Добавлена зависимость", - "AddedAsBlocked": "Отмечено как заблокировано", - "AddedAsBlocking": "Отмечено как блокирующее", - - "IssueTemplate": "Шаблон", - "IssueTemplates": "Шаблоны", - "NewProcess": "Новый шаблон", - "SaveProcess": "Сохранить шаблон", - "NoIssueTemplate": "Шаблон не задан", - "TemplateReplace": "Вы хотите применить выбранный шаблон?", - "TemplateReplaceConfirm": "Все внесенные изменения в будут заменены значениями из нового шаблона", - "Apply": "Применить", - - "CurrentWorkDay": "Текущий Рабочий День", - "PreviousWorkDay": "Предыдущий Рабочий День", - "TimeReportDayTypeLabel": "Выберите тип дня для временного отчета", - "DefaultAssignee": "Исполнитель задачи по умолчанию", - - "SevenHoursLength": "Семь Часов", - "EightHoursLength": "Восемь Часов", - "HourLabel": "ч", - "MinuteLabel": "м", - "Saved": "Сохранено...", - "CreatedIssue": "Создал(а) задачу", - "CreatedSubIssue": "Создал(а) подзадачу", - "ChangeStatus": "Изменение статуса", - "ConfigLabel": "Трекер", - "ConfigDescription": "Расширение по управлению задачами, чтобы все было в срок.", - "NoStatusFound": "Статус не найдет", - "CreateMissingStatus": "Создать отсутствующий статус", - "UnsetParent": "Родительская задача будет убрана", - "AllProjects": "Все проекты", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "Обновлено {senderName}", - "IssueNotificationChanged": "{senderName} изменил {property}", - "IssueNotificationChangedProperty": "{senderName} изменил {property} на \"{newValue}\"", - "IssueNotificationMessage": "{senderName}: {message}", - "PreviousAssigned": "Ранее назначенные", - "IssueAssignedToYou": "Назначено вам", - "RelatedIssueTargetDescription": "Настройка проекта по умолчанию для Класса или пространства", - "MapRelatedIssues": "Настроить проекты по умолчанию для связанных задач", - "DefaultIssueStatus": "Статус по умолчанию", - "IssueStatus": "Статус", - "Extensions": "Дополнительно", - "UnsetParentIssue": "Снять родительскую задачу", - "ForbidCreateProjectPermission": "Запретить создание проекта", - "ForbidCreateProjectPermissionDescription": "Запрещает пользователям создавать новые проекты", - "AllowCreatingIssues": "Разрешить создание задач", - "Day": "День", - "Gantt": "Gantt", - "Month": "Месяц", - "Quarter": "Квартал", - "Week": "Неделя" - }, - "status": {} -} + "string": { + "TrackerApplication": "Трекер", + "Projects": "Проекты", + "More": "Больше", + "Default": "По умолчанию", + "MakeDefault": "Установить по умолчанию", + "Delete": "Удалить", + "Open": "Открыть", + "Members": "Участники", + "Inbox": "Входящие", + "ViewIssue": "Открыть задачу", + "IssueCreated": "Задача создана", + "MyIssues": "Мои задачи", + "Issues": "Задачи", + "Views": "Отображения", + "Active": "Активные", + "AllIssues": "Все задачи", + "ActiveIssues": "Активные задачи", + "BacklogIssues": "Пул задач", + "Backlog": "Пул задач", + "Board": "Канбан", + "Components": "Компоненты", + "AllComponents": "Все", + "BacklogComponents": "Пул задач", + "ActiveComponents": "Активные", + "ClosedComponents": "Закрытые", + "NewComponent": "Новый компонент", + "CreateComponent": "Создать компонент", + "ComponentNamePlaceholder": "Название компонента", + "ComponentDescriptionPlaceholder": "Описание (необязательно)", + "ComponentLead": "Руководитель", + "ComponentMembers": "Участники", + "StartDate": "Дата начала", + "TargetDate": "Дата завершения", + "Planned": "Запланирован", + "InProgress": "В работе", + "Paused": "Приостановлен", + "Completed": "Завершен", + "Canceled": "Отменено", + "CreateProject": "Новый проект", + "NewProject": "Новый проект", + "ProjectTitle": "Название проекта", + "ProjectTitlePlaceholder": "Новый проект", + "UsedInIssueIDs": "Используется в идентификаторах задач", + "Identifier": "Идентификатор", + "Import": "Импорт", + "ProjectIdentifier": "Идентификатор проекта", + "IdentifierExists": "Идентификатор уже существует проекта", + "ProjectIdentifierPlaceholder": "ПКТ", + "ChooseIcon": "Выбрать иконку", + "AddIssue": "Добавить задачу", + "NewIssue": "Новая задача", + "NewIssuePlaceholder": "Новая", + "ResumeDraft": "Восстановить черновик", + "SaveIssue": "Создать задачу", + "SetPriority": "Установить приоритет…", + "SetStatus": "Установить статус…", + "SelectIssue": "Выбрать задачу", + "Priority": "Приоритет", + "NoPriority": "Нет приоритета", + "Urgent": "Наивысший", + "High": "Высокий", + "Medium": "Средний", + "Low": "Низкий", + "Unassigned": "Не назначен", + "Back": "Назад", + "List": "Список", + "NumberLabels": "{count, plural, =0 {нет меток} one {# метка} few {# метки} other {# меток}}", + "CategoryBacklog": "Пул", + "CategoryUnstarted": "Не запущенные", + "CategoryStarted": "Запущенный", + "CategoryCompleted": "Завершенные", + "CategoryCanceled": "Отменённые", + "Title": "Заголовок", + "Name": "Имя", + "Description": "Описание", + "Status": "Статус", + "Number": "Номер", + "Assignee": "Исполнитель", + "AssignTo": "Назначить...", + "AssignedTo": "Назначен на {value}", + "Parent": "Родительская задача", + "SetParent": "Задать родительскую задачу…", + "ChangeParent": "Изменить родительскую задачу…", + "RemoveParent": "Удалить родительскую задачу", + "OpenParent": "Открыть родительскую задачу", + "SubIssues": "Подзадачи", + "SubIssuesList": "Подзадачи ({subIssues})", + "OpenSubIssues": "Открыть подзадачи", + "AddSubIssues": "Добавить подзадачу", + "BlockedBy": "Блокируется", + "RelatedTo": "Связано", + "Comments": "Комментарии", + "Attachments": "Вложения", + "Labels": "Метки", + "Component": "Компонент", + "Space": "", + "SetDueDate": "Указать срок выполнения…", + "ChangeDueDate": "Изменить срок выполнения…", + "ModificationDate": "Изменено {value}", + "Project": "Проект", + "Issue": "Задача", + "SubIssue": "Подзадача", + "Document": "", + "DocumentIcon": "", + "DocumentColor": "", + "Rank": "Ранг", + "TypeIssuePriority": "Приоритет задачи", + "IssueTitlePlaceholder": "Имя задачи", + "SubIssueTitlePlaceholder": "Имя подзадачи", + "IssueDescriptionPlaceholder": "Добавить описание…", + "SubIssueDescriptionPlaceholder": "Описание подзадачи", + "AddIssueTooltip": "Добавить задачу…", + "NewIssueDialogClose": "Вы действительно хотите закрыть окно?", + "NewIssueDialogCloseNote": "Все внесенные изменения будут потеряны", + "RemoveComponentDialogClose": "Удалить компонент?", + "RemoveComponentDialogCloseNote": "Уверены, что хотите удалить этот компонент? Эта операция не может быть отменена.", + "Grouping": "Группировка", + "Ordering": "Сортировка", + "CompletedIssues": "Завершённые задачи", + "NoGrouping": "Нет группировки", + "NoAssignee": "Нет исполнителя", + "LastUpdated": "Последнее обновление", + "DueDate": "Срок", + "IssueStartDate": "Дата начала", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "Пользовательский", + "All": "Все", + "PastWeek": "Предыдущая неделя", + "PastMonth": "Предыдущий месяц", + "CopyIssueUrl": "Копировать URL-адрес задачи в буфер обмена", + "CopyIssueId": "Копировать ID задачи в буфер обмена", + "CopyIssueBranch": "Копировать имя ветки Git в буфер обмена", + "CopyIssueTitle": "Копировать имя задачи в буфер обмена", + "AssetLabel": "Asset", + "AddToComponent": "Добавить в компонент…", + "MoveToComponent": "Переместить в компонент…", + "NoComponent": "Без компонента", + "ComponentLeadTitle": "Руководитель компонента", + "ComponentMembersTitle": "Участники компонента", + "ComponentLeadSearchPlaceholder": "Назначьте руководителя компонента…", + "ComponentMembersSearchPlaceholder": "Изменить участников компонента…", + "MoveToProject": "Изменить проект", + "Duplicate": "Дублировать", + "GotoIssues": "Перейти к задачам", + "GotoActive": "Перейти к активным задачам", + "GotoBacklog": "Перейти к пулу задач", + "GotoComponents": "Перейти к компоненту", + "GotoMyIssues": "Перейти к моим задачам", + "GotoTrackerApplication": "Перейти к приложению Трекер", + "CreatedOne": "Создана", + "MoveIssues": "Переместить задачи", + "MoveIssuesDescription": "Выберите проект, в который вы хотите переместить задачи", + "ManageAttributes": "Управление атрибутами", + "KeepOriginalAttributes": "Оставить оригинальные аттрибуты", + "KeepOriginalAttributesTooltip": "Оригинальные статусы и компоненты будут сохранены в новом проекте", + "SelectReplacement": "Следующие элементы не доступны в новом проекте. Выберите замену.", + "MissingItem": "ОТСУТСТВУЮЩИЙ ЭЛЕМЕНТ", + "Replacement": "ЗАМЕНА", + "Original": "ОРИГИНАЛ", + "OriginalDescription": "Элементы из этой секции будут созданы в новом проекте", + "Relations": "Зависимости", + "RemoveRelation": "Удалить зависимость...", + "AddBlockedBy": "Отметить как блокируемую...", + "AddIsBlocking": "Отметить как блокирующую...", + "AddRelatedIssue": "Связать с задачей...", + "RelatedIssue": "Связанная задача {id} - {title}", + "BlockedIssue": "Блокируемая задача {id} - {title}", + "BlockingIssue": "Блокирующая {id} - {title}", + "BlockedBySearchPlaceholder": "Поиск блокирующей задачи...", + "IsBlockingSearchPlaceholder": "Поиск блокируемой задачи...", + "RelatedIssueSearchPlaceholder": "Поиск связанной задачи...", + "Blocks": "Блокирует", + "Related": "Связан", + "RelatedIssues": "Связанные задачи", + "EditIssue": "Редактирование {title}", + "EditWorkflowStatuses": "Редактировать статусы задач", + "EditProject": "Редактировать проект", + "DeleteProject": "Удалить проект", + "ArchiveProjectName": "Архивировать проект {name}?", + "ArchiveProjectConfirm": "Вы действительно хотите заархивировать этот проект?", + "DeleteProjectConfirm": "Вы действительно хотите удалить этот проект со всем содержимым?", + "ProjectHasIssues": "Для данного проекта существуют задачи, уверены, что хотите заархивировать?", + "ManageWorkflowStatuses": "Управлять типами проекта", + "AddWorkflowStatus": "Добавить статус задачи", + "EditWorkflowStatus": "Редактировать статус задачи", + "DeleteWorkflowStatus": "Удалить статус задачи", + "DeleteWorkflowStatusConfirm": "Вы действительно хотите удалить \"{status}\" статус?", + "DeleteWorkflowStatusErrorDescription": "Статус \"{status}\" {count, plural, one {имеет # задача} few {имеет $ задачи} other {имеют # задач}}. Пожалуйста, выберите статус для перемещения.", + "Save": "Сохранить", + "IncludeItemsThatMatch": "Включить элементы, которые соответствуют", + "AnyFilter": "любому фильтру", + "AllFilters": "всем фильтрам", + "NoDescription": "Нет описания", + "SearchIssue": "Поиск задачи...", + "StatusHistory": "История состояний", + "NewSubIssue": "Добавить подзадачу...", + "AddLabel": "Добавить метку", + "DeleteIssue": "Удалить {issueCount, plural, =1 {задачу} one {# задачу} few {# задачи} other {задач}}", + "DeleteIssueConfirm": "Вы действительно хотите удалить {issueCount, plural, =1 {задачу} one {# задачу} few {# задачи} other {задач}}{subIssueCount, plural, =0 {?} =1 { и подзадачу?} one { и # подзадачу?} other { и # подзадач?}}", + "Milestone": "Этап", + "NoMilestone": "Без Этапа", + "MoveToMilestone": "Выбрать Этап", + "Milestones": "Этапы", + "AllMilestones": "Все", + "PlannedMilestones": "Запланировано", + "ActiveMilestones": "Активно", + "ClosedMilestones": "Завершено", + "AddToMilestone": "Добавить в Этап", + "MilestoneNamePlaceholder": "Название Этапа", + "NewMilestone": "Новый Этап", + "CreateMilestone": "Создать", + "MoveAndDeleteMilestone": "Переместить Задачи в {newMilestone} и Удалить {deleteMilestone}", + "MoveAndDeleteMilestoneConfirm": "Вы действительно хотите удалить этап и перенести задачи в другой?", + "Estimation": "Оценка", + "ReportedTime": "Потраченное времени", + "RemainingTime": "Осталось времени", + "TimeSpendReports": "Отчеты по времени", + "TimeSpendReport": "Время", + "TimeSpendReportAdd": "Добавить затраченное время", + "TimeSpendReportDate": "Дата", + "TimeSpendReportValue": "Затраченное время", + "TimeSpendReportValueTooltip": "Затраченное время в человеко днях", + "TimeSpendReportDescription": "Описание", + "TimeSpendDays": "{value} д", + "TimeSpendHours": "{value} ч", + "TimeSpendMinutes": "{value} м", + "ChildEstimation": "Оценка подзадач", + "ChildReportedTime": "Время подзадач", + "CapacityValue": "из {value} д", + "NewRelatedIssue": "Завести связанную задачу", + "RelatedIssuesNotFound": "Связанные задачи не найдены", + "AddedReference": "Добавлена зависимость", + "AddedAsBlocked": "Отмечено как заблокировано", + "AddedAsBlocking": "Отмечено как блокирующее", + "IssueTemplate": "Шаблон", + "IssueTemplates": "Шаблоны", + "NewProcess": "Новый шаблон", + "SaveProcess": "Сохранить шаблон", + "NoIssueTemplate": "Шаблон не задан", + "TemplateReplace": "Вы хотите применить выбранный шаблон?", + "TemplateReplaceConfirm": "Все внесенные изменения в будут заменены значениями из нового шаблона", + "Apply": "Применить", + "CurrentWorkDay": "Текущий Рабочий День", + "PreviousWorkDay": "Предыдущий Рабочий День", + "TimeReportDayTypeLabel": "Выберите тип дня для временного отчета", + "DefaultAssignee": "Исполнитель задачи по умолчанию", + "SevenHoursLength": "Семь Часов", + "EightHoursLength": "Восемь Часов", + "HourLabel": "ч", + "MinuteLabel": "м", + "Saved": "Сохранено...", + "CreatedIssue": "Создал(а) задачу", + "CreatedSubIssue": "Создал(а) подзадачу", + "ChangeStatus": "Изменение статуса", + "ConfigLabel": "Трекер", + "ConfigDescription": "Расширение по управлению задачами, чтобы все было в срок.", + "NoStatusFound": "Статус не найдет", + "CreateMissingStatus": "Создать отсутствующий статус", + "UnsetParent": "Родительская задача будет убрана", + "AllProjects": "Все проекты", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "Обновлено {senderName}", + "IssueNotificationChanged": "{senderName} изменил {property}", + "IssueNotificationChangedProperty": "{senderName} изменил {property} на \"{newValue}\"", + "IssueNotificationMessage": "{senderName}: {message}", + "PreviousAssigned": "Ранее назначенные", + "IssueAssignedToYou": "Назначено вам", + "RelatedIssueTargetDescription": "Настройка проекта по умолчанию для Класса или пространства", + "MapRelatedIssues": "Настроить проекты по умолчанию для связанных задач", + "DefaultIssueStatus": "Статус по умолчанию", + "IssueStatus": "Статус", + "Extensions": "Дополнительно", + "UnsetParentIssue": "Снять родительскую задачу", + "ForbidCreateProjectPermission": "Запретить создание проекта", + "ForbidCreateProjectPermissionDescription": "Запрещает пользователям создавать новые проекты", + "AllowCreatingIssues": "Разрешить создание задач", + "Day": "День", + "Gantt": "Gantt", + "Month": "Месяц", + "Quarter": "Квартал", + "Week": "Неделя", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-assets/lang/zh.json b/plugins/tracker-assets/lang/zh.json index 7bf3bc34d7c..80679f10f1e 100644 --- a/plugins/tracker-assets/lang/zh.json +++ b/plugins/tracker-assets/lang/zh.json @@ -1,304 +1,289 @@ { - "string": { - "TrackerApplication": "追踪器", - "Projects": "您的项目", - "More": "更多", - "Default": "默认", - "MakeDefault": "设为默认", - "Delete": "删除", - "Open": "打开", - "Members": "成员", - "Inbox": "收件箱", - "MyIssues": "我的问题", - "ViewIssue": "查看问题", - "IssueCreated": "问题已创建", - "Issues": "问题", - "Views": "视图", - "Active": "活跃", - "AllIssues": "所有问题", - "ActiveIssues": "活跃问题", - "BacklogIssues": "积压", - "Backlog": "积压", - "Board": "看板", - "Components": "组件", - "AllComponents": "全部", - "BacklogComponents": "积压", - "ActiveComponents": "活跃", - "ClosedComponents": "已关闭", - "NewComponent": "新组件", - "CreateComponent": "创建组件", - "ComponentNamePlaceholder": "组件名称", - "ComponentDescriptionPlaceholder": "描述(可选)", - "ComponentLead": "负责人", - "ComponentMembers": "成员", - "StartDate": "开始日期", - "TargetDate": "目标日期", - "Planned": "已计划", - "InProgress": "进行中", - "Paused": "暂停", - "Completed": "已完成", - "Canceled": "已取消", - "CreateProject": "创建项目", - "NewProject": "新项目", - "ProjectTitle": "项目标题", - "ProjectTitlePlaceholder": "新项目", - "UsedInIssueIDs": "引用问题ID", - "Identifier": "标识符", - "Import": "导入", - "ProjectIdentifier": "项目标识符", - "IdentifierExists": "项目标识符已存在", - "ProjectIdentifierPlaceholder": "PRJCT", - "ChooseIcon": "选择图标", - "AddIssue": "添加问题", - "NewIssue": "新问题", - "NewIssuePlaceholder": "新问题", - "ResumeDraft": "恢复草稿", - "SaveIssue": "创建问题", - "SetPriority": "设置优先级…", - "SetStatus": "设置状态…", - "SelectIssue": "选择问题", - "Priority": "优先级", - "NoPriority": "无优先级", - "Urgent": "紧急", - "High": "高", - "Medium": "中", - "Low": "低", - "Unassigned": "未分配", - "Back": "返回", - "List": "列表", - "NumberLabels": "{count, plural, =0 {无标签} =1 {1 个标签} other {# 个标签}}", - - "CategoryBacklog": "积压", - "CategoryUnstarted": "未开始", - "CategoryStarted": "已开始", - "CategoryCompleted": "已完成", - "CategoryCanceled": "已取消", - - "Title": "标题", - "Name": "名称", - "Description": "描述", - "Status": "状态", - "Number": "编号", - "Assignee": "受理人", - "AssignTo": "分配给…", - "AssignedTo": "分配给 {value}", - "Parent": "父问题", - "SetParent": "设置父问题…", - "ChangeParent": "更改父问题…", - "RemoveParent": "移除父问题", - "OpenParent": "打开父问题", - "SubIssues": "子问题", - "SubIssuesList": "子问题 ({subIssues})", - "OpenSubIssues": "打开子问题", - "AddSubIssues": "添加子问题", - "BlockedBy": "被阻塞", - "RelatedTo": "相关", - "Comments": "评论", - "Attachments": "附件", - "Labels": "标签", - "Component": "组件", - "Space": "", - "SetDueDate": "设置截止日期…", - "ChangeDueDate": "更改截止日期…", - "ModificationDate": "更新于 {value}", - "Project": "项目", - "Issue": "问题", - "SubIssue": "子问题", - "Document": "", - "DocumentIcon": "", - "DocumentColor": "", - "Rank": "排名", - "TypeIssuePriority": "问题优先级", - "IssueTitlePlaceholder": "问题标题", - "SubIssueTitlePlaceholder": "子问题标题", - "IssueDescriptionPlaceholder": "添加描述…", - "SubIssueDescriptionPlaceholder": "添加子问题描述", - "AddIssueTooltip": "添加问题…", - "NewIssueDialogClose": "您想要关闭此对话框吗?", - "NewIssueDialogCloseNote": "所有更改将会丢失", - "RemoveComponentDialogClose": "删除组件?", - "RemoveComponentDialogCloseNote": "您确定要删除此组件吗?此操作无法撤销", - "Grouping": "分组", - "Ordering": "排序", - "CompletedIssues": "已完成问题", - "NoGrouping": "无分组", - "NoAssignee": "无受理人", - "LastUpdated": "最后更新", - "DueDate": "截止日期", - "IssueStartDate": "开始日期", - "GanttDependency": "Dependency", - "GanttLag": "Lag", - "Manual": "手动", - "All": "全部", - "PastWeek": "过去一周", - "PastMonth": "过去一个月", - "CopyIssueUrl": "复制问题 URL 到剪贴板", - "CopyIssueId": "复制问题 ID 到剪贴板", - "CopyIssueBranch": "复制 Git 分支名称到剪贴板", - "CopyIssueTitle": "复制问题标题到剪贴板", - "AssetLabel": "资产", - "AddToComponent": "添加到组件…", - "MoveToComponent": "移动到组件…", - "NoComponent": "无组件", - "ComponentLeadTitle": "组件负责人", - "ComponentMembersTitle": "组件成员", - "ComponentLeadSearchPlaceholder": "设置组件负责人…", - "ComponentMembersSearchPlaceholder": "更改组件成员…", - "MoveToProject": "移动到项目", - "Duplicate": "复制", - - "GotoIssues": "前往问题", - "GotoActive": "前往活跃问题", - "GotoBacklog": "前往积压", - "GotoComponents": "前往组件", - "GotoMyIssues": "前往我的问题", - "GotoTrackerApplication": "切换到追踪器应用", - - "CreatedOne": "已创建", - "MoveIssues": "移动问题", - "MoveIssuesDescription": "选择要移动问题的项目", - "ManageAttributes": "管理属性", - "KeepOriginalAttributes": "保留原始属性", - "KeepOriginalAttributesTooltip": "原始问题状态和组件将在新项目中保留", - "SelectReplacement": "以下项目在新项目中不可用。请选择替换项。", - "MissingItem": "缺失项目", - "Replacement": "替换项", - "Original": "原始", - "OriginalDescription": "此部分的项目将在新项目中创建", - - "Relations": "关系", - "RemoveRelation": "移除关系…", - "AddBlockedBy": "标记为被阻塞…", - "AddIsBlocking": "标记为阻塞…", - "AddRelatedIssue": "引用其他问题…", - "RelatedIssue": "相关问题 {id} - {title}", - "BlockedIssue": "被阻塞的问题 {id} - {title}", - "BlockingIssue": "阻塞的问题 {id} - {title}", - "BlockedBySearchPlaceholder": "搜索要标记为被阻塞的问题…", - "IsBlockingSearchPlaceholder": "搜索要标记为阻塞的问题…", - "RelatedIssueSearchPlaceholder": "搜索要引用的问题…", - "Blocks": "阻塞", - "Related": "相关", - "RelatedIssues": "相关问题", - - "EditIssue": "编辑 {title}", - "EditWorkflowStatuses": "编辑问题状态", - "EditProject": "编辑项目", - "DeleteProject": "删除项目", - "ArchiveProjectName": "归档项目 {name}?", - "ArchiveProjectConfirm": "您想要归档此项目吗?", - "DeleteProjectConfirm": "您想要删除此项目及所有问题吗?", - "ProjectHasIssues": "此项目中存在问题,您确定要归档吗?", - "ManageWorkflowStatuses": "管理项目类型", - "AddWorkflowStatus": "添加问题状态", - "EditWorkflowStatus": "编辑问题状态", - "DeleteWorkflowStatus": "删除问题状态", - "DeleteWorkflowStatusConfirm": "您想要删除“{status}”状态吗?", - "DeleteWorkflowStatusErrorDescription": "“{status}”状态有 {count, plural, =1 {1 个问题} other {# 个问题}} 分配。请选择一个状态进行移动", - - "Save": "保存", - "IncludeItemsThatMatch": "包括符合条件的项目", - "AnyFilter": "任何过滤器", - "AllFilters": "所有过滤器", - "NoDescription": "无描述", - "SearchIssue": "搜索任务…", - - "StatusHistory": "状态历史", - "NewSubIssue": "添加子问题…", - "AddLabel": "添加标签", - - "DeleteIssue": "删除 {issueCount, plural, =1 {问题} other {# 个问题}}", - "DeleteIssueConfirm": "您想要删除 {issueCount, plural, =1 {问题} other {问题}}{subIssueCount, plural, =0 {?} =1 { 和子问题?} other { 和子问题?}}", - - "Milestone": "里程碑", - "NoMilestone": "无里程碑", - "MoveToMilestone": "选择里程碑", - "Milestones": "里程碑", - "AllMilestones": "全部", - "PlannedMilestones": "已计划", - "ActiveMilestones": "活跃", - "ClosedMilestones": "已完成", - "AddToMilestone": "添加到里程碑", - "MilestoneNamePlaceholder": "里程碑名称", - - "NewMilestone": "新里程碑", - "CreateMilestone": "创建", - - "MoveAndDeleteMilestone": "将问题移动到 {newMilestone} 并删除 {deleteMilestone}", - "MoveAndDeleteMilestoneConfirm": "您想要删除里程碑并将问题移动到其他里程碑吗?", - - "Estimation": "估算", - "ReportedTime": "花费时间", - "RemainingTime": "剩余时间", - "TimeSpendReports": "花费时间报告", - "TimeSpendReport": "时间", - "TimeSpendReportAdd": "添加时间报告", - "TimeSpendReportDate": "日期", - "TimeSpendReportValue": "花费时间", - "TimeSpendReportValueTooltip": "花费时间(小时)", - "TimeSpendReportDescription": "描述", - "TimeSpendDays": "{value} 天", - "TimeSpendHours": "{value} 小时", - "TimeSpendMinutes": "{value} 分钟", - "ChildEstimation": "子问题估算", - "ChildReportedTime": "子问题时间", - "CapacityValue": "共 {value} 天", - "NewRelatedIssue": "新相关问题", - "RelatedIssuesNotFound": "未找到相关问题", - - "AddedReference": "添加了引用", - "AddedAsBlocked": "标记为被阻塞", - "AddedAsBlocking": "标记为阻塞", - - "IssueTemplate": "模板", - "IssueTemplates": "模板", - "NewProcess": "新模板", - "SaveProcess": "保存模板", - "NoIssueTemplate": "无模板", - "TemplateReplace": "您想要应用新模板吗?", - "TemplateReplaceConfirm": "所有字段将被新模板值覆盖", - "Apply": "应用", - - "CurrentWorkDay": "当前工作日", - "PreviousWorkDay": "前一个工作日", - "TimeReportDayTypeLabel": "选择时间报告日类型", - "DefaultAssignee": "默认问题受理人", - - "SevenHoursLength": "七小时", - "EightHoursLength": "八小时", - "HourLabel": "小时", - "MinuteLabel": "分", - "Saved": "已保存...", - "CreatedIssue": "已创建问题", - "CreatedSubIssue": "已创建子问题", - "ChangeStatus": "更改状态", - "ConfigLabel": "追踪器", - "ConfigDescription": "扩展用于管理工作项并完成所有工作。", - "NoStatusFound": "未找到匹配的状态", - "CreateMissingStatus": "创建缺失的状态", - "UnsetParent": "父问题将被取消设置", - "AllProjects": "所有项目", - "IssueNotificationTitle": "{issueTitle}", - "IssueNotificationBody": "由 {senderName} 更新", - "IssueNotificationChanged": "{senderName} 更改了 {property}", - "IssueNotificationChangedProperty": "{senderName} 将 {property} 更改为 \"{newValue}\"", - "IssueNotificationMessage": "{senderName}:{message}", - "PreviousAssigned": "以前分配的", - "IssueAssignedToYou": "分配给您", - "RelatedIssueTargetDescription": "相关问题项目目标用于类别或空间", - "MapRelatedIssues": "配置相关问题默认项目", - "DefaultIssueStatus": "默认问题状态", - "IssueStatus": "状态", - "Extensions": "扩展", - "UnsetParentIssue": "取消父问题", - "ForbidCreateProjectPermission": "禁止创建项目", - "ForbidCreateProjectPermissionDescription": "禁止用户创建新项目", - "AllowCreatingIssues": "允许创建问题", - "Day": "日", - "Gantt": "Gantt", - "Month": "月", - "Quarter": "季度", - "Week": "周" - }, - "status": {} -} + "string": { + "TrackerApplication": "追踪器", + "Projects": "您的项目", + "More": "更多", + "Default": "默认", + "MakeDefault": "设为默认", + "Delete": "删除", + "Open": "打开", + "Members": "成员", + "Inbox": "收件箱", + "MyIssues": "我的问题", + "ViewIssue": "查看问题", + "IssueCreated": "问题已创建", + "Issues": "问题", + "Views": "视图", + "Active": "活跃", + "AllIssues": "所有问题", + "ActiveIssues": "活跃问题", + "BacklogIssues": "积压", + "Backlog": "积压", + "Board": "看板", + "Components": "组件", + "AllComponents": "全部", + "BacklogComponents": "积压", + "ActiveComponents": "活跃", + "ClosedComponents": "已关闭", + "NewComponent": "新组件", + "CreateComponent": "创建组件", + "ComponentNamePlaceholder": "组件名称", + "ComponentDescriptionPlaceholder": "描述(可选)", + "ComponentLead": "负责人", + "ComponentMembers": "成员", + "StartDate": "开始日期", + "TargetDate": "目标日期", + "Planned": "已计划", + "InProgress": "进行中", + "Paused": "暂停", + "Completed": "已完成", + "Canceled": "已取消", + "CreateProject": "创建项目", + "NewProject": "新项目", + "ProjectTitle": "项目标题", + "ProjectTitlePlaceholder": "新项目", + "UsedInIssueIDs": "引用问题ID", + "Identifier": "标识符", + "Import": "导入", + "ProjectIdentifier": "项目标识符", + "IdentifierExists": "项目标识符已存在", + "ProjectIdentifierPlaceholder": "PRJCT", + "ChooseIcon": "选择图标", + "AddIssue": "添加问题", + "NewIssue": "新问题", + "NewIssuePlaceholder": "新问题", + "ResumeDraft": "恢复草稿", + "SaveIssue": "创建问题", + "SetPriority": "设置优先级…", + "SetStatus": "设置状态…", + "SelectIssue": "选择问题", + "Priority": "优先级", + "NoPriority": "无优先级", + "Urgent": "紧急", + "High": "高", + "Medium": "中", + "Low": "低", + "Unassigned": "未分配", + "Back": "返回", + "List": "列表", + "NumberLabels": "{count, plural, =0 {无标签} =1 {1 个标签} other {# 个标签}}", + "CategoryBacklog": "积压", + "CategoryUnstarted": "未开始", + "CategoryStarted": "已开始", + "CategoryCompleted": "已完成", + "CategoryCanceled": "已取消", + "Title": "标题", + "Name": "名称", + "Description": "描述", + "Status": "状态", + "Number": "编号", + "Assignee": "受理人", + "AssignTo": "分配给…", + "AssignedTo": "分配给 {value}", + "Parent": "父问题", + "SetParent": "设置父问题…", + "ChangeParent": "更改父问题…", + "RemoveParent": "移除父问题", + "OpenParent": "打开父问题", + "SubIssues": "子问题", + "SubIssuesList": "子问题 ({subIssues})", + "OpenSubIssues": "打开子问题", + "AddSubIssues": "添加子问题", + "BlockedBy": "被阻塞", + "RelatedTo": "相关", + "Comments": "评论", + "Attachments": "附件", + "Labels": "标签", + "Component": "组件", + "Space": "", + "SetDueDate": "设置截止日期…", + "ChangeDueDate": "更改截止日期…", + "ModificationDate": "更新于 {value}", + "Project": "项目", + "Issue": "问题", + "SubIssue": "子问题", + "Document": "", + "DocumentIcon": "", + "DocumentColor": "", + "Rank": "排名", + "TypeIssuePriority": "问题优先级", + "IssueTitlePlaceholder": "问题标题", + "SubIssueTitlePlaceholder": "子问题标题", + "IssueDescriptionPlaceholder": "添加描述…", + "SubIssueDescriptionPlaceholder": "添加子问题描述", + "AddIssueTooltip": "添加问题…", + "NewIssueDialogClose": "您想要关闭此对话框吗?", + "NewIssueDialogCloseNote": "所有更改将会丢失", + "RemoveComponentDialogClose": "删除组件?", + "RemoveComponentDialogCloseNote": "您确定要删除此组件吗?此操作无法撤销", + "Grouping": "分组", + "Ordering": "排序", + "CompletedIssues": "已完成问题", + "NoGrouping": "无分组", + "NoAssignee": "无受理人", + "LastUpdated": "最后更新", + "DueDate": "截止日期", + "IssueStartDate": "开始日期", + "GanttDependency": "Dependency", + "GanttLag": "Lag", + "Manual": "手动", + "All": "全部", + "PastWeek": "过去一周", + "PastMonth": "过去一个月", + "CopyIssueUrl": "复制问题 URL 到剪贴板", + "CopyIssueId": "复制问题 ID 到剪贴板", + "CopyIssueBranch": "复制 Git 分支名称到剪贴板", + "CopyIssueTitle": "复制问题标题到剪贴板", + "AssetLabel": "资产", + "AddToComponent": "添加到组件…", + "MoveToComponent": "移动到组件…", + "NoComponent": "无组件", + "ComponentLeadTitle": "组件负责人", + "ComponentMembersTitle": "组件成员", + "ComponentLeadSearchPlaceholder": "设置组件负责人…", + "ComponentMembersSearchPlaceholder": "更改组件成员…", + "MoveToProject": "移动到项目", + "Duplicate": "复制", + "GotoIssues": "前往问题", + "GotoActive": "前往活跃问题", + "GotoBacklog": "前往积压", + "GotoComponents": "前往组件", + "GotoMyIssues": "前往我的问题", + "GotoTrackerApplication": "切换到追踪器应用", + "CreatedOne": "已创建", + "MoveIssues": "移动问题", + "MoveIssuesDescription": "选择要移动问题的项目", + "ManageAttributes": "管理属性", + "KeepOriginalAttributes": "保留原始属性", + "KeepOriginalAttributesTooltip": "原始问题状态和组件将在新项目中保留", + "SelectReplacement": "以下项目在新项目中不可用。请选择替换项。", + "MissingItem": "缺失项目", + "Replacement": "替换项", + "Original": "原始", + "OriginalDescription": "此部分的项目将在新项目中创建", + "Relations": "关系", + "RemoveRelation": "移除关系…", + "AddBlockedBy": "标记为被阻塞…", + "AddIsBlocking": "标记为阻塞…", + "AddRelatedIssue": "引用其他问题…", + "RelatedIssue": "相关问题 {id} - {title}", + "BlockedIssue": "被阻塞的问题 {id} - {title}", + "BlockingIssue": "阻塞的问题 {id} - {title}", + "BlockedBySearchPlaceholder": "搜索要标记为被阻塞的问题…", + "IsBlockingSearchPlaceholder": "搜索要标记为阻塞的问题…", + "RelatedIssueSearchPlaceholder": "搜索要引用的问题…", + "Blocks": "阻塞", + "Related": "相关", + "RelatedIssues": "相关问题", + "EditIssue": "编辑 {title}", + "EditWorkflowStatuses": "编辑问题状态", + "EditProject": "编辑项目", + "DeleteProject": "删除项目", + "ArchiveProjectName": "归档项目 {name}?", + "ArchiveProjectConfirm": "您想要归档此项目吗?", + "DeleteProjectConfirm": "您想要删除此项目及所有问题吗?", + "ProjectHasIssues": "此项目中存在问题,您确定要归档吗?", + "ManageWorkflowStatuses": "管理项目类型", + "AddWorkflowStatus": "添加问题状态", + "EditWorkflowStatus": "编辑问题状态", + "DeleteWorkflowStatus": "删除问题状态", + "DeleteWorkflowStatusConfirm": "您想要删除“{status}”状态吗?", + "DeleteWorkflowStatusErrorDescription": "“{status}”状态有 {count, plural, =1 {1 个问题} other {# 个问题}} 分配。请选择一个状态进行移动", + "Save": "保存", + "IncludeItemsThatMatch": "包括符合条件的项目", + "AnyFilter": "任何过滤器", + "AllFilters": "所有过滤器", + "NoDescription": "无描述", + "SearchIssue": "搜索任务…", + "StatusHistory": "状态历史", + "NewSubIssue": "添加子问题…", + "AddLabel": "添加标签", + "DeleteIssue": "删除 {issueCount, plural, =1 {问题} other {# 个问题}}", + "DeleteIssueConfirm": "您想要删除 {issueCount, plural, =1 {问题} other {问题}}{subIssueCount, plural, =0 {?} =1 { 和子问题?} other { 和子问题?}}", + "Milestone": "里程碑", + "NoMilestone": "无里程碑", + "MoveToMilestone": "选择里程碑", + "Milestones": "里程碑", + "AllMilestones": "全部", + "PlannedMilestones": "已计划", + "ActiveMilestones": "活跃", + "ClosedMilestones": "已完成", + "AddToMilestone": "添加到里程碑", + "MilestoneNamePlaceholder": "里程碑名称", + "NewMilestone": "新里程碑", + "CreateMilestone": "创建", + "MoveAndDeleteMilestone": "将问题移动到 {newMilestone} 并删除 {deleteMilestone}", + "MoveAndDeleteMilestoneConfirm": "您想要删除里程碑并将问题移动到其他里程碑吗?", + "Estimation": "估算", + "ReportedTime": "花费时间", + "RemainingTime": "剩余时间", + "TimeSpendReports": "花费时间报告", + "TimeSpendReport": "时间", + "TimeSpendReportAdd": "添加时间报告", + "TimeSpendReportDate": "日期", + "TimeSpendReportValue": "花费时间", + "TimeSpendReportValueTooltip": "花费时间(小时)", + "TimeSpendReportDescription": "描述", + "TimeSpendDays": "{value} 天", + "TimeSpendHours": "{value} 小时", + "TimeSpendMinutes": "{value} 分钟", + "ChildEstimation": "子问题估算", + "ChildReportedTime": "子问题时间", + "CapacityValue": "共 {value} 天", + "NewRelatedIssue": "新相关问题", + "RelatedIssuesNotFound": "未找到相关问题", + "AddedReference": "添加了引用", + "AddedAsBlocked": "标记为被阻塞", + "AddedAsBlocking": "标记为阻塞", + "IssueTemplate": "模板", + "IssueTemplates": "模板", + "NewProcess": "新模板", + "SaveProcess": "保存模板", + "NoIssueTemplate": "无模板", + "TemplateReplace": "您想要应用新模板吗?", + "TemplateReplaceConfirm": "所有字段将被新模板值覆盖", + "Apply": "应用", + "CurrentWorkDay": "当前工作日", + "PreviousWorkDay": "前一个工作日", + "TimeReportDayTypeLabel": "选择时间报告日类型", + "DefaultAssignee": "默认问题受理人", + "SevenHoursLength": "七小时", + "EightHoursLength": "八小时", + "HourLabel": "小时", + "MinuteLabel": "分", + "Saved": "已保存...", + "CreatedIssue": "已创建问题", + "CreatedSubIssue": "已创建子问题", + "ChangeStatus": "更改状态", + "ConfigLabel": "追踪器", + "ConfigDescription": "扩展用于管理工作项并完成所有工作。", + "NoStatusFound": "未找到匹配的状态", + "CreateMissingStatus": "创建缺失的状态", + "UnsetParent": "父问题将被取消设置", + "AllProjects": "所有项目", + "IssueNotificationTitle": "{issueTitle}", + "IssueNotificationBody": "由 {senderName} 更新", + "IssueNotificationChanged": "{senderName} 更改了 {property}", + "IssueNotificationChangedProperty": "{senderName} 将 {property} 更改为 \"{newValue}\"", + "IssueNotificationMessage": "{senderName}:{message}", + "PreviousAssigned": "以前分配的", + "IssueAssignedToYou": "分配给您", + "RelatedIssueTargetDescription": "相关问题项目目标用于类别或空间", + "MapRelatedIssues": "配置相关问题默认项目", + "DefaultIssueStatus": "默认问题状态", + "IssueStatus": "状态", + "Extensions": "扩展", + "UnsetParentIssue": "取消父问题", + "ForbidCreateProjectPermission": "禁止创建项目", + "ForbidCreateProjectPermissionDescription": "禁止用户创建新项目", + "AllowCreatingIssues": "允许创建问题", + "Day": "日", + "Gantt": "Gantt", + "Month": "月", + "Quarter": "季度", + "Week": "周", + "GanttShowIssueCode": "Show issue code", + "GanttShowTitle": "Show title" + }, + "status": {} +} \ No newline at end of file diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index f48f2b05343..a8eaa00985b 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -64,9 +64,12 @@ const ZOOM_LEVELS: readonly ZoomLevel[] = ['day', 'week', 'month', 'quarter'] let userSidebarWidth: number = DEFAULT_SIDEBAR_WIDTH - let showIssueCode: boolean = true - let showTitle: boolean = true - let showSettings: boolean = false + + // Sidebar column visibility is wired to two ToggleViewOptions registered + // in models/tracker/src/viewlets.ts (Customize-View dropdown). Issue-code + // defaults OFF — the code is still surfaced in the hover tooltip. + $: showIssueCode = (viewOptions as Record)?.ganttShowIssueCode === true + $: showTitle = ((viewOptions as Record)?.ganttShowTitle ?? true) !== false function setZoom (z: ZoomLevel): void { zoom = z @@ -215,6 +218,10 @@ } } + function issueCode (i: Issue): string { + return (i as unknown as { identifier?: string }).identifier ?? 'Issue' + } + function onIssueOpen (e: CustomEvent<{ issue: { _id: string, _class: string } }>): void { showPanel( tracker.component.EditIssue, @@ -367,26 +374,7 @@ >{z[0].toUpperCase() + z.slice(1)} {/each}
-
- - {#if showSettings} -
- - -
- {/if} -
+
@@ -470,7 +458,8 @@ {/if}
Target: {new Date(ms.targetDate).toISOString().slice(0, 10)}
{:else if issue !== null} -
Issue
+ {@const code = issueCode(issue)} +
{code}
{issue.title}
{#if issue.startDate !== null}
Start: {new Date(issue.startDate).toISOString().slice(0, 10)}
diff --git a/plugins/tracker-resources/src/plugin.ts b/plugins/tracker-resources/src/plugin.ts index fc1f85177ad..28533dfd662 100644 --- a/plugins/tracker-resources/src/plugin.ts +++ b/plugins/tracker-resources/src/plugin.ts @@ -315,7 +315,9 @@ export default mergeIds(trackerId, tracker, { Gantt: '' as IntlString, Month: '' as IntlString, Quarter: '' as IntlString, - Week: '' as IntlString + Week: '' as IntlString, + GanttShowIssueCode: '' as IntlString, + GanttShowTitle: '' as IntlString }, component: { NopeComponent: '' as AnyComponent, From 29961278fd67838fd2453b1715ba14b70e33ccd1 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 026/248] fix(tracker-resources): clip GanttSidebar inside its host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: .gantt-sidebar rendered at its natural height (≈ rows × 36px), and .sidebar-host had overflow:visible, so for many issues the sidebar visually overflowed gantt-body which in turn made gantt-body's scrollHeight balloon to the sidebar's natural height. The result was that scrolling over the sidebar appeared to scroll the page area instead of just the canvas. Fix: - .sidebar-host now overflow:hidden + height:100% + flex-column - .gantt-sidebar height:100% + flex:1 1 auto so it fills the host rather than stretching past it Verified: gantt-body scrollHeight == clientHeight, only .canvas-scroller has scrollable content. Wheel-forward over the sidebar moves canvas-scroller and the sidebar transform together. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttSidebar.svelte | 3 ++- .../src/components/gantt/GanttView.svelte | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte index a972e69d92a..82b6b028320 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte @@ -167,13 +167,14 @@ From 1ddb3c58f95eb75047084aeff5962d9bdf2cb244 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 031/248] fix(tracker-resources): resize handle no longer fights canvas pan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sidebar resize handle and the canvas pointer-pan handler both lived on the gantt-scroller, so dragging the handle started a canvas pan in parallel — the result was visible stutter and barely-controllable resize. Two fixes: - Add .resize-cell to onCanvasPanStart's element exclusion list so the pan never starts when the handle is the pointerdown target. - The resize-cell's own pointer handlers now stopPropagation + preventDefault, so even if a sibling listener picked up the bubbled event it would not double-process it. setPointerCapture stays so the drag tracks the pointer outside the cell bounds. Plus the v16 toolbar consolidation: - Move the always-visible blue + button to IssuesView.svelte (the shared wrapper for List, Kanban and Gantt). Single source — no duplication per viewlet. - NewIssueHeader stays as the original HeaderButton-based component; inline category-level + buttons in the issue list are unaffected. Signed-off-by: Michael Uray --- .../src/components/NewIssueHeader.svelte | 153 +++++++----------- .../src/components/gantt/GanttView.svelte | 12 +- .../src/components/issues/IssuesView.svelte | 14 +- 3 files changed, 78 insertions(+), 101 deletions(-) diff --git a/plugins/tracker-resources/src/components/NewIssueHeader.svelte b/plugins/tracker-resources/src/components/NewIssueHeader.svelte index b1e73a6fa2c..3078a2168d2 100644 --- a/plugins/tracker-resources/src/components/NewIssueHeader.svelte +++ b/plugins/tracker-resources/src/components/NewIssueHeader.svelte @@ -14,10 +14,10 @@ --> -{#if loading} - -{:else if projectExists || draftExists} -
- -
-{:else} -
-
-{/if} + $: updateActions(draftExists, projectExists, closed) + - + diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index 44f6b07839f..d629fbf802c 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -271,9 +271,10 @@ function onCanvasPanStart (e: PointerEvent): void { if (scrollerEl === undefined) return // Only pan with primary mouse button on empty space — let SVG/HTML - // children (bars, buttons, links) handle their own click/dblclick. + // children (bars, buttons, links, resize-handle) handle their own + // click/drag without competing with the canvas pan. const target = e.target as HTMLElement - if (target.closest('.bar-wrap, button, a, .toggle-btn, .jump-btn, .settings-popover')) return + if (target.closest('.bar-wrap, button, a, .toggle-btn, .jump-btn, .settings-popover, .resize-cell')) return panning = true panStartX = e.clientX panStartY = e.clientY @@ -296,6 +297,8 @@ let resizeStartX = 0 let resizeStartWidth = 0 function onResizeStart (e: PointerEvent): void { + e.stopPropagation() + e.preventDefault() resizing = true resizeStartX = e.clientX resizeStartWidth = userSidebarWidth @@ -303,13 +306,16 @@ } function onResizeMove (e: PointerEvent): void { if (!resizing) return + e.stopPropagation() const next = resizeStartWidth + (e.clientX - resizeStartX) userSidebarWidth = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, next)) - queueMicrotask(syncViewport) } function onResizeEnd (e: PointerEvent): void { + if (!resizing) return + e.stopPropagation() resizing = false ;(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId) + queueMicrotask(syncViewport) } let resizeObs: ResizeObserver | undefined diff --git a/plugins/tracker-resources/src/components/issues/IssuesView.svelte b/plugins/tracker-resources/src/components/issues/IssuesView.svelte index 72af1e78e9f..b65d4950b91 100644 --- a/plugins/tracker-resources/src/components/issues/IssuesView.svelte +++ b/plugins/tracker-resources/src/components/issues/IssuesView.svelte @@ -3,12 +3,16 @@ import { Asset, IntlString, translateCB } from '@hcengineering/platform' import { ComponentExtensions } from '@hcengineering/presentation' import { Issue, TrackerEvents } from '@hcengineering/tracker' - import { IModeSelector, themeStore } from '@hcengineering/ui' + import { Button, IconAdd, IModeSelector, showPopup, themeStore } from '@hcengineering/ui' import { ViewOptions, Viewlet } from '@hcengineering/view' import { FilterBar, SpaceHeader, ViewletContentView, ViewletSettingButton } from '@hcengineering/view-resources' import tracker from '../../plugin' import CreateIssue from '../CreateIssue.svelte' + function newIssue (): void { + showPopup(CreateIssue, { space, shouldSaveDraft: true }, 'top') + } + export let space: Ref | undefined = undefined export let query: DocumentQuery = {} export let title: IntlString | undefined = undefined @@ -65,6 +69,14 @@ extension={tracker.extensions.IssueListHeader} props={{ size: 'small', kind: 'tertiary', space }} /> + - + + - - - jumpToDate(datePickerValue)} - /> + + +
{#each ZOOM_LEVELS as z (z)} @@ -510,13 +529,28 @@ class="gantt-grid" style="grid-template-columns: {sidebarWidthPx}px 5px 1fr; --sidebar-w: {sidebarWidthPx}px;" > - +
- - {#if showStatus}{/if} - {#if showIssueCode}{/if} - {#if showTitle}{/if} - +
+ + {#if showStatus}{/if} + {#if showIssueCode}{/if} + {#if showTitle}{/if} + +
+
+ + { if (e.key === 'Enter') jumpToToday() }} role="button" tabindex="0"> + {formatRange(dateRange.from)} – {formatRange(dateRange.to)} + + +
@@ -577,7 +611,7 @@ {#if vHasOverflow}
+ style="top: {TOOLBAR_HEIGHT}px; bottom: 11px;">
Date: Fri, 15 May 2026 12:33:15 +0000 Subject: [PATCH 037/248] feat(tracker-resources): inline-add row in Gantt sidebar (PR2.1) Plane-style: dashed-top-border "+ Add issue" row at the bottom of GanttSidebar opens the standard CreateIssue popup via showPopup. - GanttSidebar dispatches `addIssue` from a keyboard- and click- accessible row using the existing tracker.string.AddIssue label. - GanttView wires it to showPopup(CreateIssue, { space, shouldSaveDraft }). - Row is hidden when no space is available (defensive guard in newIssue handler). Verified on dk3 huly_v7 (gantt-v28): row renders, click opens "New issue" dialog with Issue title field focused. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttSidebar.svelte | 44 ++++++++++++++++++- .../src/components/gantt/GanttView.svelte | 9 +++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte index 2be033622dd..d02e4df50c0 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte @@ -3,7 +3,7 @@ --> + +{#if state.kind !== 'idle' && state.kind !== 'hover-bar' && geom !== null} + + +{/if} + +{#if gx !== null} + +{/if} + +{#if pd !== null && gx !== null} + + + {fmt(pd)} + +{/if} + +{#if tip !== null && gx !== null} + + + {tip} + +{/if} + + diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index 72abb97fb6f..d7eb93e133d 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -628,8 +628,11 @@ milestoneStripHeight={MILESTONE_STRIP_HEIGHT} {hoveredRowId} {statusCategoryMap} + {editableIssueIds} + {activeDrag} on:openIssue={onIssueOpen} on:hoverRow={onRowHover} + on:barMouseDown />
From 35f411e769bec672ff990c421167316ff5154867 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:39:53 +0000 Subject: [PATCH 051/248] feat(tracker-resources): wire drag reducer + atomic commitDrag in GanttView Signed-off-by: Michael Uray --- .../src/components/gantt/GanttView.svelte | 99 ++++++++++++++++++- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index d7eb93e133d..9f9e8265753 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -3,9 +3,10 @@ --> -
+ +
{#if loading} {:else} @@ -750,6 +825,7 @@ {statusCategoryMap} {editableIssueIds} {activeDrag} + {focusedIssueId} on:openIssue={onIssueOpen} on:hoverRow={onRowHover} on:barMouseDown={handleBarMouseDown} @@ -852,6 +928,7 @@ width: 100%; overflow: hidden; position: relative; + outline: none; } .gantt-toolbar { flex: 0 0 auto; From 31a4ef62fd6201a06c8a19b59397429702b18ad2 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:39:53 +0000 Subject: [PATCH 055/248] feat(tracker-resources): dim non-active rows during drag (spotlight focus) Signed-off-by: Michael Uray --- .../src/components/gantt/GanttCanvas.svelte | 16 ++++++++++++++++ .../src/components/gantt/GanttSidebar.svelte | 10 +++++++++- .../src/components/gantt/GanttView.svelte | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte index ccba52dd819..b2eb12dc22d 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte @@ -55,6 +55,17 @@ return focusedIssueId !== null && String(issueId) === focusedIssueId } + // PR 3 spotlight dim: while a drag is active, dim every row that is NOT the + // active issue. Keeps the cursor's row at full opacity so the user can see + // where the bar is being placed relative to other rows. + $: dragState = $activeDrag + $: activeIssueIdStr = 'issue' in dragState ? String((dragState as { issue: { _id: unknown } }).issue._id) : null + $: anyDragActive = dragState.kind !== 'idle' && dragState.kind !== 'hover-bar' + + function isDimmed (issueId: unknown): boolean { + return anyDragActive && activeIssueIdStr !== null && String(issueId) !== activeIssueIdStr + } + $: visibleRows = filterVisibleRows(rows, scrollTop, viewportHeight) $: rowsHeight = rows.length > 0 ? rows[rows.length - 1].y + rows[rows.length - 1].height : 0 $: totalHeight = rowsHeight + milestoneStripHeight @@ -113,6 +124,7 @@ class="row-rect" class:hovered={isHover} class:milestone-bg={row.kind === 'milestone'} + class:dimmed={row.issue !== null && isDimmed(row.issue._id)} /> {/each} @@ -221,6 +233,10 @@ :global(svg.gantt-canvas .row-rect.milestone-bg) { fill: color-mix(in srgb, var(--theme-state-info-color, #6366f1) 6%, transparent); } + :global(svg.gantt-canvas .row-rect.dimmed) { + fill: var(--theme-bg-color); + opacity: 0.55; + } :global(svg.gantt-canvas .row-rect.milestone-bg.hovered) { fill: color-mix(in srgb, var(--theme-state-info-color, #6366f1) 14%, transparent); } diff --git a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte index 1bba19568b0..967ca751bde 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte @@ -3,9 +3,10 @@ --> + + + +
+ + {issue.title} +
+
+
diff --git a/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte b/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte index 4022e0f596e..387db9fbbbb 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttHeader.svelte @@ -20,6 +20,13 @@ $: ticks = timeScale.ticks(visibleRange) + + + {#each ticks as tick (tick.date)} {@const x = timeScale.toX(tick.date)} + + {#if tick.secondaryLabel !== undefined} + + {tick.secondaryLabel} + + {/if} @@ -63,4 +92,9 @@ .tick-label-major { font-weight: 600; } + .tick-label-secondary { + font-size: 11px; + font-weight: 600; + opacity: 0.7; + } diff --git a/plugins/tracker-resources/src/components/gantt/GanttHierarchySubmenu.svelte b/plugins/tracker-resources/src/components/gantt/GanttHierarchySubmenu.svelte new file mode 100644 index 00000000000..40ee5fd20ba --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttHierarchySubmenu.svelte @@ -0,0 +1,68 @@ + + + + diff --git a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte index fd2647cfa42..625208f4fce 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttSidebar.svelte @@ -41,6 +41,12 @@ dispatch('openIssue', { issue: { _id: issue._id as string, _class: issue._class as string } }) } + function onDragGripDown (issue: Issue): (evt: MouseEvent) => void { + return (evt: MouseEvent): void => { + dispatch('rowDragStart', { issue, cursorX: evt.clientX }) + } + } + function onRowContextMenu (evt: MouseEvent, row: LayoutRow): void { if (row.issue === null) return evt.preventDefault() @@ -136,8 +142,8 @@ { if (row.issue !== null) dispatch('rowDragStart', { issue: row.issue, cursorX: e.clientX }) }} + use:tooltip={{ label: tracker.string.GanttDragToSchedule }} + on:mousedown|stopPropagation={row.issue !== null ? onDragGripDown(row.issue) : undefined} >⋮⋮ {/if} {#if showStatus} diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index ddf2a919d38..b418acc81b6 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -354,7 +354,7 @@ if (state.kind === 'dragging-unscheduled' && !state.hasCanvasTarget) return const client = getClient() const ops = client.apply('gantt-drag') - if (state.kind === 'dragging-body' || state.kind === 'dragging-unscheduled') { + if (state.kind === 'dragging-body') { await ops.update(state.issue, { startDate: state.previewStart, dueDate: state.previewDue }) const delta = state.previewStart - state.originStart if (delta !== 0) { @@ -365,6 +365,13 @@ }) } } + } else if (state.kind === 'dragging-unscheduled') { + // Unscheduled-drag only schedules the parent issue. originStart is the + // synthetic "today" anchor — using its delta to shift existing scheduled + // descendants would move them by a wildly unrelated amount (Codex + // review-4 2026-05-11). Descendants stay put; the user can drag the + // (now-scheduled) parent again to do a coordinated shift. + await ops.update(state.issue, { startDate: state.previewStart, dueDate: state.previewDue }) } else if (state.kind === 'resizing-left') { await ops.update(state.issue, { startDate: state.previewStart }) } else if (state.kind === 'resizing-right') { @@ -411,7 +418,11 @@ 'tracker:action:EditRelatedTargets', 'tracker:action:MoveToProject', 'tracker:action:CopyAsMarkdownTable', - 'tracker:action:UnsetParent' + 'tracker:action:UnsetParent', + // Below are surfaced via the local Hierarchy ▸ submenu instead, so the + // top-level menu has one collapsed entry rather than three separate ones. + 'tracker:action:SetParent', + 'tracker:action:NewSubIssue' ] function openGanttMenu (event: MouseEvent, issue: Issue): void { @@ -662,8 +673,18 @@ scrollerEl.scrollTop = panStartScrollTop - (e.clientY - panStartY) } function onCanvasPanEnd (e: PointerEvent): void { + // Guard: only release the pointer if we actually captured it. A pointerup + // bubbling from a child element that was excluded by the pan-handler + // exclusion list (e.g. resize-handle, drag-grip) shouldn't reach + // releasePointerCapture, but browsers throw `InvalidStateError` if the + // element isn't actually capturing the given pointerId. Codex review-4 + // 2026-05-11. + if (!panning) return panning = false - ;(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId) + const el = e.currentTarget as HTMLElement + if (typeof el.hasPointerCapture === 'function' && el.hasPointerCapture(e.pointerId)) { + el.releasePointerCapture(e.pointerId) + } } // Drag-resize the sidebar. @@ -947,7 +968,7 @@ {/if} {#if issue.startDate !== null && issue.dueDate !== null} {@const days = Math.round((Math.max(issue.dueDate, issue.startDate) - Math.min(issue.dueDate, issue.startDate)) / 86_400_000) + 1} -
Duration: {days} day{days === 1 ? '' : 's'}
+
{/if} {/if}
diff --git a/plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts b/plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts index e0b3b8262d2..474fe5ba053 100644 --- a/plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts +++ b/plugins/tracker-resources/src/components/gantt/lib/__tests__/time-scale.test.ts @@ -76,3 +76,38 @@ describe('createTimeScale', () => { } }) }) + +describe('time-scale — secondary label (year/month supra row)', () => { + const DAY = 86_400_000 + + it('day-zoom: secondaryLabel set on 1st of month and on first visible tick', () => { + const scale = createTimeScale('day', Date.UTC(2026, 0, 1)) + // Span 2 months: Jan 28 – Feb 5 + const ticks = scale.ticks([Date.UTC(2026, 0, 28), Date.UTC(2026, 1, 5)]) + const withSecondary = ticks.filter((t) => t.secondaryLabel !== undefined) + // Expect: first visible tick (Jan 28) + Feb 1 + expect(withSecondary).toHaveLength(2) + expect(withSecondary[0].secondaryLabel).toMatch(/jan/i) + expect(new Date(withSecondary[1].date).getUTCDate()).toBe(1) + expect(withSecondary[1].secondaryLabel).toMatch(/feb/i) + }) + + it('week-zoom: secondaryLabel = year on first visible week + first week of new year', () => { + const scale = createTimeScale('week', Date.UTC(2026, 11, 1)) + // Span across the 2026/2027 year boundary + const ticks = scale.ticks([Date.UTC(2026, 11, 1), Date.UTC(2027, 0, 31)]) + const withSecondary = ticks.filter((t) => t.secondaryLabel !== undefined) + expect(withSecondary.length).toBeGreaterThanOrEqual(2) + expect(withSecondary[0].secondaryLabel).toBe('2026') + expect(withSecondary[1].secondaryLabel).toBe('2027') + }) + + it('quarter-zoom: label is "Qn" only, year goes to secondaryLabel', () => { + const scale = createTimeScale('quarter', Date.UTC(2026, 0, 1)) + const ticks = scale.ticks([Date.UTC(2026, 0, 1), Date.UTC(2026, 11, 31)]) + expect(ticks.map((t) => t.label)).toEqual(['Q1', 'Q2', 'Q3', 'Q4']) + expect(ticks[0].secondaryLabel).toBe('2026') + expect(ticks[1].secondaryLabel).toBeUndefined() // Q2 same year + void DAY + }) +}) diff --git a/plugins/tracker-resources/src/components/gantt/lib/menu-actions.ts b/plugins/tracker-resources/src/components/gantt/lib/menu-actions.ts index 9190d092cec..bf0d14a1a9a 100644 --- a/plugins/tracker-resources/src/components/gantt/lib/menu-actions.ts +++ b/plugins/tracker-resources/src/components/gantt/lib/menu-actions.ts @@ -5,12 +5,14 @@ import type { Issue } from '@hcengineering/tracker' import type { Action as UiAction, PopupAlignment } from '@hcengineering/ui' -import { DatePopup, showPopup } from '@hcengineering/ui' +import { DatePopup, NotificationSeverity, addNotification, showPopup } from '@hcengineering/ui' import Calendar from '@hcengineering/ui/src/components/icons/Calendar.svelte' import { getClient } from '@hcengineering/presentation' +import { translate } from '@hcengineering/platform' import type { Timestamp } from '@hcengineering/core' import tracker from '../../../plugin' import { snapToUtcMidnight } from './time-scale' +import GanttHierarchySubmenu from '../GanttHierarchySubmenu.svelte' const DAY_MS = 86_400_000 @@ -49,7 +51,13 @@ export function openSetStartDate (issue: Issue, anchor: PopupAlignment | undefin if (newStart !== null && issue.startDate == null && issue.dueDate == null) { patch.dueDate = newStart + DAY_MS } - void client.updateDoc(issue._class, issue.space, issue._id, patch) + // Surface failures (permission denied, validation, conflict) the same + // way commitDrag does (Codex review-4 2026-05-11) so the user gets + // visible feedback instead of a silent fire-and-forget. + client.updateDoc(issue._class, issue.space, issue._id, patch).catch(async (err) => { + const title = await translate(tracker.string.GanttDragFailed, {}, undefined) + addNotification(title, String(err), undefined as any, undefined, NotificationSeverity.Error) + }) } ) } @@ -75,6 +83,21 @@ export function ganttExtraActions (issue: Issue, anchor: PopupAlignment | undefi action: async () => { openSetStartDate(issue, anchor) } + }, + { + // Hierarchy ▸ submenu: combines SetParent, Add sub-issue, Link existing + // as sub-issue into one Gantt entry. The two existing model actions + // (tracker:action:SetParent, tracker:action:NewSubIssue) are excluded + // from the parent menu via GANTT_MENU_EXCLUDED_ACTIONS in GanttView. + // `action` is required by the ui.Action interface but is a no-op for + // submenu entries — Menu.svelte routes the click to `component` when + // present (showActionPopup at packages/ui/src/components/Menu.svelte:82). + label: tracker.string.Hierarchy, + icon: tracker.icon.Parent, + group: 'associate', + action: async () => { /* submenu — handled by component */ }, + component: GanttHierarchySubmenu, + props: { issue } } ] } diff --git a/plugins/tracker-resources/src/components/gantt/lib/time-scale.ts b/plugins/tracker-resources/src/components/gantt/lib/time-scale.ts index da290d0389b..bdfd937b7fd 100644 --- a/plugins/tracker-resources/src/components/gantt/lib/time-scale.ts +++ b/plugins/tracker-resources/src/components/gantt/lib/time-scale.ts @@ -46,46 +46,84 @@ export function createTimeScale (zoom: ZoomLevel, origin: number): TimeScale { switch (zoom) { case 'day': { + // Secondary label: month name on the 1st of each month, or on the + // first visible tick (so the user sees a context label even if the + // viewport starts mid-month). + let lastMonth = -1 + let first = true while (cursor <= to) { const d = new Date(cursor) const isMonday = d.getUTCDay() === 1 + const m = d.getUTCMonth() + let secondary: string | undefined + if (first || m !== lastMonth) { + secondary = d.toLocaleString(undefined, { month: 'short', timeZone: 'UTC' }) + lastMonth = m + first = false + } result.push({ date: cursor, label: d.getUTCDate().toString(), - level: isMonday ? 'major' : 'minor' + level: isMonday ? 'major' : 'minor', + secondaryLabel: secondary }) cursor += DAY_MS } break } case 'week': { + // Secondary label: year on the first week of each year (or first + // visible week). Aligns the supra-row with year boundaries. const d = new Date(cursor) const dow = d.getUTCDay() // 0=Sun const offsetToMonday = ((1 - dow) + 7) % 7 cursor += offsetToMonday * DAY_MS + let lastYear = -1 + let first = true while (cursor <= to) { const c = new Date(cursor) const isFirstWeekOfMonth = c.getUTCDate() <= 7 + const y = c.getUTCFullYear() + let secondary: string | undefined + if (first || y !== lastYear) { + secondary = String(y) + lastYear = y + first = false + } result.push({ date: cursor, label: `W${isoWeekNumber(c)}`, - level: isFirstWeekOfMonth ? 'major' : 'minor' + level: isFirstWeekOfMonth ? 'major' : 'minor', + secondaryLabel: secondary }) cursor += 7 * DAY_MS } break } case 'month': { + // Secondary label: year on January (or first visible month). Was + // entirely missing before — without it, you couldn't tell which year + // a month belonged to in a multi-year span. const start = new Date(cursor) let y = start.getUTCFullYear() let m = start.getUTCMonth() cursor = Date.UTC(y, m, 1) + let lastYear = -1 + let first = true while (cursor <= to) { const c = new Date(cursor) + const yr = c.getUTCFullYear() + let secondary: string | undefined + if (first || yr !== lastYear) { + secondary = String(yr) + lastYear = yr + first = false + } result.push({ date: cursor, label: c.toLocaleString(undefined, { month: 'short', timeZone: 'UTC' }), - level: c.getUTCMonth() === 0 ? 'major' : 'minor' + level: c.getUTCMonth() === 0 ? 'major' : 'minor', + secondaryLabel: secondary }) m += 1 if (m > 11) { m = 0; y += 1 } @@ -94,17 +132,30 @@ export function createTimeScale (zoom: ZoomLevel, origin: number): TimeScale { break } case 'quarter': { + // Split the year out of the primary label and put it on the supra + // row — previously the label was "Q1 2026" side-by-side, which is + // visually noisy in a dense Quarter view. const start = new Date(cursor) let y = start.getUTCFullYear() let q = Math.floor(start.getUTCMonth() / 3) cursor = Date.UTC(y, q * 3, 1) + let lastYear = -1 + let first = true while (cursor <= to) { const c = new Date(cursor) const qNum = Math.floor(c.getUTCMonth() / 3) + 1 + const yr = c.getUTCFullYear() + let secondary: string | undefined + if (first || yr !== lastYear) { + secondary = String(yr) + lastYear = yr + first = false + } result.push({ date: cursor, - label: `Q${qNum} ${c.getUTCFullYear()}`, - level: qNum === 1 ? 'major' : 'minor' + label: `Q${qNum}`, + level: qNum === 1 ? 'major' : 'minor', + secondaryLabel: secondary }) q += 1 if (q > 3) { q = 0; y += 1 } diff --git a/plugins/tracker-resources/src/components/gantt/lib/types.ts b/plugins/tracker-resources/src/components/gantt/lib/types.ts index e72db33d0c3..7164360bf6e 100644 --- a/plugins/tracker-resources/src/components/gantt/lib/types.ts +++ b/plugins/tracker-resources/src/components/gantt/lib/types.ts @@ -12,8 +12,16 @@ export type ZoomLevel = 'day' | 'week' | 'month' | 'quarter' /** A single tick on the time-axis header (vertical gridline + label). */ export interface Tick { date: number // UTC ms - label: string // pre-formatted, locale-aware + label: string // pre-formatted, locale-aware (primary row) level: 'major' | 'minor' // major ticks render thicker + with text label + /** + * Optional supra-label rendered on a second header row above `label` when + * the segment changes from the previous tick. Day view sets it to month + * name on the 1st of each month; week view sets it to the year on the + * first week of each year; month view sets it to the year on January; + * quarter view sets it to the year on Q1. + */ + secondaryLabel?: string } /** A row in the flattened layout. May be an issue, milestone, or swimlane header. */ diff --git a/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte b/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte index bf7de0a5db4..4bd35c4eced 100644 --- a/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte +++ b/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte @@ -37,8 +37,10 @@ Label, createFocusManager, getCurrentResolvedLocation, - navigate + navigate, + showPopup } from '@hcengineering/ui' + import SetParentIssueActionPopup from '../../SetParentIssueActionPopup.svelte' import view from '@hcengineering/view' import { DocNavLink, ParentsNavigator, showMenu, RelationsEditor } from '@hcengineering/view-resources' import ProjectPresenter from '../../projects/ProjectPresenter.svelte' @@ -322,6 +324,19 @@
{/if}
+ {:else if !effectiveReadonly && issue !== undefined} +
+
{/if} + +
+
+ + +
+
+
+ +
+ + diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index b418acc81b6..1f00e83aef8 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -13,6 +13,7 @@ import tracker from '../../plugin' import { canEditIssue } from '../../utils' import GanttCanvas from './GanttCanvas.svelte' + import GanttConfirmCommitPopup from './GanttConfirmCommitPopup.svelte' import GanttHeader from './GanttHeader.svelte' import GanttSidebar from './GanttSidebar.svelte' import { reduce } from './lib/drag-controller' @@ -74,6 +75,14 @@ const activeDrag = writable({ kind: 'idle' }) let editableIssueIds: Set = new Set() + /** + * Click-to-select gate (user feedback 2026-05-11): a bar must be clicked + * once to become "selected" (blue outline) before drag/resize can begin + * on it. Prevents accidentally dragging an issue while panning the + * canvas horizontally. Clicking outside any bar clears the selection. + */ + let selectedIssueId: string | null = null + let canvasViewportLeft = 0 let canvasViewportWidth = 1200 let scrollTop = 0 @@ -94,6 +103,8 @@ $: showIssueCode = (viewOptions as Record)?.ganttShowIssueCode === true $: showTitle = ((viewOptions as Record)?.ganttShowTitle ?? true) !== false $: showStatus = ((viewOptions as Record)?.ganttShowStatus ?? true) !== false + $: confirmMove = ((viewOptions as Record)?.ganttConfirmMove ?? true) !== false + $: confirmResize = ((viewOptions as Record)?.ganttConfirmResize ?? true) !== false function setZoom (z: ZoomLevel): void { zoom = z @@ -305,6 +316,15 @@ // ------------------------------------------------------------------------- function handleBarMouseDown (e: CustomEvent<{ issue: Issue, edge: 'left' | 'right' | 'body', cursorX: number }>): void { + const id = String(e.detail.issue._id) + // Click-to-select gate: if this bar isn't already selected, the first + // mousedown just selects it (no drag starts). User has to mousedown a + // second time on the now-selected bar to actually drag/resize. Codex- + // safe and matches Plane's UX (user feedback 2026-05-11). + if (selectedIssueId !== id) { + selectedIssueId = id + return + } activeDrag.update((s) => reduce(s, { type: 'mousedown-bar', issue: e.detail.issue, @@ -313,6 +333,17 @@ }, timeScale)) } + /** + * Clear selection when the user clicks outside any bar — e.g. on the + * canvas background. Bar mousedowns stopPropagation, so this only fires + * for clicks that didn't land on a bar. + */ + function onBackgroundClick (e: MouseEvent): void { + const target = e.target as HTMLElement | null + if (target?.closest('.bar-wrap') !== null) return + selectedIssueId = null + } + /** * Translate the window-space `MouseEvent.clientX` into the canvas's content * coordinate. Used for `dragging-unscheduled` so the bar lands at the date @@ -338,7 +369,20 @@ const state = $activeDrag activeDrag.set({ kind: 'idle' }) if (state.kind === 'idle' || state.kind === 'hover-bar') return + // Skip the confirmation prompt when the preview didn't actually change + // anything (drag with zero-delta — e.g. mouseup without movement). + const previewDelta = previewChangedFromOrigin(state) + if (!previewDelta) return + // Wrap the commit in the user-configurable confirmation dialog when the + // matching ganttConfirm{Move,Resize} ViewOption is on (default-on). + const needsConfirm = + ((state.kind === 'dragging-body' || state.kind === 'dragging-unscheduled') && confirmMove) || + ((state.kind === 'resizing-left' || state.kind === 'resizing-right') && confirmResize) try { + if (needsConfirm) { + const proceed = await askConfirm(state) + if (!proceed) return + } await commitDrag(state) } catch (err) { const title = await translate(tracker.string.GanttDragFailed, {}, undefined) @@ -346,6 +390,38 @@ } } + /** True when the preview window is different from the origin window. */ + function previewChangedFromOrigin (state: DragState): boolean { + if (state.kind === 'dragging-body' || state.kind === 'dragging-unscheduled') { + return state.previewStart !== state.originStart || state.previewDue !== state.originDue + } + if (state.kind === 'resizing-left') return state.previewStart !== state.originStart + if (state.kind === 'resizing-right') return state.previewDue !== state.originDue + return false + } + + /** + * Open the confirm dialog and resolve true on Apply / false on Cancel. + * The popup dispatches `close` with a boolean payload, which Huly's + * showPopup routes to the 4th-arg resultHandler. + */ + async function askConfirm (state: DragState): Promise { + if (state.kind === 'idle' || state.kind === 'hover-bar') return false + const newStart = (state.kind === 'resizing-right' ? state.originStart : (state as { previewStart: number }).previewStart) + const newDue = (state.kind === 'resizing-left' ? state.originDue : (state as { previewDue: number }).previewDue) + const kind: 'move' | 'resize' = state.kind === 'resizing-left' || state.kind === 'resizing-right' ? 'resize' : 'move' + return await new Promise((resolve) => { + showPopup( + GanttConfirmCommitPopup, + { issue: state.issue, kind, newStart, newDue }, + 'top', + (result: boolean | undefined) => { + resolve(result === true) + } + ) + }) + } + async function commitDrag (state: DragState): Promise { if (state.kind === 'idle' || state.kind === 'hover-bar') return // Guard: an unscheduled-drag that never reached the canvas (e.g. the user @@ -742,8 +818,8 @@ $: loading = loadingIssues || loadingMilestones - -
+ +
{#if loading} {:else} @@ -882,6 +958,7 @@ {editableIssueIds} {activeDrag} {focusedIssueId} + {selectedIssueId} on:openIssue={onIssueOpen} on:hoverRow={onRowHover} on:barMouseDown={handleBarMouseDown} diff --git a/plugins/tracker-resources/src/components/gantt/lib/__tests__/link-sub-issue-cycle.test.ts b/plugins/tracker-resources/src/components/gantt/lib/__tests__/link-sub-issue-cycle.test.ts new file mode 100644 index 00000000000..0785b9ffa8c --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/lib/__tests__/link-sub-issue-cycle.test.ts @@ -0,0 +1,97 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// SPDX-License-Identifier: EPL-2.0 +// + +/** + * Standalone test for the cycle-safe ignore-set used by + * LinkSubIssueActionPopup. The component itself can't be exercised + * headlessly (it's a Svelte popup with DOM dependencies), but the + * computeIgnoreSet function is pure and worth fencing in jest so a + * future refactor doesn't silently regress the cycle protection. + * + * The function below is a duplicate of the one in + * `LinkSubIssueActionPopup.svelte` — kept in sync deliberately. + * Codex review-5 (2026-05-11) flagged the absence of a cycle test. + */ + +import type { Issue } from '@hcengineering/tracker' +import type { Ref } from '@hcengineering/core' + +function computeIgnoreSet (root: Issue, all: Issue[]): Set> { + const ignored = new Set>([root._id]) + if (Array.isArray(root.parents)) { + for (const p of root.parents as Array<{ parentId: Ref }>) { + if (p?.parentId !== undefined) ignored.add(p.parentId) + } + } + const childrenByParent = new Map, Issue[]>() + for (const i of all) { + const parentId = i.parents?.[0]?.parentId as Ref | undefined + if (parentId === undefined) continue + const bucket = childrenByParent.get(parentId) + if (bucket === undefined) childrenByParent.set(parentId, [i]) + else bucket.push(i) + } + const queue: Issue[] = [...(childrenByParent.get(root._id) ?? [])] + while (queue.length > 0) { + const next = queue.shift() as Issue + if (ignored.has(next._id)) continue + ignored.add(next._id) + const children = childrenByParent.get(next._id) + if (children !== undefined) { + for (const c of children) queue.push(c) + } + } + return ignored +} + +function mk (id: string, parents: string[] = []): Issue { + return { + _id: id as Ref, + parents: parents.map((p) => ({ parentId: p as Ref, parentTitle: '', space: 'sp' })) + } as unknown as Issue +} + +describe('LinkSubIssue — cycle-safe ignore set', () => { + it('excludes self', () => { + const root = mk('r') + const result = computeIgnoreSet(root, [root]) + expect(result.has(root._id)).toBe(true) + }) + + it('excludes direct ancestors via parents[]', () => { + const root = mk('r', ['p', 'gp']) // r is child of p, p is child of gp + const result = computeIgnoreSet(root, [root]) + expect(result.has('p' as Ref)).toBe(true) + expect(result.has('gp' as Ref)).toBe(true) + }) + + it('excludes direct + transitive descendants', () => { + const root = mk('r') + const child = mk('c', ['r']) + const grand = mk('g', ['c', 'r']) // transitive parents — direct = c + const result = computeIgnoreSet(root, [root, child, grand]) + expect(result.has('c' as Ref)).toBe(true) + expect(result.has('g' as Ref)).toBe(true) + }) + + it('does NOT exclude an unrelated sibling of root', () => { + const root = mk('r') + const sibling = mk('s') + const result = computeIgnoreSet(root, [root, sibling]) + expect(result.has('s' as Ref)).toBe(false) + }) + + it('cycle-safe: a self-referential issue does not infinite-loop', () => { + const root = mk('r') + const cyclic = mk('x', ['x']) + expect(() => computeIgnoreSet(root, [root, cyclic])).not.toThrow() + }) + + it('cycle-safe: mutual parent loop (a<->b) terminates', () => { + const a = mk('a', ['b']) + const b = mk('b', ['a']) + expect(() => computeIgnoreSet(a, [a, b])).not.toThrow() + }) +}) diff --git a/plugins/tracker/src/index.ts b/plugins/tracker/src/index.ts index 367be9efbb4..0ae9eeea7a6 100644 --- a/plugins/tracker/src/index.ts +++ b/plugins/tracker/src/index.ts @@ -569,6 +569,13 @@ const pluginState = plugin(trackerId, { SetParentIssueLabel: '' as IntlString, GanttDragToSchedule: '' as IntlString, GanttDurationTooltip: '' as IntlString, + GanttConfirmMove: '' as IntlString, + GanttConfirmResize: '' as IntlString, + GanttConfirmMoveTitle: '' as IntlString, + GanttConfirmResizeTitle: '' as IntlString, + GanttConfirmMoveBody: '' as IntlString, + GanttConfirmResizeBody: '' as IntlString, + GanttConfirmApply: '' as IntlString, GanttDependency: '' as IntlString, GanttLag: '' as IntlString, NewProject: '' as IntlString, From 5db9d536a0fdf009e8813c4772655b048514062a Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:39:54 +0000 Subject: [PATCH 062/248] fix(tracker-resources): Gantt parent-drag truth source + canvasX offset + a11y roles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit per explicit user preference for both select-first + confirm-popup default-on): 1. Parent-drag now shifts the FULL descendant tree, not just children that happen to be in the view's filtered `issues` array. Previously a Tracker filter that hid some sub-issues caused them to stay at the old date while their parent moved — silent tree drift. commitDrag and shiftFocused both now run `client.findAll(tracker.class.Issue, { space: issue.space })` to query the full space before walking descendantsWithDates. One extra query per commit, no impact on drag- interactive frame rate. 2. computeCanvasX (unscheduled drag) now offsets by sidebarWidthPx + 5 px instead of just sidebarWidthPx. The 5 px is the resize-cell column between sidebar and canvas (.resize-cell). The hscrollbar has always used this offset (style="padding-left: {sidebarWidthPx + 5}px"); unscheduled-drag was the only consumer that missed it, causing drops to land a few pixels off at high zoom. 3. A11y: the 6 interactive s on GanttBar (regular + summary + resize handles, both branches) now carry role="button", tabindex="-1", and aria-label so screen readers announce them as actionable. The a11y-click-events-have-key-events warning is suppressed per-rect with svelte-ignore plus a banner comment explaining that keyboard support lives at the GanttView level (Tab/ArrowLeft/Right). 94 → 88 warnings. Tests: 5 suites / 60 passed. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttBar.svelte | 32 +++++++++++++++++++ .../src/components/gantt/GanttView.svelte | 22 +++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte index e8f225966b0..d75bbee7cfe 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte @@ -149,6 +149,15 @@ editable but functionally inert — commitDrag's parent-pulls- children path was unreachable from the UI. --> {#if editable && !isMilestoneSummary} + + {#if w >= 18} + + @@ -215,6 +235,7 @@ {/if} {tooltipText} {:else} + {#if editable && w >= 18} + + diff --git a/plugins/tracker-resources/src/components/gantt/GanttView.svelte b/plugins/tracker-resources/src/components/gantt/GanttView.svelte index 1f00e83aef8..ba5660da0b6 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttView.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttView.svelte @@ -351,10 +351,17 @@ * the cursor is outside the canvas (e.g., still over the sidebar) so the * reducer keeps its default preview. */ + /** Width of the resize-cell (drag-handle column) between sidebar and canvas. + * The horizontal scrollbar already offsets by `sidebarWidthPx + 5` (see + * .gantt-hscrollbar padding-left), so the canvas content origin is at + * rect.left + sidebarWidthPx + this constant, not just sidebarWidthPx. + * Codex review-6 2026-05-11 caught the off-by-5 in unscheduled drag. */ + const RESIZE_CELL_W = 5 + function computeCanvasX (e: MouseEvent): number | undefined { if (scrollerEl === undefined) return undefined const rect = scrollerEl.getBoundingClientRect() - const sidebarEdge = rect.left + sidebarWidthPx + const sidebarEdge = rect.left + sidebarWidthPx + RESIZE_CELL_W if (e.clientX < sidebarEdge) return undefined return e.clientX - sidebarEdge + canvasViewportLeft } @@ -434,7 +441,12 @@ await ops.update(state.issue, { startDate: state.previewStart, dueDate: state.previewDue }) const delta = state.previewStart - state.originStart if (delta !== 0) { - for (const child of descendantsWithDates(state.issue, issues)) { + // Fetch the full space's issues here rather than reusing the + // view-filtered `issues` array — otherwise children hidden by an + // active Tracker filter wouldn't shift with the parent and the + // tree would drift out of sync. Codex review-6 2026-05-11. + const allInSpace = await client.findAll(tracker.class.Issue, { space: state.issue.space }) + for (const child of descendantsWithDates(state.issue, allInSpace)) { await ops.update(child, { startDate: (child.startDate as number) + delta, dueDate: (child.dueDate as number) + delta @@ -560,7 +572,11 @@ startDate: i.startDate + days * DAY_MS, dueDate: i.dueDate + days * DAY_MS }) - for (const child of descendantsWithDates(i, issues)) { + // Same filter-vs-truth issue as commitDrag: query the full space so + // filter-hidden descendants still shift with the parent (Codex + // review-6 2026-05-11). + const allInSpace = await client.findAll(tracker.class.Issue, { space: i.space }) + for (const child of descendantsWithDates(i, allInSpace)) { await ops.update(child, { startDate: (child.startDate as number) + days * DAY_MS, dueDate: (child.dueDate as number) + days * DAY_MS From abc9a92b53e7e21e9b1ae5a2df122eb8fcaa6ec5 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:39:54 +0000 Subject: [PATCH 063/248] =?UTF-8?q?chore(tracker-resources):=20Gantt=20pol?= =?UTF-8?q?ish=20=E2=80=94=20drop=20stale=20CSS,=20i18n=20ARIA=20labels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit friction observation about select-first + confirm-popup left as-is per explicit user preference, the parent-drag performance note is a future-large-space concern): 1. .settings-btn / .settings-popover removed from GanttView.svelte. They were leftover from an earlier custom-popover prototype that was replaced by Huly's standard ToggleViewOption pattern in PR2; the CSS was orphaned. Drops 4 svelte-check warnings. 2. Resize handle ARIA labels were hardcoded English ("Resize start" / "Resize end"). Now translated via tracker.string.GanttAriaResizeStart and GanttAriaResizeEnd. Translation resolves async on themeStore language change; falls back to the English default until resolved. 84 warnings (was 88). Signed-off-by: Michael Uray --- plugins/tracker-assets/lang/en.json | 4 ++- .../src/components/gantt/GanttBar.svelte | 18 +++++++--- .../src/components/gantt/GanttView.svelte | 36 +++---------------- plugins/tracker/src/index.ts | 2 ++ 4 files changed, 23 insertions(+), 37 deletions(-) diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index 7585ceb0263..4657194a3b5 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -329,7 +329,9 @@ "GanttConfirmResizeTitle": "Change issue dates?", "GanttConfirmMoveBody": "Move {title} to {start} – {due}?", "GanttConfirmResizeBody": "Change {title} to {start} – {due}?", - "GanttConfirmApply": "Apply" + "GanttConfirmApply": "Apply", + "GanttAriaResizeStart": "Resize start date", + "GanttAriaResizeEnd": "Resize due date" }, "status": {} } diff --git a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte index d75bbee7cfe..56a5a9f049e 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte @@ -4,8 +4,11 @@ + + + + From fd21ae9ce6741f6f1669b7bad79da07554abb492 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:53:23 +0000 Subject: [PATCH 082/248] feat(tracker-resources): GanttDependencyArrow bezier + lag pill (PR4a) Signed-off-by: Michael Uray --- .../gantt/GanttDependencyArrow.svelte | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttDependencyArrow.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttDependencyArrow.svelte b/plugins/tracker-resources/src/components/gantt/GanttDependencyArrow.svelte new file mode 100644 index 00000000000..89cb52e7d45 --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttDependencyArrow.svelte @@ -0,0 +1,109 @@ + + + +{#if path !== null && head !== null && mid !== null} + + + + + + {#if signedLag(relation.lag) !== '' } + + + + {pillText} + + {/if} + +{/if} + + From dce5f16edf75209baaf7daa029acdaa9f2ba2e91 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:53:24 +0000 Subject: [PATCH 083/248] feat(tracker-resources): GanttDependencyLayer overlay (PR4a) Iterates IssueRelation list, renders one GanttDependencyArrow each, plus a live bezier preview tied to connector-drawing / connector-target-hover drag states. Signed-off-by: Michael Uray --- .../gantt/GanttDependencyLayer.svelte | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte diff --git a/plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte b/plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte new file mode 100644 index 00000000000..5ab66d08dff --- /dev/null +++ b/plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte @@ -0,0 +1,78 @@ + + + + + {#each relations as rel (rel._id)} + {@const src = barRects.get(String(rel.attachedTo)) ?? null} + {@const dst = barRects.get(String(rel.target)) ?? null} + + {/each} + + {#if live !== null} + + + {/if} + + + From 0eb2e39f0c32c639531d39dde2ab30dfc9c62e3a Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:53:24 +0000 Subject: [PATCH 084/248] feat(tracker-resources): DependencyEditor popup (PR4a) Kind dropdown (FS/SS/FF/SF), lag NumberInput (-30..+90), delete with inline confirm, save/cancel. canEdit gates write actions; writes use the same client.apply().commit() pattern as PR3's drag-controller. Signed-off-by: Michael Uray --- .../src/components/DependencyEditor.svelte | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 plugins/tracker-resources/src/components/DependencyEditor.svelte diff --git a/plugins/tracker-resources/src/components/DependencyEditor.svelte b/plugins/tracker-resources/src/components/DependencyEditor.svelte new file mode 100644 index 00000000000..2a1429e86e0 --- /dev/null +++ b/plugins/tracker-resources/src/components/DependencyEditor.svelte @@ -0,0 +1,174 @@ + + + +
+
+ +
+
+ + +
+
+ +
+ +
+
+ + {#if confirmingDelete} +
+
+ {:else} + + {/if} +
+ + From 9c4fe951f925dcf49a1a081591e9f2397b4de639 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:53:24 +0000 Subject: [PATCH 085/248] fix(tracker-resources): GanttDependencyLayer use stroke-dasharray:0 not :none (PR4a) 'none' is not a valid stroke-dasharray value; browsers fall back to solid silently. Use the canonical '0' reset so the CSS is portable. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttDependencyLayer.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte b/plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte index 5ab66d08dff..6d3eff2b567 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttDependencyLayer.svelte @@ -72,7 +72,9 @@ stroke-dasharray: 5,4; } :global(svg.gantt-canvas .live-connector.targeting) { - stroke-dasharray: none; + /* "0" is the canonical "no dash" reset; "none" is not a valid value + for stroke-dasharray and browsers fall back silently. */ + stroke-dasharray: 0; stroke: #475569; } From 2c1a00234730cba3fada62d71f760ec2e50085db Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:53:24 +0000 Subject: [PATCH 086/248] feat(tracker-resources): GanttBar hover state + connector dot mount (PR4a) - Local `hovered` flag, surfaced via barHover event for hover-emphasize. - ConnectorDot mounts only when editable && hovered && issue-target, excluding milestone and summary-claw bars from connector dragging. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttBar.svelte | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte index 031860010ab..146b4e034dd 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte @@ -11,6 +11,7 @@ import tracker from '../../plugin' import type { TimeScale } from './lib/time-scale' import type { DragState, DragTarget } from './lib/types' + import GanttConnectorDot from './GanttConnectorDot.svelte' // Bar is rendered for both Issues and synthetic milestone summaries; the // structural subset below is all the bar geometry needs. @@ -39,8 +40,12 @@ const dispatch = createEventDispatcher<{ barMouseDown: { target: DragTarget, edge: 'left' | 'right' | 'body', cursorX: number } contextMenu: { issue: Issue, event: MouseEvent } + connectorDown: { source: Issue, cursorClientX: number, cursorClientY: number } + barHover: { issue: Issue | null } }>() + let hovered = false + function onBarContextMenu (evt: MouseEvent): void { // Context-menu is currently issue-only (PR3 menu wires Issue actions); // milestone bars don't surface it. PR3.3 keeps this gated until the @@ -189,6 +194,14 @@ aria-label={issue.title} on:mousedown={onBarDown('body')} on:contextmenu={onBarContextMenu} + on:mouseenter={() => { + hovered = true + if (dragTarget !== undefined && dragTarget.kind === 'issue') dispatch('barHover', { issue: dragTarget.doc }) + }} + on:mouseleave={() => { + hovered = false + dispatch('barHover', { issue: null }) + }} /> {#if selected && w >= 18} @@ -273,6 +286,14 @@ aria-label={issue.title} on:mousedown={onBarDown('body')} on:contextmenu={onBarContextMenu} + on:mouseenter={() => { + hovered = true + if (dragTarget !== undefined && dragTarget.kind === 'issue') dispatch('barHover', { issue: dragTarget.doc }) + }} + on:mouseleave={() => { + hovered = false + dispatch('barHover', { issue: null }) + }} /> {#if editable && selected && w >= 18} @@ -306,6 +327,20 @@ on:contextmenu={onBarContextMenu} /> {/if} + {#if editable && hovered && dragTarget !== undefined && dragTarget.kind === 'issue' && w >= 18} + { + if (dragTarget === undefined || dragTarget.kind !== 'issue') return + dispatch('connectorDown', { + source: dragTarget.doc, + cursorClientX: e.detail.cursorX, + cursorClientY: e.detail.cursorY + }) + }} + /> + {/if} {#if barLabel !== ''} Date: Fri, 15 May 2026 12:53:24 +0000 Subject: [PATCH 087/248] fix(tracker-resources): GanttBar isMilestoneSummary use dragTarget (PR4a follow-up) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR3.3 renamed export issueObj → dragTarget but left this $: block referencing the now-undeclared issueObj. The expression always evaluated true (undefined === undefined), so isMilestoneSummary collapsed to isSummary and parent-issue summary claws lost their hit-rect. Replace with the dragTarget-discriminated check. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttBar.svelte | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte index 146b4e034dd..22c30241b9e 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttBar.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttBar.svelte @@ -140,10 +140,15 @@ $: tooltipText = visible ? `${issue.title} (${new Date(startVal).toISOString().slice(0, 10)} → ${new Date(dueVal).toISOString().slice(0, 10)})` : '' - // Milestone summaries pass no issueObj because they aggregate child dates - // synthetically — those stay read-only. Parent-issue summaries DO have an - // issueObj (the parent), and that one is editable. - $: isMilestoneSummary = isSummary && issueObj === undefined + // Milestone-synthetic-summary claws were dropped in PR3.3 (the milestone + // is now rendered as its own editable bar). Parent-issue summaries have a + // dragTarget of kind='issue' carrying the parent Issue. So the only + // remaining case for `isMilestoneSummary` is a summary row without an + // issue-target — defensive: keeps the gate intact if a future row-kind + // mounts GanttBar without a dragTarget. Was `issueObj === undefined` + // before PR3.3 renamed `issueObj` → `dragTarget` (latent regression + // surfaced during PR4a code review 2026-05-11). + $: isMilestoneSummary = isSummary && (dragTarget === undefined || dragTarget.kind !== 'issue') // ARIA labels for the resize handles — translated up-front so the rect // can use them as plain strings (svg `aria-label` accepts only strings). From 4e2dc87f35fcb24ab14d8f564fd7db5b593fb9f3 Mon Sep 17 00:00:00 2001 From: Michael Uray Date: Fri, 15 May 2026 12:53:24 +0000 Subject: [PATCH 088/248] feat(tracker-resources): GanttCanvas mounts DependencyLayer + barRects (PR4a) Reactive Map, BarRect> computed from the same row geometry that positions GanttBar; threaded into GanttDependencyLayer so arrows can anchor without a separate measurement pass. Signed-off-by: Michael Uray --- .../src/components/gantt/GanttCanvas.svelte | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte index ba0b612fab0..9475941401e 100644 --- a/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte +++ b/plugins/tracker-resources/src/components/gantt/GanttCanvas.svelte @@ -4,24 +4,40 @@