Initial commit

This commit is contained in:
kddd21a
2026-07-29 10:47:40 +00:00
commit 402c3ec6ed
171 changed files with 10536 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Page Not Found | Testpoint</title><style>body{margin:0;font-family:Arial,sans-serif;background:#f4f8ff;color:#10213e}.error{min-height:100vh;display:grid;place-items:center;padding:24px;text-align:center}.card{max-width:520px}.icon{width:60px;height:60px;margin:auto;border-radius:15px;display:grid;place-items:center;background:#1264e8;color:#fff;font-size:20px;font-weight:700}.code{margin:25px 0 5px;color:#1264e8;font-size:14px;font-weight:700}.card h1{margin:0;font-size:38px}.card p{color:#5b687b;line-height:1.65}.card a{display:inline-block;margin-top:12px;padding:13px 25px;border-radius:8px;background:#1264e8;color:#fff;text-decoration:none;font-weight:700}</style></head><body><main class="error"><div class="card"><div class="icon">404</div><p class="code">ERROR 404</p><h1>Page not found</h1><p>The page may have moved or the address may be incorrect.</p><a href="/">Return home</a></div></main></body></html>
+1
View File
@@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Server Error | Testpoint</title><style>body{margin:0;font-family:Arial,sans-serif;background:#f4f8ff;color:#10213e}.error{min-height:100vh;display:grid;place-items:center;padding:24px;text-align:center}.card{max-width:520px}.icon{width:60px;height:60px;margin:auto;border-radius:15px;display:grid;place-items:center;background:#1264e8;color:#fff;font-size:20px;font-weight:700}.code{margin:25px 0 5px;color:#1264e8;font-size:14px;font-weight:700}.card h1{margin:0;font-size:38px}.card p{color:#5b687b;line-height:1.65}.card a{display:inline-block;margin-top:12px;padding:13px 25px;border-radius:8px;background:#1264e8;color:#fff;text-decoration:none;font-weight:700}</style></head><body><main class="error"><div class="card"><div class="icon">500</div><p class="code">ERROR 500</p><h1>Something went wrong</h1><p>We could not complete this request. Please try again in a moment.</p><a href="/">Return home</a></div></main></body></html>
+5
View File
@@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block title %}Manage Email | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-envelope-at"></i></span><h1>Manage email</h1><p>Add and verify the email address used to sign in.</p></div>
{% if emailaddresses %}<form method="post" action="{% url 'account_email' %}" class="email-manage-form">{% csrf_token %}<div class="email-address-list">{% for emailaddress in emailaddresses %}<label class="email-address-item"><input type="radio" name="email" value="{{ emailaddress.email }}"{% if emailaddress.primary or emailaddresses|length == 1 %} checked{% endif %}><span><strong>{{ emailaddress.email }}</strong><small>{% if emailaddress.verified %}Verified{% else %}Not verified{% endif %}{% if emailaddress.primary %} · Primary{% endif %}</small></span></label>{% endfor %}</div><div class="email-actions"><button type="submit" name="action_primary">Make primary</button><button type="submit" name="action_send">Resend verification</button><button class="email-remove" type="submit" name="action_remove">Remove</button></div></form>{% endif %}
<div class="auth-divider"><span>Add another address</span></div><form method="post" action="{% url 'account_email' %}">{% csrf_token %}<div class="auth-field"><label for="id_email">New email address</label><input id="id_email" type="email" name="email" autocomplete="email" required>{% for error in form.email.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div><button class="auth-submit" type="submit" name="action_add">Add email</button></form><p class="auth-card__switch"><a href="{% url 'student_settings' %}">Back to profile settings</a></p></div></section>{% include "components/footer.html" %}{% endblock %}
+3
View File
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Confirm Email | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card auth-card--compact"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-patch-check"></i></span><h1>Confirm your email</h1>{% if confirmation %}<p>Confirm <strong>{{ confirmation.email_address.email }}</strong> as the email for your Testpoint account.</p>{% else %}<p>This confirmation link is invalid or has expired.</p>{% endif %}</div>{% if confirmation %}<form method="post" action="{% url 'account_confirm_email' confirmation.key %}">{% csrf_token %}<button class="auth-submit" type="submit">Confirm email</button></form>{% else %}<a class="auth-submit auth-submit--link" href="{% url 'account_email' %}">Request a new link</a>{% endif %}<p class="auth-card__switch"><a href="{% url 'home' %}">Return home</a></p></div></section>{% include "components/footer.html" %}{% endblock %}
+12
View File
@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block title %}Login | Testpoint{% endblock %}
{% block content %}
<section class="auth-page"><div class="auth-card"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-book"></i></span><h1>Welcome back</h1><p>Continue your IELTS preparation.</p></div>
{% if form.non_field_errors %}<div class="auth-alert" role="alert">{{ form.non_field_errors }}</div>{% endif %}
<form method="post" action="{% url 'account_login' %}" novalidate>{% csrf_token %}{% if redirect_field_value %}<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}">{% endif %}
<div class="auth-field"><label for="id_login">Email</label><input id="id_login" type="email" name="login" value="{{ form.login.value|default:'' }}" placeholder="you@example.com" autocomplete="email" required autofocus aria-describedby="login-errors">{% for error in form.login.errors %}<p class="auth-error" id="login-errors">{{ error }}</p>{% endfor %}</div>
<div class="auth-field"><div class="auth-label-row"><label for="id_password">Password</label><a href="{% url 'account_reset_password' %}">Forgot password?</a></div><input id="id_password" type="password" name="password" placeholder="Enter your password" autocomplete="current-password" required aria-describedby="password-errors">{% for error in form.password.errors %}<p class="auth-error" id="password-errors">{{ error }}</p>{% endfor %}</div>
<label class="auth-check"><input type="checkbox" name="remember"> <span>Remember me</span></label><button class="auth-submit" type="submit">Sign in</button></form>
<p class="auth-card__switch">Don't have an account? <a href="{% url 'account_signup' %}">Create one</a></p></div></section>
{% include "components/footer.html" %}
{% endblock %}
+3
View File
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Logout | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card auth-card--compact"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-box-arrow-right"></i></span><h1>Sign out?</h1><p>You can return and continue your preparation at any time.</p></div><form method="post" action="{% url 'account_logout' %}">{% csrf_token %}<button class="auth-submit" type="submit">Sign out</button></form><p class="auth-card__switch"><a href="{% url 'home' %}">Cancel and return home</a></p></div></section>{% include "components/footer.html" %}{% endblock %}
+3
View File
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Change Password | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-shield-lock"></i></span><h1>Change password</h1><p>Choose a strong password you do not use elsewhere.</p></div>{% if form.non_field_errors %}<div class="auth-alert" role="alert">{{ form.non_field_errors }}</div>{% endif %}<form method="post" action="{% url 'account_change_password' %}">{% csrf_token %}<div class="auth-field"><label for="id_oldpassword">Current password</label><input id="id_oldpassword" type="password" name="oldpassword" autocomplete="current-password" required>{% for error in form.oldpassword.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div><div class="auth-field"><label for="id_password1">New password</label><input id="id_password1" type="password" name="password1" autocomplete="new-password" required>{% for error in form.password1.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div><div class="auth-field"><label for="id_password2">Confirm new password</label><input id="id_password2" type="password" name="password2" autocomplete="new-password" required>{% for error in form.password2.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div><button class="auth-submit" type="submit">Update password</button></form><p class="auth-card__switch"><a href="{% url 'student_settings' %}">Back to profile settings</a></p></div></section>{% include "components/footer.html" %}{% endblock %}
+3
View File
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Reset Password | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-envelope"></i></span><h1>Reset your password</h1><p>Enter your email and we'll send reset instructions.</p></div>{% if form.non_field_errors %}<div class="auth-alert" role="alert">{{ form.non_field_errors }}</div>{% endif %}<form method="post" action="{% url 'account_reset_password' %}">{% csrf_token %}<div class="auth-field"><label for="id_email">Email</label><input id="id_email" type="email" name="email" value="{{ form.email.value|default:'' }}" placeholder="you@example.com" autocomplete="email" required>{% for error in form.email.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div><button class="auth-submit" type="submit">Send reset link</button></form><p class="auth-card__switch"><a href="{% url 'account_login' %}">Back to sign in</a></p></div></section>{% include "components/footer.html" %}{% endblock %}
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Reset Email Sent | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card auth-card--compact"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-send-check"></i></span><h1>Check your email</h1><p>If an account exists for that address, we sent instructions for choosing a new password.</p></div><div class="auth-alert auth-alert--info">For your security, the message may take a few minutes to arrive.</div><p class="auth-card__switch"><a href="{% url 'account_login' %}">Back to sign in</a></p></div></section>{% include "components/footer.html" %}{% endblock %}
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Choose New Password | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-shield-lock"></i></span>{% if token_fail %}<h1>Invalid reset link</h1><p>This password-reset link has expired or has already been used.</p>{% else %}<h1>Choose a new password</h1><p>Use a strong password you do not use on another site.</p>{% endif %}</div>{% if token_fail %}<a class="auth-submit auth-submit--link" href="{% url 'account_reset_password' %}">Request another link</a>{% else %}<form method="post">{% csrf_token %}{% if form.non_field_errors %}<div class="auth-alert" role="alert">{{ form.non_field_errors }}</div>{% endif %}<div class="auth-field"><label for="id_password1">New password</label><input id="id_password1" type="password" name="password1" autocomplete="new-password" required>{% for error in form.password1.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div><div class="auth-field"><label for="id_password2">Confirm new password</label><input id="id_password2" type="password" name="password2" autocomplete="new-password" required>{% for error in form.password2.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div><button class="auth-submit" type="submit">Change password</button></form>{% endif %}</div></section>{% include "components/footer.html" %}{% endblock %}
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Password Changed | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card auth-card--compact"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-check-circle"></i></span><h1>Password changed</h1><p>Your password has been updated successfully. You can now sign in.</p></div><a class="auth-submit auth-submit--link" href="{% url 'account_login' %}">Sign in</a></div></section>{% include "components/footer.html" %}{% endblock %}
+13
View File
@@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block title %}Register | Testpoint{% endblock %}
{% block content %}
<section class="auth-page"><div class="auth-card"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-book"></i></span><h1>Create your account</h1><p>Start preparing for your target IELTS band.</p></div>
{% if form.non_field_errors %}<div class="auth-alert" role="alert">{{ form.non_field_errors }}</div>{% endif %}
<form method="post" action="{% url 'account_signup' %}" novalidate>{% csrf_token %}
<div class="auth-field"><label for="id_username">Username</label><input id="id_username" type="text" name="username" value="{{ form.username.value|default:'' }}" placeholder="Choose a username" autocomplete="username" required>{% for error in form.username.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div>
<div class="auth-field"><label for="id_email">Email</label><input id="id_email" type="email" name="email" value="{{ form.email.value|default:'' }}" placeholder="you@example.com" autocomplete="email" required>{% for error in form.email.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div>
<div class="auth-field"><label for="id_password1">Password</label><input id="id_password1" type="password" name="password1" placeholder="Create a secure password" autocomplete="new-password" required>{% for error in form.password1.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div>
<div class="auth-field"><label for="id_password2">Confirm password</label><input id="id_password2" type="password" name="password2" placeholder="Repeat your password" autocomplete="new-password" required>{% for error in form.password2.errors %}<p class="auth-error">{{ error }}</p>{% endfor %}</div>
<button class="auth-submit" type="submit">Create Account</button></form><p class="auth-card__switch">Already have an account? <a href="{% url 'account_login' %}">Sign in</a></p></div></section>
{% include "components/footer.html" %}
{% endblock %}
+3
View File
@@ -0,0 +1,3 @@
{% extends "base.html" %}
{% block title %}Verify Your Email | Testpoint{% endblock %}
{% block content %}<section class="auth-page"><div class="auth-card auth-card--compact"><div class="auth-card__header"><span class="auth-card__icon"><i class="bi bi-envelope-check"></i></span><h1>Check your email</h1><p>We sent you a verification link. Open it to activate your Testpoint account.</p></div><div class="auth-alert auth-alert--info">If it is not in your inbox, check your spam folder and confirm that you entered the correct address.</div><p class="auth-card__switch"><a href="{% url 'account_login' %}">Return to sign in</a></p></div></section>{% include "components/footer.html" %}{% endblock %}
@@ -0,0 +1,49 @@
{% extends "admin/base_site.html" %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="{% url 'admin:index' %}">Home</a> &rsaquo; <a href="{% url 'admin:exams_examset_changelist' %}">Tests</a> &rsaquo; Import Excel</div>{% endblock %}
{% block content_title %}{% endblock %}
{% block content %}
<div class="html-import excel-import">
<header class="html-import__header">
<div><p class="html-import__eyebrow">Structured test importer</p><h1>Import an Excel test</h1><p>Create editable sections, grouped worksheets, questions, answers, and passage references from one validated workbook.</p></div>
<a class="excel-template-download" href="{% url 'admin:exams_examset_excel_template' %}">&#8595; Download Excel template</a>
</header>
<ol class="html-import__steps" aria-label="Import progress">
<li class="{% if not preview %}is-current{% else %}is-complete{% endif %}"><span>{% if preview %}&#10003;{% else %}1{% endif %}</span><div><strong>Complete template</strong><small>Test, Sections, Groups, Questions</small></div></li>
<li class="{% if preview %}is-current{% endif %}"><span>2</span><div><strong>Review</strong><small>Validate workbook content</small></div></li>
<li><span>3</span><div><strong>Save</strong><small>Create editable test</small></div></li>
</ol>
{% if preview %}
<section class="html-import-card html-import-review">
<div class="html-import-card__heading"><div><p class="html-import__eyebrow">Workbook preview</p><h2>{{ preview.title }}</h2><p>{{ preview.description|default:"No test description supplied." }}</p></div><span class="html-import-status {% if preview.publish %}is-published{% else %}is-draft{% endif %}">{% if preview.publish %}Will publish{% else %}Will save as draft{% endif %}</span></div>
<div class="html-import-summary"><div><strong>{{ preview.sections|length }}</strong><span>Sections</span></div><div><strong>{{ preview_group_count }}</strong><span>Groups</span></div><div><strong>{{ preview_question_count }}</strong><span>Questions</span></div><div><strong>{{ preview.category|title }}</strong><span>Category</span></div></div>
<div class="html-import-preview-list">
{% for section in preview.sections %}
<article class="html-import-preview-item"><div class="html-import-preview-item__number">{{ section.order }}</div><div class="html-import-preview-item__body"><div class="html-import-preview-item__top"><h3>{{ section.section_type|title }} section</h3><span>{{ section.time_limit_minutes }} min &middot; {{ section.questions|length }} questions</span></div><p>{{ section.passage_text|default:"No passage text for this section."|truncatechars:220 }}</p><details><summary>Show imported questions</summary><div>{% for question in section.questions %}<span class="html-import-type">Q{{ question.order }} &middot; {{ question.question_type }}</span>{% endfor %}</div></details></div></article>
{% endfor %}
</div>
<form method="post">{% csrf_token %}<input type="hidden" name="import_payload" value="{{ import_payload }}"><div class="html-import-actions"><a href="{% url 'admin:exams_examset_import_excel' %}">&#8592; Upload again</a><button type="submit" class="default" name="confirm_import" value="1">Confirm import and save</button></div></form>
</section>
{% else %}
<form method="post" enctype="multipart/form-data" class="html-import-card html-import-form">{% csrf_token %}
<div class="html-import-form__main excel-import__guide">
<div class="html-import-card__heading"><div><p class="html-import__eyebrow">Before uploading</p><h2>Use the official workbook</h2><p>The importer rejects renamed sheets, changed headers, duplicate question numbers, missing answers, and invalid options before anything is saved.</p></div></div>
<ol class="excel-import-checklist"><li><span>1</span><div><strong>Complete Test and Sections</strong><small>Enter test details, timing, and passage content.</small></div></li><li><span>2</span><div><strong>Add optional Groups</strong><small>Build notes, tables, forms, or flow charts with [[question number]] placeholders.</small></div></li><li><span>3</span><div><strong>Add Questions</strong><small>Use one row per answer and connect grouped rows with group_key. Leave it blank for ordinary cards.</small></div></li></ol>
<a class="excel-import-template-card" href="{% url 'admin:exams_examset_excel_template' %}"><span>XL</span><div><strong>IELTS_Test_Import_Template.xlsx</strong><small>Download a fresh formatted workbook</small></div><b>&#8595;</b></a>
</div>
<aside class="html-import-form__aside">
<p class="html-import__eyebrow">Completed workbook</p><h2>Upload Excel file</h2><p class="html-import-form__intro">Your workbook is validated first. Nothing is saved until you confirm the preview.</p>
<div class="html-file-picker{% if form.excel_file.errors %} has-errors{% endif %}"><span class="html-file-picker__icon">XL</span>{{ form.excel_file }}<strong id="excel-file-picker-label">Choose an .xlsx file</strong><small>Official IELTS template &middot; maximum 5 MB</small></div>
{% for error in form.excel_file.errors %}<p class="html-import-errors">{{ error }}</p>{% endfor %}
<label class="html-import-publish">{{ form.publish }}<span><strong>{{ form.publish.label }}</strong><small>{{ form.publish.help_text }}</small></span></label>
<div class="html-import-safety"><strong>Safe import</strong><p>The test is built as native editable content. You can adjust every section and question after saving.</p></div>
</aside>
<footer class="html-import-form__footer"><a href="{% url 'admin:exams_examset_changelist' %}">Cancel</a><button type="submit" class="default">Upload and validate</button></footer>
</form>
{% endif %}
</div>
{% endblock %}
{% block footer %}{{ block.super }}<script>document.addEventListener('DOMContentLoaded',function(){var input=document.getElementById('id_excel_file'),label=document.getElementById('excel-file-picker-label');if(input&&label)input.addEventListener('change',function(){label.textContent=input.files.length?input.files[0].name:'Choose an .xlsx file';});});</script>{% endblock %}
@@ -0,0 +1,51 @@
{% extends "admin/base_site.html" %}
{% block breadcrumbs %}
<div class="breadcrumbs"><a href="{% url 'admin:index' %}">Home</a> &rsaquo; <a href="{% url 'admin:exams_examset_changelist' %}">Tests</a> &rsaquo; Import HTML</div>
{% endblock %}
{% block content_title %}{% endblock %}
{% block content %}
<div class="html-import">
<header class="html-import__header">
<div><p class="html-import__eyebrow">Original file importer</p><h1>Import an HTML test</h1><p>Upload one complete HTML test and preserve its original layout, styling, scripts, questions, and controls.</p></div>
<a href="{% url 'admin:exams_examset_add' %}">Build manually instead</a>
</header>
<ol class="html-import__steps" aria-label="Import progress">
<li class="{% if not preview_exact %}is-current{% else %}is-complete{% endif %}"><span>{% if preview_exact %}&#10003;{% else %}1{% endif %}</span><div><strong>Upload</strong><small>Choose the HTML file</small></div></li>
<li class="{% if preview_exact %}is-current{% endif %}"><span>2</span><div><strong>Review</strong><small>Confirm the original file</small></div></li>
<li><span>3</span><div><strong>Save</strong><small>Create the test</small></div></li>
</ol>
{% if preview_exact %}
<section class="html-import-card html-import-review">
<div class="html-import-card__heading"><div><p class="html-import__eyebrow">Import preview</p><h2>{{ preview_title }}</h2><p>Confirm that this is the complete original HTML document you want to publish.</p></div><span class="html-import-status {% if preview_publish %}is-published{% else %}is-draft{% endif %}">{% if preview_publish %}Will publish{% else %}Will save as draft{% endif %}</span></div>
<div class="html-import-summary"><div><strong>Exact</strong><span>HTML rendering</span></div><div><strong>1</strong><span>Original file</span></div><div><strong>{% if preview_publish %}Live{% else %}Draft{% endif %}</strong><span>Initial status</span></div></div>
<div class="html-import-preview-list">
<article class="html-import-preview-item"><div class="html-import-preview-item__number">1</div><div class="html-import-preview-item__body"><div class="html-import-preview-item__top"><h3>{{ preview_source_name }}</h3><span>{{ preview_file_size|filesizeformat }}</span></div><p>The original markup, styling, scripts, question layout, and internal controls will be preserved inside the protected HTML viewer.</p><div class="html-import-warning">The source experience is preserved. Native answer-by-answer review requires a separate result bridge supplied by the HTML test.</div></div></article>
</div>
<form method="post">{% csrf_token %}<input type="hidden" name="import_payload" value="{{ import_payload }}"><div class="html-import-actions"><a href="{% url 'admin:exams_examset_import_html' %}">&#8592; Upload again</a><button type="submit" class="default" name="confirm_import" value="1">Confirm import and save</button></div></form>
</section>
{% else %}
<form method="post" enctype="multipart/form-data" class="html-import-card html-import-form">{% csrf_token %}
<div class="html-import-form__main">
<div class="html-import-card__heading"><div><p class="html-import__eyebrow">Test details</p><h2>Name and classify this test</h2><p>The uploaded document itself stays unchanged.</p></div></div>
{% for field in form %}{% if field.name != 'html_files' and field.name != 'publish' %}<div class="html-import-row{% if field.errors %} has-errors{% endif %}">{{ field.label_tag }}{{ field }}{% if field.help_text %}<p class="html-import-help">{{ field.help_text }}</p>{% endif %}{% for error in field.errors %}<p class="html-import-errors">{{ error }}</p>{% endfor %}</div>{% endif %}{% endfor %}
</div>
<aside class="html-import-form__aside">
<p class="html-import__eyebrow">Source file</p><h2>Choose the HTML test</h2><p class="html-import-form__intro">The complete file is stored unchanged and runs only inside the protected test viewer.</p>
<div class="html-file-picker{% if form.html_files.errors %} has-errors{% endif %}"><span class="html-file-picker__icon">&#8679;</span>{{ form.html_files }}<strong id="html-file-picker-label">Choose an HTML file</strong><small>One complete HTML or HTM file &middot; maximum 2 MB</small></div>
{% for error in form.html_files.errors %}<p class="html-import-errors">{{ error }}</p>{% endfor %}
<label class="html-import-publish">{{ form.publish }}<span><strong>{{ form.publish.label }}</strong><small>{{ form.publish.help_text }}</small></span></label>
{% for error in form.non_field_errors %}<p class="html-import-errors">{{ error }}</p>{% endfor %}
<div class="html-import-safety"><strong>Recommended workflow</strong><p>Import as a draft, open the student test once, then publish it from the Tests page.</p></div>
</aside>
<footer class="html-import-form__footer"><a href="{% url 'admin:exams_examset_changelist' %}">Cancel</a><button type="submit" class="default">Upload and review</button></footer>
</form>
{% endif %}
</div>
{% endblock %}
{% block footer %}{{ block.super }}<script>document.addEventListener('DOMContentLoaded',function(){var input=document.getElementById('id_html_files'),label=document.getElementById('html-file-picker-label');if(input&&label)input.addEventListener('change',function(){label.textContent=input.files.length?input.files[0].name:'Choose an HTML file';});});</script>{% endblock %}
+54
View File
@@ -0,0 +1,54 @@
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}Dashboard | {{ site_title }}{% endblock %}
{% block content %}
<div class="ielts-admin-dashboard">
<header class="ielts-admin-dashboard__heading">
<div><h1>Dashboard</h1><p>A clear view of your Testpoint platform.</p></div>
<nav aria-label="Dashboard shortcuts" class="ielts-admin-dashboard__tabs">
<a class="is-active" href="{% url 'admin:index' %}" aria-current="page">Overview</a>
<a href="{% url 'admin:exams_studentattempt_changelist' %}">Results</a>
<a href="{% url 'admin:sites_site_changelist' %}">Settings</a>
</nav>
</header>
<section class="ielts-admin-metrics" aria-label="Platform overview">
{% for metric in dashboard_metrics %}
<a class="ielts-admin-metric" href="{{ metric.link }}">
<div class="ielts-admin-metric__main"><div><span>{{ metric.label }}</span><strong>{{ metric.value }}</strong></div><i class="material-symbols-outlined" aria-hidden="true">{{ metric.icon }}</i></div>
<small>{{ metric.detail }}</small>
</a>
{% endfor %}
</section>
<section class="ielts-admin-panel ielts-admin-chart" aria-labelledby="activity-heading">
<header><div><h2 id="activity-heading">Activity overview</h2><p>Student attempts and active students across the last 7 days.</p></div><span>Last 7 days</span></header>
{% if has_chart_activity %}
<div class="ielts-admin-chart__legend"><span><i></i> Student attempts</span><span><i></i> Active students</span></div>
<div class="ielts-admin-chart__canvas" role="img" aria-label="Seven-day activity line chart">
<svg viewBox="0 0 680 170" preserveAspectRatio="none" aria-hidden="true">
<line x1="18" y1="18" x2="662" y2="18"></line><line x1="18" y1="71" x2="662" y2="71"></line><line x1="18" y1="124" x2="662" y2="124"></line><line x1="18" y1="152" x2="662" y2="152"></line>
<polyline class="ielts-admin-chart__attempts" points="{{ attempt_points }}"></polyline>
<polyline class="ielts-admin-chart__students" points="{{ active_points }}"></polyline>
</svg>
<div class="ielts-admin-chart__labels">{% for label in chart_labels %}<span>{{ label }}</span>{% endfor %}</div>
</div>
{% else %}
<div class="ielts-admin-empty"><i class="material-symbols-outlined" aria-hidden="true">show_chart</i><div><strong>No activity yet</strong><p>Activity will appear here when students start tests.</p></div></div>
{% endif %}
</section>
<section class="ielts-admin-panel ielts-admin-tests" aria-labelledby="recent-tests-heading">
<header><div><h2 id="recent-tests-heading">Recent tests</h2><p>Your newest test content and its real activity.</p></div><a href="{% url 'admin:exams_examset_changelist' %}">View all tests <span aria-hidden="true"></span></a></header>
{% if recent_tests %}
<div class="ielts-admin-table-wrap"><table><thead><tr><th>Test title</th><th>Skill</th><th>Questions</th><th>Attempts</th><th>Status</th><th>Created</th></tr></thead><tbody>
{% for test in recent_tests %}<tr><th><a href="{% url 'admin:exams_examset_change' test.pk %}">{{ test.title }}</a></th><td>{{ test.get_category_display }}</td><td>{{ test.question_count }}</td><td>{{ test.attempt_count }}</td><td><span class="ielts-admin-status {% if test.is_published %}is-live{% else %}is-draft{% endif %}">{% if test.is_published %}Published{% else %}Draft{% endif %}</span></td><td>{{ test.created_at|date:"M j, Y" }}</td></tr>{% endfor %}
</tbody></table></div>
{% else %}
<div class="ielts-admin-empty"><i class="material-symbols-outlined" aria-hidden="true">description</i><div><strong>No tests created yet</strong><p>Create your first test to start building the practice library.</p><a href="{% url 'admin:exams_examset_add' %}">Create a test</a></div></div>
{% endif %}
</section>
</div>
{% endblock %}
+48
View File
@@ -0,0 +1,48 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{% block meta_description %}Practice IELTS with timed mock tests, instant results, and detailed progress tracking.{% endblock %}">
<title>{% block title %}Testpoint{% endblock %}</title>
<meta property="og:type" content="website">
<meta property="og:site_name" content="Testpoint">
<meta property="og:title" content="{% block og_title %}Testpoint — Practice Smarter. Score Higher.{% endblock %}">
<meta property="og:description" content="Timed IELTS mock tests, instant results, and detailed performance analysis.">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="{{ request.build_absolute_uri }}">
<link rel="icon" href="{% static 'images/favicon.svg' %}" type="image/svg+xml">
<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=Poppins:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'css/style.css' %}?v=20260717.2">
<link rel="stylesheet" href="{% static 'css/navbar.css' %}?v=20260727.2">
<link rel="stylesheet" href="{% static 'css/skill-navigation.css' %}?v=20260717.1">
<link rel="stylesheet" href="{% static 'css/hero.css' %}?v=20260717.2">
<link rel="stylesheet" href="{% static 'css/exams.css' %}?v=20260717.2">
<link rel="stylesheet" href="{% static 'css/how-it-works.css' %}?v=20260717.2">
<link rel="stylesheet" href="{% static 'css/footer.css' %}?v=20260727.2">
<link rel="stylesheet" href="{% static 'css/reference.css' %}?v=20260717.2">
<link rel="stylesheet" href="{% static 'css/hero-device.css' %}?v=20260717.4">
<link rel="stylesheet" href="{% static 'css/site-polish.css' %}?v=20260718.1">
<link rel="stylesheet" href="{% static 'css/mobile.css' %}?v=20260718.1">
<link rel="stylesheet" href="{% static 'css/auth.css' %}?v=20260717.2">
<link rel="stylesheet" href="{% static 'css/accessibility.css' %}?v=20260717.2">
<link rel="stylesheet" href="{% static 'css/contact.css' %}?v=20260717.2">
{% block extra_css %}{% endblock %}
</head>
<body>
<a class="skip-link" href="#main-content">Skip to main content</a>
{% include 'components/navbar.html' %}
{% if messages %}<div class="site-messages" aria-live="polite">{% for message in messages %}<div class="site-message site-message--{{ message.tags|default:'info' }}" role="status">{{ message }}</div>{% endfor %}</div>{% endif %}
<main id="main-content" tabindex="-1">{% block content %}{% endblock %}</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" defer></script>
{% block extra_js %}{% endblock %}
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<section class="py-5 bg-white" id="available-tests">
<div class="container">
<h2 class="text-center fw-bold mb-5">Available Tests</h2>
<div class="row g-4">
<div class="col-md-6 col-lg-3"><div class="test-card h-100"><div class="test-icon test-icon-reading"><i class="bi bi-book"></i></div><h4>Reading</h4><p>Original passages with focused comprehension questions.</p><a href="{% url 'exam_list' %}?skill=reading" class="test-link test-link-reading">Start Practice <i class="bi bi-arrow-right"></i></a></div></div>
<div class="col-md-6 col-lg-3"><div class="test-card h-100"><div class="test-icon test-icon-listening"><i class="bi bi-headphones"></i></div><h4>Listening</h4><p>Audio recordings with timed objective questions.</p><a href="{% url 'exam_list' %}?skill=listening" class="test-link test-link-listening">Start Practice <i class="bi bi-arrow-right"></i></a></div></div>
<div class="col-md-6 col-lg-3"><div class="test-card h-100"><div class="test-icon test-icon-writing"><i class="bi bi-pencil"></i></div><h4>Writing</h4><p>Timed essay practice ready for instructor review.</p><a href="{% url 'exam_list' %}?skill=writing" class="test-link test-link-writing">Start Practice <i class="bi bi-arrow-right"></i></a></div></div>
<div class="col-md-6 col-lg-3"><div class="test-card h-100"><div class="test-icon test-icon-speaking"><i class="bi bi-mic"></i></div><h4>Speaking</h4><p>Speaking prompts with audio-response upload.</p><a href="{% url 'exam_list' %}?skill=speaking" class="test-link test-link-speaking">Start Practice <i class="bi bi-arrow-right"></i></a></div></div>
</div>
</div>
</section>
+11
View File
@@ -0,0 +1,11 @@
<section class="site-cta site-cta--premium" aria-labelledby="premium-cta-title">
<div class="container site-cta__inner">
<div class="site-cta__premium-mark" aria-hidden="true"><i class="bi bi-infinity"></i></div>
<div class="site-cta__copy">
<span>{% if has_lifetime_premium %}<i class="bi bi-patch-check-fill"></i> Premium active{% else %}Lifetime Premium{% endif %}</span>
<h2 id="premium-cta-title">{% if has_lifetime_premium %}Every test is unlocked for you.{% else %}Unlock every test. Keep access forever.{% endif %}</h2>
<p>{% if has_lifetime_premium %}Your one-time access includes current and future Premium tests.{% else %}One payment gives you unlimited access to the complete practice library—no subscription and no expiry.{% endif %}</p>
</div>
<a href="{% if has_lifetime_premium %}{% url 'exam_list' %}{% else %}{% url 'premium' %}{% endif %}" class="site-cta__button">{% if has_lifetime_premium %}Browse all tests{% else %}Explore Premium{% endif %} <i class="bi bi-arrow-right"></i></a>
</div>
</section>
+11
View File
@@ -0,0 +1,11 @@
<section class="py-5 bg-light" id="about">
<div class="container">
<h2 class="text-center fw-bold mb-5">Why Choose Us?</h2>
<div class="row g-4">
<div class="col-md-6 col-lg-3"><div class="feature-card h-100"><div class="feature-icon"><i class="bi bi-stopwatch"></i></div><h4>Real Exam Timer</h4><p>Practice with the same time limits as the real IELTS exam.</p></div></div>
<div class="col-md-6 col-lg-3"><div class="feature-card h-100"><div class="feature-icon"><i class="bi bi-bar-chart"></i></div><h4>Instant Results</h4><p>Get your band score immediately after the test.</p></div></div>
<div class="col-md-6 col-lg-3"><div class="feature-card h-100"><div class="feature-icon"><i class="bi bi-book"></i></div><h4>Updated Questions</h4><p>All tests are based on the latest IELTS format.</p></div></div>
<div class="col-md-6 col-lg-3"><div class="feature-card h-100"><div class="feature-icon"><i class="bi bi-phone"></i></div><h4>Practice Anywhere</h4><p>Access our platform on any device, anytime.</p></div></div>
</div>
</div>
</section>
+14
View File
@@ -0,0 +1,14 @@
<footer class="site-footer">
<div class="container">
<div class="site-footer__grid">
<div class="site-footer__intro">
<a class="site-footer__brand" href="{% url 'home' %}" aria-label="Testpoint home"><span class="brand-icon"><i class="bi bi-book" aria-hidden="true"></i></span>Testpoint</a>
<p>Focused practice for your<br>IELTS preparation.</p>
</div>
<nav aria-label="Footer quick links"><h3>Quick Links</h3><a href="{% url 'home' %}">Home</a><a href="{% url 'exam_list' %}">Exams</a><a href="{% url 'home' %}#about">About</a></nav>
<nav aria-label="Footer support links"><h3>Support</h3><a href="{% url 'contact' %}">Contact Us</a><a href="{% url 'faq' %}">FAQ</a><a href="{% url 'privacy' %}">Privacy Policy</a><a href="{% url 'terms' %}">Terms of Use</a></nav>
<div><h3>Stay Connected</h3>{% if social_facebook_url or social_instagram_url or social_youtube_url or social_telegram_url %}<div class="site-footer__socials">{% if social_facebook_url %}<a href="{{ social_facebook_url }}" target="_blank" rel="noopener noreferrer" aria-label="Facebook"><i class="bi bi-facebook"></i></a>{% endif %}{% if social_instagram_url %}<a href="{{ social_instagram_url }}" target="_blank" rel="noopener noreferrer" aria-label="Instagram"><i class="bi bi-instagram"></i></a>{% endif %}{% if social_youtube_url %}<a href="{{ social_youtube_url }}" target="_blank" rel="noopener noreferrer" aria-label="YouTube"><i class="bi bi-youtube"></i></a>{% endif %}{% if social_telegram_url %}<a href="{{ social_telegram_url }}" target="_blank" rel="noopener noreferrer" aria-label="Telegram"><i class="bi bi-telegram"></i></a>{% endif %}</div>{% else %}<a class="site-footer__email" href="mailto:{{ site_support_email }}">{{ site_support_email }}</a>{% endif %}</div>
</div>
<p class="site-footer__copyright">&copy; {% now "Y" %} Testpoint. All rights reserved.</p>
</div>
</footer>
+15
View File
@@ -0,0 +1,15 @@
{% load static %}
<section class="landing-hero" aria-labelledby="landing-title">
<div class="landing-shell landing-hero__grid">
<div class="landing-hero__copy">
<p class="landing-eyebrow">IELTS practice, built for exam day</p>
<h1 id="landing-title">Practice IELTS.<br><span>Fix weak areas.</span></h1>
<p class="landing-hero__lead">Timed practice, clear scoring, and passage evidence for objective answers.</p>
<div class="landing-actions">
<a href="{% url 'account_signup' %}" class="landing-button landing-button--primary">Start free</a>
<a href="{% url 'exam_list' %}" class="landing-button landing-button--secondary">Browse tests <i class="bi bi-arrow-right" aria-hidden="true"></i></a>
</div>
</div>
{% include "components/leaderboard_preview.html" %}
</div>
</section>
+11
View File
@@ -0,0 +1,11 @@
<section class="how-it-works py-5">
<div class="container">
<h2 class="text-center mb-5 fw-bold">How It Works</h2>
<div class="row g-4 text-center">
<div class="col-md-3"><div class="how-step"><div class="step-icon"><span class="step-number">1</span><i class="bi bi-person"></i></div><h3>Register</h3><p>Create your free account in seconds.</p></div></div>
<div class="col-md-3"><div class="how-step"><div class="step-icon"><span class="step-number">2</span><i class="bi bi-file-earmark-text"></i></div><h3>Choose a Test</h3><p>Select the skill and start your test.</p></div></div>
<div class="col-md-3"><div class="how-step"><div class="step-icon"><span class="step-number">3</span><i class="bi bi-clock"></i></div><h3>Finish the Exam</h3><p>Complete the test within the time limit.</p></div></div>
<div class="col-md-3"><div class="how-step"><div class="step-icon"><span class="step-number">4</span><i class="bi bi-trophy"></i></div><h3>Get Your Score</h3><p>View your band score and detailed report.</p></div></div>
</div>
</div>
</section>
@@ -0,0 +1,21 @@
<aside class="leaderboard-preview" aria-labelledby="preview-title">
<header class="leaderboard-preview__header">
<div><h2 id="preview-title">Weekly leaderboard</h2><p>See who is improving this week</p></div>
{% if leaderboard_preview.0.demo %}<span>Preview</span>{% endif %}
</header>
<nav class="leaderboard-preview__tabs" aria-label="Leaderboard period preview">
<span class="is-active">This Week</span><span>This Month</span><span>All Time</span>
</nav>
<div class="leaderboard-preview__podium">
{% with student=leaderboard_preview.1 %}<div class="preview-winner preview-winner--silver"><b>2</b><span>{{ student.initial }}</span><strong>{{ student.name }}</strong><small>Band {{ student.band|floatformat:1 }}</small></div>{% endwith %}
{% with student=leaderboard_preview.0 %}<div class="preview-winner preview-winner--gold"><b>1</b><span>{{ student.initial }}</span><strong>{{ student.name }}</strong><small>Band {{ student.band|floatformat:1 }}</small></div>{% endwith %}
{% with student=leaderboard_preview.2 %}<div class="preview-winner preview-winner--bronze"><b>3</b><span>{{ student.initial }}</span><strong>{{ student.name }}</strong><small>Band {{ student.band|floatformat:1 }}</small></div>{% endwith %}
</div>
<div class="leaderboard-preview__rows">
{% for student in leaderboard_preview|slice:"3:5" %}
<div><b>{{ student.rank }}</b><span class="preview-avatar">{{ student.initial }}</span><strong>{{ student.name }}</strong><span>{{ student.band|floatformat:1 }}</span></div>
{% endfor %}
<div class="is-current"><b>{{ leaderboard_preview_user.rank }}</b><span class="preview-avatar">{{ leaderboard_preview_user.initial }}</span><strong>{{ leaderboard_preview_user.name }} {% if request.user.is_authenticated %}(You){% else %}(Example){% endif %}</strong><span>Band {{ leaderboard_preview_user.band|floatformat:1 }} <i class="bi bi-arrow-up"></i></span></div>
</div>
<a class="leaderboard-preview__link" href="{% if request.user.is_authenticated %}{% url 'leaderboard' %}{% else %}{% url 'account_signup' %}{% endif %}">{% if request.user.is_authenticated %}View full leaderboard{% else %}Join the leaderboard{% endif %} <i class="bi bi-arrow-right"></i></a>
</aside>
+30
View File
@@ -0,0 +1,30 @@
<nav class="navbar navbar-expand-lg py-3" aria-label="Main navigation">
<div class="container">
<a class="navbar-brand fw-bold" href="{% url 'home' %}">
<span class="brand-icon"><i class="bi bi-book"></i></span> Testpoint
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavigation" aria-controls="mainNavigation" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNavigation">
<ul class="navbar-nav mx-auto">
<li class="nav-item"><a class="nav-link{% if request.resolver_match.url_name == 'home' %} active{% endif %}" href="{% url 'home' %}"{% if request.resolver_match.url_name == 'home' %} aria-current="page"{% endif %}>Home</a></li>
<li class="nav-item"><a class="nav-link{% if request.resolver_match.url_name == 'exam_list' %} active{% endif %}" href="{% url 'exam_list' %}"{% if request.resolver_match.url_name == 'exam_list' %} aria-current="page"{% endif %}>Exams</a></li>
<li class="nav-item"><a class="nav-link nav-skill-link{% if request.GET.skill == 'reading' %} active{% endif %}" href="{% url 'exam_list' %}?skill=reading">Reading</a></li>
<li class="nav-item"><a class="nav-link nav-skill-link{% if request.GET.skill == 'listening' %} active{% endif %}" href="{% url 'exam_list' %}?skill=listening">Listening</a></li>
<li class="nav-item"><a class="nav-link nav-skill-link{% if request.GET.skill == 'writing' %} active{% endif %}" href="{% url 'exam_list' %}?skill=writing">Writing</a></li>
<li class="nav-item"><a class="nav-link nav-skill-link{% if request.GET.skill == 'speaking' %} active{% endif %}" href="{% url 'exam_list' %}?skill=speaking">Speaking</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'home' %}#about">About</a></li>
</ul>
<div class="navbar-actions">
{% if request.user.is_authenticated %}
<a href="{% url 'student_dashboard' %}" class="btn btn-light me-2">Dashboard</a>
<a href="{% url 'account_logout' %}" class="btn btn-primary">Logout</a>
{% else %}
<a href="{% url 'account_login' %}" class="btn btn-light me-2">Login</a>
<a href="{% url 'account_signup' %}" class="btn btn-primary">Register</a>
{% endif %}
</div>
</div>
</div>
</nav>
+135
View File
@@ -0,0 +1,135 @@
<div class="card shadow-sm border-0 rounded-4 mt-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0">
IELTS Skill Progress
</h4>
<span class="text-muted">
Current level
</span>
</div>
<!-- Reading -->
<div class="mb-4">
<div class="d-flex justify-content-between mb-2">
<span>
<i class="bi bi-book text-primary"></i>
Reading
</span>
<span class="fw-semibold">
80%
</span>
</div>
<div class="progress" style="height:10px;">
<div class="progress-bar"
style="width:80%">
</div>
</div>
</div>
<!-- Listening -->
<div class="mb-4">
<div class="d-flex justify-content-between mb-2">
<span>
<i class="bi bi-headphones text-primary"></i>
Listening
</span>
<span class="fw-semibold">
90%
</span>
</div>
<div class="progress" style="height:10px;">
<div class="progress-bar"
style="width:90%">
</div>
</div>
</div>
<!-- Writing -->
<div class="mb-4">
<div class="d-flex justify-content-between mb-2">
<span>
<i class="bi bi-pencil-square text-primary"></i>
Writing
</span>
<span class="fw-semibold">
65%
</span>
</div>
<div class="progress" style="height:10px;">
<div class="progress-bar"
style="width:65%">
</div>
</div>
</div>
<!-- Speaking -->
<div>
<div class="d-flex justify-content-between mb-2">
<span>
<i class="bi bi-mic text-primary"></i>
Speaking
</span>
<span class="fw-semibold">
70%
</span>
</div>
<div class="progress" style="height:10px;">
<div class="progress-bar"
style="width:70%">
</div>
</div>
</div>
</div>
</div>
+74
View File
@@ -0,0 +1,74 @@
<div class="col-md-2 sidebar">
<div class="logo">
<h3 class="fw-bold">
<i class="bi bi-mortarboard-fill"></i>
Testpoint
</h3>
</div>
<hr>
<ul class="nav flex-column mt-4">
<li class="nav-item">
<a href="{% url 'student_dashboard' %}" class="nav-link active">
<i class="bi bi-house-door"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="{% url 'exam_list' %}" class="nav-link">
<i class="bi bi-clipboard-check"></i>
Mock Tests
</a>
</li>
<li class="nav-item">
<a href="{% url 'exam_list' %}" class="nav-link">
<i class="bi bi-book"></i>
Reading
</a>
</li>
<li class="nav-item">
<a href="{% url 'exam_list' %}" class="nav-link">
<i class="bi bi-headphones"></i>
Listening
</a>
</li>
<li class="nav-item">
<a href="{% url 'exam_list' %}" class="nav-link">
<i class="bi bi-pencil-square"></i>
Writing
</a>
</li>
<li class="nav-item">
<a href="{% url 'exam_list' %}" class="nav-link">
<i class="bi bi-mic"></i>
Speaking
</a>
</li>
<li class="nav-item">
<a href="{% url 'student_dashboard' %}" class="nav-link">
<i class="bi bi-graph-up"></i>
Progress
</a>
</li>
<li class="nav-item">
<a href="{% url 'student_dashboard' %}" class="nav-link">
<i class="bi bi-gear"></i>
Settings
</a>
</li>
</ul>
</div>
+14
View File
@@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block title %}Contact Support | Testpoint{% endblock %}
{% block meta_description %}Contact Testpoint support for help with your account, practice tests, or results.{% endblock %}
{% block content %}
<section class="contact-page"><div class="container contact-page__layout"><div class="contact-page__intro"><p class="info-page__eyebrow">Support</p><h1>How can we help?</h1><p>Send us a message about your account or the platform. Our support team will reply by email.</p><div class="contact-page__detail"><i class="bi bi-envelope"></i><div><strong>Email support</strong><span>{{ support_email|default:"support@ieltsmock.com" }}</span></div></div></div>
<div class="contact-card"><form method="post" novalidate>{% csrf_token %}{{ form.website }}
<div class="contact-field"><label for="id_name">Name</label><input id="id_name" name="name" value="{{ form.name.value|default:'' }}" autocomplete="name" required>{% for error in form.name.errors %}<p>{{ error }}</p>{% endfor %}</div>
<div class="contact-field"><label for="id_email">Email</label><input id="id_email" type="email" name="email" value="{{ form.email.value|default:'' }}" autocomplete="email" required>{% for error in form.email.errors %}<p>{{ error }}</p>{% endfor %}</div>
<div class="contact-field"><label for="id_subject">Subject</label><input id="id_subject" name="subject" value="{{ form.subject.value|default:'' }}" required>{% for error in form.subject.errors %}<p>{{ error }}</p>{% endfor %}</div>
<div class="contact-field"><label for="id_message">Message</label><textarea id="id_message" name="message" rows="6" required>{{ form.message.value|default:'' }}</textarea>{% for error in form.message.errors %}<p>{{ error }}</p>{% endfor %}</div>
{% for error in form.website.errors %}<p class="contact-form-error">{{ error }}</p>{% endfor %}<button type="submit">Send Message</button>
</form></div></div></section>
{% include "components/footer.html" %}
{% endblock %}
+12
View File
@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block title %}FAQ | Testpoint{% endblock %}
{% block content %}
<section class="info-page"><div class="container info-page__inner"><p class="info-page__eyebrow">Support</p><h1>Frequently Asked Questions</h1><p class="info-page__lead">Everything you need to know before starting your IELTS practice.</p><div class="info-page__cards">
<details open><summary>How do I start a mock test?</summary><p>Create a free account, open the Exams page, and select an available mock test.</p></details>
<details><summary>Can I use Testpoint on my phone?</summary><p>Yes. The public website and student experience are designed for desktop, tablet, and mobile screens.</p></details>
<details><summary>When will I receive my results?</summary><p>Automatically graded questions are scored immediately. Writing and Speaking responses may require manual review.</p></details>
<details><summary>Is this an official IELTS service?</summary><p>No. Testpoint is an independent practice platform. Practice results do not replace or guarantee an official IELTS result.</p></details>
<details><summary>How can I contact support?</summary><p>Email <a href="mailto:{{ site_support_email }}">{{ site_support_email }}</a> and include the email address connected to your account.</p></details>
</div></div></section>
{% include "components/footer.html" %}
{% endblock %}
+16
View File
@@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Testpoint | Focused practice for exam day{% endblock %}
{% block meta_description %}Practise IELTS in realistic timed sessions, review every answer, and understand what to improve next.{% endblock %}
{% block extra_css %}<link rel="stylesheet" href="{% static 'css/landing-v2.css' %}?v=20260720.5"><link rel="stylesheet" href="{% static 'css/leaderboard.css' %}?v=20260727.1">{% endblock %}
{% block content %}
{% include 'components/hero.html' %}
{% include 'components/features.html' %}
{% include 'components/available_tests.html' %}
{% include 'components/how_it_works.html' %}
{% include 'components/cta.html' %}
{% include 'components/footer.html' %}
{% endblock %}
+11
View File
@@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block title %}Privacy Policy | Testpoint{% endblock %}
{% block content %}<section class="info-page"><div class="container info-page__inner"><p class="info-page__eyebrow">Legal</p><h1>Privacy Policy</h1><p class="info-page__lead">How Testpoint handles account, practice, and uploaded-response data.</p><div class="info-page__article">
<h2>Information we collect</h2><p>We store information you provide when creating or updating an account, test answers, progress, scores, support messages, and speaking audio you choose to upload.</p>
<h2>How information is used</h2><p>Data is used to authenticate accounts, deliver timed tests, calculate progress, allow authorised instructors to review responses, prevent abuse, and answer support requests.</p>
<h2>Audio and manual review</h2><p>Uploaded speaking responses and written responses may be accessed by authorised administrators or instructors for grading. Do not include unnecessary sensitive personal information in a test response.</p>
<h2>Service providers and security</h2><p>Hosting, database, email, and storage providers may process data only as needed to operate the service. We use access controls, HTTPS, secure cookies, and operational backups, but no internet service can guarantee absolute security.</p>
<h2>Retention and your choices</h2><p>Practice records are retained while needed to provide progress history and operate the service. You may request access, correction, export, or deletion by contacting <a href="mailto:{{ site_support_email }}">{{ site_support_email }}</a>. Some records may be retained when required for security or legal obligations.</p>
<h2>Cookies</h2><p>Essential cookies are used for authentication, security, and session continuity. The application does not enable advertising cookies by default.</p>
<h2>Policy updates</h2><p>This policy may change as the service develops. Material revisions will be posted on this page.</p>
</div></div></section>{% include "components/footer.html" %}{% endblock %}
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for url in urls %} <url><loc>{{ url }}</loc></url>
{% endfor %}</urlset>
+10
View File
@@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block title %}Terms of Use | Testpoint{% endblock %}
{% block content %}<section class="info-page"><div class="container info-page__inner"><p class="info-page__eyebrow">Legal</p><h1>Terms of Use</h1><p class="info-page__lead">Rules for using Testpoint and interpreting practice results.</p><div class="info-page__article">
<h2>Practice service</h2><p>Testpoint provides independent preparation materials and practice feedback. It is not an official IELTS examination service, and practice scores do not guarantee a result in an official exam.</p>
<h2>Your account</h2><p>You are responsible for accurate registration information, protecting your password, and activity performed through your account. Notify support if you believe your account has been compromised.</p>
<h2>Acceptable use</h2><p>Do not disrupt the service, attempt unauthorised access, upload malicious files, impersonate another person, scrape restricted content, or reproduce test material without permission.</p>
<h2>Responses and feedback</h2><p>Automatic scoring applies only to supported objective questions. Writing and speaking feedback may require manual review and reflects practice performance rather than an official IELTS band decision.</p>
<h2>Availability</h2><p>We aim to keep the service available and accurate but may perform maintenance, correct content, or suspend features for security and operational reasons.</p>
<h2>Contact</h2><p>Questions about these terms may be sent to <a href="mailto:{{ site_support_email }}">{{ site_support_email }}</a>.</p>
</div></div></section>{% include "components/footer.html" %}{% endblock %}