diff --git a/assets/pasted-20251210-161034-71c4025f.jpg b/assets/pasted-20251210-161034-71c4025f.jpg new file mode 100644 index 0000000..9827864 Binary files /dev/null and b/assets/pasted-20251210-161034-71c4025f.jpg differ diff --git a/assets/vm-shot-2025-12-10T16-08-25-291Z.jpg b/assets/vm-shot-2025-12-10T16-08-25-291Z.jpg new file mode 100644 index 0000000..9827864 Binary files /dev/null and b/assets/vm-shot-2025-12-10T16-08-25-291Z.jpg differ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index daefbd7..ce6a9f5 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 291d043..ea31784 100644 --- a/config/settings.py +++ b/config/settings.py @@ -180,3 +180,8 @@ if EMAIL_USE_SSL: # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# Authentication +LOGIN_URL = 'core:login' +LOGIN_REDIRECT_URL = 'core:student_dashboard' +LOGOUT_REDIRECT_URL = 'core:login' diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 142d461..4392b75 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index cf2ae9f..281c9d2 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/templates/core/article_detail.html b/core/templates/core/article_detail.html index 8820990..99a45ea 100644 --- a/core/templates/core/article_detail.html +++ b/core/templates/core/article_detail.html @@ -1,13 +1,68 @@ -{% extends 'base.html' %} - -{% block title %}{{ article.title }}{% endblock %} +{% extends "base.html" %} +{% load static %} {% block content %} -
-

{{ article.title }}

-

Published on {{ article.created_at|date:"F d, Y" }}

-
-
+ + + + +
+
+

{{ article.title }}

+

Published on {{ article.created_at|date:"F d, Y" }}

+
+
{{ article.content|safe }}
diff --git a/core/templates/core/assignment_detail.html b/core/templates/core/assignment_detail.html index 9caae81..5e42994 100644 --- a/core/templates/core/assignment_detail.html +++ b/core/templates/core/assignment_detail.html @@ -1,63 +1,133 @@ -{% extends 'base.html' %} +{% extends "base.html" %} +{% load static %} {% block content %} -
-

{{ assignment.title }}

-

{{ assignment.description }}

-
-

Exercises

+ + + + +
+
+

{{ assignment.title }}

+

{{ assignment.description }}

+
+ {% for exercise in assignment.exercises.all %} -
-
-

{{ exercise.question }}

-
- {% csrf_token %} - -
- -
- - - -
- +{% endblock %} {% block extra_js %} {% endblock %} - -{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index 7a5d234..464e0ab 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -13,7 +13,7 @@

Welcome to CodeCraft

The ultimate platform for coding challenges and assignments. Sharpen your skills, compete with peers, and get feedback from instructors.

diff --git a/core/templates/core/login.html b/core/templates/core/login.html new file mode 100644 index 0000000..15a0c35 --- /dev/null +++ b/core/templates/core/login.html @@ -0,0 +1,93 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} + + + +{% endblock %} diff --git a/core/templates/core/student_dashboard.html b/core/templates/core/student_dashboard.html index dc1d234..b41021b 100644 --- a/core/templates/core/student_dashboard.html +++ b/core/templates/core/student_dashboard.html @@ -1,17 +1,104 @@ -{% extends 'base.html' %} +{% extends "base.html" %} +{% load static %} {% block content %} -
-

Student Dashboard

-
+ + + + +
+
+

Welcome, {{ user.username }}!

+

Here are your current assignments. Stay on track!

+
+ +
{% for assignment in assignments %} - -
-
{{ assignment.title }}
- Due: {{ assignment.due_date }} -
-

{{ assignment.description }}

-
+
+
+
{{ assignment.title }}
+

Due: {{ assignment.due_date|date:"M d, Y" }}

+

{{ assignment.description|truncatewords:15 }}

+ View Details +
+
+ {% empty %} +
+
+ +

You're all caught up!

+

No pending assignments at the moment. Great job!

