Remove review Category (#1727)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-05-12 16:40:22 +07:00 committed by GitHub
parent 6c96af45d4
commit f8e0e58a6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 291 additions and 790 deletions

View File

@ -44,7 +44,7 @@ import { Applicant, Candidate, Candidates, Vacancy } from '@anticrm/recruit'
import { KeyBinding } from '@anticrm/view' import { KeyBinding } from '@anticrm/view'
import recruit from './plugin' import recruit from './plugin'
import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review' import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review'
import { TOpinion, TReview, TReviewCategory } from './review-model' import { TOpinion, TReview } from './review-model'
@Model(recruit.class.Vacancy, task.class.SpaceWithStates) @Model(recruit.class.Vacancy, task.class.SpaceWithStates)
@UX(recruit.string.Vacancy, recruit.icon.Vacancy) @UX(recruit.string.Vacancy, recruit.icon.Vacancy)
@ -123,7 +123,7 @@ export class TApplicant extends TTask implements Applicant {
} }
export function createModel (builder: Builder): void { export function createModel (builder: Builder): void {
builder.createModel(TVacancy, TCandidates, TCandidate, TApplicant, TReviewCategory, TReview, TOpinion) builder.createModel(TVacancy, TCandidates, TCandidate, TApplicant, TReview, TOpinion)
builder.mixin(recruit.class.Vacancy, core.class.Class, workbench.mixin.SpaceView, { builder.mixin(recruit.class.Vacancy, core.class.Class, workbench.mixin.SpaceView, {
view: { view: {
@ -156,14 +156,7 @@ export function createModel (builder: Builder): void {
icon: recruit.icon.RecruitApplication, icon: recruit.icon.RecruitApplication,
hidden: false, hidden: false,
navigatorModel: { navigatorModel: {
spaces: [ spaces: [],
{
label: recruit.string.ReviewCategory,
spaceClass: recruit.class.ReviewCategory,
addSpaceLabel: recruit.string.CreateReviewCategory,
createComponent: recruit.component.CreateReviewCategory
}
],
specials: [ specials: [
{ {
id: vacanciesId, id: vacanciesId,
@ -218,15 +211,19 @@ export function createModel (builder: Builder): void {
} }
}, },
{ {
id: 'upcoming', id: 'reviews',
component: calendar.component.UpcomingEvents, component: calendar.component.Events,
componentProps: { componentProps: {
viewLabel: recruit.string.Reviews,
viewIcon: recruit.icon.Review,
_class: recruit.class.Review, _class: recruit.class.Review,
options: reviewTableOptions, options: reviewTableOptions,
config: reviewTableConfig config: reviewTableConfig,
createLabel: recruit.string.ReviewCreateLabel,
createComponent: recruit.component.CreateReview
}, },
icon: calendar.icon.Calendar, icon: calendar.icon.Calendar,
label: calendar.string.UpcomingEvents, label: recruit.string.Reviews,
position: 'event' position: 'event'
} }
] ]
@ -343,10 +340,6 @@ export function createModel (builder: Builder): void {
editor: recruit.component.EditVacancy editor: recruit.component.EditVacancy
}) })
builder.mixin(recruit.class.ReviewCategory, core.class.Class, view.mixin.ObjectEditor, {
editor: recruit.component.EditReviewCategory
})
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.AttributePresenter, { builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.AttributePresenter, {
presenter: recruit.component.ApplicationPresenter presenter: recruit.component.ApplicationPresenter
}) })
@ -355,10 +348,6 @@ export function createModel (builder: Builder): void {
presenter: recruit.component.VacancyPresenter presenter: recruit.component.VacancyPresenter
}) })
builder.mixin(recruit.class.ReviewCategory, core.class.Class, view.mixin.AttributePresenter, {
presenter: recruit.component.ReviewCategoryPresenter
})
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.ObjectValidator, { builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.ObjectValidator, {
validator: recruit.validator.ApplicantValidator validator: recruit.validator.ApplicantValidator
}) })

View File

@ -13,8 +13,10 @@
// limitations under the License. // limitations under the License.
// //
import core, { Doc, Ref, Space, TxOperations } from '@anticrm/core' import core, { Class, Doc, DOMAIN_TX, Ref, Space, TxOperations } from '@anticrm/core'
import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model' import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
import { DOMAIN_CALENDAR } from '@anticrm/model-calendar'
import { DOMAIN_SPACE } from '@anticrm/model-core'
import tags, { TagCategory } from '@anticrm/model-tags' import tags, { TagCategory } from '@anticrm/model-tags'
import { createKanbanTemplate, createSequence } from '@anticrm/model-task' import { createKanbanTemplate, createSequence } from '@anticrm/model-task'
import { getCategories } from '@anticrm/skillset' import { getCategories } from '@anticrm/skillset'
@ -22,7 +24,31 @@ import { KanbanTemplate } from '@anticrm/task'
import recruit from './plugin' import recruit from './plugin'
export const recruitOperation: MigrateOperation = { export const recruitOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> {}, async migrate (client: MigrationClient): Promise<void> {
await client.update(
DOMAIN_CALENDAR,
{
_class: recruit.class.Review,
space: { $nin: [recruit.space.Reviews] }
},
{
space: recruit.space.Reviews
}
)
const categories = await client.find(DOMAIN_SPACE, {
_class: 'recruit:class:ReviewCategory' as Ref<Class<Doc>>
})
for (const cat of categories) {
await client.delete(DOMAIN_SPACE, cat._id)
}
const catTx = await client.find(DOMAIN_TX, {
objectClass: 'recruit:class:ReviewCategory' as Ref<Class<Doc>>
})
for (const cat of catTx) {
await client.delete(DOMAIN_TX, cat._id)
}
},
async upgrade (client: MigrationUpgradeClient): Promise<void> { async upgrade (client: MigrationUpgradeClient): Promise<void> {
const tx = new TxOperations(client, core.account.System) const tx = new TxOperations(client, core.account.System)
await createDefaults(tx) await createDefaults(tx)
@ -30,7 +56,7 @@ export const recruitOperation: MigrateOperation = {
} }
async function createDefaults (tx: TxOperations): Promise<void> { async function createDefaults (tx: TxOperations): Promise<void> {
await createSpace(tx) await createSpaces(tx)
await createOrUpdate( await createOrUpdate(
tx, tx,
@ -66,44 +92,6 @@ async function createDefaults (tx: TxOperations): Promise<void> {
await createSequence(tx, recruit.class.Opinion) await createSequence(tx, recruit.class.Opinion)
await createSequence(tx, recruit.class.Applicant) await createSequence(tx, recruit.class.Applicant)
await createDefaultKanbanTemplate(tx) await createDefaultKanbanTemplate(tx)
await createReviewTemplates(tx)
}
async function createReviewTemplates (tx: TxOperations): Promise<void> {
if ((await tx.findOne(core.class.TxCreateDoc, { objectId: recruit.template.Interview })) === undefined) {
await createKanbanTemplate(tx, {
kanbanId: recruit.template.Interview,
space: recruit.space.ReviewTemplates as Ref<Doc> as Ref<Space>,
title: 'Interview',
states: [
{ color: 9, title: 'Prepare' },
{ color: 10, title: 'Appointment' },
{ color: 1, title: 'Opinions' }
],
doneStates: [
{ isWon: true, title: 'Pass' },
{ isWon: false, title: 'Failed' }
]
})
}
if ((await tx.findOne(core.class.TxCreateDoc, { objectId: recruit.template.Task })) === undefined) {
await createKanbanTemplate(tx, {
kanbanId: recruit.template.Task,
space: recruit.space.ReviewTemplates as Ref<Doc> as Ref<Space>,
title: 'Test task',
states: [
{ color: 9, title: 'Prepare' },
{ color: 10, title: 'Assigned' },
{ color: 1, title: 'Review' },
{ color: 4, title: 'Opinions' }
],
doneStates: [
{ isWon: true, title: 'Pass' },
{ isWon: false, title: 'Failed' }
]
})
}
} }
async function createDefaultKanbanTemplate (tx: TxOperations): Promise<Ref<KanbanTemplate>> { async function createDefaultKanbanTemplate (tx: TxOperations): Promise<Ref<KanbanTemplate>> {
@ -129,7 +117,7 @@ async function createDefaultKanbanTemplate (tx: TxOperations): Promise<Ref<Kanba
}) })
} }
async function createSpace (tx: TxOperations): Promise<void> { async function createSpaces (tx: TxOperations): Promise<void> {
const current = await tx.findOne(core.class.Space, { const current = await tx.findOne(core.class.Space, {
_id: recruit.space.CandidatesPublic _id: recruit.space.CandidatesPublic
}) })
@ -147,4 +135,22 @@ async function createSpace (tx: TxOperations): Promise<void> {
recruit.space.CandidatesPublic recruit.space.CandidatesPublic
) )
} }
const currentReviews = await tx.findOne(core.class.Space, {
_id: recruit.space.Reviews
})
if (currentReviews === undefined) {
await tx.createDoc(
core.class.Space,
core.space.Space,
{
name: 'Reviews',
description: 'Public reviews',
private: true,
members: [],
archived: false
},
recruit.space.Reviews
)
}
} }

