1
0
forked from test/crm

feat: Kanban Board View

This commit is contained in:
Shariq Ansari 2024-06-20 18:22:10 +05:30
parent c71cade089
commit 8fc09341dd
3 changed files with 101 additions and 5 deletions

View File

@ -376,6 +376,24 @@ def get_list_data(
"list_script": get_form_script(doctype, "List"),
}
@frappe.whitelist()
def get_kanban_data(doctype: str, filters: dict, order_by: str, column_field: str, columns=None, rows=None):
filters = frappe._dict(filters)
columns = frappe.parse_json(columns)
data = []
for column in columns:
column_filters = filters.copy()
column_filters.update({column_field: column})
column_data = frappe.get_list(
doctype,
fields=rows,
filters=column_filters,
order_by=order_by,
page_length=20,
)
data.append({"column": column, "data": column_data, "count": len(column_data)})
return data
@frappe.whitelist()
def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False):
not_allowed_fieldtypes = [

View File

@ -0,0 +1,62 @@
<template>
<div class="flex sm:mx-5 mx-3 mb-3 overflow-hidden">
<Draggable
:list="columns.data"
item-key="column"
class="flex gap-2 overflow-x-auto"
>
<template #item="{ element: column }">
<div class="flex flex-col gap-2 overflow-hidden min-w-[268px]">
<div>{{ column.column }}</div>
<Draggable
:list="column.data"
group="fields"
item-key="name"
class="flex flex-col gap-2 overflow-y-auto h-full"
>
<template #item="{ element: fields }">
<div class="p-3 rounded border bg-white">
<div v-for="value in fields">
<div>{{ value }}</div>
</div>
</div>
</template>
</Draggable>
</div>
</template>
</Draggable>
</div>
</template>
<script setup>
import { createResource } from 'frappe-ui'
import Draggable from 'vuedraggable'
const props = defineProps({
doctype: { type: String, required: true },
filters: { type: Object, required: true },
column_field: { type: String, required: true },
columns: { type: Object, required: true },
rows: { type: Array, required: true },
})
function getParams() {
return {
doctype: props.doctype,
filters: props.filters,
order_by: 'modified',
column_field: props.column_field,
columns: props.columns,
rows: props.rows,
}
}
const columns = createResource({
url: 'crm.api.doc.get_kanban_data',
params: getParams(),
cache: ['Kanban', props.doctype],
auto: true,
onSuccess(data) {
data
},
})
</script>

View File

@ -29,9 +29,24 @@
allowedViews: ['list', 'group_by', 'kanban'],
}"
/>
<KanbanView
doctype="CRM Lead"
:filters="{ converted: 0 }"
column_field="status"
:columns="[
'New',
'Contacted',
'Nurture',
'Qualified',
'Unqualified',
'Junk',
]"
:rows="['name', 'status', 'organization', 'lead_owner']"
v-if="route.params.viewType == 'kanban'"
/>
<LeadsListView
ref="leadsListView"
v-if="leads.data && rows.length"
v-else-if="leads.data && rows.length"
v-model="leads.data.page_length_count"
v-model:list="leads"
:rows="rows"
@ -69,6 +84,7 @@ import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import LeadsIcon from '@/components/Icons/LeadsIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue'
import LeadsListView from '@/components/ListViews/LeadsListView.vue'
import KanbanView from '@/components/Kanban/KanbanView.vue'
import LeadModal from '@/components/Modals/LeadModal.vue'
import ViewControls from '@/components/ViewControls.vue'
import { usersStore } from '@/stores/users'
@ -111,7 +127,7 @@ const rows = computed(() => {
if (!leads.value?.data.group_by_field?.name) return []
return getGroupedByRows(
leads.value?.data.data,
leads.value?.data.group_by_field
leads.value?.data.group_by_field,
)
} else {
return parseRows(leads.value?.data.data)
@ -177,8 +193,8 @@ function parseRows(rows) {
lead.sla_status == 'Failed'
? 'red'
: lead.sla_status == 'Fulfilled'
? 'green'
: 'orange'
? 'green'
: 'orange'
if (value == 'First Response Due') {
value = __(timeAgo(lead.response_by))
tooltipText = dateFormat(lead.response_by, dateTooltipFormat)
@ -213,7 +229,7 @@ function parseRows(rows) {
}
} else if (
['first_response_time', 'first_responded_on', 'response_by'].includes(
row
row,
)
) {
let field = row == 'response_by' ? 'response_by' : 'first_responded_on'