Compare commits

..

131 Commits

Author SHA1 Message Date
Oliver
30ed470a1a new chat url 2025-10-29 11:15:55 -03:00
hil
4320ef22b1 Update Posts 2025-10-29 03:00:10 +00:00
Oliver
c06b9f27b5 meppel 2025-10-25 05:47:24 -03:00
Oliver
88e38a7c00 sp+mb 2025-10-23 11:51:53 -03:00
Oliver
a1035c87d6 added server 2025-10-23 11:46:07 -03:00
Oliver
ab6ed50cd9 image 2025-10-17 06:34:38 -03:00
hil
bf29824c0b Update Posts 2025-10-14 09:23:18 +00:00
Oliver
e056e8e01f hil 2025-10-13 05:48:12 -03:00
hil
7ab6dde1a4 Update Posts 2025-10-11 08:07:23 +00:00
hil
7cd8bc54e9 Update Posts 2025-10-07 03:00:46 +00:00
Oliver
c462d75c4c show 2025-10-04 19:07:47 -03:00
Oliver
88a99d4942 fix 2025-10-04 19:00:07 -03:00
Oliver
04555d17ff fix 2025-10-04 18:58:34 -03:00
Oliver
a57452e764 namespace 2025-10-04 18:51:19 -03:00
Oliver
b33b93d3b7 a 2025-10-04 18:43:39 -03:00
Oliver
12b888a678 tryNow 2025-10-04 18:37:34 -03:00
hil
8849e5035c Update Posts 2025-10-04 20:35:52 +00:00
hil
7a9874b6ec Update Posts 2025-10-04 20:32:34 +00:00
Oliver
a14f49fdb4 fix no open form 2025-10-04 07:09:40 -03:00
Oliver
baad50071d yea 2025-10-04 06:54:04 -03:00
Oliver
e8fa02b6b6 remove _ 2025-10-04 06:49:26 -03:00
Oliver
f72690988e close 2025-10-04 06:34:45 -03:00
Oliver
4aea655837 d 2025-10-04 06:25:56 -03:00
Oliver
fa6f967778 loaction select 2025-10-04 06:25:56 -03:00
hil
e81876e542 Update Posts 2025-10-03 15:12:57 +00:00
Oliver
3459deb219 a 2025-10-03 12:12:44 -03:00
Oliver
59501a259e ass 2025-10-03 11:00:38 -03:00
Oliver
3c94fb782f ass 2025-10-03 10:58:00 -03:00
Oliver
7f77ffb6d0 f 2025-10-03 10:46:36 -03:00
Oliver
7461225230 ds 2025-10-03 10:42:13 -03:00
Oliver
e681478550 hmm 2025-10-03 10:35:35 -03:00
Oliver
b3a16f81c8 hmm 2025-10-03 10:17:47 -03:00
Oliver
f3c2acfae7 fixes 2025-10-03 10:12:55 -03:00
Oliver
53204119a1 changes 2025-10-03 10:07:24 -03:00
hil
8232058ec5 Update Posts 2025-10-03 12:11:35 +00:00
Oliver
d82a0dc71c prices 2025-10-03 09:11:15 -03:00
Oliver
e7c48b9eb3 spelling 2025-10-03 06:42:08 -03:00
Oliver
9e263483c0 webook 2025-10-03 06:39:08 -03:00
hil
dd5b49b46a Update Posts 2025-10-03 09:35:00 +00:00
Oliver
f71c8f3da7 update 2025-10-03 06:34:06 -03:00
Oliver
478e23b337 agenda 2025-10-03 06:21:27 -03:00
Oliver
73c0be90f2 flyer 2025-10-03 06:19:36 -03:00
Oliver
f29022d6e0 buymodal 2025-10-02 19:52:08 -03:00
hil
f3b715f87c Update Posts 2025-10-02 14:48:15 +00:00
Oliver
96e38f194f WTF? 2025-10-02 11:46:51 -03:00
Oliver
499e49360a images 2025-10-01 16:12:46 -03:00
hil
dc7218ba82 Update Posts 2025-10-01 15:36:55 +00:00
Oliver
48d152e6e4 image 2025-10-01 12:34:56 -03:00
hil
cc0364974e Update Posts 2025-10-01 14:25:49 +00:00
hil
7e3ea8a2e6 Update Posts 2025-10-01 10:55:27 +00:00
Oliver
5bb66af177 w 2025-10-01 07:51:38 -03:00
Oliver
95f66ce8bb w 2025-10-01 07:51:06 -03:00
Oliver
a88559b4ad w 2025-10-01 07:45:52 -03:00
hil
ad8acab32c Update Posts 2025-10-01 10:44:24 +00:00
Oliver
136c189224 added training 2025-10-01 07:44:16 -03:00
hil
be856f3a43 Update Posts 2025-10-01 10:41:21 +00:00
hil
286c7bfa10 Update Posts 2025-10-01 10:38:02 +00:00
hil
098e275871 Update Posts 2025-10-01 10:35:32 +00:00
hil
8e99082864 Update Posts 2025-10-01 10:35:08 +00:00
hil
2e584f4a9c Update Posts 2025-10-01 10:28:48 +00:00
hil
a4192411a6 Update Posts 2025-10-01 10:24:00 +00:00
Oliver
d99446a317 added image 2025-10-01 06:13:31 -03:00
Oliver
aaa94dc559 update to ODOO 19 2025-09-25 16:06:08 -03:00
Oliver
74edc1c870 added ODOO 2025-09-25 14:44:16 -03:00
Oliver
859840c63e workshops 2025-09-25 11:37:41 -03:00
Oliver
ef82235057 . 2025-09-01 19:23:11 +02:00
Oliver
6adf95ea2f boston 2025-09-01 19:20:25 +02:00
Oliver
2d5edcbdfb pl 2025-09-01 19:11:09 +02:00
Oliver
ff580e0d20 trial 2025-09-01 09:31:31 +02:00
Oliver
b440f99449 remove boston 2025-09-01 07:36:42 +02:00
Oliver
f2bb664baa remove text add boston 2025-09-01 07:29:51 +02:00
Oliver
9a11d86497 hide ODOO 2025-09-01 07:27:16 +02:00
Oliver
15a755937c right url 2025-08-31 15:42:51 +02:00
Oliver
b3adf938c3 new url 2025-08-31 15:34:55 +02:00
Oliver
eea991a680 working form 2025-08-31 15:34:55 +02:00
hil
72eb69e6cc Update Posts 2025-08-07 19:26:05 +00:00
hil
8c84296948 Update Posts 2025-08-07 19:03:42 +00:00
Oliver
95f5b955b4 bog container 2025-08-07 16:02:55 -03:00
hil
8107e6003b Update Posts 2025-08-07 18:55:51 +00:00
hil
5abd47ab7e Update Posts 2025-08-07 18:54:16 +00:00
hil
382b02c5e8 Update Posts 2025-08-07 18:50:54 +00:00
hil
d975586f89 Update Posts 2025-08-07 18:40:19 +00:00
hil
cc47d591cf Update Posts 2025-08-07 18:33:54 +00:00
hil
30b24a0fe1 Update Posts 2025-08-07 18:27:59 +00:00
Oliver
64c6ba4b22 new design 2025-08-07 15:27:30 -03:00
Oliver
d57a28d7a6 hostinger image 2025-08-03 06:24:19 -03:00
Oliver
55536a20ed hostinger image 2025-08-03 06:24:19 -03:00
hil
cbf8ab4622 Update Posts 2025-07-31 18:01:55 +00:00
hil
64ca9123b2 Update Posts 2025-07-31 17:51:53 +00:00
hil
2805eb94a2 Update Posts 2025-07-31 17:49:42 +00:00
hil
8b0cae4437 Update Posts 2025-07-31 17:46:52 +00:00
hil
80715c0640 Update Posts 2025-07-31 17:45:40 +00:00
Oliver
26440cbffb server 2025-07-31 14:45:15 -03:00
hil
de8d1af83d Update Posts 2025-07-31 17:42:46 +00:00
hil
1745ebce21 Update Posts 2025-07-31 17:02:26 +00:00
hil
1b95180ca6 Update Posts 2025-07-31 17:01:04 +00:00
hil
02e2c14a5c Update Posts 2025-07-31 16:50:50 +00:00
Oliver
aa1ed0129d add template 2025-07-31 13:33:27 -03:00
Oliver
d19aa9196a read post 2025-07-31 09:23:13 -03:00
Oliver
96fbab191e custom events 2025-07-31 09:20:34 -03:00
Oliver
8eb0deda05 1st post 2025-07-30 18:13:08 -03:00
Oliver
ae00faa031 hang up 2025-07-29 12:14:20 -03:00
Oliver
95f1cb9a4c css 2025-07-29 12:10:31 -03:00
Oliver
53acd80b21 css 2025-07-29 12:07:35 -03:00
Oliver
c73d566c25 css 2025-07-29 12:04:48 -03:00
Oliver
d20d5706aa fix 2025-07-29 11:58:14 -03:00
Oliver
d0e361b154 fix 2025-07-29 11:49:34 -03:00
Oliver
cd25240ad1 add 2025-07-29 11:45:48 -03:00
Oliver
2e12c7e685 d 2025-07-28 18:59:24 -03:00
Oliver
ed23263084 JS sucks 2025-07-28 18:57:49 -03:00
Oliver
58cca9e3d2 DOM Load 2025-07-28 18:53:36 -03:00
Oliver
a8a4203778 DOM Load 2025-07-28 18:52:33 -03:00
Oliver
979e4f9ac0 lets see 2025-07-28 18:48:18 -03:00
Oliver
4629df910a lets see 2025-07-28 18:46:28 -03:00
Oliver
1798c48d3b elevenlab 2025-07-28 18:37:49 -03:00
Oliver
2122dd6602 logo 2025-07-28 12:26:01 -03:00
Oliver
da5202b99e logo 2025-07-28 12:15:53 -03:00
Oliver
d07cba231f logo 2025-07-28 12:13:22 -03:00
Oliver
8d1576eee7 ci 2025-07-28 12:09:24 -03:00
Oliver
682d820f2c css 2025-07-28 08:35:19 -03:00
Oliver
e0444dbd47 order 2025-07-28 08:27:13 -03:00
Oliver
5f419d580f url fix 2025-07-28 08:24:37 -03:00
Oliver
95771e4f54 brandize 2025-07-28 08:22:53 -03:00
Oliver
6511c71a03 config 2025-07-28 07:56:25 -03:00
Oliver
d8f7706c33 s 2025-07-28 07:54:01 -03:00
Oliver
e927ae5de6 url 2025-07-28 07:52:51 -03:00
Oliver
8d7cc60518 wizzard 2025-07-28 07:50:15 -03:00
Oliver
7695e30d59 working 2025-07-28 07:22:36 -03:00
Oliver
abe9ae4712 working 2025-07-28 05:32:39 -03:00
Oliver
948e09b621 not working 2025-07-27 18:08:01 -03:00
Oliver
16e9ad67e9 overflow 2025-07-26 15:55:40 -03:00
49 changed files with 4169 additions and 543 deletions

BIN
public/agenda/q1-2026.pdf Normal file

Binary file not shown.

BIN
public/agenda/q2-2026.pdf Normal file

Binary file not shown.

BIN
public/agenda/q3-2026.pdf Normal file

Binary file not shown.

BIN
public/agenda/q4-2026.pdf Normal file

Binary file not shown.

View File

