Skip to content

Commit 681d83a

Browse files
authored
Initial Estimations support. (#2251)
Signed-off-by: Andrey Sobolev <[email protected]>
1 parent 42ba7c0 commit 681d83a

File tree

54 files changed

+1172
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1172
-136
lines changed

models/contact/src/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ export function createModel (builder: Builder): void {
206206
'',
207207
{
208208
key: '$lookup.contact.$lookup.channels',
209+
label: contact.string.Channel,
209210
sortingKey: ['$lookup.contact.$lookup.channels.lastMessage', '$lookup.contact.channels']
210211
},
211212
'modifiedOn'
@@ -232,7 +233,11 @@ export function createModel (builder: Builder): void {
232233
'attachments',
233234
'modifiedOn',
234235
{ key: '', presenter: view.component.RolePresenter, label: view.string.Role },
235-
{ key: '$lookup.channels', sortingKey: ['$lookup.channels.lastMessage', 'channels'] }
236+
{
237+
key: '$lookup.channels',
238+
label: contact.string.ContactInfo,
239+
sortingKey: ['$lookup.channels.lastMessage', 'channels']
240+
}
236241
],
237242
hiddenKeys: ['name']
238243
},

models/hr/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ export function createModel (builder: Builder): void {
342342
'',
343343
{
344344
key: '$lookup.channels',
345+
label: contact.string.ContactInfo,
345346
sortingKey: ['$lookup.channels.lastMessage', 'channels']
346347
},
347348
'modifiedOn'

models/lead/src/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,11 @@ export function createModel (builder: Builder): void {
182182
'$lookup._class',
183183
'leads',
184184
'modifiedOn',
185-
{ key: '$lookup.channels', sortingKey: ['$lookup.channels.lastMessage', 'channels'] }
185+
{
186+
key: '$lookup.channels',
187+
label: contact.string.ContactInfo,
188+
sortingKey: ['$lookup.channels.lastMessage', 'channels']
189+
}
186190
],
187191
hiddenKeys: ['name']
188192
},

models/recruit/src/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,11 @@ export function createModel (builder: Builder): void {
283283
}
284284
},
285285
'modifiedOn',
286-
{ key: '$lookup.channels', sortingKey: ['$lookup.channels.lastMessage', 'channels'] }
286+
{
287+
key: '$lookup.channels',
288+
label: contact.string.ContactInfo,
289+
sortingKey: ['$lookup.channels.lastMessage', 'channels']
290+
}
287291
],
288292
hiddenKeys: ['name']
289293
},

models/tracker/src/index.ts

