Skip to content

Commit

Permalink
feat: ticket: refactor (#1286)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssiyad authored Jun 28, 2023
1 parent 781d0db commit 670d2c4
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 140 deletions.
8 changes: 8 additions & 0 deletions desk/src/composables/listManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,13 @@ export function createListManager(options: ListOptions) {
);
}

watch(
() => list.list.loading,
() => {
list.hasPreviousPage = false;
list.hasNextPage = false;
}
);

return list;
}
41 changes: 18 additions & 23 deletions desk/src/pages/desk/ticket-list/AssignedInfo.vue
Original file line number Diff line number Diff line change
@@ -1,40 +1,35 @@
<template>
<Tooltip :text="getTooltipLabel(agentName)">
<Avatar size="sm" :label="agentName" :image="avatarUrl" />
<Tooltip v-if="agent" :text="getTooltipLabel(user.doc?.full_name)">
<Avatar
size="sm"
:label="user.doc?.full_name"
:image="user.doc?.user_image"
/>
</Tooltip>
</template>
<script setup lang="ts">
import { computed, defineProps, toRefs } from "vue";
import { computed, defineProps, toRef } from "vue";
import { createDocumentResource, Avatar, Tooltip } from "frappe-ui";
const props = defineProps({
ticketId: {
assign: {
type: String,
required: true,
required: false,
default: "",
},
});
const { ticketId } = toRefs(props);
const assignees = computed(() => ticket.getAssignees?.data?.message || []);
const assignee = computed(() => [...assignees.value].pop());
const agentName = computed(() => assignee.value?.full_name);
const avatarUrl = computed(() => assignee.value?.user_image);
const ticket = createDocumentResource({
doctype: "HD Ticket",
name: ticketId,
cache: ["Ticket", ticketId],
whitelistedMethods: {
getAssignees: {
method: "get_assignees",
cache: ["TicketAssignees", ticketId],
},
},
const assign = toRef(props, "assign");
const assignJson = computed(() => JSON.parse(assign.value));
const agent = computed(() => [...assignJson.value].pop());
const user = createDocumentResource({
doctype: "User",
name: agent.value,
auto: true,
cache: ["User", agent.value],
});
ticket.getAssignees.fetch();
function getTooltipLabel(s: string) {
return "Assigned to " + s;
}
Expand Down
11 changes: 9 additions & 2 deletions desk/src/pages/desk/ticket-list/MainTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
row-key="name"
>
<template #subject="{ data }">
<TicketSummary class="col-subject" :ticket-name="data.name" />
<TicketSummary
class="col-subject"
:name="data.name"
:subject="data.subject"
:communications="data.count_communication"
:comments="data.count_comment"
:seen="data._seen"
/>
</template>
<template #status="{ data }">
<Dropdown :options="statusDropdownOptions(data.name, data.status)">
Expand Down Expand Up @@ -53,7 +60,7 @@
{{ data.via_customer_portal ? "Customer Portal" : "Email" }}
</template>
<template #row-extra="{ data }">
<AssignedInfo :ticket-id="data.name" />
<AssignedInfo :assign="data._assign" />
</template>
<template #actions="{ selection: s }">
<Dropdown :options="assignOpts(s as Set<number>)">
Expand Down
71 changes: 32 additions & 39 deletions desk/src/pages/desk/ticket-list/TicketSummary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,73 +15,66 @@
<div
class="flex gap-2 opacity-0 transition"
:class="{
'group-hover:opacity-100': conversationCount || commentCount,
'group-hover:opacity-100': communications || comments,
}"
>
<div v-if="conversationCount" class="flex items-center gap-1 text-xs">
<div v-if="communications" class="flex items-center gap-1 text-xs">
<IconMail class="h-3 w-3" />
{{ conversationCount }}
{{ communications }}
</div>
<div v-if="commentCount" class="flex items-center gap-1 text-xs">
<div v-if="comments" class="flex items-center gap-1 text-xs">
<IconComment class="h-3 w-3" />
{{ commentCount }}
{{ comments }}
</div>
</div>
<div class="flex items-center gap-1 text-xs">
<IconHash class="h-3 w-3" />
{{ ticketName }}
{{ name }}
</div>
</div>
</RouterLink>
</template>

<script setup lang="ts">
import { computed, toRefs } from "vue";
import { createDocumentResource } from "frappe-ui";
import { useAuthStore } from "@/stores/auth";
import IconComment from "~icons/lucide/message-square";
import IconHash from "~icons/espresso/hash";
import IconMail from "~icons/lucide/mail";
import IconComment from "~icons/lucide/message-square";
const props = defineProps({
ticketName: {
name: {
type: Number,
required: true,
},
subject: {
type: String,
required: true,
},
communications: {
type: Number,
required: false,
default: 0,
},
comments: {
type: Number,
required: false,
default: 0,
},
seen: {
type: String,
required: false,
default: "",
},
});
const { ticketName } = toRefs(props);
const authStore = useAuthStore();
const { name, seen } = toRefs(props);
const isSeen = computed(() => seen.value?.includes(authStore.userId));
const toRoute = computed(() => ({
name: "DeskTicket",
params: {
ticketId: ticketName.value,
ticketId: name.value,
},
}));
const subject = computed(() => ticket.doc?.subject);
const metaData = computed(() => ticket.getMeta?.data?.message);
const conversationCount = computed(() => metaData.value?.conversation_count);
const commentCount = computed(() => metaData.value?.comment_count);
const isSeen = computed(() => metaData.value?.is_seen);
const ticket = createDocumentResource({
doctype: "HD Ticket",
name: ticketName,
cache: ["Ticket", ticketName],
whitelistedMethods: {
getMeta: {
method: "get_meta",
cache: ["TicketMetaData", ticketName],
},
},
});
ticket.getMeta.fetch();
</script>

<style scoped>
.badge-new {
padding: 3px 6px;
width: 38px;
height: 20px;
border-radius: 5px;
}
</style>
27 changes: 22 additions & 5 deletions helpdesk/extends/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ def get_list(
group_by=group_by,
)

query = apply_custom_filters(doctype, query,fields=fields)
query = apply_custom_filters(doctype, query)
query = apply_hook(doctype, query)
query = apply_sort(doctype, order_by, query)

if not fields:
query = apply_custom_select(doctype, query)

return query.run(as_dict=True, debug=debug)


Expand Down Expand Up @@ -104,14 +107,28 @@ def check_permissions(doctype, parent):
frappe.throw(f"Insufficient Permission for {doctype}", frappe.PermissionError)


def apply_custom_filters(doctype: str, query,fields:list=[]):
def apply_custom_filters(doctype: str, query):
"""
Apply custom filters to query
"""
controller = get_controller(doctype)

if hasattr(controller, "get_list_query"):
return_value = controller.get_list_query(query,fields)
if hasattr(controller, "get_list_filters"):
return_value = controller.get_list_filters(query)
if return_value is not None:
query = return_value

return query


def apply_custom_select(doctype: str, query):
"""
Apply custom select logic to query
"""
controller = get_controller(doctype)

if hasattr(controller, "get_list_select"):
return_value = controller.get_list_select(query)
if return_value is not None:
query = return_value

Expand All @@ -126,7 +143,7 @@ def apply_hook(doctype: str, query):
_module_path = "helpdesk.helpdesk.hooks." + doctype.lower()
_module = importlib.import_module(_module_path)
_class = getattr(_module, doctype)
_function = getattr(_class, "get_list_query")
_function = getattr(_class, "get_list_filters")
return _function(query)
except:
return query
10 changes: 7 additions & 3 deletions helpdesk/helpdesk/doctype/hd_article/hd_article.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class HDArticle(Document):
@staticmethod
def get_list_query(query):
def get_list_filters(query):
QBArticle = DocType("HD Article")
QBCategory = DocType("Category")

Expand Down Expand Up @@ -84,10 +84,14 @@ def add_feedback(hd_article, helpful):
field = "helpful" if helpful else "not_helpful"

value = cint(frappe.db.get_value("HD Article", hd_article, field))
frappe.db.set_value("HD Article", hd_article, field, value + 1, update_modified=False)
frappe.db.set_value(
"HD Article", hd_article, field, value + 1, update_modified=False
)


@frappe.whitelist(allow_guest=True)
def increment_view(hd_article):
value = cint(frappe.db.get_value("HD Article", hd_article, "views"))
frappe.db.set_value("HD Article", hd_article, "views", value + 1, update_modified=False)
frappe.db.set_value(
"HD Article", hd_article, "views", value + 1, update_modified=False
)
70 changes: 22 additions & 48 deletions helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

import json
from datetime import timedelta
from typing import List
from functools import lru_cache
from typing import List

import frappe
from frappe import _
from frappe.core.utils import get_parent_doc
from frappe.database.database import Criterion, Query
from frappe.desk.form.assign_to import add as assign
from frappe.desk.form.assign_to import clear as clear_all_assignments
from frappe.email.inbox import link_communication_to_document
Expand All @@ -18,6 +17,8 @@
from frappe.query_builder.functions import Count
from frappe.utils import date_diff, get_datetime, now_datetime, time_diff_in_seconds
from frappe.utils.user import is_website_user
from pypika.queries import Query
from pypika.terms import Criterion

from helpdesk.helpdesk.doctype.hd_ticket_activity.hd_ticket_activity import (
log_ticket_activity,
Expand All @@ -26,22 +27,34 @@
default_outgoing_email_account,
default_ticket_outgoing_email_account,
)
from helpdesk.utils import publish_event, capture_event
from helpdesk.utils import capture_event, publish_event


class HDTicket(Document):
@staticmethod
def get_list_query(query: Query, fields):
def get_list_select(query: Query):
QBTicket = frappe.qb.DocType("HD Ticket")

query = HDTicket.filter_by_team(query)
if not fields:
query = query.select(QBTicket.star)
QBComment = frappe.qb.DocType("HD Ticket Comment")
QBCommunication = frappe.qb.DocType("Communication")

query = (
query.left_join(QBComment)
.on(QBComment.reference_ticket == QBTicket.name)
.select(Count(QBComment.name).as_("count_comment"))
.left_join(QBCommunication)
.on(
(QBCommunication.reference_doctype == "HD Ticket")
& (QBCommunication.reference_name == QBTicket.name)
)
.select(Count(QBCommunication.name).as_("count_communication"))
.select(QBTicket.star)
.groupby(QBTicket.name)
)

return query

@staticmethod
def filter_by_team(query: Query):
def get_list_filters(query: Query):
user = frappe.session.user

if HDTicket.can_ignore_restrictions(user):
Expand Down Expand Up @@ -538,45 +551,6 @@ def create_communication_via_contact(self, message, attachments=[]):
def mark_seen(self):
self.add_seen()

def get_comment_count(self):
QBComment = DocType("HD Ticket Comment")

count = Count("*").as_("count")
res = (
frappe.qb.from_(QBComment)
.select(count)
.where(QBComment.reference_ticket == self.name)
.run(as_dict=True)
)

return res.pop().count

def get_conversation_count(self):
QBCommunication = DocType("Communication")

count = Count("*").as_("count")
res = (
frappe.qb.from_(QBCommunication)
.select(count)
.where(QBCommunication.reference_doctype == "HD Ticket")
.where(QBCommunication.reference_name == self.name)
.run(as_dict=True)
)

return res.pop().count

def is_seen(self):
seen = self._seen or ""
return frappe.session.user in seen

@frappe.whitelist()
def get_meta(self):
return {
"comment_count": self.get_comment_count(),
"conversation_count": self.get_conversation_count(),
"is_seen": self.is_seen(),
}

@frappe.whitelist()
def get_assignees(self):
QBUser = DocType("User")
Expand Down
Loading

0 comments on commit 670d2c4

Please sign in to comment.