1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-09-28 20:33:32 +02:00

Compare commits

..

8 Commits

Author SHA1 Message Date
Otthorn
c4eea0be5e Merge branch 'qrcode' into 'main'
Draft: Qrcode

See merge request bde/nk20!196
2025-09-02 23:07:01 +02:00
Nicolas Margulies
e6f3084588 Added a first pass for automatically entering an activity with a qrcode 2023-10-11 18:01:51 +02:00
otthorn
145e55da75 remove useless comment 2022-03-22 15:06:04 +01:00
otthorn
d3ba95cdca Insecable space for more clarity 2022-03-22 15:04:41 +01:00
otthorn
8ffb0ebb56 Use DetailView 2022-03-22 14:59:01 +01:00
otthorn
5038af9e34 Final html template 2022-03-22 14:58:26 +01:00
otthorn
819b4214c9 Add QRCode View, URL and test template 2022-03-22 12:26:44 +01:00
otthorn
b8a93b0b75 Add link to QR code 2022-03-19 16:25:15 +01:00
21 changed files with 104 additions and 297 deletions

View File

@@ -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;

View File

@@ -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 %}

View File

@@ -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">

View File

@@ -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)

View File

@@ -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

View File

@@ -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),

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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

View File

@@ -73,7 +73,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>&nbsp;{% trans 'API token' %}
</a>
<a class="small badge badge-secondary" href="{% url 'member:qr_code' user_object.pk %}">
<i class="fa fa-qrcode"></i>&nbsp;{% trans 'QR Code' %}
</a> </a>
</div> </div>
{% endif %} {% endif %}

View File

@@ -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 %}

View 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 %}

View File

@@ -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'),
] ]

View File

@@ -407,6 +407,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 #

View File

@@ -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)

View File

@@ -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
] ]
} }
}, },

View File

@@ -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 () {

View File

@@ -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'

View File

@@ -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 }}

View File

@@ -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 %}