/** @odoo-module **/ // Fusion Repairs dashboard - OWL client action. // Uses standalone rpc() from @web/core/network/rpc per project rule #3 // and useService("action") to navigate to backend act_window actions. import { Component, useState, onWillStart } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { rpc } from "@web/core/network/rpc"; import { useService } from "@web/core/utils/hooks"; import { _t } from "@web/core/l10n/translation"; export class FusionRepairsDashboard extends Component { static template = "fusion_repairs.Dashboard"; static props = ["*"]; setup() { this.action = useService("action"); this.notification = useService("notification"); this.state = useState({ loading: true, stats: {}, urgency_breakdown: [], source_breakdown: [], recent: [], upcoming: [], portals: {}, failures_by_product: [], failures_by_symptom: [], margin_summary: {}, }); onWillStart(async () => { await this._loadData(); }); } async _loadData() { try { const data = await rpc("/web/dataset/call_kw", { model: "fusion.repair.dashboard", method: "get_dashboard_data", args: [], kwargs: {}, }); this.state.stats = data.stats || {}; this.state.urgency_breakdown = data.urgency_breakdown || []; this.state.source_breakdown = data.source_breakdown || []; this.state.recent = data.recent || []; this.state.upcoming = data.upcoming || []; this.state.portals = data.portals || {}; this.state.failures_by_product = data.failures_by_product || []; this.state.failures_by_symptom = data.failures_by_symptom || []; this.state.margin_summary = data.margin_summary || {}; } catch (e) { this.notification.add(_t("Could not load dashboard data."), { type: "danger", }); } finally { this.state.loading = false; } } async refresh() { this.state.loading = true; await this._loadData(); } openAction(xmlId, extraContext) { return this.action.doAction(xmlId, { additionalContext: extraContext || {}, }); } openWizard() { return this.action.doAction("fusion_repairs.action_open_repair_intake_wizard"); } openRepair(repairId) { return this.action.doAction({ type: "ir.actions.act_window", res_model: "repair.order", res_id: repairId, views: [[false, "form"]], target: "current", }); } openContract(contractId) { return this.action.doAction({ type: "ir.actions.act_window", res_model: "fusion.repair.maintenance.contract", res_id: contractId, views: [[false, "form"]], target: "current", }); } openUrl(url) { if (url) { window.open(url, "_blank", "noopener"); } } async copyUrl(url) { if (!url) return; try { await navigator.clipboard.writeText(url); this.notification.add(_t("Copied to clipboard."), { type: "success" }); } catch (e) { this.notification.add(_t("Could not copy URL. Select and copy manually."), { type: "warning", }); } } formatDate(value) { if (!value) return ""; return value.slice(0, 10); } formatMoney(value) { const v = Number(value || 0); return v.toLocaleString("en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: 0, }); } formatPercent(value) { const v = Number(value || 0); return `${v.toFixed(1)}%`; } urgencyPillClass(urgency) { if (urgency === "safety") return "fr-pill fr-pill-safety"; if (urgency === "urgent") return "fr-pill fr-pill-urgent"; return "fr-pill fr-pill-normal"; } urgencyLabel(urgency) { const map = { safety: "Safety", urgent: "Urgent", normal: "Normal" }; return map[urgency] || "Normal"; } } registry .category("actions") .add("fusion_repairs.dashboard", FusionRepairsDashboard);