diff --git a/base/components/components.py b/base/components/components.py index 4370d2a0..4116fb45 100644 --- a/base/components/components.py +++ b/base/components/components.py @@ -1,7 +1,7 @@ from typing import Literal from django_components import Component, register -from pydantic import BaseModel +from pydantic import BaseModel, EmailStr from base.main import TAB_VAR, ObjectList from base.pagination import PAGE_VAR, Pagination @@ -107,3 +107,25 @@ def get_template_data(self, args, kwargs, slots, context): "tabs": self.create_all_tabs(object_list), "object_list": object_list, } + + +@register("contact_information_button") +class ContactInformationButton(Component): + MAINTAINER_EMAILS = [ + "antoliny0919@gmail.com", + "wedgemail@gmail.com", + ] + template_file = "contact_information_button.html" + + class Kwargs(BaseModel): + button_text: str + description: str + contact_emails: list[EmailStr] | None = None + + def get_template_data(self, args, kwargs, slots, context): + contact_emails = kwargs.contact_emails or self.MAINTAINER_EMAILS + return { + "button_text": kwargs.button_text, + "description": kwargs.description, + "contact_emails": contact_emails, + } diff --git a/base/components/contact_information_button.html b/base/components/contact_information_button.html new file mode 100644 index 00000000..a62a0efc --- /dev/null +++ b/base/components/contact_information_button.html @@ -0,0 +1,20 @@ +{% if contact_emails %} +
+ +
+ {{ description }} +
+ {% for email in contact_emails %} + {{ email }} + {% endfor %} +
+
+
+{% endif %} diff --git a/base/forms.py b/base/forms.py new file mode 100644 index 00000000..05064cc2 --- /dev/null +++ b/base/forms.py @@ -0,0 +1,9 @@ +from allauth.account.forms import LoginForm + + +class DjangoSnippetsLoginForm(LoginForm): + + def _setup_password_field(self): + super()._setup_password_field() + # Disable allauth's automatically set help_text + self.fields["password"].help_text = None diff --git a/djangosnippets/adapters.py b/djangosnippets/adapters.py index fea9376f..c05104b6 100644 --- a/djangosnippets/adapters.py +++ b/djangosnippets/adapters.py @@ -1,13 +1,6 @@ -from allauth.account.adapter import DefaultAccountAdapter from allauth.socialaccount.adapter import DefaultSocialAccountAdapter -class DjangoSnippetsAccountAdapter(DefaultAccountAdapter): - def is_open_for_signup(self, request): - """Disabling common signup completely""" - return False - - class DjangoSnippetsSocialAccountAdapter(DefaultSocialAccountAdapter): def is_open_for_signup(self, request, sociallogin): """ diff --git a/djangosnippets/settings/base.py b/djangosnippets/settings/base.py index c6fd5ccc..307844f9 100644 --- a/djangosnippets/settings/base.py +++ b/djangosnippets/settings/base.py @@ -74,6 +74,7 @@ def user_url(user): "django_components", "rest_framework", "django_htmx", + "widget_tweaks", ] MIDDLEWARE = ( @@ -137,12 +138,15 @@ def user_url(user): MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage" ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 7 -ACCOUNT_SIGNUP_FIELDS = ["email*", "username*", "password1*", "password2*"] +ACCOUNT_SIGNUP_FIELDS = ["username*", "email*", "password1*", "password2*"] ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https" ACCOUNT_LOGOUT_ON_GET = True ACCOUNT_USERNAME_MIN_LENGTH = 3 -ACCOUNT_ADAPTER = "djangosnippets.adapters.DjangoSnippetsAccountAdapter" +ACCOUNT_FORMS = { + "login": "base.forms.DjangoSnippetsLoginForm", +} +ACCOUNT_SESSION_REMEMBER = True SOCIALACCOUNT_ADAPTER = "djangosnippets.adapters.DjangoSnippetsSocialAccountAdapter" SOCIALACCOUNT_AUTO_SIGNUP = False SOCIALACCOUNT_LOGIN_ON_GET = True diff --git a/djangosnippets/static/img/verification_sent.png b/djangosnippets/static/img/verification_sent.png new file mode 100644 index 00000000..69587399 Binary files /dev/null and b/djangosnippets/static/img/verification_sent.png differ diff --git a/djangosnippets/templates/account/login.html b/djangosnippets/templates/account/login.html index 9491bff0..daf10a39 100644 --- a/djangosnippets/templates/account/login.html +++ b/djangosnippets/templates/account/login.html @@ -1,48 +1,47 @@ -{% extends "account/base.html" %} +{% extends "base.html" %} -{% load i18n %} -{% load account %} -{% load socialaccount %} +{% load static account socialaccount widget_tweaks %} -{% block head_title %}{% trans "Login" %}{% endblock %} - -{% block content_header %}{% trans "Login" %}{% endblock %} +{% block header%}{% endblock %} {% block content %} -

