1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-02-25 11:46:30 +00:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Emmy D'Anello
ece128836a
Temporary disable payment form
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 23:51:33 +01:00
Emmy D'Anello
2e574d0659
Fix participation detail test (a tournament is required)
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 23:47:01 +01:00
Emmy D'Anello
850659bf48
Display payment information on the sidebar
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 23:31:24 +01:00
Emmy D'Anello
672529382d
Fix payment view
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 23:12:48 +01:00
Emmy D'Anello
c1ce7cb70f
Display pending validations for organizers
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 22:39:11 +01:00
Emmy D'Anello
bc67d1cf1f
Add information about team registration
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 22:24:22 +01:00
Emmy D'Anello
652e913f49
Fix user update view
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 21:41:37 +01:00
Emmy D'Anello
089374b937
Fix join team view
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 21:40:06 +01:00
Emmy D'Anello
226e5620f9
Better footer on small screens
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 21:06:22 +01:00
Emmy D'Anello
ca9652cc60
Collapse sidebar on small screens
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 20:59:34 +01:00
Emmy D'Anello
acd1d80c75
First important informations
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 20:20:28 +01:00
Emmy D'Anello
e7c207d2af
Sidebar structure
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 19:23:16 +01:00
Emmy D'Anello
196ccb69ad
Remove headers on index page
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 19:15:36 +01:00
Emmy D'Anello
2b941cb30f
Rearrange base template with separated contents, add sidebar
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-11 18:43:23 +01:00
18 changed files with 1161 additions and 577 deletions

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from registration.models import VolunteerRegistration
from registration.models import VolunteerRegistration, Payment
from tfjm.lists import get_sympa_client
@ -66,6 +66,140 @@ class Team(models.Model):
def coaches(self):
return self.participants.filter(coachregistration__isnull=False)
def can_validate(self):
if any(not r.email_confirmed for r in self.participants.all()):
return False
if self.students.count() < 4:
return False
if not self.coaches.exists():
return False
if not self.participation.tournament:
return False
if any(not r.photo_authorization for r in self.participants.all()):
return False
if not self.motivation_letter:
return False
if not self.participation.tournament.remote:
if any(r.under_18 and not r.health_sheet for r in self.students.all()):
return False
if any(r.under_18 and not r.vaccine_sheet for r in self.students.all()):
return False
if any(r.under_18 and not r.parental_authorization for r in self.students.all()):
return False
return True
def important_informations(self):
informations = []
if self.participation.valid is None:
if not self.participation.tournament:
text = _("The team {trigram} is not registered to any tournament. "
"You can register the team to a tournament using <a href='{url}'>this link</a>.")
url = reverse_lazy("participation:update_team", args=(self.pk,))
content = format_lazy(text, trigram=self.trigram, url=url)
informations.append({
'title': _("No tournament"),
'type': "danger",
'priority': 4,
'content': content,
})
else:
text = _("Registrations for the tournament of {tournament} are ending on the {date:%Y-%m-%d %H:%M}.")
content = format_lazy(text,
tournament=self.participation.tournament.name,
date=self.participation.tournament.inscription_limit)
informations.append({
'title': _("Registrations closure"),
'type': "info",
'priority': 1,
'content': content,
})
if not self.motivation_letter:
text = _("The team {trigram} has not uploaded a motivation letter. "
"You can upload your motivation letter using <a href='{url}'>this link</a>.")
url = reverse_lazy("participation:upload_team_motivation_letter", args=(self.pk,))
content = format_lazy(text, trigram=self.trigram, url=url)
informations.append({
'title': _("No motivation letter"),
'type': "danger",
'priority': 10,
'content': content,
})
nb_students = self.students.count()
nb_coaches = self.coaches.count()
if nb_students < 4:
text = _("The team {trigram} has less than 4 students ({nb_students}). "
"You can invite more students to join the team using "
"the invite code <strong>{code}</strong>.")
content = format_lazy(text, trigram=self.trigram, nb_students=nb_students, code=self.access_code)
informations.append({
'title': _("Not enough students"),
'type': "warning",
'priority': 7,
'content': content,
})
if not nb_coaches:
text = _("The team {trigram} has no coach. "
"You can invite a coach to join the team using the invite code <strong>{code}</strong>.")
content = format_lazy(text, trigram=self.trigram, nb_students=nb_students, code=self.access_code)
informations.append({
'title': _("No coach"),
'type': "warning",
'priority': 8,
'content': content,
})
if nb_students > 6 or nb_coaches > 2:
text = _("The team {trigram} has more than 6 students ({nb_students}) "
"or more than 2 coaches ({nb_coaches})."
"You have to restrict the number of students and coaches to 6 and 2, respectively.")
content = format_lazy(text, trigram=self.trigram, nb_students=nb_students, nb_coaches=nb_coaches)
informations.append({
'title': _("Too many members"),
'type': "warning",
'priority': 7,
'content': content,
})
elif nb_students >= 4 and nb_coaches >= 1:
if self.can_validate():
text = _("The team {trigram} is ready to be validated. "
"You can request validation on <a href='{url}'>the page of your team</a>.")
url = reverse_lazy("participation:team_detail", args=(self.pk,))
content = format_lazy(text, trigram=self.trigram, url=url)
informations.append({
'title': _("Validate team"),
'type': "success",
'priority': 2,
'content': content,
})
else:
text = _("The team {trigram} has enough participants, but is not ready to be validated. "
"Please make sure that all the participants have uploaded the required documents. "
"To invite more participants, use the invite code <strong>{code}</strong>.")
content = format_lazy(text, trigram=self.trigram, code=self.access_code)
informations.append({
'title': _("Validate team"),
'type': "warning",
'priority': 10,
'content': content,
})
elif self.participation.valid is False:
text = _("The team {trigram} has not been validated by the organizers yet. Please be patient.")
content = format_lazy(text, trigram=self.trigram)
informations.append({
'title': _("Pending validation"),
'type': "warning",
'priority': 2,
'content': content,
})
else:
informations.extend(self.participation.important_informations())
return informations
@property
def email(self):
"""
@ -328,6 +462,42 @@ class Participation(models.Model):
def __str__(self):
return _("Participation of the team {name} ({trigram})").format(name=self.team.name, trigram=self.team.trigram)
def important_informations(self):
informations = []
missing_payments = Payment.objects.filter(registration__in=self.team.participants.all(), valid=False)
if missing_payments.exists():
text = _("<p>The team {trigram} has {nb_missing_payments} missing payments. Each member of the team "
"must have a valid payment (or send a scholarship notification) "
"to participate to the tournament.</p>"
"<p>Participants that have not paid yet are: {participants}.</p>")
content = format_lazy(text, trigram=self.team.trigram, nb_missing_payments=missing_payments.count(),
participants=", ".join(str(p.registration) for p in missing_payments.all()))
informations.append({
'title': _("Missing payments"),
'type': "danger",
'priority': 10,
'content': content,
})
if timezone.now() <= self.tournament.solution_limit:
text = _("<p>The solutions for the tournament of {tournament} are due on the {date:%Y-%m-%d %H:%M}.</p>"
"<p>You have currently sent <strong>{nb_solutions}</strong> solutions. "
"We suggest to send at least <strong>{min_solutions}</strong> different solutions.</p>"
"<p>You can upload your solutions on <a href='{url}'>your participation page</a>.</p>")
url = reverse_lazy("participation:participation_detail", args=(self.pk,))
content = format_lazy(text, tournament=self.tournament.name, date=self.tournament.solution_limit,
nb_solutions=self.solutions.count(), min_solutions=len(settings.PROBLEMS) - 3,
url=url)
informations.append({
'title': _("Solutions due"),
'type': "info",
'priority': 1,
'content': content,
})
return informations
class Meta:
verbose_name = _("participation")
verbose_name_plural = _("participations")

View File

@ -2,7 +2,7 @@
{% load django_tables2 i18n %}
{% block contenttitle %}
{% block content-title %}
<h1>{% trans "All teams" %}</h1>
{% endblock %}

View File

@ -2,7 +2,7 @@
{% load django_tables2 i18n %}
{% block contenttitle %}
{% block content-title %}
<h1>{% trans "All tournaments" %}</h1>
{% endblock %}

View File

@ -438,6 +438,7 @@ class TestStudentParticipation(TestCase):
self.user.registration.save()
# Team is valid
self.team.participation.tournament = self.tournament
self.team.participation.valid = True
self.team.participation.save()
response = self.client.post(reverse("participation:team_leave"))
@ -479,6 +480,7 @@ class TestStudentParticipation(TestCase):
reverse("participation:participation_detail", args=(self.team.participation.pk,)),
302, 403)
self.team.participation.tournament = self.tournament
self.team.participation.valid = True
self.team.participation.save()
response = self.client.get(reverse("participation:my_participation_detail"))

View File

@ -90,7 +90,7 @@ class JoinTeamView(LoginRequiredMixin, FormView):
model = Team
form_class = JoinTeamForm
extra_context = dict(title=_("Join team"))
template_name = "participation/create_team.html"
template_name = "participation/join_team.html"
def dispatch(self, request, *args, **kwargs):
user = request.user
@ -180,15 +180,7 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
context["validation_form"] = ValidateParticipationForm(self.request.POST or None)
# A team is complete when there are at least 4 members plus a coache that have sent their authorizations,
# their health sheet, they confirmed their email address and under-18 people sent their parental authorization.
# TODO: Add vaccine sheets
context["can_validate"] = team.students.count() >= 4 and team.coaches.exists() and \
team.participation.tournament and \
all(r.photo_authorization for r in team.participants.all()) and \
(team.participation.tournament.remote
or all(r.health_sheet for r in team.students.all() if r.under_18)) and \
(team.participation.tournament.remote
or all(r.parental_authorization for r in team.students.all() if r.under_18)) and \
team.motivation_letter
context["can_validate"] = team.can_validate()
return context

View File

@ -228,6 +228,7 @@ class PaymentForm(forms.ModelForm):
self.fields["valid"].widget.choices[0] = ('unknown', _("Pending"))
def clean_scholarship_file(self):
print(self.files)
if "scholarship_file" in self.files:
file = self.files["scholarship_file"]
if file.size > 2e6:
@ -240,7 +241,7 @@ class PaymentForm(forms.ModelForm):
cleaned_data = super().clean()
if "type" in cleaned_data and cleaned_data["type"] == "scholarship" \
and "scholarship_file" not in cleaned_data and not self.instance.scholarship_file:
and "scholarship_file" not in self.files and not self.instance.scholarship_file:
self.add_error("scholarship_file", _("You must upload your scholarship attestation."))
return cleaned_data

View File

@ -12,6 +12,7 @@ from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField
from polymorphic.models import PolymorphicModel
@ -88,6 +89,28 @@ class Registration(PolymorphicModel):
def get_absolute_url(self):
return reverse_lazy("registration:user_detail", args=(self.user_id,))
def registration_informations(self):
return []
def important_informations(self):
informations = []
if not self.email_confirmed:
text = _("Your email address is not validated. Please click on the link you received by email. "
"You can resend a mail by clicking on <a href=\"{send_email_url}\">this link</a>.")
send_email_url = reverse_lazy("registration:email_validation_resend", args=(self.user_id,))
content = format_lazy(text, send_email_url=send_email_url)
informations.append({
'title': "Validation e-mail",
'type': "warning",
'priority': 0,
'content': content,
})
informations.extend(self.registration_informations())
informations.sort(key=lambda info: (info['priority'], info['title']))
return informations
def __str__(self):
return f"{self.user.first_name} {self.user.last_name}"
@ -196,6 +219,38 @@ class ParticipantRegistration(Registration):
def form_class(self): # pragma: no cover
raise NotImplementedError
def registration_informations(self):
informations = []
if not self.team:
text = _("You are not in a team. You can <a href=\"{create_url}\">create one</a> "
"or <a href=\"{join_url}\">join an existing one</a> to participate.")
create_url = reverse_lazy("participation:create_team")
join_url = reverse_lazy("participation:join_team")
content = format_lazy(text, create_url=create_url, join_url=join_url)
informations.append({
'title': _("No team"),
'type': "danger",
'priority': 1,
'content': content,
})
else:
if self.team.participation.tournament:
if not self.photo_authorization:
text = _("You have not uploaded your photo authorization. "
"You can do it by clicking on <a href=\"{photo_url}\">this link</a>.")
photo_url = reverse_lazy("registration:upload_user_photo_authorization", args=(self.id,))
content = format_lazy(text, photo_url=photo_url)
informations.append({
'title': _("Photo authorization"),
'type': "danger",
'priority': 5,
'content': content,
})
informations.extend(self.team.important_informations())
return informations
class Meta:
verbose_name = _("participant registration")
verbose_name_plural = _("participant registrations")
@ -271,6 +326,68 @@ class StudentRegistration(ParticipantRegistration):
from registration.forms import StudentRegistrationForm
return StudentRegistrationForm
def registration_informations(self):
informations = super().registration_informations()
if self.team and self.team.participation.tournament and self.under_18:
if not self.parental_authorization:
text = _("You have not uploaded your parental authorization. "
"You can do it by clicking on <a href=\"{parental_url}\">this link</a>.")
parental_url = reverse_lazy("registration:upload_user_parental_authorization", args=(self.id,))
content = format_lazy(text, parental_url=parental_url)
informations.append({
'title': _("Parental authorization"),
'type': "danger",
'priority': 5,
'content': content,
})
if not self.health_sheet:
text = _("You have not uploaded your health sheet. "
"You can do it by clicking on <a href=\"{health_url}\">this link</a>.")
health_url = reverse_lazy("registration:upload_user_health_sheet", args=(self.id,))
content = format_lazy(text, health_url=health_url)
informations.append({
'title': _("Health sheet"),
'type': "danger",
'priority': 5,
'content': content,
})
if not self.vaccine_sheet:
text = _("You have not uploaded your vaccine sheet. "
"You can do it by clicking on <a href=\"{vaccine_url}\">this link</a>.")
vaccine_url = reverse_lazy("registration:upload_user_vaccine_sheet", args=(self.id,))
content = format_lazy(text, vaccine_url=vaccine_url)
informations.append({
'title': _("Vaccine sheet"),
'type': "danger",
'priority': 5,
'content': content,
})
if self.team and self.team.participation.valid:
if self.payment.valid is False:
text = _("You have to pay {amount} € for your registration, or send a scholarship "
"notification or a payment proof. "
"You can do it on <a href=\"{url}\">the payment page</a>.")
url = reverse_lazy("registration:update_payment", args=(self.payment.id,))
content = format_lazy(text, amount=self.team.participation.tournament.price, url=url)
informations.append({
'title': _("Payment"),
'type': "danger",
'priority': 3,
'content': content,
})
elif self.payment.valid is None:
text = _("Your payment is under approval.")
content = text
informations.append({
'title': _("Payment"),
'type': "warning",
'priority': 3,
'content': content,
})
return informations
class Meta:
verbose_name = _("student registration")
verbose_name_plural = _("student registrations")
@ -334,6 +451,41 @@ class VolunteerRegistration(Registration):
from registration.forms import VolunteerRegistrationForm
return VolunteerRegistrationForm
def important_informations(self):
informations = []
for tournament in self.organized_tournaments.all():
if timezone.now() < tournament.inscription_limit \
or tournament.participations.filter(valid=True).count() < tournament.max_teams:
text = _("Registrations for tournament {tournament} are closing on {date:%Y-%m-%d %H:%M}. "
"There are for now {validated_teams} validated teams (+ {pending_teams} pending) "
"on {max_teams} expected.")
content = format_lazy(text, tournament=tournament.name, date=tournament.inscription_limit,
validated_teams=tournament.participations.filter(valid=True).count(),
pending_teams=tournament.participations.filter(valid=False).count(),
max_teams=tournament.max_teams)
informations.append({
'title': _("Registrations"),
'type': "info",
'priority': 2,
'content': content,
})
for pending_participation in tournament.participations.filter(valid=False).all():
text = _("The team {trigram} requested to be validated for the tournament of {tournament}. "
"You can check the status of the team on the <a href=\"{url}\">team page</a>.")
url = reverse_lazy("participation:team_detail", args=(pending_participation.team.id,))
content = format_lazy(text, trigram=pending_participation.team.trigram,
tournament=tournament.name, url=url)
informations.append({
'title': _("Pending validation"),
'type': "warning",
'priority': 4,
'content': content,
})
return informations
class Meta:
verbose_name = _("volunteer registration")
verbose_name_plural = _("volunteer registrations")

View File

@ -3,50 +3,8 @@
{% load crispy_forms_filters i18n %}
{% block content %}
<form method="post" enctype="multipart/form-data">
<div id="form-content">
<div class="alert alert-info text-justify">
<p>
{% blocktrans trimmed with price=payment.registration.team.participation.tournament.price %}
The price of the tournament is {{ price }} €. The participation fee is offered for coaches
and for students who have a scholarship. If so, please send us your scholarship attestation.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
You can pay with a credit card through
<a class="alert-link" href="https://www.helloasso.com/associations/animath/evenements/tfjm-2023-tournois-regionaux">our Hello Asso page</a>.
To make the validation of the payment easier, <span class="text-danger">please use the same e-mail
address that you use on this platform.</span> The payment verification will be checked automatically
under 10 minutes, you don't necessary need to fill this form.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
You can also send a bank transfer to the bank account of Animath. You must put in the reference of the
transfer the mention "TFJMpu" followed by the last name and the first name of the student.
{% endblocktrans %}
</p>
<p>
IBAN : FR76 1027 8065 0000 0206 4290 127<br>
BIC : CMCIFR2A
</p>
<p>
{% blocktrans trimmed %}
If any payment mean is available to you, please contact us at <a class="alert-link" href="mailto:contact@tfjm.org">contact@tfjm.org</a>
to find a solution to your difficulties.
{% endblocktrans %}
</p>
<div class="alert alert-warning">
Le formulaire de paiement est temporairement désactivé. Il sera accessible d'ici quelques jours.
</div>
{% csrf_token %}
{{ form|crispy }}
</div>
<button class="btn btn-primary" type="submit">{% trans "Update" %}</button>
</form>
{% endblock content %}

View File

@ -263,6 +263,7 @@ class UserUpdateView(UserMixin, UpdateView):
Update the detail about a user and its registration.
"""
model = User
context_object_name = "user_object"
form_class = UserForm
template_name = "registration/update_user.html"
@ -455,13 +456,17 @@ class PaymentUpdateView(LoginRequiredMixin, UpdateView):
def get_form(self, form_class=None):
form = super().get_form(form_class)
if not self.request.user.registration.is_admin:
del form.fields["type"].widget.choices[-1]
form.fields["type"].widget.choices = list(form.fields["type"].widget.choices)[:-1]
del form.fields["valid"]
return form
def form_valid(self, form):
if not self.request.user.registration.is_admin:
form.instance.valid = None
old_instance = Payment.objects.get(pk=self.object.pk)
if old_instance.scholarship_file:
old_instance.scholarship_file.delete()
old_instance.save()
return super().form_valid(form)

