1
0
forked from test/crm

Merge pull request #613 from frappe/develop

This commit is contained in:
Shariq Ansari 2025-02-26 19:39:04 +05:30 committed by GitHub
commit 2c36ddb0ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 158 additions and 7 deletions

View File

@ -379,9 +379,11 @@ def get_data(
new_filters = filters.copy()
new_filters.update({column_field: kc.get("name")})
all_count = len(
frappe.get_list(doctype, filters=convert_filter_to_tuple(doctype, new_filters))
)
all_count = frappe.get_list(
doctype,
filters=convert_filter_to_tuple(doctype, new_filters),
fields="count(*) as total_count"
)[0].total_count
kc["all_count"] = all_count
kc["count"] = len(column_data)
@ -483,7 +485,9 @@ def get_data(
"page_length_count": page_length_count,
"is_default": is_default,
"views": get_views(doctype),
"total_count": len(frappe.get_list(doctype, filters=filters)),
"total_count": frappe.get_list(
doctype, filters=filters, fields="count(*) as total_count"
)[0].total_count,
"row_count": len(data),
"form_script": get_form_script(doctype),
"list_script": get_form_script(doctype, "List"),

View File

@ -82,7 +82,7 @@ const props = defineProps({
required: true,
},
filters: {
type: [Array, String],
type: [Array, Object, String],
default: [],
},
modelValue: {
@ -133,7 +133,7 @@ watchDebounced(
const options = createResource({
url: 'frappe.desk.search.search_link',
cache: [props.doctype, text.value, props.hideMe],
cache: [props.doctype, text.value, props.hideMe, props.filters],
method: 'POST',
params: {
txt: text.value,

View File

@ -39,7 +39,6 @@
</template>
<script setup>
import { TextInput } from 'frappe-ui'
import { ref, nextTick } from 'vue'
const props = defineProps({

View File

@ -0,0 +1,140 @@
<template>
<div>
<div
class="group flex flex-wrap gap-1 min-h-20 p-1.5 rounded text-base bg-surface-gray-2 hover:bg-surface-gray-3 focus:border-outline-gray-4 focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors w-full"
>
<Button
ref="valuesRef"
v-for="value in parsedValues"
:key="value"
:label="value"
theme="gray"
variant="subtle"
class="rounded bg-surface-white hover:!bg-surface-gray-1 focus-visible:ring-outline-gray-4"
@keydown.delete.capture.stop="removeLastValue"
>
<template #suffix>
<FeatherIcon
class="h-3.5"
name="x"
@click.stop="removeValue(value)"
/>
</template>
</Button>
<div class="w-full">
<Link
v-if="linkField"
class="form-control flex-1 truncate cursor-text"
:value="query"
:filters="filters"
:doctype="linkField.options"
@change="(v) => addValue(v)"
:hideMe="true"
>
<template #target="{ togglePopover }">
<button
class="w-full h-7 cursor-text"
@click.stop="togglePopover"
/>
</template>
</Link>
</div>
</div>
<ErrorMessage class="mt-2 pl-2" v-if="error" :message="error" />
</div>
</template>
<script setup>
import Link from '@/components/Controls/Link.vue'
import { getMeta } from '@/stores/meta'
import { ref, computed, nextTick } from 'vue'
const props = defineProps({
doctype: {
type: String,
required: true,
},
errorMessage: {
type: Function,
default: (value) => `${value} is an Invalid value`,
},
})
const { getFields } = getMeta(props.doctype)
const values = defineModel()
const valuesRef = ref([])
const error = ref(null)
const query = ref('')
const linkField = ref('')
const filters = computed(() => {
if (!linkField.value) return []
return {
name: ['not in', parsedValues.value],
}
})
const parsedValues = computed(() => {
error.value = ''
getLinkField()
if (!linkField.value) return []
return values.value.map((row) => row[linkField.value.fieldname])
})
const getLinkField = () => {
error.value = ''
if (!linkField.value) {
let fields = getFields()
linkField.value = fields?.find((df) =>
['Link', 'User'].includes(df.fieldtype),
)
if (!linkField.value) {
error.value =
'Table MultiSelect requires a Table with atleast one Link field'
}
}
return linkField.value
}
const addValue = (value) => {
error.value = null
if (values.value.some((row) => row[linkField.value.fieldname] === value)) {
error.value = 'Value already exists'
return
}
if (value) {
values.value.push({ [linkField.value.fieldname]: value })
!error.value && (query.value = '')
}
}
const removeValue = (value) => {
values.value = values.value.filter(
(row) => row[linkField.value.fieldname] !== value,
)
}
const removeLastValue = () => {
if (query.value) return
let valueRef = valuesRef.value[valuesRef.value.length - 1]?.$el
if (document.activeElement === valueRef) {
values.value.pop()
nextTick(() => {
if (values.value.length) {
valueRef = valuesRef.value[valuesRef.value.length - 1].$el
valueRef?.focus()
} else {
setFocus()
}
})
} else {
valueRef?.focus()
}
}
</script>

View File

@ -81,6 +81,12 @@
</Button>
</div>
<TableMultiselectInput
v-else-if="field.fieldtype === 'Table MultiSelect'"
v-model="data[field.fieldname]"
:doctype="field.options"
/>
<Link
v-else-if="field.fieldtype === 'User'"
class="form-control"
@ -177,6 +183,7 @@
import EditIcon from '@/components/Icons/EditIcon.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import TableMultiselectInput from '@/components/Controls/TableMultiselectInput.vue'
import Link from '@/components/Controls/Link.vue'
import Grid from '@/components/Controls/Grid.vue'
import { getFormat, evaluateDependsOnValue } from '@/utils'

View File

@ -158,6 +158,7 @@ const props = defineProps({
const restrictedFieldTypes = [
'Table',
'Table MultiSelect',
'Geolocation',
'Attach',
'Attach Image',