From a5e001fb5dadb5d955528542ecfcedd67b7a7b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Szczeci=C5=84ski?= Date: Sun, 7 Jul 2024 23:04:51 +0200 Subject: [PATCH 01/20] Redirect user to home page after logout --- app/server/users/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/server/users/urls.py b/app/server/users/urls.py index 8c709128..b0f5f1dd 100644 --- a/app/server/users/urls.py +++ b/app/server/users/urls.py @@ -1,4 +1,4 @@ -from django.contrib.auth.views import LoginView +from django.contrib.auth.views import LogoutView from django.urls import path from django.urls import include, re_path @@ -14,6 +14,7 @@ r'^activate/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$', views.activate, name='accounts_activate'), path('login/', anonymous_required(views.ReactLoginView.as_view()), name='login'), + path('logout/', LogoutView.as_view(next_page='index'), name='logout'), path('organizations/', views.organizations, name='organizations'), path('organizations/accept/', views.toggle_organization, name='toggle_organization'), path('organizations/add/', views.update_organization, name='organization_add'), @@ -33,7 +34,6 @@ path('register/', views.register, name='user_zosia_register'), path('', include('django.contrib.auth.urls')), # NOTE: it adds following URLs: - # ^logout/$ [name='logout'] # ^password_change/$ [name='password_change'] # ^password_change/done/$ [name='password_change_done'] # ^password_reset/$ [name='password_reset'] From 824f66a6b9775a17c2136e9ba661bdd6f722dad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Szczeci=C5=84ski?= Date: Mon, 8 Jul 2024 02:53:07 +0200 Subject: [PATCH 02/20] Add SignUp template --- .../components/forms/BasicFormField.tsx | 43 ++++-- app/client/templates/SignUp.tsx | 20 +++ app/package-lock.json | 123 ++++++++++++++++++ app/package.json | 1 + app/server/users/forms.py | 11 +- app/server/users/templates.py | 7 + app/server/users/views.py | 6 +- 7 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 app/client/templates/SignUp.tsx diff --git a/app/client/components/forms/BasicFormField.tsx b/app/client/components/forms/BasicFormField.tsx index cadd06aa..109296ac 100644 --- a/app/client/components/forms/BasicFormField.tsx +++ b/app/client/components/forms/BasicFormField.tsx @@ -1,5 +1,6 @@ -import { Field, Input, Label } from "@headlessui/react"; +import { Description, Field, Input, Label } from "@headlessui/react"; import { FieldHandler, Widget } from "@reactivated"; +import parse from "html-react-parser"; import React from "react"; interface BasicFormFieldProps { @@ -11,24 +12,28 @@ export const BasicFormField = ({ field }: BasicFormFieldProps) => { switch (field.tag) { case "django.forms.widgets.TextInput": + case "django.forms.widgets.EmailInput": + case "django.forms.widgets.PasswordInput": widget = ( ); break; - case "django.forms.widgets.PasswordInput": + case "django.forms.widgets.CheckboxInput": widget = ( - ); break; @@ -36,10 +41,28 @@ export const BasicFormField = ({ field }: BasicFormFieldProps) => { widget = ; } + const description = + field.help_text || field.error ? ( + + {field.error && ( + {parse(field.error)} + )} + {field.help_text && ( + {parse(field.help_text)} + )} + + ) : ( + <> + ); + return ( - - + + {widget} + {description} ); }; diff --git a/app/client/templates/SignUp.tsx b/app/client/templates/SignUp.tsx new file mode 100644 index 00000000..f30239f9 --- /dev/null +++ b/app/client/templates/SignUp.tsx @@ -0,0 +1,20 @@ +import { Layout } from "@client/components/Layout"; +import { PageTitle } from "@client/components/PageTitle"; +import { CenteredFormContainer } from "@client/components/containers/CenteredFormContainer"; +import { BasicForm } from "@client/components/forms/BasicForm"; +import { templates, useForm } from "@reactivated"; +import React from "react"; + +export const Template = (props: templates.SignUp) => { + const form = useForm({ form: props.form }); + + return ( + + Sign Up + + + {/* TODO: Add reCaptcha */} + + + ); +}; diff --git a/app/package-lock.json b/app/package-lock.json index a06093e2..557d4dd4 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -17,6 +17,7 @@ "autoprefixer": "^10.4.19", "chart.js": "^3.9.1", "daisyui": "^4.11.1", + "html-react-parser": "^5.1.10", "jquery": "^3.6.1", "postcss": "^8.4.38", "react": "^18.3.1", @@ -2471,6 +2472,57 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "license": "MIT" @@ -2508,6 +2560,17 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.23.3", "license": "MIT", @@ -3843,6 +3906,35 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/html-dom-parser": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.8.tgz", + "integrity": "sha512-vuWiX9EXgu8CJ5m9EP5c7bvBmNSuQVnrY8tl0z0ZX96Uth1IPlYH/8W8VZ/hBajFf18EN+j2pukbCNd01HEd1w==", + "dependencies": { + "domhandler": "5.0.3", + "htmlparser2": "9.1.0" + } + }, + "node_modules/html-react-parser": { + "version": "5.1.10", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.1.10.tgz", + "integrity": "sha512-gV22PvLij4wdEdtrZbGVC7Zy2OVWnQ0bYhX63S196ZRSx4+K0TuutCreHSXr+saUia8KeKB+2TYziVfijpH4Tw==", + "dependencies": { + "domhandler": "5.0.3", + "html-dom-parser": "5.0.8", + "react-property": "2.0.2", + "style-to-js": "1.1.12" + }, + "peerDependencies": { + "@types/react": "17 || 18", + "react": "0.14 || 15 || 16 || 17 || 18" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/html-url-attributes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", @@ -3852,6 +3944,24 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "license": "MIT", @@ -6097,6 +6207,11 @@ "react": ">=18" } }, + "node_modules/react-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==" + }, "node_modules/react-refresh": { "version": "0.14.2", "license": "MIT", @@ -6782,6 +6897,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-js": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.12.tgz", + "integrity": "sha512-tv+/FkgNYHI2fvCoBMsqPHh5xovwiw+C3X0Gfnss/Syau0Nr3IqGOJ9XiOYXoPnToHVbllKFf5qCNFJGwFg5mg==", + "dependencies": { + "style-to-object": "1.0.6" + } + }, "node_modules/style-to-object": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz", diff --git a/app/package.json b/app/package.json index 42333ea6..1b999dd1 100644 --- a/app/package.json +++ b/app/package.json @@ -25,6 +25,7 @@ "autoprefixer": "^10.4.19", "chart.js": "^3.9.1", "daisyui": "^4.11.1", + "html-react-parser": "^5.1.10", "jquery": "^3.6.1", "postcss": "^8.4.38", "react": "^18.3.1", diff --git a/app/server/users/forms.py b/app/server/users/forms.py index 7887b603..b40bc6a6 100644 --- a/app/server/users/forms.py +++ b/app/server/users/forms.py @@ -1,5 +1,5 @@ from django import forms -from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, UsernameField from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site from django.urls import reverse @@ -91,9 +91,13 @@ def send_mail(self): print(users) +class UserAuthenticationForm(AuthenticationForm): + username = UsernameField(widget=forms.EmailInput(attrs={"autofocus": True})) + + class UserForm(UserCreationForm): privacy_consent = forms.BooleanField(required=True) - captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox) + # captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox) class Meta: model = User @@ -101,9 +105,8 @@ class Meta: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - label = f'I agree to the Privacy Policy' + label = f'I agree to the Privacy Policy' self.fields['privacy_consent'].label = mark_safe(label) - print('Captcha', self.fields['captcha'].label) def save(self, request): user = super().save(commit=False) diff --git a/app/server/users/templates.py b/app/server/users/templates.py index f30d7b87..37743d30 100644 --- a/app/server/users/templates.py +++ b/app/server/users/templates.py @@ -2,8 +2,15 @@ from reactivated import template from django.contrib.auth.forms import AuthenticationForm +from .forms import UserForm + @template class Login(NamedTuple): form: AuthenticationForm is_redirected_from_another_page: bool + + +@template +class SignUp(NamedTuple): + form: UserForm diff --git a/app/server/users/views.py b/app/server/users/views.py index 780503a6..9170d05c 100644 --- a/app/server/users/views.py +++ b/app/server/users/views.py @@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _ from django.views.decorators.http import require_http_methods -from .templates import Login +from .templates import Login, SignUp from server.conferences.models import Zosia from server.lectures.models import Lecture from . import forms @@ -26,6 +26,8 @@ class ReactLoginView(LoginView): + authentication_form = forms.UserAuthenticationForm + def render_to_response(self, context, **response_kwargs): is_redirected = context.get('next', '') != '' @@ -78,7 +80,7 @@ def signup(request): form.save(request) return render(request, 'users/signup_done.html', ctx) - return render(request, 'users/signup.html', ctx) + return SignUp(form=form).render(request) @login_required From 91243dcf5743a5daa5b48dc5d755b0283faf8cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Szczeci=C5=84ski?= Date: Mon, 8 Jul 2024 13:05:43 +0200 Subject: [PATCH 03/20] Remove reCaptcha --- app/requirements.txt | 1 - app/server/settings/common.py | 1 - app/server/settings/prod.py | 4 ---- app/server/users/forms.py | 3 --- 4 files changed, 9 deletions(-) diff --git a/app/requirements.txt b/app/requirements.txt index 6631b116..cd5c69ab 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -15,7 +15,6 @@ argon2-cffi~=23.1.0 boto3>=1.34.93, <2.0 python-dotenv~=1.0.1 google-cloud-secret-manager~=2.20.0 -django-recaptcha reactivated~=0.41.0 django-stubs-ext~=5.0.0 django-storages[google]~=1.14.3 \ No newline at end of file diff --git a/app/server/settings/common.py b/app/server/settings/common.py index 4908e175..2e520a90 100644 --- a/app/server/settings/common.py +++ b/app/server/settings/common.py @@ -129,7 +129,6 @@ def random_string(length=10): "django.contrib.messages", "django.contrib.staticfiles", "storages", - "django_recaptcha", "reactivated", ] diff --git a/app/server/settings/prod.py b/app/server/settings/prod.py index 2f59f492..d4b9c6a2 100644 --- a/app/server/settings/prod.py +++ b/app/server/settings/prod.py @@ -7,10 +7,6 @@ CSRF_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True -# Django reCaptcha config -RECAPTCHA_PUBLIC_KEY = os.environ.get("CAPTCHA_PUBLIC", "") -RECAPTCHA_PRIVATE_KEY = os.environ.get("CAPTCHA_PRIVATE", "") - # Logs SQL queries. Should be enough, since we can check docker logs LOGGING = { "version": 1, diff --git a/app/server/users/forms.py b/app/server/users/forms.py index b40bc6a6..8f28abf7 100644 --- a/app/server/users/forms.py +++ b/app/server/users/forms.py @@ -5,8 +5,6 @@ from django.urls import reverse from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -from django_recaptcha.fields import ReCaptchaField -from django_recaptcha.widgets import ReCaptchaV2Checkbox from server.conferences.models import Transport, Zosia from .actions import SendActivationEmail, SendEmailToAll @@ -97,7 +95,6 @@ class UserAuthenticationForm(AuthenticationForm): class UserForm(UserCreationForm): privacy_consent = forms.BooleanField(required=True) - # captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox) class Meta: model = User From 173f2c07b9f85b6423c351b33bd194c18a74d675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Szczeci=C5=84ski?= Date: Mon, 8 Jul 2024 13:53:29 +0200 Subject: [PATCH 04/20] Fix non-clickable link in form field label --- .../components/forms/BasicFormField.tsx | 22 +++++++++---------- app/server/users/forms.py | 4 +++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/client/components/forms/BasicFormField.tsx b/app/client/components/forms/BasicFormField.tsx index 109296ac..eb2cc546 100644 --- a/app/client/components/forms/BasicFormField.tsx +++ b/app/client/components/forms/BasicFormField.tsx @@ -1,4 +1,4 @@ -import { Description, Field, Input, Label } from "@headlessui/react"; +import { Checkbox, Description, Field, Input, Label } from "@headlessui/react"; import { FieldHandler, Widget } from "@reactivated"; import parse from "html-react-parser"; import React from "react"; @@ -27,11 +27,9 @@ export const BasicFormField = ({ field }: BasicFormFieldProps) => { break; case "django.forms.widgets.CheckboxInput": widget = ( - @@ -56,12 +54,14 @@ export const BasicFormField = ({ field }: BasicFormFieldProps) => { ); return ( - - - {widget} + +
+ + {widget} +
{description}
); diff --git a/app/server/users/forms.py b/app/server/users/forms.py index 8f28abf7..f2d2217c 100644 --- a/app/server/users/forms.py +++ b/app/server/users/forms.py @@ -102,8 +102,10 @@ class Meta: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - label = f'I agree to the Privacy Policy' + label = 'I agree to the Privacy Policy' + help_text = f'Read full Privacy Policy' self.fields['privacy_consent'].label = mark_safe(label) + self.fields['privacy_consent'].help_text = mark_safe(help_text) def save(self, request): user = super().save(commit=False) From b319c57f3134ad9c18bd6d0089b790125d7e6087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Szczeci=C5=84ski?= Date: Mon, 8 Jul 2024 14:16:44 +0200 Subject: [PATCH 05/20] Add successful sign up message --- app/client/templates/SignUp.tsx | 31 ++++++++++++++++++++++++++----- app/server/users/templates.py | 1 + app/server/users/views.py | 5 +---- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/app/client/templates/SignUp.tsx b/app/client/templates/SignUp.tsx index f30239f9..57354150 100644 --- a/app/client/templates/SignUp.tsx +++ b/app/client/templates/SignUp.tsx @@ -4,17 +4,38 @@ import { CenteredFormContainer } from "@client/components/containers/CenteredFor import { BasicForm } from "@client/components/forms/BasicForm"; import { templates, useForm } from "@reactivated"; import React from "react"; +import Markdown from "react-markdown"; + +const successfulSignUpMessageMarkdown = (email: string) => ` +We've sent an e-mail to **${email}** with instructions for +activating your account. You should receive them shortly. + +If you don't receive the e-mail, please make sure you've entered +correct address and/or check your spam folder. + +If you experience any problems with signing up, please let us know +at [ksi@cs.uni.wroc.pl](mailto:ksi@cs.uni.wroc.pl). +`; export const Template = (props: templates.SignUp) => { const form = useForm({ form: props.form }); return ( - Sign Up - - - {/* TODO: Add reCaptcha */} - + + Sign Up {props.is_signup_successful ? "Successful" : ""} + + {props.is_signup_successful ? ( +
+ + {successfulSignUpMessageMarkdown(form.fields.email.value ?? "")} + +
+ ) : ( + + + + )}
); }; diff --git a/app/server/users/templates.py b/app/server/users/templates.py index 37743d30..831cde71 100644 --- a/app/server/users/templates.py +++ b/app/server/users/templates.py @@ -14,3 +14,4 @@ class Login(NamedTuple): @template class SignUp(NamedTuple): form: UserForm + is_signup_successful: bool = False diff --git a/app/server/users/views.py b/app/server/users/views.py index 9170d05c..7b264391 100644 --- a/app/server/users/views.py +++ b/app/server/users/views.py @@ -71,14 +71,11 @@ def profile(request): @require_http_methods(['GET', 'POST']) def signup(request): form = forms.UserForm(request.POST or None) - ctx = { - 'form': form, - } if request.method == 'POST': if form.is_valid(): form.save(request) - return render(request, 'users/signup_done.html', ctx) + return SignUp(form=form, is_signup_successful=True).render(request) return SignUp(form=form).render(request) From fa1e10477af92720f6496f644a1e7937c4c7454e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Szczeci=C5=84ski?= Date: Mon, 8 Jul 2024 14:25:30 +0200 Subject: [PATCH 06/20] Add CenteredContainer to markdown articles --- app/client/templates/PrivacyPolicy.tsx | 15 +++++---- app/client/templates/SignUp.tsx | 13 +++++--- app/client/templates/SignupRules.tsx | 15 +++++---- app/client/templates/TermsAndConditions.tsx | 37 +++++++++++---------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/app/client/templates/PrivacyPolicy.tsx b/app/client/templates/PrivacyPolicy.tsx index 0c7e827c..6c03f307 100644 --- a/app/client/templates/PrivacyPolicy.tsx +++ b/app/client/templates/PrivacyPolicy.tsx @@ -1,3 +1,4 @@ +import { CenteredContainer } from "@client/components/containers/CenteredContainer"; import { Layout } from "@client/components/Layout"; import React from "react"; import Markdown from "react-markdown"; @@ -151,12 +152,14 @@ później jednak niż w ciągu 7 dni. export const Template = () => { return ( -
-

- Polityka Prywatności Zimowego Obozu Studentów Informatyki A -

- {privacyPolicyMarkdown} -
+ +
+

+ Polityka Prywatności Zimowego Obozu Studentów Informatyki A +

+ {privacyPolicyMarkdown} +
+
); }; diff --git a/app/client/templates/SignUp.tsx b/app/client/templates/SignUp.tsx index 57354150..75db2c87 100644 --- a/app/client/templates/SignUp.tsx +++ b/app/client/templates/SignUp.tsx @@ -1,5 +1,6 @@ import { Layout } from "@client/components/Layout"; import { PageTitle } from "@client/components/PageTitle"; +import { CenteredContainer } from "@client/components/containers/CenteredContainer"; import { CenteredFormContainer } from "@client/components/containers/CenteredFormContainer"; import { BasicForm } from "@client/components/forms/BasicForm"; import { templates, useForm } from "@reactivated"; @@ -26,11 +27,13 @@ export const Template = (props: templates.SignUp) => { Sign Up {props.is_signup_successful ? "Successful" : ""} {props.is_signup_successful ? ( -
- - {successfulSignUpMessageMarkdown(form.fields.email.value ?? "")} - -
+ +
+ + {successfulSignUpMessageMarkdown(form.fields.email.value ?? "")} + +
+
) : ( diff --git a/app/client/templates/SignupRules.tsx b/app/client/templates/SignupRules.tsx index c137a902..f590117f 100644 --- a/app/client/templates/SignupRules.tsx +++ b/app/client/templates/SignupRules.tsx @@ -1,3 +1,4 @@ +import { CenteredContainer } from "@client/components/containers/CenteredContainer"; import { Layout } from "@client/components/Layout"; import React from "react"; import Markdown from "react-markdown"; @@ -38,12 +39,14 @@ mailową. Dziękujemy. export const Template = () => { return ( -
-

- Zapisy dla Zaproszonych na Zimowy Obóz Studentów Informatyki A -

- {signupRulesMarkdown} -
+ +
+

+ Zapisy dla Zaproszonych na Zimowy Obóz Studentów Informatyki A +

+ {signupRulesMarkdown} +
+
); }; diff --git a/app/client/templates/TermsAndConditions.tsx b/app/client/templates/TermsAndConditions.tsx index 5e27d393..ab9c8a63 100644 --- a/app/client/templates/TermsAndConditions.tsx +++ b/app/client/templates/TermsAndConditions.tsx @@ -1,3 +1,4 @@ +import { CenteredContainer } from "@client/components/containers/CenteredContainer"; import { Layout } from "@client/components/Layout"; import { getLocalDate } from "@client/utils/time"; import { templates } from "@reactivated"; @@ -114,23 +115,25 @@ export const Template = (props: templates.TermsAndConditions) => { return ( -
-

- Regulamin Zimowego Obozu Studentów Informatyki A -

- {props.zosia ? ( - - {privacyPolicyMarkdown( - props.zosia_title, - props.place.town, - startDate, - endDate, - )} - - ) : ( -

W przygotowaniu...

- )} -
+ +
+

+ Regulamin Zimowego Obozu Studentów Informatyki A +

+ {props.zosia ? ( + + {privacyPolicyMarkdown( + props.zosia_title, + props.place.town, + startDate, + endDate, + )} + + ) : ( +

W przygotowaniu...

+ )} +
+
); }; From 26e607ea7a73d4a2dbc8f7f44b8b590bcc046024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Szczeci=C5=84ski?= Date: Mon, 8 Jul 2024 17:23:34 +0200 Subject: [PATCH 07/20] Add initial Register template --- .../components/forms/BasicFormField.tsx | 49 ++++++++++++++++--- app/client/templates/Register.tsx | 20 ++++++++ app/server/users/forms.py | 6 +-- app/server/users/models.py | 3 +- app/server/users/templates.py | 7 ++- app/server/users/views.py | 4 +- 6 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 app/client/templates/Register.tsx diff --git a/app/client/components/forms/BasicFormField.tsx b/app/client/components/forms/BasicFormField.tsx index eb2cc546..74b676a5 100644 --- a/app/client/components/forms/BasicFormField.tsx +++ b/app/client/components/forms/BasicFormField.tsx @@ -1,4 +1,12 @@ -import { Checkbox, Description, Field, Input, Label } from "@headlessui/react"; +import { + Checkbox, + Description, + Field, + Input, + Label, + Select, + Textarea, +} from "@headlessui/react"; import { FieldHandler, Widget } from "@reactivated"; import parse from "html-react-parser"; import React from "react"; @@ -20,7 +28,6 @@ export const BasicFormField = ({ field }: BasicFormFieldProps) => { name={field.name} className="input input-bordered w-full" required={field.widget.required} - disabled={field.disabled} defaultValue={field.value ?? ""} /> ); @@ -29,12 +36,42 @@ export const BasicFormField = ({ field }: BasicFormFieldProps) => { widget = ( ); break; + case "django.forms.widgets.Textarea": + widget = ( +