Skip to content

Commit

Permalink
Merge pull request #1935 from frappe/develop
Browse files Browse the repository at this point in the history
chore: Merge develop to main
  • Loading branch information
RitvikSardana authored Aug 21, 2024
2 parents 0924b7e + 5c4d68d commit 1b98c1c
Show file tree
Hide file tree
Showing 21 changed files with 388 additions and 113 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Frappe Helpdesk offers an easy setup, clean user interface, and automation tools
- Empower customers with a comprehensive knowledge base and self-service portal
- Automate redundant tasks like agent assignment and set up triggers to notify agents and customers based on certain events

![screenshot](./screenshot.png)
![screenshot](./screenshot.webp)

## Installation

Expand Down
2 changes: 1 addition & 1 deletion desk/src/components/AssignmentModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
</template>

<script setup lang="ts">
import { ref, defineModel } from "vue";
import { ref } from "vue";
import { createResource } from "frappe-ui";
import { UserAvatar, SearchComplete } from "@/components";
import { useUserStore } from "@/stores/user";
Expand Down
2 changes: 1 addition & 1 deletion desk/src/components/CannedResponseSelectorModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

<script setup>
import { TextEditor, createListResource } from "frappe-ui";
import { ref, computed, nextTick, watch, onMounted, defineModel } from "vue";
import { ref, computed, nextTick, watch, onMounted } from "vue";
const props = defineProps({
doctype: {
Expand Down
2 changes: 1 addition & 1 deletion desk/src/components/CommentTextEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
</TextEditor>
</template>
<script setup lang="ts">
import { defineModel, computed, ref } from "vue";
import { computed, ref } from "vue";
import {
TextEditorFixedMenu,
TextEditor,
Expand Down
2 changes: 1 addition & 1 deletion desk/src/components/CommunicationArea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
</template>

<script setup lang="ts">
import { ref, defineModel } from "vue";
import { ref } from "vue";
import { EmailEditor, CommentTextEditor } from "@/components";
import { EmailIcon, CommentIcon } from "@/components/icons/";
Expand Down
2 changes: 1 addition & 1 deletion desk/src/components/EmailEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
</template>

<script setup lang="ts">
import { defineModel, ref, computed, nextTick } from "vue";
import { ref, computed, nextTick } from "vue";
import { useStorage } from "@vueuse/core";
import {
FileUploader,
Expand Down
2 changes: 1 addition & 1 deletion desk/src/components/ViewControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
</template>

<script setup lang="ts">
import { ref, computed } from "vue";
import { ref, computed, h } from "vue";
import { Dropdown, FeatherIcon, FormControl } from "frappe-ui";
import { Filter, Sort, ColumnSettings, FadedScrollableDiv } from "@/components";
import { useAuthStore } from "@/stores/auth";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
</template>

<script setup>
import { ref, defineModel } from "vue";
import { TextEditor, call, TextInput } from "frappe-ui";
const props = defineProps({
Expand Down
14 changes: 5 additions & 9 deletions desk/src/components/command-palette/CP.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
>
<template #body>
<div>
<Combobox
v-slot="{ activeIndex }"
nullable
@update:model-value="onSelection"
>
<Combobox nullable @update:model-value="onSelection">
<div class="relative">
<div class="pl-4.5 absolute inset-y-0 left-0 flex items-center">
<LucideSearch class="h-4 w-4" />
Expand All @@ -28,7 +24,7 @@
:hold="true"
>
<div
v-for="(group, index) in groupedSearchResults"
v-for="group in groupedSearchResults"
:key="group.title"
class="mt-4.5 mb-2 first:mt-3"
>
Expand Down Expand Up @@ -69,10 +65,8 @@ import {
} from "@headlessui/vue";
import CPGroup from "./CPGroup.vue";
import CPGroupResult from "./CPGroupResult.vue";
import LucideLayoutGrid from "~icons/lucide/layout-grid";
import LucideSearch from "~icons/lucide/file-search";
import LucideTicket from "~icons/lucide/ticket";
import LucideUser from "~icons/lucide/user";
import LucideBookOpen from "~icons/lucide/book-open";
let show = ref(false);
Expand Down Expand Up @@ -133,7 +127,9 @@ export default {
} else if (group.title === "Articles") {
group.component = "CPGroupResult";
group.items = group.items.map((item) => {
item.subject = item.payload.category + " / " + item.subject;
if (item.headings) {
item.subject = item.subject + " / " + item.headings;
}
item.route = {
name: "DeskKBArticle",
params: {
Expand Down
91 changes: 87 additions & 4 deletions desk/src/components/ticket/TicketsAgentList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,31 @@
</ListRowItem>
</ListRow>
</ListRows>
<ListSelectBanner>
<template #actions="{ selections }">
<Dropdown
:options="[
{
group: 'Options',
hideLabel: true,
items: [
{
label: 'Export',
icon: () =>
h(FeatherIcon, { name: 'download', class: 'h-4 w-4' }),
onClick: () => {
selectedRows = selections;
showExportDialog = true;
},
},
],
},
]"
>
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
</ListSelectBanner>
</ListView>
<div v-else class="flex h-full items-center justify-center">
<div
Expand All @@ -116,12 +141,60 @@
@update:model-value="emit('update:pageLength', $event)"
@load-more="emit('update:pageLength', 'loadMore')"
/>
<Dialog
v-model="showExportDialog"
:options="{
title: 'Export',
actions: [
{
label: 'Download',
variant: 'solid',
onClick: () => {
emit('event:export', {
export_type: export_type,
export_all: export_all,
selections: Array.from(selectedRows).join(','),
});
showExportDialog = false;
export_type = 'Excel';
export_all = false;
},
},
],
}"
>
<template #body-content>
<FormControl
v-model="export_type"
variant="outline"
:label="'Export Type'"
type="select"
:options="[
{
label: 'Excel',
value: 'Excel',
},
{
label: 'CSV',
value: 'CSV',
},
]"
:placeholder="'Excel'"
/>
<div class="mt-3">
<FormControl
v-model="export_all"
type="checkbox"
:label="`Export All ${options.totalCount} Record(s)`"
/>
</div>
</template>
</Dialog>
</template>

<script setup lang="ts">
import { dayjs } from "@/dayjs";
import { ref } from "vue";
import { useRouter } from "vue-router";
import { ref, h } from "vue";
import { useTicketStatusStore } from "@/stores/ticketStatus";
import { TicketIcon } from "@/components/icons";
import {
Expand All @@ -131,11 +204,17 @@ import {
ListRowItem,
ListHeader,
ListFooter,
ListSelectBanner,
FeatherIcon,
Dropdown,
} from "frappe-ui";
import { MultipleAvatar, StarRating } from "@/components";
const ticketStatusStore = useTicketStatusStore();
const router = useRouter();
const showExportDialog = ref(false);
const export_type = ref("Excel");
const export_all = ref(false);
let selectedRows;
const props = defineProps({
columns: {
Expand Down Expand Up @@ -168,7 +247,11 @@ const props = defineProps({
},
});
let emit = defineEmits(["update:pageLength", "event:fieldClick"]);
let emit = defineEmits([
"update:pageLength",
"event:fieldClick",
"event:export",
]);
let pageLength = ref(props.pageLength);
function handleFieldClick(e, name: string, value: string | [string]) {
Expand Down
60 changes: 58 additions & 2 deletions desk/src/pages/KnowledgeBaseArticle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
<div class="overflow-auto">
<div class="container m-auto my-12">
<TextEditor
:content="article.data?.content"
:content="textEditorContentWithIDs"
:editable="editMode"
:placeholder="placeholder"
:extensions="[PreserveIds]"
class="rounded"
:class="{
shadow: editMode,
Expand Down Expand Up @@ -63,7 +64,7 @@
</div>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
import { computed, onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { capture } from "@/telemetry";
import {
Expand Down Expand Up @@ -93,6 +94,7 @@ import KnowledgeBaseArticleTopEdit from "./knowledge-base/KnowledgeBaseArticleTo
import KnowledgeBaseArticleTopNew from "./knowledge-base/KnowledgeBaseArticleTopNew.vue";
import KnowledgeBaseArticleTopPublic from "./knowledge-base/KnowledgeBaseArticleTopPublic.vue";
import KnowledgeBaseArticleTopView from "./knowledge-base/KnowledgeBaseArticleTopView.vue";
import { Extension } from "@tiptap/core";
const props = defineProps({
articleId: {
Expand All @@ -101,6 +103,20 @@ const props = defineProps({
},
});
onMounted(() => {
setTimeout(() => {
scrollToHeading();
}, 100);
});
function scrollToHeading() {
const articleHeading = window.location.hash;
if (!articleHeading) return;
const headingElement = document.querySelector(articleHeading);
if (!headingElement) return;
headingElement.scrollIntoView({ behavior: "smooth" });
}
const route = useRoute();
const router = useRouter();
const authStore = useAuthStore();
Expand Down Expand Up @@ -331,4 +347,44 @@ const textEditorMenuButtons = [
"DeleteTable",
],
];
// extension to preserve ids in html of headings
const PreserveIds: Extension = Extension.create({
name: "preserveIds",
addGlobalAttributes() {
return [
{
types: ["heading"],
attributes: {
id: {
default: null,
parseHTML: (element) => element.getAttribute("id"),
renderHTML: (attributes) => {
if (!attributes.id) {
return {};
}
return { id: attributes.id };
},
},
},
},
];
},
});
const textEditorContentWithIDs = computed(() =>
addLinksToHeadings(article.data?.content)
);
function addLinksToHeadings(content: string) {
const parser = new DOMParser();
const doc = parser.parseFromString(content, "text/html");
const headings = doc.querySelectorAll("h2, h3, h4, h5, h6");
headings.forEach((heading) => {
const text = heading.textContent.trim();
const id = text.replace(/[^a-z0-9]+/gi, "-").toLowerCase();
heading.setAttribute("id", id);
});
return doc.body.innerHTML;
}
</script>
10 changes: 8 additions & 2 deletions desk/src/pages/TicketAgent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,16 @@ function updateTicket(fieldname: string, value: string) {
iconClasses: "text-green-600",
});
},
onError: () => {
onError: (e) => {
isLoading.value = false;
const title =
e.messages && e.messages.length > 0
? e.messages[0]
: "Failed to update ticket";
createToast({
title: "Failed to update ticket",
title,
icon: "x",
iconClasses: "text-red-600",
});
Expand Down
24 changes: 24 additions & 0 deletions desk/src/pages/TicketsAgent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
}"
@update:page-length="updatePageLength"
@event:field-click="processFieldClick"
@event:export="
(e) => {
exportRows(e.export_type, e.export_all, e.selections);
}
"
/>
</div>
</template>
Expand Down Expand Up @@ -128,6 +133,25 @@ const colFieldType = computed(() => {
return obj;
});
async function exportRows(export_type, export_all, selections) {
let filters;
let page_length;
let fields = JSON.stringify(columns.map((f) => f.key));
let order_by = sortsToApply;
if (export_all) {
filters = JSON.stringify(filtersToApply);
page_length = tickets?.data?.total_count;
} else {
let filtersClone = { ...filtersToApply };
filtersClone["name"] = ["IN", selections];
filters = JSON.stringify(filtersClone);
page_length = selections.length;
}
window.location.href = `/api/method/frappe.desk.reportview.export_query?file_format_type=${export_type}&title=HD Ticket&doctype=HD Ticket&fields=${fields}&filters=${filters}&order_by=${order_by}&page_length=${page_length}&start=0&view=Report&with_comment_count=1`;
}
function updatePageLength(value) {
if (value == "loadMore") {
pageLengthCount = tickets.data.data.length + pageLength.value;
Expand Down
Loading

0 comments on commit 1b98c1c

Please sign in to comment.