feat: created sorby feature to sort list based on query params
This commit is contained in:
parent
0fdd455ef1
commit
df84aa01c9
@ -9,18 +9,20 @@
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"feather-icons": "^4.28.0",
|
||||
"frappe-ui": "^0.1.0-alpha.11",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.2",
|
||||
"pinia": "^2.0.33"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vueuse/core": "^10.3.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"@vueuse/integrations": "^10.3.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"feather-icons": "^4.28.0",
|
||||
"frappe-ui": "^0.1.0-alpha.11",
|
||||
"pinia": "^2.0.33",
|
||||
"postcss": "^8.4.5",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"vite": "^4.3.9"
|
||||
"tailwindcss": "^3.3.3",
|
||||
"vite": "^4.4.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.2",
|
||||
"sortablejs": "^1.15.0"
|
||||
}
|
||||
}
|
||||
|
||||
16
frontend/src/components/Icons/DragIcon.vue
Normal file
16
frontend/src/components/Icons/DragIcon.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M13 6.75C12.3096 6.75 11.75 6.19036 11.75 5.5C11.75 4.80964 12.3096 4.25 13 4.25C13.6904 4.25 14.25 4.80964 14.25 5.5C14.25 6.19036 13.6904 6.75 13 6.75ZM3 6.75C2.30964 6.75 1.75 6.19036 1.75 5.5C1.75 4.80964 2.30964 4.25 3 4.25C3.69036 4.25 4.25 4.80964 4.25 5.5C4.25 6.19036 3.69036 6.75 3 6.75ZM6.75 5.5C6.75 6.19036 7.30964 6.75 8 6.75C8.69036 6.75 9.25 6.19036 9.25 5.5C9.25 4.80964 8.69036 4.25 8 4.25C7.30964 4.25 6.75 4.80964 6.75 5.5ZM13 11.75C12.3096 11.75 11.75 11.1904 11.75 10.5C11.75 9.80964 12.3096 9.25 13 9.25C13.6904 9.25 14.25 9.80964 14.25 10.5C14.25 11.1904 13.6904 11.75 13 11.75ZM1.75 10.5C1.75 11.1904 2.30964 11.75 3 11.75C3.69036 11.75 4.25 11.1904 4.25 10.5C4.25 9.80964 3.69036 9.25 3 9.25C2.30964 9.25 1.75 9.80964 1.75 10.5ZM8 11.75C7.30964 11.75 6.75 11.1904 6.75 10.5C6.75 9.80964 7.30964 9.25 8 9.25C8.69036 9.25 9.25 9.80964 9.25 10.5C9.25 11.1904 8.69036 11.75 8 11.75Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
163
frontend/src/components/SortBy.vue
Normal file
163
frontend/src/components/SortBy.vue
Normal file
@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<NestedPopover v-if="sortOptions.data">
|
||||
<template #target>
|
||||
<Button label="Sort" ref="sortButtonRef">
|
||||
<template #prefix><SortIcon class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
<template #body="{ close }">
|
||||
<div class="rounded-lg border border-gray-100 bg-white shadow-xl my-2">
|
||||
<div class="p-2">
|
||||
<div
|
||||
v-if="sortValues.length"
|
||||
id="sort-list"
|
||||
class="flex flex-col gap-2 mb-3"
|
||||
>
|
||||
<div
|
||||
v-for="(sort, i) in sortValues"
|
||||
:key="sort.fieldname"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<div class="flex items-center justify-center h-7 w-7 handle">
|
||||
<DragIcon class="h-4 w-4 cursor-grab text-gray-600" />
|
||||
</div>
|
||||
<Autocomplete
|
||||
class="!w-32"
|
||||
v-model="sort.fieldname"
|
||||
:options="sortOptions.data"
|
||||
placeholder="Sort by"
|
||||
/>
|
||||
<FormControl
|
||||
class="!w-32"
|
||||
type="select"
|
||||
v-model="sort.direction"
|
||||
:options="[
|
||||
{ label: 'Ascending', value: 'asc' },
|
||||
{ label: 'Descending', value: 'desc' },
|
||||
]"
|
||||
placeholder="Sort by"
|
||||
/>
|
||||
<Button variant="ghost" icon="x" @click="removeSort(i)" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-gray-600 text-sm px-3 py-2">
|
||||
Empty - Choose a field to sort by
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<Autocomplete
|
||||
:options="sortOptions.data"
|
||||
value=""
|
||||
placeholder="Sort by"
|
||||
@change="(e) => setSort(e)"
|
||||
>
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
variant="ghost"
|
||||
@click="togglePopover()"
|
||||
label="Add sort"
|
||||
>
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4" />
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
</Autocomplete>
|
||||
<Button
|
||||
v-if="sortValues.length"
|
||||
variant="ghost"
|
||||
label="Clear sort"
|
||||
@click="clearSort(close)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</NestedPopover>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import NestedPopover from '@/components/NestedPopover.vue'
|
||||
import SortIcon from '@/components/Icons/SortIcon.vue'
|
||||
import DragIcon from '@/components/Icons/DragIcon.vue'
|
||||
import { useSortable } from '@vueuse/integrations/useSortable'
|
||||
import { useOrderBy } from '@/composables/orderby'
|
||||
import {
|
||||
FeatherIcon,
|
||||
Button,
|
||||
Autocomplete,
|
||||
FormControl,
|
||||
createResource,
|
||||
} from 'frappe-ui'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
doctype: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const { get: getOrderBy, set: setOrderBy } = useOrderBy()
|
||||
|
||||
const sortButtonRef = ref(null)
|
||||
const sortValues = ref(initialOrderBy())
|
||||
|
||||
const sortOptions = createResource({
|
||||
url: 'crm.extends.doc.sort_options',
|
||||
auto: true,
|
||||
params: {
|
||||
doctype: props.doctype,
|
||||
},
|
||||
})
|
||||
|
||||
function initialOrderBy() {
|
||||
const orderBy = getOrderBy()
|
||||
if (!orderBy) return []
|
||||
const sortOptions = orderBy.split(', ')
|
||||
return sortOptions.map((sortOption) => {
|
||||
const [fieldname, direction] = sortOption.split(' ')
|
||||
return { fieldname, direction }
|
||||
})
|
||||
}
|
||||
|
||||
const sortSortable = useSortable('#sort-list', sortValues, {
|
||||
handle: '.handle',
|
||||
animation: 200,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => sortValues.value,
|
||||
(value) => {
|
||||
const updatedSort = value
|
||||
.map((sort) => {
|
||||
if (typeof sort.fieldname == 'object') {
|
||||
sort.fieldname = sort.fieldname.value
|
||||
}
|
||||
const option = sortOptions.data.find((o) => o.value === sort.fieldname)
|
||||
return `${option.value} ${sort.direction}`
|
||||
})
|
||||
.join(', ')
|
||||
setOrderBy(updatedSort)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
)
|
||||
|
||||
function setSort(data) {
|
||||
sortValues.value = [
|
||||
...sortValues.value,
|
||||
{ fieldname: data.value, direction: 'asc' },
|
||||
]
|
||||
sortSortable.start()
|
||||
}
|
||||
|
||||
function removeSort(index) {
|
||||
sortValues.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function clearSort(close) {
|
||||
sortValues.value = []
|
||||
close()
|
||||
}
|
||||
</script>
|
||||
@ -25,9 +25,7 @@
|
||||
</Dropdown>
|
||||
</template>
|
||||
<template #right-subheader>
|
||||
<Button label="Sort">
|
||||
<template #prefix><SortIcon class="h-4" /></template>
|
||||
</Button>
|
||||
<SortBy doctype="CRM Lead" />
|
||||
<Button label="Filter">
|
||||
<template #prefix><FilterIcon class="h-4" /></template>
|
||||
</Button>
|
||||
@ -58,10 +56,11 @@
|
||||
import ListView from '@/components/ListView.vue'
|
||||
import LayoutHeader from '@/components/LayoutHeader.vue'
|
||||
import Breadcrumbs from '@/components/Breadcrumbs.vue'
|
||||
import SortIcon from '@/components/Icons/SortIcon.vue'
|
||||
import FilterIcon from '@/components/Icons/FilterIcon.vue'
|
||||
import NewLead from '@/components/NewLead.vue'
|
||||
import SortBy from '@/components/SortBy.vue'
|
||||
import { usersStore } from '@/stores/users'
|
||||
import { useOrderBy } from '@/composables/orderby'
|
||||
import {
|
||||
FeatherIcon,
|
||||
Dialog,
|
||||
@ -71,7 +70,7 @@ import {
|
||||
createResource,
|
||||
} from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, computed, reactive } from 'vue'
|
||||
import { ref, computed, reactive, onBeforeUpdate } from 'vue'
|
||||
|
||||
const list = {
|
||||
title: 'Leads',
|
||||
@ -79,6 +78,7 @@ const list = {
|
||||
singular_label: 'Lead',
|
||||
}
|
||||
const { getUser } = usersStore()
|
||||
const { get: getOrderBy } = useOrderBy()
|
||||
|
||||
const currentView = ref({
|
||||
label: 'List',
|
||||
@ -101,9 +101,9 @@ const leads = createListResource({
|
||||
'lead_owner',
|
||||
'modified',
|
||||
],
|
||||
orderBy: 'modified desc',
|
||||
orderBy: getOrderBy() || 'modified desc',
|
||||
cache: 'Leads',
|
||||
pageLength: 999,
|
||||
pageLength: 20,
|
||||
auto: true,
|
||||
})
|
||||
|
||||
@ -247,7 +247,7 @@ const createLead = createResource({
|
||||
},
|
||||
})
|
||||
|
||||
const router = useRouter();
|
||||
const router = useRouter()
|
||||
|
||||
function createNewLead(close) {
|
||||
createLead
|
||||
@ -268,4 +268,9 @@ function createNewLead(close) {
|
||||
})
|
||||
.then(close)
|
||||
}
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
leads.orderBy = getOrderBy() || 'modified desc'
|
||||
leads.reload()
|
||||
})
|
||||
</script>
|
||||
|
||||
32
yarn.lock
32
yarn.lock
@ -3154,7 +3154,7 @@
|
||||
"@vueuse/shared" "10.3.0"
|
||||
vue-demi ">=0.14.5"
|
||||
|
||||
"@vueuse/integrations@^10.2.1":
|
||||
"@vueuse/integrations@^10.2.1", "@vueuse/integrations@^10.3.0":
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-10.3.0.tgz#765e9505358590f21531998194c6e60a8b23655c"
|
||||
integrity sha512-Jgiv7oFyIgC6BxmDtiyG/fxyGysIds00YaY7sefwbhCZ2/tjEx1W/1WcsISSJPNI30in28+HC2J4uuU8184ekg==
|
||||
@ -3428,7 +3428,7 @@ asynckit@^0.4.0:
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||
|
||||
autoprefixer@^10.4.13, autoprefixer@^10.4.2:
|
||||
autoprefixer@^10.4.13, autoprefixer@^10.4.14:
|
||||
version "10.4.14"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d"
|
||||
integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==
|
||||
@ -6328,7 +6328,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.1.10, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.26, postcss@^8.4.5:
|
||||
postcss@^8.1.10, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.26, postcss@^8.4.27, postcss@^8.4.5:
|
||||
version "8.4.27"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.27.tgz#234d7e4b72e34ba5a92c29636734349e0d9c3057"
|
||||
integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==
|
||||
@ -7038,6 +7038,13 @@ rollup@^3.25.2:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rollup@^3.27.1:
|
||||
version "3.27.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.27.2.tgz#59adc973504408289be89e5978e938ce852c9520"
|
||||
integrity sha512-YGwmHf7h2oUHkVBT248x0yt6vZkYQ3/rvE5iQuVBh3WO8GcJ6BNeOkpoX1yMHIiBm18EMLjBPIoUDkhgnyxGOQ==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rope-sequence@^1.3.0:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.4.tgz#df85711aaecd32f1e756f76e43a415171235d425"
|
||||
@ -7262,6 +7269,11 @@ socket.io-parser@~4.2.4:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.1"
|
||||
|
||||
sortablejs@^1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.0.tgz#53230b8aa3502bb77a29e2005808ffdb4a5f7e2a"
|
||||
integrity sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
@ -7460,7 +7472,7 @@ tabbable@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
|
||||
integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
|
||||
|
||||
tailwindcss@^3.2.7, tailwindcss@^3.3.1:
|
||||
tailwindcss@^3.2.7, tailwindcss@^3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.3.tgz#90da807393a2859189e48e9e7000e6880a736daf"
|
||||
integrity sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==
|
||||
@ -7876,14 +7888,14 @@ vite@^4.1.0, vite@^4.4.7:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
vite@^4.3.9:
|
||||
version "4.4.7"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.7.tgz#71b8a37abaf8d50561aca084dbb77fa342824154"
|
||||
integrity sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==
|
||||
vite@^4.4.9:
|
||||
version "4.4.9"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.9.tgz#1402423f1a2f8d66fd8d15e351127c7236d29d3d"
|
||||
integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==
|
||||
dependencies:
|
||||
esbuild "^0.18.10"
|
||||
postcss "^8.4.26"
|
||||
rollup "^3.25.2"
|
||||
postcss "^8.4.27"
|
||||
rollup "^3.27.1"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user