Skip to content

Instantly share code, notes, and snippets.

@JVegaB
Last active July 7, 2023 20:53
Show Gist options
  • Save JVegaB/df41386c1660b7b1d8aadffc8c14d9b7 to your computer and use it in GitHub Desktop.
Save JVegaB/df41386c1660b7b1d8aadffc8c14d9b7 to your computer and use it in GitHub Desktop.

Field JS

odoo.define('academy.field', (require) => {

    const registry = require('web.field_registry');
    const Widget = require('web.AbstractField');

    const ShareCount = Widget.extend({
        xmlDependencies: ['/academy/static/src/xml/field.xml'],
        template: 'academy.field',
        events: {
            'click .js_reset_field': 'resetField',
        },
        renderElement () {
            this.stringValue = Intl.NumberFormat().format(this.value);
            return this._super.apply(this, arguments);
        },
        isSet () {
            return true;
        },
        async resetField (ev) {
            ev.preventDefault();
            this._setValue('0');
            this.renderElement();
        }
    });

    registry.add('share_count', ShareCount);
});

Chatter js

odoo.define('academy.chatter', (require) => {

    const { PortalComposer } = require('portal.composer');

    PortalComposer.include({
        xmlDependencies: ['/academy/static/src/xml/portal.xml'],
        events: {
            'click .btn-danger': 'clearComment',
        },
        clearComment (ev) {
            ev.preventDefault();
            this.$('textarea').val(null);
        },
    });

});

Button js

odoo.define('academy.button', (require) => {

    const { Widget, registry } = require('web.public.widget');
    const Dialog = require('web.Dialog');

    registry.NewButton = Widget.extend({
        selector: '.js_class',
        events: {
            'click button': 'clickEvent',
        },
        start () {
            this._super.apply(this, arguments);
            this.button = this.el.querySelector('button');
            new ClipboardJS(this.el, {
                text: () => document.location.origin + this.button.dataset.url,
            });
        },
        async clickEvent (ev) {
            await this._rpc({
                route: '/academy/share_count',
                params: {
                    product_id: this.button.dataset.id,
                },
            });
            Dialog.alert(this, 'Now you can share this content', { title: 'Copied!'  });
        },
    });

});

Bundles

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <template id="assets_frontend" inherit_id="web.assets_frontend">
        <xpath expr="//script[last()]" position="after">
            <script type="text/javascript" src="/academy/static/src/js/button.js"></script>
            <script type="text/javascript" src="/academy/static/src/js/chatter.js"></script>
        </xpath>
        <xpath expr="//link[last()]" position="after">
            <link rel="stylesheet" href="/academy/static/src/sass/chatter.scss" />
        </xpath>
    </template>

    <template id="assets_backend" inherit_id="web.assets_backend">
        <xpath expr="//script[last()]" position="after">
            <script type="text/javascript" src="/academy/static/src/js/field.js"></script>
        </xpath>
        <xpath expr="//link[last()]" position="after">
            <link rel="stylesheet" href="/academy/static/src/sass/field.scss" />
        </xpath>
    </template>
</odoo>

Field XML

<templates id="template" xml:space="preserve">
    <t t-name="academy.field">
        <div class="border shadow share_count_widget d-inline-flex align-items-center p-2 mx-2">
            <div class="d-flex align-items-center">
                <b class="field_title">Current<br/>shares</b>
                <h3 class="d-flex flex-column ml-2 m-0">
                    <i class="fa fa-share-alt text-info mb-1" />
                    <i type="button" class="fa fa-times text-danger js_reset_field" />
                </h3>
            </div>
            <div class="ml-1">
                <h1 class="m-0 ml-3" t-esc="widget.stringValue" />
            </div>
        </div>
    </t>
</templates>

Chatter XML

<templates id="template" xml:space="preserve">
    <t t-extend="portal.Composer">
        <t t-jquery="button:last-of-type" t-operation="after">
            <button class="btn btn-danger">Cancel</button>
        </t>
    </t>
</templates>

Product XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <template id="button" inherit_id="website_sale.products_item" customize_show="True" name="Wishlist">
        <xpath expr="//div[hasclass('o_wsale_product_btn')]" position="inside">
            <div class="js_class d-inline">
                <button class="btn btn-primary" type="button" role="button"
                    t-att-data-id="product.id"
                    t-attf-data-url="/shop/product/{{slug(product)}}">
                    <i class="fa fa-share-alt" role="img" />
                </button>
            </div>
        </xpath>
    </template>

    <record id="product_template_form_view" model="ir.ui.view">
        <field name="name">product.template.common.form.inherit</field>
        <field name="inherit_id" ref="product.product_template_form_view" />
        <field name="model">product.template</field>            
        <field name="arch" type="xml">
            <xpath expr="//form/sheet//div[@name='options']" position="inside">
                <div>
                    <field name="share_count" widget="share_count"/>
                </div>
            </xpath>
        </field>
    </record>
</odoo>

CSS chatter

$shadow: 1px 1px 3px 0.4px grey;

.o_portal_chatter_composer_form {
    textarea, &:focus {
        box-shadow: $shadow !important;
        border-radius: .5px;
        border: none;
        resize: none;
        background-color: rgba(209, 195, 158, 0.17) !important;
    }
    button {
        border-radius: 1px;
        box-shadow: $shadow;
        float: right;
        margin-left: 10px
    }
}
.o_portal_chatter_composer {
    .o_portal_chatter_avatar {
        border-radius: 20px;
        margin-top: 1px;
        @include size(55px);
        animation-name: blinking;
        animation-duration: 1.5s;
        animation-iteration-count: infinite;
    }
}

@keyframes blinking {
    0% {
        box-shadow: 0 0 0px 0.4px transparent;
    }
    30% {
        box-shadow: 0 0 10px 0.9px cadetblue;
    }
    100% {
        box-shadow: 0 0 0px 0.4px transparent;
    }
}

CSS Field

.share_count_widget {
    .field_title {
        line-height: 16px;
        text-align: center;
        font-size: 15px;   
    }
}

Product py

from odoo import models, fields


class ProductTemplate(models.Model):
    _inherit = 'product.template'

    share_count = fields.Integer()

Controller

from odoo import http

class Controller(http.Controller):

    @http.route('/academy/share_count', auth='public', type='json')
    def update_share_count(self, product_id=None):
        product = http.request.env['product.template'].sudo().browse(
            int(product_id))
        product.share_count = product.share_count + 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment