Author SHA1 Message Date
oliver a54dc22bd1 copy(footer): say "AI Agents and N8N automations" in the About-us blurb
Updates the homepage footer tagline to name AI Agents and N8N
automations explicitly. Markup-preserving: existing <strong> emphasis
on "stability" and "power" is unchanged; no structural, style or
layout changes.
2026-10-20 13:09:31 +00:00
oliver ca89d8e7b7 confirm 2026-10-17 17:10:09 -03:00
oliver 29127d4025 chat fixes 2026-10-17 10:50:45 -03:00
oliver b4e0faa62e UTM 2026-10-17 10:40:05 -03:00
oliver 575e3efece Update upsell.html 2026-10-17 09:39:08 -03:00
oliver 62f92769a1 Update upsell.html 2026-10-17 09:30:55 -03:00
oliver 3637094896 url 2026-10-16 08:42:42 -03:00
oliver 609f9d64bf new produt id 2026-10-16 08:39:01 -03:00
oliver 1ab6c47df6 Update index.html 2026-10-16 08:29:29 -03:00
Reef 354799b9ca fix: repair contact flow and buy flow targets
- Buy Now product IDs aligned to the Odoo shop (54/55/56), matching odoo/upsell.html
- support.html contact form: rewire from dead ODOO4projects.com/website/form endpoint to n8n CRM webhook; remove /web/csrf_token prefetch; show success only on res.ok (previously always showed 'submitted' even when nothing was delivered)
- /contactus links (footer nav, Schema contactPoint, order widget fallback, trial email) retargeted to support.html#contact; canonical/og:url corrected to /support.html
- Custom Quote and footer contact handlers hardened with res.ok checks
- buyNow basket target verified: 20.odoo4projects.com/shop/post_basket (200)
2026-10-12 15:41:11 +00:00
7 changed files with 157 additions and 46 deletions

No files matched your search