@@ -1,21 +1,82 @@
/**
* Function to handle the scroll behavior of the navigation bar
*/
let header = document.querySelector('.navbar-main');
let buttonMobile = document.getElementById('Burger-menu');
let nav = document.querySelector('nav');
let links = document.querySelectorAll('nav ul li a');
const scrollFunction = () => {
if (
document.body.scrollTop > 100 ||
document.documentElement.scrollTop > 100
) {
header.classList.add("bg");
} else {
header.classList.remove("bg");
}
};
window.addEventListener('scroll', scrollFunction);
window.addEventListener('load', scrollFunction);
/**
* Function to handle the accordion behavior
* for the FAQ section
*/
const accTitles = document.querySelectorAll('.accordion-title'); const accTitles = document.querySelectorAll('.accordion-title');
function closeAllAccordions(exceptThis = null) {
accTitles.forEach(title => {
if (title !== exceptThis) {
title.classList.remove('active');
const content = title.nextElementSibling;
content.style.maxHeight = null;
}
});
}
function openAccordion(title) {
const content = title.nextElementSibling;
requestAnimationFrame(() => {
content.style.maxHeight = content.scrollHeight + 'px';
});
title.classList.add('active');
}
accTitles.forEach(title => { accTitles.forEach(title => {
title.addEventListener('click', () => { title.addEventListener('click', () => {
accTitles.forEach(otherTitle => { const content = title.nextElementSibling;
if (otherTitle !== title) { const isOpen = title.classList.contains('active');
otherTitle.classList.remove('active');
otherTitle.nextElementSibling.style.display = 'none'; if (isOpen) {
// Close current
title.classList.remove('active');
content.style.maxHeight = null;
} else {
// Close others, open this
closeAllAccordions(title);
openAccordion(title);
} }
}); });
});
// Adjust height on resize (especially for images inside content)
window.addEventListener('resize', () => {
accTitles.forEach(title => {
if (title.classList.contains('active')) {
const content = title.nextElementSibling; const content = title.nextElementSibling;
const isOpen = content.style.display === 'block'; content.style.maxHeight = content.scrollHeight + 'px';
title.classList.toggle('active'); }
content.style.display = isOpen ? 'none' : 'block';
}); });
}); });
/**
* Function to handle team section slider
*/
const slides = document.querySelectorAll('.slide'); const slides = document.querySelectorAll('.slide');
let currentIndex = 0; let currentIndex = 0;
@@ -32,126 +93,40 @@ if (slides.length > 0) {
setInterval(nextSlide, 10000); setInterval(nextSlide, 10000);
} }
(function () { /**
const preamble = ` * Function to handle the blog teaser toggle
We use what we preach — this chatbot is powered by our own n8n automation 🤖 to help you quickly find what you need. * for showing full content
*/
Want to talk to a real automation expert? Just buy a service pack 💼 — it includes a 1-hour get-to-know session with our team 👥. function toggleContent(button) {
const teaser = button.parentElement;
If its not the right fit, no worries — well refund you 💸. const fullContent = teaser.querySelector('.blog-full-content');
`; if (fullContent.style.display === 'none') {
fullContent.style.display = 'block';
const logo="logo.svg" button.innerHTML = 'Read Less <span class="read-less-btn-icon"><i class="fa-solid fa-angle-up"></i></span>';
const api='https://ai.odoo4projects.com/webhook/81742473-b50b-4845-a5f9-916d9fe60876/chat' button.classList.add('read-less-btn');
// Elements
const chatToggle = document.getElementById('cw-chatToggle');
const chatWidget = document.getElementById('cw-chatWidget');
const chatForm = document.getElementById('cw-chatForm');
const chatInput = document.getElementById('cw-chatInput');
const chatMessages = document.getElementById('cw-chatMessages');
const sessionId = crypto.randomUUID();
let chatOpened = false;
function appendMessage(text, sender) {
const msg = document.createElement('div');
msg.classList.add('cw-message', sender === 'user' ? 'user' : 'bot');
msg.innerHTML = text;
chatMessages.appendChild(msg);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
chatToggle.addEventListener('click', () => {
const isVisible = chatWidget.classList.toggle('active');
if (isVisible && !chatOpened) {
appendMessage(
preamble,
'bot'
);
chatOpened = true;
}
});
chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
const input = chatInput.value.trim();
if (!input) return;
appendMessage(input, 'user');
chatInput.value = '';
chatInput.focus();
try {
const response = await fetch(api, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'sendMessage', sessionId, chatInput: input })
});
const data = await response.json();
console.log("12344")
const statusDiv = document.getElementById('cw-status');
console.log("ASS")
console.log(data)
if (data.status) {
statusDiv.innerHTML = data.status;
// Wait one frame to ensure DOM is updated
requestAnimationFrame(() => {
statusDiv.style.padding = '10px';
statusDiv.style.maxHeight = statusDiv.scrollHeight + 'px';
});
} else { } else {
statusDiv.style.maxHeight = '0'; fullContent.style.display = 'none';
statusDiv.style.padding = '0 10px'; button.innerHTML = 'Read More <span class="read-more-btn-icon"><i class="fa-solid fa-angle-right"></i></span>';
}
appendMessage(data.output, 'bot');
} catch (error) {
appendMessage('Fehler beim Verbinden mit dem Server. Bitte versuchen Sie es später erneut.', 'bot');
console.error(error);
}
});
function handleBuyClick(buttonId, message) {
const btn = document.getElementById(buttonId);
if (!btn) return;
btn.addEventListener('click', (e) => {
e.preventDefault();
if (!chatWidget.classList.contains('active')) {
chatWidget.classList.add('active');
if (!chatOpened) {
appendMessage(preamble, 'bot');
chatOpened = true;
} }
} }
appendMessage(message, 'user'); /**
* Function to handle the mobile menu toggle
*/
fetch(api, { const mobMenu = () => {
method: 'POST', for (let i = 0; i < links.length; i++) {
headers: { 'Content-Type': 'application/json' }, links[i].addEventListener('click', mobMenu)
body: JSON.stringify({ action: 'sendMessage', sessionId, chatInput: message }) }
}) if (nav.classList.contains('responsive')) {
.then((response) => response.json()) nav.classList.remove('responsive');
.then((data) => { document.body.style.overflow = '';
appendMessage(data.output, 'bot');
}) } else {
.catch((error) => { nav.classList.add('responsive');
appendMessage('Fehler beim Verbinden mit dem Server. Bitte versuchen Sie es später erneut.', 'bot'); document.body.style.overflow = 'hidden';
console.error(error); }
});
});
} }
handleBuyClick('buy-3h', 'I want to buy the 3 Hours pack for $450.'); buttonMobile.addEventListener('click', mobMenu);
handleBuyClick('buy-5h', 'I want to buy the 5 Hours pack for $675.');
handleBuyClick('buy-10h', 'I want to buy the 10 Hours pack for $1200.');
handleBuyClick('buy-bundle', 'I want to buy the ODOO and N8N bundle for $395 per year.');
})();

21
public/brandize.html Normal file
View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OD8N - Odoo & n8n Automation Experts</title>
<script>const configUrl = `https://od8n.com/widget/custom/brandize.json`;</script>
<script src="widget/widget.js"></script>
<script type="module" src="widget/custom/brandize.js"></script>
<link rel="stylesheet" href="widget/custom/brandize.css">
</head>
<body>
</body>
</html>

BIN
public/button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

220
public/buyModal.js Normal file
View File

@@ -0,0 +1,220 @@
const WEBHOOK_URL = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/c76e6b4e-af2f-4bc3-9875-6460d0ffc8e3";
function createModal() {
const modal = document.createElement("div");
modal.id = "buyNowModal";
modal.style.position = "fixed";
modal.style.top = "0";
modal.style.left = "0";
modal.style.width = "100%";
modal.style.height = "100%";
modal.style.backgroundColor = "rgba(0,0,0,0.6)";
modal.style.display = "none";
modal.style.justifyContent = "center";
modal.style.alignItems = "center";
modal.style.zIndex = "1000";
modal.innerHTML = `
<div style="
background: #ffffff;
padding: 40px 30px;
border-radius: 16px;
max-width: 500px;
width: 90%;
position: relative;
font-family: 'Arial', sans-serif;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
">
<span id="closeModal" style="
position: absolute;
top: 15px;
right: 20px;
cursor: pointer;
font-weight: bold;
font-size: 24px;
color: #555;
">&times;</span>
<h2 style="margin-bottom: 15px; font-size: 24px; color: #333;">Order Details</h2>
<p id="productText" style="margin-bottom: 25px; font-weight: 500; color: #555;"></p>
<form id="buyForm" style="display: flex; flex-direction: column; gap: 15px;">
<!-- Hidden select, shown only if product starts with "_" -->
<select name="location" id="locationSelect" required style="
display: none;
padding: 12px 15px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 8px;
outline: none;
">
<option value="">Select Location</option>
<option value="Boston">US, Boston</option>
<option value="Manchester">UK, Manchester</option>
<option value="Mumbai">IN, Mumbai</option>
<option value="Saopaulo">BR, Sao Paulo</option>
<option value="meppel">NL, Meppel</option>
</select>
<input type="text" name="name" placeholder="Name" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<input type="text" name="company" placeholder="Company" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<input type="text" name="country" placeholder="Country" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<input type="text" name="street" placeholder="Street" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<div style="display: flex; gap: 10px;">
<input type="text" name="zip" placeholder="ZIP" required style="max-width: 100px; flex: 1 1 0; padding: 12px 10px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px;">
<input type="text" name="town" placeholder="Town" required style="flex: 2 1 0; padding: 12px 10px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px;">
</div>
<input type="email" name="email" placeholder="Email" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<input type="hidden" name="product">
<input type="hidden" name="price">
<button id="submitBuy" type="submit" style="
padding: 14px;
background: #007BFF;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
transition: background 0.3s;
">Send Order</button>
</form>
<div id="confirmation" style="
display: none;
margin-top: 20px;
padding: 20px;
background-color: #e6ffed;
color: #056608;
border-radius: 12px;
text-align: center;
font-weight: 500;
">
<p>Thank you for your purchase! 🎉</p>
<button id="closeConfirmation" style="
margin-top: 10px;
padding: 10px 20px;
background: #007BFF;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s;
">Close</button>
</div>
</div>
`;
document.body.appendChild(modal);
// Close modal
document.getElementById("closeModal").onclick = () => { document.getElementById("buyNowModal").style.display = "none"; };
document.getElementById("closeConfirmation").onclick = () => {
document.getElementById("confirmation").style.display = "none";
document.getElementById("buyNowModal").style.display = "none";
};
modal.onclick = (e) => { if (e.target === modal) modal.style.display = "none"; };
return modal;
}
function openModal(productHref) {
const modal = document.getElementById("buyNowModal");
modal.style.display = "flex";
// Reset form display for repeated orders
const form = modal.querySelector("#buyForm");
const confirmation = modal.querySelector("#confirmation");
form.style.display = "flex"; // show form
confirmation.style.display = "none"; // hide confirmation
let product = productHref;
let price = "";
if (productHref.includes("/")) {
const parts = productHref.split("/").filter(Boolean);
product = parts[parts.length - 2] || "Product";
price = parts[parts.length - 1] || "0";
}
// Show/hide location select
const locationSelect = document.getElementById("locationSelect");
if (product.startsWith("_")) {
locationSelect.style.display = "block";
locationSelect.setAttribute("required", "true");
} else {
locationSelect.style.display = "none";
locationSelect.removeAttribute("required");
locationSelect.value = ""; // reset
}
product = product.replace(/_/g, ""); // This needs to be after the locationSelect show ;)
modal.querySelector('input[name="product"]').value = product;
modal.querySelector('input[name="price"]').value = price;
modal.querySelector("#productText").textContent = `You will buy ${product} for $${price}.`;
}
function handleFormSubmit() {
const form = document.getElementById("buyForm");
const confirmation = document.getElementById("confirmation");
if (!form) {
console.error("Form not found!");
return;
}
form.addEventListener("submit", async (e) => {
console.log("handler");
e.preventDefault();
const data = {};
new FormData(form).forEach((value, key) => (data[key] = value));
try {
const res = await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
if (res.ok) {
form.style.display = "none";
confirmation.style.display = "block";
form.reset();
} else {
alert("Failed to submit form.");
}
} catch (err) {
console.error(err);
alert("Error submitting form.");
}
});
}
function attachButtons() {
const buttons = Array.from(document.querySelectorAll("button, a"));
buttons.forEach(btn => {
const text = btn.textContent.trim();
if (text === "Buy Now" || text === "Book Now") {
btn.addEventListener("click", (e) => {
e.preventDefault();
const href = btn.getAttribute("href") || btn.dataset.product || "Unknown/0";
openModal(href);
});
}
});
}
document.addEventListener("DOMContentLoaded", () => {
createModal();
handleFormSubmit();
attachButtons();
});

View File

@@ -0,0 +1,6 @@
<svg width="150" height="49" viewBox="0 0 150 49" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.4955 15.3691C7.40148 15.3691 0 22.6877 0 31.6972C0 40.7066 7.40148 48.0252 16.4955 48.0252C25.5894 48.0252 32.9909 40.7066 32.9909 31.6972C32.9909 22.6877 25.5894 15.3691 16.4955 15.3691ZM16.4955 42.7508C10.3318 42.7508 5.30482 37.7792 5.30482 31.6719C5.30482 25.5647 10.3318 20.5931 16.4955 20.5931C22.6591 20.5931 27.6861 25.5647 27.6861 31.6719C27.6861 37.7792 22.6591 42.7508 16.4955 42.7508Z" fill="white"/>
<path d="M66.487 0C64.8703 0 63.582 1.28707 63.582 2.90221V20.2397C60.8538 17.817 57.3425 15.8486 53.5281 15.8486C49.158 15.8486 45.192 17.6404 42.3375 20.5678C39.4577 23.47 37.6895 27.5079 37.6895 31.9243C37.6895 36.3659 39.4577 40.3786 42.3375 43.2808C45.2172 46.183 49.158 48 53.5281 48C57.8983 48 61.8643 46.2082 64.7188 43.2808C67.4975 40.4543 69.2405 36.6183 69.3415 32.3533C69.3668 32.2271 69.3668 32.1009 69.3668 31.9748V2.90221C69.3921 1.28707 68.0785 0 66.487 0ZM53.5281 42.5237C47.5918 42.5237 42.9943 38.0568 42.9943 31.9243C42.9943 25.7918 47.5918 21.3249 53.5281 21.3249C59.4645 21.3249 63.8094 25.7918 63.8094 31.9243C63.8094 38.0568 59.4645 42.5237 53.5281 42.5237Z" fill="white"/>
<path d="M135.323 15.571C127.088 15.571 120.647 22.6877 120.647 31.7729V44.6183C120.647 46.2334 121.682 47.5205 123.299 47.5205C124.916 47.5205 126.204 46.2334 126.204 44.6183V31.7729C126.204 25.9432 130.347 21.3754 135.323 21.3754C140.249 21.3754 144.367 25.8675 144.443 31.6215L144.417 44.6183C144.417 46.2334 145.706 47.5205 147.322 47.5205C148.939 47.5205 149.975 46.2334 149.975 44.6183V32.2019L150 31.9495V31.7981C150 22.6877 143.533 15.571 135.323 15.571Z" fill="white"/>
<path d="M102.055 19.2555C103.596 17.5899 104.53 15.3691 104.53 12.9464C104.53 7.7981 100.337 3.60883 95.1836 3.60883C90.0303 3.60883 85.837 7.7981 85.837 12.9464C85.837 15.3691 86.7717 17.5899 88.3126 19.2555C81.9721 21.5268 77.5261 26.8517 77.5261 33.0347C77.5261 41.2871 85.4581 48 95.2088 48C104.96 48 112.892 41.2871 112.892 33.0347C112.841 26.8517 108.395 21.5268 102.055 19.2555ZM95.1583 8.65614C97.5329 8.65614 99.4527 10.5741 99.4527 12.9464C99.4527 15.3186 97.5329 17.2366 95.1583 17.2366C92.7838 17.2366 90.864 15.3186 90.864 12.9464C90.864 10.5741 92.7838 8.65614 95.1583 8.65614ZM95.1583 41.9937C88.742 41.9937 83.5383 37.9811 83.5383 33.0347C83.5383 28.0883 88.742 23.3186 95.1583 23.3186C101.575 23.3186 106.778 28.0883 106.778 33.0347C106.778 37.9811 101.575 41.9937 95.1583 41.9937Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

BIN
public/images/coach.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/images/odoo_node.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
public/images/open.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
public/images/server.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/images/server.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
public/images/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
public/images/share.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
public/images/youtube.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

File diff suppressed because one or more lines are too long

701
public/index.html.design Normal file
View File

@@ -0,0 +1,701 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script defer data-domain="od8n.com" src="https://plausible.odoo4projects.com/js/script.tagged-events.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Unlock your business potential with OD8N, your expert partners in Odoo and n8n automation. Get a free 1-hour consultation with our automation engineers. Risk-free guarantee!">
<meta name="keywords" content="OD8N, Odoo, n8n, automation, business automation, consulting, integration, service packs, Odoo development, n8n workflows">
<meta name="author" content="OD8N">
<title>OD8N - Odoo & n8n Automation Experts</title>
<link rel="stylesheet" href="style.css">
<script>const configUrl = `https://od8n.com/widget/custom/od8n.json`;</script>
<script src="widget/widget.js"></script>
<link rel="stylesheet" href="widget/custom/od8n.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "OD8N",
"url": "https://www.od8n.com/",
"logo": "https://www.od8n.com/logo.svg",
"description": "OD8N are experts in Odoo and n8n automation, offering flexible man-hour service packs with a free 1-hour consultation.",
"contactPoint": {
"@type": "ContactPoint",
"telephone": "",
"contactType": "customer service"
},
"sameAs": []
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "3 Hours Service Pack",
"description": "Quick fixes and small automation improvements.",
"offers": {
"@type": "Offer",
"price": "450",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "5 Hours Service Pack",
"description": "Ideal for mid-level integrations and custom workflows.",
"offers": {
"@type": "Offer",
"price": "675",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "10 Hours Service Pack",
"description": "Perfect for large projects and advanced automation.",
"offers": {
"@type": "Offer",
"price": "1200",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "ODOO & N8N Bundle",
"description": "Perfect for taking conrtol over your company automation installation.",
"offers": {
"@type": "Offer",
"price": "395",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Oliver Arnold",
"jobTitle": "Expert in Odoo & System Design",
"worksFor": {
"@type": "Organization",
"name": "OD8N"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Mark Gutmann",
"jobTitle": "Expert in AI and Process Consulting",
"worksFor": {
"@type": "Organization",
"name": "OD8N"
}
}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
<!-- Header Section -->
<header class="hero" id="home">
<div class="navbar-main">
<div class="logo">
<a href="/" class="logo-link" aria-label="Go to homepage">
<img src="./icons/logo-white.svg" alt="OD8N Logo" class="logo-icon">
</a>
</div>
<nav>
<button id="Burger-menu">
<i class="fa-solid fa-bars"></i>
</button>
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#packages">packages</a></li>
<li><a href="#team">Team</a></li>
<li><a href="#blog">Blog</a></li>
<li><a href="#story">Story</a></li>
</ul>
</nav>
</div>
<section class="hero-content">
<h1>Odoo & n8n Automation Experts</h1>
<p>We charge upfront — this lets us skip the sales talk and give serious clients immediate access to our best engineers.</p>
<ul class="hero-list">
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>One-hour get-to-know session included — not satisfied? Full refund, no questions asked</span></li>
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>Experts in Odoo and n8n — no outsourcing, no fluff</span></li>
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>Hosting solutions for Odoo & n8n available</span></li>
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>Custom development tailored to your workflows</span></li>
</ul>
<button class="hero-button-cta plausible-event-name=ExplorePackages" onclick="location.href='#packages'">Explore packages</button>
</section>
</header>
<!-- Product Packages -->
<section id="packages" class="packages">
<h2 class="section-header">Service Packs</h2>
<div class="package-list container">
<div class="package">
<h3>3 Hours<span class="asteric-icon">*</span></h3>
<p>Quick fixes and small automation improvements.</p>
<span class="price">$450</span>
<button class="btn-secondary plausible-event-name=ChoosePlan" onclick="sendMessageToBot('I want to buy 3 Hours of service for $450')">
Choose Plan
</button>
</div>
<div class="package featured">
<div class="badge">Most popular</div>
<h3>5 Hours<span class="asteric-icon">*</span></h3>
<p>Ideal for mid-level integrations and custom workflows.</p>
<span class="price">$675</span>
<button class="btn-secondary btn-featured" onclick="sendMessageToBot('I want to buy 5 hours of service for $675')">
Choose Plan
</button>
</div>
<div class="package">
<h3>10 Hours<span class="asteric-icon">*</span></h3>
<p>Perfect for large projects and advanced automation.</p>
<span class="price">$1.200</span>
<button class="btn-secondary plausible-event-name=ChoosePlan" onclick="sendMessageToBot('I want to buy 10 hours of service for $1.200')">
Choose Plan
</button>
</div>
<div class="package">
<h3>N8N Installation and Maintainance<span class="asteric-icon">*</span></h3>
<p>We install N8N on your Container and maintain it for one year.</p>
<span class="price">$75/Year</span>
<button class="btn-secondary plausible-event-name=ChoosePlan" onclick="sendMessageToBot('I want you to install N8N on my container and maintain it.')">
Subscribe
</button>
</div>
</div>
<p class="free-offer"><span class="asteric-icon">*</span> All hour packs include <strong>1 Free Consulting Hour for first clients</strong> — with a 100% refund if youre not satisfied!</p>
</section>
<!-- Image Slider -->
<section id="team" class="slider-section">
<h2 class="section-header">Meet Our Team</h2>
<div class="slide container">
<div class="slide-content">
<div class="slide-image">
<img src="oliver.webp" alt="Oliver Arnold, Odoo & System Design Expert at OD8N">
</div>
<div class="slide-text">
<h2>Oliver Arnold</h2>
<h4>Expert in Odoo & System Design</h4>
<p>I am an expert in Odoo and system design with years of experience in consulting.</p>
<ul>
<li>Odoo functional consulting & development</li>
<li>N8N workflow design & custom nodes</li>
<li>System integration & architecture design</li>
</ul>
</div>
</div>
</div>
<div class="slide container">
<div class="slide-content">
<div class="slide-image">
<img src="mark.webp" alt="Mark Gutmann, AI and Process Consulting Expert at OD8N">
</div>
<div class="slide-text">
<h2>Mark Gutmann</h2>
<h4>Expert in AI and Process Consulting</h4>
<p>I am an expert in Odoo and system design with years of experience in consulting.</p>
<ul>
<li>Odoo functional consulting & development</li>
<li>N8N workflow design & custom nodes</li>
<li>System integration & architecture design</li>
</ul>
</div>
</div>
</div>
</section>
<section id="blog" class="accordion-section blog-section">
<h2 class="section-header">Latest Blog Posts</h2>
<div class="blog-list container">
<!-- 1. Example HTML to use badge in blog post -->
<!-- <div class="blog-teaser blog-badge-green">
<div class="blog-badge-green-tag">Company</div>
<h3 class="blog-title tutorial"> From Chat to Checkout: Building a Dynamic Chat Widget with PayPal and n8n</h3>
<small class="blog-date">30.07.2025</small>
<div class="blog-snippet">
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could guide users through an entire procurement flow and end with a PayPal payment button, ready to go?
</div>
<div class="blog-full-content" style="display:none;">
<p>
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could <strong>guide users through an entire procurement flow</strong> and end with a PayPal payment button, ready to go?
</p>
</div>
<button class="read-more-btn plausible-event-name=ReadPost" onclick="toggleContent(this)">Read More <span class="read-more-btn-icon"><i class="fa-solid fa-angle-right"></i></span></button>
</div> -->
<!-- 2. Example HTML to skip badge in blog post -->
<!-- <div class="blog-teaser">
<h3 class="blog-title tutorial"> From Chat to Checkout: Building a Dynamic Chat Widget with PayPal and n8n</h3>
<small class="blog-date">30.07.2025</small>
<div class="blog-snippet">
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could guide users through an entire procurement flow and end with a PayPal payment button, ready to go?
</div>
<div class="blog-full-content" style="display:none;">
<p>
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could <strong>guide users through an entire procurement flow</strong> and end with a PayPal payment button, ready to go?
</p>
</div>
<button class="read-more-btn plausible-event-name=ReadPost" onclick="toggleContent(this)">Read More <span class="read-more-btn-icon"><i class="fa-solid fa-angle-right"></i></span></button>
</div> -->
<!-- 3. Uncomment this to get one blog post ready with blue badge -->
<!-- <div class="blog-teaser blog-badge-blue">
<div class="blog-badge-blue-tag">Most popular</div>
<h3 class="blog-title tutorial"> From Chat to Checkout: Building a Dynamic Chat Widget with PayPal and n8n</h3>
<small class="blog-date">30.07.2025</small>
<div class="blog-snippet">
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could guide users through an entire procurement flow and end with a PayPal payment button, ready to go?
</div>
<div class="blog-full-content" style="display:none;">
<p>
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could <strong>guide users through an entire procurement flow</strong> and end with a PayPal payment button, ready to go?
</p>
<p>
In this tutorial, well walk through the key steps we used to build a <strong>dynamic chat widget</strong> using <code>n8n</code>, an AI agent, and a smart process flow that finishes in PayPal. We wont cover the basics of implementing an AI chat agent — well assume youve got that part covered.
</p>
<h2 class="blog-content-section-title">💡 The Real Magic: State-Aware Chat Responses</h2>
<p>
The key innovation here lies in how we return the agents response to the frontend. Instead of sending back a simple string, we return a rich object that contains not just the output text, but also a dynamic <strong>state object</strong> that tracks context and next steps.
</p>
<p>Heres what our return looks like from the backend:</p>
<pre><code>return {
json: {
output: output_,
state: state_
}
};</code></pre>
<p>
This small design decision opens up a world of flexibility. The frontend can now dynamically react to the current state of the conversation — whether its collecting user details, offering product options, or finalizing a purchase.
</p>
<h2 class="blog-content-section-title">🛒 Embedding the Product ID in State</h2>
<p>
Once the conversation has gathered enough information from the user (product type, quantity, shipping address, etc.), we instruct the AI agent to <strong>embed the product ID directly into the state object</strong>.
</p>
<p>
This ID is then parsed by the frontend to dynamically display the appropriate <strong>PayPal payment button</strong>, configured for the selected product and amount.
</p>
<p>
Its a smooth handoff: the chat does the heavy lifting, and the UI reacts based on structured, predictable metadata.
</p>
<h2 class="blog-content-section-title">🔧 Under the Hood: What We Used</h2>
<ul>
<li><strong>n8n</strong> for workflow logic and webhook handling</li>
<li><strong>Custom HTML + JS widget</strong> to handle frontend interaction</li>
<li><strong>OpenAI Agent</strong> with system prompts to guide the process</li>
<li><strong>PayPal API</strong> for seamless payment integration</li>
</ul>
<h2 class="blog-content-section-title">✨ Bonus: A Process Wizard</h2>
<p>
We also built a simple process wizard UI that activates when certain states are reached. This provides users with an optional visual flow, letting them see how far along they are in the checkout process — especially useful when the chat is collecting structured information.
</p>
<h2 class="blog-content-section-title">📚 Want to Try This Yourself?</h2>
<p>
Head over to our <a href="/">homepage</a> to read more blog posts like this and explore our implementation in detail.
</p>
<p>
Or, if you'd like to help us out with a little SEO love, just Google <strong>"N8N ODOO API"</strong> and click on our homepage: <strong>OD8N</strong>. As a reward, weve got an in-depth breakdown of the full implementation waiting for you!
</p>
<p>Heres a full video walkthrough of the dynamic chat widget in action:</p>
<span class="blog-video-description">
In this tutorial, well walk through the key steps we used to build a dynamic chat widget using n8n, an AI agent, and a smart process flow that finishes in PayPal. We wont cover the basics of implementing an AI chat agent — well assume youve got that part covered.
💡 The Real Magic: State-Aware Chat Responses
The key innovation here lies in how we return the agents response to the frontend. Instead of sending back a simple string, we return a rich object that contains not just the output text, but also a dynamic state object that tracks context and next steps.
Heres what our return looks like from the backend:
return {
json: {
output: output_,
state: state_
}
};
This small design decision opens up a world of flexibility. The frontend can now dynamically react to the current state of the conversation — whether its collecting user details, offering product options, or finalizing a purchase.
🛒 Embedding the Product ID in State
Once the conversation has gathered enough information from the user (product type, quantity, shipping address, etc.), we instruct the AI agent to embed the product ID directly into the state object.
This ID is then parsed by the frontend to dynamically display the appropriate PayPal payment button, configured for the selected product and amount.
Its a smooth handoff: the chat does the heavy lifting, and the UI reacts based on structured, predictable metadata.
🔧 Under the Hood: What We Used
n8n for workflow logic and webhook handling
Custom HTML + JS widget to handle frontend interaction
OpenAI Agent with system prompts to guide the process
PayPal API for seamless payment integration
✨ Bonus: A Process Wizard
We also built a simple process wizard UI that activates when certain states are reached. This provides users with an optional visual flow, letting them see how far along they are in the checkout process — especially useful when the chat is collecting structured information.
📚 Want to Try This Yourself?
Head over to our homepage to read more blog posts like this and explore our implementation in detail.
Or, if you'd like to help us out with a little SEO love, just Google "N8N ODOO API" and click on our homepage: OD8N. As a reward, weve got an in-depth breakdown of the full implementation waiting for you!
Heres a full video walkthrough of the dynamic chat widget in action:
</span>
<iframe width="560" height="315" class="ytvideo" src="https://youtu.be/4BPLTfeQfHE" title="Video" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>
<button class="read-more-btn plausible-event-name=ReadPost" onclick="toggleContent(this)">Read More <span class="read-more-btn-icon"><i class="fa-solid fa-angle-right"></i></span></button>
</div> -->
<div class="blog-teaser">
<h3 class="blog-title tutorial"> From Chat to Checkout: Building a Dynamic Chat Widget with PayPal and n8n</h3>
<small class="blog-date">30.07.2025</small>
<div class="blog-snippet">
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could guide users through an entire procurement flow and end with a PayPal payment button, ready to go?
</div>
<div class="blog-full-content" style="display:none;">
<p>
So you've wired up a chat with your AI agent and it responds nicely — but what if your chat could <strong>guide users through an entire procurement flow</strong> and end with a PayPal payment button, ready to go?
</p>
<p>
In this tutorial, well walk through the key steps we used to build a <strong>dynamic chat widget</strong> using <code>n8n</code>, an AI agent, and a smart process flow that finishes in PayPal. We wont cover the basics of implementing an AI chat agent — well assume youve got that part covered.
</p>
<h2 class="blog-content-section-title">💡 The Real Magic: State-Aware Chat Responses</h2>
<p>
The key innovation here lies in how we return the agents response to the frontend. Instead of sending back a simple string, we return a rich object that contains not just the output text, but also a dynamic <strong>state object</strong> that tracks context and next steps.
</p>
<p>Heres what our return looks like from the backend:</p>
<pre><code>return {
json: {
output: output_,
state: state_
}
};</code></pre>
<p>
This small design decision opens up a world of flexibility. The frontend can now dynamically react to the current state of the conversation — whether its collecting user details, offering product options, or finalizing a purchase.
</p>
<h2 class="blog-content-section-title">🛒 Embedding the Product ID in State</h2>
<p>
Once the conversation has gathered enough information from the user (product type, quantity, shipping address, etc.), we instruct the AI agent to <strong>embed the product ID directly into the state object</strong>.
</p>
<p>
This ID is then parsed by the frontend to dynamically display the appropriate <strong>PayPal payment button</strong>, configured for the selected product and amount.
</p>
<p>
Its a smooth handoff: the chat does the heavy lifting, and the UI reacts based on structured, predictable metadata.
</p>
<h2 class="blog-content-section-title">🔧 Under the Hood: What We Used</h2>
<ul>
<li><strong>n8n</strong> for workflow logic and webhook handling</li>
<li><strong>Custom HTML + JS widget</strong> to handle frontend interaction</li>
<li><strong>OpenAI Agent</strong> with system prompts to guide the process</li>
<li><strong>PayPal API</strong> for seamless payment integration</li>
</ul>
<h2 class="blog-content-section-title">✨ Bonus: A Process Wizard</h2>
<p>
We also built a simple process wizard UI that activates when certain states are reached. This provides users with an optional visual flow, letting them see how far along they are in the checkout process — especially useful when the chat is collecting structured information.
</p>
<h2 class="blog-content-section-title">📚 Want to Try This Yourself?</h2>
<p>
Head over to our <a href="/">homepage</a> to read more blog posts like this and explore our implementation in detail.
</p>
<p>
Or, if you'd like to help us out with a little SEO love, just Google <strong>"N8N ODOO API"</strong> and click on our homepage: <strong>OD8N</strong>. As a reward, weve got an in-depth breakdown of the full implementation waiting for you!
</p>
<p>Heres a full video walkthrough of the dynamic chat widget in action:</p>
<span class="blog-video-description">
In this tutorial, well walk through the key steps we used to build a dynamic chat widget using n8n, an AI agent, and a smart process flow that finishes in PayPal. We wont cover the basics of implementing an AI chat agent — well assume youve got that part covered.
💡 The Real Magic: State-Aware Chat Responses
The key innovation here lies in how we return the agents response to the frontend. Instead of sending back a simple string, we return a rich object that contains not just the output text, but also a dynamic state object that tracks context and next steps.
Heres what our return looks like from the backend:
return {
json: {
output: output_,
state: state_
}
};
This small design decision opens up a world of flexibility. The frontend can now dynamically react to the current state of the conversation — whether its collecting user details, offering product options, or finalizing a purchase.
🛒 Embedding the Product ID in State
Once the conversation has gathered enough information from the user (product type, quantity, shipping address, etc.), we instruct the AI agent to embed the product ID directly into the state object.
This ID is then parsed by the frontend to dynamically display the appropriate PayPal payment button, configured for the selected product and amount.
Its a smooth handoff: the chat does the heavy lifting, and the UI reacts based on structured, predictable metadata.
🔧 Under the Hood: What We Used
n8n for workflow logic and webhook handling
Custom HTML + JS widget to handle frontend interaction
OpenAI Agent with system prompts to guide the process
PayPal API for seamless payment integration
✨ Bonus: A Process Wizard
We also built a simple process wizard UI that activates when certain states are reached. This provides users with an optional visual flow, letting them see how far along they are in the checkout process — especially useful when the chat is collecting structured information.
📚 Want to Try This Yourself?
Head over to our homepage to read more blog posts like this and explore our implementation in detail.
Or, if you'd like to help us out with a little SEO love, just Google "N8N ODOO API" and click on our homepage: OD8N. As a reward, weve got an in-depth breakdown of the full implementation waiting for you!
Heres a full video walkthrough of the dynamic chat widget in action:
</span>
<iframe width="560" height="315" class="ytvideo" src="https://youtu.be/4BPLTfeQfHE" title="Video" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>
<button class="read-more-btn plausible-event-name=ReadPost" onclick="toggleContent(this)">Read More <span class="read-more-btn-icon"><i class="fa-solid fa-angle-right"></i></span></button>
</div>
<div class="blog-teaser">
<h3 class="blog-title company"> ODOO & N8N: Our Strategy</h3>
<small class="blog-date">31.07.2025</small>
<div class="blog-snippet">
<p>At the heart of every modern enterprise lies the need to balance two forces: stability and agility. Our approach harnesses the best of both worlds—leveraging the proven foundation of <strong>ODOO</strong> while embracing the innovation engine of <strong>N8N</strong>.</p>
</div>
<div class="blog-full-content" style="display:none;">
<p>At the heart of every modern enterprise lies the need to balance two forces: stability and agility. Our approach harnesses the best of both worlds—leveraging the proven foundation of <strong>ODOO</strong> while embracing the innovation engine of <strong>N8N</strong>.</p>
<h2 class="blog-content-section-title">ODOO: The Rock-Solid ERP Foundation</h2>
<p>Weve seen countless ERP systems come and go. But <strong>ODOO</strong> stands tall—a battle-hardened suite refined over years of real-world application. From <strong>Accounting</strong> to <strong>CRM</strong>, <strong>Invoicing</strong>, <strong>Production</strong>, and <strong>Logistics</strong>, ODOO delivers reliable, scalable modules that form the digital spine of any serious company.</p>
<div class="highlight">
Still awesome after all these years: ODOO continues to evolve while staying true to its mission—powering real businesses with real needs.
</div>
<h2 class="blog-content-section-title">N8N: The Agile Integrator for the AI Era</h2>
<p>But the world is changing. Fast. AI tools are evolving weekly. APIs are everywhere. Customers demand speed and personalization like never before.</p>
<p>Enter <strong>N8N</strong>—our weapon of choice for agile automation, AI integration, and rapid deployment. Whether it's triggering actions from an AI model, syncing tools across platforms, or automating multi-step workflows, N8N gives us the power to move as fast as your market does.</p>
<p>While ODOO keeps your core business running like a well-oiled machine, N8N lets us bolt on new AI-driven capabilities almost instantly—without waiting for the next ERP update cycle.</p>
<h2 class="blog-content-section-title">Together: The Future of Digital Operations</h2>
<p>By combining ODOO and N8N, we create a powerful synergy. One gives you reliable structure. The other, unmatched flexibility. This strategy enables us to build systems that are not only stable—but also smart, adaptive, and future-ready.</p>
<div class="highlight">
💡 <strong class="blog-content-section-highlight">Smart Strategy = ODOO as the backbone + N8N as the nervous system.</strong>
</div>
<div class="cta">
Ready to take your business operations to the next level?<br>
Lets talk ODOO. Lets talk N8N. Lets build your future—today.
</div>
<img src="https://OD8N.com/images/server.webp" class="image" width="560">
</div>
<button class="read-more-btn plausible-event-name=ReadPost" onclick="toggleContent(this)">Read More <span class="read-more-btn-icon"><i class="fa-solid fa-angle-right"></i></button>
</div>
</div>
</section>
<!-- Success Stories Accordion -->
<section id="story" class="accordion-section success-stories">
<h2 class="section-header">Success Stories in Automation</h2>
<div class="accordion container">
<div class="accordion-item">
<button class="accordion-title">From Manual Tracking to Automated Precision: A Retailer's Story <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: No API, No Problem.</h2>
<p class="panel-bar-content-text">
A growing retail client was struggling with a major operational bottleneck: their shipping provider offered no API for tracking. This meant hours of manual data entry, a high risk of errors, and no real-time visibility into their 800+ daily shipments.
</p>
<p class="panel-bar-content-text">
We engineered a robust solution using n8n and Puppeteer to create a custom web scraper. This automated system intelligently mimics human interaction to extract shipment statuses twice daily, ensuring near real-time accuracy.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> Complete automation, zero manual entry, and a system that provides flawless, up-to-the-minute tracking data. All running seamlessly on our n8n community instance.
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Automate Your Repetitive Tasks <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
<div class="accordion-item">
<button class="accordion-title">Scaling on Demand: Automated Odoo Instance Management <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: Global Scale, Local Management</h2>
<p class="panel-bar-content-text">
ODOO4projects needed to instantly deploy new Odoo instances for clients worldwide, a process that was manual, time-consuming, and prone to configuration errors. They required a system that could scale globally and provide centralized oversight.
</p>
<p class="panel-bar-content-text">
We built a smart agent powered by an n8n workflow that completely automates the provisioning of Odoo instances. The system also generates a weekly server status report, providing a clear, consolidated view of their entire infrastructure.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> A fully automated, scalable, and reliable system for Odoo instance management, all running on our cost-effective n8n community instance.
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Scale Your Operations with Automation <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
<div class="accordion-item">
<button class="accordion-title">From Simple Chatbot to Intelligent Odoo-Powered Agent <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: Unlocking the Power of Conversational AI</h2>
<p class="panel-bar-content-text">
A client wanted to move beyond a basic FAQ chatbot. They needed an intelligent agent that could handle complex internal tasks, provide instant knowledge base access, and integrate seamlessly with their Odoo chatter.
</p>
<p class="panel-bar-content-text">
Leveraging powerful OCA modules and an n8n-powered backend, we built a sophisticated AI agent. This solution connects to LLMs, agents, and RAG pipelines, transforming their Odoo chatter into a powerful, intelligent hub for communication and task management.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> A truly intelligent conversational AI that streamlines employee onboarding, provides instant knowledge access, and can be embedded on any website.
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Integrate AI into Your Workflows <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
<div class="accordion-item">
<button class="accordion-title">The Future of Sales: A Checkout Process Inside a Chatbox <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: Reinventing the Sales Funnel</h2>
<p class="panel-bar-content-text">
We asked ourselves: could we transform a simple conversation into a complete sales transaction? The goal was to create a frictionless procurement workflow that eliminates the need for emails, forms, and spreadsheets.
</p>
<p class="panel-bar-content-text">
Using only n8n and PayPal, we built a revolutionary checkout process that lives entirely within a chatbox. This allows for a seamless, conversational sales experience, from initial inquiry to final purchase.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> A groundbreaking, conversational commerce solution that redefines the client interaction and digital selling. Ready to see it in action?
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Experience the Future of Sales <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
</div>
</section>
<footer>
<p>For ODOO and N8N tips and tricks and news follow us on
<a href="https://www.linkedin.com/company/od8n" target="_blank" aria-label="LinkedIn"><i class="fab fa-linkedin"></i></a>
</p>
<p>© 2025 OD8N. All Rights Reserved. Service provided by ASC Consultatores Paraguay</p>
</footer>
<script defer src="./app.js"></script>
</body>
</html>

491
public/index.html.template Normal file
View File

@@ -0,0 +1,491 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script defer data-domain="od8n.com" src="https://plausible.odoo4projects.com/js/script.tagged-events.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Unlock your business potential with OD8N, your expert partners in Odoo and n8n automation. Get a free 1-hour consultation with our automation engineers. Risk-free guarantee!">
<meta name="keywords" content="OD8N, Odoo, n8n, automation, business automation, consulting, integration, service packs, Odoo development, n8n workflows">
<meta name="author" content="OD8N">
<title>OD8N - Odoo & n8n Automation Experts</title>
<link rel="stylesheet" href="style.css">
<script>const configUrl = `https://od8n.com/widget/custom/od8n.json`;</script>
<script src="widget/widget.js"></script>
<script src="buyModal.js"></script>
<link rel="stylesheet" href="widget/custom/od8n.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "OD8N",
"url": "https://www.od8n.com/",
"logo": "https://www.od8n.com/logo.svg",
"description": "OD8N are experts in Odoo and n8n automation, offering flexible man-hour service packs with a free 1-hour consultation.",
"contactPoint": {
"@type": "ContactPoint",
"telephone": "",
"contactType": "customer service"
},
"sameAs": []
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "3 Hours Service Pack",
"description": "Quick fixes and small automation improvements.",
"offers": {
"@type": "Offer",
"price": "450",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "5 Hours Service Pack",
"description": "Ideal for mid-level integrations and custom workflows.",
"offers": {
"@type": "Offer",
"price": "675",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "10 Hours Service Pack",
"description": "Perfect for large projects and advanced automation.",
"offers": {
"@type": "Offer",
"price": "1200",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "Odoo & n8n Automation Consulting",
"provider": {
"@type": "Organization",
"name": "OD8N"
},
"name": "ODOO & N8N Bundle",
"description": "Perfect for taking conrtol over your company automation installation.",
"offers": {
"@type": "Offer",
"price": "395",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Oliver Arnold",
"jobTitle": "Expert in Odoo & System Design",
"worksFor": {
"@type": "Organization",
"name": "OD8N"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Mark Gutmann",
"jobTitle": "Expert in AI and Process Consulting",
"worksFor": {
"@type": "Organization",
"name": "OD8N"
}
}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
<!-- Header Section -->
<header class="hero" id="home">
<div class="navbar-main">
<div class="logo">
<a href="/" class="logo-link" aria-label="Go to homepage">
<img src="./icons/logo-white.svg" alt="OD8N Logo" class="logo-icon">
</a>
</div>
<nav>
<button id="Burger-menu">
<i class="fa-solid fa-bars"></i>
</button>
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#packages">Packages</a></li>
<li><a href="#team">Team</a></li>
<li><a href="#blog">Blog</a></li>
<li><a href="#story">Story</a></li>
</ul>
</nav>
</div>
<section class="hero-content">
<div class="hero-left">
<h1>Odoo & n8n Automation Experts</h1>
<p>We charge upfront — you will get immediate access to our best engineers.</p>
<ul class="hero-list">
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>One-hour get-to-know session included — not satisfied? Full refund, no questions asked</span></li>
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>Experts in Odoo and n8n — no outsourcing, no fluff</span></li>
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>Hosting solutions for Odoo & n8n available</span></li>
<li><span class="icon"><i class="fa-solid fa-circle-check"></i></span><span>Custom development tailored to your workflows</span></li>
</ul>
<button class="hero-button-cta plausible-event-name=ExplorePackages" onclick="location.href='#packages'">
Explore packages
</button>
</div>
<div class="hero-right">
<div id="my-signup-widget"></div>
<script src="https://od8n.com/widget-signup/widget.js"
data-affiliate="od8n"></script>
</div>
</section>
</header>
<section id="workshops" class="workshops">
<h2 class="section-header">Startup Workshops</h2>
<p class="section-subtitle">
We bring your business from zero to ERP and AI in just three months:
</p>
<ul class="workshop-highlights">
<li><strong>6 interactive 2-hour sessions</strong> with our expert instructors</li>
<li><strong>Hands-on topics:</strong> "First Configuration ODOO", "Sales Module", "Build your Website with ODOO", "CRM", "Automations with AI and N8N"</li>
<li><strong>Practical system setup:</strong> Work directly on your ERP so you see real results immediately</li>
<li><strong>Free tools included:</strong> ODOO and N8N for one year</li>
<li><strong>Exclusive alumni chat group:</strong> Stay connected, share tips, and get support</li>
</ul>
<div class="workshop-list container">
<div class="workshop">
<div class="badge next">Next</div>
<h3>Class Q1/2026</h3>
<p><strong>Language:</strong> German</p>
<p><strong>Places available:</strong> 4</p>
<a href="/agenda/q1-2026.pdf" class="agenda-link">Download Agenda</a>
<div class="workshop-action">
<span class="price">$790.00 <br> per Person</span>
<a href="https://Q1-2026/790.00"
target="_blank"
class="btn-secondary">
Book Now
</a>
</div>
</div>
<div class="workshop">
<h3>Class Q2/2026</h3>
<p><strong>Language:</strong> English</p>
<p><strong>Places available:</strong> 5</p>
<a href="/agenda/q2-2026.pdf" class="agenda-link">Download Agenda</a>
<div class="workshop-action">
<span class="price">$790.00 <br> per Person</span>
<a href="https://Q2-2026/790.00"
target="_blank"
class="btn-secondary">
Book Now
</a>
</div>
</div>
<div class="workshop">
<h3>Class Q3/2026</h3>
<p><strong>Language:</strong> German</p>
<p><strong>Places available:</strong> 6</p>
<a href="/agenda/q3-2026.pdf" class="agenda-link">Download Agenda</a>
<div class="workshop-action">
<span class="price">$790.00 <br> per Person</span>
<a href="https://Q3-2026/790.00"
target="_blank"
class="btn-secondary">
Book Now
</a>
</div>
</div>
<div class="workshop">
<h3>Class Q4/2026</h3>
<p><strong>Language:</strong> English</p>
<p><strong>Places available:</strong> 6</p>
<a href="/agenda/q4-2026.pdf" class="agenda-link">Download Agenda</a>
<div class="workshop-action">
<span class="price">$790.00 <br> per Person</span>
<a href="https://Q4-2026/790.00"
target="_blank"
class="btn-secondary">
Book Now
</a>
</div>
</div>
</div>
</section>
<!-- Product Packages -->
<section id="packages" class="packages">
<h2 class="section-header">Service Packs</h2>
<div class="package-list container">
<div class="package">
<h3>3 Hours<span class="asteric-icon">*</span></h3>
<p>Quick fixes and small automation improvements.</p>
<span class="price">$450</span>
<button class="btn-secondary plausible-event-name=ChoosePlan" onclick="sendMessageToBot('I want to buy 3 Hours of service for $450')">
Choose Plan
</button>
</div>
<div class="package featured">
<div class="badge">Most popular</div>
<h3>5 Hours<span class="asteric-icon">*</span></h3>
<p>Ideal for mid-level integrations and custom workflows.</p>
<span class="price">$675</span>
<button class="btn-secondary btn-featured" onclick="sendMessageToBot('I want to buy 5 hours of service for $675')">
Choose Plan
</button>
</div>
<div class="package">
<h3>10 Hours<span class="asteric-icon">*</span></h3>
<p>Perfect for large projects and advanced automation.</p>
<span class="price">$1.200</span>
<button class="btn-secondary plausible-event-name=ChoosePlan" onclick="sendMessageToBot('I want to buy 10 hours of service for $1.200')">
Choose Plan
</button>
</div>
<div class="package">
<h3>N8N Installation and Maintainance<span class="asteric-icon">*</span></h3>
<p>We install N8N on your Container and maintain it for one year.</p>
<span class="price">$75/Year</span>
<button class="btn-secondary plausible-event-name=ChoosePlan" onclick="sendMessageToBot('I want you to install N8N on my container and maintain it.')">
Subscribe
</button>
</div>
</div>
<p class="free-offer"><span class="asteric-icon">*</span> All hour packs include <strong>1 Free Consulting Hour for first clients</strong> — with a 100% refund if youre not satisfied!</p>
</section>
<!-- Image Slider -->
<section id="team" class="slider-section">
<h2 class="section-header">Meet Our Team</h2>
<div class="slide container">
<div class="slide-content">
<div class="slide-image">
<img src="oliver.webp" alt="Oliver Arnold, Odoo & System Design Expert at OD8N">
</div>
<div class="slide-text">
<h2>Oliver Arnold</h2>
<h4>Expert in Odoo & System Design</h4>
<p>I am an expert in Odoo and system design with years of experience in consulting.</p>
<ul>
<li>Odoo functional consulting & development</li>
<li>N8N workflow design & custom nodes</li>
<li>System integration & architecture design</li>
</ul>
</div>
</div>
</div>
<div class="slide container">
<div class="slide-content">
<div class="slide-image">
<img src="mark.webp" alt="Mark Gutmann, AI and Process Consulting Expert at OD8N">
</div>
<div class="slide-text">
<h2>Mark Gutmann</h2>
<h4>Expert in AI and Process Consulting</h4>
<p>I am an expert in Odoo and system design with years of experience in consulting.</p>
<ul>
<li>Odoo functional consulting & development</li>
<li>N8N workflow design & custom nodes</li>
<li>System integration & architecture design</li>
</ul>
</div>
</div>
</div>
</section>
<section class="accordion-section">
<h2 class="section-header">Latest Blog Posts</h2>
<div class="blog-list container">
<BLOG>
</div>
</section>
<!-- Success Stories Accordion -->
<section id="story" class="accordion-section success-stories">
<h2 class="section-header">Success Stories in Automation</h2>
<div class="accordion container">
<div class="accordion-item">
<button class="accordion-title">From Manual Tracking to Automated Precision: A Retailer's Story <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: No API, No Problem.</h2>
<p class="panel-bar-content-text">
A growing retail client was struggling with a major operational bottleneck: their shipping provider offered no API for tracking. This meant hours of manual data entry, a high risk of errors, and no real-time visibility into their 800+ daily shipments.
</p>
<p class="panel-bar-content-text">
We engineered a robust solution using n8n and Puppeteer to create a custom web scraper. This automated system intelligently mimics human interaction to extract shipment statuses twice daily, ensuring near real-time accuracy.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> Complete automation, zero manual entry, and a system that provides flawless, up-to-the-minute tracking data. All running seamlessly on our n8n community instance.
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Automate Your Repetitive Tasks <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
<div class="accordion-item">
<button class="accordion-title">Scaling on Demand: Automated Odoo Instance Management <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: Global Scale, Local Management</h2>
<p class="panel-bar-content-text">
ODOO4projects needed to instantly deploy new Odoo instances for clients worldwide, a process that was manual, time-consuming, and prone to configuration errors. They required a system that could scale globally and provide centralized oversight.
</p>
<p class="panel-bar-content-text">
We built a smart agent powered by an n8n workflow that completely automates the provisioning of Odoo instances. The system also generates a weekly server status report, providing a clear, consolidated view of their entire infrastructure.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> A fully automated, scalable, and reliable system for Odoo instance management, all running on our cost-effective n8n community instance.
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Scale Your Operations with Automation <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
<div class="accordion-item">
<button class="accordion-title">From Simple Chatbot to Intelligent Odoo-Powered Agent <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: Unlocking the Power of Conversational AI</h2>
<p class="panel-bar-content-text">
A client wanted to move beyond a basic FAQ chatbot. They needed an intelligent agent that could handle complex internal tasks, provide instant knowledge base access, and integrate seamlessly with their Odoo chatter.
</p>
<p class="panel-bar-content-text">
Leveraging powerful OCA modules and an n8n-powered backend, we built a sophisticated AI agent. This solution connects to LLMs, agents, and RAG pipelines, transforming their Odoo chatter into a powerful, intelligent hub for communication and task management.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> A truly intelligent conversational AI that streamlines employee onboarding, provides instant knowledge access, and can be embedded on any website.
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Integrate AI into Your Workflows <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
<div class="accordion-item">
<button class="accordion-title">The Future of Sales: A Checkout Process Inside a Chatbox <span class="panel-bar-title"><i class="fa-solid fa-chevron-down"></i></span></button>
<div class="accordion-content">
<section class="panel-bar-content">
<h2 class="panel-bar-content-title">The Challenge: Reinventing the Sales Funnel</h2>
<p class="panel-bar-content-text">
We asked ourselves: could we transform a simple conversation into a complete sales transaction? The goal was to create a frictionless procurement workflow that eliminates the need for emails, forms, and spreadsheets.
</p>
<p class="panel-bar-content-text">
Using only n8n and PayPal, we built a revolutionary checkout process that lives entirely within a chatbox. This allows for a seamless, conversational sales experience, from initial inquiry to final purchase.
</p>
<p class="panel-bar-content-text">
<strong>The Result:</strong> A groundbreaking, conversational commerce solution that redefines the client interaction and digital selling. Ready to see it in action?
</p>
<div style="margin-top: 2em;">
<a href="#packages" class="btn-text">Experience the Future of Sales <span class="btn-text-icon"><i class="fa-solid fa-angle-right"></i></span></a>
</div>
</section>
</div>
</div>
</div>
</section>
<footer>
<p>For ODOO and N8N tips and tricks and news follow us on
<a href="https://www.linkedin.com/company/od8n" target="_blank" aria-label="LinkedIn"><i class="fab fa-linkedin"></i></a>
</p>
<p>© 2025 OD8N. All Rights Reserved. Service provided by ASC Consultatores Paraguay</p>
</footer>
<script defer src="./app.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

167
public/tryNow.js Normal file
View File

@@ -0,0 +1,167 @@
const TRYNOW_WEBHOOK_URL = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/c25169c6-4234-4b47-8e74-612b9539da0a";
function tryNow_createModal() {
const modal = document.createElement("div");
modal.id = "trynowModal";
modal.style.position = "fixed";
modal.style.top = "0";
modal.style.left = "0";
modal.style.width = "100%";
modal.style.height = "100%";
modal.style.backgroundColor = "rgba(0,0,0,0.6)";
modal.style.display = "none";
modal.style.justifyContent = "center";
modal.style.alignItems = "center";
modal.style.zIndex = "1000";
modal.innerHTML = `
<div style="
background: #ffffff;
padding: 40px 30px;
border-radius: 16px;
max-width: 400px;
width: 90%;
position: relative;
font-family: 'Arial', sans-serif;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
">
<span id="trynowCloseModal" style="
position: absolute;
top: 15px;
right: 20px;
cursor: pointer;
font-weight: bold;
font-size: 24px;
color: #555;
">&times;</span>
<h2 style="margin-bottom: 20px; font-size: 24px; color: #333;">Order Details</h2>
<form id="trynowForm" style="display: flex; flex-direction: column; gap: 15px;">
<input type="email" name="email" placeholder="Email" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<select name="location" id="trynowLocationSelect" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<option value="">Select Location</option>
<option value="Boston">Boston</option>
<option value="Manchester">Manchester</option>
<option value="Mumbai">Mumbai</option>
<option value="Saopaulo">Sao Paulo</option>
</select>
<select name="product" required style="padding: 12px 15px; font-size: 16px; border: 1px solid #ccc; border-radius: 8px; outline: none;">
<option value="odoo_19" selected>ODOO</option>
<option value="N8N">n8n</option>
</select>
<button type="submit" style="
padding: 14px;
background: #007BFF;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
transition: background 0.3s;
">Start free Trial</button>
</form>
<div id="trynowConfirmation" style="
display: none;
margin-top: 20px;
padding: 20px;
background-color: #e6ffed;
color: #056608;
border-radius: 12px;
text-align: center;
font-weight: 500;
">
<p>Thank you for your submission! 🎉</p>
<button id="trynowCloseConfirmation" style="
margin-top: 10px;
padding: 10px 20px;
background: #007BFF;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s;
">Close</button>
</div>
</div>
`;
document.body.appendChild(modal);
// Close modal handlers
document.getElementById("trynowCloseModal").onclick = () => { modal.style.display = "none"; };
document.getElementById("trynowCloseConfirmation").onclick = () => {
document.getElementById("trynowConfirmation").style.display = "none";
modal.style.display = "none";
};
modal.onclick = (e) => { if (e.target === modal) modal.style.display = "none"; };
return modal;
}
function tryNow_handleFormSubmit() {
const form = document.getElementById("trynowForm");
const confirmation = document.getElementById("trynowConfirmation");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const data = {};
new FormData(form).forEach((value, key) => (data[key] = value));
try {
const res = await fetch(TRYNOW_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
if (res.ok) {
form.style.display = "none";
confirmation.style.display = "block";
form.reset();
} else {
alert("Failed to submit form.");
}
} catch (err) {
console.error(err);
alert("Error submitting form.");
}
});
}
function tryNow_attachButtons() {
const buttons = Array.from(document.querySelectorAll("a, button"));
buttons.forEach(btn => {
if (btn.textContent && btn.textContent.trim() === "Try Now") {
btn.addEventListener("click", (e) => {
e.preventDefault();
const modal = document.getElementById("trynowModal");
if (modal) {
modal.style.display = "flex";
// Reset modal state
const form = document.getElementById("trynowForm");
const confirmation = document.getElementById("trynowConfirmation");
if (form && confirmation) {
form.style.display = "flex";
confirmation.style.display = "none";
}
}
});
}
});
}
document.addEventListener("DOMContentLoaded", () => {
tryNow_createModal();
tryNow_handleFormSubmit();
tryNow_attachButtons();
});

View File

@@ -0,0 +1,171 @@
(function() {
console.log("WIDGET COOL")
const currentScript = document.currentScript;
const affiliateFromAttr = currentScript.getAttribute("data-affiliate");
const params = new URLSearchParams(window.location.search);
const affiliateFromURL = params.get("affiliate");
const affiliateCode = affiliateFromURL || affiliateFromAttr || "None";
const container = document.getElementById("my-signup-widget");
if (!container) return;
const shadow = container.attachShadow({ mode: "open" });
const styles = `
:host {
all: initial;
font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Inter,Arial,sans-serif;
display: block;
max-width: 420px;
margin: 0 auto;
}
.card {
background: linear-gradient(135deg,#ffffff 0%,#f7fafc 60%);
border: 1px solid #e6e8eb;
border-radius: 16px;
box-shadow: 0 6px 20px rgba(16,24,40,.08);
padding: 20px;
}
.header { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.icon { width: 36px; height: 36px; border-radius: 12px;
display: flex; align-items: center; justify-content: center;
font-weight: 700; color: #fff;
}
.title { font-size: 18px; line-height: 1.2; font-weight: 800; color: #0f172a; }
.subtitle { font-size: 13px; color: #475569; }
label, .label { display: block; font-size: 12px; color: #334155; font-weight: 600; margin: 12px 0 6px; }
.options { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 6px; }
.option { flex: 1; display: flex; align-items: center; gap: 8px;
border: 1px solid #cbd5e1; border-radius: 10px; padding: 10px 12px;
cursor: pointer; background: #fff;
}
input[type="radio"] { accent-color: #3b82f6; margin: 0; }
select, input[type="email"], .affiliate-display {
width: 100%;
background: #fff;
border: 1px solid #cbd5e1;
border-radius: 10px;
padding: 10px 12px;
font-size: 14px;
color: #0f172a;
outline: none;
box-sizing: border-box;
}
.affiliate-display { background: #f8fafc; }
button {
margin-top: 16px;
width: 100%;
border: none;
border-radius: 12px;
padding: 12px 14px;
font-size: 15px;
font-weight: 800;
background: linear-gradient(135deg,#22c55e,#3b82f6);
color: #fff;
cursor: pointer;
box-shadow: 0 8px 18px rgba(37,99,235,.25);
}
.footnote { margin-top: 10px; font-size: 11px; color: #64748b; text-align: center; }
.status {
padding: 20px;
font-size: 15px;
color: #0f172a;
background: linear-gradient(135deg,#f0f9ff,#e0f2fe);
border: 1px solid #cbd5e1;
border-radius: 12px;
text-align: center;
box-shadow: 0 4px 12px rgba(16,24,40,.08);
word-break: break-word;
}
.status.success { background: linear-gradient(135deg,#d1fae5,#a7f3d0); color: #065f46; }
.status.error { background: linear-gradient(135deg,#fee2e2,#fecaca); color: #991b1b; }
`;
const html = `
<form class="card">
<div class="header">
<img class="icon" src="https://sp01.odoo4projects.com/hugo/live/4.svg">
<div>
<div class="title">Try Managed ODOO or n8n - 4 weeks Free</div>
<div class="subtitle">Full-featured 4-week trial. No credit card required.</div>
</div>
</div>
<div class="label">Product</div>
<div class="options">
<label class="option">
<input type="radio" name="product" value="odoo_19" required checked>
<span>ODOO</span>
</label>
<label class="option">
<input type="radio" name="product" value="N8N" required>
<span>N8N</span>
</label>
</div>
<label for="location">Location</label>
<select id="location" name="location" required>
<option value="" disabled>Select a region</option>
<option value="manchester">UK, Manchester</option>
<option value="boston">US, Boston</option>
<option value="mumbai">IN, Mumbai</option>
<option value="saopaulo">BR, Sao Paulo</option>
<option value="Meppel">NL, Meppel</option>
</select>
<label for="email">Work Email</label>
<input id="email" name="email" type="email" required placeholder="you@company.com">
<input type="hidden" name="affiliate_code" value="${affiliateCode}">
<button type="submit" class="plausible-event-name=Trial">Start 4-Week Free Trial</button>
<div class="footnote">
By submitting, you agree to the T&C of ODOO4projects.
</div>
</form>
<div id="status" class="status" style="display:none;"></div>
`;
shadow.innerHTML = `<style>${styles}</style>${html}`;
const form = shadow.querySelector("form");
const statusEl = shadow.getElementById("status");
form.addEventListener("submit", async (e) => {
e.preventDefault();
statusEl.style.display = "block";
statusEl.textContent = "Submitting...";
statusEl.className = "status";
try {
const formData = new FormData(form);
const res = await fetch("https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/c25169c6-4234-4b47-8e74-612b9539da0a", {
method: "POST",
body: formData,
});
const text = await res.text();
// Hide the form
form.style.display = "none";
// Show the webhook response nicely
statusEl.textContent = text;
statusEl.className = "status success";
} catch (err) {
statusEl.textContent = "❌ Failed to submit. Please try again.";
statusEl.className = "status error";
}
});
})();

View File

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Order Status Wizard</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<ul class="wizard">
<li class="open">
<div class="header">
<i class="fa-solid fa-signature"></i>
<span class="title">Name</span>
</div>
<div class="text">Open</div>
</li>
<li class="done">
<div class="header">
<i class="fa-solid fa-envelope"></i>
<span class="title">Email</span>
</div>
<div class="text">deraa@fewew.de</div>
</li>
<li class="done">
<div class="header">
<i class="fa-solid fa-cart-shopping"></i>
<span class="title">Product</span>
</div>
<div class="text">3 Hours</div>
</li>
</ul>
</body>
</html>

View File

@@ -0,0 +1,69 @@
body {
margin: 0;
font-family: Arial, sans-serif;
}
.wizard {
display: flex;
justify-content: space-between;
list-style: none;
padding: 0;
margin: 0;
width: 100%;
background-color: #e0e0e0;
}
.wizard li {
flex: 1;
display: flex;
flex-direction: column;
border-right: 2px solid #fff;
color: white;
box-sizing: border-box;
}
.wizard li:last-child {
border-right: none;
}
.wizard .header {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
padding: 10px;
font-weight: bold;
font-size: 16px;
text-align: center;
}
/* Detail text section */
.wizard .text {
padding: 20px 10px;
text-align: center;
font-size: 14px;
flex-grow: 1;
}
/* DARK GREY: Open */
.wizard li.open {
background-color: #7f8c8d;
}
.wizard li.open .header {
background-color: #5d6d7e;
}
/* DARK GREEN: Done */
.wizard li.done {
background-color: #1e8449;
}
.wizard li.done .header {
background-color: #145a32;
}
.wizard i {
font-size: 18px;
}

View File

@@ -0,0 +1,270 @@
/* cw.css */
/* Container */
#cw-chatToggle {
position: fixed;
bottom: 24px;
right: 24px;
color: white;
padding: 12px 16px;
border-radius: 25px;
font-weight: 600;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
z-index: 100000;
border: none;
user-select: none;
transition: background-color 0.3s ease;
}
#cw-chatToggle:hover {
transform: translateY(-5px);
}
#cw-chatToggle div {
display: flex;
align-items: center;
gap: 8px;
}
#cw-chatToggle img {
image-rendering: pixelated;
image-rendering: optimizeQuality;
image-rendering: -webkit-optimize-contrast; /* Chrome */
image-rendering: crisp-edges; /* Fallback */
}
#cw-chatToggle span.text-sm {
font-weight: 500;
font-size: 0.875rem; /* 14px */
}
#cw-chatToggle span.text-xs {
font-size: 0.75rem; /* 12px */
}
/* Chat widget container */
#cw-chatWidget {
display: none;
position: fixed;
bottom: 100px;
right: 24px;
width: 500px;
max-height: 70vh;
background: white;
border: 1px solid #ccc;
border-radius: 12px;
flex-direction: column;
z-index: 100000;
font-family: system-ui, sans-serif;
}
#cw-chatWidget.active {
display: flex;
}
/* Logo container */
#cw-chatWidget .logo-container {
display: flex;
justify-content: center;
padding-top: 16px;
padding-bottom: 10px;
}
#cw-chatWidget .logo-container img {
height: 60px;
user-select: none;
image-rendering: optimizeQuality;
image-rendering: pixelated;
image-rendering: -webkit-optimize-contrast; /* Chrome */
image-rendering: crisp-edges; /* Fallback */
}
/* Header */
#cw-status {
padding: 10px 10px;
background-color: #f0f0f0;
border-bottom: 1px solid #ddd;
max-height: 0;
transition: max-height 0.5s ease-in-out, padding 0.5s ease-in-out;
}
#cw-chatWidget .header {
background-color: #0070c0;
color: white;
padding: 8px 16px;
font-weight: 600;
text-align: center;
user-select: none;
}
/* Messages container */
#cw-chatMessages {
flex: 1 1 auto;
overflow-y: auto;
padding: 16px;
font-size: 0.875rem;
line-height: 1.4;
user-select: text;
scroll-behavior: smooth;
background: #fafafa;
}
/* Chat form */
#cw-chatForm {
display: flex;
margin: 0px;
border-top: 1px solid #ddd;
}
#cw-chatInput {
flex: 1;
border: none;
padding: 8px 20px;
font-size: 1rem;
outline-offset: 2px;
border-radius: 0 0 0 12px;
}
#cw-chatInput:focus {
outline: 0px solid #0070c0;
}
#cw-chatForm button {
background-color: #0070c0;
border: none;
color: white;
padding: 0 16px;
cursor: pointer;
font-weight: 600;
border-radius: 0 0 12px 0;
transition: background-color 0.3s ease;
}
#cw-chatForm button:hover {
background-color: #005a9e;
}
/* Message bubbles */
.cw-message {
max-width: 75%;
margin-bottom: 8px;
padding: 8px 12px;
border-radius: 12px;
word-wrap: break-word;
white-space: pre-wrap;
}
.cw-message.user {
background-color: #DCF8C6;
color: #333;
margin-left: auto;
text-align: right;
}
.cw-message.bot {
background-color: #E0E0E0;
color: #333;
margin-right: auto;
text-align: left;
}
.button {
display: inline-block;
padding: 12px 24px;
background-color: transparent;
color: inherit;
text-align: center;
text-decoration: none;
border-radius: 0px;
font-size: 16px;
border: none;
font-weight: bold;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
position: absolute;
right: 0;
top: 10%;
transform: translateY(-50%)
}
.button:hover {
opacity: 0.8;
}
.button:active {
opacity: 0.6;
}
/* Headline above the wizard */
.wizard-headline {
text-align: center;
font-size: 24px;
font-weight: bold;
padding: 20px 0;
background-color: #f8f9f9;
color: #2c3e50;
border-bottom: 2px solid #ddd;
}
/* Main wizard container */
.wizard {
display: flex;
justify-content: space-between;
list-style: none;
padding: 0;
margin: 0;
width: 100%;
background-color: #e0e0e0;
}
/* Each step box */
.wizard li {
flex: 1;
display: flex;
flex-direction: column;
border-right: 2px solid #fff;
color: white;
box-sizing: border-box;
}
.wizard li:last-child {
border-right: none;
}
/* Header inside each box (icon + label) */
.wizard .header {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
padding: 10px;
font-weight: bold;
font-size: 16px;
text-align: center;
}
/* Detail text area inside the box */
.wizard .text {
padding: 20px 10px;
text-align: center;
font-size: 14px;
flex-grow: 1;
}
/* DARK GREY: Open */
.wizard li.open {
background-color: #7f8c8d;
}
.wizard li.open .header {
background-color: #5d6d7e;
}
/* DARK GREEN: Done */
.wizard li.done {
background-color: #1e8449;
}
.wizard li.done .header {
background-color: #145a32;
}
/* Icon style */
.wizard i {
font-size: 18px;
}

