mobile support for home and player

This commit is contained in:
cnachtigall1991
2021-10-06 19:25:00 +02:00
parent 886f182ff2
commit 08ffe9e550
11 changed files with 602 additions and 389 deletions

View File

@@ -17,5 +17,13 @@ $warning: #c3a235;
$info: $blue;
$success: #609926;
// Custom classes
.mt-n5 {
margin-top: -3rem !important;
}
.mt-n4 {
margin-top: -2rem !important;
}
// Bootstrap
@import "../node_modules/bootstrap/scss/bootstrap";

View File

@@ -1,7 +1,7 @@
<template>
<div class="bg-secondary text-center pt-5 pb-4">
<div class="icons pb-4">
<a class="text-white" target="_blank" href="https://git.harting.dev/CSGOWTF/csgowtf">
<a class="text-white" target="_blank" href="https://git.harting.dev/CSGOWTF">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-github"
viewBox="0 0 16 16">
<path
@@ -10,10 +10,8 @@
</a>
</div>
<div class="text">
<p>This site is an open source project, originally created and maintained by <span class="text-warning">anonfunc</span> and <span
class="text-warning">vikingowl</span>.</p>
<p class="text-muted">For feedback contact us at <a class="text-muted text-decoration-none"
href="mailto:#">EMAIL</a></p>
<p>This site is an open source project, originally created and maintained by the <span class="text-warning">CSGOWTF</span> team.</p>
<p class="text-muted">For feedback contact us on gitea.</p>
</div>
</div>
</template>

View File

@@ -1,21 +1,21 @@
<template>
<nav class="navbar navbar-expand navbar-dark fixed-top">
<div class="container-fluid w-75">
<div class="navbar-nav">
<router-link class="navbar-brand text-warning fw-bold fs-3" to="/">CSGO<span class="text-up text-white fw-bold">WTF</span>
<div class="container">
<div class="navbar-nav fs-5">
<router-link class="navbar-brand text-warning fw-bold fs-3" to="/">
CSGO<span class="text-up text-white fw-bold">WTF</span>
</router-link>
<router-link class="nav-link" to="/explore">Explore</router-link>
</div>
<form class="d-flex col-5 justify-content-end" @keydown.enter.prevent="parseSearch">
<form class="d-flex justify-content-end" @keydown.enter.prevent="parseSearch">
<label for="search">
<svg class="bi bi-search" fill="currentColor" height="24" viewBox="0 0 16 16" width="24"
xmlns="http://www.w3.org/2000/svg">
<svg class="bi bi-search" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
</svg>
</label>
<input id="search" v-model="data.searchInput" aria-label="Search"
class="form-control w-75 bg-transparent border-0"
class="form-control bg-transparent border-0"
placeholder="SteamID64, Profile Link or Custom URL"
type="search">
</form>
@@ -68,6 +68,8 @@ export default {
router.push(`/player/${store.state.vanityUrl}`)
}
}
document.activeElement.blur()
}
return {
@@ -79,6 +81,7 @@ export default {
<style lang="scss" scoped>
nav {
max-width: 100vw;
height: 70px;
width: 100vw;
background: rgba(16, 18, 26, 0.5);
@@ -88,6 +91,12 @@ nav {
vertical-align: top;
}
svg {
width: 24px;
height: 24px;
fill: currentColor;
}
label {
padding-top: 6px;
}
@@ -107,4 +116,16 @@ nav {
}
}
}
@media screen and (max-width: 768px) {
nav input[type="search"] {
min-width: 0;
max-width: 0;
&:focus {
min-width: 100%;
max-width: 100%;
}
}
}
</style>

View File

@@ -1,45 +1,49 @@
<template>
<table>
<thead>
<tr>
<th class="player__avatar"></th>
<th class="player__name"></th>
<th class="player__rank"></th>
<th class="player__kills cursor__help" title="Kills">K</th>
<th class="player__assist cursor__help" title="Assists">A</th>
<th class="player__deaths cursor__help" title="Deaths">D</th>
<th class="player__diff cursor__help" title="Kill death difference">+/-</th>
<th class="player__kd cursor__help" title="Kill death ratio">K/D</th>
<th class="player__adr cursor__help" title="Average damage per round">ADR</th>
<th class="player__hs cursor__help" title="Percentage of kills with a headshot">HS%</th>
<th class="player__kast cursor__help" title="Percentage of rounds with a Kill, Assist, Survived or Death Traded">
KAST
</th>
<th class="player__rating cursor__help" title="Estimated HLTV Rating 1.0">Rating</th>
</tr>
</thead>
<tbody v-for="player in props.stats" :key="player.player.steamid64">
<tr v-if="player.team_id === props.team_id" class="player">
<ScoreTeamPlayer
:assists="player.assists"
:avatar="player.player.avatar"
:deaths="player.deaths"
:dmg="player.extended?.dmg?.enemy"
:hs="player.headshot"
:kast="player.extended?.kast"
:kdiff="player.kills - player.deaths"
:kills="player.kills"
:mk_duo="player.extended?.multi_kills?.duo"
:mk_pent="player.extended?.multi_kills?.pent"
:mk_quad="player.extended?.multi_kills?.quad"
:mk_triple="player.extended?.multi_kills?.triple"
:name="player.player.name"
:rounds_played="props.rounds_played"
:rank="player.extended?.rank?.old"
/>
</tr>
</tbody>
</table>
<div class="scoreboard" :class="'team-' + props.team_id">
<table>
<caption :class="props.score === 16 ? 'text-success' : 'text-danger'">{{props.score}}</caption>
<thead>
<tr>
<th class="player__avatar"></th>
<th class="player__name"></th>
<th class="player__rank"></th>
<th class="player__kills cursor__help" title="Kills">K</th>
<th class="player__assist cursor__help" title="Assists">A</th>
<th class="player__deaths cursor__help" title="Deaths">D</th>
<th class="player__diff cursor__help" title="Kill death difference">+/-</th>
<th class="player__kd cursor__help" title="Kill death ratio">K/D</th>
<th class="player__adr cursor__help" title="Average damage per round">ADR</th>
<th class="player__hs cursor__help" title="Percentage of kills with a headshot">HS%</th>
<th class="player__rating cursor__help" title="Estimated HLTV Rating 1.0">Rating</th>
<th class="player__mvp cursor__help" title="Most valuable player">MVP</th>
<th class="player__score">Score</th>
</tr>
</thead>
<tbody v-for="player in props.stats" :key="player.player.steamid64">
<tr v-if="player.team_id === props.team_id" class="player">
<ScoreTeamPlayer
:assists="player.assists"
:avatar="player.player.avatar"
:deaths="player.deaths"
:dmg="player.extended?.dmg?.enemy"
:hs="player.headshot"
:kdiff="player.kills - player.deaths"
:kills="player.kills"
:mk_duo="player.extended?.multi_kills?.duo"
:mk_pent="player.extended?.multi_kills?.pent"
:mk_quad="player.extended?.multi_kills?.quad"
:mk_triple="player.extended?.multi_kills?.triple"
:mvp="player.mvp"
:name="player.player.name"
:player_score="player.score"
:rank="player.extended?.rank?.old"
:rounds_played="props.rounds_played"
/>
</tr>
</tbody>
</table>
</div>
<hr v-if="props.team_id === 1">
</template>
<script>
@@ -62,10 +66,49 @@ export default {
type: Number,
required: true,
default: 1
},
score: {
type: Number,
required: true,
default: 0
}
},
setup(props) {
return {props}
}
}
</script>
</script>
<style lang="scss" scoped>
.scoreboard {
margin-bottom: 20px;
}
table {
width: 900px;
text-align: center;
margin-top: 120px;
margin-bottom: -100px;
caption {
color: white;
font-size: 3rem;
caption-side: top;
padding: 0;
margin-left: -70px;
margin-bottom: -178px;
}
tr {
height: 40px;
}
td {
padding: 5px 10px;
}
.cursor__help {
cursor: help;
}
}
</style>

