commit
e81976889f
@ -0,0 +1,41 @@ |
||||
# Generated by Django 5.1 on 2025-05-06 10:21 |
||||
|
||||
import django.db.models.deletion |
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('shop', '0026_alter_order_user'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.CreateModel( |
||||
name='ShippingAddress', |
||||
fields=[ |
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||
('street_address', models.CharField(max_length=255)), |
||||
('apartment', models.CharField(blank=True, max_length=50, null=True)), |
||||
('city', models.CharField(max_length=100)), |
||||
('state', models.CharField(blank=True, max_length=100, null=True)), |
||||
('postal_code', models.CharField(max_length=20)), |
||||
('country', models.CharField(max_length=100)), |
||||
], |
||||
), |
||||
migrations.AlterField( |
||||
model_name='order', |
||||
name='payment_status', |
||||
field=models.CharField(choices=[('UNPAID', 'Unpaid'), ('PAID', 'Paid'), ('FAILED', 'Failed'), ('REFUNDED', 'Refunded')], default='UNPAID', max_length=20), |
||||
), |
||||
migrations.AlterField( |
||||
model_name='order', |
||||
name='status', |
||||
field=models.CharField(choices=[('PENDING', 'Pending'), ('PAID', 'Paid'), ('SHIPPED', 'Shipped'), ('DELIVERED', 'Delivered'), ('CANCELED', 'Canceled'), ('REFUNDED', 'Refunded'), ('PREPARED', 'Prepared')], default='PENDING', max_length=20), |
||||
), |
||||
migrations.AddField( |
||||
model_name='order', |
||||
name='shipping_address', |
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.shippingaddress'), |
||||
), |
||||
] |
||||
@ -0,0 +1,18 @@ |
||||
# Generated by Django 5.1 on 2025-05-06 19:39 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('shop', '0027_shippingaddress_alter_order_payment_status_and_more'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AlterField( |
||||
model_name='order', |
||||
name='status', |
||||
field=models.CharField(choices=[('PENDING', 'Pending'), ('PAID', 'Paid'), ('SHIPPED', 'Shipped'), ('DELIVERED', 'Delivered'), ('CANCELED', 'Canceled'), ('REFUNDED', 'Refunded'), ('PREPARED', 'Prepared'), ('READY', 'Ready')], default='PENDING', max_length=20), |
||||
), |
||||
] |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,32 @@ |
||||
{% extends "admin/base_site.html" %} |
||||
{% load i18n l10n admin_urls %} |
||||
|
||||
{% block content %} |
||||
<div class="module"> |
||||
<h2>{{ title }}</h2> |
||||
<div> |
||||
<p>Change status for the following {{ orders|length }} orders:</p> |
||||
<ul> |
||||
{% for order in orders %} |
||||
<li>{{ order }}</li> |
||||
{% endfor %} |
||||
</ul> |
||||
</div> |
||||
|
||||
<form action="{% url 'admin:shop_order_changelist' %}" method="post"> |
||||
{% csrf_token %} |
||||
<input type="hidden" name="action" value="{{ action }}" /> |
||||
|
||||
{{ form.as_p }} |
||||
|
||||
{% for obj in orders %} |
||||
<input type="hidden" name="_selected_action" value="{{ obj.pk }}" /> |
||||
{% endfor %} |
||||
|
||||
<div class="actions"> |
||||
<input type="submit" name="apply" value="Change Status" class="default" /> |
||||
<a href="{% url 'admin:shop_order_changelist' %}" class="button cancel-link">Cancel</a> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
{% endblock %} |
||||
@ -0,0 +1,81 @@ |
||||
{% extends 'tournaments/base.html' %} |
||||
|
||||
{% block head_title %}Mes Commandes{% endblock %} |
||||
{% block first_title %}Padel Club{% endblock %} |
||||
{% block second_title %}Mes Commandes{% endblock %} |
||||
|
||||
{% block content %} |
||||
{% include 'shop/partials/navigation_base.html' %} |
||||
|
||||
<div class="grid-x"> |
||||
<div class="cell medium-12 large-6 padding10"> |
||||
<h1 class="club padding10">Mes Commandes</h1> |
||||
<div class="bubble"> |
||||
{% if orders %} |
||||
<table class="order-table"> |
||||
<tbody> |
||||
{% for order in orders %} |
||||
<tr class="{% cycle 'odd-row' 'even-row' %}"> |
||||
<td class="text-left">Commande #{{ order.id }}</td> |
||||
<td> |
||||
<a href="{% url 'shop:order_detail' order.id %}" class="view-btn">Détails</a> |
||||
</td> |
||||
<td class="text-left">{{ order.date_ordered|date:"d/m/Y H:i" }}</td> |
||||
<td class="text-left"> |
||||
{% if order.status == 'PENDING' %} |
||||
<span class="status-badge pending">En attente</span> |
||||
{% elif order.status == 'PAID' %} |
||||
<span class="status-badge paid">Payée</span> |
||||
{% elif order.status == 'PREPARED' %} |
||||
<span class="status-badge prepared">En cours de préparation</span> |
||||
{% elif order.status == 'SHIPPED' %} |
||||
<span class="status-badge shipped">Expédiée</span> |
||||
{% elif order.status == 'DELIVERED' %} |
||||
<span class="status-badge delivered">Livrée</span> |
||||
{% elif order.status == 'CANCELED' %} |
||||
<span class="status-badge canceled">Annulée</span> |
||||
{% elif order.status == 'REFUNDED' %} |
||||
<span class="status-badge refunded">Remboursée</span> |
||||
{% endif %} |
||||
</td> |
||||
<td class="price-column"> |
||||
{% if order.discount_amount > 0 %} |
||||
<span class="original-price">{{ order.total_price }}€</span> |
||||
<span class="discounted-price">{{ order.get_total_after_discount }}€</span> |
||||
{% else %} |
||||
{{ order.total_price }}€ |
||||
{% endif %} |
||||
</td> |
||||
<td class="actions"> |
||||
{% if order.status == 'PENDING' or order.status == 'PAID' %} |
||||
<form method="post" action="{% url 'shop:cancel_order' order.id %}" class="inline-form" onsubmit="return confirm('Êtes-vous sûr de vouloir annuler cette commande?');"> |
||||
{% csrf_token %} |
||||
<button type="submit" class="remove-btn">Annuler</button> |
||||
</form> |
||||
{% endif %} |
||||
</td> |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
</table> |
||||
{% else %} |
||||
<div class="empty-orders"> |
||||
<p>Vous n'avez pas encore de commandes.</p> |
||||
<a href="{% url 'shop:product_list' %}" class="checkout-button confirm-nav-button">Découvrir la boutique</a> |
||||
</div> |
||||
{% endif %} |
||||
</div> |
||||
|
||||
{% if messages %} |
||||
<div class="messages"> |
||||
{% for message in messages %} |
||||
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}"> |
||||
{{ message }} |
||||
</div> |
||||
{% endfor %} |
||||
</div> |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
|
||||
{% endblock %} |
||||
@ -0,0 +1,150 @@ |
||||
{% extends 'tournaments/base.html' %} |
||||
|
||||
{% block head_title %}Détail de commande{% endblock %} |
||||
{% block first_title %}Padel Club{% endblock %} |
||||
{% block second_title %}Détail de commande{% endblock %} |
||||
|
||||
{% block content %} |
||||
{% include 'shop/partials/navigation_base.html' %} |
||||
|
||||
<div class="grid-x"> |
||||
<div class="cell medium-8 large-8 padding10"> |
||||
<h1 class="club padding10">Commande #{{ order.id }}</h1> |
||||
<div class="bubble"> |
||||
<div class="order-meta"> |
||||
<div class="order-date"> |
||||
<strong>Date:</strong> {{ order.date_ordered|date:"d/m/Y H:i" }} |
||||
</div> |
||||
<div class="order-status"> |
||||
<strong>Statut:</strong> |
||||
{% if order.status == 'PENDING' %} |
||||
<span class="status-badge pending">En attente</span> |
||||
{% elif order.status == 'PREPARED' %} |
||||
<span class="status-badge prepared">En cours de préparation</span> |
||||
{% elif order.status == 'PAID' %} |
||||
<span class="status-badge paid">Payée</span> |
||||
{% elif order.status == 'SHIPPED' %} |
||||
<span class="status-badge shipped">Expédiée</span> |
||||
{% elif order.status == 'DELIVERED' %} |
||||
<span class="status-badge delivered">Livrée</span> |
||||
{% elif order.status == 'CANCELED' %} |
||||
<span class="status-badge canceled">Annulée</span> |
||||
{% elif order.status == 'REFUNDED' %} |
||||
<span class="status-badge refunded">Remboursée</span> |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="order-items-section"> |
||||
<h3>Produits</h3> |
||||
{% with items=order_items total_quantity=total_quantity total_price=order.total_price %} |
||||
{% include 'shop/partials/order_items_display.html' with items=items total_quantity=total_quantity total_price=total_price edit_mode=False cancel_mode=order.is_cancellable %} |
||||
{% endwith %} |
||||
</div> |
||||
|
||||
<div class="coupon-section"> |
||||
<div>Adresse de livraison (dans la mesure du possible)</div> |
||||
{% if order.shipping_address %} |
||||
<div class="address-details" style="margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 4px;"> |
||||
<p style="margin: 0;">{{ order.shipping_address.street_address }}</p> |
||||
{% if order.shipping_address.apartment %} |
||||
<p style="margin: 5px 0;">{{ order.shipping_address.apartment }}</p> |
||||
{% endif %} |
||||
<p style="margin: 0;">{{ order.shipping_address.postal_code }} {{ order.shipping_address.city }}, {{ order.shipping_address.country }}</p> |
||||
</div> |
||||
{% if order.shipping_address_can_be_edited %} |
||||
<button class="edit-address-btn confirm-nav-button" style="margin-top: 10px;" onclick="toggleAddressForm()">Modifier l'adresse</button> |
||||
|
||||
<div id="address-form-container" style="display: none; margin-top: 10px;"> |
||||
<form method="post" action="{% url 'shop:update_shipping_address' order.id %}" class="coupon-form"> |
||||
{% csrf_token %} |
||||
<input type="text" name="street_address" class="address-input" style="width: 100%;" placeholder="Adresse" value="{{ order.shipping_address.street_address }}"> |
||||
<div style="margin: 10px 0;"> |
||||
<input type="text" name="apartment" class="address-input" style="width: 100%;" placeholder="Appartement (optionnel)" value="{{ order.shipping_address.apartment|default:'' }}"> |
||||
</div> |
||||
<div style="display: flex; gap: 10px;"> |
||||
<input type="text" name="postal_code" class="address-input" style="width: 25%;" placeholder="Code postal" value="{{ order.shipping_address.postal_code }}"> |
||||
<input type="text" name="city" class="address-input" style="width: 25%;" placeholder="Ville" value="{{ order.shipping_address.city }}"> |
||||
<input type="text" name="country" class="address-input" style="width: 25%;" placeholder="Pays" value="{{ order.shipping_address.country }}"> |
||||
</div> |
||||
<button type="submit" class="save-btn confirm-nav-button">Enregistrer</button> |
||||
</form> |
||||
</div> |
||||
{% endif %} |
||||
{% else %} |
||||
<p>Aucune adresse de livraison renseignée</p> |
||||
{% if order.shipping_address_can_be_edited %} |
||||
<button class="add-address-btn confirm-nav-button" onclick="toggleAddressForm()">Ajouter une adresse</button> |
||||
<div id="address-form-container" style="display: none; margin-top: 10px;"> |
||||
<form method="post" action="{% url 'shop:update_shipping_address' order.id %}" class="coupon-form"> |
||||
{% csrf_token %} |
||||
<input type="text" name="street_address" class="address-input" style="width: 100%;" placeholder="Adresse"> |
||||
<div style="margin: 10px 0;"> |
||||
<input type="text" name="apartment" class="address-input" style="width: 100%;" placeholder="Appartement (optionnel)"> |
||||
</div> |
||||
<div style="display: flex; gap: 10px;"> |
||||
<input type="text" name="postal_code" class="address-input" style="width: 25%;" placeholder="Code postal"> |
||||
<input type="text" name="city" class="address-input" style="width: 25%;" placeholder="Ville"> |
||||
<input type="text" name="country" class="address-input" style="width: 25%;" placeholder="Pays"> |
||||
</div> |
||||
<button type="submit" class="save-btn confirm-nav-button">Enregistrer</button> |
||||
</form> |
||||
</div> |
||||
{% endif %} |
||||
{% endif %} |
||||
</div> |
||||
|
||||
{% if order.discount_amount > 0 %} |
||||
<div class="discount-section"> |
||||
<div class="discount-row"> |
||||
<span>Sous-total:</span> |
||||
<span class="price-value">{{ order.total_price }}€</span> |
||||
</div> |
||||
<div class="discount-row"> |
||||
<span>Réduction:</span> |
||||
<span class="price-value">-{{ order.discount_amount }}€</span> |
||||
</div> |
||||
<div class="discount-row total-row"> |
||||
<span>Total final:</span> |
||||
<span class="price-value">{{ order.get_total_after_discount }}€</span> |
||||
</div> |
||||
|
||||
{% if order.coupon %} |
||||
<div class="coupon-info"> |
||||
Coupon appliqué: {{ order.coupon.code }} |
||||
</div> |
||||
{% endif %} |
||||
</div> |
||||
{% endif %} |
||||
|
||||
<div class="order-actions"> |
||||
{% if order.status == 'PENDING' or order.status == 'PAID' %} |
||||
<form method="post" action="{% url 'shop:cancel_order' order.id %}" class="inline-form" onsubmit="return confirm('Êtes-vous sûr de vouloir annuler cette commande? Cette action est irréversible.');"> |
||||
{% csrf_token %} |
||||
<button type="submit" class="remove-btn">Annuler la commande</button> |
||||
</form> |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
{% if messages %} |
||||
<div class="messages"> |
||||
{% for message in messages %} |
||||
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}"> |
||||
{{ message }} |
||||
</div> |
||||
{% endfor %} |
||||
</div> |
||||
{% endif %} |
||||
</div> |
||||
|
||||
<script> |
||||
function toggleAddressForm() { |
||||
const formContainer = document.getElementById('address-form-container'); |
||||
const isHidden = formContainer.style.display === 'none'; |
||||
formContainer.style.display = isHidden ? 'block' : 'none'; |
||||
} |
||||
</script> |
||||
|
||||
{% endblock %} |
||||
@ -0,0 +1,12 @@ |
||||
<nav class="margin10"> |
||||
<a href="{% url 'index' %}" class="orange">Accueil</a> |
||||
<a href="{% url 'clubs' %}" class="orange">Clubs</a> |
||||
{% if user.is_authenticated %} |
||||
<a href="{% url 'my-tournaments' %}" class="orange">Mes tournois</a> |
||||
<a href="{% url 'shop:my_orders' %}">Mes commandes</a> |
||||
<a href="{% url 'profile' %}">Mon compte</a> |
||||
{% else %} |
||||
<a href="{% url 'custom-login' %}">Se connecter</a> |
||||
{% endif %} |
||||
<a href="{% url 'shop:product_list' %}">La boutique</a> |
||||
</nav> |
||||
@ -1,60 +1,68 @@ |
||||
<table class="cart-table"> |
||||
<tbody> |
||||
{% for item in items %} |
||||
<tr class="{% cycle 'odd-row' 'even-row' %}"> |
||||
<td class="text-left product-name" data-label="Produit">{{ item.product_title }}</td> |
||||
{% if item.product_description %} |
||||
<td class="text-left product-description" data-label="Description">{{ item.product_description }}</td> |
||||
{% endif %} |
||||
<td class="product-color" data-label="Couleur"> |
||||
<div class="color-display"> |
||||
<div class="color-sample-cart" |
||||
{% if item.secondary_color_hex %} |
||||
style="background-image: linear-gradient(to right, {{ item.color_hex }} 50%, {{ item.secondary_color_hex }} 50%);" |
||||
{% else %} |
||||
style="background-color: {{ item.color_hex }};" |
||||
{% endif %} |
||||
></div> |
||||
{{ item.color_name }} | {{ item.size_name }} |
||||
</div> |
||||
</td> |
||||
<td class="product-quantity" data-label="Quantité"> |
||||
{% if edit_mode %} |
||||
<div class="quantity-controls"> |
||||
<form method="post" action="{% url 'shop:update_cart_item' %}" class="quantity-form"> |
||||
{% csrf_token %} |
||||
<input type="hidden" name="item_id" value="{{ item.id }}"> |
||||
<button type="submit" name="action" value="decrease" class="quantity-btn" {% if item.quantity <= 1 %}disabled{% endif %}>-</button> |
||||
<span class="quantity-value">{{ item.quantity }}</span> |
||||
<button type="submit" name="action" value="increase" class="quantity-btn">+</button> |
||||
</form> |
||||
</div> |
||||
{% else %} |
||||
<span>x {{ item.quantity }}</span> |
||||
{% endif %} |
||||
</td> |
||||
<td class="price-column product-price" data-label="Prix">{{ item.total_price }} €</td> |
||||
{% if edit_mode %} |
||||
<td class="product-actions"> |
||||
<form method="post" action="{% url 'shop:remove_from_cart' %}" class="remove-form"> |
||||
{% csrf_token %} |
||||
<input type="hidden" name="item_id" value="{{ item.id }}"> |
||||
<button type="submit" class="remove-btn">retirer</button> |
||||
</form> |
||||
</td> |
||||
{% endif %} |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
<tfoot> |
||||
<tr> |
||||
<td class="total-quantity" data-label="total-quantity">{{ total_quantity }} produit(s)</td> |
||||
<td class="total-label text-left"></td> |
||||
<td class="total-label text-left"></td> |
||||
<td class="price-column total-price" data-label="total-price">{{ total_price }} €</td> |
||||
{% if edit_mode %} |
||||
<td class="total-label text-left"></td> |
||||
{% endif %} |
||||
</tr> |
||||
</tfoot> |
||||
<tbody> |
||||
{% for item in items %} |
||||
<tr class="{% cycle 'odd-row' 'even-row' %}"> |
||||
<td class="text-left product-name" data-label="Produit">{{ item.product_title }}</td> |
||||
{% if item.product_description %} |
||||
<td class="text-left product-description" data-label="Description">{{ item.product_description }}</td> |
||||
{% endif %} |
||||
<td class="product-color" data-label="Couleur"> |
||||
<div class="color-display"> |
||||
<div class="color-sample-cart" |
||||
{% if item.secondary_color_hex %} |
||||
style="background-image: linear-gradient(to right, {{ item.color_hex }} 50%, {{ item.secondary_color_hex }} 50%);" |
||||
{% else %} |
||||
style="background-color: {{ item.color_hex }};" |
||||
{% endif %} |
||||
></div> |
||||
{{ item.color_name }} | {{ item.size_name }} |
||||
</div> |
||||
</td> |
||||
<td class="product-quantity" data-label="Quantité"> |
||||
{% if edit_mode %} |
||||
<div class="quantity-controls"> |
||||
<form method="post" action="{% url 'shop:update_cart_item' %}" class="quantity-form"> |
||||
{% csrf_token %} |
||||
<input type="hidden" name="item_id" value="{{ item.id }}"> |
||||
<button type="submit" name="action" value="decrease" class="quantity-btn" {% if item.quantity <= 1 %}disabled{% endif %}>-</button> |
||||
<span class="quantity-value">{{ item.quantity }}</span> |
||||
<button type="submit" name="action" value="increase" class="quantity-btn">+</button> |
||||
</form> |
||||
</div> |
||||
{% else %} |
||||
<span>x {{ item.quantity }}</span> |
||||
{% endif %} |
||||
</td> |
||||
<td class="price-column product-price" data-label="Prix">{{ item.total_price }} €</td> |
||||
{% if edit_mode %} |
||||
<td class="product-actions"> |
||||
<form method="post" action="{% url 'shop:remove_from_cart' %}" class="remove-form"> |
||||
{% csrf_token %} |
||||
<input type="hidden" name="item_id" value="{{ item.id }}"> |
||||
<button type="submit" class="remove-btn">retirer</button> |
||||
</form> |
||||
</td> |
||||
{% elif cancel_mode and items.count > 1 %} |
||||
<td class="product-actions"> |
||||
<form method="post" action="{% url 'shop:cancel_order_item' order.id item.id %}" class="remove-form" |
||||
onsubmit="return confirm('Êtes-vous sûr de vouloir annuler cet article ? {% if order.status == 'PAID' %}Un remboursement sera effectué.{% endif %}');"> |
||||
{% csrf_token %} |
||||
<button type="submit" class="remove-btn">annuler</button> |
||||
</form> |
||||
</td> |
||||
{% endif %} |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
<tfoot> |
||||
<tr> |
||||
<td class="total-quantity" data-label="total-quantity">{{ total_quantity }} produit(s)</td> |
||||
<td class="total-label text-left"></td> |
||||
<td class="total-label text-left"></td> |
||||
<td class="price-column total-price" data-label="total-price">{{ total_price }} €</td> |
||||
{% if edit_mode or cancel_mode %} |
||||
<td class="total-label text-left"></td> |
||||
{% endif %} |
||||
</tr> |
||||
</tfoot> |
||||
</table> |
||||
|
||||
@ -0,0 +1,18 @@ |
||||
# Generated by Django 5.1 on 2025-05-03 05:34 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0117_playerregistration_user_teamregistration_user_and_more'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='tournament', |
||||
name='animation_type', |
||||
field=models.IntegerField(choices=[(0, 'Tournoi'), (1, 'Mêlée')], default=0), |
||||
), |
||||
] |
||||
@ -0,0 +1,18 @@ |
||||
# Generated by Django 5.1 on 2025-05-06 07:56 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0118_tournament_animation_type'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AlterField( |
||||
model_name='tournament', |
||||
name='animation_type', |
||||
field=models.IntegerField(choices=[(0, 'Tournoi'), (1, 'Mêlée'), (2, 'Classement'), (3, 'Consolation')], default=0), |
||||
), |
||||
] |
||||
@ -0,0 +1,33 @@ |
||||
# Generated by Django 5.1 on 2025-05-07 07:58 |
||||
|
||||
import django.db.models.deletion |
||||
import django.utils.timezone |
||||
import tournaments.models.image |
||||
import uuid |
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0119_alter_tournament_animation_type'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.CreateModel( |
||||
name='Image', |
||||
fields=[ |
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), |
||||
('title', models.CharField(blank=True, max_length=255)), |
||||
('description', models.TextField(blank=True)), |
||||
('image', models.ImageField(upload_to=tournaments.models.image.image_upload_path)), |
||||
('uploaded_at', models.DateTimeField(default=django.utils.timezone.now)), |
||||
('image_type', models.CharField(choices=[('sponsor', 'Sponsor'), ('club', 'Club')], default='sponsor', max_length=20)), |
||||
('is_primary', models.BooleanField(default=False)), |
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='tournaments.event')), |
||||
], |
||||
options={ |
||||
'ordering': ['-uploaded_at'], |
||||
}, |
||||
), |
||||
] |
||||
@ -0,0 +1,26 @@ |
||||
# Generated by Django 5.1 on 2025-05-07 11:42 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0120_image'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AlterModelOptions( |
||||
name='image', |
||||
options={'ordering': ['order']}, |
||||
), |
||||
migrations.RemoveField( |
||||
model_name='image', |
||||
name='is_primary', |
||||
), |
||||
migrations.AddField( |
||||
model_name='image', |
||||
name='order', |
||||
field=models.IntegerField(default=0), |
||||
), |
||||
] |
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,18 @@ |
||||
# Generated by Django 5.1 on 2025-05-13 09:44 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0122_groupstage_planned_start_date_and_more'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='teamregistration', |
||||
name='unique_random_index', |
||||
field=models.IntegerField(default=0), |
||||
), |
||||
] |
||||
@ -0,0 +1,33 @@ |
||||
# Generated by Django 5.1 on 2025-05-19 09:33 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0123_teamregistration_unique_random_index'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='playerregistration', |
||||
name='club_code', |
||||
field=models.CharField(blank=True, max_length=20, null=True), |
||||
), |
||||
migrations.AddField( |
||||
model_name='playerregistration', |
||||
name='club_member', |
||||
field=models.BooleanField(default=False), |
||||
), |
||||
migrations.AddField( |
||||
model_name='tournament', |
||||
name='club_member_fee_deduction', |
||||
field=models.FloatField(blank=True, null=True), |
||||
), |
||||
migrations.AlterField( |
||||
model_name='club', |
||||
name='code', |
||||
field=models.CharField(blank=True, max_length=20, null=True), |
||||
), |
||||
] |
||||
@ -0,0 +1,18 @@ |
||||
# Generated by Django 5.1 on 2025-05-19 13:05 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0124_playerregistration_club_code_and_more'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='tournament', |
||||
name='unregister_delta_in_hours', |
||||
field=models.IntegerField(default=24), |
||||
), |
||||
] |
||||
@ -0,0 +1,24 @@ |
||||
# Generated by Django 5.1 on 2025-05-20 09:46 |
||||
|
||||
import django.db.models.deletion |
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0125_tournament_unregister_delta_in_hours'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='image', |
||||
name='club', |
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='tournaments.club'), |
||||
), |
||||
migrations.AlterField( |
||||
model_name='image', |
||||
name='event', |
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='tournaments.event'), |
||||
), |
||||
] |
||||
@ -0,0 +1,14 @@ |
||||
# Generated by Django 5.1 on 2025-06-04 13:34 |
||||
|
||||
from django.db import migrations |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('tournaments', '0119_customuser_can_synchronize'), |
||||
('tournaments', '0126_image_club_alter_image_event'), |
||||
] |
||||
|
||||
operations = [ |
||||
] |
||||
@ -0,0 +1,49 @@ |
||||
from django.db import models |
||||
import uuid |
||||
import os |
||||
from django.utils.timezone import now |
||||
from .event import Event, Club |
||||
|
||||
def image_upload_path(instance, filename): |
||||
"""Generate a unique file path for the uploaded image.""" |
||||
# Get the file extension from the original filename |
||||
ext = filename.split('.')[-1] |
||||
# Create a unique filename using UUID |
||||
unique_filename = f"{uuid.uuid4().hex}.{ext}" |
||||
|
||||
if instance.event: |
||||
# Determine the folder based on the event |
||||
folder = f"event_{instance.event.id}" |
||||
elif instance.club: |
||||
folder = f"club_{instance.club.id}" |
||||
else: |
||||
folder = f"unknown" |
||||
# Return the complete upload path |
||||
return os.path.join('images', folder, unique_filename) |
||||
|
||||
class Image(models.Model): |
||||
"""Model for storing uploaded images for events.""" |
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) |
||||
title = models.CharField(max_length=255, blank=True) |
||||
description = models.TextField(blank=True) |
||||
image = models.ImageField(upload_to=image_upload_path) |
||||
uploaded_at = models.DateTimeField(default=now) |
||||
|
||||
# Relation to event model |
||||
event = models.ForeignKey(Event, null=True, blank=True, on_delete=models.CASCADE, related_name='images') |
||||
club = models.ForeignKey(Club, null=True, blank=True, on_delete=models.CASCADE, related_name='images') |
||||
|
||||
# Image type for filtering |
||||
IMAGE_TYPES = ( |
||||
('sponsor', 'Sponsor'), |
||||
('club', 'Club'), |
||||
) |
||||
image_type = models.CharField(max_length=20, choices=IMAGE_TYPES, default='sponsor') |
||||
|
||||
order = models.IntegerField(default=0) |
||||
|
||||
class Meta: |
||||
ordering = ['order'] |
||||
|
||||
def __str__(self): |
||||
return self.title or f"Event Image {self.id}" |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@ |
||||
{% extends 'tournaments/base.html' %} |
||||
{% block head_title %} Intégration Stripe {% endblock %} |
||||
{% block first_title %} Padel Club {% endblock %} |
||||
{% block second_title %} Intégration Stripe {% endblock %} |
||||
|
||||
{% block content %} |
||||
{% load static %} |
||||
{% load tz %} |
||||
|
||||
<div class="grid-x"> |
||||
<div class="bubble"> |
||||
<div class="cell medium-6 large-6 padding10"> |
||||
<label class="title">Intégration Stripe Terminée !</label> |
||||
<p>Veuillez retourner dans l'application pour terminer et valider la configuration.</p> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
@ -0,0 +1,19 @@ |
||||
{% extends 'tournaments/base.html' %} |
||||
{% block head_title %} Configuration Stripe {% endblock %} |
||||
{% block first_title %} Padel Club {% endblock %} |
||||
{% block second_title %} Configuration Stripe {% endblock %} |
||||
|
||||
{% block content %} |
||||
{% load static %} |
||||
{% load tz %} |
||||
|
||||
<div class="grid-x"> |
||||
<div class="bubble"> |
||||
<div class="cell medium-6 large-6 padding10"> |
||||
<label class="title">Renouvellement du Lien Stripe</label> |
||||
<p>Votre lien d'intégration Stripe a expiré. Un nouveau lien doit être généré.</p> |
||||
<p>Veuillez retourner dans l'application et ré-essayer.</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
@ -0,0 +1,375 @@ |
||||
<!DOCTYPE html> |
||||
{% load static %} |
||||
{% load qr_code %} |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> |
||||
<link rel="stylesheet" href="{% static 'tournaments/css/foundation.min.css' %}" /> |
||||
<link rel="stylesheet" href="{% static 'tournaments/css/basics.css' %}" /> |
||||
<link rel="stylesheet" href="{% static 'tournaments/css/style.css' %}" /> |
||||
<link rel="stylesheet" href="{% static 'tournaments/css/broadcast.css' %}" /> |
||||
<style> |
||||
.bubble { |
||||
padding: 10px; |
||||
background-color: white; |
||||
border-radius: 24px; |
||||
box-shadow: 0 0 0px 0px #fbead6; |
||||
} |
||||
.running { |
||||
background-color: #90ee90 !important; |
||||
} |
||||
.timeslot { |
||||
font-family: "Anybody-ExtraBold"; |
||||
font-size: clamp(0.4rem, 1.5vw - 0.2rem, 3rem); /* Adjust these values as needed */ |
||||
color: white; |
||||
} |
||||
.match-cell { |
||||
box-sizing: border-box; |
||||
padding: 0; |
||||
flex-grow: 0; |
||||
flex-shrink: 0; |
||||
} |
||||
.match-cell .bubble { |
||||
height: 13vh; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
box-sizing: border-box; |
||||
/* Dynamic font size based on viewport width */ |
||||
font-size: clamp(0.6rem, 1.2vw - 0.2rem, 5rem); /* Adjust these values as needed */ |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
color: black; |
||||
background-color: white; |
||||
margin: 0; |
||||
width: 100%; |
||||
} |
||||
.match-cell .bubble.even { |
||||
background-color: white; |
||||
} |
||||
.match-cell .bubble.empty { |
||||
background-color: rgba(173, 216, 230, 0.3); |
||||
color: white; |
||||
} |
||||
.match-cell .bubble.ended { |
||||
background-color: rgba(173, 216, 230, 0.3); |
||||
} |
||||
.court-label { |
||||
box-sizing: border-box; |
||||
padding: 0; |
||||
flex-grow: 0; |
||||
flex-shrink: 0; |
||||
} |
||||
.court-label .bubble { |
||||
height: 4vh; |
||||
font-weight: bold; |
||||
margin: 0; |
||||
width: 100%; |
||||
text-align: center; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
/* Slightly smaller dynamic font size for court labels */ |
||||
font-size: clamp(0.7rem, 0.8vw + 0.1rem, 3rem); /* Adjust these values as needed */ |
||||
color: white; |
||||
background: #1a223a; |
||||
} |
||||
.bubble-timeslot { |
||||
color: white; |
||||
background: #1a223a; |
||||
display: flex; |
||||
border-radius: 24px; |
||||
box-shadow: 0 0 0px 0px #fbead6; |
||||
align-items: center; |
||||
justify-content: center; |
||||
padding: 0px 20px; |
||||
margin: 0; |
||||
} |
||||
.courts-row, |
||||
.matches-row { |
||||
display: flex; |
||||
width: 100%; |
||||
gap: 10px; |
||||
} |
||||
.grid-x { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
} |
||||
.matchtitle { |
||||
font-size: inherit; /* Inherit the dynamic font size from .bubble */ |
||||
font-weight: bold; |
||||
margin-bottom: 0.2em; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
white-space: nowrap; |
||||
} |
||||
.minor-info { |
||||
font-size: inherit; /* Inherit the dynamic font size from .bubble */ |
||||
font-weight: normal; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
white-space: nowrap; |
||||
} |
||||
.score { |
||||
font-size: inherit; /* Inherit the dynamic font size from .bubble */ |
||||
} |
||||
.bold { |
||||
font-weight: bold; |
||||
} |
||||
.ws { |
||||
white-space: nowrap; |
||||
} |
||||
</style> |
||||
|
||||
<link rel="icon" type="image/png" href="{% static 'tournaments/images/favicon.png' %}" /> |
||||
|
||||
<title>Programmation</title> |
||||
|
||||
<script src="{% static 'tournaments/js/alpine.min.js' %}"></script> |
||||
<script> |
||||
var _paq = window._paq = window._paq || []; |
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */ |
||||
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]); |
||||
_paq.push(["setDoNotTrack", true]); |
||||
_paq.push(['trackPageView']); |
||||
_paq.push(['enableLinkTracking']); |
||||
(function() { |
||||
var u="//matomo.padelclub.app/"; |
||||
_paq.push(['setTrackerUrl', u+'matomo.php']); |
||||
_paq.push(['setSiteId', '1']); |
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; |
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); |
||||
})(); |
||||
</script> |
||||
</head> |
||||
|
||||
<body x-data="{ |
||||
days: [], |
||||
currentDayIndex: 0, |
||||
currentPageIndex: 0, |
||||
has_sponsors: {{ tournament.has_sponsors|lower }}, |
||||
matchGroups: [], |
||||
courtCount: {{ tournament.court_count|default:1 }}, |
||||
courtNames: {{ tournament.court_names }}, |
||||
retrieveData() { |
||||
fetch('/tournament/{{ tournament.id }}/planning/json/') |
||||
.then(res => res.json()) |
||||
.then((data) => { |
||||
this.days = data.days || []; |
||||
this.matchGroups = data.match_groups || []; |
||||
this.currentPageIndex = 0; |
||||
|
||||
if (this.days.length > 0 && this.currentDayIndex >= this.days.length) { |
||||
this.currentDayIndex = 0; |
||||
} |
||||
}); |
||||
}, |
||||
|
||||
getMatchGroupsForDay(day) { |
||||
let groupsPerPage = 12; |
||||
if (window.innerHeight <=720) { |
||||
groupsPerPage = 8; |
||||
} else if (window.innerHeight <=1080) { |
||||
groupsPerPage = 10; |
||||
} |
||||
|
||||
if (this.has_sponsors) { |
||||
groupsPerPage = groupsPerPage - 2; |
||||
} |
||||
|
||||
const formattedDay = day; |
||||
const filteredGroups = this.matchGroups.filter(group => { |
||||
if (!group.matches || group.matches.length === 0) return false; |
||||
return group.name && formattedDay && group.name.includes(formattedDay); |
||||
}); |
||||
|
||||
let groupsPerPageThreshold = this.courtCount >= 5 ? Math.ceil(groupsPerPage / 2) : groupsPerPage; |
||||
let columns = this.courtCount >= 5 ? 1 : 2; // Number of columns to display |
||||
|
||||
const paginatedGroups = []; |
||||
for (let i = 0; i < Math.ceil(filteredGroups.length / groupsPerPageThreshold); i++) { |
||||
// For each page |
||||
const pageGroups = filteredGroups.slice(i * groupsPerPageThreshold, (i + 1) * groupsPerPageThreshold); |
||||
|
||||
// Rearrange groups in vertical order |
||||
if (columns === 2) { |
||||
const rearrangedGroups = []; |
||||
const halfPageLength = Math.ceil(pageGroups.length / 2); |
||||
|
||||
// Create groups with alternating order: [0, halfPageLength, 1, halfPageLength+1, ...] |
||||
for (let col = 0; col < halfPageLength; col++) { |
||||
rearrangedGroups.push(pageGroups[col]); // First column |
||||
if (col + halfPageLength < pageGroups.length) { |
||||
rearrangedGroups.push(pageGroups[col + halfPageLength]); // Second column |
||||
} |
||||
} |
||||
paginatedGroups.push(rearrangedGroups); |
||||
} else { |
||||
paginatedGroups.push(pageGroups); |
||||
} |
||||
} |
||||
return paginatedGroups; |
||||
}, |
||||
|
||||
getCourtNumber(courtIndex) { |
||||
if (courtIndex == null) return 999; |
||||
return courtIndex; |
||||
}, |
||||
|
||||
organizeMatchesByCourt(matches) { |
||||
const courtMatches = Array(this.courtCount).fill(null); |
||||
if (matches && matches.length > 0) { |
||||
matches.forEach(match => { |
||||
if (match && match.court) { |
||||
const courtNum = this.getCourtNumber(match.court_index); |
||||
if (courtNum >= 0 && courtNum < this.courtCount) { |
||||
courtMatches[courtNum] = match; |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
return courtMatches; |
||||
}, |
||||
|
||||
loop() { |
||||
this.retrieveData(); |
||||
setInterval(() => { |
||||
if (this.days.length > 0) { |
||||
const currentDay = this.days[this.currentDayIndex]; |
||||
const pagesForDay = this.getMatchGroupsForDay(currentDay); |
||||
|
||||
if (pagesForDay && pagesForDay.length > 1) { |
||||
const _currentPageIndex = this.currentPageIndex; |
||||
this.currentPageIndex = (this.currentPageIndex + 1) % pagesForDay.length; |
||||
if (_currentPageIndex >= 1 && this.currentPageIndex === 0) { |
||||
this.currentDayIndex = (this.currentDayIndex + 1) % this.days.length; |
||||
} |
||||
} else { |
||||
this.currentPageIndex = 0; |
||||
this.currentDayIndex = (this.currentDayIndex + 1) % this.days.length; |
||||
} |
||||
} else { |
||||
this.currentDayIndex = 0; |
||||
this.currentPageIndex = 0; |
||||
} |
||||
}, 15000); |
||||
}, |
||||
|
||||
calculateFractionWidth() { |
||||
if (this.courtCount >= 5) { |
||||
const reductionFactor = 0.94; // Adjust this value |
||||
return `calc((100% / ${this.courtCount}) * ${reductionFactor})`; |
||||
} else { |
||||
const reductionFactor = 0.90; // Adjust this value |
||||
return `calc((100% / (${this.courtCount})) * ${reductionFactor})`; |
||||
} |
||||
} |
||||
}" x-init="loop()"> |
||||
|
||||
<header> |
||||
<div id="header" class="header-broadcast"> |
||||
<div class="left-content bubble-header"> |
||||
<img src="{% static 'tournaments/images/PadelClub_logo_512.png' %}" alt="logo" class="logo"> |
||||
<div class="left-margin"> |
||||
<h1 class="club">{{ tournament.broadcast_event_display_name }}</h1> |
||||
<h1 class="event" x-text="days[currentDayIndex]"></h1> |
||||
</div> |
||||
</div> |
||||
<div class="right-content">{% qr_from_text qr_code_url options=qr_code_options %}</div> |
||||
</div> |
||||
</header> |
||||
|
||||
<div class="wrapper"> |
||||
<main> |
||||
<div class="grid-x"> |
||||
<div class="cell" :class="{'large-12': courtCount >= 5, 'large-6': courtCount < 5}"> |
||||
<div style="display: flex; margin-bottom: 10px;"> |
||||
<div class="bubble-timeslot" style="visibility: hidden; align-items: center; justify-content: center; margin-right: 10px; width: 6vw;"> |
||||
<h1 class="timeslot">00:00</h1> |
||||
</div> |
||||
<div class="courts-row" style="margin-left: 10px; margin-bottom: -10px;"> |
||||
<template x-for="courtName in courtNames" :key="courtName"> |
||||
<div class="court-label" :style="{'width': calculateFractionWidth()}"> |
||||
<div class="bubble"> |
||||
<div class="score ws bold"><span x-text="courtName"></span></div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="cell" :class="{'large-12': courtCount >= 5, 'large-6': courtCount < 5}" x-show="courtCount < 5"> |
||||
<div style="display: flex; margin-bottom: 10px;"> |
||||
<div class="bubble-timeslot" style="visibility: hidden; align-items: center; justify-content: center; margin-right: 10px; width: 6vw;"> |
||||
<h1 class="timeslot">00:00</h1> |
||||
</div> |
||||
<div class="courts-row" style="margin-bottom: -10px;"> |
||||
<template x-for="courtName in courtNames" :key="courtName"> |
||||
<div class="court-label" :style="{'width': calculateFractionWidth()}"> |
||||
<div class="bubble"> |
||||
<div class="score ws bold"><span x-text="courtName"></span></div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<template x-for="(day, dayIndex) in days" :key="day"> |
||||
<div class="padding10" x-show="currentDayIndex === dayIndex"> |
||||
<template x-for="(groupPage, pageIndex) in getMatchGroupsForDay(day)" :key="'page-' + pageIndex"> |
||||
<div x-show="currentPageIndex === pageIndex"> |
||||
<div class="grid-x"> |
||||
<template x-for="(group, groupIndex) in groupPage" :key="groupIndex"> |
||||
<div class="cell" :class="{'large-12': courtCount >= 5, 'large-6': courtCount < 5}"> |
||||
<div style="display: flex; margin-bottom: 10px;"> |
||||
<div class="bubble-timeslot" style="align-items: center; justify-content: center; margin-right: 10px; width: 6vw;"> |
||||
<h1 class="timeslot" x-text="group.name.slice(-5)"></h1> |
||||
</div> |
||||
|
||||
<div class="matches-row"> |
||||
<template x-for="(match, courtIndex) in organizeMatchesByCourt(group.matches)" :key="courtIndex"> |
||||
<div class="match-cell" :style="{'width': calculateFractionWidth()}"> |
||||
<template x-if="match"> |
||||
<div class="bubble" :class="{'running': !match.ended && match.started, 'even': courtIndex % 2 === 1, 'ended': match.ended}" style="text-align: center;"> |
||||
<template x-if="match.tournament_title"> |
||||
<div class="minor-info semibold" x-text="match.tournament_title"></div> |
||||
</template> |
||||
<div class="bold" x-text="match.group_stage_name ? match.group_stage_name : match.title"></div> |
||||
<div class="minor-info" x-text="match.format"></div> |
||||
</div> |
||||
</template> |
||||
<template x-if="!match"> |
||||
<div class="bubble empty" style="text-align: center;"> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</template> |
||||
</main> |
||||
</div> |
||||
|
||||
<footer class="footer-broadcast"> |
||||
{% if tournament.event.images.exists %} |
||||
<div class="bubble-footer"> |
||||
<div class="bubble-sponsor"> |
||||
{% for image in tournament.event.images.all %} |
||||
<img src="{{ image.image.url }}" alt="{{ image.title|default:'Sponsor' }}" |
||||
class="sponsor-logo-broadcast"> |
||||
{% endfor %} |
||||
</div> |
||||
</div> |
||||
{% endif %} |
||||
</footer> |
||||
</body> |
||||
</html> |
||||
@ -1,40 +1,36 @@ |
||||
{% extends 'tournaments/base.html' %} |
||||
|
||||
{% block head_title %}Matchs du {{ tournament.display_name }}{% endblock %} |
||||
{% block head_title %}Programmation{% endblock %} |
||||
{% block first_title %}{{ tournament.event.display_name }}{% endblock %} |
||||
{% block second_title %}{{ tournament.display_name }}{% endblock %} |
||||
{% block second_title %}Programmation{% endblock %} |
||||
|
||||
{% block content %} |
||||
|
||||
{% if tournament.display_matches %} |
||||
{% if tournament.display_prog %} |
||||
{% include 'tournaments/navigation_tournament.html' %} |
||||
|
||||
{% if tournament.display_matches or tournament.display_group_stages %} |
||||
|
||||
{% regroup match_groups.matches by start_date|date:"l d F Y" as matches_by_date %} |
||||
|
||||
{% for date in matches_by_date %} |
||||
|
||||
{% regroup date.list by start_date|date:"H:i" as matches_by_hour %} |
||||
|
||||
{% for hour_group in matches_by_hour %} |
||||
<h1 class="club padding10 topmargin20">{{ date.grouper }} {{ hour_group.grouper }}</h1> |
||||
|
||||
{% regroup hour_group.list by court_index as matches_by_court %} |
||||
{% if days %} |
||||
<nav class="margin10"> |
||||
{% for day in days %} |
||||
<a href="{% url 'tournament-prog' tournament.id %}?day={{ day|date:'Y-m-d' }}" class="mybox topmargin5 {% if selected_day == day|date:'Y-m-d' %}active{% endif %}"> |
||||
{{ day|date:"l d F"|capfirst }} |
||||
</a> |
||||
{% endfor %} |
||||
</nav> |
||||
|
||||
{% if match_groups %} |
||||
{% for match_group in match_groups %} |
||||
<h1 class="club padding10 topmargin20">{{ match_group.name }}</h1> |
||||
<div class="grid-x"> |
||||
{% for court in matches_by_court|dictsort:"grouper" %} |
||||
{% for match_data in court.list %} |
||||
{% with match=match_data.match %} |
||||
{% include 'tournaments/match_cell.html' %} |
||||
{% endwith %} |
||||
{% endfor %} |
||||
{% for match in match_group.matches %} |
||||
{% include 'tournaments/match_cell.html' %} |
||||
{% endfor %} |
||||
</div> |
||||
{% endfor %} |
||||
{% endfor %} |
||||
|
||||
|
||||
{% else %} |
||||
<p class="padding10 topmargin20">Aucun match planifié pour cette journée.</p> |
||||
{% endif %} |
||||
{% else %} |
||||
<p class="padding10 topmargin20">Aucun match planifié pour ce tournoi.</p> |
||||
{% endif %} |
||||
{% endif %} |
||||
{% endblock %} |
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue