Merge pull request #484 from shariquerik/remove-scroll-effect
This commit is contained in:
commit
30d9f1c6c1
@ -11,7 +11,10 @@
|
||||
row-key="name"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<ListHeader class="sm:mx-5 mx-3" @columnWidthUpdated="emit('columnWidthUpdated')">
|
||||
<ListHeader
|
||||
class="sm:mx-5 mx-3"
|
||||
@columnWidthUpdated="emit('columnWidthUpdated')"
|
||||
>
|
||||
<ListHeaderItem
|
||||
v-for="column in columns"
|
||||
:key="column.key"
|
||||
@ -29,32 +32,52 @@
|
||||
</Button>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows class="mx-3 sm:mx-5" id="list-rows">
|
||||
<ListRow
|
||||
v-for="row in rows"
|
||||
:key="row.name"
|
||||
v-slot="{ idx, column, item }"
|
||||
:row="row"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="['caller', 'receiver'].includes(column.key)">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.image"
|
||||
:label="item.label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="['type', 'duration'].includes(column.key)">
|
||||
<FeatherIcon :name="item.icon" class="h-3 w-3" />
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
<ListRows
|
||||
class="mx-3 sm:mx-5"
|
||||
:rows="rows"
|
||||
v-slot="{ idx, column, item }"
|
||||
doctype="CRM Call Log"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="['caller', 'receiver'].includes(column.key)">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.image"
|
||||
:label="item.label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="['type', 'duration'].includes(column.key)">
|
||||
<FeatherIcon :name="item.icon" class="h-3 w-3" />
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'status'" class="truncate text-base">
|
||||
<Badge
|
||||
:variant="'subtle'"
|
||||
:theme="item.color"
|
||||
size="md"
|
||||
:label="__(item.label)"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
@ -65,69 +88,46 @@
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'status'" class="truncate text-base">
|
||||
<Badge
|
||||
:variant="'subtle'"
|
||||
:theme="item.color"
|
||||
size="md"
|
||||
:label="__(item.label)"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() =>
|
||||
emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() => emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRow>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ selections, unselectAll }">
|
||||
@ -161,13 +161,12 @@
|
||||
<script setup>
|
||||
import HeartIcon from '@/components/Icons/HeartIcon.vue'
|
||||
import ListBulkActions from '@/components/ListBulkActions.vue'
|
||||
import ListRows from '@/components/ListViews/ListRows.vue'
|
||||
import {
|
||||
Avatar,
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListSelectBanner,
|
||||
ListRowItem,
|
||||
ListFooter,
|
||||
@ -232,7 +231,7 @@ const listBulkActionsRef = ref(null)
|
||||
|
||||
defineExpose({
|
||||
customListActions: computed(
|
||||
() => listBulkActionsRef.value?.customListActions
|
||||
() => listBulkActionsRef.value?.customListActions,
|
||||
),
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -36,96 +36,93 @@
|
||||
</Button>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows class="mx-3 sm:mx-5" id="list-rows">
|
||||
<ListRow
|
||||
v-for="row in rows"
|
||||
:key="row.name"
|
||||
v-slot="{ idx, column, item }"
|
||||
:row="row"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="column.key === 'full_name'">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.image"
|
||||
:label="item.image_label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'company_name'">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.logo"
|
||||
:label="item.label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'mobile_no'">
|
||||
<PhoneIcon class="h-4 w-4" />
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
<ListRows
|
||||
class="mx-3 sm:mx-5"
|
||||
:rows="rows"
|
||||
v-slot="{ idx, column, item }"
|
||||
doctype="Contact"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="column.key === 'full_name'">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.image"
|
||||
:label="item.image_label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'company_name'">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.logo"
|
||||
:label="item.label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'mobile_no'">
|
||||
<PhoneIcon class="h-4 w-4" />
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() => emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() =>
|
||||
emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRow>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ selections, unselectAll }">
|
||||
@ -160,13 +157,12 @@
|
||||
import HeartIcon from '@/components/Icons/HeartIcon.vue'
|
||||
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
||||
import ListBulkActions from '@/components/ListBulkActions.vue'
|
||||
import ListRows from '@/components/ListViews/ListRows.vue'
|
||||
import {
|
||||
Avatar,
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListSelectBanner,
|
||||
ListRowItem,
|
||||
ListFooter,
|
||||
|
||||
@ -36,7 +36,11 @@
|
||||
</Button>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows :rows="rows" v-slot="{ idx, column, item, row }">
|
||||
<ListRows
|
||||
:rows="rows"
|
||||
v-slot="{ idx, column, item, row }"
|
||||
doctype="CRM Deal"
|
||||
>
|
||||
<div v-if="column.key === '_assign'" class="flex items-center">
|
||||
<MultipleAvatar
|
||||
:avatars="item"
|
||||
|
||||
@ -10,7 +10,10 @@
|
||||
}"
|
||||
row-key="name"
|
||||
>
|
||||
<ListHeader class="sm:mx-5 mx-3" @columnWidthUpdated="emit('columnWidthUpdated')">
|
||||
<ListHeader
|
||||
class="sm:mx-5 mx-3"
|
||||
@columnWidthUpdated="emit('columnWidthUpdated')"
|
||||
>
|
||||
<ListHeaderItem
|
||||
v-for="column in columns"
|
||||
:key="column.key"
|
||||
@ -28,21 +31,41 @@
|
||||
</Button>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows class="mx-3 sm:mx-5" id="list-rows">
|
||||
<ListRow
|
||||
v-for="row in rows"
|
||||
:key="row.name"
|
||||
v-slot="{ idx, column, item }"
|
||||
:row="row"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<!-- <template #prefix>
|
||||
<ListRows
|
||||
class="mx-3 sm:mx-5"
|
||||
:rows="rows"
|
||||
v-slot="{ idx, column, item }"
|
||||
doctype="Email Template"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<!-- <template #prefix>
|
||||
|
||||
</template> -->
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'status'" class="truncate text-base">
|
||||
<Badge
|
||||
:variant="'subtle'"
|
||||
:theme="item.color"
|
||||
size="md"
|
||||
:label="item.label"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
@ -53,69 +76,46 @@
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.key === 'status'" class="truncate text-base">
|
||||
<Badge
|
||||
:variant="'subtle'"
|
||||
:theme="item.color"
|
||||
size="md"
|
||||
:label="item.label"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() =>
|
||||
emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() => emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRow>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ selections, unselectAll }">
|
||||
@ -148,12 +148,11 @@
|
||||
<script setup>
|
||||
import HeartIcon from '@/components/Icons/HeartIcon.vue'
|
||||
import ListBulkActions from '@/components/ListBulkActions.vue'
|
||||
import ListRows from '@/components/ListViews/ListRows.vue'
|
||||
import {
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListSelectBanner,
|
||||
ListRowItem,
|
||||
ListFooter,
|
||||
@ -219,7 +218,7 @@ const listBulkActionsRef = ref(null)
|
||||
|
||||
defineExpose({
|
||||
customListActions: computed(
|
||||
() => listBulkActionsRef.value?.customListActions
|
||||
() => listBulkActionsRef.value?.customListActions,
|
||||
),
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -36,7 +36,11 @@
|
||||
</Button>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows :rows="rows" v-slot="{ idx, column, item, row }">
|
||||
<ListRows
|
||||
:rows="rows"
|
||||
v-slot="{ idx, column, item, row }"
|
||||
doctype="CRM Lead"
|
||||
>
|
||||
<div v-if="column.key === '_assign'" class="flex items-center">
|
||||
<MultipleAvatar
|
||||
:avatars="item"
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</ListGroupHeader>
|
||||
<ListGroupRows :group="group" id="list-rows">
|
||||
<ListGroupRows :group="group">
|
||||
<ListRow
|
||||
v-for="row in group.rows"
|
||||
:key="row.name"
|
||||
@ -27,7 +27,12 @@
|
||||
</ListGroupRows>
|
||||
</div>
|
||||
</div>
|
||||
<ListRows class="mx-3 sm:mx-5" v-else id="list-rows">
|
||||
<ListRows
|
||||
v-else
|
||||
ref="scrollContainer"
|
||||
class="mx-3 sm:mx-5"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<ListRow
|
||||
v-for="row in reactivieRows"
|
||||
:key="row.name"
|
||||
@ -40,27 +45,53 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { ListRows, ListRow, ListGroupHeader, ListGroupRows } from 'frappe-ui'
|
||||
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed, watch, onBeforeUnmount, onMounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
rows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
doctype: {
|
||||
type: String,
|
||||
default: 'CRM Lead',
|
||||
},
|
||||
})
|
||||
|
||||
const reactivieRows = ref(props.rows)
|
||||
|
||||
watch(
|
||||
() => props.rows,
|
||||
(val) => (reactivieRows.value = val)
|
||||
(val) => (reactivieRows.value = val),
|
||||
)
|
||||
|
||||
let showGroupedRows = computed(() => {
|
||||
return props.rows.every(
|
||||
(row) => row.group && row.rows && Array.isArray(row.rows)
|
||||
(row) => row.group && row.rows && Array.isArray(row.rows),
|
||||
)
|
||||
})
|
||||
|
||||
const scrollPosition = useStorage(`scrollPosition${props.doctype}`, 0)
|
||||
const scrollContainer = ref(null)
|
||||
|
||||
const handleScroll = () => {
|
||||
if (scrollContainer.value) {
|
||||
scrollPosition.value = scrollContainer.value.$el.scrollTop
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (scrollContainer.value) {
|
||||
scrollContainer.value.$el.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (scrollContainer.value) {
|
||||
scrollContainer.value.$el.addEventListener('scroll', handleScroll)
|
||||
scrollContainer.value.$el.scrollTop = scrollPosition.value
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -14,7 +14,10 @@
|
||||
}"
|
||||
row-key="name"
|
||||
>
|
||||
<ListHeader class="sm:mx-5 mx-3" @columnWidthUpdated="emit('columnWidthUpdated')">
|
||||
<ListHeader
|
||||
class="sm:mx-5 mx-3"
|
||||
@columnWidthUpdated="emit('columnWidthUpdated')"
|
||||
>
|
||||
<ListHeaderItem
|
||||
v-for="column in columns"
|
||||
:key="column.key"
|
||||
@ -32,84 +35,81 @@
|
||||
</Button>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows class="mx-3 sm:mx-5" id="list-rows">
|
||||
<ListRow
|
||||
v-for="row in rows"
|
||||
:key="row.name"
|
||||
v-slot="{ idx, column, item }"
|
||||
:row="row"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="column.key === 'organization_name'">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.logo"
|
||||
:label="item.label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
<ListRows
|
||||
class="mx-3 sm:mx-5"
|
||||
:rows="rows"
|
||||
v-slot="{ idx, column, item }"
|
||||
doctype="CRM Organization"
|
||||
>
|
||||
<ListRowItem :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="column.key === 'organization_name'">
|
||||
<Avatar
|
||||
v-if="item.label"
|
||||
class="flex items-center"
|
||||
:image="item.logo"
|
||||
:label="item.label"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() => emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() =>
|
||||
emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRow>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ selections, unselectAll }">
|
||||
@ -142,13 +142,12 @@
|
||||
<script setup>
|
||||
import HeartIcon from '@/components/Icons/HeartIcon.vue'
|
||||
import ListBulkActions from '@/components/ListBulkActions.vue'
|
||||
import ListRows from '@/components/ListViews/ListRows.vue'
|
||||
import {
|
||||
Avatar,
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListSelectBanner,
|
||||
ListRowItem,
|
||||
ListFooter,
|
||||
@ -216,7 +215,7 @@ const listBulkActionsRef = ref(null)
|
||||
|
||||
defineExpose({
|
||||
customListActions: computed(
|
||||
() => listBulkActionsRef.value?.customListActions
|
||||
() => listBulkActionsRef.value?.customListActions,
|
||||
),
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -31,107 +31,102 @@
|
||||
</Button>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows class="mx-3 sm:mx-5" id="list-rows">
|
||||
<ListRow
|
||||
v-for="row in rows"
|
||||
:key="row.name"
|
||||
v-slot="{ idx, column, item }"
|
||||
:row="row"
|
||||
>
|
||||
<div v-if="column.key === 'due_date'">
|
||||
<Tooltip
|
||||
:text="item && formatDate(item, 'ddd, MMM D, YYYY | hh:mm a')"
|
||||
>
|
||||
<div class="flex items-center gap-2 truncate text-base">
|
||||
<div><CalendarIcon /></div>
|
||||
<div v-if="item" class="truncate">
|
||||
{{ formatDate(item, 'D MMM, hh:mm a') }}
|
||||
</div>
|
||||
<ListRows
|
||||
class="mx-3 sm:mx-5"
|
||||
:rows="rows"
|
||||
v-slot="{ idx, column, item }"
|
||||
doctype="CRM Task"
|
||||
>
|
||||
<div v-if="column.key === 'due_date'">
|
||||
<Tooltip :text="item && formatDate(item, 'ddd, MMM D, YYYY | hh:mm a')">
|
||||
<div class="flex items-center gap-2 truncate text-base">
|
||||
<div><CalendarIcon /></div>
|
||||
<div v-if="item" class="truncate">
|
||||
{{ formatDate(item, 'D MMM, hh:mm a') }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<ListRowItem v-else :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="column.key === 'status'">
|
||||
<TaskStatusIcon :status="item" />
|
||||
</div>
|
||||
<div v-else-if="column.key === 'priority'">
|
||||
<TaskPriorityIcon :priority="item" />
|
||||
</div>
|
||||
<div v-else-if="column.key === 'assigned_to'">
|
||||
<Avatar
|
||||
v-if="item.full_name"
|
||||
class="flex items-center"
|
||||
:image="item.user_image"
|
||||
:label="item.full_name"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="column.type === 'Text Editor'"
|
||||
v-html="item"
|
||||
class="truncate text-base h-4 [&>p]:truncate"
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<ListRowItem v-else :item="item">
|
||||
<template #prefix>
|
||||
<div v-if="column.key === 'status'">
|
||||
<TaskStatusIcon :status="item" />
|
||||
</div>
|
||||
<div v-else-if="column.key === 'priority'">
|
||||
<TaskPriorityIcon :priority="item" />
|
||||
</div>
|
||||
<div v-else-if="column.key === 'assigned_to'">
|
||||
<Avatar
|
||||
v-if="item.full_name"
|
||||
class="flex items-center"
|
||||
:image="item.user_image"
|
||||
:label="item.full_name"
|
||||
size="sm"
|
||||
/>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() =>
|
||||
emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="['modified', 'creation'].includes(column.key)"
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
<Tooltip :text="item.label">
|
||||
<div>{{ item.timeAgo }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="column.type === 'Text Editor'"
|
||||
v-html="item"
|
||||
class="truncate text-base h-4 [&>p]:truncate"
|
||||
/>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:modelValue="item"
|
||||
:disabled="true"
|
||||
class="text-ink-gray-9"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.key === '_liked_by'">
|
||||
<Button
|
||||
v-if="column.key == '_liked_by'"
|
||||
variant="ghosted"
|
||||
:class="isLiked(item) ? 'fill-red-500' : 'fill-white'"
|
||||
@click.stop.prevent="
|
||||
() => emit('likeDoc', { name: row.name, liked: isLiked(item) })
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRow>
|
||||
<HeartIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="truncate text-base"
|
||||
@click="
|
||||
(event) =>
|
||||
emit('applyFilter', {
|
||||
event,
|
||||
idx,
|
||||
column,
|
||||
item,
|
||||
firstColumn: columns[0],
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
</template>
|
||||
</ListRowItem>
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ selections, unselectAll }">
|
||||
@ -167,14 +162,13 @@ import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
||||
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
|
||||
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
||||
import ListBulkActions from '@/components/ListBulkActions.vue'
|
||||
import ListRows from '@/components/ListViews/ListRows.vue'
|
||||
import { formatDate } from '@/utils'
|
||||
import {
|
||||
Avatar,
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListSelectBanner,
|
||||
ListRowItem,
|
||||
ListFooter,
|
||||
|
||||
@ -18,7 +18,6 @@ const routes = [
|
||||
path: '/leads/view/:viewType?',
|
||||
name: 'Leads',
|
||||
component: () => import('@/pages/Leads.vue'),
|
||||
meta: { scrollPos: { top: 0, left: 0 } },
|
||||
},
|
||||
{
|
||||
path: '/leads/:leadId',
|
||||
@ -31,7 +30,6 @@ const routes = [
|
||||
path: '/deals/view/:viewType?',
|
||||
name: 'Deals',
|
||||
component: () => import('@/pages/Deals.vue'),
|
||||
meta: { scrollPos: { top: 0, left: 0 } },
|
||||
},
|
||||
{
|
||||
path: '/deals/:dealId',
|
||||
@ -56,7 +54,6 @@ const routes = [
|
||||
path: '/contacts/view/:viewType?',
|
||||
name: 'Contacts',
|
||||
component: () => import('@/pages/Contacts.vue'),
|
||||
meta: { scrollPos: { top: 0, left: 0 } },
|
||||
},
|
||||
{
|
||||
path: '/contacts/:contactId',
|
||||
@ -69,7 +66,6 @@ const routes = [
|
||||
path: '/organizations/view/:viewType?',
|
||||
name: 'Organizations',
|
||||
component: () => import('@/pages/Organizations.vue'),
|
||||
meta: { scrollPos: { top: 0, left: 0 } },
|
||||
},
|
||||
{
|
||||
path: '/organizations/:organizationId',
|
||||
@ -82,14 +78,12 @@ const routes = [
|
||||
path: '/call-logs/view/:viewType?',
|
||||
name: 'Call Logs',
|
||||
component: () => import('@/pages/CallLogs.vue'),
|
||||
meta: { scrollPos: { top: 0, left: 0 } },
|
||||
},
|
||||
{
|
||||
alias: '/email-templates',
|
||||
path: '/email-templates/view/:viewType?',
|
||||
name: 'Email Templates',
|
||||
component: () => import('@/pages/EmailTemplates.vue'),
|
||||
meta: { scrollPos: { top: 0, left: 0 } },
|
||||
},
|
||||
{
|
||||
path: '/email-templates/:emailTemplateId',
|
||||
@ -108,29 +102,9 @@ const handleMobileView = (componentName) => {
|
||||
return window.innerWidth < 768 ? `Mobile${componentName}` : componentName
|
||||
}
|
||||
|
||||
const scrollBehavior = (to, from, savedPosition) => {
|
||||
if (to.name === from.name) {
|
||||
to.meta?.scrollPos && (to.meta.scrollPos.top = 0)
|
||||
return { left: 0, top: 0 }
|
||||
}
|
||||
const scrollpos = to.meta?.scrollPos || { left: 0, top: 0 }
|
||||
|
||||
if (scrollpos.top > 0) {
|
||||
setTimeout(() => {
|
||||
let el = document.querySelector('#list-rows')
|
||||
el.scrollTo({
|
||||
top: scrollpos.top,
|
||||
left: scrollpos.left,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
|
||||
let router = createRouter({
|
||||
history: createWebHistory('/crm'),
|
||||
routes,
|
||||
scrollBehavior,
|
||||
})
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
@ -138,10 +112,6 @@ router.beforeEach(async (to, from, next) => {
|
||||
|
||||
isLoggedIn && (await userResource.promise)
|
||||
|
||||
if (from.meta?.scrollPos) {
|
||||
from.meta.scrollPos.top = document.querySelector('#list-rows')?.scrollTop
|
||||
}
|
||||
|
||||
if (to.name === 'Home' && isLoggedIn) {
|
||||
next({ name: 'Leads' })
|
||||
} else if (!isLoggedIn) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user