add session search
Django CI/CD / test (push) Successful in 1m10s Details
Django CI/CD / build-and-push (push) Successful in 2m16s Details

This commit is contained in:
Lukáš Kucharczyk 2024-11-09 21:34:01 +00:00
parent a3ed93c154
commit c6b1badf39
4 changed files with 235 additions and 136 deletions

View File

@ -1,5 +1,113 @@
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(63 131 248 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(63 131 248 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
/* /*
! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com ! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com
*/ */
/* /*
@ -442,7 +550,7 @@ video {
/* Make elements with the HTML hidden attribute stay hidden by default */ /* Make elements with the HTML hidden attribute stay hidden by default */
[hidden] { [hidden]:where(:not([hidden="until-found"])) {
display: none; display: none;
} }
@ -1104,114 +1212,6 @@ input:checked + .toggle-bg {
border-color: #1C64F2; border-color: #1C64F2;
} }
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(63 131 248 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(63 131 248 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
.container { .container {
width: 100%; width: 100%;
} }
@ -1258,6 +1258,10 @@ input:checked + .toggle-bg {
border-width: 0; border-width: 0;
} }
.pointer-events-none {
pointer-events: none;
}
.visible { .visible {
visibility: visible; visibility: visible;
} }
@ -1290,6 +1294,11 @@ input:checked + .toggle-bg {
inset: 0px; inset: 0px;
} }
.inset-y-0 {
top: 0px;
bottom: 0px;
}
.bottom-0 { .bottom-0 {
bottom: 0px; bottom: 0px;
} }
@ -1318,6 +1327,10 @@ input:checked + .toggle-bg {
right: 0.75rem; right: 0.75rem;
} }
.start-0 {
inset-inline-start: 0px;
}
.top-0 { .top-0 {
top: 0px; top: 0px;
} }
@ -1418,6 +1431,10 @@ input:checked + .toggle-bg {
margin-inline-start: 0.625rem; margin-inline-start: 0.625rem;
} }
.mt-1 {
margin-top: 0.25rem;
}
.mt-2 { .mt-2 {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
@ -1535,6 +1552,10 @@ input:checked + .toggle-bg {
width: 16rem; width: 16rem;
} }
.w-80 {
width: 20rem;
}
.w-full { .w-full {
width: 100%; width: 100%;
} }
@ -1965,6 +1986,18 @@ input:checked + .toggle-bg {
padding-bottom: 1rem; padding-bottom: 1rem;
} }
.pb-4 {
padding-bottom: 1rem;
}
.ps-10 {
padding-inline-start: 2.5rem;
}
.ps-3 {
padding-inline-start: 0.75rem;
}
.pt-2 { .pt-2 {
padding-top: 0.5rem; padding-top: 0.5rem;
} }
@ -2607,6 +2640,11 @@ textarea:disabled:is(.dark *) {
z-index: 10; z-index: 10;
} }
.focus\:border-blue-500:focus {
--tw-border-opacity: 1;
border-color: rgb(63 131 248 / var(--tw-border-opacity));
}
.focus\:text-blue-700:focus { .focus\:text-blue-700:focus {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(26 86 219 / var(--tw-text-opacity)); color: rgb(26 86 219 / var(--tw-text-opacity));
@ -2634,6 +2672,11 @@ textarea:disabled:is(.dark *) {
--tw-ring-color: rgb(164 202 254 / var(--tw-ring-opacity)); --tw-ring-color: rgb(164 202 254 / var(--tw-ring-opacity));
} }
.focus\:ring-blue-500:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(63 131 248 / var(--tw-ring-opacity));
}
.focus\:ring-blue-700:focus { .focus\:ring-blue-700:focus {
--tw-ring-opacity: 1; --tw-ring-opacity: 1;
--tw-ring-color: rgb(26 86 219 / var(--tw-ring-opacity)); --tw-ring-color: rgb(26 86 219 / var(--tw-ring-opacity));
@ -2875,6 +2918,16 @@ textarea:disabled:is(.dark *) {
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.dark\:placeholder-gray-400:is(.dark *)::-moz-placeholder {
--tw-placeholder-opacity: 1;
color: rgb(156 163 175 / var(--tw-placeholder-opacity));
}
.dark\:placeholder-gray-400:is(.dark *)::placeholder {
--tw-placeholder-opacity: 1;
color: rgb(156 163 175 / var(--tw-placeholder-opacity));
}
.odd\:dark\:bg-gray-900:is(.dark *):nth-child(odd) { .odd\:dark\:bg-gray-900:is(.dark *):nth-child(odd) {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(17 24 39 / var(--tw-bg-opacity)); background-color: rgb(17 24 39 / var(--tw-bg-opacity));
@ -2945,6 +2998,11 @@ textarea:disabled:is(.dark *) {
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.dark\:focus\:border-blue-500:focus:is(.dark *) {
--tw-border-opacity: 1;
border-color: rgb(63 131 248 / var(--tw-border-opacity));
}
.dark\:focus\:text-white:focus:is(.dark *) { .dark\:focus\:text-white:focus:is(.dark *) {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));

View File

@ -0,0 +1,12 @@
<c-vars placeholder="Search" :name="id" />
<div class="pb-4 bg-white dark:bg-gray-900">
<label for="table-search" class="sr-only">Search</label>
<div class="relative mt-1">
<div class="absolute inset-y-0 rtl:inset-r-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
</svg>
</div>
<input type="text" id="{{ id }}" name="{{ name }}" value="{{ search_string }}" class="block pt-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg w-80 bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="{{ placeholder }}">
</div>
</div>

View File

@ -116,6 +116,7 @@ urlpatterns = [
name="list_sessions_end_session", name="list_sessions_end_session",
), ),
path("session/list", session.list_sessions, name="list_sessions"), path("session/list", session.list_sessions, name="list_sessions"),
path("session/search", session.search_sessions, name="search_sessions"),
path("stats/", general.stats_alltime, name="stats_alltime"), path("stats/", general.stats_alltime, name="stats_alltime"),
path( path(
"stats/<int:year>", "stats/<int:year>",

View File

@ -8,7 +8,15 @@ from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from common.components import A, Button, Div, Icon, LinkedNameWithPlatformIcon, Popover from common.components import (
A,
Button,
Div,
Form,
Icon,
LinkedNameWithPlatformIcon,
Popover,
)
from common.time import ( from common.time import (
dateformat, dateformat,
durationformat, durationformat,
@ -24,11 +32,14 @@ from games.views.general import use_custom_redirect
@login_required @login_required
def list_sessions(request: HttpRequest) -> HttpResponse: def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse:
context: dict[Any, Any] = {} context: dict[Any, Any] = {}
page_number = request.GET.get("page", 1) page_number = request.GET.get("page", 1)
limit = request.GET.get("limit", 10) limit = request.GET.get("limit", 10)
sessions = Session.objects.order_by("-timestamp_start") sessions = Session.objects.order_by("-timestamp_start")
search_string = request.GET.get("search_string", search_string)
if search_string != "":
sessions = sessions.filter(purchase__edition__name__icontains=search_string)
last_session = sessions.latest() last_session = sessions.latest()
page_obj = None page_obj = None
if int(limit) != 0: if int(limit) != 0:
@ -49,37 +60,49 @@ def list_sessions(request: HttpRequest) -> HttpResponse:
"data": { "data": {
"header_action": Div( "header_action": Div(
children=[ children=[
A( Form(
url="add_session", children=[
children=Button( render_to_string(
icon=True, "cotton/search_field.html", {"id": "search_string"}
size="xs", )
children=[Icon("play"), "LOG"], ]
),
), ),
A( Div(
url=reverse( children=[
"list_sessions_start_session_from_session", A(
args=[last_session.pk], url="add_session",
), children=Button(
children=Popover(
popover_content=last_session.purchase.edition.name,
children=[
Button(
icon=True, icon=True,
color="gray",
size="xs", size="xs",
children=[Icon("play"), "LOG"],
),
),
A(
url=reverse(
"list_sessions_start_session_from_session",
args=[last_session.pk],
),
children=Popover(
popover_content=last_session.purchase.edition.name,
children=[ children=[
Icon("play"), Button(
truncate( icon=True,
f"{last_session.purchase.edition.name}" color="gray",
), size="xs",
children=[
Icon("play"),
truncate(
f"{last_session.purchase.edition.name}"
),
],
)
], ],
) ),
], ),
), ]
), ),
], ],
attributes=[("class", "flex justify-between")],
), ),
"columns": [ "columns": [
"Name", "Name",
@ -150,6 +173,11 @@ def list_sessions(request: HttpRequest) -> HttpResponse:
return render(request, "list_purchases.html", context) return render(request, "list_purchases.html", context)
@login_required
def search_sessions(request: HttpRequest) -> HttpResponse:
return list_sessions(request, search_string=request.GET.get("search_string", ""))
@login_required @login_required
def add_session(request: HttpRequest, purchase_id: int = 0) -> HttpResponse: def add_session(request: HttpRequest, purchase_id: int = 0) -> HttpResponse:
context = {} context = {}