//! Templates for public-facing pages: landing, auth, content, blog, discover. //! //! Git source-browser templates live in the `git` submodule and are //! re-exported flat — call sites still see `templates::GitRepoTemplate` etc. mod git; mod health; pub use git::*; pub use health::*; use std::sync::Arc; use askama::Template; use crate::auth::SessionUser; use crate::types::*; use super::CsrfTokenOption; // ============================================================================ // Public Pages // ============================================================================ /// Sandbox info page explaining the ephemeral demo mode. #[derive(Template)] #[template(path = "pages/sandbox.html")] pub struct SandboxTemplate { pub csrf_token: CsrfTokenOption, } /// Content policy page. #[derive(Template)] #[template(path = "pages/policy.html")] pub struct PolicyTemplate { /// CSRF token injected into forms; `None` on public pages that have no forms. pub csrf_token: CsrfTokenOption, /// Logged-in user context for the site header; `None` when not authenticated. pub session_user: Option, } /// Landing page. #[derive(Template)] #[template(path = "pages/index.html")] pub struct IndexTemplate { pub csrf_token: CsrfTokenOption, pub host_url: Arc, pub total_creators: u32, pub total_items: u32, /// Whether the founder pricing window is currently open. When true the /// landing page features the founder rate prominently and links to /docs /// /guide/tiers. See `project_founder_pricing.md`. pub founder_window_open: bool, /// Remaining founder slots (1,000 cap). Only shown when small enough to /// convey urgency; not exposed when comfortably above the cap. pub founder_slots_remaining: Option, pub tier_prices: crate::tier_prices::TierPrices, /// Screenshot frames for the "see the platform" carousel. Currently /// placeholders; swap the images (and tighten the alt text) once real /// captures exist. Empty hides the section. pub landing_carousel: Vec, /// The "Last shipped" velocity line, drawn from the most recent published, /// landing-flagged changelog post. `None` suppresses the line entirely /// (no placeholder) — same honesty rule the runway disclosure uses. pub last_shipped: Option, } /// One-line "Last shipped" velocity signal for the landing page. pub struct LandingVelocity { /// Post title. pub title: String, /// Publication date, preformatted (e.g. "Jun 07, 2026"). pub date: String, /// Link target: `/changelog/{slug}`. pub href: String, } /// User's library shell with inline purchases tab (other tabs loaded via HTMX). #[derive(Template)] #[template(path = "pages/library.html")] pub struct LibraryTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub purchases: Vec, pub subscriptions: Vec, pub has_mt_memberships: bool, } /// Shopping cart page with items grouped by seller. #[derive(Template)] #[template(path = "pages/cart.html")] pub struct CartTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub seller_groups: Vec, pub wishlist_suggestions: Vec, pub total_items: usize, /// Set to "partial" when a multi-seller checkout partially succeeded. pub checkout_status: String, } /// A group of cart items from the same seller. pub struct CartSellerGroup { pub seller_username: String, pub seller_id: String, pub stripe_ready: bool, pub items: Vec, pub subtotal_cents: i32, pub item_count: usize, /// How much the creator saves vs. individual purchases ($0.30 per extra item). pub savings_cents: i32, } impl CartSellerGroup { pub fn subtotal_display(&self) -> String { crate::formatting::format_revenue(self.subtotal_cents as i64) } pub fn savings_display(&self) -> String { crate::formatting::format_revenue(self.savings_cents as i64) } } /// Login page. #[derive(Template)] #[template(path = "pages/login.html")] pub struct LoginTemplate { pub csrf_token: CsrfTokenOption, /// Re-displayed in the username/email input on validation failure so the /// user doesn't have to retype it. Empty on the first GET. pub prefill_login: String, /// Shown inline above the form on a failed POST. None hides the banner. pub error: Option, /// Neutral informational notice above the form (e.g. the access-gate prompt /// on the testnot staging mirror). None hides it. Separate from `error` so /// it doesn't render as a failure. pub notice: Option, /// When true, the page shows a single "Sign in with Makenot.work" button /// (delegated SSO) instead of the local password form. Set on the testnot /// mirror where `[sso]` is configured. pub sso_enabled: bool, } // ============================================================================ // Join Wizard // ============================================================================ /// Full page: join/signup wizard. #[derive(Template)] #[template(path = "wizards/wizard_join.html")] pub struct WizardJoinTemplate { pub csrf_token: CsrfTokenOption, pub nav: Vec, pub invite_code: Option, } /// Step 1 partial: account creation (for back-nav reload). #[derive(Template)] #[template(path = "wizards/steps/join/account.html")] pub struct WizardJoinAccountTemplate { pub nav: Vec, pub csrf_token: CsrfTokenOption, pub invite_code: Option, } /// Step 2 partial: profile (display name + bio). #[derive(Template)] #[template(path = "wizards/steps/join/profile.html")] pub struct WizardJoinProfileTemplate { pub nav: Vec, } /// Step 3 partial: welcome/complete with intent branching. #[derive(Template)] #[template(path = "wizards/steps/join/complete.html")] pub struct WizardJoinCompleteTemplate { pub nav: Vec, pub display_name: String, /// Whether this user already has creator access. pub is_creator: bool, /// Whether this user arrived via invite (already has waitlist entry). pub has_invite: bool, } /// Two-factor authentication verification page (login flow). #[derive(Template)] #[template(path = "pages/two_factor.html")] pub struct TwoFactorTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub error: Option, } /// OAuth2 authorization / consent page. #[derive(Template)] #[template(path = "pages/oauth_authorize.html")] pub struct OAuthAuthorizeTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub app_name: String, pub client_id: String, pub redirect_uri: String, pub state: String, pub code_challenge: String, pub code_challenge_method: String, pub error_message: Option, } /// Forgot password form. #[derive(Template)] #[template(path = "pages/forgot_password.html")] pub struct ForgotPasswordTemplate { pub csrf_token: CsrfTokenOption, } /// Password reset form (reached via email link). #[derive(Template)] #[template(path = "pages/reset_password.html")] pub struct ResetPasswordTemplate { pub csrf_token: CsrfTokenOption, pub valid: bool, pub user_id: String, pub expires: String, pub sig: String, /// Inline error banner (e.g. "Passwords do not match"). None hides the /// banner. Used on non-HTMX form-validation failures so the user stays /// on the form with the signed link fields intact. pub error: Option, } /// Public user profile page. #[derive(Template)] #[template(path = "pages/user.html")] #[allow(dead_code)] // Fields used by Askama template pub struct UserTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub user: User, pub custom_links: Vec, pub projects: Vec, pub public_collections: Vec, /// User ID for the follow button target. pub user_id: String, /// Whether the current viewer is looking at their own profile. pub is_own_profile: bool, /// Whether the current viewer is following this user. pub is_following: bool, /// Total follower count for this user. pub follower_count: i64, /// Base URL for OG meta tags. pub host_url: Arc, /// Whether this creator has voluntarily paused their account. pub creator_paused: bool, /// Whether this creator accepts tips. pub tips_enabled: bool, /// Creator's user ID for tip checkout (string for template use). pub creator_id: String, /// Project ID for tip attribution (None on user profile pages). pub tip_project_id: Option, } /// Public collection page (shareable URL). #[derive(Template)] #[template(path = "pages/collection.html")] #[allow(dead_code)] // Fields used by Askama template pub struct CollectionTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub collection: Collection, pub items: Vec, pub owner_username: String, pub owner_display_name: Option, pub is_owner: bool, } /// Public project page with item listing. #[derive(Template)] #[template(path = "pages/project.html")] #[allow(dead_code)] // Fields used by Askama template pub struct ProjectTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub project: Project, pub creator_username: String, pub items: Vec, /// Project ID for the follow button target. pub project_id: String, /// Whether the current viewer is following this project. pub is_following: bool, /// Total follower count for this project. pub follower_count: i64, /// Active subscription tiers available for this project. pub subscription_tiers: Vec, /// Whether the current viewer already has an active subscription. pub has_subscription: bool, /// Base URL for OG meta tags. pub host_url: Arc, /// Linked git repositories: (name, URL) pairs. pub git_repos: Vec<(String, String)>, /// Whether this project has any published blog posts. pub has_blog_posts: bool, /// URL to the paired MT community forum (None if no community provisioned). pub community_url: Option, /// Whether the project owner accepts tips. pub tips_enabled: bool, /// Creator's user ID for tip checkout (string for template use). pub creator_id: String, /// Project ID for tip attribution. pub tip_project_id: Option, /// Whether the current viewer owns this project. pub is_owner: bool, /// Tabbed markdown sections (privacy, terms, FAQ, etc). pub sections: Vec, /// Ordered gallery images rendered through the shared carousel widget /// (empty → the carousel section is suppressed). Additive to cover_image_url. pub gallery: Vec, } /// Project paywall landing page (shown when a project requires purchase/subscription). #[derive(Template)] #[template(path = "pages/project_paywall.html")] pub struct ProjectPaywallTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub project: Project, pub creator_username: String, /// Human-readable pricing (e.g. "$19.99", "Subscription"). pub price_display: String, /// What kind of checkout flow is needed. pub checkout_type: crate::pricing::CheckoutType, /// Available subscription tiers (for subscription-model projects). pub subscription_tiers: Vec, /// Base URL for OG meta tags. pub host_url: Arc, } /// Public item detail page. #[derive(Template)] #[template(path = "pages/item.html")] #[allow(dead_code)] // Fields used by Askama template pub struct ItemTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub project_title: String, pub project_slug: String, /// Base URL for OG meta tags. pub host_url: Arc, /// URL to the MT discussion thread (None if no linked thread or MT unavailable). pub discussion_url: Option, /// Number of posts in the linked discussion thread. pub discussion_count: Option, /// Project cover image URL (fallback for og:image when item has no cover). pub project_cover_image_url: Option, /// Child items for bundle-type items (empty for non-bundles). pub bundle_items: Vec, /// Bundles containing this item (for unlisted items, to show "Available in" links). pub containing_bundles: Vec, /// Tabbed content sections (e.g. Features, Installation, Specs). pub sections: Vec, /// Whether the current user is the item's creator (for dashboard links). pub is_owner: bool, /// Whether the current user has wishlisted this item. pub is_wishlisted: bool, /// Whether the current user has this item in their cart. pub in_cart: bool, /// How many of the current user's collections contain this item. pub collection_count: u32, /// Whether the current user can consume this item (purchased, free, subscribed, creator, bundle). /// Drives the store-page CTA swap: true → "View in library", false → Buy/PWYW. pub has_access: bool, /// Ordered gallery images rendered through the shared carousel widget /// (empty → the carousel section is suppressed). Additive to cover_image_url. pub gallery: Vec, } /// Library (consumption) view for download / bundle / other items. /// Audio + video items currently render this too; dedicated templates land in /// Phases 2–3. #[derive(Template)] #[template(path = "pages/library_downloads.html")] #[allow(dead_code)] pub struct LibraryDownloadsTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub project_title: String, pub project_slug: String, pub host_url: Arc, pub versions: Vec, /// Child items if this is a bundle; otherwise empty. Children get `/l/` links /// because the viewer (by being on this page) has access via the bundle. pub bundle_items: Vec, pub sections: Vec, pub discussion_url: Option, pub discussion_count: Option, pub is_owner: bool, } /// 403 page shown when a viewer hits /l/{id} but lacks access. #[derive(Template)] #[template(path = "pages/library_locked.html")] #[allow(dead_code)] pub struct LibraryLockedTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub host_url: Arc, /// For unlisted items: bundles that contain this item. pub containing_bundles: Vec, pub is_logged_in: bool, } /// Library (consumption) view for text items — full article body, discussion. #[derive(Template)] #[template(path = "pages/library_text.html")] #[allow(dead_code)] pub struct LibraryTextTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub creator_display_name: Option, pub creator_avatar_initials: String, pub project_title: String, pub project_slug: String, /// Fully rendered article body HTML. pub body_html: Option, pub reading_time: Option, pub host_url: Arc, pub discussion_url: Option, pub discussion_count: Option, pub is_owner: bool, } /// Blog/article reader view. #[derive(Template)] #[template(path = "pages/text_reader.html")] #[allow(dead_code)] // Fields used by Askama template pub struct TextReaderTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub creator_display_name: Option, /// First-letter initials for the avatar circle (e.g. "JD" for "Jane Doe"). pub creator_avatar_initials: String, pub project_title: String, pub project_slug: String, /// Whether the item has a zero price (free content, no purchase required). pub is_free: bool, /// Whether the current user already has this item in their library. pub in_library: bool, /// Drives the CTA swap: true → "Read in library", false → Buy/PWYW/Add-to-Library. pub has_access: bool, pub reading_time: Option, /// Short plain-text preview of the article body, shown on the store page. pub excerpt: Option, /// Base URL for OG meta tags. pub host_url: Arc, /// URL to the MT discussion thread (None if no linked thread or MT unavailable). pub discussion_url: Option, /// Number of posts in the linked discussion thread. pub discussion_count: Option, } /// Library (consumption) view for audio items — full player, chapters, /// description, optional source-file downloads, discussion. #[derive(Template)] #[template(path = "pages/library_audio.html")] #[allow(dead_code)] pub struct LibraryAudioTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub creator_display_name: Option, pub creator_avatar_initials: String, pub project_title: Option, pub project_slug: String, pub audio_url: Option, pub chapters: Vec, pub segments_json: String, /// Source-file downloads if the creator offers them alongside the stream. pub versions: Vec, pub host_url: Arc, pub discussion_url: Option, pub discussion_count: Option, pub is_owner: bool, } /// Audio streaming player view. #[derive(Template)] #[template(path = "pages/audio_player.html")] pub struct AudioPlayerTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub creator_display_name: Option, /// First-letter initials for the avatar circle. pub creator_avatar_initials: String, pub project_title: Option, pub project_slug: String, /// Whether the item has a zero price. pub is_free: bool, /// Whether the current user already has this item in their library. pub in_library: bool, /// Drives the CTA swap: true → "View in library", false → Buy/PWYW or Add-to-Library. pub has_access: bool, /// Base URL for OG meta tags. pub host_url: Arc, /// URL to the MT discussion thread (None if no linked thread or MT unavailable). pub discussion_url: Option, /// Number of posts in the linked discussion thread. pub discussion_count: Option, } /// Library (consumption) view for video items — full player, chapters, /// description, optional source-file downloads, discussion. #[derive(Template)] #[template(path = "pages/library_video.html")] #[allow(dead_code)] pub struct LibraryVideoTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub creator_display_name: Option, pub creator_avatar_initials: String, pub project_title: Option, pub project_slug: String, pub video_url: Option, pub chapters: Vec, pub segments_json: String, pub versions: Vec, pub host_url: Arc, pub discussion_url: Option, pub discussion_count: Option, pub is_owner: bool, } /// Video player page with custom controls, insertions, chapters. #[derive(Template)] #[template(path = "pages/video_player.html")] pub struct VideoPlayerTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub item: Item, pub creator_username: String, pub creator_display_name: Option, pub creator_avatar_initials: String, pub project_title: Option, pub project_slug: String, pub is_free: bool, pub in_library: bool, pub has_access: bool, pub host_url: Arc, pub discussion_url: Option, pub discussion_count: Option, } /// Browse/discover page with filtering and pagination. #[derive(Template)] #[template(path = "pages/discover.html")] pub struct DiscoverTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub items: Vec, pub projects: Vec, /// Active browse mode: `"items"` or `"projects"`. pub mode: String, /// Item type facets for sidebar (e.g. text, audio, download). pub type_filters: Vec, /// Tag facets for sidebar (top tags by count). pub tag_filters: Vec, /// Project category facets for sidebar (projects mode only). pub category_filters: Vec, pub price_filters: Vec, pub total_items: u32, pub current_page: u32, pub total_pages: u32, pub search_query: String, /// Active sort key (e.g. `"most_sold"`, `"newest"`, `"price_asc"`). pub sort_by: String, /// Currently selected item_type filter, or empty for "All". pub current_type: String, /// Currently selected tag slug filter, or empty for "All". pub current_tag: String, /// Currently selected category slug filter, or empty for "All". pub current_category: String, /// Page numbers to render in the pagination bar. pub pagination_range: Vec, pub showing_start: u32, pub showing_end: u32, /// AI tier facets for sidebar (items mode only). pub ai_tier_filters: Vec, /// Currently selected AI tier filter slug, or empty for "All". pub current_ai_tier: String, /// Whether the "has source code" filter is active (projects mode). pub has_source: bool, /// Number of active filters (for mobile filter toggle badge). pub active_filter_count: u32, /// Whether the current user is authenticated (for collection save buttons in results). pub is_authenticated: bool, } /// Tag tree browser with breadcrumb navigation. #[derive(Template)] #[template(path = "pages/tag_tree.html")] pub struct TagTreeTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub categories: Vec, pub breadcrumbs: Vec, pub current_tag: Option, } /// Purchase confirmation page showing fee breakdown. #[derive(Template)] #[template(path = "pages/purchase.html")] pub struct PurchaseTemplate { pub csrf_token: CsrfTokenOption, pub item: Item, pub creator_username: String, pub stripe_fee: String, pub creator_receives: String, /// Pre-filled promo code from `?code=` query parameter. pub promo_code: String, /// Whether PWYW pricing is enabled for this item. pub pwyw_enabled: bool, /// Minimum price in cents when PWYW is enabled. pub pwyw_min_cents: i32, /// Formatted suggested price in dollars (e.g. "9.99"). pub suggested_price: String, /// Formatted minimum price in dollars (e.g. "1.00"). pub pwyw_min_dollars: String, /// Whether the creator has Stripe Tax enabled. pub stripe_tax_enabled: bool, /// Whether the current visitor is logged in (show guest checkout if not). pub is_logged_in: bool, /// If the buyer has an in-progress (pending) checkout for this item, /// the relative time it was started (e.g. "5 minutes ago"). Empty /// string means no pending checkout. pub pending_started: String, } /// Minimal direct purchase page — no navigation, for link-in-bio sharing. #[derive(Template)] #[template(path = "pages/buy.html")] pub struct BuyPageTemplate { pub item: Item, pub creator_username: String, pub creator_display_name: Option, pub pwyw_enabled: bool, pub pwyw_min_dollars: String, pub suggested_price: String, pub host_url: Arc, } /// Feed page showing items from followed users, projects, and tags. #[derive(Template)] #[template(path = "pages/feed.html")] pub struct FeedTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub items: Vec, pub total_items: u32, pub current_page: u32, pub total_pages: u32, pub pagination_range: Vec, pub showing_start: u32, pub showing_end: u32, } /// Public page: Stripe Connect disclaimer and terms before onboarding. #[derive(Template)] #[template(path = "pages/stripe_disclaimer.html")] pub struct StripeConnectDisclaimerTemplate { pub csrf_token: CsrfTokenOption, } // ============================================================================ // Fan+ // ============================================================================ /// Fan+ subscription page: marketing, subscribe, or manage. #[derive(Template)] #[template(path = "pages/fan_plus.html")] pub struct FanPlusTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, /// Whether the user has an active Fan+ subscription. pub is_subscribed: bool, /// Current billing period end (if subscribed). pub period_end: Option, /// Whether a `?subscribed=true` query was present (just subscribed). pub just_subscribed: bool, } // ============================================================================ // Blog Pages // ============================================================================ /// Public blog index for a project. #[derive(Template)] #[template(path = "pages/project_blog.html")] pub struct ProjectBlogTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub project: Project, pub creator_username: String, pub project_slug: String, pub posts: Vec, } /// Public blog post reader. #[derive(Template)] #[template(path = "pages/blog_post.html")] pub struct BlogPostTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub title: String, /// Title escaped for JSON string embedding (JSON-LD). pub title_json: String, pub body_html: String, pub published_at: String, pub creator_username: String, pub creator_display_name: Option, pub creator_avatar_initials: String, pub project_title: String, /// Project title escaped for JSON string embedding (JSON-LD). pub project_title_json: String, pub project_slug: String, /// URL-safe slug for this blog post. pub post_slug: String, /// Base URL for OG meta tags. pub host_url: Arc, /// Project cover image URL (fallback for og:image when blog post has no specific image). pub project_cover_image_url: Option, /// URL to the MT discussion thread (None if no linked thread or MT unavailable). pub discussion_url: Option, /// Number of posts in the linked discussion thread. pub discussion_count: Option, } // ============================================================================ // Documentation Pages // ============================================================================ /// Individual documentation page. #[derive(Template)] #[template(path = "pages/doc.html")] pub struct DocTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub title: String, pub section: String, pub content: String, } /// Entry in a doc section for the index page. pub struct DocSectionEntry { pub title: String, pub slug: String, } /// A collapsible subcategory within a doc section. pub struct DocSubsection { pub label: String, pub entries: Vec, } /// A group of doc entries under a section heading. pub struct DocSection { pub name: String, pub entries: Vec, pub subsections: Vec, } /// Documentation index page listing all docs by section. #[derive(Template)] #[template(path = "pages/doc_index.html")] pub struct DocIndexTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub sections: Vec, } // ============================================================================ // Pricing Calculator // ============================================================================ /// Interactive pricing calculator comparing MNW to competitors. #[derive(Template)] #[template(path = "pages/pricing.html")] pub struct PricingTemplate { pub csrf_token: CsrfTokenOption, pub tier_prices: crate::tier_prices::TierPrices, pub cost_allocation: crate::tier_prices::CostAllocation, } /// Platform economics + runway disclosure page. Renders at `/economics` /// (the retired markdown page's `/docs/economics` URL 301s here). See the /// doc comment on `templates/pages/economics.html` for the maintenance /// contract. #[derive(Template)] #[template(path = "pages/economics.html")] #[allow(dead_code)] // Fields used by Askama template pub struct EconomicsTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, /// `quarters` + `last_updated_iso` from `[runway]` in /// `assumptions.toml`. Operator-edited; refreshed quarterly. pub runway_config: crate::tier_prices::RunwayConfig, /// Live count of `status='active'` creator subscriptions. Pulled at /// request time so the page never lies about how many seats are /// revenue-bearing right now. pub paying_creators: i64, /// Live count of `status='trialing'` plus canceled-with-grace /// creators. Disclosed only when non-zero (the template hides the /// bullet otherwise so a quiet platform doesn't show "0 in trial"). pub trialing_or_grace: i64, } /// Use cases page showcasing creator types. #[derive(Template)] #[template(path = "pages/use_cases.html")] pub struct UseCasesTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub tier_prices: crate::tier_prices::TierPrices, } /// Team page listing the founder, residents, and fellows. #[derive(Template)] #[template(path = "pages/team.html")] pub struct TeamTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, } // ============================================================================ // Creator Invite System // ============================================================================ /// Public page: creator invite waves and waitlist status. #[derive(Template)] #[template(path = "pages/creators.html")] pub struct CreatorsTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub waves: Vec, pub total_creators: u32, pub waitlist_pending: u32, pub is_creator: bool, pub tier_prices: crate::tier_prices::TierPrices, } // ============================================================================ // Email & Account // ============================================================================ /// Public page: email action result (verification, unsubscribe, etc.). #[derive(Template)] #[template(path = "pages/email_result.html")] pub struct EmailResultTemplate { pub csrf_token: CsrfTokenOption, pub title: String, pub message: String, pub link_url: String, pub link_text: String, } /// Purchase receipt page. #[derive(Template)] #[template(path = "pages/receipt.html")] pub struct ReceiptTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub transaction_id: String, pub item_id: String, pub item_title: String, pub seller_username: String, pub amount: String, pub is_free: bool, pub status: String, pub date: String, } /// Confirmation page shown before account deletion (GET step). #[derive(Template)] #[template(path = "pages/confirm_delete.html")] pub struct ConfirmDeleteTemplate { pub csrf_token: CsrfTokenOption, pub user: String, pub expires: String, pub sig: String, } /// Public page: confirmation that account has been deleted. #[derive(Template)] #[template(path = "pages/account-deleted.html")] pub struct AccountDeletedTemplate { pub csrf_token: CsrfTokenOption, }