new upsell

This commit is contained in:
Oliver
2025-10-12 09:06:37 -03:00
parent 67db593d33
commit 1b16ea225e
3 changed files with 464 additions and 197 deletions

20
public/start Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
#!/bin/bash
# Simple static file server with live reload
# Requires Node.js and live-server
# Check if live-server is installed
if ! command -v live-server &> /dev/null
then
echo "Installing live-server..."
npm install -g live-server
fi
# Serve the current directory with auto-reload
echo "Starting live-server on http://localhost:8080 ..."
live-server .

View File

@@ -2,249 +2,245 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upgrade Form</title>
<title>Check and Upgrade Your Plan</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
max-width: 700px;
margin: auto;
background: #f9f9f9;
font-family: "Inter", "Segoe UI", Arial, sans-serif;
background-color: #f4f5f7;
color: #333;
margin: 0;
padding: 40px;
display: flex;
justify-content: center;
}
.summary-container {
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
padding: 40px;
max-width: 720px;
width: 100%;
}
h2 {
text-align: center;
color: #333;
color: #262626;
margin-bottom: 8px;
font-weight: 600;
}
form {
.subtitle {
text-align: center;
font-size: 0.9em;
color: #777;
margin-bottom: 30px;
line-height: 1.5;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
background: #fff;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
label {
display: block;
font-weight: bold;
.grid-item {
background: #fafafa;
border: 1px solid #e2e2e2;
border-radius: 8px;
padding: 15px 20px;
}
.grid-item.full {
grid-column: 1 / -1;
}
.label {
font-weight: 600;
font-size: 0.9em;
color: #555;
margin-bottom: 6px;
}
input[type="number"], input[type="text"] {
width: 100%;
padding: 8px;
border-radius: 6px;
border: 1px solid #ccc;
.value {
font-size: 1.1em;
color: #222;
}
input[type="checkbox"] {
transform: scale(1.2);
margin-right: 6px;
.footer {
text-align: center;
font-size: 0.9em;
color: #888;
margin-top: 30px;
}
.full-width {
grid-column: 1 / -1;
}
.submit-section {
grid-column: 1 / -1;
/* Button styling */
.actions {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
margin-top: 20px;
margin-top: 40px;
gap: 15px;
}
button {
padding: 10px 20px;
background-color: #4CAF50;
.action-row {
display: flex;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
}
.action-btn {
background-color: #875A7B; /* Odoo purple */
color: white;
border: none;
border-radius: 8px;
padding: 10px 22px;
font-size: 15px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.2s ease, transform 0.1s ease;
min-width: 180px;
}
button:disabled {
background-color: #aaa;
cursor: not-allowed;
.action-btn:hover {
background-color: #744e6a;
transform: translateY(-1px);
}
.cost {
font-weight: bold;
font-size: 18px;
.action-btn.secondary {
background-color: #6c757d;
}
.readonly-text {
padding: 8px;
background: #eee;
border-radius: 6px;
border: 1px solid #ccc;
}
.confirmation {
text-align: center;
font-size: 1.2em;
padding: 40px;
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
color: #333;
.action-btn.secondary:hover {
background-color: #5a636a;
}
</style>
</head>
<body>
<h2>Upgrade Your Plan</h2>
<div id="uuidDisplay" style="text-align:center; font-size: 0.9em; color: #666; margin-bottom: 10px;">Enhance your Odoo experience by upgrading your feature set here. Youll only pay for the remaining duration of your current Odoo contract.
When its time for your annual renewal, you will receive a reminder email with all the details.</div>
<form id="upgradeForm">
<div>
<label><input type="checkbox" id="git" name="git"> GIT</label>
<div class="summary-container">
<h2>Check and Upgrade Your Plan</h2>
<div class="subtitle">
Review your current Odoo configuration and choose an upgrade option below.
</div>
<div>
<label for="domains">Domains</label>
<input type="number" id="domains" name="domains" min="1" max="10">
<div class="grid">
<div class="grid-item">
<div class="label">GIT</div>
<div class="value" id="gitValue">Loading...</div>
</div>
<div class="grid-item">
<div class="label">Domains</div>
<div class="value" id="domainsValue">Loading...</div>
</div>
<div class="grid-item">
<div class="label">Backup Slots</div>
<div class="value" id="backupValue">Loading...</div>
</div>
<div class="grid-item">
<div class="label">Workers</div>
<div class="value" id="workersValue">Loading...</div>
</div>
<div class="grid-item full">
<div class="label">HDD (MB)</div>
<div class="value" id="hddValue">Loading...</div>
</div>
<div class="grid-item full">
<div class="label">Expires</div>
<div class="value" id="expiresValue">Loading...</div>
</div>
</div>
<div>
<label for="backupSlots">Backup Slots</label>
<input type="number" id="backupSlots" name="backupSlots" min="2" max="10">
<!-- Buttons -->
<div class="actions">
<div class="action-row">
<button class="action-btn" id="upgradeRiseBtn">Upgrade to "On the Rise"</button>
<button class="action-btn" id="upgradePowerhouseBtn">Upgrade to "Powerhouse"</button>
</div>
<div class="action-row">
<button class="action-btn secondary" id="addDomainBtn">Add a Domain</button>
<button class="action-btn secondary" id="addBackupsBtn">Add 5 Backup Slots</button>
</div>
</div>
<div>
<label for="workers">Workers</label>
<input type="number" id="workers" name="workers" min="1" max="3">
<div class="footer" id="uuidDisplay">
UUID: <span id="uuidText">Loading...</span>
</div>
<div class="full-width">
<label for="hdd">HDD (MB)</label>
<input type="number" id="hdd" name="hdd" min="250" max="2048" step="250">
</div>
<div class="full-width">
<label>Expires</label>
<div id="expires" class="readonly-text">Loading...</div>
</div>
<div class="submit-section">
<button type="submit" id="submitBtn" disabled>Upgrade</button>
<div class="cost" id="cost">$0.00</div>
</div>
</form>
</div>
<script>
const params = new URLSearchParams(window.location.search);
const uuid = params.get("uuid");
// Configuration
const webhookUrl = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/0c8536be-d175-4740-8e78-123159193b23";
const webhook_buy = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/buy"; // <-- easy to configure
const webhookUrl = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/0c8536be-d175-4740-8e78-123159193b23";
const webhookPost = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/3709b60f-935e-43e4-834a-5060a40182dd";
const params = new URLSearchParams(window.location.search);
const uuid = params.get("uuid");
async function loadData() {
if (!uuid) {
alert("Missing uuid parameter in URL");
return;
async function loadData() {
if (!uuid) {
alert("Missing uuid parameter in URL");
return;
}
document.getElementById("uuidText").textContent = uuid;
try {
const res = await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ uuid })
});
if (!res.ok) throw new Error("Failed to fetch data");
const data = await res.json();
document.getElementById("gitValue").textContent = data.git ? "Enabled" : "Disabled";
document.getElementById("domainsValue").textContent = data.domains || "-";
document.getElementById("backupValue").textContent = data.backupSlots || "-";
document.getElementById("workersValue").textContent = data.workers || "-";
document.getElementById("hddValue").textContent = data.hdd ? `${data.hdd} MB` : "-";
document.getElementById("expiresValue").textContent = data.expires || "-";
} catch (err) {
console.error(err);
alert("Error loading data.");
}
}
try {
const res = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ uuid })
});
async function buyProduct(product_id) {
if (!uuid) return;
try {
const res = await fetch(webhook_buy, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ uuid, product_id })
});
if (!res.ok) throw new Error("Failed to fetch data");
const data = await res.json();
if (!res.ok) throw new Error("Failed to send purchase request");
window.originalData = data;
window.prices = data.prices || {
git: 10,
domain: 1,
backupSlot: 1,
worker: 50,
hddUnit: 10
};
document.getElementById("git").checked = data.git || false;
document.getElementById("domains").value = data.domains || 1;
document.getElementById("backupSlots").value = data.backupSlots || 2;
document.getElementById("workers").value = data.workers || 1;
document.getElementById("hdd").value = data.hdd || 250;
document.getElementById("expires").textContent = data.expires || "-";
calculateCost();
} catch (err) {
console.error(err);
alert("Error loading data.");
document.body.innerHTML = `
<div style="text-align:center;padding:80px;background:#fff;border-radius:12px;max-width:700px;margin:auto;box-shadow:0 4px 12px rgba(0,0,0,0.1);">
<h2>✅ Your request has been sent</h2>
<p>Please check your email for confirmation.</p>
</div>
`;
} catch (err) {
console.error(err);
alert("Error sending request.");
}
}
}
function calculateCost() {
if (!window.originalData || !window.prices) return;
// Button event listeners with product_id mapping
document.getElementById("upgradeRiseBtn").addEventListener("click", () => buyProduct(4));
document.getElementById("upgradePowerhouseBtn").addEventListener("click", () => buyProduct(5));
document.getElementById("addDomainBtn").addEventListener("click", () => buyProduct(100));
document.getElementById("addBackupsBtn").addEventListener("click", () => buyProduct(101));
const domains = parseInt(document.getElementById("domains").value) || 0;
const backups = parseInt(document.getElementById("backupSlots").value) || 0;
const workers = parseInt(document.getElementById("workers").value) || 0;
const hdd = parseInt(document.getElementById("hdd").value) || 0;
const gitChecked = document.getElementById("git").checked;
const baselineDomains = parseInt(window.originalData.domains) || 0;
const baselineBackups = parseInt(window.originalData.backupSlots) || 0;
const baselineWorkers = parseInt(window.originalData.workers) || 0;
const baselineHDD = parseInt(window.originalData.hdd) || 0;
const baselineGit = window.originalData.git || false;
const extraDomains = Math.max(domains - baselineDomains, 0);
const extraBackups = Math.max(backups - baselineBackups, 0);
const extraWorkers = Math.max(workers - baselineWorkers, 0);
const extraHDDUnits = Math.max(Math.floor((hdd - baselineHDD) / 250), 0);
const extraGit = gitChecked && !baselineGit ? 1 : 0;
const cost =
extraGit * window.prices.git +
extraDomains * window.prices.domain +
extraBackups * window.prices.backupSlot +
extraWorkers * window.prices.worker +
extraHDDUnits * window.prices.hddUnit;
document.getElementById("cost").textContent = `$${cost.toFixed(2)}`;
// Enable/disable button based on cost
document.getElementById("submitBtn").disabled = cost <= 0;
}
document.querySelectorAll("input").forEach(input => {
input.addEventListener("input", calculateCost);
});
document.getElementById("upgradeForm").addEventListener("submit", async (e) => {
e.preventDefault();
const payload = {
uuid: uuid,
git: document.getElementById("git").checked,
domains: parseInt(document.getElementById("domains").value),
backupSlots: parseInt(document.getElementById("backupSlots").value),
workers: parseInt(document.getElementById("workers").value),
hdd: parseInt(document.getElementById("hdd").value),
cost: document.getElementById("cost").textContent.replace('$','')
};
try {
const res = await fetch(webhookPost, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (!res.ok) throw new Error("Failed to submit data");
// Replace page content with confirmation message
document.body.innerHTML = `
<div class="confirmation">
<h2>✅ Your upgrade request has been sent</h2>
<p>Please check your email for confirmation.</p>
</div>
`;
} catch (err) {
console.error(err);
alert("Error submitting form.");
}
});
loadData();
loadData();
</script>
</body>
</html>

251
public/upsell.html_ Normal file
View File

@@ -0,0 +1,251 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upgrade Form</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
max-width: 700px;
margin: auto;
background: #f9f9f9;
}
h2 {
text-align: center;
color: #333;
}
form {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
background: #fff;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
label {
display: block;
font-weight: bold;
margin-bottom: 6px;
}
input[type="number"], input[type="text"] {
width: 100%;
padding: 8px;
border-radius: 6px;
border: 1px solid #ccc;
}
input[type="checkbox"] {
transform: scale(1.2);
margin-right: 6px;
}
.full-width {
grid-column: 1 / -1;
}
.submit-section {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: 12px;
margin-top: 20px;
}
button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
}
button:disabled {
background-color: #aaa;
cursor: not-allowed;
}
.cost {
font-weight: bold;
font-size: 18px;
}
.readonly-text {
padding: 8px;
background: #eee;
border-radius: 6px;
border: 1px solid #ccc;
}
.confirmation {
text-align: center;
font-size: 1.2em;
padding: 40px;
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
color: #333;
}
</style>
</head>
<body>
<h2>Upgrade Your Plan</h2>
<div id="uuidDisplay" style="text-align:center; font-size: 0.9em; color: #666; margin-bottom: 10px;">Enhance your Odoo experience by upgrading your feature set here. Youll only pay for the remaining duration of your current Odoo contract.
When its time for your annual renewal, you will receive a reminder email with all the details.</div>
<form id="upgradeForm">
<div>
<label><input type="checkbox" id="git" name="git"> GIT</label>
</div>
<div>
<label for="domains">Domains</label>
<input type="number" id="domains" name="domains" min="1" max="10">
</div>
<div>
<label for="backupSlots">Backup Slots</label>
<input type="number" id="backupSlots" name="backupSlots" min="2" max="10">
</div>
<div>
<label for="workers">Workers</label>
<input type="number" id="workers" name="workers" min="1" max="3">
</div>
<div class="full-width">
<label for="hdd">HDD (MB)</label>
<input type="number" id="hdd" name="hdd" min="250" max="2048" step="250">
</div>
<div class="full-width">
<label>Expires</label>
<div id="expires" class="readonly-text">Loading...</div>
</div>
<div class="submit-section">
<button type="submit" id="submitBtn" disabled>Upgrade</button>
<div class="cost" id="cost">$0.00</div>
</div>
</form>
<script>
const params = new URLSearchParams(window.location.search);
const uuid = params.get("uuid");
const webhookUrl = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/0c8536be-d175-4740-8e78-123159193b23";
const webhookPost = "https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/3709b60f-935e-43e4-834a-5060a40182dd";
async function loadData() {
if (!uuid) {
alert("Missing uuid parameter in URL");
return;
}
try {
const res = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ uuid })
});
if (!res.ok) throw new Error("Failed to fetch data");
const data = await res.json();
window.originalData = data;
window.prices = data.prices || {
git: 10,
domain: 1,
backupSlot: 1,
worker: 50,
hddUnit: 10
};
document.getElementById("git").checked = data.git || false;
document.getElementById("domains").value = data.domains || 1;
document.getElementById("backupSlots").value = data.backupSlots || 2;
document.getElementById("workers").value = data.workers || 1;
document.getElementById("hdd").value = data.hdd || 250;
document.getElementById("expires").textContent = data.expires || "-";
calculateCost();
} catch (err) {
console.error(err);
alert("Error loading data.");
}
}
function calculateCost() {
if (!window.originalData || !window.prices) return;
const domains = parseInt(document.getElementById("domains").value) || 0;
const backups = parseInt(document.getElementById("backupSlots").value) || 0;
const workers = parseInt(document.getElementById("workers").value) || 0;
const hdd = parseInt(document.getElementById("hdd").value) || 0;
const gitChecked = document.getElementById("git").checked;
const baselineDomains = parseInt(window.originalData.domains) || 0;
const baselineBackups = parseInt(window.originalData.backupSlots) || 0;
const baselineWorkers = parseInt(window.originalData.workers) || 0;
const baselineHDD = parseInt(window.originalData.hdd) || 0;
const baselineGit = window.originalData.git || false;
const extraDomains = Math.max(domains - baselineDomains, 0);
const extraBackups = Math.max(backups - baselineBackups, 0);
const extraWorkers = Math.max(workers - baselineWorkers, 0);
const extraHDDUnits = Math.max(Math.floor((hdd - baselineHDD) / 250), 0);
const extraGit = gitChecked && !baselineGit ? 1 : 0;
const cost =
extraGit * window.prices.git +
extraDomains * window.prices.domain +
extraBackups * window.prices.backupSlot +
extraWorkers * window.prices.worker +
extraHDDUnits * window.prices.hddUnit;
document.getElementById("cost").textContent = `$${cost.toFixed(2)}`;
// Enable/disable button based on cost
document.getElementById("submitBtn").disabled = cost <= 0;
}
document.querySelectorAll("input").forEach(input => {
input.addEventListener("input", calculateCost);
});
document.getElementById("upgradeForm").addEventListener("submit", async (e) => {
e.preventDefault();
const payload = {
uuid: uuid,
git: document.getElementById("git").checked,
domains: parseInt(document.getElementById("domains").value),
backupSlots: parseInt(document.getElementById("backupSlots").value),
workers: parseInt(document.getElementById("workers").value),
hdd: parseInt(document.getElementById("hdd").value),
cost: document.getElementById("cost").textContent.replace('$','')
};
try {
const res = await fetch(webhookPost, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (!res.ok) throw new Error("Failed to submit data");
// Replace page content with confirmation message
document.body.innerHTML = `
<div class="confirmation">
<h2>✅ Your upgrade request has been sent</h2>
<p>Please check your email for confirmation.</p>
</div>
`;
} catch (err) {
console.error(err);
alert("Error submitting form.");
}
});
loadData();
</script>
</body>
</html>