mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-12 12:47:45 +02:00
feat(tracker): add Gantt scheduling schema (startDate + IssueRelation)
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<Issue> 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<Issue|GithubPullRequest> 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 <michaeluray@users.noreply.github.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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<Project>
|
||||
|
||||
@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<Issue>
|
||||
|
||||
@Prop(TypeRef(tracker.class.Issue), tracker.string.Issue)
|
||||
@Index(IndexKind.Indexed)
|
||||
target!: Ref<Issue>
|
||||
|
||||
@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
|
||||
|
||||
|
||||
@@ -591,6 +591,7 @@ export class WorkspaceImporter {
|
||||
rank,
|
||||
comments: issue.comments?.length ?? 0,
|
||||
subIssues: issue.subdocs.length,
|
||||
startDate: null,
|
||||
dueDate: null,
|
||||
parents: parentsInfo,
|
||||
remainingTime,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "先週",
|
||||
|
||||
@@ -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": "지난주",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "Предыдущая неделя",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "过去一周",
|
||||
|
||||
@@ -186,6 +186,7 @@
|
||||
priority: priority ?? IssuePriority.NoPriority,
|
||||
space: _space as Ref<Project>,
|
||||
component: component ?? $activeComponent ?? null,
|
||||
startDate: null,
|
||||
dueDate: null,
|
||||
attachments: 0,
|
||||
estimation: 0,
|
||||
@@ -312,6 +313,7 @@
|
||||
_id: generateId(),
|
||||
space: _space as Ref<Project>,
|
||||
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
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
rank: '',
|
||||
comments: 0,
|
||||
subIssues: 0,
|
||||
startDate: subIssue.startDate ?? null,
|
||||
dueDate: null,
|
||||
parents,
|
||||
reportedTime: 0,
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
status: MilestoneStatus.Planned,
|
||||
comments: 0,
|
||||
attachments: 0,
|
||||
startDate: null,
|
||||
targetDate: Date.now() + 14 * 24 * 60 * 60 * 1000
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
assignee: project.defaultAssignee ?? null,
|
||||
status: project.defaultIssueStatus,
|
||||
space: project._id,
|
||||
startDate: null,
|
||||
dueDate: null,
|
||||
subIssues: [],
|
||||
attachments: 0,
|
||||
|
||||
@@ -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<Project>
|
||||
|
||||
milestone?: Ref<Milestone> | null
|
||||
@@ -236,6 +255,7 @@ export interface IssueDraft {
|
||||
assignee: Ref<Person> | null
|
||||
component: Ref<Component> | null
|
||||
space: Ref<Project>
|
||||
startDate: Timestamp | null
|
||||
dueDate: Timestamp | null
|
||||
milestone?: Ref<Milestone> | null
|
||||
|
||||
@@ -323,6 +343,22 @@ export interface IssueParentInfo {
|
||||
space: Ref<Space>
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Issue> // source / predecessor
|
||||
target: Ref<Issue> // 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<Class<Project>>,
|
||||
Issue: '' as Ref<Class<Issue>>,
|
||||
IssueRelation: '' as Ref<Class<IssueRelation>>,
|
||||
IssueTemplate: '' as Ref<Class<IssueTemplate>>,
|
||||
Component: '' as Ref<Class<Component>>,
|
||||
IssueStatus: '' as Ref<Class<IssueStatus>>,
|
||||
@@ -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,
|
||||
|
||||
@@ -69,6 +69,7 @@ WithMarkup<Issue>,
|
||||
| 'reports'
|
||||
| 'childInfo'
|
||||
| 'dueDate'
|
||||
| 'startDate'
|
||||
| 'kind'
|
||||
| 'reviews'
|
||||
| 'reviewThreads'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user