4bbf6d9728
- 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
40 lines
1.8 KiB
Plaintext
40 lines
1.8 KiB
Plaintext
<% var body = `
|
|
<main class="page-courses">
|
|
<section class="action-card">
|
|
<div class="action-card-tabs" role="tablist">
|
|
<button class="action-tab is-active" role="tab" aria-selected="true" data-tab="find" id="tab-find">Find courses</button>
|
|
<button class="action-tab" role="tab" aria-selected="false" data-tab="tjing" id="tab-tjing">Import from Tjing</button>
|
|
</div>
|
|
<div class="action-card-body">
|
|
<div class="action-pane is-active" id="tab-pane-find" role="tabpanel" aria-labelledby="tab-find">
|
|
<input type="text" id="course-filter-input" placeholder="Find a course…" autocomplete="off">
|
|
<p class="action-hint">Filters the list below as you type.</p>
|
|
</div>
|
|
<div class="action-pane" id="tab-pane-tjing" role="tabpanel" aria-labelledby="tab-tjing" hidden>
|
|
<div class="tjing-search-row">
|
|
<input type="text" id="tjing-search-input" placeholder="Search Tjing courses…" autocomplete="off">
|
|
<button id="tjing-search-btn" class="btn-primary" onclick="searchTjing()">Search Tjing</button>
|
|
</div>
|
|
<p class="action-hint">Find and import Swedish courses from tjing.se.</p>
|
|
<div id="tjing-results"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="results-bar">
|
|
<span class="results-count">Showing <strong id="visible-count">0</strong> of <strong id="total-count">0</strong> courses</span>
|
|
<a class="results-link" href="#">View all →</a>
|
|
</div>
|
|
|
|
<div id="course-table-region" hx-get="/partials/course-table" hx-trigger="load, refresh from:body" hx-swap="innerHTML"></div>
|
|
</main>
|
|
`; %>
|
|
|
|
<%- include('../partials/layout', {
|
|
title: 'PDGA Courses - Sweden',
|
|
activePage: 'courses',
|
|
cssFiles: ['courses.css'],
|
|
jsFiles: ['courses.js'],
|
|
body: body
|
|
}) %>
|