Skip to main content

max / makenotwork

Add the team screen module the previous commit left untracked 4239540d committed by pathspec, which does not pick up an untracked file, so the described screen itself was missing and the tree did not build from that commit.
Author: Max Johnson <me@maxj.phd> · 2026-09-01 00:49 UTC
Signed with PGP, not checked
Commit: 770033cb6b264c66a4e7bdcb9bea5813cdd3eab2
Parent: fde8b6b
1 file changed, +196 insertions, -0 deletions
@@ -1,0 +1,196 @@
1 + //! The team page at `/team`, described.
2 + //!
3 + //! The first described document this server serves to a reader who may or may
4 + //! not hold a session, which is the question `b5cbb646` left open and
5 + //! [`super::Audience`] answers. It replaces `templates/pages/team.html`,
6 + //! `TeamTemplate` and `landing::team_page`.
7 + //!
8 + //! # Why this is the screen that answers the signed-out question
9 + //!
10 + //! The gated documents (`/feed`, `/dashboard/export`) resolve a session and
11 + //! refuse without one. `/pricing` resolves nothing and takes a state built once
12 + //! at startup, which is why it carries no site header: with no per-request
13 + //! session there is nobody for a header to greet.
14 + //!
15 + //! Every remaining `pages/` template is neither. They are public, they carry
16 + //! the header, and the header reads differently once a reader signs in --
17 + //! `crate::shell::site_header` puts Library, Dashboard and the cart behind
18 + //! `Some(user)`. A screen like that needs a per-request viewer whose user is
19 + //! optional, and `/team` is the smallest of them: static content, no writes, no
20 + //! reads, so the only thing being proved is the mount.
21 + //!
22 + //! # One behaviour this changes, and it is a fix
23 + //!
24 + //! `team_page` resolved its user through `MaybeUserUnverified`, which reads the
25 + //! session and short-circuits a legacy one but never validates the tracking row.
26 + //! [`super::viewer_factory`] calls `crate::auth::authenticate`, which does:
27 + //! a revoked or suspended session renders as signed out here where it would have
28 + //! kept rendering as signed in. That is the posture `auth`'s own doc recommends
29 + //! ("prefer this anywhere the identity actually gates"), and the cost is one
30 + //! cached session touch on a page that made no database call at all before.
31 + //!
32 + //! # The card is a row, and the bio is a disclosure
33 + //!
34 + //! `<details class="team-card-bio">` is the exact disclosure shape the wiki note
35 + //! records (`mnw-server-conversion-plan`, 2026-08-26): a selective region
36 + //! showing at most one, holding exactly one labelled sub-region. The near miss
37 + //! -- label on the outer slot -- renders as a carousel with Prev/Next buttons
38 + //! and compiles fine, so [`the_bio_is_a_disclosure_and_not_a_stepper`] asserts
39 + //! the rendering rather than the text.
40 + //!
41 + //! [`the_bio_is_a_disclosure_and_not_a_stepper`]: tests::the_bio_is_a_disclosure_and_not_a_stepper
42 +
43 + use makeover_layout as layout;
44 + use quasi_router::screen::{Act, Row};
45 + use quasi_router::{
46 + Action, Document, Node, RegionKind, Request, Response, RouteError, Screen as Described, Slot,
47 + };
48 + use quasi_webview::Webview;
49 +
50 + /// The address, registered whole. See [`super::public_document_mount`].
51 + pub const PATH: &str = "/team";
52 +
53 + /// The page's own region, and what the skip link points at.
54 + pub const PAGE_REGION: &str = "team";
55 +
56 + /// The disclosure that holds the founder's bio.
57 + const BIO: &str = "founder-bio";
58 +
59 + /// The one labelled frame inside it, which is what makes it a disclosure rather
60 + /// than a one-slide carousel.
61 + const BIO_BODY: &str = "founder-bio-body";
62 +
63 + const MEASURE: layout::Measure = layout::Measure::Wide;
64 +
65 + /// The page, which reads nothing and so takes no round trip.
66 + pub fn screen(_viewer: &super::Viewer, _request: Request) -> Result<Response, RouteError> {
67 + Ok(page_screen().into())
68 + }
69 +
70 + /// The whole document: the title, the measure, the body.
71 + fn page_screen() -> Described {
72 + Described::single("Team - Makenotwork")
73 + .measured(MEASURE)
74 + .documented(Document::default().classed(crate::shell::body_class(MEASURE, &["team-page"])))
75 + .summarised("Small by design: who builds Makenotwork, and what they did before.")
76 + .with(
77 + Slot::new(PAGE_REGION, RegionKind::Pane)
78 + .with(Node::page("Team"))
79 + .with(Node::text(
80 + "Small by design. The base platform was designed by the founder, who intends \
81 + to stay as the technical lead through the project's growth.",
82 + ))
83 + .with(Node::section("Founder"))
84 + .with(Node::list([founder()]))
85 + .with(bio()),
86 + )
87 + }
88 +
89 + /// The one card on the page.
90 + ///
91 + /// A row rather than a grid of one: `use-case-grid-2col` held a single article,
92 + /// so the grid was describing an intention rather than a layout. When there is a
93 + /// second person the list grows a second row and the design system decides how
94 + /// two rows sit, which is the arrangement question answered once for every
95 + /// screen instead of in this page's own CSS.
96 + fn founder() -> Row {
97 + Row::new("Max")
98 + .secondary("CEO & Tech Lead")
99 + .meta("Designed and built the platform.")
100 + .act(Act::new("@max", Action::get("/u/max").navigating()))
101 + }
102 +
103 + /// The bio, closed until asked for.
104 + ///
105 + /// `showing_at_most_one(None)` is closed; `Some(0)` would be `<details open>`.
106 + fn bio() -> Node {
107 + Node::Region(
108 + Slot::new(BIO, RegionKind::Group)
109 + .with(Node::Region(
110 + Slot::new(BIO_BODY, RegionKind::Pane)
111 + .label("Bio")
112 + .with(Node::text(BIO_TEXT)),
113 + ))
114 + .showing_at_most_one(None),
115 + )
116 + }
117 +
118 + /// Carried over verbatim from the template. Prose the founder wrote, so it is
119 + /// content rather than copy and nothing here rewrites it.
120 + const BIO_TEXT: &str = "I started in the creator space as a young kid making backgrounds and \
121 + montages for CoD MW2 players. I picked up programming around the same time, helping Minecraft \
122 + server owners with whatever they needed. I spent the years after that focused on my \
123 + education: a BA in mathematics at the University of Chicago, then a PhD in mathematics at UC \
124 + San Diego. Afterward I decided to start my own business, and here we are.";
125 +
126 + /// The document this screen is drawn in.
127 + ///
128 + /// The head, the tail and the token meta come off
129 + /// [`super::Viewer::document_shell`]. What is added is what every public page on
130 + /// this site opens with: the skip link, and the site header, which reads the
131 + /// viewer's user through an `Option` because on this mount there may not be one.
132 + #[must_use]
133 + pub fn renderer(viewer: &super::Viewer) -> Webview {
134 + Webview::new().with_shell(viewer.document_shell().with_body_first(format!(
135 + "{}{}",
136 + crate::shell::skip_link(PAGE_REGION),
137 + crate::shell::site_header(viewer.user.as_ref(), Some(&viewer.csrf)),
138 + )))
139 + }
140 +
141 + #[cfg(test)]
142 + mod tests {
143 + use super::*;
144 +
145 + fn html() -> String {
146 + use quasi_axum::Serves as _;
147 +
148 + Webview::new().screen(&page_screen())
149 + }
150 +
151 + /// `2790e5c4` for this screen. The template wrote the measure on the body
152 + /// and `team-page` on a container div inside it; a described document has no
153 + /// container, so both land on the body and the CSS measure moved onto
154 + /// `.team-page main` the way `/feed`'s did. Same two classes, one element.
155 + #[test]
156 + fn the_document_carries_the_classes_the_template_carried() {
157 + let screen = page_screen();
158 +
159 + assert_eq!(
160 + screen.document.body_class.as_deref(),
161 + Some("padded-page team-page")
162 + );
163 + assert!(html().contains("padded-page team-page"), "{}", html());
164 + }
165 +
166 + /// The disclosure shape, asserted by what it renders rather than by what it
167 + /// says: a label on the outer slot instead of the inner one is a legal
168 + /// carousel, so the text is identical and the markup is not.
169 + #[test]
170 + fn the_bio_is_a_disclosure_and_not_a_stepper() {
171 + let html = html();
172 +
173 + assert!(html.contains(r#"aria-expanded="false""#), "{html}");
174 + assert!(!html.contains(r#"data-shows="next""#), "{html}");
175 + assert!(html.contains("University of Chicago"), "{html}");
176 + }
177 +
178 + /// The founder's row says who, what, and where to read more.
179 + #[test]
180 + fn the_founder_row_links_to_the_profile() {
181 + let html = html();
182 +
183 + assert!(html.contains("CEO &amp; Tech Lead"), "{html}");
184 + assert!(html.contains(r#"href="/u/max""#), "{html}");
185 + }
186 +
187 + /// `736f45a5`: this screen's markup carries none of the four spellings.
188 + #[test]
189 + fn the_page_spells_no_spinner() {
190 + let html = html();
191 +
192 + for spelling in ["htmx-indicator", "spinner", "loading-text", "loading-state"] {
193 + assert!(!html.contains(spelling), "{spelling} survives in {html}");
194 + }
195 + }
196 + }