Skip to content

Commit

Permalink
Chunter: user status (#1608) (#1692)
Browse files Browse the repository at this point in the history
Signed-off-by: budaeva <[email protected]>
  • Loading branch information
budaeva authored May 12, 2022
1 parent fee0f28 commit d0cba23
Show file tree
Hide file tree
Showing 18 changed files with 404 additions and 20 deletions.
25 changes: 21 additions & 4 deletions models/contact/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import type {
Organization,
Organizations,
Person,
Persons
Persons,
Status
} from '@anticrm/contact'
import type { Domain, Ref, Timestamp } from '@anticrm/core'
import type { Class, Domain, Ref, Timestamp } from '@anticrm/core'
import { DOMAIN_MODEL, IndexKind } from '@anticrm/core'
import { Builder, Collection, Index, Model, Prop, TypeRef, TypeString, TypeTimestamp, UX } from '@anticrm/model'
import attachment from '@anticrm/model-attachment'
Expand Down Expand Up @@ -93,9 +94,20 @@ export class TPerson extends TContact implements Person {}
@UX(contact.string.Organization, contact.icon.Company, undefined, 'name')
export class TOrganization extends TContact implements Organization {}

@Model(contact.class.Status, core.class.AttachedDoc, DOMAIN_CONTACT)
export class TStatus extends TAttachedDoc implements Status {
attachedTo!: Ref<Employee>
attachedToClass!: Ref<Class<Employee>>
name!: string
dueDate!: Timestamp
}

@Model(contact.class.Employee, contact.class.Person)
@UX(contact.string.Employee, contact.icon.Person)
export class TEmployee extends TPerson implements Employee {}
export class TEmployee extends TPerson implements Employee {
@Prop(Collection(contact.class.Status), contact.string.Status)
statuses?: number
}

@Model(contact.class.EmployeeAccount, core.class.Account)
export class TEmployeeAccount extends TAccount implements EmployeeAccount {
Expand All @@ -121,7 +133,8 @@ export function createModel (builder: Builder): void {
TOrganizations,
TEmployee,
TEmployeeAccount,
TChannel
TChannel,
TStatus
)

builder.mixin(contact.class.Person, core.class.Class, view.mixin.ObjectFactory, {
Expand Down Expand Up @@ -265,6 +278,10 @@ export function createModel (builder: Builder): void {
presenter: contact.component.ContactPresenter
})

builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributePresenter, {
presenter: contact.component.EmployeePresenter
})

builder.mixin(contact.class.Employee, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Delete]
})
Expand Down
5 changes: 3 additions & 2 deletions models/contact/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ export default mergeIds(contactId, contact, {
OrganizationPresenter: '' as AnyComponent,
Contacts: '' as AnyComponent,
EmployeeAccountPresenter: '' as AnyComponent,
PersonEditor: '' as AnyComponent,
OrganizationEditor: '' as AnyComponent
OrganizationEditor: '' as AnyComponent,
EmployeePresenter: '' as AnyComponent,
PersonEditor: '' as AnyComponent
},
string: {
Persons: '' as IntlString,
Expand Down
7 changes: 5 additions & 2 deletions plugins/chunter-resources/src/components/Message.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
import { Attachment } from '@anticrm/attachment'
import { AttachmentList, AttachmentRefInput } from '@anticrm/attachment-resources'
import type { ChunterMessage, Message } from '@anticrm/chunter'
import { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
import { Employee, EmployeeAccount } from '@anticrm/contact'
import { EmployeePresenter } from '@anticrm/contact-resources'
import { Ref, WithLookup, getCurrentAccount } from '@anticrm/core'
import { NotificationClientImpl } from '@anticrm/notification-resources'
import { getResource } from '@anticrm/platform'
Expand Down Expand Up @@ -179,7 +180,9 @@
<div class="avatar"><Avatar size={'medium'} avatar={employee?.avatar} /></div>
<div class="message">
<div class="header">
{#if employee}{formatName(employee.name)}{/if}
{#if employee}
<EmployeePresenter value={employee} shouldShowAvatar={false} />
{/if}
<span>{getTime(message.createOn)}</span>
{#if message.editedOn}
<span>
Expand Down
10 changes: 9 additions & 1 deletion plugins/contact-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@
"SocialLinks": "Socail links",
"ViewActivity": "View activity",
"PersonAlreadyExists": "Person already exists...",
"TypeLabel": "Type"
"Status": "Status",
"SetStatus": "Set status",
"ClearStatus": "Clear status",
"SaveStatus": "Save",
"Cancel": "Cancel",
"StatusDueDate": "Due date",
"NoExpire": "No expire",
"TypeLabel": "Type",
"StatusDueDateTooltip": "Until {date}"
}
}
10 changes: 9 additions & 1 deletion plugins/contact-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@
"SocialLinks": "Контактная информация",
"ViewActivity": "Посмотреть активность",
"PersonAlreadyExists": "Контакт уже существует...",
"TypeLabel": "Тип"
"Status": "Статус",
"SetStatus": "Установить статус",
"ClearStatus": "Очистить статус",
"SaveStatus": "Сохранить",
"Cancel": "Отмена",
"StatusDueDate": "Дата конца",
"NoExpire": "Бессрочно",
"TypeLabel": "Тип",
"StatusDueDateTooltip": "До {date}"
}
}
44 changes: 44 additions & 0 deletions plugins/contact-resources/src/components/EmployeePresenter.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts">
import { Employee } from '@anticrm/contact'
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
import PersonPresenter from '../components/PersonPresenter.svelte'
import { showPopup } from '@anticrm/ui'
import EmployeePreviewPopup from './EmployeePreviewPopup.svelte'
export let value: Employee
export let shouldShowAvatar: boolean = true
let container: HTMLElement
function onEdit (event: MouseEvent) {
showPopup(
EmployeePreviewPopup,
{
employeeId: value._id,
space: value.space
},
container
)
}
</script>

<div bind:this={container} class="flex container">
<div class="pr-2 over-underline">
<PersonPresenter {value} {onEdit} {shouldShowAvatar} />
</div>
<div class="status">
<EmployeeStatusPresenter employeeId={value._id} />
</div>
</div>

<style lang="scss">
.container {
width: fit-content;
margin-bottom: 0.25rem;
}
.status {
font-weight: 400;
font-size: 0.875rem;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<script lang="ts">
import { Employee, formatName, Status } from '@anticrm/contact'
import { Ref, Space, WithLookup } from '@anticrm/core'
import { Avatar, createQuery, getClient } from '@anticrm/presentation'
import { Button, Label, showPopup } from '@anticrm/ui'
import EmployeeSetStatusPopup from './EmployeeSetStatusPopup.svelte'
import contact from '../plugin'
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
import Edit from './icons/Edit.svelte'
import { createEventDispatcher } from 'svelte'
export let employeeId: Ref<Employee>
export let space: Ref<Space>
const client = getClient()
const stattusQuery = createQuery()
let status: WithLookup<Status>
$: employee = status?.$lookup?.attachedTo
stattusQuery.query(contact.class.Status, { attachedTo: employeeId }, (res) => (status = res[0]), {
lookup: {
attachedTo: contact.class.Employee
}
})
const dispatch = createEventDispatcher()
function onEdit () {
showPopup(
EmployeeSetStatusPopup,
{
currentStatus: status
},
undefined,
() => {},
(newStatus: Status) => {
if (status && newStatus) {
client.update(status, { ...newStatus })
} else if (status && !newStatus) {
client.remove(status)
} else {
client.createDoc(contact.class.Status, space, {
attachedTo: employeeId,
attachedToClass: contact.class.Employee,
collection: 'statuses',
name: newStatus.name,
dueDate: newStatus.dueDate
})
}
}
)
dispatch('close')
}
</script>

<div class="antiPopup p-4 flex-col">
<div class="flex-col-center pb-2">
<Avatar size="x-large" avatar={employee?.avatar} />
</div>
<div class="pb-2">{formatName(employee?.name ?? '')}</div>
<div class="pb-2">
<Label label={contact.string.Status} />
{#if status}
<div class="flex-row-stretch statusContainer">
<div class="pr-2">
<EmployeeStatusPresenter {employeeId} withTooltip={false} />
</div>
<div class="setStatusButton">
<Button icon={Edit} title={contact.string.SetStatus} on:click={onEdit} />
</div>
</div>
{:else}
<div class="flex-row-stretch over-underline" on:click={onEdit}>
<Label label={contact.string.SetStatus} />
</div>
{/if}
</div>
</div>

<style lang="scss">
.statusContainer {
.setStatusButton {
opacity: 0;
}
&:hover .setStatusButton {
opacity: 1;
}
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script lang="ts">
import { Status } from '@anticrm/contact'
import { Timestamp } from '@anticrm/core'
import { Card } from '@anticrm/presentation'
import { EditBox, Grid, Label, ticker } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import contact from '../plugin'
import EmployeeStatusDueDatePresenter from './EmployeeStatusDueDatePresenter.svelte'
export let currentStatus: Status | undefined
let statusName: string = currentStatus?.name ?? ''
let statusDueDate: Timestamp | undefined = currentStatus?.dueDate
const dispatch = createEventDispatcher()
const handleDueDateChanged = async (event: CustomEvent<Timestamp>) => {
statusDueDate = event.detail
}
$: statusChanged = statusName !== currentStatus?.name || statusDueDate !== currentStatus?.dueDate
$: isOverdue = statusDueDate && statusDueDate < $ticker
$: canSave = statusName.length > 0 && !isOverdue
</script>

<Card
label={contact.string.SetStatus}
okAction={() => {
if (statusChanged) {
dispatch('update', {
name: statusName,
dueDate: statusDueDate
})
dispatch('close')
} else {
dispatch('update', undefined)
dispatch('close')
}
}}
{canSave}
okLabel={statusChanged ? contact.string.SaveStatus : contact.string.ClearStatus}
on:close={() => {
dispatch('close')
}}
>
<Grid column={1} rowGap={1}>
<EditBox bind:value={statusName} maxWidth={'16rem'} />
<div><Label label={contact.string.StatusDueDate} /></div>

<EmployeeStatusDueDatePresenter {statusDueDate} on:change={handleDueDateChanged} />
</Grid>
</Card>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import { Icon, IconDPCalendarOver, IconDPCalendar, Label } from '@anticrm/ui'
import contact from '../plugin'
export let isOverdue: boolean = false
export let formattedDate: string | undefined
</script>

<div class="iconContainer">
<Icon icon={isOverdue ? IconDPCalendarOver : IconDPCalendar} size={'full'} />
{#if formattedDate}
<div>{formattedDate}</div>
{:else}
<Label label={contact.string.NoExpire} />
{/if}
</div>

<style lang="scss">
.iconContainer {
color: var(--content-color);
margin-right: 1rem;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script lang="ts">
import { Timestamp, TypeDate } from '@anticrm/core'
import { ticker, Tooltip } from '@anticrm/ui'
import { DateEditor } from '@anticrm/view-resources'
import EmployeeStatusDueDatePopup from './EmployeeStatusDueDatePopup.svelte'
import { formatDate } from '../utils'
import { createEventDispatcher } from 'svelte'
export let statusDueDate: Timestamp | undefined
$: isOverdue = statusDueDate && statusDueDate < $ticker
$: formattedDate = statusDueDate && formatDate(statusDueDate)
const dispatch = createEventDispatcher()
const type = { withTime: true } as TypeDate
</script>

<Tooltip
direction={'top'}
component={EmployeeStatusDueDatePopup}
props={{
formattedDate,
isOverdue
}}
>
<DateEditor
value={statusDueDate}
{type}
onChange={(v) => {
statusDueDate = v
dispatch('change', v)
}}
/>
</Tooltip>
Loading

0 comments on commit d0cba23

Please sign in to comment.