Allow directly updating device in session list
This commit is contained in:
+18
-1
@@ -5,7 +5,7 @@ from django.shortcuts import get_object_or_404
|
||||
from django.utils.timezone import now as django_timezone_now
|
||||
from ninja import Field, ModelSchema, NinjaAPI, Router, Schema
|
||||
|
||||
from games.models import Game, PlayEvent
|
||||
from games.models import Game, PlayEvent, Session
|
||||
|
||||
api = NinjaAPI()
|
||||
playevent_router = Router()
|
||||
@@ -93,3 +93,20 @@ def delete_playevent(request, playevent_id: int):
|
||||
api.add_router("/playevent", playevent_router)
|
||||
api.add_router("/games", game_router)
|
||||
|
||||
session_router = Router()
|
||||
|
||||
|
||||
class SessionDeviceUpdate(Schema):
|
||||
device_id: int
|
||||
|
||||
|
||||
@session_router.patch("/{session_id}/device", response={204: None})
|
||||
def partial_update_session_device(request, session_id: int, payload: SessionDeviceUpdate):
|
||||
session = get_object_or_404(Session, id=session_id)
|
||||
session.device_id = payload.device_id
|
||||
session.save()
|
||||
return 204, None
|
||||
|
||||
|
||||
api.add_router("/session", session_router)
|
||||
|
||||
|
||||
@@ -2490,9 +2490,15 @@
|
||||
.text-gray-900 {
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.text-green-600 {
|
||||
color: var(--color-green-600);
|
||||
}
|
||||
.text-heading {
|
||||
color: var(--color-heading);
|
||||
}
|
||||
.text-red-600 {
|
||||
color: var(--color-red-600);
|
||||
}
|
||||
.text-slate-300 {
|
||||
color: var(--color-slate-300);
|
||||
}
|
||||
@@ -3333,6 +3339,16 @@
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
}
|
||||
.dark\:text-green-400 {
|
||||
&:is(.dark *) {
|
||||
color: var(--color-green-400);
|
||||
}
|
||||
}
|
||||
.dark\:text-red-400 {
|
||||
&:is(.dark *) {
|
||||
color: var(--color-red-400);
|
||||
}
|
||||
}
|
||||
.dark\:text-slate-300 {
|
||||
&:is(.dark *) {
|
||||
color: var(--color-slate-300);
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
<tr class="odd:bg-white dark:odd:bg-gray-900 even:bg-gray-50 dark:even:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 [&_a]:underline [&_a]:underline-offset-4 [&_a]:decoration-2 [&_td:last-child]:text-right">
|
||||
<tr class="odd:bg-white dark:odd:bg-gray-900 even:bg-gray-50 dark:even:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 [&_a]:underline [&_a]:underline-offset-4 [&_a]:decoration-2 [&_td:last-child]:text-right"
|
||||
{% if data.row_id %}id="{{ data.row_id }}"{% endif %}
|
||||
{% if data.hx_trigger %}hx-trigger="{{ data.hx_trigger }}"{% endif %}
|
||||
{% if data.hx_get %}hx-get="{{ data.hx_get }}"{% endif %}
|
||||
{% if data.hx_select %}hx-select="{{ data.hx_select }}"{% endif %}
|
||||
{% if data.hx_swap %}hx-swap="{{ data.hx_swap }}"{% endif %}
|
||||
>
|
||||
{% if slot %}
|
||||
{{ slot }}
|
||||
{% elif data.row_id %}
|
||||
{% for td in data.cell_data %}
|
||||
{% if forloop.first %}
|
||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{{ td }}</th>
|
||||
{% else %}
|
||||
<c-table-td>
|
||||
{{ td }}
|
||||
</c-table-td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for td in data %}
|
||||
{% if forloop.first %}
|
||||
<th scope="row"
|
||||
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{{ td }}</th>
|
||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{{ td }}</th>
|
||||
{% else %}
|
||||
<c-table-td>
|
||||
{{ td }}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<div class="flex gap-2 items-center"
|
||||
x-data="{
|
||||
originalDeviceId: {{ session.device.id|default:'null' }},
|
||||
originalDeviceName: '{{ session.device.name|default:'Unknown'|escapejs }}',
|
||||
deviceId: {{ session.device.id|default:'null' }},
|
||||
deviceName: '{{ session.device.name|default:'Unknown'|escapejs }}',
|
||||
open: false,
|
||||
saving: false,
|
||||
error: '',
|
||||
success: false,
|
||||
setDevice(newDeviceId, newDeviceName) {
|
||||
this.deviceId = newDeviceId;
|
||||
this.deviceName = newDeviceName;
|
||||
this.saving = true;
|
||||
this.error = '';
|
||||
this.success = false;
|
||||
fetch(`/api/session/{{ session.id }}/device`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': '{{ csrf_token }}'
|
||||
},
|
||||
body: JSON.stringify({ device_id: newDeviceId })
|
||||
})
|
||||
.then(() => {
|
||||
this.success = true;
|
||||
this.error = '';
|
||||
document.body.dispatchEvent(new CustomEvent('device-changed'));
|
||||
})
|
||||
.catch(() => {
|
||||
this.error = 'Failed to update device';
|
||||
this.deviceName = this.originalDeviceName;
|
||||
this.deviceId = this.originalDeviceId;
|
||||
})
|
||||
.finally(() => this.saving = false);
|
||||
}
|
||||
}"
|
||||
>
|
||||
<div class="inline-flex rounded-md shadow-2xs" role="group" @click.outside="open = false">
|
||||
<button type="button" @click="open = !open" class="relative px-4 py-2 text-sm font-medium bg-white border border-gray-200 rounded-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white align-middle hover:cursor-pointer">
|
||||
<span class="flex flex-row gap-4 justify-between items-center">
|
||||
<span x-text="deviceName"></span>
|
||||
<c-icon.arrowdown />
|
||||
</span>
|
||||
<div class="absolute top-[105%] left-0 w-full whitespace-nowrap z-10 text-sm font-medium bg-gray-800/20 backdrop-blur-lg rounded-md rounded-t-none border border-gray-200 dark:border-gray-700" x-show="open" style="display: none;">
|
||||
<ul class="[&_li:first-of-type_a]:rounded-none [&_li:last-of-type_a]:rounded-t-none">
|
||||
{% for device in session_devices %}
|
||||
<li><a href="#" @click.prevent.stop="setDevice({{ device.id }}, '{{ device.name|escapejs }}'); open = false;" class="block px-4 py-2 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white rounded-sm no-underline! border-0!" :class="{ 'font-bold': deviceId === {{ device.id }} }">{{ device.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="success" class="text-xs text-green-600 dark:text-green-400" style="display: none;">Saved</div>
|
||||
<div x-show="error" x-text="error" class="text-xs text-red-600 dark:text-red-400" style="display: none;"></div>
|
||||
</div>
|
||||
+62
-46
@@ -25,7 +25,7 @@ from common.time import (
|
||||
)
|
||||
from common.utils import truncate
|
||||
from games.forms import SessionForm
|
||||
from games.models import Game, Session
|
||||
from games.models import Device, Game, Session
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -34,6 +34,7 @@ def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse
|
||||
page_number = request.GET.get("page", 1)
|
||||
limit = request.GET.get("limit", 10)
|
||||
sessions = Session.objects.order_by("-timestamp_start", "created_at")
|
||||
device_list = Device.objects.order_by("name")
|
||||
search_string = request.GET.get("search_string", search_string)
|
||||
if search_string != "":
|
||||
sessions = sessions.filter(
|
||||
@@ -123,51 +124,66 @@ def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse
|
||||
"Actions",
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
NameWithIcon(session_id=session.pk),
|
||||
f"{local_strftime(session.timestamp_start)}{f' — {local_strftime(session.timestamp_end, timeformat)}' if session.timestamp_end else ''}",
|
||||
session.duration_formatted_with_mark,
|
||||
session.device,
|
||||
session.created_at.strftime(dateformat),
|
||||
render_to_string(
|
||||
"cotton/button_group.html",
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"href": reverse(
|
||||
"list_sessions_end_session", args=[session.pk]
|
||||
),
|
||||
"slot": Icon("end"),
|
||||
"title": "Finish session now",
|
||||
"color": "green",
|
||||
"hover": "green",
|
||||
}
|
||||
if session.timestamp_end is None
|
||||
# this only works without leaving an empty
|
||||
# a element and wrong rounding of button edges
|
||||
# because we check if button.href is not None
|
||||
# in the button group component
|
||||
else {},
|
||||
{
|
||||
"href": reverse("edit_session", args=[session.pk]),
|
||||
"slot": Icon("edit"),
|
||||
"title": "Edit",
|
||||
# "color": "gray",
|
||||
"hover": "green",
|
||||
},
|
||||
{
|
||||
"href": reverse(
|
||||
"delete_session", args=[session.pk]
|
||||
),
|
||||
"slot": Icon("delete"),
|
||||
"title": "Delete",
|
||||
"color": "red",
|
||||
"hover": "red",
|
||||
},
|
||||
]
|
||||
},
|
||||
),
|
||||
]
|
||||
{
|
||||
"row_id": f"session-row-{session.pk}",
|
||||
"hx_trigger": "device-changed from:body",
|
||||
"hx_get": "",
|
||||
"hx_select": f"#session-row-{session.pk}",
|
||||
"hx_swap": "outerHTML",
|
||||
"cell_data": [
|
||||
NameWithIcon(session_id=session.pk),
|
||||
f"{local_strftime(session.timestamp_start)}{f' — {local_strftime(session.timestamp_end, timeformat)}' if session.timestamp_end else ''}",
|
||||
session.duration_formatted_with_mark,
|
||||
render_to_string(
|
||||
"partials/sessiondevice_selector.html",
|
||||
{
|
||||
"session": session,
|
||||
"session_device": session.device,
|
||||
"session_devices": device_list,
|
||||
},
|
||||
request=request,
|
||||
),
|
||||
session.created_at.strftime(dateformat),
|
||||
render_to_string(
|
||||
"cotton/button_group.html",
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"href": reverse(
|
||||
"list_sessions_end_session", args=[session.pk]
|
||||
),
|
||||
"slot": Icon("end"),
|
||||
"title": "Finish session now",
|
||||
"color": "green",
|
||||
"hover": "green",
|
||||
}
|
||||
if session.timestamp_end is None
|
||||
# this only works without leaving an empty
|
||||
# a element and wrong rounding of button edges
|
||||
# because we check if button.href is not None
|
||||
# in the button group component
|
||||
else {},
|
||||
{
|
||||
"href": reverse("edit_session", args=[session.pk]),
|
||||
"slot": Icon("edit"),
|
||||
"title": "Edit",
|
||||
# "color": "gray",
|
||||
"hover": "green",
|
||||
},
|
||||
{
|
||||
"href": reverse(
|
||||
"delete_session", args=[session.pk]
|
||||
),
|
||||
"slot": Icon("delete"),
|
||||
"title": "Delete",
|
||||
"color": "red",
|
||||
"hover": "red",
|
||||
},
|
||||
]
|
||||
},
|
||||
),
|
||||
],
|
||||
}
|
||||
for session in sessions
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user