feat: created sorby feature to sort list based on query params

This commit is contained in:
Shariq Ansari 2023-08-09 19:51:01 +05:30
parent 0fdd455ef1
commit df84aa01c9
5 changed files with 226 additions and 28 deletions

View File

@ -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"
}
}

View 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>

View 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>

View File

@ -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>

View File

@ -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"