+
+
{% endfor %}
diff --git a/core/urls.py b/core/urls.py index ee00cbc..95f9a1b 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,11 +1,21 @@ from django.urls import path -from .views import home, student_dashboard, assignment_detail, get_hint, call_teacher +from .views import ( + home, + student_dashboard, + assignment_detail, + get_hint, + call_teacher, + login_view, + logout_view, +) app_name = "core" urlpatterns = [ path("", home, name="home"), + path("login/", login_view, name="login"), + path("logout/", logout_view, name="logout"), path("dashboard/", student_dashboard, name="student_dashboard"), path("assignment//", assignment_detail, name="assignment_detail"), path("hint//", get_hint, name="get_hint"), diff --git a/core/views.py b/core/views.py index 9a8f107..37783e1 100644 --- a/core/views.py +++ b/core/views.py @@ -3,14 +3,18 @@ import platform import random from django import get_version as django_version +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.decorators import login_required +from django.contrib.auth.forms import AuthenticationForm from django.http import JsonResponse -from django.shortcuts import render, get_object_or_404 +from django.shortcuts import render, get_object_or_404, redirect from django.utils import timezone from .models import Assignment, Exercise, Hint, Submission + def home(request): - """Render the landing screen with loader and environment details.""" + """Render the landing screen with loader and environment details.""" host_name = request.get_host().lower() agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" now = timezone.now() @@ -27,10 +31,34 @@ def home(request): } return render(request, "core/index.html", context) + +def login_view(request): + if request.method == 'POST': + form = AuthenticationForm(request, data=request.POST) + if form.is_valid(): + username = form.cleaned_data.get('username') + password = form.cleaned_data.get('password') + user = authenticate(username=username, password=password) + if user is not None: + login(request, user) + return redirect('core:student_dashboard') + else: + form = AuthenticationForm() + return render(request, 'core/login.html', {'form': form}) + + +def logout_view(request): + logout(request) + return redirect('core:login') + + +@login_required def student_dashboard(request): assignments = Assignment.objects.all() return render(request, "core/student_dashboard.html", {"assignments": assignments}) + +@login_required def assignment_detail(request, assignment_id): assignment = get_object_or_404(Assignment, pk=assignment_id) if request.method == 'POST': @@ -56,6 +84,8 @@ def assignment_detail(request, assignment_id): return render(request, "core/assignment_detail.html", {"assignment": assignment}) + +@login_required def get_hint(request, exercise_id): exercise = get_object_or_404(Exercise, pk=exercise_id) hints = exercise.hints.all() @@ -64,6 +94,9 @@ def get_hint(request, exercise_id): return JsonResponse({'hint': hint.hint_text}) return JsonResponse({'hint': 'No hints available for this exercise.'}) + +@login_required def call_teacher(request): # In a real application, this would trigger a notification to the teacher return JsonResponse({'message': 'A teacher has been called to help you.'}) + diff --git a/create_dummy_data.py b/create_dummy_data.py new file mode 100644 index 0000000..bd61aa0 --- /dev/null +++ b/create_dummy_data.py @@ -0,0 +1,62 @@ +from django.contrib.auth.models import User +from core.models import Assignment, Exercise, Hint +from datetime import datetime, timedelta + +# Create a student user +student, created = User.objects.get_or_create(username='student', defaults={'first_name': 'John', 'last_name': 'Doe'}) +if created: + student.set_password('password') + student.save() + print("Student user created.") +else: + print("Student user already exists.") + +# Create an assignment +assignment, created = Assignment.objects.get_or_create( + title='Math Homework 1', + defaults={ + 'description': 'Complete the following exercises.', + 'due_date': datetime.now() + timedelta(days=7) + } +) +if created: + print("Assignment created.") +else: + print("Assignment already exists.") + + +# Create exercises +exercise1, created = Exercise.objects.get_or_create( + assignment=assignment, + question='What is 2 + 2?', + defaults={'answer': '4'} +) +if created: + print("Exercise 1 created.") +else: + print("Exercise 1 already exists.") + + +exercise2, created = Exercise.objects.get_or_create( + assignment=assignment, + question='What is 5 * 5?', + defaults={'answer': '25'} +) +if created: + print("Exercise 2 created.") +else: + print("Exercise 2 already exists.") + + +# Create a hint +hint, created = Hint.objects.get_or_create( + exercise=exercise1, + defaults={'hint_text': 'The answer is a single digit number.'} +) + +if created: + print("Hint created.") +else: + print("Hint already exists.") + +print("Dummy data creation complete.") diff --git a/static/css/custom.css b/static/css/custom.css index 14aa7b7..aec0ec4 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -3,6 +3,7 @@ --primary-color: #4285F4; --secondary-color: #FBBC05; --accent-color: #34A853; + --tertiary-color: #EA4335; --neutral-light-gray: #F8F9FA; --neutral-dark-text: #202124; --font-headings: 'Poppins', sans-serif; @@ -12,7 +13,7 @@ body { font-family: var(--font-body); color: var(--neutral-dark-text); - background-color: #fff; + background-color: var(--neutral-light-gray); line-height: 1.6; } @@ -53,47 +54,347 @@ h1, h2, h3, h4, h5, h6 { border-color: #F9A825; } -.hero { - padding: 100px 0; - background: linear-gradient(135deg, #E3F2FD, #FFFFFF); - text-align: center; - position: relative; - overflow: hidden; +.btn-tertiary { + background-color: var(--tertiary-color); + border-color: var(--tertiary-color); + color: #fff; } -.hero h1 { - font-size: 3.5rem; +.btn-tertiary:hover { + background-color: #D93025; + border-color: #D93025; +} + +.login-container { + display: grid; + grid-template-columns: 1fr 1fr; + min-height: 100vh; + width: 100%; +} + +.login-branding { + background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + text-align: center; +} + +.login-branding-content h1 { + font-size: 2.8rem; font-weight: 700; margin-bottom: 20px; +} + +.login-branding-content p { + font-size: 1.1rem; + max-width: 400px; + margin: 0 auto; +} + +.login-form-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + background-color: #fff; +} + +.login-form-container { + max-width: 400px; + width: 100%; +} + +.login-form-container h2 { + font-size: 2rem; + font-weight: 700; + margin-bottom: 10px; color: var(--neutral-dark-text); } -.hero .lead { - font-size: 1.25rem; - margin-bottom: 40px; - color: #5f6368; +.login-form { + margin-top: 30px; } -.features-section { - padding: 80px 0; +.form-group { + margin-bottom: 20px; } -.feature-card { - background-color: var(--neutral-light-gray); - border: none; - border-radius: 12px; - padding: 30px; +.form-group label { + display: block; + font-weight: 600; + margin-bottom: 8px; + color: var(--neutral-dark-text); +} + +.form-group input, .form-group textarea { + width: 100%; + padding: 12px 15px; + border: 1px solid #ccc; + border-radius: 8px; + font-size: 1rem; + transition: border-color 0.3s ease; +} + +.form-group input:focus, .form-group textarea:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(66, 133, 244, 0.1); +} + +.btn-block { + width: 100%; + padding: 15px; + font-size: 1rem; +} + +.login-form-footer { text-align: center; + margin-top: 20px; + font-size: 0.9rem; +} + +/* Student Dashboard */ +.dashboard-container { + display: flex; + min-height: 100vh; +} + +.sidebar { + width: 260px; + background-color: var(--neutral-dark-text); + color: #fff; + display: flex; + flex-direction: column; + position: fixed; + height: 100%; +} + +.sidebar-header { + padding: 25px 20px; + border-bottom: 1px solid rgba(255,255,255,0.1); +} + +.sidebar-header h3 { + color: #fff; + font-weight: 600; +} + +.sidebar-nav { + flex-grow: 1; +} + +.sidebar-nav a { + display: block; + padding: 15px 20px; + color: #fff; + text-decoration: none; + font-weight: 500; + transition: background-color 0.3s ease; +} + +.sidebar-nav a:hover, .sidebar-nav a.active { + background-color: rgba(255,255,255,0.1); +} + +.sidebar-nav a i { + margin-right: 15px; + width: 20px; + text-align: center; +} + +.sidebar-footer { + padding: 20px; + border-top: 1px solid rgba(255,255,255,0.1); +} + +.logout-btn { + display: block; + text-align: center; + background-color: var(--primary-color); + color: #fff; + padding: 10px; + border-radius: 8px; + text-decoration: none; + font-weight: 600; + transition: background-color 0.3s ease; +} + +.logout-btn:hover { + background-color: #3367D6; +} + +.main-content { + margin-left: 260px; + flex-grow: 1; + background-color: var(--neutral-light-gray); + padding: 30px; +} + +.header { + background-color: #fff; + padding: 30px; + border-radius: 12px; + margin-bottom: 30px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05); +} + +.header h2 { + font-size: 2.2rem; + margin-bottom: 5px; +} + +.assignments-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 25px; +} + +.assignment-card { + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05); + display: flex; + flex-direction: column; transition: transform 0.3s ease, box-shadow 0.3s ease; } -.feature-card:hover { - transform: translateY(-10px); - box-shadow: 0 10px 20px rgba(0,0,0,0.05); +.assignment-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 20px rgba(0,0,0,0.08); } -.feature-card .icon { - font-size: 3rem; - color: var(--primary-color); +.assignment-card-header { + padding: 20px; + border-bottom: 1px solid #eee; +} + +.assignment-title { + font-size: 1.2rem; + font-weight: 600; + margin-bottom: 0; +} + +.assignment-due-date { + font-size: 0.85rem; + color: #5f6368; +} + +.assignment-card-body { + padding: 20px; + flex-grow: 1; +} + +.assignment-card-footer { + padding: 0 20px 20px; +} + +.empty-state { + grid-column: 1 / -1; + text-align: center; + padding: 60px 20px; + background-color: #fff; + border-radius: 12px; +} + +.empty-state i { + font-size: 4rem; + color: var(--accent-color); margin-bottom: 20px; +} + +.empty-state h4 { + font-size: 1.5rem; + margin-bottom: 10px; +} + +/* Assignment Detail */ +.exercises-section { + max-width: 900px; +} + +.exercise-list { + display: flex; + flex-direction: column; + gap: 30px; +} + +.exercise-card { + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05); +} + +.exercise-card-header { + padding: 20px; + border-bottom: 1px solid #eee; + background-color: #fafafa; +} + +.exercise-card-header h4 { + margin: 0; + font-size: 1.3rem; +} + +.exercise-card-body { + padding: 25px; +} + +.exercise-question { + font-size: 1.1rem; + margin-bottom: 25px; +} + +.exercise-actions { + margin-top: 20px; + display: flex; + gap: 15px; + flex-wrap: wrap; +} + +.hint-container .alert { + margin-top: 20px; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .login-container { + grid-template-columns: 1fr; + } + + .login-branding { + display: none; + } + + .login-form-wrapper { + padding: 20px; + } + + .sidebar { + position: static; + width: 100%; + height: auto; + flex-direction: row; + align-items: center; + } + + .main-content { + margin-left: 0; + } + + .sidebar-header { + display: none; + } + + .sidebar-nav { + display: flex; + flex-grow: 1; + justify-content: space-around; + } + + .sidebar-footer { + display: none; /* Or adjust to a dropdown menu */ + } } \ No newline at end of file diff --git a/staticfiles/css/custom.css b/staticfiles/css/custom.css index 14aa7b7..aec0ec4 100644 --- a/staticfiles/css/custom.css +++ b/staticfiles/css/custom.css @@ -3,6 +3,7 @@ --primary-color: #4285F4; --secondary-color: #FBBC05; --accent-color: #34A853; + --tertiary-color: #EA4335; --neutral-light-gray: #F8F9FA; --neutral-dark-text: #202124; --font-headings: 'Poppins', sans-serif; @@ -12,7 +13,7 @@ body { font-family: var(--font-body); color: var(--neutral-dark-text); - background-color: #fff; + background-color: var(--neutral-light-gray); line-height: 1.6; } @@ -53,47 +54,347 @@ h1, h2, h3, h4, h5, h6 { border-color: #F9A825; } -.hero { - padding: 100px 0; - background: linear-gradient(135deg, #E3F2FD, #FFFFFF); - text-align: center; - position: relative; - overflow: hidden; +.btn-tertiary { + background-color: var(--tertiary-color); + border-color: var(--tertiary-color); + color: #fff; } -.hero h1 { - font-size: 3.5rem; +.btn-tertiary:hover { + background-color: #D93025; + border-color: #D93025; +} + +.login-container { + display: grid; + grid-template-columns: 1fr 1fr; + min-height: 100vh; + width: 100%; +} + +.login-branding { + background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + text-align: center; +} + +.login-branding-content h1 { + font-size: 2.8rem; font-weight: 700; margin-bottom: 20px; +} + +.login-branding-content p { + font-size: 1.1rem; + max-width: 400px; + margin: 0 auto; +} + +.login-form-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + background-color: #fff; +} + +.login-form-container { + max-width: 400px; + width: 100%; +} + +.login-form-container h2 { + font-size: 2rem; + font-weight: 700; + margin-bottom: 10px; color: var(--neutral-dark-text); } -.hero .lead { - font-size: 1.25rem; - margin-bottom: 40px; - color: #5f6368; +.login-form { + margin-top: 30px; } -.features-section { - padding: 80px 0; +.form-group { + margin-bottom: 20px; } -.feature-card { - background-color: var(--neutral-light-gray); - border: none; - border-radius: 12px; - padding: 30px; +.form-group label { + display: block; + font-weight: 600; + margin-bottom: 8px; + color: var(--neutral-dark-text); +} + +.form-group input, .form-group textarea { + width: 100%; + padding: 12px 15px; + border: 1px solid #ccc; + border-radius: 8px; + font-size: 1rem; + transition: border-color 0.3s ease; +} + +.form-group input:focus, .form-group textarea:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(66, 133, 244, 0.1); +} + +.btn-block { + width: 100%; + padding: 15px; + font-size: 1rem; +} + +.login-form-footer { text-align: center; + margin-top: 20px; + font-size: 0.9rem; +} + +/* Student Dashboard */ +.dashboard-container { + display: flex; + min-height: 100vh; +} + +.sidebar { + width: 260px; + background-color: var(--neutral-dark-text); + color: #fff; + display: flex; + flex-direction: column; + position: fixed; + height: 100%; +} + +.sidebar-header { + padding: 25px 20px; + border-bottom: 1px solid rgba(255,255,255,0.1); +} + +.sidebar-header h3 { + color: #fff; + font-weight: 600; +} + +.sidebar-nav { + flex-grow: 1; +} + +.sidebar-nav a { + display: block; + padding: 15px 20px; + color: #fff; + text-decoration: none; + font-weight: 500; + transition: background-color 0.3s ease; +} + +.sidebar-nav a:hover, .sidebar-nav a.active { + background-color: rgba(255,255,255,0.1); +} + +.sidebar-nav a i { + margin-right: 15px; + width: 20px; + text-align: center; +} + +.sidebar-footer { + padding: 20px; + border-top: 1px solid rgba(255,255,255,0.1); +} + +.logout-btn { + display: block; + text-align: center; + background-color: var(--primary-color); + color: #fff; + padding: 10px; + border-radius: 8px; + text-decoration: none; + font-weight: 600; + transition: background-color 0.3s ease; +} + +.logout-btn:hover { + background-color: #3367D6; +} + +.main-content { + margin-left: 260px; + flex-grow: 1; + background-color: var(--neutral-light-gray); + padding: 30px; +} + +.header { + background-color: #fff; + padding: 30px; + border-radius: 12px; + margin-bottom: 30px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05); +} + +.header h2 { + font-size: 2.2rem; + margin-bottom: 5px; +} + +.assignments-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 25px; +} + +.assignment-card { + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05); + display: flex; + flex-direction: column; transition: transform 0.3s ease, box-shadow 0.3s ease; } -.feature-card:hover { - transform: translateY(-10px); - box-shadow: 0 10px 20px rgba(0,0,0,0.05); +.assignment-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 20px rgba(0,0,0,0.08); } -.feature-card .icon { - font-size: 3rem; - color: var(--primary-color); +.assignment-card-header { + padding: 20px; + border-bottom: 1px solid #eee; +} + +.assignment-title { + font-size: 1.2rem; + font-weight: 600; + margin-bottom: 0; +} + +.assignment-due-date { + font-size: 0.85rem; + color: #5f6368; +} + +.assignment-card-body { + padding: 20px; + flex-grow: 1; +} + +.assignment-card-footer { + padding: 0 20px 20px; +} + +.empty-state { + grid-column: 1 / -1; + text-align: center; + padding: 60px 20px; + background-color: #fff; + border-radius: 12px; +} + +.empty-state i { + font-size: 4rem; + color: var(--accent-color); margin-bottom: 20px; +} + +.empty-state h4 { + font-size: 1.5rem; + margin-bottom: 10px; +} + +/* Assignment Detail */ +.exercises-section { + max-width: 900px; +} + +.exercise-list { + display: flex; + flex-direction: column; + gap: 30px; +} + +.exercise-card { + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 10px rgba(0,0,0,0.05); +} + +.exercise-card-header { + padding: 20px; + border-bottom: 1px solid #eee; + background-color: #fafafa; +} + +.exercise-card-header h4 { + margin: 0; + font-size: 1.3rem; +} + +.exercise-card-body { + padding: 25px; +} + +.exercise-question { + font-size: 1.1rem; + margin-bottom: 25px; +} + +.exercise-actions { + margin-top: 20px; + display: flex; + gap: 15px; + flex-wrap: wrap; +} + +.hint-container .alert { + margin-top: 20px; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .login-container { + grid-template-columns: 1fr; + } + + .login-branding { + display: none; + } + + .login-form-wrapper { + padding: 20px; + } + + .sidebar { + position: static; + width: 100%; + height: auto; + flex-direction: row; + align-items: center; + } + + .main-content { + margin-left: 0; + } + + .sidebar-header { + display: none; + } + + .sidebar-nav { + display: flex; + flex-grow: 1; + justify-content: space-around; + } + + .sidebar-footer { + display: none; /* Or adjust to a dropdown menu */ + } } \ No newline at end of file diff --git a/staticfiles/pasted-20251210-161034-71c4025f.jpg b/staticfiles/pasted-20251210-161034-71c4025f.jpg new file mode 100644 index 0000000..9827864 Binary files /dev/null and b/staticfiles/pasted-20251210-161034-71c4025f.jpg differ diff --git a/staticfiles/vm-shot-2025-12-10T16-08-25-291Z.jpg b/staticfiles/vm-shot-2025-12-10T16-08-25-291Z.jpg new file mode 100644 index 0000000..9827864 Binary files /dev/null and b/staticfiles/vm-shot-2025-12-10T16-08-25-291Z.jpg differ