mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-23 08:48:01 +00:00
Vacancy details (#486)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
41f92c977a
commit
32d559a07b
@ -75,3 +75,6 @@ export class TTypeBoolean extends TType {}
|
||||
|
||||
@Model(core.class.TypeTimestamp, core.class.Type)
|
||||
export class TTypeTimestamp extends TType {}
|
||||
|
||||
@Model(core.class.TypeDate, core.class.Type)
|
||||
export class TTypeDate extends TType {}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import { Builder } from '@anticrm/model'
|
||||
import core from './component'
|
||||
import { TAttribute, TClass, TDoc, TMixin, TObj, TType, TTypeString, TTypeBoolean, TTypeTimestamp, TAttachedDoc } from './core'
|
||||
import { TAttribute, TClass, TDoc, TMixin, TObj, TType, TTypeString, TTypeBoolean, TTypeTimestamp, TTypeDate, TAttachedDoc } from './core'
|
||||
import { TSpace, TAccount, TState, TSpaceWithStates, TDocWithState } from './security'
|
||||
import { TTx, TTxCreateDoc, TTxMixin, TTxUpdateDoc, TTxCUD, TTxPutBag, TTxRemoveDoc, TTxBulkWrite, TTxCollectionCUD } from './tx'
|
||||
|
||||
@ -49,6 +49,7 @@ export function createModel (builder: Builder): void {
|
||||
TTypeString,
|
||||
TTypeBoolean,
|
||||
TTypeTimestamp,
|
||||
TTypeDate,
|
||||
TState
|
||||
)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import type { Account, Arr, Domain, Ref, Space, State } from '@anticrm/core'
|
||||
import { DOMAIN_MODEL } from '@anticrm/core'
|
||||
import { Implements, Model, Prop, TypeState, TypeString } from '@anticrm/model'
|
||||
import { Implements, Model, Prop, TypeBoolean, TypeState, TypeString } from '@anticrm/model'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import core from './component'
|
||||
import { TDoc } from './core'
|
||||
@ -29,8 +29,12 @@ export class TSpace extends TDoc implements Space {
|
||||
@Prop(TypeString(), 'Name' as IntlString)
|
||||
name!: string
|
||||
|
||||
@Prop(TypeString(), 'Description' as IntlString)
|
||||
description!: string
|
||||
|
||||
@Prop(TypeBoolean(), 'Private' as IntlString)
|
||||
private!: boolean
|
||||
|
||||
members!: Arr<Ref<Account>>
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
import activity from '@anticrm/activity'
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import type { Doc, Domain, FindOptions, Ref } from '@anticrm/core'
|
||||
import { Builder, Model, Prop, TypeBoolean, TypeString, UX } from '@anticrm/model'
|
||||
import type { Doc, Domain, FindOptions, Ref, Timestamp } from '@anticrm/core'
|
||||
import { Builder, Model, Prop, TypeBoolean, TypeDate, TypeString, UX } from '@anticrm/model'
|
||||
import chunter from '@anticrm/model-chunter'
|
||||
import contact, { TPerson } from '@anticrm/model-contact'
|
||||
import core, { TAttachedDoc, TDocWithState, TSpace, TSpaceWithStates } from '@anticrm/model-core'
|
||||
@ -30,7 +30,22 @@ export const DOMAIN_RECRUIT = 'recruit' as Domain
|
||||
|
||||
@Model(recruit.class.Vacancy, core.class.SpaceWithStates)
|
||||
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
|
||||
export class TVacancy extends TSpaceWithStates implements Vacancy {}
|
||||
export class TVacancy extends TSpaceWithStates implements Vacancy {
|
||||
@Prop(TypeString(), 'Full description' as IntlString)
|
||||
fullDescription?: string
|
||||
|
||||
@Prop(TypeString(), 'Attachments' as IntlString)
|
||||
attachments?: number
|
||||
|
||||
@Prop(TypeDate(), 'Due date' as IntlString, recruit.icon.Calendar)
|
||||
dueTo?: Timestamp
|
||||
|
||||
@Prop(TypeString(), 'Location' as IntlString, recruit.icon.Location)
|
||||
location?: string
|
||||
|
||||
@Prop(TypeString(), 'Company' as IntlString, recruit.icon.Company)
|
||||
company?: string
|
||||
}
|
||||
|
||||
@Model(recruit.class.Candidates, core.class.Space)
|
||||
@UX(recruit.string.CandidatePools, recruit.icon.RecruitApplication)
|
||||
@ -109,7 +124,8 @@ export function createModel (builder: Builder): void {
|
||||
label: recruit.string.Vacancies,
|
||||
spaceClass: recruit.class.Vacancy,
|
||||
addSpaceLabel: recruit.string.CreateVacancy,
|
||||
createComponent: recruit.component.CreateVacancy
|
||||
createComponent: recruit.component.CreateVacancy,
|
||||
component: recruit.component.EditVacancy
|
||||
},
|
||||
{
|
||||
label: recruit.string.CandidatePools,
|
||||
|
@ -43,7 +43,8 @@ export default mergeIds(recruitId, recruit, {
|
||||
EditCandidate: '' as AnyComponent,
|
||||
KanbanCard: '' as AnyComponent,
|
||||
ApplicationPresenter: '' as AnyComponent,
|
||||
ApplicationsPresenter: '' as AnyComponent
|
||||
ApplicationsPresenter: '' as AnyComponent,
|
||||
EditVacancy: '' as AnyComponent
|
||||
},
|
||||
space: {
|
||||
CandidatesPublic: '' as Ref<Space>
|
||||
|
@ -109,6 +109,14 @@ export function createModel (builder: Builder): void {
|
||||
presenter: view.component.TimestampPresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.TypeDate, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: view.component.DatePresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.TypeDate, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: view.component.DateEditor
|
||||
})
|
||||
|
||||
builder.mixin(core.class.State, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: view.component.StateEditor
|
||||
})
|
||||
|
@ -34,7 +34,8 @@ export default mergeIds(viewId, view, {
|
||||
StatePresenter: '' as AnyComponent,
|
||||
StateEditor: '' as AnyComponent,
|
||||
TimestampPresenter: '' as AnyComponent,
|
||||
|
||||
DateEditor: '' as AnyComponent,
|
||||
DatePresenter: '' as AnyComponent,
|
||||
TableView: '' as AnyComponent,
|
||||
KanbanView: '' as AnyComponent
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ export default plugin(coreId, {
|
||||
TypeString: '' as Ref<Class<Type<string>>>,
|
||||
TypeBoolean: '' as Ref<Class<Type<boolean>>>,
|
||||
TypeTimestamp: '' as Ref<Class<Type<Timestamp>>>,
|
||||
TypeDate: '' as Ref<Class<Type<Timestamp | Date>>>,
|
||||
Bag: '' as Ref<Class<Type<Record<string, PropertyType>>>>
|
||||
},
|
||||
interface: {
|
||||
|
@ -298,6 +298,13 @@ export function TypeTimestamp (): Type<string> {
|
||||
return { _class: core.class.TypeTimestamp, label: 'TypeTimestamp' as IntlString }
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function TypeDate (): Type<string> {
|
||||
return { _class: core.class.TypeDate, label: 'TypeDate' as IntlString }
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -25,7 +25,7 @@
|
||||
// export let _class: Ref<Class<Doc>>
|
||||
export let key: string
|
||||
export let object: Doc
|
||||
export let maxWidth: string
|
||||
export let maxWidth: string | undefined = undefined
|
||||
export let focus: boolean = false
|
||||
export let minimize: boolean = false
|
||||
export let showHeader: boolean = true
|
||||
@ -59,7 +59,7 @@
|
||||
<div class="flex-row-center">
|
||||
<CircleButton icon={attribute.icon} size={'large'} />
|
||||
{#if !minimize}
|
||||
<div class="flex-col with-icon">
|
||||
<div class="flex-col with-icon ml-2">
|
||||
{#if showHeader}
|
||||
<Label label={attribute.label} />
|
||||
{/if}
|
||||
|
@ -37,10 +37,11 @@
|
||||
<div class="ref-container">
|
||||
<div class="textInput">
|
||||
<div class="inputMsg">
|
||||
<TextEditor content={content} bind:this={textEditor} on:content={
|
||||
<TextEditor bind:content={content} bind:this={textEditor} on:content={
|
||||
ev => {
|
||||
dispatch('message', ev.detail)
|
||||
content = ''
|
||||
textEditor.clear()
|
||||
}
|
||||
}/>
|
||||
</div>
|
||||
|
@ -32,6 +32,7 @@ import { getClient } from '@anticrm/presentation'
|
||||
import contact from '@anticrm/contact'
|
||||
|
||||
export let content: string = ''
|
||||
export let placeholder: string = 'Type something...'
|
||||
|
||||
let element: HTMLElement
|
||||
let editor: Editor
|
||||
@ -42,6 +43,9 @@ const client = getClient()
|
||||
export function submit (): void {
|
||||
content = editor.getHTML()
|
||||
dispatch('content', content)
|
||||
}
|
||||
|
||||
export function clear (): void {
|
||||
content = ''
|
||||
editor.commands.clearContent(false)
|
||||
}
|
||||
@ -66,7 +70,7 @@ onMount(() => {
|
||||
StarterKit,
|
||||
Highlight,
|
||||
// Typography, // we need to disable 1/2 -> ½ rule (https://github.com/hcengineering/anticrm/issues/345)
|
||||
Placeholder.configure({placeholder: 'Type something...'}),
|
||||
Placeholder.configure({placeholder: placeholder}),
|
||||
Mention.configure({
|
||||
HTMLAttributes: {
|
||||
class: 'mention',
|
||||
@ -97,11 +101,13 @@ onMount(() => {
|
||||
},
|
||||
}),
|
||||
],
|
||||
// content: 'dfgdfg',
|
||||
onTransaction: () => {
|
||||
// force re-render so `editor.isActive` works as expected
|
||||
editor = editor
|
||||
},
|
||||
onBlur: () => {
|
||||
dispatch('blur')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -191,6 +191,7 @@ p:last-child { margin-block-end: 0; }
|
||||
.mr-8 { margin-right: 2rem; }
|
||||
.mt-2 { margin-top: .5rem; }
|
||||
.mt-5 { margin-top: 1.25rem; }
|
||||
.mt-10 { margin-top: 2.5rem; }
|
||||
.mt-14 { margin-top: 3.5rem; }
|
||||
.mb-1 { margin-bottom: .25rem; }
|
||||
|
||||
|
@ -15,55 +15,29 @@
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import Label from './Label.svelte'
|
||||
import PopupMenu from './PopupMenu.svelte'
|
||||
import Calendar from './icons/Calendar.svelte'
|
||||
import Close from './icons/Close.svelte'
|
||||
import Back from './icons/Back.svelte'
|
||||
import Forward from './icons/Forward.svelte'
|
||||
import { DatePopup, showPopup } from '..'
|
||||
|
||||
export let title: IntlString
|
||||
export let selected: Date = new Date(Date.now())
|
||||
export let show: boolean = false
|
||||
let container: HTMLElement
|
||||
|
||||
let view: Date = selected
|
||||
const months: Array<string> = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
]
|
||||
let monthYear: string
|
||||
let days: Array<number>
|
||||
|
||||
const daysInMonth = (date: Date): number => {
|
||||
return 33 - new Date(date.getFullYear(), date.getMonth(), 33).getDate()
|
||||
}
|
||||
|
||||
$: {
|
||||
monthYear = months[view.getMonth()] + ' ' + view.getFullYear()
|
||||
days = []
|
||||
for (let i = 1; i <= daysInMonth(view); i++) {
|
||||
days.push(new Date(view.getFullYear(), view.getMonth(), i).getDay())
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center">
|
||||
<PopupMenu bind:show={show}>
|
||||
<button
|
||||
slot="trigger"
|
||||
class="focused-button btn"
|
||||
class:selected={show}
|
||||
on:click|preventDefault={() => {
|
||||
show = !show
|
||||
show = true
|
||||
showPopup(DatePopup, { selected, title }, container, (result) => {
|
||||
if (result) {
|
||||
selected = result
|
||||
}
|
||||
show = false
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div class="icon">
|
||||
@ -71,52 +45,6 @@
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div class="flex-col caption-color">
|
||||
<div class="title"><Label label={title} /></div>
|
||||
<div class="flex-between nav">
|
||||
<button
|
||||
class="focused-button arrow"
|
||||
on:click|preventDefault={() => {
|
||||
view.setMonth(view.getMonth() - 1)
|
||||
view = view
|
||||
}}><div class="icon"><Back size={'small'} /></div></button>
|
||||
<div class="monthYear">
|
||||
{monthYear}
|
||||
</div>
|
||||
<button
|
||||
class="focused-button arrow"
|
||||
on:click|preventDefault={() => {
|
||||
view.setMonth(view.getMonth() + 1)
|
||||
view = view
|
||||
}}><div class="icon"><Forward size={'small'} /></div></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="calendar">
|
||||
<div class="caption">Mo</div>
|
||||
<div class="caption">Tu</div>
|
||||
<div class="caption">We</div>
|
||||
<div class="caption">Th</div>
|
||||
<div class="caption">Fr</div>
|
||||
<div class="caption">Sa</div>
|
||||
<div class="caption">Su</div>
|
||||
{#each days as day, i}
|
||||
<div
|
||||
class="day"
|
||||
class:selected={i + 1 === selected.getDate() &&
|
||||
view.getMonth() === selected.getMonth() &&
|
||||
view.getFullYear() === selected.getFullYear()}
|
||||
style="grid-column: {day + 1}/{day + 2};"
|
||||
on:click={() => {
|
||||
selected = new Date(view.getFullYear(), view.getMonth(), i + 1)
|
||||
show = false
|
||||
}}
|
||||
>
|
||||
{i + 1}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</PopupMenu>
|
||||
<div class="selectDate">
|
||||
<div class="label"><Label label={title} /></div>
|
||||
<div class="date">
|
||||
@ -133,57 +61,6 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: .75rem;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
}
|
||||
.nav {
|
||||
min-width: 16.5rem;
|
||||
|
||||
.monthYear {
|
||||
margin: 0 1rem;
|
||||
line-height: 150%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: .125rem;
|
||||
margin-top: .5rem;
|
||||
|
||||
.caption, .day {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
}
|
||||
.caption {
|
||||
font-size: .75rem;
|
||||
}
|
||||
.day {
|
||||
border-radius: .5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
background-color: var(--theme-button-bg-focused);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selectDate {
|
||||
margin-left: .75rem;
|
||||
.label {
|
||||
|
169
packages/ui/src/components/DatePopup.svelte
Normal file
169
packages/ui/src/components/DatePopup.svelte
Normal file
@ -0,0 +1,169 @@
|
||||
<!--
|
||||
// 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 Label from './Label.svelte'
|
||||
import Back from './icons/Back.svelte'
|
||||
import Forward from './icons/Forward.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let title: IntlString
|
||||
export let selected: Date = new Date(Date.now())
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let view: Date = selected
|
||||
const months: Array<string> = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
]
|
||||
let monthYear: string
|
||||
let days: Array<number>
|
||||
|
||||
const daysInMonth = (date: Date): number => {
|
||||
return 33 - new Date(date.getFullYear(), date.getMonth(), 33).getDate()
|
||||
}
|
||||
|
||||
$: {
|
||||
monthYear = months[view.getMonth()] + ' ' + view.getFullYear()
|
||||
days = []
|
||||
for (let i = 1; i <= daysInMonth(view); i++) {
|
||||
days.push(new Date(view.getFullYear(), view.getMonth(), i).getDay())
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="popup">
|
||||
<div class="flex-col caption-color">
|
||||
<div class="title"><Label label={title} /></div>
|
||||
<div class="flex-between nav">
|
||||
<button
|
||||
class="focused-button arrow"
|
||||
on:click|preventDefault={() => {
|
||||
view.setMonth(view.getMonth() - 1)
|
||||
view = view
|
||||
}}><div class="icon"><Back size={'small'} /></div></button>
|
||||
<div class="monthYear">
|
||||
{monthYear}
|
||||
</div>
|
||||
<button
|
||||
class="focused-button arrow"
|
||||
on:click|preventDefault={() => {
|
||||
view.setMonth(view.getMonth() + 1)
|
||||
view = view
|
||||
}}><div class="icon"><Forward size={'small'} /></div></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="calendar">
|
||||
<div class="caption">Mo</div>
|
||||
<div class="caption">Tu</div>
|
||||
<div class="caption">We</div>
|
||||
<div class="caption">Th</div>
|
||||
<div class="caption">Fr</div>
|
||||
<div class="caption">Sa</div>
|
||||
<div class="caption">Su</div>
|
||||
{#each days as day, i}
|
||||
<div
|
||||
class="day"
|
||||
class:selected={i + 1 === selected.getDate() &&
|
||||
view.getMonth() === selected.getMonth() &&
|
||||
view.getFullYear() === selected.getFullYear()}
|
||||
style="grid-column: {day + 1}/{day + 2};"
|
||||
on:click={() => {
|
||||
selected = new Date(view.getFullYear(), view.getMonth(), i + 1)
|
||||
dispatch('close', selected)
|
||||
}}
|
||||
>
|
||||
{i + 1}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.popup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
max-height: 100%;
|
||||
color: var(--theme-caption-color);
|
||||
background-color: var(--theme-button-bg-hovered);
|
||||
border: 1px solid var(--theme-button-border-enabled);
|
||||
border-radius: .75rem;
|
||||
user-select: none;
|
||||
filter: drop-shadow(0 1.5rem 4rem rgba(0, 0, 0, .35));
|
||||
}
|
||||
|
||||
.arrow {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: .75rem;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
}
|
||||
.nav {
|
||||
min-width: 16.5rem;
|
||||
|
||||
.monthYear {
|
||||
margin: 0 1rem;
|
||||
line-height: 150%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: .125rem;
|
||||
margin-top: .5rem;
|
||||
|
||||
.caption, .day {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
}
|
||||
.caption {
|
||||
font-size: .75rem;
|
||||
}
|
||||
.day {
|
||||
border-radius: .5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
background-color: var(--theme-button-bg-focused);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -46,6 +46,7 @@ export { default as PopupMenu } from './components/PopupMenu.svelte'
|
||||
export { default as TextArea } from './components/TextArea.svelte'
|
||||
export { default as Section } from './components/Section.svelte'
|
||||
export { default as DatePicker } from './components/DatePicker.svelte'
|
||||
export { default as DatePopup } from './components/DatePopup.svelte'
|
||||
export { default as StylishEdit } from './components/StylishEdit.svelte'
|
||||
export { default as Grid } from './components/Grid.svelte'
|
||||
export { default as Row } from './components/Row.svelte'
|
||||
|
@ -23,5 +23,20 @@
|
||||
<path d="M16.9,12.2c-0.3,0-0.5,0.2-0.5,0.5l-0.1,1.9c-0.1,1-0.9,1.8-1.9,1.8H5.7c-1,0-1.8-0.8-1.9-1.8l-0.1-1.9 c0-0.3-0.3-0.5-0.5-0.5c-0.3,0-0.5,0.3-0.5,0.5l0.1,1.9c0.1,1.5,1.4,2.7,2.9,2.7h8.6c1.5,0,2.8-1.2,2.9-2.7l0.1-1.9 C17.4,12.5,17.2,12.2,16.9,12.2z"/>
|
||||
</g>
|
||||
</symbol>
|
||||
|
||||
<symbol id="location" viewBox="0 0 16 16">
|
||||
<g>
|
||||
<path
|
||||
d="M8,3.7c-1.9,0-3.4,1.5-3.4,3.4s1.5,3.4,3.4,3.4s3.4-1.5,3.4-3.4S9.9,3.7,8,3.7z M8,9.3c-1.2,0-2.1-1-2.1-2.1 C5.9,6,6.8,5,8,5s2.1,1,2.1,2.1C10.1,8.3,9.2,9.3,8,9.3z"
|
||||
/>
|
||||
<path
|
||||
d="M8,0C4.1,0,0.9,3.2,0.9,7.1c0,2.5,1.2,4.4,2.6,5.9c1.4,1.4,2.9,2.4,3.7,2.8c0.5,0.3,1.1,0.3,1.6,0 c0.8-0.4,2.4-1.4,3.7-2.8c1.4-1.4,2.6-3.4,2.6-5.9C15.1,3.2,11.9,0,8,0z M11.6,12.1c-1.2,1.3-2.7,2.1-3.4,2.5 c-0.1,0.1-0.3,0.1-0.4,0c-0.7-0.4-2.2-1.3-3.4-2.5c-1.2-1.3-2.2-2.9-2.2-5c0-3.2,2.6-5.8,5.8-5.8s5.8,2.6,5.8,5.8 C13.8,9.2,12.8,10.8,11.6,12.1z"
|
||||
/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="company" viewBox="0 0 16 16">
|
||||
<path d="M15.3,14.7h-0.7V4c0-0.3-0.2-0.6-0.5-0.6l-7.3-2c-0.2-0.1-0.4,0-0.6,0.1C6.1,1.6,6,1.8,6,2v3.3H2 C1.6,5.3,1.3,5.6,1.3,6v8.7H0.7C0.3,14.7,0,15,0,15.3C0,15.7,0.3,16,0.7,16h14.7c0.4,0,0.7-0.3,0.7-0.7C16,15,15.7,14.7,15.3,14.7z M2.7,14.7v-8h6v8H2.7z M9.3,5.3h-2V2.9l6,1.6v10.2H10V6C10,5.6,9.7,5.3,9.3,5.3z"/>
|
||||
</symbol>
|
||||
<symbol id="calendar" viewBox="0 0 24 24">
|
||||
<path d="M19.5,5h-2.1V4.5c0-0.3-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5V5H8.1V4.5C8.1,4.2,7.9,4,7.6,4S7.1,4.2,7.1,4.5V5H5 C4.2,5,3.5,5.7,3.5,6.5V19c0,0.8,0.7,1.5,1.5,1.5h14.5c0.8,0,1.5-0.7,1.5-1.5V6.5C21,5.7,20.3,5,19.5,5z M5,6h2.1v0.5 c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5V6h8.3v0.5c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5V6h2.1C19.7,6,20,6.3,20,6.5v3.1H4.5V6.5 C4.5,6.3,4.7,6,5,6z M19.5,19.5H5c-0.3,0-0.5-0.2-0.5-0.5v-8.3H20V19C20,19.2,19.7,19.5,19.5,19.5z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 4.9 KiB |
@ -19,7 +19,10 @@ import recruit, { recruitId } from '@anticrm/recruit'
|
||||
const icons = require('../assets/icons.svg')
|
||||
loadMetadata(recruit.icon, {
|
||||
RecruitApplication: `${icons}#recruitment`,
|
||||
Vacancy: `${icons}#vacancy`
|
||||
Vacancy: `${icons}#vacancy`,
|
||||
Location: `${icons}#location`,
|
||||
Company: `${icons}#company`,
|
||||
Calendar: `${icons}#calendar`,
|
||||
})
|
||||
|
||||
addStringsLoader(recruitId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -43,6 +43,7 @@
|
||||
"@anticrm/login": "~0.6.0",
|
||||
"deep-equal": "^2.0.5",
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/activity": "~0.6.0",
|
||||
"@anticrm/chunter-resources": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0"
|
||||
|
@ -49,9 +49,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
members: [],
|
||||
states: [],
|
||||
order: []
|
||||
members: []
|
||||
})
|
||||
const s1 = await client.createDoc(core.class.State, id, {
|
||||
title: 'Initial',
|
||||
|
219
plugins/recruit-resources/src/components/EditVacancy.svelte
Normal file
219
plugins/recruit-resources/src/components/EditVacancy.svelte
Normal file
@ -0,0 +1,219 @@
|
||||
<!--
|
||||
// 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 { IntlString } from '@anticrm/platform'
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { IconClose, Label, EditBox, ToggleWithLabel, Grid, Icon, Component } from '@anticrm/ui'
|
||||
import { TextEditor } from '@anticrm/text-editor'
|
||||
import { getClient, createQuery } from '@anticrm/presentation'
|
||||
import { Vacancy } from '@anticrm/recruit'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import AttributesBar from './AttributesBar.svelte'
|
||||
import activity from '@anticrm/activity'
|
||||
import recruit from '../plugin'
|
||||
import Attachments from './Attachments.svelte'
|
||||
|
||||
export let _id: Ref<Vacancy>
|
||||
|
||||
let object: Vacancy
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const query = createQuery()
|
||||
const clazz = client.getHierarchy().getClass(recruit.class.Vacancy)
|
||||
$: query.query(recruit.class.Vacancy, { _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">
|
||||
<Icon icon={clazz.icon} size={'medium'} />
|
||||
<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={['dueTo', 'location', 'company']} />
|
||||
</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.VacancyName} bind:value={object.name} placeholder="Software Engineer" maxWidth="39rem" focus on:change={() => {onChange('name', object.name)}}/>
|
||||
<EditBox label='Description' bind:value={object.description} placeholder='Description' 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={'This vacancy is private'} description={recruit.string.MakePrivateDescription}/>
|
||||
{:else if selected === 2}
|
||||
<Component is={activity.component.Activity} props={{object}} />
|
||||
{/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-dialog-bg-spec);
|
||||
border-radius: 1.25rem;
|
||||
box-shadow: var(--theme-dialog-shadow);
|
||||
backdrop-filter: blur(15px);
|
||||
|
||||
.header {
|
||||
flex-shrink: 0;
|
||||
padding: 0 2rem 0 2.5rem;
|
||||
height: 4.5rem;
|
||||
border-bottom: 1px solid var(--theme-dialog-divider);
|
||||
|
||||
.tool {
|
||||
margin-left: .75rem;
|
||||
transform-origin: center center;
|
||||
transform: scale(.75);
|
||||
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: .125rem solid transparent;
|
||||
border-bottom: .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 0;
|
||||
padding: 1.5rem 2.5rem;
|
||||
|
||||
.box {
|
||||
margin-right: 1px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--theme-menu-color);
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-right: .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: .75rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
</style>
|
@ -27,7 +27,7 @@
|
||||
</div>
|
||||
{#if vacancy}
|
||||
<div class="name">{vacancy.name}</div>
|
||||
<div class="description">Company</div>
|
||||
<div class="description">{vacancy.description}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
@ -22,6 +22,7 @@ import CreateApplication from './components/CreateApplication.svelte'
|
||||
import EditCandidate from './components/EditCandidate.svelte'
|
||||
import Attachments from './components/Attachments.svelte'
|
||||
import KanbanCard from './components/KanbanCard.svelte'
|
||||
import EditVacancy from './components/EditVacancy.svelte'
|
||||
import ApplicationPresenter from './components/ApplicationPresenter.svelte'
|
||||
import ApplicationsPresenter from './components/ApplicationsPresenter.svelte'
|
||||
import TxApplicantUpdate from './components/activity/TxApplicantUpdate.svelte'
|
||||
@ -46,7 +47,8 @@ export default async (): Promise<Resources> => ({
|
||||
Attachments,
|
||||
KanbanCard,
|
||||
ApplicationPresenter,
|
||||
ApplicationsPresenter
|
||||
ApplicationsPresenter,
|
||||
EditVacancy
|
||||
},
|
||||
activity: {
|
||||
TxApplicantUpdate
|
||||
|
@ -26,9 +26,6 @@ export default mergeIds(recruitId, recruit, {
|
||||
CandidateRequired: '' as StatusCode,
|
||||
VacancyRequired: '' as StatusCode,
|
||||
},
|
||||
class: {
|
||||
Vacancy: '' as Ref<Class<Vacancy>>
|
||||
},
|
||||
string: {
|
||||
CreateVacancy: '' as IntlString,
|
||||
VacancyName: '' as IntlString,
|
||||
|
@ -15,13 +15,19 @@
|
||||
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Plugin, Asset } from '@anticrm/platform'
|
||||
import type { Space, SpaceWithStates, DocWithState, Ref, Class, AttachedDoc } from '@anticrm/core'
|
||||
import type { Space, SpaceWithStates, DocWithState, Ref, Class, AttachedDoc, Timestamp } from '@anticrm/core'
|
||||
import type { Employee, Person } from '@anticrm/contact'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Vacancy extends SpaceWithStates {}
|
||||
export interface Vacancy extends SpaceWithStates {
|
||||
fullDescription?: string
|
||||
attachments?: number
|
||||
dueTo?: Timestamp
|
||||
location?: string
|
||||
company?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -59,10 +65,14 @@ export default plugin(recruitId, {
|
||||
class: {
|
||||
Applicant: '' as Ref<Class<Applicant>>,
|
||||
Candidate: '' as Ref<Class<Candidate>>,
|
||||
Candidates: '' as Ref<Class<Candidates>>
|
||||
Candidates: '' as Ref<Class<Candidates>>,
|
||||
Vacancy: '' as Ref<Class<Vacancy>>
|
||||
},
|
||||
icon: {
|
||||
RecruitApplication: '' as Asset,
|
||||
Vacancy: '' as Asset
|
||||
Vacancy: '' as Asset,
|
||||
Company: '' as Asset,
|
||||
Location: '' as Asset,
|
||||
Calendar: '' as Asset
|
||||
}
|
||||
})
|
||||
|
44
plugins/view-resources/src/components/DateEditor.svelte
Normal file
44
plugins/view-resources/src/components/DateEditor.svelte
Normal file
@ -0,0 +1,44 @@
|
||||
<!--
|
||||
// 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 { IntlString } from '@anticrm/platform'
|
||||
import { DatePopup, showPopup } from '@anticrm/ui'
|
||||
import DatePresenter from './DatePresenter.svelte'
|
||||
|
||||
export let value: number | Date | undefined
|
||||
export let label: IntlString
|
||||
export let onChange: (value: any) => void
|
||||
$: date = value ? new Date(value) : new Date()
|
||||
let container: HTMLElement
|
||||
let opened: boolean = false
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center" bind:this={container}
|
||||
on:click|preventDefault={() => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(DatePopup, { selected: date, title: label }, container, (result) => {
|
||||
if (result) {
|
||||
value = result.getTime()
|
||||
onChange(value)
|
||||
}
|
||||
opened = false
|
||||
})
|
||||
}
|
||||
}} >
|
||||
<DatePresenter {value} />
|
||||
</div>
|
28
plugins/view-resources/src/components/DatePresenter.svelte
Normal file
28
plugins/view-resources/src/components/DatePresenter.svelte
Normal file
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
// 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">
|
||||
export let value: number | Date | undefined
|
||||
$: date = value ? new Date(value) : undefined
|
||||
</script>
|
||||
|
||||
<div class=".caption-color">
|
||||
{#if date}
|
||||
{date.getMonth() + 1} / {date.getDate()} / {date.getFullYear()}
|
||||
{:else}
|
||||
No date
|
||||
{/if}
|
||||
</div>
|
@ -23,6 +23,8 @@ import BooleanPresenter from './components/BooleanPresenter.svelte'
|
||||
import StatePresenter from './components/StatePresenter.svelte'
|
||||
import StateEditor from './components/StateEditor.svelte'
|
||||
import TimestampPresenter from './components/TimestampPresenter.svelte'
|
||||
import DateEditor from './components/DateEditor.svelte'
|
||||
import DatePresenter from './components/DatePresenter.svelte'
|
||||
import TableView from './components/TableView.svelte'
|
||||
import Table from './components/Table.svelte'
|
||||
import KanbanView from './components/KanbanView.svelte'
|
||||
@ -64,6 +66,8 @@ export default async () => ({
|
||||
StateEditor,
|
||||
TableView,
|
||||
KanbanView,
|
||||
TimestampPresenter
|
||||
TimestampPresenter,
|
||||
DateEditor,
|
||||
DatePresenter
|
||||
}
|
||||
})
|
||||
|
@ -61,7 +61,7 @@
|
||||
label: 'Open' as IntlString,
|
||||
icon: IconEdit,
|
||||
action: async (_id: Ref<Doc>): Promise<void> => {
|
||||
showPopup(SpacePanel, { _id, spaceClass: model.spaceClass }, 'right')
|
||||
showPopup(model.component ?? SpacePanel, { _id, spaceClass: model.spaceClass }, 'right')
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user