diff --git a/gestiojeux/settings_base.py b/gestiojeux/settings_base.py
index 78c4d5d..dad2919 100644
--- a/gestiojeux/settings_base.py
+++ b/gestiojeux/settings_base.py
@@ -26,6 +26,7 @@ INSTALLED_APPS = [
"django.contrib.messages",
"django.contrib.staticfiles",
"django_cas_ng",
+ "django_tables2",
"markdownx",
"haystack",
"website",
diff --git a/inventory/migrations/0004_alter_category_options_alter_gameloan_options_and_more.py b/inventory/migrations/0004_alter_category_options_alter_gameloan_options_and_more.py
new file mode 100644
index 0000000..282fb06
--- /dev/null
+++ b/inventory/migrations/0004_alter_category_options_alter_gameloan_options_and_more.py
@@ -0,0 +1,31 @@
+# Generated by Django 4.2.8 on 2024-05-06 14:23
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('inventory', '0003_gameloan'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='category',
+ options={'ordering': ['name'], 'verbose_name': 'étagère'},
+ ),
+ migrations.AlterModelOptions(
+ name='gameloan',
+ options={'ordering': ['borrow_date'], 'permissions': [('can_see_loan_details', 'Can see loan details')], 'verbose_name': 'emprunt', 'verbose_name_plural': 'emprunts'},
+ ),
+ migrations.AlterField(
+ model_name='gameloan',
+ name='borrow_date',
+ field=models.DateTimeField(auto_now_add=True, verbose_name='Date d’emprunt'),
+ ),
+ migrations.AlterField(
+ model_name='gameloan',
+ name='return_date',
+ field=models.DateTimeField(null=True, verbose_name='Date de retour'),
+ ),
+ ]
diff --git a/inventory/models.py b/inventory/models.py
index f7aa776..bd05a03 100644
--- a/inventory/models.py
+++ b/inventory/models.py
@@ -162,3 +162,5 @@ class GameLoan(AbstractLoan):
class Meta(AbstractLoan.Meta):
abstract = False
+ permissions = [("can_see_loan_details", "Can see loan details")]
+
diff --git a/inventory/tables.py b/inventory/tables.py
new file mode 100644
index 0000000..d66e231
--- /dev/null
+++ b/inventory/tables.py
@@ -0,0 +1,33 @@
+import django_tables2 as tables
+from django.utils.html import format_html
+from django.urls import reverse
+from .models import GameLoan
+
+class LoanTable(tables.Table):
+ next_pattern = "inventory:all_loans"
+
+ class Meta:
+ model = GameLoan
+ sequence = ("lent_object", "borrow_date", "return_date", "mail")
+ exclude = ("id",)
+ slug = tables.Column(verbose_name="Actions", orderable=False)
+
+ def render_lent_object(self, value, record):
+ return format_html("{}",
+ reverse("inventory:game", args=[record.lent_object.slug]), value)
+
+ def render_slug(self, value, record):
+ res = ""
+ if record.return_date == None:
+ res = format_html("Rendre le jeu",
+ reverse("inventory:return_game", args=[value]),
+ reverse(self.next_pattern))
+ return res
+
+
+class OngoingLoansTable(LoanTable):
+ next_pattern = "inventory:ongoing_loans"
+
+ class Meta(LoanTable.Meta):
+ exclude = ("return_date", "mail", "id")
+
diff --git a/inventory/templates/inventory/inventory.html b/inventory/templates/inventory/inventory.html
index a9d0219..aecadbf 100644
--- a/inventory/templates/inventory/inventory.html
+++ b/inventory/templates/inventory/inventory.html
@@ -11,6 +11,9 @@
+
+ Emprunts en cours
+
Liste des étagères
diff --git a/inventory/templates/inventory/loans/loans_table.html b/inventory/templates/inventory/loans/loans_table.html
new file mode 100644
index 0000000..c1ffe38
--- /dev/null
+++ b/inventory/templates/inventory/loans/loans_table.html
@@ -0,0 +1,9 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+
+{% block "content" %}
+
Liste des emprunts
+ {% render_table table %}
+{% endblock %}
+
+{% block "main_attributes" %}class="wide-main"{% endblock %}
diff --git a/inventory/templates/inventory/loans/ongoing.html b/inventory/templates/inventory/loans/ongoing.html
new file mode 100644
index 0000000..32d4967
--- /dev/null
+++ b/inventory/templates/inventory/loans/ongoing.html
@@ -0,0 +1,7 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+
+{% block "content" %}
+ Emprunts en cours
+ {% render_table table %}
+{% endblock %}
diff --git a/inventory/urls.py b/inventory/urls.py
index 65cca0c..29d3813 100644
--- a/inventory/urls.py
+++ b/inventory/urls.py
@@ -13,6 +13,8 @@ from .views import (
GameLoanView,
BorrowGameView,
ReturnGameView,
+ OngoingLoansView,
+ DetailLoanView,
)
app_name = "inventory"
@@ -34,7 +36,9 @@ urlpatterns = [
name="modify_game_comment",
),
path("search/", InventorySearchView.as_view(), name="search"),
- path("loans//", GameLoanView.as_view(), name="game_loan"),
+ path("loans/game//", GameLoanView.as_view(), name="game_loan"),
path("loans/return//", ReturnGameView.as_view(), name="return_game"),
path("loans/borrow//", BorrowGameView.as_view(), name="borrow_game"),
+ path("loans/ongoing/", OngoingLoansView.as_view(), name="ongoing_loans"),
+ path("loans/all/", DetailLoanView.as_view(), name="all_loans"),
]
diff --git a/inventory/views.py b/inventory/views.py
index cd9ccf2..52d9dfa 100644
--- a/inventory/views.py
+++ b/inventory/views.py
@@ -1,11 +1,14 @@
from django.views.generic import TemplateView, ListView, DetailView
+from django.contrib.auth.mixins import PermissionRequiredMixin
from haystack.generic_views import SearchView
from haystack.forms import SearchForm
from haystack.query import SearchQuerySet
+from django_tables2.views import SingleTableView
from comments.views import AddCommentView, ModifyCommentView
from loans.views import BorrowView, ReturnView, DetailLoanView
from .models import Category, Tag, Game, GameComment, GameLoan
from .forms import BorrowGameForm
+from .tables import LoanTable, OngoingLoansTable
class InventoryView(TemplateView):
@@ -67,6 +70,7 @@ class ModifyGameCommentView(ModifyCommentView):
template_name = "inventory/game.html"
success_pattern_name = "inventory:game"
+
class BorrowGameView(BorrowView):
model = Game
loan_model = GameLoan
@@ -74,12 +78,27 @@ class BorrowGameView(BorrowView):
form_class = BorrowGameForm
success_pattern_name = "inventory:game_loan"
+
class ReturnGameView(ReturnView):
model = GameLoan
pattern_name = "inventory:game_loan"
+
class GameLoanView(DetailLoanView):
model = Game
loan_model = GameLoan
template_name = "inventory/loans/game_loan.html"
+
+class OngoingLoansView(SingleTableView):
+ queryset = GameLoan.ongoing_loans()
+ table_class = OngoingLoansTable
+ template_name = "inventory/loans/ongoing.html"
+
+
+class DetailLoanView(PermissionRequiredMixin, SingleTableView):
+ permission_required = "inventory.can_see_loan_details"
+ model = GameLoan
+ table_class = LoanTable
+ template_name = "inventory/loans/loans_table.html"
+
diff --git a/loans/models.py b/loans/models.py
index ac52a77..ba8b23f 100644
--- a/loans/models.py
+++ b/loans/models.py
@@ -5,8 +5,9 @@ from django.utils.timezone import now
class AbstractLoan(models.Model):
lent_object = None # Fill this with a foreign key in subclasses
slug = AutoSlugField(unique=True, populate_from="lent_object")
- borrow_date = models.DateTimeField(auto_now_add=True)
- return_date = models.DateTimeField(null=True)
+ borrow_date = models.DateTimeField(
+ auto_now_add=True, verbose_name="Date d’emprunt")
+ return_date = models.DateTimeField(null=True, verbose_name="Date de retour")
mail = models.EmailField()
lent_object_slug_field = "slug"
diff --git a/loans/views.py b/loans/views.py
index 9903472..ab061bf 100644
--- a/loans/views.py
+++ b/loans/views.py
@@ -21,6 +21,8 @@ class ReturnView(SingleObjectMixin, RedirectView):
kwargs[self.redirect_slug_field] = getattr(loan.lent_object,
loan.lent_object_slug_field)
messages.success(self.request, "Rendu effectué.")
+ if "next" in self.request.GET:
+ return self.request.GET["next"]
return super().get_redirect_url(*args, **kwargs)
diff --git a/requirements.txt b/requirements.txt
index 6a50ce2..8dbcd9e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,3 +7,4 @@ Pillow
django-cas-ng
django-haystack
Whoosh
+django-tables2
diff --git a/website/scss/style.scss b/website/scss/style.scss
index e9be175..9f2e684 100644
--- a/website/scss/style.scss
+++ b/website/scss/style.scss
@@ -31,7 +31,6 @@ body {
}
main {
- max-width: $page_width;
width: 100%;
padding: 20px 40px;
margin: 0 auto 50px;
@@ -48,6 +47,10 @@ main {
}
}
+main:not(.wide-main){
+ max-width: $page_width;
+}
+
footer {
background-color: $footer_bg_color;
font-size: 0.7em;
@@ -153,6 +156,24 @@ table {
th {
border-bottom-width: 2px;
}
+
+ th.asc::before {
+ display: inline;
+ font-family: FontAwesome;
+ content: '\f0de'; // Sort up symbol
+ }
+
+ th.desc::before {
+ display: inline;
+ font-family: FontAwesome;
+ content: '\f0dd'; // Sort down symbol
+ }
+
+ th.orderable:not(.asc, .desc)::before {
+ display: inline;
+ font-family: FontAwesome;
+ content: '\f0dc'; // Sort up/down symbol
+ }
}
iframe {
diff --git a/website/static/css/style.css b/website/static/css/style.css
index 551630d..d17859f 100644
--- a/website/static/css/style.css
+++ b/website/static/css/style.css
@@ -277,9 +277,7 @@ body {
@media (max-width: 700px) {
body {
font-size: 12pt; } }
-
main {
- max-width: 850px;
width: 100%;
padding: 20px 40px;
margin: 0 auto 50px;
@@ -291,6 +289,9 @@ main {
main.small_page {
max-width: 550px; }
+main:not(.wide-main) {
+ max-width: 850px; }
+
footer {
background-color: #6bb8c4;
font-size: 0.7em;
@@ -403,6 +404,18 @@ table {
text-align: left; }
table th {
border-bottom-width: 2px; }
+ table th.asc::before {
+ display: inline;
+ font-family: FontAwesome;
+ content: '\f0de'; }
+ table th.desc::before {
+ display: inline;
+ font-family: FontAwesome;
+ content: '\f0dd'; }
+ table th.orderable:not(.asc, .desc)::before {
+ display: inline;
+ font-family: FontAwesome;
+ content: '\f0dc'; }
iframe {
width: 100%;
diff --git a/website/templates/partials/header.html b/website/templates/partials/header.html
index 8064ce8..fd1d171 100644
--- a/website/templates/partials/header.html
+++ b/website/templates/partials/header.html
@@ -11,6 +11,9 @@
Inventaire
Suggestions
+ {% if perms.inventory.can_see_loan_details %}
+ Gestion des emprunts
+ {% endif %}
{% if request.user.is_staff %}
Admin
{% endif %}