Public release from ruodoo-project: 19.0 - 2026-08-30 21:16:35 UTC
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import {Component, useState} from "@odoo/owl";
|
||||
import {Dropdown} from "@web/core/dropdown/dropdown";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {useDiscussSystray} from "@mail/utils/common/hooks";
|
||||
import {useDropdownState} from "@web/core/dropdown/dropdown_hooks";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
|
||||
export class TierReviewMenu extends Component {
|
||||
static components = {Dropdown};
|
||||
static props = [];
|
||||
static template = "base_tier_validation.TierReviewMenu";
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.discussSystray = useDiscussSystray();
|
||||
this.orm = useService("orm");
|
||||
this.store = useState(useService("mail.store"));
|
||||
this.action = useService("action");
|
||||
this.dropdown = useDropdownState();
|
||||
this.fetchSystrayReviewer();
|
||||
}
|
||||
|
||||
async fetchSystrayReviewer() {
|
||||
const groups = await this.orm.call("res.users", "review_user_count");
|
||||
let total = 0;
|
||||
for (const group of groups) {
|
||||
total += group.pending_count || 0;
|
||||
}
|
||||
this.store.tierReviewCounter = total;
|
||||
this.store.tierReviewGroups = groups;
|
||||
}
|
||||
|
||||
availableViews() {
|
||||
return [
|
||||
[false, "kanban"],
|
||||
[false, "list"],
|
||||
[false, "form"],
|
||||
[false, "activity"],
|
||||
];
|
||||
}
|
||||
|
||||
openReviewGroup(group) {
|
||||
this.dropdown.close();
|
||||
const context = {};
|
||||
const domain = [["can_review", "=", true]];
|
||||
if (group.active_field) {
|
||||
domain.push(["active", "in", [true, false]]);
|
||||
}
|
||||
const views = this.availableViews();
|
||||
|
||||
this.action.doAction(
|
||||
{
|
||||
context,
|
||||
domain,
|
||||
name: group.name,
|
||||
res_model: group.model,
|
||||
search_view_id: [false],
|
||||
type: "ir.actions.act_window",
|
||||
views,
|
||||
},
|
||||
{
|
||||
clearBreadcrumbs: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const systrayItem = {
|
||||
Component: TierReviewMenu,
|
||||
};
|
||||
|
||||
registry
|
||||
.category("systray")
|
||||
.add("base_tier_validation.ReviewerMenu", systrayItem, {sequence: 99});
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="base_tier_validation.TierReviewMenu">
|
||||
<Dropdown
|
||||
position="'bottom-end'"
|
||||
state="dropdown"
|
||||
beforeOpen.bind="fetchSystrayReviewer"
|
||||
menuClass="discussSystray.menuClass"
|
||||
>
|
||||
<button>
|
||||
<i
|
||||
class="fa fa-lg fa-pencil-square-o"
|
||||
role="img"
|
||||
aria-label="Reviews"
|
||||
/>
|
||||
<span
|
||||
t-if="store.tierReviewCounter"
|
||||
class="o-mail-ActivityMenu-counter badge rounded-pill"
|
||||
>
|
||||
<t t-out="store.tierReviewCounter" />
|
||||
</span>
|
||||
</button>
|
||||
<t t-set-slot="content">
|
||||
<div t-att-class="`${discussSystray.contentClass} o-mail-ActivityMenu`">
|
||||
<div
|
||||
t-if="store.tierReviewCounter === 0"
|
||||
class="o-mail-ActivityMenu-empty align-items-center text-muted p-2 opacity-50 d-flex justify-content-center"
|
||||
>
|
||||
<span>No reviews to do.</span>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex flex-column list-group-flush"
|
||||
name="reviewGroups"
|
||||
>
|
||||
<t
|
||||
t-foreach="store.tierReviewGroups"
|
||||
t-as="review"
|
||||
t-key="review_index"
|
||||
name="reviewGroupLoop"
|
||||
>
|
||||
<div
|
||||
class="o-mail-ActivityGroup list-group-item list-group-item-action d-flex p-2 cursor-pointer"
|
||||
t-att-data-model_name="review.model"
|
||||
t-on-click="() => this.openReviewGroup(review)"
|
||||
>
|
||||
<img alt="Review" t-att-src="review.icon" />
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div
|
||||
class="d-flex px-2"
|
||||
name="reviewTitle"
|
||||
t-out="review.name"
|
||||
/>
|
||||
<div class="d-flex">
|
||||
<span
|
||||
t-attf-class="#{review.pending_count ? 'btn btn-link' : 'text-muted'} py-0 px-2 text-truncate"
|
||||
t-on-click.stop="() => this.openReviewGroup(review)"
|
||||
>
|
||||
<t t-out="review.pending_count" /> Pending
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</Dropdown>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,53 @@
|
||||
import {Component, useState} from "@odoo/owl";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
|
||||
export class ReviewsTable extends Component {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.orm = useService("orm");
|
||||
this.state = useState({
|
||||
collapse: false,
|
||||
});
|
||||
}
|
||||
|
||||
_getReviewData() {
|
||||
const records = this.props.record.data.review_ids.records;
|
||||
return records.map((record) => record.data);
|
||||
}
|
||||
|
||||
onToggleCollapse(ev) {
|
||||
const panelHeading = ev.currentTarget.closest(".panel-heading");
|
||||
const collapseDiv = panelHeading.nextElementSibling.matches("div#collapse1")
|
||||
? panelHeading.nextElementSibling
|
||||
: null;
|
||||
if (!collapseDiv) return;
|
||||
if (this.state.collapse) {
|
||||
collapseDiv.style.display = "none";
|
||||
} else {
|
||||
collapseDiv.style.display = "block";
|
||||
}
|
||||
this.state.collapse = !this.state.collapse;
|
||||
}
|
||||
}
|
||||
|
||||
ReviewsTable.template = "base_tier_validation.Collapse";
|
||||
|
||||
export const reviewsTableComponent = {
|
||||
component: ReviewsTable,
|
||||
supportedTypes: ["one2many"],
|
||||
relatedFields: [
|
||||
{name: "id", type: "integer"},
|
||||
{name: "sequence", type: "integer"},
|
||||
{name: "name", type: "char"},
|
||||
{name: "display_status", type: "char"},
|
||||
{name: "todo_by", type: "char"},
|
||||
{name: "status", type: "char"},
|
||||
{name: "reviewed_formated_date", type: "char"},
|
||||
{name: "comment", type: "char"},
|
||||
{name: "requested_by", type: "many2one", relation: "partner"},
|
||||
{name: "done_by", type: "many2one", relation: "partner"},
|
||||
],
|
||||
};
|
||||
|
||||
registry.category("fields").add("form.tier_validation", reviewsTableComponent);
|
||||
@@ -0,0 +1,41 @@
|
||||
ul.o_review {
|
||||
min-width: 600px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.panel-group {
|
||||
min-height: auto !important;
|
||||
margin-top: -6px !important;
|
||||
padding: 16px 16px 8px 16px !important;
|
||||
}
|
||||
|
||||
.panel-heading {
|
||||
background-color: initial !important;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.panel-title > a,
|
||||
.panel-title > a:active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.panel-heading a:before {
|
||||
font-family: FontAwesome;
|
||||
content: "\f0d7";
|
||||
float: right;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.panel-heading.active a:before {
|
||||
-webkit-transform: rotate(180deg);
|
||||
-moz-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates>
|
||||
<t t-name="base_tier_validation.Collapse">
|
||||
<div class="o_form_sheet panel-group">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a
|
||||
class="o_info_btn"
|
||||
data-toggle="state.collapse"
|
||||
href="#"
|
||||
data-target="#collapse1"
|
||||
t-on-click="onToggleCollapse"
|
||||
>
|
||||
Reviews
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapse1" class="panel-collapse collapse active">
|
||||
<div class="panel-body o_review">
|
||||
<table class="oe_mt32 table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
name="th_sequence"
|
||||
class="text-center"
|
||||
>Sequence</th>
|
||||
<th
|
||||
name="th_requested_by"
|
||||
class="text-start"
|
||||
>Requested by</th>
|
||||
<th name="th_name" class="text-end">Description</th>
|
||||
<th
|
||||
name="th_display_status"
|
||||
class="text-end"
|
||||
>Status</th>
|
||||
<th name="th_todo_by" class="text-end">Todo by</th>
|
||||
<th name="th_done_by" class="text-end">Done by</th>
|
||||
<th
|
||||
name="th_reviewed_date"
|
||||
class="text-end"
|
||||
>Validation Date</th>
|
||||
<th name="th_comment" class="text-end">Comment</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="sale_tbody">
|
||||
<t
|
||||
t-foreach="_getReviewData()"
|
||||
t-as="review"
|
||||
t-key="review.sequence"
|
||||
>
|
||||
<t
|
||||
t-if="review.status == 'waiting'"
|
||||
t-set="status_class"
|
||||
t-value="'text-muted'"
|
||||
/>
|
||||
<t
|
||||
t-if="review.status == 'pending'"
|
||||
t-set="status_class"
|
||||
t-value=""
|
||||
/>
|
||||
<t
|
||||
t-if="review.status == 'approved'"
|
||||
t-set="status_class"
|
||||
t-value="'alert alert-success'"
|
||||
/>
|
||||
<t
|
||||
t-if="review.status == 'rejected'"
|
||||
t-set="status_class"
|
||||
t-value="'alert alert-danger'"
|
||||
/>
|
||||
<tr t-att-class="status_class">
|
||||
<td name="td_sequence" class="text-center">
|
||||
<span t-out="review.sequence" />
|
||||
</td>
|
||||
<td name="td_requested_by" class="text-start">
|
||||
<span t-out="review.requested_by[1]" />
|
||||
</td>
|
||||
<td name="td_name" class="text-end">
|
||||
<span t-out="review.name" />
|
||||
</td>
|
||||
<td name="td_display_status" class="text-end">
|
||||
<span t-out="review.display_status" />
|
||||
</td>
|
||||
<td name="td_todo_by" class="text-end">
|
||||
<span t-out="review.todo_by" />
|
||||
</td>
|
||||
<td name="td_done_by" class="text-end">
|
||||
<span t-out="review.done_by[1]" />
|
||||
</td>
|
||||
<td name="td_reviewed_date" class="text-end">
|
||||
<t t-if="review.reviewed_formated_date">
|
||||
<span
|
||||
t-out="review.reviewed_formated_date"
|
||||
/>
|
||||
</t>
|
||||
</td>
|
||||
<td name="td_comment" class="text-start">
|
||||
<t t-if="review.comment">
|
||||
<span t-out="review.comment" />
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,33 @@
|
||||
import {reactive} from "@odoo/owl";
|
||||
import {registry} from "@web/core/registry";
|
||||
|
||||
export class TierReviewService {
|
||||
constructor(env, services) {
|
||||
this.env = env;
|
||||
this.store = services["mail.store"];
|
||||
this.busService = services.bus_service;
|
||||
}
|
||||
setup() {
|
||||
this.busService.subscribe("base.tier.validation/updated", (payload) => {
|
||||
if (payload.review_created) {
|
||||
this.store.tierReviewCounter++;
|
||||
}
|
||||
if (payload.review_deleted) {
|
||||
this.store.tierReviewCounter--;
|
||||
}
|
||||
});
|
||||
this.busService.start();
|
||||
}
|
||||
}
|
||||
|
||||
export const tierReviewService = {
|
||||
dependencies: ["bus_service", "mail.store"],
|
||||
|
||||
start(env, services) {
|
||||
const tier_review_service = reactive(new TierReviewService(env, services));
|
||||
tier_review_service.setup();
|
||||
return tier_review_service;
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("tierReviewService", tierReviewService);
|
||||
Reference in New Issue
Block a user