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
UnreadOnly bool
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), time, and whether it is still
// unread by at least one recipient.
type MessageRow struct {
ID string
SenderID string
SenderName string
Source string
IP string
Body string
GameID string
CreatedAt string
Unread bool
}
// ChatMessageDetailView is one chat message with its per-seat read breakdown, for the
// console message card.
type ChatMessageDetailView struct {
ID string
GameID string
SenderID string
SenderName string
Source string
Kind string
Body string
IP string
CreatedAt string
Unread bool
Seats []ChatSeatStatusRow
}
// ChatSeatStatusRow is one seat's read status on the message card: the seat index, the
// occupant (linked to the user card) and its role ("sender", "read" or "unread").
type ChatSeatStatusRow struct {
Seat int
AccountID string
DisplayName string
Role 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
// Blocks, BlockedBy and Friends are the social graph on the card: who this account has
// blocked, who currently blocks it, and its mutual friendships โ each cross-linked to the
// other account with the date it happened. They are the full truth; the asymmetric block
// suppression that hides relationships from players never applies to the console.
Blocks []RelationRow
BlockedBy []RelationRow
Friends []RelationRow
}
// RelationRow is one cross-linked account in the user card's blocks / blocked-by / friends
// lists: the other account's id (the link target), its display name, and the pre-formatted date.
type RelationRow struct {
AccountID string
DisplayName string
Date 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
Moves int
HintsUsed 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
// VsAI marks an honest-AI game (rendered as ๐ค in the list's AI column).
VsAI bool
}
// 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
// VsAI marks an honest-AI game (shown as a ๐ค flag in the summary).
VsAI bool
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
// ReplayJSON is the game-replay payload (board, seats, per-step racks/scores/bag) the
// game_detail page feeds to its vanilla-JS stepper; HasReplay gates the replay section.
ReplayJSON template.JS
HasReplay bool
// SetupDraws is the first-move draw โ one row per tile drawn (docs/ARCHITECTURE.md ยง6) โ
// and FirstMover is the resolved name of the seat-0 player the draw elected.
SetupDraws []SetupDrawRow
FirstMover string
}
// SetupDrawRow is one tile drawn in the first-move seeding (docs/ARCHITECTURE.md ยง6): the
// round, the player (Name/AccountID, or "(opponent)" with an empty AccountID for an
// auto-match synthetic draw not yet back-filled), the drawn letter (upper-cased; "?" for a
// blank) and its draw rank.
type SetupDrawRow struct {
Round int
Name string
AccountID string
Letter string
Blank bool
Rank 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 temporary IP bans the
// gateway is currently enforcing, 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 {
Bans []BanRow
Episodes []ThrottleEpisodeRow
Flagged []FlaggedAccountRow
FlagThreshold int
FlagWindow string
}
// BanRow is one temporary IP ban the gateway is enforcing, with its reason and its
// since/expiry timestamps; the row carries an unban action.
type BanRow struct {
IP string
Reason string
Since string
Expires 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
}
// BannersView is the advertising-campaign list page.
type BannersView struct {
Items []BannerCampaignRow
}
// BannerCampaignRow is one campaign in the list. Window is a human-readable
// validity window ("perpetual" for the default); ActiveNow reports whether it
// would rotate right now (enabled and within its window).
type BannerCampaignRow struct {
ID string
Name string
Weight int
IsDefault bool
Enabled bool
Window string
Messages int
ActiveNow bool
}
// BannerDetailView is the campaign detail/edit page. StartsAt/EndsAt are the
// "YYYY-MM-DDTHH:MM" (UTC) values for the datetime-local inputs, empty when open.
type BannerDetailView struct {
ID string
Name string
Weight int
IsDefault bool
Enabled bool
StartsAt string
EndsAt string
Messages []BannerMessageRow
}
// BannerMessageRow is one bilingual message of a campaign. First/Last drive the
// reorder buttons (disabled at the ends).
type BannerMessageRow struct {
ID string
BodyEn string
BodyRu string
First bool
Last bool
}
// BannerSettingsView is the global display-timings form.
type BannerSettingsView struct {
HoldMs int
EdgePauseMs int
ScrollPxPerSec int
FadeOutMs int
GapMs int
FadeInMs int
}
// 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
// InterfaceLanguage is the sender's interface language (account preference).
InterfaceLanguage 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
}