also looking at this
LSU's own coaching surface, and what auditing the site turned up
#12
86 files
+3943
-1190
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -26,7 +26,11 @@ const canonicalPath = '/coaching' | ||
| 26 | 26 | <span class="kicker">Last Soul Ultra / Coaching</span> |
| 27 | 27 | <h1>Say how it felt.<br><span class="accent">The plan changes.</span></h1> |
| 28 | 28 | <p>Send a voice message after your run. An AI rebuilds next week around what you said, inside a coach's method.</p> |
| 29 | @if(hubLive) | |
| 29 | 30 | <a class="btn btn-lg" href="#training">Open the portal</a> |
| 31 | @else | |
| 32 | <a class="btn btn-lg" href="#training">See what is coming</a> | |
| 33 | @endif | |
| 30 | 34 | </div> |
| 31 | 35 | <div class="coaching-hero-shot"> |
| 32 | 36 | <img src="/assets/lsu/images/recap-06-810.webp" srcset="/assets/lsu/images/recap-06-540.webp 540w, /assets/lsu/images/recap-06-810.webp 810w, /assets/lsu/images/recap-06-1080.webp 1080w" sizes="(min-width: 900px) 42vw, 100vw" alt="A runner in a white sun hat and neck cover looks down at his watch." width="1080" height="1440" loading="eager" fetchpriority="high" decoding="async"> |
| @@ -96,8 +100,13 @@ const canonicalPath = '/coaching' | ||
| 96 | 100 | |
| 97 | 101 | <section id="training"> |
| 98 | 102 | <div class="coaching-head"> |
| 103 | @if(hubLive) | |
| 99 | 104 | <h2 class="h2-sm">What is open.<br><span class="accent">And where it runs.</span></h2> |
| 100 | 105 | <p>The tiers a coach has published, then the eight panels your training lives in once you are in one.</p> |
| 106 | @else | |
| 107 | <h2 class="h2-sm">Not open yet.<br><span class="accent">It opens here.</span></h2> | |
| 108 | <p>No coach has published a method and no price has been set. The first tier appears on this page, under the coach's own name.</p> | |
| 109 | @endif | |
| 101 | 110 | </div> |
| 102 | 111 | <div class="coaching-surface"> |
| 103 | 112 | <CoachingOffers /> |
| @@ -249,3 +249,31 @@ describe('the coaching hub', () => { | ||
| 249 | 249 | expect(html).toContain('Nothing is open yet') |
| 250 | 250 | }) |
| 251 | 251 | }) |
| 252 | ||
| 253 | describe('what the hub promises', () => { | |
| 254 | async function hubHtml(): Promise<string> { | |
| 255 | const path = resolve('resources/views/coaching.stx') | |
| 256 | return processDirectives(await Bun.file(path).text(), {}, path, { | |
| 257 | enabled: true, | |
| 258 | cache: false, | |
| 259 | componentsDir: resolve('resources/components'), | |
| 260 | layoutsDir: resolve('resources/layouts'), | |
| 261 | partialsDir: resolve('resources/partials'), | |
| 262 | }, new Set()) | |
| 263 | } | |
| 264 | ||
| 265 | it('offers the portal only when there is one', async () => { | |
| 266 | stubHq() | |
| 267 | expect(await hubHtml()).toContain('Open the portal') | |
| 268 | }) | |
| 269 | ||
| 270 | it('promises neither a portal nor eight panels it is not showing', async () => { | |
| 271 | stubHq(null) | |
| 272 | const html = await hubHtml() | |
| 273 | ||
| 274 | expect(html).not.toContain('Open the portal') | |
| 275 | expect(html).not.toContain('the eight panels your training lives in') | |
| 276 | expect(html).toContain('See what is coming') | |
| 277 | expect(html).toContain('Not open yet') | |
| 278 | }) | |
| 279 | }) | |