View File

@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block contenttitle %}
{% block content-title %}
<h1>À propos</h1>
{% endblock %}

View File

@ -1,15 +1,15 @@
{% load static i18n static %}
{% load i18n static %}
<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<html lang="{{ LANGUAGE_CODE|default:"en" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %} class="position-relative h-100">
<html lang="{{ LANGUAGE_CODE|default:"fr" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %} class="position-relative h-100">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>
{% block title %}{{ title }}{% endblock title %} - Plateforme du TFJM²
</title>
<meta name="description" content="Plateform d'inscription au TFJM².">
<meta name="description" content="Plateforme d'inscription au TFJM².">
{# Favicon #}
<link rel="shortcut icon" href="{% static "favicon.ico" %}">
@ -25,7 +25,7 @@
{# Bootstrap JavaScript #}
<script src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
{# bootstrap-select for beautyful selects and JQuery dependency #}
{# bootstrap-select for beautiful selects and JQuery dependency #}
<script src="{% static 'jquery/jquery.min.js' %}"></script>
<script src="{% static 'bootstrap-select/js/bootstrap-select.min.js' %}"></script>
<script src="{% static 'bootstrap-select/js/defaults-fr_FR.min.js' %}"></script>
@ -38,206 +38,31 @@
{% block extracss %}{% endblock %}
</head>
<body class="d-flex w-100 h-100 flex-column">
<nav class="navbar navbar-expand-lg fixed-navbar shadow-sm">
<div class="container-fluid">
<a class="navbar-brand" href="https://tfjm.org/">
<img src="{% static "tfjm.svg" %}" style="height: 2em;" alt="Logo TFJM²" id="navbar-logo">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNavDropdown"
aria-controls="navbarNavDropdown" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div id="navbarNavDropdown" class="collapse navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item active">
<a href="{% url "index" %}" class="nav-link"><i class="fas fa-home"></i> {% trans "Home" %}</a>
</li>
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#tournamentListModal">
<i class="fas fa-calendar-day"></i> {% trans "Tournaments" %}
</a>
</li>
{% if user.is_authenticated and user.registration.is_volunteer %}
<li class="nav-item active">
<a href="{% url "registration:user_list" %}" class="nav-link"><i class="fas fa-user"></i> {% trans "Users" %}</a>
</li>
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#teamsModal"><i class="fas fa-users"></i> {% trans "Teams" %}</a>
</li>
{% elif user.is_authenticated and user.registration.participates %}
{% if not user.registration.team %}
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#createTeamModal">
<i class="fas fa-users"></i> {% trans "Create team" %}
</a>
</li>
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#joinTeamModal">
<i class="fas fa-users"></i> {% trans "Join team" %}
</a>
</li>
{% else %}
<li class="nav-item active">
<a href="{% url "participation:my_team_detail" %}" class="nav-link">
<i class="fas fa-users"></i> {% trans "My team" %}
</a>
</li>
<li class="nav-item active">
<a href="{% url "participation:my_participation_detail" %}" class="nav-link">
<i class="fas fa-file-pdf"></i> {% trans "My participation" %}
</a>
</li>
{% endif %}
{% endif %}
{% if user.is_authenticated %}
{% if user.registration.is_volunteer or user.registration.team %}
<li class="nav-item active">
<a class="nav-link" href="{% url 'draw:index' %}">
<i class="fas fa-archive"></i> {% trans "Draw" %}
</a>
</li>
{% endif %}
{% endif %}
<li class="nav-item active d-none">
<a class="nav-link" href="{% url "participation:chat" %}">
<i class="fas fa-comments"></i> {% trans "Chat" %}
</a>
</li>
{% if user.registration.is_admin %}
<li class="nav-item active">
<a class="nav-link" href="{% url "admin:index" %}"><i class="fas fa-cog"></i> {% trans "Administration" %}</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav ms-auto">
{% if user.registration.is_admin %}
<form class="navbar-form d-flex" role="search" onsubmit="event.preventDefault()">
<div class="input-group">
<input type="text" class="form-control" placeholder="{% trans "Search" %}" name="q" id="search-term" value="{{ request.GET.q }}">
<div class="input-group-btn">
<button class="btn btn-default" data-bs-toggle="modal" data-bs-target="#searchModal"><i class="fa fa-search text-body"></i></button>
</div>
</div>
</form>
{% endif %}
{% if "_fake_user_id" in request.session %}
<li class="nav-item active">
<a class="nav-link" href="{% url "registration:reset_admin" %}?path={{ request.path }}"><i class="fas fa-tools"></i> {% trans "Return to admin view" %}</a>
</li>
{% endif %}
{% if not user.is_authenticated %}
<li class="nav-item active">
<a class="nav-link" href="{% url "registration:signup" %}"><i class="fas fa-user-plus"></i> {% trans "Register" %}</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#loginModal">
<i class="fas fa-sign-in-alt"></i> {% trans "Log in" %}
</a>
</li>
{% else %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink"
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-user"></i> {{ user.first_name }} {{ user.last_name }}
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
<li>
<a class="dropdown-item" href="{% url "registration:my_account_detail" %}">
<i class="fas fa-user"></i> {% trans "My account" %}
</a>
</li>
<li>
<a class="dropdown-item" href="{% url "logout" %}">
<i class="fas fa-sign-out-alt"></i> {% trans "Log out" %}
</a>
</li>
</ul>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<main class="mb-auto flex-shrink-0">
{% block fullcontent %}
<div class="{% block containertype %}container{% endblock %} my-3">
{% block contenttitle %}<h1>{{ title }}</h1>{% endblock %}
{% if user.is_authenticated and not user.registration.email_confirmed %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
{% url "registration:email_validation_resend" pk=user.pk as send_email_url %}
{% blocktrans trimmed %}
Your email address is not validated. Please click on the link you received by email.
You can resend a mail by clicking on <a href="{{ send_email_url }}">this link</a>.
{% endblocktrans %}
</div>
{% endif %}
<div id="messages">
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
{{ message | safe }}
</div>
{% endfor %}
</div>
{% include "navbar.html" %}
<div id="body-wrapper" class="row w-100 my-3">
<aside class="col-lg-2 px-2">
{% include "sidebar.html" %}
</aside>
<main class="col d-flex flex-column">
<div class="container">
{% block content-title %}<h1 id="content-title">{{ title }}</h1>{% endblock %}
{% include "messages.html" %}
<div id="content">
{% block content %}
<p>Default content...</p>
{% endblock content %}
</div>
</div>
{% endblock %}
</main>
</main>
<footer class="text-primary mt-auto py-2">
<div class="container-fluid">
<div class="row">
<div class="col-sm-1">
<span class="text-muted mr-1">
<a target="_blank" href="mailto:&#99;&#111;&#110;&#116;&#97;&#99;&#116;&#64;&#116;&#102;&#106;&#109;&#46;&#111;&#114;&#103;"
class="text-muted"><i class="fas fa-envelope"></i> {% trans "Contact us" %}</a>
</span>
</div>
<div class="col-sm-1">
<form action="{% url 'set_language' %}" method="post"
class="form-inline">
{% csrf_token %}
<select title="language" name="language"
class="form-control form-control-sm language"
onchange="this.form.submit()">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% for lang_code, lang_name in LANGUAGES %}
<option value="{{ lang_code }}"
{% if lang_code == LANGUAGE_CODE %}
selected{% endif %}>
{{ lang_name }} ({{ lang_code }})
</option>
{% endfor %}
</select> &nbsp;
<noscript>
<input type="submit">
</noscript>
</form>
</div>
<div class="col-sm-9">
<aside class="col-lg-2"></aside>
</div>
<a target="_blank" class="text-muted" href="{% url "about" %}">{% trans "About" %}</a> &nbsp; &mdash; &nbsp;
<a target="_blank" class="text-muted"
href="https://gitlab.com/animath/si/plateforme-tfjm">
<i class="fab fa-gitlab"></i>
</a>
</div>
<div class="col-sm-1 text-end">
<a href="#" class="text-muted">
<i class="fa fa-arrow-up" aria-hidden="true"></i>
</a>
</div>
</div>
</div>
</footer>
{% include "footer.html" %}
{% trans "All tournaments" as modal_title %}
{% include "base_modal.html" with modal_id="tournamentList" modal_additional_class="modal-lg" %}

View File

@ -0,0 +1,51 @@
{% load i18n static %}
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<footer class="text-primary mt-lg-auto">
<div class="container-fluid">
<div class="row">
<div class="col-sm-2 col-lg-1">
<span class="text-muted mr-1">
<a target="_blank" href="mailto:&#99;&#111;&#110;&#116;&#97;&#99;&#116;&#64;&#116;&#102;&#106;&#109;&#46;&#111;&#114;&#103;"
class="text-muted"><i class="fas fa-envelope"></i> {% trans "Contact us" %}</a>
</span>
</div>
<div class="col-sm-2 col-lg-1">
<form action="{% url 'set_language' %}" method="post"
class="form-inline">
{% csrf_token %}
<select title="language" name="language"
class="form-control form-control-sm language"
onchange="this.form.submit()">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% for lang_code, lang_name in LANGUAGES %}
<option value="{{ lang_code }}"
{% if lang_code == LANGUAGE_CODE %}
selected{% endif %}>
{{ lang_name }} ({{ lang_code }})
</option>
{% endfor %}
</select> &nbsp;
<noscript>
<input type="submit">
</noscript>
</form>
</div>
<div class="col-sm-7 col-lg-9">
<a target="_blank" class="text-muted" href="{% url "about" %}">{% trans "About" %}</a> &nbsp; &mdash; &nbsp;
<a target="_blank" class="text-muted" href="/doc/">Documentation</a> &nbsp; &mdash; &nbsp;
<a target="_blank" class="text-muted"
href="https://gitlab.com/animath/si/plateforme-tfjm">
<i class="fab fa-gitlab"></i>
</a>
</div>
<div class="col-sm-1 text-end">
<a href="#" class="text-muted">
<i class="fa fa-arrow-up" aria-hidden="true"></i>
</a>
</div>
</div>
</div>
</footer>

View File

@ -1,24 +1,6 @@
{% extends "base.html" %}
{% block content %}
<div>
<div class="alert alert-success">
<p>
Les inscriptions pour la session 2024 sont à présent ouvertes, vous pouvez créer votre compte.
Prenez garde toutefois aux dates indiquées qui sont pour l'instant provisoires.
</p>
<p>
Une documentation plus complète est disponible à l'adresse
<a href="https://inscription.tfjm.org/doc/">https://inscription.tfjm.org/doc/</a>
et sera progressivement actualisée.
</p>
</div>
<div class="alert alert-warning">
Diverses améliorations fonctionnelles pourront être apportées au site au cours des prochaines semaines.
</div>
<div class="jumbotron p-5">
<div class="row text-center">
<h1 class="display-4">
@ -69,8 +51,13 @@
<h2>J'ai une question</h2>
<p class="text-justify">
Pour toute question, vous pouvez soit la poser dans <code>#faq</code> dans l'onglet chat comme indiqué ci-dessus, soit nous
contacter par mail à l'adresse <a href="mailto:contact@tfjm.org">contact@tfjm.org</a>.
N'hésitez pas à consulter la <a href="/doc/" target="_blank">documentation</a> du site, pour vérifier si
la réponse ne s'y trouve pas déjà. Référez-vous également bien sûr au
<a href="https://tfjm.org/reglement/" target="_blank">règlement du 𝕋𝔽𝕁𝕄²</a>.
Pour toute autre question, n'hésitez pas à nous contacter par mail à l'adresse
<a href="mailto:&#99;&#111;&#110;&#116;&#97;&#99;&#116;&#64;&#116;&#102;&#106;&#109;&#46;&#111;&#114;&#103;">
&#99;&#111;&#110;&#116;&#97;&#99;&#116;&#64;&#116;&#102;&#106;&#109;&#46;&#111;&#114;&#103;
</a>.
</p>
<div class="alert alert-warning">
@ -78,6 +65,4 @@
ne pourrez malheureusement pas participer au 𝕋𝔽𝕁𝕄².
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,8 @@
<div id="messages">
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
{{ message | safe }}
</div>
{% endfor %}
</div>

124
tfjm/templates/navbar.html Normal file
View File

@ -0,0 +1,124 @@
{% load i18n static %}
<nav class="navbar navbar-expand-lg fixed-navbar shadow-sm">
<div class="container-fluid">
<a class="navbar-brand" href="https://tfjm.org/">
<img src="{% static "tfjm.svg" %}" style="height: 2em;" alt="Logo TFJM²" id="navbar-logo">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNavDropdown"
aria-controls="navbarNavDropdown" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div id="navbarNavDropdown" class="collapse navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item active">
<a href="{% url "index" %}" class="nav-link"><i class="fas fa-home"></i> {% trans "Home" %}</a>
</li>
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#tournamentListModal">
<i class="fas fa-calendar-day"></i> {% trans "Tournaments" %}
</a>
</li>
{% if user.is_authenticated and user.registration.is_volunteer %}
<li class="nav-item active">
<a href="{% url "registration:user_list" %}" class="nav-link"><i class="fas fa-user"></i> {% trans "Users" %}</a>
</li>
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#teamsModal"><i class="fas fa-users"></i> {% trans "Teams" %}</a>
</li>
{% elif user.is_authenticated and user.registration.participates %}
{% if not user.registration.team %}
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#createTeamModal">
<i class="fas fa-users"></i> {% trans "Create team" %}
</a>
</li>
<li class="nav-item active">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#joinTeamModal">
<i class="fas fa-users"></i> {% trans "Join team" %}
</a>
</li>
{% else %}
<li class="nav-item active">
<a href="{% url "participation:my_team_detail" %}" class="nav-link">
<i class="fas fa-users"></i> {% trans "My team" %}
</a>
</li>
<li class="nav-item active">
<a href="{% url "participation:my_participation_detail" %}" class="nav-link">
<i class="fas fa-file-pdf"></i> {% trans "My participation" %}
</a>
</li>
{% endif %}
{% endif %}
{% if user.is_authenticated %}
{% if user.registration.is_volunteer or user.registration.team %}
<li class="nav-item active">
<a class="nav-link" href="{% url 'draw:index' %}">
<i class="fas fa-archive"></i> {% trans "Draw" %}
</a>
</li>
{% endif %}
{% endif %}
<li class="nav-item active d-none">
<a class="nav-link" href="{% url "participation:chat" %}">
<i class="fas fa-comments"></i> {% trans "Chat" %}
</a>
</li>
{% if user.registration.is_admin %}
<li class="nav-item active">
<a class="nav-link" href="{% url "admin:index" %}"><i class="fas fa-cog"></i> {% trans "Administration" %}</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav ms-auto">
{% if user.registration.is_admin %}
<form class="navbar-form d-flex" role="search" onsubmit="event.preventDefault()">
<div class="input-group">
<input type="text" class="form-control" placeholder="{% trans "Search" %}" name="q" id="search-term" value="{{ request.GET.q }}">
<div class="input-group-btn">
<button class="btn btn-default" data-bs-toggle="modal" data-bs-target="#searchModal"><i class="fa fa-search text-body"></i></button>
</div>
</div>
</form>
{% endif %}
{% if "_fake_user_id" in request.session %}
<li class="nav-item active">
<a class="nav-link" href="{% url "registration:reset_admin" %}?path={{ request.path }}"><i class="fas fa-tools"></i> {% trans "Return to admin view" %}</a>
</li>
{% endif %}
{% if not user.is_authenticated %}
<li class="nav-item active">
<a class="nav-link" href="{% url "registration:signup" %}"><i class="fas fa-user-plus"></i> {% trans "Register" %}</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#loginModal">
<i class="fas fa-sign-in-alt"></i> {% trans "Log in" %}
</a>
</li>
{% else %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink"
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-user"></i> {{ user.first_name }} {{ user.last_name }}
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
<li>
<a class="dropdown-item" href="{% url "registration:my_account_detail" %}">
<i class="fas fa-user"></i> {% trans "My account" %}
</a>
</li>
<li>
<a class="dropdown-item" href="{% url "logout" %}">
<i class="fas fa-sign-out-alt"></i> {% trans "Log out" %}
</a>
</li>
</ul>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
{% load i18n crispy_forms_filters %}
{% block title %}{% trans "Log in" %}{% endblock %}
{% block contenttitle %}<h1>{% trans "Log in" %}</h1>{% endblock %}
{% block content-title %}<h1>{% trans "Log in" %}</h1>{% endblock %}
{% block content %}
{% if user.is_authenticated %}

View File

@ -0,0 +1,40 @@
{% load i18n %}
{% if user.is_authenticated %}
<div class="card">
<div class="card-header bg-dark-subtle">
<div class="d-lg-none btn" data-bs-toggle="collapse"
data-bs-target="#sidebar-card" aria-controls="sidebar-card" aria-expanded="false"
aria-label="Toggle information sidebar">
<h3 class="card-title">
{% trans "Informations" %}
<span class="d-lg-none">
<span class="badge text-small bg-danger">
<i class="fa fa-warning"></i>
{{ user.registration.important_informations|length }}
</span>
<span class="navbar-toggler-icon"></span>
</span>
</h3>
</div>
<div class="d-none d-lg-block">
<h3 class="card-title">{% trans "Informations" %}</h3>
</div>
</div>
<div id="sidebar-card" class="collapse d-lg-block">
<div class="card-body">
{% for information in user.registration.important_informations %}
<div class="card my-2">
<div class="card-header bg-dark-subtle">
<h5 class="card-title">{{ information.title }}</h5>
</div>
<div class="card-body bg-{{ information.type }}-subtle">
{{ information.content|safe }}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}