Update EstimationPopup layout (#2513)

This commit is contained in:
Alexander Platov 2023-01-17 04:07:05 +03:00 committed by GitHub
parent ecc3f55d23
commit 835a37c6f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 301 additions and 227 deletions

View File

@ -381,11 +381,6 @@ input.search {
} }
/* Margins & Paddings */ /* Margins & Paddings */
.step-lr25 + .step-lr25 { margin-left: .25rem; }
.step-lr75 + .step-lr75 { margin-left: .75rem; }
.step-tb75 + .step-tb75 { margin-top: .75rem; }
.step-tb-6 + .step-tb-6 { margin-top: 1.5rem; }
.ml-0-5 { margin-left: .125rem; } .ml-0-5 { margin-left: .125rem; }
.ml-1 { margin-left: .25rem; } .ml-1 { margin-left: .25rem; }
.ml-1-5 { margin-left: .375rem; } .ml-1-5 { margin-left: .375rem; }
@ -476,6 +471,28 @@ input.search {
.p-6 { padding: 1.5rem; } .p-6 { padding: 1.5rem; }
.p-10 { padding: 2.5rem; } .p-10 { padding: 2.5rem; }
.p-text { padding: .125rem .25rem; }
.p-text-2 { padding: .25rem .5rem; }
.step-lr25 + .step-lr25 { margin-left: .25rem; }
.step-lr75 + .step-lr75 { margin-left: .75rem; }
.step-tb75 + .step-tb75 { margin-top: .75rem; }
.step-tb-6 + .step-tb-6 { margin-top: 1.5rem; }
.step-tb-2-accent + .step-tb-2-accent {
position: relative;
margin-top: .5rem;
&::before {
content: '';
position: absolute;
top: -.25rem;
left: 0;
width: 100%;
height: 1px;
background-color: var(--popup-bg-hover);
}
}
/* --------- */ /* --------- */
.no-word-wrap { word-wrap: none; } .no-word-wrap { word-wrap: none; }
.relative { position: relative; } .relative { position: relative; }

View File

@ -275,6 +275,9 @@
min-height: 1px; min-height: 1px;
height: 1px; height: 1px;
background-color: var(--divider-color); background-color: var(--divider-color);
&.dark { background-color: var(--body-accent); }
&.noMargin { margin: 0; }
} }
.antiSection { .antiSection {

View File

@ -173,10 +173,10 @@
.antiCard-pool { .antiCard-pool {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin: 0 .75rem .75rem; margin: 0 .5rem .25rem;
font-size: .75rem; font-size: .75rem;
& > *:not(:last-child) { margin-right: .375rem; } & > * { margin: 0 .25rem .5rem; }
} }
.antiCard-footer { .antiCard-footer {
direction: ltr; direction: ltr;

View File

@ -0,0 +1,85 @@
<!--
// 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 IconDownOutline from './icons/DownOutline.svelte'
export let size: 'small' | 'medium' = 'medium'
export let fill = 'var(--dark-color)'
export let expanded: boolean = false
export let direction: 'left' | 'right' = 'right'
export let outline: boolean = false
export let marginRight: string | undefined = undefined
export let marginLeft: string | undefined = undefined
export let margin: string | undefined = undefined
</script>
<div
class="chevron {size} {direction}"
class:outline
class:expanded
style:margin-right={marginRight}
style:margin-left={marginLeft}
style:margin
>
{#if outline}
<IconDownOutline {size} {fill} />
{:else}
<svg {fill} viewBox="0 0 6 6" xmlns="http://www.w3.org/2000/svg">
<path d="M0,0L6,3L0,6Z" />
</svg>
{/if}
</div>
<style lang="scss">
.chevron {
flex-shrink: 0;
transition: transform 0.15s var(--timing-main);
&.outline {
transform-origin: center;
&.right {
transform: rotate(-90deg);
}
&.left {
transform: rotate(90deg);
}
&.expanded {
transform: rotate(0deg);
}
}
&:not(.outline) {
transform-origin: 35% center;
transform: rotate(0deg);
&.small {
width: 0.325rem;
height: 0.325rem;
}
&.medium {
width: 0.375rem;
height: 0.375rem;
}
&.right {
transform: rotate(0deg);
}
&.left {
transform: rotate(180deg);
}
&.expanded {
transform: rotate(90deg);
}
}
}
</style>

View File

@ -18,7 +18,7 @@
import { tweened } from 'svelte/motion' import { tweened } from 'svelte/motion'
export let isExpanded = false export let isExpanded = false
export let duration = 200 export let duration = 150
export let easing: (t: number) => number = quintOut export let easing: (t: number) => number = quintOut
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -17,51 +17,62 @@
import ExpandCollapse from './ExpandCollapse.svelte' import ExpandCollapse from './ExpandCollapse.svelte'
import Icon from './Icon.svelte' import Icon from './Icon.svelte'
import Label from './Label.svelte' import Label from './Label.svelte'
import Chevron from './Chevron.svelte'
export let icon: Asset | undefined = undefined export let icon: Asset | undefined = undefined
export let label: IntlString | undefined = undefined export let label: IntlString | undefined = undefined
export let expanded: boolean = false export let expanded: boolean = false
export let bordered: boolean = false
export let expandable = true export let expandable = true
export let contentColor = false
</script> </script>
<!-- svelte-ignore a11y-mouse-events-have-key-events --> <div class="flex-col">
<!-- svelte-ignore a11y-click-events-have-key-events --> <div class="expandable-header flex-between" class:expanded class:bordered>
<div class="flex-grow flex" class:expanded class:expandable> <!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
class="fs-title flex-row-center mr-4" class="flex-row-center mr-4"
on:click|stopPropagation={() => { class:cursor-pointer={expandable}
expanded = !expanded on:click|stopPropagation={() => {
}} if (expandable) expanded = !expanded
> }}
<div class="chevron" class:expanded></div> >
<div class="an-element__icon"> <Chevron {expanded} marginRight={'.5rem'} />
{#if icon} {#if icon}
<Icon {icon} size={'small'} /> <div class="min-w-4 mr-2">
<Icon {icon} size={'small'} />
</div>
{/if} {/if}
<span class="fs-title overflow-label" class:content-color={contentColor}>
{#if label}<Label {label} />{/if}<slot name="title" />
</span>
</div> </div>
<span class="an-element__label title"> {#if $$slots.tools}
{#if label}<Label {label} />{/if} <div class="buttons-group small-gap">
<slot name="title" /> <slot name="tools" />
</span> </div>
{/if}
</div> </div>
<slot name="tools" /> <ExpandCollapse isExpanded={expanded}>
</div>
<ExpandCollapse isExpanded={expanded}>
<div class="antiComponent p-2">
<slot /> <slot />
</div> </ExpandCollapse>
</ExpandCollapse> </div>
<style lang="scss"> <style lang="scss">
.expandable { .expandable-header {
.chevron { transition: margin-bottom 0.15s var(--timing-main);
content: '▶';
margin-right: 0.5rem; &:not(.expanded) {
font-size: 0.75rem; margin-bottom: 0;
color: var(--dark-color); }
&.expanded { &.expanded {
transform: rotateZ(90deg); margin-bottom: 0.75rem;
} }
&.bordered {
padding: 0.25rem 0.5rem;
background-color: var(--body-accent);
border: 1px solid var(--divider-color);
border-radius: 0.25rem;
} }
} }
</style> </style>

View File

@ -18,6 +18,7 @@
export let selection: number = 0 export let selection: number = 0
export let count: number export let count: number
export let addClass: string | undefined = undefined
const refs: HTMLElement[] = [] const refs: HTMLElement[] = []
@ -66,8 +67,9 @@
> >
{#each Array(count) as _, row} {#each Array(count) as _, row}
<slot name="category" item={row} /> <slot name="category" item={row} />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
class="list-item" class="list-item{addClass ? ` ${addClass}` : ''}"
class:selection={row === selection} class:selection={row === selection}
on:mouseover={() => onRow(row)} on:mouseover={() => onRow(row)}
on:focus={() => {}} on:focus={() => {}}

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
export let size: 'x-small' | 'small' | 'medium' | 'large' | 'full' export let size: 'x-small' | 'small' | 'medium' | 'large' | 'full'
const fill: string = 'currentColor' export let fill: string = 'currentColor'
</script> </script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">

View File

@ -99,6 +99,7 @@ export { default as ErrorPresenter } from './components/ErrorPresenter.svelte'
export { default as Scroller } from './components/Scroller.svelte' export { default as Scroller } from './components/Scroller.svelte'
export { default as ScrollerBar } from './components/ScrollerBar.svelte' export { default as ScrollerBar } from './components/ScrollerBar.svelte'
export { default as TabList } from './components/TabList.svelte' export { default as TabList } from './components/TabList.svelte'
export { default as Chevron } from './components/Chevron.svelte'
export { default as IconAdd } from './components/icons/Add.svelte' export { default as IconAdd } from './components/icons/Add.svelte'
export { default as IconBack } from './components/icons/Back.svelte' export { default as IconBack } from './components/icons/Back.svelte'

View File

@ -75,9 +75,7 @@
<div class="antiComponent max-w-240 flex-grow p-1"> <div class="antiComponent max-w-240 flex-grow p-1">
<Expandable icon={ofClass?.icon} label={getEmbeddedLabel(typeTitle?.label ?? '')}> <Expandable icon={ofClass?.icon} label={getEmbeddedLabel(typeTitle?.label ?? '')}>
<svelte:fragment slot="tools"> <svelte:fragment slot="tools">
<div class="flex flex-reverse flex-grow"> <Button icon={IconDelete} on:click={() => client.remove(mapping)} size={'small'} />
<Button icon={IconDelete} on:click={() => client.remove(mapping)} size={'small'} />
</div>
</svelte:fragment> </svelte:fragment>
<Expandable label={getEmbeddedLabel('Options')}> <Expandable label={getEmbeddedLabel('Options')}>
<div class="flex-col"> <div class="flex-col">

View File

@ -323,44 +323,43 @@
<Expandable label={getEmbeddedLabel(mapping.type)}> <Expandable label={getEmbeddedLabel(mapping.type)}>
<svelte:fragment slot="tools"> <svelte:fragment slot="tools">
<div class="flex-row-center"> <SpaceSelect
<SpaceSelect _class={core.class.Space}
_class={core.class.Space} label={core.string.Space}
label={core.string.Space} bind:value={space}
bind:value={space} on:change={(evt) => {
on:change={(evt) => { space = evt.detail
space = evt.detail }}
autoSelect
spaceQuery={{ _id: { $in: [contact.space.Contacts] } }}
/>
<DropdownLabels
label={getEmbeddedLabel('Direction')}
items={[
{ id: 'ASC', label: 'Ascending' },
{ id: 'DSC', label: 'Descending' }
]}
bind:selected={direction}
/>
<div class="fs-title">
<NumberEditor
kind={'button'}
value={limit}
focus={false}
placeholder={getEmbeddedLabel('Limit')}
onChange={(val) => {
if (val) {
limit = val
}
}} }}
autoSelect
spaceQuery={{ _id: { $in: [contact.space.Contacts] } }}
/> />
<DropdownLabels </div>
label={getEmbeddedLabel('Direction')} <div class="buttons-divider" />
items={[ <div class="flex-row-center">
{ id: 'ASC', label: 'Ascending' }, <div class="p-1">
{ id: 'DSC', label: 'Descending' } {state}
]}
bind:selected={direction}
/>
<div class="fs-title flex-row-center">
<NumberEditor
kind={'button'}
value={limit}
focus={false}
placeholder={getEmbeddedLabel('Limit')}
onChange={(val) => {
if (val) {
limit = val
}
}}
/>
</div>
<div class="ml-2 flex-row-center">
<div class="p-1">
{state}
</div>
<Button size={'large'} label={getEmbeddedLabel('Synchronize')} {loading} on:click={doSync} />
</div> </div>
<Button size={'large'} label={getEmbeddedLabel('Synchronize')} {loading} on:click={doSync} />
</div> </div>
</svelte:fragment> </svelte:fragment>
<div class="flex-row flex-grow bottom-divider p-2"> <div class="flex-row flex-grow bottom-divider p-2">

View File

@ -16,10 +16,8 @@
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core' import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import { calcRank, Issue, IssueStatus, Team } from '@hcengineering/tracker' import { calcRank, Issue, IssueStatus, Team } from '@hcengineering/tracker'
import { Button, Spinner, ExpandCollapse, closeTooltip, IconAdd } from '@hcengineering/ui' import { Button, Spinner, ExpandCollapse, closeTooltip, IconAdd, Chevron, Label } from '@hcengineering/ui'
import tracker from '../../../plugin' import tracker from '../../../plugin'
import Collapsed from '../../icons/Collapsed.svelte'
import Expanded from '../../icons/Expanded.svelte'
import CreateSubIssue from './CreateSubIssue.svelte' import CreateSubIssue from './CreateSubIssue.svelte'
import SubIssueList from './SubIssueList.svelte' import SubIssueList from './SubIssueList.svelte'
@ -62,16 +60,18 @@
{#if hasSubIssues} {#if hasSubIssues}
<Button <Button
width="min-content" width="min-content"
icon={isCollapsed ? Collapsed : Expanded}
size="small" size="small"
kind="transparent" kind="transparent"
label={tracker.string.SubIssuesList}
labelParams={{ subIssues: issue.subIssues }}
on:click={() => { on:click={() => {
isCollapsed = !isCollapsed isCollapsed = !isCollapsed
isCreating = false isCreating = false
}} }}
/> >
<svelte:fragment slot="content">
<Chevron size={'small'} expanded={!isCollapsed} outline fill={'var(--caption-color)'} marginRight={'.375rem'} />
<Label label={tracker.string.SubIssuesList} params={{ subIssues: issue.subIssues }} />
</svelte:fragment>
</Button>
{/if} {/if}
<Button <Button

View File

@ -120,9 +120,6 @@
<IssuePresenter value={object} disableClick /> <IssuePresenter value={object} disableClick />
</svelte:fragment> </svelte:fragment>
<div class="header no-border flex-col p-1">
<div class="flex-row-center flex-between" />
</div>
{#if currentTeam && issueStatuses} {#if currentTeam && issueStatuses}
<SubIssuesEstimations <SubIssuesEstimations
issue={object} issue={object}
@ -143,13 +140,17 @@
icon={IconAdd} icon={IconAdd}
size={'small'} size={'small'}
on:click={(event) => { on:click={(event) => {
showPopup(TimeSpendReportPopup, { showPopup(
issueId: object._id, TimeSpendReportPopup,
issueClass: object._class, {
space: object.space, issueId: object._id,
assignee: object.assignee, issueClass: object._class,
defaultTimeReportDay space: object.space,
}) assignee: object.assignee,
defaultTimeReportDay
},
'top'
)
}} }}
label={tracker.string.TimeSpendReportAdd} label={tracker.string.TimeSpendReportAdd}
/> />

View File

@ -17,7 +17,7 @@
import { Doc, Ref } from '@hcengineering/core' import { Doc, Ref } from '@hcengineering/core'
import { UserBox } from '@hcengineering/presentation' import { UserBox } from '@hcengineering/presentation'
import { Issue, Team } from '@hcengineering/tracker' import { Issue, Team } from '@hcengineering/tracker'
import { getEventPositionElement, ListView, showPopup } from '@hcengineering/ui' import { getEventPositionElement, ListView, showPopup, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import { ContextMenu, FixedColumn, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources' import { ContextMenu, FixedColumn, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
import { getIssueId } from '../../../issues' import { getIssueId } from '../../../issues'
import tracker from '../../../plugin' import tracker from '../../../plugin'
@ -28,18 +28,19 @@
export let teams: Map<Ref<Team>, Team> export let teams: Map<Ref<Team>, Team>
function showContextMenu (ev: MouseEvent, object: Issue) { function showContextMenu (ev: MouseEvent, object: Issue) {
showPopup(ContextMenu, { object }, getEventPositionElement(ev)) showPopup(ContextMenu, { object }, $deviceInfo.isMobile ? 'top' : getEventPositionElement(ev))
} }
const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {}) const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {})
$: twoRows = $deviceInfo.twoRows
</script> </script>
<ListView count={issues.length}> <ListView count={issues.length} addClass={'step-tb-2-accent'}>
<svelte:fragment slot="item" let:item> <svelte:fragment slot="item" let:item>
{@const issue = issues[item]} {@const issue = issues[item]}
{@const currentTeam = teams.get(issue.space)} {@const currentTeam = teams.get(issue.space)}
<div <div
class="flex-between row" class="{twoRows ? 'flex-col' : 'flex-between'} p-text-2"
on:contextmenu|preventDefault={(ev) => showContextMenu(ev, issue)} on:contextmenu|preventDefault={(ev) => showContextMenu(ev, issue)}
on:mouseover={() => { on:mouseover={() => {
listProvider.updateFocus(issue) listProvider.updateFocus(issue)
@ -48,55 +49,32 @@
listProvider.updateFocus(issue) listProvider.updateFocus(issue)
}} }}
> >
<div class="flex-row-center clear-mins gap-2 p-2 flex-grow"> <div class="flex-row-center clear-mins gap-2 flex-grow mr-4" class:p-text={twoRows}>
<span class="issuePresenter"> <FixedColumn key={'estimation_issue'} justify={'left'} addClass={'fs-bold'}>
<FixedColumn key={'estimation_issue'} justify={'left'}> {#if currentTeam}
{#if currentTeam} {getIssueId(currentTeam, issue)}
{getIssueId(currentTeam, issue)} {/if}
{/if} </FixedColumn>
</FixedColumn> <span class="overflow-label fs-bold caption-color" title={issue.title}>
</span>
<span class="text name" title={issue.title}>
{issue.title} {issue.title}
</span> </span>
</div> </div>
<FixedColumn key={'estimation_issue_assignee'} justify={'right'}> <div class="flex-row-center clear-mins gap-2 self-end" class:p-text={twoRows}>
<UserBox <FixedColumn key={'estimation_issue_assignee'} justify={'right'}>
width={'100%'} <UserBox
label={tracker.string.Assignee} width={'100%'}
_class={contact.class.Employee} label={tracker.string.Assignee}
value={issue.assignee} _class={contact.class.Employee}
readonly value={issue.assignee}
showNavigate={false} readonly
/> showNavigate={false}
</FixedColumn> />
<FixedColumn key={'estimation'} justify={'left'}> </FixedColumn>
<EstimationEditor value={issue} kind={'list'} /> <FixedColumn key={'estimation'} justify={'left'}>
</FixedColumn> <EstimationEditor value={issue} kind={'list'} />
</FixedColumn>
</div>
</div> </div>
</svelte:fragment> </svelte:fragment>
</ListView> </ListView>
<style lang="scss">
.row {
.text {
font-weight: 500;
color: var(--caption-color);
}
.issuePresenter {
flex-shrink: 0;
min-width: 0;
min-height: 0;
font-weight: 500;
color: var(--content-color);
}
.name {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
</style>

View File

@ -16,7 +16,7 @@
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core' import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation' import { createQuery } from '@hcengineering/presentation'
import { Issue, IssueStatus, Team } from '@hcengineering/tracker' import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
import { Scroller, Spinner } from '@hcengineering/ui' import { Spinner } from '@hcengineering/ui'
import Expandable from '@hcengineering/ui/src/components/Expandable.svelte' import Expandable from '@hcengineering/ui/src/components/Expandable.svelte'
import tracker from '../../../plugin' import tracker from '../../../plugin'
import EstimationSubIssueList from './EstimationSubIssueList.svelte' import EstimationSubIssueList from './EstimationSubIssueList.svelte'
@ -38,15 +38,9 @@
{#if subIssues && issueStatuses} {#if subIssues && issueStatuses}
{#if hasSubIssues} {#if hasSubIssues}
<Expandable label={tracker.string.ChildEstimation}> <Expandable label={tracker.string.ChildEstimation} contentColor bordered>
<svelte:fragment slot="title"> <svelte:fragment slot="title">: <span class="caption-color">{total}</span></svelte:fragment>
: {total} <EstimationSubIssueList issues={subIssues} {teams} />
</svelte:fragment>
<div class="h-50">
<Scroller>
<EstimationSubIssueList issues={subIssues} {teams} />
</Scroller>
</div>
</Expandable> </Expandable>
{/if} {/if}
{:else} {:else}

View File

@ -15,11 +15,13 @@
<script lang="ts"> <script lang="ts">
import { TimeReportDayType } from '@hcengineering/tracker' import { TimeReportDayType } from '@hcengineering/tracker'
import { DropdownIntlItem, DropdownLabelsIntl } from '@hcengineering/ui' import { DropdownIntlItem, DropdownLabelsIntl } from '@hcengineering/ui'
import type { ButtonKind } from '@hcengineering/ui'
import tracker from '../../../plugin' import tracker from '../../../plugin'
import TimeReportDayIcon from './TimeReportDayIcon.svelte' import TimeReportDayIcon from './TimeReportDayIcon.svelte'
export let label = tracker.string.TimeReportDayTypeLabel export let label = tracker.string.TimeReportDayTypeLabel
export let selected: TimeReportDayType | undefined export let selected: TimeReportDayType | undefined
export let kind: ButtonKind = 'link-bordered'
const workDaysDropdownItems: DropdownIntlItem[] = [ const workDaysDropdownItems: DropdownIntlItem[] = [
{ {
@ -34,7 +36,7 @@
</script> </script>
<DropdownLabelsIntl <DropdownLabelsIntl
kind="link-bordered" {kind}
icon={TimeReportDayIcon} icon={TimeReportDayIcon}
shouldUpdateUndefined={false} shouldUpdateUndefined={false}
{label} {label}

View File

@ -18,7 +18,7 @@
import type { IntlString } from '@hcengineering/platform' import type { IntlString } from '@hcengineering/platform'
import presentation, { Card, getClient, UserBox } from '@hcengineering/presentation' import presentation, { Card, getClient, UserBox } from '@hcengineering/presentation'
import { Issue, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker' import { Issue, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
import { DatePresenter, EditBox } from '@hcengineering/ui' import { DatePresenter, EditBox, Button } from '@hcengineering/ui'
import tracker from '../../../plugin' import tracker from '../../../plugin'
import { getTimeReportDate, getTimeReportDayType } from '../../../utils' import { getTimeReportDate, getTimeReportDayType } from '../../../utils'
import TimeReportDayDropdown from './TimeReportDayDropdown.svelte' import TimeReportDayDropdown from './TimeReportDayDropdown.svelte'
@ -85,23 +85,31 @@
> >
<div class="flex-row-center gap-2"> <div class="flex-row-center gap-2">
<EditBox focus bind:value={data.value} {placeholder} format={'number'} maxDigitsAfterPoint={3} kind={'editbox'} /> <EditBox focus bind:value={data.value} {placeholder} format={'number'} maxDigitsAfterPoint={3} kind={'editbox'} />
<Button kind={'link-bordered'} on:click={() => (data.value = 0.125)}><span slot="content">1/8</span></Button>
<Button kind={'link-bordered'} on:click={() => (data.value = 0.25)}><span slot="content">1/4</span></Button>
<Button kind={'link-bordered'} on:click={() => (data.value = 0.5)}><span slot="content">1/2</span></Button>
<Button kind={'link-bordered'} on:click={() => (data.value = 0.75)}><span slot="content">3/4</span></Button>
<div class="buttons-divider" />
<Button kind={'link-bordered'} on:click={() => (data.value = 1)}><span slot="content">1</span></Button>
</div>
<EditBox bind:value={data.description} placeholder={tracker.string.TimeSpendReportDescription} kind={'editbox'} />
<svelte:fragment slot="pool">
<UserBox <UserBox
_class={contact.class.Employee} _class={contact.class.Employee}
label={contact.string.Employee} label={contact.string.Employee}
kind={'link-bordered'} kind={'no-border'}
bind:value={data.employee} bind:value={data.employee}
showNavigate={false} showNavigate={false}
/> />
<TimeReportDayDropdown <TimeReportDayDropdown
kind={'no-border'}
bind:selected={selectedTimeReportDay} bind:selected={selectedTimeReportDay}
on:selected={({ detail }) => (data.date = getTimeReportDate(detail))} on:selected={({ detail }) => (data.date = getTimeReportDate(detail))}
/> />
<DatePresenter <DatePresenter
kind={'link'}
bind:value={data.date} bind:value={data.date}
editable editable
on:change={({ detail }) => (selectedTimeReportDay = getTimeReportDayType(detail))} on:change={({ detail }) => (selectedTimeReportDay = getTimeReportDayType(detail))}
/> />
</div> </svelte:fragment>
<EditBox bind:value={data.description} placeholder={tracker.string.TimeSpendReportDescription} kind={'editbox'} />
</Card> </Card>

View File

@ -16,7 +16,7 @@
import { DocumentQuery, Ref, SortingOrder } from '@hcengineering/core' import { DocumentQuery, Ref, SortingOrder } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation' import { createQuery } from '@hcengineering/presentation'
import { Issue, Team, TimeSpendReport } from '@hcengineering/tracker' import { Issue, Team, TimeSpendReport } from '@hcengineering/tracker'
import { Expandable, floorFractionDigits, Label, Scroller, Spinner } from '@hcengineering/ui' import { Expandable, floorFractionDigits, Label, Spinner } from '@hcengineering/ui'
import tracker from '../../../plugin' import tracker from '../../../plugin'
import TimePresenter from './TimePresenter.svelte' import TimePresenter from './TimePresenter.svelte'
import TimeSpendReportsList from './TimeSpendReportsList.svelte' import TimeSpendReportsList from './TimeSpendReportsList.svelte'
@ -42,21 +42,19 @@
</script> </script>
{#if reports} {#if reports}
<Expandable expanded={true}> <Expandable expanded={true} contentColor bordered>
<svelte:fragment slot="title"> <svelte:fragment slot="title">
<span class="overflow-label flex-nowrap"> <span class="overflow-label flex-nowrap">
<Label label={tracker.string.ReportedTime} />: <TimePresenter value={reportedTime} {workDayLength} /> <Label label={tracker.string.ReportedTime} />:
<Label label={tracker.string.TimeSpendReports} />: <TimePresenter value={total} {workDayLength} /> <span class="caption-color"><TimePresenter value={reportedTime} {workDayLength} /></span>.
<Label label={tracker.string.TimeSpendReports} />:
<span class="caption-color"><TimePresenter value={total} {workDayLength} /></span>
</span> </span>
</svelte:fragment> </svelte:fragment>
<div class="h-50"> <TimeSpendReportsList {reports} {teams} />
<Scroller>
<TimeSpendReportsList {reports} {teams} />
</Scroller>
</div>
</Expandable> </Expandable>
{:else} {:else}
<div class="flex-center pt-3"> <div class="flex-center">
<Spinner /> <Spinner />
</div> </div>
{/if} {/if}

View File

@ -18,6 +18,7 @@
import UserBox from '@hcengineering/presentation/src/components/UserBox.svelte' import UserBox from '@hcengineering/presentation/src/components/UserBox.svelte'
import { Team, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker' import { Team, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
import { eventToHTMLElement, getEventPositionElement, ListView, showPopup } from '@hcengineering/ui' import { eventToHTMLElement, getEventPositionElement, ListView, showPopup } from '@hcengineering/ui'
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import DatePresenter from '@hcengineering/ui/src/components/calendar/DatePresenter.svelte' import DatePresenter from '@hcengineering/ui/src/components/calendar/DatePresenter.svelte'
import { ContextMenu, FixedColumn, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources' import { ContextMenu, FixedColumn, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
import { getIssueId } from '../../../issues' import { getIssueId } from '../../../issues'
@ -51,18 +52,19 @@
assignee: value.employee, assignee: value.employee,
defaultTimeReportDay defaultTimeReportDay
}, },
eventToHTMLElement(event) $deviceInfo.isMobile ? 'top' : eventToHTMLElement(event)
) )
} }
$: twoRows = $deviceInfo.twoRows
</script> </script>
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<ListView count={reports.length}> <ListView count={reports.length} addClass={'step-tb-2-accent'}>
<svelte:fragment slot="item" let:item> <svelte:fragment slot="item" let:item>
{@const report = reports[item]} {@const report = reports[item]}
{@const currentTeam = teams.get(toTeamId(report.space))} {@const currentTeam = teams.get(toTeamId(report.space))}
<div <div
class="flex-between row" class="{twoRows ? 'flex-col' : 'flex-between'} p-text-2"
on:contextmenu|preventDefault={(ev) => showContextMenu(ev, report)} on:contextmenu|preventDefault={(ev) => showContextMenu(ev, report)}
on:mouseover={() => { on:mouseover={() => {
listProvider.updateFocus(report) listProvider.updateFocus(report)
@ -72,62 +74,36 @@
}} }}
on:click={(evt) => editSpendReport(evt, report, currentTeam?.defaultTimeReportDay)} on:click={(evt) => editSpendReport(evt, report, currentTeam?.defaultTimeReportDay)}
> >
<div class="flex-row-center clear-mins gap-2 p-2 flex-grow"> <div class="flex-row-center clear-mins gap-2 flex-grow mr-4" class:p-text={twoRows}>
<span class="issuePresenter"> <FixedColumn key={'tmiespend_issue'} justify={'left'} addClass={'fs-bold'}>
<FixedColumn key={'tmiespend_issue'} justify={'left'}> {#if currentTeam && report.$lookup?.attachedTo}
{#if currentTeam && report.$lookup?.attachedTo} {getIssueId(currentTeam, report.$lookup?.attachedTo)}
{getIssueId(currentTeam, report.$lookup?.attachedTo)} {/if}
{/if} </FixedColumn>
</FixedColumn>
</span>
{#if report.$lookup?.attachedTo?.title} {#if report.$lookup?.attachedTo?.title}
<span class="text name" title={report.$lookup?.attachedTo?.title}> <span class="overflow-label fs-bold caption-color" title={report.$lookup?.attachedTo?.title}>
{report.$lookup?.attachedTo?.title} {report.$lookup?.attachedTo?.title}
</span> </span>
{/if} {/if}
</div> </div>
<FixedColumn key={'timespend_assignee'} justify={'left'}> <div class="flex-row-center clear-mins gap-2 self-end" class:p-text={twoRows}>
<UserBox <FixedColumn key={'timespend_assignee'} justify={'left'}>
width={'100%'} <UserBox
label={tracker.string.Assignee} width={'100%'}
_class={contact.class.Employee} label={tracker.string.Assignee}
value={report.employee} _class={contact.class.Employee}
readonly value={report.employee}
showNavigate={false} readonly
/> showNavigate={false}
</FixedColumn> />
</FixedColumn>
<FixedColumn key={'timespend_reported'} justify={'center'}> <FixedColumn key={'timespend_reported'} justify={'center'}>
<div class="p-1">
<TimePresenter value={report.value} workDayLength={currentTeam?.workDayLength} /> <TimePresenter value={report.value} workDayLength={currentTeam?.workDayLength} />
</div> </FixedColumn>
</FixedColumn> <FixedColumn key={'timespend_date'} justify={'left'}>
<FixedColumn key={'timespend_date'} justify={'left'}> <DatePresenter value={report.date} />
<DatePresenter value={report.date} /> </FixedColumn>
</FixedColumn> </div>
</div> </div></svelte:fragment
</svelte:fragment> >
</ListView> </ListView>
<style lang="scss">
.row {
.text {
font-weight: 500;
color: var(--caption-color);
}
.issuePresenter {
flex-shrink: 0;
min-width: 0;
min-height: 0;
font-weight: 500;
color: var(--content-color);
}
.name {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
</style>

View File

@ -20,6 +20,7 @@
export let key: string export let key: string
export let justify: string = '' export let justify: string = ''
export let addClass: string | undefined = undefined
let prevKey = key let prevKey = key
let element: HTMLDivElement | undefined let element: HTMLDivElement | undefined
@ -48,7 +49,7 @@
<div <div
bind:this={element} bind:this={element}
class="flex-no-shrink" class="flex-no-shrink{addClass ? ` ${addClass}` : ''}"
style="{justify !== '' ? `text-align: ${justify}; ` : ''} min-width: {$fixedWidthStore[key] ?? 0}px;" style="{justify !== '' ? `text-align: ${justify}; ` : ''} min-width: {$fixedWidthStore[key] ?? 0}px;"
use:resizeObserver={resize} use:resizeObserver={resize}
> >