Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
dad190e7dd final 2025-10-12 01:09:09 +00:00
3 changed files with 996 additions and 145 deletions

206
index.php
View File

@ -1,150 +1,68 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<html lang="ro">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Testul de Sănătate (HealthType)</title>
<link rel="stylesheet" href="styles.css?v=<?php echo time(); ?>">
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<main id="app" class="app" aria-live="polite">
<!-- Start Screen -->
<section id="start-screen" class="screen screen--start" aria-hidden="false">
<h1 class="title"> începem!</h1>
<p class="subtitle">Răspunde sincer. Compară-te cu persoane de aceeași vârstă. Pentru unele întrebări poți alege mai multe opțiuni.</p>
<div class="sex-select" role="list">
<button type="button" class="sex-card" data-sex="female" aria-label="Sunt Femeie">Sunt Femeie</button>
<button type="button" class="sex-card" data-sex="male" aria-label="Sunt Bărbat">Sunt Bărbat</button>
</div>
</section>
<!-- Questionnaire Screen -->
<section id="question-screen" class="screen screen--question" aria-hidden="true">
<header class="q-header">
<div class="progress" aria-hidden="true">
<span id="progress-text">Întrebarea 0/0</span>
<div class="progress-bar" aria-hidden="true">
<div id="progress-fill" class="progress-fill" style="width:0%"></div>
</div>
</div>
<div class="nav-top">
<button id="btn-back" class="btn" type="button" aria-label="Înapoi">Înapoi</button>
<button id="btn-reset" class="btn btn--ghost" type="button">Reia testul</button>
</div>
</header>
<form id="question-form" novalidate>
<div id="question-container" class="question-container" role="region" aria-live="polite">
<!-- Questions rendered here by script.js -->
</div>
<div class="q-actions">
<div id="validation-message" class="validation" aria-live="assertive"></div>
<button id="btn-next" class="btn btn--primary" type="button">Înainte</button>
</div>
</form>
</section>
<!-- Result Screen -->
<section id="result-screen" class="screen screen--result" aria-hidden="true">
<header>
<h2 id="result-title">Rezultat</h2>
</header>
<div id="result-content" class="result-content" role="region" aria-live="polite">
<!-- Result rendered here by script.js -->
</div>
<div class="result-actions">
<button id="btn-print" class="btn btn--primary">Descarcă PDF</button>
<button id="btn-reset-result" class="btn btn--ghost" type="button">Reia testul</button>
</div>
<!-- Content for printing -->
<div class="print-summary" id="print-summary-content">
<!-- Summary for printing is populated here -->
</div>
</section>
</main>
<script src="script.js?v=<?php echo time(); ?>" defer></script>
</body>
</html>

525
script.js Normal file
View File

