fix: use crmUsers in all link field

(cherry picked from commit e9573278773f77b9b179c0e088a73011f3f5f9ff)
This commit is contained in:
Shariq Ansari 2025-06-20 18:57:09 +05:30 committed by Mergify
parent b83b4847e7
commit 59999599a3
8 changed files with 116 additions and 58 deletions

View File

@ -31,7 +31,7 @@
</div> </div>
<div <div
v-if="document.get.loading" v-if="document.get.loading"
class="flex flex-1 flex-col items-center justify-center gap-3 text-xl font-medium text-gray-500" class="flex flex-1 flex-col items-center justify-center gap-3 text-xl font-medium text-ink-gray-6"
> >
<LoadingIndicator class="h-6 w-6" /> <LoadingIndicator class="h-6 w-6" />
<span>{{ __('Loading...') }}</span> <span>{{ __('Loading...') }}</span>

View File

@ -393,7 +393,7 @@ const {
getGridSettings, getGridSettings,
} = getMeta(props.doctype) } = getMeta(props.doctype)
getMeta(props.parentDoctype) getMeta(props.parentDoctype)
const { getUser } = usersStore() const { users, getUser } = usersStore()
const rows = defineModel() const rows = defineModel()
const parentDoc = defineModel('parent') const parentDoc = defineModel('parent')
@ -438,6 +438,14 @@ function getFieldObj(field) {
} }
} }
if (field.fieldtype === 'Link' && field.options === 'User') {
field.fieldtype = 'User'
field.link_filters = JSON.stringify({
...(field.link_filters ? JSON.parse(field.link_filters) : {}),
name: ['in', users.data.crmUsers?.map((user) => user.name)],
})
}
return { return {
...field, ...field,
filters: field.link_filters && JSON.parse(field.link_filters), filters: field.link_filters && JSON.parse(field.link_filters),

View File

@ -243,7 +243,7 @@ const isGridRow = inject('isGridRow')
const { getFormattedPercent, getFormattedFloat, getFormattedCurrency } = const { getFormattedPercent, getFormattedFloat, getFormattedCurrency } =
getMeta(doctype) getMeta(doctype)
const { getUser } = usersStore() const { users, getUser } = usersStore()
let triggerOnChange let triggerOnChange
let parentDoc let parentDoc
@ -278,6 +278,10 @@ const field = computed(() => {
if (field.fieldtype === 'Link' && field.options === 'User') { if (field.fieldtype === 'Link' && field.options === 'User') {
field.fieldtype = 'User' field.fieldtype = 'User'
field.link_filters = JSON.stringify({
...(field.link_filters ? JSON.parse(field.link_filters) : {}),
name: ['in', users.data.crmUsers?.map((user) => user.name)],
})
} }
if (field.fieldtype === 'Link' && field.options !== 'User') { if (field.fieldtype === 'Link' && field.options !== 'User') {

View File

@ -22,7 +22,7 @@
<div class="p-2 group bg-surface-gray-2 hover:bg-surface-gray-3 rounded"> <div class="p-2 group bg-surface-gray-2 hover:bg-surface-gray-3 rounded">
<MultiSelectUserInput <MultiSelectUserInput
v-if="users?.data?.crmUsers.length" v-if="users?.data?.crmUsers?.length"
class="flex-1" class="flex-1"
inputClass="!bg-surface-gray-2 hover:!bg-surface-gray-3 group-hover:!bg-surface-gray-3" inputClass="!bg-surface-gray-2 hover:!bg-surface-gray-3 group-hover:!bg-surface-gray-3"
:placeholder="__('john@doe.com')" :placeholder="__('john@doe.com')"

View File

@ -33,6 +33,7 @@
doctype="User" doctype="User"
@change="(option) => addValue(option) && ($refs.input.value = '')" @change="(option) => addValue(option) && ($refs.input.value = '')"
:placeholder="__('John Doe')" :placeholder="__('John Doe')"
:filters="{ name: ['in', users.data.crmUsers?.map((user) => user.name)] }"
:hideMe="true" :hideMe="true"
> >
<template #item-prefix="{ option }"> <template #item-prefix="{ option }">
@ -105,7 +106,7 @@ const oldAssignees = ref([])
const error = ref('') const error = ref('')
const { getUser } = usersStore() const { users, getUser } = usersStore()
const removeValue = (value) => { const removeValue = (value) => {
assignees.value = assignees.value.filter( assignees.value = assignees.value.filter(

View File

@ -1,23 +1,32 @@
<template> <template>
<Dialog v-model="show" :options="{ <Dialog
size: 'xl', v-model="show"
actions: [ :options="{
{ size: 'xl',
label: editMode ? __('Update') : __('Create'), actions: [
variant: 'solid', {
onClick: () => updateTask(), label: editMode ? __('Update') : __('Create'),
}, variant: 'solid',
], onClick: () => updateTask(),
}"> },
],
}"
>
<template #body-title> <template #body-title>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9"> <h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
{{ editMode ? __('Edit Task') : __('Create Task') }} {{ editMode ? __('Edit Task') : __('Create Task') }}
</h3> </h3>
<Button v-if="task?.reference_docname" size="sm" :label="task.reference_doctype == 'CRM Deal' <Button
? __('Open Deal') v-if="task?.reference_docname"
: __('Open Lead') size="sm"
" @click="redirect()"> :label="
task.reference_doctype == 'CRM Deal'
? __('Open Deal')
: __('Open Lead')
"
@click="redirect()"
>
<template #suffix> <template #suffix>
<ArrowUpRightIcon class="w-4 h-4" /> <ArrowUpRightIcon class="w-4 h-4" />
</template> </template>
@ -27,17 +36,29 @@
<template #body-content> <template #body-content>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div> <div>
<FormControl ref="title" :label="__('Title')" v-model="_task.title" :placeholder="__('Call with John Doe')" <FormControl
required /> ref="title"
:label="__('Title')"
v-model="_task.title"
:placeholder="__('Call with John Doe')"
required
/>
</div> </div>
<div> <div>
<div class="mb-1.5 text-xs text-ink-gray-5"> <div class="mb-1.5 text-xs text-ink-gray-5">
{{ __('Description') }} {{ __('Description') }}
</div> </div>
<TextEditor variant="outline" ref="description" <TextEditor
variant="outline"
ref="description"
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors" editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
:bubbleMenu="true" :content="_task.description" @change="(val) => (_task.description = val)" :placeholder="__('Took a call with John Doe and discussed the new project.') :bubbleMenu="true"
" /> :content="_task.description"
@change="(val) => (_task.description = val)"
:placeholder="
__('Took a call with John Doe and discussed the new project.')
"
/>
</div> </div>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<Dropdown :options="taskStatusOptions(updateTaskStatus)"> <Dropdown :options="taskStatusOptions(updateTaskStatus)">
@ -47,24 +68,38 @@
</template> </template>
</Button> </Button>
</Dropdown> </Dropdown>
<Link class="form-control" :value="getUser(_task.assigned_to).full_name" doctype="User" <Link
@change="(option) => (_task.assigned_to = option)" :placeholder="__('John Doe')" :hideMe="true"> class="form-control"
<template #prefix> :value="getUser(_task.assigned_to).full_name"
<UserAvatar class="mr-2 !h-4 !w-4" :user="_task.assigned_to" /> doctype="User"
</template> @change="(option) => (_task.assigned_to = option)"
<template #item-prefix="{ option }"> :placeholder="__('John Doe')"
<UserAvatar class="mr-2" :user="option.value" size="sm" /> :filters="{
</template> name: ['in', users.data.crmUsers?.map((user) => user.name)],
<template #item-label="{ option }"> }"
<Tooltip :text="option.value"> :hideMe="true"
<div class="cursor-pointer text-ink-gray-9"> >
{{ getUser(option.value).full_name }} <template #prefix>
</div> <UserAvatar class="mr-2 !h-4 !w-4" :user="_task.assigned_to" />
</Tooltip> </template>
</template> <template #item-prefix="{ option }">
<UserAvatar class="mr-2" :user="option.value" size="sm" />
</template>
<template #item-label="{ option }">
<Tooltip :text="option.value">
<div class="cursor-pointer text-ink-gray-9">
{{ getUser(option.value).full_name }}
</div>
</Tooltip>
</template>
</Link> </Link>
<DateTimePicker class="datepicker w-36" v-model="_task.due_date" :placeholder="__('01/04/2024 11:30 PM')" <DateTimePicker
:formatter="(date) => getFormat(date, '', true, true)" input-class="border-none" /> class="datepicker w-36"
v-model="_task.due_date"
:placeholder="__('01/04/2024 11:30 PM')"
:formatter="(date) => getFormat(date, '', true, true)"
input-class="border-none"
/>
<Dropdown :options="taskPriorityOptions(updateTaskPriority)"> <Dropdown :options="taskPriorityOptions(updateTaskPriority)">
<Button :label="_task.priority" class="justify-between w-full"> <Button :label="_task.priority" class="justify-between w-full">
<template #prefix> <template #prefix>
@ -114,7 +149,7 @@ const tasks = defineModel('reloadTasks')
const emit = defineEmits(['updateTask', 'after']) const emit = defineEmits(['updateTask', 'after'])
const router = useRouter() const router = useRouter()
const { getUser } = usersStore() const { users, getUser } = usersStore()
const { updateOnboardingStep } = useOnboarding('frappecrm') const { updateOnboardingStep } = useOnboarding('frappecrm')
const error = ref(null) const error = ref(null)
@ -164,20 +199,24 @@ async function updateTask() {
emit('after', d) emit('after', d)
} }
} else { } else {
let d = await call('frappe.client.insert', { let d = await call(
doc: { 'frappe.client.insert',
doctype: 'CRM Task', {
reference_doctype: props.doctype, doc: {
reference_docname: props.doc || null, doctype: 'CRM Task',
..._task.value, reference_doctype: props.doctype,
reference_docname: props.doc || null,
..._task.value,
},
}, },
}, { {
onError: (err) => { onError: (err) => {
if (err.error.exc_type == 'MandatoryError') { if (err.error.exc_type == 'MandatoryError') {
error.value = "Title is mandatory" error.value = 'Title is mandatory'
} }
} },
}) },
)
if (d.name) { if (d.name) {
updateOnboardingStep('create_first_task') updateOnboardingStep('create_first_task')
capture('task_created') capture('task_created')

View File

@ -429,7 +429,7 @@ const emit = defineEmits(['afterFieldChange', 'reload'])
const { getFormattedPercent, getFormattedFloat, getFormattedCurrency } = const { getFormattedPercent, getFormattedFloat, getFormattedCurrency } =
getMeta(props.doctype) getMeta(props.doctype)
const { isManager, getUser } = usersStore() const { users, isManager, getUser } = usersStore()
const showSidePanelModal = ref(false) const showSidePanelModal = ref(false)
@ -471,8 +471,11 @@ function parsedField(field) {
} }
if (field.fieldtype === 'Link' && field.options === 'User') { if (field.fieldtype === 'Link' && field.options === 'User') {
field.options = field.options
field.fieldtype = 'User' field.fieldtype = 'User'
field.link_filters = JSON.stringify({
...(field.link_filters ? JSON.parse(field.link_filters) : {}),
name: ['in', users.data?.crmUsers?.map((user) => user.name)],
})
} }
let _field = { let _field = {

View File

@ -45,6 +45,9 @@
doctype="User" doctype="User"
@change="(option) => (task.assigned_to = option)" @change="(option) => (task.assigned_to = option)"
:placeholder="__('John Doe')" :placeholder="__('John Doe')"
:filters="{
name: ['in', users.data?.crmUsers?.map((user) => user.name)],
}"
:hideMe="true" :hideMe="true"
> >
<template #prefix> <template #prefix>
@ -94,7 +97,7 @@ const props = defineProps({
}, },
}) })
const { getUser } = usersStore() const { users, getUser } = usersStore()
function updateTaskStatus(status) { function updateTaskStatus(status) {
props.task.status = status props.task.status = status