View File

@@ -19,7 +19,7 @@
<td class="player__deaths">
{{ props.deaths }}
</td>
<td :class="props.kdiff >= 0 ? 'text-success' : 'text-danger'" class="player__diff">
<td :class="props.kdiff >= 0 ? 'text-success' : 'text-danger'" class="player__kdiff">
{{ props.kdiff }}
</td>
<td class="player__kd">
@@ -31,14 +31,17 @@
<td class="player__hs">
{{ (props.hs > 0 && props.kills > 0) ? (props.hs * 100 / props.kills).toFixed(0) + "%" : "0%" }}
</td>
<td class="player__kast">
{{ props.kast ? props.kast + "%" : "-" }}
</td>
<td class="player__rating">
{{
GetHLTV_1(props.kills, props.rounds_played, props.deaths, props.mk_duo, props.mk_triple, props.mk_quad, props.mk_pent)
}}
</td>
<td class="player__mvp">
{{props.mvp}}
</td>
<td class="player__score">
{{props.player_score}}
</td>
</template>
<script>
@@ -112,19 +115,57 @@ export default {
required: true,
default: 0
},
kast: {
type: Number,
required: true,
default: 0
},
dmg: {
type: Number,
required: true,
default: 0
},
mvp: {
type: Number,
required: true,
default: 0
},
player_score: {
type: Number,
required: true,
default: 0
}
},
setup(props) {
return {props, GetHLTV_1}
}
}
</script>
</script>
<style scoped lang="scss">
.player__avatar {
width: 30px;
height: 30px;
border-radius: 50%;
}
.player__name {
text-align: left;
width: 150px;
}
.player__rank {
width: 60px;
}
.player__kills, .player__assist, .player__deaths, .player__kdiff, .player__mvp {
width: 40px;
}
.player__kd, .player__hs, .player__rating, .player__score {
width: 75px;
}
.player__adr {
width: 85px;
}
.player__rating {
border-radius: 25% 25%;
}
</style>

View File

@@ -5,4 +5,4 @@ import store from './store'
import 'bootstrap'
import '../scss/custom.scss'
createApp(App).use(store).use(router).mount('#app')
createApp(App).use(store).use(router).mount('#app')

View File

