Add SaaS workplace frontend
This commit is contained in:
parent
09130ef5b7
commit
21958eca15
@ -19,8 +19,11 @@
|
||||
"security/security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"data/sequence_data.xml",
|
||||
"data/workplace_data.xml",
|
||||
"views/support_ticket_views.xml",
|
||||
"views/workplace_backend_views.xml",
|
||||
"views/website_templates.xml",
|
||||
"views/workplace_templates.xml",
|
||||
"views/website_menus.xml",
|
||||
"data/website_data.xml",
|
||||
],
|
||||
|
||||
@ -13,6 +13,143 @@ class McsCorporateWebsite(http.Controller):
|
||||
)
|
||||
return request.render(template, values)
|
||||
|
||||
def _workplace_render(self, template, values=None):
|
||||
values = values or {}
|
||||
values.update(self._workplace_context())
|
||||
return request.render(template, values)
|
||||
|
||||
def _employee(self):
|
||||
return request.env["hr.employee"].sudo().search([("user_id", "=", request.env.user.id)], limit=1)
|
||||
|
||||
def _role(self):
|
||||
user = request.env.user
|
||||
if user.has_group("base.group_system"):
|
||||
return "admin"
|
||||
if user.has_group("mcs_corporate_website.group_mcs_website_support_user"):
|
||||
return "support_agent"
|
||||
if self._employee():
|
||||
return "employee"
|
||||
return "client"
|
||||
|
||||
def _workplace_context(self):
|
||||
user = request.env.user
|
||||
employee = self._employee()
|
||||
role = self._role()
|
||||
partner = user.partner_id.commercial_partner_id
|
||||
return {
|
||||
"wp_user": user,
|
||||
"wp_role": role,
|
||||
"wp_employee": employee,
|
||||
"wp_first_name": (employee.name or user.name).split(" ")[0],
|
||||
"wp_company_name": partner.name or user.company_id.name,
|
||||
"wp_today": fields.Date.context_today(request.env.user),
|
||||
"wp_nav": self._workplace_nav(role),
|
||||
}
|
||||
|
||||
def _workplace_nav(self, role):
|
||||
if role == "client":
|
||||
return [
|
||||
("Dashboard", "/workplace", "dashboard"),
|
||||
("Support Tickets", "/workplace/tickets", "support"),
|
||||
("Create Ticket", "/workplace/tickets/new", "create"),
|
||||
("Knowledge Base", "/workplace/knowledge", "knowledge"),
|
||||
("Profile", "/workplace/profile", "profile"),
|
||||
]
|
||||
items = [
|
||||
("Dashboard", "/workplace", "dashboard"),
|
||||
("Attendance", "/workplace/attendance", "attendance"),
|
||||
("Leave", "/workplace/leave", "leave"),
|
||||
("Support", "/workplace/tickets", "support"),
|
||||
("Knowledge", "/workplace/knowledge", "knowledge"),
|
||||
("Announcements", "/workplace/announcements", "announcements"),
|
||||
]
|
||||
if role in ("support_agent", "admin"):
|
||||
items.insert(4, ("Queue", "/workplace/support-queue", "queue"))
|
||||
return items
|
||||
|
||||
def _ticket_domain(self, role):
|
||||
if role in ("admin", "support_agent"):
|
||||
return []
|
||||
return [("partner_id", "child_of", request.env.user.partner_id.commercial_partner_id.id)]
|
||||
|
||||
def _ticket_values(self, tickets):
|
||||
mapped = []
|
||||
for ticket in tickets:
|
||||
mapped.append(
|
||||
{
|
||||
"id": ticket.id,
|
||||
"number": ticket.name,
|
||||
"subject": ticket.subject,
|
||||
"priority": dict(ticket._fields["priority"].selection).get(ticket.priority),
|
||||
"priority_key": ticket.priority,
|
||||
"status": dict(ticket._fields["state"].selection).get(ticket.state),
|
||||
"status_key": ticket.state,
|
||||
"customer": ticket.partner_id.name or ticket.customer_name,
|
||||
"assigned_team": dict(ticket._fields["service_area"].selection).get(ticket.service_area),
|
||||
"created": ticket.create_date,
|
||||
"updated": ticket.write_date,
|
||||
"updated_label": fields.Datetime.to_string(ticket.write_date) if ticket.write_date else "",
|
||||
}
|
||||
)
|
||||
return mapped
|
||||
|
||||
def _dashboard_values(self, role):
|
||||
employee = self._employee()
|
||||
today = fields.Date.context_today(request.env.user)
|
||||
Ticket = request.env["mcs.support.ticket"].sudo()
|
||||
tickets = Ticket.search(self._ticket_domain(role), order="write_date desc", limit=8)
|
||||
values = {
|
||||
"tickets": self._ticket_values(tickets),
|
||||
"announcements": self._announcements(role, 3),
|
||||
"articles": self._knowledge(role, 3),
|
||||
"ticket_counts": {
|
||||
"open": Ticket.search_count(self._ticket_domain(role) + [("state", "=", "new")]),
|
||||
"in_progress": Ticket.search_count(self._ticket_domain(role) + [("state", "=", "in_progress")]),
|
||||
"waiting": Ticket.search_count(self._ticket_domain(role) + [("state", "=", "waiting")]),
|
||||
"resolved": Ticket.search_count(self._ticket_domain(role) + [("state", "in", ["resolved", "closed"])]),
|
||||
},
|
||||
}
|
||||
if employee and role != "client":
|
||||
Attendance = request.env["hr.attendance"].sudo()
|
||||
attendance = Attendance.search(
|
||||
[("employee_id", "=", employee.id), ("check_in", ">=", fields.Datetime.to_string(today))],
|
||||
order="check_in desc",
|
||||
limit=1,
|
||||
)
|
||||
month_start = today.replace(day=1)
|
||||
values.update(
|
||||
{
|
||||
"attendance": attendance,
|
||||
"worked_today": round(attendance.worked_hours, 2) if attendance else 0,
|
||||
"month_present": Attendance.search_count(
|
||||
[("employee_id", "=", employee.id), ("check_in", ">=", fields.Date.to_string(month_start))]
|
||||
),
|
||||
"leave_balance": self._leave_balance(employee),
|
||||
"recent_activity": self._recent_activity(employee),
|
||||
}
|
||||
)
|
||||
return values
|
||||
|
||||
def _leave_balance(self, employee):
|
||||
LeaveType = request.env["hr.leave.type"].sudo()
|
||||
return [
|
||||
{"name": leave_type.name, "days": round(leave_type.with_context(employee_id=employee.id).virtual_remaining_leaves, 2)}
|
||||
for leave_type in LeaveType.search([], limit=3)
|
||||
]
|
||||
|
||||
def _recent_activity(self, employee):
|
||||
Attendance = request.env["hr.attendance"].sudo()
|
||||
records = Attendance.search([("employee_id", "=", employee.id)], order="check_in desc", limit=5)
|
||||
return [{"label": "Attendance", "detail": record.check_in, "status": "Present" if record.check_out else "Open"} for record in records]
|
||||
|
||||
def _knowledge(self, role, limit=12):
|
||||
domain = [("active", "=", True), ("audience", "in", ["all", "client" if role == "client" else "employee"])]
|
||||
return request.env["mcs.knowledge.article"].sudo().search(domain, limit=limit)
|
||||
|
||||
def _announcements(self, role, limit=10):
|
||||
domain = [("active", "=", True), ("audience", "in", ["all", "client" if role == "client" else "employee"])]
|
||||
return request.env["mcs.announcement"].sudo().search(domain, order="date desc, id desc", limit=limit)
|
||||
|
||||
def _contact(self):
|
||||
return {
|
||||
"phone": "+1-647-679-7651",
|
||||
@ -98,6 +235,69 @@ class McsCorporateWebsite(http.Controller):
|
||||
def privacy(self, **kwargs):
|
||||
return self._render("mcs_corporate_website.page_privacy")
|
||||
|
||||
@http.route(["/workplace"], type="http", auth="user", website=True)
|
||||
def workplace_dashboard(self, **kwargs):
|
||||
role = self._role()
|
||||
template = "mcs_corporate_website.workplace_client_dashboard" if role == "client" else "mcs_corporate_website.workplace_employee_dashboard"
|
||||
return self._workplace_render(template, self._dashboard_values(role))
|
||||
|
||||
@http.route(["/workplace/attendance"], type="http", auth="user", website=True)
|
||||
def workplace_attendance(self, **kwargs):
|
||||
employee = self._employee()
|
||||
if not employee:
|
||||
return request.redirect("/workplace")
|
||||
today = fields.Date.context_today(request.env.user)
|
||||
month_start = today.replace(day=1)
|
||||
records = request.env["hr.attendance"].sudo().search(
|
||||
[("employee_id", "=", employee.id), ("check_in", ">=", fields.Date.to_string(month_start))],
|
||||
order="check_in desc",
|
||||
limit=40,
|
||||
)
|
||||
return self._workplace_render(
|
||||
"mcs_corporate_website.workplace_attendance",
|
||||
{"attendance_records": records, "attendance": records[:1], "worked_hours": round(sum(records.mapped("worked_hours")), 2)},
|
||||
)
|
||||
|
||||
@http.route(["/workplace/leave"], type="http", auth="user", website=True)
|
||||
def workplace_leave(self, **kwargs):
|
||||
employee = self._employee()
|
||||
if not employee:
|
||||
return request.redirect("/workplace")
|
||||
leaves = request.env["hr.leave"].sudo().search([("employee_id", "=", employee.id)], order="request_date_from desc", limit=20)
|
||||
return self._workplace_render("mcs_corporate_website.workplace_leave", {"leave_balance": self._leave_balance(employee), "leaves": leaves})
|
||||
|
||||
@http.route(["/workplace/tickets", "/workplace/support-queue"], type="http", auth="user", website=True)
|
||||
def workplace_tickets(self, **kwargs):
|
||||
role = self._role()
|
||||
tickets = request.env["mcs.support.ticket"].sudo().search(self._ticket_domain(role), order="priority desc, write_date desc", limit=80)
|
||||
template = "mcs_corporate_website.workplace_support_queue" if role in ("admin", "support_agent") and request.httprequest.path.endswith("support-queue") else "mcs_corporate_website.workplace_ticket_list"
|
||||
return self._workplace_render(template, {"tickets": self._ticket_values(tickets)})
|
||||
|
||||
@http.route(["/workplace/tickets/new"], type="http", auth="user", website=True)
|
||||
def workplace_ticket_new(self, **kwargs):
|
||||
return self._workplace_render("mcs_corporate_website.workplace_ticket_new")
|
||||
|
||||
@http.route(["/workplace/tickets/<int:ticket_id>"], type="http", auth="user", website=True)
|
||||
def workplace_ticket_detail(self, ticket_id, **kwargs):
|
||||
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()
|
||||
messages = ticket.message_ids.sorted(lambda item: item.date)
|
||||
return self._workplace_render("mcs_corporate_website.workplace_ticket_detail", {"ticket": ticket, "messages": messages})
|
||||
|
||||
@http.route(["/workplace/knowledge"], type="http", auth="user", website=True)
|
||||
def workplace_knowledge(self, **kwargs):
|
||||
return self._workplace_render("mcs_corporate_website.workplace_knowledge", {"articles": self._knowledge(self._role())})
|
||||
|
||||
@http.route(["/workplace/announcements"], type="http", auth="user", website=True)
|
||||
def workplace_announcements(self, **kwargs):
|
||||
return self._workplace_render("mcs_corporate_website.workplace_announcements", {"announcements": self._announcements(self._role())})
|
||||
|
||||
@http.route(["/workplace/profile"], type="http", auth="user", website=True)
|
||||
def workplace_profile(self, **kwargs):
|
||||
return self._workplace_render("mcs_corporate_website.workplace_profile")
|
||||
|
||||
@http.route(["/support/submit"], type="http", auth="public", website=True, methods=["POST"])
|
||||
def submit_support(self, **post):
|
||||
ticket = request.env["mcs.support.ticket"].sudo().create(
|
||||
|
||||
29
addons/mcs_corporate_website/data/workplace_data.xml
Normal file
29
addons/mcs_corporate_website/data/workplace_data.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<odoo noupdate="1">
|
||||
<record id="knowledge_getting_started" model="mcs.knowledge.article">
|
||||
<field name="name">Getting started with the workplace portal</field>
|
||||
<field name="category">getting_started</field>
|
||||
<field name="summary">Use the dashboard to access attendance, leave, support, announcements, and profile information.</field>
|
||||
<field name="audience">all</field>
|
||||
<field name="body"><![CDATA[<p>The workplace portal brings daily employee workflows and customer support into one authenticated Odoo-backed interface.</p>]]></field>
|
||||
</record>
|
||||
<record id="knowledge_support_ticket" model="mcs.knowledge.article">
|
||||
<field name="name">How to raise a useful support ticket</field>
|
||||
<field name="category">technical</field>
|
||||
<field name="summary">Include project name, affected page or module, priority, screenshots, and expected outcome.</field>
|
||||
<field name="audience">all</field>
|
||||
<field name="body"><![CDATA[<p>Clear ticket details help the team triage, assign, and resolve issues faster.</p>]]></field>
|
||||
</record>
|
||||
<record id="knowledge_billing" model="mcs.knowledge.article">
|
||||
<field name="name">Billing and invoice support</field>
|
||||
<field name="category">billing</field>
|
||||
<field name="summary">Create a support ticket for invoice questions, payment references, or account updates.</field>
|
||||
<field name="audience">client</field>
|
||||
<field name="body"><![CDATA[<p>Billing queries are tracked through support tickets so communication remains attached to the customer account.</p>]]></field>
|
||||
</record>
|
||||
<record id="announcement_workplace_launch" model="mcs.announcement">
|
||||
<field name="name">Metatroncube Workplace portal update</field>
|
||||
<field name="category">company</field>
|
||||
<field name="audience">all</field>
|
||||
<field name="content"><![CDATA[<p>The workplace portal now provides a cleaner dashboard for employee and customer support workflows.</p>]]></field>
|
||||
</record>
|
||||
</odoo>
|
||||
@ -1,2 +1,4 @@
|
||||
from . import support_ticket
|
||||
from . import website_setup
|
||||
from . import knowledge
|
||||
from . import announcement
|
||||
|
||||
37
addons/mcs_corporate_website/models/announcement.py
Normal file
37
addons/mcs_corporate_website/models/announcement.py
Normal file
@ -0,0 +1,37 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class McsAnnouncement(models.Model):
|
||||
_name = "mcs.announcement"
|
||||
_description = "Metatroncube Announcement"
|
||||
_inherit = ["mail.thread", "mail.activity.mixin"]
|
||||
_order = "date desc, id desc"
|
||||
|
||||
name = fields.Char(string="Title", required=True, tracking=True)
|
||||
author_id = fields.Many2one(
|
||||
"res.users",
|
||||
default=lambda self: self.env.user,
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
date = fields.Date(default=fields.Date.context_today, required=True, tracking=True)
|
||||
category = fields.Selection(
|
||||
[
|
||||
("company", "Company"),
|
||||
("hr", "HR"),
|
||||
("delivery", "Delivery"),
|
||||
("it", "Internal IT"),
|
||||
("client", "Client"),
|
||||
],
|
||||
default="company",
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
audience = fields.Selection(
|
||||
[("all", "All"), ("employee", "Employees"), ("client", "Clients")],
|
||||
default="employee",
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
content = fields.Html(required=True)
|
||||
active = fields.Boolean(default=True)
|
||||
30
addons/mcs_corporate_website/models/knowledge.py
Normal file
30
addons/mcs_corporate_website/models/knowledge.py
Normal file
@ -0,0 +1,30 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class McsKnowledgeArticle(models.Model):
|
||||
_name = "mcs.knowledge.article"
|
||||
_description = "Metatroncube Knowledge Article"
|
||||
_order = "sequence, name"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
category = fields.Selection(
|
||||
[
|
||||
("getting_started", "Getting Started"),
|
||||
("account", "Account"),
|
||||
("billing", "Billing"),
|
||||
("website", "Website"),
|
||||
("technical", "Technical Support"),
|
||||
("internal_it", "Internal IT"),
|
||||
],
|
||||
default="getting_started",
|
||||
required=True,
|
||||
)
|
||||
summary = fields.Char(required=True)
|
||||
body = fields.Html()
|
||||
audience = fields.Selection(
|
||||
[("all", "All"), ("employee", "Employees"), ("client", "Clients")],
|
||||
default="all",
|
||||
required=True,
|
||||
)
|
||||
sequence = fields.Integer(default=10)
|
||||
active = fields.Boolean(default=True)
|
||||
@ -1,3 +1,7 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_mcs_support_ticket_user,mcs.support.ticket.user,model_mcs_support_ticket,mcs_corporate_website.group_mcs_website_support_user,1,1,1,0
|
||||
access_mcs_support_ticket_manager,mcs.support.ticket.manager,model_mcs_support_ticket,mcs_corporate_website.group_mcs_website_support_manager,1,1,1,1
|
||||
access_mcs_knowledge_article_user,mcs.knowledge.article.user,model_mcs_knowledge_article,mcs_corporate_website.group_mcs_website_support_user,1,1,1,0
|
||||
access_mcs_knowledge_article_manager,mcs.knowledge.article.manager,model_mcs_knowledge_article,mcs_corporate_website.group_mcs_website_support_manager,1,1,1,1
|
||||
access_mcs_announcement_user,mcs.announcement.user,model_mcs_announcement,mcs_corporate_website.group_mcs_website_support_user,1,1,1,0
|
||||
access_mcs_announcement_manager,mcs.announcement.manager,model_mcs_announcement,mcs_corporate_website.group_mcs_website_support_manager,1,1,1,1
|
||||
|
||||
|
@ -399,3 +399,476 @@
|
||||
padding: 56px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-workplace {
|
||||
--background: #F6F7F9;
|
||||
--surface: #FFFFFF;
|
||||
--sidebar: #101114;
|
||||
--sidebar-hover: #1B1D21;
|
||||
--text-primary: #17181B;
|
||||
--text-secondary: #6B7280;
|
||||
--border: #E6E8EC;
|
||||
--accent: #1C4ED8;
|
||||
--success: #16A34A;
|
||||
--warning: #F59E0B;
|
||||
--danger: #DC2626;
|
||||
|
||||
min-height: calc(100vh - 76px);
|
||||
display: grid;
|
||||
grid-template-columns: 250px minmax(0, 1fr);
|
||||
background: var(--background);
|
||||
color: var(--text-primary);
|
||||
font-family: Inter, Manrope, "Segoe UI", sans-serif;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-wp-sidebar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
min-height: calc(100vh - 76px);
|
||||
background: var(--sidebar);
|
||||
padding: 22px 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 26px;
|
||||
|
||||
nav {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-wp-brand {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
padding: 0 8px;
|
||||
|
||||
img {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
span {
|
||||
display: grid;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
small {
|
||||
color: #9CA3AF;
|
||||
font-size: 0.66rem;
|
||||
letter-spacing: 0.16em;
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-wp-nav,
|
||||
.mcs-wp-sidebar-bottom a {
|
||||
color: #CBD5E1;
|
||||
text-decoration: none;
|
||||
border-radius: 10px;
|
||||
padding: 11px 12px;
|
||||
font-weight: 700;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
color: #fff;
|
||||
background: var(--sidebar-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-wp-sidebar-bottom {
|
||||
margin-top: auto;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mcs-wp-main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mcs-wp-topbar {
|
||||
height: 72px;
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
.mcs-wp-kicker {
|
||||
display: block;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
.mcs-wp-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mcs-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
border-radius: 999px;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.mcs-avatar--lg {
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.mcs-wp-content {
|
||||
max-width: 1500px;
|
||||
margin: 0 auto;
|
||||
padding: 34px;
|
||||
}
|
||||
|
||||
.mcs-wp-page-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
p {
|
||||
margin: 0 0 6px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 2.15rem;
|
||||
line-height: 1.12;
|
||||
font-weight: 820;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-wp-grid {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.mcs-wp-grid--2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.mcs-wp-grid--3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.mcs-wp-grid--4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.mcs-wp-two {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 380px;
|
||||
gap: 22px;
|
||||
margin-bottom: 22px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.mcs-wp-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
padding: 22px;
|
||||
box-shadow: 0 14px 38px rgba(17, 24, 39, 0.06);
|
||||
}
|
||||
|
||||
.mcs-metric {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
|
||||
span {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 800;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 1.9rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
small {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-card-head,
|
||||
.mcs-list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.mcs-card-head {
|
||||
margin-bottom: 16px;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 820;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-list-row {
|
||||
padding: 13px 0;
|
||||
border-top: 1px solid var(--border);
|
||||
|
||||
span {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-list-row--stack {
|
||||
display: grid;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.mcs-wp-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
padding: 11px 16px;
|
||||
background: var(--accent);
|
||||
color: #fff !important;
|
||||
font-weight: 800;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 10px 24px rgba(28, 78, 216, 0.2);
|
||||
}
|
||||
|
||||
.mcs-table-wrap {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.mcs-wp-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 13px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.78rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 26px;
|
||||
border-radius: 999px;
|
||||
padding: 4px 10px;
|
||||
background: #EEF2FF;
|
||||
color: #3730A3;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.mcs-ticket-in_progress,
|
||||
.mcs-priority-1 {
|
||||
background: #FEF3C7;
|
||||
color: #92400E;
|
||||
}
|
||||
|
||||
.mcs-ticket-resolved,
|
||||
.mcs-ticket-closed,
|
||||
.mcs-att-present {
|
||||
background: #DCFCE7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.mcs-ticket-waiting {
|
||||
background: #F3E8FF;
|
||||
color: #6B21A8;
|
||||
}
|
||||
|
||||
.mcs-priority-2 {
|
||||
background: #FEE2E2;
|
||||
color: #991B1B;
|
||||
}
|
||||
|
||||
.mcs-filterbar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
|
||||
span {
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
border-radius: 999px;
|
||||
padding: 8px 12px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-calendar-strip {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 16px;
|
||||
|
||||
span {
|
||||
min-width: 62px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
display: grid;
|
||||
text-align: center;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
small {
|
||||
color: var(--success);
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-wp-form {
|
||||
display: grid;
|
||||
gap: 15px;
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
color: var(--text-primary);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-search {
|
||||
width: min(420px, 100%);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.mcs-timeline {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mcs-message {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
padding: 16px;
|
||||
background: #F9FAFB;
|
||||
}
|
||||
|
||||
.mcs-message--support {
|
||||
background: #EFF6FF;
|
||||
border-color: #BFDBFE;
|
||||
}
|
||||
|
||||
.mcs-reply {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
|
||||
textarea {
|
||||
min-height: 120px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-empty {
|
||||
padding: 24px;
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mcs-profile-card {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.mcs-workplace {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mcs-wp-sidebar {
|
||||
position: static;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.mcs-wp-grid--2,
|
||||
.mcs-wp-grid--3,
|
||||
.mcs-wp-grid--4,
|
||||
.mcs-wp-two {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mcs-wp-content {
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.mcs-wp-page-head,
|
||||
.mcs-wp-topbar {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
<odoo>
|
||||
<record id="view_mcs_knowledge_article_tree" model="ir.ui.view">
|
||||
<field name="name">mcs.knowledge.article.tree</field>
|
||||
<field name="model">mcs.knowledge.article</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="category"/>
|
||||
<field name="audience"/>
|
||||
<field name="active"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_mcs_knowledge_article_form" model="ir.ui.view">
|
||||
<field name="name">mcs.knowledge.article.form</field>
|
||||
<field name="model">mcs.knowledge.article</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="summary"/>
|
||||
<field name="category"/>
|
||||
<field name="audience"/>
|
||||
<field name="sequence"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<field name="body"/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_mcs_knowledge_article" model="ir.actions.act_window">
|
||||
<field name="name">Knowledge Articles</field>
|
||||
<field name="res_model">mcs.knowledge.article</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mcs_announcement_tree" model="ir.ui.view">
|
||||
<field name="name">mcs.announcement.tree</field>
|
||||
<field name="model">mcs.announcement</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="date"/>
|
||||
<field name="name"/>
|
||||
<field name="author_id"/>
|
||||
<field name="category"/>
|
||||
<field name="audience"/>
|
||||
<field name="active"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_mcs_announcement_form" model="ir.ui.view">
|
||||
<field name="name">mcs.announcement.form</field>
|
||||
<field name="model">mcs.announcement</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="date"/>
|
||||
<field name="author_id"/>
|
||||
<field name="category"/>
|
||||
<field name="audience"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<field name="content"/>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_mcs_announcement" model="ir.actions.act_window">
|
||||
<field name="name">Announcements</field>
|
||||
<field name="res_model">mcs.announcement</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_mcs_knowledge_article" name="Knowledge Base" parent="menu_mcs_website_root" action="action_mcs_knowledge_article" sequence="20"/>
|
||||
<menuitem id="menu_mcs_announcement" name="Announcements" parent="menu_mcs_website_root" action="action_mcs_announcement" sequence="30"/>
|
||||
</odoo>
|
||||
240
addons/mcs_corporate_website/views/workplace_templates.xml
Normal file
240
addons/mcs_corporate_website/views/workplace_templates.xml
Normal file
@ -0,0 +1,240 @@
|
||||
<odoo>
|
||||
<template id="workplace_layout" name="MCS Workplace App Layout">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="mcs-workplace">
|
||||
<aside class="mcs-wp-sidebar">
|
||||
<a class="mcs-wp-brand" href="/workplace">
|
||||
<img src="/mcs_corporate_website/static/src/img/metatroncube-logo.png" alt="Metatroncube"/>
|
||||
<span>Metatroncube<small>WORKPLACE</small></span>
|
||||
</a>
|
||||
<nav>
|
||||
<t t-foreach="wp_nav" t-as="item">
|
||||
<a t-att-href="item[1]" t-attf-class="mcs-wp-nav {{ 'active' if request.httprequest.path == item[1] else '' }}">
|
||||
<span t-esc="item[0]"/>
|
||||
</a>
|
||||
</t>
|
||||
</nav>
|
||||
<div class="mcs-wp-sidebar-bottom">
|
||||
<a href="/workplace/profile">Profile</a>
|
||||
<a href="/my/account">Settings</a>
|
||||
<a href="/web/session/logout?redirect=/">Logout</a>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="mcs-wp-main">
|
||||
<header class="mcs-wp-topbar">
|
||||
<div>
|
||||
<span class="mcs-wp-kicker" t-esc="wp_role.replace('_', ' ').title()"/>
|
||||
<strong t-esc="wp_company_name"/>
|
||||
</div>
|
||||
<div class="mcs-wp-user">
|
||||
<span t-esc="wp_user.name"/>
|
||||
<span class="mcs-avatar" t-esc="wp_user.name[:1]"/>
|
||||
</div>
|
||||
</header>
|
||||
<div class="mcs-wp-content">
|
||||
<t t-out="0"/>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="mcs_metric_card" name="MCS Metric Card">
|
||||
<article class="mcs-wp-card mcs-metric">
|
||||
<span t-esc="label"/>
|
||||
<strong t-esc="value"/>
|
||||
<small t-if="hint" t-esc="hint"/>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<template id="workplace_employee_dashboard" name="Workplace Employee Dashboard">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head">
|
||||
<div>
|
||||
<p>Dashboard</p>
|
||||
<h1>Good morning, <t t-esc="wp_first_name"/></h1>
|
||||
</div>
|
||||
<span t-esc="wp_today.strftime('%A, %d %B %Y')"/>
|
||||
</section>
|
||||
<section class="mcs-wp-grid mcs-wp-grid--4">
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Attendance Status</t><t t-set="value"><t t-esc="'Clocked in' if attendance and not attendance.check_out else 'Not clocked in'"/></t><t t-set="hint">Today</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Worked Today</t><t t-set="value"><t t-esc="worked_today"/>h</t><t t-set="hint">Recorded hours</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Monthly Attendance</t><t t-set="value" t-value="month_present"/><t t-set="hint">Present days</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Assigned Tickets</t><t t-set="value" t-value="ticket_counts['open'] + ticket_counts['in_progress']"/><t t-set="hint">Open queue</t></t>
|
||||
</section>
|
||||
<section class="mcs-wp-two">
|
||||
<article class="mcs-wp-card">
|
||||
<div class="mcs-card-head"><h2>Clock In / Clock Out</h2><span class="mcs-status mcs-status--blue">Live</span></div>
|
||||
<p>Attendance is recorded in Odoo. Use the main Attendance app for clock actions until biometric/device integration is enabled here.</p>
|
||||
<a class="mcs-wp-btn" href="/web#menu_id=120&action=136">Open Attendance</a>
|
||||
</article>
|
||||
<article class="mcs-wp-card">
|
||||
<div class="mcs-card-head"><h2>Leave Balance</h2><a href="/workplace/leave">View all</a></div>
|
||||
<t t-foreach="leave_balance" t-as="leave">
|
||||
<div class="mcs-list-row"><span t-esc="leave['name']"/><strong><t t-esc="leave['days']"/> days</strong></div>
|
||||
</t>
|
||||
</article>
|
||||
</section>
|
||||
<section class="mcs-wp-two">
|
||||
<article class="mcs-wp-card">
|
||||
<div class="mcs-card-head"><h2>Recent Support Tickets</h2><a href="/workplace/tickets">View all</a></div>
|
||||
<t t-call="mcs_corporate_website.ticket_table"/>
|
||||
</article>
|
||||
<article class="mcs-wp-card">
|
||||
<div class="mcs-card-head"><h2>Announcements</h2><a href="/workplace/announcements">View all</a></div>
|
||||
<t t-foreach="announcements" t-as="announcement">
|
||||
<div class="mcs-list-row mcs-list-row--stack"><strong t-esc="announcement.name"/><span t-field="announcement.date"/></div>
|
||||
</t>
|
||||
<t t-if="not announcements"><p>No announcements yet.</p></t>
|
||||
</article>
|
||||
</section>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_client_dashboard" name="Workplace Client Dashboard">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head">
|
||||
<div><p>Client dashboard</p><h1>Welcome, <t t-esc="wp_company_name"/></h1></div>
|
||||
<a class="mcs-wp-btn" href="/workplace/tickets/new">Create New Ticket</a>
|
||||
</section>
|
||||
<section class="mcs-wp-grid mcs-wp-grid--4">
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Open</t><t t-set="value" t-value="ticket_counts['open']"/><t t-set="hint">New tickets</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">In Progress</t><t t-set="value" t-value="ticket_counts['in_progress']"/><t t-set="hint">Being handled</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Waiting for You</t><t t-set="value" t-value="ticket_counts['waiting']"/><t t-set="hint">Needs reply</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Resolved</t><t t-set="value" t-value="ticket_counts['resolved']"/><t t-set="hint">Closed work</t></t>
|
||||
</section>
|
||||
<section class="mcs-wp-two">
|
||||
<article class="mcs-wp-card"><div class="mcs-card-head"><h2>Recent Tickets</h2><a href="/workplace/tickets">View all</a></div><t t-call="mcs_corporate_website.ticket_table"/></article>
|
||||
<article class="mcs-wp-card"><div class="mcs-card-head"><h2>Knowledge Base</h2><a href="/workplace/knowledge">Open</a></div><t t-call="mcs_corporate_website.article_list"/></article>
|
||||
</section>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="ticket_table" name="MCS Ticket Table">
|
||||
<div class="mcs-table-wrap">
|
||||
<table class="mcs-wp-table">
|
||||
<thead><tr><th>Ticket</th><th>Subject</th><th>Priority</th><th>Status</th><th>Assigned Team</th><th>Updated</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<t t-foreach="tickets" t-as="ticket">
|
||||
<tr>
|
||||
<td t-esc="ticket['number']"/>
|
||||
<td t-esc="ticket['subject']"/>
|
||||
<td><span t-attf-class="mcs-status mcs-priority-{{ ticket['priority_key'] }}" t-esc="ticket['priority']"/></td>
|
||||
<td><span t-attf-class="mcs-status mcs-ticket-{{ ticket['status_key'] }}" t-esc="ticket['status']"/></td>
|
||||
<td t-esc="ticket['assigned_team']"/>
|
||||
<td t-esc="ticket['updated_label']"/>
|
||||
<td><a t-att-href="'/workplace/tickets/%s' % ticket['id']">View</a></td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
<div t-if="not tickets" class="mcs-empty">No tickets found.</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="article_list" name="MCS Article List">
|
||||
<t t-foreach="articles" t-as="article">
|
||||
<div class="mcs-list-row mcs-list-row--stack"><strong t-esc="article.name"/><span t-esc="article.summary"/></div>
|
||||
</t>
|
||||
<t t-if="not articles"><p>No articles are published yet.</p></t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_attendance" name="Workplace Attendance">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Attendance</p><h1>Attendance</h1></div><span t-esc="wp_today.strftime('%A, %d %B %Y')"/></section>
|
||||
<section class="mcs-wp-grid mcs-wp-grid--4">
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Present Days</t><t t-set="value" t-value="len(attendance_records)"/><t t-set="hint">This month</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Late Days</t><t t-set="value">0</t><t t-set="hint">Requires policy setup</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Total Hours</t><t t-set="value" t-value="worked_hours"/><t t-set="hint">This month</t></t>
|
||||
<t t-call="mcs_corporate_website.mcs_metric_card"><t t-set="label">Overtime</t><t t-set="value">0h</t><t t-set="hint">Requires policy setup</t></t>
|
||||
</section>
|
||||
<article class="mcs-wp-card"><div class="mcs-card-head"><h2>Monthly Attendance</h2><a href="/web#menu_id=120&action=136">Open Odoo Attendance</a></div><div class="mcs-calendar-strip"><t t-foreach="attendance_records[:20]" t-as="record"><span><t t-esc="record.check_in.strftime('%d')"/> <small>Present</small></span></t></div><t t-call="mcs_corporate_website.attendance_table"/></article>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="attendance_table" name="MCS Attendance Table">
|
||||
<div class="mcs-table-wrap">
|
||||
<table class="mcs-wp-table">
|
||||
<thead><tr><th>Date</th><th>Check In</th><th>Check Out</th><th>Worked Hours</th><th>Status</th></tr></thead>
|
||||
<tbody><t t-foreach="attendance_records" t-as="record"><tr><td><span t-field="record.check_in"/></td><td><span t-field="record.check_in"/></td><td><span t-field="record.check_out"/></td><td t-esc="round(record.worked_hours, 2)"/><td><span class="mcs-status mcs-att-present">Present</span></td></tr></t></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="workplace_leave" name="Workplace Leave">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Leave & Time Off</p><h1>Leave & Time Off</h1></div><a class="mcs-wp-btn" href="/web#model=hr.leave&view_type=form">Request Leave</a></section>
|
||||
<section class="mcs-wp-grid mcs-wp-grid--3"><t t-foreach="leave_balance" t-as="leave"><article class="mcs-wp-card mcs-metric"><span t-esc="leave['name']"/><strong><t t-esc="leave['days']"/> days</strong><small>Available balance</small></article></t></section>
|
||||
<article class="mcs-wp-card"><div class="mcs-card-head"><h2>Request History</h2></div><div class="mcs-table-wrap"><table class="mcs-wp-table"><thead><tr><th>Leave Type</th><th>From</th><th>To</th><th>Status</th></tr></thead><tbody><t t-foreach="leaves" t-as="leave"><tr><td t-esc="leave.holiday_status_id.name"/><td t-esc="leave.request_date_from"/><td t-esc="leave.request_date_to"/><td><span class="mcs-status" t-esc="leave.state"/></td></tr></t></tbody></table></div></article>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_ticket_list" name="Workplace Tickets">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Support Tickets</p><h1>Support Tickets</h1></div><a class="mcs-wp-btn" href="/workplace/tickets/new">Create Ticket</a></section>
|
||||
<article class="mcs-wp-card"><t t-call="mcs_corporate_website.ticket_table"/></article>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_support_queue" name="Workplace Support Queue">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Support team queue</p><h1>Support Tickets</h1></div></section>
|
||||
<div class="mcs-filterbar"><span>All</span><span>My Tickets</span><span>Unassigned</span><span>Urgent</span><span>Overdue</span><span>Waiting for Customer</span></div>
|
||||
<article class="mcs-wp-card"><t t-call="mcs_corporate_website.ticket_table"/></article>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_ticket_new" name="Workplace Create Ticket">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Create Support Ticket</p><h1>Create Support Ticket</h1></div></section>
|
||||
<section class="mcs-wp-two">
|
||||
<form action="/support/submit" method="post" class="mcs-wp-card mcs-wp-form" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<label>Subject<input name="subject" required="required"/></label>
|
||||
<label>Category<select name="service_area"><option value="general">General</option><option value="website">Website</option><option value="erp">ERP / Odoo</option><option value="seo">SEO / Content</option><option value="marketing">Digital Marketing</option><option value="design">Design / Branding</option></select></label>
|
||||
<label>Priority<select name="priority"><option value="0">Normal</option><option value="1">High</option><option value="2">Urgent</option></select></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>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>
|
||||
<button class="mcs-wp-btn" type="submit">Create Ticket</button>
|
||||
</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>
|
||||
</section>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_ticket_detail" name="Workplace Ticket Detail">
|
||||
<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-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>
|
||||
<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>
|
||||
</section>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_knowledge" name="Workplace Knowledge">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Knowledge Base</p><h1>Search the knowledge base</h1></div><input class="mcs-search" placeholder="Search the knowledge base"/></section>
|
||||
<section class="mcs-wp-grid mcs-wp-grid--3"><t t-foreach="articles" t-as="article"><article class="mcs-wp-card"><span class="mcs-status" t-esc="article.category.replace('_', ' ').title()"/><h2 t-esc="article.name"/><p t-esc="article.summary"/><div t-out="article.body"/></article></t></section>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_announcements" name="Workplace Announcements">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Announcements</p><h1>Company Announcements</h1></div></section>
|
||||
<section class="mcs-wp-grid mcs-wp-grid--2"><t t-foreach="announcements" t-as="announcement"><article class="mcs-wp-card"><span class="mcs-status" t-esc="announcement.category.title()"/><h2 t-esc="announcement.name"/><small><t t-esc="announcement.author_id.name"/> - <span t-field="announcement.date"/></small><div t-out="announcement.content"/></article></t></section>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_profile" name="Workplace Profile">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Profile</p><h1 t-esc="wp_user.name"/></div><a class="mcs-wp-btn" href="/my/account">Edit Profile</a></section>
|
||||
<section class="mcs-wp-two">
|
||||
<article class="mcs-wp-card mcs-profile-card"><span class="mcs-avatar mcs-avatar--lg" t-esc="wp_user.name[:1]"/><h2 t-esc="wp_user.name"/><p t-esc="wp_role.replace('_', ' ').title()"/></article>
|
||||
<article class="mcs-wp-card"><h2>Information</h2><div class="mcs-list-row"><span>Email</span><strong t-esc="wp_user.email"/></div><div class="mcs-list-row"><span>Phone</span><strong t-esc="wp_user.phone or '-'"/></div><div class="mcs-list-row"><span>Designation</span><strong t-esc="wp_employee.job_title if wp_employee else '-'"/></div><div class="mcs-list-row"><span>Department</span><strong t-esc="wp_employee.department_id.name if wp_employee and wp_employee.department_id else '-'"/></div><div class="mcs-list-row"><span>Reporting Manager</span><strong t-esc="wp_employee.parent_id.name if wp_employee and wp_employee.parent_id else '-'"/></div></article>
|
||||
</section>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
Loading…
x
Reference in New Issue
Block a user