@ -0,0 +1,49 @@ |
|||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
Product Search Snippet |
||||
|
====================== |
||||
|
This module helps to search products in category and all category wise. |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
* No additional configurations needed |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
General Public License, Version 3 (AGPL v3). |
||||
|
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
* Developer: (V18) Nivedhya T, |
||||
|
(V17) Ajith V, |
||||
|
(V16) Farhana Jahan PT |
||||
|
* Contact: odoo@cybrosys.com |
||||
|
|
||||
|
Contacts |
||||
|
-------- |
||||
|
* Mail Contact : odoo@cybrosys.com |
||||
|
* Website : https://cybrosys.com |
||||
|
|
||||
|
Bug Tracker |
||||
|
----------- |
||||
|
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
||||
|
|
||||
|
Maintainer |
||||
|
========== |
||||
|
.. image:: https://cybrosys.com/images/logo.png |
||||
|
:target: https://cybrosys.com |
||||
|
|
||||
|
This module is maintained by Cybrosys Technologies. |
||||
|
|
||||
|
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import controllers |
||||
|
from . import models |
@ -0,0 +1,61 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
{ |
||||
|
'name': 'Product Search Snippet', |
||||
|
'version': '18.0.1.0.0', |
||||
|
'category': 'Website', |
||||
|
'summary': """Product Search Snippet for Website.""", |
||||
|
'description': """This module enables users to search for products |
||||
|
within a specific category or across all categories using the search |
||||
|
bar on the website snippet and redirect to its details.""", |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'website': "https://cybrosys.com/", |
||||
|
'depends': ['website', 'sale_management'], |
||||
|
'data': [ |
||||
|
'views/snippets/search_snippet_templates.xml', |
||||
|
'views/snippets/product_search_templates.xml', |
||||
|
'views/snippets/product_details_templates.xml', |
||||
|
'views/snippets/selected_product_from_all_product_templates.xml', |
||||
|
'views/snippets/product_all_result_templates.xml', |
||||
|
'views/snippets/category_details_templates.xml', |
||||
|
'views/snippets/category_selected_product_templates.xml', |
||||
|
'views/snippets/selected_category_from_all_category_templates.xml', |
||||
|
'views/snippets/category_all_result_templates.xml', |
||||
|
'views/snippets/product_select_from_category_templates.xml', |
||||
|
], |
||||
|
'assets': { |
||||
|
'web.assets_frontend': [ |
||||
|
'website_product_search_snippet/static/src/css/website_product_search_snippet.scss', |
||||
|
'website_product_search_snippet/static/src/js/website_product_search_snippet.js', |
||||
|
'website_product_search_snippet/static/src/js/search_bar.js', |
||||
|
'website_product_search_snippet/static/src/xml/product_templates.xml', |
||||
|
'website_product_search_snippet/static/src/xml/category_templates.xml', |
||||
|
] |
||||
|
}, |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import website_product_search_snippet |
@ -0,0 +1,146 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import http |
||||
|
from odoo.http import request |
||||
|
|
||||
|
|
||||
|
class WebsiteSnippetPage(http.Controller): |
||||
|
"""Controller for setting routes.Pass all categories and |
||||
|
category wise products as array to a template""" |
||||
|
|
||||
|
@http.route('/category/form', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def category_page(self, **kw): |
||||
|
"""Function for rendering specific category and products of that |
||||
|
category into website""" |
||||
|
category = request.env['product.category'].sudo().browse( |
||||
|
int(kw.get('category_id'))) |
||||
|
parent = request.env['product.category'].sudo().browse( |
||||
|
int(kw.get('parent_id'))) |
||||
|
values = { |
||||
|
'category': category, |
||||
|
'products': request.env['product.template'].search( |
||||
|
[('categ_id', '=', category.id)]), |
||||
|
'products_category': request.env['product.template'].search( |
||||
|
[('category_id', '=', parent.id)]) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
'website_product_search_snippet.category_snippet_img', values) |
||||
|
|
||||
|
@http.route('/selected/category/result', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def category_all_page(self, **kw): |
||||
|
"""Function for rendering specific category and products of that |
||||
|
category into website""" |
||||
|
category = request.env['product.category'].sudo().browse( |
||||
|
int(kw.get('category_id'))) |
||||
|
values = { |
||||
|
'category': category, |
||||
|
'products': request.env['product.template'].search( |
||||
|
[('categ_id', '=', category.id)]) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
'website_product_search_snippet.all_category_snippet_img', values) |
||||
|
|
||||
|
@http.route('/selected/category/from/all_category/result', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def category_from_all_category_page(self, **kw): |
||||
|
"""Function for rendering specific category and products of that |
||||
|
category into website""" |
||||
|
category = request.env['product.category'].sudo().browse( |
||||
|
int(kw.get('category_id'))) |
||||
|
values = { |
||||
|
'category': category, |
||||
|
'products': request.env['product.template'].search( |
||||
|
[('categ_id', '=', category.id)]) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
'website_product_search_snippet.category_from_all_category_snippet_img', values) |
||||
|
|
||||
|
@http.route('/product/form', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def product_page(self, **kw): |
||||
|
"""Function for rendering specific product into website""" |
||||
|
values = { |
||||
|
'products': request.env['product.template'].sudo().browse( |
||||
|
int(kw.get('product_id'))) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
'website_product_search_snippet.products_snippet_img', values) |
||||
|
|
||||
|
@http.route('/selected/product/from/category', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def selected_product_page(self, **kw): |
||||
|
"""Function for rendering specific product into website""" |
||||
|
values = { |
||||
|
'products': request.env['product.template'].sudo().browse( |
||||
|
int(kw.get('product_id'))) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
'website_product_search_snippet.selected_products_from_category_snippet_img', |
||||
|
values) |
||||
|
|
||||
|
@http.route('/all/product/selected/product/details', type='http', |
||||
|
auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def product_all_page(self, **kw): |
||||
|
"""Function for rendering specific product into website""" |
||||
|
values = { |
||||
|
'products': request.env['product.template'].sudo().browse( |
||||
|
int(kw.get('product_id'))) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
'website_product_search_snippet.all_products_snippet_img', values) |
||||
|
|
||||
|
@http.route('/select/product/from/category', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def product_category_all_page(self, **kw): |
||||
|
"""Function for rendering specific product into website""" |
||||
|
values = { |
||||
|
'products': request.env['product.template'].sudo().browse( |
||||
|
int(kw.get('product_id'))) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
'website_product_search_snippet.products_category_snippet_img', |
||||
|
values) |
||||
|
|
||||
|
@http.route('/product/form/all/results', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def product_page_result(self): |
||||
|
"""Function for rendering all products into website""" |
||||
|
values = { |
||||
|
'products': request.env['product.template'].search([]) |
||||
|
} |
||||
|
return http.request.render( |
||||
|
"website_product_search_snippet.product_all_result_template", |
||||
|
values) |
||||
|
|
||||
|
@http.route('/category/form/all/results', type='http', auth='public', |
||||
|
website=True, csrf=False, sitemap=False, cache=300) |
||||
|
def category_page_result(self): |
||||
|
"""Function for rendering all categories into website""" |
||||
|
values = {'category': request.env['product.category'].search( |
||||
|
[('id', '!=', request.env.ref('product.product_category_all').id), |
||||
|
('id', '!=', request.env.ref('product.product_category_1').id)])} |
||||
|
return http.request.render( |
||||
|
"website_product_search_snippet.category_all_result_template", |
||||
|
values) |
@ -0,0 +1,5 @@ |
|||||
|
## Module <website_product_search_snippet> |
||||
|
#### 19.12.2024 |
||||
|
#### Version 18.0.1.0.0 |
||||
|
##### ADD |
||||
|
- Initial commit for Product Search Snippet |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import product_template |
@ -0,0 +1,81 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import api, fields, models |
||||
|
|
||||
|
|
||||
|
class ProductTemplate(models.Model): |
||||
|
""" Inherit product_template model for fetch products and categories to |
||||
|
frontend""" |
||||
|
_inherit = 'product.template' |
||||
|
|
||||
|
parent_category_id = fields.Many2one("product.category", |
||||
|
string="Parent Category") |
||||
|
category_boolean = fields.Boolean(default=True) |
||||
|
category_id = fields.Char(string='Parent Category', |
||||
|
compute="_compute_parent_id") |
||||
|
|
||||
|
@api.depends('category_boolean') |
||||
|
def _compute_parent_id(self): |
||||
|
""" Get the parent category of the product""" |
||||
|
self.category_id = self.categ_id.parent_id.id |
||||
|
|
||||
|
@api.model |
||||
|
def search_products(self, qry): |
||||
|
""" Search all products in product.template, |
||||
|
and pass searched products into templates """ |
||||
|
products = self.env['product.template'].search([('name', 'ilike', qry)]) |
||||
|
return [[product.name, product.id, |
||||
|
product.list_price, |
||||
|
'/web/image/product.template/{}/image_512/'.format(product.id), |
||||
|
product.currency_id.symbol, ] |
||||
|
for product in products] |
||||
|
|
||||
|
@api.model |
||||
|
def product_category(self, qry): |
||||
|
""" Search all category in product_category, |
||||
|
and pass category into another template """ |
||||
|
category = self.env['product.category'].search( |
||||
|
[('id', '!=', self.env.ref('product.product_category_all').id), |
||||
|
('name', 'ilike', qry)]) |
||||
|
return [[category.name, category.id, category.parent_id.name, |
||||
|
category.parent_id.id, |
||||
|
category.product_count] |
||||
|
for category in category] |
||||
|
|
||||
|
@api.model |
||||
|
def search_all_categories(self): |
||||
|
""" Search products in all categories """ |
||||
|
products = self.env['product.template'].search([]) |
||||
|
return [[product.name, product.id, |
||||
|
product.list_price, |
||||
|
'/web/image/product.template/{}/image_512/'.format(product.id), |
||||
|
product.currency_id.symbol, ] |
||||
|
for product in products] |
||||
|
|
||||
|
@api.model |
||||
|
def product_all_categories(self): |
||||
|
""" Search all product categories """ |
||||
|
categories = self.env['product.category'].search([ |
||||
|
('id', '!=', self.env.ref('product.product_category_all').id)]) |
||||
|
return [[category.name, category.id, category.parent_id.name, |
||||
|
category.parent_id.id, category.product_count] |
||||
|
for category in categories] |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 1.0 MiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 880 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 8.6 KiB |
@ -0,0 +1,25 @@ |
|||||
|
.heading{ |
||||
|
color: #fff6f8; |
||||
|
background: #575758; |
||||
|
font-size: 47px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
.search_bar{ |
||||
|
margin-left: 47px; |
||||
|
border-radius: 15px; |
||||
|
} |
||||
|
.search_container{ |
||||
|
background: #9c9a9a |
||||
|
} |
||||
|
#sub_temp{ |
||||
|
background: #e9e9f1; |
||||
|
} |
||||
|
#category_template{ |
||||
|
background: #e9e9f1; |
||||
|
} |
||||
|
#table_header { |
||||
|
background-color: #bababa; |
||||
|
color: white; |
||||
|
} |
||||
|
#table_row:nth-child(even){background-color: #ededed} |
||||
|
#category_table:nth-child(even){background-color: #c3c3c3} |
After Width: | Height: | Size: 14 KiB |
@ -0,0 +1,52 @@ |
|||||
|
/** @odoo-module **/ |
||||
|
|
||||
|
import publicWidget from "@web/legacy/js/public/public_widget"; |
||||
|
|
||||
|
|
||||
|
publicWidget.registry.portalDetails = publicWidget.Widget.extend({ |
||||
|
/** |
||||
|
* This widget handles search functionality within a product table on the website. |
||||
|
* It listens for keyup events on the search input field and filters table rows |
||||
|
* based on the user's input. |
||||
|
*/ |
||||
|
selector: '.product_search_bar', |
||||
|
events: { |
||||
|
'keyup .search_product_bar': '_onKeyUp', |
||||
|
}, |
||||
|
/** |
||||
|
* Initializes the widget and starts the functionality. |
||||
|
* |
||||
|
* @returns {Promise} - Resolves when the widget has fully started. |
||||
|
*/ |
||||
|
start: function () { |
||||
|
this._super.apply(this, arguments); |
||||
|
}, |
||||
|
/** |
||||
|
* Handles the keyup event to filter table data. |
||||
|
* |
||||
|
* This function retrieves the value from the search input field, converts it to |
||||
|
* uppercase, and then compares it with the text content of each table cell. |
||||
|
* Rows that contain a match are displayed, while others are hidden. |
||||
|
* |
||||
|
* @private |
||||
|
*/ |
||||
|
_onKeyUp: function () { |
||||
|
var input, filter, table, tr, td, i, txtValue; |
||||
|
input = this.$el.find("#searchBarInput")[0]; |
||||
|
filter = input.value.toUpperCase(); |
||||
|
table = this.$el.find("#category_table") |
||||
|
tr = table[0].children.category_table_body.children; |
||||
|
for (i = 0; i < tr.length; i++) { |
||||
|
td = tr[i].children; |
||||
|
for (var j = 0; j < td.length; j++) { |
||||
|
txtValue = td[j].textContent || td[j].innerText; |
||||
|
if (txtValue.toUpperCase().indexOf(filter) > -1) { |
||||
|
tr[i].style.display = ""; |
||||
|
break; |
||||
|
} else { |
||||
|
tr[i].style.display = "none"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
}); |
@ -0,0 +1,135 @@ |
|||||
|
/** @odoo-module **/ |
||||
|
import publicWidget from "@web/legacy/js/public/public_widget"; |
||||
|
import { rpc } from "@web/core/network/rpc"; |
||||
|
import {renderToElement} from "@web/core/utils/render"; |
||||
|
|
||||
|
var Dynamic = publicWidget.Widget.extend({ |
||||
|
selector: '.dynamic_search_snippet', |
||||
|
events: { |
||||
|
'click .search_container': '_onClick', |
||||
|
'keyup .search_bar': '_onKeyUp', |
||||
|
'change .category_options': '_filterProducts', |
||||
|
}, |
||||
|
/** |
||||
|
* _onClick: Clears the search input field. |
||||
|
* |
||||
|
* This function is triggered when a specific element is clicked. It clears the value of |
||||
|
* the input field with the ID `#searchInput`. |
||||
|
* |
||||
|
* Functionality: |
||||
|
* 1. Locates the input field with the ID `#searchInput` within the current element (`this.$el`). |
||||
|
* 2. Sets the value of the `#searchInput` field to an empty string, effectively clearing any text that was entered. |
||||
|
*/ |
||||
|
_onClick: function () { |
||||
|
this.$el.find('#searchInput').val(""); |
||||
|
}, |
||||
|
/** |
||||
|
* _onKeyUp: Asynchronously searches and filters products based on the user's input and selected category. |
||||
|
* |
||||
|
* This function is triggered when a key is released while typing in a search input field. |
||||
|
* It captures the current search query and the selected category, then calls the appropriate |
||||
|
* Odoo model method to fetch the relevant products. The fetched products are rendered in the |
||||
|
* specified HTML element using the corresponding QWeb template. |
||||
|
* |
||||
|
* @param {Object} ev - The event object associated with the keyup action. |
||||
|
* |
||||
|
* Functionality: |
||||
|
* 1. Captures the selected category from a dropdown menu with the class `.category_options`. |
||||
|
* 2. Retrieves the user's search query from the input field. |
||||
|
* 3. If "All Categories" is selected: |
||||
|
* - Calls the `search_products` method on the `product.template` model with the search query as an argument. |
||||
|
* - Renders the result using the 'website_product_search_snippet.product_template' template. |
||||
|
* 4. If "Category" is selected: |
||||
|
* - Calls the `product_category` method on the `product.template` model with the search query as an argument. |
||||
|
* - Renders the result using the 'website_product_search_snippet.product_category' template. |
||||
|
* 5. Updates the HTML element with the class `.qweb_product_id` to display the filtered products based on the search query and selected category. |
||||
|
* |
||||
|
* Note: The function uses `jsonrpc` to make RPC calls to the Odoo backend. |
||||
|
*/ |
||||
|
_onKeyUp: async function (ev) { |
||||
|
var self = this; |
||||
|
var category = this.$el.find(".category_options").find(":selected").text(); |
||||
|
var qry = $(ev.currentTarget).val() |
||||
|
if (category === "All Categories") { |
||||
|
await rpc('/web/dataset/call_kw', { |
||||
|
model: 'product.template', |
||||
|
method: 'search_products', |
||||
|
args: [qry], |
||||
|
kwargs: {}, |
||||
|
}).then(function (result) { |
||||
|
self.$('.qweb_product_id').html(""); |
||||
|
self.$('.qweb_product_id').append(renderToElement('website_product_search_snippet.product_template', { |
||||
|
result: result |
||||
|
})); |
||||
|
}); |
||||
|
} |
||||
|
if (category === "Category") { |
||||
|
var self = this; |
||||
|
await rpc('/web/dataset/call_kw', { |
||||
|
model: 'product.template', |
||||
|
method: 'product_category', |
||||
|
args: [qry], |
||||
|
kwargs: {}, |
||||
|
}).then(function (result) { |
||||
|
self.$('.qweb_product_id').html(""); |
||||
|
self.$('.qweb_product_id').append(renderToElement('website_product_search_snippet.product_category', { |
||||
|
result: result |
||||
|
})); |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
/** |
||||
|
* _filterProducts: Asynchronously filters products based on the selected category. |
||||
|
* |
||||
|
* This function is triggered by an event (e.g., a change in category selection). |
||||
|
* It checks the selected category from a dropdown menu and calls the appropriate |
||||
|
* Odoo model method to fetch the relevant products. The fetched products are then |
||||
|
* rendered in the specified HTML element using the corresponding QWeb template. |
||||
|
* |
||||
|
* @param {Object} ev - The event object associated with the action triggering this function. |
||||
|
* |
||||
|
* Functionality: |
||||
|
* 1. Retrieves the selected category from a dropdown menu with class `.category_options`. |
||||
|
* 2. If "All Categories" is selected, it calls the `search_all_categories` method |
||||
|
* on the `product.template` model to fetch all products, and renders the result |
||||
|
* using the 'website_product_search_snippet.product_template' template. |
||||
|
* 3. If "Category" is selected, it calls the `product_all_categories` method on |
||||
|
* the `product.template` model to fetch products by category, and renders the |
||||
|
* result using the 'website_product_search_snippet.product_category' template. |
||||
|
* 4. Updates the HTML element with class `.qweb_product_id` to display the filtered products. |
||||
|
* |
||||
|
* Note: The function uses `jsonrpc` to make RPC calls to the Odoo backend. |
||||
|
*/ |
||||
|
_filterProducts: async function (ev) { |
||||
|
var self = this |
||||
|
var category = this.$el.find(".category_options").find(":selected").text(); |
||||
|
if (category === "All Categories") { |
||||
|
await rpc('/web/dataset/call_kw', { |
||||
|
model: 'product.template', |
||||
|
method: 'search_all_categories', |
||||
|
args: [], |
||||
|
kwargs: {} |
||||
|
}).then(function (result) { |
||||
|
self.$('.qweb_product_id').html(""); |
||||
|
self.$('.qweb_product_id').append(renderToElement('website_product_search_snippet.product_template', { |
||||
|
result: result |
||||
|
})); |
||||
|
}); |
||||
|
} |
||||
|
if (category === "Category") { |
||||
|
await rpc('/web/dataset/call_kw', { |
||||
|
model: 'product.template', |
||||
|
method: 'product_all_categories', |
||||
|
args: [], |
||||
|
kwargs: {} |
||||
|
}).then(function (result) { |
||||
|
self.$('.qweb_product_id').html(""); |
||||
|
self.$('.qweb_product_id').append(renderToElement('website_product_search_snippet.product_category', { |
||||
|
result: result |
||||
|
})); |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
publicWidget.registry.dynamic_search_snippet = Dynamic; |
||||
|
return Dynamic; |
@ -0,0 +1,60 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<!--When the key is pressed in the search bar the searched |
||||
|
category will be shown with its parent category--> |
||||
|
<templates id="template" xml:space="preserve"> |
||||
|
<t t-name="website_product_search_snippet.product_category"> |
||||
|
<div class="container" id="category_template"> |
||||
|
<section> |
||||
|
<form id="category_form"> |
||||
|
<div style="height:50px"/> |
||||
|
<center> |
||||
|
<h3>Categories</h3> |
||||
|
</center> |
||||
|
<t t-if="result.length === 0"> |
||||
|
<p class="text-center">No results found. Please try another search.</p> |
||||
|
</t> |
||||
|
<div style="height:20px"/> |
||||
|
<table class="table"> |
||||
|
<thead id="table_header"> |
||||
|
<td><b>Parent Category</b></td> |
||||
|
<td><b>Category</b></td> |
||||
|
</thead> |
||||
|
<tbody id="table_category_body" class="body"> |
||||
|
<t t-set="result_limit" t-value="5"/> |
||||
|
<t t-set="counter" t-value="0"/> |
||||
|
<t t-foreach="result" t-as="category" t-key="result"> |
||||
|
<t t-if="counter < result_limit"> |
||||
|
<tr id="table_row"> |
||||
|
<td><t t-esc="category[2]"/></td> |
||||
|
<t t-if="category[4] === 0"> |
||||
|
<td><a t-attf-href="/category/form?category_id=#{category[1]}&parent_id=#{category[3]}" |
||||
|
loading="fast" style="color: #de3434;"><t t-esc="category[0]" |
||||
|
/></a></td> |
||||
|
</t> |
||||
|
<t t-else=""> |
||||
|
<td><a t-attf-href="/category/form?category_id=#{category[1]}&parent_id=#{category[3]}" |
||||
|
loading="fast"><t t-esc="category[0]" |
||||
|
/></a></td> |
||||
|
</t> |
||||
|
</tr> |
||||
|
</t> |
||||
|
<t t-else=""> |
||||
|
<tr id="table_row_hidden" class="d-none"> |
||||
|
<td><t t-esc="category[2]"/></td> |
||||
|
<td><a t-attf-href="/category/form?category_id=#{category[1]}" |
||||
|
loading="fast"><t t-esc="category[0]" |
||||
|
/></a></td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
<t t-set="counter" t-value="counter+1"/> |
||||
|
</t> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
<a t-attf-href="/category/form/all/results" |
||||
|
style="margin-left: 38%;">See All Categories</a> |
||||
|
</form> |
||||
|
</section> |
||||
|
<div style="height:20px"/> |
||||
|
</div> |
||||
|
</t> |
||||
|
</templates> |
@ -0,0 +1,61 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<!--When the key is pressed in the search bar the searched products |
||||
|
will be shown with their price and image--> |
||||
|
<templates id="template" xml:space="preserve"> |
||||
|
<t t-name="website_product_search_snippet.product_template"> |
||||
|
<div class="container" id="sub_temp"> |
||||
|
<section cdata-vxml="001"> |
||||
|
<form id="product_form" style="background-color: #f3f3f3;"> |
||||
|
<div style="height:50px"/> |
||||
|
<center> |
||||
|
<h3 style="margin-left: 0%;">Products</h3> |
||||
|
</center> |
||||
|
<t t-if="result.length === 0"> |
||||
|
<p class="text-center">No results found. Please try another search.</p> |
||||
|
</t> |
||||
|
<div style="height:20px"/> |
||||
|
<table class="table"> |
||||
|
<thead id="table_header"> |
||||
|
<td><b>Image</b></td> |
||||
|
<td><b>Product</b></td> |
||||
|
<td style="padding-left:2%;"><b> </b></td> |
||||
|
<td><b>Price</b></td> |
||||
|
</thead> |
||||
|
<tbody id="table_body" class="body"> |
||||
|
<t t-set="result_limit" t-value="5"/> |
||||
|
<t t-set="counter" t-value="0"/> |
||||
|
<t t-foreach="result" t-as="product" t-key="result"> |
||||
|
<t t-if="counter < result_limit"> |
||||
|
<tr id="table_row"> |
||||
|
<td><img t-att-src="product[3]" |
||||
|
style="width:25%;"/></td> |
||||
|
<td><a t-attf-href="/product/form?product_id=#{product[1]}" |
||||
|
loading="fast"><t t-esc="product[0]" |
||||
|
/></a></td> |
||||
|
<td style="padding-left:2%;"><t t-esc="product[4]"/></td> |
||||
|
<td><t t-esc="product[2]"/></td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
<t t-else=""> |
||||
|
<tr id="table_row_hidden" class="d-none"> |
||||
|
<td><img t-att-src="product[3]" |
||||
|
style="width:25%;"/></td> |
||||
|
<td><a t-attf-href="/product/form?product_id=#{product[1]}" |
||||
|
loading="fast"><t t-esc="product[0]" |
||||
|
/></a></td> |
||||
|
<td style="padding-left:2%;"><t t-esc="product[4]"/></td> |
||||
|
<td><t t-esc="product[2]"/></td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
<t t-set="counter" t-value="counter+1"/> |
||||
|
</t> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
<a t-attf-href="/product/form/all/results" |
||||
|
style="margin-left: 38%;">See All Products</a> |
||||
|
</form> |
||||
|
</section> |
||||
|
<div style="height:20px"/> |
||||
|
</div> |
||||
|
</t> |
||||
|
</templates> |
@ -0,0 +1,73 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!--When clicking on 'See All Category' rendering a page the route as |
||||
|
/category/form/all/results,where the categories are displayed--> |
||||
|
<template id="category_all_result_template" name="Category results Snippet"> |
||||
|
<t t-call="website.layout"> |
||||
|
<section class="row category_result_search_bar" loading="eager"> |
||||
|
<div class="container"> |
||||
|
<div style="height:20px"/> |
||||
|
<div class="back_button" align="left"> |
||||
|
<a t-attf-href="/" name="create" |
||||
|
style="margin-left: 13%;font-weight: bold;">Back |
||||
|
</a> |
||||
|
</div> |
||||
|
<div style="height:20px"/> |
||||
|
<center> |
||||
|
<form align="center" id="search_result" |
||||
|
action="/category/form/all/results"> |
||||
|
<div style="height:20px"/> |
||||
|
<div style="height:20px"/> |
||||
|
<center> |
||||
|
<h3 style="font-size: 26px;">All Categories |
||||
|
</h3> |
||||
|
</center> |
||||
|
<table class="table" |
||||
|
style="width: 66%;margin-left: 17%;"> |
||||
|
<thead id="table_header"> |
||||
|
<td> |
||||
|
<b>Parent Category</b> |
||||
|
</td> |
||||
|
<td> |
||||
|
<b>Category</b> |
||||
|
</td> |
||||
|
</thead> |
||||
|
<tbody id="table_category_body" |
||||
|
class="body"> |
||||
|
<t t-foreach="category" t-as="product"> |
||||
|
<tr style="nth-child(even){background-color: #f2f2f2}" |
||||
|
id="category_table"> |
||||
|
<td> |
||||
|
<t t-esc="product.parent_id.name"/> |
||||
|
</td> |
||||
|
<t t-if="product.product_count"> |
||||
|
<td> |
||||
|
<a t-attf-href="/selected/category/from/all_category/result?category_id=#{product.id}" |
||||
|
loading="fast"> |
||||
|
<t t-esc="product.name" |
||||
|
/> |
||||
|
</a> |
||||
|
</td> |
||||
|
</t> |
||||
|
<t t-else=""> |
||||
|
<td> |
||||
|
<a t-attf-href="/selected/category/from/all_category/result?category_id=#{product.id}" |
||||
|
loading="fast" |
||||
|
style="color: #de3434;"> |
||||
|
<t t-esc="product.name" |
||||
|
/> |
||||
|
</a> |
||||
|
</td> |
||||
|
</t> |
||||
|
</tr> |
||||
|
</t> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</form> |
||||
|
</center> |
||||
|
</div> |
||||
|
</section> |
||||
|
<div style="height:20px"/> |
||||
|
</t> |
||||
|
</template> |
||||
|
</odoo> |