mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-26 13:47:26 +03:00
Create Customer (#1689)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
8e1667b9fd
commit
6c8afa1fda
@ -215,6 +215,10 @@ export function createModel (builder: Builder): void {
|
||||
mode: ['workbench', 'browser', 'editor', 'panel', 'popup']
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(lead.mixin.Customer, core.class.Mixin, view.mixin.ObjectFactory, {
|
||||
component: lead.component.CreateCustomer
|
||||
})
|
||||
}
|
||||
|
||||
export { leadOperation } from './migration'
|
||||
|
@ -60,14 +60,16 @@
|
||||
</ScrollBox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons" class:shown={showButtons}>
|
||||
<div class="tool"><TextStyle size={'large'} /></div>
|
||||
<div class="tool"><Emoji size={'large'} /></div>
|
||||
<div class="tool"><GIF size={'large'} /></div>
|
||||
<div class="flex-grow">
|
||||
<slot />
|
||||
{#if showButtons}
|
||||
<div class="buttons">
|
||||
<div class="tool"><TextStyle size={'large'} /></div>
|
||||
<div class="tool"><Emoji size={'large'} /></div>
|
||||
<div class="tool"><GIF size={'large'} /></div>
|
||||
<div class="flex-grow">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@ -77,14 +79,6 @@
|
||||
flex-direction: column;
|
||||
min-height: 4.5rem;
|
||||
|
||||
.buttons {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.shown {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.textInput {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
|
@ -22,6 +22,9 @@
|
||||
"LeadPlaceholder": "The simple lead",
|
||||
"ManageFunnelStatuses": "Manage funnel statuses",
|
||||
"FunnelBrowser": "Funnel browser",
|
||||
"GotoLeadApplication": "Switch to Lead Application"
|
||||
"GotoLeadApplication": "Switch to Lead Application",
|
||||
"IssueDescriptionPlaceholder": "Add description...",
|
||||
"CreateCustomer": "Create Customer",
|
||||
"CreateCustomerLabel": "Customer"
|
||||
}
|
||||
}
|
@ -22,6 +22,9 @@
|
||||
"LeadPlaceholder": "Простая сделка",
|
||||
"ManageFunnelStatuses": "Управление статусами воронки",
|
||||
"FunnelBrowser": "Браузер воронок",
|
||||
"GotoLeadApplication": "Открыть приложение Сделки"
|
||||
"GotoLeadApplication": "Открыть приложение Сделки",
|
||||
"IssueDescriptionPlaceholder": "Добавить описание...",
|
||||
"CreateCustomer": "Добавить Клиента",
|
||||
"CreateCustomerLabel": "Клиент"
|
||||
}
|
||||
}
|
@ -46,6 +46,8 @@
|
||||
"@anticrm/attachment-resources": "~0.6.0",
|
||||
"@anticrm/contact-resources": "~0.6.0",
|
||||
"@anticrm/chunter-resources": "~0.6.0",
|
||||
"@anticrm/notification": "~0.6.0"
|
||||
"@anticrm/notification": "~0.6.0",
|
||||
"@anticrm/attachment": "~0.6.1",
|
||||
"@anticrm/text-editor": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
178
plugins/lead-resources/src/components/CreateCustomer.svelte
Normal file
178
plugins/lead-resources/src/components/CreateCustomer.svelte
Normal file
@ -0,0 +1,178 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { Channel, combineName, findPerson, Person } from '@anticrm/contact'
|
||||
import { ChannelsDropdown } from '@anticrm/contact-resources'
|
||||
import PersonPresenter from '@anticrm/contact-resources/src/components/PersonPresenter.svelte'
|
||||
import contact from '@anticrm/contact-resources/src/plugin'
|
||||
import { AttachedData, Data, generateId, MixinData, Ref } from '@anticrm/core'
|
||||
import type { Customer } from '@anticrm/lead'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { Card, EditableAvatar, getClient } from '@anticrm/presentation'
|
||||
import { EditBox, IconInfo, Label } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import lead from '../plugin'
|
||||
|
||||
let firstName = ''
|
||||
let lastName = ''
|
||||
|
||||
export function canClose (): boolean {
|
||||
return firstName === '' && lastName === ''
|
||||
}
|
||||
|
||||
const object: Customer = {
|
||||
_class: contact.class.Person
|
||||
} as Customer
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const customerId = generateId()
|
||||
|
||||
let channels: AttachedData<Channel>[] = []
|
||||
let avatar: File | undefined
|
||||
|
||||
async function createCustomer () {
|
||||
const candidate: Data<Person> = {
|
||||
name: combineName(firstName, lastName),
|
||||
city: object.city
|
||||
}
|
||||
if (avatar !== undefined) {
|
||||
const uploadFile = await getResource(attachment.helper.UploadFile)
|
||||
candidate.avatar = await uploadFile(avatar)
|
||||
}
|
||||
const candidateData: MixinData<Person, Customer> = {
|
||||
description: object.description
|
||||
}
|
||||
|
||||
const id = await client.createDoc(contact.class.Person, contact.space.Contacts, candidate, customerId)
|
||||
await client.createMixin(
|
||||
id as Ref<Person>,
|
||||
contact.class.Person,
|
||||
contact.space.Contacts,
|
||||
lead.mixin.Customer,
|
||||
candidateData
|
||||
)
|
||||
|
||||
for (const channel of channels) {
|
||||
await client.addCollection(
|
||||
contact.class.Channel,
|
||||
contact.space.Contacts,
|
||||
customerId,
|
||||
contact.class.Person,
|
||||
'channels',
|
||||
{
|
||||
value: channel.value,
|
||||
provider: channel.provider
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
function onAvatarDone (e: any) {
|
||||
const { file } = e.detail
|
||||
|
||||
avatar = file
|
||||
}
|
||||
|
||||
let matches: Person[] = []
|
||||
$: findPerson(client, { ...object, name: combineName(firstName, lastName) }, channels).then((p) => {
|
||||
matches = p
|
||||
})
|
||||
|
||||
function removeAvatar (): void {
|
||||
avatar = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={lead.string.CreateCustomer}
|
||||
okAction={createCustomer}
|
||||
canSave={firstName.length > 0 && lastName.length > 0 && matches.length === 0}
|
||||
space={contact.space.Contacts}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-between flex-row-top">
|
||||
<div class="flex-col flex-grow">
|
||||
<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>
|
||||
<EditBox
|
||||
placeholder={lead.string.IssueDescriptionPlaceholder}
|
||||
bind:value={object.description}
|
||||
kind={'small-style'}
|
||||
maxWidth={'32rem'}
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-4 flex">
|
||||
<EditableAvatar
|
||||
bind:direct={avatar}
|
||||
avatar={object.avatar}
|
||||
size={'large'}
|
||||
on:remove={removeAvatar}
|
||||
on:done={onAvatarDone}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<svelte:fragment slot="pool">
|
||||
<ChannelsDropdown bind:value={channels} editable />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
{#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">
|
||||
.resume {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--accent-bg-color);
|
||||
border: 1px dashed var(--divider-color);
|
||||
border-radius: 0.5rem;
|
||||
|
||||
&.solid {
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -16,10 +16,11 @@
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Icon, Label, Scroller, SearchEdit } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import { Button, Icon, IconAdd, Label, Scroller, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import lead from '../plugin'
|
||||
import CreateCustomer from './CreateCustomer.svelte'
|
||||
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Doc> = {}
|
||||
@ -33,6 +34,10 @@
|
||||
function updateResultQuery (search: string): void {
|
||||
resultQuery = search === '' ? {} : { $search: search }
|
||||
}
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateCustomer, {}, 'top')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ac-header full">
|
||||
@ -47,6 +52,13 @@
|
||||
updateResultQuery(search)
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={lead.string.CreateCustomerLabel}
|
||||
kind={'primary'}
|
||||
on:click={(ev) => showCreateDialog(ev)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Scroller tableFade>
|
||||
|
@ -24,6 +24,7 @@ import LeadPresenter from './components/LeadPresenter.svelte'
|
||||
import Leads from './components/Leads.svelte'
|
||||
import LeadsPresenter from './components/LeadsPresenter.svelte'
|
||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import CreateCustomer from './components/CreateCustomer.svelte'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
@ -35,6 +36,7 @@ export default async (): Promise<Resources> => ({
|
||||
TemplatesIcon,
|
||||
Customers,
|
||||
LeadsPresenter,
|
||||
Leads
|
||||
Leads,
|
||||
CreateCustomer
|
||||
}
|
||||
})
|
||||
|
@ -33,7 +33,10 @@ export default mergeIds(leadId, lead, {
|
||||
Customers: '' as IntlString,
|
||||
Leads: '' as IntlString,
|
||||
NoLeadsForDocument: '' as IntlString,
|
||||
LeadPlaceholder: '' as IntlString
|
||||
LeadPlaceholder: '' as IntlString,
|
||||
CreateCustomer: '' as IntlString,
|
||||
IssueDescriptionPlaceholder: '' as IntlString,
|
||||
CreateCustomerLabel: '' as IntlString
|
||||
},
|
||||
component: {
|
||||
CreateCustomer: '' as AnyComponent,
|
||||
|
@ -21,7 +21,7 @@
|
||||
export let placeholder: IntlString
|
||||
export let value: any
|
||||
export let focus: boolean
|
||||
export let maxWidth: string
|
||||
export let maxWidth: string = '10rem'
|
||||
export let onChange: (value: string) => void
|
||||
|
||||
function _onchange (ev: Event) {
|
||||
|
Loading…
Reference in New Issue
Block a user