From 29bf042efb062ca647dac8a239c254d713ccf776 Mon Sep 17 00:00:00 2001 From: metatroncubeswdev Date: Mon, 31 Aug 2026 11:12:13 -0400 Subject: [PATCH] Add Metatroncube corporate website theme --- addons/mcs_corporate_website/__init__.py | 2 + addons/mcs_corporate_website/__manifest__.py | 33 ++ .../controllers/__init__.py | 1 + .../mcs_corporate_website/controllers/main.py | 132 ++++++++ .../data/sequence_data.xml | 9 + .../mcs_corporate_website/models/__init__.py | 1 + .../models/support_ticket.py | 97 ++++++ .../security/ir.model.access.csv | 3 + .../security/security.xml | 12 + .../static/src/scss/corporate_website.scss | 305 ++++++++++++++++++ .../views/support_ticket_views.xml | 88 +++++ .../views/website_menus.xml | 44 +++ .../views/website_templates.xml | 293 +++++++++++++++++ 13 files changed, 1020 insertions(+) create mode 100644 addons/mcs_corporate_website/__init__.py create mode 100644 addons/mcs_corporate_website/__manifest__.py create mode 100644 addons/mcs_corporate_website/controllers/__init__.py create mode 100644 addons/mcs_corporate_website/controllers/main.py create mode 100644 addons/mcs_corporate_website/data/sequence_data.xml create mode 100644 addons/mcs_corporate_website/models/__init__.py create mode 100644 addons/mcs_corporate_website/models/support_ticket.py create mode 100644 addons/mcs_corporate_website/security/ir.model.access.csv create mode 100644 addons/mcs_corporate_website/security/security.xml create mode 100644 addons/mcs_corporate_website/static/src/scss/corporate_website.scss create mode 100644 addons/mcs_corporate_website/views/support_ticket_views.xml create mode 100644 addons/mcs_corporate_website/views/website_menus.xml create mode 100644 addons/mcs_corporate_website/views/website_templates.xml diff --git a/addons/mcs_corporate_website/__init__.py b/addons/mcs_corporate_website/__init__.py new file mode 100644 index 0000000..91c5580 --- /dev/null +++ b/addons/mcs_corporate_website/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/addons/mcs_corporate_website/__manifest__.py b/addons/mcs_corporate_website/__manifest__.py new file mode 100644 index 0000000..8968610 --- /dev/null +++ b/addons/mcs_corporate_website/__manifest__.py @@ -0,0 +1,33 @@ +{ + "name": "Metatroncube Corporate Website", + "version": "17.0.1.0.0", + "category": "Website/Theme", + "summary": "Corporate website pages and customer support intake for Metatroncube", + "author": "Metatroncube Software Solutions", + "license": "LGPL-3", + "depends": [ + "crm", + "portal", + "project", + "website", + "website_crm", + "website_form_project", + "website_hr_recruitment", + "mcs_appointment_booking", + ], + "data": [ + "security/security.xml", + "security/ir.model.access.csv", + "data/sequence_data.xml", + "views/support_ticket_views.xml", + "views/website_templates.xml", + "views/website_menus.xml", + ], + "assets": { + "web.assets_frontend": [ + "mcs_corporate_website/static/src/scss/corporate_website.scss", + ], + }, + "installable": True, + "application": False, +} diff --git a/addons/mcs_corporate_website/controllers/__init__.py b/addons/mcs_corporate_website/controllers/__init__.py new file mode 100644 index 0000000..12a7e52 --- /dev/null +++ b/addons/mcs_corporate_website/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/addons/mcs_corporate_website/controllers/main.py b/addons/mcs_corporate_website/controllers/main.py new file mode 100644 index 0000000..fad12e6 --- /dev/null +++ b/addons/mcs_corporate_website/controllers/main.py @@ -0,0 +1,132 @@ +from odoo import fields, http +from odoo.http import request + + +class McsCorporateWebsite(http.Controller): + def _render(self, template, values=None): + values = values or {} + values.update( + { + "mcs_services": self._services(), + "mcs_contact": self._contact(), + } + ) + return request.render(template, values) + + def _contact(self): + return { + "phone": "+1-647-679-7651", + "email": "info@metatroncubesolutions.com", + "location": "Waterloo, Ontario Canada", + } + + def _services(self): + return [ + { + "key": "website", + "name": "Website Development", + "summary": "Fast, responsive, SEO-ready websites built around conversion and maintainability.", + "image": "https://metatroncubesolutions.com/assets/images/services/service/img-1.webp", + }, + { + "key": "mobile", + "name": "Mobile Application Development", + "summary": "Reliable mobile and web applications for operational workflows and customer products.", + "image": "https://metatroncubesolutions.com/assets/images/services/service/img-2.webp", + }, + { + "key": "design", + "name": "Graphic Designing", + "summary": "Brand systems, visual campaigns, and assets that make digital experiences consistent.", + "image": "https://metatroncubesolutions.com/assets/images/services/service/img-3.webp", + }, + { + "key": "ux", + "name": "UI / UX Designing", + "summary": "Product flows, prototypes, and interfaces designed for clarity and repeated use.", + "image": "https://metatroncubesolutions.com/assets/images/services/service/img-4.webp", + }, + { + "key": "seo", + "name": "SEO & Content Writing", + "summary": "Technical SEO, search content, and positioning that improves qualified discovery.", + "image": "https://metatroncubesolutions.com/assets/images/services/service/img-5.webp", + }, + { + "key": "marketing", + "name": "Digital Marketing", + "summary": "Campaign strategy, performance tracking, and content programs for measurable growth.", + "image": "https://metatroncubesolutions.com/assets/images/services/service/img-6.webp", + }, + { + "key": "erp", + "name": "ERP Development & Implementation", + "summary": "Custom ERP and Odoo implementation across finance, sales, HR, inventory, and delivery.", + "image": "https://metatroncubesolutions.com/assets/images/services/erp/card-img.webp", + }, + ] + + @http.route(["/", "/home"], type="http", auth="public", website=True) + def home(self, **kwargs): + return self._render("mcs_corporate_website.page_home") + + @http.route(["/services"], type="http", auth="public", website=True) + def services(self, **kwargs): + return self._render("mcs_corporate_website.page_services") + + @http.route(["/about"], type="http", auth="public", website=True) + def about(self, **kwargs): + return self._render("mcs_corporate_website.page_about") + + @http.route(["/portfolio"], type="http", auth="public", website=True) + def portfolio(self, **kwargs): + return self._render("mcs_corporate_website.page_portfolio") + + @http.route(["/support"], type="http", auth="public", website=True) + def support(self, **kwargs): + return self._render("mcs_corporate_website.page_support") + + @http.route(["/contact"], type="http", auth="public", website=True) + def contact(self, **kwargs): + return self._render("mcs_corporate_website.page_contact") + + @http.route(["/privacy"], type="http", auth="public", website=True) + def privacy(self, **kwargs): + return self._render("mcs_corporate_website.page_privacy") + + @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( + { + "subject": (post.get("subject") or "").strip(), + "customer_name": (post.get("customer_name") or "").strip(), + "customer_email": (post.get("customer_email") or "").strip(), + "customer_phone": (post.get("customer_phone") or "").strip(), + "service_area": post.get("service_area") or "general", + "priority": post.get("priority") or "0", + "description": (post.get("description") or "").strip(), + } + ) + return request.redirect("/support/thanks/%s" % ticket.access_token) + + @http.route(["/support/thanks/"], type="http", auth="public", website=True) + def support_thanks(self, token, **kwargs): + ticket = request.env["mcs.support.ticket"].sudo().search([("access_token", "=", token)], limit=1) + if not ticket: + return request.not_found() + return self._render("mcs_corporate_website.page_support_thanks", {"ticket": ticket}) + + @http.route(["/contact/submit"], type="http", auth="public", website=True, methods=["POST"]) + def submit_contact(self, **post): + request.env["crm.lead"].sudo().create( + { + "name": (post.get("subject") or "Website enquiry").strip(), + "contact_name": (post.get("customer_name") or "").strip(), + "email_from": (post.get("customer_email") or "").strip(), + "phone": (post.get("customer_phone") or "").strip(), + "description": (post.get("message") or "").strip(), + "type": "lead", + "date_open": fields.Datetime.now(), + } + ) + return self._render("mcs_corporate_website.page_contact_thanks") diff --git a/addons/mcs_corporate_website/data/sequence_data.xml b/addons/mcs_corporate_website/data/sequence_data.xml new file mode 100644 index 0000000..af8046e --- /dev/null +++ b/addons/mcs_corporate_website/data/sequence_data.xml @@ -0,0 +1,9 @@ + + + MCS Support Ticket + mcs.support.ticket + MCS-SUP- + 5 + + + diff --git a/addons/mcs_corporate_website/models/__init__.py b/addons/mcs_corporate_website/models/__init__.py new file mode 100644 index 0000000..0fcb451 --- /dev/null +++ b/addons/mcs_corporate_website/models/__init__.py @@ -0,0 +1 @@ +from . import support_ticket diff --git a/addons/mcs_corporate_website/models/support_ticket.py b/addons/mcs_corporate_website/models/support_ticket.py new file mode 100644 index 0000000..c1c7469 --- /dev/null +++ b/addons/mcs_corporate_website/models/support_ticket.py @@ -0,0 +1,97 @@ +from uuid import uuid4 + +from odoo import api, fields, models + + +class McsSupportTicket(models.Model): + _name = "mcs.support.ticket" + _description = "Metatroncube Support Ticket" + _inherit = ["mail.thread", "mail.activity.mixin"] + _order = "priority desc, create_date desc" + + name = fields.Char(default="New", readonly=True, copy=False, tracking=True) + subject = fields.Char(required=True, tracking=True) + customer_name = fields.Char(required=True) + customer_email = fields.Char(required=True) + customer_phone = fields.Char() + partner_id = fields.Many2one("res.partner", string="Customer", tracking=True) + company_id = fields.Many2one( + "res.company", + default=lambda self: self.env.company, + required=True, + tracking=True, + ) + service_area = fields.Selection( + [ + ("website", "Website Development"), + ("mobile", "Mobile Application"), + ("erp", "ERP / Odoo"), + ("seo", "SEO / Content"), + ("marketing", "Digital Marketing"), + ("design", "Design / Branding"), + ("general", "General Support"), + ], + default="general", + required=True, + tracking=True, + ) + priority = fields.Selection( + [ + ("0", "Normal"), + ("1", "High"), + ("2", "Urgent"), + ], + default="0", + tracking=True, + ) + state = fields.Selection( + [ + ("new", "New"), + ("in_progress", "In Progress"), + ("waiting", "Waiting on Customer"), + ("resolved", "Resolved"), + ("closed", "Closed"), + ], + default="new", + required=True, + tracking=True, + ) + description = fields.Text(required=True) + access_token = fields.Char(default=lambda self: uuid4().hex, readonly=True, copy=False) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get("name", "New") == "New": + vals["name"] = self.env["ir.sequence"].next_by_code("mcs.support.ticket") or "New" + if not vals.get("partner_id") and vals.get("customer_email"): + vals["partner_id"] = self._find_or_create_partner(vals) + return super().create(vals_list) + + @api.model + def _find_or_create_partner(self, vals): + email = (vals.get("customer_email") or "").strip() + partner = self.env["res.partner"].sudo().search([("email", "=", email)], limit=1) + if partner: + return partner.id + partner = self.env["res.partner"].sudo().create( + { + "name": vals.get("customer_name") or email, + "email": email, + "phone": vals.get("customer_phone"), + "company_id": vals.get("company_id") or self.env.company.id, + } + ) + return partner.id + + def action_start(self): + self.write({"state": "in_progress"}) + + def action_waiting(self): + self.write({"state": "waiting"}) + + def action_resolve(self): + self.write({"state": "resolved"}) + + def action_close(self): + self.write({"state": "closed"}) diff --git a/addons/mcs_corporate_website/security/ir.model.access.csv b/addons/mcs_corporate_website/security/ir.model.access.csv new file mode 100644 index 0000000..756e9d3 --- /dev/null +++ b/addons/mcs_corporate_website/security/ir.model.access.csv @@ -0,0 +1,3 @@ +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 diff --git a/addons/mcs_corporate_website/security/security.xml b/addons/mcs_corporate_website/security/security.xml new file mode 100644 index 0000000..82dafb0 --- /dev/null +++ b/addons/mcs_corporate_website/security/security.xml @@ -0,0 +1,12 @@ + + + Website Support User + + + + + Website Support Manager + + + + diff --git a/addons/mcs_corporate_website/static/src/scss/corporate_website.scss b/addons/mcs_corporate_website/static/src/scss/corporate_website.scss new file mode 100644 index 0000000..3503470 --- /dev/null +++ b/addons/mcs_corporate_website/static/src/scss/corporate_website.scss @@ -0,0 +1,305 @@ +.mcs-site { + --mcs-ink: #101828; + --mcs-muted: #5b6474; + --mcs-line: #d8dee8; + --mcs-blue: #155eef; + --mcs-blue-dark: #0f2f6f; + --mcs-surface: #f5f8fc; + + color: var(--mcs-ink); + background: #ffffff; + + .container { + max-width: 1180px; + } + + h1, + h2, + h3 { + letter-spacing: 0; + } + + h1 { + font-size: clamp(2.4rem, 4rem, 4rem); + line-height: 1.02; + font-weight: 750; + } + + h2 { + font-size: 2rem; + line-height: 1.18; + font-weight: 720; + } + + h3 { + font-size: 1.15rem; + line-height: 1.3; + font-weight: 700; + } + + p { + color: var(--mcs-muted); + line-height: 1.7; + } + + .btn { + border-radius: 6px; + font-weight: 700; + padding: 0.75rem 1.05rem; + } +} + +.mcs-hero { + position: relative; + min-height: 680px; + display: flex; + align-items: center; + overflow: hidden; + color: #ffffff; + background: #07111f; + + &::before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(90deg, rgba(7, 17, 31, 0.94), rgba(7, 17, 31, 0.72), rgba(7, 17, 31, 0.18)); + z-index: 1; + } +} + +.mcs-hero__media { + position: absolute; + inset: 0; + + img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center right; + opacity: 0.86; + } +} + +.mcs-hero__content { + position: relative; + z-index: 2; + max-width: 720px; + padding: 96px 0; + + .mcs-lead { + max-width: 650px; + margin: 24px 0 0; + color: rgba(255, 255, 255, 0.82); + font-size: 1.18rem; + } +} + +.mcs-eyebrow { + margin: 0 0 12px; + color: var(--mcs-blue); + font-size: 0.78rem; + font-weight: 800; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.mcs-actions { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 28px; +} + +.mcs-band { + padding: 76px 0; +} + +.mcs-band--quiet { + background: var(--mcs-surface); +} + +.mcs-section-head { + display: flex; + justify-content: space-between; + align-items: end; + gap: 24px; + margin-bottom: 28px; + + h2 { + max-width: 760px; + margin: 0; + } +} + +.mcs-service-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 18px; +} + +.mcs-service-grid--wide { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.mcs-service-card, +.mcs-portfolio-grid article, +.mcs-form, +.mcs-help-panel, +.mcs-metrics div { + border: 1px solid var(--mcs-line); + border-radius: 8px; + background: #ffffff; +} + +.mcs-service-card { + overflow: hidden; + + img { + width: 100%; + aspect-ratio: 16 / 9; + object-fit: cover; + background: var(--mcs-surface); + } + + div { + padding: 22px; + } + + p { + margin-bottom: 0; + } + + a { + display: inline-flex; + margin-top: 12px; + font-weight: 700; + text-decoration: none; + } +} + +.mcs-split { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(320px, 0.9fr); + gap: 42px; + align-items: center; +} + +.mcs-metrics { + display: grid; + gap: 12px; + + div { + padding: 20px; + } + + strong { + display: block; + font-size: 1.2rem; + } + + span { + display: block; + color: var(--mcs-muted); + margin-top: 4px; + } +} + +.mcs-rounded-image { + width: 100%; + border-radius: 8px; + object-fit: cover; +} + +.mcs-cta { + background: #101828; + color: #ffffff; + + h2 { + max-width: 760px; + margin: 0; + } +} + +.mcs-page-head { + padding: 96px 0 56px; + background: #101828; + color: #ffffff; + + p:not(.mcs-eyebrow) { + max-width: 760px; + color: rgba(255, 255, 255, 0.78); + font-size: 1.12rem; + } +} + +.mcs-portfolio-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; + + article { + padding: 26px; + } +} + +.mcs-form-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 360px; + gap: 24px; + align-items: start; +} + +.mcs-form, +.mcs-help-panel { + padding: 26px; +} + +.mcs-form { + label { + font-weight: 700; + color: var(--mcs-ink); + } + + .form-control, + .form-select { + border-radius: 6px; + border-color: var(--mcs-line); + min-height: 44px; + } + + textarea.form-control { + min-height: 160px; + } +} + +.mcs-help-panel { + position: sticky; + top: 24px; +} + +@media (max-width: 991.98px) { + .mcs-site h1 { + font-size: 2.5rem; + } + + .mcs-hero { + min-height: 620px; + } + + .mcs-service-grid, + .mcs-service-grid--wide, + .mcs-split, + .mcs-portfolio-grid, + .mcs-form-layout { + grid-template-columns: 1fr; + } + + .mcs-section-head { + align-items: start; + flex-direction: column; + } + + .mcs-help-panel { + position: static; + } +} diff --git a/addons/mcs_corporate_website/views/support_ticket_views.xml b/addons/mcs_corporate_website/views/support_ticket_views.xml new file mode 100644 index 0000000..3395732 --- /dev/null +++ b/addons/mcs_corporate_website/views/support_ticket_views.xml @@ -0,0 +1,88 @@ + + + mcs.support.ticket.tree + mcs.support.ticket + + + + + + + + + + + + + + + + mcs.support.ticket.form + mcs.support.ticket + +
+
+
+ +
+

+

+
+ + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + mcs.support.ticket.search + mcs.support.ticket + + + + + + + + + + + + + + + + + + Support Tickets + mcs.support.ticket + tree,form + {'search_default_open': 1} + + + + +
diff --git a/addons/mcs_corporate_website/views/website_menus.xml b/addons/mcs_corporate_website/views/website_menus.xml new file mode 100644 index 0000000..5221e92 --- /dev/null +++ b/addons/mcs_corporate_website/views/website_menus.xml @@ -0,0 +1,44 @@ + + + Home + / + + 5 + + + Services + /services + + 10 + + + Portfolio + /portfolio + + 20 + + + About + /about + + 30 + + + Careers + /jobs + + 40 + + + Support + /support + + 50 + + + Contact + /contact + + 60 + + diff --git a/addons/mcs_corporate_website/views/website_templates.xml b/addons/mcs_corporate_website/views/website_templates.xml new file mode 100644 index 0000000..a08564f --- /dev/null +++ b/addons/mcs_corporate_website/views/website_templates.xml @@ -0,0 +1,293 @@ + + + + + + + + + + + +