Skip to content

Commit

Permalink
feat: Implement Usage Report
Browse files Browse the repository at this point in the history
  • Loading branch information
potato4d committed Sep 27, 2020
1 parent b1e76ec commit 4a3e39b
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 4 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@nuxtjs/dotenv": "^1.4.1",
"@nuxtjs/tailwindcss": "^1.0.0",
"@types/axios": "^0.14.0",
"@types/jest": "^26.0.14",
"@types/uuid": "^7.0.3",
"@vue/test-utils": "^1.0.0-beta.27",
"babel-jest": "^24.1.0",
Expand Down
3 changes: 3 additions & 0 deletions src/components/common/AppUsage.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.usage-chart {
transition: width 0.4s ease-out;
}
193 changes: 193 additions & 0 deletions src/components/common/AppUsage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import Vue, { CreateElement, VNode, PropType } from 'vue'
import * as tsx from 'vue-tsx-support'
import { Pokemon } from '~/analyzer/config/dex'
import { BattleRecord } from '~/types/struct'
import './AppUsage.css'
// import data from './example'

// const example: RecordSet = data as any

type RecordSet = {
party: BattleRecord['myParty']
items: BattleRecord[]
}

type RateResult = {
selectCount: number
battleCount: number
winCount: number
}

type WinRateItem = { slug: string } & RateResult

type WinRates = {
items: WinRateItem[]
bestWin: number
bestSelect: number
battleCount: number
}

export const AppUsage = tsx.component({
props: {
recordSets: Object as PropType<RecordSet>,
},
data() {
return {
isMounted: false,
}
},
mounted() {
setTimeout(() => {
this.isMounted = true
}, 100)
},
computed: {
winRates(): WinRates {
const items: WinRates['items'] = this.recordSets.party.reduce(
(b: any, pokemon: Pokemon) => {
return {
...b,
[pokemon.slug]: this.recordSets.items.reduce(
(counts: any, record: BattleRecord) => {
if (record.myChoice.includes(pokemon.slug)) {
counts.selectCount++
if (record.result === 'win') {
counts.winCount++
}
}
return counts
},
{
selectCount: 0,
battleCount: this.recordSets.items.length,
winCount: 0,
}
),
}
},
{} as WinRates['items']
)
return {
items: Object.entries(items)
.map(([slug, item]) => ({
slug,
...item,
}))
.sort((b, a) => {
if (b.winCount === 0 || b.selectCount === 0) {
return +1
}
if (a.winCount === 0 || a.selectCount === 0) {
return -1
}
return b.selectCount < a.selectCount ? +1 : -1
}),
bestWin: Object.values(items).reduce((b: number, a) => {
if (a.winCount === 0) {
return 0
}
if (a.selectCount === 0) {
return 0
}
return b < a.winCount / a.selectCount ? a.winCount / a.selectCount : b
}, 0),
bestSelect: Object.values(items).reduce((b: number, a) => {
return b < a.selectCount ? a.selectCount : b
}, 0),
battleCount: this.recordSets.items.length,
}
},
},
render() {
return (
<div
class="p-4 w-full text-white"
style={{
background: '#343334',
}}
>
<div class="flex items-center justify-between px-6 py-4">
<h4 class="text-2xl font-bold text-white">この構築の利用統計</h4>
<div class="flex items-center">
<span class="inline-block w-4 h-4 rounded-full bg-blue-600"></span>
<span class="inline-block ml-2 mr-12">選出率</span>
<span class="inline-block w-4 h-4 rounded-full bg-pink-600"></span>
<span class="inline-block ml-2">勝率</span>
</div>
</div>
<ul>
{this.winRates.items.map((item) => (
<li class="flex justify-start items-center">
<div
class="flex items-center justify-center"
style={{
width: '80px',
height: '70px',
}}
>
<img
style={{
width: '60px',
height: '50px',
imageRendering: 'pixelated',
}}
class="mr-3 object-cover object-center-bottom"
src={`/pokemon63/static/images/icons/${item.slug}.png`}
alt=""
/>
</div>
<div class="flex-1 text-xl pt-4 pr-4">
<div class="flex w-full items-center">
<div class="flex-1">
<div class="bg-black w-full rounded">
<div
class="usage-chart h-2 rounded bg-blue-600"
style={{
width: this.isMounted
? `${~~(
(item.selectCount / item.battleCount) *
100
)}%`
: 0,
}}
></div>
</div>
</div>
<div class="w-48 pl-4 font-bold">
選出率{~~((item.selectCount / item.battleCount) * 100)}%
</div>
</div>

<div class="w-1 h-3"></div>

<div class="flex w-full items-center">
<div class="flex-1">
<div class="bg-black w-full rounded">
<div
class="usage-chart h-2 rounded bg-pink-600"
style={{
width: this.isMounted
? `${
item.selectCount && item.winCount
? ~~((item.winCount / item.selectCount) * 100)
: 0
}%`
: 0,
}}
></div>
</div>
</div>
<div class="w-48 pl-4 font-bold">
勝率{~~((item.winCount / item.selectCount) * 100)}%
</div>
</div>
</div>
</li>
))}
</ul>
</div>
)
},
})

