Complete Email Sortierer implementation with Appwrite and Stripe integration
This commit is contained in:
260
public/index.html
Normal file
260
public/index.html
Normal file
@@ -0,0 +1,260 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Email Sortierer</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1>Email Sortierer</h1>
|
||||
<div id="form-container"></div>
|
||||
<div id="navigation">
|
||||
<button id="prev-btn" style="display:none;">Zurück</button>
|
||||
<button id="next-btn" style="display:none;">Weiter</button>
|
||||
</div>
|
||||
<div id="summary" style="display:none;">
|
||||
<h2>Zusammenfassung</h2>
|
||||
<div id="summary-content"></div>
|
||||
<button id="buy-btn">Jetzt kaufen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let questions = [];
|
||||
let answers = {};
|
||||
let currentStep = 1;
|
||||
let submissionId = null;
|
||||
|
||||
async function loadQuestions() {
|
||||
try {
|
||||
const response = await fetch('/api/questions?productSlug=email-sorter');
|
||||
questions = await response.json();
|
||||
renderStep();
|
||||
} catch (error) {
|
||||
console.error('Error loading questions:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderStep() {
|
||||
const container = document.getElementById('form-container');
|
||||
const stepQuestions = questions.filter(q => q.step === currentStep);
|
||||
|
||||
if (stepQuestions.length === 0) {
|
||||
showSummary();
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = `<h2>Schritt ${currentStep}</h2>`;
|
||||
|
||||
stepQuestions.forEach(question => {
|
||||
const div = document.createElement('div');
|
||||
div.style.marginBottom = '20px';
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.textContent = question.label + (question.required ? ' *' : '');
|
||||
label.style.display = 'block';
|
||||
label.style.marginBottom = '5px';
|
||||
div.appendChild(label);
|
||||
|
||||
if (question.helpText) {
|
||||
const help = document.createElement('small');
|
||||
help.textContent = question.helpText;
|
||||
help.style.display = 'block';
|
||||
help.style.marginBottom = '5px';
|
||||
div.appendChild(help);
|
||||
}
|
||||
|
||||
let input;
|
||||
switch (question.type) {
|
||||
case 'textarea':
|
||||
input = document.createElement('textarea');
|
||||
input.rows = 4;
|
||||
break;
|
||||
case 'select':
|
||||
input = document.createElement('select');
|
||||
let options = [];
|
||||
try {
|
||||
const parsed = JSON.parse(question.optionsJson || '[]');
|
||||
// Handle both array format and {options: [...]} format
|
||||
options = Array.isArray(parsed) ? parsed : (parsed.options || []);
|
||||
} catch (e) {
|
||||
console.error('Error parsing options:', e);
|
||||
options = [];
|
||||
}
|
||||
options.forEach(opt => {
|
||||
const option = document.createElement('option');
|
||||
// Handle both string and {value, label} format
|
||||
option.value = typeof opt === 'string' ? opt : opt.value;
|
||||
option.textContent = typeof opt === 'string' ? opt : opt.label;
|
||||
input.appendChild(option);
|
||||
});
|
||||
break;
|
||||
case 'multiselect':
|
||||
input = document.createElement('select');
|
||||
input.multiple = true;
|
||||
input.size = 5;
|
||||
let multiOptions = [];
|
||||
try {
|
||||
const parsed = JSON.parse(question.optionsJson || '[]');
|
||||
// Handle both array format and {options: [...]} format
|
||||
multiOptions = Array.isArray(parsed) ? parsed : (parsed.options || []);
|
||||
} catch (e) {
|
||||
console.error('Error parsing options:', e);
|
||||
multiOptions = [];
|
||||
}
|
||||
multiOptions.forEach(opt => {
|
||||
const option = document.createElement('option');
|
||||
// Handle both string and {value, label} format
|
||||
option.value = typeof opt === 'string' ? opt : opt.value;
|
||||
option.textContent = typeof opt === 'string' ? opt : opt.label;
|
||||
input.appendChild(option);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
input = document.createElement('input');
|
||||
input.type = question.type;
|
||||
}
|
||||
|
||||
input.id = question.key;
|
||||
input.name = question.key;
|
||||
input.required = question.required;
|
||||
|
||||
// Restore previous values
|
||||
if (question.type === 'multiselect' && Array.isArray(answers[question.key])) {
|
||||
// For multiselect, select all previously selected options
|
||||
Array.from(input.options).forEach(option => {
|
||||
if (answers[question.key].includes(option.value)) {
|
||||
option.selected = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
input.value = answers[question.key] || '';
|
||||
}
|
||||
|
||||
input.style.width = '100%';
|
||||
input.style.padding = '8px';
|
||||
|
||||
div.appendChild(input);
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
updateNavigation();
|
||||
}
|
||||
|
||||
function updateNavigation() {
|
||||
const prevBtn = document.getElementById('prev-btn');
|
||||
const nextBtn = document.getElementById('next-btn');
|
||||
|
||||
prevBtn.style.display = currentStep > 1 ? 'inline-block' : 'none';
|
||||
nextBtn.style.display = 'inline-block';
|
||||
nextBtn.textContent = hasMoreSteps() ? 'Weiter' : 'Zur Zusammenfassung';
|
||||
}
|
||||
|
||||
function hasMoreSteps() {
|
||||
const maxStep = Math.max(...questions.map(q => q.step));
|
||||
return currentStep < maxStep;
|
||||
}
|
||||
|
||||
function validateCurrentStep() {
|
||||
const stepQuestions = questions.filter(q => q.step === currentStep);
|
||||
|
||||
for (const question of stepQuestions) {
|
||||
const input = document.getElementById(question.key);
|
||||
if (question.required) {
|
||||
// For multiselect, check if at least one option is selected
|
||||
if (question.type === 'multiselect') {
|
||||
if (input.selectedOptions.length === 0) {
|
||||
alert(`Bitte wählen Sie mindestens eine Option für "${question.label}" aus.`);
|
||||
return false;
|
||||
}
|
||||
} else if (!input.value) {
|
||||
alert(`Bitte füllen Sie das Feld "${question.label}" aus.`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function saveCurrentStep() {
|
||||
const stepQuestions = questions.filter(q => q.step === currentStep);
|
||||
stepQuestions.forEach(question => {
|
||||
const input = document.getElementById(question.key);
|
||||
if (question.type === 'multiselect') {
|
||||
answers[question.key] = Array.from(input.selectedOptions).map(opt => opt.value);
|
||||
} else {
|
||||
answers[question.key] = input.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showSummary() {
|
||||
document.getElementById('form-container').style.display = 'none';
|
||||
document.getElementById('navigation').style.display = 'none';
|
||||
document.getElementById('summary').style.display = 'block';
|
||||
|
||||
const summaryContent = document.getElementById('summary-content');
|
||||
summaryContent.innerHTML = '';
|
||||
|
||||
questions.forEach(question => {
|
||||
const div = document.createElement('div');
|
||||
div.style.marginBottom = '10px';
|
||||
div.innerHTML = `<strong>${question.label}:</strong> ${formatAnswer(answers[question.key])}`;
|
||||
summaryContent.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
function formatAnswer(answer) {
|
||||
if (Array.isArray(answer)) {
|
||||
return answer.join(', ');
|
||||
}
|
||||
return answer || '-';
|
||||
}
|
||||
|
||||
document.getElementById('prev-btn').addEventListener('click', () => {
|
||||
saveCurrentStep();
|
||||
currentStep--;
|
||||
renderStep();
|
||||
});
|
||||
|
||||
document.getElementById('next-btn').addEventListener('click', () => {
|
||||
if (!validateCurrentStep()) return;
|
||||
|
||||
saveCurrentStep();
|
||||
currentStep++;
|
||||
renderStep();
|
||||
});
|
||||
|
||||
document.getElementById('buy-btn').addEventListener('click', async () => {
|
||||
try {
|
||||
const submitResponse = await fetch('/api/submissions', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
productSlug: 'email-sorter',
|
||||
answers: answers
|
||||
})
|
||||
});
|
||||
|
||||
const submitData = await submitResponse.json();
|
||||
submissionId = submitData.submissionId;
|
||||
|
||||
const checkoutResponse = await fetch('/api/checkout', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ submissionId })
|
||||
});
|
||||
|
||||
const checkoutData = await checkoutResponse.json();
|
||||
window.location.href = checkoutData.url;
|
||||
} catch (error) {
|
||||
console.error('Error during checkout:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
});
|
||||
|
||||
loadQuestions();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user