Major redesign #73
@ -6,6 +6,7 @@
 | 
			
		||||
* Add stats for dropped purchases, monthly playtimes
 | 
			
		||||
* Allow deleting purchases
 | 
			
		||||
* Add all-time stats
 | 
			
		||||
* Manage purchases
 | 
			
		||||
 | 
			
		||||
## Improved
 | 
			
		||||
* mark refunded purchases red on game overview
 | 
			
		||||
 | 
			
		||||
@ -120,14 +120,6 @@ textarea:disabled {
 | 
			
		||||
  @apply mx-1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th {
 | 
			
		||||
  @apply text-right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th label {
 | 
			
		||||
  @apply mr-4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.basic-button-container {
 | 
			
		||||
  @apply flex space-x-2 justify-center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										83
									
								
								games/purchaseviews.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								games/purchaseviews.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.db.models.manager import BaseManager
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
 | 
			
		||||
from games.models import Purchase
 | 
			
		||||
from games.views import dateformat
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def list_purchases(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
    context: dict[Any, Any] = {}
 | 
			
		||||
    purchases: BaseManager[Purchase] = Purchase.objects.all()[0:10]
 | 
			
		||||
    context = {
 | 
			
		||||
        "title": "Manage purchases",
 | 
			
		||||
        "data": {
 | 
			
		||||
            "columns": [
 | 
			
		||||
                "Name",
 | 
			
		||||
                "Platform",
 | 
			
		||||
                "Price",
 | 
			
		||||
                "Currency",
 | 
			
		||||
                "Infinite",
 | 
			
		||||
                "Purchased",
 | 
			
		||||
                "Refunded",
 | 
			
		||||
                "Finished",
 | 
			
		||||
                "Dropped",
 | 
			
		||||
                "Created",
 | 
			
		||||
                "Actions",
 | 
			
		||||
            ],
 | 
			
		||||
            "rows": [
 | 
			
		||||
                [
 | 
			
		||||
                    purchase.edition.name,
 | 
			
		||||
                    purchase.platform,
 | 
			
		||||
                    purchase.price,
 | 
			
		||||
                    purchase.price_currency,
 | 
			
		||||
                    purchase.infinite,
 | 
			
		||||
                    purchase.date_purchased.strftime(dateformat),
 | 
			
		||||
                    (
 | 
			
		||||
                        purchase.date_refunded.strftime(dateformat)
 | 
			
		||||
                        if purchase.date_refunded
 | 
			
		||||
                        else "-"
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        purchase.date_finished.strftime(dateformat)
 | 
			
		||||
                        if purchase.date_finished
 | 
			
		||||
                        else "-"
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        purchase.date_dropped.strftime(dateformat)
 | 
			
		||||
                        if purchase.date_dropped
 | 
			
		||||
                        else "-"
 | 
			
		||||
                    ),
 | 
			
		||||
                    purchase.created_at.strftime(dateformat),
 | 
			
		||||
                    render_to_string(
 | 
			
		||||
                        "components/button_group_sm.html",
 | 
			
		||||
                        {
 | 
			
		||||
                            "buttons": [
 | 
			
		||||
                                {
 | 
			
		||||
                                    "href": reverse(
 | 
			
		||||
                                        "edit_purchase", args=[purchase.pk]
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    "text": "Edit",
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    "href": reverse(
 | 
			
		||||
                                        "delete_purchase", args=[purchase.pk]
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    "text": "Delete",
 | 
			
		||||
                                    "color": "red",
 | 
			
		||||
                                },
 | 
			
		||||
                            ]
 | 
			
		||||
                        },
 | 
			
		||||
                    ),
 | 
			
		||||
                ]
 | 
			
		||||
                for purchase in purchases
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
    return render(request, "list_purchases.html", context)
 | 
			
		||||
@ -1659,6 +1659,10 @@ input:checked + .toggle-bg {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.overflow-x-auto {
 | 
			
		||||
  overflow-x: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.truncate {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
@ -1717,6 +1721,10 @@ input:checked + .toggle-bg {
 | 
			
		||||
  border-width: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.border-b {
 | 
			
		||||
  border-bottom-width: 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.border-blue-600 {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(28 100 242 / var(--tw-border-opacity));
 | 
			
		||||
@ -1762,6 +1770,11 @@ input:checked + .toggle-bg {
 | 
			
		||||
  background-color: rgb(229 231 235 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bg-gray-50 {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(249 250 251 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bg-gray-800 {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(31 41 55 / var(--tw-bg-opacity));
 | 
			
		||||
@ -1822,6 +1835,11 @@ input:checked + .toggle-bg {
 | 
			
		||||
  padding-right: 1.25rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.px-6 {
 | 
			
		||||
  padding-left: 1.5rem;
 | 
			
		||||
  padding-right: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.py-1 {
 | 
			
		||||
  padding-top: 0.25rem;
 | 
			
		||||
  padding-bottom: 0.25rem;
 | 
			
		||||
@ -1842,6 +1860,11 @@ input:checked + .toggle-bg {
 | 
			
		||||
  padding-bottom: 0.75rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.py-4 {
 | 
			
		||||
  padding-top: 1rem;
 | 
			
		||||
  padding-bottom: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.pb-16 {
 | 
			
		||||
  padding-bottom: 4rem;
 | 
			
		||||
}
 | 
			
		||||
@ -1866,6 +1889,10 @@ input:checked + .toggle-bg {
 | 
			
		||||
  padding-top: 2rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.text-left {
 | 
			
		||||
  text-align: left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.text-center {
 | 
			
		||||
  text-align: center;
 | 
			
		||||
}
 | 
			
		||||
@ -1943,6 +1970,10 @@ input:checked + .toggle-bg {
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.uppercase {
 | 
			
		||||
  text-transform: uppercase;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.leading-6 {
 | 
			
		||||
  line-height: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
@ -2256,13 +2287,13 @@ textarea:disabled:is(.dark *) {
 | 
			
		||||
  margin-right: 0.25rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th {
 | 
			
		||||
  text-align: right;
 | 
			
		||||
}
 | 
			
		||||
/* th {
 | 
			
		||||
  @apply text-right;
 | 
			
		||||
} */
 | 
			
		||||
 | 
			
		||||
th label {
 | 
			
		||||
  margin-right: 1rem;
 | 
			
		||||
}
 | 
			
		||||
/* th label {
 | 
			
		||||
  @apply mr-4;
 | 
			
		||||
} */
 | 
			
		||||
 | 
			
		||||
.basic-button-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
@ -2366,11 +2397,26 @@ th label {
 | 
			
		||||
  }  
 | 
			
		||||
} */
 | 
			
		||||
 | 
			
		||||
.odd\:bg-white:nth-child(odd) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(255 255 255 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.even\:bg-gray-50:nth-child(even) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(249 250 251 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:border-gray-300:hover {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(209 213 219 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:border-green-600:hover {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(5 122 85 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:bg-blue-800:hover {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(30 66 159 / var(--tw-bg-opacity));
 | 
			
		||||
@ -2386,6 +2432,16 @@ th label {
 | 
			
		||||
  background-color: rgb(156 163 175 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:bg-gray-50:hover {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(249 250 251 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:bg-green-500:hover {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(14 159 110 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:bg-green-700:hover {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(4 108 78 / var(--tw-bg-opacity));
 | 
			
		||||
@ -2396,6 +2452,11 @@ th label {
 | 
			
		||||
  background-color: rgb(253 232 232 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:bg-red-500:hover {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(240 82 82 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:bg-violet-700:hover {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(109 40 217 / var(--tw-bg-opacity));
 | 
			
		||||
@ -2426,6 +2487,11 @@ th label {
 | 
			
		||||
  color: rgb(17 24 39 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:text-white:hover {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(255 255 255 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hover\:underline:hover {
 | 
			
		||||
  text-decoration-line: underline;
 | 
			
		||||
}
 | 
			
		||||
@ -2476,6 +2542,11 @@ th label {
 | 
			
		||||
  --tw-ring-color: rgb(14 159 110 / var(--tw-ring-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.focus\:ring-green-700:focus {
 | 
			
		||||
  --tw-ring-opacity: 1;
 | 
			
		||||
  --tw-ring-color: rgb(4 108 78 / var(--tw-ring-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.focus\:ring-violet-500:focus {
 | 
			
		||||
  --tw-ring-opacity: 1;
 | 
			
		||||
  --tw-ring-color: rgb(139 92 246 / var(--tw-ring-opacity));
 | 
			
		||||
@ -2654,6 +2725,26 @@ th label {
 | 
			
		||||
  color: rgb(255 255 255 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.odd\:dark\:bg-gray-900:is(.dark *):nth-child(odd) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(17 24 39 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.even\:dark\:bg-gray-800:is(.dark *):nth-child(even) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(31 41 55 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:hover\:border-green-700:hover:is(.dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(4 108 78 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:hover\:border-red-700:hover:is(.dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(200 30 30 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:hover\:bg-blue-700:hover:is(.dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(26 86 219 / var(--tw-bg-opacity));
 | 
			
		||||
@ -2674,6 +2765,11 @@ th label {
 | 
			
		||||
  background-color: rgb(31 41 55 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:hover\:bg-green-600:hover:is(.dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(5 122 85 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:hover\:bg-red-700:hover:is(.dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(200 30 30 / var(--tw-bg-opacity));
 | 
			
		||||
@ -2704,6 +2800,11 @@ th label {
 | 
			
		||||
  --tw-ring-color: rgb(63 131 248 / var(--tw-ring-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:focus\:ring-green-500:focus:is(.dark *) {
 | 
			
		||||
  --tw-ring-opacity: 1;
 | 
			
		||||
  --tw-ring-color: rgb(14 159 110 / var(--tw-ring-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 640px) {
 | 
			
		||||
  .sm\:inline {
 | 
			
		||||
    display: inline;
 | 
			
		||||
@ -2721,6 +2822,10 @@ th label {
 | 
			
		||||
    max-width: 36rem;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .sm\:rounded-lg {
 | 
			
		||||
    border-radius: 0.5rem;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .sm\:px-4 {
 | 
			
		||||
    padding-left: 1rem;
 | 
			
		||||
    padding-right: 1rem;
 | 
			
		||||
@ -2793,3 +2898,45 @@ th label {
 | 
			
		||||
.rtl\:space-x-reverse:where([dir="rtl"], [dir="rtl"] *) > :not([hidden]) ~ :not([hidden]) {
 | 
			
		||||
  --tw-space-x-reverse: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rtl\:text-right:where([dir="rtl"], [dir="rtl"] *) {
 | 
			
		||||
  text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&\:first-of-type_button\]\:rounded-s-lg:first-of-type button {
 | 
			
		||||
  border-start-start-radius: 0.5rem;
 | 
			
		||||
  border-end-start-radius: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&\:last-of-type_button\]\:rounded-e-lg:last-of-type button {
 | 
			
		||||
  border-start-end-radius: 0.5rem;
 | 
			
		||||
  border-end-end-radius: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&_td\:first-of-type\]\:whitespace-nowrap td:first-of-type {
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&_td\:first-of-type\]\:px-6 td:first-of-type {
 | 
			
		||||
  padding-left: 1.5rem;
 | 
			
		||||
  padding-right: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&_td\:first-of-type\]\:py-4 td:first-of-type {
 | 
			
		||||
  padding-top: 1rem;
 | 
			
		||||
  padding-bottom: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&_td\:first-of-type\]\:font-medium td:first-of-type {
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&_td\:first-of-type\]\:text-gray-900 td:first-of-type {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(17 24 39 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.\[\&_td\:first-of-type\]\:dark\:text-white:is(.dark *) td:first-of-type {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(255 255 255 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,9 @@
 | 
			
		||||
components:
 | 
			
		||||
  gamelink: "components/game_link.html"
 | 
			
		||||
  popover: "components/popover.html"
 | 
			
		||||
  table: "components/table.html"
 | 
			
		||||
  table_row: "components/table_row.html"
 | 
			
		||||
  table_td: "components/table_td.html"
 | 
			
		||||
  simple_table: "components/simple_table.html"
 | 
			
		||||
  button_group_sm: "components/button_group_sm.html"
 | 
			
		||||
  button_group_button_sm: "components/button_group_button_sm.html"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								games/templates/components/button_group_button_sm.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								games/templates/components/button_group_button_sm.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
{% var color=color|default:"gray" %}
 | 
			
		||||
<a href="{{ href }}"
 | 
			
		||||
   class="[&:first-of-type_button]:rounded-s-lg [&:last-of-type_button]:rounded-e-lg">
 | 
			
		||||
    {% if color == "gray" %}
 | 
			
		||||
        <button type="button"
 | 
			
		||||
                class="px-2 py-1 text-xs font-medium text-gray-900 bg-white border border-gray-200 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:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white">
 | 
			
		||||
            {{ text }}
 | 
			
		||||
        </button>
 | 
			
		||||
    {% elif color == "red" %}
 | 
			
		||||
        <button type="button"
 | 
			
		||||
                class="px-2 py-1 text-xs font-medium text-gray-900 bg-white border border-gray-200 hover:bg-red-500 hover:text-white focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:border-red-700 dark:hover:bg-red-700 dark:focus:ring-blue-500 dark:focus:text-white">
 | 
			
		||||
            {{ text }}
 | 
			
		||||
        </button>
 | 
			
		||||
    {% elif color == "green" %}
 | 
			
		||||
        <button type="button"
 | 
			
		||||
                class="px-2 py-1 text-xs font-medium text-gray-900 bg-white border border-gray-200 hover:bg-green-500 hover:border-green-600 hover:text-white focus:z-10 focus:ring-2 focus:ring-green-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:border-green-700 dark:hover:bg-green-600 dark:focus:ring-green-500 dark:focus:text-white">
 | 
			
		||||
            {{ text }}
 | 
			
		||||
        </button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
</a>
 | 
			
		||||
							
								
								
									
										5
									
								
								games/templates/components/button_group_sm.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								games/templates/components/button_group_sm.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
<div class="inline-flex rounded-md shadow-sm" role="group">
 | 
			
		||||
    {% for button in buttons %}
 | 
			
		||||
        {% button_group_button_sm href=button.href text=button.text color=button.color %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										14
									
								
								games/templates/components/simple_table.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								games/templates/components/simple_table.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
<div class="relative overflow-x-auto">
 | 
			
		||||
    <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
 | 
			
		||||
        <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
 | 
			
		||||
            <tr>
 | 
			
		||||
                {% for column in columns %}<th scope="col" class="px-6 py-3">{{ column }}</th>{% endfor %}
 | 
			
		||||
            </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
            {% for row in rows %}
 | 
			
		||||
                {% table_row data=row %}
 | 
			
		||||
            {% endfor %}
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										12
									
								
								games/templates/components/table.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								games/templates/components/table.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
 | 
			
		||||
    <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
 | 
			
		||||
        <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
 | 
			
		||||
            <tr>
 | 
			
		||||
                {% for column in columns %}<th scope="col" class="px-6 py-3">{{ column }}</th>{% endfor %}
 | 
			
		||||
            </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
            {{ children }}
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										15
									
								
								games/templates/components/table_row.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								games/templates/components/table_row.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
{% fragment as default_content %}
 | 
			
		||||
{% 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>
 | 
			
		||||
    {% else %}
 | 
			
		||||
        {% #table_td %}
 | 
			
		||||
        {{ td }}
 | 
			
		||||
        {% /table_td %}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
{% endfor %}
 | 
			
		||||
{% endfragment %}
 | 
			
		||||
<tr class="[&_td:first-of-type]:px-6 [&_td:first-of-type]:py-4 [&_td:first-of-type]:font-medium [&_td:first-of-type]:text-gray-900 [&_td:first-of-type]:whitespace-nowrap [&_td:first-of-type]:dark:text-white odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 border-b">
 | 
			
		||||
    {{ children|default:default_content }}
 | 
			
		||||
</tr>
 | 
			
		||||
							
								
								
									
										1
									
								
								games/templates/components/table_td.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								games/templates/components/table_td.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<td class="px-6 py-4">{{ children }}</td>
 | 
			
		||||
							
								
								
									
										8
									
								
								games/templates/list_purchases.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								games/templates/list_purchases.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
{% extends "base.html" %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
{% block title %}
 | 
			
		||||
    {{ title }}
 | 
			
		||||
{% endblock title %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
    {% simple_table columns=data.columns rows=data.rows %}
 | 
			
		||||
{% endblock content %}
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
from django.urls import path
 | 
			
		||||
 | 
			
		||||
from games import views
 | 
			
		||||
from games import purchaseviews, views
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path("", views.index, name="index"),
 | 
			
		||||
@ -25,6 +25,11 @@ urlpatterns = [
 | 
			
		||||
        views.delete_purchase,
 | 
			
		||||
        name="delete_purchase",
 | 
			
		||||
    ),
 | 
			
		||||
    path(
 | 
			
		||||
        "purchase/list",
 | 
			
		||||
        purchaseviews.list_purchases,
 | 
			
		||||
        name="list_purchases",
 | 
			
		||||
    ),
 | 
			
		||||
    path(
 | 
			
		||||
        "purchase/related-purchase-by-edition",
 | 
			
		||||
        views.related_purchase_by_edition,
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,9 @@ from .forms import (
 | 
			
		||||
)
 | 
			
		||||
from .models import Edition, Game, Platform, Purchase, Session
 | 
			
		||||
 | 
			
		||||
dateformat: str = "%d/%m/%Y"
 | 
			
		||||
datetimeformat: str = "%d/%m/%Y %H:%M"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def model_counts(request):
 | 
			
		||||
    return {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user