Files
pdga-rating/views/partials/course-layouts.ejs
T
Samuel Enocsson 4bbf6d9728 feat: redesign Courses page with tabs + restore Tjing import (#8)
- Restore src/scrapers/tjing.js with AbortController timeout (8s),
  error-object returns, and verbatim GraphQL queries
- Add getOrCreateLayout() to src/models/course.js
- New /api/tjing/search and /api/tjing/import/:tjingId routes;
  course-table route now includes layoutCount/activeLayoutCount via
  LEFT JOIN aggregation
- Rewrite courses.ejs: action-card with Find/Import tabs, results bar,
  HTMX course-table-region with body:refresh trigger
- Rewrite course-table.ejs: CSS-grid div structure replacing <table>,
  lazy-load expanded layouts via JS htmx.ajax
- Rewrite course-layouts.ejs: layout-card chips with rating tier colouring,
  collapsible inactive layouts section
- Rewrite courses.js: tab switching, live client-side filter, count display,
  Tjing search/import using DOM API (no innerHTML with untrusted data)
- Rewrite courses.css: full new design system using project tokens
2026-05-25 09:39:44 +02:00

62 lines
2.2 KiB
Plaintext

<% if (!layouts || layouts.length === 0) { %>
<div class="no-layouts">No layouts found. Click the refresh button to scrape layouts.</div>
<% } else {
var oneYearAgo = new Date();
oneYearAgo.setDate(oneYearAgo.getDate() - 365);
var activeLayouts = [];
var inactiveLayouts = [];
layouts.forEach(function(l) {
if (l.last_played && new Date(l.last_played) >= oneYearAgo) {
activeLayouts.push(l);
} else {
inactiveLayouts.push(l);
}
});
function ratingTier(r) {
if (r == null) return null;
if (r >= 970) return 'green';
if (r >= 940) return 'amber';
return 'orange';
}
%>
<div class="layouts-header">
<span class="layouts-kicker">LAYOUTS</span>
<span class="layouts-count"><%= activeLayouts.length %> active &middot; <%= inactiveLayouts.length %> inactive</span>
</div>
<ul class="layout-list">
<% activeLayouts.forEach(function(l) { var tier = ratingTier(l.mean_rating); %>
<li class="layout-card layout-card--active">
<div class="layout-info">
<span class="layout-name"><%= l.name %></span>
<span class="layout-last-played">Last played: <%= l.last_played %></span>
</div>
<div class="layout-chips">
<span class="chip chip-par">Par <%= l.par %></span>
<% if (tier) { %><span class="chip chip-rating chip-rating--<%= tier %>">Rating: <%= Math.round(l.mean_rating) %></span><% } %>
</div>
</li>
<% }); %>
</ul>
<% if (inactiveLayouts.length > 0) { %>
<div class="inactive-layouts">
<button class="inactive-toggle" type="button" onclick="toggleInactiveLayouts(this)" aria-expanded="false">
<span>Inactive layouts (<%= inactiveLayouts.length %>) — Not played in last year</span>
<i class="icon-chev">&#9662;</i>
</button>
<ul class="layout-list inactive-layouts-body" hidden>
<% inactiveLayouts.forEach(function(l) { %>
<li class="layout-card layout-card--inactive">
<div class="layout-info">
<span class="layout-name"><%= l.name %></span>
<span class="layout-never-played">Never played</span>
</div>
<div class="layout-chips">
<span class="chip chip-par">Par <%= l.par %></span>
</div>
</li>
<% }); %>
</ul>
</div>
<% } %>
<% } %>