Add game overview page

Fixes #8
This commit is contained in:
Lukáš Kucharczyk 2023-10-01 21:28:02 +02:00
parent 787ee8640f
commit 7467e2732d
7 changed files with 199 additions and 2 deletions

View File

@ -11,6 +11,7 @@
* Improve session list (https://git.kucharczyk.xyz/lukas/timetracker/issues/53) * Improve session list (https://git.kucharczyk.xyz/lukas/timetracker/issues/53)
* Change fonts to IBM Plex * Change fonts to IBM Plex
* Only use local WOFF2 font files * Only use local WOFF2 font files
* Add game overview page (https://git.kucharczyk.xyz/lukas/timetracker/issues/8)
## 1.0.3 / 2023-02-20 17:16+01:00 ## 1.0.3 / 2023-02-20 17:16+01:00

View File

@ -781,6 +781,21 @@ select {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.my-1 {
margin-top: 0.25rem;
margin-bottom: 0.25rem;
}
.my-2 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.mb-4 { .mb-4 {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@ -793,6 +808,30 @@ select {
margin-top: 1rem; margin-top: 1rem;
} }
.mt-1 {
margin-top: 0.25rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.ml-1 {
margin-left: 0.25rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.block { .block {
display: block; display: block;
} }
@ -809,6 +848,10 @@ select {
display: table; display: table;
} }
.list-item {
display: list-item;
}
.hidden { .hidden {
display: none; display: none;
} }
@ -829,6 +872,10 @@ select {
width: 100%; width: 100%;
} }
.w-5 {
width: 1.25rem;
}
.max-w-screen-lg { .max-w-screen-lg {
max-width: 1024px; max-width: 1024px;
} }
@ -837,6 +884,18 @@ select {
max-width: 20rem; max-width: 20rem;
} }
.max-w-lg {
max-width: 32rem;
}
.max-w-3xl {
max-width: 48rem;
}
.max-w-sm {
max-width: 24rem;
}
@keyframes spin { @keyframes spin {
to { to {
transform: rotate(360deg); transform: rotate(360deg);
@ -847,6 +906,10 @@ select {
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
} }
.list-disc {
list-style-type: disc;
}
.flex-col { .flex-col {
flex-direction: column; flex-direction: column;
} }
@ -890,6 +953,21 @@ select {
border-color: rgb(229 231 235 / var(--tw-border-opacity)); border-color: rgb(229 231 235 / var(--tw-border-opacity));
} }
.border-red-300 {
--tw-border-opacity: 1;
border-color: rgb(252 165 165 / var(--tw-border-opacity));
}
.border-red-500 {
--tw-border-opacity: 1;
border-color: rgb(239 68 68 / var(--tw-border-opacity));
}
.border-slate-500 {
--tw-border-opacity: 1;
border-color: rgb(100 116 139 / var(--tw-border-opacity));
}
.bg-green-600 { .bg-green-600 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(22 163 74 / var(--tw-bg-opacity)); background-color: rgb(22 163 74 / var(--tw-bg-opacity));
@ -900,6 +978,11 @@ select {
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
} }
.bg-slate-400 {
--tw-bg-opacity: 1;
background-color: rgb(148 163 184 / var(--tw-bg-opacity));
}
.p-4 { .p-4 {
padding: 1rem; padding: 1rem;
} }
@ -998,6 +1081,11 @@ select {
line-height: 1.75rem; line-height: 1.75rem;
} }
.text-3xl {
font-size: 1.875rem;
line-height: 2.25rem;
}
.font-semibold { .font-semibold {
font-weight: 600; font-weight: 600;
} }
@ -1029,6 +1117,27 @@ select {
color: rgb(253 224 71 / var(--tw-text-opacity)); color: rgb(253 224 71 / var(--tw-text-opacity));
} }
.text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity));
}
.underline {
text-decoration-line: underline;
}
.decoration-red-500 {
text-decoration-color: #ef4444;
}
.decoration-slate-400 {
text-decoration-color: #94a3b8;
}
.decoration-slate-500 {
text-decoration-color: #64748b;
}
.shadow-md { .shadow-md {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
@ -1295,6 +1404,14 @@ th label {
} }
@media (min-width: 640px) { @media (min-width: 640px) {
.sm\:ml-2 {
margin-left: 0.5rem;
}
.sm\:inline {
display: inline;
}
.sm\:table-cell { .sm\:table-cell {
display: table-cell; display: table-cell;
} }
@ -1303,10 +1420,26 @@ th label {
max-width: 28rem; max-width: 28rem;
} }
.sm\:max-w-lg {
max-width: 32rem;
}
.sm\:max-w-xl {
max-width: 36rem;
}
.sm\:px-4 { .sm\:px-4 {
padding-left: 1rem; padding-left: 1rem;
padding-right: 1rem; padding-right: 1rem;
} }
.sm\:pl-2 {
padding-left: 0.5rem;
}
.sm\:decoration-2 {
text-decoration-thickness: 2px;
}
} }
@media (min-width: 768px) { @media (min-width: 768px) {
@ -1345,4 +1478,8 @@ th label {
.lg\:max-w-lg { .lg\:max-w-lg {
max-width: 32rem; max-width: 32rem;
} }
.lg\:max-w-3xl {
max-width: 48rem;
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

View File

@ -39,7 +39,11 @@
<td <td
class="px-2 sm:px-4 md:px-6 md:py-2 purchase-name truncate max-w-20char md:max-w-40char" class="px-2 sm:px-4 md:px-6 md:py-2 purchase-name truncate max-w-20char md:max-w-40char"
> >
<a
class="underline decoration-slate-500 sm:decoration-2"
href="{% url 'view_game' data.purchase.edition.game.id %}">
{{ data.purchase.edition }} {{ data.purchase.edition }}
</a>
</td> </td>
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono hidden sm:table-cell"> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono hidden sm:table-cell">
{{ data.timestamp_start | date:"d/m/Y H:i" }} {{ data.timestamp_start | date:"d/m/Y H:i" }}

View File

@ -0,0 +1,41 @@
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock title %}
{% load static %}
{% block content %}
<div class="dark:text-white max-w-sm sm:max-w-xl lg:max-w-3xl mx-auto">
<h1 class="text-4xl">{{ game.name }} (#{{ game.pk }})</h1>
<h2 class="text-lg my-2 ml-2">{{ total_playtime }} ({{ first_session.timestamp_end | date:"M Y"}} — {{ first_session.timestamp_end | date:"M Y"}}) </h2>
<hr class="border-slate-500">
<h1 class="text-3xl mt-4 mb-1">Editions</h1>
<ul>
{% for edition in editions %}
<li class="sm:pl-2">
{{ edition.name }} ({{ edition.platform }}, {{ edition.year_released }})
{% if edition.wikidata %}
<span class="hidden sm:inline">
<a href="https://www.wikidata.org/wiki/{{ edition.wikidata }}">
<img class="inline mx-2 w-6" src="{% static 'icons/wikidata.png' %}"/>
</a>
</span>
{% endif %}
</li>
{% endfor %}
</ul>
<h1 class="text-3xl mt-4 mb-1">Purchases</h1>
<ul>
{% for purchase in purchases %}
<li class="sm:pl-2">{{ purchase.platform }} ({{ purchase.get_ownership_type_display }}, {{ purchase.date_purchased | date:"Y" }}, {{ purchase.price }} {{ purchase.price_currency}})</li>
{% endfor %}
</ul>
<h1 class="text-3xl mt-4 mb-1">Sessions</h1>
<ul>
{% for session in sessions %}
<li class="sm:pl-2">{{ session.timestamp_end | date:"d/m/Y" }} ({{ session.device.get_type_display | default:"Unknown" }}, {{ session.duration_formatted }})</li>
{% endfor %}
</ul>
</div>
{% endblock content %}

View File

@ -31,7 +31,7 @@ urlpatterns = [
path("add-purchase/", views.add_purchase, name="add_purchase"), path("add-purchase/", views.add_purchase, name="add_purchase"),
path("add-edition/", views.add_edition, name="add_edition"), path("add-edition/", views.add_edition, name="add_edition"),
path("edit-edition/<int:edition_id>", views.edit_edition, name="edit_edition"), path("edit-edition/<int:edition_id>", views.edit_edition, name="edit_edition"),
path("edit-game/<int:game_id>", views.edit_game, name="edit_game"), path("game/<int:game_id>/view", views.view_game, name="view_game"),
path("edit-platform/<int:platform_id>", views.edit_platform, name="edit_platform"), path("edit-platform/<int:platform_id>", views.edit_platform, name="edit_platform"),
path("add-device/", views.add_device, name="add_device"), path("add-device/", views.add_device, name="add_device"),
path("edit-session/<int:session_id>", views.edit_session, name="edit_session"), path("edit-session/<int:session_id>", views.edit_session, name="edit_session"),

View File

@ -91,6 +91,20 @@ def edit_game(request, game_id=None):
return render(request, "add.html", context) return render(request, "add.html", context)
def view_game(request, game_id=None):
context = {}
game = Game.objects.get(id=game_id)
context["title"] = "View Game"
context["game"] = game
context["editions"] = Edition.objects.filter(game_id=game_id)
context["purchases"] = Purchase.objects.filter(edition__game_id=game_id)
context["sessions"] = Session.objects.filter(purchase__edition__game_id=game_id)
context["total_playtime"] = context["sessions"].total_duration()
context["last_session"] = context["sessions"].last
context["first_session"] = context["sessions"].first
return render(request, "view_game.html", context)
def edit_platform(request, platform_id=None): def edit_platform(request, platform_id=None):
context = {} context = {}
purchase = Platform.objects.get(id=platform_id) purchase = Platform.objects.get(id=platform_id)