@ -0,0 +1,525 @@
document.addEventListener('DOMContentLoaded', () => {
const app = document.getElementById('app');
const screens = {
start: document.getElementById('start-screen'),
question: document.getElementById('question-screen'),
result: document.getElementById('result-screen'),
};
const buttons = {
sex: document.querySelectorAll('.sex-card'),
back: document.getElementById('btn-back'),
reset: document.getElementById('btn-reset'),
next: document.getElementById('btn-next'),
print: document.getElementById('btn-print'),
};
const questionElements = {
container: document.getElementById('question-container'),
form: document.getElementById('question-form'),
progressText: document.getElementById('progress-text'),
progressFill: document.getElementById('progress-fill'),
validation: document.getElementById('validation-message'),
};
const resultElements = {
title: document.getElementById('result-title'),
content: document.getElementById('result-content'),
printSummary: document.getElementById('print-summary-content'),
};
let state = {
sex: null, // 'male' or 'female'
currentQuestionIndex: 0,
answers: [], // { questionId, answerValue }
history: [], // previous question indices
height: { value: null, unit: 'cm' },
weight: { value: null, unit: 'kg' },
};
const data = {
female: [
{ id: 'F1', text: 'Cum este menstruația ta?', help: 'Alege o singură variantă.', type: 'radio', options: [
{ text: 'Regulată, fără dureri sau disconfort', value: 0 },
{ text: 'Neregulată, dar fără alte simptome', value: 1 },
{ text: 'Dureri, crampe, cheaguri, sângerări abundente', value: 2 },
{ text: 'Am intrat la menopauză', value: 3, next: 'F2' },
]},
{ id: 'F1.1', text: 'Ai sindrom premenstrual (dureri de sâni, iritabilitate, balonare)?', type: 'radio', options: [
{ text: 'Nu, niciodată', value: 0 },
{ text: 'Uneori, dar ușor', value: 1 },
{ text: 'Da, intens și frecvent', value: 2 },
]},
{ id: 'F2', text: 'Ai simptome specifice menopauzei (bufeuri, transpirații nocturne, uscăciune vaginală)?', type: 'radio', options: [
{ text: 'Nu / Nu este cazul', value: 0 },
{ text: 'Da, moderate', value: 1 },
{ text: 'Da, severe și frecvente', value: 2 },
]},
// Common questions start here, but can be specific if needed
...getCommonQuestions('female'),
],
male: [
{ id: 'M1', text: 'Ai probleme cu urinarea (jet slab, urinări frecvente/nocturne)?', type: 'radio', options: [
{ text: 'Nu, deloc', value: 0 },
{ text: 'Uneori sau moderat', value: 1 },
{ text: 'Da, frecvent și deranjant', value: 2 },
]},
{ id: 'M2', text: 'Ai observat o scădere a libidoului sau a performanței sexuale?', type: 'radio', options: [
{ text: 'Nu, totul este normal', value: 0 },
{ text: 'O scădere ușoară', value: 1 },
{ text: 'O scădere semnificativă', value: 2 },
]},
...getCommonQuestions('male'),
],
};
function getCommonQuestions(sex) {
return [
{ id: 'C1', text: 'Cum este nivelul tău de energie pe parcursul zilei?', type: 'radio', options: [
{ text: 'Constant și ridicat', value: 0 },
{ text: 'Scade după-amiaza, dar îmi revin', value: 1 },
{ text: 'Scăzut, oboseală cronică', value: 2 },
]},
{ id: 'C2', text: 'Cum dormi?', help: 'Poți alege mai multe variante.', type: 'checkbox', options: [
{ text: 'Adorm greu sau mă trezesc des', value: 1 },
{ text: 'Nu mă simt odihnit dimineața', value: 1 },
{ text: 'Sforăi sau am apnee în somn', value: 1 },
{ text: 'Dorm bine și mă simt odihnit (bifează doar aceasta)', value: 0, exclusive: true },
]},
{ id: 'C3', text: 'Cum este digestia ta?', help: 'Poți alege mai multe variante.', type: 'checkbox', options: [
{ text: 'Balonare, gaze, disconfort abdominal', value: 1 },
{ text: 'Constipație sau diaree frecventă', value: 1 },
{ text: 'Arsuri la stomac, reflux acid', value: 1 },
{ text: 'Digestie normală, fără probleme (bifează doar aceasta)', value: 0, exclusive: true },
]},
{ id: 'C4', text: 'Cum reacționezi la stres?', type: 'radio', options: [
{ text: 'Calm, gestionez bine situațiile', value: 0 },
{ text: 'Anxios, iritabil, copleșit', value: 1 },
{ text: 'Apatie, lipsă de motivație', value: 2 },
]},
{ id: 'C5', text: 'Ai pofte alimentare necontrolate (dulciuri, carbohidrați, sărat)?', type: 'radio', options: [
{ text: 'Rareori sau deloc', value: 0 },
{ text: 'Uneori, mai ales la stres', value: 1 },
{ text: 'Frecvent și intens', value: 2 },
]},
{ id: 'C6', text: 'Cum este pielea, părul și unghiile tale?', help: 'Poți alege mai multe variante.', type: 'checkbox', options: [
{ text: 'Piele uscată, păr fragil, unghii casante', value: 1 },
{ text: 'Acnee, ten gras, mătreață', value: 1 },
{ text: 'Căderea excesivă a părului', value: 1 },
{ text: 'Aspect sănătos, fără probleme (bifează doar aceasta)', value: 0, exclusive: true },
]},
{ id: 'C7', text: 'Ai dureri articulare sau musculare inexplicabile?', type: 'radio', options: [
{ text: 'Nu', value: 0 },
{ text: 'Da, ocazional', value: 1 },
{ text: 'Da, cronice și răspândite', value: 2 },
]},
{ id: 'C8', text: 'Cum este starea ta de spirit generală?', type: 'radio', options: [
{ text: 'Bună, optimistă', value: 0 },
{ text: 'Variabilă, cu episoade de tristețe', value: 1 },
{ text: 'Predominant tristă, apatică', value: 2 },
]},
{ id: 'HW', text: 'Introdu înălțimea și greutatea ta.', type: 'hw' },
];
}
const results = {
A: {
title: "HealthType A: Vitalitate Optimă",
description: "Profilul tău indică un echilibru hormonal și metabolic excelent. Ai o bază solidă pe care poți construi și optimiza.",
recommendations: [
"Continuă cu stilul de viață sănătos: alimentație curată, mișcare regulată și management eficient al stresului.",
"Explorează practici de longevitate: post intermitent, exerciții de respirație (Wim Hof, Buteyko), expunere la frig.",
"Concentrează-te pe personalizarea dietei în funcție de obiectivele tale (performanță sportivă, claritate mentală).",
],
},
B: {
title: "HealthType B: Stres Adrenal și Oboseală",
description: "Sistemul tău adrenal pare a fi suprasolicitat. Acest lucru duce la oboseală, anxietate și dificultăți de concentrare.",
recommendations: [
"Prioritizează somnul: 7-9 ore pe noapte, într-un mediu întunecat și răcoros.",
"Redu consumul de cofeină și zahăr, care epuizează glandele suprarenale.",
"Introdu tehnici de relaxare: meditație, yoga, plimbări în natură. Chiar și 10 minute pe zi fac o diferență.",
"Consideră suplimente adaptogene precum Ashwagandha sau Rhodiola, după consultarea unui specialist.",
],
},
C: {
title: "HealthType C: Dezechilibru Glicemic și Inflamație",
description: "Răspunsurile tale sugerează o posibilă rezistență la insulină și un nivel crescut de inflamație în corp.",
recommendations: [
"Adoptă o dietă low-carb, bogată în grăsimi sănătoase (avocado, nuci, ulei de măsline) și proteine de calitate.",
"Include antrenamente de forță (greutăți, benzi elastice) pentru a îmbunătăți sensibilitatea la insulină.",
"Consumă alimente antiinflamatorii: pește gras (somon, sardine), turmeric, ghimbir, fructe de pădure.",
"Evită carbohidrații rafinați (pâine albă, paste, patiserie) și uleiurile vegetale procesate (floarea-soarelui, soia).",
],
},
D: {
title: "HealthType D: Digestie Compromisă și Sănătate Intestinală Scăzută",
description: "Simptomele tale indică probleme la nivelul sistemului digestiv, care pot afecta absorbția nutrienților și starea generală de bine.",
recommendations: [
"Încearcă o dietă de eliminare (ex: fără gluten și lactate timp de 30 de zile) pentru a identifica posibile intoleranțe.",
"Introdu în dietă alimente fermentate (chefir, varză murată) și o sursă de probiotice de calitate.",
"Asigură-te că mesteci bine mâncarea și mănânci într-un mediu relaxat, fără grabă.",
"Consideră enzime digestive sau oțet de mere diluat în apă înainte de mese pentru a sprijini digestia.",
],
},
E: {
title: "HealthType E: Dezechilibru Hormonal (Estrogen/Testosteron)",
description: "Datele sugerează un dezechilibru al hormonilor sexuali, care poate cauza o varietate de simptome fizice și emoționale.",
recommendations: [
"Sprijină detoxifierea ficatului prin consumul de legume crucifere (broccoli, conopidă, varză).",
"Asigură un aport adecvat de zinc (semințe de dovleac, carne roșie) și vitamina B6.",
"Redu expunerea la xenoestrogeni din plastic, cosmetice și pesticide (alege produse organice și recipiente de sticlă).",
"Pentru femei: monitorizează ciclul menstrual și ajustează dieta și antrenamentele în funcție de fazele acestuia.",
"Pentru bărbați: optimizează nivelul de testosteron prin antrenamente de forță, somn adecvat și managementul stresului.",
],
},
};
function switchScreen(screenId) {
Object.values(screens).forEach(screen => screen.setAttribute('aria-hidden', 'true'));
screens[screenId].setAttribute('aria-hidden', 'false');
window.scrollTo(0, 0);
}
function startTest(sex) {
resetState();
state.sex = sex;
state.currentQuestionIndex = 0;
renderQuestion();
switchScreen('question');
}
function renderQuestion() {
const questions = data[state.sex];
const question = questions[state.currentQuestionIndex];
if (!question) {
calculateResult();
return;
}
let html = `<h2 class="question-title">${question.text}</h2>`;
if (question.help) {
html += `<p class="question-help">${question.help}</p>`;
}
if (question.type === 'hw') {
html += renderHWInputs();
} else {
html += '<ul class="option-list">';
question.options.forEach((opt, index) => {
const inputId = `q${state.currentQuestionIndex}-opt${index}`;
html += `
<li>
<label for="${inputId}" class="option-label">
<input type="${question.type}" id="${inputId}" name="answer" value="${opt.value}" data-exclusive="${opt.exclusive || false}">
<span>${opt.text}</span>
</label>
</li>
`;
});
html += '</ul>';
}
questionElements.container.innerHTML = html;
updateProgress();
restoreAnswer();
addOptionListeners();
buttons.back.disabled = state.history.length === 0;
}
function renderHWInputs() {
return `
<div class="input-group">
<label for="height">Înălțime</label>
<div style="display: flex; align-items: center;">
<input type="number" id="height" class="input-field" value="${state.height.value || ''}">
<span id="height-unit" class="unit-toggle">${state.height.unit}</span>
</div>
</div>
<div class="input-group">
<label for="weight">Greutate</label>
<div style="display: flex; align-items: center;">
<input type="number" id="weight" class="input-field" value="${state.weight.value || ''}">
<span id="weight-unit" class="unit-toggle">${state.weight.unit}</span>
</div>
</div>
`;
}
function addOptionListeners() {
const question = data[state.sex][state.currentQuestionIndex];
if (question.type === 'hw') {
document.getElementById('height').addEventListener('input', e => state.height.value = e.target.value);
document.getElementById('weight').addEventListener('input', e => state.weight.value = e.target.value);
document.getElementById('height-unit').addEventListener('click', () => toggleUnit('height'));
document.getElementById('weight-unit').addEventListener('click', () => toggleUnit('weight'));
return;
}
const labels = questionElements.container.querySelectorAll('.option-label');
labels.forEach(label => {
label.addEventListener('click', (e) => {
const input = label.querySelector('input');
if (input.type === 'radio') {
labels.forEach(l => l.classList.remove('selected'));
label.classList.add('selected');
} else { // checkbox
label.classList.toggle('selected');
if (input.dataset.exclusive === 'true' && input.checked) {
// Uncheck others and remove selected class
labels.forEach(l => {
const i = l.querySelector('input');
if (i !== input) {
i.checked = false;
l.classList.remove('selected');
}
});
} else {
// Uncheck the exclusive option if another is selected
const exclusiveOpt = questionElements.container.querySelector('input[data-exclusive="true"]');
if (exclusiveOpt) exclusiveOpt.checked = false;
questionElements.container.querySelector('input[data-exclusive="true"]').parentElement.classList.remove('selected');
}
}
});
});
}
function toggleUnit(type) {
if (type === 'height') {
const currentUnit = state.height.unit;
const currentValue = parseFloat(document.getElementById('height').value);
if (currentUnit === 'cm') {
state.height.unit = 'ft';
if (!isNaN(currentValue)) {
document.getElementById('height').value = (currentValue * 0.0328084).toFixed(1);
}
} else {
state.height.unit = 'cm';
if (!isNaN(currentValue)) {
document.getElementById('height').value = (currentValue * 30.48).toFixed(0);
}
}
document.getElementById('height-unit').textContent = state.height.unit;
} else { // weight
const currentUnit = state.weight.unit;
const currentValue = parseFloat(document.getElementById('weight').value);
if (currentUnit === 'kg') {
state.weight.unit = 'lbs';
if (!isNaN(currentValue)) {
document.getElementById('weight').value = (currentValue * 2.20462).toFixed(1);
}
} else {
state.weight.unit = 'lbs';
if (!isNaN(currentValue)) {
document.getElementById('weight').value = (currentValue / 2.20462).toFixed(1);
}
}
document.getElementById('weight-unit').textContent = state.weight.unit;
}
}
function updateProgress() {
const questions = data[state.sex];
const progress = (state.currentQuestionIndex / (questions.length -1)) * 100;
questionElements.progressText.textContent = `Întrebarea ${state.currentQuestionIndex + 1}/${questions.length}`;
questionElements.progressFill.style.width = `${progress}%`;
}
function saveAnswer() {
const question = data[state.sex][state.currentQuestionIndex];
let answerValue;
if (question.type === 'hw') {
const h = parseFloat(document.getElementById('height').value);
const w = parseFloat(document.getElementById('weight').value);
state.height.value = h;
state.weight.value = w;
answerValue = { height: h, weight: w };
} else {
const inputs = questionElements.form.querySelectorAll('input:checked');
if (inputs.length === 0) return null; // Validation failed
if (question.type === 'radio') {
answerValue = parseFloat(inputs[0].value);
} else { // checkbox
answerValue = Array.from(inputs).reduce((sum, input) => sum + parseFloat(input.value), 0);
}
}
const existingAnswerIndex = state.answers.findIndex(a => a.questionId === question.id);
if (existingAnswerIndex > -1) {
state.answers[existingAnswerIndex].answerValue = answerValue;
} else {
state.answers.push({ questionId: question.id, answerValue });
}
return answerValue;
}
function restoreAnswer() {
const question = data[state.sex][state.currentQuestionIndex];
const savedAnswer = state.answers.find(a => a.questionId === question.id);
if (!savedAnswer) return;
const inputs = questionElements.form.querySelectorAll('input');
inputs.forEach(input => {
if (question.type === 'radio') {
if (parseFloat(input.value) === savedAnswer.answerValue) {
input.checked = true;
input.parentElement.classList.add('selected');
}
} else { // checkbox
// This is tricky for checkboxes as value is a sum.
// A simple restore isn't possible without storing individual selections.
// For now, we skip checkbox restore to avoid complexity.
}
});
}
function validateAndGoNext() {
const question = data[state.sex][state.currentQuestionIndex];
let answerValue = saveAnswer();
let validationPassed = true;
if (question.type === 'hw') {
const h = state.height.value;
const w = state.weight.value;
if (!h || !w || h <= 0 || w <= 0) {
questionElements.validation.textContent = 'Te rugăm să introduci valori valide.';
validationPassed = false;
} else {
questionElements.validation.textContent = '';
}
} else {
if (answerValue === null) {
questionElements.validation.textContent = 'Te rugăm să selectezi o opțiune.';
validationPassed = false;
} else {
questionElements.validation.textContent = '';
}
}
if (!validationPassed) return;
state.history.push(state.currentQuestionIndex);
// Branching logic
let nextIndex = state.currentQuestionIndex + 1;
if (question.type === 'radio') {
const selectedOption = question.options.find(o => o.value === answerValue);
if (selectedOption && selectedOption.next) {
const nextQuestion = data[state.sex].find(q => q.id === selectedOption.next);
if (nextQuestion) {
nextIndex = data[state.sex].indexOf(nextQuestion);
}
}
}
state.currentQuestionIndex = nextIndex;
renderQuestion();
}
function goBack() {
if (state.history.length > 0) {
state.currentQuestionIndex = state.history.pop();
renderQuestion();
}
}
function resetState() {
state = {
sex: null,
currentQuestionIndex: 0,
answers: [],
history: [],
height: { value: null, unit: 'cm' },
weight: { value: null, unit: 'kg' },
};
}
function resetTest() {
resetState();
switchScreen('start');
}
function calculateResult() {
let scores = { B: 0, C: 0, D: 0, E: 0 };
state.answers.forEach(answer => {
const qId = answer.questionId;
const val = answer.answerValue;
if (qId.startsWith('F') || qId.startsWith('M')) scores.E += val;
if (['C1', 'C4'].includes(qId)) scores.B += val;
if (['C5'].includes(qId)) scores.C += val;
if (['C3'].includes(qId)) scores.D += val;
if (['C2', 'C6', 'C7', 'C8'].includes(qId)) {
scores.B += val * 0.5;
scores.C += val * 0.5;
scores.D += val * 0.5;
}
});
// BMI calculation
let h = state.height.value;
if (state.height.unit === 'ft') h = h * 30.48; // to cm
let w = state.weight.value;
if (state.weight.unit === 'lbs') w = w / 2.20462; // to kg
const bmi = w / ((h / 100) ** 2);
if (bmi > 25) scores.C += 2;
if (bmi < 18.5) scores.D += 1;
const maxScore = Object.entries(scores).reduce((max, entry) => entry[1] > max[1] ? entry : max, [null, -1]);
const finalType = maxScore[1] > 2 ? maxScore[0] : 'A';
displayResult(finalType);
}
function displayResult(type) {
const result = results[type];
resultElements.title.textContent = result.title;
let html = `<div class="result-section"><h3>Descriere</h3><p>${result.description}</p></div>`;
html += `<div class="result-section"><h3>Recomandări</h3><ul>`;
result.recommendations.forEach(rec => {
html += `<li>${rec}</li>`;
});
html += `</ul></div>`;
resultElements.content.innerHTML = html;
// Prepare print summary
let summaryHtml = '<h3>Rezumatul Răspunsurilor</h3><ul>';
data[state.sex].forEach(q => {
const answer = state.answers.find(a => a.questionId === q.id);
if (answer) {
summaryHtml += `<li><strong>${q.text}</strong>: `;
if (q.type === 'hw') {
summaryHtml += `Înălțime: ${state.height.value} ${state.height.unit}, Greutate: ${state.weight.value} ${state.weight.unit}`;
} else {
// This part is complex to get right without storing more data
// For now, just show the score value
summaryHtml += `Răspuns (valoare: ${answer.answerValue})`;
}
summaryHtml += `</li>`;
}
});
summaryHtml += '</ul>';
summaryHtml += `<p>Data testului: ${new Date().toLocaleString('ro-RO')}</p>`;
resultElements.printSummary.innerHTML = summaryHtml;
switchScreen('result');
}
// Event Listeners
buttons.sex.forEach(button => {
button.addEventListener('click', () => startTest(button.dataset.sex));
});
buttons.next.addEventListener('click', validateAndGoNext);
buttons.back.addEventListener('click', goBack);
buttons.reset.addEventListener('click', resetTest);
buttons.print.addEventListener('click', () => window.print());
// Initial setup
switchScreen('start');
});

