Skip to content

Commit

Permalink
add user status (#1608)
Browse files Browse the repository at this point in the history
Signed-off-by: budaeva <irina.budaeva@xored.com>
  • Loading branch information
budaeva committed May 6, 2022
1 parent 5d84e38 commit 4cbb660
Show file tree
Hide file tree
Showing 20 changed files with 419 additions and 18 deletions.
21 changes: 18 additions & 3 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 } 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, UX } from '@anticrm/model'
import attachment from '@anticrm/model-attachment'
Expand All @@ -38,6 +39,7 @@ import contact from './plugin'

export const DOMAIN_CONTACT = 'contact' as Domain
export const DOMAIN_CHANNEL = 'channel' as Domain
export const DOMAIN_STATUS = 'status' as Domain

@Model(contact.class.ChannelProvider, core.class.Doc, DOMAIN_MODEL)
export class TChannelProvider extends TDoc implements ChannelProvider {
Expand Down Expand Up @@ -90,6 +92,14 @@ 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_STATUS)
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 {}
Expand Down Expand Up @@ -118,7 +128,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 @@ -253,6 +264,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
3 changes: 2 additions & 1 deletion models/contact/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export default mergeIds(contactId, contact, {
OrganizationPresenter: '' as AnyComponent,
Contacts: '' as AnyComponent,
EmployeeAccountPresenter: '' as AnyComponent,
OrganizationEditor: '' as AnyComponent
OrganizationEditor: '' as AnyComponent,
EmployeePresenter: '' as AnyComponent
},
string: {
Persons: '' as IntlString,
Expand Down
2 changes: 1 addition & 1 deletion plugins/activity-assets/src/tsdoc-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"toolPackages": [
{
"packageName": "@microsoft/api-extractor",
"packageVersion": "7.19.2"
"packageVersion": "7.23.0"
}
]
}
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
8 changes: 7 additions & 1 deletion plugins/contact-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
"Facebook": "Facebook",
"SocialLinks": "Socail links",
"ViewActivity": "View activity",
"PersonAlreadyExists": "Person already exists..."
"PersonAlreadyExists": "Person already exists...",
"Status": "Status",
"SetStatus": "Set status",
"ClearStatus": "Clear status",
"SaveStatus": "Save",
"Cancel": "Cancel",
"StatusDueDate": "Due date"
}
}
8 changes: 7 additions & 1 deletion plugins/contact-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
"Facebook": "Facebook",
"SocialLinks": "Контактная информация",
"ViewActivity": "Посмотреть активность",
"PersonAlreadyExists": "Контакт уже существует..."
"PersonAlreadyExists": "Контакт уже существует...",
"Status": "Статус",
"SetStatus": "Установить статус",
"ClearStatus": "Очистить статус",
"SaveStatus": "Сохранить",
"Cancel": "Отмена",
"StatusDueDate": "Дата конца"
}
}
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="container">
<div class="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="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-center 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="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,66 @@
<script lang="ts">
import { Status } from '@anticrm/contact'
import { Timestamp } from '@anticrm/core'
import { Button, EditBox, Label } 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
}
</script>

<div class="antiPopup antiPopup-withHeader popup">
<div class="ap-header">
<div class="ap-caption">
<Label label={contact.string.SetStatus} />
</div>
</div>
<div class="p-4 flex-col flex-gap-1">
<EditBox bind:value={statusName} maxWidth={'8rem'} />
<Label label={contact.string.StatusDueDate} />

<EmployeeStatusDueDatePresenter bind:statusDueDate on:change={handleDueDateChanged} />
</div>
<div class="ap-footer">
{#if statusName.length > 0 && (statusName !== currentStatus?.name || statusDueDate !== currentStatus?.dueDate)}
<Button
on:click={() => {
dispatch('close')
}}
label={contact.string.Cancel}
/>
<Button
on:click={() => {
dispatch('update', {
name: statusName,
dueDate: statusDueDate
})
dispatch('close')
}}
label={contact.string.SaveStatus}
/>
{:else}
<Button
on:click={() => {
dispatch('update', undefined)
dispatch('close')
}}
label={contact.string.ClearStatus}
/>
{/if}
</div>
</div>

<style lang="scss">
.popup {
width: 10rem;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import { Icon, IconDPCalendarOver, IconDPCalendar } from '@anticrm/ui'
export let isOverdue: boolean = false
export let formattedDate: string = ''
</script>

<div class="iconContainer">
<Icon icon={isOverdue ? IconDPCalendarOver : IconDPCalendar} size={'full'} />
<div>{formattedDate}</div>
</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,24 @@
<script lang="ts">
import { Timestamp } from '@anticrm/core'
import { DatePresenter, Tooltip } from '@anticrm/ui'
import EmployeeStatusDueDatePopup from './EmployeeStatusDueDatePopup.svelte'
import { formatDate } from '../utils'
export let statusDueDate: Timestamp | undefined
$: today = new Date(Date.now())
$: dueDateMs = statusDueDate
$: isOverdue = dueDateMs !== undefined && dueDateMs !== null && dueDateMs < today.getTime()
$: formattedDate = formatDate(dueDateMs)
</script>

<Tooltip
direction={'top'}
component={EmployeeStatusDueDatePopup}
props={{
formattedDate: formattedDate,
isOverdue
}}
>
<DatePresenter bind:value={statusDueDate} editable={true} shouldShowLabel={true} withTime={true} on:change />
</Tooltip>
Loading

0 comments on commit 4cbb660

Please sign in to comment.