@@ -1,16 +1,22 @@
import {DateTime} from "luxon/build/es6/luxon";
import router from '@/router'
import {DateTime, Duration} from "luxon/build/es6/luxon";
import router from '../router'
export const FormatDuration = (d) => {
const hours = Math.floor(d / 3600)
const num = d % 3600
const minutes = Math.floor(num % 3600 / 60)
const seconds = Math.floor(num % 3600 % 60)
const duration = Duration.fromObject({hours: 0, minutes: 0, seconds: d}).normalize().toObject()
if (hours !== 0)
return `${hours}:${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds}`
else
return `${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds}`
if (duration.hours > 1)
return `${duration.hours} h ${duration.minutes} min`
else if (duration.hours < 1)
return `${duration.minutes} min`
}
export const FormatFullDuration = (d) => {
const duration = Duration.fromObject({hours: 0, minutes: 0, seconds: d}).normalize()
if (duration.hours > 1)
return duration.toFormat('hh:mm:ss')
else if (duration.hours < 1)
return duration.toFormat('mm:ss')
}
export const FormatDate = (date) => {
@@ -18,7 +24,7 @@ export const FormatDate = (date) => {
const diff = DateTime.now().diff(matchDate)
if (diff.as('days') > 10)
return matchDate.toLocaleString({weekday: 'short', day: 'numeric', month: 'numeric', year: 'numeric'})
return matchDate.toLocaleString({weekday: 'short', day: '2-digit', month: '2-digit', year: 'numeric'})
else if (diff.as('days') < 1)
if (diff.as('hours') < 1)
return Math.floor(diff.as('minutes')) + ' minutes ago'
@@ -28,6 +34,20 @@ export const FormatDate = (date) => {
return Math.floor(diff.as('days')) + ' days ago'
}
export const FormatFullDate = (date) => {
const matchDate = DateTime.fromISO(date)
return matchDate.toLocaleString({
weekday: 'short',
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
export const GoToMatch = (id) => {
router.push(`/match/${id}`)
}
@@ -47,4 +67,17 @@ export const GetHLTV_1 = (kills = 0, rounds, deaths = 0, k2 = 0, k3 = 0, k4 = 0,
const RoundsWithMultipleKillsRating = (k1 + 4 * k2 + 9 * k3 + 16 * k4 + 25 * k5) / rounds / Weight_RMK
return ((KillRating + 0.7 * SurvivalRating + RoundsWithMultipleKillsRating) / 2.7).toFixed(2)
}
export const SaveLastVisitedToLocalStorage = (data) => {
let a = JSON.parse(localStorage.getItem('recent-visited')) || [];
if (a.length === 0) {
a.push(data);
} else if (a.find(p => p.steamid64 === data.steamid64)) {
a.splice(a.findIndex(j => j.steamid64 === data.steamid64), 1)
a.push(data)
} else if (!a.find(p => p.steamid64 === data.steamid64)) {
a.push(data)
}
localStorage.setItem('recent-visited', JSON.stringify(a));
}

View File

@@ -1,9 +1,21 @@
<template>
Explore
<div class="container text-center">
<h1>This site will be available soon</h1>
</div>
</template>
<script>
export default {
name: 'Explore'
name: 'Explore',
// TODO: Events / Lans etc. +
setup() {
document.title = "Explore | csgoWTF"
}
}
</script>
</script>
<style scoped>
.container {
margin: 200px auto;
}
</style>

View File

@@ -1,62 +1,77 @@
<template>
<div class="main-content content text-center">
<div class="head">
<h1 class="text-warning fw-bold">CSGO<span class="text-up text-white">WTF</span></h1>
<h3>Open source CSGO data platform</h3>
<div class="head pt-5 pb-5">
<h1 class="text-warning fw-bold mt-lg-5">CSGO<span class="text-up text-white">WTF</span></h1>
<h3 class="mb-lg-5">Open source CSGO data platform</h3>
</div>
<div class="body row m-auto mt-5 mb-5">
<div class="col-4">
<svg class="mb-4" height="80" viewBox="0 0 24 24" width="80" xmlns="http://www.w3.org/2000/svg">
<path d="M7,12 L14.5,12 C16.277025,12 17.7447372,10.6756742 17.970024,8.96013518 C16.2885152,8.7047201 15,7.25283448 15,5.5 C15,3.56700338 16.5670034,2 18.5,2 C20.4329966,2 22,3.56700338 22,5.5 C22,7.27155475 20.6838151,8.73569805 18.9759671,8.96790818 C18.7419236,11.2333126 16.8272778,13 14.5,13 L7,13 L7,15.0354444 C8.69614707,15.2780593 10,16.736764 10,18.5 C10,20.4329966 8.43299662,22 6.5,22 C4.56700338,22 3,20.4329966 3,18.5 C3,16.736764 4.30385293,15.2780593 6,15.0354444 L6,8.96455557 C4.30385293,8.72194074 3,7.26323595 3,5.5 C3,3.56700338 4.56700338,2 6.5,2 C8.43299662,2 10,3.56700338 10,5.5 C10,7.26323595 8.69614707,8.72194074 7,8.96455557 L7,12 Z M4,18.5 C4,19.8807119 5.11928813,21 6.5,21 C7.88071187,21 9,19.8807119 9,18.5 C9,17.1192881 7.88071187,16 6.5,16 C5.11928813,16 4,17.1192881 4,18.5 Z M4,5.5 C4,6.88071187 5.11928813,8 6.5,8 C7.88071187,8 9,6.88071187 9,5.5 C9,4.11928813 7.88071187,3 6.5,3 C5.11928813,3 4,4.11928813 4,5.5 Z M18.5,3 C17.1192881,3 16,4.11928813 16,5.5 C16,6.88071187 17.1192881,8 18.5,8 C19.8807119,8 21,6.88071187 21,5.5 C21,4.11928813 19.8807119,3 18.5,3 Z"
fill="white"/>
<div class="mt-n4">
<span class="text-muted">Image by <a class="text-decoration-none text-warning"
href="https://unsplash.com/photos/6ou8gWpS9ns"
target="_blank">Fredrick Tendong</a></span>
</div>
<div v-if="recentVisited !== null" class="recent-search mt-5 mb-5 row gap-2 justify-content-center">
<h4 class="mb-3s">Recently visited players</h4>
<div v-for="player in recentVisited" :key="player.steamid64" class="player-card"
@click="GoToPlayer(player.steamid64)">
<div class="p-2">
<div class="col-md-4 m-auto">
<img :alt="player.name" :src="player.avatar">
</div>
<div class="col-md-8 m-auto">
<p>{{ player.name }}</p>
</div>
</div>
</div>
</div>
<hr v-if="recentVisited !== null" class="m-auto text-muted">
<div class="body container m-auto row mt-5 mb-5 justify-content-center">
<div class="col-sm-12 col-md-4 col-lg-3">
<svg class="mb-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M7,12 L14.5,12 C16.277025,12 17.7447372,10.6756742 17.970024,8.96013518 C16.2885152,8.7047201 15,7.25283448 15,5.5 C15,3.56700338 16.5670034,2 18.5,2 C20.4329966,2 22,3.56700338 22,5.5 C22,7.27155475 20.6838151,8.73569805 18.9759671,8.96790818 C18.7419236,11.2333126 16.8272778,13 14.5,13 L7,13 L7,15.0354444 C8.69614707,15.2780593 10,16.736764 10,18.5 C10,20.4329966 8.43299662,22 6.5,22 C4.56700338,22 3,20.4329966 3,18.5 C3,16.736764 4.30385293,15.2780593 6,15.0354444 L6,8.96455557 C4.30385293,8.72194074 3,7.26323595 3,5.5 C3,3.56700338 4.56700338,2 6.5,2 C8.43299662,2 10,3.56700338 10,5.5 C10,7.26323595 8.69614707,8.72194074 7,8.96455557 L7,12 Z M4,18.5 C4,19.8807119 5.11928813,21 6.5,21 C7.88071187,21 9,19.8807119 9,18.5 C9,17.1192881 7.88071187,16 6.5,16 C5.11928813,16 4,17.1192881 4,18.5 Z M4,5.5 C4,6.88071187 5.11928813,8 6.5,8 C7.88071187,8 9,6.88071187 9,5.5 C9,4.11928813 7.88071187,3 6.5,3 C5.11928813,3 4,4.11928813 4,5.5 Z M18.5,3 C17.1192881,3 16,4.11928813 16,5.5 C16,6.88071187 17.1192881,8 18.5,8 C19.8807119,8 21,6.88071187 21,5.5 C21,4.11928813 19.8807119,3 18.5,3 Z"
fill="white"/>
</svg>
<h4 class="fw-light">Open Source</h4>
<p class="w-75 m-auto fw-light">All project code is open source and available for contributors to improve and
<p class="fw-light">All project code is open source and available for contributors to improve and
modify.</p>
</div>
<div class="col-4">
<svg class="bi bi-bar-chart-fill mb-4" fill="currentColor" height="80" viewBox="0 0 16 16"
width="80" xmlns="http://www.w3.org/2000/svg">
<div class="col-sm-12 col-md-4 col-lg-3">
<svg class="bi bi-bar-chart-fill mb-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="M1 11a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-3zm5-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7zm5-5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V2z"/>
</svg>
<h4 class="fw-light">In-Depth Data</h4>
<p class="w-75 m-auto fw-light">Parsing replay files provides highly detailed match data.</p>
<p class="fw-light">Parsing replay files provides highly detailed match data.</p>
</div>
<div class="col-4">
<svg class="bi bi-stars mb-4" fill="currentColor" height="80" viewBox="0 0 16 16" width="80"
xmlns="http://www.w3.org/2000/svg">
<div class="col-sm-12 col-md-4 col-lg-3">
<svg class="bi bi-stars mb-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.657 6.247c.11-.33.576-.33.686 0l.645 1.937a2.89 2.89 0 0 0 1.829 1.828l1.936.645c.33.11.33.576 0 .686l-1.937.645a2.89 2.89 0 0 0-1.828 1.829l-.645 1.936a.361.361 0 0 1-.686 0l-.645-1.937a2.89 2.89 0 0 0-1.828-1.828l-1.937-.645a.361.361 0 0 1 0-.686l1.937-.645a2.89 2.89 0 0 0 1.828-1.828l.645-1.937zM3.794 1.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387A1.734 1.734 0 0 0 4.593 5.69l-.387 1.162a.217.217 0 0 1-.412 0L3.407 5.69A1.734 1.734 0 0 0 2.31 4.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387A1.734 1.734 0 0 0 3.407 2.31l.387-1.162zM10.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732L9.1 2.137a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L10.863.1z"/>
</svg>
<h4 class="fw-light">Free of Charge</h4>
<p class="w-75 m-auto fw-light">This service is free of charge. If you want to support us, just contact us.</p>
<p class="fw-light">This service is free of charge. If you want to support us, just contact us.</p>
</div>
</div>
<hr class="m-auto text-muted">
<div class="foot">
MORE TEXT
</div>
<span class="image-by">Image by <a class="text-decoration-none text-info" href="https://unsplash.com/photos/6ou8gWpS9ns"
target="_blank">Fredrick Tendong</a></span>
</div>
</template>
<script>
import {onMounted} from "vue";
import {GoToPlayer} from "../utils";
export default {
name: 'Home',
setup() {
onMounted(() => {
document.title = 'Home | CSGO-W.TV'
})
document.title = 'Home | csgoWTF'
const recentVisited = JSON.parse(localStorage.getItem('recent-visited')).reverse()
return {recentVisited, GoToPlayer}
}
}
</script>
<style scoped lang="scss">
<style lang="scss" scoped>
.main-content {
.head {
background-image: url("https://images.unsplash.com/photo-1560419015-7c427e8ae5ba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80");
@@ -64,9 +79,6 @@ export default {
background-size: cover;
background-position: center;
padding-top: 10rem;
padding-bottom: 10rem;
h1 {
font-size: 5rem;
@@ -89,24 +101,55 @@ export default {
}
}
.body, hr {
width: 65%;
.recent-search {
max-width: 900px;
margin: 0 auto;
p {
font-size: .9rem;
.player-card {
width: 180px;
height: 75px;
background: var(--bs-primary);
border-radius: 5% 30%;
&:hover {
background: var(--bs-secondary);
cursor: pointer;
}
img {
border-radius: 50%;
width: 40px;
height: 40px;
}
p {
font-size: .9rem;
}
}
}
.foot {
margin-top: 5rem;
padding-bottom: 5rem;
@media screen and (max-width: 768px) {
.recent-search {
.player-card {
height: 60px;
img {
width: 30px;
height: 30px;
}
}
}
}
.image-by {
position: absolute;
bottom: -35px;
right: 2rem;
font-size: .8rem;
.body, hr {
p {
font-size: .9rem;
}
svg {
height: 80px;
width: 80px;
fill: currentColor;
}
}
}
</style>

View File

@@ -21,14 +21,9 @@
</ul>
</div>
<div class="container row m-auto">
<div class="score col-3 flex-column">
{{data.score[0]}}
-
{{data.score[1]}}
</div>
<div v-if="data.score.length === 2 && data.stats" class="col-9 d-flex flex-column">
<ScoreTeam :rounds_played="data.score.reduce((a, b) => a + b)" :stats="data.stats" :team_id="1"/>
<ScoreTeam :rounds_played="data.score.reduce((a, b) => a + b)" :stats="data.stats" :team_id="2"/>
<div v-if="data.score.length === 2 && data.stats" class="col-8 m-auto d-flex flex-column">
<ScoreTeam :rounds_played="data.score.reduce((a, b) => a + b)" :stats="data.stats" :team_id="1" :score="data.score[0]"/>
<ScoreTeam :rounds_played="data.score.reduce((a, b) => a + b)" :stats="data.stats" :team_id="2" :score="data.score[1]"/>
</div>
</div>
</template>
@@ -37,7 +32,6 @@
import {defineAsyncComponent, onBeforeMount, reactive, watch} from "vue";
import axios from 'axios'
import {GetHLTV_1, GoToPlayer} from "../utils";
import "../components/ScoreTeam"
const ScoreTeam = defineAsyncComponent(() => import('../components/ScoreTeam'))
@@ -61,12 +55,13 @@ export default {
// Functions
const GetMatch = () => {
axios
.get(`http://localhost:1337/http://localhost:8000/match/${props.match_id}`)
.get(`http://localhost:8000/match/${props.match_id}`)
.then((response) => {
document.title = `${response.data.map} | CSGO-W.TF`
data.matchDetails = response.data
data.stats = response.data.stats
data.score = response.data.score
console.log(response.data)
})
.catch((e) => {
console.log(e)
@@ -106,56 +101,10 @@ export default {
}
</script>
<style lang="scss">
<style lang="scss" scoped>
.rank-icon {
max-width: 50px;
}
.map-icon {
max-width: 60px;
}
.score {
font-size: 3.5rem;
}
table {
width: 900px;
text-align: center;
tr {
height: 50px;
}
td {
padding: 5px 10px;
}
.cursor__help {
cursor: help;
}
.player__avatar {
width: 30px;
height: 30px;
border-radius: 50%;
}
.player__name {
text-align: left;
width: 150px;
}
.player__rank {
width: 60px;
}
.player__rating {
border-radius: 25% 25%;
}
}
.nav {
width: 100vw;
background: rgba(0, 0, 0, 0.6);

View File

@@ -1,146 +1,146 @@
<template>
<div class="wrapper">
<div class="container">
<div class="card mb-3 bg-transparent border-0" style="max-width: 540px;">
<div class="row g-0">
<div class="img-container col-md-4 pt-3">
<img
:src="data.playerDetails.avatar" alt="Player avatar" class="img-fluid avatar">
</div>
<div class="col-md-8">
<div class="card-body">
<h3 class="card-title"><a
:href="/^\d{17}$/.test(props.id) ? 'https://steamcommunity.com/profiles/' + props.id : 'https://steamcommunity.com/id/' + props.id"
class="text-decoration-none text-white"
target="_blank"
title="Open steam profile">{{
data.playerDetails.name
}}
<svg class="bi bi-link-45deg" fill="currentColor" height="16" viewBox="0 0 16 16"
width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1.002 1.002 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4.018 4.018 0 0 1-.128-1.287z"/>
<path
d="M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243L6.586 4.672z"/>
</svg>
</a></h3>
<table class="table table-borderless">
<tr>
<th class="text-uppercase text-muted">Wins</th>
<th class="text-uppercase text-muted">Losses</th>
<th class="text-uppercase text-muted">WinRate</th>
</tr>
<tr>
<td>{{ wl.win }}</td>
<td>{{ wl.loss }}</td>
<td>{{ (wl.win / wl.loss * 100).toFixed(2) }}%</td>
</tr>
</table>
<small v-if="data.playerDetails.tracked" class="card-text text-muted">
Currently tracked
</small>
<div v-else class="dropdown">
<button
id="login-dropdown"
aria-expanded="false"
class="btn border-2 btn-outline-info"
data-bs-toggle="dropdown"
type="button"
>
Track Me!
</button>
<div aria-labelledby="login-dropdown" class="dropdown-menu mt-2 border-2 border-primary bg-body"
style="width: 320px">
<form class="px-4 py-3">
<!-- AuthCode input -->
<div class="form-outline mb-4">
<input id="track-authcode" v-model="data.userData.authcode" class="form-control bg-secondary"
placeholder="AuthCode*"
required type="text"/>
</div>
<div class="container">
<div class="card mb-3 bg-transparent border-0" style="max-width: 540px;">
<div class="row g-0">
<div class="img-container col-md-4 pt-3">
<img
:src="data.playerDetails.avatar" alt="Player avatar" class="img-fluid avatar">
</div>
<div class="col-md-8">
<div class="card-body">
<h3 class="card-title"><a
:href="/^\d{17}$/.test(props.id) ? 'https://steamcommunity.com/profiles/' + props.id : 'https://steamcommunity.com/id/' + props.id"
class="text-decoration-none text-white"
target="_blank"
title="Open steam profile">{{
data.playerDetails.name
}}
<svg class="bi bi-link-45deg" fill="currentColor" height="16" viewBox="0 0 16 16"
width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1.002 1.002 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4.018 4.018 0 0 1-.128-1.287z"/>
<path
d="M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243L6.586 4.672z"/>
</svg>
</a></h3>
<table class="table table-borderless">
<tr>
<th class="text-uppercase text-muted">Wins</th>
<th class="text-uppercase text-muted">Losses</th>
<th class="text-uppercase text-muted">WinRate</th>
</tr>
<tr>
<td>{{ wl.win }}</td>
<td>{{ wl.loss }}</td>
<td>{{ (wl.win / wl.loss * 100).toFixed(2) }}%</td>
</tr>
</table>
<small v-if="data.playerDetails.tracked" class="card-text text-muted">
Currently tracked
</small>
<div v-else class="dropdown">
<button
id="login-dropdown"
aria-expanded="false"
class="btn border-2 btn-outline-info"
data-bs-toggle="dropdown"
type="button"
>
Track Me!
</button>
<div aria-labelledby="login-dropdown" class="dropdown-menu mt-2 border-2 border-primary bg-body"
style="width: 320px">
<form class="px-4 py-3">
<!-- AuthCode input -->
<div class="form-outline mb-4">
<input id="track-authcode" v-model="data.userData.authcode" class="form-control bg-secondary"
placeholder="AuthCode*"
required type="text"/>
</div>
<!-- ShareCode input -->
<div class="form-outline mb-2">
<input id="track-sharecode" v-model="data.userData.sharecode" class="form-control bg-secondary"
placeholder="ShareCode"
type="text"/>
</div>
<!-- ShareCode input -->
<div class="form-outline mb-2">
<input id="track-sharecode" v-model="data.userData.sharecode" class="form-control bg-secondary"
placeholder="ShareCode"
type="text"/>
</div>
<div class="form-outline mb-4">
<small>You can find your AuthCode and ShareCode <a
href="https://help.steampowered.com/en/wizard/HelpWithGameIssue/?appid=730&issueid=128" target="_blank">here</a>.</small>
</div>
<div class="form-outline mb-4">
<small>You can find your AuthCode and ShareCode <a
href="https://help.steampowered.com/en/wizard/HelpWithGameIssue/?appid=730&issueid=128"
target="_blank">here</a>.</small>
</div>
<!-- Submit button -->
<button class="btn btn-outline-warning border-2" type="submit" @click.prevent="TrackMe">
TrackMe
</button>
</form>
</div>
<!-- Submit button -->
<button class="btn btn-outline-warning border-2" type="submit" @click.prevent="TrackMe">
TrackMe
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="matches m-auto">
<table v-if="data.matches" class="table table-borderless">
<thead class="border-bottom">
<tr>
<th class="text-center" scope="col">Map</th>
<th class="text-center" scope="col">Rank</th>
<th class="text-center" scope="col">Score</th>
<th class="text-center" scope="col">K</th>
<th class="text-center" scope="col">A</th>
<th class="text-center" scope="col">D</th>
<th class="text-center" scope="col" style="cursor: help" title="Kill-to-death ratio">+/-</th>
<th class="text-center" scope="col" style="cursor: help" title="Average damage per round">ADR</th>
<th class="text-center" scope="col" style="cursor: help" title="HLTV 1.0 Rating">Rating</th>
<th class="text-center" scope="col">Duration</th>
<th scope="col">Date</th>
</tr>
</thead>
<tbody>
<tr v-for="match in data.matches"
:key="match.match_id"
class="match"
@click="GoToMatch(match.match_id)">
<td class="td-map text-center">
<img :alt="match.map ? match.map : 'Map not found'"
:src="mapImg.includes(match.map) ? require('@/images/maps/' + match.map + '.png') : require('@/images/maps/not_found.png')"
:title="match.map"
class="map-icon">
</td>
<td class="td-rank text-center">
<img
:src="match.stats[0].extended?.rank?.new ? require('@/images/ranks/' + match.stats[0].extended?.rank?.new + '.png') : require('@/images/ranks/0.png')"
alt="Rank icon"
class="rank-icon">
</td>
<td :class="match.stats[0].team_id === match.match_result ? 'text-success' : !match.match_result ? 'text-warning' : 'text-danger'"
class="td-score text-center fw-bold">
{{ match.score[0] }} - {{ match.score[1] }}
</td>
<td class="td-kills text-center">
{{ match.stats[0].kills ? match.stats[0].kills : "0" }}
</td>
<td class="td-assists text-center">
{{ match.stats[0].assists ? match.stats[0].assists : "0" }}
</td>
<td class="td-deaths text-center">
{{ match.stats[0].deaths ? match.stats[0].deaths : "0" }}
</td>
<td :class="(match.stats[0].kills ? match.stats[0].kills : 0) - (match.stats[0].deaths ? match.stats[0].deaths : 0) >= 0 ? 'text-success' : 'text-danger'"
class="td-plus text-center">
{{
(match.stats[0].kills ? match.stats[0].kills : 0) - (match.stats[0].deaths ? match.stats[0].deaths : 0)
}}
</td>
<td class="td-adr text-center">
{{
Math.floor((match.stats[0].extended.dmg.total ? match.stats[0].extended.dmg.total : 0) / (match.score[0] + match.score[1]))
}}
</td>
<td :class="GetHLTV_1(
</div>
<div class="matches m-auto">
<table v-if="data.matches" class="table table-borderless">
<thead class="border-bottom">
<tr>
<th class="text-center map" scope="col">Map</th>
<th class="text-center rank" scope="col">Rank</th>
<th class="text-center score" scope="col">Score</th>
<th class="text-center kills" scope="col">K</th>
<th class="text-center assists" scope="col">A</th>
<th class="text-center deaths" scope="col">D</th>
<th class="text-center kdiff" scope="col" style="cursor: help" title="Kill-to-death ratio">+/-</th>
<th class="text-center adr" scope="col" style="cursor: help" title="Average damage per round">ADR</th>
<th class="text-center hltv" scope="col" style="cursor: help" title="HLTV 1.0 Rating">Rating</th>
<th class="text-center duration" scope="col">Duration</th>
<th class="date" scope="col">Date</th>
</tr>
</thead>
<tbody>
<tr v-for="match in data.matches"
:key="match.match_id"
class="match"
@click="GoToMatch(match.match_id)">
<td class="td-map text-center">
<img :alt="match.map ? match.map : 'Map not found'"
:src="mapImg.includes(match.map) ? require('@/images/maps/' + match.map + '.png') : require('@/images/maps/not_found.png')"
:title="match.map"
class="map-icon">
</td>
<td class="td-rank text-center">
<img
:src="match.stats[0].extended?.rank?.new ? require('@/images/ranks/' + match.stats[0].extended?.rank?.new + '.png') : require('@/images/ranks/0.png')"
alt="Rank icon"
class="rank-icon">
</td>
<td :class="match.stats[0].team_id === match.match_result ? 'text-success' : !match.match_result ? 'text-warning' : 'text-danger'"
class="td-score text-center fw-bold">
{{ match.score[0] }} - {{ match.score[1] }}
</td>
<td class="td-kills text-center">
{{ match.stats[0].kills ? match.stats[0].kills : "0" }}
</td>
<td class="td-assists text-center">
{{ match.stats[0].assists ? match.stats[0].assists : "0" }}
</td>
<td class="td-deaths text-center">
{{ match.stats[0].deaths ? match.stats[0].deaths : "0" }}
</td>
<td :class="(match.stats[0].kills ? match.stats[0].kills : 0) - (match.stats[0].deaths ? match.stats[0].deaths : 0) >= 0 ? 'text-success' : 'text-danger'"
class="td-plus text-center">
{{
(match.stats[0].kills ? match.stats[0].kills : 0) - (match.stats[0].deaths ? match.stats[0].deaths : 0)
}}
</td>
<td class="td-adr text-center">
{{
Math.floor((match.stats[0].extended.dmg.enemy ? match.stats[0].extended.dmg.enemy : 0) / (match.score[0] + match.score[1]))
}}
</td>
<td :class="GetHLTV_1(
match.stats[0].kills,
match.score[0] + match.score[1],
match.stats[0].deaths,
@@ -148,29 +148,28 @@
match.stats[0].extended?.multi_kills?.triple,
match.stats[0].extended?.multi_kills?.quad,
match.stats[0].extended?.multi_kills?.pent) >= 1 ? 'text-success' : 'text-warning'"
class="td-hltv text-center fw-bold">
{{
GetHLTV_1(
match.stats[0].kills,
match.score[0] + match.score[1],
match.stats[0].deaths,
match.stats[0].extended?.multi_kills?.duo,
match.stats[0].extended?.multi_kills?.triple,
match.stats[0].extended?.multi_kills?.quad,
match.stats[0].extended?.multi_kills?.pent)
}}
</td>
<td class="td-duration text-center">
{{ FormatDuration(match.duration) }}
</td>
<td class="td-date">
{{ FormatDate(match.date) }}
</td>
</tr>
</tbody>
</table>
<h5 v-else>No matches on record</h5>
</div>
class="td-hltv text-center fw-bold">
{{
GetHLTV_1(
match.stats[0].kills,
match.score[0] + match.score[1],
match.stats[0].deaths,
match.stats[0].extended?.multi_kills?.duo,
match.stats[0].extended?.multi_kills?.triple,
match.stats[0].extended?.multi_kills?.quad,
match.stats[0].extended?.multi_kills?.pent)
}}
</td>
<td :title="FormatFullDuration(match.duration)" class="td-duration text-center">
{{ FormatDuration(match.duration) }}
</td>
<td :title="FormatFullDate(match.date)" class="td-date">
{{ FormatDate(match.date) }}
</td>
</tr>
</tbody>
</table>
<h5 v-else>No matches on record</h5>
</div>
</div>
</template>
@@ -179,7 +178,15 @@
import {onBeforeMount, reactive, watch} from "vue";
import axios from "axios";
import {useStore} from "vuex";
import {FormatDate, FormatDuration, GetHLTV_1, GoToMatch} from "@/utils";
import {
FormatDate,
FormatDuration,
FormatFullDate,
FormatFullDuration,
GetHLTV_1,
GoToMatch,
SaveLastVisitedToLocalStorage
} from "../utils";
export default {
name: 'Player',
@@ -216,7 +223,7 @@ export default {
data.statusError = 'Is not a valid authcode'
const res = await axios
.post(`http://localhost:1337/http://localhost:8000/player/trackme`, `id=${store.state.id64}&authcode=${data.userData.authcode}&sharecode=${data.userData.sharecode}`)
.post(`http://localhost:8000/player/trackme`, `id=${store.state.id64}&authcode=${data.userData.authcode}&sharecode=${data.userData.sharecode}`)
if (res.status === 401) {
data.statusError = 'Data does not match player'
@@ -231,13 +238,19 @@ export default {
const GetUser = () => {
axios
.get(`http://localhost:1337/http://localhost:8000/player/${props.id}`)
.get(`http://localhost:8000/player/${props.id}`)
.then((response) => {
data.playerDetails = response.data
data.matches = response.data.matches
store.state.id64 = response.data.steamid64
console.log(response.data)
let player = {
'steamid64': response.data.steamid64,
'name': response.data.name,
'avatar': response.data.avatar
}
SaveLastVisitedToLocalStorage(player)
document.title = `${response.data.name} | CSGO-W.TF`
})
.catch((e) => {
@@ -252,7 +265,19 @@ export default {
})
return {
data, store, mapImg, wl, props, GetUser, TrackMe, FormatDate, FormatDuration, GoToMatch, GetHLTV_1
data,
store,
mapImg,
wl,
props,
GetUser,
TrackMe,
FormatDate,
FormatFullDate,
FormatDuration,
FormatFullDuration,
GoToMatch,
GetHLTV_1
}
}
}
@@ -261,57 +286,97 @@ export default {
<style lang="scss" scoped>
.card {
padding-top: 10px;
.avatar {
border-radius: 50%;
height: 150px;
width: 150px;
box-shadow: 0 0 10px black;
}
}
td {
vertical-align: middle;
table {
tr {
th {
line-height: 1;
}
td {
line-height: 60px;
font-size: 1rem;
}
th:last-child, td:last-child {
text-align: right;
width: 150px;
}
th[title] {
text-decoration: underline dotted grey;
}
td {
vertical-align: middle;
}
.td-map img {
width: 60px;
height: auto;
max-height: 60px;
.map-icon {
height: 75px;
}
}
.td-rank img {
width: 70px;
height: auto;
.rank-icon {
height: 35px;
}
}
.td-score {
font-size: 1.2rem;
width: 120px;
}
}
.match {
border-bottom: 1px solid rgba(73, 73, 73, 0.73);
&:last-child {
border: none;
}
&:hover {
cursor: pointer;
background: rgba(0, 0, 0, 0.25);
}
}
}
th:last-child, td:last-child {
text-align: right;
width: 150px;
}
.td-map {
width: 100px;
height: 100px;
}
.td-rank {
width: 88px;
height: 35px;
}
.td-score {
font-size: 1.2rem;
width: 120px;
}
.avatar {
border-radius: 50%;
height: 150px;
width: 150px;
box-shadow: 0 0 10px black;
}
.map-icon {
height: 75px;
}
.rank-icon {
height: 35px;
}
.match {
border-bottom: 1px solid rgba(73, 73, 73, 0.73);
}
.match:last-child {
border: none;
}
.match:hover {
cursor: pointer;
background: rgba(0, 0, 0, 0.25);
@media screen and (max-width: 768px) {
.card {
.avatar {
height: 75px;
width: 75px;
}
}
.td-map img {
width: 40px;
height: auto;
max-height: 40px;
}
.td-rank img {
width: 50px;
height: auto;
}
.kills, .deaths, .assists, .kdiff, .adr, .duration, .hltv,
.td-kills, .td-deaths, .td-assists, .td-plus, .td-adr, .td-duration, .td-hltv {
display: none;
}
}
</style>