export default AppUsage
49 changes: 46 additions & 3 deletions src/pages/u/_userId.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,51 @@
リストで表示
</a>
</li>
<li
class="border-r border-black"
:class="{ 'text-red-700': viewType === 'usage' }"
>
<a
href="#"
@click.prevent="viewType = 'usage'"
class="inline-block px-9 py-3"
>
使用率を表示
</a>
</li>
</ul>
</div>
<template v-if="viewType === 'usage'">
<div v-for="recordSets in groupedBattleRecord" class="mb-15">
<div>
<AppSubHeading class="flex text-2xl justify-start items-center">
<ul class="flex items-end justify-start">
<span v-for="pokemon in recordSets.party" class="inline-block">
<img
:src="`/pokemon63/static/images/icons/${pokemon.slug}.png`"
:style="{
imageRendering: 'pixelated',
width: '60px',
height: '50px',
objectFit: 'cover',
}"
alt=""
/>
</span>
<span
class="mb-2 flex items-center justify-center h-full text-2xl font-bold"
>
での戦績
</span>
</ul>
</AppSubHeading>
<!-- <div class=" pt-18 grid justify-between items-start"> -->
<AppUsage :recordSets="recordSets" />
<!-- </div> -->
</div>
</div>
</template>

<template v-if="viewType === 'revision'">
<div v-for="recordSets in groupedBattleRecord" class="mb-15">
<div>
Expand Down Expand Up @@ -110,7 +153,7 @@ import {
type LocalData = {
user: User | null
viewType: 'revision' | 'list'
viewType: 'revision' | 'list' | 'usage'
battleRecords: BattleRecord[]
groupedBattleRecord: RecordSet[]
}
Expand Down Expand Up @@ -148,7 +191,7 @@ export default Vue.extend({
data(): LocalData {
return {
user: null,
viewType: 'revision',
viewType: 'usage',
battleRecords: [],
groupedBattleRecord: [],
}
Expand All @@ -164,7 +207,7 @@ export default Vue.extend({
.orderBy('season', 'desc')
.orderBy('createdAt', 'desc')
.where('userId', '==', userId)
.limit(200)
.limit(300)
.get(),
])
if (!user.exists) {
Expand Down
1 change: 1 addition & 0 deletions src/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = {
gray: colors.gray,
red: colors.red,
blue: colors.blue,
pink: colors.pink,
},
extend: {
width: {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"types": [
"@types/node",
"@nuxt/types",
"@types/jest",
"@nuxtjs/dayjs"
]
},
Expand Down

0 comments on commit 4a3e39b

Please sign in to comment.