docs(tasks): update task artifacts for stats workflow
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
---
|
||||
id: TASK-161
|
||||
title: Current match full player summary and feed badges
|
||||
status: done
|
||||
type: backend
|
||||
team: Backend Senior
|
||||
supporting_teams:
|
||||
- Frontend Senior
|
||||
- Experto en interfaz
|
||||
roadmap_item: foundation
|
||||
priority: medium
|
||||
---
|
||||
|
||||
# TASK-161 - Current match full player summary and feed badges
|
||||
|
||||
## Goal
|
||||
|
||||
Ampliar el resumen de jugadores de `/api/current-match/players` para incluir a todos los participantes detectados de la partida actual y ajustar el feed de combate para mostrar nombre + facción en una sola línea sin cambiar su lógica funcional.
|
||||
|
||||
## Context
|
||||
|
||||
`frontend/partida-actual.html` ya expone feed de combate y tabla de estadísticas en vivo. El payload actual de jugadores depende solo de eventos `kill`, por lo que deja fuera jugadores conectados o vistos en la ventana de partida actual sin bajas registradas.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Revisar parser, storage y payloads actuales de `current match`.
|
||||
2. Ampliar la agregación backend usando eventos AdminLog y muestra live cuando exista.
|
||||
3. Ajustar el render del feed para reutilizar el patrón visual de cápsulas de facción.
|
||||
4. Validar el alcance con tests y checks pedidos.
|
||||
|
||||
## Files to Read First
|
||||
|
||||
- `AGENTS.md`
|
||||
- `ai/architecture-index.md`
|
||||
- `ai/repo-context.md`
|
||||
- `backend/app/rcon_admin_log_storage.py`
|
||||
- `backend/app/payloads.py`
|
||||
- `frontend/assets/js/partida-actual.js`
|
||||
|
||||
## Expected Files to Modify
|
||||
|
||||
- `backend/app/rcon_admin_log_storage.py`
|
||||
- `backend/app/payloads.py`
|
||||
- `backend/tests/test_current_match_payload.py`
|
||||
- `backend/tests/test_rcon_admin_log_storage.py`
|
||||
- `frontend/assets/js/partida-actual.js`
|
||||
- `frontend/assets/css/historico.css`
|
||||
|
||||
## Constraints
|
||||
|
||||
- No tocar histórico, rankings, retención, mantenimiento BDD ni export Gitea.
|
||||
- No cambiar endpoints ni contratos incompatibles del frontend.
|
||||
- No cambiar la lógica funcional del feed de combate.
|
||||
- Mantener la derivación principal de estadísticas desde AdminLog y ampliarla.
|
||||
|
||||
## Validation
|
||||
|
||||
- `python -m compileall backend/app`
|
||||
- `$env:PYTHONPATH = "backend"`
|
||||
- `python -m unittest backend.tests.test_current_match_payload`
|
||||
- `python -m unittest discover -s backend/tests -p "*current*"`
|
||||
- `python -m unittest discover -s backend/tests -p "*rcon*"`
|
||||
- `node --check frontend/assets/js/partida-actual.js`
|
||||
- `git diff --check`
|
||||
|
||||
## Outcome
|
||||
|
||||
- `backend/app/rcon_admin_log_storage.py` ahora construye el resumen vivo de participantes de la ventana actual combinando eventos `kill`, `connected`, `disconnected`, `team_switch`, `chat` y `message`, manteniendo las estadísticas de bajas derivadas desde `kill`.
|
||||
- `frontend/assets/js/partida-actual.js` y `frontend/assets/css/historico.css` ajustan solo el render visual del feed para mostrar nombre + cápsula en la misma línea reutilizando el badge de equipo de la tabla.
|
||||
- Validación ejecutada:
|
||||
- `python -m compileall backend/app`
|
||||
- `node --check frontend/assets/js/partida-actual.js`
|
||||
- `git diff --check`
|
||||
- Arnes Python local que ejecuta directamente las funciones `test_*` de `backend/tests/test_current_match_payload.py` y `backend/tests/test_rcon_admin_log_storage.py`
|
||||
- Observaciones de validación:
|
||||
- `python -m unittest backend.tests.test_current_match_payload` y `python -m unittest discover -s backend/tests -p "*current*"` no detectan tests porque esos módulos usan estilo `pytest`.
|
||||
- `python -m unittest discover -s backend/tests -p "*rcon*"` falla por un problema ajeno y preexistente en `test_rcon_materialization_pipeline`.
|
||||
- Limitación conocida: el helper live actual del repositorio expone conteos y marcador (`GetServerInformation`), pero no un roster nominal de jugadores; por tanto el resumen usa todas las señales nominales ya persistidas en AdminLog y no puede inventar jugadores silenciosos si nunca fueron vistos por ningún evento.
|
||||
|
||||
## Change Budget
|
||||
|
||||
- Preferir menos de 5 archivos si es viable; si no, mantener el cambio concentrado en current match.
|
||||
- Preferir cambios pequeños y verificables por tests.
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: TASK-161
|
||||
title: Define Stats section functional contract
|
||||
status: pending
|
||||
status: done
|
||||
type: documentation
|
||||
team: Analista
|
||||
supporting_teams:
|
||||
@@ -176,17 +176,21 @@ Before completing the task ensure:
|
||||
|
||||
## Outcome
|
||||
|
||||
Document:
|
||||
Completed as documentation-only.
|
||||
|
||||
- validation performed
|
||||
- files changed
|
||||
- notable decisions
|
||||
- recommended next tasks, likely:
|
||||
- add player stats search endpoint
|
||||
- add player personal stats endpoint
|
||||
- add annual leaderboard timeframe support
|
||||
- design annual ranking snapshot schema
|
||||
- add Stats frontend section
|
||||
- Created `docs/stats-section-functional-plan.md` as the focused contract for the future `Stats` section.
|
||||
- Kept the contract aligned with the current RCON-first materialized historical read model and the weekly/monthly window policy already present in `backend/app/rcon_historical_leaderboards.py`.
|
||||
- Defined the V1 user flow, player search contract, personal stats payload, ranking position rules and annual top 20 snapshot persistence direction.
|
||||
- Explicitly kept backend endpoints, database migrations and frontend implementation out of scope.
|
||||
|
||||
## Validation Result
|
||||
|
||||
- Verified `docs/stats-section-functional-plan.md` exists and is specific to HLL Vietnam.
|
||||
- Verified the document references the RCON-first materialized data direction.
|
||||
- Verified the document defines V1 scope, API contracts, payload expectations and annual snapshot direction.
|
||||
- Verified no backend implementation files were modified for this task.
|
||||
- Verified no frontend implementation files were modified for this task.
|
||||
- No automated or integration tests applied because this task is documentation-only.
|
||||
|
||||
## Change Budget
|
||||
|
||||
113
ai/tasks/pending/TASK-162-add-stats-player-search-endpoint.md
Normal file
113
ai/tasks/pending/TASK-162-add-stats-player-search-endpoint.md
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
id: TASK-162
|
||||
title: Add Stats player search endpoint (RCON materialized backend V1)
|
||||
status: pending
|
||||
type: backend
|
||||
team: Backend Senior
|
||||
supporting_teams: [Arquitecto de Base de Datos, Arquitecto Python]
|
||||
roadmap_item: foundation
|
||||
priority: medium
|
||||
---
|
||||
|
||||
# TASK-162 - Add Stats player search endpoint (RCON materialized backend V1)
|
||||
|
||||
## Goal
|
||||
|
||||
Implement a backend V1 endpoint to support player search for the future Stats section:
|
||||
`GET /api/stats/players/search?q=<query>`.
|
||||
|
||||
The endpoint must use existing RCON materialized tables and return search matches that include:
|
||||
|
||||
- `status`
|
||||
- `query`
|
||||
- `items`
|
||||
- `player_id`
|
||||
- `player_name`
|
||||
- `matches_considered`
|
||||
- `last_seen_at`
|
||||
- optional `servers_seen` only if it can be added with low scope cost
|
||||
|
||||
No frontend work, no migrations, no yearly ranking, no Elo/MMR workarounds, no Comunidad Hispana #03 reactivation.
|
||||
|
||||
## Context
|
||||
|
||||
The repository already has a RCON materialized model (`rcon_match_player_stats` + `rcon_materialized_matches`) used by leaderboard reads.
|
||||
The new endpoint is the first part of the Stats backend contract defined in `docs/stats-section-functional-plan.md`.
|
||||
It should reuse existing routing/payload conventions in the current backend bootstrap.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Inspect required files first (mandatory before implementation):
|
||||
- `AGENTS.md`
|
||||
- `ai/repo-context.md`
|
||||
- `ai/architecture-index.md`
|
||||
- `docs/stats-section-functional-plan.md`
|
||||
- `backend/app/rcon_historical_leaderboards.py`
|
||||
- `backend/app/main.py`
|
||||
- `backend/app/rcon_admin_log_materialization.py` (for table/schema context)
|
||||
2. Reuse the existing `routes.py` + `payloads.py` endpoint/payload pattern.
|
||||
3. Implement one new endpoint: `GET /api/stats/players/search`.
|
||||
4. Validate `q` parameter and optional `server` or `server_id` and `limit` handling using current project conventions.
|
||||
5. Query existing RCON materialized tables with read-only logic (preferably `rcon_match_player_stats` joined with `rcon_materialized_matches`).
|
||||
6. Keep response shape aligned with existing backend payload conventions.
|
||||
7. Add a minimal, self-contained module if needed (preferably `backend/app/rcon_historical_player_stats.py`).
|
||||
8. Run backend validation/checks and document outcome.
|
||||
|
||||
## Files to Read First
|
||||
|
||||
- `AGENTS.md`
|
||||
- `ai/repo-context.md`
|
||||
- `ai/architecture-index.md`
|
||||
- `docs/stats-section-functional-plan.md`
|
||||
- `backend/app/main.py`
|
||||
- `backend/app/routes.py`
|
||||
- `backend/app/payloads.py`
|
||||
- `backend/app/rcon_historical_leaderboards.py`
|
||||
- `backend/app/rcon_admin_log_materialization.py`
|
||||
|
||||
## Expected Files to Modify
|
||||
|
||||
- `backend/app/rcon_historical_player_stats.py` (new module preferred)
|
||||
- `backend/app/payloads.py`
|
||||
- `backend/app/routes.py`
|
||||
- `backend/app/main.py` (solo si se detecta una dependencia de export explícita o import requerido)
|
||||
|
||||
## Constraints
|
||||
|
||||
- Keep the change small and verifiable.
|
||||
- Keep frontend untouched.
|
||||
- No migrations.
|
||||
- No annual ranking implementation.
|
||||
- No Elo/MMR reactivation.
|
||||
- No Comunidad Hispana #03 behavior reintroduction.
|
||||
- No historical worker changes.
|
||||
- Do not modify `frontend/assets/js/partida-actual.js`.
|
||||
- Do not create unnecessary abstractions beyond this endpoint.
|
||||
- If `/ai/` is partially ignored by git-exclude, document traceability impact only.
|
||||
|
||||
## Validation
|
||||
|
||||
Before considering the task complete:
|
||||
|
||||
- Run `scripts/run-integration-tests.ps1`.
|
||||
- Run relevant backend tests if they exist for stats/player or historical materialized reads.
|
||||
- If no dedicated tests exist for this endpoint, run manual validation with `curl` or `Invoke-WebRequest`.
|
||||
- Validate `git diff --name-only` and check that only expected files were changed.
|
||||
- Move task to `ai/tasks/done` on completion, or to `ai/tasks/review` if human validation is required.
|
||||
|
||||
## Outcome (for worker)
|
||||
|
||||
Document:
|
||||
|
||||
- endpoint contract implemented and observed behavior
|
||||
- SQL/search strategy used (scope, match logic, ordering, fallback behavior)
|
||||
- validation command results
|
||||
- whether `servers_seen` was included and why
|
||||
- any schema/data limitations found
|
||||
- any blocker from git tracking (`/ai/` ignore behavior) and impact on traceability if relevant
|
||||
|
||||
## Change Budget
|
||||
|
||||
- Prefer <5 modified files.
|
||||
- Prefer <200 added/changed lines per file when feasible.
|
||||
- Split into follow-up tasks if the scope starts expanding.
|
||||
311
docs/stats-section-functional-plan.md
Normal file
311
docs/stats-section-functional-plan.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Stats Section Functional Plan
|
||||
|
||||
## Objective
|
||||
|
||||
Define the V1 functional contract for a future `Stats` section where a player can search their profile, review personal historical performance across HLL Vietnam community servers, and understand their weekly and monthly ranking position without implementing backend or frontend yet.
|
||||
|
||||
## Current Foundation
|
||||
|
||||
- HLL Vietnam historical read paths are RCON-first.
|
||||
- `backend/app/rcon_historical_leaderboards.py` already defines weekly and monthly leaderboard windows over materialized RCON/AdminLog match stats.
|
||||
- Current reliable materialized counters for public player-facing V1 are kills, deaths, teamkills and matches considered.
|
||||
- Public scoreboard data remains fallback or enrichment only; it must not become the normal primary source for this section while RCON coverage exists.
|
||||
|
||||
## V1 Scope
|
||||
|
||||
The first `Stats` contract covers:
|
||||
|
||||
- player search by name or player id
|
||||
- player detail view for one selected player
|
||||
- personal totals for a selected timeframe and server scope
|
||||
- weekly ranking position by kills
|
||||
- monthly ranking position by kills
|
||||
- data freshness and source metadata
|
||||
- annual top 20 preparation through a dedicated snapshot model
|
||||
|
||||
V1 should support two server scopes:
|
||||
|
||||
- one specific server id
|
||||
- `all` aggregated across active supported servers
|
||||
|
||||
## User Flow
|
||||
|
||||
1. User opens the future `Stats` section.
|
||||
2. User searches by partial player name or exact player id.
|
||||
3. Backend returns compact search matches ordered by best textual match and recent activity.
|
||||
4. User selects one player result.
|
||||
5. Frontend requests the player detail payload for a chosen server scope and timeframe.
|
||||
6. UI shows core totals, calculated ratios, weekly rank position and monthly rank position.
|
||||
7. UI may later request annual ranking snapshot data for the selected year without recalculating it on each public request.
|
||||
|
||||
## API Contract Direction
|
||||
|
||||
### 1. Player search
|
||||
|
||||
```http
|
||||
GET /api/stats/players/search?q=<query>&server_id=<server-or-all>&limit=10
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- resolve a user-entered name fragment or player id into selectable player records backed by materialized historical data
|
||||
|
||||
Query rules:
|
||||
|
||||
- `q` is required
|
||||
- `server_id` is optional and defaults to `all`
|
||||
- `limit` is optional and defaults to `10`, with a hard cap such as `20`
|
||||
|
||||
Response shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"data": {
|
||||
"query": "rambo",
|
||||
"server_id": "all",
|
||||
"items": [
|
||||
{
|
||||
"player_id": "76561198000000000",
|
||||
"player_name": "Rambo",
|
||||
"matches_considered": 42,
|
||||
"last_seen_at": "2026-06-06T21:40:00Z",
|
||||
"servers_seen": ["comunidad-hispana-01", "comunidad-hispana-02"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Search result notes:
|
||||
|
||||
- `player_id` is the canonical selection key for later requests
|
||||
- `player_name` is the latest display name known from materialized stats
|
||||
- `matches_considered` is the aggregated closed-match count in the selected scope
|
||||
- `servers_seen` is optional and may be omitted when `server_id` is not `all`
|
||||
|
||||
### 2. Personal player stats
|
||||
|
||||
```http
|
||||
GET /api/stats/players/{player_id}?server_id=<server-or-all>&timeframe=weekly|monthly|all
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- return the selected player summary for the chosen scope and timeframe plus ranking position context
|
||||
|
||||
Response shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"data": {
|
||||
"player_id": "76561198000000000",
|
||||
"player_name": "Rambo",
|
||||
"server_id": "all",
|
||||
"timeframe": "monthly",
|
||||
"window_start": "2026-06-01T00:00:00Z",
|
||||
"window_end": "2026-06-07T18:00:00Z",
|
||||
"matches_considered": 12,
|
||||
"kills": 356,
|
||||
"deaths": 241,
|
||||
"teamkills": 4,
|
||||
"kd_ratio": 1.48,
|
||||
"kills_per_match": 29.67,
|
||||
"deaths_per_match": 20.08,
|
||||
"weekly_ranking": {
|
||||
"metric": "kills",
|
||||
"ranking_position": 8,
|
||||
"window_kind": "current-week",
|
||||
"window_start": "2026-06-02T00:00:00Z",
|
||||
"window_end": "2026-06-07T18:00:00Z"
|
||||
},
|
||||
"monthly_ranking": {
|
||||
"metric": "kills",
|
||||
"ranking_position": 5,
|
||||
"window_kind": "current-month",
|
||||
"window_start": "2026-06-01T00:00:00Z",
|
||||
"window_end": "2026-06-07T18:00:00Z"
|
||||
},
|
||||
"source": {
|
||||
"primary_source": "rcon",
|
||||
"read_model": "rcon-materialized-admin-log-player-stats",
|
||||
"generated_at": "2026-06-07T18:02:00Z",
|
||||
"source_range_start": "2026-06-01T00:14:00Z",
|
||||
"source_range_end": "2026-06-07T17:51:00Z",
|
||||
"freshness": "runtime"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Field rules:
|
||||
|
||||
- `timeframe=weekly` returns the player totals within the same weekly window policy used by leaderboard reads
|
||||
- `timeframe=monthly` returns the player totals within the same monthly window policy used by leaderboard reads
|
||||
- `timeframe=all` returns all-time totals for the selected scope, but still includes weekly and monthly ranking blocks as separate comparative context
|
||||
- `kd_ratio`, `kills_per_match` and `deaths_per_match` should be rounded for display-safe payloads
|
||||
|
||||
### 3. Annual ranking snapshot
|
||||
|
||||
```http
|
||||
GET /api/stats/rankings/annual?year=<year>&server_id=<server-or-all>&metric=kills
|
||||
```
|
||||
|
||||
Purpose:
|
||||
|
||||
- return a precomputed annual top 20 leaderboard snapshot without recalculating the full year on each public request
|
||||
|
||||
Response shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"data": {
|
||||
"year": 2026,
|
||||
"server_id": "all",
|
||||
"metric": "kills",
|
||||
"generated_at": "2027-01-01T01:30:00Z",
|
||||
"snapshot_status": "ready",
|
||||
"items": [
|
||||
{
|
||||
"ranking_position": 1,
|
||||
"player_id": "76561198000000000",
|
||||
"player_name": "Rambo",
|
||||
"metric_value": 4210,
|
||||
"matches_considered": 148,
|
||||
"kills": 4210,
|
||||
"deaths": 2950,
|
||||
"teamkills": 18,
|
||||
"kd_ratio": 1.43
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
V1 annual endpoint notes:
|
||||
|
||||
- only `metric=kills` needs to be committed in the first annual design pass
|
||||
- annual read path should be snapshot-backed, not runtime full-range aggregation
|
||||
- if a requested year is not generated yet, respond with a controlled empty or pending state, not a slow public recalculation
|
||||
|
||||
## Ranking Calculation Rules
|
||||
|
||||
### Weekly position
|
||||
|
||||
- Use the same weekly window selected by `select_leaderboard_window(... timeframe="weekly")`
|
||||
- Respect the existing sufficient-sample policy:
|
||||
- use current week when the closed-match threshold is met
|
||||
- fall back to previous week when the current week sample is insufficient
|
||||
- Rank by total kills descending
|
||||
- Break ties by:
|
||||
- `matches_considered` descending
|
||||
- `player_name` ascending
|
||||
- Player position should be derived from the same aggregate query family as the public leaderboard to avoid contradictory ordering
|
||||
|
||||
### Monthly position
|
||||
|
||||
- Use the same monthly window selected by `select_leaderboard_window(... timeframe="monthly")`
|
||||
- Respect the current rule already present in the read model:
|
||||
- from day 1 to day 7, use previous month
|
||||
- from day 8 onward, use current month
|
||||
- Rank by total kills descending with the same tie-break rules used in weekly ranking
|
||||
|
||||
### Personal totals
|
||||
|
||||
- Use the same RCON materialized match/player stats source as leaderboard calculations
|
||||
- Only closed matches within the selected scope and selected timeframe window should count
|
||||
- Do not add support, offense, defense or weapon breakdown fields until they are proven reliable in the RCON materialized model
|
||||
|
||||
## Annual Snapshot Persistence Direction
|
||||
|
||||
Future implementation should add a dedicated persistence model instead of recomputing annual top 20 on every request.
|
||||
|
||||
### Table: `rcon_annual_ranking_snapshots`
|
||||
|
||||
Expected columns:
|
||||
|
||||
- `id`
|
||||
- `server_id`
|
||||
- `year`
|
||||
- `metric`
|
||||
- `generated_at`
|
||||
- `source_range_start`
|
||||
- `source_range_end`
|
||||
- `snapshot_status`
|
||||
- `item_count`
|
||||
- `generation_policy`
|
||||
- `notes` nullable
|
||||
|
||||
Expected uniqueness:
|
||||
|
||||
- unique on `(server_id, year, metric)`
|
||||
|
||||
Expected behavior:
|
||||
|
||||
- one authoritative snapshot row per year, scope and metric
|
||||
- regenerated only by an explicit batch job, scheduled maintenance command or year-close workflow
|
||||
|
||||
### Table: `rcon_annual_ranking_snapshot_items`
|
||||
|
||||
Expected columns:
|
||||
|
||||
- `id`
|
||||
- `snapshot_id`
|
||||
- `ranking_position`
|
||||
- `player_id`
|
||||
- `player_name`
|
||||
- `metric_value`
|
||||
- `matches_considered`
|
||||
- `kills`
|
||||
- `deaths`
|
||||
- `teamkills`
|
||||
- `kd_ratio`
|
||||
|
||||
Expected uniqueness:
|
||||
|
||||
- unique on `(snapshot_id, ranking_position)`
|
||||
- unique on `(snapshot_id, player_id)`
|
||||
|
||||
Expected behavior:
|
||||
|
||||
- store only the top 20 rows for the selected annual snapshot
|
||||
- preserve display-ready values used by the frontend
|
||||
- keep enough totals to show a concise annual card without joining additional tables at read time
|
||||
|
||||
### Generation policy
|
||||
|
||||
- Run after year close or through an explicit maintenance workflow
|
||||
- Read from materialized RCON historical data, not live RCON calls
|
||||
- Default scope should support both `all` and individual active servers
|
||||
- Prefer idempotent replace or upsert semantics for a given `(server_id, year, metric)`
|
||||
- Store `generated_at` and source range metadata for traceability
|
||||
|
||||
## V1 Non-goals
|
||||
|
||||
- Elo or MMR reactivation
|
||||
- Comunidad Hispana #03 reintroduction
|
||||
- support, combat, offense or defense ranking when not already reliable in materialized RCON stats
|
||||
- weapon-level breakdowns
|
||||
- charts, heatmaps or advanced visualizations
|
||||
- authenticated or private player profiles
|
||||
- backend endpoint implementation
|
||||
- database migrations
|
||||
- frontend page, components or scripts
|
||||
|
||||
## Future Extensions After V1
|
||||
|
||||
- map-by-map player breakdowns
|
||||
- server comparison cards per player
|
||||
- annual ranking filters beyond kills
|
||||
- profile history charts when a stable event or snapshot series exists
|
||||
- external links to trusted scoreboard detail when correlation is already validated
|
||||
|
||||
## Recommended Follow-up Tasks
|
||||
|
||||
- add player search endpoint over materialized RCON player stats
|
||||
- add personal player stats endpoint with weekly and monthly ranking context
|
||||
- design and implement annual ranking snapshot schema and generation command
|
||||
- extend frontend data consumption plan with the `Stats` section integration sequence
|
||||
- implement the `Stats` frontend section with static-safe fallback behavior
|
||||
Reference in New Issue
Block a user