mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-03 08:57:14 +03:00
UBER-53: My Leads view (#3259)
Signed-off-by: Vyacheslav Tumanov <me@slavatumanov.me>
This commit is contained in:
parent
b7463a3a16
commit
1ada38e652
@ -124,6 +124,13 @@ export function createModel (builder: Builder): void {
|
|||||||
hidden: false,
|
hidden: false,
|
||||||
navigatorModel: {
|
navigatorModel: {
|
||||||
specials: [
|
specials: [
|
||||||
|
{
|
||||||
|
id: 'my-leads',
|
||||||
|
label: lead.string.MyLeads,
|
||||||
|
icon: lead.icon.Lead,
|
||||||
|
component: lead.component.MyLeads,
|
||||||
|
position: 'top'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'customers',
|
id: 'customers',
|
||||||
label: lead.string.Customers,
|
label: lead.string.Customers,
|
||||||
@ -378,6 +385,10 @@ export function createModel (builder: Builder): void {
|
|||||||
filters: ['attachedTo']
|
filters: ['attachedTo']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(lead.class.Lead, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||||
|
fields: ['createdBy', 'assignee']
|
||||||
|
})
|
||||||
|
|
||||||
builder.mixin(lead.mixin.Customer, core.class.Class, view.mixin.ClassFilters, {
|
builder.mixin(lead.mixin.Customer, core.class.Class, view.mixin.ClassFilters, {
|
||||||
filters: ['_class']
|
filters: ['_class']
|
||||||
})
|
})
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"Customer": "Customer",
|
"Customer": "Customer",
|
||||||
"Customers": "Customers",
|
"Customers": "Customers",
|
||||||
"Leads": "Leads",
|
"Leads": "Leads",
|
||||||
|
"MyLeads": "My Leads",
|
||||||
"SelectCustomer": "Select customer",
|
"SelectCustomer": "Select customer",
|
||||||
"Lead": "Lead",
|
"Lead": "Lead",
|
||||||
"Assignee": "Assignee",
|
"Assignee": "Assignee",
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"Customer": "Клиент",
|
"Customer": "Клиент",
|
||||||
"Customers": "Клиенты",
|
"Customers": "Клиенты",
|
||||||
"Leads": "Сделки",
|
"Leads": "Сделки",
|
||||||
|
"MyLeads": "Мои сделки",
|
||||||
"SelectCustomer": "Выбрать клиента",
|
"SelectCustomer": "Выбрать клиента",
|
||||||
"Lead": "Сделка",
|
"Lead": "Сделка",
|
||||||
"Assignee": "Исполнитель",
|
"Assignee": "Исполнитель",
|
||||||
|
156
plugins/lead-resources/src/components/MyLeads.svelte
Normal file
156
plugins/lead-resources/src/components/MyLeads.svelte
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { EmployeeAccount } from '@hcengineering/contact'
|
||||||
|
import { AttachedDoc, Class, DocumentQuery, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||||
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
|
import task, { Task } from '@hcengineering/task'
|
||||||
|
import { IModeSelector, Label, resolvedLocationStore, SearchEdit, ModeSelector, Loading } from '@hcengineering/ui'
|
||||||
|
import {
|
||||||
|
FilterButton,
|
||||||
|
getViewOptions,
|
||||||
|
makeViewletKey,
|
||||||
|
TableBrowser,
|
||||||
|
viewOptionStore
|
||||||
|
} from '@hcengineering/view-resources'
|
||||||
|
import { IntlString } from '@hcengineering/platform'
|
||||||
|
import ViewletSettingButton from '@hcengineering/view-resources/src/components/ViewletSettingButton.svelte'
|
||||||
|
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||||
|
import { onDestroy } from 'svelte'
|
||||||
|
import FilterBar from '@hcengineering/view-resources/src/components/filter/FilterBar.svelte'
|
||||||
|
import lead from '../plugin'
|
||||||
|
import { Lead } from '@hcengineering/lead'
|
||||||
|
|
||||||
|
export let _class: Ref<Class<Lead>> = lead.class.Lead
|
||||||
|
export let labelTasks = lead.string.MyLeads
|
||||||
|
|
||||||
|
let search = ''
|
||||||
|
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||||
|
const assigned = { assignee: currentUser.employee }
|
||||||
|
const created = { createdBy: currentUser._id }
|
||||||
|
let subscribed = { _id: { $in: [] as Ref<Task>[] } }
|
||||||
|
|
||||||
|
$: baseQuery = updateBaseQuery(mode, { assigned, created, subscribed })
|
||||||
|
function updateBaseQuery (mode: string, queries: { [key: string]: DocumentQuery<Lead> }) {
|
||||||
|
return { ...queries[mode] }
|
||||||
|
}
|
||||||
|
let searchQuery: DocumentQuery<Lead> = { ...baseQuery }
|
||||||
|
function updateSearchQuery (search: string): void {
|
||||||
|
searchQuery = search === '' ? { ...baseQuery } : { ...baseQuery, $search: search }
|
||||||
|
}
|
||||||
|
$: if (baseQuery) updateSearchQuery(search)
|
||||||
|
$: resultQuery = { ...searchQuery }
|
||||||
|
|
||||||
|
const subscribedQuery = createQuery()
|
||||||
|
function getSubscribed () {
|
||||||
|
subscribedQuery.query(
|
||||||
|
_class,
|
||||||
|
{ 'notification:mixin:Collaborators.collaborators': getCurrentAccount()._id },
|
||||||
|
(result) => {
|
||||||
|
const newSub = result.map((p) => p._id as Ref<AttachedDoc> as Ref<Lead>)
|
||||||
|
const curSub = subscribed._id.$in
|
||||||
|
if (curSub.length !== newSub.length || curSub.some((id, i) => newSub[i] !== id)) {
|
||||||
|
subscribed = { _id: { $in: newSub } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ sort: { _id: 1 } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
$: if (mode === 'subscribed') getSubscribed()
|
||||||
|
const config: [string, IntlString, object][] = [
|
||||||
|
['assigned', view.string.Assigned, {}],
|
||||||
|
['created', view.string.Created, {}],
|
||||||
|
['subscribed', view.string.Subscribed, {}]
|
||||||
|
]
|
||||||
|
let [[mode]] = config
|
||||||
|
function handleChangeMode (newMode: string) {
|
||||||
|
if (newMode === mode) return
|
||||||
|
mode = newMode
|
||||||
|
}
|
||||||
|
$: modeSelectorProps = {
|
||||||
|
config,
|
||||||
|
mode,
|
||||||
|
onChange: handleChangeMode
|
||||||
|
} as IModeSelector
|
||||||
|
|
||||||
|
let viewlet: Viewlet | undefined
|
||||||
|
let loading = true
|
||||||
|
|
||||||
|
let key = makeViewletKey()
|
||||||
|
let preference: ViewletPreference | undefined
|
||||||
|
onDestroy(
|
||||||
|
resolvedLocationStore.subscribe((loc) => {
|
||||||
|
key = makeViewletKey(loc)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const preferenceQuery = createQuery()
|
||||||
|
const client = getClient()
|
||||||
|
client
|
||||||
|
.findOne<Viewlet>(view.class.Viewlet, { attachTo: _class, descriptor: task.viewlet.StatusTable })
|
||||||
|
.then((res) => {
|
||||||
|
viewlet = res
|
||||||
|
preferenceQuery.query(
|
||||||
|
view.class.ViewletPreference,
|
||||||
|
{
|
||||||
|
attachedTo: res._id
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
preference = res[0]
|
||||||
|
loading = false
|
||||||
|
},
|
||||||
|
{ limit: 1 }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="ac-header full divide"
|
||||||
|
class:header-with-mode-selector={modeSelectorProps !== undefined}
|
||||||
|
class:header-without-label={!labelTasks}
|
||||||
|
>
|
||||||
|
<div class="ac-header__wrap-title">
|
||||||
|
<span class="ac-header__title"><Label label={labelTasks} /></span>
|
||||||
|
{#if modeSelectorProps !== undefined}
|
||||||
|
<ModeSelector props={modeSelectorProps} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ac-header full divide search-start">
|
||||||
|
<div class="ac-header-full small-gap">
|
||||||
|
<SearchEdit bind:value={search} />
|
||||||
|
<div class="buttons-divider" />
|
||||||
|
<FilterButton {_class} />
|
||||||
|
</div>
|
||||||
|
{#if viewlet}
|
||||||
|
<ViewletSettingButton bind:viewOptions {viewlet} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<FilterBar {_class} query={searchQuery} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
|
||||||
|
|
||||||
|
{#if viewlet}
|
||||||
|
{#if loading}
|
||||||
|
<Loading />
|
||||||
|
{:else}
|
||||||
|
<TableBrowser
|
||||||
|
{_class}
|
||||||
|
config={preference?.config ?? viewlet.config}
|
||||||
|
options={viewlet.options}
|
||||||
|
query={resultQuery}
|
||||||
|
showNotification
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
@ -27,6 +27,7 @@ import CreateCustomer from './components/CreateCustomer.svelte'
|
|||||||
import NewItemsHeader from './components/NewItemsHeader.svelte'
|
import NewItemsHeader from './components/NewItemsHeader.svelte'
|
||||||
import { getLeadTitle } from './utils'
|
import { getLeadTitle } from './utils'
|
||||||
import EditFunnel from './components/EditFunnel.svelte'
|
import EditFunnel from './components/EditFunnel.svelte'
|
||||||
|
import MyLeads from './components/MyLeads.svelte'
|
||||||
|
|
||||||
export default async (): Promise<Resources> => ({
|
export default async (): Promise<Resources> => ({
|
||||||
component: {
|
component: {
|
||||||
@ -40,7 +41,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
Leads,
|
Leads,
|
||||||
CreateCustomer,
|
CreateCustomer,
|
||||||
NewItemsHeader,
|
NewItemsHeader,
|
||||||
EditFunnel
|
EditFunnel,
|
||||||
|
MyLeads
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
LeadTitleProvider: getLeadTitle
|
LeadTitleProvider: getLeadTitle
|
||||||
|
@ -31,6 +31,7 @@ export default mergeIds(leadId, lead, {
|
|||||||
SelectCustomer: '' as IntlString,
|
SelectCustomer: '' as IntlString,
|
||||||
Customers: '' as IntlString,
|
Customers: '' as IntlString,
|
||||||
Leads: '' as IntlString,
|
Leads: '' as IntlString,
|
||||||
|
MyLeads: '' as IntlString,
|
||||||
NoLeadsForDocument: '' as IntlString,
|
NoLeadsForDocument: '' as IntlString,
|
||||||
LeadPlaceholder: '' as IntlString,
|
LeadPlaceholder: '' as IntlString,
|
||||||
CreateCustomer: '' as IntlString,
|
CreateCustomer: '' as IntlString,
|
||||||
@ -47,7 +48,8 @@ export default mergeIds(leadId, lead, {
|
|||||||
CreateCustomer: '' as AnyComponent,
|
CreateCustomer: '' as AnyComponent,
|
||||||
LeadsPresenter: '' as AnyComponent,
|
LeadsPresenter: '' as AnyComponent,
|
||||||
CreateFunnel: '' as AnyComponent,
|
CreateFunnel: '' as AnyComponent,
|
||||||
EditFunnel: '' as AnyComponent
|
EditFunnel: '' as AnyComponent,
|
||||||
|
MyLeads: '' as AnyComponent
|
||||||
},
|
},
|
||||||
function: {
|
function: {
|
||||||
LeadTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>) => Promise<string>>
|
LeadTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>) => Promise<string>>
|
||||||
|
Loading…
Reference in New Issue
Block a user