feat: admin panel enrichment
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
|
||||
from allauth.account.adapter import DefaultAccountAdapter
|
||||
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
||||
from django.urls import reverse
|
||||
|
||||
from .models import ClientProfile
|
||||
|
||||
|
||||
class TecvicoAccountAdapter(DefaultAccountAdapter):
|
||||
def get_login_redirect_url(self, request):
|
||||
return reverse("projects:dashboard")
|
||||
|
||||
def get_signup_redirect_url(self, request):
|
||||
return reverse("projects:dashboard")
|
||||
|
||||
|
||||
class TecvicoSocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
def save_user(self, request, sociallogin, form=None):
|
||||
user = super().save_user(request, sociallogin, form)
|
||||
self._apply_profile_names(user, sociallogin)
|
||||
user.save()
|
||||
ClientProfile.objects.get_or_create(user=user)
|
||||
return user
|
||||
|
||||
def _apply_profile_names(self, user, sociallogin):
|
||||
extra_data = sociallogin.account.extra_data or {}
|
||||
if sociallogin.account.provider == "google":
|
||||
if not user.first_name:
|
||||
user.first_name = extra_data.get("given_name", "")
|
||||
if not user.last_name:
|
||||
user.last_name = extra_data.get("family_name", "")
|
||||
return
|
||||
if sociallogin.account.provider == "github" and not user.first_name:
|
||||
name = (extra_data.get("name") or "").strip()
|
||||
if name:
|
||||
parts = name.split(" ", 1)
|
||||
user.first_name = parts[0]
|
||||
user.last_name = parts[1] if len(parts) > 1 else ""
|
||||
@@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import ClientProfile
|
||||
|
||||
|
||||
@admin.register(ClientProfile)
|
||||
class ClientProfileAdmin(admin.ModelAdmin):
|
||||
list_display = ("user", "company", "phone", "timezone", "updated_at")
|
||||
search_fields = ("user__username", "user__email", "user__first_name", "user__last_name", "company")
|
||||
list_filter = ("timezone",)
|
||||
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
||||
def ensure_site(sender, **kwargs):
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
domain = os.environ.get("SITE_DOMAIN", "localhost:8000")
|
||||
Site.objects.update_or_create(
|
||||
id=getattr(settings, "SITE_ID", 1),
|
||||
defaults={"domain": domain, "name": "Tecvico"},
|
||||
)
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "apps.accounts"
|
||||
label = "accounts"
|
||||
|
||||
def ready(self):
|
||||
from . import signals # noqa: F401
|
||||
|
||||
post_migrate.connect(ensure_site, sender=self)
|
||||
@@ -0,0 +1,12 @@
|
||||
from django.conf import settings
|
||||
|
||||
from .social import enabled_social_providers
|
||||
|
||||
|
||||
def social_auth(request):
|
||||
providers = enabled_social_providers()
|
||||
return {
|
||||
"social_providers": providers,
|
||||
"social_auth_enabled": bool(providers),
|
||||
"site_domain": settings.SITE_DOMAIN,
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import ClientProfile
|
||||
|
||||
|
||||
class SignUpForm(UserCreationForm):
|
||||
first_name = forms.CharField(max_length=150, required=True)
|
||||
last_name = forms.CharField(max_length=150, required=True)
|
||||
email = forms.EmailField(required=True)
|
||||
company = forms.CharField(max_length=200, required=False)
|
||||
phone = forms.CharField(max_length=50, required=False)
|
||||
timezone = forms.ChoiceField(choices=ClientProfile.TIMEZONE_CHOICES, required=True)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "first_name", "last_name", "email", "password1", "password2")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for field_name in ("username", "first_name", "last_name", "email", "company", "phone", "timezone"):
|
||||
if field_name in self.fields:
|
||||
self.fields[field_name].widget.attrs.setdefault("class", "form-control")
|
||||
for field_name in ("password1", "password2"):
|
||||
self.fields[field_name].widget.attrs.setdefault("class", "form-control")
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data["email"].strip().lower()
|
||||
if User.objects.filter(email__iexact=email).exists():
|
||||
raise forms.ValidationError("An account with this email already exists.")
|
||||
return email
|
||||
|
||||
def save(self, commit=True):
|
||||
user = super().save(commit=False)
|
||||
user.email = self.cleaned_data["email"]
|
||||
user.first_name = self.cleaned_data["first_name"]
|
||||
user.last_name = self.cleaned_data["last_name"]
|
||||
if commit:
|
||||
user.save()
|
||||
ClientProfile.objects.create(
|
||||
user=user,
|
||||
company=self.cleaned_data.get("company", ""),
|
||||
phone=self.cleaned_data.get("phone", ""),
|
||||
timezone=self.cleaned_data.get("timezone", "UTC"),
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
class ClientLoginForm(AuthenticationForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["username"].widget.attrs.update(
|
||||
{"placeholder": "Username or email", "class": "form-control"}
|
||||
)
|
||||
self.fields["password"].widget.attrs.update(
|
||||
{"placeholder": "Password", "class": "form-control"}
|
||||
)
|
||||
|
||||
|
||||
class ClientProfileForm(forms.ModelForm):
|
||||
first_name = forms.CharField(max_length=150, required=True)
|
||||
last_name = forms.CharField(max_length=150, required=True)
|
||||
email = forms.EmailField(required=True)
|
||||
|
||||
class Meta:
|
||||
model = ClientProfile
|
||||
fields = ("company", "phone", "timezone")
|
||||
widgets = {
|
||||
"company": forms.TextInput(attrs={"class": "form-control"}),
|
||||
"phone": forms.TextInput(attrs={"class": "form-control"}),
|
||||
"timezone": forms.Select(attrs={"class": "form-control"}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop("user")
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["first_name"].initial = self.user.first_name
|
||||
self.fields["last_name"].initial = self.user.last_name
|
||||
self.fields["email"].initial = self.user.email
|
||||
for name in ("first_name", "last_name", "email"):
|
||||
self.fields[name].widget.attrs.setdefault("class", "form-control")
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data["email"].strip().lower()
|
||||
if (
|
||||
User.objects.filter(email__iexact=email)
|
||||
.exclude(pk=self.user.pk)
|
||||
.exists()
|
||||
):
|
||||
raise forms.ValidationError("An account with this email already exists.")
|
||||
return email
|
||||
|
||||
def save(self, commit=True):
|
||||
profile = super().save(commit=False)
|
||||
self.user.first_name = self.cleaned_data["first_name"]
|
||||
self.user.last_name = self.cleaned_data["last_name"]
|
||||
self.user.email = self.cleaned_data["email"]
|
||||
if commit:
|
||||
self.user.save()
|
||||
profile.save()
|
||||
return profile
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-09 07:14
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ClientProfile',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('company', models.CharField(blank=True, max_length=200)),
|
||||
('phone', models.CharField(blank=True, max_length=50)),
|
||||
('timezone', models.CharField(choices=[('UTC', 'UTC'), ('America/New_York', 'America/New York'), ('America/Chicago', 'America/Chicago'), ('America/Denver', 'America/Denver'), ('America/Los_Angeles', 'America/Los Angeles'), ('Europe/London', 'Europe/London'), ('Europe/Berlin', 'Europe/Berlin'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Australia/Sydney', 'Australia/Sydney')], default='UTC', max_length=64)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='client_profile', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Client Profile',
|
||||
'verbose_name_plural': 'Client Profiles',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,47 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
|
||||
class ClientProfile(models.Model):
|
||||
TIMEZONE_CHOICES = [
|
||||
("UTC", "UTC"),
|
||||
("America/New_York", "America/New York"),
|
||||
("America/Chicago", "America/Chicago"),
|
||||
("America/Denver", "America/Denver"),
|
||||
("America/Los_Angeles", "America/Los Angeles"),
|
||||
("Europe/London", "Europe/London"),
|
||||
("Europe/Berlin", "Europe/Berlin"),
|
||||
("Asia/Dubai", "Asia/Dubai"),
|
||||
("Asia/Tehran", "Asia/Tehran"),
|
||||
("Asia/Tokyo", "Asia/Tokyo"),
|
||||
("Australia/Sydney", "Australia/Sydney"),
|
||||
]
|
||||
|
||||
user = models.OneToOneField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="client_profile",
|
||||
)
|
||||
company = models.CharField(max_length=200, blank=True)
|
||||
phone = models.CharField(max_length=50, blank=True)
|
||||
timezone = models.CharField(
|
||||
max_length=64,
|
||||
choices=TIMEZONE_CHOICES,
|
||||
default="UTC",
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Client Profile"
|
||||
verbose_name_plural = "Client Profiles"
|
||||
|
||||
def __str__(self):
|
||||
return self.user.get_full_name() or self.user.email or str(self.user.pk)
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
full_name = self.user.get_full_name().strip()
|
||||
if full_name:
|
||||
return full_name
|
||||
return self.user.email
|
||||
@@ -0,0 +1,10 @@
|
||||
from django.dispatch import receiver
|
||||
|
||||
from allauth.account.signals import user_signed_up
|
||||
|
||||
from .models import ClientProfile
|
||||
|
||||
|
||||
@receiver(user_signed_up)
|
||||
def ensure_client_profile(sender, request, user, **kwargs):
|
||||
ClientProfile.objects.get_or_create(user=user)
|
||||
@@ -0,0 +1,15 @@
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def enabled_social_providers():
|
||||
providers = []
|
||||
configured = getattr(settings, "SOCIALACCOUNT_PROVIDERS", {})
|
||||
labels = {
|
||||
"google": "Google",
|
||||
"github": "GitHub",
|
||||
}
|
||||
for provider_id, label in labels.items():
|
||||
app = configured.get(provider_id, {}).get("APP", {})
|
||||
if app.get("client_id") and app.get("secret"):
|
||||
providers.append({"id": provider_id, "label": label})
|
||||
return providers
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.accounts.forms import SignUpForm
|
||||
from apps.accounts.models import ClientProfile
|
||||
|
||||
|
||||
class SignUpFormTest(TestCase):
|
||||
def test_signup_creates_user_and_profile(self):
|
||||
form = SignUpForm(
|
||||
data={
|
||||
"username": "client1",
|
||||
"first_name": "Ada",
|
||||
"last_name": "Lovelace",
|
||||
"email": "ada@example.com",
|
||||
"company": "Analytical Engines",
|
||||
"phone": "+1 555 0100",
|
||||
"timezone": "UTC",
|
||||
"password1": "Str0ngPass!word",
|
||||
"password2": "Str0ngPass!word",
|
||||
}
|
||||
)
|
||||
self.assertTrue(form.is_valid(), form.errors)
|
||||
user = form.save()
|
||||
self.assertEqual(user.email, "ada@example.com")
|
||||
profile = ClientProfile.objects.get(user=user)
|
||||
self.assertEqual(profile.company, "Analytical Engines")
|
||||
self.assertEqual(profile.timezone, "UTC")
|
||||
|
||||
|
||||
class AccountViewsTest(TestCase):
|
||||
def test_signup_page_renders(self):
|
||||
response = self.client.get(reverse("accounts:signup"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_signup_creates_account_and_logs_in(self):
|
||||
response = self.client.post(
|
||||
reverse("accounts:signup"),
|
||||
data={
|
||||
"username": "newclient",
|
||||
"first_name": "Grace",
|
||||
"last_name": "Hopper",
|
||||
"email": "grace@example.com",
|
||||
"company": "",
|
||||
"phone": "",
|
||||
"timezone": "UTC",
|
||||
"password1": "Str0ngPass!word",
|
||||
"password2": "Str0ngPass!word",
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, reverse("projects:dashboard"))
|
||||
self.assertTrue(User.objects.filter(username="newclient").exists())
|
||||
|
||||
def test_login_required_for_profile(self):
|
||||
response = self.client.get(reverse("accounts:profile"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn(reverse("accounts:login"), response.url)
|
||||
|
||||
def test_logout_requires_post_and_clears_session(self):
|
||||
self.client.login(username="newclient", password="Str0ngPass!word")
|
||||
response = self.client.get(reverse("accounts:logout"))
|
||||
self.assertEqual(response.status_code, 405)
|
||||
|
||||
response = self.client.post(reverse("accounts:logout"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, reverse("pages:home"))
|
||||
|
||||
response = self.client.get(reverse("projects:dashboard"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn(reverse("accounts:login"), response.url)
|
||||
@@ -0,0 +1,99 @@
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.accounts.adapters import TecvicoSocialAccountAdapter
|
||||
from apps.accounts.social import enabled_social_providers
|
||||
|
||||
|
||||
class SocialAuthHelpersTest(TestCase):
|
||||
@override_settings(
|
||||
SOCIALACCOUNT_PROVIDERS={
|
||||
"google": {"APP": {"client_id": "g-id", "secret": "g-secret"}},
|
||||
"github": {"APP": {"client_id": "", "secret": ""}},
|
||||
}
|
||||
)
|
||||
def test_enabled_social_providers_filters_missing_credentials(self):
|
||||
providers = enabled_social_providers()
|
||||
self.assertEqual(len(providers), 1)
|
||||
self.assertEqual(providers[0]["id"], "google")
|
||||
|
||||
|
||||
class SocialAuthViewsTest(TestCase):
|
||||
@override_settings(
|
||||
SOCIALACCOUNT_PROVIDERS={
|
||||
"google": {"APP": {"client_id": "g-id", "secret": "g-secret"}},
|
||||
"github": {"APP": {"client_id": "gh-id", "secret": "gh-secret"}},
|
||||
}
|
||||
)
|
||||
def test_login_page_renders_social_buttons(self):
|
||||
response = self.client.get(reverse("accounts:login"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Continue with")
|
||||
self.assertContains(response, "Google")
|
||||
self.assertContains(response, "GitHub")
|
||||
self.assertContains(response, "/accounts/google/login/")
|
||||
|
||||
@override_settings(
|
||||
SOCIALACCOUNT_PROVIDERS={
|
||||
"google": {"APP": {"client_id": "", "secret": ""}},
|
||||
"github": {"APP": {"client_id": "", "secret": ""}},
|
||||
}
|
||||
)
|
||||
def test_login_page_hides_social_buttons_without_credentials(self):
|
||||
response = self.client.get(reverse("accounts:login"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(response, "Continue with")
|
||||
|
||||
@override_settings(
|
||||
SOCIALACCOUNT_PROVIDERS={
|
||||
"google": {"APP": {"client_id": "g-id", "secret": "g-secret"}},
|
||||
"github": {"APP": {"client_id": "gh-id", "secret": "gh-secret"}},
|
||||
}
|
||||
)
|
||||
def test_signup_page_renders_social_buttons(self):
|
||||
response = self.client.get(reverse("accounts:signup"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Google")
|
||||
self.assertContains(response, "GitHub")
|
||||
|
||||
@override_settings(
|
||||
SOCIALACCOUNT_PROVIDERS={
|
||||
"google": {"APP": {"client_id": "g-id", "secret": "g-secret"}},
|
||||
"github": {"APP": {"client_id": "gh-id", "secret": "gh-secret"}},
|
||||
}
|
||||
)
|
||||
def test_google_login_page_uses_portal_styles(self):
|
||||
response = self.client.get(reverse("google_login"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Sign in with Google")
|
||||
self.assertContains(response, "social-oauth-card")
|
||||
self.assertContains(response, "navbar")
|
||||
self.assertContains(response, "Continue to Google")
|
||||
|
||||
|
||||
class TecvicoSocialAccountAdapterTest(TestCase):
|
||||
def test_apply_profile_names_from_google(self):
|
||||
user = User(username="oauth-user", email="oauth@example.com")
|
||||
sociallogin = MagicMock()
|
||||
sociallogin.account.provider = "google"
|
||||
sociallogin.account.extra_data = {
|
||||
"given_name": "OAuth",
|
||||
"family_name": "User",
|
||||
}
|
||||
adapter = TecvicoSocialAccountAdapter()
|
||||
adapter._apply_profile_names(user, sociallogin)
|
||||
self.assertEqual(user.first_name, "OAuth")
|
||||
self.assertEqual(user.last_name, "User")
|
||||
|
||||
def test_apply_profile_names_from_github(self):
|
||||
user = User(username="gh-user", email="gh@example.com")
|
||||
sociallogin = MagicMock()
|
||||
sociallogin.account.provider = "github"
|
||||
sociallogin.account.extra_data = {"name": "Grace Hopper"}
|
||||
adapter = TecvicoSocialAccountAdapter()
|
||||
adapter._apply_profile_names(user, sociallogin)
|
||||
self.assertEqual(user.first_name, "Grace")
|
||||
self.assertEqual(user.last_name, "Hopper")
|
||||
@@ -0,0 +1,45 @@
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = "accounts"
|
||||
|
||||
urlpatterns = [
|
||||
path("signup/", views.SignUpView.as_view(), name="signup"),
|
||||
path("login/", views.ClientLoginView.as_view(), name="login"),
|
||||
path("logout/", views.ClientLogoutView.as_view(), name="logout"),
|
||||
path("profile/", views.ProfileView.as_view(), name="profile"),
|
||||
path(
|
||||
"password-reset/",
|
||||
auth_views.PasswordResetView.as_view(
|
||||
template_name="accounts/password_reset.html",
|
||||
email_template_name="accounts/emails/password_reset_email.txt",
|
||||
subject_template_name="accounts/emails/password_reset_subject.txt",
|
||||
success_url="/accounts/password-reset/done/",
|
||||
),
|
||||
name="password_reset",
|
||||
),
|
||||
path(
|
||||
"password-reset/done/",
|
||||
auth_views.PasswordResetDoneView.as_view(
|
||||
template_name="accounts/password_reset_done.html",
|
||||
),
|
||||
name="password_reset_done",
|
||||
),
|
||||
path(
|
||||
"password-reset/<uidb64>/<token>/",
|
||||
auth_views.PasswordResetConfirmView.as_view(
|
||||
template_name="accounts/password_reset_confirm.html",
|
||||
success_url="/accounts/password-reset/complete/",
|
||||
),
|
||||
name="password_reset_confirm",
|
||||
),
|
||||
path(
|
||||
"password-reset/complete/",
|
||||
auth_views.PasswordResetCompleteView.as_view(
|
||||
template_name="accounts/password_reset_complete.html",
|
||||
),
|
||||
name="password_reset_complete",
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,75 @@
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.views import LoginView, LogoutView
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import CreateView, FormView
|
||||
|
||||
from .forms import ClientLoginForm, ClientProfileForm, SignUpForm
|
||||
from .models import ClientProfile
|
||||
|
||||
|
||||
class SignUpView(CreateView):
|
||||
form_class = SignUpForm
|
||||
template_name = "accounts/signup.html"
|
||||
success_url = reverse_lazy("projects:dashboard")
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated:
|
||||
return self.redirect_authenticated()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def redirect_authenticated(self):
|
||||
from django.shortcuts import redirect
|
||||
|
||||
return redirect("projects:dashboard")
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super().form_valid(form)
|
||||
login(self.request, self.object, backend="django.contrib.auth.backends.ModelBackend")
|
||||
messages.success(self.request, "Welcome! Your client account is ready.")
|
||||
return response
|
||||
|
||||
|
||||
class ClientLoginView(LoginView):
|
||||
form_class = ClientLoginForm
|
||||
template_name = "accounts/login.html"
|
||||
redirect_authenticated_user = True
|
||||
|
||||
def get_success_url(self):
|
||||
return self.request.GET.get("next") or reverse_lazy("projects:dashboard")
|
||||
|
||||
|
||||
class ClientLogoutView(LogoutView):
|
||||
next_page = reverse_lazy("pages:home")
|
||||
http_method_names = ["post", "options"]
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
response = super().dispatch(request, *args, **kwargs)
|
||||
if request.method.lower() == "post":
|
||||
messages.success(request, "You have been logged out.")
|
||||
return response
|
||||
|
||||
|
||||
class ProfileView(LoginRequiredMixin, FormView):
|
||||
form_class = ClientProfileForm
|
||||
template_name = "accounts/profile.html"
|
||||
success_url = reverse_lazy("accounts:profile")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["user"] = self.request.user
|
||||
profile, _ = ClientProfile.objects.get_or_create(user=self.request.user)
|
||||
kwargs["instance"] = profile
|
||||
return kwargs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
profile, _ = ClientProfile.objects.get_or_create(user=self.request.user)
|
||||
ctx["profile"] = profile
|
||||
return ctx
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
messages.success(self.request, "Profile updated.")
|
||||
return super().form_valid(form)
|
||||
Reference in New Issue
Block a user