feat(frontend): integrate tailwind pipeline and update templates
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
{% load static %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
@ -8,31 +8,34 @@
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.12" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="container row-between">
|
||||
<a class="brand" href="{% url 'core:home' %}">HoopScout</a>
|
||||
<nav class="row-gap">
|
||||
<a href="{% url 'players:index' %}">Players</a>
|
||||
<a href="{% url 'competitions:index' %}">Competitions</a>
|
||||
<a href="{% url 'teams:index' %}">Teams</a>
|
||||
<a href="{% url 'scouting:index' %}">Scouting</a>
|
||||
<body class="min-h-full bg-slate-100 text-slate-900">
|
||||
<header class="border-b border-slate-200 bg-white">
|
||||
<div class="page-container flex flex-wrap items-center justify-between gap-4 py-3">
|
||||
<a class="text-xl font-bold tracking-tight text-slate-900 no-underline" href="{% url 'core:home' %}">HoopScout</a>
|
||||
<nav class="flex flex-wrap items-center gap-2 text-sm">
|
||||
<a class="rounded-md px-2 py-1 hover:bg-slate-100" href="{% url 'players:index' %}">Players</a>
|
||||
<a class="rounded-md px-2 py-1 hover:bg-slate-100" href="{% url 'competitions:index' %}">Competitions</a>
|
||||
<a class="rounded-md px-2 py-1 hover:bg-slate-100" href="{% url 'teams:index' %}">Teams</a>
|
||||
<a class="rounded-md px-2 py-1 hover:bg-slate-100" href="{% url 'scouting:index' %}">Scouting</a>
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'core:dashboard' %}">Dashboard</a>
|
||||
<a class="rounded-md px-2 py-1 hover:bg-slate-100" href="{% url 'core:dashboard' %}">Dashboard</a>
|
||||
<form method="post" action="{% url 'users:logout' %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="link-button">Logout</button>
|
||||
<button type="submit" class="btn-secondary px-2 py-1 text-xs">Logout</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<a href="{% url 'users:login' %}">Login</a>
|
||||
<a href="{% url 'users:signup' %}">Signup</a>
|
||||
<a class="rounded-md px-2 py-1 hover:bg-slate-100" href="{% url 'users:login' %}">Login</a>
|
||||
<a class="btn px-2 py-1 text-xs" href="{% url 'users:signup' %}">Signup</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
<main class="page-container py-6">
|
||||
{% include 'partials/messages.html' %}
|
||||
<div id="htmx-loading" class="htmx-indicator mb-4 rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-600" aria-live="polite">
|
||||
Loading...
|
||||
</div>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
{% if messages %}
|
||||
<section class="messages">
|
||||
<section class="mb-4 space-y-2" aria-live="polite">
|
||||
{% for message in messages %}
|
||||
<div class="message {{ message.tags }}">{{ message }}</div>
|
||||
{% if message.tags == "success" %}
|
||||
<div role="status" class="rounded-md border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm text-emerald-800">{{ message }}</div>
|
||||
{% elif message.tags == "error" %}
|
||||
<div role="alert" class="rounded-md border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-800">{{ message }}</div>
|
||||
{% else %}
|
||||
<div role="status" class="rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-700">{{ message }}</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
@ -4,50 +4,51 @@
|
||||
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<div class="row-between wrap-gap">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h1>{{ player.full_name }}</h1>
|
||||
<p class="muted-text">
|
||||
{{ player.nominal_position.name|default:"No nominal position" }}
|
||||
· {{ player.inferred_role.name|default:"No inferred role" }}
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-slate-600">{{ player.nominal_position.name|default:"No nominal position" }} · {{ player.inferred_role.name|default:"No inferred role" }}</p>
|
||||
</div>
|
||||
<div class="row-gap">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
{% if request.user.is_authenticated %}
|
||||
{% include "scouting/partials/favorite_button.html" with player=player is_favorite=is_favorite next_url=request.get_full_path %}
|
||||
{% endif %}
|
||||
<a class="button ghost" href="{% url 'players:index' %}">Back to search</a>
|
||||
<a class="btn-secondary" href="{% url 'players:index' %}">Back to search</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-grid mt-16">
|
||||
<div class="detail-card">
|
||||
<h2>Summary</h2>
|
||||
<p><strong>Nationality:</strong> {{ player.nationality.name|default:"-" }}</p>
|
||||
<p><strong>Origin competition:</strong> {{ player.origin_competition.name|default:"-" }}</p>
|
||||
<p><strong>Origin team:</strong> {{ player.origin_team.name|default:"-" }}</p>
|
||||
<p><strong>Birth date:</strong> {{ player.birth_date|date:"Y-m-d"|default:"-" }}</p>
|
||||
<p><strong>Age:</strong> {{ age|default:"-" }}</p>
|
||||
<p><strong>Height:</strong> {{ player.height_cm|default:"-" }} cm</p>
|
||||
<p><strong>Weight:</strong> {{ player.weight_kg|default:"-" }} kg</p>
|
||||
<p><strong>Dominant hand:</strong> {{ player.get_dominant_hand_display|default:"-" }}</p>
|
||||
<div class="mt-4 grid gap-3 md:grid-cols-3">
|
||||
<div class="rounded-lg border border-slate-200 p-4">
|
||||
<h2 class="text-base">Summary</h2>
|
||||
<dl class="mt-2 space-y-1 text-sm">
|
||||
<div><dt class="inline font-semibold">Nationality:</dt> <dd class="inline">{{ player.nationality.name|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Origin competition:</dt> <dd class="inline">{{ player.origin_competition.name|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Origin team:</dt> <dd class="inline">{{ player.origin_team.name|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Birth date:</dt> <dd class="inline">{{ player.birth_date|date:"Y-m-d"|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Age:</dt> <dd class="inline">{{ age|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Height:</dt> <dd class="inline">{{ player.height_cm|default:"-" }} cm</dd></div>
|
||||
<div><dt class="inline font-semibold">Weight:</dt> <dd class="inline">{{ player.weight_kg|default:"-" }} kg</dd></div>
|
||||
<div><dt class="inline font-semibold">Dominant hand:</dt> <dd class="inline">{{ player.get_dominant_hand_display|default:"-" }}</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<h2>Current Assignment</h2>
|
||||
<div class="rounded-lg border border-slate-200 p-4">
|
||||
<h2 class="text-base">Current Assignment</h2>
|
||||
{% if current_assignment %}
|
||||
<p><strong>Team:</strong> {{ current_assignment.team.name|default:"-" }}</p>
|
||||
<p><strong>Competition:</strong> {{ current_assignment.competition.name|default:"-" }}</p>
|
||||
<p><strong>Season:</strong> {{ current_assignment.season.label|default:"-" }}</p>
|
||||
<p><strong>Games:</strong> {{ current_assignment.games_played }}</p>
|
||||
<dl class="mt-2 space-y-1 text-sm">
|
||||
<div><dt class="inline font-semibold">Team:</dt> <dd class="inline">{{ current_assignment.team.name|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Competition:</dt> <dd class="inline">{{ current_assignment.competition.name|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Season:</dt> <dd class="inline">{{ current_assignment.season.label|default:"-" }}</dd></div>
|
||||
<div><dt class="inline font-semibold">Games:</dt> <dd class="inline">{{ current_assignment.games_played }}</dd></div>
|
||||
</dl>
|
||||
{% else %}
|
||||
<p>No active assignment available.</p>
|
||||
<div class="empty-state mt-2">No active assignment available.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<h2>Aliases</h2>
|
||||
<ul>
|
||||
<div class="rounded-lg border border-slate-200 p-4">
|
||||
<h2 class="text-base">Aliases</h2>
|
||||
<ul class="mt-2 list-inside list-disc text-sm text-slate-700">
|
||||
{% for alias in player.aliases.all %}
|
||||
<li>{{ alias.alias }}{% if alias.source %} ({{ alias.source }}){% endif %}</li>
|
||||
{% empty %}
|
||||
@ -58,50 +59,33 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel mt-16">
|
||||
<section class="panel mt-4">
|
||||
<h2>Team History</h2>
|
||||
{% if season_rows %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Season</th>
|
||||
<th>Team</th>
|
||||
<th>Competition</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div class="table-wrap mt-3">
|
||||
<table class="data-table">
|
||||
<thead><tr><th>Season</th><th>Team</th><th>Competition</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100 bg-white">
|
||||
{% for row in season_rows %}
|
||||
<tr>
|
||||
<td>{{ row.season.label|default:"-" }}</td>
|
||||
<td>{{ row.team.name|default:"-" }}</td>
|
||||
<td>{{ row.competition.name|default:"-" }}</td>
|
||||
</tr>
|
||||
<tr><td>{{ row.season.label|default:"-" }}</td><td>{{ row.team.name|default:"-" }}</td><td>{{ row.competition.name|default:"-" }}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No team history available.</p>
|
||||
<div class="empty-state mt-3">No team history available.</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="panel mt-16">
|
||||
<section class="panel mt-4">
|
||||
<h2>Career History</h2>
|
||||
{% if career_entries %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<div class="table-wrap mt-3">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Season</th>
|
||||
<th>Team</th>
|
||||
<th>Competition</th>
|
||||
<th>Role</th>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
</tr>
|
||||
<tr><th>Season</th><th>Team</th><th>Competition</th><th>Role</th><th>From</th><th>To</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="divide-y divide-slate-100 bg-white">
|
||||
{% for entry in career_entries %}
|
||||
<tr>
|
||||
<td>{{ entry.season.label|default:"-" }}</td>
|
||||
@ -116,44 +100,28 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No career entries available.</p>
|
||||
<div class="empty-state mt-3">No career entries available.</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="panel mt-16">
|
||||
<section class="panel mt-4">
|
||||
<h2>Season-by-Season Stats</h2>
|
||||
{% if season_rows %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<div class="table-wrap mt-3">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Season</th>
|
||||
<th>Team</th>
|
||||
<th>Competition</th>
|
||||
<th>Games</th>
|
||||
<th>MPG</th>
|
||||
<th>PPG</th>
|
||||
<th>RPG</th>
|
||||
<th>APG</th>
|
||||
<th>SPG</th>
|
||||
<th>BPG</th>
|
||||
<th>TOPG</th>
|
||||
<th>FG%</th>
|
||||
<th>3P%</th>
|
||||
<th>FT%</th>
|
||||
<th>Impact</th>
|
||||
<th>Season</th><th>Team</th><th>Competition</th><th>Games</th><th>MPG</th><th>PPG</th><th>RPG</th><th>APG</th><th>SPG</th><th>BPG</th><th>TOPG</th><th>FG%</th><th>3P%</th><th>FT%</th><th>Impact</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="divide-y divide-slate-100 bg-white">
|
||||
{% for row in season_rows %}
|
||||
<tr>
|
||||
<td>{{ row.season.label|default:"-" }}</td>
|
||||
<td>{{ row.team.name|default:"-" }}</td>
|
||||
<td>{{ row.competition.name|default:"-" }}</td>
|
||||
<td>{{ row.games_played }}</td>
|
||||
<td>
|
||||
{% if row.mpg is not None %}{{ row.mpg|floatformat:1 }}{% else %}-{% endif %}
|
||||
</td>
|
||||
<td>{% if row.mpg is not None %}{{ row.mpg|floatformat:1 }}{% else %}-{% endif %}</td>
|
||||
<td>{% if row.stats %}{{ row.stats.points }}{% else %}-{% endif %}</td>
|
||||
<td>{% if row.stats %}{{ row.stats.rebounds }}{% else %}-{% endif %}</td>
|
||||
<td>{% if row.stats %}{{ row.stats.assists }}{% else %}-{% endif %}</td>
|
||||
@ -170,7 +138,7 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No season stats available.</p>
|
||||
<div class="empty-state mt-3">No season stats available.</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
@ -5,18 +5,19 @@
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<h1>Player Search</h1>
|
||||
<p>Filter players by profile, context, and production metrics.</p>
|
||||
<p class="mt-1 text-sm text-slate-600">Filter players by profile, origin, context, and production metrics.</p>
|
||||
|
||||
<form
|
||||
method="get"
|
||||
class="stack search-form"
|
||||
class="mt-4 space-y-4"
|
||||
hx-get="{% url 'players:index' %}"
|
||||
hx-target="#player-results"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
hx-indicator="#htmx-loading"
|
||||
hx-trigger="submit, change delay:200ms from:select, keyup changed delay:400ms from:#id_q"
|
||||
>
|
||||
<div class="filter-grid filter-grid-4">
|
||||
<div class="grid gap-3 md:grid-cols-4">
|
||||
<div>
|
||||
<label for="id_q">Name</label>
|
||||
{{ search_form.q }}
|
||||
@ -29,13 +30,13 @@
|
||||
<label for="id_page_size">Page size</label>
|
||||
{{ search_form.page_size }}
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<button type="submit" class="button">Apply</button>
|
||||
<a class="button ghost" href="{% url 'players:index' %}">Reset</a>
|
||||
<div class="flex items-end gap-2">
|
||||
<button type="submit" class="btn">Apply</button>
|
||||
<a class="btn-secondary" href="{% url 'players:index' %}">Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-grid filter-grid-3">
|
||||
<div class="grid gap-3 md:grid-cols-3">
|
||||
<div><label for="id_nominal_position">Nominal position</label>{{ search_form.nominal_position }}</div>
|
||||
<div><label for="id_inferred_role">Inferred role</label>{{ search_form.inferred_role }}</div>
|
||||
<div><label for="id_nationality">Nationality</label>{{ search_form.nationality }}</div>
|
||||
@ -46,9 +47,9 @@
|
||||
<div><label for="id_origin_team">Origin team</label>{{ search_form.origin_team }}</div>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<details class="rounded-lg border border-slate-200 bg-slate-50 p-3">
|
||||
<summary>Physical and age filters</summary>
|
||||
<div class="filter-grid filter-grid-4">
|
||||
<div class="mt-3 grid gap-3 md:grid-cols-4">
|
||||
<div><label for="id_age_min">Age min</label>{{ search_form.age_min }}</div>
|
||||
<div><label for="id_age_max">Age max</label>{{ search_form.age_max }}</div>
|
||||
<div><label for="id_height_min">Height min (cm)</label>{{ search_form.height_min }}</div>
|
||||
@ -58,34 +59,29 @@
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<details class="rounded-lg border border-slate-200 bg-slate-50 p-3">
|
||||
<summary>Statistical filters</summary>
|
||||
<div class="filter-grid filter-grid-4">
|
||||
<div class="mt-3 grid gap-3 md:grid-cols-4">
|
||||
<div><label for="id_games_played_min">Games min</label>{{ search_form.games_played_min }}</div>
|
||||
<div><label for="id_games_played_max">Games max</label>{{ search_form.games_played_max }}</div>
|
||||
<div><label for="id_minutes_per_game_min">MPG min</label>{{ search_form.minutes_per_game_min }}</div>
|
||||
<div><label for="id_minutes_per_game_max">MPG max</label>{{ search_form.minutes_per_game_max }}</div>
|
||||
|
||||
<div><label for="id_points_per_game_min">PPG min</label>{{ search_form.points_per_game_min }}</div>
|
||||
<div><label for="id_points_per_game_max">PPG max</label>{{ search_form.points_per_game_max }}</div>
|
||||
<div><label for="id_rebounds_per_game_min">RPG min</label>{{ search_form.rebounds_per_game_min }}</div>
|
||||
<div><label for="id_rebounds_per_game_max">RPG max</label>{{ search_form.rebounds_per_game_max }}</div>
|
||||
|
||||
<div><label for="id_assists_per_game_min">APG min</label>{{ search_form.assists_per_game_min }}</div>
|
||||
<div><label for="id_assists_per_game_max">APG max</label>{{ search_form.assists_per_game_max }}</div>
|
||||
<div><label for="id_steals_per_game_min">SPG min</label>{{ search_form.steals_per_game_min }}</div>
|
||||
<div><label for="id_steals_per_game_max">SPG max</label>{{ search_form.steals_per_game_max }}</div>
|
||||
|
||||
<div><label for="id_blocks_per_game_min">BPG min</label>{{ search_form.blocks_per_game_min }}</div>
|
||||
<div><label for="id_blocks_per_game_max">BPG max</label>{{ search_form.blocks_per_game_max }}</div>
|
||||
<div><label for="id_turnovers_per_game_min">TOPG min</label>{{ search_form.turnovers_per_game_min }}</div>
|
||||
<div><label for="id_turnovers_per_game_max">TOPG max</label>{{ search_form.turnovers_per_game_max }}</div>
|
||||
|
||||
<div><label for="id_fg_pct_min">FG% min</label>{{ search_form.fg_pct_min }}</div>
|
||||
<div><label for="id_fg_pct_max">FG% max</label>{{ search_form.fg_pct_max }}</div>
|
||||
<div><label for="id_three_pct_min">3P% min</label>{{ search_form.three_pct_min }}</div>
|
||||
<div><label for="id_three_pct_max">3P% max</label>{{ search_form.three_pct_max }}</div>
|
||||
|
||||
<div><label for="id_ft_pct_min">FT% min</label>{{ search_form.ft_pct_min }}</div>
|
||||
<div><label for="id_ft_pct_max">FT% max</label>{{ search_form.ft_pct_max }}</div>
|
||||
<div><label for="id_efficiency_metric_min">Impact min</label>{{ search_form.efficiency_metric_min }}</div>
|
||||
@ -95,7 +91,7 @@
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section id="player-results" class="panel mt-16">
|
||||
<section id="player-results" class="panel mt-4" aria-live="polite">
|
||||
{% include "players/partials/results.html" %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{% load player_query %}
|
||||
|
||||
<div class="row-between wrap-gap">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<h2>Results</h2>
|
||||
<div class="muted-text">
|
||||
<div class="text-sm text-slate-600">
|
||||
{{ page_obj.paginator.count }} player{{ page_obj.paginator.count|pluralize }} found
|
||||
</div>
|
||||
</div>
|
||||
@ -12,8 +12,8 @@
|
||||
{% endif %}
|
||||
|
||||
{% if players %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<div class="table-wrap mt-4">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Player</th>
|
||||
@ -29,20 +29,15 @@
|
||||
{% if request.user.is_authenticated %}<th>Watchlist</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="divide-y divide-slate-100 bg-white">
|
||||
{% for player in players %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'players:detail' player.pk %}">{{ player.full_name }}</a>
|
||||
</td>
|
||||
<td><a class="font-medium" href="{% url 'players:detail' player.pk %}">{{ player.full_name }}</a></td>
|
||||
<td>{{ player.nationality.name|default:"-" }}</td>
|
||||
<td>
|
||||
{{ player.nominal_position.code|default:"-" }}
|
||||
/ {{ player.inferred_role.name|default:"-" }}
|
||||
</td>
|
||||
<td>{{ player.nominal_position.code|default:"-" }} / {{ player.inferred_role.name|default:"-" }}</td>
|
||||
<td>
|
||||
{{ player.origin_competition.name|default:"-" }}
|
||||
{% if player.origin_team %}<br><span class="muted-text">{{ player.origin_team.name }}</span>{% endif %}
|
||||
{% if player.origin_team %}<div class="text-xs text-slate-500">{{ player.origin_team.name }}</div>{% endif %}
|
||||
</td>
|
||||
<td>{{ player.height_cm|default:"-" }} / {{ player.weight_kg|default:"-" }}</td>
|
||||
<td>{{ player.games_played_value|floatformat:0 }}</td>
|
||||
@ -65,37 +60,21 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="pagination row-gap mt-16">
|
||||
{% if page_obj.has_previous %}
|
||||
{% query_transform page=page_obj.previous_page_number as prev_query %}
|
||||
<a
|
||||
class="button ghost"
|
||||
href="?{{ prev_query }}"
|
||||
hx-get="?{{ prev_query }}"
|
||||
hx-target="#player-results"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
>
|
||||
Previous
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
{% query_transform page=page_obj.next_page_number as next_query %}
|
||||
<a
|
||||
class="button ghost"
|
||||
href="?{{ next_query }}"
|
||||
hx-get="?{{ next_query }}"
|
||||
hx-target="#player-results"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
>
|
||||
Next
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="mt-4 flex items-center justify-between gap-3">
|
||||
<div>
|
||||
{% if page_obj.has_previous %}
|
||||
{% query_transform page=page_obj.previous_page_number as prev_query %}
|
||||
<a class="btn-secondary" href="?{{ prev_query }}" hx-get="?{{ prev_query }}" hx-target="#player-results" hx-swap="innerHTML" hx-push-url="true" hx-indicator="#htmx-loading">Previous</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<span class="text-sm text-slate-600">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
|
||||
<div>
|
||||
{% if page_obj.has_next %}
|
||||
{% query_transform page=page_obj.next_page_number as next_query %}
|
||||
<a class="btn-secondary" href="?{{ next_query }}" hx-get="?{{ next_query }}" hx-target="#player-results" hx-swap="innerHTML" hx-push-url="true" hx-indicator="#htmx-loading">Next</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No players matched the current filters.</p>
|
||||
<div class="empty-state mt-4">No players matched the current filters.</div>
|
||||
{% endif %}
|
||||
|
||||
@ -4,24 +4,24 @@
|
||||
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<div class="row-between wrap-gap">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h1>Scouting Workspace</h1>
|
||||
<p class="muted-text">Manage saved searches and your player watchlist.</p>
|
||||
<p class="mt-1 text-sm text-slate-600">Manage saved searches and your player watchlist.</p>
|
||||
</div>
|
||||
<div class="row-gap">
|
||||
<a class="button ghost" href="{% url 'scouting:saved_search_list' %}">All saved searches</a>
|
||||
<a class="button ghost" href="{% url 'scouting:watchlist' %}">Watchlist</a>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a class="btn-secondary" href="{% url 'scouting:saved_search_list' %}">All saved searches</a>
|
||||
<a class="btn-secondary" href="{% url 'scouting:watchlist' %}">Watchlist</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel mt-16">
|
||||
<section class="panel mt-4">
|
||||
<h2>Saved Searches</h2>
|
||||
{% include "scouting/partials/saved_search_table.html" with saved_searches=saved_searches %}
|
||||
</section>
|
||||
|
||||
<section class="panel mt-16">
|
||||
<section class="panel mt-4">
|
||||
<h2>Watchlist</h2>
|
||||
{% include "scouting/partials/watchlist_table.html" with favorites=favorites %}
|
||||
</section>
|
||||
|
||||
@ -5,12 +5,13 @@
|
||||
hx-post="{% url 'scouting:favorite_toggle' player.id %}"
|
||||
hx-target="#favorite-form-{{ player.id }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-indicator="#htmx-loading"
|
||||
>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{{ next_url }}">
|
||||
{% if is_favorite %}
|
||||
<button type="submit" class="button ghost">Remove favorite</button>
|
||||
<button type="submit" class="btn-secondary">Remove favorite</button>
|
||||
{% else %}
|
||||
<button type="submit" class="button ghost">Add favorite</button>
|
||||
<button type="submit" class="btn-secondary">Add favorite</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
<div class="message {% if ok %}success{% else %}error{% endif %}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% if ok %}
|
||||
<div class="rounded-md border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm text-emerald-800" role="status">{{ message }}</div>
|
||||
{% else %}
|
||||
<div class="rounded-md border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-800" role="alert">{{ message }}</div>
|
||||
{% endif %}
|
||||
|
||||
@ -1,18 +1,23 @@
|
||||
<div class="panel mt-16">
|
||||
<div class="mt-4 rounded-lg border border-slate-200 bg-slate-50 p-4">
|
||||
<h3>Save Current Search</h3>
|
||||
<p class="muted-text">Store current filters and replay them later.</p>
|
||||
<p class="mt-1 text-sm text-slate-600">Store current filters and replay them later.</p>
|
||||
|
||||
<form
|
||||
method="post"
|
||||
action="{% url 'scouting:saved_search_create' %}"
|
||||
class="row-gap"
|
||||
class="mt-3 flex flex-wrap items-end gap-3"
|
||||
hx-post="{% url 'scouting:saved_search_create' %}"
|
||||
hx-target="#saved-search-feedback"
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#htmx-loading"
|
||||
>
|
||||
{% csrf_token %}
|
||||
<input type="text" name="name" placeholder="Search name" required>
|
||||
<label class="inline-label">
|
||||
<div class="min-w-56 flex-1">
|
||||
<label for="saved-search-name">Search name</label>
|
||||
<input id="saved-search-name" type="text" name="name" placeholder="Search name" required>
|
||||
</div>
|
||||
|
||||
<label class="inline-flex items-center gap-2 pb-2 text-sm text-slate-700">
|
||||
<input type="checkbox" name="is_public">
|
||||
Public
|
||||
</label>
|
||||
@ -21,7 +26,7 @@
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}">
|
||||
{% endfor %}
|
||||
|
||||
<button class="button" type="submit">Save search</button>
|
||||
<button class="btn" type="submit">Save search</button>
|
||||
</form>
|
||||
<div id="saved-search-feedback" class="mt-16"></div>
|
||||
<div id="saved-search-feedback" class="mt-3" aria-live="polite"></div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% if saved_searches %}
|
||||
<div class="table-wrap mt-16">
|
||||
<table>
|
||||
<div class="table-wrap mt-4">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
@ -10,20 +10,20 @@
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="divide-y divide-slate-100 bg-white">
|
||||
{% for saved_search in saved_searches %}
|
||||
<tr>
|
||||
<td>{{ saved_search.name }}</td>
|
||||
<td class="font-medium text-slate-800">{{ saved_search.name }}</td>
|
||||
<td>{% if saved_search.is_public %}Public{% else %}Private{% endif %}</td>
|
||||
<td>{{ saved_search.updated_at|date:"Y-m-d H:i" }}</td>
|
||||
<td>{{ saved_search.last_run_at|date:"Y-m-d H:i"|default:"-" }}</td>
|
||||
<td>
|
||||
<div class="row-gap">
|
||||
<a class="button ghost" href="{% url 'scouting:saved_search_run' saved_search.pk %}">Run</a>
|
||||
<a class="button ghost" href="{% url 'scouting:saved_search_edit' saved_search.pk %}">Edit</a>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a class="btn-secondary" href="{% url 'scouting:saved_search_run' saved_search.pk %}">Run</a>
|
||||
<a class="btn-secondary" href="{% url 'scouting:saved_search_edit' saved_search.pk %}">Edit</a>
|
||||
<form method="post" action="{% url 'scouting:saved_search_delete' saved_search.pk %}">
|
||||
{% csrf_token %}
|
||||
<button class="button ghost" type="submit">Delete</button>
|
||||
<button class="btn-secondary" type="submit">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
@ -33,5 +33,5 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="mt-16">No saved searches yet.</p>
|
||||
<div class="empty-state mt-4">No saved searches yet.</div>
|
||||
{% endif %}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% if favorites %}
|
||||
<div class="table-wrap mt-16">
|
||||
<table>
|
||||
<div class="table-wrap mt-4">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Player</th>
|
||||
@ -10,15 +10,12 @@
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="divide-y divide-slate-100 bg-white">
|
||||
{% for favorite in favorites %}
|
||||
<tr>
|
||||
<td><a href="{% url 'players:detail' favorite.player_id %}">{{ favorite.player.full_name }}</a></td>
|
||||
<td><a class="font-medium" href="{% url 'players:detail' favorite.player_id %}">{{ favorite.player.full_name }}</a></td>
|
||||
<td>{{ favorite.player.nationality.name|default:"-" }}</td>
|
||||
<td>
|
||||
{{ favorite.player.nominal_position.code|default:"-" }}
|
||||
/ {{ favorite.player.inferred_role.name|default:"-" }}
|
||||
</td>
|
||||
<td>{{ favorite.player.nominal_position.code|default:"-" }} / {{ favorite.player.inferred_role.name|default:"-" }}</td>
|
||||
<td>{{ favorite.created_at|date:"Y-m-d" }}</td>
|
||||
<td>
|
||||
<div id="favorite-toggle-{{ favorite.player_id }}">
|
||||
@ -31,5 +28,5 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="mt-16">No players in your watchlist yet.</p>
|
||||
<div class="empty-state mt-4">No players in your watchlist yet.</div>
|
||||
{% endif %}
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
{% block title %}HoopScout | Edit Saved Search{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="panel narrow">
|
||||
<section class="panel mx-auto max-w-lg">
|
||||
<h1>Edit Saved Search</h1>
|
||||
<form method="post" class="stack">
|
||||
<form method="post" class="mt-4 space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="row-gap">
|
||||
<button type="submit" class="button">Update</button>
|
||||
<a class="button ghost" href="{% url 'scouting:index' %}">Cancel</a>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button type="submit" class="btn">Update</button>
|
||||
<a class="btn-secondary" href="{% url 'scouting:index' %}">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<div class="row-between wrap-gap">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<h1>Saved Searches</h1>
|
||||
<a class="button ghost" href="{% url 'scouting:index' %}">Back to scouting</a>
|
||||
<a class="btn-secondary" href="{% url 'scouting:index' %}">Back to scouting</a>
|
||||
</div>
|
||||
{% include "scouting/partials/saved_search_table.html" with saved_searches=saved_searches %}
|
||||
</section>
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<div class="row-between wrap-gap">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<h1>Watchlist</h1>
|
||||
<a class="button ghost" href="{% url 'scouting:index' %}">Back to scouting</a>
|
||||
<a class="btn-secondary" href="{% url 'scouting:index' %}">Back to scouting</a>
|
||||
</div>
|
||||
{% include "scouting/partials/watchlist_table.html" with favorites=favorites %}
|
||||
</section>
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
{% block title %}HoopScout | Login{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="panel narrow">
|
||||
<section class="panel mx-auto max-w-lg">
|
||||
<h1>Login</h1>
|
||||
<form method="post" class="stack">
|
||||
<form method="post" class="mt-4 space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="button">Sign in</button>
|
||||
<button type="submit" class="btn">Sign in</button>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
{% block title %}HoopScout | Signup{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="panel narrow">
|
||||
<section class="panel mx-auto max-w-lg">
|
||||
<h1>Create account</h1>
|
||||
<form method="post" class="stack">
|
||||
<form method="post" class="mt-4 space-y-4">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="button">Create account</button>
|
||||
<button type="submit" class="btn">Create account</button>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user