diff --git a/generative_ai/README.md b/generative_ai/README.md new file mode 100644 index 000000000..d8eff32ae --- /dev/null +++ b/generative_ai/README.md @@ -0,0 +1,75 @@ +# Generative AI for Odoo 18 + +[![Odoo](https://img.shields.io/badge/Odoo-%23A24689.svg?style=for-the-badge&logo=Odoo&logoColor=white)](https://www.odoo.com) +[![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](https://opensource.org/licenses/MIT) + +## Overview + +The Generative AI for Odoo 18 module allows users to generate website snippets using AI-powered text prompts directly within the Odoo Website Editor. It seamlessly integrates with OpenAI and OpenRouter to enhance content creation and streamline web development workflows. + +## Features + +- 💾 Instantly generate custom website snippets from text prompts using OpenAI or OpenRouter. +- 🧠 Create, preview, and insert AI-generated snippets directly within Odoo’s website editor. +- ⚙️ Easily configure AI provider, model, and token limits from Odoo settings for full control. + +## Screenshots + +Here are some glimpses of Generative AI + +### User Interface of Website module + +
+ + + Screenshot 1 + + +
+
+ + + Screenshot 2 + + +
+
+ + + Screenshot 3 + + +
+
+ + + Screenshot 4 + + +
+
+ + + Screenshot 5 + + +
+
+ + + Screenshot 6 + + +
+ + + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- An active Odoo Community/Enterprise Edition instance (local or hosted) + +## Configuration +- Ensure the OpenAI Python package is installed. + diff --git a/generative_ai/__init__.py b/generative_ai/__init__.py new file mode 100644 index 000000000..e32630263 --- /dev/null +++ b/generative_ai/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies(). +# Author: Cybrosys Techno Solutions() +# +# 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 . +# +############################################################################# +from . import controllers +from . import models diff --git a/generative_ai/__manifest__.py b/generative_ai/__manifest__.py new file mode 100644 index 000000000..bb56e1c54 --- /dev/null +++ b/generative_ai/__manifest__.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies(). +# Author: Cybrosys Techno Solutions() +# +# 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 . +# +############################################################################# +{ + 'name': 'Generative AI In Snippet Creation', + 'version': '18.0.1.0.0', + 'category': 'Generative AI/Generative AI', + 'summary': 'To integrate AI tools in website snippet creation.', + 'description': """This module contains details about Generative AI in website.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': [ + 'base', 'website', 'web', 'web_editor' + ], + 'external_dependencies': { + 'python': [ + 'openai', + ], + }, + 'data': [ + 'security/ir.model.access.csv', + 'data/test_data.xml', + 'views/res_config_settings_views.xml', + 'views/snippets/s_snippet_group_with_ai_content.xml', + 'views/snippets.xml', + ], + 'assets': { + 'web_editor.assets_wysiwyg': [ + 'generative_ai/static/src/xml/website_dialogue_box.xml', + 'generative_ai/static/src/js/ai_button_action.js', + ('include', 'web._assets_helpers'), + 'generative_ai/static/src/js/title.js', + ], + 'web.assets_backend': [ + 'generative_ai/static/src/img/placeholder.png', + 'generative_ai/static/src/css/snippets.css', + ], + }, + 'images': ['static/description/banner.jpg'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': True, +} diff --git a/generative_ai/controllers/__init__.py b/generative_ai/controllers/__init__.py new file mode 100644 index 000000000..48e780b1d --- /dev/null +++ b/generative_ai/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies(). +# Author: Cybrosys Techno Solutions() +# +# 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 . +# +############################################################################# +from . import main diff --git a/generative_ai/controllers/main.py b/generative_ai/controllers/main.py new file mode 100644 index 000000000..7794d3ebd --- /dev/null +++ b/generative_ai/controllers/main.py @@ -0,0 +1,400 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies(). +# Author: Cybrosys Techno Solutions() +# +# 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 . +# +############################################################################# +import hashlib +import re +import requests +from markupsafe import Markup +from openai import OpenAI +from odoo import http +from odoo.http import request + + +def preprocess_ai_output(text): + """ + Preprocesses AI-generated text by carefully extracting and closing CSS rules. + Args: + text (str): Input text containing HTML and CSS + Returns: + str: Preprocessed text with properly closed CSS + """ + text = re.sub(r'^```(html|css|plaintext)?\s*', '', text.strip(), flags=re.MULTILINE) + text = re.sub(r'\s*```$', '', text, flags=re.MULTILINE) + # Check if style tags already exist + style_match = re.search(r'', text, re.DOTALL) + if style_match: + return text + css_lines = [] + html_lines = [] + current_rule = [] + in_css_block = False + for line in text.split('\n'): + stripped_line = line.strip() + if re.match(r'^(\.|#|[a-zA-Z])[\w\s,#>:.()-]+\s*{', stripped_line): + in_css_block = True + current_rule = [line] + elif in_css_block: + current_rule.append(line) + if stripped_line.endswith('}'): + css_lines.extend(current_rule) + current_rule = [] + in_css_block = False + else: + if stripped_line: + html_lines.append(line) + if current_rule: + if not current_rule[-1].strip().endswith('}'): + current_rule[-1] = current_rule[-1] + '\n}' + css_lines.extend(current_rule) + if css_lines: + css_output = "" + else: + css_output = "" + html_output = "\n".join(html_lines) + final_output = f"{css_output}\n{html_output}" if css_output else html_output + return final_output.strip() + + +class SnippetGenerator(http.Controller): + + + def _generate_unique_id(self, snippet_name): + """Generate a unique ID for the snippet to avoid CSS conflicts""" + return hashlib.md5(f"{snippet_name}_{request.session.sid}".encode()).hexdigest()[ + :8] + + def _generate_openai_response(self, prompt, api_key, model_id, max_tokens, + snippet_name): + """Helper method for OpenAI API calls""" + response = OpenAI(api_key=api_key).chat.completions.create( + model=model_id, + messages=[ + {"role": "system", + "content": "You are a helpful assistant that generates website snippets similar to Odoo's inbuilt website snippets. " + "Create responsive snippets using Bootstrap classes, ensuring proper container and row/column structure. " + "Use placeholder images from 'https://via.placeholder.com/' with appropriate sizes. " + "Return only clean HTML and CSS without any wrapper divs or preview elements. " + "Do not include any code block markers (```). " + "Focus on creating functional, beautiful snippets that work well in Odoo's website builder. " + "Use modern design principles with good typography, spacing, and colors." + }, + {"role": "user", "content": prompt} + ], + max_tokens=int(max_tokens), + temperature=0.7 + ) + return response.choices[0].message.content.strip() + + def _generate_openrouter_response(self, prompt, api_key, model_id, max_tokens, + snippet_name): + """Helper method for OpenRouter API calls with improved error handling""" + url = "https://openrouter.ai/api/v1/chat/completions" + headers = { + "Authorization": f"Bearer {api_key}", + "HTTP-Referer": request.env['ir.config_parameter'].sudo().get_param( + 'web.base.url', ''), + "X-Title": "Odoo Integration" + } + payload = { + "model": model_id, + "messages": [ + {"role": "system", + "content": "You are a helpful assistant that generates website snippets similar to Odoo's inbuilt website snippets. " + "Create responsive snippets using Bootstrap classes, ensuring proper container and row/column structure. " + "Use placeholder images from 'https://via.placeholder.com/' with appropriate sizes. " + "Return only clean HTML and CSS without any wrapper divs or preview elements. " + "Do not include any code block markers (```). " + "Focus on creating functional, beautiful snippets that work well in Odoo's website builder. " + "Use modern design principles with good typography, spacing, and colors." + }, + {"role": "user", "content": prompt} + ], + "max_tokens": int(max_tokens), + "temperature": 0.7 + } + response = requests.post(url, headers=headers, json=payload) + if response.status_code != 200: + raise Exception( + f"OpenRouter API error: {response.status_code} - {response.text}") + json_response = response.json() + if 'choices' not in json_response or not json_response['choices']: + raise Exception("Invalid response format from OpenRouter API") + choice = json_response['choices'][0] + if 'message' not in choice: + raise Exception("No message in OpenRouter API response") + message = choice['message'] + content = message.get('content', '') + reasoning = message.get('reasoning', '') + partial_content = content.strip() if content else reasoning.strip() + if choice.get('finish_reason') == 'length' and partial_content: + completion_prompt = f""" + You previously started generating a website snippet but it was cut off. + Please complete the snippet based on this partial content: + {partial_content} + Continue from where it was cut off and make sure the HTML/CSS is complete and valid. + """ + completion_payload = { + "model": model_id, + "messages": [ + {"role": "system", + "content": "You are completing a partially generated website snippet. " + "Return only HTML/CSS code to complete the snippet, don't start over."}, + {"role": "user", "content": completion_prompt} + ], + "max_tokens": int(max_tokens), + "temperature": 0.7 + } + try: + completion_response = requests.post(url, headers=headers, + json=completion_payload) + completion_response.raise_for_status() + completion_json = completion_response.json() + if 'choices' in completion_json and completion_json['choices']: + completion_message = completion_json['choices'][0]['message'] + completion_content = completion_message.get('content', '') + if completion_content: + cleaned_completion = re.sub(r"^```(html|css)?\s*", "", + completion_content.strip(), + flags=re.MULTILINE) + cleaned_completion = re.sub(r"\s*```$", "", cleaned_completion, + flags=re.MULTILINE) + combined_content = partial_content + "\n" + cleaned_completion + return combined_content + return partial_content + except Exception: + return partial_content + "\n" + if content: + return content.strip() + elif reasoning: + return reasoning.strip() + else: + raise Exception( + "Empty response from AI model. Please try again with a " + "different prompt or model.") + + @http.route('/website/generate_snippet', type='json', auth='user', website=True) + def generate_snippet(self, **kwargs): + """Single route to handle both OpenAI and OpenRouter snippet generation""" + prompt = kwargs.get('prompt') + snippet_name = kwargs.get('name') + if not prompt: + return {'error': "Prompt is required"} + try: + api_key = request.env['ir.config_parameter'].sudo().get_param('api_key') + ai_system = request.env['ir.config_parameter'].sudo().get_param( + 'generative_ai_systems') + max_tokens = request.env['ir.config_parameter'].sudo().get_param('max_token') + if not api_key: + return {'error': "API key not configured"} + unique_id = self._generate_unique_id(snippet_name) + try: + if ai_system == 'openai': + model_id = request.env['ir.config_parameter'].sudo().get_param( + 'model_id') + if not model_id: + return {'error': "No OpenAI model selected"} + snippet_text = self._generate_openai_response(prompt, api_key, + model_id, max_tokens, + snippet_name) + elif ai_system == 'openrouter': + model_id = request.env['ir.config_parameter'].sudo().get_param( + 'openrouter_model_id') + if not model_id or model_id == 'none': + return {'error': "No OpenRouter model selected"} + snippet_text = self._generate_openrouter_response(prompt, api_key, + model_id, + max_tokens, + snippet_name) + else: + return {'error': "Invalid AI system selected"} + except Exception as api_error: + return {'error': f"API Error: {str(api_error)}", 'retry': True} + # Check if we got a valid response + if not snippet_text or snippet_text.strip() == "": + return { + 'error': "AI did not generate any content. Please try again with a different prompt or model.", + 'retry': True + } + processed_snippet_text = preprocess_ai_output(snippet_text) + preview_style = f""" +""" + + # Wrap content with unique scoped classes + wrapped_content = f""" +{preview_style} +
+
{snippet_name}
+
+ {processed_snippet_text} +
+
+""" + if not wrapped_content or wrapped_content.strip() == "": + return { + 'error': "Could not process AI output into a valid snippet. Please try again.", + 'retry': True + } + snippet = request.env['website.snippet.data'].sudo().create({ + 'name': snippet_name, + 'content': Markup(wrapped_content), + 'image_url': '/generative_ai/static/src/img/placeholder.png', + 'is_ai_generated': True, + }) + request.env['ir.qweb'].clear_caches() + request.env['ir.ui.view'].clear_caches() + return { + 'success': True, + 'snippet': snippet, + 'snippet_id': snippet.id, + 'content': snippet.content, + 'image_url': '/generative_ai/static/src/img/placeholder.png' + } + except Exception as e: + return {'error': str(e), 'retry': True} diff --git a/generative_ai/data/test_data.xml b/generative_ai/data/test_data.xml new file mode 100644 index 000000000..5e20df3a3 --- /dev/null +++ b/generative_ai/data/test_data.xml @@ -0,0 +1,17 @@ + + + + + Default AI Snippet + +

Welcome to AI Generated Snippets

+ AI Snippet Image +

This is a default snippet that showcases AI capabilities.

+ + ]]>
+ generative_ai/static/src/img/placeholder.png + True +
+
+
diff --git a/generative_ai/doc/RELEASE_NOTES.md b/generative_ai/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..f8fc8d83c --- /dev/null +++ b/generative_ai/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 21.07.2025 +#### Version 18.0.1.0.0 +##### ADD +- Initial commit for Generative AI. \ No newline at end of file diff --git a/generative_ai/models/__init__.py b/generative_ai/models/__init__.py new file mode 100644 index 000000000..11733c08d --- /dev/null +++ b/generative_ai/models/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies(). +# Author: Cybrosys Techno Solutions() +# +# 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 . +# +############################################################################# +from . import res_config_settings +from . import website_snippet_data diff --git a/generative_ai/models/res_config_settings.py b/generative_ai/models/res_config_settings.py new file mode 100644 index 000000000..de3cd3b82 --- /dev/null +++ b/generative_ai/models/res_config_settings.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies(). +# Author: Cybrosys Techno Solutions() +# +# 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 . +# +############################################################################# +import requests +from odoo import api, fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + generative_ai_systems = fields.Selection([ + ('openai', 'Open AI'), + ('openrouter', 'OpenRouter') + ], string='Generative AI Systems') + api_key = fields.Char('API Key', copy=False) + max_token = fields.Char(string='Maximum tokens', + default=4096, config_parameter='generative_ai.max_token') + model_id = fields.Selection(selection=[ + ('gpt-3.5-turbo', 'gpt-3.5-turbo'), + ('gpt-4-turbo', 'gpt-4-turbo'), + ('gpt-4o', 'gpt-4o'), + ('gpt-4o-mini', 'gpt-4o-mini'), + ('gpt-4-0613', 'gpt-4-0613'), + ]) + openrouter_model_id = fields.Selection(selection='_get_model_selection', + string="AI Model") + + @api.model + def default_get(self, fields): + """Override default_get to set a default value for 'max_token' field + when it is included in the fields to fetch.""" + res = super(ResConfigSettings, self).default_get(fields) + if 'max_token' in fields: + res['max_token'] = "4096" + return res + + @api.model + def get_values(self): + """Override get_values to load saved configuration parameters from the system settings. + These values are shown on the settings UI.""" + res = super(ResConfigSettings, self).get_values() + res['generative_ai_systems'] = self.env['ir.config_parameter'].sudo().get_param( + 'generative_ai_systems') + res['api_key'] = self.env['ir.config_parameter'].sudo().get_param('api_key') + res['model_id'] = self.env['ir.config_parameter'].sudo().get_param('model_id') + res['openrouter_model_id'] = self.env['ir.config_parameter'].sudo().get_param( + 'openrouter_model_id') + res['max_token'] = self.env['ir.config_parameter'].sudo().get_param('max_token') + return res + + @api.onchange('generative_ai_systems') + def _onchange_generative_ai_systems(self): + """Clear model selection when system or API key changes""" + if not self.generative_ai_systems or not self.api_key: + self.model_id = False + self.openrouter_model_id = False + + def _get_model_selection(self): + """Return list of tuples for model selection""" + try: + config_system = self.env['ir.config_parameter'].sudo().get_param( + 'generative_ai_systems') + config_key = self.env['ir.config_parameter'].sudo().get_param('api_key') + ai_system = self.generative_ai_systems or config_system + api_key = self.api_key or config_key + if ai_system and api_key: + if ai_system == 'openrouter': + url = "https://openrouter.ai/api/v1/models" + # Required headers for OpenRouter API + headers = { + 'Authorization': f'Bearer {api_key}', + 'HTTP-Referer': self.env['ir.config_parameter'].sudo().get_param( + 'web.base.url', ''), + 'X-Title': 'Odoo Integration' + } + response = requests.get(url, headers=headers, timeout=10) + if response.status_code == 200: + models = response.json().get('data', + []) # OpenRouter returns models in 'data' field + if models: + model_list = [(str(model['id']), str(model['name'])) for model + in models] + return model_list + else: + return [('none', 'No Models Available')] + else: + return [('none', f'API Error: {response.status_code}')] + except requests.exceptions.RequestException: + return [('none', 'Connection Error')] + except Exception: + return [('none', 'Error Loading Models')] + return [('none', 'No Models Available')] + + def set_values(self): + """Save settings and fetch models""" + super(ResConfigSettings, self).set_values() + self.env['ir.config_parameter'].sudo().set_param('generative_ai_systems', + self.generative_ai_systems) + self.env['ir.config_parameter'].sudo().set_param('api_key', self.api_key) + self.env['ir.config_parameter'].sudo().set_param('max_token', self.max_token) + if self.generative_ai_systems == 'openrouter': + self.env['ir.config_parameter'].sudo().set_param('openrouter_model_id', + self.openrouter_model_id) + self.env['ir.config_parameter'].sudo().set_param('model_id', + '') + else: + self.env['ir.config_parameter'].sudo().set_param('model_id', self.model_id) + if self.generative_ai_systems and self.api_key: + models = self._get_model_selection() + if models and models != [('none', 'No Models Available')]: + # Only save model_id if it's in the list of available models + if self.openrouter_model_id and any( + self.openrouter_model_id == model[0] for model in models): + self.env['ir.config_parameter'].sudo().set_param( + 'openrouter_model_id', self.openrouter_model_id) + else: + # If current model_id is not in available models, set to first available model + self.env['ir.config_parameter'].sudo().set_param( + 'openrouter_model_id', models[0][0]) + self.openrouter_model_id = models[0][0] + else: + self.env['ir.config_parameter'].sudo().set_param('openrouter_model_id', '') + self.openrouter_model_id = False diff --git a/generative_ai/models/website_snippet_data.py b/generative_ai/models/website_snippet_data.py new file mode 100644 index 000000000..2c6093312 --- /dev/null +++ b/generative_ai/models/website_snippet_data.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies(). +# Author: Cybrosys Techno Solutions() +# +# 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 . +# +############################################################################# +from markupsafe import Markup +from odoo import fields, models + + +class WebsiteSnippetData(models.Model): + _name = 'website.snippet.data' + _description = 'AI Generated Website Snippet' + + name = fields.Char(string="Snippet Name", required=True) + content = fields.Text(string="Snippet Content", required=True) + image_url = fields.Char(string="Image URL") + create_date = fields.Datetime(string="Created Date", readonly=True) + is_ai_generated = fields.Boolean(string="AI Generated") + + def get_content(self): + return Markup(self.content) diff --git a/generative_ai/security/ir.model.access.csv b/generative_ai/security/ir.model.access.csv new file mode 100644 index 000000000..c87c0e4c8 --- /dev/null +++ b/generative_ai/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_website_snippet_data,access.website.snippet.data,model_website_snippet_data,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/generative_ai/static/description/assets/cybro-icon.png b/generative_ai/static/description/assets/cybro-icon.png new file mode 100644 index 000000000..06e73e11d Binary files /dev/null and b/generative_ai/static/description/assets/cybro-icon.png differ diff --git a/generative_ai/static/description/assets/cybro-odoo.png b/generative_ai/static/description/assets/cybro-odoo.png new file mode 100644 index 000000000..ed02e07a4 Binary files /dev/null and b/generative_ai/static/description/assets/cybro-odoo.png differ diff --git a/generative_ai/static/description/assets/h2.png b/generative_ai/static/description/assets/h2.png new file mode 100755 index 000000000..0bfc4707d Binary files /dev/null and b/generative_ai/static/description/assets/h2.png differ diff --git a/generative_ai/static/description/assets/icons/arrows-repeat.svg b/generative_ai/static/description/assets/icons/arrows-repeat.svg new file mode 100644 index 000000000..1d7efabc5 --- /dev/null +++ b/generative_ai/static/description/assets/icons/arrows-repeat.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/banner-1.png b/generative_ai/static/description/assets/icons/banner-1.png new file mode 100644 index 000000000..c180db172 Binary files /dev/null and b/generative_ai/static/description/assets/icons/banner-1.png differ diff --git a/generative_ai/static/description/assets/icons/banner-2.svg b/generative_ai/static/description/assets/icons/banner-2.svg new file mode 100644 index 000000000..e606d97d9 --- /dev/null +++ b/generative_ai/static/description/assets/icons/banner-2.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/banner-bg.png b/generative_ai/static/description/assets/icons/banner-bg.png new file mode 100644 index 000000000..a8238d3c0 Binary files /dev/null and b/generative_ai/static/description/assets/icons/banner-bg.png differ diff --git a/generative_ai/static/description/assets/icons/banner-bg.svg b/generative_ai/static/description/assets/icons/banner-bg.svg new file mode 100644 index 000000000..b1378103e --- /dev/null +++ b/generative_ai/static/description/assets/icons/banner-bg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/banner-call.svg b/generative_ai/static/description/assets/icons/banner-call.svg new file mode 100644 index 000000000..96c687e81 --- /dev/null +++ b/generative_ai/static/description/assets/icons/banner-call.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/generative_ai/static/description/assets/icons/banner-mail.svg b/generative_ai/static/description/assets/icons/banner-mail.svg new file mode 100644 index 000000000..cbf0d158d --- /dev/null +++ b/generative_ai/static/description/assets/icons/banner-mail.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/generative_ai/static/description/assets/icons/banner-pattern.svg b/generative_ai/static/description/assets/icons/banner-pattern.svg new file mode 100644 index 000000000..9c1c7e101 --- /dev/null +++ b/generative_ai/static/description/assets/icons/banner-pattern.svg @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/banner-promo.svg b/generative_ai/static/description/assets/icons/banner-promo.svg new file mode 100644 index 000000000..d52791b11 --- /dev/null +++ b/generative_ai/static/description/assets/icons/banner-promo.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/brand-pair.svg b/generative_ai/static/description/assets/icons/brand-pair.svg new file mode 100644 index 000000000..d8db7fc1e --- /dev/null +++ b/generative_ai/static/description/assets/icons/brand-pair.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/check.png b/generative_ai/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/generative_ai/static/description/assets/icons/check.png differ diff --git a/generative_ai/static/description/assets/icons/chevron.png b/generative_ai/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/generative_ai/static/description/assets/icons/chevron.png differ diff --git a/generative_ai/static/description/assets/icons/close-icon.svg b/generative_ai/static/description/assets/icons/close-icon.svg new file mode 100644 index 000000000..df8cce37a --- /dev/null +++ b/generative_ai/static/description/assets/icons/close-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/generative_ai/static/description/assets/icons/cogs.png b/generative_ai/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/generative_ai/static/description/assets/icons/cogs.png differ diff --git a/generative_ai/static/description/assets/icons/collabarate-icon.svg b/generative_ai/static/description/assets/icons/collabarate-icon.svg new file mode 100644 index 000000000..dd4e10518 --- /dev/null +++ b/generative_ai/static/description/assets/icons/collabarate-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/generative_ai/static/description/assets/icons/consultation.png b/generative_ai/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/generative_ai/static/description/assets/icons/consultation.png differ diff --git a/generative_ai/static/description/assets/icons/cybro-logo.png b/generative_ai/static/description/assets/icons/cybro-logo.png new file mode 100644 index 000000000..ff4b78220 Binary files /dev/null and b/generative_ai/static/description/assets/icons/cybro-logo.png differ diff --git a/generative_ai/static/description/assets/icons/down.svg b/generative_ai/static/description/assets/icons/down.svg new file mode 100644 index 000000000..f21c36271 --- /dev/null +++ b/generative_ai/static/description/assets/icons/down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/generative_ai/static/description/assets/icons/ecom-black.png b/generative_ai/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/generative_ai/static/description/assets/icons/ecom-black.png differ diff --git a/generative_ai/static/description/assets/icons/education-black.png b/generative_ai/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/generative_ai/static/description/assets/icons/education-black.png differ diff --git a/generative_ai/static/description/assets/icons/faq.png b/generative_ai/static/description/assets/icons/faq.png new file mode 100644 index 000000000..4250b5b81 Binary files /dev/null and b/generative_ai/static/description/assets/icons/faq.png differ diff --git a/generative_ai/static/description/assets/icons/feature-icon.svg b/generative_ai/static/description/assets/icons/feature-icon.svg new file mode 100644 index 000000000..fa0ea6850 --- /dev/null +++ b/generative_ai/static/description/assets/icons/feature-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/feature.png b/generative_ai/static/description/assets/icons/feature.png new file mode 100644 index 000000000..ac7a785c0 Binary files /dev/null and b/generative_ai/static/description/assets/icons/feature.png differ diff --git a/generative_ai/static/description/assets/icons/gear.svg b/generative_ai/static/description/assets/icons/gear.svg new file mode 100644 index 000000000..0cc66b6ea --- /dev/null +++ b/generative_ai/static/description/assets/icons/gear.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/hero.gif b/generative_ai/static/description/assets/icons/hero.gif new file mode 100644 index 000000000..d28160470 Binary files /dev/null and b/generative_ai/static/description/assets/icons/hero.gif differ diff --git a/generative_ai/static/description/assets/icons/hire-odoo.svg b/generative_ai/static/description/assets/icons/hire-odoo.svg new file mode 100644 index 000000000..e1ac089b0 --- /dev/null +++ b/generative_ai/static/description/assets/icons/hire-odoo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/hotel-black.png b/generative_ai/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/generative_ai/static/description/assets/icons/hotel-black.png differ diff --git a/generative_ai/static/description/assets/icons/license.png b/generative_ai/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/generative_ai/static/description/assets/icons/license.png differ diff --git a/generative_ai/static/description/assets/icons/life-ring-icon.svg b/generative_ai/static/description/assets/icons/life-ring-icon.svg new file mode 100644 index 000000000..3ae6e1d89 --- /dev/null +++ b/generative_ai/static/description/assets/icons/life-ring-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/lifebuoy.png b/generative_ai/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/generative_ai/static/description/assets/icons/lifebuoy.png differ diff --git a/generative_ai/static/description/assets/icons/mail.svg b/generative_ai/static/description/assets/icons/mail.svg new file mode 100644 index 000000000..1eedde695 --- /dev/null +++ b/generative_ai/static/description/assets/icons/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/generative_ai/static/description/assets/icons/manufacturing-black.png b/generative_ai/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/generative_ai/static/description/assets/icons/manufacturing-black.png differ diff --git a/generative_ai/static/description/assets/icons/notes.png b/generative_ai/static/description/assets/icons/notes.png new file mode 100644 index 000000000..ee5e95404 Binary files /dev/null and b/generative_ai/static/description/assets/icons/notes.png differ diff --git a/generative_ai/static/description/assets/icons/notification icon.svg b/generative_ai/static/description/assets/icons/notification icon.svg new file mode 100644 index 000000000..053189973 --- /dev/null +++ b/generative_ai/static/description/assets/icons/notification icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/odoo-consultancy.svg b/generative_ai/static/description/assets/icons/odoo-consultancy.svg new file mode 100644 index 000000000..e05f65bde --- /dev/null +++ b/generative_ai/static/description/assets/icons/odoo-consultancy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/generative_ai/static/description/assets/icons/odoo-licencing.svg b/generative_ai/static/description/assets/icons/odoo-licencing.svg new file mode 100644 index 000000000..2606c88b0 --- /dev/null +++ b/generative_ai/static/description/assets/icons/odoo-licencing.svg @@ -0,0 +1,3 @@ + + + diff --git a/generative_ai/static/description/assets/icons/odoo-logo.png b/generative_ai/static/description/assets/icons/odoo-logo.png new file mode 100644 index 000000000..0e4d0eb5a Binary files /dev/null and b/generative_ai/static/description/assets/icons/odoo-logo.png differ diff --git a/generative_ai/static/description/assets/icons/patter.svg b/generative_ai/static/description/assets/icons/patter.svg new file mode 100644 index 000000000..25c9c0a8f --- /dev/null +++ b/generative_ai/static/description/assets/icons/patter.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/pattern1.png b/generative_ai/static/description/assets/icons/pattern1.png new file mode 100644 index 000000000..09ab0fb2d Binary files /dev/null and b/generative_ai/static/description/assets/icons/pattern1.png differ diff --git a/generative_ai/static/description/assets/icons/pos-black.png b/generative_ai/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/generative_ai/static/description/assets/icons/pos-black.png differ diff --git a/generative_ai/static/description/assets/icons/puzzle-piece-icon.svg b/generative_ai/static/description/assets/icons/puzzle-piece-icon.svg new file mode 100644 index 000000000..3e9ad9373 --- /dev/null +++ b/generative_ai/static/description/assets/icons/puzzle-piece-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/puzzle.png b/generative_ai/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/generative_ai/static/description/assets/icons/puzzle.png differ diff --git a/generative_ai/static/description/assets/icons/replace-icon.svg b/generative_ai/static/description/assets/icons/replace-icon.svg new file mode 100644 index 000000000..d0e3a7af1 --- /dev/null +++ b/generative_ai/static/description/assets/icons/replace-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/restaurant-black.png b/generative_ai/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/generative_ai/static/description/assets/icons/restaurant-black.png differ diff --git a/generative_ai/static/description/assets/icons/screenshot-main.png b/generative_ai/static/description/assets/icons/screenshot-main.png new file mode 100644 index 000000000..575f8e676 Binary files /dev/null and b/generative_ai/static/description/assets/icons/screenshot-main.png differ diff --git a/generative_ai/static/description/assets/icons/screenshot.png b/generative_ai/static/description/assets/icons/screenshot.png new file mode 100644 index 000000000..cef272529 Binary files /dev/null and b/generative_ai/static/description/assets/icons/screenshot.png differ diff --git a/generative_ai/static/description/assets/icons/service-black.png b/generative_ai/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/generative_ai/static/description/assets/icons/service-black.png differ diff --git a/generative_ai/static/description/assets/icons/skype-fill.svg b/generative_ai/static/description/assets/icons/skype-fill.svg new file mode 100644 index 000000000..c17423639 --- /dev/null +++ b/generative_ai/static/description/assets/icons/skype-fill.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/skype.png b/generative_ai/static/description/assets/icons/skype.png new file mode 100644 index 000000000..51b409fb3 Binary files /dev/null and b/generative_ai/static/description/assets/icons/skype.png differ diff --git a/generative_ai/static/description/assets/icons/skype.svg b/generative_ai/static/description/assets/icons/skype.svg new file mode 100644 index 000000000..df3dad39b --- /dev/null +++ b/generative_ai/static/description/assets/icons/skype.svg @@ -0,0 +1,3 @@ + + + diff --git a/generative_ai/static/description/assets/icons/star-1.svg b/generative_ai/static/description/assets/icons/star-1.svg new file mode 100644 index 000000000..7e55ab162 --- /dev/null +++ b/generative_ai/static/description/assets/icons/star-1.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/star-2.svg b/generative_ai/static/description/assets/icons/star-2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/generative_ai/static/description/assets/icons/star-2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/support.png b/generative_ai/static/description/assets/icons/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/generative_ai/static/description/assets/icons/support.png differ diff --git a/generative_ai/static/description/assets/icons/test-1 - Copy.png b/generative_ai/static/description/assets/icons/test-1 - Copy.png new file mode 100644 index 000000000..f6a902663 Binary files /dev/null and b/generative_ai/static/description/assets/icons/test-1 - Copy.png differ diff --git a/generative_ai/static/description/assets/icons/test-1.png b/generative_ai/static/description/assets/icons/test-1.png new file mode 100644 index 000000000..0908add2b Binary files /dev/null and b/generative_ai/static/description/assets/icons/test-1.png differ diff --git a/generative_ai/static/description/assets/icons/test-2.png b/generative_ai/static/description/assets/icons/test-2.png new file mode 100644 index 000000000..4671fe91e Binary files /dev/null and b/generative_ai/static/description/assets/icons/test-2.png differ diff --git a/generative_ai/static/description/assets/icons/trading-black.png b/generative_ai/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/generative_ai/static/description/assets/icons/trading-black.png differ diff --git a/generative_ai/static/description/assets/icons/training.png b/generative_ai/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/generative_ai/static/description/assets/icons/training.png differ diff --git a/generative_ai/static/description/assets/icons/translate.svg b/generative_ai/static/description/assets/icons/translate.svg new file mode 100644 index 000000000..af9c8a1aa --- /dev/null +++ b/generative_ai/static/description/assets/icons/translate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/update.png b/generative_ai/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/generative_ai/static/description/assets/icons/update.png differ diff --git a/generative_ai/static/description/assets/icons/user.png b/generative_ai/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/generative_ai/static/description/assets/icons/user.png differ diff --git a/generative_ai/static/description/assets/icons/video.png b/generative_ai/static/description/assets/icons/video.png new file mode 100644 index 000000000..576705b17 Binary files /dev/null and b/generative_ai/static/description/assets/icons/video.png differ diff --git a/generative_ai/static/description/assets/icons/whatsapp.png b/generative_ai/static/description/assets/icons/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/generative_ai/static/description/assets/icons/whatsapp.png differ diff --git a/generative_ai/static/description/assets/icons/wrench-icon.svg b/generative_ai/static/description/assets/icons/wrench-icon.svg new file mode 100644 index 000000000..174b5a465 --- /dev/null +++ b/generative_ai/static/description/assets/icons/wrench-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/generative_ai/static/description/assets/icons/wrench.png b/generative_ai/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/generative_ai/static/description/assets/icons/wrench.png differ diff --git a/generative_ai/static/description/assets/modules/b1.png b/generative_ai/static/description/assets/modules/b1.png new file mode 100644 index 000000000..6e617f3d3 Binary files /dev/null and b/generative_ai/static/description/assets/modules/b1.png differ diff --git a/generative_ai/static/description/assets/modules/b2.png b/generative_ai/static/description/assets/modules/b2.png new file mode 100644 index 000000000..696582fa8 Binary files /dev/null and b/generative_ai/static/description/assets/modules/b2.png differ diff --git a/generative_ai/static/description/assets/modules/b3.png b/generative_ai/static/description/assets/modules/b3.png new file mode 100644 index 000000000..cf81c09f8 Binary files /dev/null and b/generative_ai/static/description/assets/modules/b3.png differ diff --git a/generative_ai/static/description/assets/modules/b4.png b/generative_ai/static/description/assets/modules/b4.png new file mode 100644 index 000000000..206e14c47 Binary files /dev/null and b/generative_ai/static/description/assets/modules/b4.png differ diff --git a/generative_ai/static/description/assets/modules/b5.png b/generative_ai/static/description/assets/modules/b5.png new file mode 100644 index 000000000..1b0ce4674 Binary files /dev/null and b/generative_ai/static/description/assets/modules/b5.png differ diff --git a/generative_ai/static/description/assets/modules/b6.png b/generative_ai/static/description/assets/modules/b6.png new file mode 100644 index 000000000..0249a98f1 Binary files /dev/null and b/generative_ai/static/description/assets/modules/b6.png differ diff --git a/generative_ai/static/description/assets/screenshots/Screenshot1.png b/generative_ai/static/description/assets/screenshots/Screenshot1.png new file mode 100644 index 000000000..1b1634387 Binary files /dev/null and b/generative_ai/static/description/assets/screenshots/Screenshot1.png differ diff --git a/generative_ai/static/description/assets/screenshots/Screenshot2.png b/generative_ai/static/description/assets/screenshots/Screenshot2.png new file mode 100644 index 000000000..f5890ca25 Binary files /dev/null and b/generative_ai/static/description/assets/screenshots/Screenshot2.png differ diff --git a/generative_ai/static/description/assets/screenshots/Screenshot3.png b/generative_ai/static/description/assets/screenshots/Screenshot3.png new file mode 100644 index 000000000..761f1ee1d Binary files /dev/null and b/generative_ai/static/description/assets/screenshots/Screenshot3.png differ diff --git a/generative_ai/static/description/assets/screenshots/Screenshot4.png b/generative_ai/static/description/assets/screenshots/Screenshot4.png new file mode 100644 index 000000000..45aef633c Binary files /dev/null and b/generative_ai/static/description/assets/screenshots/Screenshot4.png differ diff --git a/generative_ai/static/description/assets/screenshots/Screenshot5.png b/generative_ai/static/description/assets/screenshots/Screenshot5.png new file mode 100644 index 000000000..e9819b108 Binary files /dev/null and b/generative_ai/static/description/assets/screenshots/Screenshot5.png differ diff --git a/generative_ai/static/description/assets/screenshots/Screenshot6.png b/generative_ai/static/description/assets/screenshots/Screenshot6.png new file mode 100644 index 000000000..eca7528b8 Binary files /dev/null and b/generative_ai/static/description/assets/screenshots/Screenshot6.png differ diff --git a/generative_ai/static/description/assets/y18.jpg b/generative_ai/static/description/assets/y18.jpg new file mode 100755 index 000000000..eea1714f2 Binary files /dev/null and b/generative_ai/static/description/assets/y18.jpg differ diff --git a/generative_ai/static/description/banner.jpg b/generative_ai/static/description/banner.jpg new file mode 100644 index 000000000..c47f4435d Binary files /dev/null and b/generative_ai/static/description/banner.jpg differ diff --git a/generative_ai/static/description/hero.gif b/generative_ai/static/description/hero.gif new file mode 100644 index 000000000..d1421ecc9 Binary files /dev/null and b/generative_ai/static/description/hero.gif differ diff --git a/generative_ai/static/description/icon.png b/generative_ai/static/description/icon.png new file mode 100644 index 000000000..1f631f241 Binary files /dev/null and b/generative_ai/static/description/icon.png differ diff --git a/generative_ai/static/description/index.html b/generative_ai/static/description/index.html new file mode 100644 index 000000000..c6fb4d739 --- /dev/null +++ b/generative_ai/static/description/index.html @@ -0,0 +1,857 @@ + + + + + + Generative AI + + + + + + + + + + +
+
+ + + +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+
+

+ Generates responsive website snippets from text prompts using AI. +

+

Generative AI +

+
+
+ +
+ + + + + + +
+
+ +
+
+ +
+
+ +
+
+
+

Key + Highlights

+
+
+
+
+ +
+
+ Easy to set up. +
+

+

+
+
+
+
+
+ +
+
+ AI-powered snippet creation. +
+

+

+
+
+
+
+
+ +
+
+ Unique preview & styling. +
+

+ +

+
+
+ +
+
+ +
+
+
+ Generative AI +

+ Are you ready to make your business more + organized? +
Improve now! +

+ +
+
+ +
+
+
+ + + + +
+
+ +
+
+
+
+ acc_bg +
+ +
+
+
+
+

+ Navigate to the + + AI tools section. +

+
+
+

+ In Settings → Website → Configuration → AI tools section, + select your AI system (OpenAI or OpenRouter), enter your API key, + set maximum tokens and choose your preferred AI model. +

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Click + + Edit Button. +

+
+
+

+ Click the “Edit” button to open the Website Editor Sidebar. + +

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Open + + Add Snippet Dialog. +

+
+
+

+ Click AI Generated to view the Add Snippet Dialog. +

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Click + + + Generate with AI. +

+
+
+

+ Click Generate with AI to open the Generate Snippet with AI dialog box. +

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Generate + + + Snippet. +

+
+
+

+ Enter the snippet name and prompt, and click Generate. +

+
+
+
+ +
+
+
+

+ You can view the newly created snippet in the Add Snippet dialog box. +

+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+

+ Instantly generate custom website snippets from text prompts using + OpenAI or OpenRouter. +

+
+ +
+
+
+
+
+
+ +
+

+ Create, preview, and insert AI-generated snippets directly within + Odoo’s website editor. +

+
+ +
+
+
+
+
+
+ + + +
+

+ Related Products +

+ +
+ + +
+

+ Our Services

+ +
+ + +
+
+ .... +
+
+ +
+ + +
+
+ + + + + + diff --git a/generative_ai/static/src/css/snippets.css b/generative_ai/static/src/css/snippets.css new file mode 100644 index 000000000..ebecbc57c --- /dev/null +++ b/generative_ai/static/src/css/snippets.css @@ -0,0 +1,3 @@ +.s_ai_snippet .loading-spinner { + min-height: 100px; +} \ No newline at end of file diff --git a/generative_ai/static/src/img/placeholder.png b/generative_ai/static/src/img/placeholder.png new file mode 100644 index 000000000..344223469 Binary files /dev/null and b/generative_ai/static/src/img/placeholder.png differ diff --git a/generative_ai/static/src/js/ai_button_action.js b/generative_ai/static/src/js/ai_button_action.js new file mode 100644 index 000000000..24ae560e9 --- /dev/null +++ b/generative_ai/static/src/js/ai_button_action.js @@ -0,0 +1,117 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { useService } from "@web/core/utils/hooks"; +import { rpc } from "@web/core/network/rpc"; +import { Component, xml, useRef, useState } from "@odoo/owl"; +import { AddSnippetDialog } from "@web_editor/js/editor/add_snippet_dialog"; + + +// Define a custom Owl component for the AI Prompt Modal +class AIPromptModal extends Component { + setup() { + this.orm = useService("orm"); + this.notification = useService("notification"); + this.textAreaRef = useRef('textAreaRef'); + this.nameRef = useRef('snippetNameRef'); + this.state = useState({ + isLoading: false, + }); + } + // Close the modal (disabled if a request is loading) + onClose() { + if (this.state.isLoading) { + return; + } + this.props.close(); + } + async onSubmit() { + const promptValue = this.textAreaRef.el.value; + const snippetName = this.nameRef.el.value; + + if (!promptValue || !snippetName) { + this.notification.add( + "Please fill in both the prompt and snippet name", + { type: "warning" } + ); + return; + } + try { + this.state.isLoading = true; + const result = await rpc('/website/generate_snippet', { + prompt: promptValue, + name: snippetName + }); + if (result.error) { + this.notification.add(result.error, { type: "danger" }); + } else { + this.notification.add( + "Snippet generated successfully! Refreshing snippet panel...", + { type: "success" } + ); + window.location.reload(); + } + } catch (error) { + this.notification.add( + "Failed to generate snippet: " + error, + { type: "danger" } + ); + } finally { + this.state.isLoading = false; + } + this.props.close(); + } +} + +// Define the template for the modal +AIPromptModal.template = xml` +