View File

@@ -0,0 +1,62 @@
import { Conversation } from 'https://cdn.skypack.dev/@elevenlabs/client';
let conversation = null;
let inCall = false;
document.addEventListener('JSsucks', () => {
const callBtn = document.getElementById('callBtn');
callBtn.addEventListener('click', async () => {
if (!inCall) {
console.log('Requesting microphone access...');
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (error) {
console.log('Microphone access denied.');
return;
}
console.log('Connecting to agent...');
try {
conversation = await Conversation.startSession({
agentId: 'agent_01jx2xm2w4exwvjhbq52js687f',
connectionType: 'websocket',
onConnect: () => {
console.log('✅ Connected to AI Agent!');
callBtn.style.color = 'red';
inCall = true;
},
onDisconnect: () => {
console.log('🔌 Disconnected.');
callBtn.style.color = 'grey';
inCall = false;
},
onError: (err) => {
console.log(`❌ Error: ${err.message || err}`);
},
onMessage: (msg) => {
console.log('Agent:', msg);
},
});
} catch (err) {
console.log('Failed to start session.');
console.error(err);
}
} else {
console.log('Hanging up...');
try {
await conversation.endSession();
conversation = null;
inCall = false;
callBtn.style.color = 'grey';
console.log('✅ Call ended.');
} catch (err) {
console.error('Error ending the call:', err);
}
}
});
});

