feat(fusion_claims): add OWL countdown widget for posting deadline

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-21 03:53:18 -04:00
parent 1420a5c445
commit 07f9bcf79b
3 changed files with 73 additions and 0 deletions

View File

@@ -178,6 +178,9 @@
# Dashboard: tokens MUST load before dashboard layout
'fusion_claims/static/src/scss/_fc_dashboard_tokens.scss',
'fusion_claims/static/src/scss/fc_dashboard.scss',
# Dashboard OWL countdown widget
'fusion_claims/static/src/js/fc_posting_countdown.js',
'fusion_claims/static/src/xml/fc_posting_countdown.xml',
],
'web.assets_web_dark': [
# Dark bundle recompiles the same SCSS with the dark

View File

@@ -0,0 +1,63 @@
/** @odoo-module **/
// Fusion Claims — Posting Period Countdown
// Reads the submission_deadline_dt field, computes "Nd Xh to cutoff" client-side,
// re-renders every 60 seconds, swaps colour class as the deadline approaches.
// Copyright 2026 Nexa Systems Inc.
// License OPL-1
import { Component, useState, onWillDestroy } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
class FcPostingCountdown extends Component {
static template = "fusion_claims.PostingCountdown";
static props = { ...standardFieldProps };
setup() {
this.state = useState({ text: "", level: "info" });
this._render();
this._timer = setInterval(() => this._render(), 60_000);
onWillDestroy(() => {
if (this._timer) {
clearInterval(this._timer);
this._timer = null;
}
});
}
_render() {
const deadline = this.props.record.data[this.props.name];
if (!deadline) {
this.state.text = "";
this.state.level = "muted";
return;
}
// Odoo provides a luxon DateTime for Datetime fields
const now = luxon.DateTime.now();
const diff = deadline.diff(now, ["days", "hours", "minutes"]).toObject();
if (diff.days < 0 || (diff.days === 0 && diff.hours < 0)) {
this.state.text = "Cutoff passed";
this.state.level = "muted";
return;
}
const days = Math.floor(diff.days);
const hours = Math.floor(diff.hours);
if (days < 1) {
this.state.text = `${hours}h to cutoff`;
this.state.level = "danger";
} else if (days < 3) {
this.state.text = `${days}d ${hours}h to cutoff`;
this.state.level = "warning";
} else {
this.state.text = `${days} days to cutoff`;
this.state.level = "info";
}
}
}
registry.category("fields").add("fc_posting_countdown", {
component: FcPostingCountdown,
});

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="fusion_claims.PostingCountdown">
<span t-attf-class="o_fc_countdown o_fc_countdown--{{state.level}}"
t-esc="state.text"/>
</t>
</templates>