package adminconsole import "html/template" // The *View types are the display models the gin handlers fill and the templates // render. Time values are pre-formatted to strings by the handlers so the // templates stay logic-free. // Pager is the shared list pagination state. type Pager struct { Page int PageSize int Total int HasPrev bool HasNext bool PrevPage int NextPage int } // NewPager builds the pagination state for a 1-based page of pageSize over total // items. func NewPager(page, pageSize, total int) Pager { if page < 1 { page = 1 } p := Pager{Page: page, PageSize: pageSize, Total: total, PrevPage: page - 1, NextPage: page + 1} p.HasPrev = page > 1 p.HasNext = page*pageSize < total return p } // VariantVersions lists the dictionary versions resident for one variant. type VariantVersions struct { Variant string Latest string Versions []string } // DashboardView is the landing-page summary. type DashboardView struct { Accounts int Games int ActiveGames int OpenComplaints int OpenFeedback int PendingChanges int // ActiveVersion is the dictionary version new games pin (the persisted active // version), distinct from the per-variant resident versions. ActiveVersion string Variants []VariantVersions } // UsersView is the paginated account list. type UsersView struct { Items []UserRow Pager Pager // Robots is the active people/robots toggle; NameMask/ExternalIDMask are the current // glob filters; FilterQuery is those URL-encoded for the pager links. It is an // already-escaped query fragment (url.Values.Encode), so it is typed template.URL to // be emitted verbatim — interpolated as a plain string it would have its "=" and "&" // percent-encoded again by the contextual escaper. Robots bool NameMask string ExternalIDMask string FilterQuery template.URL } // UserRow is one account row in the list. MoveMin/Avg/Max are the account's // pre-formatted move-duration summary (empty when it has no timed move); // FlaggedHighRate marks the soft high-rate badge. type UserRow struct { ID string DisplayName string Kind string Language string Guest bool FlaggedHighRate bool CreatedAt string HasMoveStats bool MoveMin string MoveAvg string MoveMax string } // MessagesView is the paginated chat-message moderation list. NameMask/ExtMask are the // current sender glob filters; GameID/UserID pin the list to one game / sender (set from a // game or user card); FilterQuery is the active filters URL-encoded for the pager and CSV // links — an already-escaped query fragment, hence template.URL so it is not re-encoded // inside the link (see UsersView.FilterQuery). type MessagesView struct { Items []MessageRow Pager Pager NameMask string ExtMask string GameID string UserID string FilterQuery template.URL } // MessageRow is one chat message in the moderation list: its sender (linked to the user // card), source, IP, body, game (linked to the game card) and time. type MessageRow struct { ID string SenderID string SenderName string Source string IP string Body string GameID string CreatedAt string } // UserDetailView is one account with its stats, identities and recent games. type UserDetailView struct { ID string DisplayName string Language string TimeZone string Guest bool NotificationsInAppOnly bool PaidAccount bool // MergedInto is the primary account id when this account has been retired by a // merge, or empty for a live account. MergedInto string // FlaggedHighRateAt is the pre-formatted soft high-rate marker timestamp, // empty for an unflagged account; the card shows it with the Clear action. FlaggedHighRateAt string HintBalance int // HintGrantMax is the per-grant cap the operator's "add hints" form enforces (it mirrors the // server's maxHintGrant), passed through so the policy value lives in one place. HintGrantMax int CreatedAt string HasStats bool Stats StatsRow Identities []IdentityRow Games []GameRow TelegramID string ConnectorEnabled bool // MoveChart is the pre-rendered inline SVG of the account's per-move-number think // time (min/mean/max), empty when the account has no timed move. MoveChart template.HTML // Suspension is the account's current manual-block state, shown with the block/unblock // form; Reasons is the operator-editable reason picklist offered in the block form. Suspension SuspensionView Reasons []ReasonOption // Roles is the account's current roles (each revocable); KnownRoles is the set the // grant form offers. The first role is the feedback ban (see internal/account). Roles []string KnownRoles []string } // SuspensionView is an account's current manual-block state shown on the user card: whether it // is blocked, whether the block is permanent, the pre-formatted expiry (empty when permanent), // when it was applied, and the reason snapshot in both languages (empty when none was cited). type SuspensionView struct { Blocked bool Permanent bool Until string BlockedAt string ReasonEn string ReasonRu string } // ReasonOption is one suspension-reason picklist entry offered in the block form's dropdown. type ReasonOption struct { ID string TextEn string TextRu string } // StatsRow is an account's lifetime statistics. type StatsRow struct { Wins int Losses int Draws int MaxGamePoints int MaxWordPoints int } // IdentityRow is one platform/email identity of an account. type IdentityRow struct { Kind string ExternalID string Confirmed bool CreatedAt string } // GameRow is one game row in a list. type GameRow struct { ID string Variant string Status string Players int UpdatedAt string } // GamesView is the paginated games list, optionally filtered by status. type GamesView struct { Items []GameRow Status string Pager Pager } // GameDetailView is one game with its seats. type GameDetailView struct { ID string Variant string DictVersion string Status string Players int ToMove int EndReason string MoveCount int CreatedAt string UpdatedAt string FinishedAt string Seats []SeatRow // HasRobot is true when any seat is a robot, gating the robot-target caption; // RobotTargetPct is the configured global play-to-win rate, in percent. HasRobot bool RobotTargetPct int } // SeatRow is one seat of a game. For a robot seat (IsRobot) RobotIntent is the game's // deterministic play-to-win decision ("play to win"/"play to lose"), and NextMove is the // scheduled next-move ETA shown only while it is that robot's turn in an active game. type SeatRow struct { Seat int DisplayName string AccountID string Score int HintsUsed int Winner bool IsRobot bool RobotIntent string NextMove string } // ComplaintsView is the paginated complaint review queue. type ComplaintsView struct { Items []ComplaintRow Status string Pager Pager } // ComplaintRow is one complaint row in the queue. type ComplaintRow struct { ID string Word string Variant string WasValid bool Status string Disposition string CreatedAt string } // ComplaintDetailView is one complaint with its resolution state and form. type ComplaintDetailView struct { ID string Word string Variant string DictVersion string WasValid bool Note string Status string Disposition string ResolutionNote string CreatedAt string ResolvedAt string GameID string Resolved bool } // DictionaryView lists the resident versions per variant, the active version new // games pin, and the pending wordlist changes from accepted complaints. type DictionaryView struct { // ActiveVersion is the dictionary version new games pin; the update form sets it. ActiveVersion string Variants []VariantVersions Changes []DictChangeRow } // DictChangeRow is one pending wordlist edit. type DictChangeRow struct { Variant string Word string Action string ResolvedAt string } // DictionaryPreviewView is the second step of a dictionary update: the version // parsed from the uploaded archive (editable before confirming), the staging token // that names the uploaded files on disk, the active version the diff is against, and // the per-variant word diff. type DictionaryPreviewView struct { Version string Token string ActiveVersion string Variants []VariantDiffRow } // VariantDiffRow summarises one variant's word diff in the update preview. // AddedCount/RemovedCount are the full totals; AddedSample/RemovedSample are the // first words shown (capped); AddedTruncated/RemovedTruncated mark a capped list; // LargeRemoval flags a removal large enough to warrant caution before confirming. type VariantDiffRow struct { Variant string AddedCount int RemovedCount int AddedSample []string RemovedSample []string AddedTruncated bool RemovedTruncated bool LargeRemoval bool } // BroadcastView is the operator-broadcast form page. type BroadcastView struct { ConnectorEnabled bool } // ThrottledView is the rate-limit observability page: the recent gateway-reported // throttle episodes (in-memory, reset on restart) and the accounts currently // carrying the high-rate flag. FlagThreshold and FlagWindow caption the active // auto-flag tuning. type ThrottledView struct { Episodes []ThrottleEpisodeRow Flagged []FlaggedAccountRow FlagThreshold int FlagWindow string } // ThrottleEpisodeRow is one recently throttled limiter key. UserID links to the // user card and is set only for the user class (the other classes key by IP). type ThrottleEpisodeRow struct { Class string Key string UserID string Rejected int FirstSeen string LastSeen string } // FlaggedAccountRow is one account carrying the high-rate flag. type FlaggedAccountRow struct { ID string DisplayName string FlaggedAt string } // ReasonsView is the suspension-reason picklist management page: every editable reason entry. type ReasonsView struct { Items []ReasonRow } // ReasonRow is one editable suspension-reason entry, with its English and Russian text. type ReasonRow struct { ID string TextEn string TextRu string CreatedAt string } // MessageView is the result page shown after a POST action. type MessageView struct { Heading string Body string Back string } // FeedbackView is the paginated user-feedback queue. Status is the active // unread/read/archived filter; NameMask/ExtMask are the sender glob filters; // UserID pins the list to one account (the per-user link from /users); // FilterQuery is the active filters URL-encoded for the pager links (already // escaped, hence template.URL — see UsersView.FilterQuery). type FeedbackView struct { Items []FeedbackRow Status string NameMask string ExtMask string UserID string Pager Pager FilterQuery template.URL } // FeedbackRow is one feedback message in the queue: its sender (linked to the user // card), source, channel, whether it has an attachment / a reply, its state and // time. type FeedbackRow struct { ID string AccountID string SenderName string Source string Channel string HasAttachment bool Read bool Replied bool Archived bool CreatedAt string } // FeedbackDetailView is one feedback message with its body, attachment, state and // the reply / archive / delete forms. Body, AttachmentName, SenderName and IP are // user-controlled and rendered as plain auto-escaped text. IsImage gates the inline // preview; Banned shows whether the sender already holds the feedback ban. type FeedbackDetailView struct { ID string AccountID string SenderName string Source string Channel string IP string Body string HasAttachment bool AttachmentName string IsImage bool Read bool Archived bool Replied bool ReplyBody string RepliedAt string CreatedAt string Banned bool }