fix: add/delete column in kanban

This commit is contained in:
Shariq Ansari 2024-07-01 16:17:51 +05:30
parent 57a954eb9a
commit 98cc43b683
2 changed files with 173 additions and 136 deletions

View File

@ -332,7 +332,8 @@ def get_data(
for kc in kanban_columns: for kc in kanban_columns:
column_filters = { column_field: kc.get('name') } column_filters = { column_field: kc.get('name') }
if column_field in filters and filters.get(column_field) != kc.name: order = kc.get("order")
if column_field in filters and filters.get(column_field) != kc.name or kc.get('delete'):
column_data = [] column_data = []
else: else:
column_filters.update(filters.copy()) column_filters.update(filters.copy())
@ -341,7 +342,6 @@ def get_data(
if kc.get("page_length"): if kc.get("page_length"):
page_length = kc.get("page_length") page_length = kc.get("page_length")
order = kc.get("order")
if order: if order:
column_data = get_records_based_on_order(doctype, rows, column_filters, page_length, order) column_data = get_records_based_on_order(doctype, rows, column_filters, page_length, order)
else: else:

View File

@ -1,145 +1,169 @@
<template> <template>
<Draggable <div class="flex overflow-x-auto">
v-if="columns" <Draggable
:list="columns" v-if="columns"
item-key="column" :list="columns"
@end="updateColumn" item-key="column"
class="flex sm:mx-2.5 mx-2 pb-3.5 overflow-x-auto" @end="updateColumn"
> class="flex sm:mx-2.5 mx-2 pb-3.5"
<template #item="{ element: column }"> >
<div <template #item="{ element: column }">
v-if="!column.delete" <div
class="flex flex-col gap-2.5 min-w-72 w-72 hover:bg-gray-100 rounded-lg p-2.5" v-if="!column.column.delete"
> class="flex flex-col gap-2.5 min-w-72 w-72 hover:bg-gray-100 rounded-lg p-2.5"
<div class="flex gap-2 items-center group justify-between"> >
<div class="flex items-center text-base"> <div class="flex gap-2 items-center group justify-between">
<NestedPopover> <div class="flex items-center text-base">
<template #target> <NestedPopover>
<Button variant="ghost" size="sm" class="hover:!bg-gray-100"> <template #target>
<IndicatorIcon <Button variant="ghost" size="sm" class="hover:!bg-gray-100">
:class="colorClasses(column.column.color, true)" <IndicatorIcon
/> :class="colorClasses(column.column.color, true)"
</Button>
</template>
<template #body="{ close }">
<div
class="flex flex-col gap-3 px-3 py-2.5 rounded-lg border border-gray-100 bg-white shadow-xl"
>
<div class="flex gap-1">
<Button
:class="colorClasses(color)"
variant="ghost"
v-for="color in colors"
:key="color"
@click="() => (column.column.color = color)"
>
<IndicatorIcon />
</Button>
</div>
<div class="flex flex-row-reverse">
<Button
variant="solid"
:label="__('Apply')"
@click="updateColumn"
/> />
</div> </Button>
</div> </template>
</template> <template #body="{ close }">
</NestedPopover> <div
<div>{{ column.column.name }}</div> class="flex flex-col gap-3 px-3 py-2.5 rounded-lg border border-gray-100 bg-white shadow-xl"
</div> >
<div class="flex"> <div class="flex gap-1">
<Dropdown :options="actions(column)"> <Button
<template #default> :class="colorClasses(color)"
<Button variant="ghost"
class="hidden group-hover:flex" v-for="color in colors"
icon="more-horizontal" :key="color"
variant="ghost" @click="() => (column.column.color = color)"
/> >
</template> <IndicatorIcon />
</Dropdown> </Button>
<Button </div>
icon="plus" <div class="flex flex-row-reverse">
variant="ghost" <Button
@click="options.onNewClick(column)" variant="solid"
/> :label="__('Apply')"
</div> @click="updateColumn"
</div> />
<div class="overflow-y-auto flex flex-col gap-2 h-full">
<Draggable
:list="column.data"
group="fields"
item-key="name"
class="flex flex-col gap-3.5 flex-1"
@end="updateColumn"
:data-column="column.column.name"
>
<template #item="{ element: fields }">
<component
:is="options.getRoute ? 'router-link' : 'div'"
class="pt-3 px-3.5 pb-2.5 rounded-lg border bg-white text-base flex flex-col"
:data-name="fields.name"
v-bind="{
to: options.getRoute ? options.getRoute(fields) : undefined,
onClick: options.onClick
? () => options.onClick(fields)
: undefined,
}"
>
<slot
name="title"
v-bind="{ fields, titleField, itemName: fields.name }"
>
<div class="h-5 flex items-center">
<div v-if="fields[titleField]">
{{ fields[titleField] }}
</div> </div>
<div class="text-gray-500" v-else>{{ __('No Title') }}</div>
</div> </div>
</slot> </template>
<div class="border-b h-px my-2.5" /> </NestedPopover>
<div>{{ column.column.name }}</div>
<div class="flex flex-col gap-3.5"> </div>
<template v-for="value in column.fields" :key="value"> <div class="flex">
<slot <Dropdown :options="actions(column)">
name="fields" <template #default>
v-bind="{ <Button
fields, class="hidden group-hover:flex"
fieldName: value, icon="more-horizontal"
itemName: fields.name, variant="ghost"
}" />
> </template>
<div v-if="fields[value]" class="truncate"> </Dropdown>
{{ fields[value] }} <Button
icon="plus"
variant="ghost"
@click="options.onNewClick(column)"
/>
</div>
</div>
<div class="overflow-y-auto flex flex-col gap-2 h-full">
<Draggable
:list="column.data"
group="fields"
item-key="name"
class="flex flex-col gap-3.5 flex-1"
@end="updateColumn"
:data-column="column.column.name"
>
<template #item="{ element: fields }">
<component
:is="options.getRoute ? 'router-link' : 'div'"
class="pt-3 px-3.5 pb-2.5 rounded-lg border bg-white text-base flex flex-col"
:data-name="fields.name"
v-bind="{
to: options.getRoute ? options.getRoute(fields) : undefined,
onClick: options.onClick
? () => options.onClick(fields)
: undefined,
}"
>
<slot
name="title"
v-bind="{ fields, titleField, itemName: fields.name }"
>
<div class="h-5 flex items-center">
<div v-if="fields[titleField]">
{{ fields[titleField] }}
</div> </div>
</slot> <div class="text-gray-500" v-else>
</template> {{ __('No Title') }}
</div> </div>
<div class="border-b h-px mt-2.5 mb-2" /> </div>
<slot name="actions" v-bind="{ itemName: fields.name }"> </slot>
<div class="flex gap-2 items-center justify-between"> <div class="border-b h-px my-2.5" />
<div></div>
<Button icon="plus" variant="ghost" @click.stop.prevent /> <div class="flex flex-col gap-3.5">
<template v-for="value in column.fields" :key="value">
<slot
name="fields"
v-bind="{
fields,
fieldName: value,
itemName: fields.name,
}"
>
<div v-if="fields[value]" class="truncate">
{{ fields[value] }}
</div>
</slot>
</template>
</div> </div>
</slot> <div class="border-b h-px mt-2.5 mb-2" />
</component> <slot name="actions" v-bind="{ itemName: fields.name }">
</template> <div class="flex gap-2 items-center justify-between">
</Draggable> <div></div>
<div <Button icon="plus" variant="ghost" @click.stop.prevent />
v-if="column.column.count < column.column.all_count" </div>
class="flex items-center justify-center" </slot>
> </component>
<Button </template>
:label="__('Load More')" </Draggable>
@click="emit('loadMore', column.column.name)" <div
/> v-if="column.column.count < column.column.all_count"
class="flex items-center justify-center"
>
<Button
:label="__('Load More')"
@click="emit('loadMore', column.column.name)"
/>
</div>
</div> </div>
</div> </div>
</div> </template>
</template> </Draggable>
</Draggable> <div v-if="deletedColumns.length" class="shrink-0 min-w-64">
<Autocomplete
value=""
:options="deletedColumns"
@change="(e) => addColumn(e)"
>
<template #target="{ togglePopover }">
<Button
class="w-full mt-2.5 mb-1 mr-5"
@click="togglePopover()"
:label="__('Add Column')"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
</Autocomplete>
</div>
</div>
</template> </template>
<script setup> <script setup>
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
import NestedPopover from '@/components/NestedPopover.vue' import NestedPopover from '@/components/NestedPopover.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import Draggable from 'vuedraggable' import Draggable from 'vuedraggable'
@ -179,6 +203,14 @@ const columns = computed(() => {
return _columns return _columns
}) })
const deletedColumns = computed(() => {
return columns.value
.filter((col) => col.column['delete'])
.map((col) => {
return { label: col.column.name, value: col.column.name }
})
})
function actions(column) { function actions(column) {
return [ return [
{ {
@ -189,7 +221,7 @@ function actions(column) {
label: __('Delete'), label: __('Delete'),
icon: 'trash-2', icon: 'trash-2',
onClick: () => { onClick: () => {
column['delete'] = true column.column['delete'] = true
updateColumn() updateColumn()
}, },
}, },
@ -198,6 +230,12 @@ function actions(column) {
] ]
} }
function addColumn(e) {
let column = columns.value.find((col) => col.column.name == e.value)
column.column['delete'] = false
updateColumn()
}
function updateColumn(d) { function updateColumn(d) {
let toColumn = d?.to?.dataset.column let toColumn = d?.to?.dataset.column
let fromColumn = d?.from?.dataset.column let fromColumn = d?.from?.dataset.column
@ -205,7 +243,6 @@ function updateColumn(d) {
let _columns = [] let _columns = []
columns.value.forEach((col) => { columns.value.forEach((col) => {
if (col.delete) return
col.column['order'] = col.data.map((d) => d.name) col.column['order'] = col.data.map((d) => d.name)
if (col.column.page_length) { if (col.column.page_length) {
delete col.column.page_length delete col.column.page_length