+60-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
Index,
2525
Model,
2626
Prop,
27+
ReadOnly,
2728
TypeDate,
2829
TypeMarkup,
2930
TypeNumber,
@@ -34,11 +35,12 @@ import {
3435
import attachment from '@anticrm/model-attachment'
3536
import chunter from '@anticrm/model-chunter'
3637
import core, { DOMAIN_SPACE, TAttachedDoc, TDoc, TSpace, TType } from '@anticrm/model-core'
37-
import view, { createAction } from '@anticrm/model-view'
38+
import view, { classPresenter, createAction } from '@anticrm/model-view'
3839
import workbench, { createNavigateAction } from '@anticrm/model-workbench'
3940
import notification from '@anticrm/notification'
4041
import { Asset, IntlString } from '@anticrm/platform'
4142
import setting from '@anticrm/setting'
43+
import tags from '@anticrm/tags'
4244
import task from '@anticrm/task'
4345
import {
4446
Document,
@@ -52,10 +54,10 @@ import {
5254
Sprint,
5355
SprintStatus,
5456
Team,
57+
TimeSpendReport,
5558
trackerId
5659
} from '@anticrm/tracker'
5760
import { KeyBinding } from '@anticrm/view'
58-
import tags from '@anticrm/tags'
5961
import tracker from './plugin'
6062

6163
import presentation from '@anticrm/model-presentation'
@@ -159,6 +161,13 @@ export class TTeam extends TSpace implements Team {
159161
defaultIssueStatus!: Ref<IssueStatus>
160162
}
161163

164+
/**
165+
* @public
166+
*/
167+
export function TypeReportedTime (): Type<number> {
168+
return { _class: tracker.class.TypeReportedTime, label: core.string.Number }
169+
}
170+
162171
/**
163172
* @public
164173
*/
@@ -219,8 +228,38 @@ export class TIssue extends TAttachedDoc implements Issue {
219228

220229
@Prop(TypeRef(tracker.class.Sprint), tracker.string.Sprint)
221230
sprint!: Ref<Sprint> | null
231+
232+
@Prop(TypeNumber(), tracker.string.Estimation)
233+
estimation!: number
234+
235+
@Prop(TypeReportedTime(), tracker.string.ReportedTime)
236+
@ReadOnly()
237+
reportedTime!: number
238+
239+
@Prop(Collection(tracker.class.TimeSpendReport), tracker.string.TimeSpendReports)
240+
reports!: number
222241
}
223242

243+
/**
244+
* @public
245+
*/
246+
@Model(tracker.class.TimeSpendReport, core.class.AttachedDoc, DOMAIN_TRACKER)
247+
@UX(tracker.string.TimeSpendReport, tracker.icon.TimeReport, tracker.string.TimeSpendReport)
248+
export class TTimeSpendReport extends TAttachedDoc implements TimeSpendReport {
249+
declare attachedTo: Ref<Issue>
250+
251+
@Prop(TypeRef(contact.class.Employee), contact.string.Employee)
252+
employee!: Ref<Employee>
253+
254+
@Prop(TypeDate(), tracker.string.TimeSpendReportDate)
255+
date!: Timestamp | null
256+
257+
@Prop(TypeNumber(), tracker.string.TimeSpendReportValue)
258+
value!: number
259+
260+
@Prop(TypeString(), tracker.string.TimeSpendReportDescription)
261+
description!: string
262+
}
224263
/**
225264
* @public
226265
*/
@@ -321,6 +360,10 @@ export class TSprint extends TDoc implements Sprint {
321360
declare space: Ref<Team>
322361
}
323362

363+
@UX(core.string.Number)
364+
@Model(tracker.class.TypeReportedTime, core.class.Type)
365+
export class TTypeReportedTime extends TType {}
366+
324367
export function createModel (builder: Builder): void {
325368
builder.createModel(
326369
TTeam,
@@ -331,7 +374,9 @@ export function createModel (builder: Builder): void {
331374
TTypeIssuePriority,
332375
TTypeProjectStatus,
333376
TSprint,
334-
TTypeSprintStatus
377+
TTypeSprintStatus,
378+
TTimeSpendReport,
379+
TTypeReportedTime
335380
)
336381

337382
builder.createDoc(view.class.Viewlet, core.space.Model, {
@@ -357,6 +402,7 @@ export function createModel (builder: Builder): void {
357402
presenter: tracker.component.SprintEditor,
358403
props: { kind: 'list', size: 'small', shape: 'round', shouldShowPlaceholder: false }
359404
},
405+
{ key: '', presenter: tracker.component.EstimationEditor, props: { kind: 'list', size: 'small' } },
360406
{ key: 'modifiedOn', presenter: tracker.component.ModificationDatePresenter, props: { fixed: 'right' } },
361407
{
362408
key: '$lookup.assignee',
@@ -474,6 +520,10 @@ export function createModel (builder: Builder): void {
474520
presenter: tracker.component.IssuePreview
475521
})
476522

523+
builder.mixin(tracker.class.TimeSpendReport, core.class.Class, view.mixin.AttributePresenter, {
524+
presenter: tracker.component.TimeSpendReport
525+
})
526+
477527
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ObjectTitle, {
478528
titleProvider: tracker.function.IssueTitleProvider
479529
})
@@ -1015,4 +1065,11 @@ export function createModel (builder: Builder): void {
10151065
},
10161066
tracker.action.Relations
10171067
)
1068+
1069+
classPresenter(
1070+
builder,
1071+
tracker.class.TypeReportedTime,
1072+
view.component.NumberPresenter,
1073+
tracker.component.ReportedTimeEditor
1074+
)
10181075
}

models/tracker/src/migration.ts

+9
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,15 @@ async function upgradeProjects (tx: TxOperations): Promise<void> {
317317

318318
export const trackerOperation: MigrateOperation = {
319319
async migrate (client: MigrationClient): Promise<void> {
320+
await client.update(
321+
DOMAIN_TRACKER,
322+
{ _class: tracker.class.Issue, reports: { $exists: false } },
323+
{
324+
reports: 0,
325+
estimation: 0,
326+
reportedTime: 0
327+
}
328+
)
320329
await Promise.all([migrateIssueProjects(client), migrateParentIssues(client)])
321330
await migrateIssueParentInfo(client)
322331
},

packages/core/src/classes.ts

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface UXObject extends Obj {
6565
label: IntlString
6666
icon?: Asset
6767
hidden?: boolean
68+
readonly?: boolean
6869
}
6970

7071
/**

packages/core/src/component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export default plugin(coreId, {
8080
Type: '' as Ref<Class<Type<any>>>,
8181
TypeString: '' as Ref<Class<Type<string>>>,
8282
TypeIntlString: '' as Ref<Class<Type<IntlString>>>,
83-
TypeNumber: '' as Ref<Class<Type<string>>>,
83+
TypeNumber: '' as Ref<Class<Type<number>>>,
8484
TypeMarkup: '' as Ref<Class<Type<string>>>,
8585
TypeBoolean: '' as Ref<Class<Type<boolean>>>,
8686
TypeTimestamp: '' as Ref<Class<Type<Timestamp>>>,

packages/core/src/tx.ts

+42
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,48 @@ export abstract class TxProcessor implements WithTx {
292292
return rawDoc
293293
}
294294

295+
static buildDoc2Doc<D extends Doc>(txes: Tx[]): D | undefined {
296+
let doc: Doc
297+
let createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc)
298+
const collectionTxes = false
299+
if (createTx === undefined) {
300+
const collectionTxes = txes.filter((tx) => tx._class === core.class.TxCollectionCUD) as Array<
301+
TxCollectionCUD<Doc, AttachedDoc>
302+
>
303+
createTx = collectionTxes.find((p) => p.tx._class === core.class.TxCreateDoc)
304+
}
305+
if (createTx === undefined) return
306+
doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
307+
for (let tx of txes) {
308+
if (collectionTxes) {
309+
tx = TxProcessor.extractTx(tx)
310+
}
311+
if (tx._class === core.class.TxUpdateDoc) {
312+
doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
313+
} else if (tx._class === core.class.TxMixin) {
314+
const mixinTx = tx as TxMixin<Doc, Doc>
315+
doc = TxProcessor.updateMixin4Doc(doc, mixinTx)
316+
}
317+
}
318+
return doc as D
319+
}
320+
321+
static extractTx (tx: Tx): Tx {
322+
if (tx._class === core.class.TxCollectionCUD) {
323+
const ctx = tx as TxCollectionCUD<Doc, AttachedDoc>
324+
if (ctx.tx._class === core.class.TxCreateDoc) {
325+
const create = ctx.tx as TxCreateDoc<AttachedDoc>
326+
create.attributes.attachedTo = ctx.objectId
327+
create.attributes.attachedToClass = ctx.objectClass
328+
create.attributes.collection = ctx.collection
329+
return create
330+
}
331+
return ctx.tx
332+
}
333+
334+
return tx
335+
}
336+
295337
protected abstract txCreateDoc (tx: TxCreateDoc<Doc>): Promise<TxResult>
296338
protected abstract txPutBag (tx: TxPutBag<PropertyType>): Promise<TxResult>
297339
protected abstract txUpdateDoc (tx: TxUpdateDoc<Doc>): Promise<TxResult>

packages/model/src/dsl.ts

+9
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ export function Hidden () {
158158
}
159159
}
160160

161+
/**
162+
* @public
163+
*/
164+
export function ReadOnly () {
165+
return function (target: any, propertyKey: string): void {
166+
setAttr(target, propertyKey, 'readonly', true)
167+
}
168+
}
169+
161170
/**
162171
* @public
163172
*/

packages/presentation/src/components/AttributeBarEditor.svelte

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
type={attribute?.type}
9393
{maxWidth}
9494
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
95+
readonly={attribute.readonly ?? false}
9596
space={object.space}
9697
{onChange}
9798
{focus}
@@ -105,6 +106,7 @@
105106
type={attribute?.type}
106107
{maxWidth}
107108
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
109+
readonly={attribute.readonly ?? false}
108110
space={object.space}
109111
{onChange}
110112
{focus}

packages/ui/src/components/ProgressCircle.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
if (value < min) value = min
2828
2929
const lenghtC: number = Math.PI * 14 - 1
30-
const procC: number = lenghtC / (max - min)
30+
$: procC = lenghtC / (max - min)
3131
$: dashOffset = (value - min) * procC
3232
</script>
3333

plugins/tracker-assets/assets/icons.svg

+5
Loading

plugins/tracker-assets/lang/en.json

+13-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,19 @@
196196
"AddToSprint": "Add to Sprint",
197197

198198
"NewSprint": "New Sprint",
199-
"CreateSprint": "Create"
199+
"CreateSprint": "Create",
200+
201+
"Estimation": "Estimation",
202+
"ReportedTime": "Reported Time",
203+
"TimeSpendReports": "Time spend reports",
204+
"TimeSpendReport": "Time spend report",
205+
"TimeSpendReportAdd": "Add time report",
206+
"TimeSpendReportDate": "Date",
207+
"TimeSpendReportValue": "Reported time",
208+
"TimeSpendReportValueTooltip": "Reported time in man days",
209+
"TimeSpendReportDescription": "Description",
210+
"TimeSpendValue": "{value}d",
211+
"SprintPassed": "{from}d/{to}d"
200212
},
201213
"status": {}
202214
}

plugins/tracker-assets/lang/ru.json

+13-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,19 @@
196196
"AddToSprint": "Добавить в Спринт",
197197

198198
"NewSprint": "Новый Спринт",
199-
"CreateSprint": "Создать"
199+
"CreateSprint": "Создать",
200+
201+
"Estimation": "Оценка",
202+
"ReportedTime": "Использовано",
203+
"TimeSpendReports": "Отчеты по времени",
204+
"TimeSpendReport": "Отчет по времени",
205+
"TimeSpendReportAdd": "Добавить завтраченное время",
206+
"TimeSpendReportDate": "Дата",
207+
"TimeSpendReportValue": "Затраченное время",
208+
"TimeSpendReportValueTooltip": "Затраченное время в человеко днях",
209+
"TimeSpendReportDescription": "Описание",
210+
"TimeSpendValue": "{value}d",
211+
"SprintPassed": "{from}d/{to}d"
200212
},
201213
"status": {}
202214
}

plugins/tracker-assets/src/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ loadMetadata(tracker.icon, {
6666

6767
CopyID: `${icons}#copyID`,
6868
CopyURL: `${icons}#copyURL`,
69-
CopyBranch: `${icons}#copyBranch`
69+
CopyBranch: `${icons}#copyBranch`,
70+
TimeReport: `${icons}#timeReport`,
71+
Estimation: `${icons}#timeReport`
7072
})
7173

7274
addStringsLoader(trackerId, async (lang: string) => await import(`../lang/${lang}.json`))

0 commit comments

Comments
 (0)