//! Templates for HTMX partials: alerts, form status, tab content, tags. use std::sync::Arc; use askama::Template; use crate::db::{DbUserSession, UserSessionId}; use crate::types::*; use super::CsrfTokenOption; // ============================================================================ // HTMX Partials // ============================================================================ /// HTMX partial for discover page results. #[derive(Template)] #[template(path = "partials/discover_results.html")] pub struct DiscoverResultsTemplate { pub items: Vec, pub projects: Vec, pub mode: String, pub total_items: u32, pub current_page: u32, pub total_pages: u32, pub pagination_range: Vec, pub showing_start: u32, pub showing_end: u32, /// Currently selected category slug, for HTMX pagination to preserve. pub current_category: String, /// Whether the current user is authenticated (for collection save buttons). pub is_authenticated: bool, } /// HTMX partial: dismissible alert/notification banner. #[derive(Template)] #[template(path = "partials/alert.html")] pub struct AlertTemplate { pub alert_type: String, pub message: String, pub link_url: Option, pub link_text: Option, } impl AlertTemplate { pub fn new(alert_type: &str, message: &str) -> Self { Self { alert_type: alert_type.to_string(), message: message.to_string(), link_url: None, link_text: None, } } pub fn with_link(mut self, url: &str, text: &str) -> Self { self.link_url = Some(url.to_string()); self.link_text = Some(text.to_string()); self } } /// HTMX partial: success/error status message after form submission. #[derive(Template)] #[template(path = "partials/form_status.html")] pub struct FormStatusTemplate { pub success: bool, pub message: String, } impl FormStatusTemplate { pub fn render_string(&self) -> String { self.render().unwrap_or_else(|_| "".to_string()) } } /// HTMX partial: library action status message. #[derive(Template)] #[template(path = "partials/library_status.html")] pub struct LibraryStatusTemplate { pub message: String, } /// HTMX partial: data-URI download link for an export file. #[derive(Template)] #[template(path = "partials/export_download.html")] pub struct ExportDownloadTemplate { pub data_uri: String, pub filename: String, } /// HTMX partial: download button shown when a content export is ready. #[derive(Template)] #[template(path = "partials/export_content_ready.html")] pub struct ExportContentReadyTemplate { pub download_url: String, } /// HTMX partial: inline login error message. #[derive(Template)] #[template(path = "partials/login_error.html")] pub struct LoginErrorTemplate { pub message: String, } impl LoginErrorTemplate { pub fn render_string(&self) -> String { self.render().unwrap_or_else(|_| "Error".to_string()) } } /// HTMX partial: username availability check result. #[derive(Template)] #[template(path = "partials/username_status.html")] pub struct UsernameStatusTemplate { pub available: bool, } impl UsernameStatusTemplate { pub fn render_string(&self) -> String { self.render().unwrap_or_else(|_| "".to_string()) } } /// HTMX partial: project slug availability check result. #[derive(Template)] #[template(path = "partials/slug_status.html")] pub struct SlugStatusTemplate { pub available: bool, pub suggestions: Vec, } impl SlugStatusTemplate { pub fn render_string(&self) -> String { self.render().unwrap_or_else(|_| "".to_string()) } } /// HTMX partial: inline save confirmation or error indicator. #[derive(Template)] #[template(path = "partials/save_status.html")] pub struct SaveStatusTemplate { pub success: bool, pub message: String, } impl SaveStatusTemplate { pub fn render_string(&self) -> String { self.render().unwrap_or_else(|_| "".to_string()) } } /// HTMX partial: paginated transaction history table. #[derive(Template)] #[template(path = "partials/transactions_table.html")] pub struct TransactionsTableTemplate { pub transactions: Vec, } // ============================================================================ // Dashboard Tab Partials // ============================================================================ /// Dashboard tab: public identity — links, bio, domain, feed. #[derive(Template)] #[template(path = "partials/tabs/user_profile.html")] pub struct UserProfileTabTemplate { pub user: User, pub custom_links: Vec, /// HMAC-signed personal RSS feed URL for this user. pub feed_url: String, /// Whether this user has creator access (controls custom domain visibility). pub can_create_projects: bool, /// The user's custom domain, if configured. pub custom_domain: Option, } /// Dashboard settings meta-tab with sub-navigation (profile, account, plan, etc.) #[derive(Template)] #[template(path = "partials/tabs/user_settings.html")] pub struct UserSettingsTabTemplate { pub user: User, pub custom_links: Vec, pub feed_url: String, pub can_create_projects: bool, pub custom_domain: Option, pub has_media: bool, pub git_enabled: bool, pub has_mt_memberships: bool, } /// Dashboard tab: account mechanics — security, sessions, notifications, data. #[derive(Template)] #[template(path = "partials/tabs/user_account.html")] pub struct UserAccountTabTemplate { pub user: User, pub sessions: Vec, pub current_session_id: Option, /// Whether this user has creator access (controls creator-specific prefs). pub can_create_projects: bool, /// Whether the user's email address has been verified. pub email_verified: bool, /// Active (unresolved) moderation actions for the "Account Status" section. pub moderation_active: Vec, /// Resolved moderation history (collapsed by default). pub moderation_history: Vec, /// Whether this creator has voluntarily paused their account. pub creator_paused: bool, /// Compact Fan+ pane state for the account tab. `None` = the user is not /// a Fan+ subscriber; the tab renders a one-line "Support the platform" /// link instead of the active pane. pub fan_plus: Option, /// CSRF token for the Fan+ cancel/resume/billing-portal form posts. The /// rest of the tab uses HTMX (which sends `X-CSRF-Token` automatically), /// but these are vanilla form POSTs that redirect. pub csrf_token: super::CsrfTokenOption, } /// Compact dashboard view of the user's Fan+ subscription. Lives under the /// account tab; intentionally small, no upsell copy. pub struct FanPlusPaneView { /// Current period end as a formatted date (e.g., "Dec 14, 2026"). `None` /// when Stripe hasn't reported a period yet (rare; just after checkout). pub period_end: Option, /// Subscription is scheduled to cancel at `period_end`. Drives the Resume /// affordance. pub cancel_at_period_end: bool, } /// View model for a moderation action displayed on the settings page. pub struct ModerationActionView { /// Human-readable label (e.g., "Warning", "Content Removal", "Suspension") pub action_label: String, pub reason: String, pub created_at: String, pub resolved_at: Option, } /// Custom domain info for dashboard display. pub struct CustomDomainInfo { pub id: String, pub domain: String, pub verified: bool, pub verification_token: String, pub instructions: String, } /// Custom link with ID for dashboard editing #[derive(Clone)] pub struct CustomLinkWithId { pub id: String, pub url: String, pub title: String, } /// Dashboard tab: payment history, payouts, tips, and revenue splits. #[derive(Template)] #[template(path = "partials/tabs/user_payments.html")] pub struct UserPaymentsTabTemplate { pub user: User, pub payout_summary: Option, pub transactions: Vec, pub tips_received: Vec, pub tips_total: String, pub tips_count: i64, /// Revenue owed to you from other creators' projects (as a collaborator). pub splits_incoming_total: String, pub splits_incoming_count: i64, /// Revenue you owe to collaborators on your projects. pub splits_outgoing_total: String, /// Whether this user has creator access (controls seller section visibility). pub can_create_projects: bool, } /// Dashboard tab: user's projects list with create button. #[derive(Template)] #[template(path = "partials/tabs/user_projects.html")] pub struct UserProjectsTabTemplate { pub projects: Vec, pub can_create_projects: bool, } /// Creator tab in the user dashboard showing invite/waitlist status. #[derive(Template)] #[template(path = "partials/tabs/user_creator.html")] #[allow(dead_code)] // Fields used by Askama template pub struct UserCreatorTabTemplate { pub csrf_token: CsrfTokenOption, /// Whether this user has been granted creator privileges (can publish content). pub can_create_projects: bool, /// Whether the user's email is verified (required before joining waitlist). pub email_verified: bool, /// Number of unique followers who would receive a broadcast email. pub follower_count: i64, /// The user's waitlist entry, if they have applied for creator access. pub waitlist_entry: Option, /// Whether the invite system is enabled. pub invites_enabled: bool, /// Number of active (unredeemed) invite codes this creator has. pub active_invite_count: i64, /// Maximum number of unredeemed invite codes per creator. pub invite_limit: i64, /// Creator's invite codes for display. pub invite_codes: Vec, /// Total number of users with creator access. pub total_creators: u32, /// Number of waitlist entries still pending review. pub waitlist_pending: u32, /// Wave history for non-creator informational display. pub waves: Vec, /// The creator's current tier label (e.g. "Basic", "Small Files"), if subscribed. pub creator_tier_label: Option, /// The creator's subscription period end date (human-readable), if subscribed. pub creator_period_end: Option, /// The creator's subscription status (e.g. Active, PastDue), if subscribed. pub creator_sub_status: Option, /// Whether creator tier Stripe checkout is configured. pub creator_tiers_configured: bool, /// Storage usage breakdown (audio, covers, downloads, insertions, video, media). pub storage_audio: String, pub storage_covers: String, pub storage_downloads: String, pub storage_insertions: String, pub storage_video: String, pub storage_media: String, pub storage_total: String, pub storage_max: String, /// Storage usage percentage (0-100) for the progress bar. pub storage_pct: u8, /// Whether the founder pricing window is currently open. Used to badge /// in-progress founders ("Founder pricing — locked in when the window /// closes") before the close sweep stamps `founder_locked_at`. pub founder_window_open: bool, /// Whether this user holds an unsweep'd founder flag (subscribed during /// the window, status not yet finalized by the close sweep). pub is_founder: bool, /// Whether this user's founder pricing is permanently locked in. pub is_founder_locked: bool, /// Tier cards rendered in the upgrade grid. Built from `state.tier_prices` /// so a price change in `assumptions.toml` flows through automatically. pub tier_cards: Vec, } /// Dashboard tab: project overview with stat cards. #[derive(Template)] #[template(path = "partials/tabs/project_overview.html")] pub struct ProjectOverviewTabTemplate { pub stats: Vec, pub project_slug: String, pub stripe_connected: bool, pub has_items: bool, pub has_published_item: bool, } /// A soft-deleted item for the "Recently Deleted" section. pub struct DeletedItemRow { pub id: String, pub title: String, pub deleted_at: String, } /// Dashboard tab: project content items list. #[derive(Template)] #[template(path = "partials/tabs/project_content.html")] pub struct ProjectContentTabTemplate { pub items: Vec, pub deleted_items: Vec, pub project_slug: String, pub project_id: String, pub posts: Vec, } /// Dashboard tab: project analytics with stats, chart, and top items. #[derive(Template)] #[template(path = "partials/tabs/project_analytics.html")] pub struct ProjectAnalyticsTabTemplate { pub stats: Vec, pub bars: Vec, pub items: Vec, pub project_slug: String, pub active_range: String, } /// Dashboard tab: project settings, categories, labels, and features. #[derive(Template)] #[template(path = "partials/tabs/project_settings.html")] pub struct ProjectSettingsTabTemplate { pub project: Project, /// Current category name for pre-populating the form, or empty. pub category_name: String, /// Project ID as string for HTMX targets. pub project_id: String, /// Active features on this project. pub features: Vec, /// All available features as (value, label, description) tuples. pub project_features: &'static [(&'static str, &'static str, &'static str)], /// Tabbed markdown sections (privacy policy, terms, FAQ, etc). pub sections: Vec, /// Current pricing model as kebab string ("free", "buy_once", "pwyw", "subscription"). pub pricing_model: String, /// Current buy-once price in dollars (formatted), empty if not set. pub price_dollars: String, /// Current PWYW minimum in dollars (formatted), empty if not set. pub pwyw_min_dollars: String, } /// Dashboard code tab partial (git repos management). #[derive(Template)] #[template(path = "partials/tabs/project_code.html")] pub struct ProjectCodeTabTemplate { pub project: Project, pub git_enabled: bool, pub linked_repos: Vec, pub available_repos: Vec, pub project_id: String, } /// Dashboard tab: SyncKit apps for a specific project. #[derive(Template)] #[template(path = "partials/tabs/project_synckit.html")] pub struct ProjectSyncKitTabTemplate { pub apps: Vec, pub project_id: String, } /// A linked repo with its collaborators, for the Code tab. pub struct LinkedRepoView { pub id: String, pub name: String, pub collaborators: Vec, } /// View model for a repo collaborator in the Code tab. pub struct RepoCollaboratorView { pub user_id: String, pub username: String, pub can_push: bool, } /// Dashboard blog tab partial. #[derive(Template)] #[template(path = "partials/tabs/project_blog.html")] pub struct ProjectBlogTabTemplate { pub project_id: String, pub project_slug: String, pub posts: Vec, } /// Dashboard subscriptions tab partial for tier management. #[derive(Template)] #[template(path = "partials/tabs/project_subscriptions.html")] #[allow(dead_code)] // Fields used by Askama template pub struct ProjectSubscriptionsTabTemplate { pub project_id: String, pub project_slug: String, pub tiers: Vec, pub subscriber_count: i64, pub stripe_connected: bool, } /// Dashboard members tab partial for managing project members and revenue splits. #[derive(Template)] #[template(path = "partials/tabs/project_members.html")] #[allow(dead_code)] pub struct ProjectMembersTabTemplate { pub project_id: String, pub project_slug: String, pub members: Vec, pub owner_split: i64, } /// Combined monetization tab: tiers, promo codes, and team splits. #[derive(Template)] #[template(path = "partials/tabs/project_monetization.html")] pub struct ProjectMonetizationTabTemplate { pub project_id: String, pub project_slug: String, pub tiers: Vec, pub subscriber_count: i64, pub stripe_connected: bool, pub promo_codes: Vec, pub items: Vec, pub members: Vec, pub owner_split: i64, } /// SyncKit tab in the user dashboard for managing sync apps. #[derive(Template)] #[template(path = "partials/tabs/user_synckit.html")] pub struct UserSyncKitTabTemplate { pub apps: Vec, pub projects: Vec, } /// Row in the Forums tab showing a community membership. pub struct ForumMembership { pub community_name: String, pub profile_url: String, pub role: String, pub joined: String, pub post_count: i64, } /// Forums tab in the user dashboard — lists MT community memberships. #[derive(Template)] #[template(path = "partials/tabs/user_forums.html")] pub struct UserForumsTabTemplate { pub memberships: Vec, pub mt_base_url: String, } /// A media file row for the Media tab. pub struct MediaFileRow { pub id: String, pub folder: String, pub filename: String, pub content_type: String, pub file_size: String, pub media_type: String, pub cdn_url: String, pub markdown_ref: String, pub created_at: String, } /// Media tab in the user dashboard — media library with folders. #[derive(Template)] #[template(path = "partials/tabs/user_media.html")] pub struct UserMediaTabTemplate { pub files: Vec, pub folders: Vec, pub storage_display: String, } /// Support tab in the user dashboard — submit a support ticket. #[derive(Template)] #[template(path = "partials/tabs/user_support.html")] pub struct UserSupportTabTemplate { pub email: String, } // ============================================================================ // Library Tab Partials // ============================================================================ /// Library purchases tab. #[derive(Template)] #[template(path = "partials/tabs/library_purchases.html")] pub struct LibraryPurchasesTabTemplate { pub purchases: Vec, pub subscriptions: Vec, } /// Library collections tab. #[derive(Template)] #[template(path = "partials/tabs/library_collections.html")] pub struct LibraryCollectionsTabTemplate { pub collections: Vec, pub username: String, pub wishlists: Vec, } /// Library contacts tab. #[derive(Template)] #[template(path = "partials/tabs/library_contacts.html")] pub struct LibraryContactsTabTemplate { pub shared_creators: Vec, pub buyer_contacts: Vec, pub total_buyer_contacts: usize, } /// Library feed tab (items from followed users, projects, and tags). #[derive(Template)] #[template(path = "partials/tabs/library_feed.html")] pub struct LibraryFeedTabTemplate { 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, } /// Library communities tab (Multithreaded forum memberships). #[derive(Template)] #[template(path = "partials/tabs/library_communities.html")] pub struct LibraryCommunitiesTabTemplate { pub memberships: Vec, pub mt_base_url: String, } /// Per-project revenue for the user analytics top projects list. pub struct ProjectRevenue { pub title: String, pub revenue: String, } /// User-level analytics tab (aggregated across all projects). #[derive(Template)] #[template(path = "partials/tabs/user_analytics.html")] pub struct UserAnalyticsTabTemplate { pub stats: Vec, pub bars: Vec, pub top_projects: Vec, pub active_range: String, pub project_comparisons: Vec, } /// Buyer contacts section, HTMX-loaded into the Payments tab. #[derive(Template)] #[template(path = "partials/tabs/buyer_contacts.html")] pub struct BuyerContactsPartialTemplate { pub contacts: Vec, } /// SSH keys list partial for HTMX updates. #[derive(Template)] #[template(path = "partials/ssh_keys_list.html")] pub struct SshKeysListTemplate { pub ssh_keys: Vec, } /// Dashboard tab: SSH key management for git access. #[derive(Template)] #[template(path = "partials/tabs/user_ssh_keys_tab.html")] pub struct UserSshKeysTabTemplate { pub username: String, } /// Sessions list partial, used by session revocation API responses (HTMX swap into `#sessions-list`). #[derive(Template)] #[template(path = "partials/tabs/user_sessions.html")] pub struct UserSessionsPartialTemplate { pub sessions: Vec, pub current_session_id: Option, } /// HTMX partial: single removable tag pill on an item. #[derive(Template)] #[template(path = "partials/tag.html")] pub struct TagTemplate { pub item_id: String, pub tag_id: String, pub tag_name: String, pub is_primary: bool, } impl TagTemplate { pub fn render_string(&self) -> String { self.render().unwrap_or_else(|_| "".to_string()) } } /// HTMX partial: editable content item row in the project dashboard. #[derive(Template)] #[template(path = "partials/item_edit_row.html")] pub struct ItemEditRowTemplate { pub item: ContentItem, } /// HTMX partial: editable custom link row in the user details tab. #[derive(Template)] #[template(path = "partials/link_row.html")] pub struct LinkRowTemplate { pub id: String, pub title: String, pub url: String, } impl LinkRowTemplate { pub fn render_string(&self) -> String { self.render().unwrap_or_else(|_| "".to_string()) } } // ============================================================================ // Project Labels Partial // ============================================================================ // ============================================================================ // Admin Partials // ============================================================================ /// Admin HTMX partial: creator waitlist entries table. #[derive(Template)] #[template(path = "partials/admin_waitlist_entries.html")] pub struct AdminWaitlistEntriesTemplate { pub entries: Vec, } /// Admin user table rows (HTMX partial). #[derive(Template)] #[template(path = "partials/admin_user_entries.html")] pub struct AdminUserEntriesTemplate { pub users: Vec, pub current_page: i64, pub total_pages: i64, pub current_filter: String, } /// Admin upload review entries (HTMX partial). #[derive(Template)] #[template(path = "partials/admin_upload_entries.html")] pub struct AdminUploadEntriesTemplate { pub held_uploads: Vec, } /// Active-queue counts partial — refreshed by the admin dashboard every 10s. #[derive(Template)] #[template(path = "partials/admin_queue_summary.html")] pub struct AdminQueueSummaryTemplate { pub queue_pending: i64, pub queue_running: i64, } /// Admin appeal rows (HTMX partial). #[derive(Template)] #[template(path = "partials/admin_appeal_entries.html")] pub struct AdminAppealEntriesTemplate { pub appeals: Vec, } /// Admin report entries (HTMX partial). #[derive(Template)] #[template(path = "partials/admin_report_entries.html")] pub struct AdminReportEntriesTemplate { pub reports: Vec, } /// Suspension banner shown on dashboard for suspended users. #[derive(Template)] #[template(path = "partials/suspension_banner.html")] pub struct SuspensionBannerTemplate { pub reason: String, pub has_pending_appeal: bool, pub appeal_decision: Option, pub appeal_response: Option, } // ============================================================================ // Promo Code Partials // ============================================================================ /// Promo codes table partial (shared by project promotions tab, item dashboard, and API responses). #[derive(Template)] #[template(path = "partials/promo_codes_list.html")] pub struct PromoCodesListTemplate { pub promo_codes: Vec, } /// Promotions tab content for the project dashboard. #[derive(Template)] #[template(path = "partials/tabs/project_promotions.html")] pub struct ProjectPromotionsTabTemplate { pub project_id: String, pub project_slug: String, pub promo_codes: Vec, pub items: Vec, } // ============================================================================ // License Key Partials // ============================================================================ /// License keys table for the item dashboard. #[derive(Template)] #[template(path = "partials/item_license_keys.html")] pub struct ItemLicenseKeysTemplate { pub license_keys: Vec, } // ============================================================================ // Follow Button Partial // ============================================================================ /// HTMX follow/unfollow toggle button. #[derive(Template)] #[template(path = "partials/follow_button.html")] pub struct FollowButtonTemplate { pub target_type: String, pub target_id: String, pub is_following: bool, pub follower_count: i64, } // ============================================================================ // Tag Follow Toggle (compact, for discover sidebar) // ============================================================================ /// Compact follow/unfollow toggle for tags in the discover sidebar. #[derive(Template)] #[template(path = "partials/tag_follow_toggle.html")] pub struct TagFollowToggleTemplate { pub tag_id: String, pub is_following: bool, } // ============================================================================ // TOTP 2FA Partials // ============================================================================ /// TOTP setup partial: QR code, manual key, backup codes, and confirmation form. #[derive(Template)] #[template(path = "partials/totp_setup.html")] pub struct TotpSetupTemplate { pub qr_base64: String, pub secret_base32: String, pub backup_codes: Vec, } /// TOTP status partial: enabled/disabled state with action buttons. #[derive(Template)] #[template(path = "partials/totp_status.html")] pub struct TotpStatusTemplate { pub enabled: bool, } // ============================================================================ // Passkey Partials // ============================================================================ /// A registered passkey for dashboard display. pub struct PasskeyDisplay { pub id: String, pub name: String, pub created_at: String, pub last_used_at: Option, } /// Passkey list partial for the dashboard settings tab. #[derive(Template)] #[template(path = "partials/passkey_list.html")] pub struct PasskeyListTemplate { pub passkeys: Vec, } // ============================================================================ // Tag Suggestions // ============================================================================ /// A suggested tag for an item, based on metadata matching. #[derive(Clone)] pub struct TagSuggestion { pub id: String, pub name: String, } /// Auto-suggested tags for the item dashboard. #[derive(Template)] #[template(path = "partials/tag_suggestions.html")] pub struct TagSuggestionsTemplate { pub suggestions: Vec, } // ============================================================================ // Content Insertion Partials // ============================================================================ /// Item-level analytics partial (stats + revenue chart). #[derive(Template)] #[template(path = "partials/item_analytics.html")] pub struct ItemAnalyticsPartialTemplate { pub stats: Vec, pub bars: Vec, pub item_id: String, pub active_range: String, } /// An insertion clip for display in the dashboard. #[derive(Clone)] pub struct InsertionDisplay { pub id: String, pub title: String, pub media_type: String, pub duration_display: String, pub created_at: String, } /// Creator's insertion clip library list. #[derive(Template)] #[template(path = "partials/insertion_list.html")] pub struct InsertionListTemplate { pub insertions: Vec, } /// A placement for display in the item dashboard. #[derive(Clone)] pub struct PlacementDisplay { pub id: String, pub insertion_title: String, pub position: String, pub offset_display: Option, pub sort_order: i32, } /// Per-item placement management list. #[derive(Template)] #[template(path = "partials/placement_list.html")] pub struct PlacementListTemplate { pub item_id: String, pub placements: Vec, pub available_insertions: Vec, } // ============================================================================ // Item Dashboard Tab Partials // ============================================================================ /// Item overview tab: quick actions + analytics (lazy-loaded). #[derive(Template)] #[template(path = "partials/tabs/item_overview.html")] pub struct ItemOverviewTabTemplate { pub item: Item, } /// Item details tab: name, description, tags, content editor, bundle contents, sections. #[derive(Template)] #[template(path = "partials/tabs/item_details.html")] pub struct ItemDetailsTabTemplate { pub item: Item, pub bundle_items: Vec, pub bundleable_items: Vec, pub sections: Vec, } /// Item pricing tab: PWYW settings, license keys, promo codes. #[derive(Template)] #[template(path = "partials/tabs/item_pricing.html")] pub struct ItemPricingTabTemplate { pub item: Item, pub license_keys: Vec, pub promo_codes: Vec, pub license_preset_options: Vec<(&'static str, &'static str)>, } /// Item files tab: version upload + download table. #[derive(Template)] #[template(path = "partials/tabs/item_files.html")] pub struct ItemFilesTabTemplate { pub item: Item, pub versions: Vec, } /// Item sales tab: transaction history with refund actions. #[derive(Template)] #[template(path = "partials/tabs/item_sales.html")] pub struct ItemSalesTabTemplate { pub item: Item, pub sales: Vec, } /// Item embed tab: copy-paste embed codes for this item. #[derive(Template)] #[template(path = "partials/tabs/item_embed.html")] pub struct ItemEmbedTabTemplate { pub item: Item, pub host_url: Arc, pub is_audio: bool, }