Compare commits
47 Commits
family
...
b1c0e79b28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1c0e79b28 | ||
|
|
7af3c42a02 | ||
|
|
73b63186fd | ||
|
|
e119e2295c | ||
|
|
37beb8f421 | ||
|
|
cae86bcd46 | ||
|
|
74aee64161 | ||
|
|
206a967827 | ||
|
|
04001202f2 | ||
|
|
69aedccbae | ||
|
|
af36d1427a | ||
|
|
75a59e0a7a | ||
|
|
af39bf7068 | ||
|
|
4c40566513 | ||
|
|
7c45b59298 | ||
|
|
418268db27 | ||
|
|
73045586a3 | ||
|
|
22d668a75c | ||
|
|
5dfa12fad2 | ||
|
|
5af69f719d | ||
|
|
4f6b1d5b6c | ||
|
|
d4cb464169 | ||
|
|
27a1f36183 | ||
|
|
83c8b9a3d0 | ||
|
|
cb3b34f874 | ||
|
|
0962a3735e | ||
|
|
9907cfbd86 | ||
|
|
ad90887691 | ||
|
|
47d2476b51 | ||
|
|
5d8720cf46 | ||
|
|
8700144dea | ||
|
|
d17ab26f2f | ||
|
|
297f289d7e | ||
|
|
034ad9a4ce | ||
|
|
897d37f74d | ||
|
|
42fb0aa2d6 | ||
|
|
4bc43ec3cb | ||
|
|
00737da69f | ||
|
|
0934b8fa34 | ||
|
|
7633c9ab4b | ||
|
|
e6f3084588 | ||
|
|
145e55da75 | ||
|
|
d3ba95cdca | ||
|
|
8ffb0ebb56 | ||
|
|
5038af9e34 | ||
|
|
819b4214c9 | ||
|
|
b8a93b0b75 |
@@ -37,6 +37,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<div id="guests_table">
|
||||
{% render_table guests %}
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<button class="btn btn-block btn-primary mb-3" onclick="window.location.href='?_export=1&table=guests'">
|
||||
{% trans "Export to CSV" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -38,6 +38,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</a>
|
||||
|
||||
<input id="alias" type="text" class="form-control" placeholder="Nom/note ...">
|
||||
<button id="trigger" class="btn btn-secondary">Click me !</button>
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -63,15 +64,46 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
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) {
|
||||
let code = event.originalEvent.keyCode
|
||||
if (65 <= code <= 122 || code === 13) {
|
||||
debounce(reloadTable)()
|
||||
}
|
||||
if (code === 0)
|
||||
process_qrcode();
|
||||
});
|
||||
|
||||
$(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() {
|
||||
$(".table-row").click(function (e) {
|
||||
let target = e.target.parentElement;
|
||||
@@ -168,4 +200,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "base_search.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
@@ -44,6 +44,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<h3 class="card-header text-center">
|
||||
{% trans "All activities" %}
|
||||
</h3>
|
||||
{% render_table table %}
|
||||
{% render_table all %}
|
||||
</div>
|
||||
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n perms pretty_money %}
|
||||
{% load i18n perms pretty_money dict_get %}
|
||||
{% url 'activity:activity_detail' activity.pk as activity_detail_url %}
|
||||
|
||||
<div id="activity_info" class="card bg-light shadow mb-3">
|
||||
@@ -53,6 +53,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<dt class="col-xl-6">{% trans 'opened'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ activity.open|yesno }}</dd>
|
||||
</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 class="card-footer text-center">
|
||||
|
||||
0
apps/activity/templatetags/__init__.py
Normal file
12
apps/activity/templatetags/dict_get.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# 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,32 +67,65 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin
|
||||
tables = [
|
||||
lambda data: ActivityTable(data, prefix="all-"),
|
||||
lambda data: ActivityTable(data, prefix="upcoming-"),
|
||||
lambda data: ActivityTable(data, prefix="search-"),
|
||||
]
|
||||
extra_context = {"title": _("Activities")}
|
||||
|
||||
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):
|
||||
# first table = all activities, second table = upcoming
|
||||
# first table = all activities, second table = upcoming, third table = search
|
||||
|
||||
# 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 [
|
||||
self.get_queryset().order_by("-date_start"),
|
||||
Activity.objects.filter(date_end__gt=timezone.now())
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Activity, "view"))
|
||||
.distinct()
|
||||
.order_by("date_start")
|
||||
.order_by("date_start"),
|
||||
search_table,
|
||||
]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
tables = context["tables"]
|
||||
for name, table in zip(["table", "upcoming"], tables):
|
||||
for name, table in zip(["all", "upcoming", "table"], tables):
|
||||
context[name] = table
|
||||
|
||||
started_activities = self.get_queryset().filter(open=True, valid=True).distinct().all()
|
||||
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
|
||||
|
||||
|
||||
@@ -103,20 +136,74 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMix
|
||||
model = Activity
|
||||
context_object_name = "activity"
|
||||
extra_context = {"title": _("Activity detail")}
|
||||
export_formats = ["csv"]
|
||||
|
||||
tables = [
|
||||
lambda data: GuestTable(data, prefix="guests-"),
|
||||
lambda data: OpenerTable(data, prefix="opener-"),
|
||||
GuestTable,
|
||||
OpenerTable,
|
||||
]
|
||||
|
||||
def get_tables(self):
|
||||
tables = super().get_tables()
|
||||
tables[0].prefix = "guests"
|
||||
tables[1].prefix = "opener"
|
||||
return tables
|
||||
|
||||
def get_tables_data(self):
|
||||
return [
|
||||
Guest.objects.filter(activity=self.object)
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Guest, "view")),
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Guest, "view"))
|
||||
.distinct(),
|
||||
self.object.opener.filter(activity=self.object)
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Opener, "view")),
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Opener, "view"))
|
||||
.distinct(),
|
||||
]
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
"""
|
||||
Gère l'export CSV manuel pour MultiTableMixin.
|
||||
"""
|
||||
if "_export" in self.request.GET:
|
||||
import tablib
|
||||
table_name = self.request.GET.get("table")
|
||||
if table_name:
|
||||
tables = self.get_tables()
|
||||
data_list = self.get_tables_data()
|
||||
|
||||
for t, d in zip(tables, data_list):
|
||||
if t.prefix == table_name:
|
||||
# Préparer le CSV
|
||||
dataset = tablib.Dataset()
|
||||
columns = list(t.base_columns) # noms des colonnes
|
||||
dataset.headers = columns
|
||||
|
||||
for row in d:
|
||||
values = []
|
||||
for col in columns:
|
||||
try:
|
||||
val = getattr(row, col, "")
|
||||
# Gestion spéciale pour la colonne 'entry'
|
||||
if col == "entry":
|
||||
if getattr(row, "has_entry", False):
|
||||
val = timezone.localtime(row.entry.time).strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
val = ""
|
||||
values.append(str(val) if val is not None else "")
|
||||
except Exception: # RelatedObjectDoesNotExist ou autre
|
||||
values.append("")
|
||||
dataset.append(values)
|
||||
|
||||
csv_bytes = dataset.export("csv")
|
||||
if isinstance(csv_bytes, str):
|
||||
csv_bytes = csv_bytes.encode("utf-8")
|
||||
|
||||
response = HttpResponse(csv_bytes, content_type="text/csv")
|
||||
response["Content-Disposition"] = f'attachment; filename="{table_name}.csv"'
|
||||
return response
|
||||
|
||||
# Sinon rendu normal
|
||||
return super().render_to_response(context, **response_kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data()
|
||||
|
||||
@@ -137,6 +224,14 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMix
|
||||
"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
|
||||
|
||||
@@ -216,7 +311,7 @@ class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
form.instance.activity = Activity.objects\
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Activity, "view")).get(pk=self.kwargs["pk"])
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Activity, "view")).distinct().get(pk=self.kwargs["pk"])
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import django_tables2 as tables
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import Food
|
||||
|
||||
@@ -10,10 +11,25 @@ class FoodTable(tables.Table):
|
||||
"""
|
||||
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:
|
||||
model = Food
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
fields = ('name', 'owner', 'allergens', 'expiry_date')
|
||||
fields = ('name', 'owner', 'qr_code_numbers', 'allergens', 'date', 'expiry_date')
|
||||
row_attrs = {
|
||||
'class': 'table-row',
|
||||
'data-href': lambda record: 'detail/' + str(record.pk),
|
||||
|
||||
@@ -34,6 +34,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</div>
|
||||
</div>
|
||||
<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"
|
||||
placeholder="{% trans "Search by attribute such as name..." %}">
|
||||
</div>
|
||||
@@ -114,7 +120,26 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endif %}
|
||||
</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>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('goButton').addEventListener('click', function(event) {
|
||||
|
||||
@@ -65,16 +65,24 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
||||
suffix = '__iregex' if valid_regex else '__istartswith'
|
||||
prefix = '^' if valid_regex else ''
|
||||
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:
|
||||
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'))
|
||||
# table open
|
||||
open_table = self.get_queryset().order_by('expiry_date').filter(
|
||||
open_table = self.get_queryset().filter(
|
||||
Q(polymorphic_ctype__model='transformedfood')
|
||||
| Q(polymorphic_ctype__model='basicfood', basicfood__date_type='DLC')).filter(
|
||||
expiry_date__lt=timezone.now(), end_of_life='').filter(
|
||||
PermissionBackend.filter_queryset(self.request, Food, 'view'))
|
||||
open_table = open_table.union(self.get_queryset().filter(
|
||||
Q(end_of_life='', order__iexact='open')
|
||||
).filter(
|
||||
PermissionBackend.filter_queryset(self.request, Food, 'view'))).order_by('expiry_date')
|
||||
# table served
|
||||
served_table = self.get_queryset().order_by('-pk').filter(
|
||||
end_of_life='', is_ready=True).exclude(
|
||||
@@ -95,6 +103,7 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
||||
owner=club, end_of_life='').filter(
|
||||
PermissionBackend.filter_queryset(self.request, Food, 'view')
|
||||
))
|
||||
|
||||
return [search_table, open_table, served_table] + club_table
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -218,7 +227,7 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
copy = self.request.GET.get('copy', None)
|
||||
if copy is not None:
|
||||
food = BasicFood.objects.get(pk=copy)
|
||||
print(context['form'].fields)
|
||||
|
||||
for field in context['form'].fields:
|
||||
if field == 'allergens':
|
||||
context['form'].fields[field].initial = getattr(food, field).all()
|
||||
|
||||
@@ -10,6 +10,7 @@ from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import transaction
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from note.models import NoteSpecial, Alias
|
||||
@@ -45,6 +46,11 @@ class ProfileForm(forms.ModelForm):
|
||||
A form for the extras field provided by the :model:`member.Profile` model.
|
||||
"""
|
||||
# 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"))
|
||||
|
||||
last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date"))
|
||||
@@ -72,7 +78,12 @@ class ProfileForm(forms.ModelForm):
|
||||
if not self.instance.section or (("department" in self.changed_data
|
||||
or "promotion" in self.changed_data) and "section" not in self.changed_data):
|
||||
self.instance.section = self.instance.section_generated
|
||||
return super().save(commit)
|
||||
instance = super().save(commit=False)
|
||||
if instance.phone_number:
|
||||
instance.phone_number = instance.phone_number.as_e164
|
||||
if commit:
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class Meta:
|
||||
model = Profile
|
||||
|
||||
@@ -417,7 +417,7 @@ class Membership(models.Model):
|
||||
A membership is valid if today is between the start and the end date.
|
||||
"""
|
||||
if self.date_end is not None:
|
||||
return self.date_start.toordinal() <= datetime.datetime.now().toordinal() < self.date_end.toordinal()
|
||||
return self.date_start.toordinal() <= datetime.datetime.now().toordinal() <= self.date_end.toordinal()
|
||||
else:
|
||||
return self.date_start.toordinal() <= datetime.datetime.now().toordinal()
|
||||
|
||||
|
||||
@@ -92,6 +92,20 @@ class MembershipTable(tables.Table):
|
||||
}
|
||||
)
|
||||
|
||||
user_email = tables.Column(
|
||||
verbose_name="Email",
|
||||
accessor="user.email",
|
||||
orderable=False,
|
||||
visible=False,
|
||||
)
|
||||
|
||||
user_full_name = tables.Column(
|
||||
verbose_name=_("Full name"),
|
||||
accessor="user.get_full_name",
|
||||
orderable=False,
|
||||
visible=False,
|
||||
)
|
||||
|
||||
def render_user(self, value):
|
||||
# If the user has the right, link the displayed user with the page of its detail.
|
||||
s = value.username
|
||||
@@ -149,6 +163,16 @@ class MembershipTable(tables.Table):
|
||||
+ "'>" + s + "</a>")
|
||||
return s
|
||||
|
||||
def value_user(self, record):
|
||||
return record.user.username if record.user else ""
|
||||
|
||||
def value_club(self, record):
|
||||
return record.club.name if record.club else ""
|
||||
|
||||
def value_roles(self, record):
|
||||
roles = record.roles.all()
|
||||
return ", ".join(str(role) for role in roles)
|
||||
|
||||
class Meta:
|
||||
attrs = {
|
||||
'class': 'table table-condensed table-striped',
|
||||
|
||||
@@ -36,7 +36,13 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% trans "There is no membership found with this pattern." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-footer text-center">
|
||||
<button class="btn btn-block btn-primary mb-3" onclick="window.location.href='?_export=csv'">
|
||||
{% trans "Export to CSV" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -73,7 +73,10 @@
|
||||
{% if user_object.pk == user.pk %}
|
||||
<div class="text-center">
|
||||
<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>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -10,7 +10,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<form method="post" id="profile-form">
|
||||
{% csrf_token %}
|
||||
{{ form | crispy }}
|
||||
{{ profile_form | crispy }}
|
||||
@@ -20,4 +20,46 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% 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
@@ -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>/trust', views.ProfileTrustView.as_view(), name="user_trust"),
|
||||
path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'),
|
||||
path('user/<int:pk>/qr_code/', views.QRCodeView.as_view(), name='qr_code'),
|
||||
]
|
||||
|
||||
@@ -17,6 +17,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView, UpdateView, TemplateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
from django_tables2.views import MultiTableMixin, SingleTableMixin, SingleTableView
|
||||
from django_tables2.export.views import ExportMixin
|
||||
from rest_framework.authtoken.models import Token
|
||||
from api.viewsets import is_regex
|
||||
from note.models import Alias, NoteClub, NoteUser, Trust
|
||||
@@ -49,6 +50,15 @@ class CustomLoginView(LoginView):
|
||||
self.request.session['permission_mask'] = form.cleaned_data['permission_mask'].rank
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
user_agent = self.request.META.get('HTTP_USER_AGENT', '').lower()
|
||||
|
||||
context['display_appstore_badge'] = 'iphone' in user_agent or 'android' not in user_agent
|
||||
context['display_playstore_badge'] = 'android' in user_agent or 'iphone' not in user_agent
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||
"""
|
||||
@@ -407,6 +417,14 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
|
||||
context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
|
||||
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 #
|
||||
@@ -950,11 +968,12 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||
return reverse_lazy('member:user_detail', kwargs={'pk': self.object.user.id})
|
||||
|
||||
|
||||
class ClubMembersListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
|
||||
class ClubMembersListView(ExportMixin, ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
|
||||
model = Membership
|
||||
table_class = MembershipTable
|
||||
template_name = "member/club_members.html"
|
||||
extra_context = {"title": _("Members of the club")}
|
||||
export_formats = ["csv"]
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
qs = super().get_queryset().filter(club_id=self.kwargs["pk"])
|
||||
@@ -986,6 +1005,14 @@ class ClubMembersListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableV
|
||||
|
||||
return qs.distinct()
|
||||
|
||||
def get_export_filename(self, export_format):
|
||||
return "members.csv"
|
||||
|
||||
def get_export_content_type(self, export_format):
|
||||
if export_format == "csv":
|
||||
return "text/csv"
|
||||
return super().get_export_content_type(export_format)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
club = Club.objects.filter(
|
||||
|
||||
@@ -228,7 +228,7 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
|
||||
addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' +
|
||||
'but the emitter note %s is negative.'), [source_alias, source_alias]), 'warning', 30000)
|
||||
}
|
||||
if (source.membership && source.membership.date_end < new Date().toISOString()) {
|
||||
if (source.membership && source.membership.date_end <= new Date().toISOString()) {
|
||||
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source_alias]),
|
||||
'danger', 30000)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ $(document).ready(function () {
|
||||
arr.push(last)
|
||||
|
||||
last.quantity = 1
|
||||
|
||||
|
||||
|
||||
if (last.note.club) {
|
||||
$('#last_name').val(last.note.name)
|
||||
@@ -111,7 +113,8 @@ $(document).ready(function () {
|
||||
dest.removeClass('d-none')
|
||||
$('#dest_note_list').removeClass('d-none')
|
||||
$('#debit_type').addClass('d-none')
|
||||
|
||||
$('#reason').val('')
|
||||
|
||||
$('#source_note_label').text(select_emitters_label)
|
||||
$('#dest_note_label').text(select_receveirs_label)
|
||||
|
||||
@@ -134,6 +137,7 @@ $(document).ready(function () {
|
||||
dest.val('')
|
||||
dest.tooltip('hide')
|
||||
$('#debit_type').addClass('d-none')
|
||||
$('#reason').val('Rechargement note')
|
||||
|
||||
$('#source_note_label').text(transfer_type_label)
|
||||
$('#dest_note_label').text(select_receveir_label)
|
||||
@@ -162,6 +166,7 @@ $(document).ready(function () {
|
||||
dest.addClass('d-none')
|
||||
dest.tooltip('hide')
|
||||
$('#debit_type').removeClass('d-none')
|
||||
$('#reason').val('')
|
||||
|
||||
$('#source_note_label').text(select_emitter_label)
|
||||
$('#dest_note_label').text(transfer_type_label)
|
||||
@@ -305,10 +310,10 @@ $('#btn_transfer').click(function () {
|
||||
destination: dest.note.id,
|
||||
destination_alias: dest.name
|
||||
}).done(function () {
|
||||
if (source.note.membership && source.note.membership.date_end < new Date().toISOString()) {
|
||||
if (source.note.membership && source.note.membership.date_end <= new Date().toISOString()) {
|
||||
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source.name]), 'danger', 30000)
|
||||
}
|
||||
if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString()) {
|
||||
if (dest.note.membership && dest.note.membership.date_end <= new Date().toISOString()) {
|
||||
addMsg(interpolate(gettext('Warning, the destination note %s is no more a BDE member.'), [dest.name]), 'danger', 30000)
|
||||
}
|
||||
|
||||
@@ -409,7 +414,7 @@ $('#btn_transfer').click(function () {
|
||||
bank: $('#bank').val()
|
||||
}).done(function () {
|
||||
addMsg(gettext('Credit/debit succeed!'), 'success', 10000)
|
||||
if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) }
|
||||
if (user_note.membership && user_note.membership.date_end <= new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) }
|
||||
reset()
|
||||
}).fail(function (err) {
|
||||
const errObj = JSON.parse(err.responseText)
|
||||
|
||||
@@ -927,7 +927,7 @@
|
||||
"note",
|
||||
"transactiontemplate"
|
||||
],
|
||||
"query": "{\"destination\": [\"club\", \"note\"]}",
|
||||
"query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, {\"category__name\": \"Clubs\"}]",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
@@ -943,7 +943,7 @@
|
||||
"note",
|
||||
"transactiontemplate"
|
||||
],
|
||||
"query": "{\"destination\": [\"club\", \"note\"]}",
|
||||
"query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, {\"category__name\": \"Clubs\"}]",
|
||||
"type": "add",
|
||||
"mask": 3,
|
||||
"field": "",
|
||||
@@ -959,7 +959,7 @@
|
||||
"note",
|
||||
"transactiontemplate"
|
||||
],
|
||||
"query": "{\"destination\": [\"club\", \"note\"]}",
|
||||
"query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, {\"category__name\": \"Clubs\"}]",
|
||||
"type": "change",
|
||||
"mask": 3,
|
||||
"field": "",
|
||||
@@ -3484,7 +3484,23 @@
|
||||
"mask": 1,
|
||||
"permanent": false,
|
||||
"description": "Voir la bouffe servie"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 223,
|
||||
"fields": {
|
||||
"model": [
|
||||
"note",
|
||||
"templatecategory"
|
||||
],
|
||||
"query": "{\"name\": \"Clubs\"}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir la catégorie de bouton Clubs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
@@ -4430,6 +4446,22 @@
|
||||
"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",
|
||||
"pk": 311,
|
||||
@@ -4686,6 +4718,22 @@
|
||||
"description": "Supprimer un succès"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 330,
|
||||
"fields": {
|
||||
"model": [
|
||||
"auth",
|
||||
"user"
|
||||
],
|
||||
"query": "{\"memberships__club\": [\"club\"]}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "email",
|
||||
"permanent": false,
|
||||
"description": "Voir l'adresse mail des membres de son club"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.role",
|
||||
"pk": 1,
|
||||
@@ -4833,7 +4881,11 @@
|
||||
221,
|
||||
247,
|
||||
258,
|
||||
259
|
||||
259,
|
||||
260,
|
||||
263,
|
||||
265,
|
||||
330
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -4845,7 +4897,6 @@
|
||||
"name": "Pr\u00e9sident\u22c5e de club",
|
||||
"permissions": [
|
||||
62,
|
||||
135,
|
||||
142
|
||||
]
|
||||
}
|
||||
@@ -4861,7 +4912,6 @@
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
27,
|
||||
59,
|
||||
60,
|
||||
61,
|
||||
@@ -4872,6 +4922,7 @@
|
||||
182,
|
||||
184,
|
||||
185,
|
||||
223,
|
||||
239,
|
||||
240,
|
||||
241
|
||||
@@ -5122,7 +5173,8 @@
|
||||
289,
|
||||
290,
|
||||
291,
|
||||
293
|
||||
293,
|
||||
298
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -5182,6 +5234,7 @@
|
||||
"permissions": [
|
||||
37,
|
||||
41,
|
||||
42,
|
||||
53,
|
||||
54,
|
||||
55,
|
||||
@@ -5233,7 +5286,15 @@
|
||||
168,
|
||||
176,
|
||||
177,
|
||||
197
|
||||
197,
|
||||
211,
|
||||
212,
|
||||
213,
|
||||
214,
|
||||
215,
|
||||
216,
|
||||
311,
|
||||
319
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -5313,7 +5374,8 @@
|
||||
289,
|
||||
290,
|
||||
291,
|
||||
293
|
||||
293,
|
||||
298
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
18
apps/treasury/migrations/0011_sogecredit_valid.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.6 on 2025-09-28 20:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('treasury', '0010_alter_invoice_bde'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='sogecredit',
|
||||
name='valid',
|
||||
field=models.BooleanField(blank=True, default=False, verbose_name='Valid'),
|
||||
),
|
||||
]
|
||||
@@ -308,6 +308,12 @@ class SogeCredit(models.Model):
|
||||
null=True,
|
||||
)
|
||||
|
||||
valid = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("Valid"),
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Credit from the Société générale")
|
||||
verbose_name_plural = _("Credits from the Société générale")
|
||||
@@ -332,7 +338,7 @@ class SogeCredit(models.Model):
|
||||
last_name=self.user.last_name,
|
||||
first_name=self.user.first_name,
|
||||
bank="Société générale",
|
||||
valid=False,
|
||||
valid=True,
|
||||
)
|
||||
credit_transaction._force_save = True
|
||||
credit_transaction.save()
|
||||
@@ -346,12 +352,12 @@ class SogeCredit(models.Model):
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def valid(self):
|
||||
def valid_legacy(self):
|
||||
return self.credit_transaction and self.credit_transaction.valid
|
||||
|
||||
@property
|
||||
def amount(self):
|
||||
if self.valid:
|
||||
if self.valid_legacy:
|
||||
return self.credit_transaction.total
|
||||
amount = 0
|
||||
transactions_wei = self.transactions.filter(membership__club__weiclub__isnull=False)
|
||||
@@ -397,7 +403,7 @@ class SogeCredit(models.Model):
|
||||
self.transactions.add(m.transaction)
|
||||
|
||||
for tr in self.transactions.all():
|
||||
tr.valid = False
|
||||
tr.valid = True
|
||||
tr.save()
|
||||
|
||||
def invalidate(self):
|
||||
@@ -422,6 +428,7 @@ class SogeCredit(models.Model):
|
||||
self.invalidate()
|
||||
# Refresh credit amount
|
||||
self.save()
|
||||
self.valid = True
|
||||
self.credit_transaction.valid = True
|
||||
self.credit_transaction._force_save = True
|
||||
self.credit_transaction.save()
|
||||
|
||||
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 104 KiB |
@@ -56,6 +56,7 @@ class InvoiceTable(tables.Table):
|
||||
model = Invoice
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
fields = ('id', 'name', 'object', 'acquitted', 'invoice',)
|
||||
order_by = ('-id',)
|
||||
|
||||
|
||||
class RemittanceTable(tables.Table):
|
||||
|
||||
@@ -417,7 +417,7 @@ class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableVi
|
||||
)
|
||||
|
||||
if "valid" not in self.request.GET or not self.request.GET["valid"]:
|
||||
qs = qs.filter(credit_transaction__valid=False)
|
||||
qs = qs.filter(valid=False)
|
||||
|
||||
return qs
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from ...models import WEIMembership, Bus
|
||||
|
||||
WORDS = {
|
||||
'list': [
|
||||
'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nert et geek', 'Jeux de rôles et danse rock',
|
||||
'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nerd et geek', 'Jeux de rôles et danse rock',
|
||||
'Strass et paillettes', 'Spectaculaire', 'Splendide', 'Flow inégalable', 'Rap', 'Battles légendaires',
|
||||
'Techno', 'Alcool', 'Kiffeur·euse', 'Rugby', 'Médiéval', 'Festif',
|
||||
'Stylé', 'Chipie', 'Rétro', 'Vache', 'Farfadet', 'Fanfare',
|
||||
@@ -57,7 +57,7 @@ WORDS = {
|
||||
42: "Un burgouzz de valouzz",
|
||||
47: "Un ocarina (pour me téléporter hors de ce bourbier)",
|
||||
48: "Des paillettes, un micro de karaoké et une enceinte bluetooth",
|
||||
45: "",
|
||||
45: "Un kebab",
|
||||
44: "Une 86 et un caisson pour taper du pied",
|
||||
46: "Une épée, un ballon et une tireuse",
|
||||
43: "Des lunettes de soleil",
|
||||
@@ -176,7 +176,33 @@ WORDS = {
|
||||
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 = {
|
||||
@@ -235,7 +261,7 @@ class WEISurveyForm2025(forms.Form):
|
||||
all_preferred_words = WORDS['list']
|
||||
rng.shuffle(all_preferred_words)
|
||||
self.fields["words"].choices = [(w, w) for w in all_preferred_words]
|
||||
else:
|
||||
elif information.step <= len(WORDS['questions']):
|
||||
questions = list(WORDS['questions'].items())
|
||||
idx = information.step - 1
|
||||
if idx < len(questions):
|
||||
@@ -251,6 +277,15 @@ class WEISurveyForm2025(forms.Form):
|
||||
widget=OptionalImageRadioSelect(images=IMAGES.get(q, {})),
|
||||
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):
|
||||
data = self.cleaned_data['words']
|
||||
@@ -377,7 +412,7 @@ class WEISurvey2025(WEISurvey):
|
||||
setattr(self.information, "word" + str(i), word)
|
||||
self.information.step += 1
|
||||
self.save()
|
||||
else:
|
||||
elif 1 <= self.information.step <= len(WORDS['questions']):
|
||||
questions = list(WORDS['questions'].keys())
|
||||
idx = self.information.step - 1
|
||||
if idx < len(questions):
|
||||
@@ -385,6 +420,13 @@ class WEISurvey2025(WEISurvey):
|
||||
setattr(self.information, q, form.cleaned_data[q])
|
||||
self.information.step += 1
|
||||
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
|
||||
def get_algorithm_class(cls):
|
||||
@@ -394,7 +436,7 @@ class WEISurvey2025(WEISurvey):
|
||||
"""
|
||||
The survey is complete once the bus is chosen.
|
||||
"""
|
||||
return self.information.step > len(WORDS['questions'])
|
||||
return self.information.step > len(WORDS['questions']) + 1
|
||||
|
||||
@classmethod
|
||||
@lru_cache()
|
||||
|
||||
BIN
apps/wei/static/wei/img/logo_auvergne_rhone_alpes.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
@@ -11,7 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<form id="registration-form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{{ membership_form|crispy }}
|
||||
@@ -22,6 +22,46 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endblock %}
|
||||
|
||||
{% 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 %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
||||
@@ -53,9 +53,11 @@ class TestWEIAlgorithm(TestCase):
|
||||
birth_date='2000-01-01',
|
||||
)
|
||||
information = WEISurveyInformation2025(registration)
|
||||
for j in range(1, 21):
|
||||
for j in range(1, 1 + NB_WORDS):
|
||||
setattr(information, f'word{j}', random.choice(WORDS['list']))
|
||||
information.step = 20
|
||||
for q in WORDS['questions']:
|
||||
setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys())))
|
||||
information.step = len(WORDS['questions']) + 2
|
||||
information.save(registration)
|
||||
registration.save()
|
||||
|
||||
@@ -87,7 +89,7 @@ class TestWEIAlgorithm(TestCase):
|
||||
setattr(information, f'word{j}', random.choice(WORDS['list']))
|
||||
for q in WORDS['questions']:
|
||||
setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys())))
|
||||
information.step = len(WORDS['questions']) + 1
|
||||
information.step = len(WORDS['questions']) + 2
|
||||
information.save(registration)
|
||||
registration.save()
|
||||
survey = WEISurvey2025(registration)
|
||||
|
||||
@@ -680,7 +680,7 @@ class TestWEIRegistration(TestCase):
|
||||
self.assertTrue(soge_credit.exists())
|
||||
soge_credit = soge_credit.get()
|
||||
self.assertTrue(membership.transaction in soge_credit.transactions.all())
|
||||
self.assertFalse(membership.transaction.valid)
|
||||
self.assertTrue(membership.transaction.valid)
|
||||
|
||||
# Check that if the WEI is started, we can't update a wei
|
||||
self.wei.date_start = date(2000, 1, 1)
|
||||
|
||||
@@ -4366,6 +4366,14 @@ msgstr ""
|
||||
msgid "Forgotten your password or username?"
|
||||
msgstr "Passwort oder Username vergessen?"
|
||||
|
||||
#: note_kfet/templates/registration/login.html:44
|
||||
msgid "Download on the AppStore"
|
||||
msgstr "Im AppStore herunterladen"
|
||||
|
||||
#: note_kfet/templates/registration/login.html:48
|
||||
msgid "Get it on Google Play"
|
||||
msgstr "Bei Google Play herunterladen"
|
||||
|
||||
#: note_kfet/templates/registration/password_change_done.html:13
|
||||
msgid "Your password was changed."
|
||||
msgstr "Ihr Passwort wurde geändert."
|
||||
|
||||
@@ -4281,6 +4281,14 @@ msgstr ""
|
||||
msgid "Forgotten your password or username?"
|
||||
msgstr "¿ Contraseña o nombre de usuario olvidado ?"
|
||||
|
||||
#: note_kfet/templates/registration/login.html:44
|
||||
msgid "Download on the AppStore"
|
||||
msgstr "Descargar en la AppStore"
|
||||
|
||||
#: note_kfet/templates/registration/login.html:48
|
||||
msgid "Get it on Google Play"
|
||||
msgstr "Descargar en Google Play"
|
||||
|
||||
#: note_kfet/templates/registration/password_change_done.html:13
|
||||
msgid "Your password was changed."
|
||||
msgstr "Su contraseña fue cambiada con éxito."
|
||||
|
||||
@@ -770,7 +770,7 @@ msgstr "Créer une famille ou un défi"
|
||||
|
||||
#: apps/family/templates/family/manage.html:96
|
||||
msgid "Add a family"
|
||||
msgstr "Ajouter une famille"
|
||||
msgstr "Fonder une famille"
|
||||
|
||||
#: apps/family/templates/family/manage.html:101
|
||||
msgid "Add a challenge"
|
||||
@@ -4584,6 +4584,14 @@ msgstr ""
|
||||
msgid "Forgotten your password or username?"
|
||||
msgstr "Mot de passe ou pseudo oublié ?"
|
||||
|
||||
#: note_kfet/templates/registration/login.html:44
|
||||
msgid "Download on the AppStore"
|
||||
msgstr "Télécharger sur l'AppStore"
|
||||
|
||||
#: note_kfet/templates/registration/login.html:48
|
||||
msgid "Get it on Google Play"
|
||||
msgstr "Télécharger sur Google Play"
|
||||
|
||||
#: note_kfet/templates/registration/password_change_done.html:13
|
||||
msgid "Your password was changed."
|
||||
msgstr "Votre mot de passe a bien été changé."
|
||||
|
||||
@@ -306,8 +306,8 @@ PIC_WIDTH = 200
|
||||
PIC_RATIO = 1
|
||||
|
||||
# Custom phone number format
|
||||
PHONENUMBER_DB_FORMAT = 'NATIONAL'
|
||||
PHONENUMBER_DEFAULT_REGION = 'FR'
|
||||
PHONENUMBER_DB_FORMAT = 'E164'
|
||||
PHONENUMBER_DEFAULT_REGION = None
|
||||
|
||||
# We add custom information to CAS, in order to give a normalized name to other services
|
||||
CAS_AUTH_CLASS = 'member.auth.CustomAuthUser'
|
||||
|
||||
50
note_kfet/static/img/appstore_badge_fr.svg
Normal file
@@ -0,0 +1,50 @@
|
||||
<svg id="livetype" xmlns="http://www.w3.org/2000/svg" width="126.50751" height="40" viewBox="0 0 126.50751 40">
|
||||
<title>Download_on_the_App_Store_Badge_FR_RGB_blk_100517</title>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M116.97821,0H9.53468c-.3667,0-.729,0-1.09473.002-.30615.002-.60986.00781-.91895.0127A13.21476,13.21476,0,0,0,5.5171.19141a6.66509,6.66509,0,0,0-1.90088.627A6.43779,6.43779,0,0,0,1.99757,1.99707,6.25844,6.25844,0,0,0,.81935,3.61816a6.60119,6.60119,0,0,0-.625,1.90332,12.993,12.993,0,0,0-.1792,2.002C.00587,7.83008.00489,8.1377,0,8.44434V31.5586c.00489.3105.00587.6113.01515.9219a12.99232,12.99232,0,0,0,.1792,2.0019,6.58756,6.58756,0,0,0,.625,1.9043A6.20778,6.20778,0,0,0,1.99757,38.001a6.27445,6.27445,0,0,0,1.61865,1.1787,6.70082,6.70082,0,0,0,1.90088.6308,13.45514,13.45514,0,0,0,2.0039.1768c.30909.0068.6128.0107.91895.0107C8.80567,40,9.168,40,9.53468,40H116.97821c.3594,0,.7246,0,1.084-.002.3047,0,.6172-.0039.9219-.0107a13.279,13.279,0,0,0,2-.1768,6.80432,6.80432,0,0,0,1.9082-.6308,6.27742,6.27742,0,0,0,1.6172-1.1787,6.39482,6.39482,0,0,0,1.1816-1.6143,6.60413,6.60413,0,0,0,.6191-1.9043,13.50641,13.50641,0,0,0,.1856-2.0019c.0039-.3106.0039-.6114.0039-.9219.0078-.3633.0078-.7246.0078-1.0938V9.53613c0-.36621,0-.72949-.0078-1.09179,0-.30664,0-.61426-.0039-.9209a13.50709,13.50709,0,0,0-.1856-2.002,6.6177,6.6177,0,0,0-.6191-1.90332,6.46619,6.46619,0,0,0-2.7988-2.7998,6.76753,6.76753,0,0,0-1.9082-.627,13.04394,13.04394,0,0,0-2-.17676c-.3047-.00488-.6172-.01074-.9219-.01269-.3594-.002-.7246-.002-1.084-.002Z" style="fill: #a6a6a6"/>
|
||||
<path d="M8.44482,39.125c-.30467,0-.602-.0039-.90428-.0107a12.68714,12.68714,0,0,1-1.86914-.1631,5.88381,5.88381,0,0,1-1.65674-.5479,5.40573,5.40573,0,0,1-1.397-1.0166,5.32082,5.32082,0,0,1-1.02051-1.3965,5.72186,5.72186,0,0,1-.543-1.6572,12.41351,12.41351,0,0,1-.1665-1.875c-.00634-.2109-.01464-.9131-.01464-.9131V8.44434S.88185,7.75293.8877,7.5498a12.37138,12.37138,0,0,1,.16552-1.87207,5.75577,5.75577,0,0,1,.54347-1.6621A5.37365,5.37365,0,0,1,2.61182,2.61768,5.56562,5.56562,0,0,1,4.01417,1.59521a5.82309,5.82309,0,0,1,1.65332-.54394A12.58579,12.58579,0,0,1,7.543.88721L8.44532.875h109.612l.9131.0127a12.38493,12.38493,0,0,1,1.8584.16259,5.93833,5.93833,0,0,1,1.6709.54785,5.59375,5.59375,0,0,1,2.415,2.41993,5.76267,5.76267,0,0,1,.5352,1.64892,12.995,12.995,0,0,1,.1738,1.88721c.0029.2832.0029.5874.0029.89014.0079.375.0079.73193.0079,1.09179V30.4648c0,.3633,0,.7178-.0079,1.0752,0,.3252,0,.6231-.0039.9297a12.73126,12.73126,0,0,1-.1709,1.8535,5.739,5.739,0,0,1-.54,1.67,5.48029,5.48029,0,0,1-1.0156,1.3857,5.4129,5.4129,0,0,1-1.3994,1.0225,5.86168,5.86168,0,0,1-1.668.5498,12.54218,12.54218,0,0,1-1.8692.1631c-.2929.0068-.5996.0107-.8974.0107l-1.084.002Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g id="_Group_" data-name="<Group>">
|
||||
<g id="_Group_2" data-name="<Group>">
|
||||
<g id="_Group_3" data-name="<Group>">
|
||||
<path id="_Path_" data-name="<Path>" d="M24.7718,20.30068a4.94881,4.94881,0,0,1,2.35656-4.15206,5.06566,5.06566,0,0,0-3.99116-2.15768c-1.67924-.17626-3.30719,1.00483-4.1629,1.00483-.87227,0-2.18977-.98733-3.6085-.95814a5.31529,5.31529,0,0,0-4.47292,2.72787c-1.934,3.34842-.49141,8.26947,1.3612,10.97608.9269,1.32535,2.01018,2.8058,3.42763,2.7533,1.38706-.05753,1.9051-.88448,3.5794-.88448,1.65876,0,2.14479.88448,3.591.8511,1.48838-.02416,2.42613-1.33124,3.32051-2.66914A10.962,10.962,0,0,0,27.691,24.69985,4.78205,4.78205,0,0,1,24.7718,20.30068Z" style="fill: #fff"/>
|
||||
<path id="_Path_2" data-name="<Path>" d="M22.04017,12.21089a4.87248,4.87248,0,0,0,1.11452-3.49062,4.95746,4.95746,0,0,0-3.20758,1.65961,4.63634,4.63634,0,0,0-1.14371,3.36139A4.09905,4.09905,0,0,0,22.04017,12.21089Z" style="fill: #fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="_Group_4" data-name="<Group>">
|
||||
<g>
|
||||
<path d="M35.65528,14.70166V9.57813h-1.877V8.73486h4.67676v.84326H36.582v5.12354Z" style="fill: #fff"/>
|
||||
<path d="M42.76466,13.48584a1.828,1.828,0,0,1-1.95117,1.30273,2.04531,2.04531,0,0,1-2.08008-2.32422,2.07685,2.07685,0,0,1,2.07617-2.35254c1.25293,0,2.00879.856,2.00879,2.27v.31006H39.63868v.0498a1.1902,1.1902,0,0,0,1.19922,1.29,1.07934,1.07934,0,0,0,1.07129-.5459Zm-3.126-1.45117H41.9131a1.08647,1.08647,0,0,0-1.1084-1.1665A1.15162,1.15162,0,0,0,39.63868,12.03467ZM40.2754,9.4458l1.03809-1.42236h1.042L41.19337,9.4458Z" style="fill: #fff"/>
|
||||
<path d="M44.05274,8.44092h.88867v6.26074h-.88867Z" style="fill: #fff"/>
|
||||
<path d="M50.208,13.48584a1.828,1.828,0,0,1-1.95117,1.30273,2.04531,2.04531,0,0,1-2.08008-2.32422,2.07685,2.07685,0,0,1,2.07617-2.35254c1.25293,0,2.00879.856,2.00879,2.27v.31006H47.082v.0498a1.1902,1.1902,0,0,0,1.19922,1.29,1.07934,1.07934,0,0,0,1.07129-.5459Zm-3.126-1.45117h2.27441a1.08647,1.08647,0,0,0-1.1084-1.1665A1.15162,1.15162,0,0,0,47.082,12.03467Zm.63672-2.58887,1.03809-1.42236h1.042L48.63673,9.4458Z" style="fill: #fff"/>
|
||||
<path d="M54.40333,11.67041a1.00546,1.00546,0,0,0-1.06348-.76465c-.74414,0-1.19922.57031-1.19922,1.52979,0,.97607.459,1.55908,1.19922,1.55908a.97873.97873,0,0,0,1.06348-.74023h.86426a1.762,1.762,0,0,1-1.92285,1.53418,2.06791,2.06791,0,0,1-2.11328-2.353,2.05305,2.05305,0,0,1,2.1084-2.32373,1.77731,1.77731,0,0,1,1.92773,1.55859Z" style="fill: #fff"/>
|
||||
<path d="M56.44728,8.44092h.88086v2.48145h.07031a1.3856,1.3856,0,0,1,1.373-.80664,1.48339,1.48339,0,0,1,1.55078,1.67871v2.90723h-.88965v-2.688c0-.71924-.335-1.0835-.96289-1.0835a1.05194,1.05194,0,0,0-1.13379,1.1416v2.62988h-.88867Z" style="fill: #fff"/>
|
||||
<path d="M61.43946,13.42822c0-.81055.60352-1.27783,1.6748-1.34424l1.21973-.07031V11.625c0-.47559-.31445-.74414-.92188-.74414-.49609,0-.83984.18213-.93848.50049h-.86035c.09082-.77344.81836-1.26953,1.83984-1.26953,1.12891,0,1.76514.562,1.76514,1.51318v3.07666h-.855v-.63281H64.293a1.515,1.515,0,0,1-1.35254.707A1.36026,1.36026,0,0,1,61.43946,13.42822Zm2.89453-.38477V12.667l-1.09961.07031c-.62012.0415-.90137.25244-.90137.64941,0,.40527.35156.64111.835.64111A1.0615,1.0615,0,0,0,64.334,13.04346Z" style="fill: #fff"/>
|
||||
<path d="M66.60987,10.19873h.85547v.69043h.06641a1.22092,1.22092,0,0,1,1.21582-.76514,1.86836,1.86836,0,0,1,.39648.03711v.877a2.43442,2.43442,0,0,0-.49609-.05371A1.05507,1.05507,0,0,0,67.49855,12.043v2.65869h-.88867Z" style="fill: #fff"/>
|
||||
<path d="M69.96144,15.15234h.90918c.0752.32666.45117.5376,1.05078.5376.74023,0,1.17871-.35156,1.17871-.94678v-.86426H73.0337a1.51433,1.51433,0,0,1-1.38965.75635c-1.14941,0-1.86035-.88867-1.86035-2.23682,0-1.373.71875-2.27441,1.86914-2.27441a1.56045,1.56045,0,0,1,1.41406.79395h.07031v-.71924h.85156v4.54c0,1.02979-.80664,1.68311-2.08008,1.68311C70.7837,16.42188,70.05616,15.91748,69.96144,15.15234Zm3.15527-2.7583c0-.897-.46387-1.47168-1.2207-1.47168-.76465,0-1.19434.57471-1.19434,1.47168,0,.89746.42969,1.47217,1.19434,1.47217C72.65773,13.86621,73.11671,13.2959,73.11671,12.394Z" style="fill: #fff"/>
|
||||
<path d="M79.21241,13.48584a1.828,1.828,0,0,1-1.95117,1.30273,2.04531,2.04531,0,0,1-2.08008-2.32422,2.07685,2.07685,0,0,1,2.07617-2.35254c1.25293,0,2.00879.856,2.00879,2.27v.31006H76.08644v.0498a1.1902,1.1902,0,0,0,1.19922,1.29,1.07934,1.07934,0,0,0,1.07129-.5459Zm-3.126-1.45117h2.27441a1.08647,1.08647,0,0,0-1.1084-1.1665A1.15162,1.15162,0,0,0,76.08644,12.03467Z" style="fill: #fff"/>
|
||||
<path d="M80.45948,10.19873H81.315v.69043h.06641a1.22092,1.22092,0,0,1,1.21582-.76514,1.86836,1.86836,0,0,1,.39648.03711v.877a2.43442,2.43442,0,0,0-.49609-.05371A1.05507,1.05507,0,0,0,81.34815,12.043v2.65869h-.88867Z" style="fill: #fff"/>
|
||||
<path d="M86.19581,12.44824c0-1.42285.73145-2.32422,1.86914-2.32422a1.484,1.484,0,0,1,1.38086.79h.06641V8.44092h.88867v6.26074h-.85156v-.71143H89.479a1.56284,1.56284,0,0,1-1.41406.78564C86.91944,14.77588,86.19581,13.87451,86.19581,12.44824Zm.918,0c0,.95508.4502,1.52979,1.20313,1.52979.749,0,1.21191-.583,1.21191-1.52588,0-.93848-.46777-1.52979-1.21191-1.52979C87.56886,10.92236,87.11378,11.501,87.11378,12.44824Z" style="fill: #fff"/>
|
||||
<path d="M91.60206,13.42822c0-.81055.60352-1.27783,1.6748-1.34424l1.21973-.07031V11.625c0-.47559-.31445-.74414-.92187-.74414-.49609,0-.83984.18213-.93848.50049h-.86035c.09082-.77344.81836-1.26953,1.83984-1.26953,1.12891,0,1.76563.562,1.76563,1.51318v3.07666h-.85547v-.63281h-.07031a1.515,1.515,0,0,1-1.35254.707A1.36026,1.36026,0,0,1,91.60206,13.42822Zm2.89453-.38477V12.667L93.397,12.7373c-.62012.0415-.90137.25244-.90137.64941,0,.40527.35156.64111.835.64111A1.0615,1.0615,0,0,0,94.49659,13.04346Z" style="fill: #fff"/>
|
||||
<path d="M96.773,10.19873h.85547v.71533h.06641a1.348,1.348,0,0,1,1.34375-.80225,1.46456,1.46456,0,0,1,1.55859,1.6748v2.915h-.88867V12.00977c0-.72363-.31445-1.0835-.97168-1.0835a1.03294,1.03294,0,0,0-1.0752,1.14111v2.63428H96.773Z" style="fill: #fff"/>
|
||||
<path d="M103.61769,10.11182c1.0127,0,1.6748.47119,1.76172,1.26514h-.85254c-.082-.33057-.40527-.5415-.90918-.5415-.49609,0-.873.23535-.873.58691,0,.269.22754.43848.71582.55029l.748.17334c.85645.19873,1.25781.56689,1.25781,1.22852,0,.84766-.79,1.41406-1.86523,1.41406-1.07129,0-1.76953-.48389-1.84863-1.28174h.88965a.91365.91365,0,0,0,.97949.562c.55371,0,.94727-.248.94727-.60791,0-.26855-.21094-.44238-.66211-.5498l-.78516-.18213c-.85645-.20264-1.25293-.58691-1.25293-1.25684C101.86866,10.67383,102.60011,10.11182,103.61769,10.11182Z" style="fill: #fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M35.19825,18.06689h1.85938V30.48535H35.19825Z" style="fill: #fff"/>
|
||||
<path d="M39.29786,22.61084l1.01563-4.54395h1.80664l-1.23047,4.54395Z" style="fill: #fff"/>
|
||||
<path d="M49.14649,27.12891H44.4131l-1.13672,3.35645H41.27149l4.4834-12.41846h2.083l4.4834,12.41846H50.28224Zm-4.24316-1.54883h3.752l-1.84961-5.44775h-.05176Z" style="fill: #fff"/>
|
||||
<path d="M62.00294,25.959c0,2.81348-1.50586,4.62109-3.77832,4.62109a3.0693,3.0693,0,0,1-2.84863-1.584h-.043v4.48438h-1.8584V21.43115h1.79883V22.937h.03418a3.21162,3.21162,0,0,1,2.88281-1.60059C60.48829,21.33643,62.00294,23.15283,62.00294,25.959Zm-1.91016,0c0-1.8335-.94727-3.03857-2.39258-3.03857-1.41992,0-2.375,1.23047-2.375,3.03857,0,1.82422.95508,3.0459,2.375,3.0459C59.14552,29.00488,60.09278,27.80859,60.09278,25.959Z" style="fill: #fff"/>
|
||||
<path d="M71.9673,25.959c0,2.81348-1.50586,4.62109-3.77832,4.62109a3.0693,3.0693,0,0,1-2.84863-1.584h-.043v4.48438H63.43946V21.43115H65.2378V22.937H65.272a3.21162,3.21162,0,0,1,2.88281-1.60059C70.45265,21.33643,71.9673,23.15283,71.9673,25.959Zm-1.91016,0c0-1.8335-.94727-3.03857-2.39258-3.03857-1.41992,0-2.375,1.23047-2.375,3.03857,0,1.82422.95508,3.0459,2.375,3.0459C69.10987,29.00488,70.05714,27.80859,70.05714,25.959Z" style="fill: #fff"/>
|
||||
<path d="M78.55323,27.02539c.1377,1.23145,1.334,2.04,2.96875,2.04,1.56641,0,2.69336-.80859,2.69336-1.91895,0-.96387-.67969-1.541-2.28906-1.93652l-1.60937-.38818C78.03663,24.271,76.978,23.20459,76.978,21.47412c0-2.14258,1.86719-3.61426,4.51855-3.61426,2.624,0,4.42285,1.47168,4.4834,3.61426H84.104c-.1123-1.23926-1.13672-1.9873-2.63379-1.9873s-2.52148.75684-2.52148,1.8584c0,.87793.6543,1.39453,2.25488,1.79l1.36816.33594c2.54785.60254,3.60645,1.62646,3.60645,3.44287,0,2.32324-1.85059,3.77832-4.79395,3.77832-2.75391,0-4.61328-1.4209-4.7334-3.667Z" style="fill: #fff"/>
|
||||
<path d="M90.19,19.28857v2.14258h1.72168v1.47168H90.19v4.9917c0,.77539.34473,1.13672,1.10156,1.13672a5.80752,5.80752,0,0,0,.61133-.043v1.46289a5.10351,5.10351,0,0,1-1.03223.08594c-1.833,0-2.54785-.68848-2.54785-2.44434V22.90283H87.00636V21.43115h1.31641V19.28857Z" style="fill: #fff"/>
|
||||
<path d="M92.90773,25.959c0-2.84912,1.67773-4.63916,4.29395-4.63916,2.625,0,4.29492,1.79,4.29492,4.63916,0,2.85645-1.66113,4.63867-4.29492,4.63867C94.56886,30.59766,92.90773,28.81543,92.90773,25.959Zm6.69531,0c0-1.95459-.89551-3.10791-2.40137-3.10791s-2.40039,1.16211-2.40039,3.10791c0,1.96191.89453,3.10645,2.40039,3.10645S99.603,27.9209,99.603,25.959Z" style="fill: #fff"/>
|
||||
<path d="M103.02882,21.43115h1.77246v1.541h.043a2.1594,2.1594,0,0,1,2.17773-1.63574,2.86616,2.86616,0,0,1,.63672.06934V23.144a2.59794,2.59794,0,0,0-.835-.1123,1.8728,1.8728,0,0,0-1.93652,2.0835v5.37012h-1.8584Z" style="fill: #fff"/>
|
||||
<path d="M116.22608,27.82617c-.25,1.64355-1.85059,2.77148-3.89844,2.77148-2.63379,0-4.26855-1.76465-4.26855-4.5957,0-2.84033,1.64355-4.68213,4.19043-4.68213,2.50488,0,4.08008,1.7207,4.08008,4.46631v.63672h-6.39453v.1123a2.358,2.358,0,0,0,2.43555,2.56445,2.04834,2.04834,0,0,0,2.09082-1.27344ZM109.94386,25.124h4.52637a2.17744,2.17744,0,0,0-2.2207-2.29834A2.29214,2.29214,0,0,0,109.94386,25.124Z" style="fill: #fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
57
note_kfet/static/img/appstore_badge_fr_preorder.svg
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Calque_2" data-name="Calque 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 126.5 40">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #a6a6a6;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="livetype">
|
||||
<g>
|
||||
<g>
|
||||
<path class="cls-1" d="M116.97,0H9.53c-.37,0-.73,0-1.09,0-.31,0-.61,0-.92.01-.67.02-1.34.06-2,.18-.67.12-1.29.32-1.9.63-.6.31-1.15.71-1.62,1.18-.48.47-.88,1.02-1.18,1.62-.31.61-.51,1.23-.63,1.9-.12.66-.16,1.33-.18,2,0,.31-.01.61-.02.92v23.11c0,.31,0,.61.02.92.02.67.06,1.34.18,2,.12.67.31,1.3.63,1.9.3.6.7,1.14,1.18,1.61.47.48,1.02.88,1.62,1.18.61.31,1.23.51,1.9.63.66.12,1.34.16,2,.18.31,0,.61.01.92.01.37,0,.73,0,1.09,0h107.44c.36,0,.72,0,1.08,0,.3,0,.62,0,.92-.01.67-.02,1.34-.06,2-.18.67-.12,1.29-.32,1.91-.63.6-.3,1.14-.7,1.62-1.18.48-.47.87-1.02,1.18-1.61.31-.61.51-1.23.62-1.9.12-.66.16-1.33.19-2,0-.31,0-.61,0-.92,0-.36,0-.72,0-1.09V9.54c0-.37,0-.73,0-1.09,0-.31,0-.61,0-.92-.02-.67-.06-1.34-.19-2-.11-.67-.31-1.29-.62-1.9-.31-.6-.71-1.15-1.18-1.62-.47-.47-1.02-.87-1.62-1.18-.62-.31-1.24-.51-1.91-.63-.66-.12-1.33-.16-2-.18-.3,0-.62-.01-.92-.01-.36,0-.72,0-1.08,0h0Z"/>
|
||||
<path d="M8.44,39.12c-.3,0-.6,0-.9-.01-.56-.02-1.22-.05-1.87-.16-.61-.11-1.15-.29-1.66-.55-.52-.26-.99-.61-1.4-1.02-.41-.41-.75-.87-1.02-1.4-.26-.5-.44-1.05-.54-1.66-.12-.67-.15-1.36-.17-1.88,0-.21-.01-.91-.01-.91V8.44s0-.69.01-.89c.01-.52.04-1.21.17-1.87.11-.61.28-1.16.54-1.66.27-.52.61-.99,1.02-1.4.41-.41.88-.76,1.4-1.02.51-.26,1.06-.44,1.65-.54.67-.12,1.36-.15,1.88-.16h.9s109.6-.01,109.6-.01h.91c.51.03,1.2.06,1.86.18.6.11,1.15.28,1.67.55.51.26.98.61,1.39,1.02.41.41.75.88,1.02,1.4.26.51.43,1.05.54,1.65.12.63.15,1.28.17,1.89,0,.28,0,.59,0,.89,0,.38,0,.73,0,1.09v20.93c0,.36,0,.72,0,1.08,0,.33,0,.62,0,.93-.02.59-.06,1.24-.17,1.85-.1.61-.28,1.16-.54,1.67-.27.52-.61.99-1.02,1.39-.41.42-.88.76-1.4,1.02-.52.26-1.05.44-1.67.55-.64.12-1.3.15-1.87.16-.29,0-.6.01-.9.01h-1.08s-108.53,0-108.53,0Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="cls-2" d="M24.77,20.3c-.03-2.75,2.25-4.09,2.36-4.15-1.29-1.88-3.29-2.14-3.99-2.16-1.68-.18-3.31,1-4.16,1s-2.19-.99-3.61-.96c-1.83.03-3.54,1.09-4.47,2.73-1.93,3.35-.49,8.27,1.36,10.98.93,1.33,2.01,2.81,3.43,2.75,1.39-.06,1.91-.88,3.58-.88s2.14.88,3.59.85c1.49-.02,2.43-1.33,3.32-2.67,1.07-1.52,1.5-3.02,1.52-3.09-.03-.01-2.89-1.1-2.92-4.4Z"/>
|
||||
<path class="cls-2" d="M22.04,12.21c.75-.93,1.26-2.2,1.11-3.49-1.08.05-2.43.75-3.21,1.66-.69.8-1.3,2.12-1.14,3.36,1.21.09,2.46-.61,3.24-1.53Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-2" d="M37.55,8.73c1.17,0,1.97.81,1.97,1.99s-.83,1.97-2,1.97h-1.38v2.01h-.93v-5.97h2.34ZM36.14,11.88h1.17c.8,0,1.27-.41,1.27-1.15s-.45-1.17-1.27-1.17h-1.17v2.32Z"/>
|
||||
<path class="cls-2" d="M40.73,10.2h.86v.69h.07c.13-.44.63-.77,1.22-.77.13,0,.3.01.4.04v.88c-.07-.02-.34-.05-.5-.05-.67,0-1.15.43-1.15,1.06v2.66h-.89v-4.5Z"/>
|
||||
<path class="cls-2" d="M47.94,13.49c-.2.81-.92,1.3-1.95,1.3-1.29,0-2.08-.88-2.08-2.32s.81-2.35,2.08-2.35,2.01.86,2.01,2.27v.31h-3.18v.05c.03.79.49,1.29,1.2,1.29.54,0,.91-.19,1.07-.55h.86ZM44.81,12.03h2.27c-.02-.71-.45-1.17-1.11-1.17s-1.12.46-1.17,1.17ZM45.45,9.45l1.04-1.42h1.04l-1.16,1.42h-.92Z"/>
|
||||
<path class="cls-2" d="M52.14,11.67c-.1-.44-.47-.76-1.06-.76-.74,0-1.2.57-1.2,1.53s.46,1.56,1.2,1.56c.56,0,.95-.26,1.06-.74h.86c-.12.91-.81,1.53-1.92,1.53-1.31,0-2.11-.88-2.11-2.35s.8-2.32,2.11-2.32c1.13,0,1.81.66,1.93,1.56h-.86Z"/>
|
||||
<path class="cls-2" d="M53.92,12.45c0-1.45.81-2.34,2.12-2.34s2.12.88,2.12,2.34-.81,2.34-2.12,2.34-2.12-.88-2.12-2.34ZM57.25,12.45c0-.98-.44-1.55-1.21-1.55s-1.21.57-1.21,1.55.43,1.55,1.21,1.55,1.21-.57,1.21-1.55Z"/>
|
||||
<path class="cls-2" d="M59.35,10.2h.86v.72h.07c.2-.51.65-.81,1.25-.81s1.04.32,1.24.81h.07c.23-.49.74-.81,1.37-.81.91,0,1.44.55,1.44,1.49v3.1h-.89v-2.87c0-.61-.29-.91-.87-.91s-.95.41-.95.94v2.83h-.87v-2.96c0-.51-.34-.82-.87-.82s-.95.44-.95,1.02v2.75h-.89v-4.5Z"/>
|
||||
<path class="cls-2" d="M67.02,10.2h.86v.72h.07c.2-.51.65-.81,1.25-.81s1.04.32,1.24.81h.07c.23-.49.74-.81,1.37-.81.91,0,1.44.55,1.44,1.49v3.1h-.89v-2.87c0-.61-.29-.91-.87-.91s-.95.41-.95.94v2.83h-.87v-2.96c0-.51-.34-.82-.87-.82s-.95.44-.95,1.02v2.75h-.89v-4.5Z"/>
|
||||
<path class="cls-2" d="M74.43,13.43c0-.81.6-1.28,1.67-1.34l1.22-.07v-.39c0-.48-.31-.74-.92-.74-.5,0-.84.18-.94.5h-.86c.09-.77.82-1.27,1.84-1.27,1.13,0,1.77.56,1.77,1.51v3.08h-.86v-.63h-.07c-.27.45-.76.71-1.35.71-.87,0-1.5-.52-1.5-1.35ZM77.33,13.04v-.38l-1.1.07c-.62.04-.9.25-.9.65s.35.64.83.64c.67,0,1.17-.43,1.17-.98Z"/>
|
||||
<path class="cls-2" d="M79.6,10.2h.86v.72h.07c.22-.5.67-.8,1.34-.8,1,0,1.56.6,1.56,1.67v2.92h-.89v-2.69c0-.72-.31-1.08-.97-1.08s-1.08.44-1.08,1.14v2.63h-.89v-4.5Z"/>
|
||||
<path class="cls-2" d="M84.58,12.45c0-1.42.73-2.32,1.87-2.32.62,0,1.14.29,1.38.79h.07v-2.47h.89v6.26h-.85v-.71h-.07c-.27.49-.79.79-1.41.79-1.15,0-1.87-.9-1.87-2.33ZM85.5,12.45c0,.96.45,1.53,1.2,1.53s1.21-.58,1.21-1.53-.47-1.53-1.21-1.53-1.2.58-1.2,1.53Z"/>
|
||||
<path class="cls-2" d="M94.05,13.49c-.2.81-.92,1.3-1.95,1.3-1.29,0-2.08-.88-2.08-2.32s.81-2.35,2.08-2.35,2.01.86,2.01,2.27v.31h-3.18v.05c.03.79.49,1.29,1.2,1.29.54,0,.91-.19,1.07-.55h.86ZM90.92,12.03h2.27c-.02-.71-.45-1.17-1.11-1.17s-1.12.46-1.17,1.17Z"/>
|
||||
<path class="cls-2" d="M95.3,10.2h.86v.69h.07c.13-.44.63-.77,1.22-.77.13,0,.3.01.4.04v.88c-.07-.02-.34-.05-.5-.05-.67,0-1.15.43-1.15,1.06v2.66h-.89v-4.5Z"/>
|
||||
<path class="cls-2" d="M102.9,10.11c1.01,0,1.67.47,1.76,1.27h-.85c-.08-.33-.41-.54-.91-.54s-.87.24-.87.59c0,.27.23.44.72.55l.75.17c.86.2,1.26.57,1.26,1.23,0,.85-.79,1.41-1.87,1.41s-1.77-.48-1.85-1.28h.89c.11.35.44.56.98.56s.95-.25.95-.61c0-.27-.21-.44-.66-.55l-.79-.18c-.86-.2-1.25-.59-1.25-1.26,0-.8.73-1.36,1.75-1.36Z"/>
|
||||
<path class="cls-2" d="M109.74,14.7h-.86v-.72h-.07c-.22.51-.68.8-1.36.8-1,0-1.55-.61-1.55-1.67v-2.92h.89v2.69c0,.73.29,1.08.95,1.08.72,0,1.11-.43,1.11-1.13v-2.63h.89v4.5Z"/>
|
||||
<path class="cls-2" d="M111.16,10.2h.86v.69h.07c.13-.44.63-.77,1.22-.77.13,0,.3.01.4.04v.88c-.07-.02-.34-.05-.5-.05-.67,0-1.15.43-1.15,1.06v2.66h-.89v-4.5Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-2" d="M35.2,18.07h1.86v12.42h-1.86v-12.42Z"/>
|
||||
<path class="cls-2" d="M39.3,22.61l1.02-4.54h1.81l-1.23,4.54h-1.59Z"/>
|
||||
<path class="cls-2" d="M49.15,27.13h-4.73l-1.14,3.36h-2l4.48-12.42h2.08l4.48,12.42h-2.04l-1.14-3.36ZM44.9,25.58h3.75l-1.85-5.45h-.05l-1.85,5.45Z"/>
|
||||
<path class="cls-2" d="M62,25.96c0,2.81-1.51,4.62-3.78,4.62-1.29,0-2.31-.58-2.85-1.58h-.04v4.48h-1.86v-12.05h1.8v1.51h.03c.52-.97,1.62-1.6,2.88-1.6,2.3,0,3.81,1.82,3.81,4.62ZM60.09,25.96c0-1.83-.95-3.04-2.39-3.04s-2.38,1.23-2.38,3.04.96,3.05,2.38,3.05,2.39-1.2,2.39-3.05Z"/>
|
||||
<path class="cls-2" d="M71.97,25.96c0,2.81-1.51,4.62-3.78,4.62-1.29,0-2.31-.58-2.85-1.58h-.04v4.48h-1.86v-12.05h1.8v1.51h.03c.52-.97,1.62-1.6,2.88-1.6,2.3,0,3.81,1.82,3.81,4.62ZM70.06,25.96c0-1.83-.95-3.04-2.39-3.04s-2.38,1.23-2.38,3.04.96,3.05,2.38,3.05,2.39-1.2,2.39-3.05Z"/>
|
||||
<path class="cls-2" d="M78.55,27.03c.14,1.23,1.33,2.04,2.97,2.04s2.69-.81,2.69-1.92c0-.96-.68-1.54-2.29-1.94l-1.61-.39c-2.28-.55-3.34-1.62-3.34-3.35,0-2.14,1.87-3.61,4.52-3.61s4.42,1.47,4.48,3.61h-1.88c-.11-1.24-1.14-1.99-2.63-1.99s-2.52.76-2.52,1.86c0,.88.65,1.39,2.25,1.79l1.37.34c2.55.6,3.61,1.63,3.61,3.44,0,2.32-1.85,3.78-4.79,3.78-2.75,0-4.61-1.42-4.73-3.67h1.9Z"/>
|
||||
<path class="cls-2" d="M90.19,19.29v2.14h1.72v1.47h-1.72v4.99c0,.78.34,1.14,1.1,1.14.19,0,.49-.03.61-.04v1.46c-.21.05-.62.09-1.03.09-1.83,0-2.55-.69-2.55-2.44v-5.19h-1.32v-1.47h1.32v-2.14h1.87Z"/>
|
||||
<path class="cls-2" d="M92.91,25.96c0-2.85,1.68-4.64,4.29-4.64s4.29,1.79,4.29,4.64-1.66,4.64-4.29,4.64-4.29-1.78-4.29-4.64ZM99.6,25.96c0-1.95-.9-3.11-2.4-3.11s-2.4,1.16-2.4,3.11.9,3.11,2.4,3.11,2.4-1.14,2.4-3.11Z"/>
|
||||
<path class="cls-2" d="M103.03,21.43h1.77v1.54h.04c.28-1.02,1.11-1.64,2.18-1.64.27,0,.49.04.64.07v1.74c-.15-.06-.47-.11-.83-.11-1.2,0-1.94.81-1.94,2.08v5.37h-1.86v-9.05Z"/>
|
||||
<path class="cls-2" d="M116.23,27.83c-.25,1.64-1.85,2.77-3.9,2.77-2.63,0-4.27-1.76-4.27-4.6s1.64-4.68,4.19-4.68,4.08,1.72,4.08,4.47v.64h-6.39v.11c0,1.55.97,2.56,2.44,2.56,1.03,0,1.84-.49,2.09-1.27h1.76ZM109.94,25.12h4.53c-.04-1.39-.93-2.3-2.22-2.3s-2.21.93-2.31,2.3Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
63
note_kfet/static/img/playstore_badge_fr.svg
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="artwork" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 135 40">
|
||||
<!-- Generator: Adobe Illustrator 29.2.1, SVG Export Plug-In . SVG Version: 2.1.0 Build 116) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #4285f4;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.st2 {
|
||||
fill: #a6a6a6;
|
||||
}
|
||||
|
||||
.st3 {
|
||||
fill: #34a853;
|
||||
}
|
||||
|
||||
.st4 {
|
||||
fill: #fbbc04;
|
||||
}
|
||||
|
||||
.st5 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.st6 {
|
||||
fill: #ea4335;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g>
|
||||
<rect width="135" height="40" rx="5" ry="5"/>
|
||||
<path class="st2" d="M130,.8c2.3,0,4.2,1.9,4.2,4.2v30c0,2.3-1.9,4.2-4.2,4.2H5c-2.3,0-4.2-1.9-4.2-4.2V5c0-2.3,1.9-4.2,4.2-4.2h125M130,0H5C2.2,0,0,2.2,0,5v30c0,2.8,2.2,5,5,5h125c2.8,0,5-2.2,5-5V5c0-2.8-2.2-5-5-5h0Z"/>
|
||||
<path class="st5" d="M68.1,21.8c-2.4,0-4.3,1.8-4.3,4.3s1.9,4.3,4.3,4.3,4.3-1.8,4.3-4.3-1.9-4.3-4.3-4.3ZM68.1,28.6c-1.3,0-2.4-1.1-2.4-2.6s1.1-2.6,2.4-2.6,2.4,1,2.4,2.6-1.1,2.6-2.4,2.6ZM58.8,21.8c-2.4,0-4.3,1.8-4.3,4.3s1.9,4.3,4.3,4.3,4.3-1.8,4.3-4.3-1.9-4.3-4.3-4.3ZM58.8,28.6c-1.3,0-2.4-1.1-2.4-2.6s1.1-2.6,2.4-2.6,2.4,1,2.4,2.6-1.1,2.6-2.4,2.6ZM47.7,23.1v1.8h4.3c-.1,1-.5,1.8-1,2.3-.6.6-1.6,1.3-3.3,1.3-2.7,0-4.7-2.1-4.7-4.8s2.1-4.8,4.7-4.8,2.5.6,3.3,1.3l1.3-1.3c-1.1-1-2.5-1.8-4.5-1.8-3.6,0-6.7,3-6.7,6.6s3.1,6.6,6.7,6.6,3.4-.6,4.6-1.9c1.2-1.2,1.6-2.9,1.6-4.2s0-.8,0-1.1h-6.1,0ZM93.1,24.5c-.4-1-1.4-2.7-3.6-2.7s-4,1.7-4,4.3,1.8,4.3,4.2,4.3,3.1-1.2,3.5-1.9l-1.4-1c-.5.7-1.1,1.2-2.1,1.2s-1.6-.4-2.1-1.3l5.7-2.4-.2-.5h0ZM87.3,25.9c0-1.6,1.3-2.5,2.2-2.5s1.4.4,1.6.9c0,0-3.8,1.6-3.8,1.6ZM82.6,30h1.9v-12.5h-1.9v12.5ZM79.6,22.7h0c-.4-.5-1.2-1-2.2-1-2.1,0-4.1,1.9-4.1,4.3s1.9,4.2,4.1,4.2,1.8-.5,2.2-1h0v.6c0,1.6-.9,2.5-2.3,2.5s-1.9-.8-2.1-1.5l-1.6.7c.5,1.1,1.7,2.5,3.8,2.5s4-1.3,4-4.4v-7.6h-1.8s0,.7,0,.7ZM77.4,28.6c-1.3,0-2.4-1.1-2.4-2.6s1.1-2.6,2.4-2.6,2.3,1.1,2.3,2.6-1,2.6-2.3,2.6ZM101.8,17.5h-4.5v12.5h1.9v-4.7h2.6c2.1,0,4.1-1.5,4.1-3.9s-2-3.9-4.1-3.9ZM101.9,23.5h-2.7v-4.3h2.7c1.4,0,2.2,1.2,2.2,2.1s-.8,2.1-2.2,2.1h0ZM113.4,21.7c-1.4,0-2.8.6-3.3,1.9l1.7.7c.4-.7,1-.9,1.7-.9s1.9.6,2,1.6h0c-.3,0-1.1-.4-1.9-.4-1.8,0-3.6,1-3.6,2.8s1.5,2.8,3.1,2.8,1.9-.6,2.4-1.2h0v1h1.8v-4.8c0-2.2-1.7-3.5-3.8-3.5h0ZM113.2,28.6c-.6,0-1.5-.3-1.5-1.1s1.1-1.3,2-1.3,1.2.2,1.7.4c-.1,1.2-1.1,2-2.2,2ZM123.7,22l-2.1,5.4h0l-2.2-5.4h-2l3.3,7.6-1.9,4.2h1.9l5.1-11.8h-2.1ZM106.9,30h1.9v-12.5h-1.9v12.5Z"/>
|
||||
<g>
|
||||
<path class="st6" d="M20.7,19.4l-10.6,11.3s0,0,0,0c.3,1.2,1.4,2.1,2.8,2.1s1-.1,1.5-.4h0s12-6.9,12-6.9l-5.6-6.1Z"/>
|
||||
<path class="st4" d="M31.5,17.5h0s-5.2-3-5.2-3l-5.8,5.2,5.8,5.8,5.1-3c.9-.5,1.5-1.4,1.5-2.5s-.6-2-1.5-2.5h0Z"/>
|
||||
<path class="st0" d="M10.1,9.3c0,.2,0,.5,0,.7v20c0,.3,0,.5,0,.7l11-11s-11-10.4-11-10.4Z"/>
|
||||
<path class="st3" d="M20.8,20l5.5-5.5-12-6.9c-.4-.3-.9-.4-1.5-.4-1.3,0-2.5.9-2.8,2.1h0s10.7,10.7,10.7,10.7h0Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="st1">
|
||||
<g class="st1">
|
||||
<path class="st5" d="M41.8,6.9h2c.6,0,1.2.1,1.7.4s.9.6,1.1,1.1c.3.5.4,1,.4,1.6s-.1,1.1-.4,1.6c-.3.5-.6.8-1.1,1.1s-1,.4-1.7.4h-2v-6.2ZM43.8,12.2c.7,0,1.2-.2,1.6-.6.4-.4.6-.9.6-1.6s-.2-1.2-.6-1.6c-.4-.4-.9-.6-1.6-.6h-1v4.4h1Z"/>
|
||||
<path class="st5" d="M48.1,6.9h1v6.2h-1v-6.2Z"/>
|
||||
<path class="st5" d="M50.9,12.8c-.4-.3-.7-.7-.9-1.3l.9-.4c0,.3.3.6.5.8s.5.3.8.3.6,0,.8-.2c.2-.2.3-.4.3-.7s0-.5-.3-.6-.5-.3-1-.5h-.4c-.4-.3-.8-.5-1.1-.8-.3-.3-.4-.6-.4-1.1s0-.6.3-.9c.2-.3.4-.5.7-.6.3-.2.6-.2,1-.2.5,0,1,.1,1.3.4s.5.6.7.9l-.9.4c0-.2-.2-.4-.4-.5-.2-.2-.4-.2-.7-.2s-.5,0-.7.2-.3.3-.3.6,0,.4.3.5c.2.1.4.3.8.4h.4c.5.3.9.6,1.2.9s.4.7.4,1.2-.1.7-.3,1c-.2.3-.5.5-.8.6-.3.1-.7.2-1,.2-.5,0-1-.2-1.4-.5Z"/>
|
||||
<path class="st5" d="M55.5,6.9h2.2c.4,0,.7,0,1,.2.3.2.6.4.7.7s.3.6.3,1,0,.7-.3,1-.4.5-.7.7c-.3.2-.6.2-1,.2h-1.2v2.4h-1v-6.2ZM57.7,9.8c.2,0,.4,0,.6-.1.2,0,.3-.2.4-.4,0-.2.1-.3.1-.5s0-.3-.1-.5c0-.2-.2-.3-.4-.4-.2,0-.3-.1-.6-.1h-1.2v2h1.2Z"/>
|
||||
<path class="st5" d="M61.9,12.8c-.5-.3-.9-.7-1.2-1.2-.3-.5-.4-1-.4-1.6s.1-1.1.4-1.6c.3-.5.7-.9,1.2-1.2.5-.3,1-.4,1.6-.4s1.1.1,1.6.4c.5.3.9.7,1.2,1.2.3.5.4,1,.4,1.6s-.1,1.1-.4,1.6c-.3.5-.7.9-1.2,1.2-.5.3-1,.4-1.6.4s-1.2-.1-1.6-.4ZM64.6,12c.3-.2.6-.5.8-.8.2-.4.3-.8.3-1.2s0-.9-.3-1.2-.5-.6-.8-.8-.7-.3-1.1-.3-.8,0-1.1.3c-.3.2-.6.5-.8.8s-.3.8-.3,1.2.1.9.3,1.2c.2.4.5.6.8.8.3.2.7.3,1.1.3s.8,0,1.1-.3Z"/>
|
||||
<path class="st5" d="M67.9,6.9h1.2l2.8,4.5h0v-1.2c0,0,0-3.3,0-3.3h1v6.2h-1l-2.9-4.8h0v1.2c0,0,0,3.6,0,3.6h-1v-6.2Z"/>
|
||||
<path class="st5" d="M74.2,6.9h1v6.2h-1v-6.2Z"/>
|
||||
<path class="st5" d="M76.6,6.9h2.3c.3,0,.6,0,.9.2.3.1.5.3.7.6.2.3.3.5.3.8s0,.6-.2.8c-.2.2-.4.4-.6.5h0c.3.2.6.3.8.6s.3.6.3.9,0,.6-.3.9c-.2.3-.4.5-.7.6-.3.1-.6.2-1,.2h-2.4v-6.2ZM78.9,9.5c.3,0,.5,0,.7-.3.2-.2.3-.4.3-.6s0-.4-.2-.6c-.2-.2-.4-.3-.6-.3h-1.4v1.7h1.3ZM79,12.2c.3,0,.5,0,.7-.3.2-.2.3-.4.3-.6s0-.5-.3-.6c-.2-.2-.4-.3-.7-.3h-1.4v1.8h1.5Z"/>
|
||||
<path class="st5" d="M82,6.9h1v5.3h2.7v.9h-3.6v-6.2Z"/>
|
||||
<path class="st5" d="M86.7,6.9h3.8v.9h-2.8v1.7h2.5v.9h-2.5v1.7h2.8v.9h-3.8v-6.2Z"/>
|
||||
<path class="st5" d="M93.9,12.8c-.4-.3-.7-.7-.9-1.3l.9-.4c0,.3.3.6.5.8.2.2.5.3.8.3s.6,0,.8-.2c.2-.2.3-.4.3-.7s0-.5-.3-.6-.5-.3-1-.5h-.4c-.4-.3-.8-.5-1.1-.8-.3-.3-.4-.6-.4-1.1s0-.6.3-.9.4-.5.7-.6.6-.2,1-.2c.5,0,1,.1,1.3.4.3.3.5.6.7.9l-.9.4c0-.2-.2-.4-.4-.5-.2-.2-.4-.2-.7-.2s-.5,0-.7.2-.3.3-.3.6,0,.4.3.5c.2.1.4.3.8.4h.4c.5.3.9.6,1.2.9s.4.7.4,1.2-.1.7-.3,1c-.2.3-.5.5-.8.6-.3.1-.7.2-1,.2-.5,0-1-.2-1.4-.5Z"/>
|
||||
<path class="st5" d="M99.5,13c-.4-.2-.6-.5-.8-.9s-.3-.8-.3-1.3v-3.8h1v3.9c0,.5.1.8.4,1.1s.6.4,1,.4.8-.1,1-.4c.2-.3.4-.7.4-1.1v-3.9h1v3.8c0,.5,0,.9-.3,1.3s-.5.7-.8.9c-.4.2-.8.3-1.3.3s-.9-.1-1.2-.3Z"/>
|
||||
<path class="st5" d="M104.4,6.9h2.2c.4,0,.7,0,1,.2.3.2.5.4.7.7.2.3.3.6.3,1s-.1.8-.4,1.1c-.3.3-.6.5-1,.7h0s1.7,2.5,1.7,2.5h0c0,0-1.1,0-1.1,0l-1.6-2.4h-.7v2.4h-1v-6.2ZM106.5,9.8c.3,0,.5,0,.7-.3s.3-.4.3-.7,0-.3-.1-.5c0-.2-.2-.3-.3-.4-.2,0-.3-.1-.5-.1h-1.2v2h1.2Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
66
note_kfet/static/img/playstore_badge_fr_preorder.svg
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="artwork" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 135 40">
|
||||
<!-- Generator: Adobe Illustrator 29.2.1, SVG Export Plug-In . SVG Version: 2.1.0 Build 116) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #4285f4;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.st2 {
|
||||
fill: #a6a6a6;
|
||||
}
|
||||
|
||||
.st3 {
|
||||
fill: #34a853;
|
||||
}
|
||||
|
||||
.st4 {
|
||||
fill: #fbbc04;
|
||||
}
|
||||
|
||||
.st5 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.st6 {
|
||||
fill: #ea4335;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g>
|
||||
<rect width="135" height="40" rx="5" ry="5"/>
|
||||
<path class="st2" d="M130,.8c2.3,0,4.2,1.9,4.2,4.2v30c0,2.3-1.9,4.2-4.2,4.2H5c-2.3,0-4.2-1.9-4.2-4.2V5c0-2.3,1.9-4.2,4.2-4.2h125M130,0H5C2.2,0,0,2.2,0,5v30c0,2.8,2.2,5,5,5h125c2.8,0,5-2.2,5-5V5c0-2.8-2.2-5-5-5h0Z"/>
|
||||
<path class="st5" d="M68.1,21.8c-2.4,0-4.3,1.8-4.3,4.3s1.9,4.3,4.3,4.3,4.3-1.8,4.3-4.3-1.9-4.3-4.3-4.3ZM68.1,28.6c-1.3,0-2.4-1.1-2.4-2.6s1.1-2.6,2.4-2.6,2.4,1,2.4,2.6-1.1,2.6-2.4,2.6ZM58.8,21.8c-2.4,0-4.3,1.8-4.3,4.3s1.9,4.3,4.3,4.3,4.3-1.8,4.3-4.3-1.9-4.3-4.3-4.3ZM58.8,28.6c-1.3,0-2.4-1.1-2.4-2.6s1.1-2.6,2.4-2.6,2.4,1,2.4,2.6-1.1,2.6-2.4,2.6ZM47.7,23.1v1.8h4.3c-.1,1-.5,1.8-1,2.3-.6.6-1.6,1.3-3.3,1.3-2.7,0-4.7-2.1-4.7-4.8s2.1-4.8,4.7-4.8,2.5.6,3.3,1.3l1.3-1.3c-1.1-1-2.5-1.8-4.5-1.8-3.6,0-6.7,3-6.7,6.6s3.1,6.6,6.7,6.6,3.4-.6,4.6-1.9c1.2-1.2,1.6-2.9,1.6-4.2s0-.8,0-1.1h-6.1,0ZM93.1,24.5c-.4-1-1.4-2.7-3.6-2.7s-4,1.7-4,4.3,1.8,4.3,4.2,4.3,3.1-1.2,3.5-1.9l-1.4-1c-.5.7-1.1,1.2-2.1,1.2s-1.6-.4-2.1-1.3l5.7-2.4-.2-.5h0ZM87.3,25.9c0-1.6,1.3-2.5,2.2-2.5s1.4.4,1.6.9c0,0-3.8,1.6-3.8,1.6ZM82.6,30h1.9v-12.5h-1.9v12.5ZM79.6,22.7h0c-.4-.5-1.2-1-2.2-1-2.1,0-4.1,1.9-4.1,4.3s1.9,4.2,4.1,4.2,1.8-.5,2.2-1h0v.6c0,1.6-.9,2.5-2.3,2.5s-1.9-.8-2.1-1.5l-1.6.7c.5,1.1,1.7,2.5,3.8,2.5s4-1.3,4-4.4v-7.6h-1.8s0,.7,0,.7ZM77.4,28.6c-1.3,0-2.4-1.1-2.4-2.6s1.1-2.6,2.4-2.6,2.3,1.1,2.3,2.6-1,2.6-2.3,2.6ZM101.8,17.5h-4.5v12.5h1.9v-4.7h2.6c2.1,0,4.1-1.5,4.1-3.9s-2-3.9-4.1-3.9ZM101.9,23.5h-2.7v-4.3h2.7c1.4,0,2.2,1.2,2.2,2.1s-.8,2.1-2.2,2.1h0ZM113.4,21.7c-1.4,0-2.8.6-3.3,1.9l1.7.7c.4-.7,1-.9,1.7-.9s1.9.6,2,1.6h0c-.3,0-1.1-.4-1.9-.4-1.8,0-3.6,1-3.6,2.8s1.5,2.8,3.1,2.8,1.9-.6,2.4-1.2h0v1h1.8v-4.8c0-2.2-1.7-3.5-3.8-3.5h0ZM113.2,28.6c-.6,0-1.5-.3-1.5-1.1s1.1-1.3,2-1.3,1.2.2,1.7.4c-.1,1.2-1.1,2-2.2,2ZM123.7,22l-2.1,5.4h0l-2.2-5.4h-2l3.3,7.6-1.9,4.2h1.9l5.1-11.8h-2.1ZM106.9,30h1.9v-12.5h-1.9v12.5Z"/>
|
||||
<g>
|
||||
<path class="st6" d="M20.7,19.4l-10.6,11.3s0,0,0,0c.3,1.2,1.4,2.1,2.8,2.1s1-.1,1.5-.4h0s12-6.9,12-6.9l-5.6-6.1Z"/>
|
||||
<path class="st4" d="M31.5,17.5h0s-5.2-3-5.2-3l-5.8,5.2,5.8,5.8,5.1-3c.9-.5,1.5-1.4,1.5-2.5s-.6-2-1.5-2.5h0Z"/>
|
||||
<path class="st0" d="M10.1,9.3c0,.2,0,.5,0,.7v20c0,.3,0,.5,0,.7l11-11s-11-10.4-11-10.4Z"/>
|
||||
<path class="st3" d="M20.8,20l5.5-5.5-12-6.9c-.4-.3-.9-.4-1.5-.4-1.3,0-2.5.9-2.8,2.1h0s10.7,10.7,10.7,10.7h0Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="st1">
|
||||
<g class="st1">
|
||||
<path class="st5" d="M42.1,12.8c-.4-.3-.6-.7-.8-1.2l.8-.3c0,.3.2.6.5.8.2.2.5.3.8.3s.5,0,.7-.2c.2-.1.3-.3.3-.6s0-.4-.3-.6c-.2-.2-.5-.3-.9-.5h-.4c-.4-.3-.7-.5-1-.7-.3-.3-.4-.6-.4-1s0-.5.2-.8c.2-.2.4-.4.6-.6.3-.1.6-.2.9-.2.5,0,.9.1,1.2.4.3.2.5.5.6.8l-.8.3c0-.2-.2-.3-.3-.5s-.4-.2-.6-.2-.5,0-.7.2c-.2.1-.3.3-.3.5s0,.4.2.5c.2.1.4.3.8.4h.4c.5.3.9.5,1.1.8.3.3.4.6.4,1.1s0,.7-.3.9c-.2.3-.4.4-.7.6-.3.1-.6.2-.9.2-.5,0-.9-.1-1.3-.4Z"/>
|
||||
<path class="st5" d="M46.4,7.4h3.5v.9h-2.6v1.6h2.3v.8h-2.3v1.6h2.6v.9h-3.5v-5.7Z"/>
|
||||
<path class="st5" d="M52.8,7.4h2c.3,0,.6,0,.9.2.3.1.5.4.7.6s.3.6.3.9,0,.6-.3.9-.4.5-.7.6c-.3.1-.6.2-.9.2h-1.1v2.2h-.9v-5.7ZM54.8,10.1c.2,0,.4,0,.5-.1s.3-.2.3-.3.1-.3.1-.4,0-.3-.1-.4-.2-.2-.3-.3c-.1,0-.3-.1-.5-.1h-1.1v1.8h1.1Z"/>
|
||||
<path class="st5" d="M57.6,7.4h2c.3,0,.7,0,.9.2s.5.4.7.6.2.6.2.9-.1.7-.4,1-.6.5-.9.6h0s1.6,2.3,1.6,2.3h0s-1,0-1,0l-1.5-2.2h-.7v2.2h-.9v-5.7ZM59.6,10.1c.3,0,.5,0,.7-.3.2-.2.3-.4.3-.7s0-.3-.1-.4-.2-.3-.3-.3-.3-.1-.5-.1h-1.1v1.8h1.1Z"/>
|
||||
<path class="st5" d="M62.5,7.4h3.5v.9h-2.6v1.6h2.3v.8h-2.3v1.6h2.6v.9h-3.5v-5.7ZM64.2,5.9h1l-.6,1.1h-.7l.4-1.1Z"/>
|
||||
<path class="st5" d="M67.1,7.4h.9v5.7h-.9v-5.7Z"/>
|
||||
<path class="st5" d="M69.3,7.4h1.1l2.6,4.2h0v-1.1s0-3.1,0-3.1h.9v5.7h-.9l-2.7-4.4h0v1.1s0,3.3,0,3.3h-.9v-5.7Z"/>
|
||||
<path class="st5" d="M75.5,12.8c-.4-.3-.6-.7-.8-1.2l.8-.3c0,.3.2.6.5.8.2.2.5.3.8.3s.5,0,.7-.2c.2-.1.3-.3.3-.6s0-.4-.3-.6c-.2-.2-.5-.3-.9-.5h-.4c-.4-.3-.7-.5-1-.7s-.4-.6-.4-1,0-.5.2-.8c.2-.2.4-.4.6-.6.3-.1.6-.2.9-.2.5,0,.9.1,1.2.4.3.2.5.5.6.8l-.8.3c0-.2-.2-.3-.3-.5-.2-.1-.4-.2-.6-.2s-.5,0-.7.2c-.2.1-.3.3-.3.5s0,.4.2.5.4.3.8.4h.4c.5.3.9.5,1.1.8.3.3.4.6.4,1.1s0,.7-.3.9c-.2.3-.4.4-.7.6-.3.1-.6.2-.9.2-.5,0-.9-.1-1.3-.4Z"/>
|
||||
<path class="st5" d="M80.9,12.9c-.5-.3-.8-.6-1.1-1.1s-.4-1-.4-1.5.1-1.1.4-1.5.6-.8,1.1-1.1,1-.4,1.5-.4.8,0,1.2.2c.4.2.7.4.9.7l-.6.6c-.2-.2-.4-.4-.7-.5-.2-.1-.5-.2-.8-.2s-.7,0-1.1.3c-.3.2-.6.4-.8.7-.2.3-.3.7-.3,1.1s0,.8.3,1.1c.2.3.4.6.8.7s.7.3,1.1.3c.6,0,1.2-.3,1.6-.8l.6.6c-.3.3-.6.6-1,.8-.4.2-.8.3-1.3.3s-1.1-.1-1.5-.4Z"/>
|
||||
<path class="st5" d="M85.7,7.4h2c.3,0,.7,0,.9.2.3.1.5.4.7.6.2.3.2.6.2.9s-.1.7-.4,1-.6.5-.9.6h0s1.6,2.3,1.6,2.3h0s-1,0-1,0l-1.5-2.2h-.7v2.2h-.9v-5.7ZM87.7,10.1c.3,0,.5,0,.7-.3.2-.2.3-.4.3-.7s0-.3-.1-.4c0-.1-.2-.3-.3-.3s-.3-.1-.5-.1h-1.1v1.8h1.1Z"/>
|
||||
<path class="st5" d="M90.6,7.4h.9v5.7h-.9v-5.7Z"/>
|
||||
<path class="st5" d="M92.8,7.4h2c.3,0,.7,0,.9.2.3.1.5.4.7.6.2.3.2.6.2.9s-.1.7-.4,1-.6.5-.9.6h0s1.6,2.3,1.6,2.3h0s-1,0-1,0l-1.5-2.2h-.7v2.2h-.9v-5.7ZM94.8,10.1c.3,0,.5,0,.7-.3s.3-.4.3-.7,0-.3-.1-.4c0-.1-.2-.3-.3-.3s-.3-.1-.5-.1h-1.1v1.8h1.1Z"/>
|
||||
<path class="st5" d="M97.7,7.4h3.5v.9h-2.6v1.6h2.3v.8h-2.3v1.6h2.6v.9h-3.5v-5.7Z"/>
|
||||
<path class="st5" d="M104.3,12.8c-.4-.3-.6-.7-.8-1.2l.8-.3c0,.3.2.6.5.8.2.2.5.3.8.3s.5,0,.7-.2c.2-.1.3-.3.3-.6s0-.4-.3-.6c-.2-.2-.5-.3-.9-.5h-.4c-.4-.3-.7-.5-1-.7s-.4-.6-.4-1,0-.5.2-.8c.2-.2.4-.4.6-.6.3-.1.6-.2.9-.2.5,0,.9.1,1.2.4.3.2.5.5.6.8l-.8.3c0-.2-.2-.3-.3-.5-.2-.1-.4-.2-.6-.2s-.5,0-.7.2c-.2.1-.3.3-.3.5s0,.4.2.5.4.3.8.4h.4c.5.3.9.5,1.1.8.3.3.4.6.4,1.1s0,.7-.3.9c-.2.3-.4.4-.7.6-.3.1-.6.2-.9.2-.5,0-.9-.1-1.3-.4Z"/>
|
||||
<path class="st5" d="M109.5,13c-.3-.2-.6-.5-.8-.8-.2-.4-.3-.8-.3-1.2v-3.5h.9v3.6c0,.4.1.8.3,1,.2.3.5.4.9.4s.7-.1.9-.4c.2-.3.3-.6.3-1v-3.6h.9v3.5c0,.5,0,.9-.3,1.2-.2.4-.4.6-.8.8-.3.2-.7.3-1.2.3s-.8,0-1.1-.3Z"/>
|
||||
<path class="st5" d="M114,7.4h2c.3,0,.7,0,.9.2.3.1.5.4.7.6.2.3.2.6.2.9s-.1.7-.4,1-.6.5-.9.6h0s1.6,2.3,1.6,2.3h0s-1,0-1,0l-1.5-2.2h-.7v2.2h-.9v-5.7ZM116,10.1c.3,0,.5,0,.7-.3s.3-.4.3-.7,0-.3-.1-.4c0-.1-.2-.3-.3-.3s-.3-.1-.5-.1h-1.1v1.8h1.1Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
@@ -29,6 +29,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<link rel="stylesheet" href="{% static "bootstrap4/css/bootstrap.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="https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/css/intlTelInput.css">
|
||||
|
||||
{# JQuery, Bootstrap and Turbolinks JavaScript #}
|
||||
<script src="{% static "jquery/jquery.min.js" %}"></script>
|
||||
@@ -41,6 +43,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{# Translation in javascript files #}
|
||||
<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 form.media %}
|
||||
{{ form.media }}
|
||||
|
||||
@@ -39,6 +39,23 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<a href="{% url 'password_reset' %}"
|
||||
class="badge badge-light">{% trans 'Forgotten your password or username?' %}</a>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
{% now "Ymd" as current_date_str %}
|
||||
|
||||
{% if display_appstore_badge %}
|
||||
<a href="https://apps.apple.com/fr/app/la-note-kfet/id6754661723" class="d-inline-block mx-1" aria-label="{% trans 'Download on the AppStore' %}" style="cursor: pointer;">
|
||||
<img src="{% static 'img/' %}{% if current_date_str < '20260201' %}appstore_badge_fr_preorder.svg{% else %}appstore_badge_fr.svg{% endif %}"
|
||||
alt="{% trans 'Download on the AppStore' %}" style="height: 50px;">
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if display_playstore_badge %}
|
||||
<a href="https://play.google.com/store/apps/details?id=org.crans.bde.note&hl=fr" class="d-inline-block mx-1" aria-label="{% trans 'Get it on Google Play' %}" style="cursor: pointer;">
|
||||
<img src="{% static 'img/' %}{% if current_date_str < '20260201' %}playstore_badge_fr_preorder.svg{% else %}playstore_badge_fr.svg{% endif %}"
|
||||
alt="{% trans 'Get it on Google Play' %}" style="height: 50px;">
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -19,7 +19,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
<form method="post" id="profile_form">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{{ profile_form|crispy }}
|
||||
@@ -31,3 +31,45 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</div>
|
||||
</div>
|
||||
{% 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 %}
|
||||
@@ -12,10 +12,11 @@ django-filter~=25.1
|
||||
django-mailer~=2.3.2
|
||||
django-oauth-toolkit~=3.0.1
|
||||
django-phonenumber-field~=8.1.0
|
||||
django-polymorphic~=3.1.0
|
||||
django-polymorphic~=4.1.0
|
||||
djangorestframework~=3.16.0
|
||||
django-rest-polymorphic~=0.1.10
|
||||
django-tables2~=2.7.5
|
||||
python-memcached~=1.62
|
||||
phonenumbers~=9.0.8
|
||||
tablib~=3.8.0
|
||||
Pillow>=11.3.0
|
||||
|
||||