Complete workplace ticket interactions
This commit is contained in:
parent
21958eca15
commit
f118a777d3
@ -1,3 +1,5 @@
|
|||||||
|
import base64
|
||||||
|
|
||||||
from odoo import fields, http
|
from odoo import fields, http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
@ -86,6 +88,7 @@ class McsCorporateWebsite(http.Controller):
|
|||||||
"status_key": ticket.state,
|
"status_key": ticket.state,
|
||||||
"customer": ticket.partner_id.name or ticket.customer_name,
|
"customer": ticket.partner_id.name or ticket.customer_name,
|
||||||
"assigned_team": dict(ticket._fields["service_area"].selection).get(ticket.service_area),
|
"assigned_team": dict(ticket._fields["service_area"].selection).get(ticket.service_area),
|
||||||
|
"assigned_agent": ticket.user_id.name or "",
|
||||||
"created": ticket.create_date,
|
"created": ticket.create_date,
|
||||||
"updated": ticket.write_date,
|
"updated": ticket.write_date,
|
||||||
"updated_label": fields.Datetime.to_string(ticket.write_date) if ticket.write_date else "",
|
"updated_label": fields.Datetime.to_string(ticket.write_date) if ticket.write_date else "",
|
||||||
@ -286,6 +289,41 @@ class McsCorporateWebsite(http.Controller):
|
|||||||
messages = ticket.message_ids.sorted(lambda item: item.date)
|
messages = ticket.message_ids.sorted(lambda item: item.date)
|
||||||
return self._workplace_render("mcs_corporate_website.workplace_ticket_detail", {"ticket": ticket, "messages": messages})
|
return self._workplace_render("mcs_corporate_website.workplace_ticket_detail", {"ticket": ticket, "messages": messages})
|
||||||
|
|
||||||
|
@http.route(["/workplace/tickets/<int:ticket_id>/reply"], type="http", auth="user", website=True, methods=["POST"])
|
||||||
|
def workplace_ticket_reply(self, ticket_id, **post):
|
||||||
|
role = self._role()
|
||||||
|
ticket = request.env["mcs.support.ticket"].sudo().search(self._ticket_domain(role) + [("id", "=", ticket_id)], limit=1)
|
||||||
|
if not ticket:
|
||||||
|
return request.not_found()
|
||||||
|
body = (post.get("reply") or "").strip()
|
||||||
|
attachments = self._attach_uploaded_files(ticket)
|
||||||
|
if body or attachments:
|
||||||
|
ticket.message_post(
|
||||||
|
body=body or "Attachment added.",
|
||||||
|
attachment_ids=attachments.ids,
|
||||||
|
message_type="comment",
|
||||||
|
subtype_xmlid="mail.mt_comment",
|
||||||
|
)
|
||||||
|
return request.redirect("/workplace/tickets/%s" % ticket.id)
|
||||||
|
|
||||||
|
@http.route(["/workplace/tickets/<int:ticket_id>/close"], type="http", auth="user", website=True, methods=["POST"])
|
||||||
|
def workplace_ticket_close(self, ticket_id, **post):
|
||||||
|
role = self._role()
|
||||||
|
ticket = request.env["mcs.support.ticket"].sudo().search(self._ticket_domain(role) + [("id", "=", ticket_id)], limit=1)
|
||||||
|
if not ticket:
|
||||||
|
return request.not_found()
|
||||||
|
ticket.action_close()
|
||||||
|
return request.redirect("/workplace/tickets/%s" % ticket.id)
|
||||||
|
|
||||||
|
@http.route(["/workplace/tickets/<int:ticket_id>/reopen"], type="http", auth="user", website=True, methods=["POST"])
|
||||||
|
def workplace_ticket_reopen(self, ticket_id, **post):
|
||||||
|
role = self._role()
|
||||||
|
ticket = request.env["mcs.support.ticket"].sudo().search(self._ticket_domain(role) + [("id", "=", ticket_id)], limit=1)
|
||||||
|
if not ticket:
|
||||||
|
return request.not_found()
|
||||||
|
ticket.action_reopen()
|
||||||
|
return request.redirect("/workplace/tickets/%s" % ticket.id)
|
||||||
|
|
||||||
@http.route(["/workplace/knowledge"], type="http", auth="user", website=True)
|
@http.route(["/workplace/knowledge"], type="http", auth="user", website=True)
|
||||||
def workplace_knowledge(self, **kwargs):
|
def workplace_knowledge(self, **kwargs):
|
||||||
return self._workplace_render("mcs_corporate_website.workplace_knowledge", {"articles": self._knowledge(self._role())})
|
return self._workplace_render("mcs_corporate_website.workplace_knowledge", {"articles": self._knowledge(self._role())})
|
||||||
@ -311,8 +349,26 @@ class McsCorporateWebsite(http.Controller):
|
|||||||
"description": (post.get("description") or "").strip(),
|
"description": (post.get("description") or "").strip(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
self._attach_uploaded_files(ticket)
|
||||||
return request.redirect("/support/thanks/%s" % ticket.access_token)
|
return request.redirect("/support/thanks/%s" % ticket.access_token)
|
||||||
|
|
||||||
|
def _attach_uploaded_files(self, ticket):
|
||||||
|
Attachment = request.env["ir.attachment"].sudo()
|
||||||
|
attachments = Attachment.browse()
|
||||||
|
for upload in request.httprequest.files.getlist("attachments"):
|
||||||
|
if not upload or not upload.filename:
|
||||||
|
continue
|
||||||
|
attachments |= Attachment.create(
|
||||||
|
{
|
||||||
|
"name": upload.filename,
|
||||||
|
"datas": base64.b64encode(upload.stream.read()),
|
||||||
|
"res_model": "mcs.support.ticket",
|
||||||
|
"res_id": ticket.id,
|
||||||
|
"mimetype": upload.mimetype,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return attachments
|
||||||
|
|
||||||
@http.route(["/support/thanks/<string:token>"], type="http", auth="public", website=True)
|
@http.route(["/support/thanks/<string:token>"], type="http", auth="public", website=True)
|
||||||
def support_thanks(self, token, **kwargs):
|
def support_thanks(self, token, **kwargs):
|
||||||
ticket = request.env["mcs.support.ticket"].sudo().search([("access_token", "=", token)], limit=1)
|
ticket = request.env["mcs.support.ticket"].sudo().search([("access_token", "=", token)], limit=1)
|
||||||
|
|||||||
@ -15,6 +15,7 @@ class McsSupportTicket(models.Model):
|
|||||||
customer_email = fields.Char(required=True)
|
customer_email = fields.Char(required=True)
|
||||||
customer_phone = fields.Char()
|
customer_phone = fields.Char()
|
||||||
partner_id = fields.Many2one("res.partner", string="Customer", tracking=True)
|
partner_id = fields.Many2one("res.partner", string="Customer", tracking=True)
|
||||||
|
user_id = fields.Many2one("res.users", string="Assigned Agent", tracking=True)
|
||||||
company_id = fields.Many2one(
|
company_id = fields.Many2one(
|
||||||
"res.company",
|
"res.company",
|
||||||
default=lambda self: self.env.company,
|
default=lambda self: self.env.company,
|
||||||
@ -95,3 +96,6 @@ class McsSupportTicket(models.Model):
|
|||||||
|
|
||||||
def action_close(self):
|
def action_close(self):
|
||||||
self.write({"state": "closed"})
|
self.write({"state": "closed"})
|
||||||
|
|
||||||
|
def action_reopen(self):
|
||||||
|
self.write({"state": "in_progress"})
|
||||||
|
|||||||
@ -671,6 +671,11 @@
|
|||||||
box-shadow: 0 10px 24px rgba(28, 78, 216, 0.2);
|
box-shadow: 0 10px 24px rgba(28, 78, 216, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mcs-wp-btn--secondary {
|
||||||
|
background: #334155;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
.mcs-table-wrap {
|
.mcs-table-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
@ -796,6 +801,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mcs-upload {
|
||||||
|
border: 1px dashed #B8C1D1;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #F8FAFC;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mcs-search {
|
.mcs-search {
|
||||||
width: min(420px, 100%);
|
width: min(420px, 100%);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
@ -803,6 +820,80 @@
|
|||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body:has(form[action="/web/login"]) {
|
||||||
|
background: #F6F7F9;
|
||||||
|
|
||||||
|
#wrapwrap {
|
||||||
|
min-height: 100vh;
|
||||||
|
background:
|
||||||
|
linear-gradient(90deg, #101114 0%, #101114 48%, #F6F7F9 48%, #F6F7F9 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
min-height: calc(100vh - 76px);
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: 48px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main::before {
|
||||||
|
content: "Your workplace. Connected.\\A Attendance, support and collaboration in one place.";
|
||||||
|
white-space: pre-line;
|
||||||
|
position: fixed;
|
||||||
|
left: 8vw;
|
||||||
|
top: 38vh;
|
||||||
|
max-width: 440px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 3.1rem;
|
||||||
|
line-height: 1.05;
|
||||||
|
font-weight: 820;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form[action="/web/login"] {
|
||||||
|
width: min(430px, 100%);
|
||||||
|
margin-left: 42vw;
|
||||||
|
padding: 34px;
|
||||||
|
border: 1px solid #E6E8EC;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 24px 70px rgba(17, 24, 39, 0.14);
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
min-height: 46px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 46px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #1C4ED8;
|
||||||
|
border-color: #1C4ED8;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
|
body:has(form[action="/web/login"]) {
|
||||||
|
#wrapwrap {
|
||||||
|
background: #F6F7F9;
|
||||||
|
}
|
||||||
|
|
||||||
|
main::before {
|
||||||
|
position: static;
|
||||||
|
color: #17181B;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form[action="/web/login"] {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mcs-timeline {
|
.mcs-timeline {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|||||||
@ -39,6 +39,7 @@
|
|||||||
<field name="customer_email"/>
|
<field name="customer_email"/>
|
||||||
<field name="customer_phone"/>
|
<field name="customer_phone"/>
|
||||||
<field name="partner_id"/>
|
<field name="partner_id"/>
|
||||||
|
<field name="user_id"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="service_area"/>
|
<field name="service_area"/>
|
||||||
@ -66,6 +67,7 @@
|
|||||||
<field name="subject"/>
|
<field name="subject"/>
|
||||||
<field name="customer_name"/>
|
<field name="customer_name"/>
|
||||||
<field name="customer_email"/>
|
<field name="customer_email"/>
|
||||||
|
<field name="user_id"/>
|
||||||
<filter string="Open" name="open" domain="[('state', 'not in', ['resolved', 'closed'])]"/>
|
<filter string="Open" name="open" domain="[('state', 'not in', ['resolved', 'closed'])]"/>
|
||||||
<filter string="Urgent" name="urgent" domain="[('priority', '=', '2')]"/>
|
<filter string="Urgent" name="urgent" domain="[('priority', '=', '2')]"/>
|
||||||
<group expand="0" string="Group By">
|
<group expand="0" string="Group By">
|
||||||
|
|||||||
@ -196,7 +196,7 @@
|
|||||||
<label>Description<textarea name="description" required="required"/></label>
|
<label>Description<textarea name="description" required="required"/></label>
|
||||||
<label>Contact Person<input name="customer_name" t-att-value="wp_user.name" required="required"/></label>
|
<label>Contact Person<input name="customer_name" t-att-value="wp_user.name" required="required"/></label>
|
||||||
<label>Email<input type="email" name="customer_email" t-att-value="wp_user.email" required="required"/></label>
|
<label>Email<input type="email" name="customer_email" t-att-value="wp_user.email" required="required"/></label>
|
||||||
<label>Attachments<input type="file" name="attachments" multiple="multiple"/></label>
|
<label class="mcs-upload">Attachments<input type="file" name="attachments" multiple="multiple"/><span>Drop files here or choose attachments</span></label>
|
||||||
<button class="mcs-wp-btn" type="submit">Create Ticket</button>
|
<button class="mcs-wp-btn" type="submit">Create Ticket</button>
|
||||||
</form>
|
</form>
|
||||||
<aside class="mcs-wp-card"><h2>Support hours</h2><p>Standard support requests are reviewed during business hours. Urgent production issues should include affected system, business impact, and escalation contact.</p><div class="mcs-list-row"><span>Expected response</span><strong>1 business day</strong></div><div class="mcs-list-row"><span>Emergency</span><strong>Mark Urgent</strong></div></aside>
|
<aside class="mcs-wp-card"><h2>Support hours</h2><p>Standard support requests are reviewed during business hours. Urgent production issues should include affected system, business impact, and escalation contact.</p><div class="mcs-list-row"><span>Expected response</span><strong>1 business day</strong></div><div class="mcs-list-row"><span>Emergency</span><strong>Mark Urgent</strong></div></aside>
|
||||||
@ -208,8 +208,8 @@
|
|||||||
<t t-call="mcs_corporate_website.workplace_layout">
|
<t t-call="mcs_corporate_website.workplace_layout">
|
||||||
<section class="mcs-wp-page-head"><div><p t-esc="ticket.name"/><h1 t-esc="ticket.subject"/></div><span class="mcs-status" t-esc="ticket.state"/></section>
|
<section class="mcs-wp-page-head"><div><p t-esc="ticket.name"/><h1 t-esc="ticket.subject"/></div><span class="mcs-status" t-esc="ticket.state"/></section>
|
||||||
<section class="mcs-wp-two">
|
<section class="mcs-wp-two">
|
||||||
<article class="mcs-wp-card"><div class="mcs-card-head"><h2>Conversation</h2></div><div class="mcs-timeline"><div class="mcs-message"><strong t-esc="ticket.customer_name"/><p t-esc="ticket.description"/></div><t t-foreach="messages" t-as="message"><div class="mcs-message mcs-message--support"><strong t-esc="message.author_id.name"/><p t-out="message.body"/></div></t></div><form class="mcs-reply"><textarea placeholder="Write a reply"/><button class="mcs-wp-btn" type="button">Send Reply</button></form></article>
|
<article class="mcs-wp-card"><div class="mcs-card-head"><h2>Conversation</h2></div><div class="mcs-timeline"><div class="mcs-message"><strong t-esc="ticket.customer_name"/><p t-esc="ticket.description"/></div><t t-foreach="messages" t-as="message"><div class="mcs-message mcs-message--support"><strong t-esc="message.author_id.name"/><p t-out="message.body"/></div></t></div><form t-att-action="'/workplace/tickets/%s/reply' % ticket.id" method="post" enctype="multipart/form-data" class="mcs-reply"><input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/><textarea name="reply" placeholder="Write a reply"/><input type="file" name="attachments" multiple="multiple"/><button class="mcs-wp-btn" type="submit">Send Reply</button></form></article>
|
||||||
<aside class="mcs-wp-card"><h2>Ticket details</h2><div class="mcs-list-row"><span>Status</span><strong t-esc="ticket.state"/></div><div class="mcs-list-row"><span>Priority</span><strong t-esc="ticket.priority"/></div><div class="mcs-list-row"><span>Assigned Team</span><strong t-esc="ticket.service_area"/></div><div class="mcs-list-row"><span>Created</span><strong><span t-field="ticket.create_date"/></strong></div></aside>
|
<aside class="mcs-wp-card"><h2>Ticket details</h2><div class="mcs-list-row"><span>Status</span><strong t-esc="ticket.state"/></div><div class="mcs-list-row"><span>Priority</span><strong t-esc="ticket.priority"/></div><div class="mcs-list-row"><span>Assigned Team</span><strong t-esc="ticket.service_area"/></div><div class="mcs-list-row"><span>Assigned Agent</span><strong t-esc="ticket.user_id.name or '-'"/></div><div class="mcs-list-row"><span>Created</span><strong><span t-field="ticket.create_date"/></strong></div><form t-att-action="'/workplace/tickets/%s/close' % ticket.id" method="post"><input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/><button class="mcs-wp-btn" type="submit">Close Ticket</button></form><form t-att-action="'/workplace/tickets/%s/reopen' % ticket.id" method="post" class="mt-2"><input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/><button class="mcs-wp-btn mcs-wp-btn--secondary" type="submit">Reopen Ticket</button></form></aside>
|
||||||
</section>
|
</section>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user