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 recruit from './plugin'
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)
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
@ -123,7 +123,7 @@ export class TApplicant extends TTask implements Applicant {
}
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, {
view: {
@ -156,14 +156,7 @@ export function createModel (builder: Builder): void {
icon: recruit.icon.RecruitApplication,
hidden: false,
navigatorModel: {
spaces: [
{
label: recruit.string.ReviewCategory,
spaceClass: recruit.class.ReviewCategory,
addSpaceLabel: recruit.string.CreateReviewCategory,
createComponent: recruit.component.CreateReviewCategory
}
],
spaces: [],
specials: [
{
id: vacanciesId,
@ -218,15 +211,19 @@ export function createModel (builder: Builder): void {
}
},
{
id: 'upcoming',
component: calendar.component.UpcomingEvents,
id: 'reviews',
component: calendar.component.Events,
componentProps: {
viewLabel: recruit.string.Reviews,
viewIcon: recruit.icon.Review,
_class: recruit.class.Review,
options: reviewTableOptions,
config: reviewTableConfig
config: reviewTableConfig,
createLabel: recruit.string.ReviewCreateLabel,
createComponent: recruit.component.CreateReview
},
icon: calendar.icon.Calendar,
label: calendar.string.UpcomingEvents,
label: recruit.string.Reviews,
position: 'event'
}
]
@ -343,10 +340,6 @@ export function createModel (builder: Builder): void {
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, {
presenter: recruit.component.ApplicationPresenter
})
@ -355,10 +348,6 @@ export function createModel (builder: Builder): void {
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, {
validator: recruit.validator.ApplicantValidator
})

View File

@ -13,8 +13,10 @@
// 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 { DOMAIN_CALENDAR } from '@anticrm/model-calendar'
import { DOMAIN_SPACE } from '@anticrm/model-core'
import tags, { TagCategory } from '@anticrm/model-tags'
import { createKanbanTemplate, createSequence } from '@anticrm/model-task'
import { getCategories } from '@anticrm/skillset'
@ -22,7 +24,31 @@ import { KanbanTemplate } from '@anticrm/task'
import recruit from './plugin'
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> {
const tx = new TxOperations(client, core.account.System)
await createDefaults(tx)
@ -30,7 +56,7 @@ export const recruitOperation: MigrateOperation = {
}
async function createDefaults (tx: TxOperations): Promise<void> {
await createSpace(tx)
await createSpaces(tx)
await createOrUpdate(
tx,
@ -66,44 +92,6 @@ async function createDefaults (tx: TxOperations): Promise<void> {
await createSequence(tx, recruit.class.Opinion)
await createSequence(tx, recruit.class.Applicant)
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>> {
@ -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, {
_id: recruit.space.CandidatesPublic
})
@ -147,4 +135,22 @@ async function createSpace (tx: TxOperations): Promise<void> {
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,
ApplicationsPresenter: '' as AnyComponent,
VacancyPresenter: '' as AnyComponent,
ReviewCategoryPresenter: '' as AnyComponent,
EditApplication: '' as AnyComponent,
TemplatesIcon: '' as AnyComponent,
Applications: '' as AnyComponent,
@ -73,7 +72,6 @@ export default mergeIds(recruitId, recruit, {
SkillsView: '' as AnyComponent,
Vacancies: '' as AnyComponent,
CreateReviewCategory: '' as AnyComponent,
CreateReview: '' as AnyComponent,
Reviews: '' as AnyComponent,
KanbanReviewCard: '' as AnyComponent,
@ -86,8 +84,6 @@ export default mergeIds(recruitId, recruit, {
},
template: {
DefaultVacancy: '' as Ref<KanbanTemplate>,
Interview: '' as Ref<KanbanTemplate>,
Task: '' as Ref<KanbanTemplate>
},
completion: {

View File

@ -5,18 +5,11 @@ import attachment from '@anticrm/model-attachment'
import calendar, { TEvent } from '@anticrm/model-calendar'
import chunter from '@anticrm/model-chunter'
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 { Candidate, Opinion, Review, ReviewCategory } from '@anticrm/recruit'
import { Candidate, Opinion, Review } from '@anticrm/recruit'
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)
@UX(recruit.string.Review, recruit.icon.Review, recruit.string.ReviewShortLabel, 'number')
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 contact from '@anticrm/model-contact'
import core from '@anticrm/model-core'
import { actionTemplates } from '@anticrm/model-task'
import view, { createAction } from '@anticrm/model-view'
import workbench from '@anticrm/model-workbench'
import { Review } from '@anticrm/recruit'
import { BuildModelKey } from '@anticrm/view'
import recruit from './plugin'
@ -35,14 +33,6 @@ export const reviewTableConfig: (BuildModelKey | string)[] = [
]
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, {
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> = {
lookup: {
attachedTo: recruit.mixin.Candidate,

View File

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

View File

@ -21,7 +21,6 @@
"ModeMonth": "Month",
"ModeYear": "Year",
"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}}",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@
<script lang="ts">
import { Doc, DocumentQuery } from '@anticrm/core'
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 { ActionContext, TableBrowser } from '@anticrm/view-resources'
import contact from '../plugin'
@ -66,17 +66,15 @@
/>
</div>
<Scroller tableFade>
{#await tableDescriptor then descr}
{#if descr}
<TableBrowser
_class={contact.class.Contact}
config={descr.config}
options={descr.options}
query={resultQuery}
showNotification
/>
{/if}
{/await}
</Scroller>
{#await tableDescriptor then descr}
{#if descr}
<TableBrowser
_class={contact.class.Contact}
config={descr.config}
options={descr.options}
query={resultQuery}
showNotification
/>
{/if}
{/await}
</div>

View File

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

View File

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

View File

@ -18,7 +18,7 @@
import { Doc, DocumentQuery, Ref } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation'
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 { ActionContext, TableBrowser } from '@anticrm/view-resources'
import recruit from '../plugin'
@ -82,16 +82,14 @@
mode: 'browser'
}}
/>
<Scroller tableFade>
{#await tableDescriptor then descr}
{#if descr}
<TableBrowser
_class={recruit.mixin.Candidate}
config={descr.config}
options={descr.options}
query={resultQuery}
showNotification
/>
{/if}
{/await}
</Scroller>
{#await tableDescriptor then descr}
{#if descr}
<TableBrowser
_class={recruit.mixin.Candidate}
config={descr.config}
options={descr.options}
query={resultQuery}
showNotification
/>
{/if}
{/await}

View File

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

View File

@ -21,14 +21,14 @@
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
import { Card, getClient, UserBox, UserBoxList } from '@anticrm/presentation'
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 { DateRangePresenter, EditBox, Status as StatusControl } from '@anticrm/ui'
import view from '@anticrm/view'
import { createEventDispatcher } from 'svelte'
import recruit from '../../plugin'
export let space: Ref<SpaceWithStates>
// export let space: Ref<SpaceWithStates>
export let candidate: Ref<Person>
export let preserveCandidate = false
@ -49,7 +49,7 @@
attachedTo: candidate,
attachedToClass: recruit.mixin.Candidate,
_class: recruit.class.Review,
space: space,
space: recruit.space.Reviews,
_id: generateId(),
collection: 'reviews',
modifiedOn: Date.now(),
@ -147,11 +147,6 @@
labelProps={{ label: spaceLabel }}
okAction={createReview}
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={() => {
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 CreateOpinion from './components/review/CreateOpinion.svelte'
import CreateReview from './components/review/CreateReview.svelte'
import CreateReviewCategory from './components/review/CreateReviewCategory.svelte'
import EditReview from './components/review/EditReview.svelte'
import EditReviewCategory from './components/review/EditReviewCategory.svelte'
import OpinionPresenter from './components/review/OpinionPresenter.svelte'
import Opinions from './components/review/Opinions.svelte'
import OpinionsPresenter from './components/review/OpinionsPresenter.svelte'
import ReviewCategoryPresenter from './components/review/ReviewCategoryPresenter.svelte'
import ReviewPresenter from './components/review/ReviewPresenter.svelte'
import Reviews from './components/review/Reviews.svelte'
import SkillsView from './components/SkillsView.svelte'
@ -152,8 +149,6 @@ export default async (): Promise<Resources> => ({
VacancyCountPresenter,
VacancyModifiedPresenter,
CreateReviewCategory,
EditReviewCategory,
CreateReview,
ReviewPresenter,
EditReview,
@ -161,7 +156,6 @@ export default async (): Promise<Resources> => ({
Opinions,
OpinionPresenter,
OpinionsPresenter,
ReviewCategoryPresenter,
ApplicationsView,
NewCandidateHeader

View File

@ -133,7 +133,7 @@ const recruit = plugin(recruitId, {
},
space: {
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 { createQuery } from '@anticrm/presentation'
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 tags from '../plugin'
import CategoryBar from './CategoryBar.svelte'
@ -108,40 +108,38 @@
updateResultQuery(search, category)
}}
/>
<Scroller tableFade>
<TableBrowser
_class={tags.class.TagElement}
config={[
{
key: '',
label: item,
presenter: tags.component.TagElementPresenter,
props: { edit: true, keyTitle },
sortingKey: 'title'
},
...(category === undefined
? [
{
key: '$lookup.category',
presenter: tags.component.CategoryPresenter,
sortingKey: 'category',
label: tags.string.CategoryLabel
}
]
: []),
{
key: '',
presenter: tags.component.TagElementCountPresenter,
label: item,
props: { tagElements, label: item, onTag },
sortingKey: '@tagCount',
sortingFunction: countSorting
},
'description',
'modifiedOn'
]}
options={opt}
query={resultQuery}
showNotification
/>
</Scroller>
<TableBrowser
_class={tags.class.TagElement}
config={[
{
key: '',
label: item,
presenter: tags.component.TagElementPresenter,
props: { edit: true, keyTitle },
sortingKey: 'title'
},
...(category === undefined
? [
{
key: '$lookup.category',
presenter: tags.component.CategoryPresenter,
sortingKey: 'category',
label: tags.string.CategoryLabel
}
]
: []),
{
key: '',
presenter: tags.component.TagElementCountPresenter,
label: item,
props: { tagElements, label: item, onTag },
sortingKey: '@tagCount',
sortingFunction: countSorting
},
'description',
'modifiedOn'
]}
options={opt}
query={resultQuery}
showNotification
/>

View File

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

View File

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

View File

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

View File

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

View File

@ -136,34 +136,20 @@ test.describe('recruit tests', () => {
await page.locator('[id="app-recruit\\:string\\:RecruitApplication"]').click()
await page.hover('text=Reviews')
await page.click('[name="tooltip-recruit:string:CreateReviewCategory"]')
await page.click('text=Reviews')
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")')
// 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-")')
})
})