Skip to content

Commit

Permalink
feat: added sort
Browse files Browse the repository at this point in the history
  • Loading branch information
sumitbhanushali committed Feb 7, 2024
1 parent 3b733f5 commit 59b579e
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 39 deletions.
173 changes: 173 additions & 0 deletions desk/src/components/Sort.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<template>
<NestedPopover>
<template #target>
<Button ref="sortButtonRef" label="Sort">
<template #prefix><SortIcon class="h-4" /></template>
<template v-if="sorts?.length" #suffix>
<div
class="text-2xs flex h-5 w-5 items-center justify-center rounded bg-gray-900 pt-[1px] font-medium text-white"
>
{{ sorts.length }}
</div>
</template>
</Button>
</template>
<template #body="{ close }">
<div class="my-2 rounded-lg border border-gray-100 bg-white shadow-xl">
<div class="min-w-[352px] p-2">
<div
v-if="sorts?.length"
id="sort-list"
class="mb-3 flex flex-col gap-2"
>
<div
v-for="(sort, i) in sorts"
:key="sort.fieldname"
class="flex items-center gap-2"
>
<Autocomplete
class="!w-32"
:value="sort.fieldname"
:options="sortableFields"
placeholder="Sort by"
@change="(field) => updateSort(i, field)"
/>
<FormControl
class="!w-32"
type="select"
:value="sort.direction"
:options="[
{ label: 'Ascending', value: 'asc' },
{ label: 'Descending', value: 'desc' },
]"
placeholder="Sort by"
@change="(e) => updateSort(i, null, e.target.value)"
/>
<Button variant="ghost" icon="x" @click="removeSort(i)" />
</div>
</div>
<div
v-else
class="mb-3 flex h-7 items-center px-3 text-sm text-gray-600"
>
Empty - Choose a field to sort by
</div>
<div class="flex items-center justify-between gap-2">
<Autocomplete
:options="sortableFields"
value=""
placeholder="Sort by"
@change="(e) => setSort(e)"
>
<template #target="{ togglePopover }">
<Button
class="!text-gray-600"
variant="ghost"
label="Add Sort"
@click="togglePopover()"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
</Autocomplete>
<Button
v-if="sorts?.length"
class="!text-gray-600"
variant="ghost"
label="Clear Sort"
@click="clearSort(close)"
/>
</div>
</div>
</div>
</template>
</NestedPopover>
</template>

