Skip to main content

max / makenotwork

8.8 KB · 302 lines History Blame Raw
1 //! Askama template definitions for all HTML pages and fragments.
2 //!
3 //! Split by domain:
4 //! - `public`: landing, auth, content, blog, discover, health
5 //! - `dashboard`: creator dashboards, admin, export, account management
6 //! - `partials`: HTMX fragments, tab content, alerts, form status
7
8 mod public;
9 mod dashboard;
10 mod partials;
11 mod embed;
12
13 pub use public::*;
14 pub use dashboard::*;
15 pub use partials::*;
16 pub use embed::*;
17
18 use askama::Template;
19 use axum::{
20 http::StatusCode,
21 response::{Html, IntoResponse, Response},
22 };
23
24 /// Base context shared by all templates.
25 /// Note: csrf_token is Option to allow templates to work without CSRF
26 /// but all authenticated pages should include it.
27 pub type CsrfTokenOption = Option<String>;
28
29 /// One frame of the composable click-through carousel (`partials/carousel.html`).
30 ///
31 /// A carousel is just an ordered `&[CarouselFrame]`; the same macro renders it
32 /// on any surface (app product pages, landing) -- only the frame list differs.
33 /// Build a `Vec<CarouselFrame>` on a page template and pass it to the macro.
34 ///
35 /// Prefer [`CarouselFrame::new`] over a struct literal: it makes the alt text a
36 /// required, named argument and nudges (in debug builds) toward alt that
37 /// actually describes the screenshot. A carousel frame is a meaningful image,
38 /// so alt is not optional and should not be a filename or a bare label like
39 /// "screenshot" -- a screen-reader user should get the same information a
40 /// sighted viewer does.
41 #[derive(Clone)]
42 pub struct CarouselFrame {
43 /// Image URL (typically an optimized screenshot under `/static/images/shots/`).
44 pub image: String,
45 /// Alt text describing the screenshot. Required -- every frame is an image.
46 pub alt: String,
47 /// Optional caption shown under the frame.
48 pub caption: Option<String>,
49 }
50
51 impl CarouselFrame {
52 /// Build a frame, nudging toward helpful alt text.
53 ///
54 /// In debug builds this asserts the alt text is non-empty and looks like a
55 /// description rather than a filename or a one-word placeholder. The checks
56 /// are debug-only so they guide authors during development without ever
57 /// affecting a release render.
58 pub fn new(image: impl Into<String>, alt: impl Into<String>) -> Self {
59 let image = image.into();
60 let alt = alt.into();
61 debug_assert!(
62 !alt.trim().is_empty(),
63 "carousel frame `{image}` has empty alt text -- describe what the \
64 screenshot shows so screen-reader users get the same information \
65 sighted viewers do"
66 );
67 debug_assert!(
68 !alt.trim_start().starts_with('/') && !alt.contains(".webp") && !alt.contains(".png"),
69 "carousel frame alt text looks like a filename (`{alt}`) -- write a \
70 human description of what the screenshot shows instead"
71 );
72 Self { image, alt, caption: None }
73 }
74
75 /// Attach an optional caption shown under the frame.
76 #[must_use]
77 pub fn with_caption(mut self, caption: impl Into<String>) -> Self {
78 self.caption = Some(caption.into());
79 self
80 }
81 }
82
83 /// Helper to convert any Askama template into an Axum response.
84 fn render_template<T: Template>(template: T) -> Response {
85 match template.render() {
86 Ok(html) => Html(html).into_response(),
87 Err(err) => {
88 tracing::error!(error = ?err, "template rendering error");
89 (StatusCode::INTERNAL_SERVER_ERROR, "Template error").into_response()
90 }
91 }
92 }
93
94 /// Implement `IntoResponse` for one or more Askama template structs.
95 macro_rules! impl_into_response {
96 ($($T:ty),+ $(,)?) => {
97 $(
98 impl IntoResponse for $T {
99 fn into_response(self) -> Response {
100 render_template(self)
101 }
102 }
103 )+
104 };
105 }
106
107 impl_into_response!(
108 // Public pages
109 SandboxTemplate,
110 PolicyTemplate,
111 IndexTemplate,
112 LibraryTemplate,
113 CartTemplate,
114 LoginTemplate,
115 TwoFactorTemplate,
116 OAuthAuthorizeTemplate,
117 ForgotPasswordTemplate,
118 ResetPasswordTemplate,
119 UserTemplate,
120 ProjectTemplate,
121 ProjectPaywallTemplate,
122 ItemTemplate,
123 LibraryAudioTemplate,
124 LibraryDownloadsTemplate,
125 LibraryLockedTemplate,
126 LibraryTextTemplate,
127 LibraryVideoTemplate,
128 TextReaderTemplate,
129 AudioPlayerTemplate,
130 VideoPlayerTemplate,
131 DiscoverTemplate,
132 DiscoverResultsTemplate,
133 PurchaseTemplate,
134 ReceiptTemplate,
135 BuyPageTemplate,
136 FeedTemplate,
137 StripeConnectDisclaimerTemplate,
138 // Blog pages
139 ProjectBlogTemplate,
140 BlogPostTemplate,
141 // Documentation pages
142 DocTemplate,
143 DocIndexTemplate,
144 // Pricing calculator
145 PricingTemplate,
146 // Platform economics + runway disclosure
147 EconomicsTemplate,
148 // Use cases
149 UseCasesTemplate,
150 // Team
151 TeamTemplate,
152 // Fan+
153 FanPlusTemplate,
154 // Creator invite system
155 CreatorsTemplate,
156 // Email & account
157 EmailResultTemplate,
158 ConfirmDeleteTemplate,
159 AccountDeletedTemplate,
160 // Health
161 HealthTemplate,
162 // Dashboard pages
163 DashboardUserTemplate,
164 DashboardProjectTemplate,
165 DashboardItemTemplate,
166 // Admin
167 AdminWaitlistTemplate,
168 AdminUsersTemplate,
169 AdminUploadsTemplate,
170 AdminScanAuditTemplate,
171 AdminAppealsTemplate,
172 AdminReportsTemplate,
173 AdminSignupsTemplate,
174 AdminMetricsTemplate,
175 // Export, import & account management
176 ExportPortalTemplate,
177 ImportPortalTemplate,
178 DeleteAccountTemplate,
179 BlogEditorTemplate,
180 // HTMX partials
181 AlertTemplate,
182 LibraryStatusTemplate,
183 ExportDownloadTemplate,
184 ExportContentReadyTemplate,
185 TransactionsTableTemplate,
186 UserProfileTabTemplate,
187 UserSettingsTabTemplate,
188 UserAccountTabTemplate,
189 UserSshKeysTabTemplate,
190 UserPaymentsTabTemplate,
191 UserProjectsTabTemplate,
192 UserCreatorTabTemplate,
193 ProjectOverviewTabTemplate,
194 ProjectContentTabTemplate,
195 ProjectAnalyticsTabTemplate,
196 UserAnalyticsTabTemplate,
197 BuyerContactsPartialTemplate,
198 ProjectSettingsTabTemplate,
199 ProjectCodeTabTemplate,
200 ProjectBlogTabTemplate,
201 ProjectSubscriptionsTabTemplate,
202 ProjectMembersTabTemplate,
203 ProjectMonetizationTabTemplate,
204 ItemEditRowTemplate,
205 // Admin partials
206 AdminWaitlistEntriesTemplate,
207 AdminUserEntriesTemplate,
208 AdminUploadEntriesTemplate,
209 AdminQueueSummaryTemplate,
210 AdminAppealEntriesTemplate,
211 AdminReportEntriesTemplate,
212 SuspensionBannerTemplate,
213 // License keys
214 ItemLicenseKeysTemplate,
215 // Promo codes
216 PromoCodesListTemplate,
217 ProjectPromotionsTabTemplate,
218 // Sessions
219 UserSessionsPartialTemplate,
220 // SyncKit
221 UserSyncKitTabTemplate,
222 ProjectSyncKitTabTemplate,
223 // Forums (Multithreaded)
224 UserForumsTabTemplate,
225 // Media library
226 UserMediaTabTemplate,
227 // Support
228 UserSupportTabTemplate,
229 // Collections
230 CollectionTemplate,
231 // Library tabs
232 LibraryPurchasesTabTemplate,
233 LibraryFeedTabTemplate,
234 LibraryCollectionsTabTemplate,
235 LibraryContactsTabTemplate,
236 LibraryCommunitiesTabTemplate,
237 // Follow button
238 FollowButtonTemplate,
239 TagFollowToggleTemplate,
240 // Tag suggestions
241 TagSuggestionsTemplate,
242 // Item analytics
243 ItemAnalyticsPartialTemplate,
244 // Item dashboard tabs
245 ItemOverviewTabTemplate,
246 ItemDetailsTabTemplate,
247 ItemPricingTabTemplate,
248 ItemFilesTabTemplate,
249 ItemSalesTabTemplate,
250 ItemEmbedTabTemplate,
251 // Onboarding checklist
252 OnboardingChecklistPartialTemplate,
253 // Tag tree browser
254 TagTreeTemplate,
255 // TOTP 2FA
256 TotpSetupTemplate,
257 TotpStatusTemplate,
258 // Passkeys
259 PasskeyListTemplate,
260 // Git source browser
261 GitRepoTemplate,
262 GitTreeTemplate,
263 GitFileTemplate,
264 GitCommitsTemplate,
265 GitCommitDetailTemplate,
266 GitBlameTemplate,
267 GitUserReposTemplate,
268 GitExploreTemplate,
269 GitFileLogTemplate,
270 // Git issues
271 GitIssueListTemplate,
272 GitIssueDetailTemplate,
273 GitRepoSettingsTemplate,
274 // Join wizard
275 WizardJoinTemplate,
276 WizardJoinAccountTemplate,
277 WizardJoinProfileTemplate,
278 WizardJoinCompleteTemplate,
279 // Creation wizards — full pages
280 WizardProjectTemplate,
281 WizardItemTemplate,
282 // Creation wizards — project step partials
283 WizardProjectBasicsTemplate,
284 WizardProjectAppearanceTemplate,
285 WizardProjectMonetizationTemplate,
286 WizardProjectFirstContentTemplate,
287 WizardProjectPreviewTemplate,
288 // Creation wizards — item step partials
289 WizardItemTypeTemplate,
290 WizardItemBasicsTemplate,
291 WizardItemContentTemplate,
292 WizardItemSectionsTemplate,
293 WizardItemPricingTemplate,
294 WizardItemPreviewTemplate,
295 // Embed widgets
296 EmbedItemButtonTemplate,
297 EmbedItemCardTemplate,
298 EmbedItemPlayerTemplate,
299 EmbedTipButtonTemplate,
300 EmbedProjectCardTemplate,
301 );
302