+1 -1
View File
@@ -24,7 +24,7 @@ Enterprise-grade Odoo Community hosting without the enterprise price tag. Commun
- Static site (index.html, support.html, legal.html, blog/) deployed via git push to git.odoo4projects.com; auto-deploys on every change.
- Order flow: inline order widget on the homepage opens Stripe checkout; coupon FREE TRIAL = 1 month free; yearly billing saves roughly 8-10%; "Satisfied or reimbursed" guarantee; server location chosen at checkout (US, Brazil, India, Australia, Netherlands, UK).
- Support: AI support bot (agent.js from static.odoo4projects.com) plus human team; email support@odoo4projects.com; /contactus route.
- Support: AI support bot (agent.js from static.odoo4projects.com) plus human team; email support@odoo4projects.com; contact form on support.html#contact.
- Blog with guides and news (10 posts); homepage renders it with search and pagination.
## Capabilities and Constraints
+1 -1
View File
@@ -190,7 +190,7 @@
<p>
<a href="https://odoo4projects.com/legal/privacy">Privacy</a> &bull;
<a href="https://odoo4projects.com/legal/terms">Terms</a> &bull;
<a href="https://odoo4projects.com/contactus">Contact</a>
<a href="https://ODOO4projects.com/support.html#contact">Contact</a>
</p>
<p class="unsubscribe">
<a href="https://odoo4projects.com/unsubscribe?token=USER_TOKEN">Unsubscribe</a> from these emails
+17 -5
View File
@@ -20,7 +20,7 @@
"url": "https://ODOO4projects.com/",
"description": "Odoo hosting with fully managed infrastructure. Deploy in 5 minutes with automatic backups, SSL, and Git-based modules.",
"foundingDate": "2020",
"contactPoint": { "@type": "ContactPoint", "contactType": "customer support", "url": "https://ODOO4projects.com/contactus" },
"contactPoint": { "@type": "ContactPoint", "contactType": "customer support", "url": "https://ODOO4projects.com/support.html#contact" },
"sameAs": ["https://www.linkedin.com/company/odoo4projects/"],
"offers": [
{ "@type": "Offer", "name": "Side Hustle", "price": "214.00", "priceCurrency": "USD", "url": "https://ODOO4projects.com/#pricing" },
@@ -117,6 +117,13 @@
.hero-sub{font-size:1.05rem;color:rgba(255,255,255,.7);max-width:560px;line-height:1.7;margin-bottom:1.5rem}
.hero-stats{display:flex;gap:1.5rem;margin-bottom:2rem;flex-wrap:wrap}.hero-stat{display:flex;align-items:center;gap:.4rem;color:rgba(255,255,255,.65);font-size:.85rem}.hero-stat i{color:var(--brass);font-size:.75rem}
.hero-actions{display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:2rem}
.hero-notice{position:absolute;top:1.25rem;right:1.25rem;z-index:3;display:flex;align-items:center;gap:.55rem;max-width:min(360px,calc(100% - 2rem));padding:.6rem .85rem .6rem 1rem;background:#f5c542;color:var(--midnight);border:1px solid rgba(13,27,46,.14);border-radius:var(--radius);box-shadow:0 8px 24px rgba(13,27,46,.3);font-family:Inter,sans-serif;font-size:.82rem;line-height:1.4;animation:heroNoticeIn .4s cubic-bezier(.16,1,.3,1)}
.hero-notice[hidden]{display:none}
.hero-notice i{color:var(--midnight);font-size:.9rem;flex-shrink:0}
.hero-notice-text{flex:1;min-width:0;overflow-wrap:anywhere}
.hero-notice-close{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.35rem;height:1.35rem;padding:0;border:0;border-radius:50%;background:rgba(13,27,46,.12);color:var(--midnight);font-size:1rem;line-height:1;cursor:pointer;transition:background .2s}
.hero-notice-close:hover{background:rgba(13,27,46,.24)}
@keyframes heroNoticeIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
.server-banner{background:var(--midnight);padding:2rem 0 1.5rem;border-top:1px solid var(--line-brass);border-bottom:1px solid var(--line-brass);position:relative;overflow:hidden}
.server-banner::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse 60% 80% at 50% 100%,rgba(201,162,39,.06) 0%,transparent 70%);pointer-events:none}
.server-banner-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.25rem;flex-wrap:wrap;gap:.5rem}
@@ -138,7 +145,7 @@
.pricing-server-select{padding:.5rem 1rem;border-radius:var(--radius);border:1px solid var(--line-brass);background:rgba(255,255,255,.08);color:#fff;font-family:Inter,sans-serif;font-size:.85rem;outline:none;cursor:pointer;min-width:180px}
.pricing-server-select option{background:var(--midnight);color:#fff}
@media(max-width:992px){.server-grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:768px){.hero{min-height:auto;padding:3rem 0 0}.hero-actions{flex-direction:column}.hero-actions .btn{width:100%;text-align:center}.hero-stats{gap:.75rem}.hero-city-label{bottom:1rem;right:1rem;font-size:.78rem;padding:.5rem .9rem}}
@media(max-width:768px){.hero{min-height:auto;padding:3rem 0 0}.hero.has-hero-notice{padding-top:4.75rem}.hero-actions{flex-direction:column}.hero-actions .btn{width:100%;text-align:center}.hero-stats{gap:.75rem}.hero-city-label{bottom:1rem;right:1rem;font-size:.78rem;padding:.5rem .9rem}.hero-notice{top:.75rem;left:.75rem;right:.75rem;max-width:none;font-size:.78rem}}
@media(max-width:576px){.server-grid{grid-template-columns:repeat(2,1fr)}.server-banner-header{flex-direction:column;align-items:flex-start}}
.status-dot{width:7px;height:7px;border-radius:50%;background:var(--brass);display:inline-block;animation:statusPulse 1.2s ease-in-out infinite}
@keyframes statusPulse{0%,100%{opacity:.3;transform:scale(.8)}50%{opacity:1;transform:scale(1.2)}}
@@ -217,6 +224,11 @@
-->
<!-- Hero -->
<section class="hero">
<div class="hero-notice" id="heroConfirmNotice" role="status" aria-live="polite" hidden>
<i class="fas fa-check-circle" aria-hidden="true"></i>
<span class="hero-notice-text"></span>
<button type="button" class="hero-notice-close" aria-label="Dismiss notification">&times;</button>
</div>
<div class="container">
<div class="hero-kicker">Fully Managed Odoo Community Hosting</div>
<h1>Deploy Odoo in <span>5 Minutes</span> — Fully Managed Hosting</h1>
@@ -489,7 +501,7 @@
</div>
<p class="ow-coupon-status" id="orderCouponStatus">&#10003; Coupon applied</p>
</div>
<a id="orderBuyBtn" href="/contactus" class="btn btn-brass ow-buy">Continue to Stripe &rarr;</a>
<a id="orderBuyBtn" href="/support.html#contact" class="btn btn-brass ow-buy">Continue to Stripe &rarr;</a>
<p class="ow-note">&#128274; Secure Stripe checkout &middot; Credit card required</p>
</div>
</div>
@@ -614,7 +626,7 @@
<li><a href="index.html">Home</a></li>
<li><a href="support.html">Support</a></li>
<li><a href="legal.html">Legal</a></li>
<li><a href="/contactus">Contact us</a></li>
<li><a href="/support.html#contact">Contact us</a></li>
</ul>
</div>
<div>
@@ -627,7 +639,7 @@
</div>
<div class="fcol-desc">
<h4>About us</h4>
<p>We build smart, scalable business solutions by combining the <strong>stability</strong> of Odoo ERP with the <strong>power</strong> of AI and N8N automation.</p>
<p>We build smart, scalable business solutions by combining the <strong>stability</strong> of Odoo ERP with the <strong>power</strong> of AI Agents and N8N automations.</p>
<p>From streamlined sales to global rollouts, our <strong>lean, eco-friendly</strong> approach helps businesses cut costs, boost efficiency, and grow without limits.</p>
</div>
<div>
+9 -13
View File
@@ -20,7 +20,7 @@
*{box-sizing:border-box}
body{margin:0;background:linear-gradient(180deg,var(--bg),#eef2f8);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:28px}
.app{width:960px;max-width:96vw;background:transparent;display:grid;grid-template-columns:320px 1fr;gap:20px}
.app{width:1146px;max-width:96vw;background:transparent;display:grid;grid-template-columns:320px 1fr;gap:20px;align-items:stretch}
/* Left column - user list / details */
.sidebar{background:linear-gradient(180deg,rgba(255,255,255,0.9),var(--card));border-radius:var(--radius);padding:20px;box-shadow:0 6px 20px rgba(24,39,75,0.06);overflow:hidden}
@@ -55,17 +55,16 @@
.sidebar .hint{margin-top:14px;padding:10px;border-radius:10px;background:#fbf8ff;color:#5b4160;font-size:13px}
/* Chat area */
.chat-card{background:var(--card);border-radius:var(--radius);display:flex;flex-direction:column;height:70vh;box-shadow:0 10px 30px rgba(24,39,75,0.06);overflow:hidden}
.chat-card{background:var(--card);border-radius:var(--radius);display:flex;flex-direction:column;height:100%;min-height:0;box-shadow:0 10px 30px rgba(24,39,75,0.06);overflow:hidden}
.chat-header{display:flex;align-items:center;gap:12px;padding:18px;border-bottom:1px solid #eef1f6}
.chat-header .title{font-weight:600;color:var(--odoo-dark)}
.chat-header .sub{font-size:13px;color:var(--muted)}
.messages{flex:1;padding:22px;overflow:auto;background:linear-gradient(180deg,#fbfcff, #f7f9fc)}
.messages{flex:1;min-height:0;padding:22px;overflow:auto;background:linear-gradient(180deg,#fbfcff, #f7f9fc)}
.messages-inner{max-width:760px;margin:0 auto;display:flex;flex-direction:column;gap:12px}
.msg{display:flex;gap:10px;align-items:flex-end}
.msg.me{justify-content:flex-end}
.bubble{max-width:68%;padding:12px 14px;border-radius:12px;background:white;border:1px solid #eef1f6;font-size:14px;color:#222}
.msg{display:flex;gap:10px;align-items:flex-end;width:100%}
.bubble{width:100%;max-width:100%;padding:12px 14px;border-radius:12px;background:white;border:1px solid #eef1f6;font-size:14px;color:#222}
.bubble.me{background:linear-gradient(90deg,var(--odoo-purple),#9b6fa0);color:white;border:none}
.meta-time{font-size:11px;color:var(--muted);margin-top:6px}
@@ -204,16 +203,13 @@ Please note: it does not cover general Odoo usage or functional questions.
const col = document.createElement('div');
col.style.display = 'flex';
col.style.flexDirection = 'column';
col.style.flex = '1';
col.style.minWidth = '0';
col.appendChild(bubble);
col.appendChild(meta);
if(who==='me'){
wrap.appendChild(col);
wrap.appendChild(avatar);
} else {
wrap.appendChild(avatar);
wrap.appendChild(col);
}
wrap.appendChild(avatar);
wrap.appendChild(col);
messagesEl.appendChild(wrap);
messagesEl.parentElement.scrollTop = messagesEl.parentElement.scrollHeight;
+48 -9
View File
@@ -297,7 +297,7 @@
<script>
// ── Webhooks ─────────────────────────────────────────────────────────────
const getContract_Webhook =
"https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/0c8536be-d175-4740-8e78-123159193b23";
"https://n8n.odoo4projects.com/webhook/ODOO4projects/upsell";
// ── Plan hierarchy (index = tier, lower = cheaper) ────────────────────────
const PLAN_ORDER = [
@@ -337,11 +337,46 @@
// ── Product ID mapping ─────────────────────────────────────────
const PRODUCT_IDS = {
sidehustle: 54,
ontherise: 55,
powerhouse: 56,
sidehustle: 6,
ontherise: 4,
powerhouse: 5,
};
// ── UTM tracking (source / medium / campaign) ─────────────────────────────
const UTM_KEYS = ["source", "medium", "campaign"];
function getCookie(name) {
const match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
return match ? decodeURIComponent(match[2]) : null;
}
function setCookie(name, value, days) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
document.cookie =
name + "=" + encodeURIComponent(value) +
"; expires=" + date.toUTCString() +
"; path=/; SameSite=Lax";
}
UTM_KEYS.forEach(function (key) {
const val = params.get("utm_" + key);
if (val) setCookie("utm_" + key, val, 90);
});
function appendUTM(form) {
UTM_KEYS.forEach(function (key) {
const val = getCookie("utm_" + key);
if (val) {
const input = document.createElement("input");
input.type = "hidden";
input.name = "utm_" + key;
input.value = val;
form.appendChild(input);
}
});
}
// ── Buy handler ───────────────────────────────────────────────────────────
function buyProduct(productId) {
if (!uuid) {
@@ -355,7 +390,7 @@
}
var form = document.createElement("form");
form.method = "POST";
form.action = window.location.origin + "/shop/post_basket";
form.action = "https://20.odoo4projects.com/shop/post_basket";
form.target = "_blank";
form.style.display = "none";
var basket = document.createElement("input");
@@ -369,6 +404,7 @@
clear.name = "clear";
clear.value = "1";
form.appendChild(clear);
appendUTM(form);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
@@ -390,7 +426,7 @@
const currentIdx = PLAN_ORDER.indexOf(contractName);
if (currentIdx === -1) return; // unknown plan – leave defaults
const badgeLabel = expires ? "✅ " + expires : "✅";
const badgeLabel = expires ? "✅ Expires " + expires : "✅";
PLAN_ORDER.forEach(function (plan, idx) {
var meta = PLAN_META[plan];
@@ -432,12 +468,15 @@
if (!res.ok) throw new Error("HTTP " + res.status);
const payload = await res.json();
// Webhook returns: [{ "expires": "YYYY-MM-DD", "contractName": "Trial" }]
// Webhook returns: { "expires": "YYYY-MM-DD", "contractName": "Trial" }
// (tolerate an array wrapper and snake_case keys as well)
const record = Array.isArray(payload)
? payload[0]
: payload;
if (record && record.contract_name) {
applyPlanState(record.contract_name, record.expires);
const contractName =
record && (record.contractName || record.contract_name);
if (contractName) {
applyPlanState(contractName, record.expires);
}
} catch (err) {
console.error("Could not load contract data:", err);
+62 -5
View File
@@ -57,7 +57,33 @@ var si = 0,
return data;
}
window.UTM = { parse: parseUTM, getStored: getStoredUTM, inject: injectUTM };
function appendToForm(form) {
var stored = getStoredUTM();
var keys = ['source', 'medium', 'campaign'];
for (var i = 0; i < keys.length; i++) {
if (stored[keys[i]]) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'utm_' + keys[i];
input.value = stored[keys[i]];
form.appendChild(input);
}
}
}
function toQuery() {
var stored = getStoredUTM();
var parts = [];
var keys = ['source', 'medium', 'campaign'];
for (var i = 0; i < keys.length; i++) {
if (stored[keys[i]]) {
parts.push('utm_' + keys[i] + '=' + encodeURIComponent(stored[keys[i]]));
}
}
return parts.join('&');
}
window.UTM = { parse: parseUTM, getStored: getStoredUTM, inject: injectUTM, appendToForm: appendToForm, toQuery: toQuery };
window.UTM.parse(); // Parse on page load
})();
@@ -70,6 +96,30 @@ var si = 0,
}
})();
/* ---- Hero confirmation notice (shown when ?confirm=...) ---- */
(function () {
var message = new URLSearchParams(window.location.search).get('confirm');
if (!message || !message.trim()) return;
var notice = document.getElementById('heroConfirmNotice');
if (!notice) return;
var text = notice.querySelector('.hero-notice-text');
var close = notice.querySelector('.hero-notice-close');
var hero = notice.closest('.hero');
text.textContent = message.trim();
notice.hidden = false;
if (hero) hero.classList.add('has-hero-notice');
if (close) {
close.addEventListener('click', function () {
notice.hidden = true;
if (hero) hero.classList.remove('has-hero-notice');
});
}
})();
if (slides.length > 0 && dots.length > 0) {
function show(i, direction) {
if (direction === undefined) return;
@@ -196,13 +246,15 @@ var si = 0,
yearlySave.style.display = "block";
var buyBtn = document.getElementById("orderBuyBtn");
var loc = document.getElementById("orderServerLocation").value;
var utmQuery = UTM.toQuery();
buyBtn.href =
"/3/" +
price.toFixed(2) +
"/4/" +
encodeURIComponent(plan) +
"?location=" +
encodeURIComponent(loc);
encodeURIComponent(loc) +
(utmQuery ? "&" + utmQuery : "");
widget.style.display = "flex";
orderPopupShown = true;
}
@@ -233,13 +285,15 @@ var si = 0,
.trim();
var price = parseFloat(priceText);
var loc = this.value;
var utmQuery = UTM.toQuery();
document.getElementById("orderBuyBtn").href =
"/3/" +
price.toFixed(2) +
"/4/" +
encodeURIComponent(plan) +
"?location=" +
encodeURIComponent(loc);
encodeURIComponent(loc) +
(utmQuery ? "&" + utmQuery : "");
});
function applyCoupon() {
var input = document.getElementById("orderCouponInput");
@@ -719,6 +773,7 @@ var si = 0,
clear.name = 'clear';
clear.value = '1';
form.appendChild(clear);
UTM.appendToForm(form);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
@@ -846,7 +901,8 @@ var si = 0,
requirements: requirements,
area: 'quote'
}))
}).then(function() {
}).then(function(r) {
if (!r.ok) throw new Error('bad status ' + r.status);
customQuoteForm.style.display = 'none';
document.getElementById('customQuoteSuccess').style.display = 'block';
submitBtn.textContent = 'Submit Request';
@@ -904,7 +960,8 @@ var si = 0,
requirements: text,
area: 'message'
}))
}).then(function () {
}).then(function (r) {
if (!r.ok) throw new Error('bad status ' + r.status);
form.reset();
submitBtn.disabled = false;
showStatus("Thanks! We'll get back to you shortly.", false);
+19 -12
View File
@@ -8,11 +8,11 @@
content="Support and FAQ for ODOO4projects hosting. Get help with your Odoo instance, modules, backups, domains, and more."
/>
<title>Support | ODOO4projects</title>
<link rel="canonical" href="https://ODOO4projects.com/support" />
<link rel="canonical" href="https://ODOO4projects.com/support.html" />
<meta property="og:title" content="Support | ODOO4projects" />
<meta property="og:description" content="Support and FAQ for ODOO4projects hosting." />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://ODOO4projects.com/support" />
<meta property="og:url" content="https://ODOO4projects.com/support.html" />
<meta name="twitter:card" content="summary_large_image" />
<link rel="icon" href="images/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -474,8 +474,7 @@
<section class="contact-section" id="contact">
<div class="container">
<h2>Still questions? Contact our support.</h2>
<form class="contact-form" id="supportForm" action="https://ODOO4projects.com/website/form/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" id="csrfToken" value="" />
<form class="contact-form" id="supportForm">
<div class="form-group">
<label>Your Name <span class="required">*</span></label>
<input type="text" id="contactName" name="contact_name" required placeholder="Your Name" />
@@ -528,23 +527,31 @@
var form = document.getElementById("supportForm");
var submitBtn = form.querySelector(".btn");
var successDiv = document.getElementById("formSuccess");
fetch("https://ODOO4projects.com/web/csrf_token")
.then(function (r) { return r.json(); })
.then(function (data) {
document.getElementById("csrfToken").value = data.result || data.csrf_token || "";
}).catch(function () {});
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = document.getElementById("contactName").value.trim();
var email = document.getElementById("contactEmail").value.trim();
var uuid = document.getElementById("contactUuid").value.trim();
var subject = document.getElementById("contactSubject").value.trim();
var question = document.getElementById("contactQuestion").value.trim();
if (!name || !email || !subject || !question) { alert("Please fill in all required fields."); return; }
submitBtn.disabled = true;
submitBtn.textContent = "Submitting...";
fetch(form.action, { method: "POST", body: new FormData(form) })
.then(function () { submitBtn.style.display = "none"; successDiv.classList.add("visible"); })
.catch(function () { submitBtn.disabled = false; submitBtn.textContent = "Submit"; alert("Could not submit. Please try again."); });
var payload = { name: name, email: email, subject: subject, requirements: question, area: "support" };
if (uuid) payload.uuid = uuid;
fetch("https://n8n.odoo4projects.com/webhook/ODOO4projects/crm", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
}).then(function (r) {
if (!r.ok) throw new Error("bad status " + r.status);
submitBtn.style.display = "none";
successDiv.classList.add("visible");
}).catch(function () {
submitBtn.disabled = false;
submitBtn.textContent = "Submit";
alert("Could not submit. Please try again.");
});
});
</script>