<script setup lang="ts">
import { Autocomplete } from "frappe-ui";
import { NestedPopover } from "@/components";
import SortIcon from "@/components/Icons/SortIcon.vue";
const props = defineProps({
sortableFields: {
type: Array,
required: true,
},
sorts: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(["event:sort"]);
function setSort(field) {
emit("event:sort", {
event: "add",
data: {
fieldname: field,
direction: "asc",
sortToApply: convertToString(
props.sorts.concat([{ fieldname: field, direction: "asc" }])
),
},
});
}
function updateSort(index, field = null, direction = null) {
if (!field) {
field = props.sorts[index].fieldname;
}
if (!direction) {
direction = "asc";
}
emit("event:sort", {
event: "update",
data: {
index,
fieldname: field,
direction: direction,
sortToApply: convertToString(
props.sorts.map((f, i) => {
if (i === index) {
return { fieldname: field, direction: direction };
}
return f;
})
),
},
});
}
function removeSort(index) {
emit("event:sort", {
event: "remove",
data: {
index,
sortToApply: convertToString(props.sorts.filter((f, i) => i !== index)),
},
});
}
function clearSort(close: Function) {
emit("event:sort", {
event: "clear",
});
close();
}
function convertToString(values) {
let _sortValues = "";
values.forEach((f) => {
_sortValues += `${f.fieldname.value} ${f.direction}, `;
});
_sortValues = _sortValues.slice(0, -2);
return _sortValues;
}
</script>
93 changes: 59 additions & 34 deletions desk/src/components/ViewControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,25 @@
<Filter
:filters="props.filter.filters"
:filterable-fields="props.filter.filterableFields"
@event:filter="emitToParent"
@event:filter="(e) => emitToParent(e, 'event:filter')"
/>
</div>
<!-- <div class="flex-none pe-2">
<p class="text-lg">Sort</p>
</div>
<div class="flex-none pe-2">
<p class="text-lg">View Settings</p>
</div> -->
<div class="pe-2 flex-none">
<Sort
:sortable-fields="props.sort.sortableFields"
:sorts="props.sort.sorts"
@event:sort="(e) => emitToParent(e, 'event:sort')"
/>
</div>
<!-- <div class="flex-none pe-2">
<p class="text-lg">View Settings</p>
</div> -->
</div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { Filter } from "@/components";
import { Filter, Sort } from "@/components";
import { Dropdown, FeatherIcon } from "frappe-ui";
import { useAuthStore } from "@/stores/auth";
Expand All @@ -47,55 +51,74 @@ const props = defineProps({
type: Object,
required: true,
},
sort: {
type: Object,
required: true,
},
});
const presetFilters = [
{
label: "All Tickets",
onClick: (e) => {
emitToParent({
event: "clear",
});
emitToParent(
{
event: "clear",
},
"event:filter"
);
},
},
{
label: "My Open Tickets",
onClick: (e) => {
const preset = getPresetFilters("Open");
emitToParent({
event: "preset",
data: preset,
});
emitToParent(
{
event: "preset",
data: preset,
},
"event:filter"
);
},
},
{
label: "My Replied Tickets",
onClick: (e) => {
const preset = getPresetFilters("Replied");
emitToParent({
event: "preset",
data: preset,
});
emitToParent(
{
event: "preset",
data: preset,
},
"event:filter"
);
},
},
{
label: "My Resolved Tickets",
onClick: (e) => {
const preset = getPresetFilters("Resolved");
emitToParent({
event: "preset",
data: preset,
});
emitToParent(
{
event: "preset",
data: preset,
},
"event:filter"
);
},
},
{
label: "My Closed Tickets",
onClick: (e) => {
const preset = getPresetFilters("Closed");
emitToParent({
event: "preset",
data: preset,
});
emitToParent(
{
event: "preset",
data: preset,
},
"event:filter"
);
},
},
];
Expand Down Expand Up @@ -125,14 +148,16 @@ function getPresetFilters(status) {
};
}
const emit = defineEmits(["event:filter"]);
const emit = defineEmits(["event:filter", "event:sort"]);
function emitToParent(data) {
if (data.event === "clear") {
currentPreset.value = "All Tickets";
} else {
currentPreset.value = "Filtered Tickets";
function emitToParent(data, event) {
if (event === "event:filter") {
if (data.event === "clear") {
currentPreset.value = "All Tickets";
} else {
currentPreset.value = "Filtered Tickets";
}
}
emit("event:filter", data);
emit(event, data);
}
</script>
45 changes: 45 additions & 0 deletions desk/src/components/icons/SortIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<svg
width="16"
height="17"
viewBox="0 0 16 17"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1.75 3.75H10.75"
stroke="currentColor"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M1.75 7.75H7.75"
stroke="currentColor"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M1.75 11.75H5.75"
stroke="currentColor"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.25 8.25L12.25 6.25L10.25 8.25"
stroke="currentColor"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12.25 12.25L12.25 6.25"
stroke="currentColor"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
1 change: 1 addition & 0 deletions desk/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export { default as UserAvatar } from "./UserAvatar.vue";
export { default as ViewControls } from "./ViewControls.vue";
export { default as Filter } from "./Filter.vue";
export { default as LayoutHeader } from "./LayoutHeader.vue";
export { default as Sort } from "./Sort.vue";
Loading

0 comments on commit 59b579e

Please sign in to comment.