mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 21:50:34 +03:00
Add sprint members (#2392)
Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
parent
a2af1802a2
commit
dcaa0ea9b0
@ -412,6 +412,9 @@ export class TSprint extends TDoc implements Sprint {
|
|||||||
@Prop(TypeRef(contact.class.Employee), tracker.string.ProjectLead)
|
@Prop(TypeRef(contact.class.Employee), tracker.string.ProjectLead)
|
||||||
lead!: Ref<Employee> | null
|
lead!: Ref<Employee> | null
|
||||||
|
|
||||||
|
@Prop(ArrOf(TypeRef(contact.class.Employee)), tracker.string.Members)
|
||||||
|
members!: Ref<Employee>[]
|
||||||
|
|
||||||
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
|
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
|
||||||
comments!: number
|
comments!: number
|
||||||
|
|
||||||
|
@ -205,6 +205,12 @@
|
|||||||
"ActiveSprints": "Active",
|
"ActiveSprints": "Active",
|
||||||
"ClosedSprints": "Done",
|
"ClosedSprints": "Done",
|
||||||
"AddToSprint": "Add to Sprint",
|
"AddToSprint": "Add to Sprint",
|
||||||
|
"SprintNamePlaceholder": "Sprint name",
|
||||||
|
"SprintLead": "Lead",
|
||||||
|
"SprintLeadTitle": "Sprint lead",
|
||||||
|
"SprintLeadSearchPlaceholder": "Set sprint lead\u2026",
|
||||||
|
"SprintMembersTitle": "Sprint members",
|
||||||
|
"SprintMembersSearchPlaceholder": "Change sprint members\u2026",
|
||||||
|
|
||||||
"NewSprint": "New Sprint",
|
"NewSprint": "New Sprint",
|
||||||
"CreateSprint": "Create",
|
"CreateSprint": "Create",
|
||||||
|
@ -205,6 +205,12 @@
|
|||||||
"ActiveSprints": "Активно",
|
"ActiveSprints": "Активно",
|
||||||
"ClosedSprints": "Завершено",
|
"ClosedSprints": "Завершено",
|
||||||
"AddToSprint": "Добавить в Спринт",
|
"AddToSprint": "Добавить в Спринт",
|
||||||
|
"SprintNamePlaceholder": "Название спринта",
|
||||||
|
"SprintLead": "Руководитель",
|
||||||
|
"SprintLeadTitle": "Руководитель спринта",
|
||||||
|
"SprintLeadSearchPlaceholder": "Назначьте руководителя спринта\u2026",
|
||||||
|
"SprintMembersTitle": "Участники спринта",
|
||||||
|
"SprintMembersSearchPlaceholder": "Измененить участников спринта\u2026",
|
||||||
|
|
||||||
"NewSprint": "Новый Спринт",
|
"NewSprint": "Новый Спринт",
|
||||||
"CreateSprint": "Создать",
|
"CreateSprint": "Создать",
|
||||||
|
@ -81,8 +81,9 @@
|
|||||||
bind:value={object.lead}
|
bind:value={object.lead}
|
||||||
allowDeselect
|
allowDeselect
|
||||||
titleDeselect={tracker.string.Unassigned}
|
titleDeselect={tracker.string.Unassigned}
|
||||||
|
showNavigate={false}
|
||||||
/>
|
/>
|
||||||
<UserBoxList bind:items={object.members} label={tracker.string.ProjectStatusPlaceholder} />
|
<UserBoxList bind:items={object.members} label={tracker.string.ProjectMembersSearchPlaceholder} />
|
||||||
<!-- TODO: add labels after customize IssueNeedsToBeCompletedByThisDate -->
|
<!-- TODO: add labels after customize IssueNeedsToBeCompletedByThisDate -->
|
||||||
<DatePresenter bind:value={object.startDate} labelNull={tracker.string.StartDate} editable />
|
<DatePresenter bind:value={object.startDate} labelNull={tracker.string.StartDate} editable />
|
||||||
<DatePresenter bind:value={object.targetDate} labelNull={tracker.string.TargetDate} editable />
|
<DatePresenter bind:value={object.targetDate} labelNull={tracker.string.TargetDate} editable />
|
||||||
|
@ -148,7 +148,7 @@
|
|||||||
<svelte:component
|
<svelte:component
|
||||||
this={attributeModel.presenter}
|
this={attributeModel.presenter}
|
||||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||||
projectId={docObject._id}
|
parentId={docObject._id}
|
||||||
{...attributeModel.props}
|
{...attributeModel.props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,11 +15,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Data, Ref } from '@hcengineering/core'
|
import { Data, Ref } from '@hcengineering/core'
|
||||||
import { IntlString } from '@hcengineering/platform'
|
import { IntlString } from '@hcengineering/platform'
|
||||||
import { Card, EmployeeBox, getClient, SpaceSelector } from '@hcengineering/presentation'
|
import { Card, EmployeeBox, getClient, SpaceSelector, UserBoxList } from '@hcengineering/presentation'
|
||||||
import { Sprint, SprintStatus, Team } from '@hcengineering/tracker'
|
import { Project, Sprint, SprintStatus, Team } from '@hcengineering/tracker'
|
||||||
import ui, { DatePresenter, EditBox } from '@hcengineering/ui'
|
import ui, { DatePresenter, EditBox } from '@hcengineering/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
|
import ProjectSelector from '../ProjectSelector.svelte'
|
||||||
import SprintStatusSelector from './SprintStatusSelector.svelte'
|
import SprintStatusSelector from './SprintStatusSelector.svelte'
|
||||||
|
|
||||||
export let space: Ref<Team>
|
export let space: Ref<Team>
|
||||||
@ -31,8 +32,10 @@
|
|||||||
description: '',
|
description: '',
|
||||||
status: SprintStatus.Planned,
|
status: SprintStatus.Planned,
|
||||||
lead: null,
|
lead: null,
|
||||||
|
members: [],
|
||||||
comments: 0,
|
comments: 0,
|
||||||
attachments: 0,
|
attachments: 0,
|
||||||
|
capacity: 0,
|
||||||
startDate: Date.now(),
|
startDate: Date.now(),
|
||||||
targetDate: Date.now() + 14 * 24 * 60 * 60 * 1000
|
targetDate: Date.now() + 14 * 24 * 60 * 60 * 1000
|
||||||
}
|
}
|
||||||
@ -48,6 +51,14 @@
|
|||||||
|
|
||||||
object.status = newSprintStatus
|
object.status = newSprintStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleProjectIdChanged = async (projectId: Ref<Project> | null | undefined) => {
|
||||||
|
if (projectId === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
object.project = projectId ?? undefined
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
@ -61,7 +72,7 @@
|
|||||||
<SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space />
|
<SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<EditBox bind:value={object.label} placeholder={tracker.string.ProjectNamePlaceholder} kind="large-style" focus />
|
<EditBox bind:value={object.label} placeholder={tracker.string.SprintNamePlaceholder} kind="large-style" focus />
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<EditBox
|
<EditBox
|
||||||
@ -72,13 +83,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div slot="pool" class="flex-row-center text-sm gap-1-5">
|
<div slot="pool" class="flex-row-center text-sm gap-1-5">
|
||||||
<SprintStatusSelector selectedSprintStatus={object.status} onSprintStatusChange={handleProjectStatusChanged} />
|
<SprintStatusSelector selectedSprintStatus={object.status} onSprintStatusChange={handleProjectStatusChanged} />
|
||||||
|
<ProjectSelector value={object.project} onChange={handleProjectIdChanged} />
|
||||||
<EmployeeBox
|
<EmployeeBox
|
||||||
label={tracker.string.ProjectLead}
|
label={tracker.string.SprintLead}
|
||||||
placeholder={tracker.string.AssignTo}
|
placeholder={tracker.string.AssignTo}
|
||||||
bind:value={object.lead}
|
bind:value={object.lead}
|
||||||
allowDeselect
|
allowDeselect
|
||||||
titleDeselect={tracker.string.Unassigned}
|
titleDeselect={tracker.string.Unassigned}
|
||||||
|
showNavigate={false}
|
||||||
/>
|
/>
|
||||||
|
<UserBoxList bind:items={object.members} label={tracker.string.SprintMembersSearchPlaceholder} />
|
||||||
<DatePresenter
|
<DatePresenter
|
||||||
bind:value={object.startDate}
|
bind:value={object.startDate}
|
||||||
editable
|
editable
|
||||||
|
@ -171,6 +171,7 @@
|
|||||||
size: 'x-small'
|
size: 'x-small'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{ key: '', presenter: tracker.component.SprintMembersPresenter, props: { kind: 'link' } },
|
||||||
{ key: '', presenter: SprintDatePresenter, props: { field: 'startDate' } },
|
{ key: '', presenter: SprintDatePresenter, props: { field: 'startDate' } },
|
||||||
{ key: '', presenter: SprintDatePresenter, props: { field: 'targetDate' } },
|
{ key: '', presenter: SprintDatePresenter, props: { field: 'targetDate' } },
|
||||||
{ key: '', presenter: tracker.component.SprintStatusPresenter }
|
{ key: '', presenter: tracker.component.SprintStatusPresenter }
|
||||||
|
@ -178,7 +178,8 @@
|
|||||||
<svelte:component
|
<svelte:component
|
||||||
this={attributeModel.presenter}
|
this={attributeModel.presenter}
|
||||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||||
projectId={docObject._id}
|
parentId={docObject._id}
|
||||||
|
sprintId={docObject._id}
|
||||||
{...attributeModel.props}
|
{...attributeModel.props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 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. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import { Ref } from '@hcengineering/core'
|
||||||
|
import { Sprint } from '@hcengineering/tracker'
|
||||||
|
import { Button, showPopup, eventToHTMLElement } from '@hcengineering/ui'
|
||||||
|
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||||
|
import contact, { Employee } from '@hcengineering/contact'
|
||||||
|
import { getClient, UsersPopup } from '@hcengineering/presentation'
|
||||||
|
import { translate } from '@hcengineering/platform'
|
||||||
|
import tracker from '../../plugin'
|
||||||
|
|
||||||
|
export let value: Sprint
|
||||||
|
export let kind: ButtonKind = 'no-border'
|
||||||
|
export let size: ButtonSize = 'small'
|
||||||
|
export let justify: 'left' | 'center' = 'center'
|
||||||
|
export let width: string | undefined = 'min-content'
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
let buttonTitle = ''
|
||||||
|
|
||||||
|
$: translate(tracker.string.SprintMembersTitle, {}).then((res) => {
|
||||||
|
buttonTitle = res
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSprinttMembersChanged = async (result: Ref<Employee>[] | undefined) => {
|
||||||
|
if (result === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberToPull = value.members.filter((x) => !result.includes(x))[0]
|
||||||
|
const memberToPush = result.filter((x) => !value.members.includes(x))[0]
|
||||||
|
|
||||||
|
if (memberToPull) {
|
||||||
|
await client.update(value, { $pull: { members: memberToPull } })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memberToPush) {
|
||||||
|
await client.update(value, { $push: { members: memberToPush } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSprintMembersEditorOpened = async (event: MouseEvent) => {
|
||||||
|
showPopup(
|
||||||
|
UsersPopup,
|
||||||
|
{
|
||||||
|
_class: contact.class.Employee,
|
||||||
|
selectedUsers: value.members,
|
||||||
|
allowDeselect: true,
|
||||||
|
multiSelect: true,
|
||||||
|
docQuery: {
|
||||||
|
active: true
|
||||||
|
},
|
||||||
|
placeholder: tracker.string.SprintMembersSearchPlaceholder
|
||||||
|
},
|
||||||
|
eventToHTMLElement(event),
|
||||||
|
undefined,
|
||||||
|
handleSprinttMembersChanged
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
{kind}
|
||||||
|
{size}
|
||||||
|
{width}
|
||||||
|
{justify}
|
||||||
|
title={buttonTitle}
|
||||||
|
icon={tracker.icon.ProjectMembers}
|
||||||
|
on:click={handleSprintMembersEditorOpened}
|
||||||
|
/>
|
@ -74,6 +74,7 @@ import SprintEditor from './components/sprints/SprintEditor.svelte'
|
|||||||
import SprintPresenter from './components/sprints/SprintPresenter.svelte'
|
import SprintPresenter from './components/sprints/SprintPresenter.svelte'
|
||||||
import Sprints from './components/sprints/Sprints.svelte'
|
import Sprints from './components/sprints/Sprints.svelte'
|
||||||
import SprintSelector from './components/sprints/SprintSelector.svelte'
|
import SprintSelector from './components/sprints/SprintSelector.svelte'
|
||||||
|
import SprintMembersPresenter from './components/sprints/SprintMembersPresenter.svelte'
|
||||||
import SprintStatusPresenter from './components/sprints/SprintStatusPresenter.svelte'
|
import SprintStatusPresenter from './components/sprints/SprintStatusPresenter.svelte'
|
||||||
import SprintTitlePresenter from './components/sprints/SprintTitlePresenter.svelte'
|
import SprintTitlePresenter from './components/sprints/SprintTitlePresenter.svelte'
|
||||||
|
|
||||||
@ -249,6 +250,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
CreateIssueTemplate,
|
CreateIssueTemplate,
|
||||||
Sprints,
|
Sprints,
|
||||||
SprintPresenter,
|
SprintPresenter,
|
||||||
|
SprintMembersPresenter,
|
||||||
SprintStatusPresenter,
|
SprintStatusPresenter,
|
||||||
SprintTitlePresenter,
|
SprintTitlePresenter,
|
||||||
SprintSelector,
|
SprintSelector,
|
||||||
|
@ -221,6 +221,12 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
PlannedSprints: '' as IntlString,
|
PlannedSprints: '' as IntlString,
|
||||||
ActiveSprints: '' as IntlString,
|
ActiveSprints: '' as IntlString,
|
||||||
ClosedSprints: '' as IntlString,
|
ClosedSprints: '' as IntlString,
|
||||||
|
SprintNamePlaceholder: '' as IntlString,
|
||||||
|
SprintLead: '' as IntlString,
|
||||||
|
SprintLeadTitle: '' as IntlString,
|
||||||
|
SprintLeadSearchPlaceholder: '' as IntlString,
|
||||||
|
SprintMembersTitle: '' as IntlString,
|
||||||
|
SprintMembersSearchPlaceholder: '' as IntlString,
|
||||||
|
|
||||||
NewSprint: '' as IntlString,
|
NewSprint: '' as IntlString,
|
||||||
CreateSprint: '' as IntlString,
|
CreateSprint: '' as IntlString,
|
||||||
@ -304,6 +310,7 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
SprintPresenter: '' as AnyComponent,
|
SprintPresenter: '' as AnyComponent,
|
||||||
SprintStatusPresenter: '' as AnyComponent,
|
SprintStatusPresenter: '' as AnyComponent,
|
||||||
SprintTitlePresenter: '' as AnyComponent,
|
SprintTitlePresenter: '' as AnyComponent,
|
||||||
|
SprintMembersPresenter: '' as AnyComponent,
|
||||||
ReportedTimeEditor: '' as AnyComponent,
|
ReportedTimeEditor: '' as AnyComponent,
|
||||||
TimeSpendReport: '' as AnyComponent,
|
TimeSpendReport: '' as AnyComponent,
|
||||||
EstimationEditor: '' as AnyComponent,
|
EstimationEditor: '' as AnyComponent,
|
||||||
|
@ -129,6 +129,7 @@ export interface Sprint extends Doc {
|
|||||||
status: SprintStatus
|
status: SprintStatus
|
||||||
|
|
||||||
lead: Ref<Employee> | null
|
lead: Ref<Employee> | null
|
||||||
|
members: Ref<Employee>[]
|
||||||
|
|
||||||
space: Ref<Team>
|
space: Ref<Team>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user