mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-15 21:03:30 +00:00
Update dialogs layout (#1385)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
8dcdafb37d
commit
595fbf487a
@ -13,6 +13,7 @@
|
||||
"Remove": "Remove",
|
||||
"Members": "Members",
|
||||
"Search": "Search...",
|
||||
"Unassigned": "Unassigned"
|
||||
"Unassigned": "Unassigned",
|
||||
"CreateMore": "Create more"
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
"Remove": "Удалить",
|
||||
"Members": "Участники",
|
||||
"Search": "Поиск...",
|
||||
"Unassigned": "Не назначен"
|
||||
"Unassigned": "Не назначен",
|
||||
"CreateMore": "Создать еще"
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Ref, Class, Space, DocumentQuery } from '@anticrm/core'
|
||||
|
||||
import { Button, Label } from '@anticrm/ui'
|
||||
import { Button, Label, IconClose, MiniToggle } from '@anticrm/ui'
|
||||
import SpaceSelect from './SpaceSelect.svelte'
|
||||
import presentation from '..'
|
||||
|
||||
@ -34,31 +34,50 @@
|
||||
export let okAction: () => void
|
||||
export let canSave: boolean = false
|
||||
export let size: 'small'| 'medium' = 'small'
|
||||
|
||||
export let createMore: boolean | undefined = undefined
|
||||
export let okLabel: IntlString = presentation.string.Create
|
||||
export let cancelLabel: IntlString = presentation.string.Cancel
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<form class="antiCard" class:w-85={size === 'small'} class:w-165={size === 'medium'} on:submit|preventDefault={ () => {} }>
|
||||
<form class="antiCard dialog" on:submit|preventDefault={ () => {} }>
|
||||
<div class="antiCard-header">
|
||||
<div class="antiCard-header__title"><Label {label} params={labelProps ?? {}} /></div>
|
||||
{#if $$slots.error}
|
||||
<div class="antiCard-header__error">
|
||||
<slot name="error" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="antiCard-header__title-wrap">
|
||||
{#if spaceClass && spaceLabel && spacePlaceholder}
|
||||
{#if $$slots.space}
|
||||
<slot name="space" />
|
||||
{:else}
|
||||
<SpaceSelect _class={spaceClass} spaceQuery={spaceQuery} label={spaceLabel} placeholder={spacePlaceholder} bind:value={space} />
|
||||
{/if}
|
||||
<span class="antiCard-header__divider">›</span>
|
||||
{/if}
|
||||
<span class="antiCard-header__title"><Label {label} params={labelProps ?? {}} /></span>
|
||||
</div>
|
||||
<div class="buttons-group small-gap">
|
||||
<Button icon={IconClose} kind={'transparent'} on:click={() => { dispatch('close') }} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="antiCard-content"><slot /></div>
|
||||
{#if spaceClass && spaceLabel && spacePlaceholder}
|
||||
{#if (spaceClass && spaceLabel && spacePlaceholder) || $$slots.pool}
|
||||
<div class="antiCard-pool">
|
||||
<div class="antiCard-pool__separator" />
|
||||
<SpaceSelect _class={spaceClass} spaceQuery={spaceQuery} label={spaceLabel} placeholder={spacePlaceholder} bind:value={space} />
|
||||
<slot name="pool" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="antiCard-footer">
|
||||
<Button disabled={!canSave} label={okLabel} kind={'primary'} on:click={() => { okAction(); dispatch('close') }} />
|
||||
<Button label={cancelLabel} on:click={() => { dispatch('close') }} />
|
||||
<div class="antiCard-footer reverse">
|
||||
<div class="buttons-group text-sm flex-no-shrink">
|
||||
{#if createMore !== undefined}
|
||||
<MiniToggle label={presentation.string.CreateMore} bind:on={createMore} />
|
||||
{/if}
|
||||
<Button disabled={!canSave} label={okLabel} kind={'primary'} on:click={() => { okAction(); dispatch('close') }} />
|
||||
</div>
|
||||
<div class="buttons-group small-gap text-sm">
|
||||
<slot name="footer" />
|
||||
{#if $$slots.error}
|
||||
<div class="antiCard-footer__error">
|
||||
<slot name="error" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -17,7 +17,7 @@
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { getClient } from '../utils'
|
||||
|
||||
import { Label, showPopup, IconFolder } from '@anticrm/ui'
|
||||
import { Label, showPopup, IconFolder, Button } from '@anticrm/ui'
|
||||
import SpacesPopup from './SpacesPopup.svelte'
|
||||
|
||||
import type { Ref, Class, Space, DocumentQuery } from '@anticrm/core'
|
||||
@ -27,10 +27,8 @@
|
||||
export let label: IntlString
|
||||
export let placeholder: IntlString
|
||||
export let value: Ref<Space> | undefined
|
||||
export let show: boolean = false
|
||||
|
||||
let selected: Space | undefined
|
||||
let btn: HTMLElement
|
||||
|
||||
const client = getClient()
|
||||
|
||||
@ -39,43 +37,21 @@
|
||||
}
|
||||
|
||||
$: updateSelected(value)
|
||||
|
||||
onMount(() => {
|
||||
if (btn && show) {
|
||||
btn.click()
|
||||
show = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex-col cursor-pointer"
|
||||
bind:this={btn}
|
||||
on:click|preventDefault={() => {
|
||||
showPopup(SpacesPopup, { _class, spaceQuery }, btn, (result) => {
|
||||
<Button
|
||||
icon={IconFolder}
|
||||
size={'small'}
|
||||
kind={'no-border'}
|
||||
on:click={(ev) => {
|
||||
showPopup(SpacesPopup, { _class, spaceQuery }, ev.target, (result) => {
|
||||
if (result) {
|
||||
value = result._id
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div class="overflow-label label"><Label {label} /></div>
|
||||
<div class="flex-row-center space">
|
||||
<span class="mr-1"><IconFolder size={'small'} /></span>
|
||||
<span class="overflow-label" class:caption-color={selected} class:content-dark-color={!selected}>
|
||||
{#if selected}
|
||||
{selected.name}
|
||||
{:else}
|
||||
<Label label={placeholder} />
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.label {
|
||||
margin-bottom: .125rem;
|
||||
font-weight: 500;
|
||||
font-size: .75rem;
|
||||
color: var(--theme-content-accent-color);
|
||||
}
|
||||
</style>
|
||||
<span slot="content" class="text-sm">
|
||||
{#if selected}{selected.name}{:else}<Label {label} />{/if}
|
||||
</span>
|
||||
</Button>
|
||||
|
@ -42,7 +42,8 @@ export default plugin(presentationId, {
|
||||
Remove: '' as IntlString,
|
||||
Members: '' as IntlString,
|
||||
Search: '' as IntlString,
|
||||
Unassigned: '' as IntlString
|
||||
Unassigned: '' as IntlString,
|
||||
CreateMore: '' as IntlString
|
||||
},
|
||||
metadata: {
|
||||
RequiredVersion: '' as Metadata<string>
|
||||
|
@ -244,8 +244,7 @@ p:last-child { margin-block-end: 0; }
|
||||
}
|
||||
|
||||
.gap-1, .gap-1-5, .gap-2 {
|
||||
& > * { margin-right: .25rem; }
|
||||
& > *:last-child { margin-right: 0; }
|
||||
& > *:not(:last-child) { margin-right: .25rem; }
|
||||
&.reverse {
|
||||
flex-direction: row-reverse;
|
||||
& > :last-child { margin-right: .25rem; }
|
||||
@ -293,6 +292,8 @@ p:last-child { margin-block-end: 0; }
|
||||
.ml-6 { margin-left: 1.5rem; }
|
||||
.ml-8 { margin-left: 2rem; }
|
||||
.ml-10 { margin-left: 2.5rem; }
|
||||
.ml-12 { margin-left: 3rem; }
|
||||
.ml-22 { margin-left: 5.5rem; }
|
||||
.mr-1 { margin-right: .25rem; }
|
||||
.mr-2 { margin-right: .5rem; }
|
||||
.mr-3 { margin-right: .75rem; }
|
||||
@ -300,6 +301,7 @@ p:last-child { margin-block-end: 0; }
|
||||
.mr-6 { margin-right: 1.5rem; }
|
||||
.mr-8 { margin-right: 2rem; }
|
||||
.mr-10 { margin-right: 2.5rem; }
|
||||
.mt-0-5 { margin-top: .125rem; }
|
||||
.mt-1 { margin-top: .25rem; }
|
||||
.mt-2 { margin-top: .5rem; }
|
||||
.mt-3 { margin-top: .75rem; }
|
||||
@ -317,6 +319,7 @@ p:last-child { margin-block-end: 0; }
|
||||
.mx-1 { margin: 0 .25rem; }
|
||||
.mx-2 { margin: 0 .5rem; }
|
||||
.mx-3 { margin: 0 .75rem; }
|
||||
.my-4 { margin: 1rem 0; }
|
||||
|
||||
.pr-1 { padding-right: .25rem; }
|
||||
.pr-4 { padding-right: 1rem; }
|
||||
@ -377,6 +380,7 @@ p:last-child { margin-block-end: 0; }
|
||||
.min-w-4 { min-width: 1rem; }
|
||||
.min-w-9 { min-width: 2.25rem; }
|
||||
.min-h-0 { min-height: 0; }
|
||||
.min-w-min { min-width: min-content; }
|
||||
.clear-mins {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
@ -540,13 +544,14 @@ a.no-line {
|
||||
.background-bg-accent { background-color: var(--theme-bg-accent-color); }
|
||||
.background-bg-accent-normal { background-color: var(--theme-bg-accent-normal); }
|
||||
|
||||
.content-color { color: var(--theme-content-color); }
|
||||
.content-color { color: var(--content-color); }
|
||||
.content-trans-color { color: var(--theme-content-trans-color); }
|
||||
.content-accent-color { color: var(--theme-content-accent-color); }
|
||||
.content-accent-color { color: var(--accent-color); }
|
||||
.content-dark-color { color: var(--theme-content-dark-color); }
|
||||
.caption-color { color: var(--theme-caption-color); }
|
||||
.caption-color { color: var(--caption-color); }
|
||||
|
||||
.red-color { color: var(--highlight-red); }
|
||||
.error-color { color: var(--error-color); }
|
||||
|
||||
.border-radius-4 { border-radius: 1rem; }
|
||||
.border-radius-3 { border-radius: 0.75rem; }
|
||||
|
@ -111,10 +111,14 @@
|
||||
}
|
||||
|
||||
.antiCard-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
margin: 0 1rem;
|
||||
height: fit-content;
|
||||
|
||||
& > *:not(:last-child) { margin-bottom: 1rem; }
|
||||
}
|
||||
|
||||
.antiCard-pool {
|
||||
@ -157,20 +161,25 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
|
||||
& > *:not(:first-child) { margin-left: .375rem; }
|
||||
}
|
||||
|
||||
&__divider, &__title {
|
||||
margin-left: .375rem;
|
||||
font-weight: 400;
|
||||
font-size: .8125rem;
|
||||
}
|
||||
&__divider { color: var(--content-color); }
|
||||
&__title { color: var(--accent-color); }
|
||||
}
|
||||
.antiCard-content { margin: 0 1.125rem; }
|
||||
.antiCard-content { margin: .5rem 1.125rem 1rem; }
|
||||
.antiCard-pool {
|
||||
flex-direction: row;
|
||||
margin: .375rem .75rem .75rem;
|
||||
align-items: center;
|
||||
margin: 0 .75rem .75rem;
|
||||
font-size: .75rem;
|
||||
|
||||
& > *:not(:last-child) { margin-right: .375rem; }
|
||||
}
|
||||
.antiCard-footer {
|
||||
direction: ltr;
|
||||
@ -182,6 +191,16 @@
|
||||
border-top: 1px solid var(--button-bg-color);
|
||||
|
||||
&.reverse { flex-direction: row-reverse; }
|
||||
&__error {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
margin-left: .375rem;
|
||||
min-width: 0;
|
||||
font-weight: 500;
|
||||
font-size: .75rem;
|
||||
color: var(--system-error-color);
|
||||
&:empty { visibility: hidden; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,7 @@
|
||||
scrollbar-color: var(--theme-menu-color) var(--theme-bg-color);
|
||||
scrollbar-width: thin;
|
||||
--font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;
|
||||
--timing-shadow: cubic-bezier(0,.65,.35,1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
|
@ -70,6 +70,13 @@
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
.color {
|
||||
margin-right: .75rem;
|
||||
width: .875rem;
|
||||
height: .875rem;
|
||||
border: 1px solid rgba(0, 0, 0, .1);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
.label {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let labelParams: Record<string, any> = {}
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'secondary'
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'link-bordered' | 'dangerous' = 'secondary'
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'medium'
|
||||
export let shape: 'circle' | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
@ -55,10 +55,11 @@
|
||||
disabled={disabled || loading}
|
||||
style={width ? 'width: ' + width : ''}
|
||||
{title}
|
||||
type={kind === 'primary' ? 'submit' : 'button'}
|
||||
on:click
|
||||
>
|
||||
{#if icon && !loading}
|
||||
<div class="btn-icon"
|
||||
<div class="btn-icon pointer-events-none"
|
||||
class:mr-1={!iconOnly && kind === 'no-border'}
|
||||
class:mr-2={!iconOnly && kind !== 'no-border'}
|
||||
class:resetIconSize
|
||||
@ -69,11 +70,13 @@
|
||||
{#if loading}
|
||||
<Spinner />
|
||||
{:else}
|
||||
{#if label}
|
||||
<Label {label} params={labelParams}/>
|
||||
{:else if $$slots.content}
|
||||
<slot name="content" />
|
||||
{/if}
|
||||
<span class="overflow-label pointer-events-none">
|
||||
{#if label}
|
||||
<Label {label} params={labelParams} />
|
||||
{:else if $$slots.content}
|
||||
<slot name="content" />
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
@ -176,6 +179,16 @@
|
||||
.btn-icon { color: var(--content-color); }
|
||||
}
|
||||
}
|
||||
&.link-bordered {
|
||||
padding: 0 .375rem;
|
||||
color: var(--acctent-color);
|
||||
border-color: var(--button-border-color);
|
||||
&:hover {
|
||||
color: var(--acctent-color);
|
||||
border-color: var(--button-border-hover);
|
||||
.btn-icon { color: var(--accent-color); }
|
||||
}
|
||||
}
|
||||
&.primary {
|
||||
padding: 0 1rem;
|
||||
color: var(--white-color);
|
||||
|
50
packages/ui/src/components/ColorPopup.svelte
Normal file
50
packages/ui/src/components/ColorPopup.svelte
Normal file
@ -0,0 +1,50 @@
|
||||
<!--
|
||||
// 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 type { IntlString } from '@anticrm/platform'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Icon, Label, getPlatformColor } from '..'
|
||||
|
||||
export let placeholder: IntlString | undefined = undefined
|
||||
export let placeholderParam: any | undefined = undefined
|
||||
export let searchable: boolean = false
|
||||
export let value: Array<{id: number | string, color: number, label: string}>
|
||||
|
||||
let search: string = ''
|
||||
|
||||
let phTraslate: string = ''
|
||||
$: if (placeholder) translate(placeholder, placeholderParam ?? {}).then(res => { phTraslate = res })
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
{#if searchable}
|
||||
<div class="header">
|
||||
<input type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#each value.filter(el => el.label.toLowerCase().includes(search.toLowerCase())) as item}
|
||||
<button class="menu-item" on:click={() => { dispatch('close', item) }}>
|
||||
<div class="color" style="background-color: {getPlatformColor(item.color)}" />
|
||||
<span class="label">{item.label}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -21,9 +21,8 @@
|
||||
import Icon from './Icon.svelte'
|
||||
import { showPopup, Button, Tooltip, DropdownPopup } from '..'
|
||||
import type { AnySvelteComponent, ListItem, TooltipAligment } from '../types'
|
||||
import Add from './icons/Add.svelte'
|
||||
|
||||
export let icon: Asset | AnySvelteComponent = Add
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let label: IntlString
|
||||
export let placeholder: IntlString
|
||||
export let items: ListItem[] = []
|
||||
|
@ -14,24 +14,26 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
|
||||
import { IntlString, Asset } from '@anticrm/platform'
|
||||
import DropdownLabelsPopup from './DropdownLabelsPopup.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import IconUp from './icons/Up.svelte'
|
||||
import IconDown from './icons/Down.svelte'
|
||||
|
||||
import type { DropdownTextItem } from '../types'
|
||||
import { showPopup } from '..'
|
||||
import type { AnySvelteComponent, DropdownTextItem, TooltipAligment } from '../types'
|
||||
import { showPopup, Tooltip, Button, Label } from '..'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import ui from '../plugin'
|
||||
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let label: IntlString
|
||||
export let placeholder: IntlString | undefined = undefined
|
||||
export let placeholder: IntlString | undefined = ui.string.SearchDots
|
||||
export let items: DropdownTextItem[]
|
||||
export let selected: DropdownTextItem['id'] | undefined = undefined
|
||||
|
||||
let btn: HTMLElement
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let labelDirection: TooltipAligment | undefined = undefined
|
||||
|
||||
let container: HTMLElement
|
||||
let opened: boolean = false
|
||||
let isDisabled = false
|
||||
$: isDisabled = items.length === 0
|
||||
@ -46,7 +48,33 @@
|
||||
const none = ui.string.None
|
||||
</script>
|
||||
|
||||
<div class="flex-col cursor-pointer"
|
||||
<div bind:this={container} class="min-w-0">
|
||||
<Tooltip label={label} fill={width === '100%'} direction={labelDirection}>
|
||||
<Button
|
||||
{icon}
|
||||
width={width ?? 'min-content'}
|
||||
{size} {kind} {justify}
|
||||
on:click={() => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(DropdownLabelsPopup, { placeholder, items, selected }, container, (result) => {
|
||||
if (result) {
|
||||
selected = result
|
||||
dispatch('selected', result)
|
||||
}
|
||||
opened = false
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span slot="content" style="overflow: hidden">
|
||||
{#if selectedItem}{selectedItem.label}{:else}<Label label={label ?? ui.string.NotSelected} />{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<!-- <div class="flex-col cursor-pointer"
|
||||
bind:this={btn}
|
||||
on:click|preventDefault={() => {
|
||||
if (!opened) {
|
||||
@ -87,4 +115,4 @@
|
||||
font-size: .75rem;
|
||||
color: var(--theme-content-accent-color);
|
||||
}
|
||||
</style>
|
||||
</style> -->
|
||||
|
@ -29,7 +29,7 @@
|
||||
export let placeholder: IntlString = plugin.string.EditBoxPlaceholder
|
||||
export let placeholderParam: any | undefined = undefined
|
||||
export let format: 'text'| 'password' | 'number' = 'text'
|
||||
export let kind: 'editbox' | 'large-style' = 'editbox'
|
||||
export let kind: 'editbox' | 'large-style' | 'small-style' = 'editbox'
|
||||
export let focus: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -105,6 +105,10 @@
|
||||
font-weight: 500;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
.small-style {
|
||||
font-weight: 400;
|
||||
font-size: .75rem;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
|
@ -340,8 +340,8 @@
|
||||
height: 2.5rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
background-color: var(--theme-bg-color);
|
||||
color: var(--dark-color);
|
||||
background-color: var(--board-bg-color);
|
||||
box-shadow: inset 0 -1px 0 0 var(--theme-bg-focused-color);
|
||||
user-select: none;
|
||||
}
|
||||
|
@ -16,14 +16,12 @@
|
||||
import type { Asset, IntlString } from '@anticrm/platform'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Icon, Label } from '..'
|
||||
import { Icon, Label, getPlatformColor } from '..'
|
||||
|
||||
// export let _class: Ref<Class<Space>>
|
||||
// export let spaceQuery: DocumentQuery<Space> | undefined
|
||||
export let placeholder: IntlString | undefined = undefined
|
||||
export let placeholderParam: any | undefined = undefined
|
||||
export let searchable: boolean = false
|
||||
export let value: Array<{id: number | string, icon: Asset, label: IntlString}>
|
||||
export let value: Array<{id: number | string, color: number, label: IntlString}>
|
||||
|
||||
let search: string = ''
|
||||
|
||||
@ -31,9 +29,6 @@
|
||||
$: if (placeholder) translate(placeholder, placeholderParam ?? {}).then(res => { phTraslate = res })
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
// const query = createQuery()
|
||||
// $: query.query(_class, { ...(spaceQuery ?? {}), name: { $like: '%' + search + '%' } }, result => { objects = result })
|
||||
// afterUpdate(() => { dispatch('update', Date.now()) })
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
@ -44,10 +39,10 @@
|
||||
{/if}
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#each value.filter(el => el.label.toLowerCase().includes(search.toLowerCase())) as space}
|
||||
<button class="menu-item" on:click={() => { dispatch('close', space.id) }}>
|
||||
<div class="icon"><Icon icon={space.icon} size={'small'} /></div>
|
||||
<span class="label"><Label label={space.label} /></span>
|
||||
{#each value.filter(el => el.label.toLowerCase().includes(search.toLowerCase())) as item}
|
||||
<button class="menu-item" on:click={() => { dispatch('close', item.id) }}>
|
||||
<div class="color" style="background-color: {getPlatformColor(item.color)}" />
|
||||
<span class="label"><Label label={item.label} /></span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -43,6 +43,7 @@ export { default as Tabs } from './components/Tabs.svelte'
|
||||
export { default as ScrollBox } from './components/ScrollBox.svelte'
|
||||
export { default as PopupMenu } from './components/PopupMenu.svelte'
|
||||
export { default as SelectPopup } from './components/SelectPopup.svelte'
|
||||
export { default as ColorPopup } from './components/ColorPopup.svelte'
|
||||
export { default as TextArea } from './components/TextArea.svelte'
|
||||
export { default as Section } from './components/Section.svelte'
|
||||
export { default as DatePickerPopup } from './components/calendar/DatePickerPopup.svelte'
|
||||
|
@ -51,4 +51,8 @@
|
||||
<path d="M12,0C5.4,0,0,5.4,0,12c0,6.6,5.4,12,12,12c6.6,0,12-5.4,12-12C24,5.4,18.6,0,12,0z M12,22.6 C6.2,22.6,1.4,17.8,1.4,12C1.4,6.2,6.2,1.4,12,1.4c5.8,0,10.6,4.7,10.6,10.6C22.6,17.8,17.8,22.6,12,22.6z"/>
|
||||
<path d="M18.6,16.2c-0.3-0.9-0.8-1.6-1.5-2.2c-0.9-0.7-2-1.1-3.1-1.1h-1.5h-0.9h-1.5c-1.1,0-2.2,0.4-3.1,1.1 c-0.7,0.6-1.2,1.3-1.5,2.2l-0.2,0.6c-0.1,0.4,0.1,0.8,0.5,0.9c0.1,0,0.1,0,0.2,0c0.3,0,0.6-0.2,0.7-0.5L7,16.7 c0.2-0.6,0.5-1.1,1-1.5c0.6-0.5,1.4-0.8,2.2-0.8h1.5h0.9h1.5c0.8,0,1.6,0.3,2.2,0.8c0.5,0.4,0.8,0.9,1,1.5l0.2,0.6 c0.1,0.3,0.4,0.5,0.7,0.5c0.1,0,0.1,0,0.2,0c0.4-0.1,0.6-0.5,0.5-0.9L18.6,16.2z"/>
|
||||
</symbol>
|
||||
<symbol id="social-edit" viewBox="0 0 24 24">
|
||||
<path d="M12.8,16.5c0-0.4-0.3-0.8-0.8-0.8c-1.2,0-2.3-0.1-3.5-0.2c-0.3-2.3-0.3-4.6,0-6.9c2.3-0.3,4.6-0.3,6.9,0 c0.1,1.1,0.2,2.3,0.2,3.5c0,0.4,0.3,0.8,0.8,0.8c0.4,0,0.8-0.3,0.8-0.8c0-1.1-0.1-2.2-0.2-3.2c1.3,0.2,2.5,0.5,3.8,0.9 c0.1,0,0.1,0,0.2,0c0.2,0.7,0.3,1.5,0.3,2.3c0,0.4,0.3,0.8,0.8,0.8s0.8-0.3,0.8-0.8c0-5.9-4.8-10.8-10.8-10.8S1.2,6.1,1.2,12 S6.1,22.8,12,22.8c0.4,0,0.8-0.3,0.8-0.8s-0.3-0.8-0.8-0.8c-0.8,0-1.5-0.1-2.3-0.3c0-0.1,0-0.1,0-0.2C9.3,19.5,9,18.3,8.8,17 c1.1,0.1,2.1,0.2,3.2,0.2C12.4,17.2,12.8,16.9,12.8,16.5z M7,15.2c-1.3-0.2-2.5-0.5-3.8-0.9c-0.1,0-0.1,0-0.2,0 c-0.2-0.7-0.3-1.5-0.3-2.3c0-0.8,0.1-1.5,0.3-2.3c0.1,0,0.1,0,0.2,0C4.5,9.3,5.7,9,7,8.8C6.8,10.9,6.8,13.1,7,15.2z M20.3,8 c-1.2-0.3-2.3-0.6-3.5-0.8C16.6,6,16.3,4.8,16,3.7C17.9,4.6,19.4,6.1,20.3,8z M14.3,3c0,0.1,0,0.1,0,0.2C14.7,4.5,15,5.7,15.2,7 c-2.1-0.2-4.3-0.2-6.5,0C9,5.7,9.3,4.5,9.7,3.2c0-0.1,0-0.1,0-0.2c0.7-0.2,1.5-0.3,2.3-0.3C12.8,2.8,13.5,2.9,14.3,3z M7.9,3.7 C7.9,3.7,7.9,3.7,7.9,3.7C7.6,4.9,7.4,6,7.2,7.2C6,7.4,4.8,7.7,3.7,8C4.5,6.2,6,4.6,7.9,3.7z M8,20.2C7.9,20.3,7.9,20.3,8,20.2 c-1.9-0.9-3.4-2.3-4.3-4.1c0,0,0-0.1,0-0.1c1.1,0.3,2.3,0.6,3.5,0.8C7.4,18,7.6,19.1,8,20.2z" />
|
||||
<path d="M20.1,14.4c-0.5,0-1,0.3-1.5,0.8l-3.5,3.5c-0.2,0.2-0.5,0.7-0.5,1l-0.2,1.3c-0.1,0.5,0.1,0.9,0.4,1.2 c0.3,0.3,0.6,0.4,1,0.4c0.1,0,0.2,0,0.2,0l1.3-0.2c0.3,0,0.8-0.3,1-0.5l3.5-3.5c0.5-0.5,0.7-1,0.8-1.5c0.1-0.6-0.2-1.2-0.8-1.8 S20.8,14.4,20.1,14.4z M20.3,15.9C20.3,15.9,20.3,15.9,20.3,15.9c0.2,0,0.5,0.2,0.6,0.4c0.2,0.2,0.4,0.4,0.3,0.6 c0,0.1-0.1,0.3-0.3,0.5l-0.2,0.2c-0.5-0.2-0.9-0.7-1.1-1.2l0.2-0.2C19.9,16,20.1,15.9,20.3,15.9z M17.4,21c0,0-0.1,0.1-0.2,0.1 l-1.2,0.2l0.2-1.2c0,0,0-0.1,0.1-0.2l2.3-2.3c0.3,0.5,0.7,0.9,1.2,1.2L17.4,21z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 9.5 KiB |
@ -32,6 +32,7 @@ loadMetadata(contact.icon, {
|
||||
GitHub: `${icons}#github`,
|
||||
Edit: `${icons}#edit`,
|
||||
Person: `${icons}#person`,
|
||||
Company: `${icons}#company`
|
||||
Company: `${icons}#company`,
|
||||
SocialEdit: `${icons}#social-edit`
|
||||
})
|
||||
addStringsLoader(contactId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -45,18 +45,20 @@
|
||||
<span class="ml-2"><Label label={presentation.string.AddSocialLinks} /></span>
|
||||
</div>
|
||||
{:else}
|
||||
<ChannelsView value={channels} size={'small'} {integrations} on:click />
|
||||
<div id="channels-edit" class="ml-1">
|
||||
<CircleButton
|
||||
icon={contact.icon.Edit}
|
||||
size={'small'}
|
||||
selected
|
||||
on:click={(ev) =>
|
||||
showPopup(contact.component.SocialEditor, { values: channels }, ev.target, (result) => {
|
||||
if (result !== undefined) {
|
||||
dispatch('change', result)
|
||||
}
|
||||
})}
|
||||
/>
|
||||
<div class="flex-row-center min-w-min">
|
||||
<ChannelsView value={channels} size={'small'} {integrations} on:click />
|
||||
<div id="channels-edit" class="ml-1">
|
||||
<CircleButton
|
||||
icon={contact.icon.Edit}
|
||||
size={'small'}
|
||||
selected
|
||||
on:click={(ev) =>
|
||||
showPopup(contact.component.SocialEditor, { values: channels }, ev.target, (result) => {
|
||||
if (result !== undefined) {
|
||||
dispatch('change', result)
|
||||
}
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -124,6 +124,8 @@
|
||||
<style lang="scss">
|
||||
.channels {
|
||||
display: grid;
|
||||
width: min-content;
|
||||
|
||||
&.one { display: block; }
|
||||
&.short {
|
||||
grid-template-columns: repeat(4, min-content);
|
||||
|
@ -22,7 +22,7 @@
|
||||
label: cl.label,
|
||||
action: async () => {
|
||||
closePopup()
|
||||
showPopup(f.component, {}, targetElement)
|
||||
showPopup(f.component, {}, 'top')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -18,13 +18,14 @@
|
||||
|
||||
import { getClient, Card } from '@anticrm/presentation'
|
||||
|
||||
import { EditBox } from '@anticrm/ui'
|
||||
import { EditBox, Button, showPopup, IconAdd } from '@anticrm/ui'
|
||||
|
||||
import { Channel, Organization } from '@anticrm/contact'
|
||||
import contact from '../plugin'
|
||||
import Company from './icons/Company.svelte'
|
||||
import { AttachedData, generateId } from '@anticrm/core'
|
||||
import Channels from './Channels.svelte'
|
||||
import ChannelsView from './ChannelsView.svelte'
|
||||
|
||||
export function canClose (): boolean {
|
||||
return object.name === ''
|
||||
@ -63,31 +64,28 @@
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-center">
|
||||
<div class="mr-4 flex-center logo">
|
||||
<Company size={'large'} />
|
||||
</div>
|
||||
<div class="flex-col">
|
||||
<div class="fs-title">
|
||||
<EditBox placeholder={contact.string.OrganizationNamePlaceholder} maxWidth="11rem" bind:value={object.name} focus />
|
||||
</div>
|
||||
<div class="flex-row-center clear-mins">
|
||||
<div class="mr-3">
|
||||
<Button icon={Company} size={'medium'} kind={'link-bordered'} disabled />
|
||||
</div>
|
||||
<EditBox
|
||||
placeholder={contact.string.OrganizationNamePlaceholder}
|
||||
bind:value={object.name}
|
||||
maxWidth={'37.5rem'} kind={'large-style'} focus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-row-center channels">
|
||||
<Channels bind:channels={channels} on:change={(e) => { channels = e.detail }} />
|
||||
</div>
|
||||
{#if channels.length > 0}
|
||||
<ChannelsView value={channels} size={'small'} on:click />
|
||||
{/if}
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
icon={contact.icon.SocialEdit}
|
||||
kind={'transparent'}
|
||||
on:click={(ev) =>
|
||||
showPopup(contact.component.SocialEditor, { values: channels }, ev.target, (result) => {
|
||||
if (result !== undefined) channels = result
|
||||
})
|
||||
}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.logo {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
color: var(--primary-button-color);
|
||||
background-color: var(--primary-button-enabled);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.channels {
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -21,11 +21,11 @@
|
||||
import { getClient, Card, EditableAvatar } from '@anticrm/presentation'
|
||||
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { EditBox, IconInfo, Label } from '@anticrm/ui'
|
||||
import { EditBox, IconInfo, Label, Button, showPopup } from '@anticrm/ui'
|
||||
|
||||
import { Channel, combineName, findPerson, Person } from '@anticrm/contact'
|
||||
import contact from '../plugin'
|
||||
import Channels from './Channels.svelte'
|
||||
import ChannelsView from './ChannelsView.svelte'
|
||||
import PersonPresenter from './PersonPresenter.svelte'
|
||||
|
||||
let firstName = ''
|
||||
@ -93,58 +93,41 @@
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
{#if matches.length > 0}
|
||||
<div class="flex-row update-container ERROR">
|
||||
<div class="flex mb-2">
|
||||
<svelte:fragment slot="error">
|
||||
{#if matches.length > 0}
|
||||
<div class="flex-row-center error-color">
|
||||
<IconInfo size={'small'} />
|
||||
<div class="text-sm ml-2 overflow-label">
|
||||
<span class="text-sm overflow-label ml-2">
|
||||
<Label label={contact.string.PersonAlreadyExists} />
|
||||
</div>
|
||||
</span>
|
||||
<div class="ml-4"><PersonPresenter value={matches[0]} /></div>
|
||||
</div>
|
||||
<PersonPresenter value={matches[0]} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<div class="flex-row-center">
|
||||
<div class="mr-4">
|
||||
<EditableAvatar avatar={object.avatar} size={'large'} on:done={onAvatarDone} on:remove={removeAvatar} />
|
||||
</div>
|
||||
<div class="flex-col">
|
||||
<div class="fs-title">
|
||||
<EditBox placeholder={contact.string.PersonFirstNamePlaceholder} maxWidth="12rem" bind:value={firstName} focus />
|
||||
</div>
|
||||
<div class="fs-title mb-1">
|
||||
<EditBox placeholder={contact.string.PersonLastNamePlaceholder} maxWidth="12rem" bind:value={lastName} />
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<EditBox placeholder={contact.string.PersonLocationPlaceholder} maxWidth="12rem" bind:value={object.city} />
|
||||
<EditBox placeholder={contact.string.PersonFirstNamePlaceholder} bind:value={firstName} kind={'large-style'} maxWidth={'32rem'} focus />
|
||||
<EditBox placeholder={contact.string.PersonLastNamePlaceholder} bind:value={lastName} kind={'large-style'} maxWidth={'32rem'} />
|
||||
<div class="mt-1">
|
||||
<EditBox placeholder={contact.string.PersonLocationPlaceholder} bind:value={object.city} kind={'small-style'} maxWidth={'32rem'} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-row-center mt-5">
|
||||
<Channels
|
||||
bind:channels
|
||||
on:change={(e) => {
|
||||
channels = e.detail
|
||||
}}
|
||||
{#if channels.length > 0}
|
||||
<div class="ml-22"><ChannelsView value={channels} size={'small'} on:click /></div>
|
||||
{/if}
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
icon={contact.icon.SocialEdit}
|
||||
kind={'transparent'}
|
||||
on:click={(ev) =>
|
||||
showPopup(contact.component.SocialEditor, { values: channels }, ev.target, (result) => {
|
||||
if (result !== undefined) channels = result
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.update-container {
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
user-select: none;
|
||||
font-size: 14px;
|
||||
|
||||
color: var(--theme-content-color);
|
||||
&.ERROR { color: var(--system-error-color); }
|
||||
|
||||
border: 1px dashed var(--theme-zone-border);
|
||||
border-radius: 0.5rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
</style>
|
||||
|
@ -170,7 +170,8 @@ const contactPlugin = plugin(contactId, {
|
||||
GitHub: '' as Asset,
|
||||
Edit: '' as Asset,
|
||||
Person: '' as Asset,
|
||||
Company: '' as Asset
|
||||
Company: '' as Asset,
|
||||
SocialEdit: '' as Asset
|
||||
},
|
||||
space: {
|
||||
Employee: '' as Ref<Space>,
|
||||
|
@ -75,7 +75,7 @@
|
||||
"OpinionShortLabel": "OPE",
|
||||
"ReviewShortLabel": "RVE",
|
||||
"StartDate": "Дата начала",
|
||||
"DueDate": "Дата конца",
|
||||
"DueDate": "Дата окончания",
|
||||
"ReviewCategoryTitle":"Категория",
|
||||
"Verdict": "Вердикт",
|
||||
"OpinionSave": "Сохранить",
|
||||
|
@ -24,8 +24,8 @@
|
||||
import FileDuo from './icons/FileDuo.svelte'
|
||||
import chunter from '@anticrm/chunter'
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { Applicant } from '@anticrm/recruit'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
import { Applicant } from '@anticrm/recruit'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
// export let space: Ref<Space>
|
||||
|
@ -60,7 +60,7 @@
|
||||
}
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateApplication, { }, ev.target as HTMLElement)
|
||||
showPopup(CreateApplication, { }, 'top')
|
||||
}
|
||||
|
||||
function updateResultQuery (search: string): void {
|
||||
|
@ -25,6 +25,7 @@
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let candidate: Candidate
|
||||
export let disabled: boolean = false
|
||||
|
||||
let channels: Channel[] = []
|
||||
const channelsQuery = createQuery()
|
||||
@ -43,8 +44,8 @@
|
||||
<div class="label uppercase"><Label label={recruit.string.Candidate} /></div>
|
||||
<Avatar avatar={candidate.avatar} size={'large'} />
|
||||
{#if candidate}
|
||||
<div class="name lines-limit-2 over-underline" on:click={() => {
|
||||
showPanel(view.component.EditDoc, candidate._id, candidate._class, 'full')
|
||||
<div class="name lines-limit-2" class:over-underline={!disabled} on:click={() => {
|
||||
if (!disabled) showPanel(view.component.EditDoc, candidate._id, candidate._class, 'full')
|
||||
}}>{formatName(candidate.name)}</div>
|
||||
<div class="description lines-limit-2">{candidate.title ?? ''}</div>
|
||||
<div class="description overflow-label">{candidate.city ?? ''}</div>
|
||||
@ -64,15 +65,16 @@
|
||||
.card-container {
|
||||
padding: 1rem 1.5rem 1.25rem;
|
||||
background-color: var(--board-card-bg-color);
|
||||
border: 1px solid var(--board-card-bg-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: .5rem;
|
||||
transition-property: box-shadow, background-color;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-property: box-shadow, background-color, border-color;
|
||||
transition-timing-function: var(--timing-shadow);
|
||||
transition-duration: .15s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
box-shadow: var(--popup-shadow);
|
||||
border-color: var(--button-border-color);
|
||||
box-shadow: rgb(0 0 0 / 15%) 0px 4px 8px;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
@ -32,7 +32,7 @@
|
||||
const tableDescriptor = client.findOne<Viewlet>(view.class.Viewlet, { attachTo: recruit.mixin.Candidate, descriptor: view.viewlet.Table })
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateCandidate, { space: recruit.space.CandidatesPublic }, ev.target as HTMLElement)
|
||||
showPopup(CreateCandidate, { space: recruit.space.CandidatesPublic }, 'top')
|
||||
}
|
||||
|
||||
let category: Ref<TagCategory> | undefined = undefined
|
||||
|
@ -17,13 +17,16 @@
|
||||
import contact from '@anticrm/contact'
|
||||
import { Account, Class, Client, Doc, generateId, Ref, SortingOrder } from '@anticrm/core'
|
||||
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
|
||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
||||
import type { Applicant, Candidate } from '@anticrm/recruit'
|
||||
import { Card, getClient, UserBox, createQuery, AttributeEditor } from '@anticrm/presentation'
|
||||
import type { Applicant, Candidate, Vacancy } from '@anticrm/recruit'
|
||||
import task, { calcRank, SpaceWithStates, State } from '@anticrm/task'
|
||||
import { Grid, Status as StatusControl } from '@anticrm/ui'
|
||||
import ui, { Status as StatusControl, Label, Button, ColorPopup, showPopup, getPlatformColor } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
import CandidateCard from './CandidateCard.svelte'
|
||||
import VacancyCard from './VacancyCard.svelte'
|
||||
import ExpandRightDouble from './icons/ExpandRightDouble.svelte'
|
||||
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let candidate: Ref<Candidate>
|
||||
@ -58,7 +61,7 @@
|
||||
}
|
||||
|
||||
async function createApplication () {
|
||||
const state = await client.findOne(task.class.State, { space: doc.space })
|
||||
const state = await client.findOne(task.class.State, { space: doc.space, _id: selectedState._id })
|
||||
if (state === undefined) {
|
||||
throw new Error(`create application: state not found space:${doc.space}`)
|
||||
}
|
||||
@ -115,6 +118,35 @@
|
||||
}
|
||||
|
||||
$: validate(doc, doc._class)
|
||||
|
||||
let selectedVacancy: Vacancy
|
||||
let selectedCandidate: Person
|
||||
const vacancyQuery = createQuery()
|
||||
$: if (doc.space !== undefined) {
|
||||
vacancyQuery.query(recruit.class.Vacancy, { _id: doc.space }, (result) => {
|
||||
selectedVacancy = result[0]
|
||||
})
|
||||
}
|
||||
const candidateQuery = createQuery()
|
||||
$: if (doc.attachedTo !== undefined) {
|
||||
candidateQuery.query(contact.class.Person, { _id: doc.attachedTo as Ref<Person> }, (result) => {
|
||||
selectedCandidate = result[0]
|
||||
})
|
||||
}
|
||||
let states: Array<{id: number | string, color: number, label: string}> = []
|
||||
let selectedState: State
|
||||
const statesQuery = createQuery()
|
||||
$: if (doc.space !== undefined) {
|
||||
statesQuery.query(
|
||||
task.class.State,
|
||||
{ space: doc.space },
|
||||
(res) => {
|
||||
states = res.map(s => { return { id: s._id, label: s.title, color: s.color} })
|
||||
selectedState = res.filter(s => s._id === doc.state)[0] ?? res[0]
|
||||
},
|
||||
{ sort: { rank: SortingOrder.Ascending } }
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
@ -125,20 +157,38 @@
|
||||
spaceQuery={{ archived: false }}
|
||||
spaceLabel={recruit.string.Vacancy}
|
||||
spacePlaceholder={recruit.string.SelectVacancy}
|
||||
createMore={false}
|
||||
bind:space={doc.space}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<StatusControl slot="error" {status} />
|
||||
<Grid column={1} rowGap={1}>
|
||||
<!-- <div class="flex-between mt-2 mb-2">
|
||||
<div class="card" class:empty={!selectedCandidate}>
|
||||
{#if selectedCandidate}
|
||||
<CandidateCard candidate={selectedCandidate} disabled />
|
||||
{:else}
|
||||
<Label label={recruit.status.CandidateRequired} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="arrows"><ExpandRightDouble /></div>
|
||||
<div class="card" class:empty={!selectedVacancy}>
|
||||
{#if selectedVacancy}
|
||||
<VacancyCard vacancy={selectedVacancy} disabled />
|
||||
{:else}
|
||||
<Label label={recruit.status.VacancyRequired} />
|
||||
{/if}
|
||||
</div>
|
||||
</div> -->
|
||||
<svelte:fragment slot="pool">
|
||||
{#if !preserveCandidate}
|
||||
<UserBox
|
||||
_class={contact.class.Person}
|
||||
label={recruit.string.Candidate}
|
||||
placeholder={recruit.string.Candidates}
|
||||
bind:value={doc.attachedTo}
|
||||
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
|
||||
kind={'no-border'} size={'small'}
|
||||
/>
|
||||
{/if}
|
||||
<UserBox
|
||||
@ -148,7 +198,67 @@
|
||||
bind:value={doc.assignee}
|
||||
allowDeselect
|
||||
titleDeselect={recruit.string.UnAssignRecruiter}
|
||||
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
|
||||
kind={'no-border'} size={'small'}
|
||||
/>
|
||||
</Grid>
|
||||
{#if states && doc.space}
|
||||
<Button
|
||||
width="min-content"
|
||||
size="small"
|
||||
kind="no-border"
|
||||
on:click={(ev) => {
|
||||
showPopup(
|
||||
ColorPopup,
|
||||
{ value: states, searchable: true, placeholder: ui.string.SearchDots },
|
||||
ev.currentTarget,
|
||||
(result) => {
|
||||
if (result && result.id !== doc.state) {
|
||||
doc.state = result.id
|
||||
selectedState = result
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<div slot="content" class="flex-row-center">
|
||||
{#if selectedState}
|
||||
<div class="color" style="background-color: {getPlatformColor(selectedState.color)}" />
|
||||
<span class="label overflow-label">{selectedState.title}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.card {
|
||||
align-self: stretch;
|
||||
width: calc(50% - 3rem);
|
||||
min-height: 16rem;
|
||||
|
||||
&.empty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: .75rem;
|
||||
color: var(--dark-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
}
|
||||
.arrows { width: 4rem; }
|
||||
.color {
|
||||
margin-right: .375rem;
|
||||
width: .875rem;
|
||||
height: .875rem;
|
||||
border: 1px solid rgba(0, 0, 0, .1);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
.label {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import contact, { Channel, ChannelProvider, combineName, findPerson, Person } from '@anticrm/contact'
|
||||
import { Channels } from '@anticrm/contact-resources'
|
||||
import { ChannelsView } from '@anticrm/contact-resources'
|
||||
import PersonPresenter from '@anticrm/contact-resources/src/components/PersonPresenter.svelte'
|
||||
import {
|
||||
Account,
|
||||
@ -50,7 +50,9 @@
|
||||
Label,
|
||||
Link,
|
||||
showPopup,
|
||||
Spinner
|
||||
Spinner,
|
||||
Button,
|
||||
IconAttachment
|
||||
} from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
@ -397,91 +399,24 @@
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
{#if matches.length > 0}
|
||||
<div class="flex-row update-container ERROR">
|
||||
<div class="flex mb-2">
|
||||
<IconInfo size={'small'} />
|
||||
<div class="text-sm ml-2 overflow-label">
|
||||
<Label label={contact.string.PersonAlreadyExists} />
|
||||
</div>
|
||||
</div>
|
||||
<PersonPresenter value={matches[0]} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-row-center">
|
||||
<div class="mr-4">
|
||||
<EditableAvatar bind:direct={avatar} avatar={object.avatar} size={'large'} on:remove={removeAvatar} on:done={onAvatarDone} />
|
||||
</div>
|
||||
<div class="flex-col">
|
||||
<div class="fs-title">
|
||||
<EditBox placeholder={recruit.string.PersonFirstNamePlaceholder} maxWidth="10rem" bind:value={firstName} focus />
|
||||
</div>
|
||||
<div class="fs-title mb-1">
|
||||
<EditBox placeholder={recruit.string.PersonLastNamePlaceholder} maxWidth="10rem" bind:value={lastName} />
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<EditBox placeholder={recruit.string.Title} maxWidth="10rem" bind:value={object.title} />
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<EditBox placeholder={recruit.string.Location} maxWidth="10rem" bind:value={object.city} />
|
||||
<EditBox placeholder={recruit.string.PersonFirstNamePlaceholder} bind:value={firstName} kind={'large-style'} maxWidth={'32rem'} focus />
|
||||
<EditBox placeholder={recruit.string.PersonLastNamePlaceholder} bind:value={lastName} kind={'large-style'} maxWidth={'32rem'} />
|
||||
<div class="mt-1">
|
||||
<EditBox placeholder={recruit.string.Title} bind:value={object.title} kind={'small-style'} maxWidth={'32rem'} />
|
||||
</div>
|
||||
<EditBox placeholder={recruit.string.Location} bind:value={object.city} kind={'small-style'} maxWidth={'32rem'} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-row-center channels">
|
||||
<Channels
|
||||
bind:channels
|
||||
on:change={(e) => {
|
||||
channels = e.detail
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-center resume"
|
||||
class:solid={dragover || resume.uuid}
|
||||
on:dragover|preventDefault={() => {
|
||||
dragover = true
|
||||
}}
|
||||
on:dragleave={() => {
|
||||
dragover = false
|
||||
}}
|
||||
on:drop|preventDefault|stopPropagation={drop}
|
||||
>
|
||||
{#if resume.uuid}
|
||||
<Link
|
||||
label={resume.name}
|
||||
icon={FileIcon}
|
||||
maxLenght={16}
|
||||
on:click={() => {
|
||||
showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'right')
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
{#if loading}
|
||||
<Link label={'Uploading...'} icon={Spinner} disabled />
|
||||
{:else}
|
||||
<Link
|
||||
label={'Add or drop resume'}
|
||||
icon={FileUpload}
|
||||
on:click={() => {
|
||||
inputFile.click()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="separator" />
|
||||
<div class="flex-col locations">
|
||||
<span><Label label={recruit.string.WorkLocationPreferences} /></span>
|
||||
<div class="row"><Label label={recruit.string.Onsite} /><YesNo bind:value={object.onsite} /></div>
|
||||
<div class="row"><Label label={recruit.string.Remote} /><YesNo bind:value={object.remote} /></div>
|
||||
</div>
|
||||
<div class="separator" />
|
||||
<div class="flex-col locations">
|
||||
<span><Label label={recruit.string.SkillsLabel} /></span>
|
||||
{#if channels.length > 0}
|
||||
<div class="ml-22"><ChannelsView value={channels} size={'small'} on:click /></div>
|
||||
{/if}
|
||||
<div class="flex-col">
|
||||
<span class="text-sm fs-bold content-accent-color"><Label label={recruit.string.SkillsLabel} /></span>
|
||||
<div class="flex-grow">
|
||||
<Component
|
||||
is={tags.component.TagsEditor}
|
||||
@ -495,60 +430,48 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:fragment slot="pool">
|
||||
<div class="flex-between w-full">
|
||||
<span class="ml-2 content-color overflow-label"><Label label={recruit.string.WorkLocationPreferences} /></span>
|
||||
<div class="buttons-group small-gap">
|
||||
<YesNo label={recruit.string.Onsite} bind:value={object.onsite} />
|
||||
<YesNo label={recruit.string.Remote} bind:value={object.remote} />
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
icon={contact.icon.SocialEdit}
|
||||
kind={'transparent'}
|
||||
on:click={(ev) =>
|
||||
showPopup(contact.component.SocialEditor, { values: channels }, ev.target, (result) => {
|
||||
if (result !== undefined) channels = result
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
icon={!resume.uuid && loading ? Spinner : IconAttachment}
|
||||
kind={'transparent'}
|
||||
on:click={() => { inputFile.click() }}
|
||||
/>
|
||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected} />
|
||||
{#if resume.uuid}
|
||||
<Button
|
||||
icon={FileIcon}
|
||||
kind={'link-bordered'}
|
||||
on:click={() => {
|
||||
showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'right')
|
||||
}}
|
||||
><svelte:fragment slot="content">{resume.name}</svelte:fragment></Button>
|
||||
{/if}
|
||||
{#if matches.length > 0}
|
||||
<div class="flex-row-center error-color">
|
||||
<IconInfo size={'small'} />
|
||||
<span class="text-sm overflow-label ml-2">
|
||||
<Label label={contact.string.PersonAlreadyExists} />
|
||||
</span>
|
||||
<div class="ml-4"><PersonPresenter value={matches[0]} /></div>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.channels {
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.locations {
|
||||
span {
|
||||
margin-bottom: 0.125rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-content-accent-color);
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 0.75rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin: 1rem 0;
|
||||
height: 1px;
|
||||
background-color: var(--theme-card-divider);
|
||||
}
|
||||
|
||||
.resume {
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--theme-zone-bg);
|
||||
border: 1px dashed var(--theme-zone-border);
|
||||
border-radius: 0.5rem;
|
||||
backdrop-filter: blur(10px);
|
||||
&.solid {
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
.update-container {
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
user-select: none;
|
||||
font-size: 14px;
|
||||
|
||||
color: var(--theme-content-color);
|
||||
&.ERROR { color: var(--system-error-color); }
|
||||
|
||||
border: 1px dashed var(--theme-zone-border);
|
||||
border-radius: 0.5rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
</style>
|
||||
|
@ -16,9 +16,9 @@
|
||||
<script lang="ts">
|
||||
import { Organization } from '@anticrm/contact'
|
||||
import core, { Ref } from '@anticrm/core'
|
||||
import { getClient,SpaceCreateCard } from '@anticrm/presentation'
|
||||
import task, { createKanban,KanbanTemplate } from '@anticrm/task'
|
||||
import { Component,EditBox,Grid } from '@anticrm/ui'
|
||||
import { getClient, Card } from '@anticrm/presentation'
|
||||
import task, { createKanban, KanbanTemplate } from '@anticrm/task'
|
||||
import { Component, EditBox, Button } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
import { OrganizationSelector } from '@anticrm/contact-resources'
|
||||
@ -55,24 +55,32 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<SpaceCreateCard
|
||||
<Card
|
||||
label={recruit.string.CreateVacancy}
|
||||
okAction={createVacancy}
|
||||
canSave={!!name}
|
||||
on:close={() => { dispatch('close') }}
|
||||
>
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox label={recruit.string.VacancyName} bind:value={name} icon={Vacancy} placeholder={recruit.string.VacancyPlaceholder} maxWidth={'16rem'} focus/>
|
||||
<div class="flex-row-center clear-mins">
|
||||
<div class="mr-3">
|
||||
<Button icon={Vacancy} size={'medium'} kind={'link-bordered'} disabled />
|
||||
</div>
|
||||
<EditBox
|
||||
bind:value={name}
|
||||
placeholder={recruit.string.VacancyPlaceholder}
|
||||
maxWidth={'37.5rem'} kind={'large-style'} focus
|
||||
/>
|
||||
</div>
|
||||
<svelte:fragment slot="pool">
|
||||
<OrganizationSelector
|
||||
bind:value={company} label={recruit.string.Company}
|
||||
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
|
||||
kind={'no-border'} size={'small'}
|
||||
/>
|
||||
|
||||
<Component is={task.component.KanbanTemplateSelector} props={{
|
||||
folders: [recruit.space.VacancyTemplates],
|
||||
template: templateId
|
||||
}} on:change={(evt) => {
|
||||
templateId = evt.detail
|
||||
}}/>
|
||||
</Grid>
|
||||
</SpaceCreateCard>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
@ -64,7 +64,8 @@
|
||||
<style lang="scss">
|
||||
.card {
|
||||
align-self: stretch;
|
||||
width: calc(50% - 3.5rem);
|
||||
width: calc(50% - 3rem);
|
||||
min-height: 16rem;
|
||||
}
|
||||
.arrows { width: 4rem; }
|
||||
</style>
|
||||
|
@ -94,7 +94,7 @@
|
||||
}
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateVacancy, { space: recruit.space.CandidatesPublic }, ev.target as HTMLElement)
|
||||
showPopup(CreateVacancy, { space: recruit.space.CandidatesPublic }, 'top')
|
||||
}
|
||||
const applicationSorting = (a:Doc, b:Doc) => ((applications?.get(b._id as Ref<Vacancy>)?.count ?? 0) - (applications?.get(a._id as Ref<Vacancy>)?.count ?? 0)) ?? 0
|
||||
const modifiedSorting = (a:Doc, b:Doc) => ((applications?.get(b._id as Ref<Vacancy>)?.modifiedOn ?? 0) - (applications?.get(a._id as Ref<Vacancy>)?.modifiedOn ?? 0)) ?? 0
|
||||
|
@ -23,6 +23,7 @@
|
||||
import { Ref } from '@anticrm/core'
|
||||
|
||||
export let vacancy: Vacancy
|
||||
export let disabled: boolean = false
|
||||
let company: Organization | undefined
|
||||
|
||||
$: getOrganization(vacancy?.company)
|
||||
@ -43,14 +44,16 @@
|
||||
<VacancyIcon size={'large'} />
|
||||
</div>
|
||||
{#if vacancy}
|
||||
<div class="name lines-limit-2 over-underline" on:click={() => {
|
||||
closeTooltip()
|
||||
closePopup()
|
||||
closePanel()
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[2] = vacancy._id
|
||||
loc.path.length = 3
|
||||
navigate(loc)
|
||||
<div class="name lines-limit-2" class:over-underline={!disabled} on:click={() => {
|
||||
if (!disabled) {
|
||||
closeTooltip()
|
||||
closePopup()
|
||||
closePanel()
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[2] = vacancy._id
|
||||
loc.path.length = 3
|
||||
navigate(loc)
|
||||
}
|
||||
}}>{vacancy.name}</div>
|
||||
{#if company}
|
||||
<span class="label">{company.name}</span>
|
||||
@ -63,15 +66,16 @@
|
||||
.card-container {
|
||||
padding: 1rem 1.5rem 1.25rem;
|
||||
background-color: var(--board-card-bg-color);
|
||||
border: 1px solid var(--board-card-bg-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: .5rem;
|
||||
transition-property: box-shadow, background-color;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-property: box-shadow, background-color, border-color;
|
||||
transition-timing-function: var(--timing-shadow);
|
||||
transition-duration: .15s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
box-shadow: var(--popup-shadow);
|
||||
border-color: var(--button-border-color);
|
||||
box-shadow: rgb(0 0 0 / 15%) 0px 4px 8px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
|
@ -18,56 +18,82 @@
|
||||
import { Label } from '@anticrm/ui'
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let label: IntlString
|
||||
export let value: boolean | undefined
|
||||
|
||||
function getLabel(value: boolean | undefined): IntlString {
|
||||
if (value === true) return recruit.string.Yes
|
||||
if (value === false) return recruit.string.No
|
||||
return recruit.string.NA
|
||||
}
|
||||
export let disabled: boolean = false
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center yesno-container" class:yes={value === true} class:no={value === false} on:click={() => {
|
||||
<button class="yesno-container" {disabled} class:yes={value === true} class:no={value === false} on:click={() => {
|
||||
if (value === true) value = false
|
||||
else if (value === false) value = undefined
|
||||
else value = true
|
||||
}}>
|
||||
<svg class="yesno-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<circle fill="#fff" cx="8" cy="8" r="6"/>
|
||||
{#if value === true}
|
||||
<polygon fill="#60B96E" points="7.4,10.9 4.9,8.4 5.7,7.6 7.3,9.1 10.2,5.6 11.1,6.4 "/>
|
||||
{:else if value === false}
|
||||
<polygon fill="#F06C63" points="10.7,6 10,5.3 8,7.3 6,5.3 5.3,6 7.3,8 5.3,10 6,10.7 8,8.7 10,10.7 10.7,10 8.7,8 "/>
|
||||
{:else}
|
||||
<path fill="#77818E" d="M7.3,9.3h1.3V9c0.1-0.5,0.6-0.9,1.1-1.4c0.4-0.4,0.8-0.9,0.8-1.6c0-1.1-0.8-1.8-2.2-1.8c-1.4,0-2.4,0.8-2.5,2.2 h1.4c0.1-0.6,0.4-1,1-1C8.8,5.4,9,5.7,9,6.2c0,0.4-0.3,0.7-0.7,1.1c-0.5,0.5-1,0.9-1,1.7V9.3z M8,11.6c0.5,0,0.9-0.4,0.9-0.9 c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9C7.1,11.2,7.5,11.6,8,11.6z"/>
|
||||
{/if}
|
||||
</svg>
|
||||
<div class="label"><Label label={getLabel(value)} /></div>
|
||||
</div>
|
||||
<span class="overflow-label">
|
||||
<Label {label} />
|
||||
</span>
|
||||
<div class="btn-icon ml-1">
|
||||
<svg class="yesno-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<circle class="circle" class:yes={value === true} class:no={value === false} cx="8" cy="8" r="6"/>
|
||||
{#if value === true}
|
||||
<polygon fill="#fff" points="7.4,10.9 4.9,8.4 5.7,7.6 7.3,9.1 10.2,5.6 11.1,6.4 "/>
|
||||
{:else if value === false}
|
||||
<polygon fill="#fff" points="10.7,6 10,5.3 8,7.3 6,5.3 5.3,6 7.3,8 5.3,10 6,10.7 8,8.7 10,10.7 10.7,10 8.7,8 "/>
|
||||
{:else}
|
||||
<path fill="#fff" d="M7.3,9.3h1.3V9c0.1-0.5,0.6-0.9,1.1-1.4c0.4-0.4,0.8-0.9,0.8-1.6c0-1.1-0.8-1.8-2.2-1.8c-1.4,0-2.4,0.8-2.5,2.2 h1.4c0.1-0.6,0.4-1,1-1C8.8,5.4,9,5.7,9,6.2c0,0.4-0.3,0.7-0.7,1.1c-0.5,0.5-1,0.9-1,1.7V9.3z M8,11.6c0.5,0,0.9-0.4,0.9-0.9 c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9C7.1,11.2,7.5,11.6,8,11.6z"/>
|
||||
{/if}
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
.yesno-container {
|
||||
padding: .25rem .5rem .25rem .4rem;
|
||||
max-width: fit-content;
|
||||
border-radius: 1.25rem;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 0 .25rem 0 .5rem;
|
||||
min-width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5rem;
|
||||
white-space: nowrap;
|
||||
color: var(--accent-color);
|
||||
background-color: var(--button-bg-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: .25rem;
|
||||
box-shadow: var(--button-shadow);
|
||||
transition-property: border, background-color, color, box-shadow;
|
||||
transition-duration: .15s;
|
||||
|
||||
background-color: var(--grayscale-grey-03);
|
||||
&.yes { background-color: #60B96E; }
|
||||
&.no { background-color: #F06C63; }
|
||||
.btn-icon {
|
||||
color: var(--content-color);
|
||||
transition: color .15s;
|
||||
pointer-events: none;
|
||||
}
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
background-color: var(--button-bg-hover);
|
||||
transition-duration: 0;
|
||||
|
||||
.btn-icon { color: var(--caption-color); }
|
||||
}
|
||||
&:disabled {
|
||||
color: var(--content-color);
|
||||
background-color: #30323655;
|
||||
|
||||
.label {
|
||||
width: 1.4rem;
|
||||
margin-left: .25rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-size: .625rem;
|
||||
color: #FFF;
|
||||
&:hover {
|
||||
color: var(--content-color);
|
||||
.btn-icon { color: var(--content-color); }
|
||||
}
|
||||
}
|
||||
}
|
||||
.yesno-svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
.circle {
|
||||
fill: var(--grayscale-grey-03);
|
||||
&.yes { fill: #60B96E; }
|
||||
&.no { fill: #F06C63; }
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -22,7 +22,7 @@
|
||||
import type { Candidate, Review } from '@anticrm/recruit'
|
||||
import task, { SpaceWithStates } from '@anticrm/task'
|
||||
import { StyledTextBox } from '@anticrm/text-editor'
|
||||
import { DateRangePicker, Grid, Status as StatusControl, EditBox, Row } from '@anticrm/ui'
|
||||
import { DateRangePicker, Grid, Status as StatusControl, EditBox, Row, DateRangePresenter } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../../plugin'
|
||||
@ -39,8 +39,8 @@
|
||||
|
||||
let title: string = ''
|
||||
let description: string = ''
|
||||
let startDate: number = Date.now()
|
||||
let dueDate: number = Date.now()
|
||||
let startDate: number | null = null
|
||||
let dueDate: number | null = null
|
||||
let location: string = ''
|
||||
let company: Ref<Organization> | undefined = undefined
|
||||
|
||||
@ -103,8 +103,8 @@
|
||||
|
||||
await client.addCollection(recruit.class.Review, doc.space, doc.attachedTo, doc.attachedToClass, 'reviews', {
|
||||
number: (incResult as any).object.sequence,
|
||||
date: startDate ?? null,
|
||||
dueDate: dueDate ?? null,
|
||||
date: startDate ?? 0,
|
||||
dueDate: dueDate ?? 0,
|
||||
description,
|
||||
verdict: '',
|
||||
title,
|
||||
@ -143,7 +143,6 @@
|
||||
</script>
|
||||
|
||||
<Card
|
||||
size={'medium'}
|
||||
label={recruit.string.CreateReviewParams}
|
||||
labelProps={{ label: spaceLabel }}
|
||||
okAction={createReview}
|
||||
@ -158,61 +157,47 @@
|
||||
}}
|
||||
>
|
||||
<StatusControl slot="error" {status} />
|
||||
|
||||
<Grid column={2} rowGap={1.75}>
|
||||
<EditBox label={recruit.string.Title} icon={recruit.icon.Review} bind:value={title} maxWidth={'13rem'} />
|
||||
<EditBox
|
||||
placeholder={recruit.string.Title} bind:value={title}
|
||||
maxWidth={'37.5rem'} kind={'large-style'} focus
|
||||
/>
|
||||
<EditBox
|
||||
placeholder={recruit.string.Location} bind:value={location}
|
||||
maxWidth={'37.5rem'} kind={'small-style'}
|
||||
/>
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={doc.participants}
|
||||
title={calendar.string.Participants}
|
||||
on:open={(evt) => {
|
||||
doc.participants = [...(doc.participants ?? []), evt.detail._id]
|
||||
}}
|
||||
on:delete={(evt) => {
|
||||
doc.participants = doc.participants?.filter((it) => it !== evt.detail._id) ?? [currentUser.employee]
|
||||
}}
|
||||
noItems={calendar.string.NoParticipants}
|
||||
/>
|
||||
<StyledTextBox
|
||||
emphasized
|
||||
showButtons={false}
|
||||
bind:content={description}
|
||||
label={recruit.string.Description}
|
||||
alwaysEdit
|
||||
placeholder={recruit.string.AddDescription}
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
{#if !preserveCandidate}
|
||||
<UserBox
|
||||
_class={contact.class.Person}
|
||||
label={recruit.string.Candidate}
|
||||
placeholder={recruit.string.Candidates}
|
||||
bind:value={doc.attachedTo}
|
||||
kind={'link'}
|
||||
size={'x-large'}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
labelDirection={'left'}
|
||||
_class={contact.class.Person} bind:value={doc.attachedTo}
|
||||
label={recruit.string.Candidate} placeholder={recruit.string.Candidates}
|
||||
kind={'no-border'} size={'small'}
|
||||
/>
|
||||
{:else}
|
||||
<div />
|
||||
{/if}
|
||||
<EditBox label={recruit.string.Location} icon={recruit.icon.Location} bind:value={location} maxWidth={'13rem'} />
|
||||
<OrganizationSelector
|
||||
bind:value={company}
|
||||
label={recruit.string.Company}
|
||||
kind={'link'}
|
||||
size={'x-large'}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
labelDirection={'left'}
|
||||
bind:value={company} label={recruit.string.Company}
|
||||
kind={'no-border'} size={'small'}
|
||||
/>
|
||||
<DateRangePicker title={recruit.string.StartDate} bind:value={startDate} withTime on:change={updateStart} />
|
||||
<DateRangePicker title={recruit.string.DueDate} bind:value={dueDate} withTime />
|
||||
|
||||
<Row>
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={doc.participants}
|
||||
title={calendar.string.Participants}
|
||||
on:open={(evt) => {
|
||||
doc.participants = [...(doc.participants ?? []), evt.detail._id]
|
||||
}}
|
||||
on:delete={(evt) => {
|
||||
doc.participants = doc.participants?.filter((it) => it !== evt.detail._id) ?? [currentUser.employee]
|
||||
}}
|
||||
noItems={calendar.string.NoParticipants}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<StyledTextBox
|
||||
emphasized
|
||||
showButtons={false}
|
||||
bind:content={description}
|
||||
label={recruit.string.Description}
|
||||
alwaysEdit
|
||||
placeholder={recruit.string.AddDescription}
|
||||
/>
|
||||
</Row>
|
||||
</Grid>
|
||||
<DateRangePresenter bind:value={startDate} labelNull={recruit.string.StartDate} withTime editable on:change={updateStart} />
|
||||
<DateRangePresenter bind:value={dueDate} labelNull={recruit.string.DueDate} withTime editable />
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
@ -1,7 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="tags" viewBox="0 0 16 16">
|
||||
<path d="M8,8.5c1.7,0,3.2-1.4,3.2-3.2S9.7,2.2,8,2.2c-1.7,0-3.2,1.4-3.2,3.2S6.3,8.5,8,8.5z M8,3.2c1.2,0,2.2,1,2.2,2.2 S9.2,7.5,8,7.5s-2.2-1-2.2-2.2S6.8,3.2,8,3.2z"/>
|
||||
<path d="M4.2,11.2c-0.9,0.6-1.5,1.3-1.8,2.2c-0.1,0.3,0,0.5,0.3,0.6c0.3,0.1,0.5,0,0.6-0.3c0.2-0.6,0.7-1.3,1.4-1.7 c0.7-0.5,1.6-0.8,2.6-0.9c0.1,0,0.1,0,0.2,0c0.2-0.4,0.6-0.8,1-1c-0.4,0-0.8,0-1.2,0C6.1,10.3,5.1,10.7,4.2,11.2z"/>
|
||||
<path d="M14.7,11.5h-2.2V9.3c0-0.3-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5v2.2H9.3c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5h2.2v2.2 c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5v-2.2h2.2c0.3,0,0.5-0.2,0.5-0.5S14.9,11.5,14.7,11.5z"/>
|
||||
<symbol id="tags" viewBox="0 0 24 24">
|
||||
<path d="M22.8,8.7c0-0.3-0.2-0.6-0.5-0.7L11.8,3.8c-0.2-0.1-0.4-0.1-0.6,0L0.7,8C0.4,8.1,0.2,8.4,0.2,8.7 s0.2,0.6,0.5,0.7L5,11.1v6c0,0.2,0.1,0.4,0.2,0.5l0.5-0.5c-0.5,0.5-0.5,0.5-0.5,0.5l0,0l0,0l0,0l0,0c0,0,0.1,0.1,0.1,0.1 c0.1,0.1,0.2,0.2,0.3,0.3C6,18.3,6.5,18.6,7,19c1.1,0.6,2.7,1.3,4.5,1.3c1.8,0,3.4-0.7,4.5-1.3c0.5-0.3,1-0.6,1.3-0.9 c0.2-0.1,0.3-0.2,0.3-0.3c0,0,0.1-0.1,0.1-0.1l0,0l0,0l0,0l0,0l-0.5-0.5c0.5,0.5,0.5,0.5,0.5,0.5c0.1-0.1,0.2-0.3,0.2-0.5v-6 l3.3-1.3v3.6c0,0.4,0.3,0.8,0.8,0.8c0.4,0,0.8-0.3,0.8-0.8L22.8,8.7C22.8,8.7,22.8,8.7,22.8,8.7C22.8,8.7,22.8,8.7,22.8,8.7z M16.5,16.8c0,0-0.1,0.1-0.1,0.1c-0.3,0.2-0.6,0.5-1.1,0.8c-0.9,0.6-2.2,1.1-3.7,1.1c-1.5,0-2.8-0.5-3.7-1.1 c-0.5-0.3-0.8-0.5-1.1-0.8c-0.1,0-0.1-0.1-0.1-0.1v-5.1l4.7,1.9c0.2,0.1,0.4,0.1,0.6,0l4.7-1.9V16.8z M11.5,12.1L3,8.7l8.5-3.4 L20,8.7L11.5,12.1z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 746 B After Width: | Height: | Size: 956 B |
@ -16,7 +16,7 @@
|
||||
import { Class, Data, Doc, generateId, Ref } from '@anticrm/core'
|
||||
import { Card, createQuery, getClient } from '@anticrm/presentation'
|
||||
import { findTagCategory, TagCategory, TagElement } from '@anticrm/tags'
|
||||
import { DropdownLabels, EditBox, getColorNumberByText, getPlatformColor, showPopup } from '@anticrm/ui'
|
||||
import { DropdownLabels, EditBox, getColorNumberByText, getPlatformColor, showPopup, Button, IconFolder } from '@anticrm/ui'
|
||||
import { DropdownTextItem } from '@anticrm/ui/src/types'
|
||||
import { ColorsPopup } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@ -88,58 +88,65 @@
|
||||
okAction={createTagElenent}
|
||||
canSave={title.length > 0}
|
||||
space={tags.space.Tags}
|
||||
createMore={false}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-center">
|
||||
<div class="flex-col">
|
||||
<div class="fs-title flex-row-center">
|
||||
<div
|
||||
class="color"
|
||||
style={getTagStyle(getPlatformColor(color))}
|
||||
on:click={(evt) => {
|
||||
showPopup(ColorsPopup, {}, evt.target, (col) => {
|
||||
if (col != null) {
|
||||
color = col
|
||||
colorSet = true
|
||||
}
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<div class="flex-row-top clear-mins">
|
||||
<div class="mr-3">
|
||||
<Button
|
||||
size={'medium'}
|
||||
kind={'link-bordered'}
|
||||
on:click={(evt) => {
|
||||
showPopup(ColorsPopup, {}, evt.target, (col) => {
|
||||
if (col != null) {
|
||||
color = col
|
||||
colorSet = true
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="color" style={getTagStyle(getPlatformColor(color))} />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex-col mt-0-5">
|
||||
<EditBox
|
||||
bind:value={title}
|
||||
placeholder={tags.string.TagName}
|
||||
placeholderParam={{ word: keyTitle }}
|
||||
maxWidth={'35rem'} kind={'large-style'} focus
|
||||
/>
|
||||
<div class="mt-2">
|
||||
<EditBox
|
||||
focus
|
||||
placeholder={tags.string.TagName}
|
||||
placeholderParam={{ word: keyTitle }}
|
||||
maxWidth={'16rem'}
|
||||
bind:value={title}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="text-sm mt-4">
|
||||
<EditBox placeholder={tags.string.TagDescriptionPlaceholder} maxWidth={'17.75rem'} bind:value={description} />
|
||||
</div>
|
||||
|
||||
<div class="text-sm mt-4">
|
||||
<DropdownLabels
|
||||
label={tags.string.CategoryLabel}
|
||||
bind:selected={category}
|
||||
items={categoryItems}
|
||||
on:selected={() => {
|
||||
categoryWasSet = true
|
||||
}}
|
||||
bind:value={description}
|
||||
placeholder={tags.string.TagDescriptionPlaceholder}
|
||||
maxWidth={'35rem'} kind={'small-style'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:fragment slot="pool">
|
||||
<div class="ml-12">
|
||||
<DropdownLabels
|
||||
icon={IconFolder}
|
||||
label={tags.string.CategoryLabel}
|
||||
bind:selected={category}
|
||||
items={categoryItems}
|
||||
on:selected={() => {
|
||||
categoryWasSet = true
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.color {
|
||||
margin-right: 0.75rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -43,7 +43,7 @@
|
||||
}
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateTagElement, { targetClass, keyTitle }, ev.target as HTMLElement)
|
||||
showPopup(CreateTagElement, { targetClass, keyTitle }, 'top')
|
||||
}
|
||||
const opt: FindOptions<TagElement> = {
|
||||
lookup: {
|
||||
|
@ -41,4 +41,4 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<DropdownLabels {items} bind:selected={selectedItem} label={plugin.string.States} />
|
||||
<DropdownLabels {items} icon={task.icon.ManageStatuses} bind:selected={selectedItem} label={plugin.string.States} />
|
||||
|
@ -18,7 +18,7 @@
|
||||
import { Ref } from '@anticrm/core'
|
||||
import task, { State } from '@anticrm/task'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import { showPopup, Button, SelectPopup } from '@anticrm/ui'
|
||||
import StatePresenter from './StatePresenter.svelte'
|
||||
import StatesPopup from './StatesPopup.svelte'
|
||||
|
||||
@ -35,19 +35,30 @@
|
||||
</script>
|
||||
|
||||
{#if state}
|
||||
<div class="flex-row-center cursor-pointer" bind:this={container}
|
||||
on:click|preventDefault={() => {
|
||||
<Button
|
||||
width="min-content"
|
||||
size="small"
|
||||
kind="no-border"
|
||||
on:click={(ev) => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(StatesPopup, { space: state.space }, container, (result) => {
|
||||
if (result && result._id !== value) {
|
||||
value = result._id
|
||||
onChange(value)
|
||||
}
|
||||
opened = false
|
||||
})
|
||||
showPopup(
|
||||
StatesPopup,
|
||||
{ space: state.space },
|
||||
ev.currentTarget,
|
||||
(result) => {
|
||||
if (result && result._id !== value) {
|
||||
value = result._id
|
||||
onChange(value)
|
||||
}
|
||||
opened = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}} >
|
||||
<StatePresenter value={state} />
|
||||
</div>
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
<StatePresenter value={state} />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{/if}
|
||||
|
@ -19,22 +19,19 @@
|
||||
|
||||
export let value: State
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="overflow-label state-container" style="background-color: {getPlatformColor(value.color)}">
|
||||
{value.title}
|
||||
<div class="flex-row-center">
|
||||
<div class="state-container" style="background-color: {getPlatformColor(value.color)}" />
|
||||
<span class="overflow-label">{value.title ?? ''}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.state-container {
|
||||
padding: 0.25rem 0.5rem;
|
||||
width: 6.25rem;
|
||||
max-width: 6.25rem;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
letter-spacing: 0.5px;
|
||||
font-size: 0.625rem;
|
||||
color: #fff;
|
||||
margin-right: .5rem;
|
||||
width: .875rem;
|
||||
height: .875rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
@ -21,7 +21,6 @@
|
||||
"AddIssue": "Add Issue",
|
||||
"NewIssue": "New issue",
|
||||
"SaveIssue": "Save issue",
|
||||
"CreateMore": "Create more",
|
||||
"Todo": "Todo",
|
||||
"InProgress": "In Progress",
|
||||
"Done": "Done",
|
||||
|
@ -17,7 +17,6 @@
|
||||
"CreateTeam": "Create team",
|
||||
"NewIssue": "Новая задача",
|
||||
"SaveIssue": "Сохранить задачу",
|
||||
"CreateMore": "Создать еще",
|
||||
"Todo": "Todo",
|
||||
"InProgress": "В работе",
|
||||
"Done": "Выполнено",
|
||||
|
@ -1,73 +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 type { Asset, IntlString } from '@anticrm/platform'
|
||||
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Ref, Class, Space, DocumentQuery } from '@anticrm/core'
|
||||
|
||||
import { Button, Label, IconAttachment, IconExpand, IconClose, MiniToggle } from '@anticrm/ui'
|
||||
import SpaceSelect from './SpaceSelect.svelte'
|
||||
import presentation from '@anticrm/presentation'
|
||||
import tracker from '../plugin'
|
||||
|
||||
export let spaceClass: Ref<Class<Space>> | undefined = undefined
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
export let spaceQuery: DocumentQuery<Space> | undefined = { archived: false }
|
||||
export let spaceLabel: IntlString | undefined = undefined
|
||||
export let spacePlaceholder: IntlString | undefined = undefined
|
||||
export let label: IntlString
|
||||
export let labelProps: any | undefined = undefined
|
||||
export let icon: Asset | undefined = undefined
|
||||
export let okAction: () => void
|
||||
export let canSave: boolean = false
|
||||
export let createMore: boolean | undefined = undefined
|
||||
|
||||
export let okLabel: IntlString = presentation.string.Create
|
||||
export let cancelLabel: IntlString = presentation.string.Cancel
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<form class="antiCard dialog" on:submit|preventDefault={ () => {} }>
|
||||
<div class="antiCard-header">
|
||||
<div class="antiCard-header__title-wrap">
|
||||
<Button icon={icon} label={presentation.string.Save} size={'small'} kind={'no-border'} disabled on:click={() => { }} />
|
||||
<span class="antiCard-header__divider">›</span>
|
||||
<span class="antiCard-header__title"><Label {label} params={labelProps ?? {}} /></span>
|
||||
</div>
|
||||
<div class="buttons-group small-gap">
|
||||
<Button icon={IconExpand} kind={'transparent'} on:click={() => { }} />
|
||||
<Button icon={IconClose} kind={'transparent'} on:click={() => { dispatch('close') }} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="antiCard-content"><slot /></div>
|
||||
{#if spaceClass && spaceLabel && spacePlaceholder}
|
||||
<div class="antiCard-pool">
|
||||
<slot name="pool" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="antiCard-footer reverse">
|
||||
<div class="buttons-group text-sm">
|
||||
{#if createMore !== undefined}
|
||||
<MiniToggle label={tracker.string.CreateMore} bind:on={createMore} />
|
||||
{/if}
|
||||
<Button disabled={!canSave} label={okLabel} kind={'primary'} on:click={() => { okAction(); dispatch('close') }} />
|
||||
</div>
|
||||
<Button icon={IconAttachment} kind={'transparent'} on:click={() => { }} />
|
||||
</div>
|
||||
</form>
|
@ -16,14 +16,13 @@
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import core, { Data, generateId, Ref, SortingOrder } from '@anticrm/core'
|
||||
import { Asset, IntlString } from '@anticrm/platform'
|
||||
import { getClient, UserBox } from '@anticrm/presentation'
|
||||
import presentation, { getClient, UserBox, Card } from '@anticrm/presentation'
|
||||
import { Issue, IssuePriority, IssueStatus, Team } from '@anticrm/tracker'
|
||||
import { StyledTextBox } from '@anticrm/text-editor'
|
||||
import { EditBox, Button, showPopup, DatePresenter, SelectPopup } from '@anticrm/ui'
|
||||
import { EditBox, Button, showPopup, DatePresenter, SelectPopup, IconAttachment } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../plugin'
|
||||
import { calcRank } from '../utils'
|
||||
import Card from './Card.svelte'
|
||||
import StatusSelector from './StatusSelector.svelte'
|
||||
import PrioritySelector from './PrioritySelector.svelte'
|
||||
|
||||
@ -115,7 +114,6 @@
|
||||
<Card
|
||||
label={tracker.string.NewIssue}
|
||||
okAction={createIssue}
|
||||
icon={tracker.icon.Home}
|
||||
canSave={true}
|
||||
okLabel={tracker.string.SaveIssue}
|
||||
spaceClass={tracker.class.Team}
|
||||
@ -127,6 +125,9 @@
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="space">
|
||||
<Button icon={tracker.icon.Home} label={presentation.string.Save} size={'small'} kind={'no-border'} disabled on:click={() => { }} />
|
||||
</svelte:fragment>
|
||||
<EditBox
|
||||
bind:value={object.title}
|
||||
placeholder={tracker.string.IssueTitlePlaceholder}
|
||||
@ -134,17 +135,15 @@
|
||||
kind={'large-style'}
|
||||
focus
|
||||
/>
|
||||
<div class="mt-4">
|
||||
<StyledTextBox
|
||||
alwaysEdit
|
||||
showButtons={false}
|
||||
bind:content={object.description}
|
||||
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
<div slot="pool" class="flex-row-center text-sm gap-1-5">
|
||||
<StatusSelector status={object.status} onStatusChange={handleStatusChanged} />
|
||||
<PrioritySelector priority={object.priority} onPriorityChange={handlePriorityChanged} />
|
||||
<StyledTextBox
|
||||
alwaysEdit
|
||||
showButtons={false}
|
||||
bind:content={object.description}
|
||||
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
<StatusSelector bind:status={object.status} onStatusChange={handleStatusChanged} />
|
||||
<PrioritySelector bind:priority={object.priority} onPriorityChange={handlePriorityChanged} />
|
||||
<UserBox
|
||||
_class={contact.class.Employee}
|
||||
label={tracker.string.Assignee}
|
||||
@ -177,5 +176,8 @@
|
||||
showPopup(SelectPopup, { value: moreActions }, ev.currentTarget)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button icon={IconAttachment} kind={'transparent'} on:click={() => { }} />
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
@ -42,7 +42,6 @@ export default mergeIds(trackerId, tracker, {
|
||||
Team: '' as IntlString,
|
||||
SelectTeam: '' as IntlString,
|
||||
SaveIssue: '' as IntlString,
|
||||
CreateMore: '' as IntlString,
|
||||
Todo: '' as IntlString,
|
||||
InProgress: '' as IntlString,
|
||||
Done: '' as IntlString,
|
||||
|
@ -44,7 +44,7 @@
|
||||
$: query.query(core.class.Space, { _id: spaceId }, result => { space = result[0] })
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(createItemDialog as AnyComponent, { space: spaceId }, ev.target as HTMLElement)
|
||||
showPopup(createItemDialog as AnyComponent, { space: spaceId }, 'top')
|
||||
}
|
||||
|
||||
$: updateViewlets(viewlets)
|
||||
|
@ -58,7 +58,7 @@ test.describe('recruit tests', () => {
|
||||
// Click on Add button
|
||||
await page.click('.applications-container .flex-row-center .flex-center')
|
||||
|
||||
await page.click('span:has-text("Select vacancy")')
|
||||
await page.click('button:has-text("Vacancy")')
|
||||
|
||||
await page.click(`button:has-text("${vacancyId}")`)
|
||||
|
||||
@ -142,13 +142,13 @@ test.describe('recruit tests', () => {
|
||||
// Click button:has-text("Review")
|
||||
await page.click('button:has-text("Review")')
|
||||
// Click [placeholder="\ "]
|
||||
await page.click('[placeholder="placeholder"]')
|
||||
await page.click('[placeholder="Title"]')
|
||||
// Fill [placeholder="\ "]
|
||||
await page.fill('[placeholder="placeholder"]', 'Meet PEterson')
|
||||
await page.fill('[placeholder="Title"]', 'Meet PEterson')
|
||||
// Click text=Location Company Company >> [placeholder="\ "]
|
||||
await page.click('text=placeholder Location >> [placeholder="placeholder"]')
|
||||
await page.click('[placeholder="Location"]')
|
||||
// Fill text=Location Company Company >> [placeholder="\ "]
|
||||
await page.fill('text=placeholder Location >> [placeholder="placeholder"]', 'NSK')
|
||||
await page.fill('[placeholder="Location"]', 'NSK')
|
||||
// Click text=Company Company >> div
|
||||
// await page.click('text=Company Company >> div')
|
||||
// Click button:has-text("Apple")
|
||||
|
@ -30,11 +30,11 @@ test.describe('recruit tests', () => {
|
||||
// Fill [placeholder="Please\ type\ Skill\ title"]
|
||||
await page.fill('[placeholder="Please\\ type\\ Skill\\ title"]', 's1')
|
||||
// Click text=Create Skill s1 Please type description here Category Other Create Cancel >> button
|
||||
await page.click('text=Create Skill s1 Please type description here Category Other Create Cancel >> button')
|
||||
await page.click('text=Create more Create >> button')
|
||||
// Click text=s1
|
||||
await page.click('text=s1')
|
||||
// Click :nth-match(:text("Cancel"), 2)
|
||||
await page.click(':nth-match(:text("Cancel"), 2)')
|
||||
await page.click('button:has-text("Cancel")')
|
||||
// Click button:has-text("Create")
|
||||
await page.click('button:has-text("Create")')
|
||||
})
|
||||
@ -78,7 +78,7 @@ test.describe('recruit tests', () => {
|
||||
// Click text=java
|
||||
await page.click('text=java')
|
||||
// Click :nth-match(:text("Cancel"), 2)
|
||||
await page.click(':nth-match(:text("Cancel"), 2)')
|
||||
await page.click('button:has-text("Cancel")')
|
||||
// Click [placeholder="John"]
|
||||
await page.click('[placeholder="John"]')
|
||||
// Fill [placeholder="John"]
|
||||
|
@ -14,14 +14,14 @@ test.describe('workbench tests', () => {
|
||||
await page.click('text=Applications')
|
||||
await expect(page).toHaveURL('http://localhost:8083/workbench%3Acomponent%3AWorkbenchApp/recruit%3Aapp%3ARecruit/applicants')
|
||||
// Click text=Applications Application >> span
|
||||
await expect(page.locator('text=Applications Application >> span')).toBeVisible()
|
||||
await expect(page.locator('text=Applications Application')).toBeVisible()
|
||||
await expect(page.locator('text=APP-1')).toBeVisible()
|
||||
|
||||
// Click text=Candidates
|
||||
await page.click('text=Candidates')
|
||||
await expect(page).toHaveURL('http://localhost:8083/workbench%3Acomponent%3AWorkbenchApp/recruit%3Aapp%3ARecruit/candidates')
|
||||
|
||||
await expect(page.locator('text=Candidates Candidate >> span')).toBeVisible()
|
||||
await expect(page.locator('text=Candidates Candidate')).toBeVisible()
|
||||
await expect(page.locator('text=Andrey P.')).toBeVisible()
|
||||
|
||||
// Click text=Vacancies
|
||||
|
Loading…
Reference in New Issue
Block a user