diff --git a/apps/users/tests/test_auth_views.py b/apps/users/tests/test_auth_views.py index 9e8fedc..f7a334f 100644 --- a/apps/users/tests/test_auth_views.py +++ b/apps/users/tests/test_auth_views.py @@ -22,6 +22,18 @@ def test_signup_page_renders(client): assert b"New Account" in response.content +@pytest.mark.django_db +def test_authenticated_signup_redirects_to_dashboard(client): + """Signed-in users do not need the public signup page.""" + user = CustomUserFactory(email="signed-in@example.com") + client.force_login(user) + + response = client.get(reverse("users:signup")) + + assert response.status_code == 302 + assert response["Location"] == reverse("dashboard:index") + + @pytest.mark.django_db def test_signup_creates_user_and_redirects_to_dashboard(client): """A valid signup creates a user and sends them to the dashboard.""" @@ -149,6 +161,30 @@ def test_login_with_email_redirects_to_dashboard(client): assert response["Location"] == reverse("dashboard:index") +@pytest.mark.django_db +def test_authenticated_login_redirects_to_dashboard(client): + """Signed-in users are sent to the post-login product surface.""" + user = CustomUserFactory(email="already@example.com") + client.force_login(user) + + response = client.get(reverse("users:login")) + + assert response.status_code == 302 + assert response["Location"] == reverse("dashboard:index") + + +@pytest.mark.django_db +def test_logout_redirects_to_home(client): + """Logout returns users to the public landing page.""" + user = CustomUserFactory(email="logout@example.com") + client.force_login(user) + + response = client.post(reverse("users:logout")) + + assert response.status_code == 302 + assert response["Location"] == reverse("home") + + @pytest.mark.django_db def test_profile_requires_login(client): """Anonymous users are redirected before viewing a profile.""" diff --git a/apps/users/urls.py b/apps/users/urls.py index 9b8cf33..f6b7205 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -1,8 +1,8 @@ -"""User account URL routes.""" +"""URL routes for StudyBuddy account workflows.""" from __future__ import annotations -from django.contrib.auth import views as auth_views +from django.contrib.auth.views import LoginView, LogoutView from django.urls import path from apps.users import views @@ -10,8 +10,15 @@ app_name = "users" urlpatterns = [ - path("login/", auth_views.LoginView.as_view(), name="login"), - path("logout/", auth_views.LogoutView.as_view(), name="logout"), - path("profile/", views.profile, name="profile"), path("signup/", views.signup, name="signup"), + path( + "login/", + LoginView.as_view( + redirect_authenticated_user=True, + template_name="users/login.html", + ), + name="login", + ), + path("logout/", LogoutView.as_view(), name="logout"), + path("profile/", views.profile, name="profile"), ] diff --git a/apps/users/views.py b/apps/users/views.py index 92b9ae9..078f1a0 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -12,6 +12,9 @@ def signup(request: HttpRequest) -> HttpResponse: """Register a new user and send them to the dashboard.""" + if request.user.is_authenticated: + return redirect("dashboard:index") + if request.method == "POST": form = UserSignUpForm(request.POST) diff --git a/static/css/theme.css b/static/css/theme.css index d6782ef..8193ebe 100644 --- a/static/css/theme.css +++ b/static/css/theme.css @@ -122,6 +122,10 @@ p { gap: var(--space-md); } +.nav-logout-form { + margin: 0; +} + .nav-links { flex: 1; } @@ -407,6 +411,13 @@ p { text-align: right; } +.profile-pill-list { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: var(--space-sm); +} + .form-card { max-width: 560px; margin: 0 auto; diff --git a/templates/base.html b/templates/base.html index b9bc07e..23454f7 100644 --- a/templates/base.html +++ b/templates/base.html @@ -33,6 +33,10 @@ {% if request.user.is_authenticated %} Profile Dashboard + {% else %} Log In Create Account diff --git a/templates/registration/login.html b/templates/registration/login.html deleted file mode 100644 index e1d5ba2..0000000 --- a/templates/registration/login.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Log In | StudyBuddy{% endblock %} - -{% block content %} -
-
-
- Welcome back -

Log In

-

Return to your StudyBuddy dashboard and continue tracking study momentum.

-
- -
-
-

Account Access

-
- {% csrf_token %} - {{ form.as_p }} -
- - Create Account -
-
-
-
-
-
-{% endblock %} diff --git a/templates/registration/signup.html b/templates/registration/signup.html deleted file mode 100644 index 20e0ecd..0000000 --- a/templates/registration/signup.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Create Account | StudyBuddy{% endblock %} - -{% block content %} -
-
-
- Start tracking -

Create Account

-

Create your StudyBuddy account and start building a dashboard for study sessions, goals, and progress.

-
- -
-
-

New Account

-
- {% csrf_token %} - {{ form.as_p }} -
- - Log In -
-
-
-
-
-
-{% endblock %} diff --git a/templates/users/login.html b/templates/users/login.html new file mode 100644 index 0000000..9e24d9d --- /dev/null +++ b/templates/users/login.html @@ -0,0 +1,54 @@ +{% extends "base.html" %} + +{% block title %}Log In | StudyBuddy{% endblock %} + +{% block content %} +
+
+
+ Welcome back +

Log In

+

Use the email address and password linked to your StudyBuddy account.

+
+ +
+
+

Account Access

+ +
+ {% csrf_token %} + + {% if form.non_field_errors %} +
+ {{ form.non_field_errors }} +
+ {% endif %} + +

+ + {{ form.username }} + {% if form.username.errors %} + {{ form.username.errors }} + {% endif %} +

+ +

+ + {{ form.password }} + {% if form.password.errors %} + {{ form.password.errors }} + {% endif %} +

+ + + +
+ + Create Account +
+
+
+
+
+
+{% endblock %} diff --git a/templates/users/profile.html b/templates/users/profile.html index eb28f9d..21a5922 100644 --- a/templates/users/profile.html +++ b/templates/users/profile.html @@ -33,6 +33,18 @@

Account Details

Username
{{ request.user.username }}
+
+
Roles
+
+ + {% for role in request.user.studybuddy_roles.all %} + {{ role.display_name }} + {% empty %} + No roles assigned yet. + {% endfor %} + +
+