Reuse the existing Pay Period setting (Frequency + Anchor) as the single source of truth via a shared pure helper (models/pay_period.py); fusion.clock.report delegates to it. Add Current/Previous/Next Pay Period filters to the attendance search view (search-method computed booleans on hr.attendance), a Bi-Weekly Period picker wizard (pick start -> auto +2 weeks, editable; Apply opens the filtered list) reachable from an Attendance menu item and a dashboard tile. Window follows the configured frequency; TZ-correct via local-day boundaries. Bump 3.14.4 -> 3.15.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
95 lines
3.6 KiB
JavaScript
95 lines
3.6 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { Component, useState, onWillStart } from "@odoo/owl";
|
|
import { rpc } from "@web/core/network/rpc";
|
|
import { registry } from "@web/core/registry";
|
|
import { useService } from "@web/core/utils/hooks";
|
|
|
|
export class FusionClockDashboard extends Component {
|
|
static template = "fusion_clock.Dashboard";
|
|
static props = { "*": true };
|
|
|
|
setup() {
|
|
this.action = useService("action");
|
|
this.state = useState({
|
|
loading: true,
|
|
error: "",
|
|
role: "employee",
|
|
personal: {},
|
|
team: null,
|
|
});
|
|
onWillStart(async () => {
|
|
await this._fetchData();
|
|
});
|
|
}
|
|
|
|
async _fetchData() {
|
|
this.state.loading = true;
|
|
this.state.error = "";
|
|
try {
|
|
const data = await rpc("/fusion_clock/dashboard_data", {});
|
|
if (data.error) {
|
|
this.state.error = data.error;
|
|
} else {
|
|
this.state.role = data.role;
|
|
this.state.personal = data.personal;
|
|
this.state.team = data.team;
|
|
}
|
|
} catch (e) {
|
|
this.state.error = "Failed to load dashboard data.";
|
|
}
|
|
this.state.loading = false;
|
|
}
|
|
|
|
// ---- display helpers ----
|
|
get greeting() {
|
|
const h = new Date().getHours();
|
|
if (h < 12) return "Good morning";
|
|
if (h < 17) return "Good afternoon";
|
|
return "Good evening";
|
|
}
|
|
get todayLabel() {
|
|
return new Date().toLocaleDateString(undefined, {
|
|
weekday: "long", month: "long", day: "numeric",
|
|
});
|
|
}
|
|
sourceLabel(source) {
|
|
return { schedule: "Posted schedule", shift: "Recurring shift", none: "—" }[source] || "—";
|
|
}
|
|
initials(name) {
|
|
return (name || "")
|
|
.split(" ").filter(Boolean).slice(0, 2)
|
|
.map((p) => p[0].toUpperCase()).join("");
|
|
}
|
|
fmtDate(s) {
|
|
if (!s) return "";
|
|
const d = new Date(s.replace(" ", "T") + "Z");
|
|
return d.toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
}
|
|
fmtTime(s) {
|
|
if (!s) return "";
|
|
const d = new Date(s.replace(" ", "T") + "Z");
|
|
return d.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });
|
|
}
|
|
|
|
// ---- actions ----
|
|
onRefresh() { return this._fetchData(); }
|
|
onOpenClock() { this.action.doAction({ type: "ir.actions.act_url", url: "/my/clock", target: "self" }); }
|
|
onViewTimesheets() { this.action.doAction({ type: "ir.actions.act_url", url: "/my/clock/timesheets", target: "self" }); }
|
|
onViewAttendances() {
|
|
// hr_attendance's action is gantt-first, and the native gantt timeline
|
|
// renders collapsed until a manual resize. Land on the list instead —
|
|
// the better "all attendances" destination (sort/filter/export); the
|
|
// gantt is still reachable from the view switcher.
|
|
this.action.doAction("hr_attendance.hr_attendance_action", { viewType: "list" });
|
|
}
|
|
onViewCorrections() { this.action.doAction("fusion_clock.action_fusion_clock_correction"); }
|
|
onViewActivityLogs() { this.action.doAction("fusion_clock.action_fusion_clock_activity_log"); }
|
|
onViewPenalties() { this.action.doAction("fusion_clock.action_fusion_clock_penalty"); }
|
|
onViewShiftPlanner() { this.action.doAction("fusion_clock.action_fusion_clock_shift_planner"); }
|
|
onViewBiweekly() { this.action.doAction("fusion_clock.action_fusion_clock_period_picker"); }
|
|
onViewReports() { this.action.doAction("fusion_clock.action_fusion_clock_report"); }
|
|
}
|
|
|
|
registry.category("actions").add("fusion_clock.Dashboard", FusionClockDashboard);
|