-Please log in with one of the following 3rd party systems or with your existing account. -

- - - -{% include "socialaccount/snippets/login_extra.html" %} -{% endblock %} - -{% block sidebar %} -
- {% csrf_token %} - {% for field in form %} - {% if field.name == 'remember' %} - {{ field }} - {{ field.label_tag }} - {% else %} - {{ field.label_tag }} - {{ field }} - {% endif %} - {% if field.errors %} - {{ field.errors }} - {% endif %} - {% endfor %} - {% if redirect_field_value %} - - {% endif %} -
-
- - Forgotten your password? +
+

Sign In

+

Don't have an account? Sign Up

+ {% include "socialaccount/snippets/login_extra.html" %} + + {% csrf_token %} + {% if form.non_field_errors %} +
    + {% for error in form.non_field_errors %}
  • {{ error }}
  • {% endfor %} +
+ {% endif %} + {% for field in form %} +
+
+ + +
+ {% if field.errors %} + {{ field|add_class:"h-12 text-lg rounded-lg border-red-600 border-2 my-2" }} + {% else %} + {{ field|add_class:"h-12 text-lg rounded-lg border-base-green-400 border-2 my-2" }} + {% endif %} + {% if field.help_text %}
{{ field.help_text|safe }}
{% endif %} + {% if field.errors %}
    {% for error in field.errors %}
  • {{ error }}
  • {% endfor %}
{% endif %} +
+ {% endfor %} + + + Forgotten your password? +
+
+ OR +
-
- +
    + {% include "socialaccount/snippets/provider_list.html" with process="login" %} +
+
{% endblock %} +{% block footer %}{% endblock %} \ No newline at end of file diff --git a/djangosnippets/templates/account/password_reset.html b/djangosnippets/templates/account/password_reset.html index 46503470..ce1e6501 100644 --- a/djangosnippets/templates/account/password_reset.html +++ b/djangosnippets/templates/account/password_reset.html @@ -1,27 +1,43 @@ {% extends "account/base.html" %} -{% load i18n %} -{% load account %} +{% load widget_tweaks %} -{% block head_title %}{% trans "Reset password" %}{% endblock %} +{% block head_title %}Reset password{% endblock %} -{% block content_header %}{% trans "Reset password" %}{% endblock %} +{% block header %}{% endblock %} {% block content %} {% if user.is_authenticated %} {% include "account/snippets/already_logged_in.html" %} {% endif %} -

