-
-
Notifications
You must be signed in to change notification settings - Fork 19
Counter stats and update to v .7 #848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughAdds counter-statistics UI and widgets, i18n translations across multiple locales, URL-synced faction/type state in detailed stats, type changes for counters to PlayerReportCounters, a config patch bump to 2.1.7, and a small guideline note about adding keys when mapping components. Changes
Sequence Diagram(s)sequenceDiagram
participant Parent as PlayerCard / StandingsTab
participant Detailed as DetailedStatsTab
participant Inner as InnerDetailedStats
participant CounterCard as CounterStatisticsCard
participant CountersWgt as CountersWidget
Parent->>Detailed: pass playerStatsData, t
Detailed->>Detailed: read/sync faction & type from URL
Detailed->>Inner: pass stats, t
Inner->>CounterCard: pass counters (PlayerReportCounters), totalMatches, t
CounterCard->>CounterCard: compute KD/APM, format numbers, render Total & Average
Parent->>CountersWgt: pass playerStatsData, playerStandings, t
CountersWgt->>CountersWgt: compute typeWithMostGames, selectedType
CountersWgt->>CountersWgt: aggregate counters → averageCounters
CountersWgt->>Parent: emit navigation (changeView) with selectedType/faction
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (4)
screens/players/tabs/standings-tab/widgets/counters-widget.tsx (2)
70-88: Consider safer alternatives to non-null assertions.The code uses multiple non-null assertions (
!) when aggregating counters. While the aggregation initializes these fields, using non-null assertions can mask potential runtime errors if the data structure changes.Consider refactoring to avoid non-null assertions:
raceTypeArray.forEach((faction) => { const factionData = playerStatsData?.statGroups?.[selectedType]?.[faction]; if (factionData && factionData.counters) { const matches = factionData.w + factionData.l; totalMatches += matches; // Aggregate counters - aggregatedCounters.dmgdone! += factionData.counters.dmgdone || 0; - aggregatedCounters.ekills! += factionData.counters.ekills || 0; - aggregatedCounters.edeaths! += factionData.counters.edeaths || 0; + aggregatedCounters.dmgdone = (aggregatedCounters.dmgdone || 0) + (factionData.counters.dmgdone || 0); + aggregatedCounters.ekills = (aggregatedCounters.ekills || 0) + (factionData.counters.ekills || 0); + aggregatedCounters.edeaths = (aggregatedCounters.edeaths || 0) + (factionData.counters.edeaths || 0); // ... apply same pattern to other fields } });
189-223: Consider indicating the selected game type.The buttons for selecting game types (1v1, 2v2, 3v3, 4v4) don't visually indicate which type is currently selected, which could confuse users.
Add visual feedback for the selected type:
<Button variant="default" size={"compact-sm"} className={classes.mapsWidgetButton} onClick={() => setSelectedType("1v1")} + variant={selectedType === "1v1" ? "filled" : "default"} > 1 vs 1 </Button>Apply the same pattern to the other three buttons.
screens/players/tabs/detailed-stats-tab/inner-detailed-stats.tsx (1)
73-79: Simplifystats.countersguard or relax the typeGiven the prop type is
stats: { ...; counters: PlayerReportCounters } | null, a non‑nullstatsalways hascounters. The runtime guardstats.counters && <CounterStatisticsCard ...>is therefore redundant unlesscounterscan actually be missing.Either:
- Keep the invariant and drop the guard:
- {stats.counters && ( - <CounterStatisticsCard counters={stats.counters} totalMatches={totalMatches} t={t} /> - )} + <CounterStatisticsCard counters={stats.counters} totalMatches={totalMatches} t={t} />or
- If
counterscan be absent, update the type tocounters?: PlayerReportCounters | null;so TypeScript reflects reality.screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx (1)
37-91: KD/APM behavior under the “Average (per match)” columnIn
renderGroupedStat:
- KD: for both total and average columns, you effectively show the same ratio (
ekills / edeathsorekillswhen deaths are 0); the “Average (per match)” column doesn’t change the KD semantics.- APM: similarly, APM is computed from
totalcmdsandgtonly, so the total and average columns will show identical APM values.This is logically consistent (KD/APM are rate metrics, not aggregatable counts), but the “Average (per match)” heading may imply that KD/APM are also averaged per match.
If you want to avoid confusion, consider either:
- Showing KD/APM only in the Total column, or
- Adjusting the label (e.g., “Overall rates”) for these metrics in the Average column, or
- Explicitly documenting that KD/APM are global rates, not per‑match averages.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
.augment-guidelines(1 hunks)config.ts(3 hunks)public/locales/cs/players.json(1 hunks)public/locales/de/players.json(1 hunks)public/locales/en/players.json(1 hunks)public/locales/es/players.json(1 hunks)public/locales/ru/players.json(1 hunks)public/locales/zh-Hans/players.json(1 hunks)screens/players/index.tsx(1 hunks)screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx(1 hunks)screens/players/tabs/detailed-stats-tab/detailed-stats-tab.tsx(4 hunks)screens/players/tabs/detailed-stats-tab/inner-detailed-stats.tsx(5 hunks)screens/players/tabs/standings-tab/player-standings-tab.tsx(4 hunks)screens/players/tabs/standings-tab/widgets/counters-widget.tsx(1 hunks)src/coh3/coh3-types.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx (1)
src/coh3/coh3-types.ts (1)
PlayerReportCounters(205-252)
screens/players/tabs/detailed-stats-tab/detailed-stats-tab.tsx (1)
src/coh3/coh3-types.ts (5)
ProcessedCOHPlayerStats(440-490)raceTypeArray(4-4)raceType(6-6)leaderBoardTypeArray(8-8)leaderBoardType(10-10)
screens/players/tabs/standings-tab/widgets/counters-widget.tsx (2)
src/coh3/coh3-types.ts (6)
ProcessedCOHPlayerStats(440-490)InternalStandings(126-129)leaderBoardType(10-10)leaderBoardTypeArray(8-8)PlayerReportCounters(205-252)raceTypeArray(4-4)src/coh3/coh3-data.ts (1)
localizedGameTypes(333-338)
screens/players/tabs/detailed-stats-tab/inner-detailed-stats.tsx (1)
src/coh3/coh3-types.ts (3)
PlayerReportCounters(205-252)FactionSide(18-18)HistoricLeaderBoardStat(430-438)
🔇 Additional comments (19)
config.ts (4)
45-45: LGTM!The default patch selector has been correctly updated to "2.1.7" and matches a key in the statsPatchSelector object.
222-222: LGTM!The latestPatch has been correctly updated to "2.1.7" and matches a key in the patches object.
227-230: Fix the date format inconsistency.Line 229 uses "13/Nov/2025" with an abbreviated month name, while other entries in the patches object consistently use full month names (e.g., "11/Sep/2025" on Line 233, "31/July/2025" on Line 237). Please update to "13/November/2025" for consistency.
"2.1.7": { dataTag: "v2.1.5-1", // This is the tag of the data repo - dataTime: "13/Nov/2025", + dataTime: "13/November/2025", },Likely an incorrect or invalid review comment.
228-228: Verification complete—no action required.The dataTag "v2.1.5-1" exists in the coh3-data repository, and both "2.1.7" and "2.1.5" intentionally reference it. This pattern is consistent throughout the config where multiple patches share the same dataTag when there are no game data changes between them.
src/coh3/coh3-types.ts (1)
390-390: LGTM! Type refinement improves type safety.The change from
Record<string, number>toPlayerReportCountersprovides stronger typing for the counters field, ensuring consistency with the well-defined interface at lines 205-252.Also applies to: 481-481
.augment-guidelines (1)
9-9: LGTM! Useful guideline for React development.Adding a reminder about key props when iterating over arrays is a valuable best practice that helps prevent common React issues.
public/locales/zh-Hans/players.json (1)
248-297: LGTM! Comprehensive localization for counter statistics.The translation keys are well-structured and provide complete coverage for the counter statistics feature.
public/locales/de/players.json (1)
248-297: LGTM! German translations added consistently.The counterStatistics localization follows the same structure as other locales.
screens/players/tabs/standings-tab/player-standings-tab.tsx (2)
50-56: LGTM! URL state management enhanced cleanly.The optional
factionparameter allows passing additional context to the detailed stats view while maintaining backward compatibility.
111-116: LGTM! CountersWidget integration is correct.The widget is properly wired with all required props including the translation function and navigation handler.
public/locales/cs/players.json (1)
248-297: LGTM! Czech translations added consistently.The counterStatistics localization follows the established pattern across all locales.
screens/players/index.tsx (1)
263-263: LGTM! Translation function properly propagated.Passing the
tprop enables localization throughout the detailed stats tab.public/locales/en/players.json (1)
248-314: Counter statistics and counters widget strings look consistent
counterStatisticskeys (categories, stats, labels, includingstats.apm) align withCounterStatisticsCardusage, and thecounters.widgetblock appears internally consistent.screens/players/tabs/detailed-stats-tab/inner-detailed-stats.tsx (1)
4-50: Stats typing andtwiring align with shared typesUsing
PlayerReportCountersforstats.countersand addingt: TFunctiontoInnerDetailedStatsmatches the sharedProcessedCOHPlayerStatsdefinition and the new i18n flow. This keeps the component strongly typed and ready forCounterStatisticsCard.screens/players/tabs/detailed-stats-tab/detailed-stats-tab.tsx (2)
24-52: URL‑backed selection state is implemented correctlyUsing
raceTypeArrayandleaderBoardTypeArrayto validatequery.faction/query.typebefore seeding state, and syncing back from URL viauseEffect, gives deterministic behavior and tolerates invalid query params cleanly.
69-105: Verification script is misaligned with review contentThe shell script provided verifies that the
tprop is being passed toDetailedStatsTab, which is correct and unrelated to the review's suggestions. However, the review's actual recommendations—removing redundantdefaultValueprops and sanitizing invalid query parameters on mount—are optional refactoring suggestions that remain valid but were not verified by the script.The
Selectcomponents shown are indeed controlled (both havevalue={selectedFaction}andvalue={selectedGameMode}props), making thedefaultValueattributes redundant and safe to remove as suggested. The URL synchronization suggestion is also a reasonable enhancement for UX consistency.Likely an incorrect or invalid review comment.
screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx (3)
12-21: Number and average formatting are robust
formatNumber(rounded, localized) andformatAverage(division bytotalMatcheswith 0‑match guard) give sensible totals and per‑match values and avoid divide‑by‑zero issues.
93-172: Game time averaging and stat‑group rendering look correct
convertSecondsToReadableTimecorrectly converts seconds toxh ymand handles 0 gracefully.- For
gt, you compute per‑match average seconds before converting, which avoids double‑division.- Stat groups are only rendered when they contain at least one non‑zero stat, keeping the UI focused.
No functional issues here.
327-369: Card layout cleanly separates total vs. per‑match viewsThe main card header, total‑matches summary, and two‑column (Total vs Average) layout are easy to read, and the reuse of
statGroupsfor both columns keeps things DRY.
| }, | ||
| "counterStatistics": { | ||
| "title": "Estadísticas de Contadores", | ||
| "totalMatches": "Total de Partidas: {{count}}", | ||
| "total": "Total", | ||
| "average": "Promedio (por partida)", | ||
| "categories": { | ||
| "combat": "Combate", | ||
| "productionBuildings": "Producción y Edificios", | ||
| "abilitiesCommands": "Habilidades y Comandos", | ||
| "other": "Otros" | ||
| }, | ||
| "stats": { | ||
| "damageData": "Daño Causado", | ||
| "units": "Unidades", | ||
| "squads": "Escuadrones", | ||
| "vehicles": "Vehículos", | ||
| "structureDamage": "Daño a Estructuras", | ||
| "vehiclesAbandoned": "Vehículos Abandonados", | ||
| "buildings": "Edificios", | ||
| "unitsProduced": "Unidades Producidas", | ||
| "points": "Puntos", | ||
| "abilitiesUsed": "Habilidades Usadas", | ||
| "totalCommands": "Comandos Totales", | ||
| "upgrades": "Mejoras", | ||
| "requisition": "Requisición", | ||
| "power": "Energía", | ||
| "commandPointsEarned": "Puntos de Comando Ganados", | ||
| "entityReinforcements": "Refuerzos de Entidades", | ||
| "veterancy": "Veteranía", | ||
| "maxPopulation": "Población Máxima", | ||
| "gameTime": "Tiempo de Juego" | ||
| }, | ||
| "labels": { | ||
| "kills": "Bajas", | ||
| "deaths": "Muertes", | ||
| "kd": "KD", | ||
| "killed": "Eliminados", | ||
| "lost": "Perdidos", | ||
| "produced": "Producidos", | ||
| "captured": "Capturados", | ||
| "recaptured": "Recapturados", | ||
| "earned": "Ganados", | ||
| "spent": "Gastados", | ||
| "max": "Máx", | ||
| "squadXp": "XP de Escuadrón", | ||
| "squadRank": "Rango de Escuadrón", | ||
| "vehicleRank": "Rango de Vehículo" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Define counterStatistics.stats.apm for Spanish locale
CounterStatisticsCard calls t("counterStatistics.stats.apm"), but this key is not present here, so the label will fall back to another locale. To keep the UI fully localized, add a Spanish translation for counterStatistics.stats.apm (e.g., matching the style of other stats.* entries).
🤖 Prompt for AI Agents
In public/locales/es/players.json around lines 248 to 296, the Spanish locale is
missing the key counterStatistics.stats.apm which CounterStatisticsCard expects;
add an "apm" entry under the "stats" object (following the existing style, e.g.,
"apm": "APM" or a localized phrase like "Acciones por Minuto") so
t("counterStatistics.stats.apm") resolves to a Spanish string.
| }, | ||
| "counterStatistics": { | ||
| "title": "Статистика счетчиков", | ||
| "totalMatches": "Всего матчей: {{count}}", | ||
| "total": "Всего", | ||
| "average": "Среднее (за матч)", | ||
| "categories": { | ||
| "combat": "Бой", | ||
| "productionBuildings": "Производство и здания", | ||
| "abilitiesCommands": "Способности и команды", | ||
| "other": "Прочее" | ||
| }, | ||
| "stats": { | ||
| "damageData": "Нанесено урона", | ||
| "units": "Юниты", | ||
| "squads": "Отряды", | ||
| "vehicles": "Техника", | ||
| "structureDamage": "Урон по зданиям", | ||
| "vehiclesAbandoned": "Брошенная техника", | ||
| "buildings": "Здания", | ||
| "unitsProduced": "Произведено юнитов", | ||
| "points": "Точки", | ||
| "abilitiesUsed": "Использовано способностей", | ||
| "totalCommands": "Всего команд", | ||
| "upgrades": "Улучшения", | ||
| "requisition": "Реквизиция", | ||
| "power": "Энергия", | ||
| "commandPointsEarned": "Получено командных очков", | ||
| "entityReinforcements": "Подкрепления юнитов", | ||
| "veterancy": "Ветеранство", | ||
| "maxPopulation": "Макс. население", | ||
| "gameTime": "Время игры" | ||
| }, | ||
| "labels": { | ||
| "kills": "Убийств", | ||
| "deaths": "Смертей", | ||
| "kd": "КД", | ||
| "killed": "Убито", | ||
| "lost": "Потеряно", | ||
| "produced": "Произведено", | ||
| "captured": "Захвачено", | ||
| "recaptured": "Отбито", | ||
| "earned": "Получено", | ||
| "spent": "Потрачено", | ||
| "max": "Макс", | ||
| "squadXp": "Опыт отряда", | ||
| "squadRank": "Ранг отряда", | ||
| "vehicleRank": "Ранг техники" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing counterStatistics.stats.apm translation
CounterStatisticsCard uses t("counterStatistics.stats.apm"), but this locale does not define that key, so it will fall back to the default language. For consistency with en (and to avoid mixed-language UI), add a Russian translation for counterStatistics.stats.apm.
🤖 Prompt for AI Agents
In public/locales/ru/players.json around lines 248-296 the key
counterStatistics.stats.apm is missing which causes fallback to the default
locale; add the missing "apm" entry under counterStatistics.stats with an
appropriate Russian translation (for example "Действий в минуту" or similar) to
match the en locale and keep the UI fully localized.
| const statGroups = [ | ||
| { | ||
| title: t("counterStatistics.categories.combat"), | ||
| stats: [ | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.damageData"), | ||
| key: "dmgdone" as const, | ||
| }, | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.units"), | ||
| items: [ | ||
| { key: "ekills" as const, shortLabel: t("counterStatistics.labels.kills") }, | ||
| { key: "edeaths" as const, shortLabel: t("counterStatistics.labels.deaths") }, | ||
| { key: "kd" as const, shortLabel: t("counterStatistics.labels.kd") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.squads"), | ||
| items: [ | ||
| { key: "sqkill" as const, shortLabel: t("counterStatistics.labels.killed") }, | ||
| { key: "sqlost" as const, shortLabel: t("counterStatistics.labels.lost") }, | ||
| { key: "sqprod" as const, shortLabel: t("counterStatistics.labels.produced") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.vehicles"), | ||
| items: [ | ||
| { key: "vkill" as const, shortLabel: t("counterStatistics.labels.killed") }, | ||
| { key: "vlost" as const, shortLabel: t("counterStatistics.labels.lost") }, | ||
| { key: "vprod" as const, shortLabel: t("counterStatistics.labels.produced") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.structureDamage"), | ||
| key: "structdmg" as const, | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.vehiclesAbandoned"), | ||
| key: "vabnd" as const, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| title: t("counterStatistics.categories.productionBuildings"), | ||
| stats: [ | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.buildings"), | ||
| items: [ | ||
| { key: "bprod" as const, shortLabel: t("counterStatistics.labels.produced") }, | ||
| { key: "blost" as const, shortLabel: t("counterStatistics.labels.lost") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.unitsProduced"), | ||
| key: "unitprod" as const, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| title: t("counterStatistics.categories.abilitiesCommands"), | ||
| stats: [ | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.points"), | ||
| items: [ | ||
| { key: "pcap" as const, shortLabel: t("counterStatistics.labels.captured") }, | ||
| { key: "precap" as const, shortLabel: t("counterStatistics.labels.recaptured") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.abilitiesUsed"), | ||
| key: "abil" as const, | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.totalCommands"), | ||
| key: "totalcmds" as const, | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.apm"), | ||
| key: "apm" as const, | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.upgrades"), | ||
| key: "upg" as const, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| title: t("counterStatistics.categories.other"), | ||
| stats: [ | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.requisition"), | ||
| items: [ | ||
| { key: "reqearn" as const, shortLabel: t("counterStatistics.labels.earned") }, | ||
| { key: "reqspnt" as const, shortLabel: t("counterStatistics.labels.spent") }, | ||
| { key: "reqmax" as const, shortLabel: t("counterStatistics.labels.max") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.power"), | ||
| items: [ | ||
| { key: "powearn" as const, shortLabel: t("counterStatistics.labels.earned") }, | ||
| { key: "powspnt" as const, shortLabel: t("counterStatistics.labels.spent") }, | ||
| { key: "powmax" as const, shortLabel: t("counterStatistics.labels.max") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.commandPointsEarned"), | ||
| key: "cpearn" as const, | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.entityReinforcements"), | ||
| key: "erein" as const, | ||
| }, | ||
| { | ||
| type: "grouped" as const, | ||
| label: t("counterStatistics.stats.veterancy"), | ||
| items: [ | ||
| { key: "svetxp" as const, shortLabel: t("counterStatistics.labels.squadXp") }, | ||
| { key: "svetrank" as const, shortLabel: t("counterStatistics.labels.squadRank") }, | ||
| { key: "vvetrank" as const, shortLabel: t("counterStatistics.labels.vehicleRank") }, | ||
| ], | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.maxPopulation"), | ||
| key: "popmax" as const, | ||
| }, | ||
| { | ||
| type: "simple" as const, | ||
| label: t("counterStatistics.stats.gameTime"), | ||
| key: "gt" as const, | ||
| }, | ||
| ], | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Good mapping between stat groups and PlayerReportCounters, with one small simplification opportunity
The statGroups definitions:
- Use
PlayerReportCounterskeys that exist in the shared type (dmgdone,ekills,edeaths,sqkill,sqlost,sqprod,vkill,vlost,vprod,structdmg,vabnd,bprod,blost,unitprod,pcap,precap,abil,totalcmds,upg,reqearn,reqspnt,reqmax,powearn,powspnt,powmax,cpearn,erein,svetxp,svetrank,vvetrank,popmax,gt), so there’s no type mismatch. - Align i18n keys with locale JSON (
counterStatistics.categories.*,.stats.*,.labels.*).
One small cleanup: renderGroupedStat has special handling for "apm", but there’s currently no grouped stat that uses key: "apm"—APM is handled only as a simple stat. Unless you plan to add a grouped APM row, you can drop the "apm" branch from renderGroupedStat to reduce complexity.
🌐 Web query:
In Mantine and React 18, is using numeric values (e.g. `gap="4"`, `mb="4"`) on layout components like `Stack` and `Title` valid and idiomatic?
💡 Result:
Short answer: Yes — but use numbers (not quoted strings) or theme keys.
- Mantine's layout props (Stack's gap/spacing and the style props like m/mb/px) accept a MantineSpacing which can be a number, a theme spacing key ("xs" | "sm" | "md" | …) or any valid CSS value. [1][2].
- In JSX prefer gap={4} or mb={4} (numeric) or gap="md"/mb="md" (theme key). Using gap="4" (a string "4") is legal JS/JSX but ambiguous (it's a plain string, not a named theme key) and may not behave as you expect because plain "4" lacks units. Prefer numeric values or the theme keys for idiomatic code. [1][2]
Sources: Mantine Stack docs (gap/spacing) and Mantine Style Props. [1] [2]
🏁 Script executed:
#!/bin/bash
# Search for spacing props in counter-statistics-card.tsx
rg -n "(gap|mb|m|px|py|p)=\"[0-9]" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 87
🏁 Script executed:
#!/bin/bash
# Also search for any spacing prop usage to understand the context
rg -n -B2 -A2 "(gap|spacing|mb|mt|m[xy]?|p[xy]?|p=|b[rlt]?)=" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx | head -100Length of output: 1576
🏁 Script executed:
#!/bin/bash
# Look at the entire file to understand structure
cat screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx | head -400Length of output: 12558
🏁 Script executed:
#!/bin/bash
# Search specifically in the range mentioned (174-325)
sed -n '174,325p' screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 5356
🏁 Script executed:
#!/bin/bash
# Find renderGroupedStat function mentioned in review to check for "apm" handling
rg -n -B5 -A15 "renderGroupedStat|renderSimpleStat" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 3585
🏁 Script executed:
#!/bin/bash
# Check if "apm" appears anywhere in this file
rg -n "apm" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 775
🏁 Script executed:
#!/bin/bash
# Look for all grouped stat types to understand the pattern
rg -n "type.*grouped|type.*simple" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 943
🏁 Script executed:
#!/bin/bash
# Find where renderGroupedStat is actually called and what it does with "apm"
rg -n -B3 -A10 '"apm"' screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 3080
🏁 Script executed:
#!/bin/bash
# Check the complete renderGroupedStat function
ast-grep --pattern 'function renderGroupedStat($_) { $$$ }'Length of output: 0
🏁 Script executed:
#!/bin/bash
# Alternative: search for renderGroupedStat with rg to find exact location
rg -n "renderGroupedStat" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx -A 30Length of output: 2039
🏁 Script executed:
#!/bin/bash
# Check PlayerReportCounters type to confirm key existence
fd -type f "*.ts" -o -type f "*.tsx" | xargs grep -l "type PlayerReportCounters" | head -5Length of output: 170
🏁 Script executed:
#!/bin/bash
# Also verify the i18n structure for the mentioned keys
fd -type f "*.json" | xargs grep -l "counterStatistics" | head -5Length of output: 170
🏁 Script executed:
#!/bin/bash
# Find where keys like "apm", "dmgdone" might be used
rg -n "\"apm\"|\"dmgdone\"|\"kd\"" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 599
🏁 Script executed:
#!/bin/bash
# Check the actual structure with more context around line 174-325
wc -l screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 82
🏁 Script executed:
#!/bin/bash
# Expand the view of the file section
sed -n '150,350p' screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx | cat -nLength of output: 8270
🏁 Script executed:
#!/bin/bash
# Direct view of lines 174-325
sed -n '174,325p' screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx | nl -v 174Length of output: 40
🏁 Script executed:
#!/bin/bash
# Search for 'apm' in the entire codebase to understand usage
rg -n '"apm"' --type tsx --type tsLength of output: 27
🏁 Script executed:
#!/bin/bash
# Find renderGroupedStat and understand the apm handling
rg -n "apm" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx -B5 -A5Length of output: 3018
🏁 Script executed:
#!/bin/bash
# Check what the review says about apm more carefully - look for the renderGroupedStat implementation
rg -n -A50 "function renderGroupedStat" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 0
🏁 Script executed:
#!/bin/bash
# Look at the complete file context
head -50 screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx && echo "---" && tail -100 screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 5198
🏁 Script executed:
#!/bin/bash
# Try to find renderGroupedStat more explicitly
rg -n "renderGroupedStat\s*=" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx -A 30Length of output: 1204
🏁 Script executed:
#!/bin/bash
# Look for the const/function declarations
rg -n "const\s+(render|stat)" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx -B2 -A10Length of output: 2247
🏁 Script executed:
#!/bin/bash
# Search for apm within a 50-line window in render functions
rg -n "apm" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx -C10Length of output: 4416
🏁 Script executed:
#!/bin/bash
# Get broader context: lines 100-400
sed -n '100,400p' screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx | nl -v 100Length of output: 40
🏁 Script executed:
#!/bin/bash
# Check file length first to ensure valid range
wc -l < screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 3
🏁 Script executed:
#!/bin/bash
# If file is large, show structure: find function/const definitions
rg -n "^(const|function|export)" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx | head -20Length of output: 139
🏁 Script executed:
#!/bin/bash
# Look for apm anywhere in this file
grep -n "apm" screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 775
🏁 Script executed:
#!/bin/bash
# Show entire file with line numbers to understand context
nl screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx | head -500Length of output: 40
🏁 Script executed:
#!/bin/bash
# Just cat the file to see full content
cat screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsxLength of output: 12558
Fix spacing props to use idiomatic Mantine values; remove unreachable APM handling from renderGroupedStat
The statGroups mapping is correct. However, two improvements are needed:
-
Lines 166 & 169: Replace string numeric spacing (
mb="4",gap="4") with either numeric values (mb={4},gap={4}) or theme keys (mb="md",gap="md") for idiomatic Mantine usage. -
Lines 55–63 & 83–85 in
renderGroupedStat: APM is only defined as a simple stat instatGroups(line 264), never in grouped stat items. Remove the special"apm"handling branch fromrenderGroupedStatto reduce complexity.
🤖 Prompt for AI Agents
In
screens/players/tabs/detailed-stats-tab/components/counter-statistics-card.tsx
around lines 166–169 and within the renderGroupedStat function (remove the
unreachable branches at lines ~55–63 and ~83–85), replace non-idiomatic Mantine
spacing props like mb="4" and gap="4" with numeric or theme-key values (e.g.,
mb={4} or mb="md", gap={4} or gap="md"), and delete the special-case "apm"
handling in renderGroupedStat since APM is defined only as a simple stat in
statGroups; leave statGroups unchanged.
|
🌐 Branch deployed as preview to: |
Summary by CodeRabbit
New Features
Improvements
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.