mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-09-28 04:18:19 +02:00
Compare commits
8 Commits
main
...
717501bdd7
Author | SHA1 | Date | |
---|---|---|---|
|
717501bdd7 | ||
|
e6f3084588 | ||
|
145e55da75 | ||
|
d3ba95cdca | ||
|
8ffb0ebb56 | ||
|
5038af9e34 | ||
|
819b4214c9 | ||
|
b8a93b0b75 |
@@ -38,6 +38,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<input id="alias" type="text" class="form-control" placeholder="Nom/note ...">
|
<input id="alias" type="text" class="form-control" placeholder="Nom/note ...">
|
||||||
|
<button id="trigger" class="btn btn-secondary">Click me !</button>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
@@ -63,15 +64,46 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
refreshBalance();
|
refreshBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function process_qrcode() {
|
||||||
|
let name = alias_obj.val();
|
||||||
|
$.get("/api/note/note?search=" + name + "&format=json").done(
|
||||||
|
function (res) {
|
||||||
|
let note = res.results[0];
|
||||||
|
$.post("/api/activity/entry/?format=json", {
|
||||||
|
csrfmiddlewaretoken: CSRF_TOKEN,
|
||||||
|
activity: {{ activity.id }},
|
||||||
|
note: note.id,
|
||||||
|
guest: null
|
||||||
|
}).done(function () {
|
||||||
|
addMsg(interpolate(gettext(
|
||||||
|
"Entry made for %s whose balance is %s €"),
|
||||||
|
[note.name, note.balance / 100]), "success", 4000);
|
||||||
|
reloadTable(true);
|
||||||
|
}).fail(function (xhr) {
|
||||||
|
errMsg(xhr.responseJSON, 4000);
|
||||||
|
});
|
||||||
|
}).fail(function (xhr) {
|
||||||
|
errMsg(xhr.responseJSON, 4000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
alias_obj.keyup(function(event) {
|
alias_obj.keyup(function(event) {
|
||||||
let code = event.originalEvent.keyCode
|
let code = event.originalEvent.keyCode
|
||||||
if (65 <= code <= 122 || code === 13) {
|
if (65 <= code <= 122 || code === 13) {
|
||||||
debounce(reloadTable)()
|
debounce(reloadTable)()
|
||||||
}
|
}
|
||||||
|
if (code === 0)
|
||||||
|
process_qrcode();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).ready(init);
|
$(document).ready(init);
|
||||||
|
|
||||||
|
alias_obj2 = document.getElementById("alias");
|
||||||
|
$("#trigger").click(function (e) {
|
||||||
|
addMsg("Clicked", "success", 1000);
|
||||||
|
alias_obj.val(alias_obj.val() + "\0");
|
||||||
|
alias_obj2.dispatchEvent(new KeyboardEvent('keyup'));
|
||||||
|
})
|
||||||
function init() {
|
function init() {
|
||||||
$(".table-row").click(function (e) {
|
$(".table-row").click(function (e) {
|
||||||
let target = e.target.parentElement;
|
let target = e.target.parentElement;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{% extends "base_search.html" %}
|
{% extends "base.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
@@ -44,8 +44,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<h3 class="card-header text-center">
|
<h3 class="card-header text-center">
|
||||||
{% trans "All activities" %}
|
{% trans "All activities" %}
|
||||||
</h3>
|
</h3>
|
||||||
{% render_table all %}
|
{% render_table table %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ block.super }}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{% comment %}
|
{% comment %}
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
{% load i18n perms pretty_money dict_get %}
|
{% load i18n perms pretty_money %}
|
||||||
{% url 'activity:activity_detail' activity.pk as activity_detail_url %}
|
{% url 'activity:activity_detail' activity.pk as activity_detail_url %}
|
||||||
|
|
||||||
<div id="activity_info" class="card bg-light shadow mb-3">
|
<div id="activity_info" class="card bg-light shadow mb-3">
|
||||||
@@ -53,12 +53,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<dt class="col-xl-6">{% trans 'opened'|capfirst %}</dt>
|
<dt class="col-xl-6">{% trans 'opened'|capfirst %}</dt>
|
||||||
<dd class="col-xl-6">{{ activity.open|yesno }}</dd>
|
<dd class="col-xl-6">{{ activity.open|yesno }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
{% if show_entries|dict_get:activity %}
|
|
||||||
<h2 class="text-center">
|
|
||||||
{{ entries_count|dict_get:activity }}
|
|
||||||
{% if entries_count|dict_get:activity >= 2 %}{% trans "entries" %}{% else %}{% trans "entry" %}{% endif %}
|
|
||||||
</h2>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-footer text-center">
|
<div class="card-footer text-center">
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
from django import template
|
|
||||||
|
|
||||||
|
|
||||||
def dict_get(d, key):
|
|
||||||
return d.get(key)
|
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
register.filter('dict_get', dict_get)
|
|
@@ -67,65 +67,32 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin
|
|||||||
tables = [
|
tables = [
|
||||||
lambda data: ActivityTable(data, prefix="all-"),
|
lambda data: ActivityTable(data, prefix="all-"),
|
||||||
lambda data: ActivityTable(data, prefix="upcoming-"),
|
lambda data: ActivityTable(data, prefix="upcoming-"),
|
||||||
lambda data: ActivityTable(data, prefix="search-"),
|
|
||||||
]
|
]
|
||||||
extra_context = {"title": _("Activities")}
|
extra_context = {"title": _("Activities")}
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
"""
|
return super().get_queryset(**kwargs).distinct()
|
||||||
Filter the user list with the given pattern.
|
|
||||||
"""
|
|
||||||
return super().get_queryset().distinct()
|
|
||||||
|
|
||||||
def get_tables_data(self):
|
def get_tables_data(self):
|
||||||
# first table = all activities, second table = upcoming, third table = search
|
# first table = all activities, second table = upcoming
|
||||||
|
|
||||||
# table search
|
|
||||||
qs = self.get_queryset().order_by('-date_start')
|
|
||||||
if "search" in self.request.GET and self.request.GET['search']:
|
|
||||||
pattern = self.request.GET['search']
|
|
||||||
|
|
||||||
# check regex
|
|
||||||
valid_regex = is_regex(pattern)
|
|
||||||
suffix = '__iregex' if valid_regex else '__istartswith'
|
|
||||||
prefix = '^' if valid_regex else ''
|
|
||||||
qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern})
|
|
||||||
| Q(**{f'organizer__name{suffix}': prefix + pattern})
|
|
||||||
| Q(**{f'organizer__note__alias__name{suffix}': prefix + pattern}))
|
|
||||||
else:
|
|
||||||
qs = qs.none()
|
|
||||||
search_table = qs.filter(PermissionBackend.filter_queryset(self.request, Activity, 'view'))
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
self.get_queryset().order_by("-date_start"),
|
self.get_queryset().order_by("-date_start"),
|
||||||
Activity.objects.filter(date_end__gt=timezone.now())
|
Activity.objects.filter(date_end__gt=timezone.now())
|
||||||
.filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))
|
.filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by("date_start"),
|
.order_by("date_start")
|
||||||
search_table,
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
tables = context["tables"]
|
tables = context["tables"]
|
||||||
for name, table in zip(["all", "upcoming", "table"], tables):
|
for name, table in zip(["table", "upcoming"], tables):
|
||||||
context[name] = table
|
context[name] = table
|
||||||
|
|
||||||
started_activities = self.get_queryset().filter(open=True, valid=True).distinct().all()
|
started_activities = self.get_queryset().filter(open=True, valid=True).distinct().all()
|
||||||
context["started_activities"] = started_activities
|
context["started_activities"] = started_activities
|
||||||
|
|
||||||
entries_count = {}
|
|
||||||
show_entries = {}
|
|
||||||
for activity in started_activities:
|
|
||||||
if activity.activity_type.manage_entries:
|
|
||||||
entries = Entry.objects.filter(activity=activity)
|
|
||||||
entries_count[activity] = entries.count()
|
|
||||||
|
|
||||||
show_entries[activity] = True
|
|
||||||
context["entries_count"] = entries_count
|
|
||||||
context["show_entries"] = show_entries
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@@ -170,14 +137,6 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMix
|
|||||||
"placeholder": ""
|
"placeholder": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.object.activity_type.manage_entries:
|
|
||||||
entries = Entry.objects.filter(activity=self.object)
|
|
||||||
context["entries_count"] = {self.object: entries.count()}
|
|
||||||
|
|
||||||
context["show_entries"] = {self.object: timezone.now() > timezone.localtime(self.object.date_start)}
|
|
||||||
else:
|
|
||||||
context["entries_count"] = {self.object: 0}
|
|
||||||
context["show_entries"] = {self.object: False}
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from .models import Food
|
from .models import Food
|
||||||
|
|
||||||
@@ -11,25 +10,10 @@ class FoodTable(tables.Table):
|
|||||||
"""
|
"""
|
||||||
List all foods.
|
List all foods.
|
||||||
"""
|
"""
|
||||||
qr_code_numbers = tables.Column(empty_values=(), verbose_name=_("QR Codes"), orderable=False)
|
|
||||||
|
|
||||||
date = tables.Column(empty_values=(), verbose_name=_("Arrival/creation date"), orderable=False)
|
|
||||||
|
|
||||||
def render_date(self, record):
|
|
||||||
if record.__class__.__name__ == "BasicFood":
|
|
||||||
return record.arrival_date.strftime("%d/%m/%Y %H:%M")
|
|
||||||
elif record.__class__.__name__ == "TransformedFood":
|
|
||||||
return record.creation_date.strftime("%d/%m/%Y %H:%M")
|
|
||||||
else:
|
|
||||||
return "--"
|
|
||||||
|
|
||||||
def render_qr_code_numbers(self, record):
|
|
||||||
return ", ".join(str(q.qr_code_number) for q in record.QR_code.all())
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Food
|
model = Food
|
||||||
template_name = 'django_tables2/bootstrap4.html'
|
template_name = 'django_tables2/bootstrap4.html'
|
||||||
fields = ('name', 'owner', 'qr_code_numbers', 'allergens', 'date', 'expiry_date')
|
fields = ('name', 'owner', 'allergens', 'expiry_date')
|
||||||
row_attrs = {
|
row_attrs = {
|
||||||
'class': 'table-row',
|
'class': 'table-row',
|
||||||
'data-href': lambda record: 'detail/' + str(record.pk),
|
'data-href': lambda record: 'detail/' + str(record.pk),
|
||||||
|
@@ -34,12 +34,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-check">
|
|
||||||
<label for="stock_only" class="form-check-label">
|
|
||||||
<input id="stock_only" name="stock_only" type="checkbox" class="checkboxinput form-check-input" checked>
|
|
||||||
{% trans "Filter with only food in stock" %}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<input id="searchbar" type="text" class="form-control"
|
<input id="searchbar" type="text" class="form-control"
|
||||||
placeholder="{% trans "Search by attribute such as name..." %}">
|
placeholder="{% trans "Search by attribute such as name..." %}">
|
||||||
</div>
|
</div>
|
||||||
@@ -120,26 +114,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
let old_pattern = null;
|
|
||||||
let searchbar_obj = $("#searchbar");
|
|
||||||
let stock_only_obj = $("#stock_only");
|
|
||||||
|
|
||||||
function reloadTable() {
|
|
||||||
let pattern = searchbar_obj.val();
|
|
||||||
|
|
||||||
$("#dynamic-table").load(location.pathname + "?search=" + pattern.replace(" ", "%20") + (
|
|
||||||
stock_only_obj.is(':checked') ? "" : "&stock=1") + " #dynamic-table");
|
|
||||||
}
|
|
||||||
|
|
||||||
searchbar_obj.keyup(reloadTable);
|
|
||||||
stock_only_obj.change(reloadTable);
|
|
||||||
|
|
||||||
$(document).on("click", ".table-row", function () {
|
|
||||||
window.document.location = $(this).data("href");
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.getElementById('goButton').addEventListener('click', function(event) {
|
document.getElementById('goButton').addEventListener('click', function(event) {
|
||||||
|
@@ -65,13 +65,9 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
|||||||
suffix = '__iregex' if valid_regex else '__istartswith'
|
suffix = '__iregex' if valid_regex else '__istartswith'
|
||||||
prefix = '^' if valid_regex else ''
|
prefix = '^' if valid_regex else ''
|
||||||
qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern})
|
qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern})
|
||||||
| Q(**{f'owner__name{suffix}': prefix + pattern})
|
| Q(**{f'owner__name{suffix}': prefix + pattern}))
|
||||||
| Q(**{f'owner__note__alias__name{suffix}': prefix + pattern}))
|
|
||||||
else:
|
else:
|
||||||
qs = qs.none()
|
qs = qs.none()
|
||||||
if "stock" not in self.request.GET or not self.request.GET["stock"] == '1':
|
|
||||||
qs = qs.filter(end_of_life='')
|
|
||||||
|
|
||||||
search_table = qs.filter(PermissionBackend.filter_queryset(self.request, Food, 'view'))
|
search_table = qs.filter(PermissionBackend.filter_queryset(self.request, Food, 'view'))
|
||||||
# table open
|
# table open
|
||||||
open_table = self.get_queryset().order_by('expiry_date').filter(
|
open_table = self.get_queryset().order_by('expiry_date').filter(
|
||||||
@@ -99,7 +95,6 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
|||||||
owner=club, end_of_life='').filter(
|
owner=club, end_of_life='').filter(
|
||||||
PermissionBackend.filter_queryset(self.request, Food, 'view')
|
PermissionBackend.filter_queryset(self.request, Food, 'view')
|
||||||
))
|
))
|
||||||
|
|
||||||
return [search_table, open_table, served_table] + club_table
|
return [search_table, open_table, served_table] + club_table
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
@@ -223,7 +218,7 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
copy = self.request.GET.get('copy', None)
|
copy = self.request.GET.get('copy', None)
|
||||||
if copy is not None:
|
if copy is not None:
|
||||||
food = BasicFood.objects.get(pk=copy)
|
food = BasicFood.objects.get(pk=copy)
|
||||||
|
print(context['form'].fields)
|
||||||
for field in context['form'].fields:
|
for field in context['form'].fields:
|
||||||
if field == 'allergens':
|
if field == 'allergens':
|
||||||
context['form'].fields[field].initial = getattr(food, field).all()
|
context['form'].fields[field].initial = getattr(food, field).all()
|
||||||
|
@@ -10,7 +10,6 @@ from django.contrib.auth.forms import AuthenticationForm
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.forms import CheckboxSelectMultiple
|
from django.forms import CheckboxSelectMultiple
|
||||||
from phonenumber_field.formfields import PhoneNumberField
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from note.models import NoteSpecial, Alias
|
from note.models import NoteSpecial, Alias
|
||||||
@@ -46,11 +45,6 @@ class ProfileForm(forms.ModelForm):
|
|||||||
A form for the extras field provided by the :model:`member.Profile` model.
|
A form for the extras field provided by the :model:`member.Profile` model.
|
||||||
"""
|
"""
|
||||||
# Remove widget=forms.HiddenInput() if you want to use report frequency.
|
# Remove widget=forms.HiddenInput() if you want to use report frequency.
|
||||||
phone_number = PhoneNumberField(
|
|
||||||
widget=forms.TextInput(attrs={"type": "tel", "class": "form-control"}),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"))
|
report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"))
|
||||||
|
|
||||||
last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date"))
|
last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date"))
|
||||||
@@ -78,12 +72,7 @@ class ProfileForm(forms.ModelForm):
|
|||||||
if not self.instance.section or (("department" in self.changed_data
|
if not self.instance.section or (("department" in self.changed_data
|
||||||
or "promotion" in self.changed_data) and "section" not in self.changed_data):
|
or "promotion" in self.changed_data) and "section" not in self.changed_data):
|
||||||
self.instance.section = self.instance.section_generated
|
self.instance.section = self.instance.section_generated
|
||||||
instance = super().save(commit=False)
|
return super().save(commit)
|
||||||
if instance.phone_number:
|
|
||||||
instance.phone_number = instance.phone_number.as_e164
|
|
||||||
if commit:
|
|
||||||
instance.save()
|
|
||||||
return instance
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Profile
|
model = Profile
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
def save_user_profile(instance, created, raw, **_kwargs):
|
def save_user_profile(instance, created, raw, **_kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -18,7 +16,7 @@ def save_user_profile(instance, created, raw, **_kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def update_wei_registration_fee_on_membership_creation(sender, instance, created, **kwargs):
|
def update_wei_registration_fee_on_membership_creation(sender, instance, created, **kwargs):
|
||||||
if not hasattr(instance, "_no_signal") and 'wei' in settings.INSTALLED_APPS and created:
|
if not hasattr(instance, "_no_signal") and created:
|
||||||
from wei.models import WEIRegistration
|
from wei.models import WEIRegistration
|
||||||
if instance.club.id == 1 or instance.club.id == 2:
|
if instance.club.id == 1 or instance.club.id == 2:
|
||||||
registrations = WEIRegistration.objects.filter(
|
registrations = WEIRegistration.objects.filter(
|
||||||
@@ -31,8 +29,8 @@ def update_wei_registration_fee_on_membership_creation(sender, instance, created
|
|||||||
|
|
||||||
|
|
||||||
def update_wei_registration_fee_on_club_change(sender, instance, **kwargs):
|
def update_wei_registration_fee_on_club_change(sender, instance, **kwargs):
|
||||||
if not hasattr(instance, "_no_signal") and 'wei' in settings.INSTALLED_APPS and (instance.id == 1 or instance.id == 2):
|
from wei.models import WEIRegistration
|
||||||
from wei.models import WEIRegistration
|
if not hasattr(instance, "_no_signal") and (instance.id == 1 or instance.id == 2):
|
||||||
registrations = WEIRegistration.objects.filter(
|
registrations = WEIRegistration.objects.filter(
|
||||||
wei__year=instance.membership_start.year,
|
wei__year=instance.membership_start.year,
|
||||||
)
|
)
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
<dt class="col-xl-6">{% trans 'username'|capfirst %}</dt>
|
<dt class="col-xl-6">{% trans 'username'|capfirst %}</dt>
|
||||||
<dd class="col-xl-6">{{ user_object.username }}</dd>
|
<dd class="col-xl-6">{{ user_object.username }}</dd>
|
||||||
|
|
||||||
{% if family_app_installed %}
|
|
||||||
<dt class="col-xl-6">{% trans 'family'|capfirst %}</dt>
|
<dt class="col-xl-6">{% trans 'family'|capfirst %}</dt>
|
||||||
<dd class="col-xl-6">
|
<dd class="col-xl-6">
|
||||||
{% if families %}
|
{% if families %}
|
||||||
@@ -18,7 +17,6 @@
|
|||||||
<span class="text-muted">Aucune</span>
|
<span class="text-muted">Aucune</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</dd>
|
</dd>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if user_object.pk == user.pk %}
|
{% if user_object.pk == user.pk %}
|
||||||
<dt class="col-xl-6">{% trans 'password'|capfirst %}</dt>
|
<dt class="col-xl-6">{% trans 'password'|capfirst %}</dt>
|
||||||
@@ -73,7 +71,10 @@
|
|||||||
{% if user_object.pk == user.pk %}
|
{% if user_object.pk == user.pk %}
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a class="small badge badge-secondary" href="{% url 'member:auth_token' %}">
|
<a class="small badge badge-secondary" href="{% url 'member:auth_token' %}">
|
||||||
<i class="fa fa-cogs"></i>{% trans 'API token' %}
|
<i class="fa fa-cogs"></i> {% trans 'API token' %}
|
||||||
|
</a>
|
||||||
|
<a class="small badge badge-secondary" href="{% url 'member:qr_code' user_object.pk %}">
|
||||||
|
<i class="fa fa-qrcode"></i> {% trans 'QR Code' %}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@@ -10,7 +10,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="post" id="profile-form">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form | crispy }}
|
{{ form | crispy }}
|
||||||
{{ profile_form | crispy }}
|
{{ profile_form | crispy }}
|
||||||
@@ -21,45 +21,3 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extrajavascript %}
|
|
||||||
<!-- intl-tel-input CSS/JS -->
|
|
||||||
<script>
|
|
||||||
(() => {
|
|
||||||
const input = document.querySelector("input[name='phone_number']");
|
|
||||||
const form = document.querySelector("#profile-form");
|
|
||||||
|
|
||||||
if (!input || !form || input.type === "hidden" || input.disabled || input.readOnly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iti = window.intlTelInput(input, {
|
|
||||||
initialCountry: "auto",
|
|
||||||
nationalMode: false,
|
|
||||||
autoPlaceholder: "off",
|
|
||||||
geoIpLookup: callback => {
|
|
||||||
fetch("https://ipapi.co/json")
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => callback(data.country_code))
|
|
||||||
.catch(() => callback("fr"));
|
|
||||||
},
|
|
||||||
loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/utils.js"),
|
|
||||||
});
|
|
||||||
|
|
||||||
form.addEventListener("submit", function(e){
|
|
||||||
if (!input.value.trim()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const number = iti.getNumber(intlTelInput.utils.numberFormat.E164);
|
|
||||||
if (number) {
|
|
||||||
input.value = number;
|
|
||||||
form.submit();
|
|
||||||
} else {
|
|
||||||
e.preventDefault();
|
|
||||||
input.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
36
apps/member/templates/member/qr_code.html
Normal file
36
apps/member/templates/member/qr_code.html
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
{% endcomment %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card bg-light">
|
||||||
|
<h3 class="card-header text-center">
|
||||||
|
{% trans "QR Code for" %} {{ user_object.username }} ({{ user_object.first_name }} {{user_object.last_name }})
|
||||||
|
</h3>
|
||||||
|
<div class="text-center" id="qrcode">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrajavascript %}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script>
|
||||||
|
var qrc = new QRCode(document.getElementById("qrcode"), {
|
||||||
|
text: "{{ user_object.pk }}\0",
|
||||||
|
width: 1024,
|
||||||
|
height: 1024
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extracss %}
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
@@ -25,4 +25,5 @@ urlpatterns = [
|
|||||||
path('user/<int:pk>/aliases/', views.ProfileAliasView.as_view(), name="user_alias"),
|
path('user/<int:pk>/aliases/', views.ProfileAliasView.as_view(), name="user_alias"),
|
||||||
path('user/<int:pk>/trust', views.ProfileTrustView.as_view(), name="user_trust"),
|
path('user/<int:pk>/trust', views.ProfileTrustView.as_view(), name="user_trust"),
|
||||||
path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'),
|
path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'),
|
||||||
|
path('user/<int:pk>/qr_code/', views.QRCodeView.as_view(), name='qr_code'),
|
||||||
]
|
]
|
||||||
|
@@ -207,10 +207,9 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
|||||||
modified_note.is_active = True
|
modified_note.is_active = True
|
||||||
context["can_unlock_note"] = not user.note.is_active and PermissionBackend\
|
context["can_unlock_note"] = not user.note.is_active and PermissionBackend\
|
||||||
.check_perm(self.request, "note.change_noteuser_is_active", modified_note)
|
.check_perm(self.request, "note.change_noteuser_is_active", modified_note)
|
||||||
if 'family' in settings.INSTALLED_APPS:
|
|
||||||
context["family_app_installed"] = True
|
families = Family.objects.filter(memberships__user=user).distinct()
|
||||||
families = Family.objects.filter(memberships__user=user).distinct()
|
context["families"] = families
|
||||||
context["families"] = families
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@@ -407,6 +406,14 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
|
|||||||
context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
|
context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
class QRCodeView(LoginRequiredMixin, DetailView):
|
||||||
|
"""
|
||||||
|
Affiche le QR Code
|
||||||
|
"""
|
||||||
|
model = User
|
||||||
|
context_object_name = "user_object"
|
||||||
|
template_name = "member/qr_code.html"
|
||||||
|
extra_context = {"title": _("QR Code")}
|
||||||
|
|
||||||
# ******************************* #
|
# ******************************* #
|
||||||
# CLUB #
|
# CLUB #
|
||||||
|
@@ -67,8 +67,6 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
last.quantity = 1
|
last.quantity = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (last.note.club) {
|
if (last.note.club) {
|
||||||
$('#last_name').val(last.note.name)
|
$('#last_name').val(last.note.name)
|
||||||
$('#first_name').val(last.note.name)
|
$('#first_name').val(last.note.name)
|
||||||
@@ -113,7 +111,6 @@ $(document).ready(function () {
|
|||||||
dest.removeClass('d-none')
|
dest.removeClass('d-none')
|
||||||
$('#dest_note_list').removeClass('d-none')
|
$('#dest_note_list').removeClass('d-none')
|
||||||
$('#debit_type').addClass('d-none')
|
$('#debit_type').addClass('d-none')
|
||||||
$('#reason').val('')
|
|
||||||
|
|
||||||
$('#source_note_label').text(select_emitters_label)
|
$('#source_note_label').text(select_emitters_label)
|
||||||
$('#dest_note_label').text(select_receveirs_label)
|
$('#dest_note_label').text(select_receveirs_label)
|
||||||
@@ -137,7 +134,6 @@ $(document).ready(function () {
|
|||||||
dest.val('')
|
dest.val('')
|
||||||
dest.tooltip('hide')
|
dest.tooltip('hide')
|
||||||
$('#debit_type').addClass('d-none')
|
$('#debit_type').addClass('d-none')
|
||||||
$('#reason').val('Rechargement note')
|
|
||||||
|
|
||||||
$('#source_note_label').text(transfer_type_label)
|
$('#source_note_label').text(transfer_type_label)
|
||||||
$('#dest_note_label').text(select_receveir_label)
|
$('#dest_note_label').text(select_receveir_label)
|
||||||
@@ -166,7 +162,6 @@ $(document).ready(function () {
|
|||||||
dest.addClass('d-none')
|
dest.addClass('d-none')
|
||||||
dest.tooltip('hide')
|
dest.tooltip('hide')
|
||||||
$('#debit_type').removeClass('d-none')
|
$('#debit_type').removeClass('d-none')
|
||||||
$('#reason').val('')
|
|
||||||
|
|
||||||
$('#source_note_label').text(select_emitter_label)
|
$('#source_note_label').text(select_emitter_label)
|
||||||
$('#dest_note_label').text(transfer_type_label)
|
$('#dest_note_label').text(transfer_type_label)
|
||||||
|
@@ -4430,22 +4430,6 @@
|
|||||||
"description": "Modifier le type de caution de mon inscription WEI tant qu'elle n'est pas validée"
|
"description": "Modifier le type de caution de mon inscription WEI tant qu'elle n'est pas validée"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"model": "permission.permission",
|
|
||||||
"pk": 298,
|
|
||||||
"fields": {
|
|
||||||
"model": [
|
|
||||||
"wei",
|
|
||||||
"bus"
|
|
||||||
],
|
|
||||||
"query": "{\"pk\": [\"membership\", \"weimembership\", \"bus\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}",
|
|
||||||
"type": "change",
|
|
||||||
"mask": 2,
|
|
||||||
"field": "information_json",
|
|
||||||
"permanent": false,
|
|
||||||
"description": "Modifier les informations du bus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"model": "permission.permission",
|
"model": "permission.permission",
|
||||||
"pk": 311,
|
"pk": 311,
|
||||||
@@ -4849,10 +4833,7 @@
|
|||||||
221,
|
221,
|
||||||
247,
|
247,
|
||||||
258,
|
258,
|
||||||
259,
|
259
|
||||||
260,
|
|
||||||
263,
|
|
||||||
265
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4864,6 +4845,7 @@
|
|||||||
"name": "Pr\u00e9sident\u22c5e de club",
|
"name": "Pr\u00e9sident\u22c5e de club",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
62,
|
62,
|
||||||
|
135,
|
||||||
142
|
142
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -5140,8 +5122,7 @@
|
|||||||
289,
|
289,
|
||||||
290,
|
290,
|
||||||
291,
|
291,
|
||||||
293,
|
293
|
||||||
298
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5252,9 +5233,7 @@
|
|||||||
168,
|
168,
|
||||||
176,
|
176,
|
||||||
177,
|
177,
|
||||||
197,
|
197
|
||||||
311,
|
|
||||||
319
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5334,8 +5313,7 @@
|
|||||||
289,
|
289,
|
||||||
290,
|
290,
|
||||||
291,
|
291,
|
||||||
293,
|
293
|
||||||
298
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -17,7 +17,7 @@ from ...models import WEIMembership, Bus
|
|||||||
|
|
||||||
WORDS = {
|
WORDS = {
|
||||||
'list': [
|
'list': [
|
||||||
'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nerd et geek', 'Jeux de rôles et danse rock',
|
'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nert et geek', 'Jeux de rôles et danse rock',
|
||||||
'Strass et paillettes', 'Spectaculaire', 'Splendide', 'Flow inégalable', 'Rap', 'Battles légendaires',
|
'Strass et paillettes', 'Spectaculaire', 'Splendide', 'Flow inégalable', 'Rap', 'Battles légendaires',
|
||||||
'Techno', 'Alcool', 'Kiffeur·euse', 'Rugby', 'Médiéval', 'Festif',
|
'Techno', 'Alcool', 'Kiffeur·euse', 'Rugby', 'Médiéval', 'Festif',
|
||||||
'Stylé', 'Chipie', 'Rétro', 'Vache', 'Farfadet', 'Fanfare',
|
'Stylé', 'Chipie', 'Rétro', 'Vache', 'Farfadet', 'Fanfare',
|
||||||
@@ -57,7 +57,7 @@ WORDS = {
|
|||||||
42: "Un burgouzz de valouzz",
|
42: "Un burgouzz de valouzz",
|
||||||
47: "Un ocarina (pour me téléporter hors de ce bourbier)",
|
47: "Un ocarina (pour me téléporter hors de ce bourbier)",
|
||||||
48: "Des paillettes, un micro de karaoké et une enceinte bluetooth",
|
48: "Des paillettes, un micro de karaoké et une enceinte bluetooth",
|
||||||
45: "Un kebab",
|
45: "",
|
||||||
44: "Une 86 et un caisson pour taper du pied",
|
44: "Une 86 et un caisson pour taper du pied",
|
||||||
46: "Une épée, un ballon et une tireuse",
|
46: "Une épée, un ballon et une tireuse",
|
||||||
43: "Des lunettes de soleil",
|
43: "Des lunettes de soleil",
|
||||||
@@ -176,33 +176,7 @@ WORDS = {
|
|||||||
49: "Soirée raclette !"
|
49: "Soirée raclette !"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
'stats': [
|
|
||||||
{
|
|
||||||
"question": """Le WEI est structuré par bus, et au sein de chaque bus, par équipes.
|
|
||||||
Pour toi, être dans une équipe où tout le monde reste sobre (primo-entrants comme encadrants) c'est :""",
|
|
||||||
"answers": [
|
|
||||||
(1, "Inenvisageable"),
|
|
||||||
(2, "À contre cœur"),
|
|
||||||
(3, "Pourquoi pas"),
|
|
||||||
(4, "Souhaitable"),
|
|
||||||
(5, "Nécessaire"),
|
|
||||||
],
|
|
||||||
"help_text": "(De toute façon aucun alcool n'est consommé pendant les trajets du bus, ni aller, ni retour.)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Faire partie d'un bus qui n'apporte pas de boisson alcoolisée pour ses membres, pour toi c'est :",
|
|
||||||
"answers": [
|
|
||||||
(1, "Inenvisageable"),
|
|
||||||
(2, "À contre cœur"),
|
|
||||||
(3, "Pourquoi pas"),
|
|
||||||
(4, "Souhaitable"),
|
|
||||||
(5, "Nécessaire"),
|
|
||||||
],
|
|
||||||
"help_text": """(Tout les bus apportent de l'alcool cette année, cette question sert à l'organisation pour l'année prochaine.
|
|
||||||
De plus il y aura de toute façon de l'alcool commun au WEI et aucun alcool n'est consommé pendant les trajets en bus.)""",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IMAGES = {
|
IMAGES = {
|
||||||
@@ -261,7 +235,7 @@ class WEISurveyForm2025(forms.Form):
|
|||||||
all_preferred_words = WORDS['list']
|
all_preferred_words = WORDS['list']
|
||||||
rng.shuffle(all_preferred_words)
|
rng.shuffle(all_preferred_words)
|
||||||
self.fields["words"].choices = [(w, w) for w in all_preferred_words]
|
self.fields["words"].choices = [(w, w) for w in all_preferred_words]
|
||||||
elif information.step <= len(WORDS['questions']):
|
else:
|
||||||
questions = list(WORDS['questions'].items())
|
questions = list(WORDS['questions'].items())
|
||||||
idx = information.step - 1
|
idx = information.step - 1
|
||||||
if idx < len(questions):
|
if idx < len(questions):
|
||||||
@@ -277,15 +251,6 @@ class WEISurveyForm2025(forms.Form):
|
|||||||
widget=OptionalImageRadioSelect(images=IMAGES.get(q, {})),
|
widget=OptionalImageRadioSelect(images=IMAGES.get(q, {})),
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
elif information.step == len(WORDS['questions']) + 1:
|
|
||||||
for i, v in enumerate(WORDS['stats']):
|
|
||||||
self.fields[f'stat_{i}'] = forms.ChoiceField(
|
|
||||||
label=v['question'],
|
|
||||||
choices=v['answers'],
|
|
||||||
widget=forms.RadioSelect(),
|
|
||||||
required=False,
|
|
||||||
help_text=_(v.get('help_text', ''))
|
|
||||||
)
|
|
||||||
|
|
||||||
def clean_words(self):
|
def clean_words(self):
|
||||||
data = self.cleaned_data['words']
|
data = self.cleaned_data['words']
|
||||||
@@ -412,7 +377,7 @@ class WEISurvey2025(WEISurvey):
|
|||||||
setattr(self.information, "word" + str(i), word)
|
setattr(self.information, "word" + str(i), word)
|
||||||
self.information.step += 1
|
self.information.step += 1
|
||||||
self.save()
|
self.save()
|
||||||
elif 1 <= self.information.step <= len(WORDS['questions']):
|
else:
|
||||||
questions = list(WORDS['questions'].keys())
|
questions = list(WORDS['questions'].keys())
|
||||||
idx = self.information.step - 1
|
idx = self.information.step - 1
|
||||||
if idx < len(questions):
|
if idx < len(questions):
|
||||||
@@ -420,13 +385,6 @@ class WEISurvey2025(WEISurvey):
|
|||||||
setattr(self.information, q, form.cleaned_data[q])
|
setattr(self.information, q, form.cleaned_data[q])
|
||||||
self.information.step += 1
|
self.information.step += 1
|
||||||
self.save()
|
self.save()
|
||||||
else:
|
|
||||||
for i, __ in enumerate(WORDS['stats']):
|
|
||||||
ans = form.cleaned_data.get(f'stat_{i}')
|
|
||||||
if ans is not None:
|
|
||||||
setattr(self.information, f'stat_{i}', ans)
|
|
||||||
self.information.step += 1
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_algorithm_class(cls):
|
def get_algorithm_class(cls):
|
||||||
@@ -436,7 +394,7 @@ class WEISurvey2025(WEISurvey):
|
|||||||
"""
|
"""
|
||||||
The survey is complete once the bus is chosen.
|
The survey is complete once the bus is chosen.
|
||||||
"""
|
"""
|
||||||
return self.information.step > len(WORDS['questions']) + 1
|
return self.information.step > len(WORDS['questions'])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 34 KiB |
@@ -11,7 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form id="registration-form" method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
{{ membership_form|crispy }}
|
{{ membership_form|crispy }}
|
||||||
@@ -22,46 +22,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extrajavascript %}
|
{% block extrajavascript %}
|
||||||
<!-- intl-tel-input CSS/JS -->
|
|
||||||
<script>
|
|
||||||
(() => {
|
|
||||||
const input = document.querySelector("input[name='emergency_contact_phone']");
|
|
||||||
const form = document.querySelector("#registration-form");
|
|
||||||
|
|
||||||
if (!input || !form || input.type === "hidden" || input.disabled || input.readOnly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iti = window.intlTelInput(input, {
|
|
||||||
initialCountry: "auto",
|
|
||||||
nationalMode: false,
|
|
||||||
autoPlaceholder: "off",
|
|
||||||
geoIpLookup: callback => {
|
|
||||||
fetch("https://ipapi.co/json")
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => callback(data.country_code))
|
|
||||||
.catch(() => callback("fr"));
|
|
||||||
},
|
|
||||||
loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/utils.js"),
|
|
||||||
});
|
|
||||||
|
|
||||||
form.addEventListener("submit", function(e){
|
|
||||||
if (!input.value.trim()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const number = iti.getNumber(intlTelInput.utils.numberFormat.E164);
|
|
||||||
if (number) {
|
|
||||||
input.value = number;
|
|
||||||
form.submit();
|
|
||||||
} else {
|
|
||||||
e.preventDefault();
|
|
||||||
input.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% if not object.membership %}
|
{% if not object.membership %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
@@ -53,11 +53,9 @@ class TestWEIAlgorithm(TestCase):
|
|||||||
birth_date='2000-01-01',
|
birth_date='2000-01-01',
|
||||||
)
|
)
|
||||||
information = WEISurveyInformation2025(registration)
|
information = WEISurveyInformation2025(registration)
|
||||||
for j in range(1, 1 + NB_WORDS):
|
for j in range(1, 21):
|
||||||
setattr(information, f'word{j}', random.choice(WORDS['list']))
|
setattr(information, f'word{j}', random.choice(WORDS['list']))
|
||||||
for q in WORDS['questions']:
|
information.step = 20
|
||||||
setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys())))
|
|
||||||
information.step = len(WORDS['questions']) + 2
|
|
||||||
information.save(registration)
|
information.save(registration)
|
||||||
registration.save()
|
registration.save()
|
||||||
|
|
||||||
@@ -89,7 +87,7 @@ class TestWEIAlgorithm(TestCase):
|
|||||||
setattr(information, f'word{j}', random.choice(WORDS['list']))
|
setattr(information, f'word{j}', random.choice(WORDS['list']))
|
||||||
for q in WORDS['questions']:
|
for q in WORDS['questions']:
|
||||||
setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys())))
|
setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys())))
|
||||||
information.step = len(WORDS['questions']) + 2
|
information.step = len(WORDS['questions']) + 1
|
||||||
information.save(registration)
|
information.save(registration)
|
||||||
registration.save()
|
registration.save()
|
||||||
survey = WEISurvey2025(registration)
|
survey = WEISurvey2025(registration)
|
||||||
|
@@ -770,7 +770,7 @@ msgstr "Créer une famille ou un défi"
|
|||||||
|
|
||||||
#: apps/family/templates/family/manage.html:96
|
#: apps/family/templates/family/manage.html:96
|
||||||
msgid "Add a family"
|
msgid "Add a family"
|
||||||
msgstr "Fonder une famille"
|
msgstr "Ajouter une famille"
|
||||||
|
|
||||||
#: apps/family/templates/family/manage.html:101
|
#: apps/family/templates/family/manage.html:101
|
||||||
msgid "Add a challenge"
|
msgid "Add a challenge"
|
||||||
|
@@ -306,8 +306,8 @@ PIC_WIDTH = 200
|
|||||||
PIC_RATIO = 1
|
PIC_RATIO = 1
|
||||||
|
|
||||||
# Custom phone number format
|
# Custom phone number format
|
||||||
PHONENUMBER_DB_FORMAT = 'E164'
|
PHONENUMBER_DB_FORMAT = 'NATIONAL'
|
||||||
PHONENUMBER_DEFAULT_REGION = None
|
PHONENUMBER_DEFAULT_REGION = 'FR'
|
||||||
|
|
||||||
# We add custom information to CAS, in order to give a normalized name to other services
|
# We add custom information to CAS, in order to give a normalized name to other services
|
||||||
CAS_AUTH_CLASS = 'member.auth.CustomAuthUser'
|
CAS_AUTH_CLASS = 'member.auth.CustomAuthUser'
|
||||||
|
@@ -30,8 +30,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<link rel="stylesheet" href="{% static "font-awesome/css/font-awesome.min.css" %}">
|
<link rel="stylesheet" href="{% static "font-awesome/css/font-awesome.min.css" %}">
|
||||||
<link rel="stylesheet" href="{% static "css/custom.css" %}">
|
<link rel="stylesheet" href="{% static "css/custom.css" %}">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/css/intlTelInput.css">
|
|
||||||
|
|
||||||
{# JQuery, Bootstrap and Turbolinks JavaScript #}
|
{# JQuery, Bootstrap and Turbolinks JavaScript #}
|
||||||
<script src="{% static "jquery/jquery.min.js" %}"></script>
|
<script src="{% static "jquery/jquery.min.js" %}"></script>
|
||||||
<script src="{% static "popper.js/umd/popper.min.js" %}"></script>
|
<script src="{% static "popper.js/umd/popper.min.js" %}"></script>
|
||||||
@@ -43,8 +41,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{# Translation in javascript files #}
|
{# Translation in javascript files #}
|
||||||
<script src="{% static "js/jsi18n/"|add:LANGUAGE_CODE|add:".js" %}"></script>
|
<script src="{% static "js/jsi18n/"|add:LANGUAGE_CODE|add:".js" %}"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/intlTelInput.min.js"></script>
|
|
||||||
|
|
||||||
{# If extra ressources are needed for a form, load here #}
|
{# If extra ressources are needed for a form, load here #}
|
||||||
{% if form.media %}
|
{% if form.media %}
|
||||||
{{ form.media }}
|
{{ form.media }}
|
||||||
|
@@ -19,7 +19,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="post" id="profile_form">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
{{ profile_form|crispy }}
|
{{ profile_form|crispy }}
|
||||||
@@ -31,45 +31,3 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extrajavascript %}
|
|
||||||
<!-- intl-tel-input CSS/JS -->
|
|
||||||
<script>
|
|
||||||
(() => {
|
|
||||||
const input = document.querySelector("input[name='phone_number']");
|
|
||||||
const form = document.querySelector("#profile_form");
|
|
||||||
|
|
||||||
if (!input || !form || input.type === "hidden" || input.disabled || input.readOnly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iti = window.intlTelInput(input, {
|
|
||||||
initialCountry: "auto",
|
|
||||||
nationalMode: false,
|
|
||||||
autoPlaceholder: "off",
|
|
||||||
geoIpLookup: callback => {
|
|
||||||
fetch("https://ipapi.co/json")
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => callback(data.country_code))
|
|
||||||
.catch(() => callback("fr"));
|
|
||||||
},
|
|
||||||
loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/utils.js"),
|
|
||||||
});
|
|
||||||
|
|
||||||
form.addEventListener("submit", function(e){
|
|
||||||
if (!input.value.trim()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const number = iti.getNumber(intlTelInput.utils.numberFormat.E164);
|
|
||||||
if (number) {
|
|
||||||
input.value = number;
|
|
||||||
form.submit();
|
|
||||||
} else {
|
|
||||||
e.preventDefault();
|
|
||||||
input.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
Reference in New Issue
Block a user