408
styles.css Normal file
View File

@ -0,0 +1,408 @@
:root {
--primary-color: #007bff;
--primary-color-dark: #0056b3;
--background-color: #f4f7f6;
--surface-color: #ffffff;
--text-color: #333333;
--border-color: #e0e0e0;
--danger-color: #dc3545;
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--border-radius: 8px;
--shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
--transition-speed: 0.3s;
}
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--font-family);
background-color: var(--background-color);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 1rem;
}
.app {
width: 100%;
max-width: 720px;
margin: 0 auto;
position: relative;
}
.screen {
display: none;
flex-direction: column;
background-color: var(--surface-color);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
overflow: hidden;
animation: fadeIn 0.5s ease-in-out;
}
.screen[aria-hidden="false"] {
display: flex;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.screen--start {
padding: 3rem 2rem;
text-align: center;
align-items: center;
}
.title {
font-size: 2.5rem;
font-weight: 700;
margin: 0 0 0.5rem;
}
.subtitle {
font-size: 1.1rem;
color: #666;
max-width: 500px;
margin: 0 0 2.5rem;
}
.sex-select {
display: flex;
gap: 1.5rem;
width: 100%;
justify-content: center;
}
.sex-card {
flex: 1;
max-width: 220px;
padding: 2rem 1rem;
border: 2px solid var(--border-color);
border-radius: var(--border-radius);
font-size: 1.2rem;
font-weight: 600;
cursor: pointer;
background-color: transparent;
transition: all var(--transition-speed) ease;
position: relative;
overflow: hidden;
}
.sex-card:hover, .sex-card:focus-visible {
transform: translateY(-5px);
border-color: var(--primary-color);
box-shadow: 0 8px 20px rgba(0, 123, 255, 0.15);
}
.sex-card::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 4px;
background-color: var(--primary-color);
transform: scaleX(0);
transition: transform var(--transition-speed) ease;
}
.sex-card:hover::after, .sex-card:focus-visible::after {
transform: scaleX(1);
}
.screen--question {
padding: 2rem;
}
.q-header {
margin-bottom: 2rem;
}
.progress {
margin-bottom: 1rem;
}
#progress-text {
font-size: 0.9rem;
color: #555;
display: block;
margin-bottom: 0.5rem;
}
.progress-bar {
width: 100%;
height: 8px;
background-color: #e9ecef;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
width: 0;
height: 100%;
background-color: var(--primary-color);
transition: width 0.4s ease;
}
.nav-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.btn {
padding: 0.6rem 1.2rem;
font-size: 1rem;
font-weight: 600;
border-radius: var(--border-radius);
border: 2px solid transparent;
cursor: pointer;
transition: all var(--transition-speed) ease;
background-color: #f0f0f0;
color: #333;
}
.btn--primary {
background-color: var(--primary-color);
color: white;
}
.btn--primary:hover, .btn--primary:focus-visible {
background-color: var(--primary-color-dark);
box-shadow: 0 4px 10px rgba(0, 123, 255, 0.2);
}
.btn--ghost {
background-color: transparent;
border-color: var(--border-color);
}
.btn--ghost:hover, .btn--ghost:focus-visible {
background-color: #f8f9fa;
border-color: #ccc;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.question-container {
margin-bottom: 2rem;
}
.question-title {
font-size: 1.5rem;
font-weight: 600;
margin: 0 0 1.5rem;
}
.question-help {
font-size: 0.9rem;
color: #666;
margin: -1rem 0 1.5rem;
border-left: 3px solid var(--border-color);
padding-left: 1rem;
}
.option-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.option-label {
display: flex;
align-items: center;
padding: 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
cursor: pointer;
transition: all var(--transition-speed) ease;
}
.option-label:hover {
border-color: var(--primary-color);
background-color: #f8f9fa;
}
.option-label input {
margin-right: 0.75rem;
width: 1.2em;
height: 1.2em;
}
.option-label.selected {
border-color: var(--primary-color);
background-color: #e9f2ff;
}
.q-actions {
display: flex;
justify-content: flex-end;
align-items: center;
border-top: 1px solid var(--border-color);
padding-top: 1.5rem;
}
.validation {
color: var(--danger-color);
font-weight: 500;
margin-right: auto;
}
.screen--result {
padding: 2rem;
text-align: center;
}
#result-title {
font-size: 2rem;
margin-bottom: 1.5rem;
}
.result-content {
text-align: left;
line-height: 1.6;
}
.result-section {
margin-bottom: 2rem;
}
.result-section h3 {
font-size: 1.3rem;
color: var(--primary-color);
border-bottom: 2px solid var(--border-color);
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
.result-actions {
margin-top: 2.5rem;
display: flex;
justify-content: center;
gap: 1rem;
}
.input-group {
margin-bottom: 1.5rem;
}
.input-group label {
display: block;
font-weight: 600;
margin-bottom: 0.5rem;
}
.input-field {
width: 100%;
padding: 0.8rem 1rem;
font-size: 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
transition: border-color var(--transition-speed);
}
.input-field:focus {
outline: none;
border-color: var(--primary-color);
}
.unit-toggle {
margin-left: 1rem;
font-size: 0.9rem;
color: var(--primary-color);
cursor: pointer;
text-decoration: underline;
}
@media (max-width: 600px) {
body {
padding: 0;
}
.app {
max-width: 100%;
}
.screen {
border-radius: 0;
min-height: 100vh;
}
.screen--start {
padding: 2rem 1rem;
}
.sex-select {
flex-direction: column;
align-items: center;
}
.sex-card {
width: 100%;
max-width: 300px;
}
.screen--question, .screen--result {
padding: 1.5rem 1rem;
}
.title {
font-size: 2rem;
}
.q-actions {
flex-direction: column;
gap: 1rem;
}
#btn-next {
width: 100%;
}
.validation {
margin-right: 0;
text-align: center;
}
}
@media print {
body {
background-color: #fff;
color: #000;
padding: 0;
}
.app {
box-shadow: none;
max-width: 100%;
}
.screen--result {
display: block !important;
padding: 1rem;
}
.screen, .q-header, .q-actions, .result-actions {
display: none;
}
#result-title, .result-section h3 {
color: #000;
border-bottom: 2px solid #ccc;
}
.print-summary {
display: block !important;
margin-top: 2rem;
page-break-before: always;
}
.print-summary h3 {
font-size: 1.2rem;
}
.print-summary ul {
padding-left: 20px;
}
}
.print-summary {
display: none;
}