feat: update table columns in user settings
This commit is contained in:
parent
6d867d2d75
commit
721c5321e3
@ -1,10 +1,11 @@
|
|||||||
import frappe
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import get_controller
|
|
||||||
from frappe.model import no_value_fields
|
from frappe.model import no_value_fields
|
||||||
from pypika import Criterion
|
from frappe.model.document import get_controller
|
||||||
from frappe.utils import make_filter_tuple
|
from frappe.utils import make_filter_tuple
|
||||||
|
from pypika import Criterion
|
||||||
|
|
||||||
from crm.api.views import get_views
|
from crm.api.views import get_views
|
||||||
from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script
|
from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script
|
||||||
@ -675,7 +676,7 @@ def get_assigned_users(doctype, name, default_assigned_to=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_fields(doctype: str, allow_all_fieldtypes: bool = False):
|
def get_fields(doctype: str, allow_all_fieldtypes: bool = False):
|
||||||
not_allowed_fieldtypes = list(frappe.model.no_value_fields) + ["Read Only"]
|
not_allowed_fieldtypes = [*list(frappe.model.no_value_fields), "Read Only"]
|
||||||
if allow_all_fieldtypes:
|
if allow_all_fieldtypes:
|
||||||
not_allowed_fieldtypes = []
|
not_allowed_fieldtypes = []
|
||||||
fields = frappe.get_meta(doctype).fields
|
fields = frappe.get_meta(doctype).fields
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
<LoadingIndicator class="h-6 w-6" />
|
<LoadingIndicator class="h-6 w-6" />
|
||||||
<span>{{ __('Loading...') }}</span>
|
<span>{{ __('Loading...') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else class="pb-8">
|
||||||
<FieldLayout
|
<FieldLayout
|
||||||
v-if="tabs.data"
|
v-if="tabs.data"
|
||||||
:tabs="tabs.data"
|
:tabs="tabs.data"
|
||||||
|
|||||||
@ -37,7 +37,15 @@
|
|||||||
{{ __(field.label) }}
|
{{ __(field.label) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-12" />
|
<div class="w-12">
|
||||||
|
<Button
|
||||||
|
class="flex w-full items-center justify-center rounded !bg-surface-gray-2 border-0"
|
||||||
|
variant="outline"
|
||||||
|
@click="showGridFieldsEditorModal = true"
|
||||||
|
>
|
||||||
|
<FeatherIcon name="settings" class="h-4 w-4 text-ink-gray-7" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Rows -->
|
<!-- Rows -->
|
||||||
<template v-if="rows.length">
|
<template v-if="rows.length">
|
||||||
@ -183,9 +191,16 @@
|
|||||||
v-model="showGridRowFieldsModal"
|
v-model="showGridRowFieldsModal"
|
||||||
:doctype="doctype"
|
:doctype="doctype"
|
||||||
/>
|
/>
|
||||||
|
<GridFieldsEditorModal
|
||||||
|
v-if="showGridFieldsEditorModal"
|
||||||
|
v-model="showGridFieldsEditorModal"
|
||||||
|
:doctype="doctype"
|
||||||
|
:parentDoctype="parentDoctype"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import GridFieldsEditorModal from '@/components/Controls/GridFieldsEditorModal.vue'
|
||||||
import GridRowFieldsModal from '@/components/Controls/GridRowFieldsModal.vue'
|
import GridRowFieldsModal from '@/components/Controls/GridRowFieldsModal.vue'
|
||||||
import GridRowModal from '@/components/Controls/GridRowModal.vue'
|
import GridRowModal from '@/components/Controls/GridRowModal.vue'
|
||||||
import EditIcon from '@/components/Icons/EditIcon.vue'
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
@ -200,7 +215,7 @@ import {
|
|||||||
DatePicker,
|
DatePicker,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
import { ref, reactive, computed, PropType } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
label: {
|
label: {
|
||||||
@ -211,6 +226,10 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
parentDoctype: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const { getGridSettings, getFields } = getMeta(props.doctype)
|
const { getGridSettings, getFields } = getMeta(props.doctype)
|
||||||
@ -219,6 +238,7 @@ const rows = defineModel()
|
|||||||
const showRowList = ref(new Array(rows.value.length).fill(false))
|
const showRowList = ref(new Array(rows.value.length).fill(false))
|
||||||
const selectedRows = reactive(new Set())
|
const selectedRows = reactive(new Set())
|
||||||
|
|
||||||
|
const showGridFieldsEditorModal = ref(false)
|
||||||
const showGridRowFieldsModal = ref(false)
|
const showGridRowFieldsModal = ref(false)
|
||||||
|
|
||||||
const fields = computed(() => {
|
const fields = computed(() => {
|
||||||
@ -250,7 +270,7 @@ const gridTemplateColumns = computed(() => {
|
|||||||
if (gridSettings.length) {
|
if (gridSettings.length) {
|
||||||
return gridSettings.map((gs) => `minmax(0, ${gs.columns || 2}fr)`).join(' ')
|
return gridSettings.map((gs) => `minmax(0, ${gs.columns || 2}fr)`).join(' ')
|
||||||
}
|
}
|
||||||
return fields.value.map((col) => `minmax(0, ${col.width || 2}fr)`).join(' ')
|
return fields.value.map(() => `minmax(0, 2fr)`).join(' ')
|
||||||
})
|
})
|
||||||
|
|
||||||
const allRowsSelected = computed(() => {
|
const allRowsSelected = computed(() => {
|
||||||
|
|||||||
158
frontend/src/components/Controls/GridFieldsEditorModal.vue
Normal file
158
frontend/src/components/Controls/GridFieldsEditorModal.vue
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="show">
|
||||||
|
<template #body-title>
|
||||||
|
<h3
|
||||||
|
class="flex items-center gap-2 text-2xl font-semibold leading-6 text-ink-gray-9"
|
||||||
|
>
|
||||||
|
<div>{{ __('Edit Grid Fields Layout') }}</div>
|
||||||
|
<Badge
|
||||||
|
v-if="dirty"
|
||||||
|
:label="__('Not Saved')"
|
||||||
|
variant="subtle"
|
||||||
|
theme="orange"
|
||||||
|
/>
|
||||||
|
</h3>
|
||||||
|
</template>
|
||||||
|
<template #body-content>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div class="text-base text-ink-gray-8 mb-2">
|
||||||
|
{{ __('Fields Order') }}
|
||||||
|
</div>
|
||||||
|
<Draggable
|
||||||
|
v-if="oldFields.length"
|
||||||
|
:list="fields"
|
||||||
|
@end="reorder"
|
||||||
|
group="fields"
|
||||||
|
item-key="name"
|
||||||
|
class="flex flex-col gap-1"
|
||||||
|
>
|
||||||
|
<template #item="{ element: field }">
|
||||||
|
<div
|
||||||
|
class="px-1 py-0.5 border border-outline-gray-modals rounded text-base text-ink-gray-8 flex items-center justify-between gap-2"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<DragVerticalIcon class="h-3.5 cursor-grab" />
|
||||||
|
<div>{{ field.label }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button variant="ghost" icon="x" @click="removeField(field)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Draggable>
|
||||||
|
<Autocomplete
|
||||||
|
v-if="fields"
|
||||||
|
value=""
|
||||||
|
:options="fields"
|
||||||
|
@change="(e) => addField(e)"
|
||||||
|
>
|
||||||
|
<template #target="{ togglePopover }">
|
||||||
|
<Button
|
||||||
|
class="w-full mt-2"
|
||||||
|
@click="togglePopover()"
|
||||||
|
:label="__('Add Field')"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FeatherIcon name="plus" class="h-4" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #item-label="{ option }">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<div>{{ option.label }}</div>
|
||||||
|
<div class="text-ink-gray-4 text-sm">
|
||||||
|
{{ `${option.fieldname} - ${option.fieldtype}` }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Autocomplete>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Button
|
||||||
|
v-if="dirty"
|
||||||
|
class="w-full"
|
||||||
|
:label="__('Reset')"
|
||||||
|
@click="reset"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
class="w-full"
|
||||||
|
:label="__('Save')"
|
||||||
|
variant="solid"
|
||||||
|
@click="update"
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="!dirty"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
|
||||||
|
import { getMeta } from '@/stores/meta'
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
import { Dialog } from 'frappe-ui'
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
doctype: String,
|
||||||
|
parentDoctype: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { userSettings, getFields, getGridSettings, saveUserSettings } = getMeta(
|
||||||
|
props.doctype,
|
||||||
|
)
|
||||||
|
|
||||||
|
const show = defineModel()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const dirty = computed(() => {
|
||||||
|
return JSON.stringify(fields.value) !== JSON.stringify(oldFields.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const oldFields = computed(() => {
|
||||||
|
let _fields = getFields()
|
||||||
|
let gridSettings = getGridSettings()
|
||||||
|
|
||||||
|
if (gridSettings.length) {
|
||||||
|
return gridSettings.map((field) => {
|
||||||
|
return _fields.find((f) => f.fieldname === field.fieldname)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return _fields?.filter((field) => field.in_list_view)
|
||||||
|
})
|
||||||
|
|
||||||
|
const fields = ref(JSON.parse(JSON.stringify(oldFields.value)) || [])
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
fields.value = JSON.parse(JSON.stringify(oldFields.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function addField(field) {
|
||||||
|
fields.value.push(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeField(field) {
|
||||||
|
const index = fields.value.findIndex((f) => f.name === field.name)
|
||||||
|
fields.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
let updateFields = fields.value.map((field, idx) => {
|
||||||
|
return {
|
||||||
|
fieldname: field.fieldname,
|
||||||
|
columns: 2,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
saveUserSettings(props.parentDoctype, 'GridView', updateFields, () => {
|
||||||
|
loading.value = false
|
||||||
|
show.value = false
|
||||||
|
userSettings.value['GridView'][props.doctype] = updateFields
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -81,6 +81,7 @@
|
|||||||
v-model="data[field.name]"
|
v-model="data[field.name]"
|
||||||
:fields="field.fields"
|
:fields="field.fields"
|
||||||
:doctype="field.options"
|
:doctype="field.options"
|
||||||
|
:parentDoctype="doctype"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-else-if="field.type === 'Select'"
|
v-else-if="field.type === 'Select'"
|
||||||
|
|||||||
@ -76,12 +76,38 @@ export function getMeta(doctype) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveUserSettings(parentDoctype, key, value, callback) {
|
||||||
|
let oldUserSettings = userSettings.value
|
||||||
|
let newUserSettings = JSON.parse(JSON.stringify(oldUserSettings))
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
newUserSettings[key][doctype] = newUserSettings[key][doctype] || {}
|
||||||
|
Object.assign(newUserSettings[key][doctype], value)
|
||||||
|
} else {
|
||||||
|
newUserSettings[key][doctype] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON.stringify(oldUserSettings) !== JSON.stringify(newUserSettings)) {
|
||||||
|
return createResource({
|
||||||
|
url: 'frappe.model.utils.user_settings.save',
|
||||||
|
params: {
|
||||||
|
doctype: parentDoctype,
|
||||||
|
user_settings: JSON.stringify(newUserSettings),
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
onSuccess: () => callback?.(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return callback?.()
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
meta,
|
meta,
|
||||||
doctypeMeta,
|
doctypeMeta,
|
||||||
userSettings,
|
userSettings,
|
||||||
getFields,
|
getFields,
|
||||||
getGridSettings,
|
getGridSettings,
|
||||||
|
saveUserSettings,
|
||||||
getFormattedFloat,
|
getFormattedFloat,
|
||||||
getFormattedPercent,
|
getFormattedPercent,
|
||||||
getFormattedCurrency,
|
getFormattedCurrency,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user