View File

@@ -0,0 +1,6 @@
{
"api": "https://002-001-433cd554-0d64-4834-8500-3aebea781003.odoo4projects.com/webhook/81742473-b50b-4845-a5f9-916d9fe60876/chat",
"preamble": "Welcome to Brandize",
"widgetHTML": "<button id='cw-chatToggle' aria-label='Toggle chat widget' type='button'>\n <img height='50px' src='widget/custom/brandize.svg' alt='Logo'></button>\n<div id='cw-chatWidget' style='display: none;' role='region' aria-live='polite' aria-label='Chat widget'>\n <div class='logo-container'><img src='widget/custom/brandize.svg' alt='Logo' /><button id='callBtn' class='button'>📞</button></div>\n <div class='header'>Support</div>\n <div id='cw-status' style='transition: all 0.3s ease; overflow: hidden;'></div>\n <div id='cw-chatMessages' style='max-height: 200px; overflow-y: auto; padding: 10px;'></div>\n <form id='cw-chatForm' autocomplete='off'>\n <input type='text' id='cw-chatInput' placeholder='Type your message...' required autocomplete='off' />\n <button type='submit'>Send</button>\n </form>\n</div>"
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,268 @@
/* cw.css */
/* Container */
#cw-chatToggle {
position: fixed;
bottom: 24px;
right: 24px;
color: white;
padding: 12px 16px;
border-radius: 25px;
font-weight: 600;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
z-index: 100000;
border: none;
user-select: none;
transition: background-color 0.3s ease;
width: 100px;
height: 50px;
box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
}
#cw-chatToggle:hover {
transform: translateY(-5px);
}
#cw-chatToggle div {
display: flex;
align-items: center;
gap: 8px;
}
#cw-chatToggle img {
image-rendering: pixelated;
image-rendering: optimizeQuality;
image-rendering: -webkit-optimize-contrast; /* Chrome */
image-rendering: crisp-edges; /* Fallback */
}
#cw-chatToggle span.text-sm {
font-weight: 500;
font-size: 0.875rem; /* 14px */
}
#cw-chatToggle span.text-xs {
font-size: 0.75rem; /* 12px */
}
/* Chat widget container */
#cw-chatWidget {
display: none;
position: fixed;
bottom: 100px;
right: 24px;
width: 500px;
max-height: 70vh;
background: white;
border: 1px solid #ccc;
border-radius: 12px;
flex-direction: column;
z-index: 100000;
font-family: system-ui, sans-serif;
}
#cw-chatWidget.active {
display: flex;
}
/* Logo container */
#cw-chatWidget .logo-container {
display: flex;
justify-content: center;
padding-top: 16px;
padding-bottom: 10px;
}
#cw-chatWidget .logo-container img {
height: 60px;
user-select: none;
image-rendering: optimizeQuality;
image-rendering: pixelated;
image-rendering: -webkit-optimize-contrast; /* Chrome */
image-rendering: crisp-edges; /* Fallback */
}
/* Header */
#cw-status {
padding: 10px 10px;
background-color: #f0f0f0;
border-bottom: 1px solid #ddd;
max-height: 0;
transition: max-height 0.5s ease-in-out, padding 0.5s ease-in-out;
}
#cw-chatWidget .header {
background-color: #0070c0;
color: white;
padding: 8px 16px;
font-weight: 600;
text-align: center;
user-select: none;
}
/* Messages container */
#cw-chatMessages {
flex: 1 1 auto;
overflow-y: auto;
padding: 16px;
font-size: 0.875rem;
line-height: 1.4;
user-select: text;
scroll-behavior: smooth;
background: #fafafa;
}
/* Chat form */
#cw-chatForm {
display: flex;
margin: 0px;
border-top: 1px solid #ddd;
}
#cw-chatInput {
flex: 1;
border: none;
padding: 8px 20px;
font-size: 1rem;
outline-offset: 2px;
border-radius: 0 0 0 12px;
}
#cw-chatInput:focus {
outline: 0px solid #0070c0;
}
#cw-chatForm button {
background-color: #0070c0;
border: none;
color: white;
padding: 0 16px;
cursor: pointer;
font-weight: 600;
border-radius: 0 0 12px 0;
transition: background-color 0.3s ease;
}
#cw-chatForm button:hover {
background-color: #005a9e;
}
/* Message bubbles */
.cw-message {
max-width: 75%;
margin-bottom: 8px;
padding: 8px 12px;
border-radius: 12px;
word-wrap: break-word;
white-space: pre-wrap;
}
.cw-message.user {
background-color: #DCF8C6;
color: #333;
margin-left: auto;
text-align: right;
}
.cw-message.bot {
background-color: #E0E0E0;
color: #333;
margin-right: auto;
text-align: left;
}
.button {
display: inline-block;
padding: 12px 24px;
background-color: #4CAF50; /* Green */
color: white;
text-align: center;
text-decoration: none;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.button:hover {
background-color: #45a049;
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
}
.button:active {
background-color: #3e8e41;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* Headline above the wizard */
.wizard-headline {
text-align: center;
font-size: 24px;
font-weight: bold;
padding: 20px 0;
background-color: #f8f9f9;
color: #2c3e50;
border-bottom: 2px solid #ddd;
}
/* Main wizard container */
.wizard {
display: flex;
justify-content: space-between;
list-style: none;
padding: 0;
margin: 0;
width: 100%;
background-color: #e0e0e0;
}
/* Each step box */
.wizard li {
flex: 1;
display: flex;
flex-direction: column;
border-right: 2px solid #fff;
color: white;
box-sizing: border-box;
}
.wizard li:last-child {
border-right: none;
}
/* Header inside each box (icon + label) */
.wizard .header {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
padding: 10px;
font-weight: bold;
font-size: 16px;
text-align: center;
}
/* Detail text area inside the box */
.wizard .text {
padding: 20px 10px;
text-align: center;
font-size: 14px;
flex-grow: 1;
}
/* DARK GREY: Open */
.wizard li.open {
background-color: #7f8c8d;
}
.wizard li.open .header {
background-color: #5d6d7e;
}
/* DARK GREEN: Done */
.wizard li.done {
background-color: #1e8449;
}
.wizard li.done .header {
background-color: #145a32;
}
/* Icon style */
.wizard i {
font-size: 18px;
}

View File

View File

@@ -0,0 +1,6 @@
{
"api": "https://ai.odoo4projects.com/webhook/81742473-b50b-4845-a5f9-916d9fe60876/chat",
"preamble": "Welcome to OD8N. We like to help you assisting your order as well as answering FAQs about our service",
"widgetHTML": "<button id='cw-chatToggle' aria-label='Toggle chat widget' type='button' class='plausible-event-name=OpenChat'>\n <img height='50px'src='logo.svg' alt='Logo'></button>\n<div id='cw-chatWidget' style='display: none;' role='region' aria-live='polite' aria-label='Chat widget'>\n <div class='logo-container'><img src='logo.svg' alt='Logo' /></div>\n <div class='header'>Support</div>\n <div id='cw-status' style='transition: all 0.3s ease; overflow: hidden;'></div>\n <div id='cw-chatMessages' style='max-height: 200px; overflow-y: auto; padding: 10px;'></div>\n <form id='cw-chatForm' autocomplete='off'>\n <input type='text' id='cw-chatInput' placeholder='Type your message...' required autocomplete='off' />\n <button type='submit'>Send</button>\n </form>\n</div>"
}

105
public/widget/widget.js Normal file
View File

@@ -0,0 +1,105 @@
document.addEventListener('DOMContentLoaded', async () => {
try {
const response = await fetch(configUrl);
if (!response.ok) throw new Error(`Config not found at ${configUrl}`);
const config = await response.json();
const { widgetHTML, preamble, api } = config;
if (!widgetHTML || !api) {
console.error('Invalid config format. Expected widgetHTML and api.');
return;
}
// Inject HTML
document.body.insertAdjacentHTML('beforeend', widgetHTML);
console.log("HERE!")
document.dispatchEvent(new CustomEvent('JSsucks'));
// DOM references
const chatToggle = document.getElementById('cw-chatToggle');
const chatWidget = document.getElementById('cw-chatWidget');
const chatForm = document.getElementById('cw-chatForm');
const chatInput = document.getElementById('cw-chatInput');
const chatMessages = document.getElementById('cw-chatMessages');
const statusDiv = document.getElementById('cw-status');
const sessionId = crypto.randomUUID();
let chatOpened = false;
// Append a message to the chat
function appendMessage(text, sender) {
const msg = document.createElement('div');
msg.classList.add('cw-message', sender === 'user' ? 'user' : 'bot');
msg.innerHTML = text;
chatMessages.appendChild(msg);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Centralized function to send a message
async function sendMessageToBot(messageText) {
console.log(messageText)
chatWidget.style.display = 'block';
appendMessage(messageText, 'user');
chatInput.value = '';
chatInput.focus();
try {
const res = await fetch(api, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'sendMessage',
sessionId,
chatInput: messageText
})
});
const data = await res.json();
if (data.status) {
statusDiv.innerHTML = data.status;
requestAnimationFrame(() => {
statusDiv.style.padding = '10px';
statusDiv.style.maxHeight = statusDiv.scrollHeight + 'px';
});
} else {
statusDiv.style.maxHeight = '0';
statusDiv.style.padding = '0 10px';
}
appendMessage(data.output, 'bot');
} catch (error) {
appendMessage('Fehler beim Verbinden mit dem Server. Bitte versuchen Sie es später erneut.', 'bot');
console.error(error);
}
}
window.sendMessageToBot = sendMessageToBot;
// Toggle chat widget
chatToggle.addEventListener('click', () => {
const isVisible = chatWidget.style.display === 'block';
chatWidget.style.display = isVisible ? 'none' : 'block';
if (!isVisible && !chatOpened) {
appendMessage(preamble, 'bot');
chatOpened = true;
}
});
// Form submission
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
const input = chatInput.value.trim();
if (input) {
sendMessageToBot(input);
}
});
// Attach buy button listeners
} catch (err) {
console.error('Failed to initialize chat widget:', err);
}
});

5
serve
View File

@@ -1,6 +1,5 @@
#!/bin/bash #!/bin/bash
cd public
pip install livereload pip install livereload
python ../serve.py cd public
cd .. python ../serve.py public/