{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

- -
{% csrf_token %} - {{ form.as_p }} - -
-

{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}

-{% endblock %} - -{% block extra_scripts %} - +
+ {% include "socialaccount/snippets/login_extra.html" %} +

Forgotten your password?

+

Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it.

+
+ {% csrf_token %} + {% if form.non_field_errors %} +
    + {% for error in form.non_field_errors %}
  • {{ error }}
  • {% endfor %} +
+ {% endif %} + {% for field in form %} +
+
+ + +
+ {% if field.errors %} + {{ field|add_class:"h-12 text-lg rounded-lg border-red-600 border-2 my-2" }} + {% else %} + {{ field|add_class:"h-12 text-lg rounded-lg border-base-green-400 border-2 my-2" }} + {% endif %} + {% if field.help_text %}
{{ field.help_text|safe }}
{% endif %} + {% if field.errors %}
    {% for error in field.errors %}
  • {{ error }}
  • {% endfor %}
{% endif %} +
+ {% endfor %} + +
+
{% endblock %} +{% block footer %}{% endblock %} diff --git a/djangosnippets/templates/account/password_reset_done.html b/djangosnippets/templates/account/password_reset_done.html index 20681c49..a65dee2f 100644 --- a/djangosnippets/templates/account/password_reset_done.html +++ b/djangosnippets/templates/account/password_reset_done.html @@ -1,16 +1,18 @@ {% extends "account/base.html" %} -{% load i18n %} -{% load account %} +{% load static widget_tweaks %} -{% block head_title %}{% trans "Password Reset" %}{% endblock %} +{% block head_title %}Verify Your E-mail Address{% endblock %} -{% block content_header %}{% trans "Password Reset" %}{% endblock %} +{% block header%}{% endblock %} {% block content %} - {% if user.is_authenticated %} - {% include "account/snippets/already_logged_in.html" %} - {% endif %} - -

{% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

+
+

Password Reset

+ +

We have sent an e-mail to you for verification 🚀

+

Follow the link provided to finalize the password reset process. If you do not see the verification email in your main inbox, check your spam folder.

+ {% component 'contact_information_button' button_text="Didn't receive the email ?" description="If you didn't receive the verification email, please contact us at:" / %} +
{% endblock %} +{% block footer %}{% endblock %} diff --git a/djangosnippets/templates/account/signup.html b/djangosnippets/templates/account/signup.html index e508b44c..8c41c5cc 100644 --- a/djangosnippets/templates/account/signup.html +++ b/djangosnippets/templates/account/signup.html @@ -1,20 +1,48 @@ {% extends "account/base.html" %} -{% load i18n %} +{% load widget_tweaks %} -{% block head_title %}{% trans "Signup" %}{% endblock %} +{% block head_title %}Signup{% endblock %} -{% block content_header %}{% trans "Signup" %}{% endblock %} +{% block header%}{% endblock %} {% block content %} -

{% blocktrans %}Already have an account? Then please log in.{% endblocktrans %}

- - +
+

Sign Up

+

Already have an account? Then please log in

+ {% include "socialaccount/snippets/login_extra.html" %} +
+ {% csrf_token %} + {% if form.non_field_errors %} +
    + {% for error in form.non_field_errors %}
  • {{ error }}
  • {% endfor %} +
+ {% endif %} + {% for field in form %} +
+
+ + +
+ {% if field.errors %} + {{ field|add_class:"h-12 text-lg rounded-lg border-red-600 border-2 my-2" }} + {% else %} + {{ field|add_class:"h-12 text-lg rounded-lg border-base-green-400 border-2 my-2" }} + {% endif %} + {% if field.help_text %}
{{ field.help_text|safe }}
{% endif %} + {% if field.errors %}
    {% for error in field.errors %}
  • {{ error }}
  • {% endfor %}
{% endif %} +
+ {% endfor %} + +
+
+
+ OR +
+
+
    + {% include "socialaccount/snippets/provider_list.html" with process="login" %} +
+
{% endblock %} +{% block footer %}{% endblock %} diff --git a/djangosnippets/templates/account/signup_closed.html b/djangosnippets/templates/account/signup_closed.html deleted file mode 100644 index 23ceee85..00000000 --- a/djangosnippets/templates/account/signup_closed.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "account/base.html" %} - -{% load i18n %} - -{% block head_title %}{% trans "Sign Up closed" %}{% endblock %} - -{% block content_header %}{% trans "Sign Up closed" %}{% endblock %} - -{% block content %} -

{% trans "We are sorry, but the sign up is currently closed." %}

-{% endblock %} diff --git a/djangosnippets/templates/account/verification_sent.html b/djangosnippets/templates/account/verification_sent.html index 1ec0f442..7ae412b4 100644 --- a/djangosnippets/templates/account/verification_sent.html +++ b/djangosnippets/templates/account/verification_sent.html @@ -1,11 +1,18 @@ {% extends "account/base.html" %} -{% load i18n %} +{% load static widget_tweaks %} -{% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} +{% block head_title %}Verify Your E-mail Address{% endblock %} -{% block content_header %}{% trans "Verify Your E-mail Address" %}{% endblock %} +{% block header%}{% endblock %} {% block content %} -

{% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

+
+

Email Verification

+ +

We have sent an e-mail to you for verification 🚀

+

Follow the link provided to finalize the signup process. If you do not see the verification email in your main inbox, check your spam folder.

+ {% component 'contact_information_button' button_text="Didn't receive the email ?" description="If you didn't receive the verification email, please contact us at:" / %} +
{% endblock %} +{% block footer %}{% endblock %} diff --git a/djangosnippets/templates/base.html b/djangosnippets/templates/base.html index d5bb0674..acac3447 100644 --- a/djangosnippets/templates/base.html +++ b/djangosnippets/templates/base.html @@ -15,6 +15,7 @@ {% tailwind_css %} + {% block header %}

{% url 'account_logout' as logout_url %} @@ -38,6 +39,7 @@

+ {% endblock %}
{% block secondary_nav %}
+ {% block footer %} + {% endblock %} {% block extra_body %}{% endblock %} diff --git a/djangosnippets/templates/socialaccount/snippets/provider_list.html b/djangosnippets/templates/socialaccount/snippets/provider_list.html index e058913e..6a8a68af 100644 --- a/djangosnippets/templates/socialaccount/snippets/provider_list.html +++ b/djangosnippets/templates/socialaccount/snippets/provider_list.html @@ -3,7 +3,7 @@ {% get_providers as socialaccount_providers %} {% for provider in socialaccount_providers %} -
  • - {{ provider.name }} +
  • + Continue with {{ provider.name }}
  • {% endfor %} diff --git a/pyproject.toml b/pyproject.toml index c2d8144d..9df1422f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "markdown", "pillow", "psycopg2-binary", - "pydantic", + "pydantic[email]", "pygments", "python-akismet", "python-dotenv", @@ -37,6 +37,7 @@ dependencies = [ "sentry-sdk", "gevent", "greenlet", + "django-widget-tweaks>=1.5.0", ] [project.optional-dependencies] diff --git a/theme/static_src/src/styles.css b/theme/static_src/src/styles.css index 3e94d371..6683ba0d 100644 --- a/theme/static_src/src/styles.css +++ b/theme/static_src/src/styles.css @@ -6,13 +6,22 @@ --color-base-black: #0a0a0a; --color-base-gray-400: #7a7a7a; --color-base-green-400: #256918; + --color-base-green-600: #1E4F15; --color-base-green-800: #12330c; + --shadow-sign-content: rgba(0, 0, 0, 0.24) 0px 3px 8px; --font-header: "Libertinus Sans", sans-serif; --font-title: "Playfair Display", sans-serif; --font-text: "Nunito", sans-serif; --font-common: "Raleway", sans-serif; } +/* Additional default styles to be applied when SCSS is completely removed */ +@layer base { + a { + text-underline-offset: 4px ; + } +} + @layer components { .arrow-left::before { @apply content-[''] w-4 h-4 bg-current mr-2; diff --git a/uv.lock b/uv.lock index 027557fe..cc942a8c 100644 --- a/uv.lock +++ b/uv.lock @@ -343,6 +343,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cd/8f/7a651007ab9bf552221ae8adc9305c7d21113a9814eb38153b5fdb4ef5eb/django_tailwind-4.0.1-py3-none-any.whl", hash = "sha256:33e5dd5aadd5aa6facf4bddab9144a40cb501e486d4970b93e2853dbdbef4f67", size = 16714, upload-time = "2025-04-02T20:04:03.038Z" }, ] +[[package]] +name = "django-widget-tweaks" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/fe/26eb92fba83844e71bbec0ced7fc2e843e5990020e3cc676925204031654/django-widget-tweaks-1.5.0.tar.gz", hash = "sha256:1c2180681ebb994e922c754804c7ffebbe1245014777ac47897a81f57cc629c7", size = 14767, upload-time = "2023-08-25T15:29:12.778Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/6a/6cb6deb5c38b785c77c3ba66f53051eada49205979c407323eb666930915/django_widget_tweaks-1.5.0-py3-none-any.whl", hash = "sha256:a41b7b2f05bd44d673d11ebd6c09a96f1d013ee98121cb98c384fe84e33b881e", size = 8960, upload-time = "2023-08-25T15:29:05.644Z" }, +] + [[package]] name = "djangorestframework" version = "3.15.2" @@ -374,6 +383,7 @@ dependencies = [ { name = "django-redis-cache" }, { name = "django-taggit" }, { name = "django-tailwind" }, + { name = "django-widget-tweaks" }, { name = "djangorestframework" }, { name = "gevent" }, { name = "greenlet" }, @@ -381,7 +391,7 @@ dependencies = [ { name = "markdown" }, { name = "pillow" }, { name = "psycopg2-binary" }, - { name = "pydantic" }, + { name = "pydantic", extra = ["email"] }, { name = "pygments" }, { name = "python-akismet" }, { name = "python-dotenv" }, @@ -421,6 +431,7 @@ requires-dist = [ { name = "django-redis-cache" }, { name = "django-taggit" }, { name = "django-tailwind" }, + { name = "django-widget-tweaks", specifier = ">=1.5.0" }, { name = "djangorestframework" }, { name = "gevent" }, { name = "greenlet" }, @@ -430,7 +441,7 @@ requires-dist = [ { name = "pillow" }, { name = "pre-commit", marker = "extra == 'dev'" }, { name = "psycopg2-binary" }, - { name = "pydantic" }, + { name = "pydantic", extras = ["email"] }, { name = "pygments" }, { name = "python-akismet" }, { name = "python-dotenv" }, @@ -518,6 +529,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/fd/1f3ca2777adef393c135c9bd45260147a2ee72328cf4804f69ad392bebd5/djc_core_html_parser-1.0.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1c2e86c819e6dc49b8c4c81ed4e7a326d877f0aa884e6c6d3e19ce8d292d2dd3", size = 1221302, upload-time = "2025-10-21T21:04:17.876Z" }, ] +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + [[package]] name = "filelock" version = "3.20.0" @@ -1031,6 +1064,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, ] +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + [[package]] name = "pydantic-core" version = "2.33.2"