mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-05 07:58:08 +01:00
5.5.1951
This commit is contained in:
parent
b029dc6c7a
commit
8b85936912
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "挂机时间"
|
||||
all: "全部"
|
||||
allTime: "所有时间"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "数据"
|
||||
average: "平均"
|
||||
averageActivePlaytime: "平均活跃时间"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "平均游玩时间"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "平均会话时长"
|
||||
averageSessions: "平均会话"
|
||||
averageTps: "平均 TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "已被封禁"
|
||||
bestPeak: "所有时间峰值"
|
||||
bestPing: "最低延迟"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "地理位置"
|
||||
hourByHour: "按小时查看"
|
||||
inactive: "不活跃"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "最后连接时间"
|
||||
lastPeak: "上次在线峰值"
|
||||
lastSeen: "最后在线时间"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " 游玩时长"
|
||||
links: "链接"
|
||||
loadedChunks: "已加载区块"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "单独新玩家加入"
|
||||
longestSession: "最长会话时间"
|
||||
lowTpsSpikes: "低 TPS 时间"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "最大可用硬盘空间"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "最小可用硬盘空间"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "新"
|
||||
newPlayerRetention: "新玩家留坑率"
|
||||
newPlayers: "新玩家"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "昵称"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "现在"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "玩家"
|
||||
playersOnline: "在线玩家"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "在线活动总览"
|
||||
playtime: "游玩时间"
|
||||
plugins: "插件"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "服务器页面"
|
||||
serverPlaytime: "服务器游戏时间"
|
||||
serverPlaytime30days: "最近 30 天内的服务器游玩时间"
|
||||
serverSelector: "Server selector"
|
||||
servers: "服务器"
|
||||
serversTitle: "服务器"
|
||||
session: "会话次数"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "平均会话长度"
|
||||
sessionStart: "会话开始于"
|
||||
sessions: "会话"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "主题选择"
|
||||
thirdDeadliestWeapon: "第三致命的 PVP 武器"
|
||||
thirtyDays: "30 天"
|
||||
thirtyDaysAgo: "30 天前"
|
||||
timesKicked: "被踢出次数"
|
||||
toMainPage: "回到主页面"
|
||||
total: "Total"
|
||||
totalActive: "总活跃时长"
|
||||
totalAfk: "总挂机时长"
|
||||
totalPlayers: "总玩家数"
|
||||
totalPlayersOld: "总游玩时长"
|
||||
totalPlaytime: "总游玩时间"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "趋势"
|
||||
trends30days: "30 天趋势"
|
||||
uniquePlayers: "独立玩家"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "非常活跃"
|
||||
weekComparison: "每周对比"
|
||||
weekdays: "'星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'"
|
||||
|
@ -269,29 +269,33 @@ html:
|
||||
generic:
|
||||
none: "Žádný"
|
||||
label:
|
||||
active: "Aktivní"
|
||||
activePlaytime: "Aktivní herní čas"
|
||||
activityIndex: "Index aktivity"
|
||||
afk: "AFK"
|
||||
afkTime: "AFK čas"
|
||||
all: "Vše"
|
||||
allTime: "Celkově"
|
||||
asNumbers: "statistiky"
|
||||
average: "Průměr"
|
||||
averageActivePlaytime: "Průměrná herní aktivita"
|
||||
averageAfkTime: "Průměrný AFK čas"
|
||||
averageChunks: "Průměr chunků"
|
||||
averageCpuUsage: "Average CPU Usage"
|
||||
averageEntities: "Průměr entit"
|
||||
averageKdr: "Průměr KDR"
|
||||
averageMobKdr: "Průměr Mob KDR"
|
||||
averagePing: "Průměrný ping"
|
||||
active: "Aktivní"
|
||||
activePlaytime: "Aktivní herní čas"
|
||||
activityIndex: "Index aktivity"
|
||||
afk: "AFK"
|
||||
afkTime: "AFK čas"
|
||||
all: "Vše"
|
||||
allTime: "Celkově"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "statistiky"
|
||||
average: "Průměr"
|
||||
averageActivePlaytime: "Průměrná herní aktivita"
|
||||
averageAfkTime: "Průměrný AFK čas"
|
||||
averageChunks: "Průměr chunků"
|
||||
averageCpuUsage: "Average CPU Usage"
|
||||
averageEntities: "Průměr entit"
|
||||
averageKdr: "Průměr KDR"
|
||||
averageMobKdr: "Průměr Mob KDR"
|
||||
averagePing: "Průměrný ping"
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Průměr herní doby"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Průměrná délka relace"
|
||||
averageSessions: "Průměrná relace"
|
||||
averageTps: "Průměr TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Zabanován"
|
||||
bestPeak: "Nejvíce hráčů"
|
||||
bestPing: "Nejlepší ping"
|
||||
@ -308,26 +312,32 @@ html:
|
||||
dayOfweek: "Den týdne"
|
||||
deadliestWeapon: "Nejsmrtelnější PvP zbraň"
|
||||
deaths: "Smrti"
|
||||
disk: "Místo na disku"
|
||||
diskSpace: "Volné místo na disku"
|
||||
downtime: "Offline doba"
|
||||
duringLowTps: "Při nízkých TPS:"
|
||||
entities: "Entity"
|
||||
favoriteServer: "Oblíbený server"
|
||||
firstSession: "První relace"
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geolocations: "Geolokace"
|
||||
hourByHour: "Hodina po hodině"
|
||||
inactive: "Neaktivní"
|
||||
indexInactive: "Neaktivní"
|
||||
indexRegular: "Pravidelný"
|
||||
information: "INFORMACE"
|
||||
insights: "Insights"
|
||||
insights30days: "Postřehy za 30 dní"
|
||||
irregular: "Nepravidelný"
|
||||
joinAddress: "Join Address"
|
||||
disk: "Místo na disku"
|
||||
diskSpace: "Volné místo na disku"
|
||||
downtime: "Offline doba"
|
||||
duringLowTps: "Při nízkých TPS:"
|
||||
entities: "Entity"
|
||||
favoriteServer: "Oblíbený server"
|
||||
firstSession: "První relace"
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Geolokace"
|
||||
hourByHour: "Hodina po hodině"
|
||||
inactive: "Neaktivní"
|
||||
indexInactive: "Neaktivní"
|
||||
indexRegular: "Pravidelný"
|
||||
information: "INFORMACE"
|
||||
insights: "Insights"
|
||||
insights30days: "Postřehy za 30 dní"
|
||||
irregular: "Nepravidelný"
|
||||
joinAddress: "Join Address"
|
||||
joinAddresses: "Připojovací IP"
|
||||
kdr: "KDR"
|
||||
killed: "Zabit"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Poslední připojení"
|
||||
lastPeak: "Naposledy nejvíce hráčů"
|
||||
lastSeen: "Naposledy viděn"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Délka"
|
||||
links: "ODKAZY"
|
||||
loadedChunks: "Načtené chunky"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Samotná připojení nováčků"
|
||||
longestSession: "Nejdelší relace"
|
||||
lowTpsSpikes: "Nejnižší TPS"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Max. volného disku"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Min. volného disku"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Nový"
|
||||
newPlayerRetention: "Udržení nových hráčů"
|
||||
newPlayers: "Noví hráči"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Přezdívka"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Nyní"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Hráči"
|
||||
playersOnline: "Hráči online"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Přehled online aktivity"
|
||||
playtime: "Herní čas"
|
||||
plugins: "Pluginy"
|
||||
@ -420,30 +434,36 @@ html:
|
||||
serverOverview: "Přehled serveru"
|
||||
serverPage: "Stránka serveru"
|
||||
serverPlaytime: "Herní čas serveru"
|
||||
serverPlaytime30days: "Herní čas serveru za 30 dní"
|
||||
servers: "Servery"
|
||||
serversTitle: "SERVERY"
|
||||
session: "Relace"
|
||||
sessionCalendar: "Session Calendar"
|
||||
sessionEnded: " Ukončeno"
|
||||
sessionMedian: "Střední hodnota relací"
|
||||
sessionStart: "Započatá relace"
|
||||
sessions: "Relace"
|
||||
themeSelect: "Zvolené téma"
|
||||
thirdDeadliestWeapon: "3. PvP Zbraň"
|
||||
thirtyDays: "30 dní"
|
||||
thirtyDaysAgo: "před 30 dny"
|
||||
timesKicked: "Počet vykopnutí"
|
||||
toMainPage: "Zpět na hlavní stránku"
|
||||
totalActive: "Celková aktivita"
|
||||
totalAfk: "Celkem AFK"
|
||||
totalPlayers: "Hráčů celkem"
|
||||
serverPlaytime30days: "Herní čas serveru za 30 dní"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Servery"
|
||||
serversTitle: "SERVERY"
|
||||
session: "Relace"
|
||||
sessionCalendar: "Session Calendar"
|
||||
sessionEnded: " Ukončeno"
|
||||
sessionMedian: "Střední hodnota relací"
|
||||
sessionStart: "Započatá relace"
|
||||
sessions: "Relace"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Zvolené téma"
|
||||
thirdDeadliestWeapon: "3. PvP Zbraň"
|
||||
thirtyDays: "30 dní"
|
||||
thirtyDaysAgo: "před 30 dny"
|
||||
timesKicked: "Počet vykopnutí"
|
||||
toMainPage: "Zpět na hlavní stránku"
|
||||
total: "Total"
|
||||
totalActive: "Celková aktivita"
|
||||
totalAfk: "Celkem AFK"
|
||||
totalPlayers: "Hráčů celkem"
|
||||
totalPlayersOld: "Celkem hráčů"
|
||||
totalPlaytime: "Herní doba celkem"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Trend"
|
||||
trends30days: "Trendy za 30 dní"
|
||||
uniquePlayers: "Unikátní hráči"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Velmi aktivní"
|
||||
weekComparison: "Týdenní srovnání"
|
||||
weekdays: "'Pondělí', 'Úterý', 'Středa', 'Čtvrtek', 'Pátek', 'Sobota', 'Neděle'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "AFK Zeit"
|
||||
all: "Gesamt"
|
||||
allTime: "Gesamte zeit"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "als Zahlen"
|
||||
average: "Durchschnitt"
|
||||
averageActivePlaytime: "Durchschnittliche aktive Spielzeit"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Durschnittliche Spielzeit"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Durschnittliche Sitzungslänge"
|
||||
averageSessions: "Durchschnittliche Sessions"
|
||||
averageTps: "Durschnittliche TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Gebannt"
|
||||
bestPeak: "Rekord"
|
||||
bestPing: "Bester Ping"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Geolocations"
|
||||
hourByHour: "Hour by Hour"
|
||||
inactive: "Inaktiv"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Letzte Verbindung"
|
||||
lastPeak: "Letzter Höchststand"
|
||||
lastSeen: "Zuletzt gesehen"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Länge"
|
||||
links: "LINKS"
|
||||
loadedChunks: "Geladene Chunks"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Lone newbie joins"
|
||||
longestSession: "Längste Sitzung"
|
||||
lowTpsSpikes: "Low TPS Spitzen"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Max Freier Speicher"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Min Freier Speicher"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Neu"
|
||||
newPlayerRetention: "Erhaltung neuer Spieler"
|
||||
newPlayers: "Neue Spieler"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Nickname"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Jetzt"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Spieler"
|
||||
playersOnline: "Spieler Online"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Online Aktivitätsübersicht"
|
||||
playtime: "Spielzeit"
|
||||
plugins: "Plugins"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Server Seite"
|
||||
serverPlaytime: "Server Spielzeit"
|
||||
serverPlaytime30days: "Server Spielzeit für 30 Tage"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Server"
|
||||
serversTitle: "SERVER"
|
||||
session: "Sitzung"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Sitzungsdurchschnitt"
|
||||
sessionStart: "Sitzung gestartet"
|
||||
sessions: "Sitzungen"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Thema ausgewählt"
|
||||
thirdDeadliestWeapon: "3. PvP Waffe"
|
||||
thirtyDays: "30 Tage"
|
||||
thirtyDaysAgo: "30 Tage vorher"
|
||||
timesKicked: "Mal gekickt"
|
||||
toMainPage: "zur Hauptseite"
|
||||
total: "Total"
|
||||
totalActive: "Gesamte Aktive Spielzeit"
|
||||
totalAfk: "Gesamte AFK-Zeit"
|
||||
totalPlayers: "Gesamte Spieler"
|
||||
totalPlayersOld: "Gesamte Spieler"
|
||||
totalPlaytime: "Gesamte Spielzeit"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Trend"
|
||||
trends30days: "Trends für 30 Tage"
|
||||
uniquePlayers: "Einzigartige Spieler"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Sehr aktiv"
|
||||
weekComparison: "Wochenvergleich"
|
||||
weekdays: "'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'"
|
||||
|
@ -269,29 +269,33 @@ html:
|
||||
generic:
|
||||
none: "None"
|
||||
label:
|
||||
active: "Active"
|
||||
activePlaytime: "Active Playtime"
|
||||
activityIndex: "Activity Index"
|
||||
afk: "AFK"
|
||||
afkTime: "AFK Time"
|
||||
all: "All"
|
||||
allTime: "All Time"
|
||||
asNumbers: "as Numbers"
|
||||
average: "Average"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
averageAfkTime: "Average AFK Time"
|
||||
averageChunks: "Average Chunks"
|
||||
averageCpuUsage: "Average CPU Usage"
|
||||
averageEntities: "Average Entities"
|
||||
averageKdr: "Average KDR"
|
||||
averageMobKdr: "Average Mob KDR"
|
||||
averagePing: "Average Ping"
|
||||
active: "Active"
|
||||
activePlaytime: "Active Playtime"
|
||||
activityIndex: "Activity Index"
|
||||
afk: "AFK"
|
||||
afkTime: "AFK Time"
|
||||
all: "All"
|
||||
allTime: "All Time"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "as Numbers"
|
||||
average: "Average"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
averageAfkTime: "Average AFK Time"
|
||||
averageChunks: "Average Chunks"
|
||||
averageCpuUsage: "Average CPU Usage"
|
||||
averageEntities: "Average Entities"
|
||||
averageKdr: "Average KDR"
|
||||
averageMobKdr: "Average Mob KDR"
|
||||
averagePing: "Average Ping"
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Average Playtime"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Average Session Length"
|
||||
averageSessions: "Average Sessions"
|
||||
averageTps: "Average TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Banned"
|
||||
bestPeak: "All Time Peak"
|
||||
bestPing: "Best Ping"
|
||||
@ -308,26 +312,32 @@ html:
|
||||
dayOfweek: "Day of the Week"
|
||||
deadliestWeapon: "Deadliest PvP Weapon"
|
||||
deaths: "Deaths"
|
||||
disk: "Disk space"
|
||||
diskSpace: "Free Disk Space"
|
||||
downtime: "Downtime"
|
||||
duringLowTps: "During Low TPS Spikes:"
|
||||
entities: "Entities"
|
||||
favoriteServer: "Favorite Server"
|
||||
firstSession: "First session"
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geolocations: "Geolocations"
|
||||
hourByHour: "Hour by Hour"
|
||||
inactive: "Inactive"
|
||||
indexInactive: "Inactive"
|
||||
indexRegular: "Regular"
|
||||
information: "INFORMATION"
|
||||
insights: "Insights"
|
||||
insights30days: "Insights for 30 days"
|
||||
irregular: "Irregular"
|
||||
joinAddress: "Join Address"
|
||||
disk: "Disk space"
|
||||
diskSpace: "Free Disk Space"
|
||||
downtime: "Downtime"
|
||||
duringLowTps: "During Low TPS Spikes:"
|
||||
entities: "Entities"
|
||||
favoriteServer: "Favorite Server"
|
||||
firstSession: "First session"
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Geolocations"
|
||||
hourByHour: "Hour by Hour"
|
||||
inactive: "Inactive"
|
||||
indexInactive: "Inactive"
|
||||
indexRegular: "Regular"
|
||||
information: "INFORMATION"
|
||||
insights: "Insights"
|
||||
insights30days: "Insights for 30 days"
|
||||
irregular: "Irregular"
|
||||
joinAddress: "Join Address"
|
||||
joinAddresses: "Join Addresses"
|
||||
kdr: "KDR"
|
||||
killed: "Killed"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Last Connected"
|
||||
lastPeak: "Last Peak"
|
||||
lastSeen: "Last Seen"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Length"
|
||||
links: "LINKS"
|
||||
loadedChunks: "Loaded Chunks"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Lone newbie joins"
|
||||
longestSession: "Longest Session"
|
||||
lowTpsSpikes: "Low TPS Spikes"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Max Free Disk"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Min Free Disk"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "New"
|
||||
newPlayerRetention: "New Player Retention"
|
||||
newPlayers: "New Players"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Nickname"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Now"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Players"
|
||||
playersOnline: "Players Online"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Online Activity Overview"
|
||||
playtime: "Playtime"
|
||||
plugins: "Plugins"
|
||||
@ -420,30 +434,36 @@ html:
|
||||
serverOverview: "Server Overview"
|
||||
serverPage: "Server page"
|
||||
serverPlaytime: "Server Playtime"
|
||||
serverPlaytime30days: "Server Playtime for 30 days"
|
||||
servers: "Servers"
|
||||
serversTitle: "SERVERS"
|
||||
session: "Session"
|
||||
sessionCalendar: "Session Calendar"
|
||||
sessionEnded: " Ended"
|
||||
sessionMedian: "Session Median"
|
||||
sessionStart: "Session Started"
|
||||
sessions: "Sessions"
|
||||
themeSelect: "Theme Select"
|
||||
thirdDeadliestWeapon: "3rd PvP Weapon"
|
||||
thirtyDays: "30 days"
|
||||
thirtyDaysAgo: "30 days ago"
|
||||
timesKicked: "Times Kicked"
|
||||
toMainPage: "to main page"
|
||||
totalActive: "Total Active"
|
||||
totalAfk: "Total AFK"
|
||||
totalPlayers: "Total Players"
|
||||
serverPlaytime30days: "Server Playtime for 30 days"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Servers"
|
||||
serversTitle: "SERVERS"
|
||||
session: "Session"
|
||||
sessionCalendar: "Session Calendar"
|
||||
sessionEnded: " Ended"
|
||||
sessionMedian: "Session Median"
|
||||
sessionStart: "Session Started"
|
||||
sessions: "Sessions"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Theme Select"
|
||||
thirdDeadliestWeapon: "3rd PvP Weapon"
|
||||
thirtyDays: "30 days"
|
||||
thirtyDaysAgo: "30 days ago"
|
||||
timesKicked: "Times Kicked"
|
||||
toMainPage: "to main page"
|
||||
total: "Total"
|
||||
totalActive: "Total Active"
|
||||
totalAfk: "Total AFK"
|
||||
totalPlayers: "Total Players"
|
||||
totalPlayersOld: "Total Players"
|
||||
totalPlaytime: "Total Playtime"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Trend"
|
||||
trends30days: "Trends for 30 days"
|
||||
uniquePlayers: "Unique Players"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Very Active"
|
||||
weekComparison: "Week Comparison"
|
||||
weekdays: "'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'"
|
||||
|
@ -269,29 +269,33 @@ html:
|
||||
generic:
|
||||
none: "Nada"
|
||||
label:
|
||||
active: "Activo"
|
||||
activePlaytime: "Tiempo de juego activo"
|
||||
activityIndex: "Índice de actividad"
|
||||
afk: "AFK"
|
||||
afkTime: "Tiempo AFK"
|
||||
all: "Todo"
|
||||
allTime: "Todo el tiempo"
|
||||
asNumbers: "como números"
|
||||
average: "Promedio"
|
||||
averageActivePlaytime: "Tiempo de juego activo promedio"
|
||||
averageAfkTime: "Tiempo AFK promedio"
|
||||
averageChunks: "Chunks Promedio"
|
||||
averageCpuUsage: "Average CPU Usage"
|
||||
averageEntities: "Entidades promedio"
|
||||
averageKdr: "KDR promedio"
|
||||
averageMobKdr: "KDR de mobs promedio"
|
||||
averagePing: "Ping promedio"
|
||||
active: "Activo"
|
||||
activePlaytime: "Tiempo de juego activo"
|
||||
activityIndex: "Índice de actividad"
|
||||
afk: "AFK"
|
||||
afkTime: "Tiempo AFK"
|
||||
all: "Todo"
|
||||
allTime: "Todo el tiempo"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "como números"
|
||||
average: "Promedio"
|
||||
averageActivePlaytime: "Tiempo de juego activo promedio"
|
||||
averageAfkTime: "Tiempo AFK promedio"
|
||||
averageChunks: "Chunks Promedio"
|
||||
averageCpuUsage: "Average CPU Usage"
|
||||
averageEntities: "Entidades promedio"
|
||||
averageKdr: "KDR promedio"
|
||||
averageMobKdr: "KDR de mobs promedio"
|
||||
averagePing: "Ping promedio"
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Jugabilidad promedio"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Duración de sesion promedio"
|
||||
averageSessions: "Sesiones promedio"
|
||||
averageTps: "TPS promedio"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Baneado"
|
||||
bestPeak: "Mejor pico"
|
||||
bestPing: "Mejor Ping"
|
||||
@ -308,26 +312,32 @@ html:
|
||||
dayOfweek: "Dia de la semana"
|
||||
deadliestWeapon: "Arma PvP más mortal"
|
||||
deaths: "Muertes"
|
||||
disk: "Espacio del disco"
|
||||
diskSpace: "Espacio libre en el disco duro"
|
||||
downtime: "Falta de tiempo"
|
||||
duringLowTps: "Durante picos bajos de TPS:"
|
||||
entities: "Entidades"
|
||||
favoriteServer: "Servidor favorito"
|
||||
firstSession: "Primera sesión"
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geolocations: "Geolocalizaciones"
|
||||
hourByHour: "Hora a Hora"
|
||||
inactive: "Inactivo"
|
||||
indexInactive: "Inactivo"
|
||||
indexRegular: "Regular"
|
||||
information: "INFORMACIÓN"
|
||||
insights: "Insights"
|
||||
insights30days: "Ideas por 30 días"
|
||||
irregular: "Irregular"
|
||||
joinAddress: "Join Address"
|
||||
disk: "Espacio del disco"
|
||||
diskSpace: "Espacio libre en el disco duro"
|
||||
downtime: "Falta de tiempo"
|
||||
duringLowTps: "Durante picos bajos de TPS:"
|
||||
entities: "Entidades"
|
||||
favoriteServer: "Servidor favorito"
|
||||
firstSession: "Primera sesión"
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Geolocalizaciones"
|
||||
hourByHour: "Hora a Hora"
|
||||
inactive: "Inactivo"
|
||||
indexInactive: "Inactivo"
|
||||
indexRegular: "Regular"
|
||||
information: "INFORMACIÓN"
|
||||
insights: "Insights"
|
||||
insights30days: "Ideas por 30 días"
|
||||
irregular: "Irregular"
|
||||
joinAddress: "Join Address"
|
||||
joinAddresses: "Direcciones de entrada"
|
||||
kdr: "KDR"
|
||||
killed: "Muerto"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Última vez conectado"
|
||||
lastPeak: "Último pico"
|
||||
lastSeen: "Última vez visto"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Duración"
|
||||
links: "LINKS"
|
||||
loadedChunks: "Chunks cargados"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Uniones solitarias nuevas"
|
||||
longestSession: "Sesión más larga"
|
||||
lowTpsSpikes: "Picos bajos TPS"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Máximo espacio libre en el disco duro"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Mínimo espacio libre en el disco duro"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Nuevo"
|
||||
newPlayerRetention: "Retención nuevo jugador"
|
||||
newPlayers: "Jugadores nuevos"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Nick"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Ahora"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Jugadores"
|
||||
playersOnline: "Jugadores en línea"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Vista general de la actividad online"
|
||||
playtime: "Tiempo de juego"
|
||||
plugins: "Plugins"
|
||||
@ -420,30 +434,36 @@ html:
|
||||
serverOverview: "Vista general del servidor"
|
||||
serverPage: "Página del servidor"
|
||||
serverPlaytime: "Jugabilidad en números"
|
||||
serverPlaytime30days: "Jugabilidad de 30 días"
|
||||
servers: "Servidores"
|
||||
serversTitle: "SERVERS"
|
||||
session: "Sesión"
|
||||
sessionCalendar: "Session Calendar"
|
||||
sessionEnded: " Acabado"
|
||||
sessionMedian: "Sesión media."
|
||||
sessionStart: "Sesión iniciada"
|
||||
sessions: "Sesiones"
|
||||
themeSelect: "Selección de tema"
|
||||
thirdDeadliestWeapon: "3ª arma PvP"
|
||||
thirtyDays: "30 días"
|
||||
thirtyDaysAgo: "Hace 30 días"
|
||||
timesKicked: "Veces kickeado"
|
||||
toMainPage: "hasta la página principal"
|
||||
totalActive: "Activos totales"
|
||||
totalAfk: "AFK total"
|
||||
totalPlayers: "Jugadores totales"
|
||||
serverPlaytime30days: "Jugabilidad de 30 días"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Servidores"
|
||||
serversTitle: "SERVERS"
|
||||
session: "Sesión"
|
||||
sessionCalendar: "Session Calendar"
|
||||
sessionEnded: " Acabado"
|
||||
sessionMedian: "Sesión media."
|
||||
sessionStart: "Sesión iniciada"
|
||||
sessions: "Sesiones"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Selección de tema"
|
||||
thirdDeadliestWeapon: "3ª arma PvP"
|
||||
thirtyDays: "30 días"
|
||||
thirtyDaysAgo: "Hace 30 días"
|
||||
timesKicked: "Veces kickeado"
|
||||
toMainPage: "hasta la página principal"
|
||||
total: "Total"
|
||||
totalActive: "Activos totales"
|
||||
totalAfk: "AFK total"
|
||||
totalPlayers: "Jugadores totales"
|
||||
totalPlayersOld: "Jugadores totales"
|
||||
totalPlaytime: "Jugabilidad total"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Tendencia"
|
||||
trends30days: "Tendencias de 30 días"
|
||||
uniquePlayers: "Jugadores únicos"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Muy activo"
|
||||
weekComparison: "Comparación semanal"
|
||||
weekdays: "'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "Aika AFK:ina"
|
||||
all: "Kaikki"
|
||||
allTime: "Kaikkien aikojen"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "Numeroina"
|
||||
average: "Keskimäräinen"
|
||||
averageActivePlaytime: "Keskimäräinen Aktiivinen peliaika"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Keskimäräinen peliaika"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Keskimäräinen istunnon pituus"
|
||||
averageSessions: "Keskimääräinen Sessiomäärä"
|
||||
averageTps: "Keskimäräinen TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Pannassa"
|
||||
bestPeak: "Paras Huippu"
|
||||
bestPing: "Paras Vasteaika"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Sijainnit"
|
||||
hourByHour: "Tunnittainen katsaus"
|
||||
inactive: "Inaktiivinen"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Viimeisin yhteys"
|
||||
lastPeak: "Viimeisin huippu"
|
||||
lastSeen: "Nähty Viimeksi"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Pituus"
|
||||
links: "LINKIT"
|
||||
loadedChunks: "Ladatut Chunkit"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Yksinäiset uusien pelaajien liittymiset"
|
||||
longestSession: "Pisin istunto"
|
||||
lowTpsSpikes: "Matalan TPS:n piikit"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Maksimi vapaa levytila"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Minimi vapaa levytila"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Uudet"
|
||||
newPlayerRetention: "Uusien pysyvyys"
|
||||
newPlayers: "Uusia pelaajia"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Lempinimi"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Nyt"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Pelaajia"
|
||||
playersOnline: "Pelaajia Paikalla"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Yhteenveto Paikallaolosta"
|
||||
playtime: "Peliaika"
|
||||
plugins: "Lisäosat"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Palvelin"
|
||||
serverPlaytime: "Palvelimen peliaika"
|
||||
serverPlaytime30days: "Palvelimen peliaika 30 päivälle"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Palvelimet"
|
||||
serversTitle: "PALVELIMET"
|
||||
session: "Istunto"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Istuntojen Mediaani"
|
||||
sessionStart: "Istunto alkoi"
|
||||
sessions: "Istunnot"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Teemavalikko"
|
||||
thirdDeadliestWeapon: "3. PvP Ase"
|
||||
thirtyDays: "30 päivää"
|
||||
thirtyDaysAgo: "30 päivää sitten"
|
||||
timesKicked: "Heitetty pihalle"
|
||||
toMainPage: "pääsivu"
|
||||
total: "Total"
|
||||
totalActive: "Aktiivisena"
|
||||
totalAfk: "AFK"
|
||||
totalPlayers: "Pelaajia"
|
||||
totalPlayersOld: "Kaikki Pelaajat"
|
||||
totalPlaytime: "Peliaikaa yhteensä"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Suunta"
|
||||
trends30days: "Suunnat 30 päivälle"
|
||||
uniquePlayers: "Uniikkeja pelaajia"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Todella Aktiivinen"
|
||||
weekComparison: "Viikkojen vertaus"
|
||||
weekdays: "'Maanantai', 'Tiistai', 'Keskiviikko', 'Torstai', 'Perjantai', 'Lauantai', 'Sunnuntai'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "Temps AFK"
|
||||
all: "Tout"
|
||||
allTime: "Tout le Temps"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "en Chiffres"
|
||||
average: "Moyen(ne)"
|
||||
averageActivePlaytime: "Temps Actif moyen"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Temps de Jeu moyen"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Durée moyenne d'une Session"
|
||||
averageSessions: "Quantité moyenne de Sessions"
|
||||
averageTps: "TPS moyen"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Banni(e)"
|
||||
bestPeak: "Pic maximal de Joueurs en Ligne"
|
||||
bestPing: "Meilleure Latence"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Géolocalisation"
|
||||
hourByHour: "Heure par Heure"
|
||||
inactive: "Inactif(ve)"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Dernier Connecté"
|
||||
lastPeak: "Dernier pic de Joueurs en Ligne"
|
||||
lastSeen: "Dernière Connexion"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Longueur"
|
||||
links: "LIENS"
|
||||
loadedChunks: "Chunks Chargés"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Connexions de Débutants Seuls"
|
||||
longestSession: "Session la plus Longue"
|
||||
lowTpsSpikes: "Pics de TPS bas"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Espace Disque MAX disponible"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Espace Disque MIN disponible"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Nouveau(elle)"
|
||||
newPlayerRetention: "Rétention des nouveaux Joueurs"
|
||||
newPlayers: "Nouveaux Joueurs"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Surnom"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Maintenant"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Joueurs"
|
||||
playersOnline: "Joueurs en Ligne"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Aperçu de l'Activité en Ligne"
|
||||
playtime: "Temps de Jeu"
|
||||
plugins: "Plugins"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Page du Serveur"
|
||||
serverPlaytime: "Temps de Jeu du Serveur"
|
||||
serverPlaytime30days: "Temps de Jeu du Serveur sur 30 Jours"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Serveurs"
|
||||
serversTitle: "SERVEURS"
|
||||
session: "Session"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Session Médiane"
|
||||
sessionStart: "Début de la Session"
|
||||
sessions: "Sessions"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Sélection du Thème"
|
||||
thirdDeadliestWeapon: "3ᵉ Arme de Combat"
|
||||
thirtyDays: "30 jours"
|
||||
thirtyDaysAgo: "Il y a 30 jours"
|
||||
timesKicked: "Nombre d'Éjections"
|
||||
toMainPage: "Retour à la page principale"
|
||||
total: "Total"
|
||||
totalActive: "Temps Actif Total"
|
||||
totalAfk: "Temps Inactif Total"
|
||||
totalPlayers: "Joueurs Totaux"
|
||||
totalPlayersOld: "Joueurs Totaux"
|
||||
totalPlaytime: "Temps de Jeu Total"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Tendances"
|
||||
trends30days: "Tendances sur 30 Jours"
|
||||
uniquePlayers: "Joueurs Uniques"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Très Actif"
|
||||
weekComparison: "Comparaison Hebdomadaire"
|
||||
weekdays: "'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "Tempo AFK"
|
||||
all: "Tutto"
|
||||
allTime: "Tutto il Tempo"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "Statistiche"
|
||||
average: "Media"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Media Tempo di Gioco"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Media Lunghezza Sessione"
|
||||
averageSessions: "Average Sessions"
|
||||
averageTps: "Media TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Bannato"
|
||||
bestPeak: "Record Migliore"
|
||||
bestPing: "Ping Migliore"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Geolocalizazione"
|
||||
hourByHour: "Hour by Hour"
|
||||
inactive: "Inattivo"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Ultima connessione"
|
||||
lastPeak: "Record Settimanale"
|
||||
lastSeen: "Ultima Visita"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Durata"
|
||||
links: "LINKS"
|
||||
loadedChunks: "Chunks Caricati"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Nuove entrate Solitarie"
|
||||
longestSession: "Sessione più lunga"
|
||||
lowTpsSpikes: "Spicchi TPS bassi"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Spazio Disco libero Max"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Spazio Disco libero Min"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Nuovi"
|
||||
newPlayerRetention: "Retenzione Nuovo Giocatore"
|
||||
newPlayers: "Nuovi Giocatori"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Nick"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Ora"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Giocatori"
|
||||
playersOnline: "Giocatori Online"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Panoramica delle attività online"
|
||||
playtime: "Gioco"
|
||||
plugins: "Plugins"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Pagina del Server"
|
||||
serverPlaytime: "Tempo di Gioco Server"
|
||||
serverPlaytime30days: "Tempo di Gioco Server di 30 giorni"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Servers"
|
||||
serversTitle: "SERVERS"
|
||||
session: "Session"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Media Sessioni"
|
||||
sessionStart: "Sessione Iniziata"
|
||||
sessions: "Sessioni"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Selezione Tema"
|
||||
thirdDeadliestWeapon: "3° Arma PvP Preferita"
|
||||
thirtyDays: "30 giorni"
|
||||
thirtyDaysAgo: "30 giorni fa"
|
||||
timesKicked: "Cacciato"
|
||||
toMainPage: "Ritorna alla pagina principale"
|
||||
total: "Total"
|
||||
totalActive: "Totale Attivo"
|
||||
totalAfk: "Totale AFK"
|
||||
totalPlayers: "Giocatori Totali"
|
||||
totalPlayersOld: "Totale Giocatori"
|
||||
totalPlaytime: "Tempo di gioco Totale"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Tendenza"
|
||||
trends30days: "Tendenza per 30 giorni"
|
||||
uniquePlayers: "Giocatori unici"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Molto Attivo"
|
||||
weekComparison: "Confronto settimanale"
|
||||
weekdays: "'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato', 'Domenica'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "離席時間"
|
||||
all: "全て"
|
||||
allTime: "全体"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "の情報"
|
||||
average: "平均"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "平均プレイ時間"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "平均接続時間"
|
||||
averageSessions: "Average Sessions"
|
||||
averageTps: "平均TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "BAN履歴"
|
||||
bestPeak: "全体のピークタイム"
|
||||
bestPing: "最高Ping値"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "地域"
|
||||
hourByHour: "Hour by Hour"
|
||||
inactive: "休止中"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "直近の接続"
|
||||
lastPeak: "直近のピークタイム"
|
||||
lastSeen: "直近のオンライン"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " 長さ"
|
||||
links: "LINKS"
|
||||
loadedChunks: "ロードされたチャンク数"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "新しく一人での参加"
|
||||
longestSession: "最長接続時間"
|
||||
lowTpsSpikes: "TPSの低下値"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "ディスクの最大空き容量"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "ディスクの最低空き容量"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "New"
|
||||
newPlayerRetention: "新規プレイヤーの継続率"
|
||||
newPlayers: "新規プレイヤー"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "ニックネーム"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "現在"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "プレイヤー"
|
||||
playersOnline: "オンラインのプレイヤー"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "接続状況の概要"
|
||||
playtime: "プレイ時間"
|
||||
plugins: "プラグイン"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "サーバーページ"
|
||||
serverPlaytime: "各サーバーのプレイ時間"
|
||||
serverPlaytime30days: "各サーバーでの1ヶ月のプレイ時間"
|
||||
serverSelector: "Server selector"
|
||||
servers: "接続されているサーバー"
|
||||
serversTitle: "接続されているサーバー"
|
||||
session: "オンライン"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "平均オンライン"
|
||||
sessionStart: "接続した時間"
|
||||
sessions: "接続履歴"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "テーマ選択"
|
||||
thirdDeadliestWeapon: "3番目にPvPで使用されている武器"
|
||||
thirtyDays: "1ヶ月"
|
||||
thirtyDaysAgo: "1ヶ月前"
|
||||
timesKicked: "キック回数"
|
||||
toMainPage: "メインページに戻る"
|
||||
total: "Total"
|
||||
totalActive: "累計活動時間"
|
||||
totalAfk: "累計離席時間"
|
||||
totalPlayers: "トータルプレイヤー数"
|
||||
totalPlayersOld: "全プレイヤー数"
|
||||
totalPlaytime: "トータルプレイ時間"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "増減"
|
||||
trends30days: "1ヶ月間の増減"
|
||||
uniquePlayers: "接続したプレイヤーの総数"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "とてもログインしている"
|
||||
weekComparison: "直近1周間での比較"
|
||||
weekdays: "'月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日', '日曜日'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "AFK 시간"
|
||||
all: "모두"
|
||||
allTime: "모든 시간"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "숫자로"
|
||||
average: "평균"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "평균 플레이 타임"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "평균 접속 시간(세션 길이)"
|
||||
averageSessions: "Average Sessions"
|
||||
averageTps: "평균 TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Banned"
|
||||
bestPeak: "최고의 피크"
|
||||
bestPing: "최고 Ping"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "지리적 위치"
|
||||
hourByHour: "Hour by Hour"
|
||||
inactive: "비활성"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "마지막 연결"
|
||||
lastPeak: "마지막 피크"
|
||||
lastSeen: "마지막으로 본"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " 길이"
|
||||
links: "LINKS"
|
||||
loadedChunks: "로드된 청크"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "최근 신규 접속(Lone New Joins)"
|
||||
longestSession: "가장 긴 접속 시간"
|
||||
lowTpsSpikes: "낮은 TPS 스파이크"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "최대 여유 디스크용량"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "최소 여유 디스크용량"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "신규"
|
||||
newPlayerRetention: "신규 플레이어 유지"
|
||||
newPlayers: "신규 플레이어수"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "닉네임"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "현재"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "플레이어들"
|
||||
playersOnline: "접속중인 플레이어"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "온라인 활동 개요"
|
||||
playtime: "플레이타임"
|
||||
plugins: "플러그인"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "서버 페이지"
|
||||
serverPlaytime: "서버 플레이 타임"
|
||||
serverPlaytime30days: "서버 플레이 타임 - 최근 30일"
|
||||
serverSelector: "Server selector"
|
||||
servers: "서버 목록"
|
||||
serversTitle: "서버 목록"
|
||||
session: "세션"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "세션 중앙값"
|
||||
sessionStart: "세션 시작"
|
||||
sessions: "세션 목록"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "테마 선택"
|
||||
thirdDeadliestWeapon: "3rd PvP 무기"
|
||||
thirtyDays: "30일"
|
||||
thirtyDaysAgo: "30일 전"
|
||||
timesKicked: "접속종료한 시간"
|
||||
toMainPage: "메인 페이지로"
|
||||
total: "Total"
|
||||
totalActive: "총 활성화 시간"
|
||||
totalAfk: "총 AFK 시간"
|
||||
totalPlayers: "총 플레이어수"
|
||||
totalPlayersOld: "총 플레이어"
|
||||
totalPlaytime: "총 플레이타임"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "트렌드"
|
||||
trends30days: "30일 동안의 트렌드"
|
||||
uniquePlayers: "기존 플레이어"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "매우 활성화된"
|
||||
weekComparison: "주 비교"
|
||||
weekdays: "'월요일', '화요일', '수요일', '목요일', '금요일', '토요일', '일요일'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "AFK Tijd"
|
||||
all: "Alle"
|
||||
allTime: "Alle Tijd"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "als nummers"
|
||||
average: "Gemiddelde"
|
||||
averageActivePlaytime: "Gemiddelde Actieve Speeltijd"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Gemiddelde Speeltijd"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Gemiddelde sessieduur"
|
||||
averageSessions: "Gemiddelde sessies"
|
||||
averageTps: "Gemiddelde TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Verbannen"
|
||||
bestPeak: "Piek aller tijden"
|
||||
bestPing: "Beste ping"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Geolocaties"
|
||||
hourByHour: "Uur voor uur"
|
||||
inactive: "Inactief"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Laatst verbonden"
|
||||
lastPeak: "Laatste piek"
|
||||
lastSeen: "Laatste gezien"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Lengte"
|
||||
links: "LINKS"
|
||||
loadedChunks: "Geladen Chunks"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Eenzame nieuweling inloggen"
|
||||
longestSession: "Langste sessie"
|
||||
lowTpsSpikes: "Lage TPS-pieken"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Max Vrije schijfruimte"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Min Vrije schijfruimte"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Nieuw"
|
||||
newPlayerRetention: "Retentie nieuwe speler"
|
||||
newPlayers: "Nieuwe Spelers"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Gebruikersnaam"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Nu"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Spelers"
|
||||
playersOnline: "Spelers Online"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Overzicht van online activiteiten"
|
||||
playtime: "Speeltijd"
|
||||
plugins: "Plugins"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Serverpagina"
|
||||
serverPlaytime: "Server speeltijd"
|
||||
serverPlaytime30days: "Server speeltijd voor 30 dagen"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Servers"
|
||||
serversTitle: "SERVERS"
|
||||
session: "Sessie"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Sessiemediaan"
|
||||
sessionStart: "Sessie gestart"
|
||||
sessions: "Sessies"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Thema selecteren"
|
||||
thirdDeadliestWeapon: "3e PvP-wapen"
|
||||
thirtyDays: "30 dagen"
|
||||
thirtyDaysAgo: "30 dagen geleden"
|
||||
timesKicked: "Aantal keer afgetapt"
|
||||
toMainPage: "naar hoofdpagina"
|
||||
total: "Total"
|
||||
totalActive: "Totaal actief"
|
||||
totalAfk: "Totaal AFK"
|
||||
totalPlayers: "Totaal aantal spelers"
|
||||
totalPlayersOld: "Totaal aantal spelers"
|
||||
totalPlaytime: "Totale speeltijd"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Trend"
|
||||
trends30days: "Trends voor 30 dagen"
|
||||
uniquePlayers: "Unieke spelers"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Heel Actief"
|
||||
weekComparison: "Weekvergelijking"
|
||||
weekdays: "'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag', 'Zondag'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "AFK Time"
|
||||
all: "Todos"
|
||||
allTime: "All Time"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "as Numbers"
|
||||
average: "Average"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Average Playtime"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Average Session Length"
|
||||
averageSessions: "Average Sessions"
|
||||
averageTps: "Average TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Banido"
|
||||
bestPeak: "Pico Máximo"
|
||||
bestPing: "Best Ping"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Geolocalizações"
|
||||
hourByHour: "Hour by Hour"
|
||||
inactive: "Inactive"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Última Conexão"
|
||||
lastPeak: "Último Pico"
|
||||
lastSeen: "Última Vez Visto"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Length"
|
||||
links: "LINKS"
|
||||
loadedChunks: "Chunks Carregados"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Lone newbie joins"
|
||||
longestSession: "Longest Session"
|
||||
lowTpsSpikes: "Low TPS Spikes"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Max Free Disk"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Min Free Disk"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "New"
|
||||
newPlayerRetention: "Retenção de Novos Jogadores"
|
||||
newPlayers: "Novos Jogadores"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Nick"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Now"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Jogadores"
|
||||
playersOnline: "Jogadores Online"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Online Activity Overview"
|
||||
playtime: "Tempo de Jogo"
|
||||
plugins: "Plugins"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Server page"
|
||||
serverPlaytime: "Server Playtime"
|
||||
serverPlaytime30days: "Server Playtime for 30 days"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Servidores"
|
||||
serversTitle: "SERVERS"
|
||||
session: "Sessão"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Média de Sessões"
|
||||
sessionStart: "Session Started"
|
||||
sessions: "Sessões"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Theme Select"
|
||||
thirdDeadliestWeapon: "3rd PvP Weapon"
|
||||
thirtyDays: "30 days"
|
||||
thirtyDaysAgo: "30 days ago"
|
||||
timesKicked: "Vezes Kickado"
|
||||
toMainPage: "to main page"
|
||||
total: "Total"
|
||||
totalActive: "Tempo Total Ativo"
|
||||
totalAfk: "Tempo Total AFK"
|
||||
totalPlayers: "Total Players"
|
||||
totalPlayersOld: "Total de Jogadores"
|
||||
totalPlaytime: "Total Playtime"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Trend"
|
||||
trends30days: "Trends for 30 days"
|
||||
uniquePlayers: "Jogadores Únicos"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Muito Ativo"
|
||||
weekComparison: "Week Comparison"
|
||||
weekdays: "'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "Время AFK"
|
||||
all: "Все"
|
||||
allTime: "Все время"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "В числах"
|
||||
average: "Сред."
|
||||
averageActivePlaytime: "Среднее время активной игры"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Среднее кол-во игроков"
|
||||
averagePlaytime: "Среднее время игры"
|
||||
averageRamUsage: "Среднее использование памяти"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Средняя продолжительность сессии"
|
||||
averageSessions: "Средняя сессия"
|
||||
averageTps: "Средний TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Забанен"
|
||||
bestPeak: "Максимальный Пик"
|
||||
bestPing: "Наилучший пинг"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Средняя продолжительность первого сеанса"
|
||||
median: "Средняя продолжительность первого сеанса"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Геолокация"
|
||||
hourByHour: "Статистика по часам"
|
||||
inactive: "Неактивный"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Последнее подключение"
|
||||
lastPeak: "Последний Пик"
|
||||
lastSeen: "Последнее посещение"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Длина"
|
||||
links: "ССЫЛКИ"
|
||||
loadedChunks: "Загруженные чанки"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Одинокий новичок присоединяется"
|
||||
longestSession: "Самая длинная сессия"
|
||||
lowTpsSpikes: "Низкий TPS"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Макс. свободный диск"
|
||||
medianSessionLength: "Средняя продолжительность сеанса"
|
||||
minFreeDisk: "Мин. свободный диск"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Новый"
|
||||
newPlayerRetention: "Сохранение нового игрока"
|
||||
newPlayers: "Новые игроки"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Никнейм"
|
||||
noDataToDisplay: "Нет данных для отображения"
|
||||
now: "Сейчас"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Обзор базы игроков"
|
||||
players: "Игроки"
|
||||
playersOnline: "Игроки онлайн"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Обзор сетевой активности"
|
||||
playtime: "Время игры"
|
||||
plugins: "Плагины"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Страница сервера"
|
||||
serverPlaytime: "Время игры на сервере"
|
||||
serverPlaytime30days: "Время игры на сервере за 30 дней"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Серверы"
|
||||
serversTitle: "СЕРВЕРЫ"
|
||||
session: "Сессия"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Средняя сессия"
|
||||
sessionStart: "Сессия началась"
|
||||
sessions: "Сессии"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Выбор темы"
|
||||
thirdDeadliestWeapon: "3-е PvP оружие"
|
||||
thirtyDays: "30 дней"
|
||||
thirtyDaysAgo: "30 дней назад"
|
||||
timesKicked: "Кол-во киков"
|
||||
toMainPage: "На главную страницу"
|
||||
total: "Total"
|
||||
totalActive: "Общая активность"
|
||||
totalAfk: "Всего AFK"
|
||||
totalPlayers: "Всего игроков"
|
||||
totalPlayersOld: "Всего игроков"
|
||||
totalPlaytime: "Общее время игры"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Тенденция"
|
||||
trends30days: "тенденция за 30 дней"
|
||||
uniquePlayers: "Уникальные игроки"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Очень активный"
|
||||
weekComparison: "Сравнение за неделю"
|
||||
weekdays: "'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "AFK Süresi"
|
||||
all: "Tamamı"
|
||||
allTime: "Tüm zamanlar"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "Sayılar olarak"
|
||||
average: "Ortalama"
|
||||
averageActivePlaytime: "Ortalama Aktif Oyun Süresi"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "Ortalama Oyun Süresi"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "Ortalama Oturum Uzunluğu"
|
||||
averageSessions: "Ortalama Oturumlar"
|
||||
averageTps: "Ortalama TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "Yasaklanmış"
|
||||
bestPeak: "Tüm Zamanların Zirvesi"
|
||||
bestPing: "En iyi Ping"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "Coğrafi Konumlar"
|
||||
hourByHour: "Saat saat"
|
||||
inactive: "Etkin değil"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "Son bağlantı"
|
||||
lastPeak: "Son Zirve"
|
||||
lastSeen: "Son Görülme"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " Uzunluk"
|
||||
links: "BAĞLANTILAR"
|
||||
loadedChunks: "Yüklenmiş Chunks lar"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "Yalnız acemi katılıyor"
|
||||
longestSession: "En Uzun Oturum"
|
||||
lowTpsSpikes: "Low TPS Spikes"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "Maksimum Boş Disk"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "Minimum Boş Disk"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "Yeni"
|
||||
newPlayerRetention: "Yeni Oyuncu Elde Tutma"
|
||||
newPlayers: "Yeni Oyuncular"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "Takma ad"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "Şimdi"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "Oyuncular"
|
||||
playersOnline: "Oyuncu Çevrimiçi"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "Çevrimiçi Etkinliğe Genel Bakış"
|
||||
playtime: "Oyun Süresi"
|
||||
plugins: "Pluginler"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "Sunucu sayfası"
|
||||
serverPlaytime: "Sunucu Oynatma Süresi"
|
||||
serverPlaytime30days: "30 günlük Sunucu Oynatma Süresi"
|
||||
serverSelector: "Server selector"
|
||||
servers: "Sunucular"
|
||||
serversTitle: "SUNUCULAR"
|
||||
session: "Oturum"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "Session Median"
|
||||
sessionStart: "Oturum Başladı"
|
||||
sessions: "Oturumlar"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "Tema Seçimi"
|
||||
thirdDeadliestWeapon: "3. PvP Silahı"
|
||||
thirtyDays: "30 gün"
|
||||
thirtyDaysAgo: "30 gün önce"
|
||||
timesKicked: "Kere Atılmış"
|
||||
toMainPage: "Ana Sayfaya"
|
||||
total: "Total"
|
||||
totalActive: "Toplam Aktiflik"
|
||||
totalAfk: "Toplam AFKlık"
|
||||
totalPlayers: "Toplam Oyuncu"
|
||||
totalPlayersOld: "Toplam Oyuncular"
|
||||
totalPlaytime: "Toplam Oyun Süresi"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "Trend"
|
||||
trends30days: "30 günlük trendler"
|
||||
uniquePlayers: "Sunucuya İlk Defa Girenler"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "Çok Aktif"
|
||||
weekComparison: "Hafta Karşılaştırması"
|
||||
weekdays: "'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi', 'Pazar'"
|
||||
|
@ -276,6 +276,8 @@ html:
|
||||
afkTime: "掛機時間"
|
||||
all: "全部"
|
||||
allTime: "所有時間"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "資料"
|
||||
average: "平均"
|
||||
averageActivePlaytime: "平均活躍時間"
|
||||
@ -289,9 +291,11 @@ html:
|
||||
averagePlayers: "Average Players"
|
||||
averagePlaytime: "平均遊玩時間"
|
||||
averageRamUsage: "Average RAM Usage"
|
||||
averageServerDowntime: "Average Downtime / Server"
|
||||
averageSessionLength: "平均會話時長"
|
||||
averageSessions: "平均會話"
|
||||
averageTps: "平均 TPS"
|
||||
averageTps7days: "Average TPS (7 days)"
|
||||
banned: "已被封禁"
|
||||
bestPeak: "所有時間峰值"
|
||||
bestPing: "最低延遲"
|
||||
@ -318,6 +322,12 @@ html:
|
||||
firstSessionLength:
|
||||
average: "Average first session length"
|
||||
median: "Median first session length"
|
||||
geoProjection:
|
||||
dropdown: "Select projection"
|
||||
equalEarth: "Equal Earth"
|
||||
mercator: "Mercator"
|
||||
miller: "Miller"
|
||||
ortographic: "Ortographic"
|
||||
geolocations: "地理位置"
|
||||
hourByHour: "按小時查看"
|
||||
inactive: "不活躍"
|
||||
@ -337,6 +347,7 @@ html:
|
||||
lastConnected: "最後連接時間"
|
||||
lastPeak: "上次線上峰值"
|
||||
lastSeen: "最後線上時間"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
length: " 遊玩時長"
|
||||
links: "連接"
|
||||
loadedChunks: "已載入區塊"
|
||||
@ -345,6 +356,7 @@ html:
|
||||
loneNewbieJoins: "單獨新玩家加入"
|
||||
longestSession: "最長會話時間"
|
||||
lowTpsSpikes: "低 TPS 時間"
|
||||
lowTpsSpikes7days: "Low TPS Spikes (7 days)"
|
||||
maxFreeDisk: "最大可用硬碟空間"
|
||||
medianSessionLength: "Median Session Length"
|
||||
minFreeDisk: "最小可用硬碟空間"
|
||||
@ -362,6 +374,7 @@ html:
|
||||
new: "新"
|
||||
newPlayerRetention: "新玩家留坑率"
|
||||
newPlayers: "新玩家"
|
||||
newPlayers7days: "New Players (7 days)"
|
||||
nickname: "暱稱"
|
||||
noDataToDisplay: "No Data to Display"
|
||||
now: "現在"
|
||||
@ -388,6 +401,7 @@ html:
|
||||
playerbaseOverview: "Playerbase Overview"
|
||||
players: "玩家"
|
||||
playersOnline: "線上玩家"
|
||||
playersOnlineNow: "Players Online (Now)"
|
||||
playersOnlineOverview: "線上活動簡介"
|
||||
playtime: "遊玩時間"
|
||||
plugins: "插件"
|
||||
@ -421,6 +435,7 @@ html:
|
||||
serverPage: "伺服器頁面"
|
||||
serverPlaytime: "伺服器遊戲時間"
|
||||
serverPlaytime30days: "最近 30 天內的伺服器遊玩時間"
|
||||
serverSelector: "Server selector"
|
||||
servers: "伺服器"
|
||||
serversTitle: "伺服器"
|
||||
session: "會話次數"
|
||||
@ -429,21 +444,26 @@ html:
|
||||
sessionMedian: "平均會話長度"
|
||||
sessionStart: "會話開始於"
|
||||
sessions: "會話"
|
||||
sortBy: "Sort By"
|
||||
stacked: "Stacked"
|
||||
themeSelect: "主題選擇"
|
||||
thirdDeadliestWeapon: "第三致命的 PvP 武器"
|
||||
thirtyDays: "30 天"
|
||||
thirtyDaysAgo: "30 天前"
|
||||
timesKicked: "被踢出次數"
|
||||
toMainPage: "回到主頁面"
|
||||
total: "Total"
|
||||
totalActive: "總活躍時長"
|
||||
totalAfk: "總掛機時長"
|
||||
totalPlayers: "總玩家數"
|
||||
totalPlayersOld: "總遊玩時長"
|
||||
totalPlaytime: "總遊玩時間"
|
||||
totalServerDowntime: "Total Server Downtime"
|
||||
tps: "TPS"
|
||||
trend: "趨勢"
|
||||
trends30days: "30 天趨勢"
|
||||
uniquePlayers: "獨立玩家"
|
||||
uniquePlayers7days: "Unique Players (7 days)"
|
||||
veryActive: "非常活躍"
|
||||
weekComparison: "每週對比"
|
||||
weekdays: "'星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'"
|
||||
|
@ -1866,6 +1866,7 @@ a.text-dark:hover, a.text-dark:focus {
|
||||
|
||||
#wrapper {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#wrapper #content-wrapper {
|
||||
|
@ -19,7 +19,7 @@
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"axios": "^0.27.2",
|
||||
"bootstrap": "^5.2.0",
|
||||
"bootstrap": "^5.2.1",
|
||||
"datatables.net": "^1.12.1",
|
||||
"datatables.net-bs5": "^1.12.1",
|
||||
"datatables.net-responsive-bs5": "^2.3.0",
|
||||
@ -35,7 +35,7 @@
|
||||
"react-i18next": "^11.18.5",
|
||||
"react-router-dom": "6",
|
||||
"react-scripts": "5.0.1",
|
||||
"sass": "^1.54.8",
|
||||
"sass": "^1.54.9",
|
||||
"source-map-explorer": "^2.5.2",
|
||||
"swagger-ui": "^4.14.0",
|
||||
"web-vitals": "^3.0.1"
|
||||
|
@ -32,9 +32,16 @@ const LoginPage = React.lazy(() => import("./views/layout/LoginPage"));
|
||||
const ServerPerformance = React.lazy(() => import("./views/server/ServerPerformance"));
|
||||
const ServerPluginData = React.lazy(() => import("./views/server/ServerPluginData"));
|
||||
const ServerWidePluginData = React.lazy(() => import("./views/server/ServerWidePluginData"));
|
||||
const ServerJoinAddresses = React.lazy(() => import("./views/server/ServerJoinAddresses"));
|
||||
|
||||
const NetworkPage = React.lazy(() => import("./views/layout/NetworkPage"));
|
||||
const NetworkOverview = React.lazy(() => import("./views/network/NetworkOverview"));
|
||||
const NetworkServers = React.lazy(() => import("./views/network/NetworkServers"));
|
||||
const NetworkSessions = React.lazy(() => import("./views/network/NetworkSessions"));
|
||||
const NetworkJoinAddresses = React.lazy(() => import("./views/network/NetworkJoinAddresses"));
|
||||
const NetworkGeolocations = React.lazy(() => import("./views/network/NetworkGeolocations"));
|
||||
const NetworkPlayerbaseOverview = React.lazy(() => import("./views/network/NetworkPlayerbaseOverview"));
|
||||
const NetworkPerformance = React.lazy(() => import("./views/network/NetworkPerformance"));
|
||||
|
||||
const PlayersPage = React.lazy(() => import("./views/layout/PlayersPage"));
|
||||
const AllPlayers = React.lazy(() => import("./views/players/AllPlayers"));
|
||||
@ -100,6 +107,7 @@ function App() {
|
||||
<Route path="sessions" element={<Lazy><ServerSessions/></Lazy>}/>
|
||||
<Route path="pvppve" element={<Lazy><ServerPvpPve/></Lazy>}/>
|
||||
<Route path="playerbase" element={<Lazy><PlayerbaseOverview/></Lazy>}/>
|
||||
<Route path="join-addresses" element={<Lazy><ServerJoinAddresses/></Lazy>}/>
|
||||
<Route path="retention" element={<></>}/>
|
||||
<Route path="players" element={<Lazy><ServerPlayers/></Lazy>}/>
|
||||
<Route path="geolocations" element={<Lazy><ServerGeolocations/></Lazy>}/>
|
||||
@ -115,7 +123,13 @@ function App() {
|
||||
<Route path="/network" element={<Lazy><NetworkPage/></Lazy>}>
|
||||
<Route path="" element={<Lazy><OverviewRedirect/></Lazy>}/>
|
||||
<Route path="overview" element={<Lazy><NetworkOverview/></Lazy>}/>
|
||||
<Route path="serversOverview" element={<Lazy><NetworkServers/></Lazy>}/>
|
||||
<Route path="sessions" element={<Lazy><NetworkSessions/></Lazy>}/>
|
||||
<Route path="performance" element={<Lazy><NetworkPerformance/></Lazy>}/>
|
||||
<Route path="playerbase" element={<Lazy><NetworkPlayerbaseOverview/></Lazy>}/>
|
||||
<Route path="join-addresses" element={<Lazy><NetworkJoinAddresses/></Lazy>}/>
|
||||
<Route path="players" element={<Lazy><AllPlayers/></Lazy>}/>
|
||||
<Route path="geolocations" element={<Lazy><NetworkGeolocations/></Lazy>}/>
|
||||
<Route path="plugins-overview" element={<Lazy><ServerPluginData/></Lazy>}/>
|
||||
<Route path="plugins/:plugin" element={<Lazy><ServerWidePluginData/></Lazy>}/>
|
||||
<Route path="*" element={<ErrorView error={{
|
||||
|
@ -84,7 +84,8 @@ const SessionBody = ({i, session}) => {
|
||||
const SessionAccordion = (
|
||||
{
|
||||
sessions,
|
||||
isPlayer
|
||||
isPlayer,
|
||||
isNetwork
|
||||
}
|
||||
) => {
|
||||
const {t} = useTranslation();
|
||||
@ -99,7 +100,10 @@ const SessionAccordion = (
|
||||
firstColumn,
|
||||
<><Fa icon={faClock}/> {t('html.label.sessionStart')}</>,
|
||||
<><Fa icon={faClock}/> {t('html.label.length')}</>,
|
||||
<><Fa icon={faMap}/> {t('html.label.mostPlayedWorld')}</>
|
||||
<>
|
||||
{!isNetwork && <><Fa icon={faMap}/> {t('html.label.mostPlayedWorld')}</>}
|
||||
{isNetwork && <><Fa icon={faServer}/> {t('html.label.server')}</>}
|
||||
</>
|
||||
]} slices={sessions.map(session => {
|
||||
return {
|
||||
body: <SessionBody session={session}/>,
|
||||
|
@ -1,22 +1,49 @@
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {Card, Col, Row} from "react-bootstrap-v5";
|
||||
import {Card, Col, Dropdown, Row} from "react-bootstrap-v5";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
import {faExclamationTriangle, faGlobe} from "@fortawesome/free-solid-svg-icons";
|
||||
import React, {useState} from "react";
|
||||
import {faExclamationTriangle, faGlobe, faLayerGroup} from "@fortawesome/free-solid-svg-icons";
|
||||
import GeolocationBarGraph from "../../graphs/GeolocationBarGraph";
|
||||
import GeolocationWorldMap from "../../graphs/GeolocationWorldMap";
|
||||
import GeolocationWorldMap, {ProjectionOptions} from "../../graphs/GeolocationWorldMap";
|
||||
import {CardLoader} from "../../navigation/Loader";
|
||||
import DropdownToggle from "react-bootstrap-v5/lib/esm/DropdownToggle";
|
||||
import DropdownMenu from "react-bootstrap-v5/lib/esm/DropdownMenu";
|
||||
import DropdownItem from "react-bootstrap-v5/lib/esm/DropdownItem";
|
||||
|
||||
const ProjectionDropDown = ({projection, setProjection}) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
const projectionOptions = Object.values(ProjectionOptions);
|
||||
|
||||
return (
|
||||
<Dropdown className="float-end" style={{position: "absolute", right: "0.5rem"}}
|
||||
title={t('html.label.geoProjection.dropdown')}>
|
||||
<DropdownToggle variant=''>
|
||||
<Fa icon={faLayerGroup}/> {t(projection)}
|
||||
</DropdownToggle>
|
||||
|
||||
<DropdownMenu>
|
||||
<h6 className="dropdown-header">{t('html.label.geoProjection.dropdown')}</h6>
|
||||
{projectionOptions.map((option, i) => (
|
||||
<DropdownItem key={i} onClick={() => setProjection(option)}>
|
||||
{t(option)}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
const GeolocationsCard = ({data}) => {
|
||||
const {t} = useTranslation();
|
||||
const [projection, setProjection] = useState(ProjectionOptions.MILLER);
|
||||
|
||||
if (!data) return <CardLoader/>
|
||||
|
||||
if (!data?.geolocations_enabled) {
|
||||
return (
|
||||
<div className="alert alert-warning mb-0" id="geolocation-warning">
|
||||
<Fa icon={faExclamationTriangle}/>{' '}
|
||||
{t('html.description.noGeolocations')}
|
||||
<Fa icon={faExclamationTriangle}/>{' '}{t('html.description.noGeolocations')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -27,6 +54,7 @@ const GeolocationsCard = ({data}) => {
|
||||
<h6 className="col-black">
|
||||
<Fa icon={faGlobe} className="col-green"/> {t('html.label.geolocations')}
|
||||
</h6>
|
||||
<ProjectionDropDown projection={projection} setProjection={setProjection}/>
|
||||
</Card.Header>
|
||||
<Card.Body className="chart-area" style={{height: "100%"}}>
|
||||
<Row>
|
||||
@ -34,7 +62,8 @@ const GeolocationsCard = ({data}) => {
|
||||
<GeolocationBarGraph series={data.geolocation_bar_series} color={data.colors.bars}/>
|
||||
</Col>
|
||||
<Col md={9}>
|
||||
<GeolocationWorldMap series={data.geolocation_series} colors={data.colors}/>
|
||||
<GeolocationWorldMap series={data.geolocation_series} colors={data.colors}
|
||||
projection={projection}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card.Body>
|
||||
|
@ -6,7 +6,7 @@ import Scrollable from "../../Scrollable";
|
||||
import SessionAccordion from "../../accordion/SessionAccordion";
|
||||
import React from "react";
|
||||
|
||||
const RecentSessionsCard = ({sessions, isPlayer}) => {
|
||||
const RecentSessionsCard = ({sessions, isPlayer, isNetwork}) => {
|
||||
const {t} = useTranslation();
|
||||
return (
|
||||
<Card>
|
||||
@ -19,7 +19,7 @@ const RecentSessionsCard = ({sessions, isPlayer}) => {
|
||||
</h6>
|
||||
</Card.Header>
|
||||
<Scrollable>
|
||||
<SessionAccordion sessions={sessions} isPlayer={isPlayer}/>
|
||||
<SessionAccordion sessions={sessions} isPlayer={isPlayer} isNetwork={isNetwork}/>
|
||||
</Scrollable>
|
||||
</Card>
|
||||
)
|
||||
|
@ -0,0 +1,28 @@
|
||||
import {Card} from "react-bootstrap-v5";
|
||||
import React from "react";
|
||||
import {CardLoader} from "../../navigation/Loader";
|
||||
import ServerPie from "../../graphs/ServerPie";
|
||||
import {faNetworkWired} from "@fortawesome/free-solid-svg-icons";
|
||||
import CardHeader from "../CardHeader";
|
||||
import {useDataRequest} from "../../../hooks/dataFetchHook";
|
||||
import {fetchServerPie} from "../../../service/networkService";
|
||||
import {ErrorViewCard} from "../../../views/ErrorView";
|
||||
|
||||
const ServerPieCard = () => {
|
||||
const {data, loadingError} = useDataRequest(fetchServerPie, []);
|
||||
|
||||
if (!data) return <CardLoader/>;
|
||||
if (loadingError) return <ErrorViewCard error={loadingError}/>;
|
||||
|
||||
const series = data.server_pie_series_30d;
|
||||
const colors = data.server_pie_colors;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader icon={faNetworkWired} color={'teal'} label={'html.label.serverPlaytime30days'}/>
|
||||
<ServerPie series={series} colors={colors}/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default ServerPieCard;
|
@ -0,0 +1,157 @@
|
||||
import React from 'react';
|
||||
import CardTabs from "../../CardTabs";
|
||||
import {
|
||||
faDragon,
|
||||
faHdd,
|
||||
faMap,
|
||||
faMicrochip,
|
||||
faSignal,
|
||||
faTachometerAlt,
|
||||
faUser
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import {Card} from "react-bootstrap-v5";
|
||||
import {useDataRequest} from "../../../hooks/dataFetchHook";
|
||||
import {fetchPingGraph} from "../../../service/serverService";
|
||||
import {tooltip, yAxisConfigurations} from "../../../util/graphs";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {CardLoader, ChartLoader} from "../../navigation/Loader";
|
||||
import LineGraph from "../../graphs/LineGraph";
|
||||
import {ErrorViewBody, ErrorViewCard} from "../../../views/ErrorView";
|
||||
import PingGraph from "../../graphs/performance/PingGraph";
|
||||
import {useMetadata} from "../../../hooks/metadataHook";
|
||||
|
||||
const Tab = ({data, yAxis}) => {
|
||||
return (
|
||||
<LineGraph id={'performance-' + new Date().getTime()} series={data} legendEnabled tall yAxis={yAxis}/>
|
||||
)
|
||||
}
|
||||
|
||||
const PingTab = ({identifier}) => {
|
||||
const {data, loadingError} = useDataRequest(fetchPingGraph, [identifier]);
|
||||
|
||||
if (loadingError) return <ErrorViewBody error={loadingError}/>
|
||||
if (!data) return <ChartLoader style={{height: "450px"}}/>;
|
||||
|
||||
return <PingGraph id="network-performance-ping-chart" data={data}/>;
|
||||
}
|
||||
|
||||
const PerformanceGraphsCard = ({data}) => {
|
||||
const {t} = useTranslation();
|
||||
const {networkMetadata} = useMetadata();
|
||||
|
||||
if (!data || !Object.values(data).length) return <CardLoader/>
|
||||
|
||||
const zones = {
|
||||
tps: [{
|
||||
value: data.zones.tpsThresholdMed,
|
||||
color: data.colors.low
|
||||
}, {
|
||||
value: data.zones.tpsThresholdHigh,
|
||||
color: data.colors.med
|
||||
}, {
|
||||
value: 30,
|
||||
color: data.colors.high
|
||||
}],
|
||||
disk: [{
|
||||
value: data.zones.diskThresholdMed,
|
||||
color: data.colors.low
|
||||
}, {
|
||||
value: data.zones.diskThresholdHigh,
|
||||
color: data.colors.med
|
||||
}, {
|
||||
value: Number.MAX_VALUE,
|
||||
color: data.colors.high
|
||||
}]
|
||||
};
|
||||
const serverData = [];
|
||||
for (let i = 0; i < data.servers.length; i++) {
|
||||
const server = data.servers[i];
|
||||
const values = data.values[i];
|
||||
serverData.push({
|
||||
serverName: server.serverName,
|
||||
values
|
||||
});
|
||||
}
|
||||
|
||||
const series = {
|
||||
players: [],
|
||||
tps: [],
|
||||
cpu: [],
|
||||
ram: [],
|
||||
entities: [],
|
||||
chunks: [],
|
||||
disk: []
|
||||
}
|
||||
|
||||
const spline = 'spline';
|
||||
|
||||
for (const server of serverData) {
|
||||
series.players.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.playersOnline, color: data.colors.playersOnline, yAxis: 0
|
||||
});
|
||||
series.tps.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.twoDecimals,
|
||||
data: server.values.tps, color: data.colors.high, zones: zones.tps, yAxis: 0
|
||||
});
|
||||
series.cpu.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.twoDecimals,
|
||||
data: server.values.cpu, color: data.colors.cpu, yAxis: 0
|
||||
});
|
||||
series.ram.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.ram, color: data.colors.ram, yAxis: 0
|
||||
});
|
||||
series.entities.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.entities, color: data.colors.entities, yAxis: 0
|
||||
});
|
||||
series.chunks.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.chunks, color: data.colors.chunks, yAxis: 0
|
||||
});
|
||||
series.disk.push({
|
||||
name: server.serverName, type: spline, tooltip: tooltip.zeroDecimals,
|
||||
data: server.values.disk, color: data.colors.high, zones: zones.disk, yAxis: 0
|
||||
});
|
||||
}
|
||||
|
||||
if (data.errors.length) {
|
||||
return <ErrorViewCard error={data.errors[0]}/>
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTabs tabs={[
|
||||
{
|
||||
name: t('html.label.playersOnline'), icon: faUser, color: 'light-blue', href: 'players-online',
|
||||
element: <Tab data={series.players} yAxis={yAxisConfigurations.PLAYERS_ONLINE}/>
|
||||
}, {
|
||||
name: t('html.label.tps'), icon: faTachometerAlt, color: 'red', href: 'tps',
|
||||
element: <Tab data={series.tps} yAxis={yAxisConfigurations.TPS}/>
|
||||
}, {
|
||||
name: t('html.label.cpu'), icon: faTachometerAlt, color: 'amber', href: 'cpu',
|
||||
element: <Tab data={series.cpu} yAxis={yAxisConfigurations.CPU}/>
|
||||
}, {
|
||||
name: t('html.label.ram'), icon: faMicrochip, color: 'light-green', href: 'ram',
|
||||
element: <Tab data={series.ram} yAxis={yAxisConfigurations.RAM_OR_DISK}/>
|
||||
}, {
|
||||
name: t('html.label.entities'), icon: faDragon, color: 'purple', href: 'entities',
|
||||
element: <Tab data={series.entities} yAxis={yAxisConfigurations.ENTITIES}/>
|
||||
}, {
|
||||
name: t('html.label.loadedChunks'), icon: faMap, color: 'blue-grey', href: 'chunks',
|
||||
element: <Tab data={series.chunks} yAxis={yAxisConfigurations.CHUNKS}/>
|
||||
}, {
|
||||
name: t('html.label.diskSpace'), icon: faHdd, color: 'green', href: 'disk',
|
||||
element: <Tab data={series.disk} yAxis={yAxisConfigurations.RAM_OR_DISK}/>
|
||||
}, {
|
||||
name: t('html.label.ping'), icon: faSignal, color: 'amber', href: 'ping',
|
||||
element: networkMetadata ? <PingTab identifier={networkMetadata.currentServer.serverUUID}/> :
|
||||
<ChartLoader/>
|
||||
},
|
||||
]}/>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
||||
export default PerformanceGraphsCard
|
@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import {Card} from "react-bootstrap-v5";
|
||||
import CardHeader from "../CardHeader";
|
||||
import {
|
||||
faBookOpen,
|
||||
faChartLine,
|
||||
faExclamationCircle,
|
||||
faPowerOff,
|
||||
faTachometerAlt,
|
||||
faUsers
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import Datapoint from "../../Datapoint";
|
||||
|
||||
const QuickViewDataCard = ({server}) => {
|
||||
const {t} = useTranslation()
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader icon={faBookOpen} color={'light-green'} label={server.name + ' ' + t('html.label.asNumbers')}/>
|
||||
<Card.Body>
|
||||
<Datapoint icon={faPowerOff} color={'light-green'} name={t('html.label.currentUptime')}
|
||||
value={server.current_uptime}/>
|
||||
<Datapoint name={t('html.label.lastPeak') + ' (' + server.last_peak_date + ')'}
|
||||
color={'blue'} icon={faChartLine}
|
||||
value={server.last_peak_players} valueLabel={t('html.unit.players')} bold/>
|
||||
<Datapoint name={t('html.label.bestPeak') + ' (' + server.best_peak_date + ')'}
|
||||
color={'light-green'} icon={faChartLine}
|
||||
value={server.best_peak_players} valueLabel={t('html.unit.players')} bold/>
|
||||
<hr/>
|
||||
<p><b>{t('html.label.last7days')}</b></p>
|
||||
<Datapoint icon={faUsers} color={'light-blue'} name={t('html.label.uniquePlayers')}
|
||||
value={server.unique_players}/>
|
||||
<Datapoint icon={faUsers} color={'light-green'} name={t('html.label.newPlayers')}
|
||||
value={server.new_players}/>
|
||||
<Datapoint icon={faTachometerAlt} color={'orange'} name={t('html.label.averageTps')}
|
||||
value={server.avg_tps}/>
|
||||
<Datapoint icon={faExclamationCircle} color={'red'} name={t('html.label.lowTpsSpikes')}
|
||||
value={server.low_tps_spikes}/>
|
||||
<Datapoint icon={faPowerOff} color={'red'} name={t('html.label.downtime')}
|
||||
value={server.downtime}/>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
||||
export default QuickViewDataCard
|
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import CardHeader from "../CardHeader";
|
||||
import {Card} from "react-bootstrap-v5";
|
||||
import PlayersOnlineGraph from "../../graphs/PlayersOnlineGraph";
|
||||
import {faChartArea} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
const QuickViewGraphCard = ({server}) => {
|
||||
const {t} = useTranslation();
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader icon={faChartArea} color={'light-blue'}
|
||||
label={server.name + ' ' + t('html.label.onlineActivity') + ' (' + t('html.label.thirtyDays') + ')'}/>
|
||||
<PlayersOnlineGraph data={server.playersOnline}/>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
||||
export default QuickViewGraphCard
|
@ -0,0 +1,73 @@
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import {Card, Dropdown} from "react-bootstrap-v5";
|
||||
import ServersTable, {ServerSortOption} from "../../table/ServersTable";
|
||||
import {faNetworkWired} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import DropdownToggle from "react-bootstrap-v5/lib/esm/DropdownToggle";
|
||||
import DropdownMenu from "react-bootstrap-v5/lib/esm/DropdownMenu";
|
||||
import DropdownItem from "react-bootstrap-v5/lib/esm/DropdownItem";
|
||||
|
||||
const SortDropDown = ({sortBy, sortReversed, setSortBy}) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
const sortOptions = Object.values(ServerSortOption);
|
||||
|
||||
const getSortIcon = useCallback(() => {
|
||||
return sortReversed ? sortBy.iconDesc : sortBy.iconAsc;
|
||||
}, [sortBy, sortReversed]);
|
||||
|
||||
return (
|
||||
<Dropdown className="float-end" style={{position: "absolute", right: "0.5rem"}}>
|
||||
<DropdownToggle variant=''>
|
||||
<Fa icon={getSortIcon()}/> {t(sortBy.label)}
|
||||
</DropdownToggle>
|
||||
|
||||
<DropdownMenu>
|
||||
<h6 className="dropdown-header">{t('html.label.sortBy')}</h6>
|
||||
{sortOptions.map((option, i) => (
|
||||
<DropdownItem key={i} onClick={() => setSortBy(option)}>
|
||||
{t(option.label)}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
const ServersTableCard = ({servers, onSelect}) => {
|
||||
const {t} = useTranslation();
|
||||
const [sortBy, setSortBy] = useState(ServerSortOption.ALPHABETICAL);
|
||||
const [sortReversed, setSortReversed] = useState(false);
|
||||
|
||||
const setSort = option => {
|
||||
if (sortBy === option) {
|
||||
setSortReversed(!sortReversed);
|
||||
} else {
|
||||
setSortBy(option);
|
||||
setSortReversed(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card.Header style={{width: "100%"}}>
|
||||
<h6 className="col-black">
|
||||
<Fa icon={faNetworkWired} className={"col-light-green"}/> {t('html.label.servers')}
|
||||
</h6>
|
||||
<SortDropDown sortBy={sortBy} setSortBy={setSort} sortReversed={sortReversed}/>
|
||||
</Card.Header>
|
||||
{!servers.length && <Card.Body>
|
||||
<p>No servers found in the database.</p>
|
||||
<p>It appears that Plan is not installed on any game servers or not connected to the same database.
|
||||
See <a href="https://github.com/plan-player-analytics/Plan/wiki">wiki</a> for Network tutorial.</p>
|
||||
</Card.Body>}
|
||||
{servers.length && <ServersTable servers={servers}
|
||||
onSelect={onSelect}
|
||||
sortBy={sortBy}
|
||||
sortReversed={sortReversed}/>}
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
||||
export default ServersTableCard
|
@ -1,5 +1,4 @@
|
||||
import React from "react";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {useDataRequest} from "../../../../hooks/dataFetchHook";
|
||||
import {fetchPlayerbaseDevelopmentGraph} from "../../../../service/serverService";
|
||||
import {ErrorViewCard} from "../../../../views/ErrorView";
|
||||
@ -7,12 +6,11 @@ import {useTranslation} from "react-i18next";
|
||||
import {Card} from "react-bootstrap-v5";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faUsers} from "@fortawesome/free-solid-svg-icons";
|
||||
import PlayerbasePie from "../../../graphs/PlayerbasePie";
|
||||
import {CardLoader} from "../../../navigation/Loader";
|
||||
import GroupVisualizer from "../../../graphs/GroupVisualizer";
|
||||
|
||||
const CurrentPlayerbaseCard = () => {
|
||||
const CurrentPlayerbaseCard = ({identifier}) => {
|
||||
const {t} = useTranslation();
|
||||
const {identifier} = useParams();
|
||||
|
||||
const {data, loadingError} = useDataRequest(fetchPlayerbaseDevelopmentGraph, [identifier]);
|
||||
|
||||
@ -26,7 +24,7 @@ const CurrentPlayerbaseCard = () => {
|
||||
<Fa icon={faUsers} className="col-amber"/> {t('html.label.currentPlayerbase')}
|
||||
</h6>
|
||||
</Card.Header>
|
||||
<PlayerbasePie series={data.activity_pie_series}/>
|
||||
<GroupVisualizer groups={data.activity_pie_series} name={t('html.label.players')}/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
import React, {useState} from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useDataRequest} from "../../../../hooks/dataFetchHook";
|
||||
import {fetchJoinAddressByDay} from "../../../../service/serverService";
|
||||
import {ErrorViewCard} from "../../../../views/ErrorView";
|
||||
import {CardLoader} from "../../../navigation/Loader";
|
||||
import {Card} from "react-bootstrap-v5";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faChartColumn} from "@fortawesome/free-solid-svg-icons";
|
||||
import JoinAddressGraph from "../../../graphs/JoinAddressGraph";
|
||||
import Toggle from "../../../input/Toggle";
|
||||
|
||||
const JoinAddressGraphCard = ({identifier}) => {
|
||||
const {t} = useTranslation();
|
||||
const [stack, setStack] = useState(true);
|
||||
|
||||
const {data, loadingError} = useDataRequest(fetchJoinAddressByDay, [identifier]);
|
||||
|
||||
if (loadingError) return <ErrorViewCard error={loadingError}/>
|
||||
if (!data) return <CardLoader/>;
|
||||
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card.Header>
|
||||
<h6 className="col-black" style={{width: '100%'}}>
|
||||
<Fa icon={faChartColumn} className="col-amber"/> {t('html.label.joinAddresses')}
|
||||
</h6>
|
||||
<Toggle value={stack} onValueChange={setStack} color={'amber'}>{t('html.label.stacked')}</Toggle>
|
||||
</Card.Header>
|
||||
<JoinAddressGraph id={'join-address-graph'} data={data?.join_addresses_by_date} colors={data?.colors}
|
||||
stack={stack}/>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
||||
export default JoinAddressGraphCard
|
@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useDataRequest} from "../../../../hooks/dataFetchHook";
|
||||
import {fetchJoinAddressPie} from "../../../../service/serverService";
|
||||
import {ErrorViewCard} from "../../../../views/ErrorView";
|
||||
import {CardLoader} from "../../../navigation/Loader";
|
||||
import {Card} from "react-bootstrap-v5";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faLocationArrow} from "@fortawesome/free-solid-svg-icons";
|
||||
import GroupVisualizer from "../../../graphs/GroupVisualizer";
|
||||
|
||||
const JoinAddressGroupCard = ({identifier}) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
const {data, loadingError} = useDataRequest(fetchJoinAddressPie, [identifier]);
|
||||
|
||||
if (loadingError) return <ErrorViewCard error={loadingError}/>
|
||||
if (!data) return <CardLoader/>;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card.Header>
|
||||
<h6 className="col-black" style={{width: '100%'}}>
|
||||
<Fa icon={faLocationArrow} className="col-amber"/> {t('html.label.latestJoinAddresses')}
|
||||
</h6>
|
||||
</Card.Header>
|
||||
<GroupVisualizer groups={data.slices} colors={data.colors}/>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
||||
export default JoinAddressGroupCard
|
@ -14,6 +14,7 @@ import CpuRamPerformanceGraph from "../../../graphs/performance/CpuRamPerformanc
|
||||
import WorldPerformanceGraph from "../../../graphs/performance/WorldPerformanceGraph";
|
||||
import DiskPerformanceGraph from "../../../graphs/performance/DiskPerformanceGraph";
|
||||
import PingGraph from "../../../graphs/performance/PingGraph";
|
||||
import {mapPerformanceDataToSeries} from "../../../../util/graphs";
|
||||
|
||||
const AllGraphTab = ({data, dataSeries, loadingError}) => {
|
||||
if (loadingError) return <ErrorViewBody error={loadingError}/>
|
||||
@ -58,43 +59,6 @@ const PingGraphTab = ({identifier}) => {
|
||||
return <PingGraph id="server-performance-ping-chart" data={data}/>;
|
||||
}
|
||||
|
||||
function mapToDataSeries(performanceData) {
|
||||
const playersOnline = [];
|
||||
const tps = [];
|
||||
const cpu = [];
|
||||
const ram = [];
|
||||
const entities = [];
|
||||
const chunks = [];
|
||||
const disk = [];
|
||||
|
||||
return new Promise((resolve => {
|
||||
let i = 0;
|
||||
const length = performanceData.length;
|
||||
|
||||
function processNextThousand() {
|
||||
const to = Math.min(i + 1000, length);
|
||||
for (i; i < to; i++) {
|
||||
const entry = performanceData[i];
|
||||
const date = entry[0];
|
||||
playersOnline[i] = [date, entry[1]];
|
||||
tps[i] = [date, entry[2]];
|
||||
cpu[i] = [date, entry[3]];
|
||||
ram[i] = [date, entry[4]];
|
||||
entities[i] = [date, entry[5]];
|
||||
chunks[i] = [date, entry[6]];
|
||||
disk[i] = [date, entry[7]];
|
||||
}
|
||||
if (i >= length) {
|
||||
resolve({playersOnline, tps, cpu, ram, entities, chunks, disk})
|
||||
} else {
|
||||
setTimeout(processNextThousand, 10);
|
||||
}
|
||||
}
|
||||
|
||||
processNextThousand();
|
||||
}))
|
||||
}
|
||||
|
||||
const PerformanceGraphsCard = () => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
@ -104,7 +68,7 @@ const PerformanceGraphsCard = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
mapToDataSeries(data.values).then(parsed => setParsedData(parsed))
|
||||
mapPerformanceDataToSeries(data.values).then(parsed => setParsedData(parsed))
|
||||
}
|
||||
}, [data, setParsedData]);
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {useDataRequest} from "../../../../hooks/dataFetchHook";
|
||||
import {fetchPlayerbaseDevelopmentGraph} from "../../../../service/serverService";
|
||||
import {ErrorViewCard} from "../../../../views/ErrorView";
|
||||
@ -10,9 +9,8 @@ import React from "react";
|
||||
import PlayerbaseGraph from "../../../graphs/PlayerbaseGraph";
|
||||
import {CardLoader} from "../../../navigation/Loader";
|
||||
|
||||
const PlayerbaseDevelopmentCard = () => {
|
||||
const PlayerbaseDevelopmentCard = ({identifier}) => {
|
||||
const {t} = useTranslation();
|
||||
const {identifier} = useParams();
|
||||
|
||||
const {data, loadingError} = useDataRequest(
|
||||
fetchPlayerbaseDevelopmentGraph,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import InsightsFor30DaysCard from "../../common/InsightsFor30DaysCard";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {useDataRequest} from "../../../../hooks/dataFetchHook";
|
||||
import {fetchSessionOverview} from "../../../../service/serverService";
|
||||
import {ErrorViewCard} from "../../../../views/ErrorView";
|
||||
@ -8,29 +7,35 @@ import Datapoint from "../../../Datapoint";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {faGamepad, faUsers} from "@fortawesome/free-solid-svg-icons";
|
||||
import {faClock} from "@fortawesome/free-regular-svg-icons";
|
||||
import {fetchNetworkSessionsOverview} from "../../../../service/networkService";
|
||||
|
||||
const SessionInsightsCard = () => {
|
||||
const SessionInsightsCard = ({identifier}) => {
|
||||
const {t} = useTranslation();
|
||||
const {identifier} = useParams();
|
||||
|
||||
const {data, loadingError} = useDataRequest(fetchSessionOverview, [identifier]);
|
||||
const {
|
||||
data,
|
||||
loadingError
|
||||
} = useDataRequest(identifier ? fetchSessionOverview : fetchNetworkSessionsOverview, [identifier]);
|
||||
|
||||
if (loadingError) return <ErrorViewCard error={loadingError}/>
|
||||
|
||||
const insights = data?.insights;
|
||||
|
||||
return (
|
||||
<InsightsFor30DaysCard>
|
||||
<Datapoint name={t('html.label.mostActiveGamemode')} icon={faGamepad} color="teal" bold
|
||||
value={data?.insights.most_active_gamemode}
|
||||
valueLabel={data?.insights.most_active_gamemode_perc}
|
||||
value={insights?.most_active_gamemode}
|
||||
valueLabel={insights?.most_active_gamemode_perc}
|
||||
/>
|
||||
<Datapoint name={t('html.label.serverOccupied')} icon={faUsers} color="teal"
|
||||
value={'~' + data?.insights.server_occupied} valueLabel={data?.insights.server_occupied_perc}
|
||||
value={insights?.server_occupied ? '~' + insights.server_occupied : undefined}
|
||||
valueLabel={insights?.server_occupied_perc}
|
||||
/>
|
||||
<Datapoint name={t('html.label.playtime')} icon={faClock} color="green"
|
||||
value={data?.insights.total_playtime}
|
||||
value={insights?.total_playtime}
|
||||
/>
|
||||
<Datapoint name={t('html.label.afkTime')} icon={faClock} color="grey"
|
||||
value={data?.insights.afk_time} valueLabel={data?.insights.afk_time_perc}
|
||||
value={insights?.afk_time} valueLabel={insights?.afk_time_perc}
|
||||
/>
|
||||
</InsightsFor30DaysCard>
|
||||
)
|
||||
|
@ -1,20 +1,16 @@
|
||||
import React from "react";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {useDataRequest} from "../../../../hooks/dataFetchHook";
|
||||
import {fetchSessions} from "../../../../service/serverService";
|
||||
import {ErrorViewCard} from "../../../../views/ErrorView";
|
||||
import RecentSessionsCard from "../../common/RecentSessionsCard";
|
||||
|
||||
const ServerRecentSessionsCard = () => {
|
||||
|
||||
const {identifier} = useParams();
|
||||
|
||||
const ServerRecentSessionsCard = ({identifier}) => {
|
||||
const {data, loadingError} = useDataRequest(fetchSessions, [identifier])
|
||||
|
||||
if (loadingError) return <ErrorViewCard error={loadingError}/>
|
||||
|
||||
return (
|
||||
<RecentSessionsCard sessions={data?.sessions} isPlayer={true}/>
|
||||
<RecentSessionsCard sessions={data?.sessions} isPlayer={true} isNetwork={!identifier}/>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,8 @@ const ExtensionIcon = ({icon}) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const toExtensionIconHtmlString = ({icon}) => {
|
||||
return icon ? `<i class="${iconTypeToFontAwesomeClass(icon.family)} ${icon.iconName} ${icon.colorClass}"></i>` : '';
|
||||
export const toExtensionIconHtmlString = (icon) => {
|
||||
return icon ? `<i class="${iconTypeToFontAwesomeClass(icon.family)} fa-${icon.iconName} ${icon.colorClass}"></i>` : '';
|
||||
}
|
||||
|
||||
export default ExtensionIcon;
|
||||
|
@ -1,12 +1,34 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {withReducedSaturation} from "../../util/colors";
|
||||
import Highcharts from 'highcharts/highmaps.js';
|
||||
import map from '@highcharts/map-collection/custom/world.geo.json';
|
||||
import topology from '@highcharts/map-collection/custom/world.topo.json';
|
||||
import Accessibility from "highcharts/modules/accessibility";
|
||||
import NoDataDisplay from "highcharts/modules/no-data-to-display";
|
||||
|
||||
const GeolocationWorldMap = ({series, colors}) => {
|
||||
export const ProjectionOptions = {
|
||||
MILLER: "html.label.geoProjection.miller",
|
||||
MERCATOR: "html.label.geoProjection.mercator",
|
||||
EQUAL_EARTH: "html.label.geoProjection.equalEarth"
|
||||
// ORTOGRAPHIC: "html.label.geoProjection.ortographic"
|
||||
}
|
||||
|
||||
const getProjection = option => {
|
||||
switch (option) {
|
||||
case ProjectionOptions.MERCATOR:
|
||||
return {name: 'WebMercator'};
|
||||
case ProjectionOptions.EQUAL_EARTH:
|
||||
return {name: 'EqualEarth'};
|
||||
// Ortographic projection stops working after a while for some reason
|
||||
// case ProjectionOptions.ORTOGRAPHIC:
|
||||
// return {name: 'Orthographic'};
|
||||
case ProjectionOptions.MILLER:
|
||||
default:
|
||||
return {name: 'Miller'};
|
||||
}
|
||||
}
|
||||
|
||||
const GeolocationWorldMap = ({series, colors, projection}) => {
|
||||
const {t} = useTranslation();
|
||||
const {nightModeEnabled, graphTheming} = useTheme();
|
||||
|
||||
@ -14,33 +36,41 @@ const GeolocationWorldMap = ({series, colors}) => {
|
||||
const mapSeries = {
|
||||
name: t('html.label.players'),
|
||||
type: 'map',
|
||||
mapData: map,
|
||||
data: series,
|
||||
joinBy: ['iso-a3', 'code']
|
||||
};
|
||||
|
||||
NoDataDisplay(Highcharts);
|
||||
Accessibility(Highcharts);
|
||||
Highcharts.setOptions(graphTheming);
|
||||
Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}});
|
||||
Highcharts.mapChart('countryWorldMap', {
|
||||
chart: {
|
||||
map: topology,
|
||||
animation: true
|
||||
},
|
||||
title: {text: ''},
|
||||
|
||||
mapNavigation: {
|
||||
enabled: true,
|
||||
enableDoubleClickZoomTo: true
|
||||
enableDoubleClickZoomTo: true,
|
||||
enableMouseWheelZoom: true,
|
||||
enableTouchZoom: true
|
||||
},
|
||||
|
||||
mapView: {
|
||||
projection: getProjection(projection)
|
||||
},
|
||||
|
||||
colorAxis: {
|
||||
min: 1,
|
||||
type: 'logarithmic',
|
||||
minColor: nightModeEnabled ? withReducedSaturation(colors.low) : colors.low,
|
||||
maxColor: nightModeEnabled ? withReducedSaturation(colors.high) : colors.high
|
||||
minColor: colors.low,
|
||||
maxColor: colors.high
|
||||
},
|
||||
series: [mapSeries]
|
||||
})
|
||||
}, [colors, series, graphTheming, nightModeEnabled, t]);
|
||||
}, [colors, series, graphTheming, nightModeEnabled, t, projection]);
|
||||
|
||||
return (<div id="countryWorldMap"/>);
|
||||
};
|
||||
|
@ -0,0 +1,56 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {withReducedSaturation} from "../../util/colors";
|
||||
import Highcharts from "highcharts";
|
||||
import Accessibility from "highcharts/modules/accessibility";
|
||||
|
||||
const GroupBarGraph = ({id, groups, colors, horizontal, name}) => {
|
||||
const {t} = useTranslation();
|
||||
const {nightModeEnabled, graphTheming} = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const reduceColors = (colorsToReduce) => colorsToReduce.map(color => withReducedSaturation(color));
|
||||
|
||||
function getColors() {
|
||||
const actualColors = colors ? colors : groups.map(group => group.color);
|
||||
return nightModeEnabled ? reduceColors(actualColors) : actualColors;
|
||||
}
|
||||
|
||||
const bars = groups.map(group => group.y);
|
||||
const categories = groups.map(group => t(group.name));
|
||||
const barSeries = {
|
||||
name: name,
|
||||
colorByPoint: true,
|
||||
data: bars,
|
||||
colors: getColors()
|
||||
};
|
||||
|
||||
Accessibility(Highcharts);
|
||||
Highcharts.setOptions(graphTheming);
|
||||
Highcharts.chart(id, {
|
||||
chart: {type: horizontal ? 'bar' : 'column'},
|
||||
title: {text: ''},
|
||||
xAxis: {
|
||||
categories: categories,
|
||||
title: {text: ''}
|
||||
},
|
||||
yAxis: {
|
||||
min: 0,
|
||||
title: {text: '', align: 'high'},
|
||||
labels: {overflow: 'justify'}
|
||||
},
|
||||
legend: {enabled: false},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
dataLabels: {enabled: true}
|
||||
}
|
||||
},
|
||||
series: [barSeries]
|
||||
})
|
||||
}, [id, groups, colors, horizontal, name, graphTheming, nightModeEnabled, t]);
|
||||
|
||||
return (<div id={id} className="chart-area"/>);
|
||||
};
|
||||
|
||||
export default GroupBarGraph
|
@ -1,28 +1,35 @@
|
||||
import React, {useEffect} from "react";
|
||||
import Highcharts from 'highcharts';
|
||||
import React, {useEffect} from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {withReducedSaturation} from "../../util/colors";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import Accessibility from "highcharts/modules/accessibility";
|
||||
import Highcharts from "highcharts";
|
||||
|
||||
const PlayerbasePie = ({series}) => {
|
||||
const GroupPie = ({id, groups, colors, name}) => {
|
||||
const {t} = useTranslation();
|
||||
const {nightModeEnabled, graphTheming} = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const reduceColors = (slices) => slices.map(slice => {
|
||||
return {...slice, color: withReducedSaturation(slice.color)}
|
||||
});
|
||||
const reduceColors = (colorsToReduce) => colorsToReduce.map(color => withReducedSaturation(color));
|
||||
|
||||
function getColors() {
|
||||
const actualColors = colors ? colors : groups.map(group => group.color);
|
||||
return nightModeEnabled ? reduceColors(actualColors) : actualColors;
|
||||
}
|
||||
|
||||
const series = groups.map(group => {
|
||||
return {name: t(group.name), y: group.y}
|
||||
});
|
||||
const pieSeries = {
|
||||
name: t('html.label.players'),
|
||||
name: name,
|
||||
colorByPoint: true,
|
||||
data: nightModeEnabled ? reduceColors(series) : series
|
||||
colors: getColors(),
|
||||
data: series
|
||||
};
|
||||
|
||||
Accessibility(Highcharts);
|
||||
Highcharts.setOptions(graphTheming);
|
||||
Highcharts.chart('playerbase-pie', {
|
||||
Highcharts.chart(id, {
|
||||
chart: {
|
||||
backgroundColor: 'transparent',
|
||||
plotBorderWidth: null,
|
||||
@ -40,11 +47,16 @@ const PlayerbasePie = ({series}) => {
|
||||
showInLegend: true
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
formatter: function () {
|
||||
return '<b>' + this.point.name + ':</b> ' + this.y;
|
||||
}
|
||||
},
|
||||
series: [pieSeries]
|
||||
});
|
||||
}, [series, graphTheming, nightModeEnabled, t]);
|
||||
}, [id, colors, groups, name, graphTheming, nightModeEnabled, t]);
|
||||
|
||||
return (<div className="chart-area" id="playerbase-pie"/>);
|
||||
}
|
||||
return (<div className="chart-area" id={id}/>);
|
||||
};
|
||||
|
||||
export default PlayerbasePie;
|
||||
export default GroupPie;
|
@ -0,0 +1,63 @@
|
||||
import React, {useState} from 'react';
|
||||
import GroupTable from "../table/GroupTable";
|
||||
import GroupPie from "./GroupPie";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faBarChart, faChartColumn, faPieChart, faTable} from "@fortawesome/free-solid-svg-icons";
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import GroupBarGraph from "./GroupBarGraph";
|
||||
|
||||
const options = {
|
||||
BAR: 'bar',
|
||||
COLUMN: 'column',
|
||||
PIE: 'pie',
|
||||
TABLE: 'table'
|
||||
}
|
||||
|
||||
const Visualizer = ({option, groups, colors, name}) => {
|
||||
switch (option) {
|
||||
case options.TABLE:
|
||||
return <GroupTable groups={groups} colors={colors}/>
|
||||
case options.PIE:
|
||||
return <GroupPie id={'group-pie-' + new Date()} groups={groups} colors={colors} name={name}/>
|
||||
case options.BAR:
|
||||
return <GroupBarGraph id={'group-bar-' + new Date()} groups={groups} colors={colors} name={name}
|
||||
horizontal/>;
|
||||
case options.COLUMN:
|
||||
default:
|
||||
return <GroupBarGraph id={'group-bar-' + new Date()} groups={groups} colors={colors} name={name}/>;
|
||||
}
|
||||
}
|
||||
|
||||
const VisualizerSelector = ({onClick, icon}) => {
|
||||
return (
|
||||
<button className="btn float-end" onClick={onClick}>
|
||||
<FontAwesomeIcon icon={icon} className="col-gray"/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
const GroupVisualizer = ({groups, colors, name, horizontal}) => {
|
||||
const [visualization, setVisualization] = useState(groups.length > 1 ? options.COLUMN : options.TABLE);
|
||||
|
||||
const selectorFloatStyle = {
|
||||
height: "0",
|
||||
zIndex: 100,
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
right: "0",
|
||||
top: "0.5rem"
|
||||
};
|
||||
return <Row>
|
||||
<Col md={12} style={selectorFloatStyle}>
|
||||
<VisualizerSelector icon={faPieChart} onClick={() => setVisualization(options.PIE)}/>
|
||||
<VisualizerSelector icon={faTable} onClick={() => setVisualization(options.TABLE)}/>
|
||||
<VisualizerSelector icon={horizontal ? faBarChart : faChartColumn}
|
||||
onClick={() => setVisualization(horizontal ? options.BAR : options.COLUMN)}/>
|
||||
</Col>
|
||||
<Col md={12}>
|
||||
<Visualizer option={visualization} groups={groups} colors={colors} name={name}/>
|
||||
</Col>
|
||||
</Row>
|
||||
};
|
||||
|
||||
export default GroupVisualizer
|
@ -0,0 +1,82 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {withReducedSaturation} from "../../util/colors";
|
||||
import NoDataDisplay from "highcharts/modules/no-data-to-display";
|
||||
import Highcharts from "highcharts/highstock";
|
||||
import Accessibility from "highcharts/modules/accessibility";
|
||||
import {linegraphButtons} from "../../util/graphs";
|
||||
|
||||
const JoinAddressGraph = ({id, data, colors, stack}) => {
|
||||
const {t} = useTranslation()
|
||||
const {nightModeEnabled, graphTheming} = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const getColor = i => {
|
||||
const color = colors[i % colors.length];
|
||||
return nightModeEnabled ? withReducedSaturation(color) : color;
|
||||
}
|
||||
|
||||
NoDataDisplay(Highcharts);
|
||||
Accessibility(Highcharts);
|
||||
Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}})
|
||||
Highcharts.setOptions(graphTheming);
|
||||
|
||||
const valuesByAddress = {};
|
||||
const dates = []
|
||||
for (const point of data || []) {
|
||||
dates.push(point.date);
|
||||
for (const address of point.joinAddresses) {
|
||||
if (!valuesByAddress[address.joinAddress]) valuesByAddress[address.joinAddress] = [];
|
||||
valuesByAddress[address.joinAddress].push([point.date, address.count]);
|
||||
}
|
||||
}
|
||||
|
||||
const labels = dates;
|
||||
const series = Object.entries(valuesByAddress).map((entry, i) => {
|
||||
if (i >= colors.length) return {name: entry[0], data: entry[1]};
|
||||
return {name: entry[0], data: entry[1], color: getColor(i)};
|
||||
});
|
||||
|
||||
Highcharts.stockChart(id, {
|
||||
chart: {
|
||||
type: "column"
|
||||
},
|
||||
rangeSelector: {
|
||||
selected: 3,
|
||||
buttons: linegraphButtons
|
||||
},
|
||||
xAxis: {
|
||||
categories: labels,
|
||||
tickmarkPlacement: 'on',
|
||||
title: {
|
||||
enabled: false
|
||||
},
|
||||
ordinal: false
|
||||
},
|
||||
yAxis: {
|
||||
softMax: 2,
|
||||
softMin: 0
|
||||
},
|
||||
title: {text: ''},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: stack ? 'normal' : undefined,
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
enabled: true
|
||||
},
|
||||
series: series
|
||||
})
|
||||
}, [data, colors, graphTheming, id, t, nightModeEnabled, stack])
|
||||
|
||||
return (
|
||||
<div className="chart-area" style={{height: "450px"}} id={id}>
|
||||
<span className="loader"/>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export default JoinAddressGraph
|
@ -6,7 +6,7 @@ import NoDataDisplay from "highcharts/modules/no-data-to-display"
|
||||
import Accessibility from "highcharts/modules/accessibility"
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
const LineGraph = ({id, series}) => {
|
||||
const LineGraph = ({id, series, legendEnabled, tall, yAxis}) => {
|
||||
const {t} = useTranslation()
|
||||
const {graphTheming, nightModeEnabled} = useTheme();
|
||||
|
||||
@ -20,7 +20,7 @@ const LineGraph = ({id, series}) => {
|
||||
selected: 2,
|
||||
buttons: linegraphButtons
|
||||
},
|
||||
yAxis: {
|
||||
yAxis: yAxis || {
|
||||
softMax: 2,
|
||||
softMin: 0
|
||||
},
|
||||
@ -30,12 +30,17 @@ const LineGraph = ({id, series}) => {
|
||||
fillOpacity: nightModeEnabled ? 0.2 : 0.4
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
enabled: legendEnabled
|
||||
},
|
||||
series: series
|
||||
})
|
||||
}, [series, graphTheming, id, t, nightModeEnabled])
|
||||
}, [series, graphTheming, id, t, nightModeEnabled, legendEnabled, yAxis])
|
||||
|
||||
const style = tall ? {height: "450px"} : undefined;
|
||||
|
||||
return (
|
||||
<div className="chart-area" id={id}>
|
||||
<div className="chart-area" style={style} id={id}>
|
||||
<span className="loader"/>
|
||||
</div>
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ import {formatTimeAmount} from '../../util/formatters'
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {withReducedSaturation} from "../../util/colors";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import NoDataDisplay from "highcharts/modules/no-data-to-display";
|
||||
import Accessibility from "highcharts/modules/accessibility";
|
||||
|
||||
const ServerPie = ({colors, series}) => {
|
||||
@ -21,8 +22,10 @@ const ServerPie = ({colors, series}) => {
|
||||
data: series
|
||||
};
|
||||
|
||||
NoDataDisplay(Highcharts);
|
||||
Accessibility(Highcharts);
|
||||
Highcharts.setOptions(graphTheming);
|
||||
Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}});
|
||||
Highcharts.chart('server-pie', {
|
||||
chart: {
|
||||
backgroundColor: 'transparent',
|
||||
|
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import DropdownToggle from "react-bootstrap-v5/lib/esm/DropdownToggle";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import DropdownMenu from "react-bootstrap-v5/lib/esm/DropdownMenu";
|
||||
import DropdownItem from "react-bootstrap-v5/lib/esm/DropdownItem";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
const BasicDropdown = ({selected, optionList, onChange, optionLabelMapper, icon, title}) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
return (
|
||||
<BasicDropdown className="float-end" style={{position: "absolute", right: "0.5rem"}} title={t(title)}>
|
||||
<DropdownToggle variant=''>
|
||||
<Fa icon={icon}/> {t(optionLabelMapper ? optionLabelMapper(selected) : selected)}
|
||||
</DropdownToggle>
|
||||
|
||||
<DropdownMenu>
|
||||
<h6 className="dropdown-header">{t(title)}</h6>
|
||||
{optionList.map((option, i) => (
|
||||
<DropdownItem key={i} onClick={() => onChange(option)}>
|
||||
{t(optionLabelMapper ? optionLabelMapper(option) : option)}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</BasicDropdown>
|
||||
)
|
||||
};
|
||||
|
||||
export default BasicDropdown
|
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
const MultiSelect = ({options, selectedIndexes, setSelectedIndexes}) => {
|
||||
const handleChange = (event) => {
|
||||
const renderedOptions = Object.values(event.target.selectedOptions)
|
||||
.map(htmlElement => htmlElement.text)
|
||||
.map(option => options.indexOf(option));
|
||||
setSelectedIndexes(renderedOptions);
|
||||
}
|
||||
|
||||
return (
|
||||
<select className="form-control" multiple
|
||||
onChange={handleChange}>
|
||||
{options.map((option, i) => {
|
||||
return (
|
||||
<option key={i} value={selectedIndexes.includes(i)}
|
||||
selected={selectedIndexes.includes(i)}>{option}</option>
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
)
|
||||
};
|
||||
|
||||
export default MultiSelect
|
20
react/dashboard/dashboard/src/components/input/Toggle.js
Normal file
20
react/dashboard/dashboard/src/components/input/Toggle.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React, {useState} from 'react';
|
||||
|
||||
const Toggle = ({children, value, onValueChange, color}) => {
|
||||
const [renderTime] = useState(new Date().getTime());
|
||||
const id = 'checkbox-' + renderTime;
|
||||
|
||||
const handleChange = () => {
|
||||
onValueChange(!value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form-check form-switch">
|
||||
<input id={id} type={"checkbox"} className={"form-check-input bg-" + color} role="switch"
|
||||
onChange={handleChange} checked={value}/>
|
||||
<label className="form-check-label" htmlFor={id}>{children}</label>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export default Toggle
|
@ -36,7 +36,7 @@ const Header = ({page, tab}) => {
|
||||
const {toggleColorChooser} = useTheme();
|
||||
const {t} = useTranslation();
|
||||
|
||||
const {requestUpdate, updating, lastUpdate, toggleSidebar} = useNavigation();
|
||||
const {requestUpdate, lastUpdate, updating, toggleSidebar} = useNavigation();
|
||||
|
||||
const {getPlayerHeadImageUrl} = useMetadata();
|
||||
const headImageUrl = user ? getPlayerHeadImageUrl(user.playerName, user.linkedToUuid) : undefined
|
||||
@ -59,7 +59,7 @@ const Header = ({page, tab}) => {
|
||||
<span className="topbar-divider"/>
|
||||
<div className="refresh-element">
|
||||
<button onClick={requestUpdate}>
|
||||
<Fa icon={faSyncAlt} spin={updating}/>
|
||||
<Fa icon={faSyncAlt} spin={Boolean(updating)}/>
|
||||
</button>
|
||||
{' '}
|
||||
<span className="refresh-time">{lastUpdate.formatted}</span>
|
||||
|
47
react/dashboard/dashboard/src/components/table/GroupTable.js
Normal file
47
react/dashboard/dashboard/src/components/table/GroupTable.js
Normal file
@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {withReducedSaturation} from "../../util/colors";
|
||||
import Scrollable from "../Scrollable";
|
||||
|
||||
const GroupRow = ({group, color}) => {
|
||||
return (
|
||||
<tr>
|
||||
<td style={{color}}>{group.name}</td>
|
||||
<td>{group.y}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
const GroupTable = ({groups, colors}) => {
|
||||
const {t} = useTranslation();
|
||||
const {nightModeEnabled} = useTheme();
|
||||
|
||||
function getColor(i) {
|
||||
if (groups[i].color) {
|
||||
return nightModeEnabled ? withReducedSaturation(groups[i].color) : groups[i].color;
|
||||
}
|
||||
return nightModeEnabled ? withReducedSaturation(colors[i]) : colors[i];
|
||||
}
|
||||
|
||||
return (
|
||||
<Scrollable>
|
||||
<table className={"table mb-0" + (nightModeEnabled ? " table-dark" : '')}>
|
||||
<tbody>
|
||||
{groups.length ? groups.map((group, i) =>
|
||||
<GroupRow key={i}
|
||||
group={group}
|
||||
color={getColor(i)}/>) :
|
||||
<tr>
|
||||
<td>{t('generic.noData')}</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>}
|
||||
</tbody>
|
||||
</table>
|
||||
</Scrollable>
|
||||
)
|
||||
};
|
||||
|
||||
export default GroupTable
|
@ -14,11 +14,11 @@ import {TableRow} from "./TableRow";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faEye} from "@fortawesome/free-regular-svg-icons";
|
||||
import AsNumbersTable from "./AsNumbersTable";
|
||||
import {CardLoader} from "../navigation/Loader";
|
||||
import {ChartLoader} from "../navigation/Loader";
|
||||
|
||||
const PerformanceAsNumbersTable = ({data}) => {
|
||||
const {t} = useTranslation();
|
||||
if (!data) return <CardLoader/>;
|
||||
if (!data) return <ChartLoader/>;
|
||||
|
||||
return (
|
||||
<AsNumbersTable
|
||||
@ -31,12 +31,19 @@ const PerformanceAsNumbersTable = ({data}) => {
|
||||
data.low_tps_spikes_24h
|
||||
]}/>
|
||||
<TableRow icon={faPowerOff} color="red"
|
||||
text={t('html.label.serverDowntime') + ' (' + t('generic.noData') + ')'}
|
||||
text={t(data.avg_server_downtime_30d ? 'html.label.serverDowntime' : 'html.label.totalServerDowntime') + ' (' + t('generic.noData') + ')'}
|
||||
values={[
|
||||
data.server_downtime_30d,
|
||||
data.server_downtime_7d,
|
||||
data.server_downtime_24h
|
||||
]}/>
|
||||
<TableRow icon={faPowerOff} color="red"
|
||||
text={t('html.label.averageServerDowntime')}
|
||||
values={[
|
||||
data.avg_server_downtime_30d,
|
||||
data.avg_server_downtime_7d,
|
||||
data.avg_server_downtime_24h
|
||||
]}/>
|
||||
<TableRow icon={faUser} color="light-blue" text={t('html.label.averagePlayers')}
|
||||
values={[
|
||||
data.players_30d,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import Scrollable from "../Scrollable";
|
||||
|
||||
const PingRow = ({country}) => {
|
||||
return (
|
||||
@ -18,24 +19,26 @@ const PingTable = ({countries}) => {
|
||||
const {nightModeEnabled} = useTheme();
|
||||
|
||||
return (
|
||||
<table className={"table mb-0" + (nightModeEnabled ? " table-dark" : '')}>
|
||||
<thead className="bg-amber">
|
||||
<tr>
|
||||
<th>{t('html.label.country')}</th>
|
||||
<th>{t('html.label.averagePing')}</th>
|
||||
<th>{t('html.label.bestPing')}</th>
|
||||
<th>{t('html.label.worstPing')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{countries.length ? countries.map((country, i) => <PingRow key={i} country={country}/>) : <tr>
|
||||
<td>{t('generic.noData')}</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>}
|
||||
</tbody>
|
||||
</table>
|
||||
<Scrollable>
|
||||
<table className={"table mb-0" + (nightModeEnabled ? " table-dark" : '')}>
|
||||
<thead className="bg-amber" style={{position: "sticky", top: 0}}>
|
||||
<tr>
|
||||
<th>{t('html.label.country')}</th>
|
||||
<th>{t('html.label.averagePing')}</th>
|
||||
<th>{t('html.label.bestPing')}</th>
|
||||
<th>{t('html.label.worstPing')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{countries.length ? countries.map((country, i) => <PingRow key={i} country={country}/>) : <tr>
|
||||
<td>{t('generic.noData')}</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>}
|
||||
</tbody>
|
||||
</table>
|
||||
</Scrollable>
|
||||
)
|
||||
};
|
||||
|
||||
|
153
react/dashboard/dashboard/src/components/table/ServersTable.js
Normal file
153
react/dashboard/dashboard/src/components/table/ServersTable.js
Normal file
@ -0,0 +1,153 @@
|
||||
import React from "react";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faCaretSquareRight,
|
||||
faLineChart,
|
||||
faLink,
|
||||
faServer,
|
||||
faSortAlphaDown,
|
||||
faSortAlphaUp,
|
||||
faSortNumericDown,
|
||||
faSortNumericUp,
|
||||
faUser,
|
||||
faUsers
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import Scrollable from "../Scrollable";
|
||||
import {NavLink} from "react-router-dom";
|
||||
|
||||
const ServerRow = ({server, onQuickView}) => {
|
||||
const {t} = useTranslation();
|
||||
return (
|
||||
<tr>
|
||||
<td>{server.name}</td>
|
||||
<td className="p-1">
|
||||
<NavLink to={"/server/" + encodeURIComponent(server.serverUUID)}
|
||||
title={t('html.label.serverAnalysis') + ': ' + server.name}
|
||||
className={'btn bg-transparent col-light-green'}><Fa
|
||||
icon={faLink}/> {t('html.label.serverAnalysis')}
|
||||
</NavLink>
|
||||
</td>
|
||||
<td>{server.players}</td>
|
||||
<td>{server.online}</td>
|
||||
<td className="p-1">
|
||||
<button className={'btn bg-light-blue float-right'}
|
||||
title={t('html.label.quickView') + ': ' + server.name}
|
||||
onClick={onQuickView}
|
||||
>
|
||||
<Fa icon={faCaretSquareRight}/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
const sortKeepOrder = () => 0;
|
||||
const sortBySometimesNumericProperty = (propertyName) => (a, b) => {
|
||||
if (typeof (a[propertyName]) === 'number' && typeof (b[propertyName]) === 'number') return a[propertyName] - b[propertyName];
|
||||
if (typeof (a[propertyName]) === 'number') return 1;
|
||||
if (typeof (b[propertyName]) === 'number') return -1;
|
||||
return 0;
|
||||
}
|
||||
const sortByNumericProperty = (propertyName) => (a, b) => b[propertyName] - a[propertyName]; // Biggest first
|
||||
const sortBeforeReverse = (servers, sortBy) => {
|
||||
return [...servers].sort(sortBy.sortFunction);
|
||||
}
|
||||
|
||||
const reverse = (array) => {
|
||||
const reversedArray = [];
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
reversedArray.push(array[i]);
|
||||
}
|
||||
return reversedArray;
|
||||
}
|
||||
|
||||
const sort = (servers, sortBy, sortReversed) => {
|
||||
return sortReversed ? reverse(sortBeforeReverse(servers, sortBy)) : sortBeforeReverse(servers, sortBy);
|
||||
}
|
||||
|
||||
const SortOptionIcon = {
|
||||
LETTERS: {
|
||||
iconAsc: faSortAlphaDown,
|
||||
iconDesc: faSortAlphaUp
|
||||
},
|
||||
NUMBERS: {
|
||||
iconAsc: faSortNumericUp,
|
||||
iconDesc: faSortNumericDown
|
||||
}
|
||||
}
|
||||
|
||||
export const ServerSortOption = {
|
||||
ALPHABETICAL: {
|
||||
label: 'html.label.alphabetical',
|
||||
sortFunction: sortKeepOrder,
|
||||
...SortOptionIcon.LETTERS
|
||||
},
|
||||
AVERAGE_TPS: {
|
||||
label: 'html.label.averageTps7days',
|
||||
sortFunction: sortBySometimesNumericProperty('avg_tps'),
|
||||
...SortOptionIcon.NUMBERS
|
||||
},
|
||||
// DOWNTIME: 'html.label.downtime',
|
||||
LOW_TPS_SPIKES: {
|
||||
label: 'html.label.lowTpsSpikes7days',
|
||||
sortFunction: sortByNumericProperty('low_tps_spikes'),
|
||||
...SortOptionIcon.NUMBERS
|
||||
},
|
||||
NEW_PLAYERS: {
|
||||
label: 'html.label.newPlayers7days',
|
||||
sortFunction: sortByNumericProperty('new_players'),
|
||||
...SortOptionIcon.NUMBERS
|
||||
},
|
||||
PLAYERS_ONLINE: {
|
||||
label: 'html.label.playersOnlineNow',
|
||||
sortFunction: sortBySometimesNumericProperty('online'),
|
||||
...SortOptionIcon.NUMBERS
|
||||
},
|
||||
REGISTERED_PLAYERS: {
|
||||
label: 'html.label.registeredPlayers',
|
||||
sortFunction: sortByNumericProperty('players'),
|
||||
...SortOptionIcon.NUMBERS
|
||||
},
|
||||
UNIQUE_PLAYERS: {
|
||||
label: 'html.label.uniquePlayers7days',
|
||||
sortFunction: sortByNumericProperty('unique_players'),
|
||||
...SortOptionIcon.NUMBERS
|
||||
},
|
||||
}
|
||||
|
||||
const ServersTable = ({servers, onSelect, sortBy, sortReversed}) => {
|
||||
const {t} = useTranslation();
|
||||
const {nightModeEnabled} = useTheme();
|
||||
|
||||
const sortedServers = sort(servers, sortBy, sortReversed);
|
||||
|
||||
return (
|
||||
<Scrollable>
|
||||
<table className={"table mb-0 table-striped" + (nightModeEnabled ? " table-dark" : '')}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><Fa icon={faServer}/> {t('html.label.server')}</th>
|
||||
<th><Fa icon={faLineChart}/> {t('html.label.serverAnalysis')}</th>
|
||||
<th><Fa icon={faUsers}/> {t('html.label.registeredPlayers')}</th>
|
||||
<th><Fa icon={faUser}/> {t('html.label.playersOnline')}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{sortedServers.length ? sortedServers.map((server, i) => <ServerRow key={i} server={server}
|
||||
onQuickView={() => onSelect(servers.indexOf(server))}/>) :
|
||||
<tr>
|
||||
<td>{t('html.generic.none')}</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>}
|
||||
</tbody>
|
||||
</table>
|
||||
</Scrollable>
|
||||
)
|
||||
};
|
||||
|
||||
export default ServersTable;
|
@ -1,23 +1,64 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {useNavigation} from "./navigationHook";
|
||||
import {useDataStore} from "./datastoreHook";
|
||||
import {useMetadata} from "./metadataHook";
|
||||
|
||||
export const useDataRequest = (fetchMethod, parameters) => {
|
||||
const [data, setData] = useState(undefined);
|
||||
const [loadingError, setLoadingError] = useState(undefined);
|
||||
const {updateRequested, finishUpdate} = useNavigation();
|
||||
const {refreshBarrierMs} = useMetadata();
|
||||
const datastore = useDataStore();
|
||||
|
||||
/*eslint-disable react-hooks/exhaustive-deps */
|
||||
useEffect(() => {
|
||||
fetchMethod(updateRequested, ...parameters).then(({data: json, error}) => {
|
||||
datastore.setAsUpdating(fetchMethod);
|
||||
const handleResponse = (json, error, skipOldData, timeout) => {
|
||||
if (json) {
|
||||
setData(json);
|
||||
finishUpdate(json.timestamp, json.timestamp_f);
|
||||
const timestamp = json.timestamp;
|
||||
if (timestamp) {
|
||||
// Data has timestamp, the data may come from cache
|
||||
const acceptedTimestamp = timestamp + (refreshBarrierMs ? refreshBarrierMs : 15000);
|
||||
if (acceptedTimestamp < updateRequested) {
|
||||
// Request again, received data was too old
|
||||
setTimeout(() => {
|
||||
fetchMethod(new Date().getTime(), ...parameters)
|
||||
.then(({data: json, error}) => {
|
||||
handleResponse(json, error, true, timeout >= 12000 ? timeout : timeout * 2);
|
||||
});
|
||||
}, timeout);
|
||||
} else {
|
||||
// Received data was new enough to be shown
|
||||
setData(json);
|
||||
datastore.storeData(fetchMethod, json);
|
||||
datastore.finishUpdate(fetchMethod)
|
||||
finishUpdate(json.timestamp, json.timestamp_f, datastore.isSomethingUpdating());
|
||||
}
|
||||
|
||||
if (!skipOldData) {
|
||||
// Old data is shown on first pass, further passes skip old data.
|
||||
setData(json);
|
||||
datastore.storeData(fetchMethod, json);
|
||||
finishUpdate(json.timestamp, json.timestamp_f, datastore.isSomethingUpdating());
|
||||
}
|
||||
} else {
|
||||
// Response data is not cached, no timestamp, show it immediately
|
||||
setData(json);
|
||||
datastore.finishUpdate(fetchMethod);
|
||||
finishUpdate(json.timestamp, json.timestamp_f, datastore.isSomethingUpdating());
|
||||
}
|
||||
} else if (error) {
|
||||
console.warn(error);
|
||||
datastore.finishUpdate(fetchMethod)
|
||||
setLoadingError(error);
|
||||
finishUpdate(0, "Error: " + error.message, datastore.isSomethingUpdating());
|
||||
}
|
||||
};
|
||||
|
||||
fetchMethod(updateRequested, ...parameters).then(({data: json, error}) => {
|
||||
handleResponse(json, error, false, 1000);
|
||||
});
|
||||
}, [fetchMethod, ...parameters, updateRequested])
|
||||
}, [fetchMethod, ...parameters, updateRequested, refreshBarrierMs])
|
||||
/* eslint-enable react-hooks/exhaustive-deps */
|
||||
|
||||
return {data, loadingError};
|
||||
|
45
react/dashboard/dashboard/src/hooks/datastoreHook.js
Normal file
45
react/dashboard/dashboard/src/hooks/datastoreHook.js
Normal file
@ -0,0 +1,45 @@
|
||||
import {useCallback} from "react";
|
||||
import {useMetadata} from "./metadataHook";
|
||||
|
||||
export const useDataStore = () => {
|
||||
const {datastore} = useMetadata();
|
||||
|
||||
if (datastore && !datastore.dataByMethod) datastore.dataByMethod = {};
|
||||
if (datastore && !datastore.lastUpdateByMethod) datastore.lastUpdateByMethod = {};
|
||||
if (datastore && !datastore.currentlyUpdatingMethods) datastore.currentlyUpdatingMethods = {};
|
||||
|
||||
const storeData = useCallback((method, data) => {
|
||||
const hadPrevious = Boolean(datastore.dataByMethod[method]);
|
||||
if (data) {
|
||||
datastore.lastUpdateByMethod[method] = data.timestamp;
|
||||
datastore.dataByMethod[method] = data;
|
||||
}
|
||||
return hadPrevious;
|
||||
}, [datastore]);
|
||||
|
||||
const getLastUpdate = useCallback((method) => {
|
||||
return datastore?.lastUpdateByMethod[method];
|
||||
}, [datastore]);
|
||||
|
||||
const getData = useCallback((method) => {
|
||||
return datastore?.dataByMethod[method];
|
||||
}, [datastore]);
|
||||
|
||||
const isCurrentlyUpdating = useCallback((method) => {
|
||||
return datastore && Boolean(datastore.currentlyUpdatingMethods[method]);
|
||||
}, [datastore]);
|
||||
|
||||
const setAsUpdating = useCallback((method) => {
|
||||
datastore.currentlyUpdatingMethods[method] = true;
|
||||
}, [datastore]);
|
||||
|
||||
const finishUpdate = useCallback((method) => {
|
||||
delete datastore.currentlyUpdatingMethods[method];
|
||||
}, [datastore])
|
||||
|
||||
const isSomethingUpdating = useCallback(() => {
|
||||
return datastore && Boolean(Object.values(datastore.currentlyUpdatingMethods).filter(value => Boolean(value)).length);
|
||||
}, [datastore]);
|
||||
|
||||
return {storeData, getLastUpdate, getData, isCurrentlyUpdating, isSomethingUpdating, setAsUpdating, finishUpdate};
|
||||
}
|
@ -1,17 +1,24 @@
|
||||
import {createContext, useCallback, useContext, useEffect, useState} from "react";
|
||||
import {fetchPlanMetadata} from "../service/metadataService";
|
||||
import {fetchNetworkMetadata, fetchPlanMetadata} from "../service/metadataService";
|
||||
|
||||
import terminal from '../Terminal-icon.png'
|
||||
|
||||
const MetadataContext = createContext({});
|
||||
|
||||
export const MetadataContextProvider = ({children}) => {
|
||||
const [datastore] = useState({});
|
||||
const [metadata, setMetadata] = useState({});
|
||||
|
||||
const updateMetadata = useCallback(async () => {
|
||||
const {data, error} = await fetchPlanMetadata();
|
||||
if (data) {
|
||||
setMetadata(data);
|
||||
if (data.isProxy) {
|
||||
const {data: networkMetadata} = await fetchNetworkMetadata(); // error ignored
|
||||
if (networkMetadata) {
|
||||
setMetadata({...data, networkMetadata})
|
||||
}
|
||||
}
|
||||
} else if (error) {
|
||||
setMetadata({metadataError: error})
|
||||
}
|
||||
@ -34,7 +41,7 @@ export const MetadataContextProvider = ({children}) => {
|
||||
updateMetadata();
|
||||
}, [updateMetadata]);
|
||||
|
||||
const sharedState = {...metadata, getPlayerHeadImageUrl}
|
||||
const sharedState = {...metadata, getPlayerHeadImageUrl, datastore}
|
||||
return (<MetadataContext.Provider value={sharedState}>
|
||||
{children}
|
||||
</MetadataContext.Provider>
|
||||
|
@ -5,8 +5,8 @@ const NavigationContext = createContext({});
|
||||
export const NavigationContextProvider = ({children}) => {
|
||||
const [currentTab, setCurrentTab] = useState(undefined);
|
||||
const [updateRequested, setUpdateRequested] = useState(Date.now());
|
||||
const [updating, setUpdating] = useState(false);
|
||||
const [lastUpdate, setLastUpdate] = useState({date: 0, formatted: ""});
|
||||
const [updating, setUpdating] = useState({});
|
||||
const [lastUpdate, setLastUpdate] = useState({});
|
||||
|
||||
const [items, setItems] = useState([]);
|
||||
const [sidebarExpanded, setSidebarExpanded] = useState(window.innerWidth > 1350);
|
||||
@ -31,13 +31,17 @@ export const NavigationContextProvider = ({children}) => {
|
||||
}
|
||||
}, [updating, setUpdateRequested, setUpdating]);
|
||||
|
||||
const finishUpdate = useCallback((date, formatted) => {
|
||||
// TODO Logic to retry if received data is too old
|
||||
// TODO currently not possible due to extensionData getting updated off-tab
|
||||
// useEffect(requestUpdate, [currentTab]); // Force data to update when changing tab
|
||||
|
||||
const finishUpdate = useCallback((date, formatted, isStillUpdating) => {
|
||||
if (date) {
|
||||
setLastUpdate({date, formatted});
|
||||
setUpdating(false);
|
||||
if (!lastUpdate.date || date > lastUpdate.date) {
|
||||
setLastUpdate({date, formatted});
|
||||
}
|
||||
setUpdating(isStillUpdating);
|
||||
}
|
||||
}, [setLastUpdate, setUpdating]);
|
||||
}, [setLastUpdate, setUpdating, lastUpdate]);
|
||||
|
||||
const toggleSidebar = useCallback(() => {
|
||||
setSidebarExpanded(!sidebarExpanded);
|
||||
|
@ -3,4 +3,34 @@ import {doGetRequest} from "./backendConfiguration";
|
||||
export const fetchNetworkOverview = async (updateRequested) => {
|
||||
const url = `/v1/network/overview?timestamp=${updateRequested}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchServersOverview = async (updateRequested) => {
|
||||
const url = `/v1/network/servers?timestamp=${updateRequested}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchServerPie = async (timestamp) => {
|
||||
const url = `/v1/graph?type=serverPie×tamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchNetworkSessionsOverview = async (timestamp) => {
|
||||
const url = `/v1/network/sessionsOverview?timestamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchNetworkPlayerbaseOverview = async (timestamp) => {
|
||||
const url = `/v1/network/playerbaseOverview?timestamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchNetworkPingTable = async (timestamp) => {
|
||||
const url = `/v1/network/pingTable?timestamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchNetworkPerformanceOverview = async (timestamp, serverUUIDs) => {
|
||||
const url = `/v1/network/performanceOverview?servers=${encodeURIComponent(JSON.stringify(serverUUIDs))}×tamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
@ -42,7 +42,8 @@ export const fetchExtensionData = async (timestamp, identifier) => {
|
||||
}
|
||||
|
||||
export const fetchSessions = async (timestamp, identifier) => {
|
||||
const url = `/v1/sessions?server=${identifier}×tamp=${timestamp}`;
|
||||
const url = identifier ? `/v1/sessions?server=${identifier}×tamp=${timestamp}` :
|
||||
`/v1/sessions?timestamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
@ -57,7 +58,7 @@ export const fetchPlayers = async (timestamp, identifier) => {
|
||||
}
|
||||
|
||||
export const fetchPingTable = async (timestamp, identifier) => {
|
||||
const url = identifier ? `/v1/pingTable?server=${identifier}×tamp=${timestamp}` : `/v1/pingTable?timestamp=${timestamp}`;
|
||||
const url = `/v1/pingTable?server=${identifier}×tamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
@ -101,12 +102,13 @@ export const fetchWorldPie = async (timestamp, identifier) => {
|
||||
}
|
||||
|
||||
export const fetchGeolocations = async (timestamp, identifier) => {
|
||||
const url = `/v1/graph?type=geolocation&server=${identifier}×tamp=${timestamp}`;
|
||||
const url = identifier ? `/v1/graph?type=geolocation&server=${identifier}×tamp=${timestamp}` :
|
||||
`/v1/graph?type=geolocation×tamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchOptimizedPerformance = async (timestamp, identifier) => {
|
||||
const url = `/v1/graph?type=optimizedPerformance&server=${identifier}×tamp=${timestamp}`;
|
||||
export const fetchOptimizedPerformance = async (timestamp, identifier, after) => {
|
||||
const url = `/v1/graph?type=optimizedPerformance&server=${identifier}×tamp=${timestamp}&after=${after}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
@ -114,3 +116,15 @@ export const fetchPingGraph = async (timestamp, identifier) => {
|
||||
const url = `/v1/graph?type=aggregatedPing&server=${identifier}×tamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchJoinAddressPie = async (timestamp, identifier) => {
|
||||
const url = identifier ? `/v1/graph?type=joinAddressPie&server=${identifier}×tamp=${timestamp}` :
|
||||
`/v1/graph?type=joinAddressPie×tamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
||||
export const fetchJoinAddressByDay = async (timestamp, identifier) => {
|
||||
const url = identifier ? `/v1/graph?type=joinAddressByDay&server=${identifier}×tamp=${timestamp}` :
|
||||
`/v1/graph?type=joinAddressByDay×tamp=${timestamp}`;
|
||||
return doGetRequest(url);
|
||||
}
|
||||
|
@ -1868,6 +1868,7 @@ a.text-dark:hover, a.text-dark:focus {
|
||||
|
||||
#wrapper {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#wrapper #content-wrapper {
|
||||
|
@ -1344,4 +1344,9 @@ button, input[type="submit"], input[type="reset"] {
|
||||
.login-username {
|
||||
position: relative;
|
||||
top: 0.1rem;
|
||||
}
|
||||
|
||||
.dataTables_filter input {
|
||||
/* Fixes datatables search bar going outside cards */
|
||||
width: calc(100% - 3.7rem) !important;
|
||||
}
|
@ -22,4 +22,99 @@ export const linegraphButtons = [{
|
||||
export const tooltip = {
|
||||
twoDecimals: {valueDecimals: 2},
|
||||
zeroDecimals: {valueDecimals: 0}
|
||||
}
|
||||
|
||||
export const mapPerformanceDataToSeries = performanceData => {
|
||||
const playersOnline = [];
|
||||
const tps = [];
|
||||
const cpu = [];
|
||||
const ram = [];
|
||||
const entities = [];
|
||||
const chunks = [];
|
||||
const disk = [];
|
||||
|
||||
return new Promise((resolve => {
|
||||
let i = 0;
|
||||
const length = performanceData.length;
|
||||
|
||||
function processNextThousand() {
|
||||
const to = Math.min(i + 1000, length);
|
||||
for (i; i < to; i++) {
|
||||
const entry = performanceData[i];
|
||||
const date = entry[0];
|
||||
playersOnline[i] = [date, entry[1]];
|
||||
tps[i] = [date, entry[2]];
|
||||
cpu[i] = [date, entry[3]];
|
||||
ram[i] = [date, entry[4]];
|
||||
entities[i] = [date, entry[5]];
|
||||
chunks[i] = [date, entry[6]];
|
||||
disk[i] = [date, entry[7]];
|
||||
}
|
||||
if (i >= length) {
|
||||
resolve({playersOnline, tps, cpu, ram, entities, chunks, disk})
|
||||
} else {
|
||||
setTimeout(processNextThousand, 10);
|
||||
}
|
||||
}
|
||||
|
||||
processNextThousand();
|
||||
}))
|
||||
};
|
||||
|
||||
export const yAxisConfigurations = {
|
||||
PLAYERS_ONLINE: {
|
||||
labels: {
|
||||
formatter: function () {
|
||||
return this.value + ' P';
|
||||
}
|
||||
},
|
||||
softMin: 0,
|
||||
softMax: 2
|
||||
},
|
||||
TPS: {
|
||||
opposite: true,
|
||||
labels: {
|
||||
formatter: function () {
|
||||
return this.value + ' TPS';
|
||||
}
|
||||
},
|
||||
softMin: 0,
|
||||
softMax: 20
|
||||
},
|
||||
CPU: {
|
||||
opposite: true,
|
||||
labels: {
|
||||
formatter: function () {
|
||||
return this.value + '%';
|
||||
}
|
||||
},
|
||||
softMin: 0,
|
||||
softMax: 100
|
||||
},
|
||||
RAM_OR_DISK: {
|
||||
labels: {
|
||||
formatter: function () {
|
||||
return this.value + ' MB';
|
||||
}
|
||||
},
|
||||
softMin: 0
|
||||
},
|
||||
ENTITIES: {
|
||||
opposite: true,
|
||||
labels: {
|
||||
formatter: function () {
|
||||
return this.value + ' E';
|
||||
}
|
||||
},
|
||||
softMin: 0,
|
||||
softMax: 2
|
||||
},
|
||||
CHUNKS: {
|
||||
labels: {
|
||||
formatter: function () {
|
||||
return this.value + ' C';
|
||||
}
|
||||
},
|
||||
softMin: 0
|
||||
}
|
||||
}
|
24
react/dashboard/dashboard/src/views/common/Geolocations.js
Normal file
24
react/dashboard/dashboard/src/views/common/Geolocations.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import {ErrorViewCard} from "../ErrorView";
|
||||
import GeolocationsCard from "../../components/cards/common/GeolocationsCard";
|
||||
import PingTableCard from "../../components/cards/common/PingTableCard";
|
||||
import LoadIn from "../../components/animation/LoadIn";
|
||||
|
||||
const Geolocations = ({className, geolocationData, pingData, geolocationError, pingError}) => {
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className={className}>
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
{geolocationError ? <ErrorViewCard error={geolocationError}/> :
|
||||
<GeolocationsCard data={geolocationData}/>}
|
||||
{pingError ? <ErrorViewCard error={pingError}/> : <PingTableCard data={pingData}/>}
|
||||
</Col>
|
||||
</Row>
|
||||
</section>
|
||||
</LoadIn>
|
||||
)
|
||||
};
|
||||
|
||||
export default Geolocations
|
@ -8,6 +8,7 @@ import {
|
||||
faCubes,
|
||||
faGlobe,
|
||||
faInfoCircle,
|
||||
faLocationArrow,
|
||||
faNetworkWired,
|
||||
faSearch,
|
||||
faServer,
|
||||
@ -23,18 +24,15 @@ import {useMetadata} from "../../hooks/metadataHook";
|
||||
import {faCalendarCheck} from "@fortawesome/free-regular-svg-icons";
|
||||
import {SwitchTransition} from "react-transition-group";
|
||||
import MainPageRedirect from "../../components/navigation/MainPageRedirect";
|
||||
import ExtensionIcon from "../../components/extensions/ExtensionIcon";
|
||||
import {ServerExtensionContextProvider, useServerExtensionContext} from "../../hooks/serverExtensionDataContext";
|
||||
import {useDataRequest} from "../../hooks/dataFetchHook";
|
||||
import {fetchNetworkMetadata} from "../../service/metadataService";
|
||||
import {iconTypeToFontAwesomeClass} from "../../util/icons";
|
||||
|
||||
const NetworkSidebar = () => {
|
||||
const {t, i18n} = useTranslation();
|
||||
const {sidebarItems, setSidebarItems} = useNavigation();
|
||||
const {networkMetadata} = useMetadata();
|
||||
const {extensionData} = useServerExtensionContext();
|
||||
|
||||
const {data: networkMetadata} = useDataRequest(fetchNetworkMetadata, [])
|
||||
|
||||
useEffect(() => {
|
||||
const servers = networkMetadata?.servers || [];
|
||||
const items = [
|
||||
@ -73,6 +71,7 @@ const NetworkSidebar = () => {
|
||||
icon: faChartLine,
|
||||
href: "playerbase"
|
||||
},
|
||||
{name: 'html.label.joinAddresses', icon: faLocationArrow, href: "join-addresses"},
|
||||
// {name: 'html.label.playerRetention', icon: faUsersViewfinder, href: "retention"},
|
||||
{name: 'html.label.playerList', icon: faUserGroup, href: "players"},
|
||||
{name: 'html.label.geolocations', icon: faGlobe, href: "geolocations"},
|
||||
@ -89,7 +88,7 @@ const NetworkSidebar = () => {
|
||||
.map(info => {
|
||||
return {
|
||||
name: info.pluginName,
|
||||
icon: <ExtensionIcon icon={info.icon}/>,
|
||||
icon: [iconTypeToFontAwesomeClass(info.icon.family), info.icon.iconName],
|
||||
href: `plugins/${encodeURIComponent(info.pluginName)}`
|
||||
}
|
||||
}).forEach(item => items.push(item))
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
faCubes,
|
||||
faGlobe,
|
||||
faInfoCircle,
|
||||
faLocationArrow,
|
||||
faSearch,
|
||||
faUserGroup,
|
||||
faUsers
|
||||
@ -26,8 +27,8 @@ import {SwitchTransition} from "react-transition-group";
|
||||
import MainPageRedirect from "../../components/navigation/MainPageRedirect";
|
||||
import {useDataRequest} from "../../hooks/dataFetchHook";
|
||||
import {fetchServerIdentity} from "../../service/serverService";
|
||||
import ExtensionIcon from "../../components/extensions/ExtensionIcon";
|
||||
import {ServerExtensionContextProvider, useServerExtensionContext} from "../../hooks/serverExtensionDataContext";
|
||||
import {iconTypeToFontAwesomeClass} from "../../util/icons";
|
||||
|
||||
const ServerSidebar = () => {
|
||||
const {t, i18n} = useTranslation();
|
||||
@ -68,6 +69,7 @@ const ServerSidebar = () => {
|
||||
icon: faChartLine,
|
||||
href: "playerbase"
|
||||
},
|
||||
{name: 'html.label.joinAddresses', icon: faLocationArrow, href: "join-addresses"},
|
||||
// {name: 'html.label.playerRetention', icon: faUsersViewfinder, href: "retention"},
|
||||
{name: 'html.label.playerList', icon: faUserGroup, href: "players"},
|
||||
{name: 'html.label.geolocations', icon: faGlobe, href: "geolocations"},
|
||||
@ -85,7 +87,7 @@ const ServerSidebar = () => {
|
||||
.map(info => {
|
||||
return {
|
||||
name: info.pluginName,
|
||||
icon: <ExtensionIcon icon={info.icon}/>,
|
||||
icon: [iconTypeToFontAwesomeClass(info.icon.family), info.icon.iconName],
|
||||
href: `plugins/${encodeURIComponent(info.pluginName)}`
|
||||
}
|
||||
}).forEach(item => items.push(item))
|
||||
|
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import {useDataRequest} from "../../hooks/dataFetchHook";
|
||||
import Geolocations from "../common/Geolocations";
|
||||
import {fetchNetworkPingTable} from "../../service/networkService";
|
||||
import {fetchGeolocations} from "../../service/serverService";
|
||||
|
||||
const NetworkGeolocations = () => {
|
||||
const {data, loadingError} = useDataRequest(fetchGeolocations, []);
|
||||
const {data: pingData, loadingError: pingLoadingError} = useDataRequest(fetchNetworkPingTable, []);
|
||||
|
||||
return (
|
||||
<Geolocations className={"network_geolocations"}
|
||||
geolocationData={data} geolocationError={loadingError}
|
||||
pingData={pingData} pingError={pingLoadingError}
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
||||
export default NetworkGeolocations
|
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import JoinAddressGroupCard from "../../components/cards/server/graphs/JoinAddressGroupCard";
|
||||
import JoinAddressGraphCard from "../../components/cards/server/graphs/JoinAddressGraphCard";
|
||||
|
||||
const NetworkJoinAddresses = () => {
|
||||
return (
|
||||
<Row>
|
||||
<Col lg={8}>
|
||||
<JoinAddressGraphCard identifier={undefined}/>
|
||||
</Col>
|
||||
<Col lg={4}>
|
||||
<JoinAddressGroupCard identifier={undefined}/>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
};
|
||||
|
||||
export default NetworkJoinAddresses
|
@ -0,0 +1,119 @@
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import LoadIn from "../../components/animation/LoadIn";
|
||||
import {Card, Col, Row} from "react-bootstrap-v5";
|
||||
import {useMetadata} from "../../hooks/metadataHook";
|
||||
import CardHeader from "../../components/cards/CardHeader";
|
||||
import {faServer} from "@fortawesome/free-solid-svg-icons";
|
||||
import MultiSelect from "../../components/input/MultiSelect";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {fetchOptimizedPerformance} from "../../service/serverService";
|
||||
import {fetchNetworkPerformanceOverview} from "../../service/networkService";
|
||||
import PerformanceAsNumbersCard from "../../components/cards/server/tables/PerformanceAsNumbersCard";
|
||||
import {useNavigation} from "../../hooks/navigationHook";
|
||||
import {mapPerformanceDataToSeries} from "../../util/graphs";
|
||||
import PerformanceGraphsCard from "../../components/cards/network/PerformanceGraphsCard";
|
||||
|
||||
const NetworkPerformance = () => {
|
||||
const {t} = useTranslation();
|
||||
const {networkMetadata} = useMetadata();
|
||||
const {updateRequested} = useNavigation();
|
||||
|
||||
const [serverOptions, setServerOptions] = useState([]);
|
||||
const [selectedOptions, setSelectedOptions] = useState([]);
|
||||
const [visualizedServers, setVisualizedServers] = useState([]);
|
||||
|
||||
const initializeServerOptions = () => {
|
||||
if (networkMetadata) {
|
||||
const options = networkMetadata.servers;
|
||||
setServerOptions(options);
|
||||
|
||||
const indexOfProxy = options
|
||||
.findIndex(option => option.serverName === networkMetadata.currentServer.serverName);
|
||||
|
||||
setSelectedOptions([indexOfProxy]);
|
||||
setVisualizedServers([indexOfProxy]);
|
||||
}
|
||||
};
|
||||
useEffect(initializeServerOptions, [networkMetadata, setVisualizedServers]);
|
||||
|
||||
const applySelected = () => {
|
||||
setVisualizedServers(selectedOptions);
|
||||
}
|
||||
|
||||
const [performanceData, setPerformanceData] = useState({});
|
||||
const loadPerformanceData = useCallback(async () => {
|
||||
const loaded = {
|
||||
servers: [],
|
||||
data: [],
|
||||
values: [],
|
||||
errors: [],
|
||||
zones: {},
|
||||
colors: {},
|
||||
timestamp_f: ''
|
||||
}
|
||||
const time = new Date().getTime();
|
||||
const monthMs = 2592000000;
|
||||
const after = time - monthMs;
|
||||
|
||||
for (const index of visualizedServers) {
|
||||
const server = serverOptions[index];
|
||||
|
||||
const {data, error} = await fetchOptimizedPerformance(time, encodeURIComponent(server.serverUUID), after);
|
||||
if (data) {
|
||||
loaded.servers.push(server);
|
||||
const values = data.values;
|
||||
delete data.values;
|
||||
loaded.data.push(data);
|
||||
loaded.values.push(await mapPerformanceDataToSeries(values));
|
||||
loaded.zones = data.zones;
|
||||
loaded.colors = data.colors;
|
||||
loaded.timestamp_f = data.timestamp_f;
|
||||
} else if (error) {
|
||||
loaded.errors.push(error);
|
||||
}
|
||||
}
|
||||
|
||||
const selectedUUIDs = visualizedServers
|
||||
.map(index => serverOptions[index])
|
||||
.map(server => server.serverUUID);
|
||||
const {data, error} = await fetchNetworkPerformanceOverview(time, selectedUUIDs);
|
||||
if (error) loaded.errors.push(error);
|
||||
|
||||
setPerformanceData({...loaded, overview: data});
|
||||
}, [visualizedServers, serverOptions, setPerformanceData])
|
||||
|
||||
useEffect(() => {
|
||||
loadPerformanceData();
|
||||
}, [loadPerformanceData, visualizedServers, updateRequested]);
|
||||
|
||||
const isUpToDate = visualizedServers.every((s, i) => s === selectedOptions[i]);
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className={"network_performance"}>
|
||||
<Row>
|
||||
<Col>
|
||||
<PerformanceGraphsCard data={performanceData}/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<PerformanceAsNumbersCard data={performanceData?.overview?.numbers}/>
|
||||
</Col>
|
||||
<Col md={4}>
|
||||
<Card>
|
||||
<CardHeader icon={faServer} color={'light-green'} label={t('html.label.serverSelector')}/>
|
||||
<MultiSelect options={serverOptions.map(server => server.serverName)}
|
||||
selectedIndexes={selectedOptions}
|
||||
setSelectedIndexes={setSelectedOptions}/>
|
||||
<button className={'btn bg-transparent'} onClick={applySelected} disabled={isUpToDate}>
|
||||
{t('html.label.apply')}
|
||||
</button>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</section>
|
||||
</LoadIn>
|
||||
)
|
||||
};
|
||||
|
||||
export default NetworkPerformance
|
@ -0,0 +1,42 @@
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import React from "react";
|
||||
import PlayerbaseDevelopmentCard from "../../components/cards/server/graphs/PlayerbaseDevelopmentCard";
|
||||
import CurrentPlayerbaseCard from "../../components/cards/server/graphs/CurrentPlayerbaseCard";
|
||||
import {useDataRequest} from "../../hooks/dataFetchHook";
|
||||
import {ErrorViewCard} from "../ErrorView";
|
||||
import PlayerbaseTrendsCard from "../../components/cards/server/tables/PlayerbaseTrendsCard";
|
||||
import PlayerbaseInsightsCard from "../../components/cards/server/insights/PlayerbaseInsightsCard";
|
||||
import LoadIn from "../../components/animation/LoadIn";
|
||||
import {fetchNetworkPlayerbaseOverview} from "../../service/networkService";
|
||||
|
||||
const NetworkPlayerbaseOverview = () => {
|
||||
const {data, loadingError} = useDataRequest(fetchNetworkPlayerbaseOverview, []);
|
||||
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className="network_playerbase">
|
||||
<Row>
|
||||
<Col lg={8}>
|
||||
<PlayerbaseDevelopmentCard identifier={undefined}/>
|
||||
</Col>
|
||||
<Col lg={4}>
|
||||
<CurrentPlayerbaseCard identifier={undefined}/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
{loadingError && <ErrorViewCard error={loadingError}/>}
|
||||
{!loadingError && <>
|
||||
<Col lg={8}>
|
||||
<PlayerbaseTrendsCard data={data?.trends}/>
|
||||
</Col>
|
||||
<Col lg={4}>
|
||||
<PlayerbaseInsightsCard data={data?.insights}/>
|
||||
</Col>
|
||||
</>}
|
||||
</Row>
|
||||
</section>
|
||||
</LoadIn>
|
||||
)
|
||||
}
|
||||
|
||||
export default NetworkPlayerbaseOverview;
|
@ -0,0 +1,32 @@
|
||||
import React, {useState} from 'react';
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import {useDataRequest} from "../../hooks/dataFetchHook";
|
||||
import {fetchServersOverview} from "../../service/networkService";
|
||||
import ErrorView from "../ErrorView";
|
||||
import ServersTableCard from "../../components/cards/network/ServersTableCard";
|
||||
import QuickViewGraphCard from "../../components/cards/network/QuickViewGraphCard";
|
||||
import QuickViewDataCard from "../../components/cards/network/QuickViewDataCard";
|
||||
|
||||
const NetworkServers = () => {
|
||||
const [selectedServer, setSelectedServer] = useState(0);
|
||||
|
||||
const {data, loadingError} = useDataRequest(fetchServersOverview, [])
|
||||
|
||||
if (loadingError) {
|
||||
return <ErrorView error={loadingError}/>
|
||||
}
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<ServersTableCard servers={data?.servers || []} onSelect={(index) => setSelectedServer(index)}/>
|
||||
</Col>
|
||||
<Col md={6}>
|
||||
{data?.servers.length && <QuickViewGraphCard server={data.servers[selectedServer]}/>}
|
||||
{data?.servers.length && <QuickViewDataCard server={data.servers[selectedServer]}/>}
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
};
|
||||
|
||||
export default NetworkServers
|
@ -0,0 +1,26 @@
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import React from "react";
|
||||
import ServerRecentSessionsCard from "../../components/cards/server/tables/ServerRecentSessionsCard";
|
||||
import SessionInsightsCard from "../../components/cards/server/insights/SessionInsightsCard";
|
||||
import LoadIn from "../../components/animation/LoadIn";
|
||||
import ServerPieCard from "../../components/cards/common/ServerPieCard";
|
||||
|
||||
const NetworkSessions = () => {
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className="server_sessions">
|
||||
<Row>
|
||||
<Col lg={8}>
|
||||
<ServerRecentSessionsCard identifier={undefined}/>
|
||||
</Col>
|
||||
<Col lg={4}>
|
||||
<ServerPieCard/>
|
||||
<SessionInsightsCard identifier={undefined}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</section>
|
||||
</LoadIn>
|
||||
)
|
||||
}
|
||||
|
||||
export default NetworkSessions;
|
@ -20,10 +20,10 @@ const PlayerbaseOverview = () => {
|
||||
<section className="server_playerbase">
|
||||
<Row>
|
||||
<Col lg={8}>
|
||||
<PlayerbaseDevelopmentCard/>
|
||||
<PlayerbaseDevelopmentCard identifier={identifier}/>
|
||||
</Col>
|
||||
<Col lg={4}>
|
||||
<CurrentPlayerbaseCard/>
|
||||
<CurrentPlayerbaseCard identifier={identifier}/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
|
@ -2,11 +2,7 @@ import React from 'react';
|
||||
import {useParams} from "react-router-dom";
|
||||
import {useDataRequest} from "../../hooks/dataFetchHook";
|
||||
import {fetchGeolocations, fetchPingTable} from "../../service/serverService";
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import {ErrorViewCard} from "../ErrorView";
|
||||
import GeolocationsCard from "../../components/cards/common/GeolocationsCard";
|
||||
import LoadIn from "../../components/animation/LoadIn";
|
||||
import PingTableCard from "../../components/cards/common/PingTableCard";
|
||||
import Geolocations from "../common/Geolocations";
|
||||
|
||||
const ServerGeolocations = () => {
|
||||
const {identifier} = useParams();
|
||||
@ -15,17 +11,10 @@ const ServerGeolocations = () => {
|
||||
const {pingData, pingLoadingError} = useDataRequest(fetchPingTable, [identifier]);
|
||||
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className="server_geolocations">
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
{loadingError ? <ErrorViewCard error={loadingError}/> : <GeolocationsCard data={data}/>}
|
||||
{pingLoadingError ? <ErrorViewCard error={pingLoadingError}/> :
|
||||
<PingTableCard data={pingData}/>}
|
||||
</Col>
|
||||
</Row>
|
||||
</section>
|
||||
</LoadIn>
|
||||
<Geolocations className={"server_geolocations"}
|
||||
geolocationData={data} geolocationError={loadingError}
|
||||
pingData={pingData} pingError={pingLoadingError}
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import {Col, Row} from "react-bootstrap-v5";
|
||||
import JoinAddressGroupCard from "../../components/cards/server/graphs/JoinAddressGroupCard";
|
||||
import JoinAddressGraphCard from "../../components/cards/server/graphs/JoinAddressGraphCard";
|
||||
import {useParams} from "react-router-dom";
|
||||
|
||||
const ServerJoinAddresses = () => {
|
||||
const {identifier} = useParams();
|
||||
return (
|
||||
<Row>
|
||||
<Col lg={8}>
|
||||
<JoinAddressGraphCard identifier={identifier}/>
|
||||
</Col>
|
||||
<Col lg={4}>
|
||||
<JoinAddressGroupCard identifier={identifier}/>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
};
|
||||
|
||||
export default ServerJoinAddresses
|
@ -4,18 +4,20 @@ import ServerWorldPieCard from "../../components/cards/server/graphs/ServerWorld
|
||||
import ServerRecentSessionsCard from "../../components/cards/server/tables/ServerRecentSessionsCard";
|
||||
import SessionInsightsCard from "../../components/cards/server/insights/SessionInsightsCard";
|
||||
import LoadIn from "../../components/animation/LoadIn";
|
||||
import {useParams} from "react-router-dom";
|
||||
|
||||
const ServerSessions = () => {
|
||||
const {identifier} = useParams();
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className="server_sessions">
|
||||
<Row>
|
||||
<Col lg={8}>
|
||||
<ServerRecentSessionsCard/>
|
||||
<ServerRecentSessionsCard identifier={identifier}/>
|
||||
</Col>
|
||||
<Col lg={4}>
|
||||
<ServerWorldPieCard/>
|
||||
<SessionInsightsCard/>
|
||||
<SessionInsightsCard identifier={identifier}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</section>
|
||||
|
@ -2879,10 +2879,10 @@ boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
bootstrap@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.0.tgz#838727fb60f1630db370fe57c63cbcf2962bb3d3"
|
||||
integrity sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==
|
||||
bootstrap@^5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.1.tgz#45f97ff05cbe828bad807b014d8425f3aeb8ec3a"
|
||||
integrity sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
@ -8135,10 +8135,10 @@ sass-loader@^12.3.0:
|
||||
klona "^2.0.4"
|
||||
neo-async "^2.6.2"
|
||||
|
||||
sass@^1.54.8:
|
||||
version "1.54.8"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.8.tgz#4adef0dd86ea2b1e4074f551eeda4fc5f812a996"
|
||||
integrity sha512-ib4JhLRRgbg6QVy6bsv5uJxnJMTS2soVcCp9Y88Extyy13A8vV0G1fAwujOzmNkFQbR3LvedudAMbtuNRPbQww==
|
||||
sass@^1.54.9:
|
||||
version "1.54.9"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.9.tgz#b05f14ed572869218d1a76961de60cd647221762"
|
||||
integrity sha512-xb1hjASzEH+0L0WI9oFjqhRi51t/gagWnxLiwUNMltA0Ab6jIDkAacgKiGYKM9Jhy109osM7woEEai6SXeJo5Q==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user