View File

@ -64,7 +64,6 @@ export default mergeIds(recruitId, recruit, {
ApplicationPresenter: '' as AnyComponent, ApplicationPresenter: '' as AnyComponent,
ApplicationsPresenter: '' as AnyComponent, ApplicationsPresenter: '' as AnyComponent,
VacancyPresenter: '' as AnyComponent, VacancyPresenter: '' as AnyComponent,
ReviewCategoryPresenter: '' as AnyComponent,
EditApplication: '' as AnyComponent, EditApplication: '' as AnyComponent,
TemplatesIcon: '' as AnyComponent, TemplatesIcon: '' as AnyComponent,
Applications: '' as AnyComponent, Applications: '' as AnyComponent,
@ -73,7 +72,6 @@ export default mergeIds(recruitId, recruit, {
SkillsView: '' as AnyComponent, SkillsView: '' as AnyComponent,
Vacancies: '' as AnyComponent, Vacancies: '' as AnyComponent,
CreateReviewCategory: '' as AnyComponent,
CreateReview: '' as AnyComponent, CreateReview: '' as AnyComponent,
Reviews: '' as AnyComponent, Reviews: '' as AnyComponent,
KanbanReviewCard: '' as AnyComponent, KanbanReviewCard: '' as AnyComponent,
@ -86,8 +84,6 @@ export default mergeIds(recruitId, recruit, {
}, },
template: { template: {
DefaultVacancy: '' as Ref<KanbanTemplate>, DefaultVacancy: '' as Ref<KanbanTemplate>,
Interview: '' as Ref<KanbanTemplate>,
Task: '' as Ref<KanbanTemplate> Task: '' as Ref<KanbanTemplate>
}, },
completion: { completion: {

View File

@ -5,18 +5,11 @@ import attachment from '@anticrm/model-attachment'
import calendar, { TEvent } from '@anticrm/model-calendar' import calendar, { TEvent } from '@anticrm/model-calendar'
import chunter from '@anticrm/model-chunter' import chunter from '@anticrm/model-chunter'
import contact from '@anticrm/model-contact' import contact from '@anticrm/model-contact'
import core, { TAttachedDoc, TSpace } from '@anticrm/model-core' import core, { TAttachedDoc } from '@anticrm/model-core'
import task from '@anticrm/model-task' import task from '@anticrm/model-task'
import { Candidate, Opinion, Review, ReviewCategory } from '@anticrm/recruit' import { Candidate, Opinion, Review } from '@anticrm/recruit'
import recruit from './plugin' import recruit from './plugin'
@Model(recruit.class.ReviewCategory, core.class.Space)
@UX(recruit.string.ReviewCategory, recruit.icon.Review)
export class TReviewCategory extends TSpace implements ReviewCategory {
@Prop(TypeString(), recruit.string.FullDescription)
fullDescription?: string
}
@Model(recruit.class.Review, calendar.class.Event) @Model(recruit.class.Review, calendar.class.Event)
@UX(recruit.string.Review, recruit.icon.Review, recruit.string.ReviewShortLabel, 'number') @UX(recruit.string.Review, recruit.icon.Review, recruit.string.ReviewShortLabel, 'number')
export class TReview extends TEvent implements Review { export class TReview extends TEvent implements Review {

View File

@ -3,9 +3,7 @@ import { Builder } from '@anticrm/model'
import calendar from '@anticrm/model-calendar' import calendar from '@anticrm/model-calendar'
import contact from '@anticrm/model-contact' import contact from '@anticrm/model-contact'
import core from '@anticrm/model-core' import core from '@anticrm/model-core'
import { actionTemplates } from '@anticrm/model-task'
import view, { createAction } from '@anticrm/model-view' import view, { createAction } from '@anticrm/model-view'
import workbench from '@anticrm/model-workbench'
import { Review } from '@anticrm/recruit' import { Review } from '@anticrm/recruit'
import { BuildModelKey } from '@anticrm/view' import { BuildModelKey } from '@anticrm/view'
import recruit from './plugin' import recruit from './plugin'
@ -35,14 +33,6 @@ export const reviewTableConfig: (BuildModelKey | string)[] = [
] ]
export function createReviewModel (builder: Builder): void { export function createReviewModel (builder: Builder): void {
builder.mixin(recruit.class.ReviewCategory, core.class.Class, workbench.mixin.SpaceView, {
view: {
class: recruit.class.Review,
createItemDialog: recruit.component.CreateReview,
createItemLabel: recruit.string.ReviewCreateLabel
}
})
builder.mixin(recruit.class.Review, core.class.Class, view.mixin.CollectionEditor, { builder.mixin(recruit.class.Review, core.class.Class, view.mixin.CollectionEditor, {
editor: recruit.component.Reviews editor: recruit.component.Reviews
}) })
@ -106,9 +96,6 @@ export function createReviewModel (builder: Builder): void {
} }
}) })
createAction(builder, { ...actionTemplates.archiveSpace, target: recruit.class.ReviewCategory })
createAction(builder, { ...actionTemplates.unarchiveSpace, target: recruit.class.ReviewCategory })
const reviewOptions: FindOptions<Review> = { const reviewOptions: FindOptions<Review> = {
lookup: { lookup: {
attachedTo: recruit.mixin.Candidate, attachedTo: recruit.mixin.Candidate,

View File

@ -115,12 +115,7 @@
</div> </div>
<div class="scroll"> <div class="scroll">
<div class="box"> <div class="box">
<ListView <ListView bind:this={list} count={objects.length} bind:selection>
bind:this={list}
count={objects.length}
bind:selection
on:click={(evt) => handleSelection(evt, evt.detail)}
>
<svelte:fragment slot="item" let:item> <svelte:fragment slot="item" let:item>
{@const person = objects[item]} {@const person = objects[item]}
<button <button

View File

@ -21,7 +21,6 @@
"ModeMonth": "Month", "ModeMonth": "Month",
"ModeYear": "Year", "ModeYear": "Year",
"Today": "Today", "Today": "Today",
"UpcomingEvents": "Upcoming events",
"TableView": "Table", "TableView": "Table",
"DueMinutes": "{minutes, plural, =0 {less than a minute} =1 {a minute} other {# minutes}}", "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}}", "DueHours": "{hours, plural, =0 {less than an hour} =1 {1 hour} other {# hours}}",

View File

@ -21,7 +21,6 @@
"ModeMonth": "Месяц", "ModeMonth": "Месяц",
"ModeYear": "Год", "ModeYear": "Год",
"Today": "Сегодня", "Today": "Сегодня",
"UpcomingEvents": "Предстоящие события",
"TableView": "Таблица", "TableView": "Таблица",
"DueMinutes": "{minutes, plural, =0 {меньше минуты} =1 {минута} other {# минут}}", "DueMinutes": "{minutes, plural, =0 {меньше минуты} =1 {минута} other {# минут}}",
"DueHours": "{hours, plural, =0 {меньше часа} =1 {1 час} other {# часы}}", "DueHours": "{hours, plural, =0 {меньше часа} =1 {1 час} other {# часы}}",

View File

@ -14,12 +14,21 @@
--> -->
<script lang="ts"> <script lang="ts">
import { Event } from '@anticrm/calendar' import { Event } from '@anticrm/calendar'
import { EmployeeAccount } from '@anticrm/contact' import { Class, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
import { Class, DocumentQuery, FindOptions, getCurrentAccount, Ref } from '@anticrm/core'
import { Asset, IntlString } from '@anticrm/platform' import { Asset, IntlString } from '@anticrm/platform'
import { AnySvelteComponent, Icon, Label, SearchEdit, Tooltip } from '@anticrm/ui' import {
import { Table } from '@anticrm/view-resources' AnyComponent,
AnySvelteComponent,
Button,
Icon,
IconAdd,
Label,
SearchEdit,
showPopup,
Tooltip
} from '@anticrm/ui'
import view from '@anticrm/view' import view from '@anticrm/view'
import { TableBrowser } from '@anticrm/view-resources'
import calendar from '../plugin' import calendar from '../plugin'
import CalendarView from './CalendarView.svelte' import CalendarView from './CalendarView.svelte'
@ -29,15 +38,17 @@
export let baseMenuClass: Ref<Class<Event>> | undefined = undefined export let baseMenuClass: Ref<Class<Event>> | undefined = undefined
export let config: string[] export let config: string[]
const currentUser = getCurrentAccount() as EmployeeAccount export let viewIcon: Asset = calendar.icon.Calendar
export let viewLabel: IntlString = calendar.string.Events
export let createComponent: AnyComponent | undefined
export let createLabel: IntlString | undefined
let search = '' let search = ''
let resultQuery: DocumentQuery<Event> = {} let resultQuery: DocumentQuery<Event> = {}
function updateResultQuery (search: string): void { function updateResultQuery (search: string): void {
resultQuery = search === '' ? { ...query } : { ...query, $search: search } resultQuery = search === '' ? { ...query } : { ...query, $search: search }
resultQuery.participants = currentUser.employee
} }
$: updateResultQuery(search) $: updateResultQuery(search)
@ -50,6 +61,19 @@
} }
$: viewlets = [ $: viewlets = [
{
component: TableBrowser,
icon: view.icon.Table,
label: calendar.string.TableView,
props: {
_class,
query: resultQuery,
options,
baseMenuClass,
config,
search
}
},
{ {
component: CalendarView, component: CalendarView,
icon: calendar.icon.Calendar, icon: calendar.icon.Calendar,
@ -63,28 +87,22 @@
config, config,
search search
} }
},
{
component: Table,
icon: view.icon.Table,
label: calendar.string.TableView,
props: {
_class,
query: resultQuery,
options,
baseMenuClass,
config,
search
}
} }
] as CalendarViewlet[] ] as CalendarViewlet[]
let selectedViewlet = 0 let selectedViewlet = 0
function showCreateDialog () {
if (createComponent === undefined) {
return
}
showPopup(createComponent, {}, 'top')
}
</script> </script>
<div class="ac-header full"> <div class="ac-header full">
<div class="ac-header__wrap-title"> <div class="ac-header__wrap-title">
<div class="ac-header__icon"><Icon icon={calendar.icon.Calendar} size={'small'} /></div> <div class="ac-header__icon"><Icon icon={viewIcon} size={'small'} /></div>
<span class="ac-header__title"><Label label={calendar.string.UpcomingEvents} /></span> <span class="ac-header__title"><Label label={viewLabel} /></span>
</div> </div>
{#if viewlets.length > 1} {#if viewlets.length > 1}
@ -111,6 +129,7 @@
updateResultQuery(search) updateResultQuery(search)
}} }}
/> />
<Button icon={IconAdd} label={createLabel} kind={'primary'} on:click={showCreateDialog} />
</div> </div>
{#if viewlets[selectedViewlet]} {#if viewlets[selectedViewlet]}

View File

@ -21,7 +21,7 @@ import SaveEventReminder from './components/SaveEventReminder.svelte'
import DateTimePresenter from './components/DateTimePresenter.svelte' import DateTimePresenter from './components/DateTimePresenter.svelte'
import DocReminder from './components/DocReminder.svelte' import DocReminder from './components/DocReminder.svelte'
import PersonsPresenter from './components/PersonsPresenter.svelte' import PersonsPresenter from './components/PersonsPresenter.svelte'
import UpcomingEvents from './components/UpcomingEvents.svelte' import Events from './components/Events.svelte'
import ReminderPresenter from './components/ReminderPresenter.svelte' import ReminderPresenter from './components/ReminderPresenter.svelte'
import ReminderViewlet from './components/activity/ReminderViewlet.svelte' import ReminderViewlet from './components/activity/ReminderViewlet.svelte'
import EditEvent from './components/EditEvent.svelte' import EditEvent from './components/EditEvent.svelte'
@ -37,7 +37,7 @@ export default async (): Promise<Resources> => ({
ReminderPresenter, ReminderPresenter,
PersonsPresenter, PersonsPresenter,
CalendarView, CalendarView,
UpcomingEvents, Events,
DateTimePresenter, DateTimePresenter,
DocReminder, DocReminder,
RemindersPopup RemindersPopup

View File

@ -33,7 +33,6 @@ export default mergeIds(calendarId, calendar, {
ModeMonth: '' as IntlString, ModeMonth: '' as IntlString,
ModeYear: '' as IntlString, ModeYear: '' as IntlString,
Today: '' as IntlString, Today: '' as IntlString,
UpcomingEvents: '' as IntlString,
TableView: '' as IntlString, TableView: '' as IntlString,
DueMinutes: '' as IntlString, DueMinutes: '' as IntlString,
DueHours: '' as IntlString, DueHours: '' as IntlString,

View File

@ -82,7 +82,7 @@ const calendarPlugin = plugin(calendarId, {
}, },
component: { component: {
PersonsPresenter: '' as AnyComponent, PersonsPresenter: '' as AnyComponent,
UpcomingEvents: '' as AnyComponent, Events: '' as AnyComponent,
DateTimePresenter: '' as AnyComponent, DateTimePresenter: '' as AnyComponent,
DocReminder: '' as AnyComponent, DocReminder: '' as AnyComponent,
RemindersPopup: '' as AnyComponent RemindersPopup: '' as AnyComponent

View File

@ -16,7 +16,7 @@
<script lang="ts"> <script lang="ts">
import { Doc, DocumentQuery } from '@anticrm/core' import { Doc, DocumentQuery } from '@anticrm/core'
import { getClient } from '@anticrm/presentation' import { getClient } from '@anticrm/presentation'
import { Button, Icon, IconAdd, Label, Scroller, SearchEdit, showPopup } from '@anticrm/ui' import { Button, Icon, IconAdd, Label, SearchEdit, showPopup } from '@anticrm/ui'
import view, { Viewlet } from '@anticrm/view' import view, { Viewlet } from '@anticrm/view'
import { ActionContext, TableBrowser } from '@anticrm/view-resources' import { ActionContext, TableBrowser } from '@anticrm/view-resources'
import contact from '../plugin' import contact from '../plugin'
@ -66,17 +66,15 @@
/> />
</div> </div>
<Scroller tableFade> {#await tableDescriptor then descr}
{#await tableDescriptor then descr} {#if descr}
{#if descr} <TableBrowser
<TableBrowser _class={contact.class.Contact}
_class={contact.class.Contact} config={descr.config}
config={descr.config} options={descr.options}
options={descr.options} query={resultQuery}
query={resultQuery} showNotification
showNotification />
/> {/if}
{/if} {/await}
{/await}
</Scroller>
</div> </div>

View File

@ -12,9 +12,9 @@
"VacancyPlaceholder": "Разработчик", "VacancyPlaceholder": "Разработчик",
"MakePrivate": "Сделать личным", "MakePrivate": "Сделать личным",
"MakePrivateDescription": "Только пользователи могут видеть это", "MakePrivateDescription": "Только пользователи могут видеть это",
"CreateAnApplication": "Создать претендента", "CreateAnApplication": "Новый Претендент",
"NoApplicationsForCandidate": "Нет претендентов для данного кандидата.", "NoApplicationsForCandidate": "Нет претендентов для данного кандидата.",
"CreateApplication": "Создать претендента", "CreateApplication": "Новый Претендент",
"SelectVacancy": "Выбрать вакансию", "SelectVacancy": "Выбрать вакансию",
"Candidate": "Кандидат", "Candidate": "Кандидат",
"CandidateCreateLabel": "Кандидата", "CandidateCreateLabel": "Кандидата",
@ -57,21 +57,21 @@
"EditVacancy": "Редактировать", "EditVacancy": "Редактировать",
"FullDescription": "Детальное описание", "FullDescription": "Детальное описание",
"CreateReviewCategory": "Создать категорию оценки", "CreateReviewCategory": "Создать категорию ревью",
"ReviewCategoryName": "Имя категории*", "ReviewCategoryName": "Имя категории*",
"ReviewCategoryPlaceholder": "Интервью", "ReviewCategoryPlaceholder": "Интервью",
"ReviewCategory": "Оценки", "ReviewCategory": "Ревью",
"ReviewCategoryDescription": "Описание", "ReviewCategoryDescription": "Описание",
"ThisReviewCategoryIsPrivate": "Эта категория личная", "ThisReviewCategoryIsPrivate": "Эта категория личная",
"CreateReview": "Запланировать оценку", "CreateReview": "Запланировать Ревью",
"CreateReviewParams": "Запланировать {label}", "CreateReviewParams": "Запланировать {label}",
"SelectReviewCategory": "выбрать категорию", "SelectReviewCategory": "выбрать категорию",
"Reviews": "Оценки", "Reviews": "Ревью",
"Review": "Оценка", "Review": "Ревью",
"ReviewCreateLabel": "Оценку", "ReviewCreateLabel": "Ревью",
"Opinions": "Мнения", "Opinions": "Мнения",
"Opinion": "Мнение", "Opinion": "Мнение",
"OpinionValue": "Оценка", "OpinionValue": "Значение",
"OpinionShortLabel": "OPE", "OpinionShortLabel": "OPE",
"ReviewShortLabel": "RVE", "ReviewShortLabel": "RVE",
"StartDate": "Дата начала", "StartDate": "Дата начала",
@ -79,9 +79,9 @@
"ReviewCategoryTitle":"Категория", "ReviewCategoryTitle":"Категория",
"Verdict": "Вердикт", "Verdict": "Вердикт",
"OpinionSave": "Сохранить", "OpinionSave": "Сохранить",
"CandidateReviews": "Оценки кандидата", "CandidateReviews": "Ревью кандидата",
"NoReviewForCandidate": "Нет оценок", "NoReviewForCandidate": "Нет ревью",
"CreateAnReview": "Добавить оценку", "CreateAnReview": "Добавить Ревью",
"CreateOpinion": "Добавить мнение", "CreateOpinion": "Добавить мнение",
"OpinionValuePlaceholder": "10/10", "OpinionValuePlaceholder": "10/10",
"Participants": "Участники", "Participants": "Участники",

View File

@ -20,7 +20,7 @@
import { Doc, DocumentQuery, FindOptions } from '@anticrm/core' import { Doc, DocumentQuery, FindOptions } from '@anticrm/core'
import { Applicant } from '@anticrm/recruit' import { Applicant } from '@anticrm/recruit'
import task from '@anticrm/task' import task from '@anticrm/task'
import { Button, Icon, IconAdd, Label, Scroller, SearchEdit, showPopup } from '@anticrm/ui' import { Button, Icon, IconAdd, Label, SearchEdit, showPopup } from '@anticrm/ui'
import { BuildModelKey } from '@anticrm/view' import { BuildModelKey } from '@anticrm/view'
import { TableBrowser } from '@anticrm/view-resources' import { TableBrowser } from '@anticrm/view-resources'
import recruit from '../plugin' import recruit from '../plugin'
@ -82,6 +82,4 @@
<Button icon={IconAdd} label={recruit.string.ApplicationCreateLabel} kind={'primary'} on:click={showCreateDialog} /> <Button icon={IconAdd} label={recruit.string.ApplicationCreateLabel} kind={'primary'} on:click={showCreateDialog} />
</div> </div>
<Scroller tableFade> <TableBrowser _class={recruit.class.Applicant} {config} {options} query={resultQuery} showNotification />
<TableBrowser _class={recruit.class.Applicant} {config} {options} query={resultQuery} showNotification />
</Scroller>

View File

@ -18,7 +18,7 @@
import { Doc, DocumentQuery, Ref } from '@anticrm/core' import { Doc, DocumentQuery, Ref } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags' import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags'
import { Component, Icon, Label, Scroller, SearchEdit } from '@anticrm/ui' import { Component, Icon, Label, SearchEdit } from '@anticrm/ui'
import view, { Viewlet } from '@anticrm/view' import view, { Viewlet } from '@anticrm/view'
import { ActionContext, TableBrowser } from '@anticrm/view-resources' import { ActionContext, TableBrowser } from '@anticrm/view-resources'
import recruit from '../plugin' import recruit from '../plugin'
@ -82,16 +82,14 @@
mode: 'browser' mode: 'browser'
}} }}
/> />
<Scroller tableFade> {#await tableDescriptor then descr}
{#await tableDescriptor then descr} {#if descr}
{#if descr} <TableBrowser
<TableBrowser _class={recruit.mixin.Candidate}
_class={recruit.mixin.Candidate} config={descr.config}
config={descr.config} options={descr.options}
options={descr.options} query={resultQuery}
query={resultQuery} showNotification
showNotification />
/> {/if}
{/if} {/await}
{/await}
</Scroller>

View File

@ -17,8 +17,7 @@
import core, { Doc, DocumentQuery, Lookup, Ref } from '@anticrm/core' import core, { Doc, DocumentQuery, Lookup, Ref } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation' import { createQuery } from '@anticrm/presentation'
import { Vacancy } from '@anticrm/recruit' import { Vacancy } from '@anticrm/recruit'
import { Button, getCurrentLocation, Icon, IconAdd, Label, navigate, Scroller, showPopup } from '@anticrm/ui' import { Button, getCurrentLocation, Icon, IconAdd, Label, navigate, SearchEdit, showPopup } from '@anticrm/ui'
import { SearchEdit } from '@anticrm/ui'
import { TableBrowser } from '@anticrm/view-resources' import { TableBrowser } from '@anticrm/view-resources'
import recruit from '../plugin' import recruit from '../plugin'
import CreateVacancy from './CreateVacancy.svelte' import CreateVacancy from './CreateVacancy.svelte'
@ -89,44 +88,42 @@
/> />
<Button icon={IconAdd} label={recruit.string.VacancyCreateLabel} kind={'primary'} on:click={showCreateDialog} /> <Button icon={IconAdd} label={recruit.string.VacancyCreateLabel} kind={'primary'} on:click={showCreateDialog} />
</div> </div>
<Scroller tableFade> <TableBrowser
<TableBrowser _class={recruit.class.Vacancy}
_class={recruit.class.Vacancy} config={[
config={[ {
{ key: '',
key: '', presenter: recruit.component.VacancyItemPresenter,
presenter: recruit.component.VacancyItemPresenter, label: recruit.string.Vacancy,
label: recruit.string.Vacancy, sortingKey: 'name',
sortingKey: 'name', props: { action }
props: { action } },
}, {
{ key: '',
key: '', presenter: recruit.component.VacancyCountPresenter,
presenter: recruit.component.VacancyCountPresenter, label: recruit.string.Applications,
label: recruit.string.Applications, props: { applications },
props: { applications }, sortingKey: '@applications',
sortingKey: '@applications', sortingFunction: applicationSorting
sortingFunction: applicationSorting },
}, '$lookup.company',
'$lookup.company', 'location',
'location', 'description',
'description', {
{ key: '',
key: '', presenter: recruit.component.VacancyModifiedPresenter,
presenter: recruit.component.VacancyModifiedPresenter, label: core.string.Modified,
label: core.string.Modified, props: { applications },
props: { applications }, sortingKey: 'modifiedOn',
sortingKey: 'modifiedOn', sortingFunction: modifiedSorting
sortingFunction: modifiedSorting }
} ]}
]} options={{
options={{ lookup
lookup }}
}} query={{
query={{ ...resultQuery,
...resultQuery, archived: false
archived: false }}
}} showNotification
showNotification />
/>
</Scroller>

View File

@ -21,14 +21,14 @@
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform' import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
import { Card, getClient, UserBox, UserBoxList } from '@anticrm/presentation' import { Card, getClient, UserBox, UserBoxList } from '@anticrm/presentation'
import type { Candidate, Review } from '@anticrm/recruit' import type { Candidate, Review } from '@anticrm/recruit'
import task, { SpaceWithStates } from '@anticrm/task' import task from '@anticrm/task'
import { StyledTextBox } from '@anticrm/text-editor' import { StyledTextBox } from '@anticrm/text-editor'
import { DateRangePresenter, EditBox, Status as StatusControl } from '@anticrm/ui' import { DateRangePresenter, EditBox, Status as StatusControl } from '@anticrm/ui'
import view from '@anticrm/view' import view from '@anticrm/view'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import recruit from '../../plugin' import recruit from '../../plugin'
export let space: Ref<SpaceWithStates> // export let space: Ref<SpaceWithStates>
export let candidate: Ref<Person> export let candidate: Ref<Person>
export let preserveCandidate = false export let preserveCandidate = false
@ -49,7 +49,7 @@
attachedTo: candidate, attachedTo: candidate,
attachedToClass: recruit.mixin.Candidate, attachedToClass: recruit.mixin.Candidate,
_class: recruit.class.Review, _class: recruit.class.Review,
space: space, space: recruit.space.Reviews,
_id: generateId(), _id: generateId(),
collection: 'reviews', collection: 'reviews',
modifiedOn: Date.now(), modifiedOn: Date.now(),
@ -147,11 +147,6 @@
labelProps={{ label: spaceLabel }} labelProps={{ label: spaceLabel }}
okAction={createReview} okAction={createReview}
canSave={status.severity === Severity.OK && title.trim().length > 0} canSave={status.severity === Severity.OK && title.trim().length > 0}
spaceClass={recruit.class.ReviewCategory}
spaceQuery={{ archived: false }}
spaceLabel={recruit.string.ReviewCategory}
spacePlaceholder={recruit.string.SelectReviewCategory}
bind:space={doc.space}
on:close={() => { on:close={() => {
dispatch('close') dispatch('close')
}} }}

View File

@ -1,61 +0,0 @@
<!--
// Copyright © 2020 Anticrm Platform Contributors.
//
// 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 core from '@anticrm/core'
import { getClient, SpaceCreateCard } from '@anticrm/presentation'
import { EditBox } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import recruit from '../../plugin'
import Review from '../icons/Review.svelte'
const dispatch = createEventDispatcher()
let name: string = ''
const description: string = ''
export function canClose (): boolean {
return name === ''
}
const client = getClient()
async function createReviewCategory () {
await client.createDoc(recruit.class.ReviewCategory, core.space.Space, {
name,
description,
private: false,
archived: false,
members: []
})
}
</script>
<SpaceCreateCard
label={recruit.string.CreateReviewCategory}
okAction={createReviewCategory}
canSave={!!name}
on:close={() => {
dispatch('close')
}}
>
<EditBox
label={recruit.string.ReviewCategoryName}
bind:value={name}
icon={Review}
placeholder={recruit.string.ReviewCategoryPlaceholder}
maxWidth={'16rem'}
focus
/>
</SpaceCreateCard>

View File

@ -1,264 +0,0 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 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 activity from '@anticrm/activity'
import { Attachments } from '@anticrm/attachment-resources'
import type { Ref } from '@anticrm/core'
import type { IntlString } from '@anticrm/platform'
import { AttributesBar, createQuery, getClient } from '@anticrm/presentation'
import { ReviewCategory } from '@anticrm/recruit'
import { TextEditor } from '@anticrm/text-editor'
import { Component, EditBox, Grid, Icon, IconClose, Label, ToggleWithLabel } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import recruit from '../../plugin'
export let _id: Ref<ReviewCategory>
let object: ReviewCategory
const dispatch = createEventDispatcher()
const client = getClient()
const query = createQuery()
const clazz = client.getHierarchy().getClass(recruit.class.ReviewCategory)
$: query.query(recruit.class.ReviewCategory, { _id }, (result) => {
object = result[0]
})
const tabs: IntlString[] = ['General' as IntlString, 'Members' as IntlString, 'Activity' as IntlString]
let selected = 0
let textEditor: TextEditor
function onChange (key: string, value: any): void {
client.updateDoc(object._class, object.space, object._id, { [key]: value })
}
</script>
<div
class="overlay"
on:click={() => {
dispatch('close')
}}
/>
<div class="dialog-container">
{#if object}
<div class="flex-row-center header">
<div class="flex-grow">
<div class="flex">
<div class="svg-medium flex-no-shrink">
{#if clazz.icon}<Icon icon={clazz.icon} size={'medium'} />{/if}
</div>
<div class="flex-grow fs-title ml-2">
{object.name}
</div>
</div>
<div class="small-text">{object.description}</div>
</div>
<div
class="tool"
on:click={() => {
dispatch('close')
}}
>
<IconClose size={'small'} />
</div>
</div>
<div class="flex-row-center subtitle">
<AttributesBar {object} keys={[]} />
</div>
<div class="flex-stretch tab-container">
{#each tabs as tab, i}
<div
class="flex-row-center tab"
class:selected={i === selected}
on:click={() => {
selected = i
}}
>
<Label label={tab} />
</div>
{/each}
<div class="grow" />
</div>
<div class="scroll">
<div class="flex-col box">
{#if selected === 0}
<Grid column={1} rowGap={1.5}>
<EditBox
label={recruit.string.ReviewCategoryName}
bind:value={object.name}
placeholder={recruit.string.ReviewCategoryPlaceholder}
maxWidth="39rem"
focus
on:change={() => {
onChange('name', object.name)
}}
/>
<EditBox
label={recruit.string.Description}
bind:value={object.description}
placeholder={recruit.string.ReviewCategoryDescription}
maxWidth="39rem"
focus
on:change={() => {
onChange('description', object.description)
}}
/>
</Grid>
<div class="mt-10">
<span class="title">Description</span>
<div class="description-container">
<TextEditor
bind:this={textEditor}
bind:content={object.fullDescription}
on:blur={textEditor.submit}
on:content={() => {
onChange('fullDescription', object.fullDescription)
}}
/>
</div>
</div>
<div class="mt-14">
<Attachments objectId={object._id} _class={object._class} space={object.space} />
</div>
{:else if selected === 1}
<ToggleWithLabel
label={recruit.string.ThisReviewCategoryIsPrivate}
description={recruit.string.MakePrivateDescription}
/>
{:else if selected === 2}
<Component is={activity.component.Activity} props={{ object, transparent: true }} />
{/if}
</div>
</div>
{/if}
</div>
<style lang="scss">
.dialog-container {
overflow: hidden;
position: fixed;
top: 32px;
bottom: 1.25rem;
left: 50%;
right: 1rem;
display: flex;
flex-direction: column;
height: calc(100% - 32px - 1.25rem);
background: var(--theme-bg-color);
border-radius: 1.25rem;
.header {
flex-shrink: 0;
padding: 0 2rem 0 2.5rem;
height: 4.5rem;
border-bottom: 1px solid var(--theme-dialog-divider);
.tool {
margin-left: 0.75rem;
color: var(--theme-content-accent-color);
cursor: pointer;
&:hover {
color: var(--theme-caption-color);
}
}
}
.subtitle {
flex-shrink: 0;
padding: 0 2.5rem;
height: 3.5rem;
border-bottom: 1px solid var(--theme-dialog-divider);
}
}
.tab-container {
flex-shrink: 0;
flex-wrap: nowrap;
margin: 0 2.5rem;
height: 4.5rem;
border-bottom: 1px solid var(--theme-menu-divider);
.tab {
height: 4.5rem;
font-weight: 500;
color: var(--theme-content-trans-color);
cursor: pointer;
user-select: none;
&.selected {
border-top: 0.125rem solid transparent;
border-bottom: 0.125rem solid var(--theme-caption-color);
color: var(--theme-caption-color);
cursor: default;
}
}
.tab + .tab {
margin-left: 2.5rem;
}
.grow {
min-width: 2.5rem;
flex-grow: 1;
}
}
.scroll {
flex-grow: 1;
overflow-x: hidden;
overflow-y: auto;
margin: 1rem 2rem;
padding: 1.5rem 0.5rem;
height: 100%;
.box {
margin-right: 1px;
height: 100%;
}
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #000;
opacity: 0.5;
}
.title {
margin-right: 0.75rem;
font-weight: 500;
font-size: 1.25rem;
color: var(--theme-caption-color);
}
.description-container {
display: flex;
justify-content: space-between;
overflow-y: auto;
height: 100px;
padding: 0px 16px;
background-color: var(--theme-bg-accent-color);
border: 1px solid var(--theme-bg-accent-color);
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-radius: 0.75rem;
margin-top: 1.5rem;
}
</style>

View File

@ -1,78 +0,0 @@
<!--
// Copyright © 2020 Anticrm Platform Contributors.
//
// 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 Company from '../icons/Company.svelte'
import type { ReviewCategory } from '@anticrm/recruit'
import { closePanel, closePopup, closeTooltip, getCurrentLocation, navigate } from '@anticrm/ui'
export let category: ReviewCategory
</script>
<div class="flex-col h-full card-container">
<div class="label">Review category</div>
<div class="flex-center logo">
<Company size={'large'} />
</div>
{#if category}
<div
class="name over-underline"
on:click={() => {
closeTooltip()
closePopup()
closePanel()
const loc = getCurrentLocation()
loc.path[2] = category._id
loc.path.length = 3
navigate(loc)
}}
>
{category.name}
</div>
<div class="description">{category.description ?? ''}</div>
{/if}
</div>
<style lang="scss">
.card-container {
padding: 1rem 1.5rem 1.25rem;
background-color: var(--theme-button-bg-enabled);
border: 1px solid var(--theme-bg-accent-color);
border-radius: 0.75rem;
.logo {
width: 5rem;
height: 5rem;
color: var(--primary-button-color);
background-color: var(--primary-button-enabled);
border-radius: 50%;
}
.label {
margin-bottom: 1.75rem;
font-weight: 500;
font-size: 0.625rem;
color: var(--theme-content-dark-color);
}
.name {
margin: 1rem 0 0.25rem;
font-weight: 500;
font-size: 1rem;
color: var(--theme-caption-color);
}
.description {
font-size: 0.75rem;
color: var(--theme-content-dark-color);
}
}
</style>

View File

@ -1,37 +0,0 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021, 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 type { ReviewCategory } from '@anticrm/recruit'
import { Icon } from '@anticrm/ui'
import { showPanel } from '@anticrm/ui/src/panelup'
import recruit from '../../plugin'
export let value: ReviewCategory
export let inline: boolean = false
function show () {
showPanel(recruit.component.EditReviewCategory, value._id, value._class, 'right')
}
</script>
{#if value}
<div class="flex-presenter" class:inline-presenter={inline} on:click={show}>
<div class="icon">
<Icon icon={recruit.icon.Vacancy} size={'small'} />
</div>
<span class="label">{value.name}</span>
</div>
{/if}

View File

@ -33,13 +33,10 @@ import EditVacancy from './components/EditVacancy.svelte'
import KanbanCard from './components/KanbanCard.svelte' import KanbanCard from './components/KanbanCard.svelte'
import CreateOpinion from './components/review/CreateOpinion.svelte' import CreateOpinion from './components/review/CreateOpinion.svelte'
import CreateReview from './components/review/CreateReview.svelte' import CreateReview from './components/review/CreateReview.svelte'
import CreateReviewCategory from './components/review/CreateReviewCategory.svelte'
import EditReview from './components/review/EditReview.svelte' import EditReview from './components/review/EditReview.svelte'
import EditReviewCategory from './components/review/EditReviewCategory.svelte'
import OpinionPresenter from './components/review/OpinionPresenter.svelte' import OpinionPresenter from './components/review/OpinionPresenter.svelte'
import Opinions from './components/review/Opinions.svelte' import Opinions from './components/review/Opinions.svelte'
import OpinionsPresenter from './components/review/OpinionsPresenter.svelte' import OpinionsPresenter from './components/review/OpinionsPresenter.svelte'
import ReviewCategoryPresenter from './components/review/ReviewCategoryPresenter.svelte'
import ReviewPresenter from './components/review/ReviewPresenter.svelte' import ReviewPresenter from './components/review/ReviewPresenter.svelte'
import Reviews from './components/review/Reviews.svelte' import Reviews from './components/review/Reviews.svelte'
import SkillsView from './components/SkillsView.svelte' import SkillsView from './components/SkillsView.svelte'
@ -152,8 +149,6 @@ export default async (): Promise<Resources> => ({
VacancyCountPresenter, VacancyCountPresenter,
VacancyModifiedPresenter, VacancyModifiedPresenter,
CreateReviewCategory,
EditReviewCategory,
CreateReview, CreateReview,
ReviewPresenter, ReviewPresenter,
EditReview, EditReview,
@ -161,7 +156,6 @@ export default async (): Promise<Resources> => ({
Opinions, Opinions,
OpinionPresenter, OpinionPresenter,
OpinionsPresenter, OpinionsPresenter,
ReviewCategoryPresenter,
ApplicationsView, ApplicationsView,
NewCandidateHeader NewCandidateHeader

View File

@ -133,7 +133,7 @@ const recruit = plugin(recruitId, {
}, },
space: { space: {
VacancyTemplates: '' as Ref<KanbanTemplateSpace>, VacancyTemplates: '' as Ref<KanbanTemplateSpace>,
ReviewTemplates: '' as Ref<KanbanTemplateSpace> Reviews: '' as Ref<Space>
} }
}) })

View File

@ -17,7 +17,7 @@
import { IntlString, translate } from '@anticrm/platform' import { IntlString, translate } from '@anticrm/platform'
import { createQuery } from '@anticrm/presentation' import { createQuery } from '@anticrm/presentation'
import { TagCategory, TagElement } from '@anticrm/tags' import { TagCategory, TagElement } from '@anticrm/tags'
import { Button, Icon, Label, Scroller, SearchEdit, showPopup, IconAdd } from '@anticrm/ui' import { Button, Icon, IconAdd, Label, SearchEdit, showPopup } from '@anticrm/ui'
import { TableBrowser } from '@anticrm/view-resources' import { TableBrowser } from '@anticrm/view-resources'
import tags from '../plugin' import tags from '../plugin'
import CategoryBar from './CategoryBar.svelte' import CategoryBar from './CategoryBar.svelte'
@ -108,40 +108,38 @@
updateResultQuery(search, category) updateResultQuery(search, category)
}} }}
/> />
<Scroller tableFade> <TableBrowser
<TableBrowser _class={tags.class.TagElement}
_class={tags.class.TagElement} config={[
config={[ {
{ key: '',
key: '', label: item,
label: item, presenter: tags.component.TagElementPresenter,
presenter: tags.component.TagElementPresenter, props: { edit: true, keyTitle },
props: { edit: true, keyTitle }, sortingKey: 'title'
sortingKey: 'title' },
}, ...(category === undefined
...(category === undefined ? [
? [ {
{ key: '$lookup.category',
key: '$lookup.category', presenter: tags.component.CategoryPresenter,
presenter: tags.component.CategoryPresenter, sortingKey: 'category',
sortingKey: 'category', label: tags.string.CategoryLabel
label: tags.string.CategoryLabel }
} ]
] : []),
: []), {
{ key: '',
key: '', presenter: tags.component.TagElementCountPresenter,
presenter: tags.component.TagElementCountPresenter, label: item,
label: item, props: { tagElements, label: item, onTag },
props: { tagElements, label: item, onTag }, sortingKey: '@tagCount',
sortingKey: '@tagCount', sortingFunction: countSorting
sortingFunction: countSorting },
}, 'description',
'description', 'modifiedOn'
'modifiedOn' ]}
]} options={opt}
options={opt} query={resultQuery}
query={resultQuery} showNotification
showNotification />
/>
</Scroller>

View File

@ -20,7 +20,7 @@
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags' import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags'
import { DoneState, Task } from '@anticrm/task' import { DoneState, Task } from '@anticrm/task'
import { Component, Icon, Label, Scroller, SearchEdit } from '@anticrm/ui' import { Component, Icon, Label, SearchEdit } from '@anticrm/ui'
import { TableBrowser } from '@anticrm/view-resources' import { TableBrowser } from '@anticrm/view-resources'
import task from '../plugin' import task from '../plugin'
@ -99,31 +99,29 @@
on:change={(evt) => updateCategory(evt.detail)} on:change={(evt) => updateCategory(evt.detail)}
/> />
<Scroller tableFade> <TableBrowser
<TableBrowser {_class}
{_class} config={[
config={[ '',
'', '$lookup.attachedTo',
'$lookup.attachedTo', '$lookup.assignee',
'$lookup.assignee', '$lookup.state',
'$lookup.state', '$lookup.doneState',
'$lookup.doneState', {
{ key: '',
key: '', presenter: attachment.component.AttachmentsPresenter,
presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files,
label: attachment.string.Files, sortingKey: 'attachments'
sortingKey: 'attachments' },
}, {
{ key: '',
key: '', presenter: chunter.component.CommentsPresenter,
presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments,
label: chunter.string.Comments, sortingKey: 'comments'
sortingKey: 'comments' },
}, 'modifiedOn'
'modifiedOn' ]}
]} options={taskOptions}
options={taskOptions} query={resultQuery}
query={resultQuery} showNotification
showNotification />
/>
</Scroller>

View File

@ -17,13 +17,12 @@
import { Class, DocumentQuery, FindOptions, Ref, SortingOrder } from '@anticrm/core' import { Class, DocumentQuery, FindOptions, Ref, SortingOrder } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation' import { createQuery } from '@anticrm/presentation'
import { DoneState, SpaceWithStates, State, Task } from '@anticrm/task' import { DoneState, SpaceWithStates, State, Task } from '@anticrm/task'
import { ScrollBox } from '@anticrm/ui'
import Label from '@anticrm/ui/src/components/Label.svelte' import Label from '@anticrm/ui/src/components/Label.svelte'
import { TableBrowser } from '@anticrm/view-resources' import { TableBrowser } from '@anticrm/view-resources'
import task from '../plugin'
import Lost from './icons/Lost.svelte' import Lost from './icons/Lost.svelte'
import Won from './icons/Won.svelte' import Won from './icons/Won.svelte'
import StatesBar from './state/StatesBar.svelte' import StatesBar from './state/StatesBar.svelte'
import task from '../plugin'
export let _class: Ref<Class<Task>> export let _class: Ref<Class<Task>>
export let space: Ref<SpaceWithStates> export let space: Ref<SpaceWithStates>
@ -176,9 +175,7 @@
</div> </div>
</div> </div>
<div class="statustableview-container"> <div class="statustableview-container">
<ScrollBox vertical stretch noShift> <TableBrowser {_class} {query} config={resConfig} {options} showNotification />
<TableBrowser {_class} {query} config={resConfig} {options} showNotification />
</ScrollBox>
</div> </div>
<style lang="scss"> <style lang="scss">

View File

@ -14,6 +14,7 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core' import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
import { Scroller } from '@anticrm/ui'
import { BuildModelKey } from '@anticrm/view' import { BuildModelKey } from '@anticrm/view'
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { ActionContext } from '..' import { ActionContext } from '..'
@ -50,26 +51,28 @@
}} }}
/> />
<Table <Scroller tableFade>
bind:this={table} <Table
{_class} bind:this={table}
{config} {_class}
{options} {config}
{query} {options}
{showNotification} {query}
{baseMenuClass} {showNotification}
{loadingProps} {baseMenuClass}
highlightRows={true} {loadingProps}
enableChecking highlightRows={true}
checked={$selectionStore ?? []} enableChecking
selection={listProvider.current($focusStore)} checked={$selectionStore ?? []}
on:row-focus={(evt) => { selection={listProvider.current($focusStore)}
listProvider.updateFocus(evt.detail) on:row-focus={(evt) => {
}} listProvider.updateFocus(evt.detail)
on:content={(evt) => { }}
listProvider.update(evt.detail) on:content={(evt) => {
}} listProvider.update(evt.detail)
on:check={(evt) => { }}
listProvider.updateSelection(evt.detail.docs, evt.detail.value) on:check={(evt) => {
}} listProvider.updateSelection(evt.detail.docs, evt.detail.value)
/> }}
/>
</Scroller>

View File

@ -15,7 +15,6 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Class, Doc, DocumentQuery, FindOptions, Ref, Space } from '@anticrm/core' import type { Class, Doc, DocumentQuery, FindOptions, Ref, Space } from '@anticrm/core'
import { Scroller } from '@anticrm/ui'
import { TableBrowser } from '..' import { TableBrowser } from '..'
import ActionContext from './ActionContext.svelte' import ActionContext from './ActionContext.svelte'
@ -35,6 +34,4 @@
mode: 'browser' mode: 'browser'
}} }}
/> />
<Scroller tableFade> <TableBrowser {_class} {config} {options} query={resultQuery} {baseMenuClass} showNotification />
<TableBrowser {_class} {config} {options} query={resultQuery} {baseMenuClass} showNotification />
</Scroller>

View File

@ -136,34 +136,20 @@ test.describe('recruit tests', () => {
await page.locator('[id="app-recruit\\:string\\:RecruitApplication"]').click() await page.locator('[id="app-recruit\\:string\\:RecruitApplication"]').click()
await page.hover('text=Reviews') await page.click('text=Reviews')
await page.click('[name="tooltip-recruit:string:CreateReviewCategory"]')
await page.fill('[placeholder="Interview"]', interviewId)
await page.click('button:has-text("Create")')
await page.locator(`text=${interviewId}`).click()
// Click button:has-text("Review")
await page.click('button:has-text("Review")') await page.click('button:has-text("Review")')
// Click [placeholder="\ "]
await page.click('[placeholder="Title"]')
// Fill [placeholder="\ "]
await page.fill('[placeholder="Title"]', 'Meet PEterson')
// Click text=Location Company Company >> [placeholder="\ "]
await page.click('[placeholder="Location"]')
// Fill text=Location Company Company >> [placeholder="\ "]
await page.fill('[placeholder="Location"]', 'NSK')
// Click text=Company Company >> div
// await page.click('text=Company Company >> div')
// Click button:has-text("Apple")
// await page.click('button:has-text("Apple")')
// Click text=Candidate Not selected >> span
await page.click('form button:has-text("Candidate")')
// Click button:has-text("Andrey P.")
await page.click('button:has-text("Andrey P.")')
// Click text=Create
await page.click('text=Create')
await page.click('[placeholder="Title"]')
await page.fill('[placeholder="Title"]', `Meet Peterson ${interviewId}`)
await page.click('[placeholder="Location"]')
await page.fill('[placeholder="Location"]', 'NSK')
await page.click('form button:has-text("Candidate")')
await page.click('button:has-text("Andrey P.")')
await page.click('text=Create')
await page.click('td:has-text("RVE-")') await page.click('td:has-text("RVE-")')
}) })
}) })