mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 03:40:48 +00:00
Upcoming events (#1195)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
90955638c5
commit
6f7e95e233
@ -41,7 +41,8 @@ import workbench from '@anticrm/model-workbench'
|
||||
import { Applicant, Candidate, Candidates, Vacancy } from '@anticrm/recruit'
|
||||
import { TOpinion, TReview, TReviewCategory } from './review-model'
|
||||
import recruit from './plugin'
|
||||
import { createReviewModel } from './review'
|
||||
import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review'
|
||||
import calendar from '@anticrm/model-calendar'
|
||||
|
||||
@Model(recruit.class.Vacancy, task.class.SpaceWithStates)
|
||||
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
|
||||
@ -192,7 +193,18 @@ export function createModel (builder: Builder): void {
|
||||
componentProps: {
|
||||
labelTasks: recruit.string.Applications,
|
||||
_class: recruit.class.Applicant
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'upcoming',
|
||||
component: calendar.component.UpcomingEvents,
|
||||
componentProps: {
|
||||
_class: recruit.class.Review,
|
||||
options: reviewTableOptions,
|
||||
config: reviewTableConfig
|
||||
},
|
||||
icon: calendar.icon.Calendar,
|
||||
label: calendar.string.UpcomingEvents,
|
||||
position: 'top'
|
||||
}
|
||||
]
|
||||
|
@ -1,12 +1,38 @@
|
||||
import { Doc, FindOptions } from '@anticrm/core'
|
||||
import { FindOptions } from '@anticrm/core'
|
||||
import { Builder } from '@anticrm/model'
|
||||
import calendar from '@anticrm/model-calendar'
|
||||
import contact from '@anticrm/model-contact'
|
||||
import core from '@anticrm/model-core'
|
||||
import task from '@anticrm/model-task'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import { Review } from '@anticrm/recruit'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
import recruit from './plugin'
|
||||
import calendar from '@anticrm/model-calendar'
|
||||
|
||||
export const reviewTableOptions: FindOptions<Review> = {
|
||||
lookup: {
|
||||
attachedTo: recruit.mixin.Candidate,
|
||||
participants: contact.class.Employee,
|
||||
company: contact.class.Organization
|
||||
}
|
||||
}
|
||||
export const reviewTableConfig: (BuildModelKey | string)[] = [
|
||||
'',
|
||||
'title',
|
||||
'$lookup.attachedTo',
|
||||
// 'verdict',
|
||||
{ key: '', presenter: recruit.component.OpinionsPresenter, label: recruit.string.Opinions, sortingKey: 'opinions' },
|
||||
{
|
||||
key: '$lookup.participants',
|
||||
presenter: calendar.component.PersonsPresenter,
|
||||
label: calendar.string.Participants,
|
||||
sortingKey: '$lookup.participants'
|
||||
},
|
||||
'$lookup.company',
|
||||
{ key: '', presenter: calendar.component.DateTimePresenter, label: calendar.string.Date, sortingKey: 'date' },
|
||||
'modifiedOn'
|
||||
]
|
||||
|
||||
export function createReviewModel (builder: Builder): void {
|
||||
builder.mixin(recruit.class.ReviewCategory, core.class.Class, workbench.mixin.SpaceView, {
|
||||
@ -82,27 +108,20 @@ export function createReviewModel (builder: Builder): void {
|
||||
}
|
||||
})
|
||||
|
||||
const reviewOptions: FindOptions<Review> = {
|
||||
lookup: {
|
||||
attachedTo: recruit.mixin.Candidate,
|
||||
participants: contact.class.Employee,
|
||||
company: contact.class.Organization
|
||||
}
|
||||
}
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Review,
|
||||
descriptor: calendar.viewlet.Calendar,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
attachedTo: recruit.mixin.Candidate,
|
||||
participants: contact.class.Employee,
|
||||
company: contact.class.Organization
|
||||
}
|
||||
} as FindOptions<Doc>,
|
||||
config: [
|
||||
'',
|
||||
'title',
|
||||
'$lookup.attachedTo',
|
||||
'verdict',
|
||||
{ key: '', presenter: recruit.component.OpinionsPresenter, label: recruit.string.Opinions, sortingKey: 'opinions' },
|
||||
{ key: '$lookup.participants', presenter: calendar.component.PersonsPresenter, label: calendar.string.Participants, sortingKey: '$lookup.participants' },
|
||||
'$lookup.company',
|
||||
'modifiedOn'
|
||||
]
|
||||
options: reviewOptions,
|
||||
config: reviewTableConfig
|
||||
})
|
||||
}
|
||||
|
||||
@ -111,25 +130,8 @@ function createTableViewlet (builder: Builder): void {
|
||||
attachTo: recruit.class.Review,
|
||||
descriptor: view.viewlet.Table,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
attachedTo: recruit.mixin.Candidate,
|
||||
participants: contact.class.Employee,
|
||||
company: contact.class.Organization
|
||||
}
|
||||
} as FindOptions<Doc>,
|
||||
config: [
|
||||
'',
|
||||
'title',
|
||||
'$lookup.attachedTo',
|
||||
'verdict',
|
||||
{ key: '', presenter: recruit.component.OpinionsPresenter, label: recruit.string.Opinions, sortingKey: 'opinions' },
|
||||
{ key: '$lookup.participants', presenter: calendar.component.PersonsPresenter, label: calendar.string.Participants, sortingKey: '$lookup.participants' },
|
||||
'$lookup.company',
|
||||
'date',
|
||||
'dueDate',
|
||||
'modifiedOn'
|
||||
]
|
||||
options: reviewTableOptions,
|
||||
config: reviewTableConfig
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Opinion, core.class.Class, view.mixin.AttributeEditor, {
|
||||
|
@ -32,7 +32,7 @@ export type QuerySelector<T> = {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ObjQueryType<T> = T | QuerySelector<T>
|
||||
export type ObjQueryType<T> = (T extends Array<infer U> ? U | U[] : T) | QuerySelector<T>
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -50,23 +50,26 @@ export type DocumentQuery<T extends Doc> = {
|
||||
* @public
|
||||
*/
|
||||
export type ToClassRefT<T extends object, P extends keyof T> = T[P] extends Ref<infer X> | null | undefined ? Ref<Class<X>> | [Ref<Class<X>>, Lookup<X>] : never
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ToClassRefTA<T extends object, P extends keyof T> = T[P] extends Array<Ref<infer X>> | null | undefined ? Ref<Class<X>> | [Ref<Class<X>>, Lookup<X>] : never
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ToClassRef<T extends object> = {
|
||||
[P in keyof T]?: ToClassRefT<T, P>
|
||||
[P in keyof T]?: ToClassRefT<T, P> | ToClassRefTA<T, P>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type RefKeys<T extends Doc> = Pick<T, KeysByType<T, NullableRef>>
|
||||
export type NullableRef = Ref<Doc> | Array<Ref<Doc>> | null | undefined
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type NullableRef = Ref<Doc> | null | undefined
|
||||
export type RefKeys<T extends Doc> = Pick<T, KeysByType<T, NullableRef>>
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
@ -116,7 +116,7 @@ export interface MoveDescriptor<X extends PropertyType> {
|
||||
* @public
|
||||
*/
|
||||
export type ArrayAsElementPosition<T extends object> = {
|
||||
[P in keyof T]: T[P] extends Arr<infer X> ? X | Position<X> : never
|
||||
[P in keyof T]-?: T[P] extends Arr<infer X> ? X | Position<X> : never
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,10 +58,18 @@ export const test = plugin('test' as Plugin, {
|
||||
TestMixin: '' as Ref<Mixin<TestMixin>>
|
||||
},
|
||||
class: {
|
||||
TestComment: '' as Ref<Class<AttachedComment>>
|
||||
TestComment: '' as Ref<Class<AttachedComment>>,
|
||||
ParticipantsHolder: '' as Ref<Class<ParticipantsHolder>>
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ParticipantsHolder extends Doc {
|
||||
participants?: Ref<Doc>[]
|
||||
}
|
||||
|
||||
const DOMAIN_TEST: Domain = 'test' as Domain
|
||||
|
||||
/**
|
||||
@ -90,6 +98,8 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
|
||||
txes.push(createClass(test.class.TestComment, { label: 'TestComment' as IntlString, extends: core.class.AttachedDoc, kind: ClassifierKind.CLASS, domain: DOMAIN_TEST }))
|
||||
|
||||
txes.push(createClass(test.class.ParticipantsHolder, { label: 'ParticipantsHolder' as IntlString, extends: core.class.Doc, kind: ClassifierKind.CLASS, domain: DOMAIN_TEST }))
|
||||
|
||||
const u1 = 'User1' as Ref<Account>
|
||||
const u2 = 'User2' as Ref<Account>
|
||||
txes.push(
|
||||
|
@ -14,9 +14,9 @@
|
||||
//
|
||||
|
||||
import core, { createClient, Doc, generateId, Ref, SortingOrder, Space, Tx, TxCreateDoc, TxOperations, WithLookup } from '@anticrm/core'
|
||||
import { AttachedComment, test, genMinModel } from './minmodel'
|
||||
import { LiveQuery } from '..'
|
||||
import { connect } from './connection'
|
||||
import { AttachedComment, genMinModel, ParticipantsHolder, test } from './minmodel'
|
||||
|
||||
interface Channel extends Space {
|
||||
x: number
|
||||
@ -49,12 +49,12 @@ describe('query', () => {
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise((resolve) => {
|
||||
const result = await new Promise((resolve) => {
|
||||
liveQuery.query<Space>(core.class.Space, { private: false }, (result) => {
|
||||
expect(result).toHaveLength(expectedLength)
|
||||
resolve(null)
|
||||
resolve(result)
|
||||
})
|
||||
})
|
||||
expect(result).toHaveLength(expectedLength)
|
||||
})
|
||||
|
||||
it('query should be live', async () => {
|
||||
@ -743,4 +743,51 @@ describe('query', () => {
|
||||
// }
|
||||
// await pp
|
||||
// })
|
||||
|
||||
it('update-array-value', async () => {
|
||||
const { liveQuery, factory } = await getClient()
|
||||
|
||||
const spaces = await liveQuery.findAll(core.class.Space, {})
|
||||
await factory.createDoc(test.class.ParticipantsHolder, spaces[0]._id, {
|
||||
participants: ['a' as Ref<Doc>]
|
||||
})
|
||||
const a2 = await factory.createDoc(test.class.ParticipantsHolder, spaces[0]._id, {
|
||||
participants: ['b' as Ref<Doc>]
|
||||
})
|
||||
|
||||
const holderBefore = await liveQuery.findAll(test.class.ParticipantsHolder, { participants: 'a' as Ref<Doc> })
|
||||
expect(holderBefore.length).toEqual(1)
|
||||
|
||||
let attempt = 0
|
||||
let resolvePpv: (value: Doc[] | PromiseLike<Doc[]>) => void
|
||||
|
||||
const resolveP = new Promise<Doc[]>((resolve) => {
|
||||
resolvePpv = resolve
|
||||
})
|
||||
const pp = await new Promise((resolve) => {
|
||||
liveQuery.query<Space>(
|
||||
test.class.ParticipantsHolder,
|
||||
{ participants: 'a' as Ref<Doc> },
|
||||
(result) => {
|
||||
if (attempt > 0) {
|
||||
resolvePpv(result)
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
},
|
||||
{ sort: { private: SortingOrder.Ascending } }
|
||||
)
|
||||
})
|
||||
|
||||
await pp // We have first value returned
|
||||
|
||||
attempt++
|
||||
await factory.updateDoc<ParticipantsHolder>(test.class.ParticipantsHolder, spaces[0]._id, a2, {
|
||||
$push: {
|
||||
participants: 'a' as Ref<Doc>
|
||||
}
|
||||
})
|
||||
const result = await resolveP
|
||||
expect(result.length).toEqual(2)
|
||||
})
|
||||
})
|
||||
|
@ -338,9 +338,19 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
return false
|
||||
}
|
||||
|
||||
const doc: Doc = {
|
||||
_id: tx.objectId,
|
||||
_class: tx.objectClass,
|
||||
modifiedBy: tx.modifiedBy,
|
||||
modifiedOn: tx.modifiedOn,
|
||||
space: tx.objectSpace
|
||||
}
|
||||
|
||||
TxProcessor.updateDoc2Doc(doc, tx)
|
||||
|
||||
for (const key in q.query) {
|
||||
const value = (q.query as any)[key]
|
||||
const res = findProperty([tx.operations as unknown as Doc], key, value)
|
||||
const res = findProperty([doc], key, value)
|
||||
if (res.length === 1) {
|
||||
return true
|
||||
}
|
||||
|
@ -20,6 +20,11 @@
|
||||
"ModeWeek": "Week",
|
||||
"ModeMonth": "Month",
|
||||
"ModeYear": "Year",
|
||||
"Today": "Today"
|
||||
"Today": "Today",
|
||||
"UpcomingEvents": "Upcoming events",
|
||||
"TableView": "Table",
|
||||
"DueMinutes": "{minutes, plural, =0 {less than a minute} =1 {a minute} other {# minutes}}",
|
||||
"DueHours": "{hours, plural, =0 {less than an hour} =1 {1 hour} other {# hours}}",
|
||||
"DueDays": "{days, plural, =0 {today} =1 {1 day} other {# days}}"
|
||||
}
|
||||
}
|
@ -20,6 +20,11 @@
|
||||
"ModeWeek": "Неделя",
|
||||
"ModeMonth": "Месяц",
|
||||
"ModeYear": "Год",
|
||||
"Today": "Сегодня"
|
||||
"Today": "Сегодня",
|
||||
"UpcomingEvents": "Предстоящие события",
|
||||
"TableView": "Таблица",
|
||||
"DueMinutes": "{minutes, plural, =0 {меньше минуты} =1 {минута} other {# минут}}",
|
||||
"DueHours": "{hours, plural, =0 {меньше часа} =1 {1 час} other {# часы}}",
|
||||
"DueDays": "{days, plural, =0 {сегодня} =1 {1 день} other {# дня}}"
|
||||
}
|
||||
}
|
@ -38,7 +38,8 @@
|
||||
|
||||
let loading = false
|
||||
let resultQuery: DocumentQuery<Event>
|
||||
$: resultQuery = search === '' ? { ...query, space } : { ...query, $search: search, space }
|
||||
$: spaceOpt = (space ? { space } : {})
|
||||
$: resultQuery = search === '' ? { ...query, ...spaceOpt } : { ...query, $search: search, ...spaceOpt }
|
||||
|
||||
let objects: Event[] = []
|
||||
|
||||
@ -73,14 +74,16 @@
|
||||
}
|
||||
|
||||
function findEvents (events: Event[], date: Date): Event[] {
|
||||
return events.filter((it) => areDatesLess(new Date(it.date), date) && areDatesLess(date, new Date(it.dueDate ?? it.date)))
|
||||
return events.filter(
|
||||
(it) => areDatesLess(new Date(it.date), date) && areDatesLess(date, new Date(it.dueDate ?? it.date))
|
||||
)
|
||||
}
|
||||
|
||||
interface ShiftType {
|
||||
yearShift: number
|
||||
monthShift: number
|
||||
dayShift: number
|
||||
weekShift:number
|
||||
weekShift: number
|
||||
}
|
||||
|
||||
let shifts: ShiftType = {
|
||||
@ -134,7 +137,12 @@
|
||||
return res
|
||||
}
|
||||
|
||||
enum CalendarMode { Day, Week, Month, Year }
|
||||
enum CalendarMode {
|
||||
Day,
|
||||
Week,
|
||||
Month,
|
||||
Year
|
||||
}
|
||||
|
||||
let mode: CalendarMode = CalendarMode.Year
|
||||
|
||||
@ -156,7 +164,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class='fs-title ml-2 mb-2 flex-row-center'>
|
||||
<div class="fs-title ml-2 mb-2 flex-row-center">
|
||||
{label(currentDate(date, shifts), mode)}
|
||||
</div>
|
||||
|
||||
@ -202,29 +210,63 @@
|
||||
}
|
||||
mode = CalendarMode.Year
|
||||
}}
|
||||
/>
|
||||
/>
|
||||
<div class="flex ml-4 gap-1">
|
||||
<Button icon={IconBack} size={'small'} on:click={() => { inc(-1) } }/>
|
||||
<Button size={'small'} label={calendar.string.Today} on:click={() => { inc(0) }}/>
|
||||
<Button icon={IconForward} size={'small'} on:click={() => { inc(1) }}/>
|
||||
<Button
|
||||
icon={IconBack}
|
||||
size={'small'}
|
||||
on:click={() => {
|
||||
inc(-1)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size={'small'}
|
||||
label={calendar.string.Today}
|
||||
on:click={() => {
|
||||
inc(0)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={IconForward}
|
||||
size={'small'}
|
||||
on:click={() => {
|
||||
inc(1)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{#if mode === CalendarMode.Year}
|
||||
<ScrollBox bothScroll>
|
||||
<YearCalendar {mondayStart} cellHeight={'2.5rem'} bind:value={value} currentDate={currentDate(date, shifts)}>
|
||||
<ScrollBox bothScroll>
|
||||
<YearCalendar {mondayStart} cellHeight={'2.5rem'} bind:value currentDate={currentDate(date, shifts)}>
|
||||
<svelte:fragment slot="cell" let:date>
|
||||
<Day events={findEvents(objects, date)} {date} {_class} {baseMenuClass} {options} {config} query={resultQuery} />
|
||||
<Day
|
||||
events={findEvents(objects, date)}
|
||||
{date}
|
||||
{_class}
|
||||
{baseMenuClass}
|
||||
{options}
|
||||
{config}
|
||||
query={resultQuery}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</YearCalendar>
|
||||
</ScrollBox>
|
||||
{:else if mode === CalendarMode.Month}
|
||||
<div class='flex flex-grow'>
|
||||
<MonthCalendar {mondayStart} cellHeight={'8.5rem'} bind:value={value} currentDate={currentDate(date, shifts)}>
|
||||
<svelte:fragment slot="cell" let:date={date}>
|
||||
<Day events={findEvents(objects, date)} {date} size={'huge'} {_class} {baseMenuClass} {options} {config} query={resultQuery}/>
|
||||
</svelte:fragment>
|
||||
</MonthCalendar>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if mode === CalendarMode.Month}
|
||||
<div class="flex flex-grow">
|
||||
<MonthCalendar {mondayStart} cellHeight={'8.5rem'} bind:value currentDate={currentDate(date, shifts)}>
|
||||
<svelte:fragment slot="cell" let:date>
|
||||
<Day
|
||||
events={findEvents(objects, date)}
|
||||
{date}
|
||||
size={'huge'}
|
||||
{_class}
|
||||
{baseMenuClass}
|
||||
{options}
|
||||
{config}
|
||||
query={resultQuery}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</MonthCalendar>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -0,0 +1,59 @@
|
||||
<!--
|
||||
// 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 { Event } from '@anticrm/calendar'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { DatePresenter } from '@anticrm/ui'
|
||||
import calendar from '../plugin'
|
||||
|
||||
export let value: Event
|
||||
|
||||
$: date = value ? new Date(value.date) : undefined
|
||||
$: dueDate = value ? new Date(value.dueDate ?? value.date) : undefined
|
||||
|
||||
$: interval = (value.dueDate ?? value.date) - value.date
|
||||
|
||||
const SECOND = 1000
|
||||
const MINUTE = SECOND * 60
|
||||
const HOUR = MINUTE * 60
|
||||
const DAY = HOUR * 24
|
||||
|
||||
async function formatDueDate (interval: number): Promise<string> {
|
||||
let passed = interval
|
||||
if (interval < 0) passed = 0
|
||||
if (passed < HOUR) {
|
||||
return await translate(calendar.string.DueMinutes, { minutes: Math.floor(passed / MINUTE) })
|
||||
} else if (passed < DAY) {
|
||||
return await translate(calendar.string.DueHours, { hours: Math.floor(passed / HOUR) })
|
||||
} else {
|
||||
return await translate(calendar.string.DueDays, { days: Math.floor(passed / DAY) })
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="antiSelect">
|
||||
{#if date}
|
||||
<DatePresenter value={date} withTime={date.getMinutes() !== 0 && date.getHours() !== 0 && interval < DAY} />
|
||||
{#if interval > 0}
|
||||
{#await formatDueDate(interval) then t}
|
||||
<span class='ml-2 mr-1 whitespace-nowrap'>({t})</span>
|
||||
{/await}
|
||||
{/if}
|
||||
{:else}
|
||||
No date
|
||||
{/if}
|
||||
</div>
|
116
plugins/calendar-resources/src/components/UpcomingEvents.svelte
Normal file
116
plugins/calendar-resources/src/components/UpcomingEvents.svelte
Normal file
@ -0,0 +1,116 @@
|
||||
<!--
|
||||
// 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 { Event } from '@anticrm/calendar'
|
||||
import { EmployeeAccount } from '@anticrm/contact'
|
||||
import { Class, DocumentQuery, FindOptions, getCurrentAccount, Ref } from '@anticrm/core'
|
||||
import { Asset, IntlString } from '@anticrm/platform'
|
||||
import { AnySvelteComponent, Icon, Label, SearchEdit, Tooltip } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import view from '@anticrm/view'
|
||||
import calendar from '../plugin'
|
||||
import CalendarView from './CalendarView.svelte'
|
||||
|
||||
export let _class: Ref<Class<Event>>
|
||||
export let query: DocumentQuery<Event> = {}
|
||||
export let options: FindOptions<Event> | undefined = undefined
|
||||
export let baseMenuClass: Ref<Class<Event>> | undefined = undefined
|
||||
export let config: string[]
|
||||
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Event> = {}
|
||||
|
||||
function updateResultQuery (search: string): void {
|
||||
resultQuery = search === '' ? { ...query } : { ...query, $search: search }
|
||||
|
||||
resultQuery.participants = currentUser.employee
|
||||
}
|
||||
|
||||
$: updateResultQuery(search)
|
||||
|
||||
interface CalendarViewlet {
|
||||
component: AnySvelteComponent
|
||||
props: Record<string, any>
|
||||
label: IntlString
|
||||
icon: Asset
|
||||
}
|
||||
|
||||
$: viewlets = [{
|
||||
component: CalendarView,
|
||||
icon: calendar.icon.Calendar,
|
||||
label: calendar.string.Calendar,
|
||||
props: {
|
||||
_class,
|
||||
space: undefined,
|
||||
query: resultQuery,
|
||||
options,
|
||||
baseMenuClass,
|
||||
config,
|
||||
search
|
||||
}
|
||||
},
|
||||
{
|
||||
component: Table,
|
||||
icon: view.icon.Table,
|
||||
label: calendar.string.TableView,
|
||||
props: {
|
||||
_class,
|
||||
query: resultQuery,
|
||||
options,
|
||||
baseMenuClass,
|
||||
config,
|
||||
search
|
||||
}
|
||||
}] as CalendarViewlet[]
|
||||
let selectedViewlet = 0
|
||||
</script>
|
||||
|
||||
<div class="ac-header full">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="ac-header__icon"><Icon icon={calendar.icon.Calendar} size={'small'} /></div>
|
||||
<span class="ac-header__title"><Label label={calendar.string.UpcomingEvents} /></span>
|
||||
</div>
|
||||
|
||||
{#if viewlets.length > 1}
|
||||
<div class="flex">
|
||||
{#each viewlets as viewlet, i}
|
||||
<Tooltip label={viewlet.label} direction={'top'}>
|
||||
<button
|
||||
class="ac-header__icon-button"
|
||||
class:selected={selectedViewlet === i}
|
||||
on:click={() => {
|
||||
selectedViewlet = i
|
||||
}}
|
||||
>
|
||||
<Icon icon={viewlet.icon} size={'small'} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
updateResultQuery(search)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if viewlets[selectedViewlet]}
|
||||
<svelte:component this={viewlets[selectedViewlet].component} {...(viewlets[selectedViewlet].props)} />
|
||||
{/if}
|
@ -17,10 +17,14 @@ import { Resources } from '@anticrm/platform'
|
||||
|
||||
import PersonsPresenter from './components/PersonsPresenter.svelte'
|
||||
import CalendarView from './components/CalendarView.svelte'
|
||||
import UpcomingEvents from './components/UpcomingEvents.svelte'
|
||||
import DateTimePresenter from './components/DateTimePresenter.svelte'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
PersonsPresenter,
|
||||
CalendarView
|
||||
CalendarView,
|
||||
UpcomingEvents,
|
||||
DateTimePresenter
|
||||
}
|
||||
})
|
||||
|
@ -25,6 +25,11 @@ export default mergeIds(calendarId, calendar, {
|
||||
ModeWeek: '' as IntlString,
|
||||
ModeMonth: '' as IntlString,
|
||||
ModeYear: '' as IntlString,
|
||||
Today: '' as IntlString
|
||||
Today: '' as IntlString,
|
||||
UpcomingEvents: '' as IntlString,
|
||||
TableView: '' as IntlString,
|
||||
DueMinutes: '' as IntlString,
|
||||
DueHours: '' as IntlString,
|
||||
DueDays: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -69,7 +69,9 @@ const calendarPlugin = plugin(calendarId, {
|
||||
Calendar: '' as Ref<Doc>
|
||||
},
|
||||
component: {
|
||||
PersonsPresenter: '' as AnyComponent
|
||||
PersonsPresenter: '' as AnyComponent,
|
||||
UpcomingEvents: '' as AnyComponent,
|
||||
DateTimePresenter: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Title: '' as IntlString,
|
||||
|
@ -13,10 +13,10 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Contact, Organization, Person } from '@anticrm/contact'
|
||||
import type { Contact, EmployeeAccount, Organization, Person } from '@anticrm/contact'
|
||||
import contact from '@anticrm/contact'
|
||||
import { OrganizationSelector } from '@anticrm/contact-resources'
|
||||
import { Account, Class, Client, Doc, generateId, Ref } from '@anticrm/core'
|
||||
import { Account, Class, Client, Doc, generateId, getCurrentAccount, Ref } from '@anticrm/core'
|
||||
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
|
||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
||||
import type { Candidate, Review } from '@anticrm/recruit'
|
||||
@ -32,6 +32,8 @@
|
||||
|
||||
export let preserveCandidate = false
|
||||
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
|
||||
let status: Status = OK
|
||||
|
||||
let title: string = ''
|
||||
@ -56,7 +58,8 @@
|
||||
description,
|
||||
company,
|
||||
verdict: '',
|
||||
title
|
||||
title,
|
||||
participants: [currentUser.employee]
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
@ -16,7 +16,7 @@
|
||||
import type { Doc, Ref } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import task from '@anticrm/task'
|
||||
import calendar from '@anticrm/calendar'
|
||||
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import recruit from '../../plugin'
|
||||
@ -50,8 +50,7 @@
|
||||
label: recruit.string.Opinions,
|
||||
sortingKey: 'opinions'
|
||||
},
|
||||
'date',
|
||||
'dueDate'
|
||||
{ key: '', presenter: calendar.component.DateTimePresenter, label: calendar.string.Date, sortingKey: 'date' },
|
||||
]}
|
||||
options={{
|
||||
lookup: {
|
||||
|
Loading…
Reference in New Issue
Block a user